feat: 회사/제품/버전 PATCH·DELETE, 세금 DELETE, 기준정보 nav
All checks were successful
build-and-push / build (push) Successful in 32s
All checks were successful
build-and-push / build (push) Successful in 32s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
db0f857f0a
commit
dcf8b415db
@ -45,6 +45,7 @@ var navItems = []NavItem{
|
||||
{Key: "approvals", Label: "승인 관리", Path: "/admin/approvals", Icon: "CheckSquare", AdminOnly: true, Section: "관리자"},
|
||||
{Key: "attendance-admin", Label: "근무 관리", Path: "/admin/attendance", Icon: "ClipboardList", AdminOnly: true, Section: "관리자"},
|
||||
{Key: "projects-admin", Label: "프로젝트 관리", Path: "/admin/projects", Icon: "FolderCog", AdminOnly: true, Section: "관리자"},
|
||||
{Key: "master", Label: "기준정보", Path: "/admin/master", Icon: "Database", AdminOnly: true, Section: "관리자"},
|
||||
{Key: "incentive-admin", Label: "인센티브 관리", Path: "/admin/incentive", Icon: "Calculator", AdminOnly: true, Section: "관리자"},
|
||||
{Key: "accounting", Label: "회계", Path: "/admin/accounting", Icon: "Wallet", AdminOnly: true, Section: "관리자"},
|
||||
{Key: "members", Label: "구성원", Path: "/admin/members", Icon: "Users", AdminOnly: true, Section: "관리자"},
|
||||
|
||||
@ -141,6 +141,14 @@ func (s *Server) handlePatchTax(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, t)
|
||||
}
|
||||
|
||||
func (s *Server) handleDeleteTax(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
s.db.Delete(&models.TaxRecord{}, "id = ?", chi.URLParam(r, "taxId"))
|
||||
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
||||
}
|
||||
|
||||
// ---- summary: cashflow vs incentive gap -----------------------------------
|
||||
|
||||
type acctSummary struct {
|
||||
|
||||
@ -78,6 +78,60 @@ func (s *Server) handleCreateVersion(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusCreated, v)
|
||||
}
|
||||
|
||||
// patchByID applies a whitelisted-free JSON patch to a model row (admin only).
|
||||
func (s *Server) patchModel(w http.ResponseWriter, r *http.Request, dest interface{}, id string) {
|
||||
if !s.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
if err := s.db.First(dest, "id = ?", id).Error; err != nil {
|
||||
writeError(w, http.StatusNotFound, "찾을 수 없습니다")
|
||||
return
|
||||
}
|
||||
var patch map[string]interface{}
|
||||
if err := decodeJSON(r, &patch); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
delete(patch, "id")
|
||||
if err := s.db.Model(dest).Updates(patch).Error; err != nil {
|
||||
writeError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
s.db.First(dest, "id = ?", id)
|
||||
writeJSON(w, http.StatusOK, dest)
|
||||
}
|
||||
|
||||
func (s *Server) handlePatchCompany(w http.ResponseWriter, r *http.Request) {
|
||||
s.patchModel(w, r, &models.Company{}, chi.URLParam(r, "id"))
|
||||
}
|
||||
func (s *Server) handleDeleteCompany(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
s.db.Delete(&models.Company{}, "id = ?", chi.URLParam(r, "id"))
|
||||
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
||||
}
|
||||
func (s *Server) handlePatchProduct(w http.ResponseWriter, r *http.Request) {
|
||||
s.patchModel(w, r, &models.Product{}, chi.URLParam(r, "id"))
|
||||
}
|
||||
func (s *Server) handleDeleteProduct(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
s.db.Delete(&models.Product{}, "id = ?", chi.URLParam(r, "id"))
|
||||
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
||||
}
|
||||
func (s *Server) handlePatchVersion(w http.ResponseWriter, r *http.Request) {
|
||||
s.patchModel(w, r, &models.Version{}, chi.URLParam(r, "id"))
|
||||
}
|
||||
func (s *Server) handleDeleteVersion(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.requireAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
s.db.Delete(&models.Version{}, "id = ?", chi.URLParam(r, "id"))
|
||||
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
||||
}
|
||||
|
||||
// ---- projects -------------------------------------------------------------
|
||||
|
||||
// myProjectIDs returns the project IDs the caller is a member of (or PM of).
|
||||
|
||||
@ -98,10 +98,16 @@ func NewRouter(db *gorm.DB, store *storage.Storage, cfg config.Config, pusher *p
|
||||
// ---- slice 3: projects ----
|
||||
r.Get("/companies", s.handleListCompanies)
|
||||
r.Post("/companies", s.handleCreateCompany)
|
||||
r.Patch("/companies/{id}", s.handlePatchCompany)
|
||||
r.Delete("/companies/{id}", s.handleDeleteCompany)
|
||||
r.Get("/products", s.handleListProducts)
|
||||
r.Post("/products", s.handleCreateProduct)
|
||||
r.Patch("/products/{id}", s.handlePatchProduct)
|
||||
r.Delete("/products/{id}", s.handleDeleteProduct)
|
||||
r.Get("/versions", s.handleListVersions)
|
||||
r.Post("/versions", s.handleCreateVersion)
|
||||
r.Patch("/versions/{id}", s.handlePatchVersion)
|
||||
r.Delete("/versions/{id}", s.handleDeleteVersion)
|
||||
r.Get("/projects", s.handleListProjects)
|
||||
r.Post("/projects", s.handleCreateProject)
|
||||
r.Get("/projects/{id}", s.handleGetProject)
|
||||
@ -153,6 +159,7 @@ func NewRouter(db *gorm.DB, store *storage.Storage, cfg config.Config, pusher *p
|
||||
r.Get("/taxes", s.handleListTaxes)
|
||||
r.Post("/taxes", s.handleCreateTax)
|
||||
r.Patch("/taxes/{taxId}", s.handlePatchTax)
|
||||
r.Delete("/taxes/{taxId}", s.handleDeleteTax)
|
||||
r.Get("/accounting/summary", s.handleAccountingSummary) // cashflow vs incentive gap
|
||||
|
||||
// ---- slice 6: dashboard ----
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user