优化小程序报销模块
This commit is contained in:
1 parent
1e9666cefd
commit
48f3bd997c
21 files changed
+5738
-1750
No files matched your search
+227
-130
@@ -24,17 +24,17 @@ type BackendReimburseController struct {
|
||||
}
|
||||
|
||||
type reimbursementItemPayload struct {
|
||||
ID uint64 `json:"id"`
|
||||
ExpenseType string `json:"expense_type"`
|
||||
ExpenseDate string `json:"expense_date"`
|
||||
Amount float64 `json:"amount"`
|
||||
Quantity int `json:"quantity"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
Description string `json:"description"`
|
||||
ReceiptFlag int8 `json:"receipt_flag"`
|
||||
ReceiptFileIDs string `json:"receipt_file_ids"`
|
||||
ReceiptURLs string `json:"receipt_urls"`
|
||||
ReceiptAmounts *string `json:"receipt_amounts,omitempty"`
|
||||
ID uint64 `json:"id"`
|
||||
ExpenseType string `json:"expense_type"`
|
||||
ExpenseDate string `json:"expense_date"`
|
||||
Amount float64 `json:"amount"`
|
||||
Quantity int `json:"quantity"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
Description string `json:"description"`
|
||||
ReceiptFlag int8 `json:"receipt_flag"`
|
||||
ReceiptFileIDs string `json:"receipt_file_ids"`
|
||||
ReceiptURLs string `json:"receipt_urls"`
|
||||
ReceiptAmounts *string `json:"receipt_amounts,omitempty"`
|
||||
}
|
||||
|
||||
type reimbursementPayload struct {
|
||||
@@ -88,7 +88,9 @@ func (c *BackendReimburseController) getID() (uint64, bool) {
|
||||
return id, err == nil && id > 0
|
||||
}
|
||||
|
||||
func (c *BackendReimburseController) findOwned(id uint64, claims *jwtutil.Claims) (*models.BackendReimbursement, error) {
|
||||
// findOwnedReimbursement 按 id + 租户 + 申请人 查询本人报销单(未删除)。
|
||||
// 报销单为个人单据:与列表口径一致,按 JWT 中 tid + user_id 双重隔离。
|
||||
func findOwnedReimbursement(claims *jwtutil.Claims, id uint64) (*models.BackendReimbursement, error) {
|
||||
row := &models.BackendReimbursement{}
|
||||
err := models.Orm.QueryTable(new(models.BackendReimbursement)).
|
||||
Filter("id", id).
|
||||
@@ -301,7 +303,7 @@ func reimbursementItemDTO(item models.BackendReimbursementItem, scheme, host str
|
||||
"amount": item.Amount, "quantity": item.Quantity, "unit_price": item.UnitPrice,
|
||||
"description": item.Description, "receipt_flag": receiptFlag,
|
||||
"receipt_file_ids": item.ReceiptFileIDs,
|
||||
"receipt_urls": receiptURLs, "receipt_amounts": item.ReceiptAmounts,
|
||||
"receipt_urls": receiptURLs, "receipt_amounts": item.ReceiptAmounts,
|
||||
"receipt_files": receiptFiles,
|
||||
}
|
||||
}
|
||||
@@ -310,7 +312,7 @@ func reimbursementDTO(row models.BackendReimbursement, items []models.BackendRei
|
||||
_, userName := services.OperationLogUser(&row.Tid, row.UserID)
|
||||
result := map[string]interface{}{
|
||||
"id": row.ID, "tid": row.Tid, "uid": row.UID, "user_id": row.UserID,
|
||||
"user_name": userName,
|
||||
"user_name": userName,
|
||||
"department_id": row.DepartmentID, "total_amount": row.TotalAmount,
|
||||
"status": row.Status, "apply_date": row.ApplyDate.Format("2006-01-02"),
|
||||
"description": row.Description, "current_approver_id": row.CurrentApproverID,
|
||||
@@ -333,16 +335,26 @@ func (c *BackendReimburseController) List() {
|
||||
}
|
||||
page, _ := c.GetInt("page", 1)
|
||||
pageSize, _ := c.GetInt("pageSize", 10)
|
||||
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||
status, statusErr := c.GetInt("status")
|
||||
|
||||
data, code, msg := reimburseListData(claims, page, pageSize, keyword, statusErr == nil, status)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", data)
|
||||
}
|
||||
|
||||
// reimburseListData 报销单列表共享逻辑:Web 后台与移动端(/api/oa/expense/list)共用。
|
||||
// 返回 (数据, HTTP 状态码, 错误信息),错误信息为空串表示成功。
|
||||
func reimburseListData(claims *jwtutil.Claims, page, pageSize int, keyword string, hasStatus bool, status int) (map[string]interface{}, int, string) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
keyword := strings.TrimSpace(c.GetString("keyword"))
|
||||
status, statusErr := c.GetInt("status")
|
||||
hasStatus := statusErr == nil
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.BackendReimbursement)).
|
||||
Filter("tid", claims.TenantId).Filter("user_id", claims.UserID).Filter("is_deleted", 0)
|
||||
if keyword != "" {
|
||||
@@ -353,14 +365,12 @@ func (c *BackendReimburseController) List() {
|
||||
}
|
||||
total, err := qs.Count()
|
||||
if err != nil {
|
||||
c.reply(500, "查询失败", nil)
|
||||
return
|
||||
return nil, 500, "查询失败"
|
||||
}
|
||||
var rows []models.BackendReimbursement
|
||||
_, err = qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows)
|
||||
if err != nil && err != orm.ErrNoRows {
|
||||
c.reply(500, "查询失败", nil)
|
||||
return
|
||||
return nil, 500, "查询失败"
|
||||
}
|
||||
if rows == nil {
|
||||
rows = []models.BackendReimbursement{}
|
||||
@@ -383,7 +393,7 @@ func (c *BackendReimburseController) List() {
|
||||
"update_time": row.UpdateTime,
|
||||
})
|
||||
}
|
||||
c.reply(200, "success", map[string]interface{}{"list": list, "total": total})
|
||||
return map[string]interface{}{"list": list, "total": total}, 200, ""
|
||||
}
|
||||
|
||||
// Detail GET /backend/reimbursements/:id
|
||||
@@ -398,21 +408,29 @@ func (c *BackendReimburseController) Detail() {
|
||||
c.reply(400, "无效ID", nil)
|
||||
return
|
||||
}
|
||||
row, err := c.findOwned(id, claims)
|
||||
if err != nil {
|
||||
c.reply(404, "报销单不存在", nil)
|
||||
scheme, host := services.PublicRequestBaseURL(&c.Controller)
|
||||
data, code, msg := reimburseDetailData(claims, id, scheme, host)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", data)
|
||||
}
|
||||
|
||||
// reimburseDetailData 报销单详情共享逻辑(主单 + 明细 + 发票文件)。
|
||||
func reimburseDetailData(claims *jwtutil.Claims, id uint64, scheme, host string) (map[string]interface{}, int, string) {
|
||||
row, err := findOwnedReimbursement(claims, id)
|
||||
if err != nil {
|
||||
return nil, 404, "报销单不存在"
|
||||
}
|
||||
var items []models.BackendReimbursementItem
|
||||
_, err = models.Orm.QueryTable(new(models.BackendReimbursementItem)).
|
||||
Filter("reimbursement_id", id).Filter("tid", claims.TenantId).Filter("is_deleted", 0).
|
||||
OrderBy("id").All(&items)
|
||||
if err != nil && err != orm.ErrNoRows {
|
||||
c.reply(500, "查询明细失败: "+err.Error(), nil)
|
||||
return
|
||||
return nil, 500, "查询明细失败: " + err.Error()
|
||||
}
|
||||
scheme, host := services.PublicRequestBaseURL(&c.Controller)
|
||||
c.reply(200, "success", reimbursementDTO(*row, items, scheme, host))
|
||||
return reimbursementDTO(*row, items, scheme, host), 200, ""
|
||||
}
|
||||
|
||||
// Create POST /backend/reimbursements
|
||||
@@ -427,10 +445,19 @@ func (c *BackendReimburseController) Create() {
|
||||
c.reply(400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
id, code, msg := reimburseCreateCore(claims, payload)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", map[string]interface{}{"id": id})
|
||||
}
|
||||
|
||||
// reimburseCreateCore 创建报销单(草稿)共享逻辑。
|
||||
func reimburseCreateCore(claims *jwtutil.Claims, payload reimbursementPayload) (uint64, int, string) {
|
||||
applyDate, err := parseReimburseDate(payload.ApplyDate)
|
||||
if err != nil {
|
||||
c.reply(400, "申请日期格式错误", nil)
|
||||
return
|
||||
return 0, 400, "申请日期格式错误"
|
||||
}
|
||||
uid := uint64(claims.UserID)
|
||||
row := &models.BackendReimbursement{
|
||||
@@ -439,10 +466,9 @@ func (c *BackendReimburseController) Create() {
|
||||
Description: nullableReimburseString(payload.Description),
|
||||
}
|
||||
if err := saveReimbursement(row); err != nil {
|
||||
c.reply(500, "创建报销单失败", nil)
|
||||
return
|
||||
return 0, 500, "创建报销单失败"
|
||||
}
|
||||
c.reply(200, "success", map[string]interface{}{"id": row.ID})
|
||||
return row.ID, 200, ""
|
||||
}
|
||||
|
||||
// Update POST /backend/reimbursements/:id
|
||||
@@ -457,31 +483,37 @@ func (c *BackendReimburseController) Update() {
|
||||
c.reply(400, "无效ID", nil)
|
||||
return
|
||||
}
|
||||
row, err := c.findOwned(id, claims)
|
||||
if err != nil {
|
||||
c.reply(404, "报销单不存在", nil)
|
||||
return
|
||||
}
|
||||
if row.Status != 0 {
|
||||
c.reply(400, "只有草稿可以编辑", nil)
|
||||
return
|
||||
}
|
||||
payload, err := c.parsePayload()
|
||||
if err != nil {
|
||||
c.reply(400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
code, msg := reimburseUpdateCore(claims, id, payload)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", map[string]interface{}{"id": id})
|
||||
}
|
||||
|
||||
// reimburseUpdateCore 编辑报销单(仅草稿)共享逻辑。
|
||||
func reimburseUpdateCore(claims *jwtutil.Claims, id uint64, payload reimbursementPayload) (int, string) {
|
||||
row, err := findOwnedReimbursement(claims, id)
|
||||
if err != nil {
|
||||
return 404, "报销单不存在"
|
||||
}
|
||||
if row.Status != 0 {
|
||||
return 400, "只有草稿可以编辑"
|
||||
}
|
||||
applyDate, err := parseReimburseDate(payload.ApplyDate)
|
||||
if err != nil {
|
||||
c.reply(400, "申请日期格式错误", nil)
|
||||
return
|
||||
return 400, "申请日期格式错误"
|
||||
}
|
||||
row.ApplyDate, row.Description = applyDate, nullableReimburseString(payload.Description)
|
||||
if err := saveReimbursement(row); err != nil {
|
||||
c.reply(500, "更新报销单失败", nil)
|
||||
return
|
||||
return 500, "更新报销单失败"
|
||||
}
|
||||
c.reply(200, "success", map[string]interface{}{"id": row.ID})
|
||||
return 200, ""
|
||||
}
|
||||
|
||||
// Delete DELETE /backend/reimbursements/:id
|
||||
@@ -496,25 +528,32 @@ func (c *BackendReimburseController) Delete() {
|
||||
c.reply(400, "无效ID", nil)
|
||||
return
|
||||
}
|
||||
row, err := c.findOwned(id, claims)
|
||||
if err != nil {
|
||||
c.reply(404, "报销单不存在", nil)
|
||||
code, msg := reimburseDeleteCore(claims, id)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", nil)
|
||||
}
|
||||
|
||||
// reimburseDeleteCore 删除报销单(仅草稿,连明细一起软删)共享逻辑。
|
||||
func reimburseDeleteCore(claims *jwtutil.Claims, id uint64) (int, string) {
|
||||
row, err := findOwnedReimbursement(claims, id)
|
||||
if err != nil {
|
||||
return 404, "报销单不存在"
|
||||
}
|
||||
if row.Status != 0 {
|
||||
c.reply(400, "只有草稿可以删除", nil)
|
||||
return
|
||||
return 400, "只有草稿可以删除"
|
||||
}
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendReimbursement)).Filter("id", id).
|
||||
Update(orm.Params{"is_deleted": 1, "delete_time": now, "update_time": now})
|
||||
if err != nil {
|
||||
c.reply(500, "删除失败", nil)
|
||||
return
|
||||
return 500, "删除失败"
|
||||
}
|
||||
_, _ = models.Orm.QueryTable(new(models.BackendReimbursementItem)).
|
||||
Filter("reimbursement_id", id).Update(orm.Params{"is_deleted": 1, "delete_time": now})
|
||||
c.reply(200, "success", nil)
|
||||
return 200, ""
|
||||
}
|
||||
|
||||
func (c *BackendReimburseController) parseItemPayload() (reimbursementItemPayload, error) {
|
||||
@@ -532,7 +571,8 @@ func (c *BackendReimburseController) getItemID() (uint64, bool) {
|
||||
return id, err == nil && id > 0
|
||||
}
|
||||
|
||||
func (c *BackendReimburseController) findOwnedItem(id, reimbursementID uint64, claims *jwtutil.Claims) (*models.BackendReimbursementItem, error) {
|
||||
// findOwnedReimbursementItem 查询本人报销单下的费用明细(包级共享,移动端复用)。
|
||||
func findOwnedReimbursementItem(claims *jwtutil.Claims, id, reimbursementID uint64) (*models.BackendReimbursementItem, error) {
|
||||
item := &models.BackendReimbursementItem{}
|
||||
err := models.Orm.QueryTable(new(models.BackendReimbursementItem)).
|
||||
Filter("id", id).
|
||||
@@ -558,17 +598,17 @@ func validateReimbursementItem(item reimbursementItemPayload) (time.Time, error)
|
||||
return date, nil
|
||||
}
|
||||
|
||||
func (c *BackendReimburseController) ensureDraftReimbursement(id uint64, claims *jwtutil.Claims) (*models.BackendReimbursement, bool) {
|
||||
row, err := c.findOwned(id, claims)
|
||||
// ensureDraftReimbursement 校验报销单存在且为草稿(包级共享,移动端复用)。
|
||||
// 返回 (报销单, HTTP 状态码, 错误信息),错误信息为空串表示通过。
|
||||
func ensureDraftReimbursement(claims *jwtutil.Claims, id uint64) (*models.BackendReimbursement, int, string) {
|
||||
row, err := findOwnedReimbursement(claims, id)
|
||||
if err != nil {
|
||||
c.reply(404, "报销单不存在", nil)
|
||||
return nil, false
|
||||
return nil, 404, "报销单不存在"
|
||||
}
|
||||
if row.Status != 0 {
|
||||
c.reply(400, "只有草稿可以维护费用明细", nil)
|
||||
return nil, false
|
||||
return nil, 400, "只有草稿可以维护费用明细"
|
||||
}
|
||||
return row, true
|
||||
return row, 200, ""
|
||||
}
|
||||
|
||||
func recalculateReimbursementTotal(id uint64) error {
|
||||
@@ -602,25 +642,38 @@ func (c *BackendReimburseController) AddItem() {
|
||||
c.reply(400, "无效报销单ID", nil)
|
||||
return
|
||||
}
|
||||
if _, ok := c.ensureDraftReimbursement(reimbursementID, claims); !ok {
|
||||
return
|
||||
}
|
||||
payload, err := c.parseItemPayload()
|
||||
if err != nil {
|
||||
c.reply(400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
scheme, host := services.PublicRequestBaseURL(&c.Controller)
|
||||
item, code, msg := reimburseAddItemCore(claims, reimbursementID, payload, scheme, host)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", item)
|
||||
}
|
||||
|
||||
// reimburseAddItemCore 新增费用明细共享逻辑(仅草稿;自动重算报销总额)。
|
||||
func reimburseAddItemCore(claims *jwtutil.Claims, reimbursementID uint64, payload reimbursementItemPayload, scheme, host string) (map[string]interface{}, int, string) {
|
||||
if _, _, msg := ensureDraftReimbursement(claims, reimbursementID); msg != "" {
|
||||
code := 400
|
||||
if msg == "报销单不存在" {
|
||||
code = 404
|
||||
}
|
||||
return nil, code, msg
|
||||
}
|
||||
date, err := validateReimbursementItem(payload)
|
||||
if err != nil {
|
||||
c.reply(400, err.Error(), nil)
|
||||
return
|
||||
return nil, 400, err.Error()
|
||||
}
|
||||
uid := uint64(claims.UserID)
|
||||
quantity := payload.Quantity
|
||||
if quantity <= 0 {
|
||||
quantity = 1
|
||||
}
|
||||
scheme, host := services.PublicRequestBaseURL(&c.Controller)
|
||||
item := &models.BackendReimbursementItem{
|
||||
Tid: uint64(claims.TenantId), UID: &uid, ReimbursementID: reimbursementID,
|
||||
ExpenseType: strings.TrimSpace(payload.ExpenseType), ExpenseDate: date,
|
||||
@@ -629,14 +682,12 @@ func (c *BackendReimburseController) AddItem() {
|
||||
}
|
||||
applyReceiptToItem(item, payload, scheme, host)
|
||||
if _, err := models.Orm.Insert(item); err != nil {
|
||||
c.reply(500, "新增费用明细失败", nil)
|
||||
return
|
||||
return nil, 500, "新增费用明细失败"
|
||||
}
|
||||
if err := recalculateReimbursementTotal(reimbursementID); err != nil {
|
||||
c.reply(500, "更新报销总额失败", nil)
|
||||
return
|
||||
return nil, 500, "更新报销总额失败"
|
||||
}
|
||||
c.reply(200, "success", reimbursementItemDTO(*item, scheme, host))
|
||||
return reimbursementItemDTO(*item, scheme, host), 200, ""
|
||||
}
|
||||
|
||||
// UpdateItem POST /backend/reimbursements/:id/items/:item_id
|
||||
@@ -656,29 +707,41 @@ func (c *BackendReimburseController) UpdateItem() {
|
||||
c.reply(400, "无效费用明细ID", nil)
|
||||
return
|
||||
}
|
||||
if _, ok := c.ensureDraftReimbursement(reimbursementID, claims); !ok {
|
||||
return
|
||||
}
|
||||
item, err := c.findOwnedItem(itemID, reimbursementID, claims)
|
||||
if err != nil {
|
||||
c.reply(404, "费用明细不存在", nil)
|
||||
return
|
||||
}
|
||||
payload, err := c.parseItemPayload()
|
||||
if err != nil {
|
||||
c.reply(400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
scheme, host := services.PublicRequestBaseURL(&c.Controller)
|
||||
item, code, msg := reimburseUpdateItemCore(claims, reimbursementID, itemID, payload, scheme, host)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", item)
|
||||
}
|
||||
|
||||
// reimburseUpdateItemCore 更新费用明细共享逻辑(仅草稿;自动重算报销总额)。
|
||||
func reimburseUpdateItemCore(claims *jwtutil.Claims, reimbursementID, itemID uint64, payload reimbursementItemPayload, scheme, host string) (map[string]interface{}, int, string) {
|
||||
if _, _, msg := ensureDraftReimbursement(claims, reimbursementID); msg != "" {
|
||||
code := 400
|
||||
if msg == "报销单不存在" {
|
||||
code = 404
|
||||
}
|
||||
return nil, code, msg
|
||||
}
|
||||
item, err := findOwnedReimbursementItem(claims, itemID, reimbursementID)
|
||||
if err != nil {
|
||||
return nil, 404, "费用明细不存在"
|
||||
}
|
||||
date, err := validateReimbursementItem(payload)
|
||||
if err != nil {
|
||||
c.reply(400, err.Error(), nil)
|
||||
return
|
||||
return nil, 400, err.Error()
|
||||
}
|
||||
quantity := payload.Quantity
|
||||
if quantity <= 0 {
|
||||
quantity = 1
|
||||
}
|
||||
scheme, host := services.PublicRequestBaseURL(&c.Controller)
|
||||
item.ExpenseType = strings.TrimSpace(payload.ExpenseType)
|
||||
item.ExpenseDate = date
|
||||
item.Amount = payload.Amount
|
||||
@@ -687,14 +750,12 @@ func (c *BackendReimburseController) UpdateItem() {
|
||||
item.Description = strings.TrimSpace(payload.Description)
|
||||
applyReceiptToItem(item, payload, scheme, host)
|
||||
if _, err := models.Orm.Update(item); err != nil {
|
||||
c.reply(500, "更新费用明细失败", nil)
|
||||
return
|
||||
return nil, 500, "更新费用明细失败"
|
||||
}
|
||||
if err := recalculateReimbursementTotal(reimbursementID); err != nil {
|
||||
c.reply(500, "更新报销总额失败", nil)
|
||||
return
|
||||
return nil, 500, "更新报销总额失败"
|
||||
}
|
||||
c.reply(200, "success", reimbursementItemDTO(*item, scheme, host))
|
||||
return reimbursementItemDTO(*item, scheme, host), 200, ""
|
||||
}
|
||||
|
||||
// DeleteItem DELETE /backend/reimbursements/:id/items/:item_id
|
||||
@@ -714,26 +775,36 @@ func (c *BackendReimburseController) DeleteItem() {
|
||||
c.reply(400, "无效费用明细ID", nil)
|
||||
return
|
||||
}
|
||||
if _, ok := c.ensureDraftReimbursement(reimbursementID, claims); !ok {
|
||||
code, msg := reimburseDeleteItemCore(claims, reimbursementID, itemID)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
item, err := c.findOwnedItem(itemID, reimbursementID, claims)
|
||||
c.reply(200, "success", nil)
|
||||
}
|
||||
|
||||
// reimburseDeleteItemCore 删除费用明细共享逻辑(仅草稿;自动重算报销总额)。
|
||||
func reimburseDeleteItemCore(claims *jwtutil.Claims, reimbursementID, itemID uint64) (int, string) {
|
||||
if _, _, msg := ensureDraftReimbursement(claims, reimbursementID); msg != "" {
|
||||
if msg == "报销单不存在" {
|
||||
return 404, msg
|
||||
}
|
||||
return 400, msg
|
||||
}
|
||||
item, err := findOwnedReimbursementItem(claims, itemID, reimbursementID)
|
||||
if err != nil {
|
||||
c.reply(404, "费用明细不存在", nil)
|
||||
return
|
||||
return 404, "费用明细不存在"
|
||||
}
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendReimbursementItem)).
|
||||
Filter("id", item.ID).Update(orm.Params{"is_deleted": 1, "delete_time": now, "update_time": now})
|
||||
if err != nil {
|
||||
c.reply(500, "删除费用明细失败", nil)
|
||||
return
|
||||
return 500, "删除费用明细失败"
|
||||
}
|
||||
if err := recalculateReimbursementTotal(reimbursementID); err != nil {
|
||||
c.reply(500, "更新报销总额失败", nil)
|
||||
return
|
||||
return 500, "更新报销总额失败"
|
||||
}
|
||||
c.reply(200, "success", nil)
|
||||
return 200, ""
|
||||
}
|
||||
|
||||
// Submit POST /backend/reimbursements/:id/submit
|
||||
@@ -748,30 +819,37 @@ func (c *BackendReimburseController) Submit() {
|
||||
c.reply(400, "无效ID", nil)
|
||||
return
|
||||
}
|
||||
row, err := c.findOwned(id, claims)
|
||||
if err != nil {
|
||||
c.reply(404, "报销单不存在", nil)
|
||||
code, msg := reimburseSubmitCore(claims, id)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", nil)
|
||||
}
|
||||
|
||||
// reimburseSubmitCore 提交审批共享逻辑:仅草稿可提交,需租户已配置启用审批流,
|
||||
// 提交后状态置为审批中(1),并写入 action=1 的审批记录。
|
||||
func reimburseSubmitCore(claims *jwtutil.Claims, id uint64) (int, string) {
|
||||
row, err := findOwnedReimbursement(claims, id)
|
||||
if err != nil {
|
||||
return 404, "报销单不存在"
|
||||
}
|
||||
if row.Status != 0 {
|
||||
c.reply(400, "当前状态不可提交", nil)
|
||||
return
|
||||
return 400, "当前状态不可提交"
|
||||
}
|
||||
var flow models.BackendApprovalFlow
|
||||
err = models.Orm.QueryTable(new(models.BackendApprovalFlow)).
|
||||
Filter("tid", claims.TenantId).Filter("is_active", 1).Filter("is_deleted", 0).
|
||||
OrderBy("step_order").One(&flow)
|
||||
if err != nil {
|
||||
c.reply(400, "尚未配置审批流程", nil)
|
||||
return
|
||||
return 400, "尚未配置审批流程"
|
||||
}
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendReimbursement)).Filter("id", id).Update(orm.Params{
|
||||
"status": 1, "current_step": flow.StepOrder, "current_approver_id": 0, "update_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
c.reply(500, "提交失败", nil)
|
||||
return
|
||||
return 500, "提交失败"
|
||||
}
|
||||
_, err = models.Orm.Insert(&models.BackendApprovalRecord{
|
||||
Tid: uint64(claims.TenantId), UID: uint64Ptr(uint64(claims.UserID)), ReimbursementID: id,
|
||||
@@ -779,10 +857,9 @@ func (c *BackendReimburseController) Submit() {
|
||||
Step: flow.StepOrder, PrevStep: 0,
|
||||
})
|
||||
if err != nil {
|
||||
c.reply(500, "提交审批记录失败", nil)
|
||||
return
|
||||
return 500, "提交审批记录失败"
|
||||
}
|
||||
c.reply(200, "success", nil)
|
||||
return 200, ""
|
||||
}
|
||||
|
||||
type reimbursementStatusPayload struct {
|
||||
@@ -820,28 +897,35 @@ func (c *BackendReimburseController) UpdateStatus() {
|
||||
c.reply(400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
if !manualConfirmStatuses[payload.Status] {
|
||||
c.reply(400, "不支持的状态值", nil)
|
||||
newStatus, code, msg := reimburseUpdateStatusCore(claims, id, payload.Status)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
row, err := c.findOwned(id, claims)
|
||||
c.reply(200, "success", map[string]interface{}{"status": newStatus})
|
||||
}
|
||||
|
||||
// reimburseUpdateStatusCore 手动确认更改单据状态共享逻辑。
|
||||
// 返回 (生效状态, HTTP 状态码, 错误信息)。
|
||||
func reimburseUpdateStatusCore(claims *jwtutil.Claims, id uint64, status int8) (int8, int, string) {
|
||||
if !manualConfirmStatuses[status] {
|
||||
return 0, 400, "不支持的状态值"
|
||||
}
|
||||
row, err := findOwnedReimbursement(claims, id)
|
||||
if err != nil {
|
||||
c.reply(404, "报销单不存在", nil)
|
||||
return
|
||||
return 0, 404, "报销单不存在"
|
||||
}
|
||||
if row.Status == payload.Status {
|
||||
c.reply(200, "success", map[string]interface{}{"status": row.Status})
|
||||
return
|
||||
if row.Status == status {
|
||||
return row.Status, 200, ""
|
||||
}
|
||||
now := time.Now()
|
||||
_, err = models.Orm.QueryTable(new(models.BackendReimbursement)).Filter("id", id).Update(orm.Params{
|
||||
"status": payload.Status, "update_time": now,
|
||||
"status": status, "update_time": now,
|
||||
})
|
||||
if err != nil {
|
||||
c.reply(500, "更新状态失败", nil)
|
||||
return
|
||||
return 0, 500, "更新状态失败"
|
||||
}
|
||||
c.reply(200, "success", map[string]interface{}{"status": payload.Status})
|
||||
return status, 200, ""
|
||||
}
|
||||
|
||||
// Records GET /backend/reimbursements/:id/records
|
||||
@@ -856,22 +940,30 @@ func (c *BackendReimburseController) Records() {
|
||||
c.reply(400, "无效ID", nil)
|
||||
return
|
||||
}
|
||||
if _, err := c.findOwned(id, claims); err != nil {
|
||||
c.reply(404, "报销单不存在", nil)
|
||||
rows, code, msg := reimburseRecordsData(claims, id)
|
||||
if msg != "" {
|
||||
c.reply(code, msg, nil)
|
||||
return
|
||||
}
|
||||
c.reply(200, "success", rows)
|
||||
}
|
||||
|
||||
// reimburseRecordsData 审批记录共享逻辑(按 step、id 升序)。
|
||||
func reimburseRecordsData(claims *jwtutil.Claims, id uint64) ([]models.BackendApprovalRecord, int, string) {
|
||||
if _, err := findOwnedReimbursement(claims, id); err != nil {
|
||||
return nil, 404, "报销单不存在"
|
||||
}
|
||||
var rows []models.BackendApprovalRecord
|
||||
_, err := models.Orm.QueryTable(new(models.BackendApprovalRecord)).
|
||||
Filter("tid", claims.TenantId).Filter("reimbursement_id", id).Filter("is_deleted", 0).
|
||||
OrderBy("step", "id").All(&rows)
|
||||
if err != nil && err != orm.ErrNoRows {
|
||||
c.reply(500, "查询审批记录失败", nil)
|
||||
return
|
||||
return nil, 500, "查询审批记录失败"
|
||||
}
|
||||
if rows == nil {
|
||||
rows = []models.BackendApprovalRecord{}
|
||||
}
|
||||
c.reply(200, "success", rows)
|
||||
return rows, 200, ""
|
||||
}
|
||||
|
||||
type expenseTypePayload struct {
|
||||
@@ -896,6 +988,11 @@ func expenseTypeDTO(row models.BackendReimbursementExpenseType, tenantID int) ma
|
||||
}
|
||||
|
||||
func (c *BackendReimburseController) queryExpenseTypes(tenantID int, activeOnly bool) ([]models.BackendReimbursementExpenseType, error) {
|
||||
return queryReimburseExpenseTypes(tenantID, activeOnly)
|
||||
}
|
||||
|
||||
// queryReimburseExpenseTypes 费用类型查询共享逻辑:全局通用(tid=0)+ 当前租户专有。
|
||||
func queryReimburseExpenseTypes(tenantID int, activeOnly bool) ([]models.BackendReimbursementExpenseType, error) {
|
||||
qs := models.Orm.QueryTable(new(models.BackendReimbursementExpenseType)).
|
||||
SetCond(expenseTypeScopeCond(tenantID)).
|
||||
Filter("is_deleted", 0)
|
||||
|
||||
Reference in new issue
Block a user