diff --git a/controller/invoice.go b/controller/invoice.go new file mode 100644 index 0000000..c96622d --- /dev/null +++ b/controller/invoice.go @@ -0,0 +1,227 @@ +package controller + +import ( + "electricity_bill_calc/logger" + "electricity_bill_calc/model" + "electricity_bill_calc/repository" + "electricity_bill_calc/response" + "electricity_bill_calc/security" + "electricity_bill_calc/service" + "electricity_bill_calc/tools" + "electricity_bill_calc/types" + "electricity_bill_calc/vo" + + "github.com/gofiber/fiber/v2" + "github.com/jinzhu/copier" + "go.uber.org/zap" +) + +var invoiceLog = logger.Named("Controller", "Invoice") + +func InitializeInvoiceHandler(router *fiber.App) { + router.Get("/invoice", security.MustAuthenticated, listInvoices) + router.Post("/invoice", security.EnterpriseAuthorize, createNewInvoiceRecord) + router.Post("/invoice/precalculate", security.EnterpriseAuthorize, testCalculateInvoice) + router.Get("/invoice/:code", security.EnterpriseAuthorize, getInvoiceDetail) + router.Delete("/invoice/:code", security.EnterpriseAuthorize, deleteInvoiceRecord) + router.Get("/uninvoiced/tenemennt/:tid/report", security.EnterpriseAuthorize, getUninvoicedTenementReports) +} + +// 列出指定园区中的符合条件的发票记录 +func listInvoices(c *fiber.Ctx) error { + invoiceLog.Info("列出指定园区中的符合条件的发票记录") + result := response.NewResult(c) + session, err := _retreiveSession(c) + if err != nil { + invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,不能获取到有效的用户会话。", zap.Error(err)) + return result.Unauthorized("未能获取到有效的用户会话。") + } + park := tools.EmptyToNil(c.Query("park")) + if session.Type == model.USER_TYPE_ENT && park != nil && len(*park) > 0 { + pass, err := checkParkBelongs(*park, invoiceLog, c, &result) + if err != nil || !pass { + return err + } + } + startDate, err := types.ParseDatep(c.Query("start_date")) + if err != nil { + invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,开始日期参数解析错误。", zap.Error(err)) + return result.BadRequest("开始日期参数解析错误。") + } + endDate, err := types.ParseDatep(c.Query("end_date")) + if err != nil { + invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,结束日期参数解析错误。", zap.Error(err)) + return result.BadRequest("结束日期参数解析错误。") + } + keyword := tools.EmptyToNil(c.Query("keyword")) + page := c.QueryInt("page", 1) + invoices, total, err := repository.InvoiceRepository.ListInvoice(park, startDate, endDate, keyword, uint(page)) + if err != nil { + invoiceLog.Error("列出指定园区中的符合条件的发票记录失败,检索符合条件的发票记录出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "检索符合条件的发票记录出现错误。") + } + var invoiceResponse []*vo.InvoiceResponse + copier.Copy(&invoiceResponse, &invoices) + return result.Success( + "已经获取到符合条件的发票列表。", + response.NewPagedResponse(page, total).ToMap(), + fiber.Map{ + "invoices": invoiceResponse, + }, + ) +} + +// 获取指定发票的详细信息 +func getInvoiceDetail(c *fiber.Ctx) error { + result := response.NewResult(c) + invoiceNo := tools.EmptyToNil(c.Params("code")) + invoiceLog.Info("获取指定发票的详细信息", zap.Stringp("InvoiceNo", invoiceNo)) + if invoiceNo == nil { + invoiceLog.Error("获取指定发票的详细信息失败,未指定发票编号。") + return result.BadRequest("未指定发票编号。") + } + session, err := _retreiveSession(c) + if err != nil { + invoiceLog.Error("获取指定发票的详细信息失败,不能获取到有效的用户会话。", zap.Error(err)) + return result.Unauthorized("未能获取到有效的用户会话。") + } + pass, err := repository.InvoiceRepository.IsBelongsTo(*invoiceNo, session.Uid) + if err != nil { + invoiceLog.Error("获取指定发票的详细信息失败,检查发票所属权时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "检查发票所属权时出现错误。") + } + if !pass { + invoiceLog.Error("获取指定发票的详细信息失败,发票不属于当前用户。") + return result.Forbidden("不能访问不属于自己的发票。") + } + invoice, err := repository.InvoiceRepository.GetInvoiceDetail(*invoiceNo) + if err != nil { + invoiceLog.Error("获取指定发票的详细信息失败,检索发票信息时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "检索发票信息时出现错误。") + } + if invoice == nil { + invoiceLog.Error("获取指定发票的详细信息失败,指定发票不存在。") + return result.NotFound("指定发票不存在。") + } + var invoiceResponse vo.ExtendedInvoiceResponse + copier.Copy(&invoiceResponse, &invoice) + return result.Success( + "已经获取到指定发票的详细信息。", + fiber.Map{ + "invoice": invoiceResponse, + }, + ) +} + +// 获取指定商户下所有尚未开票的核算项目 +func getUninvoicedTenementReports(c *fiber.Ctx) error { + result := response.NewResult(c) + tenement := tools.EmptyToNil(c.Params("tid")) + invoiceLog.Info("获取指定商户下所有尚未开票的核算项目", zap.Stringp("Tenement", tenement)) + if tenement == nil { + invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,未指定商户。") + return result.BadRequest("未指定商户。") + } + session, err := _retreiveSession(c) + if err != nil { + invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,不能获取到有效的用户会话。", zap.Error(err)) + return result.Unauthorized("未能获取到有效的用户会话。") + } + pass, err := repository.TenementRepository.IsTenementBelongs(*tenement, session.Uid) + if err != nil { + invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,检查商户所属权时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "检查商户所属权时出现错误。") + } + if !pass { + invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,商户不属于当前用户。") + return result.Forbidden("不能访问不属于自己的商户。") + } + reports, err := repository.InvoiceRepository.ListUninvoicedTenementCharges(*tenement) + if err != nil { + invoiceLog.Error("获取指定商户下所有尚未开票的核算项目失败,检索核算项目时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "检索核算项目时出现错误。") + } + return result.Success( + "已经获取到指定商户下所有尚未开票的核算项目。", + fiber.Map{ + "records": reports, + }, + ) +} + +// 试计算指定发票的票面信息 +func testCalculateInvoice(c *fiber.Ctx) error { + result := response.NewResult(c) + var form vo.InvoiceCreationForm + if err := c.BodyParser(&form); err != nil { + invoiceLog.Error("试计算指定发票的票面信息失败,请求表单数据解析错误。", zap.Error(err)) + return result.BadRequest("请求表单数据解析错误。") + } + invoiceLog.Info("试计算指定发票的票面信息", zap.String("Park", form.Park), zap.String("Tenement", form.Tenement)) + if pass, err := checkParkBelongs(form.Park, invoiceLog, c, &result); err != nil || !pass { + return err + } + total, cargos, err := service.InvoiceService.TestCalculateInvoice(form.Park, form.Tenement, form.TaxMethod, form.TaxRate, form.Covers) + if err != nil { + invoiceLog.Error("试计算指定发票的票面信息失败,试计算发票时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "试计算发票时出现错误。") + } + return result.Success( + "已经计算出指定发票的票面信息。", + fiber.Map{ + "total": total, + "cargos": cargos, + }, + ) +} + +// 创建一个新的发票记录 +func createNewInvoiceRecord(c *fiber.Ctx) error { + result := response.NewResult(c) + var form vo.ExtendedInvoiceCreationForm + if err := c.BodyParser(&form); err != nil { + invoiceLog.Error("创建一个新的发票记录失败,请求表单数据解析错误。", zap.Error(err)) + return result.BadRequest("请求表单数据解析错误。") + } + invoiceLog.Info("创建一个新的发票记录", zap.String("Park", form.Park), zap.String("Tenement", form.Tenement)) + if pass, err := checkParkBelongs(form.Park, invoiceLog, c, &result); err != nil || !pass { + return err + } + err := service.InvoiceService.SaveInvoice(form.Park, form.Tenement, form.InvoiceNo, form.InvoiceType, form.TaxMethod, form.TaxRate, form.Covers) + if err != nil { + invoiceLog.Error("创建一个新的发票记录失败,保存发票时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "保存发票时出现错误。") + } + return result.Created("已经创建了一个新的发票记录。") +} + +// 删除指定的发票记录 +func deleteInvoiceRecord(c *fiber.Ctx) error { + result := response.NewResult(c) + invoiceNo := tools.EmptyToNil(c.Params("code")) + invoiceLog.Info("删除指定的发票记录", zap.Stringp("InvoiceNo", invoiceNo)) + if invoiceNo == nil { + invoiceLog.Error("删除指定的发票记录失败,未指定发票编号。") + return result.BadRequest("未指定发票编号。") + } + session, err := _retreiveSession(c) + if err != nil { + invoiceLog.Error("删除指定的发票记录失败,不能获取到有效的用户会话。", zap.Error(err)) + return result.Unauthorized("未能获取到有效的用户会话。") + } + pass, err := repository.InvoiceRepository.IsBelongsTo(*invoiceNo, session.Uid) + if err != nil { + invoiceLog.Error("删除指定的发票记录失败,检查发票所属权时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "检查发票所属权时出现错误。") + } + if !pass { + invoiceLog.Error("删除指定的发票记录失败,发票不属于当前用户。") + return result.Forbidden("不能删除不属于自己的发票。") + } + err = service.InvoiceService.DeleteInvoice(*invoiceNo) + if err != nil { + invoiceLog.Error("删除指定的发票记录失败,删除发票时出现错误。", zap.Error(err)) + return result.Error(fiber.StatusInternalServerError, "删除发票时出现错误。") + } + return result.Success("已经删除了指定的发票记录。") +} diff --git a/router/router.go b/router/router.go index 02b8f2f..251260c 100644 --- a/router/router.go +++ b/router/router.go @@ -49,6 +49,7 @@ func App() *fiber.App { controller.InitializeChargeHandlers(app) controller.InitializeParkHandlers(app) controller.InitializeMeterHandlers(app) + controller.InitializeInvoiceHandler(app) return app } diff --git a/vo/invoice.go b/vo/invoice.go index e60a3fa..79015e3 100644 --- a/vo/invoice.go +++ b/vo/invoice.go @@ -25,3 +25,15 @@ type InvoiceCreationForm struct { TaxRate decimal.NullDecimal `json:"taxRate"` Covers []string `json:"covers"` } + +type ExtendedInvoiceResponse struct { + InvoiceResponse + Cargos []*model.InvoiceCargo `json:"cargos"` + Covers []*model.SimplifiedTenementCharge `json:"covers"` +} + +type ExtendedInvoiceCreationForm struct { + InvoiceCreationForm + InvoiceType *string `json:"invoiceType"` + InvoiceNo string `json:"invoiceNo"` +}