better api paths
This commit is contained in:
@@ -107,20 +107,20 @@ func (r *BudgetRepo) Create(ctx context.Context, req model.CreateBudgetRequest)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (r *BudgetRepo) Update(ctx context.Context, id int, req model.UpdateBudgetRequest) (*model.Budget, error) {
|
||||
func (r *BudgetRepo) Update(ctx context.Context, req model.UpdateBudgetRequest) (*model.Budget, error) {
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE budgets
|
||||
SET version=?, amount=?, notes=?,
|
||||
updated_at=strftime('%Y-%m-%dT%H:%M:%SZ','now')
|
||||
WHERE id=?`,
|
||||
req.Version, req.Amount, req.Notes, id,
|
||||
req.Version, req.Amount, req.Notes, req.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update budget: %w", err)
|
||||
}
|
||||
|
||||
row := r.db.QueryRowContext(ctx,
|
||||
`SELECT`+budgetSelectCols+`FROM budgets WHERE id = ?`, id)
|
||||
`SELECT`+budgetSelectCols+`FROM budgets WHERE id = ?`, req.ID)
|
||||
b, err := scanBudget(row)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetch updated budget: %w", err)
|
||||
@@ -128,8 +128,8 @@ func (r *BudgetRepo) Update(ctx context.Context, id int, req model.UpdateBudgetR
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (r *BudgetRepo) Delete(ctx context.Context, id int) error {
|
||||
_, err := r.db.ExecContext(ctx, `DELETE FROM budgets WHERE id = ?`, id)
|
||||
func (r *BudgetRepo) Delete(ctx context.Context, req model.DeleteBudgetRequest) error {
|
||||
_, err := r.db.ExecContext(ctx, `DELETE FROM budgets WHERE id = ?`, req.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete budget: %w", err)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package handler
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"Engine/internal/model"
|
||||
@@ -39,12 +38,8 @@ func (h *BudgetHandler) Create(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusCreated, budget)
|
||||
}
|
||||
|
||||
// PUT /api/v1/budgets/update
|
||||
func (h *BudgetHandler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.PathValue("id"))
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
var req model.UpdateBudgetRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid request body")
|
||||
@@ -57,7 +52,7 @@ func (h *BudgetHandler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
budget, err := h.svc.Update(r.Context(), id, req)
|
||||
budget, err := h.svc.Update(r.Context(), req)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
@@ -65,13 +60,16 @@ func (h *BudgetHandler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, budget)
|
||||
}
|
||||
|
||||
// DELETE /api/v1/budgets/delete
|
||||
func (h *BudgetHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(r.PathValue("id"))
|
||||
if err != nil {
|
||||
var req struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
if err := h.svc.Delete(r.Context(), id); err != nil {
|
||||
if err := h.svc.Delete(r.Context(), req); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ func NewReferenceHandler(repo *database.ReferenceRepo) *ReferenceHandler {
|
||||
|
||||
// ── Departments ───────────────────────────────────────────────────────────────
|
||||
|
||||
// POST /api/v1/departments
|
||||
// POST /api/v1/department/create
|
||||
// PUT /api/v1/departments/{id} (same body, id from path)
|
||||
func (h *ReferenceHandler) CreateDepartment(w http.ResponseWriter, r *http.Request) {
|
||||
var req database.Department
|
||||
@@ -47,7 +47,7 @@ func (h *ReferenceHandler) CreateDepartment(w http.ResponseWriter, r *http.Reque
|
||||
writeJSON(w, http.StatusCreated, dept)
|
||||
}
|
||||
|
||||
// GET /api/v1/departments
|
||||
// GET /api/v1/department/list
|
||||
func (h *ReferenceHandler) ListDepartments(w http.ResponseWriter, r *http.Request) {
|
||||
depts, err := h.repo.ListDepartments(r.Context())
|
||||
if err != nil {
|
||||
@@ -60,8 +60,16 @@ func (h *ReferenceHandler) ListDepartments(w http.ResponseWriter, r *http.Reques
|
||||
writeJSON(w, http.StatusOK, depts)
|
||||
}
|
||||
|
||||
// DELETE /api/v1/departments/{id}
|
||||
// DELETE /api/v1/department/delete
|
||||
func (h *ReferenceHandler) DeleteDepartment(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, fmt.Sprintf("invalid body: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := pathID(r, "id")
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
|
||||
@@ -27,6 +27,14 @@ type Department struct {
|
||||
CostCenter string `json:"cost_center"`
|
||||
}
|
||||
|
||||
type GetDepartmentBudget struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type GetDepartmentActual struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type GLAccount struct {
|
||||
ID int `json:"id"`
|
||||
Code string `json:"code"`
|
||||
@@ -35,6 +43,14 @@ type GLAccount struct {
|
||||
FavourHigh bool `json:"favour_high"`
|
||||
}
|
||||
|
||||
type GetGLAccountBudget struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type GetGLAccountActual struct {
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type Budget struct {
|
||||
ID int `json:"id"`
|
||||
FiscalYear int `json:"fiscal_year"`
|
||||
@@ -94,44 +110,64 @@ func (cBudget *CreateBudgetRequest) Valid() []string {
|
||||
}
|
||||
|
||||
type UpdateBudgetRequest struct {
|
||||
ID int `json:"id"`
|
||||
FiscalYear int `json:"fiscal_year"`
|
||||
FiscalPeriod int `json:"fiscal_period"`
|
||||
Version BudgetVersion `json:"version"`
|
||||
DepartmentID int `json:"department_id"`
|
||||
GLAccountID int `json:"gl_account_id"`
|
||||
Amount float64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
Notes string `json:"notes"`
|
||||
ChangedBy string `json:"created_by"`
|
||||
ID int // required, not a pointer
|
||||
ChangedBy string // required, not a pointer
|
||||
FiscalYear *int
|
||||
FiscalPeriod *int
|
||||
Version *string
|
||||
DepartmentID *int
|
||||
GLAccountID *int
|
||||
Amount *float64
|
||||
Currency *string
|
||||
Notes *string
|
||||
}
|
||||
|
||||
type DeleteBudgetRequest struct {
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
func (uBudget *UpdateBudgetRequest) Valid() []string {
|
||||
var errs []string
|
||||
|
||||
if uBudget.FiscalYear < 1 || uBudget.FiscalYear > 2200 {
|
||||
errs = append(errs, "fiscal_year must be between 1 and 2200")
|
||||
}
|
||||
if uBudget.FiscalPeriod < 1 || uBudget.FiscalPeriod > 12 {
|
||||
errs = append(errs, "fiscal_period must be between 1 and 12")
|
||||
}
|
||||
if uBudget.Version == "" {
|
||||
errs = append(errs, "version is required")
|
||||
}
|
||||
if uBudget.DepartmentID == 0 {
|
||||
errs = append(errs, "department_id is required")
|
||||
}
|
||||
if uBudget.GLAccountID == 0 {
|
||||
errs = append(errs, "gl_account_id is required")
|
||||
}
|
||||
if uBudget.Amount <= 0 {
|
||||
errs = append(errs, "amount must be greater than 0")
|
||||
}
|
||||
if uBudget.Currency == "" {
|
||||
errs = append(errs, "currency is required")
|
||||
// Always required: identity + audit
|
||||
if uBudget.ID == 0 {
|
||||
errs = append(errs, "id is required")
|
||||
}
|
||||
if uBudget.ChangedBy == "" {
|
||||
errs = append(errs, "created_by is required")
|
||||
errs = append(errs, "changed_by is required")
|
||||
}
|
||||
|
||||
// Validate fields only if they were provided
|
||||
if uBudget.FiscalYear != nil {
|
||||
if *uBudget.FiscalYear < 1 || *uBudget.FiscalYear > 2200 {
|
||||
errs = append(errs, "fiscal_year must be between 1 and 2200")
|
||||
}
|
||||
}
|
||||
if uBudget.FiscalPeriod != nil {
|
||||
if *uBudget.FiscalPeriod < 1 || *uBudget.FiscalPeriod > 12 {
|
||||
errs = append(errs, "fiscal_period must be between 1 and 12")
|
||||
}
|
||||
}
|
||||
if uBudget.Amount != nil && *uBudget.Amount <= 0 {
|
||||
errs = append(errs, "amount must be greater than 0")
|
||||
}
|
||||
if uBudget.Version != nil && *uBudget.Version == "" {
|
||||
errs = append(errs, "version cannot be empty")
|
||||
}
|
||||
if uBudget.Currency != nil && *uBudget.Currency == "" {
|
||||
errs = append(errs, "currency cannot be empty")
|
||||
}
|
||||
|
||||
// At least one field must be set — otherwise there's nothing to do
|
||||
if uBudget.FiscalYear == nil &&
|
||||
uBudget.FiscalPeriod == nil &&
|
||||
uBudget.Version == nil &&
|
||||
uBudget.DepartmentID == nil &&
|
||||
uBudget.GLAccountID == nil &&
|
||||
uBudget.Amount == nil &&
|
||||
uBudget.Currency == nil &&
|
||||
uBudget.Notes == nil {
|
||||
errs = append(errs, "at least one field must be provided to update")
|
||||
}
|
||||
|
||||
return errs
|
||||
|
||||
@@ -19,10 +19,10 @@ func (s *BudgetService) Create(ctx context.Context, req model.CreateBudgetReques
|
||||
return s.repo.Create(ctx, req)
|
||||
}
|
||||
|
||||
func (s *BudgetService) Update(ctx context.Context, id int, req model.UpdateBudgetRequest) (*model.Budget, error) {
|
||||
return s.repo.Update(ctx, id, req)
|
||||
func (s *BudgetService) Update(ctx context.Context, req model.UpdateBudgetRequest) (*model.Budget, error) {
|
||||
return s.repo.Update(ctx, req)
|
||||
}
|
||||
|
||||
func (s *BudgetService) Delete(ctx context.Context, id int) error {
|
||||
return s.repo.Delete(ctx, id)
|
||||
func (s *BudgetService) Delete(ctx context.Context, req model.DeleteBudgetRequest) error {
|
||||
return s.repo.Delete(ctx, req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user