package models import ( "errors" "fmt" "github.com/gin-gonic/gin" "github.com/xuri/excelize/v2" orm "go-admin/common/global" "go-admin/logger" "go-admin/tools" "go-admin/tools/config" "gorm.io/gorm" "math" "math/rand" "sort" "strconv" "strings" "sync" "time" ) const ( ErpPurchaseOrderUnAudit = 1 // 待审核 ErpPurchaseOrderWaitInventory = 2 // 待入库 ErpPurchaseOrderWaitReject = 3 // 待退货 ErpPurchaseOrderFinished = 4 // 已完成 ErpPurchaseOrderEnd = 5 // 已终止 ErpProcureOrder = "procure" // 采购入库订单 ErpRejectOrder = "reject" // 采购退货订单 ErpDemandStateWait = 1 // 待采购 ErpDemandStateFinish = 2 // 完成采购 ) // ErpPurchaseOrder 采购订单表 type ErpPurchaseOrder struct { Model SerialNumber string `json:"serial_number" gorm:"index"` // 单据编号 PurchaseType string `json:"purchase_type"` // 类型:procure-采购 reject-退货 StoreId uint32 `json:"store_id" gorm:"index"` // 门店id StoreName string `json:"store_name"` // 门店名称 ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"` // 供应商id ErpSupplierName string `json:"erp_supplier_name"` // 供应商名称 HandlerId uint32 `json:"handler_id" gorm:"index"` // 经手人id HandlerName string `json:"handler_name"` // 经手人名称 MakerTime *time.Time `json:"maker_time"` // 制单时间 MakerId uint32 `json:"maker_id" gorm:"index"` // 制单人id MakerName string `json:"maker_name"` // 制单人名称 AuditTime *time.Time `json:"audit_time"` // 审核时间 AuditorId uint32 `json:"auditor_id" gorm:"index"` // 审核人id AuditorName string `json:"auditor_name"` // 审核人名称 State uint32 `json:"state"` // 1-待审核 2-待入库 3-待退货 4-已完成 5-已终止 RejectedSerialNumber string `json:"rejected_serial_number"` // 退货的采购订单单据编号 ErpCashierId uint32 `json:"erp_cashier_id"` // 付款方式/收款方式id ErpCashierName string `json:"erp_cashier_name"` // 付款方式/收款方式名称 AccountHolder string `json:"account_holder"` // 收款人 OpeningBank string `json:"opening_bank"` // 开户行 BankAccount string `json:"bank_account"` // 银行卡号 DeliveryTime string `json:"delivery_time"` // 交货日期,如:2024-02-23 DeliveryAddress string `json:"delivery_address"` // 交货地址 Remark string `json:"remark"` // 备注 Commodities []ErpPurchaseCommodity `json:"commodities" gorm:"-"` // InventoryId uint32 `json:"inventory_id" gorm:"-"` // 最近入库人id InventoryName string `json:"inventory_name" gorm:"-"` // 最近入库人名称 } // ErpPurchaseCommodity 采购订单商品表 type ErpPurchaseCommodity struct { Model ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id" gorm:"index"` // 采购订单id ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"` // 商品编号 IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码 IMEI string `json:"imei"` // 商品串码 RetailPrice float64 `json:"retail_price"` // 指导零售价 Count uint32 `json:"count"` // 计划采购数量 Price float64 `json:"price"` // 计划采购单价 Amount float64 `json:"amount"` // 计划采购金额 Remark string `json:"remark"` // 备注 RejectedPrice float64 `json:"rejected_price"` // 计划退货单价 RejectedCount uint32 `json:"rejected_count"` // 计划退货数量 RejectedAmount float64 `json:"rejected_amount"` // 计划退货金额 InventoryCount int32 `json:"inventory_count"` // 入库数量(=执行数量) ExecutionCount uint32 `json:"execute_count" gorm:"-"` // 执行数量 ExecutionPrice float64 `json:"execute_price" gorm:"-"` // 平均采购单价 ExecutionEmployeePrice float64 `json:"execute_employee_price" gorm:"-"` // 平均员工成本价 ExecutionAmount float64 `json:"execute_amount" gorm:"-"` // 执行金额 DefaultEmployeePrice float64 `json:"default_employee_price" gorm:"-"` // 默认员工成本价 EffectiveCount uint32 `json:"effective_count" gorm:"-"` // 有效数量(该商品实际库存详情处剩余有效数,不包含已出库的数量) ErpCategoryID uint32 `json:"erp_category_id" gorm:"-"` // 商品分类id ErpCategoryName string `json:"erp_category_name" gorm:"-"` // 商品分类名称 } // ErpPurchaseInventory 采购入库执行信息 type ErpPurchaseInventory struct { Model ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id" gorm:"index"` // 商品采购订单id ErpPurchaseCommodityId uint32 `json:"erp_purchase_commodity_id" gorm:"index"` // 采购订单商品表id PurchaseType string `json:"purchase_type" binding:"required"` // 采购类型:procure-采购 reject-退货 SerialNumber string `json:"serial_number" gorm:"index"` // 入库编号 ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"` // 商品编号 ErpCategoryID uint32 `json:"erp_category_id" gorm:"index"` // 商品分类id ErpCategoryName string `json:"erp_category_name"` // 商品分类名称 IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码(系统生成) 3-串码(手动添加) IMEI string `json:"imei"` // 商品串码 Count uint32 `json:"count"` // 执行数量 ImplementationPrice float64 `json:"implementation_price"` // 执行单价 Amount float64 `json:"amount"` // 执行金额 EmployeePrice float64 `json:"employee_price"` // 员工成本价 InventoryId uint32 `json:"inventory_id"` // 最近入库人id InventoryName string `json:"inventory_name"` // 最近入库人名称 ErpStockCommodityID uint32 `json:"erp_stock_commodity_id"` // 库存商品表主键id } // ErpPurchaseCreateReq 新建采购订单入参 type ErpPurchaseCreateReq struct { PurchaseType string `json:"purchase_type" binding:"required"` // 采购类型:procure-采购 reject-退货 PurchaseOrderSn string `json:"purchase_order_sn"` // 采购退货订单号:出库必传 StoreId uint32 `json:"store_id"` // 门店id:入库必传 DeliveryAddress string `json:"delivery_address"` // 交货地址:入库必传 HandlerId uint32 `json:"handler_id" binding:"required"` // 经手人id HandlerName string `json:"handler_name"` // 经手人名称 ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id:入库必传 ErpCashierId uint32 `json:"erp_cashier_id" binding:"required"` // 付款方式 AccountHolder string `json:"account_holder"` // 收款人:入库必传 OpeningBank string `json:"opening_bank"` // 开户行:入库必传 BankAccount string `json:"bank_account" ` // 银行卡号:入库必传 DeliveryTime string `json:"delivery_time" ` // 交货日期:入库必传 Remark string `json:"remark"` // 备注 ErpPurchaseCommodities []ErpPurchaseCommodity `json:"erp_purchase_commodity" binding:"required"` // 采购商品信息 } // ErpPurchaseEditReq 编辑采购订单入参 type ErpPurchaseEditReq struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id" binding:"required"` // 采购订单id PurchaseType string `json:"purchase_type" binding:"required"` // 采购类型:procure-采购 reject-退货 PurchaseOrderSn string `json:"purchase_order_sn"` // 采购退货订单号:出库必传 StoreId uint32 `json:"store_id"` // 门店id:入库必传 DeliveryAddress string `json:"delivery_address"` // 交货地址:入库必传 HandlerId uint32 `json:"handler_id" binding:"required"` // 经手人id HandlerName string `json:"handler_name"` // 经手人名称 ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id:入库必传 ErpCashierId uint32 `json:"erp_cashier_id" binding:"required"` // 付款方式 AccountHolder string `json:"account_holder"` // 收款人:入库必传 OpeningBank string `json:"opening_bank"` // 开户行:入库必传 BankAccount string `json:"bank_account" ` // 银行卡号:入库必传 DeliveryTime string `json:"delivery_time"` // 交货日期:入库必传 Remark string `json:"remark"` // 备注 ErpPurchaseCommodities []ErpPurchaseCommodity `json:"erp_purchase_commodity" binding:"required"` // 采购商品信息 } // ErpPurchaseOrderListReq 查询采购订单列表入参 type ErpPurchaseOrderListReq struct { SerialNumber string `json:"serial_number"` // 单据编号 PurchaseType string `json:"purchase_type"` // 采购类型:procure-采购 reject-退货 StoreId uint32 `json:"store_id"` // 门店id ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id AuditTimeStart string `json:"audit_time_start"` // 审核开始时间 AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间 AuditFlag string `json:"audit_flag"` // 审核标记(默认展示所有):ON-订单只展示已审核的采购入库订单,含待入库/已终止/已完成/入库中 State []uint32 `json:"state"` // 状态:1-待审核 2-待入库 3-待退货 4-已完成 5-已终止 HandlerId uint32 `json:"handler_id"` // 经手人id PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // ErpPurchaseOrderListResp 查询采购订单列表出参 type ErpPurchaseOrderListResp struct { List []ErpPurchaseOrder `json:"list"` Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // ErpPurchaseDetailReq 查询采购订单详情入参 type ErpPurchaseDetailReq struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id" binding:"required"` // 采购订单id } // ErpPurchaseInventoryReq 入库(退货)入参 type ErpPurchaseInventoryReq struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id" binding:"required"` // 采购订单id PurchaseType string `json:"purchase_type" binding:"required"` // 采购类型:procure-采购 reject-退货 InventoryId uint32 `json:"inventory_id"` // 最近入库人id InventoryName string `json:"inventory_name"` // 最近入库人名称 Inventories []ErpPurchaseInventory `json:"inventories" binding:"required"` // 采购入库执行信息 } // ErpPurchaseExecuteReq 执行(入库/退货)入参 type ErpPurchaseExecuteReq struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id" binding:"required"` // 采购订单id PurchaseType string `json:"purchase_type" binding:"required"` // 采购类型:procure-采购 reject-退货 Inventories []ErpPurchaseInventory `json:"inventories" binding:"required"` // 采购入库执行信息 } // ErpPurchaseAuditReq 审核采购订单入参 type ErpPurchaseAuditReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 State int `json:"state" binding:"required"` // 审核操作: 1-审核 2-取消审核 } // ErpPurchaseTerminateReq 终止采购入参 type ErpPurchaseTerminateReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 Remark string `json:"remark" binding:"required"` // 备注 } // ErpPurchaseDeleteReq 删除采购单入参 type ErpPurchaseDeleteReq struct { SerialNumber string `json:"serial_number" binding:"required"` // 单据编号 } // ErpPurchaseExecuteResp 执行(入库/退货)出参 type ErpPurchaseExecuteResp struct { List []ExecuteData `json:"list"` Total int `json:"total"` // 总条数 } // ExecuteData 库存执行数据 type ExecuteData struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id" gorm:"index"` // 商品采购订单id ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 CommoditySerialNumber string `json:"commodity_serial_number" gorm:"index"` // 商品编号 IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码 IMEI string `json:"imei"` // 商品串码 Count uint32 `json:"count"` // 数量 ImplementationPrice float64 `json:"implementation_price"` // 执行单价 EmployeePrice float64 `json:"employee_price"` // 员工成本价 } // ErpPurchaseDemand 采购需求表 type ErpPurchaseDemand struct { Model ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id ErpCommoditySerialNumber string `json:"erp_commodity_serial_number"` // 商品编号 ErpCommodityName string `json:"erp_commodity_name" gorm:"index"` // 商品名称 StoreId uint32 `json:"store_id"` // 门店id StoreName string `json:"store_name"` // 门店名称 Count uint32 `json:"count"` // 需采购数量 State uint32 `json:"state"` // 1-待采购 2-已采购 MakerId uint32 `json:"maker_id"` // 制单人id FinishTime *time.Time `json:"finish_time"` // 完成采购时间 //PurchaserId uint32 `json:"purchaser_id"` // 采购人id //Remark string `json:"remark"` // 备注 } // ErpPurchaseDemandRemark 采购需求备注表 type ErpPurchaseDemandRemark struct { Model ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id State uint32 `json:"state"` // 1-待采购 2-已采购 MakerId uint32 `json:"maker_id"` // 制单人id FinishTime *time.Time `json:"finish_time"` // 完成采购时间 Remark string `json:"remark"` // 备注 } // GetErpPurchaseDemandReq 获取采购需求入参 type GetErpPurchaseDemandReq struct { ErpCategoryId uint32 `json:"erp_category_id"` // 商品分类id ErpCommoditySerialNumber string `json:"erp_commodity_serial_number"` // 商品编号 ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 HideFlag string `json:"hide_flag"` // 隐藏标记(默认关闭):ON-开启,隐藏无采购需求的商品,OFF-关闭,展示所有 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示数据条数 IsExport uint32 `json:"is_export"` // 1-导出 } // DemandData 采购需求数据 type DemandData struct { ErpCommodityID uint32 `json:"erp_commodity_id"` // 商品id ErpCommoditySerialNumber string `json:"erp_commodity_serial_number"` // 商品编号 ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID uint32 `json:"erp_category_id"` // 商品分类id ErpCategoryName string `json:"erp_category_name"` // 商品分类名称 RetailPrice float64 `json:"retail_price"` // 指导零售价 LastWholesalePrice float64 `json:"last_wholesale_price"` // 最近采购价 TotalCount uint32 `json:"total_count"` // 需采购总数量 TotalAmount float64 `json:"total_amount"` // 需采购总金额 Remark string `json:"remark"` // 备注 StoreList []struct { StoreID uint32 `json:"store_id"` // 门店id StoreName string `json:"store_name"` // 门店名称 LastMonthSales uint32 `json:"last_month_sales"` // 上月销售数 StockCount uint32 `json:"stock_count"` // 库存数量 NeedCount uint32 `json:"need_count"` // 需采购数 } `json:"store_list"` } // GetErpPurchaseDemandResp 获取采购需求出参 type GetErpPurchaseDemandResp struct { List []DemandData `json:"list"` Total int64 `json:"total"` // 数据总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 ExportUrl string `json:"export_url"` // 文件路径 } // CreateErpPurchaseDemandReq 创建采购需求入参 type CreateErpPurchaseDemandReq struct { ErpCommodityID uint32 `json:"erp_commodity_id"` // 商品id ErpCommoditySerialNumber string `json:"erp_commodity_serial_number"` // 商品编号 ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 Remark string `json:"remark"` // 备注 List []struct { StoreID uint32 `json:"store_id"` // 门店id StoreName string `json:"store_name"` // 门店名称 NeedCount uint32 `json:"need_count"` // 需采购数 } `json:"list"` } // FinishErpPurchaseDemandReq 完成采购需求入参 type FinishErpPurchaseDemandReq struct { ErpCommodityID uint32 `json:"erp_commodity_id" binding:"required"` // 商品id } // ErpPurchaseReportByOrderReq 采购报表(按单)入参 type ErpPurchaseReportByOrderReq struct { SerialNumber string `json:"serial_number"` // 单据编号 PurchaseType string `json:"purchase_type"` // 采购类型:procure-采购 reject-退货 ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID uint32 `json:"erp_category_id"` // 商品分类id StoreId []uint32 `json:"store_id"` // 门店id HandlerId uint32 `json:"handler_id"` // 经手人id ErpSupplierId []uint32 `json:"erp_supplier_id"` // 供应商id State uint32 `json:"state"` // 2-待入库 3-待退货 4-已完成 5-已终止 AuditTimeStart string `json:"audit_time_start"` // 审核开始时间 AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间 IsExport uint32 `json:"is_export"` // 1-导出 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // ErpPurchaseReportByOrderResp 采购报表(按单)出参 type ErpPurchaseReportByOrderResp struct { Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 Amount float64 `json:"amount"` // 已执行金额 Count int32 `json:"count"` // 已执行数量 ExportUrl string `json:"export_url"` // 导出excel路径 List []ReportByOrderData `json:"list"` // 采购报表信息 } type ReportByOrderData struct { CommonData State uint32 `json:"state"` // 1-待审核 2-待入库 3-待退货 4-已完成 5-已终止 Remark string `json:"remark"` // 备注 Amount float64 `json:"amount"` // 已执行金额 Price float64 `json:"price"` // 执行单价 Count int32 `json:"count"` // 已执行数量 CommodityData []ErpPurchaseCommodityData `json:"commodity_data"` // 商品信息 } type CommodityData struct { ErpCommodityId uint32 `json:"erp_commodity_id"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID uint32 `json:"erp_category_id"` // 商品分类id ErpCategoryName string `json:"erp_category_name"` // 商品分类名称 } type CommonData struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id"` // 采购订单id SerialNumber string `json:"serial_number"` // 单据编号 PurchaseType string `json:"purchase_type"` // 单据类型:procure-采购 reject-退货 StoreId uint32 `json:"store_id"` // 门店id StoreName string `json:"store_name"` // 门店名称 ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id ErpSupplierName string `json:"erp_supplier_name"` // 供应商名称 HandlerId uint32 `json:"handler_id"` // 经手人id HandlerName string `json:"handler_name"` // 经手人名称 MakerTime *time.Time `json:"maker_time"` // 制单时间 MakerId uint32 `json:"maker_id"` // 制单人id MakerName string `json:"maker_name"` // 制单人名称 AuditTime *time.Time `json:"audit_time"` // 审核时间 AuditorId uint32 `json:"auditor_id"` // 审核人id AuditorName string `json:"auditor_name"` // 审核人名称 } // ErpPurchaseCommodityData 采购订单商品信息 type ErpPurchaseCommodityData struct { CommodityData Amount float64 `json:"amount"` // 已执行金额 Price float64 `json:"price"` // 已执行单价 Count int32 `json:"count"` // 已执行数量 } // ErpPurchaseReportByCommodityReq 采购报表(按商品)入参 type ErpPurchaseReportByCommodityReq struct { SerialNumber string `json:"serial_number"` // 单据编号 PurchaseType string `json:"purchase_type"` // 采购类型:procure-采购 reject-退货 ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID uint32 `json:"erp_category_id"` // 商品分类id StoreId []uint32 `json:"store_id"` // 门店id HandlerId uint32 `json:"handler_id"` // 经手人id ErpSupplierId []uint32 `json:"erp_supplier_id"` // 供应商id State uint32 `json:"state"` // 2-待入库 3-待退货 4-已完成 5-已终止 AuditTimeStart string `json:"audit_time_start"` // 审核开始时间 AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间 IsExport uint32 `json:"is_export"` // 1-导出 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // ErpPurchaseReportByCommodityResp 采购报表(按商品)出参 type ErpPurchaseReportByCommodityResp struct { Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 PurchaseData ExportUrl string `json:"export_url"` // 导出excel路径 List []ReportByCommodityData `json:"list"` // 采购报表信息 } type ReportByCommodityData struct { CommodityData PurchaseData OrderInfo []ErpCommodityPurchaseOrderData `json:"order_info"` // 采购订单信息 } // ErpCommodityPurchaseOrderData 采购商品的订单信息 type ErpCommodityPurchaseOrderData struct { CommonData PurchaseData } type TempData struct { CommodityData ErpCommodityPurchaseOrderData } // PurchaseData 采购金额和数量 type PurchaseData struct { OrderId uint32 `json:"order_id"` // 采购订单id PlanCount int32 `json:"plan_count"` // 计划采购数量 PlanPrice float64 `json:"plan_price"` // 计划采购单价 PlanAmount float64 `json:"plan_amount"` // 计划采购金额 Amount float64 `json:"amount"` // 已执行金额 Price float64 `json:"price"` // 已执行单价 Count int32 `json:"count"` // 已执行数量 NonExecutionAmount float64 `json:"non_execution_amount"` // 未执行金额 NonExecutionCount int32 `json:"non_execution_count"` // 未执行数量 } // ErpPurchaseReportBySupplierReq 供应商采购汇总入参 type ErpPurchaseReportBySupplierReq struct { PurchaseType string `json:"purchase_type"` // 采购类型:procure-采购 reject-退货 ErpCommodityName []string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID []uint32 `json:"erp_category_id"` // 商品分类id ErpSupplierId []uint32 `json:"erp_supplier_id"` // 供应商id StartTime string `json:"start_time"` // 入/出库,开始时间 EndTime string `json:"end_time"` // 入/出库,结束时间 IsExport uint32 `json:"is_export"` // 1-导出 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 StoreList []uint32 `json:"store_list"` // 门店复选 } // ErpPurchaseReportBySupplierResp 供应商采购汇总出参 type ErpPurchaseReportBySupplierResp struct { Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 Count int32 `json:"count"` // 采购数量 Amount float64 `json:"amount"` // 采购金额 RejectAmount float64 `json:"reject_amount"` // 退货金额 Difference float64 `json:"difference"` // 差额 ExportUrl string `json:"export_url"` // 导出excel路径 List []PurchaseReportData `json:"list"` // 供应商采购汇总信息 } type PurchaseReportData struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id"` // 采购订单id SerialNumber string `json:"serial_number" gorm:"index"` // 入库编号 StoreId uint32 `json:"store_id"` // 门店id StoreName string `json:"store_name"` // 门店名称 PurchaseType string `json:"purchase_type"` // 采购类型:procure-采购 reject-退货 ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id ErpSupplierName string `json:"erp_supplier_name"` // 供应商名称 ErpCommodityId uint32 `json:"erp_commodity_id"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID uint32 `json:"erp_category_id"` // 商品分类id ErpCategoryName string `json:"erp_category_name"` // 商品分类名称 Count int32 `json:"count"` // 采购数量 Amount float64 `json:"amount"` // 采购金额 RejectAmount float64 `json:"reject_amount"` // 退货金额 Difference float64 `json:"difference"` // 差额 } // ErpPurchaseReportDetailReq 采购明细入参 type ErpPurchaseReportDetailReq struct { SerialNumber string `json:"serial_number"` // 单据编号 PurchaseType string `json:"purchase_type"` // 采购类型:procure-采购 reject-退货 ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID uint32 `json:"erp_category_id"` // 商品分类id ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id StartTime string `json:"startTime"` // 入/出库,开始时间 EndTime string `json:"endTime"` // 入/出库,结束时间 IsExport uint32 `json:"is_export"` // 1-导出 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 StoreId []uint32 `json:"store_id"` // 门店id } // ErpPurchaseReportDetailResp 采购明细出参 type ErpPurchaseReportDetailResp struct { Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 Price float64 `json:"price"` // 采购价 EmployeePrice float64 `json:"employee_price"` // 员工成本价 RejectPrice float64 `json:"reject_price"` // 退货价 DifferencePrice float64 `json:"difference_price"` // 差价 ExportUrl string `json:"export_url"` // 导出excel路径 List []struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id"` // 采购订单id OrderSerialNumber string `json:"order_serial_number"` // 单据编号 SerialNumber string `json:"serial_number"` // 采购入库编号 PurchaseType string `json:"purchase_type"` // 单据类型:procure-采购 reject-退货 ExecuteTime *time.Time `json:"execute_time"` // 出/入库时间 StoreId uint32 `json:"store_id"` // 门店id StoreName string `json:"store_name"` // 门店名称 ErpSupplierId uint32 `json:"erp_supplier_id"` // 供应商id ErpSupplierName string `json:"erp_supplier_name"` // 供应商名称 ErpCommodityId uint32 `json:"erp_commodity_id"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryID uint32 `json:"erp_category_id"` // 商品分类id ErpCategoryName string `json:"erp_category_name"` // 商品分类名称 IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码 IMEI string `json:"imei"` // 商品串码 Price float64 `json:"price"` // 采购价 EmployeePrice float64 `json:"employee_price"` // 员工成本价 RejectPrice float64 `json:"reject_price"` // 退货价 DifferencePrice float64 `json:"difference_price"` // 差价 } `json:"list"` // 采购明细信息 } // List 查询采购订单列表 func (m *ErpPurchaseOrderListReq) List(c *gin.Context) (*ErpPurchaseOrderListResp, error) { resp := &ErpPurchaseOrderListResp{ PageIndex: m.PageIndex, PageSize: m.PageSize, } page := m.PageIndex - 1 if page < 0 { page = 0 } if m.PageSize == 0 { m.PageSize = 10 } qs := orm.Eloquent.Table("erp_purchase_order") var stateList []int if m.AuditFlag == "ON" { //2-待入库 4-已完成 5-已终止 6-入库中 stateList = []int{2, 4, 5, 6} qs = qs.Where("state IN ?", stateList) qs = qs.Where("purchase_type=?", ErpProcureOrder) } if m.SerialNumber != "" { qs = qs.Where("serial_number=?", m.SerialNumber) } else { if m.PurchaseType != "" { qs = qs.Where("purchase_type=?", m.PurchaseType) } if m.StoreId != 0 { qs = qs.Where("store_id=?", m.StoreId) } // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if len(storeList) > 0 { qs = qs.Where("store_id IN (?)", storeList) } else { return nil, errors.New("用户未绑定门店") } } if m.ErpSupplierId != 0 { qs = qs.Where("erp_supplier_id=?", m.ErpSupplierId) } if len(m.State) > 0 { qs = qs.Where("state IN (?)", m.State) } if m.AuditTimeStart != "" { parse, err := time.Parse(QueryTimeFormat, m.AuditTimeStart) if err != nil { logger.Errorf("erpPurchaseOrderList err:", err) return nil, err } qs = qs.Where("audit_time > ?", parse) } if m.AuditTimeEnd != "" { parse, err := time.Parse(QueryTimeFormat, m.AuditTimeEnd) if err != nil { logger.Errorf("erpPurchaseOrderList err:", err) return nil, err } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("audit_time < ?", parse) } if m.HandlerId != 0 { qs = qs.Where("handler_id=?", m.HandlerId) } } var count int64 err := qs.Count(&count).Error if err != nil { logger.Error("count err:", logger.Field("err", err)) return resp, err } resp.Total = int(count) var orders []ErpPurchaseOrder err = qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&orders).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } // 校验时间,如果为01-01-01 08:05,则赋值为空 for i, v := range orders { if v.MakerTime != nil && v.MakerTime.IsZero() { orders[i].MakerTime = nil } if v.AuditTime != nil && v.AuditTime.IsZero() { orders[i].AuditTime = nil } } resp.List = orders return resp, nil } // newErpPurchaseSn 生成采购订单号 func newErpPurchaseSn(purchaseType string) string { var prefix string switch purchaseType { case ErpProcureOrder: // 采购入库订单 prefix = "cgr" case ErpRejectOrder: // 采购退货订单 prefix = "cgt" } nowTime := time.Now() rand.Seed(nowTime.UnixNano()) max := 1 for { if max > 5 { logger.Error("create sn err") return "" } random := rand.Intn(9000) + 1000 sn := fmt.Sprintf("%s%d", nowTime.Format("060102"), random) exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_purchase_order WHERE "+ "serial_number='%s'", prefix+sn)) if err != nil { logger.Error("exist sn err") } if !exist { return prefix + sn } max++ } } func ErpPurchaseCommodityListPerfectInfo(serialNumber string, purchaseCommodities []ErpPurchaseCommodity) error { commodityIds := make([]uint32, 0, len(purchaseCommodities)) for i, _ := range purchaseCommodities { commodityIds = append(commodityIds, purchaseCommodities[i].ID) } commodityMap, err := GetErpCommodityMap(commodityIds) if err != nil { logger.Error("purchase commodities err:", logger.Field("err", err)) return err } for i, _ := range purchaseCommodities { if serialNumber != "" { // 采购退货单才需判断 // 查询入库商品实际库存详情处剩余有效数,不包含已出库的数量 nCount, err := GetCommodityStockByPurchaseId(serialNumber, purchaseCommodities[i].ErpCommodityId) if err != nil { logger.Error("ErpPurchaseCommodityListPerfectInfo GetCommodityStockByPurchaseId err:", logger.Field("err", err)) return err } // 如果库存数量不够则报错 if nCount < purchaseCommodities[i].RejectedCount { return fmt.Errorf("商品[%s]采购退货数量超出实际库存数量,请先零售退货", purchaseCommodities[i].ErpCommodityName) } } v, ok := commodityMap[purchaseCommodities[i].ErpCommodityId] if ok { purchaseCommodities[i].CommoditySerialNumber = v.SerialNumber purchaseCommodities[i].IMEIType = v.IMEIType //purchaseCommodities[i].IMEI = v.IMEI purchaseCommodities[i].ErpCommodityName = v.Name if purchaseCommodities[i].Count != 0 { purchaseCommodities[i].Amount = float64(purchaseCommodities[i].Count) * purchaseCommodities[i].Price } if purchaseCommodities[i].RejectedCount != 0 { purchaseCommodities[i].RejectedAmount = float64(purchaseCommodities[i].RejectedCount) * purchaseCommodities[i].RejectedPrice } } } return nil } // IdInit 添加商户和供应商信息 func (m *ErpPurchaseOrder) IdInit() error { if m.StoreId != 0 { store, err := GetStore(m.StoreId) if err != nil { logger.Error("get store err:", logger.Field("err", err)) return err } m.StoreName = store.Name } if m.ErpSupplierId != 0 { supplier, err := GetErpSupplier(m.ErpSupplierId) if err != nil { logger.Error("get supplier err:", logger.Field("err", err)) return err } m.ErpSupplierName = supplier.Name } if m.ErpCashierId != 0 { cashier, err := GetAccountDetail(int(m.ErpCashierId)) if err != nil { logger.Error("get cashier err:", logger.Field("err", err)) return err } m.ErpCashierName = cashier.Name } return nil } // GetPurchaseInventorySn 生成入库编号 func GetPurchaseInventorySn() string { count := 0 for { if count > 5 { return "" } nowTime := time.Now() sn := nowTime.Format("060102") sn += fmt.Sprintf("%d", nowTime.Unix()%100) rand.Seed(nowTime.UnixNano()) sn += fmt.Sprintf("%d", rand.Int31n(100)) exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_purchase_inventory WHERE serial_number='%s'", sn)) if err != nil { logger.Error("sn err:", logger.Field("err", err)) count++ continue } if err == nil && !exist { return sn } return "" } } // CheckCreateErpPurchaseOrderParam 新增采购订单-检查参数 func CheckCreateErpPurchaseOrderParam(req *ErpPurchaseCreateReq) error { if req.PurchaseType == ErpProcureOrder { // 采购入库订单 if req.StoreId == 0 { return errors.New("操作失败:门店id为空") } if req.DeliveryAddress == "" { return errors.New("操作失败:交货地址为空") } if req.ErpSupplierId == 0 { return errors.New("操作失败:供应商id为空") } if req.AccountHolder == "" { return errors.New("操作失败:收款人为空") } if req.OpeningBank == "" { return errors.New("操作失败:开户行为空") } if req.BankAccount == "" { return errors.New("操作失败:银行卡号为空") } if req.DeliveryTime == "" { return errors.New("操作失败:交货日期为空") } for _, item := range req.ErpPurchaseCommodities { if item.Count <= 0 { return errors.New("操作失败:采购数量需大于0") } } } else if req.PurchaseType == ErpRejectOrder { // 退货单 if req.PurchaseOrderSn == "" { return errors.New("操作失败:采购退货单据编号为空") } for _, item := range req.ErpPurchaseCommodities { if item.RejectedCount <= 0 { return errors.New("操作失败:采购退货数量需大于0") } } } else { return errors.New("操作失败:采购类型有误") } return nil } // CheckEditErpPurchaseOrderParam 编辑采购订单-检查参数 func CheckEditErpPurchaseOrderParam(req *ErpPurchaseEditReq) error { if req.PurchaseType == ErpProcureOrder { // 采购入库订单 if req.StoreId == 0 { return errors.New("操作失败:门店id为空") } if req.DeliveryAddress == "" { return errors.New("操作失败:交货地址为空") } if req.ErpSupplierId == 0 { return errors.New("操作失败:供应商id为空") } if req.AccountHolder == "" { return errors.New("操作失败:收款人为空") } if req.OpeningBank == "" { return errors.New("操作失败:开户行为空") } if req.BankAccount == "" { return errors.New("操作失败:银行卡号为空") } if req.DeliveryTime == "" { return errors.New("操作失败:交货日期为空") } } else if req.PurchaseType == ErpRejectOrder { // 退货单 if req.PurchaseOrderSn == "" { return errors.New("操作失败:采购退货单据编号为空") } } else { return errors.New("操作失败:采购类型有误") } return nil } // CreateErpPurchaseOrder 新增采购订单 func CreateErpPurchaseOrder(req *ErpPurchaseCreateReq, sysUser *SysUser) (*ErpPurchaseOrder, error) { var err error nowTime := time.Now() purchaseOrder := &ErpPurchaseOrder{} if req.PurchaseType == ErpProcureOrder { // 采购入库订单 purchaseOrder = &ErpPurchaseOrder{ SerialNumber: newErpPurchaseSn(req.PurchaseType), PurchaseType: req.PurchaseType, StoreId: req.StoreId, ErpSupplierId: req.ErpSupplierId, MakerTime: &nowTime, HandlerId: req.HandlerId, HandlerName: req.HandlerName, MakerId: uint32(sysUser.UserId), MakerName: sysUser.NickName, State: ErpPurchaseOrderUnAudit, // 1-待审核 ErpCashierId: req.ErpCashierId, AccountHolder: req.AccountHolder, OpeningBank: req.OpeningBank, BankAccount: req.BankAccount, DeliveryTime: req.DeliveryTime, DeliveryAddress: req.DeliveryAddress, Remark: req.Remark, } err = purchaseOrder.IdInit() } else if req.PurchaseType == ErpRejectOrder { // 采购退货订单 if req.PurchaseOrderSn == "" { return nil, errors.New("新建失败:采购退货订单号为空") } var erpPurchaseOrder ErpPurchaseOrder err = orm.Eloquent.Table("erp_purchase_order").Where("serial_number=?", req.PurchaseOrderSn).Find(&erpPurchaseOrder).Error if err != nil { logger.Error("purchase order err:", logger.Field("err", err)) return nil, err } purchaseOrder = &ErpPurchaseOrder{ SerialNumber: newErpPurchaseSn(req.PurchaseType), PurchaseType: req.PurchaseType, StoreId: erpPurchaseOrder.StoreId, ErpSupplierId: erpPurchaseOrder.ErpSupplierId, MakerTime: &nowTime, HandlerId: req.HandlerId, HandlerName: req.HandlerName, MakerId: uint32(sysUser.UserId), MakerName: sysUser.NickName, State: ErpPurchaseOrderUnAudit, // 1-待审核 ErpCashierId: req.ErpCashierId, Remark: req.Remark, RejectedSerialNumber: req.PurchaseOrderSn, } err = purchaseOrder.IdInit() } else { logger.Errorf("purchase_type err:", req.PurchaseType) return nil, errors.New("操作失败:采购类型有误") } if err != nil { logger.Error("info err:", logger.Field("err", err)) return nil, err } err = ErpPurchaseCommodityListPerfectInfo(req.PurchaseOrderSn, req.ErpPurchaseCommodities) if err != nil { logger.Error("info err:", logger.Field("err", err)) return nil, err } begin := orm.Eloquent.Begin() err = begin.Create(purchaseOrder).Error if err != nil { begin.Rollback() logger.Error("create purchase order err:", logger.Field("err", err)) return nil, err } for i, _ := range req.ErpPurchaseCommodities { req.ErpPurchaseCommodities[i].ErpPurchaseOrderId = purchaseOrder.ID req.ErpPurchaseCommodities[i].InventoryCount = 0 // todo 数量待核实 err = begin.Create(&req.ErpPurchaseCommodities[i]).Error if err != nil { begin.Rollback() logger.Error("create purchase commodity err:", logger.Field("err", err)) return nil, err } } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit purchase commodity err:", logger.Field("err", err)) return nil, err } return purchaseOrder, nil } // EditErpPurchaseOrder 编辑采购订单 func EditErpPurchaseOrder(req *ErpPurchaseEditReq, sysUser *SysUser) (*ErpPurchaseOrder, error) { // 查询订单信息 var purchaseOrder ErpPurchaseOrder err := orm.Eloquent.Table("erp_purchase_order").Where("id=?", req.ErpPurchaseOrderId).Find(&purchaseOrder).Error if err != nil { logger.Error("purchase order err:", logger.Field("err", err)) return nil, err } if purchaseOrder.State != ErpPurchaseOrderUnAudit { // 只有待审核的订单才能编辑 return nil, errors.New("订单不是待审核状态") } if req.StoreId == 0 && req.PurchaseType == ErpProcureOrder { return nil, errors.New("操作失败:门店id为空") } begin := orm.Eloquent.Begin() // 1-更新采购订单信息 purchaseOrder.StoreId = req.StoreId purchaseOrder.ErpSupplierId = req.ErpSupplierId purchaseOrder.HandlerId = req.HandlerId purchaseOrder.HandlerName = req.HandlerName purchaseOrder.MakerId = uint32(sysUser.UserId) purchaseOrder.MakerName = sysUser.NickName purchaseOrder.ErpCashierId = req.ErpCashierId purchaseOrder.AccountHolder = req.AccountHolder purchaseOrder.OpeningBank = req.OpeningBank purchaseOrder.BankAccount = req.BankAccount purchaseOrder.DeliveryTime = req.DeliveryTime purchaseOrder.DeliveryAddress = req.DeliveryAddress purchaseOrder.Remark = req.Remark err = purchaseOrder.IdInit() if err != nil { logger.Error("purchase IdInit err:", logger.Field("err", err)) return nil, err } err = begin.Model(&ErpPurchaseOrder{}).Where("id = ?", req.ErpPurchaseOrderId).Updates(purchaseOrder).Error if err != nil { begin.Rollback() logger.Error("update erp_order err:", logger.Field("err", err)) return nil, err } // 2-更新采购订单商品表 err = updatePurchaseCommodityData(begin, req.ErpPurchaseOrderId, req) if err != nil { begin.Rollback() logger.Error("update erp_purchase_commodity err:", logger.Field("err", err)) return nil, err } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) return nil, err } return &purchaseOrder, nil } // updatePurchaseCommodityData 更新采购订单商品信息 func updatePurchaseCommodityData(gdb *gorm.DB, orderId uint32, req *ErpPurchaseEditReq) error { // 查询现有的零售订单信息 var commodities []ErpPurchaseCommodity err := orm.Eloquent.Table("erp_purchase_commodity").Where("erp_purchase_order_id = ?", orderId).Find(&commodities).Error if err != nil { logger.Error("query erp_purchase_commodity err:", logger.Field("err", err)) return err } var newCommodities []ErpPurchaseCommodity var deletedCommodities []ErpPurchaseCommodity var matchingCommodities []ErpPurchaseCommodity // 找到新增的商品 for i, reqCommodity := range req.ErpPurchaseCommodities { // 订单商品表信息添加零售订单id req.ErpPurchaseCommodities[i].ErpPurchaseOrderId = orderId var found bool for _, dbCommodity := range commodities { if reqCommodity.ErpCommodityId == dbCommodity.ErpCommodityId { found = true break } } if !found { newCommodities = append(newCommodities, reqCommodity) } } // 找到删除的商品 for _, dbCommodity := range commodities { var found bool for _, reqCommodity := range req.ErpPurchaseCommodities { if reqCommodity.ID == dbCommodity.ID { // 应该是比较商品id,前端入参如果有主键id也可以比较 //if reqCommodity.ErpCommodityId == dbCommodity.ErpCommodityId { found = true // 找到匹配的商品,加入匹配列表 matchingCommodities = append(matchingCommodities, reqCommodity) break } } if !found { deletedCommodities = append(deletedCommodities, dbCommodity) } } // 2-更新商品订单信息-更新 for _, commodity := range matchingCommodities { if err := gdb.Model(&ErpPurchaseCommodity{}).Where("id = ?", commodity.ID).Updates(commodity).Error; err != nil { logger.Error("更新商品订单信息-更新 error") return errors.New("操作失败:" + err.Error()) } } // 2-更新商品订单信息-新增 if len(newCommodities) != 0 { err = gdb.Create(&newCommodities).Error if err != nil { logger.Error("更新商品订单信息-新增 error") return errors.New("操作失败:" + err.Error()) } } //2-更新商品订单信息-删除 if len(deletedCommodities) != 0 { err = gdb.Delete(&deletedCommodities).Error if err != nil { logger.Error("更新商品订单信息-删除 error") return errors.New("操作失败:" + err.Error()) } } return nil } // InventoryErpPurchase 采购订单入库 func InventoryErpPurchase(req *ErpPurchaseInventoryReq, c *gin.Context) error { // 查询采购订单信息 var purchaseOrder ErpPurchaseOrder err := orm.Eloquent.Table("erp_purchase_order").Where("id=?", req.ErpPurchaseOrderId).Find(&purchaseOrder).Error if err != nil { logger.Error("purchase order err:", logger.Field("err", err)) return err } // 校验入参门店是否包含在用户所有门店中,是否过期 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return err } if !CheckUserStore(purchaseOrder.StoreId, sysUser) { return errors.New("操作失败:您没有该门店权限") } } err = checkPurchaseInventory(req) if err != nil { logger.Error("checkPurchaseInventoryReq err:", logger.Field("err", err)) return err } begin := orm.Eloquent.Begin() var inventoryList []ErpPurchaseInventory for i, v := range req.Inventories { req.Inventories[i].SerialNumber = v.SerialNumber // 更新采购商品表的执行数量 // todo 如果用户在同一个采购单中新建了同一个商品的2条采购信息,可能采购价不同,需要区分入库的是哪一条(已修改) err = begin.Model(&ErpPurchaseCommodity{}). Where("erp_purchase_order_id = ? and erp_commodity_id = ?", v.ErpPurchaseOrderId, v.ErpCommodityId). UpdateColumn("inventory_count", gorm.Expr("inventory_count + ?", v.Count)).Error if err != nil { begin.Rollback() logger.Error("update inventory count err:", logger.Field("err", err)) return err } v.PurchaseType = req.PurchaseType // 记录采购类型 v.InventoryId = req.InventoryId // 记录入库人id v.InventoryName = req.InventoryName // 记录入库人姓名 nCount := v.Count nAmount := v.Amount for j := 0; j < int(nCount); j++ { // 采购入库记录表都是单笔数据 if v.PurchaseType == ErpProcureOrder { v.SerialNumber = GetPurchaseInventorySn() } else { v.SerialNumber = purchaseOrder.SerialNumber } v.ID = 0 v.Count = 1 v.Amount = nAmount / float64(nCount) // 前端传的执行金额是总金额 // 新建采购入库记录 err = begin.Create(&v).Error if err != nil { begin.Rollback() logger.Error("create erp inventory commodity err:", logger.Field("err", err)) return err } inventoryList = append(inventoryList, v) } } // 更新库存信息表 if purchaseOrder.PurchaseType == ErpProcureOrder { //采购入库订单 err = InventoryErpPurchaseUpdateStock(begin, inventoryList, purchaseOrder) } else if purchaseOrder.PurchaseType == ErpRejectOrder { // 采购退货订单 err = InventoryErpPurchaseUpdateRejectStock(begin, inventoryList, purchaseOrder) } else { return errors.New("订单类型有误") } if err != nil { begin.Rollback() logger.Error("update stock err:", logger.Field("err", err)) return err } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) return err } // 查询采购订单商品表入库或退库情况,如果都已经完成了,则更新采购订单的状态为:已完成 _ = CheckAndUpdatePurchaseOrderState(req.ErpPurchaseOrderId) return nil } // CheckAndUpdatePurchaseOrderState // 查询采购订单商品表入库或退库情况,如果都已经完成了,则更新采购订单的状态为:已完成 func CheckAndUpdatePurchaseOrderState(orderId uint32) error { var order ErpPurchaseOrder if err := orm.Eloquent.Where("id = ?", orderId).First(&order).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("purchase order with ID %d not found", orderId) } return err } if order.State == ErpPurchaseOrderFinished { return nil // Order is already marked as completed } var commodities []ErpPurchaseCommodity if err := orm.Eloquent.Where("erp_purchase_order_id = ?", orderId).Find(&commodities).Error; err != nil { return err } for _, commodity := range commodities { if (order.PurchaseType == "procure" && commodity.Count != uint32(commodity.InventoryCount)) || (order.PurchaseType == "reject" && commodity.RejectedCount != uint32(commodity.InventoryCount)) { return nil // Order not completed, exit early } } // If all commodities are checked and everything is in order, update the state to completed if err := orm.Eloquent.Model(&order).Update("state", ErpPurchaseOrderFinished).Update("audit_time", time.Now()).Error; err != nil { return err } return nil } // GetInventoryIdAndName 获取采购单最近入库人信息 func GetInventoryIdAndName(orderId uint32) (uint32, string, error) { var purchaseInventory ErpPurchaseInventory err := orm.Eloquent.Table("erp_purchase_inventory").Where("erp_purchase_order_id=?", orderId). Order("created_at DESC").First(&purchaseInventory).Error if err != nil { logger.Error("purchase order err:", logger.Field("err", err)) return 0, "", err } return purchaseInventory.InventoryId, purchaseInventory.InventoryName, nil } // InventoryErpPurchaseUpdateStock 采购订单入库更新库存信息 func InventoryErpPurchaseUpdateStock(gdb *gorm.DB, list []ErpPurchaseInventory, purchaseOrder ErpPurchaseOrder) error { // 遍历采购入库商品信息 var stockList []ErpStockCommodity var waitCreateStockList []ErpStock for _, v := range list { commodityInfo, err := GetCommodity(v.ErpCommodityId) if err != nil { logger.Errorf("GetCommodity err:", err) return err } exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock WHERE store_id=%d AND erp_commodity_id=%d", purchaseOrder.StoreId, v.ErpCommodityId)) if err != nil { logger.Errorf("exist err:", err) return err } if exist { err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET count=count+%d WHERE store_id=%d AND erp_commodity_id=%d", v.Count, purchaseOrder.StoreId, v.ErpCommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } } else { // 如果同时入库多个商品,刚好之前又没有记录,则可能生成多条记录 stock := ErpStock{ StoreId: purchaseOrder.StoreId, StoreName: purchaseOrder.StoreName, ErpCommodityId: v.ErpCommodityId, ErpCommodityName: v.ErpCommodityName, ErpCategoryId: commodityInfo.ErpCategoryId, ErpCategoryName: commodityInfo.ErpCategoryName, CommoditySerialNumber: v.CommoditySerialNumber, IMEIType: v.IMEIType, RetailPrice: commodityInfo.RetailPrice, MinRetailPrice: commodityInfo.MinRetailPrice, Count: v.Count, DispatchCount: 0, } waitCreateStockList = append(waitCreateStockList, stock) //err = gdb.Create(stock).Error //if err != nil { // logger.Errorf("create stock err:", err) // return err //} } nowTime := time.Now() stockCommodity := ErpStockCommodity{ StoreId: purchaseOrder.StoreId, StoreName: purchaseOrder.StoreName, ErpCommodityId: v.ErpCommodityId, ErpCommodityName: v.ErpCommodityName, CommoditySerialNumber: v.CommoditySerialNumber, ErpCategoryId: commodityInfo.ErpCategoryId, ErpCategoryName: commodityInfo.ErpCategoryName, ErpSupplierId: purchaseOrder.ErpSupplierId, ErpSupplierName: purchaseOrder.ErpSupplierName, StaffCostPrice: tools.RoundToTwoDecimalPlaces(v.EmployeePrice - v.ImplementationPrice), WholesalePrice: tools.RoundToTwoDecimalPlaces(v.ImplementationPrice), State: InStock, StorageType: PurchaseInventory, // 采购入库 FirstStockTime: nowTime, StockTime: nowTime, Count: v.Count, ErpBarcode: commodityInfo.ErpBarcode, // 240106新增商品条码 IMEIType: v.IMEIType, IMEI: v.IMEI, Remark: "", MemberDiscount: commodityInfo.MemberDiscount, MinRetailPrice: commodityInfo.MinRetailPrice, RetailPrice: commodityInfo.RetailPrice, OriginalSn: purchaseOrder.SerialNumber, StockSn: v.SerialNumber, } stockList = append(stockList, stockCommodity) } // 遍历库存信息,合并重复数据 if len(waitCreateStockList) > 0 { newStockList := combineStocks(waitCreateStockList) err := gdb.Debug().Create(&newStockList).Error if err != nil { logger.Errorf("create erp_stock err:", err) return err } } err := gdb.Debug().Create(&stockList).Error if err != nil { logger.Errorf("create erp_stock_commodity err:", err) return err } return nil } func combineStocks(waitCreateStockList []ErpStock) []ErpStock { // 创建一个map,用于存储相同 StoreId 和 ErpCommodityId 的库存信息 stockMap := make(map[string]*ErpStock) // 遍历待处理的库存列表 for _, stock := range waitCreateStockList { // 构建库存信息的唯一标识 key := fmt.Sprintf("%d_%d", stock.StoreId, stock.ErpCommodityId) // 如果该库存信息已经存在于 map 中,则累加数量 if existingStock, found := stockMap[key]; found { existingStock.Count += stock.Count } else { // 否则,将该库存信息添加到 map 中 stockMap[key] = &ErpStock{ StoreId: stock.StoreId, StoreName: stock.StoreName, ErpCommodityId: stock.ErpCommodityId, ErpCommodityName: stock.ErpCommodityName, ErpCategoryId: stock.ErpCategoryId, ErpCategoryName: stock.ErpCategoryName, CommoditySerialNumber: stock.CommoditySerialNumber, IMEIType: stock.IMEIType, RetailPrice: stock.RetailPrice, MinRetailPrice: stock.MinRetailPrice, Count: stock.Count, DispatchCount: stock.DispatchCount, } } } // 将 map 中的库存信息转换为列表 var combinedStocks []ErpStock for _, stock := range stockMap { combinedStocks = append(combinedStocks, *stock) } return combinedStocks } // InventoryErpPurchaseUpdateRejectStock 采购退货更新库存信息 func InventoryErpPurchaseUpdateRejectStock(gdb *gorm.DB, list []ErpPurchaseInventory, purchaseOrder ErpPurchaseOrder) error { usedStockCommodityIdList := make(map[uint32]bool) for i, _ := range list { if list[i].IMEIType == 2 { // 串码商品 if list[i].IMEI == "" { return errors.New("串码为空") } // 判断该串码商品是否已经销售 var stockCommodityInfo ErpStockCommodity err := orm.Eloquent.Table("erp_stock_commodity").Where("imei = ?", list[i].IMEI). Find(&stockCommodityInfo).Error if err != nil { logger.Error("Inventory RejectStock query commodities err:", logger.Field("err", err)) return err } if stockCommodityInfo.State == SoldOut { return fmt.Errorf("商品[%s]已经销售,不能退货", stockCommodityInfo.ErpCommodityName) } //else if stockCommodityInfo.State == OnSale { // return fmt.Errorf("商品[%s]在销售锁定中,不能退货", stockCommodityInfo.ErpCommodityName) //} err = gdb.Table("erp_stock_commodity").Where("imei = ?", list[i].IMEI). Updates(&map[string]interface{}{ "state": PurchaseReturn, "stock_sn": list[i].SerialNumber, }).Error // 状态更新为采购退货 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", purchaseOrder.StoreId, list[i].ErpCommodityId). Updates(map[string]interface{}{"count": gorm.Expr("count - ?", 1)}).Error // 库存数量-1 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } } else { // 查询入库商品实际库存详情处剩余有效数,不包含已出库的数量 nCount, err := GetCommodityStockByPurchaseId(purchaseOrder.RejectedSerialNumber, list[i].ErpCommodityId) if err != nil { logger.Error("InventoryErpPurchaseUpdateRejectStock GetCommodityStockByPurchaseId err:", logger.Field("err", err)) return err } // 如果库存数量不够则报错 if nCount < list[i].Count { return fmt.Errorf("商品[%s]采购退货数量超出实际库存数量,请先零售退货", list[i].ErpCommodityName) } var stockCommodity []ErpStockCommodity //// 通过门店id,商品id,查找状态为1-在库的非串码商品 //err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and store_id = ? "+ // "and state = ? and imei_type = ? ", list[i].ErpCommodityId, purchaseOrder.StoreId, InStock, 1). // Order("id DESC").Find(&stockCommodity).Error // 通过首次入库订单编号,查找状态为1-在库的非串码商品 err = orm.Eloquent.Table("erp_stock_commodity"). Where("erp_commodity_id = ? and state = ? and imei_type = ? and original_sn = ?", list[i].ErpCommodityId, InStock, 1, purchaseOrder.RejectedSerialNumber). Order("id DESC").Find(&stockCommodity).Error if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } if stockCommodity == nil { return errors.New("RetailTypeRejected find no stock commodity") } var currentID uint32 // 找一个可用的库存ID for _, item := range stockCommodity { _, ok := usedStockCommodityIdList[item.ID] if !ok { usedStockCommodityIdList[item.ID] = true currentID = item.ID break } else { continue } } if currentID == 0 { return fmt.Errorf("商品[%s]采购退货数量超出实际库存数量,请先零售退货", list[i].ErpCommodityName) } err = gdb.Table("erp_stock_commodity").Where("id = ?", currentID).Updates(&map[string]interface{}{ "state": PurchaseReturn, "stock_sn": list[i].SerialNumber, }).Error // 状态更新为采购退货 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", purchaseOrder.StoreId, list[i].ErpCommodityId). Updates(map[string]interface{}{"count": gorm.Expr("count - ?", 1)}).Error // 库存数量-1 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } } } return nil } // 校验入参数据,执行数量是否超过总数;串码商品的串码是否重复 func checkPurchaseInventory(req *ErpPurchaseInventoryReq) error { // 查询现有的零售订单信息 var commodities []ErpPurchaseCommodity err := orm.Eloquent.Table("erp_purchase_commodity").Where("erp_purchase_order_id = ?", req.ErpPurchaseOrderId).Find(&commodities).Error if err != nil { logger.Error("query erp_purchase_commodity err:", logger.Field("err", err)) return err } countMap := make(map[uint32]uint32) for i, inventory := range req.Inventories { if inventory.ErpCommodityId == 0 { return fmt.Errorf("商品Id传参错误,不能为0") } countMap[inventory.ErpCommodityId] += inventory.Count // 如果该商品是串码商品,判断其串码是否会重复 if inventory.IMEI != "" { exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock_commodity WHERE FIND_IN_SET(%s, imei) > 0", inventory.IMEI)) if err != nil { logger.Error("exist sn err") } if exist { return fmt.Errorf("串码有重复项请修改[%s]", inventory.IMEI) } } // 备注:员工成本价在"库存管理-采购单入库"里面的入库操作前端是隐藏不可修改的,会默认传0 if inventory.EmployeePrice != 0 && inventory.EmployeePrice < inventory.ImplementationPrice { return fmt.Errorf("数据错误,员工成本价不能低于采购执行单价") } erpCommodity, err := GetCommodity(inventory.ErpCommodityId) if err != nil { logger.Error("checkPurchaseInventory GetCommodity err:", logger.Field("err", err)) return fmt.Errorf("查询员工成本价报错") } req.Inventories[i].ErpCategoryID = erpCommodity.ErpCategoryId // 分类id,入库的时候前端没传,后端补充 req.Inventories[i].ErpCategoryName = erpCommodity.ErpCategoryName // 分类名称,入库的时候前端没传,后端补充 //if inventory.EmployeePrice == 0 { // 没有传入员工成本价,则默认按照商品资料设置的员工成本价加价金额 // req.Inventories[i].EmployeePrice = inventory.ImplementationPrice + float64(erpCommodity.StaffCostPrice) //} } // 入库的商品信息有误,不在之前的商品列表中 for commodityID := range countMap { found := false for _, commodity := range commodities { if commodity.ErpCommodityId == commodityID { found = true break } } if !found { return fmt.Errorf("商品编号[%d]不属于该采购订单", commodityID) } } // 本次入库的数量超出该商品未入库数量 for _, commodity := range commodities { if inventoryCount, ok := countMap[commodity.ErpCommodityId]; ok { if req.PurchaseType == ErpProcureOrder { if commodity.Count-uint32(commodity.InventoryCount) < inventoryCount { return fmt.Errorf("本次入库商品[%s]数量[%d]超出该商品未入库数量[%d]", commodity.ErpCommodityName, inventoryCount, int32(commodity.Count)-commodity.InventoryCount) } } else if req.PurchaseType == ErpRejectOrder { if commodity.RejectedCount-uint32(commodity.InventoryCount) < inventoryCount { return fmt.Errorf("本次退货商品[%s]数量[%d]超出该商品未退货数量[%d]", commodity.ErpCommodityName, inventoryCount, int32(commodity.Count)-commodity.InventoryCount) } // 查询入库商品实际库存详情处剩余有效数,不包含已出库的数量 var purchaseOrder ErpPurchaseOrder err = orm.Eloquent.Table("erp_purchase_order").Where("id=?", req.ErpPurchaseOrderId).Find(&purchaseOrder).Error if err != nil { logger.Error("checkPurchaseInventory purchase order err:", logger.Field("err", err)) return err } nCount, err := GetCommodityStockByPurchaseId(purchaseOrder.RejectedSerialNumber, commodity.ErpCommodityId) if err != nil { logger.Error("checkPurchaseInventory GetCommodityStockByPurchaseId err:", logger.Field("err", err)) return err } // 如果库存数量不够则报错 if nCount < commodity.RejectedCount { return fmt.Errorf("商品[%s]采购退货数量超出实际库存数量,请先零售退货", commodity.ErpCommodityName) } } } } return nil } // ExecuteErpPurchase 执行(入库/退货) func ExecuteErpPurchase(req *ErpPurchaseExecuteReq, c *gin.Context) (*ErpPurchaseExecuteResp, error) { // 查询采购订单信息 var purchaseOrder ErpPurchaseOrder err := orm.Eloquent.Table("erp_purchase_order").Where("id=?", req.ErpPurchaseOrderId).Find(&purchaseOrder).Error if err != nil { logger.Error("purchase order err:", logger.Field("err", err)) return nil, err } // 校验入参门店是否包含在用户所有门店中,是否过期 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, err } if !CheckUserStore(purchaseOrder.StoreId, sysUser) { return nil, errors.New("操作失败:您没有该门店权限") } } reqParam := &ErpPurchaseInventoryReq{ ErpPurchaseOrderId: req.ErpPurchaseOrderId, PurchaseType: req.PurchaseType, Inventories: req.Inventories, } err = checkPurchaseInventory(reqParam) if err != nil { logger.Error("checkPurchaseInventoryReq err:", logger.Field("err", err)) return nil, err } resp := &ErpPurchaseExecuteResp{ List: make([]ExecuteData, 0), } for _, inventory := range req.Inventories { if inventory.Count == 0 { continue } // 获取商品信息 commodityInfo, err := GetCommodity(inventory.ErpCommodityId) if err != nil { return nil, err } if commodityInfo.IMEIType == 2 || commodityInfo.IMEIType == 3 { // 如果是串码商品,根据 Count 拆分成对应数量的数据 for i := 0; i < int(inventory.Count); i++ { imei := "" // 默认退货单执行时不需要串码 if inventory.PurchaseType == ErpProcureOrder && commodityInfo.IMEIType == 2 { // 采购单 // 调用函数B生成商品串码 imei, err = GenerateIMEI(inventory.ErpCommodityId) if err != nil { return nil, err } } // 将拆分后的商品信息添加到执行响应中 resp.List = append(resp.List, ExecuteData{ ErpPurchaseOrderId: inventory.ErpPurchaseOrderId, ErpCommodityId: inventory.ErpCommodityId, ErpCommodityName: inventory.ErpCommodityName, CommoditySerialNumber: inventory.CommoditySerialNumber, IMEIType: inventory.IMEIType, IMEI: imei, Count: 1, ImplementationPrice: inventory.ImplementationPrice, EmployeePrice: inventory.EmployeePrice, }) } } else if commodityInfo.IMEIType == 1 { // 如果是非串码商品,只需拆分为1条数据 resp.List = append(resp.List, ExecuteData{ ErpPurchaseOrderId: inventory.ErpPurchaseOrderId, ErpCommodityId: inventory.ErpCommodityId, ErpCommodityName: inventory.ErpCommodityName, CommoditySerialNumber: inventory.CommoditySerialNumber, IMEIType: inventory.IMEIType, IMEI: "", Count: inventory.Count, ImplementationPrice: inventory.ImplementationPrice, EmployeePrice: inventory.EmployeePrice, }) } } resp.Total = len(resp.List) return resp, nil } // GenerateIMEI 生成串码 func GenerateIMEI(commodityId uint32) (string, error) { commodity, err := GetCommodity(commodityId) if err != nil { return "", err } return GenerateSerialCode(commodity.ErpCategoryId) } // CreateErpPurchaseDemand 创建采购需求单 func CreateErpPurchaseDemand(req *CreateErpPurchaseDemandReq, sysUser *SysUser) error { var demandInfo ErpPurchaseDemand var demandRemark ErpPurchaseDemandRemark var demandList []ErpPurchaseDemand // 采购需求信息 demandInfo.ErpCommodityId = req.ErpCommodityID demandInfo.ErpCommoditySerialNumber = req.ErpCommoditySerialNumber demandInfo.ErpCommodityName = req.ErpCommodityName demandInfo.State = ErpDemandStateWait // 待采购 for _, v := range req.List { demandInfo.StoreId = v.StoreID demandInfo.StoreName = v.StoreName demandInfo.Count = v.NeedCount demandInfo.MakerId = uint32(sysUser.UserId) demandList = append(demandList, demandInfo) } // 采购需求备注信息 demandRemark.ErpCommodityId = req.ErpCommodityID demandRemark.State = ErpDemandStateWait // 待采购 demandRemark.MakerId = uint32(sysUser.UserId) demandRemark.Remark = req.Remark begin := orm.Eloquent.Begin() // 查询当前表格,有记录则更新,没有则插入新记录 for _, v := range demandList { var demand ErpPurchaseDemand err := orm.Eloquent.Table("erp_purchase_demand").Where("erp_commodity_id = ? and store_id = ? and state = 1", v.ErpCommodityId, v.StoreId).Find(&demand).Error if err != nil { logger.Error("query erp_purchase_demand err:", logger.Field("err", err)) return err } if demand.StoreId != 0 { // 有记录 if demand.Count == v.Count { // 值没变则不更新 continue } else { err = begin.Model(&ErpPurchaseDemand{}).Where("erp_commodity_id = ? and store_id = ? and state = 1", v.ErpCommodityId, v.StoreId).Updates(map[string]interface{}{ "count": v.Count, "state": v.State, "maker_id": v.MakerId, }).Error if err != nil { begin.Rollback() logger.Error("update erp_order err:", logger.Field("err", err)) return err } } } else { // 无记录,新建 err = begin.Create(&v).Error if err != nil { logger.Error("query erp_purchase_demand err:", logger.Field("err", err)) return err } } } // 更新备注,先查询有无记录,有则更新,没有则插入新记录 var demandRemarkInfo ErpPurchaseDemandRemark err := orm.Eloquent.Table("erp_purchase_demand_remark").Where("erp_commodity_id = ? and state = 1", req.ErpCommodityID).Find(&demandRemarkInfo).Error if err != nil { logger.Error("query erp_purchase_demand_remark err:", logger.Field("err", err)) return err } if demandRemarkInfo.ErpCommodityId != 0 { // 有记录 if demandRemarkInfo.Remark != req.Remark { // 备注有变化才更新 err := begin.Model(&ErpPurchaseDemandRemark{}).Where("erp_commodity_id = ? and state = 1", req.ErpCommodityID).Updates(map[string]interface{}{ "state": demandRemark.State, "remark": demandRemark.Remark, "maker_id": demandRemark.MakerId, }).Error if err != nil { begin.Rollback() logger.Error("update erp_purchase_demand_remark err:", logger.Field("err", err)) return err } } } else { // 无记录,新建 err = begin.Create(&demandRemark).Error if err != nil { logger.Error("create erp_purchase_demand_remark err:", logger.Field("err", err)) return err } } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) return err } return nil } // FinishErpPurchaseDemand 完成采购需求 func FinishErpPurchaseDemand(req *FinishErpPurchaseDemandReq, sysUser *SysUser) error { exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_purchase_demand WHERE erp_commodity_id='%d'", req.ErpCommodityID)) if err != nil { logger.Error("exist sn err") } if !exist { return fmt.Errorf("商品编号[%d]的商品不在采购需求单中", req.ErpCommodityID) } // 批量更新状态 err = orm.Eloquent.Table("erp_purchase_demand").Where("erp_commodity_id=? and state = 1", req.ErpCommodityID). Updates(map[string]interface{}{ "state": ErpDemandStateFinish, "finish_time": time.Now(), "purchaser_id": sysUser.UserId, }).Error if err != nil { logger.Error("update err:", logger.Field("err", err)) return err } err = orm.Eloquent.Table("erp_purchase_demand_remark").Where("erp_commodity_id=? and state = 1", req.ErpCommodityID). Updates(map[string]interface{}{ "state": ErpDemandStateFinish, "finish_time": time.Now(), "purchaser_id": sysUser.UserId, }).Error if err != nil { logger.Error("update err:", logger.Field("err", err)) return err } return nil } //// GetErpPurchaseDemand 获取采购需求 //func GetErpPurchaseDemand(req *GetErpPurchaseDemandReq) (*GetErpPurchaseDemandResp, error) { // resp := &GetErpPurchaseDemandResp{ // PageIndex: req.PageIndex, // PageSize: req.PageSize, // } // page := req.PageIndex - 1 // if page < 0 { // page = 0 // } // if req.PageSize == 0 { // req.PageSize = 10 // } // // qs := orm.Eloquent.Debug().Table("erp_commodity") // if req.ErpCategoryId != 0 { // qs = qs.Where("erp_category_id=?", req.ErpCategoryId) // } // // var commodities []ErpCommodity // err := qs.Order("id DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&commodities).Error // if err != nil && err != RecordNotFound { // //logger.Error("erp commodity list err:", err) // return resp, err // } // // // 组合数据 // resp, err = convertToDemandResp(commodities) // // return resp, nil //} // //func convertToDemandResp(commodities []ErpCommodity) (*GetErpPurchaseDemandResp, error) { // resp := new(GetErpPurchaseDemandResp) // var demandData DemandData // var demandList []DemandData // // // 查询所有在线门店信息 // var stores []Store // err := orm.Eloquent.Table("store").Where("is_online = ?", 1).Find(&stores).Error // if err != nil { // logger.Error("stores err:", logger.Field("err", err)) // return nil, err // } // // demandData.List = make([]struct { // StoreID uint32 `json:"store_id"` // StoreName string `json:"store_name"` // LastMonthSales uint32 `json:"last_month_sales"` // StockCount uint32 `json:"stock_count"` // NeedCount uint32 `json:"need_count"` // }, len(stores)) // // var totalCount uint32 // 总需采购数量 // // // 遍历所有商品 // for _, v := range commodities { // // 初始化 NeedCount 为 0 // var totalNeedCount uint32 // // for i, store := range stores { // demandData.List[i].StoreID = store.ID // demandData.List[i].StoreName = store.Name // // // 初始化 NeedCount 为 0 // demandData.List[i].NeedCount = 0 // // // 设置商品相关信息 // demandData.ErpCommodityID = v.ID // demandData.ErpCommoditySerialNumber = v.SerialNumber // demandData.ErpCommodityName = v.Name // demandData.ErpCategoryID = v.ErpCategoryId // demandData.ErpCategoryName = v.ErpCategoryName // demandData.RetailPrice = v.RetailPrice // // 最近采购价 // demandData.LastWholesalePrice, _ = GetCommodityLastWholesalePrice(v.ID) // // // 查询采购需求单 // var demand []ErpPurchaseDemand // err = orm.Eloquent.Table("erp_purchase_demand").Where("erp_commodity_id = ? AND state = 1 AND store_id = ?", // v.ID, store.ID).Find(&demand).Error // if err != nil { // logger.Error("query erp_purchase_demand err:", logger.Field("err", err)) // return nil, err // } // // if len(demand) > 0 { // totalNeedCount += demand[0].Count // demandData.List[i].NeedCount = demand[0].Count // } // // // 查询某个门店某个商品的库存情况 // demandData.List[i].StockCount, _ = GetCommodityStockByStoreId(v.ID, store.ID) // // 查询某个门店某件商品的上月销售数量 // demandData.List[i].LastMonthSales, _ = GetCommodityLastMonthSalesByStoreId(v.ID, store.ID) // } // totalCount += totalNeedCount // demandData.TotalCount = totalCount // demandData.TotalAmount = float64(totalCount) * demandData.LastWholesalePrice // demandList = append(demandList, demandData) // } // resp.List = demandList // // return resp, nil //} // //// GetCommodityStockByStoreId 查询某个门店某个商品的库存情况 //func GetCommodityStockByStoreId(commodityId, storeId uint32) (uint32, error) { // var count int64 // err := orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and store_id = ? and state = 1", // commodityId, storeId).Count(&count).Error // if err != nil { // logger.Error("GetCommodityStockByStoreId count err:", logger.Field("err", err)) // return 0, err // } // // return uint32(count), nil //} // //// GetCommodityLastMonthSalesByStoreId 查询某个门店某件商品的上月销售数量 //func GetCommodityLastMonthSalesByStoreId(commodityId, storeId uint32) (uint32, error) { // var lastMonthSales uint32 // // // 获取当前时间 // now := time.Now() // year, month, _ := now.Date() // firstDay := time.Date(year, month-1, 1, 0, 0, 0, 0, now.Location()) // lastDay := firstDay.AddDate(0, 1, -1) // // // 执行查询 // err := orm.Eloquent.Model(&ErpOrderCommodity{}). // Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id"). // Select("SUM(erp_order_commodity.count) AS last_month_sales"). // Where("erp_order.pay_status = ? AND erp_order_commodity.erp_commodity_id = ? AND erp_order.store_id = ? "+ // "AND erp_order_commodity.created_at BETWEEN ? AND ?", // HavePaid, commodityId, storeId, firstDay, lastDay). // Scan(&lastMonthSales).Error // if err != nil { // logger.Error("GetCommodityLastMonthSalesByStoreId err:", logger.Field("err", err)) // return 0, err // } // // return lastMonthSales, nil //} // //// GetCommodityLastWholesalePrice 查询某个商品的最近采购价 //func GetCommodityLastWholesalePrice(commodityId uint32) (float64, error) { // var implementationPrice float64 // // // 执行查询 // err := orm.Eloquent.Table("erp_purchase_inventory"). // Select("implementation_price"). // Where("erp_commodity_id = ? AND purchase_type = ?", 167, "procure"). // Order("created_at DESC"). // Limit(1). // Scan(&implementationPrice).Error // if err != nil { // logger.Error("GetCommodityLastMonthSalesByStoreId err:", logger.Field("err", err)) // return 0, err // } // // return implementationPrice, nil //} // 222222222 //// GetErpPurchaseDemand 获取采购需求 //func GetErpPurchaseDemand(req *GetErpPurchaseDemandReq) (*GetErpPurchaseDemandResp, error) { // resp := &GetErpPurchaseDemandResp{ // PageIndex: req.PageIndex, // PageSize: req.PageSize, // } // page := req.PageIndex - 1 // if page < 0 { // page = 0 // } // if req.PageSize == 0 { // req.PageSize = 10 // } // // qs := orm.Eloquent.Debug().Table("erp_commodity") // if req.ErpCategoryId != 0 { // qs = qs.Where("erp_category_id=?", req.ErpCategoryId) // } // // var commodities []ErpCommodity // err := qs.Order("id DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&commodities).Error // if err != nil && err != RecordNotFound { // return resp, err // } // // // 批量查询门店信息 // stores, err := GetOnlineStores() // if err != nil { // return nil, err // } // // // 批量查询商品信息 // demandDataList := make([]DemandData, 0, len(commodities)) // for _, v := range commodities { // demandData, err := convertToDemandData(v, stores) // if err != nil { // return nil, err // } // demandDataList = append(demandDataList, demandData) // } // // resp.List = demandDataList // // return resp, nil //} // //// convertToDemandData 将商品转换为采购需求数据 //func convertToDemandData(commodity ErpCommodity, stores []Store) (DemandData, error) { // demandData := DemandData{ // ErpCommodityID: commodity.ID, // ErpCommoditySerialNumber: commodity.SerialNumber, // ErpCommodityName: commodity.Name, // ErpCategoryID: commodity.ErpCategoryId, // ErpCategoryName: commodity.ErpCategoryName, // RetailPrice: commodity.RetailPrice, // } // // // 批量查询最近采购价 // lastWholesalePrices, err := GetCommodityLastWholesalePrices(commodity.ID) // if err != nil { // return DemandData{}, err // } // // for i, store := range stores { // demandData.List = append(demandData.List, struct { // StoreID uint32 `json:"store_id"` // StoreName string `json:"store_name"` // LastMonthSales uint32 `json:"last_month_sales"` // StockCount uint32 `json:"stock_count"` // NeedCount uint32 `json:"need_count"` // }{ // StoreID: store.ID, // StoreName: store.Name, // }) // // // 批量查询库存情况 // stockCounts, err := GetCommodityStocksByStoreID(commodity.ID, store.ID) // if err != nil { // return DemandData{}, err // } // demandData.List[i].StockCount = stockCounts[store.ID] // // // 批量查询上月销售数量 // lastMonthSales, err := GetCommodityLastMonthSales(commodity.ID, store.ID) // if err != nil { // return DemandData{}, err // } // demandData.List[i].LastMonthSales = lastMonthSales // // // 设置最近采购价 // demandData.LastWholesalePrice = lastWholesalePrices[commodity.ID] // } // // return demandData, nil //} // //// GetCommodityLastWholesalePrices 批量查询商品的最近采购价 //func GetCommodityLastWholesalePrices(commodityID uint32) (map[uint32]float64, error) { // // 查询最近采购价 // var prices []struct { // CommodityID uint32 // Price float64 // } // err := orm.Eloquent.Table("erp_purchase_inventory"). // Select("erp_commodity_id, implementation_price AS price"). // Where("erp_commodity_id IN (?) AND purchase_type = ?", commodityID, "procure"). // Group("erp_commodity_id"). // Order("created_at DESC"). // Find(&prices).Error // if err != nil { // return nil, err // } // // // 构建结果 // result := make(map[uint32]float64) // for _, p := range prices { // result[p.CommodityID] = p.Price // } // return result, nil //} // //// GetCommodityStocksByStoreID 批量查询商品的库存情况 //func GetCommodityStocksByStoreID(commodityID, storeID uint32) (map[uint32]uint32, error) { // // 查询库存情况 // var stocks []struct { // StoreID uint32 // CommodityID uint32 // StockCount uint32 // } // err := orm.Eloquent.Table("erp_stock_commodity"). // Select("store_id, erp_commodity_id, COUNT(*) AS stock_count"). // Where("erp_commodity_id IN (?) AND store_id = ? AND state = 1", commodityID, storeID). // Group("store_id, erp_commodity_id"). // Find(&stocks).Error // if err != nil { // return nil, err // } // // // 构建结果 // result := make(map[uint32]uint32) // for _, s := range stocks { // result[s.StoreID] = s.StockCount // } // return result, nil //} // //// GetCommodityLastMonthSales 批量查询商品的上月销售数量 //func GetCommodityLastMonthSales(commodityID, storeID uint32) (uint32, error) { // // 获取上个月的时间范围 // firstDay, lastDay := GetLastMonthRange() // // // 查询上月销售数量 // var lastMonthSales struct { // Sales uint32 // } // err := orm.Eloquent.Table("erp_order_commodity"). // Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id"). // Where("erp_order.pay_status = ? AND erp_order_commodity.erp_commodity_id = ? AND erp_order.store_id = ? "+ // "AND erp_order_commodity.created_at BETWEEN ? AND ?", // HavePaid, commodityID, storeID, firstDay, lastDay). // Select("SUM(erp_order_commodity.count) AS sales"). // Scan(&lastMonthSales).Error // if err != nil { // return 0, err // } // // return lastMonthSales.Sales, nil //} // //// GetLastMonthRange 获取上个月的时间范围 //func GetLastMonthRange() (time.Time, time.Time) { // now := time.Now() // year, month, _ := now.Date() // firstDay := time.Date(year, month-1, 1, 0, 0, 0, 0, now.Location()) // lastDay := firstDay.AddDate(0, 1, -1) // return firstDay, lastDay //} // //// GetOnlineStores 查询所有在线门店信息 //func GetOnlineStores() ([]Store, error) { // var stores []Store // err := orm.Eloquent.Table("store").Where("is_online = ?", 1).Find(&stores).Error // if err != nil { // return nil, err // } // return stores, nil //} // 3333 //// GetErpPurchaseDemand 获取采购需求 //func GetErpPurchaseDemand(req *GetErpPurchaseDemandReq) (*GetErpPurchaseDemandResp, error) { // resp := &GetErpPurchaseDemandResp{ // PageIndex: req.PageIndex, // PageSize: req.PageSize, // } // page := req.PageIndex - 1 // if page < 0 { // page = 0 // } // if req.PageSize == 0 { // req.PageSize = 10 // } // // qs := orm.Eloquent.Debug().Table("erp_commodity") // if req.ErpCategoryId != 0 { // qs = qs.Where("erp_category_id=?", req.ErpCategoryId) // } // // var commodities []ErpCommodity // err := qs.Order("id DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&commodities).Error // if err != nil && err != RecordNotFound { // return resp, err // } // // // 批量查询门店信息 // stores, err := GetOnlineStores() // if err != nil { // return nil, err // } // // // 并行查询需求数据 // var wg sync.WaitGroup // demandDataList := make([]DemandData, len(commodities)) // // for i, v := range commodities { // wg.Add(1) // go func(index int, commodity ErpCommodity) { // defer wg.Done() // demandData, err := convertToDemandData(commodity, stores) // if err != nil { // // Handle error // return // } // demandDataList[index] = demandData // }(i, v) // } // // wg.Wait() // // resp.List = demandDataList // // return resp, nil //} // //// convertToDemandData 将商品转换为采购需求数据 //func convertToDemandData(commodity ErpCommodity, stores []Store) (DemandData, error) { // demandData := DemandData{ // ErpCommodityID: commodity.ID, // ErpCommoditySerialNumber: commodity.SerialNumber, // ErpCommodityName: commodity.Name, // ErpCategoryID: commodity.ErpCategoryId, // ErpCategoryName: commodity.ErpCategoryName, // RetailPrice: commodity.RetailPrice, // } // // // 并行查询最近采购价、库存情况、上月销售数量 // var wg sync.WaitGroup // wg.Add(3) // // var lastWholesalePrices map[uint32]float64 // var stockCounts map[uint32]uint32 // var lastMonthSales uint32 // var err1, err2, err3 error // // go func() { // defer wg.Done() // lastWholesalePrices, err1 = GetCommodityLastWholesalePrices(commodity.ID) // }() // // go func() { // defer wg.Done() // stockCounts, err2 = GetCommodityStocksByStoreID(commodity.ID, stores) // }() // // go func() { // defer wg.Done() // lastMonthSales, err3 = GetCommodityLastMonthSales(commodity.ID, stores) // }() // // wg.Wait() // // if err1 != nil || err2 != nil || err3 != nil { // // Handle error // return DemandData{}, err1 // } // // // 构建需求数据 // demandData.List = make([]struct { // StoreID uint32 `json:"store_id"` // StoreName string `json:"store_name"` // LastMonthSales uint32 `json:"last_month_sales"` // StockCount uint32 `json:"stock_count"` // NeedCount uint32 `json:"need_count"` // }, len(stores)) // // for i, store := range stores { // demandData.List[i].StoreID = store.ID // demandData.List[i].StoreName = store.Name // // demandData.List[i].StockCount = stockCounts[store.ID] // demandData.List[i].LastMonthSales = lastMonthSales // // // 设置最近采购价 // demandData.LastWholesalePrice = lastWholesalePrices[commodity.ID] // } // // return demandData, nil //} // //// GetCommodityLastWholesalePrices 批量查询商品的最近采购价 //func GetCommodityLastWholesalePrices(commodityID uint32) (map[uint32]float64, error) { // // 查询最近采购价 // var prices []struct { // CommodityID uint32 // Price float64 // } // err := orm.Eloquent.Table("erp_purchase_inventory"). // Select("erp_commodity_id, implementation_price AS price"). // Where("erp_commodity_id IN (?) AND purchase_type = ?", commodityID, "procure"). // Group("erp_commodity_id"). // Order("created_at DESC"). // Find(&prices).Error // if err != nil { // return nil, err // } // // // 构建结果 // result := make(map[uint32]float64) // for _, p := range prices { // result[p.CommodityID] = p.Price // } // return result, nil //} // //// GetCommodityStocksByStoreID 批量查询商品的库存情况 //func GetCommodityStocksByStoreID(commodityID uint32, stores []Store) (map[uint32]uint32, error) { // // 并行查询库存情况 // var wg sync.WaitGroup // wg.Add(len(stores)) // // result := make(map[uint32]uint32) // // for _, store := range stores { // go func(storeID uint32) { // defer wg.Done() // // 查询库存情况 // var stockCount int64 // err := orm.Eloquent.Table("erp_stock_commodity"). // Where("erp_commodity_id = ? AND store_id = ? AND state = 1", commodityID, storeID). // Count(&stockCount).Error // if err != nil { // // Handle error // return // } // result[storeID] = uint32(stockCount) // }(store.ID) // } // // wg.Wait() // // return result, nil //} // //// GetCommodityLastMonthSales 批量查询商品的上月销售数量 //func GetCommodityLastMonthSales(commodityID uint32, stores []Store) (uint32, error) { // // 获取上个月的时间范围 // firstDay, lastDay := GetLastMonthRange() // // // 并行查询上月销售数量 // var wg sync.WaitGroup // wg.Add(len(stores)) // // var totalSales uint32 // var mu sync.Mutex // 用于保护 totalSales 的并发访问 // // for _, store := range stores { // go func(storeID uint32) { // defer wg.Done() // // 查询上月销售数量 // var sales uint32 // err := orm.Eloquent.Table("erp_order_commodity"). // Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id"). // Where("erp_order.pay_status = ? AND erp_order_commodity.erp_commodity_id = ? AND erp_order.store_id = ? "+ // "AND erp_order_commodity.created_at BETWEEN ? AND ?", // HavePaid, commodityID, storeID, firstDay, lastDay). // Select("SUM(erp_order_commodity.count) AS sales"). // Scan(&sales).Error // if err != nil { // // Handle error // return // } // // // 累加销售数量 // mu.Lock() // totalSales += sales // mu.Unlock() // }(store.ID) // } // // wg.Wait() // // return totalSales, nil //} // //// GetLastMonthRange 获取上个月的时间范围 //func GetLastMonthRange() (time.Time, time.Time) { // now := time.Now() // year, month, _ := now.Date() // firstDay := time.Date(year, month-1, 1, 0, 0, 0, 0, now.Location()) // lastDay := firstDay.AddDate(0, 1, -1) // return firstDay, lastDay //} // //// GetOnlineStores 查询所有在线门店信息 //func GetOnlineStores() ([]Store, error) { // var stores []Store // err := orm.Eloquent.Table("store").Where("is_online = ?", 1).Find(&stores).Error // if err != nil { // return nil, err // } // return stores, nil //} // GetErpPurchaseDemand 获取采购需求 func GetErpPurchaseDemand(req *GetErpPurchaseDemandReq, c *gin.Context) (*GetErpPurchaseDemandResp, error) { var err error resp := new(GetErpPurchaseDemandResp) if req.HideFlag == "ON" { // 隐藏无采购需求的商品 resp, err = getErpPurchaseDemandHide(req, c) } else { // 展示所有 resp, err = getErpPurchaseDemandAll(req, c) } if err != nil { return nil, err } return resp, nil } // 展示所有采购需求 func getErpPurchaseDemandAll(req *GetErpPurchaseDemandReq, c *gin.Context) (*GetErpPurchaseDemandResp, error) { page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &GetErpPurchaseDemandResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Debug().Table("erp_commodity") if req.ErpCategoryId != 0 { categoryInfo, err := GetErpCategory(req.ErpCategoryId) if err != nil { return nil, err } qs = qs.Where("serial_number like ?", categoryInfo.Number+"%") } if req.ErpCommoditySerialNumber != "" { qs = qs.Where("serial_number=?", req.ErpCommoditySerialNumber) } if req.ErpCommodityName != "" { qs = qs.Where("name=?", req.ErpCommodityName) } var count int64 if err := qs.Count(&count).Error; err != nil { logger.Error("count err:", logger.Field("err", err)) return resp, err } var commodities []ErpCommodity err := qs.Order("id DESC").Find(&commodities).Error if err != nil && err != RecordNotFound { return resp, err } // 按商品编号进行排序 SortCommodities(commodities) // 批量查询门店信息 stores, err := GetOnlineStores(c) if err != nil { return nil, err } // 并行查询需求数据 var wg sync.WaitGroup demandDataList := make([]DemandData, len(commodities)) for i, v := range commodities { wg.Add(1) go func(index int, commodity ErpCommodity) { defer wg.Done() demandData, err := convertToDemandData(commodity, stores) if err != nil { // Handle error return } demandDataList[index] = demandData }(i, v) } wg.Wait() if req.IsExport == 1 { // 导出excel if len(demandDataList) == 0 { return nil, errors.New("未查询到数据") } resp.ExportUrl, err = demandDataExport(demandDataList) if err != nil { return nil, err } } else { // 计算分页所需的切片索引 startIndex := page * req.PageSize endIndex := (page + 1) * req.PageSize if endIndex > len(demandDataList) { endIndex = len(demandDataList) } resp.List = demandDataList[startIndex:endIndex] resp.Total = count } return resp, nil } // 隐藏无采购需求的商品 func getErpPurchaseDemandHide(req *GetErpPurchaseDemandReq, c *gin.Context) (*GetErpPurchaseDemandResp, error) { page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &GetErpPurchaseDemandResp{ PageIndex: page + 1, PageSize: req.PageSize, } // 查询采购需求单信息,筛选出有采购需求的商品id var demand []ErpPurchaseDemand err := orm.Eloquent.Table("erp_purchase_demand"). Where("state = 1").Find(&demand).Error if err != nil { return nil, err } // 用 map 存储已经出现过的 ErpCommodityId commodityIds := make(map[uint32]bool) var uniqueCommodityIds []uint32 for _, d := range demand { if _, ok := commodityIds[d.ErpCommodityId]; !ok { commodityIds[d.ErpCommodityId] = true uniqueCommodityIds = append(uniqueCommodityIds, d.ErpCommodityId) } } // 查询商品信息 qs := orm.Eloquent.Debug().Table("erp_commodity") if req.ErpCategoryId != 0 { //qs = qs.Where("erp_category_id=?", req.ErpCategoryId) categoryInfo, err := GetErpCategory(req.ErpCategoryId) if err != nil { return nil, err } qs = qs.Where("serial_number like ?", categoryInfo.Number+"%") } if req.ErpCommoditySerialNumber != "" { qs = qs.Where("serial_number=?", req.ErpCommoditySerialNumber) } if req.ErpCommodityName != "" { qs = qs.Where("name=?", req.ErpCommodityName) } var count int64 if err := qs.Count(&count).Error; err != nil { logger.Error("count err:", logger.Field("err", err)) return resp, err } var commodities []ErpCommodity if req.IsExport == 1 { // 导出excel err := qs.Order("id DESC").Find(&commodities).Error if err != nil && err != RecordNotFound { return resp, err } } else { err := qs.Order("id DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&commodities).Error if err != nil && err != RecordNotFound { return resp, err } } // 匹配商品id,有则继续查询返回数据,没有则不返回 var matchedCommodities []ErpCommodity for _, c := range commodities { for _, uniqueID := range uniqueCommodityIds { if c.ID == uniqueID { matchedCommodities = append(matchedCommodities, c) break // 找到匹配的商品后跳出内层循环,继续下一个商品 } } } if len(matchedCommodities) == 0 { resp.List = nil } else { // 批量查询门店信息 stores, err := GetOnlineStores(c) if err != nil { return nil, err } // 并行查询需求数据 var wg sync.WaitGroup demandDataList := make([]DemandData, len(matchedCommodities)) for i, v := range matchedCommodities { wg.Add(1) go func(index int, commodity ErpCommodity) { defer wg.Done() demandData, err := convertToDemandData(commodity, stores) if err != nil { // Handle error return } demandDataList[index] = demandData }(i, v) } wg.Wait() if req.IsExport == 1 { // 导出excel if len(demandDataList) == 0 { return nil, errors.New("未查询到数据") } resp.ExportUrl, err = demandDataExport(demandDataList) if err != nil { return nil, err } } else { resp.List = demandDataList resp.Total = int64(len(matchedCommodities)) } } return resp, nil } // convertToDemandData 将商品转换为采购需求数据 func convertToDemandData(commodity ErpCommodity, stores []Store) (DemandData, error) { // 查询采购商品的备注信息 var demandRemarkInfo ErpPurchaseDemandRemark err := orm.Eloquent.Table("erp_purchase_demand_remark").Where("erp_commodity_id = ? and state = 1", commodity.ID).Find(&demandRemarkInfo).Error if err != nil { logger.Error("query erp_purchase_demand_remark err:", logger.Field("err", err)) return DemandData{}, err } demandData := DemandData{ ErpCommodityID: commodity.ID, ErpCommoditySerialNumber: commodity.SerialNumber, ErpCommodityName: commodity.Name, ErpCategoryID: commodity.ErpCategoryId, ErpCategoryName: commodity.ErpCategoryName, RetailPrice: commodity.RetailPrice, Remark: demandRemarkInfo.Remark, } // 查询采购需求单 demands, err := GetCommodityPurchaseDemands(commodity.ID, stores) if err != nil { // Handle error return DemandData{}, err } //if len(demands) != 0 { // demandData.Remark = demands[0].Remark //} //使用 WaitGroup 进行并行查询 var wg sync.WaitGroup wg.Add(3) var lastWholesalePrices map[uint32]float64 var stockCounts map[uint32]uint32 var lastMonthSales map[uint32]uint32 go func() { defer wg.Done() lastWholesalePrices, err = GetCommodityLastWholesalePrices(commodity.ID) }() go func() { defer wg.Done() stockCounts, err = GetCommodityStocksByStoreID(commodity.ID, stores) }() go func() { defer wg.Done() lastMonthSales, err = GetCommodityLastMonthSales(commodity.ID, stores) }() wg.Wait() if err != nil { // Handle error //return DemandData{}, err 备注:有时会提示没记录,不做处理 } var totalCount uint32 // 总需采购数量 // 构建需求数据 demandData.StoreList = make([]struct { StoreID uint32 `json:"store_id"` StoreName string `json:"store_name"` LastMonthSales uint32 `json:"last_month_sales"` StockCount uint32 `json:"stock_count"` NeedCount uint32 `json:"need_count"` }, len(stores)) for i, store := range stores { demandData.StoreList[i].StoreID = store.ID demandData.StoreList[i].StoreName = store.Name demandData.StoreList[i].StockCount = stockCounts[store.ID] demandData.StoreList[i].LastMonthSales = lastMonthSales[store.ID] // 设置最近采购价 demandData.LastWholesalePrice = lastWholesalePrices[commodity.ID] // 设置采购需求量 for _, demand := range demands { if demand.StoreId == store.ID { demandData.StoreList[i].NeedCount = demand.Count totalCount += demand.Count break } } } demandData.TotalCount = totalCount demandData.TotalAmount = float64(totalCount) * demandData.LastWholesalePrice return demandData, nil } // GetCommodityPurchaseDemands 查询商品的采购需求单 func GetCommodityPurchaseDemands(commodityID uint32, stores []Store) ([]ErpPurchaseDemand, error) { var wg sync.WaitGroup var mu sync.Mutex var demands []ErpPurchaseDemand wg.Add(len(stores)) for _, store := range stores { go func(storeID uint32) { defer wg.Done() var demand ErpPurchaseDemand err := orm.Eloquent.Table("erp_purchase_demand"). Where("erp_commodity_id = ? AND state = 1 AND store_id = ?", commodityID, storeID). First(&demand).Error if err != nil { // Handle error return } mu.Lock() defer mu.Unlock() demands = append(demands, demand) }(store.ID) } wg.Wait() return demands, nil } // GetCommodityLastWholesalePrices 批量查询商品的最近采购价 func GetCommodityLastWholesalePrices(commodityID uint32) (map[uint32]float64, error) { // 查询最近采购价 var prices []struct { ErpCommodityID uint32 Price float64 } err := orm.Eloquent.Table("erp_purchase_inventory"). Select("erp_commodity_id, implementation_price AS price"). Where("erp_commodity_id IN (?) AND purchase_type = ?", commodityID, "procure"). //Group("erp_commodity_id"). Order("created_at DESC"). First(&prices).Error if err != nil { return nil, err } // 构建结果 result := make(map[uint32]float64) for _, p := range prices { result[p.ErpCommodityID] = p.Price } return result, nil } // GetCommodityStocksByStoreID 批量查询商品的库存情况 func GetCommodityStocksByStoreID(commodityID uint32, stores []Store) (map[uint32]uint32, error) { // 并行查询库存情况 var wg sync.WaitGroup wg.Add(len(stores)) result := make(map[uint32]uint32) for _, store := range stores { go func(storeID uint32) { defer wg.Done() // 查询库存情况 var stockCount int64 err := orm.Eloquent.Table("erp_stock_commodity"). Where("erp_commodity_id = ? AND store_id = ? AND state = 1", commodityID, storeID). Count(&stockCount).Error if err != nil { // Handle error return } result[storeID] = uint32(stockCount) }(store.ID) } wg.Wait() return result, nil } // GetCommodityStockByPurchaseId 查询入库商品实际库存详情处剩余有效数,不包含已出库的数量 func GetCommodityStockByPurchaseId(serialNumber string, orderId uint32) (uint32, error) { var count int64 err := orm.Eloquent.Table("erp_stock_commodity").Where("original_sn = ? and erp_commodity_id = ? and state = ?", serialNumber, orderId, InStock).Count(&count).Error if err != nil { return 0, err } return uint32(count), nil } // GetCommodityLastMonthSales 批量查询商品的上月销售数量 func GetCommodityLastMonthSales(commodityID uint32, stores []Store) (map[uint32]uint32, error) { // 获取上个月的时间范围 firstDay, lastDay := GetLastMonthRange() // 并行查询上月销售数量 var wg sync.WaitGroup wg.Add(len(stores)) result := make(map[uint32]uint32) var mu sync.Mutex // 用于保护 totalSales 的并发访问 for _, store := range stores { go func(storeID uint32) { defer wg.Done() // 查询上月销售数量 var sales int64 err := orm.Eloquent.Table("erp_stock_commodity"). Where("state = ? AND erp_commodity_id = ? AND store_id = ? AND updated_at BETWEEN ? AND ?", SoldOut, commodityID, storeID, firstDay, lastDay).Count(&sales).Error if err != nil { // Handle error return } // 保护 totalSales 的并发访问 mu.Lock() defer mu.Unlock() result[storeID] = uint32(sales) }(store.ID) } wg.Wait() return result, nil } // GetLastMonthRange 获取上个月的时间范围 func GetLastMonthRange() (time.Time, time.Time) { now := time.Now() year, month, _ := now.Date() firstDay := time.Date(year, month-1, 1, 0, 0, 0, 0, now.Location()) lastDay := firstDay.AddDate(0, 1, -1) return firstDay, lastDay } // GetOnlineStores 查询所有在线门店信息 func GetOnlineStores(c *gin.Context) ([]Store, error) { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, err } var stores []Store err = orm.Eloquent.Table("store").Where("is_online = ? and cooperative_business_id = ? ", 1, sysUser.CooperativeBusinessId).Find(&stores).Error if err != nil { return nil, err } return stores, nil } type Result struct { TotalCount uint32 `gorm:"column:total_count"` AvgImplementationPrice float64 `gorm:"column:avg_implementation_price"` TotalAmount float64 `gorm:"column:total_amount"` AvgEmployeePrice float64 `gorm:"column:avg_employee_price"` } // GetTotalsAndAveragesByCommodityID 查询执行数量之和、平均执行单价、执行金额之和和平均员工成本价 func GetTotalsAndAveragesByCommodityID(commodityID, orderID uint32) (Result, error) { var result Result err := orm.Eloquent.Table("erp_purchase_inventory"). Select("SUM(`count`) AS total_count, AVG(`implementation_price`) AS avg_implementation_price, "+ "SUM(`amount`) AS total_amount, AVG(`employee_price`) AS avg_employee_price"). Where("erp_commodity_id = ? and erp_purchase_order_id = ?", commodityID, orderID). Scan(&result).Error if err != nil { return Result{}, err } return result, nil } type ResultPrice struct { TotalWholesalePrice float64 `gorm:"column:total_wholesale_price"` } // GetTotalWholesalePrice 查询采购退货订单对应的采购金额 func GetTotalWholesalePrice(commodityID uint32, serialNumber string) (ResultPrice, error) { var result ResultPrice err := orm.Eloquent.Table("erp_stock_commodity"). Select("SUM(`wholesale_price`) AS total_wholesale_price"). Where("erp_commodity_id = ? and stock_sn = ?", commodityID, serialNumber). Scan(&result).Error if err != nil { return ResultPrice{}, err } return result, nil } // UpdateRetailPrice 更新指导零售价 func UpdateRetailPrice(begin *gorm.DB, commodityId uint32, retailPrice float64) error { // 更新商品信息表 err := begin.Table("erp_commodity").Where("id=?", commodityId). Updates(map[string]interface{}{ "retail_price": retailPrice, }).Error if err != nil { return err } // 更新库存表 err = begin.Table("erp_stock").Where("erp_commodity_id=?", commodityId). Updates(map[string]interface{}{ "retail_price": retailPrice, }).Error if err != nil { return err } // 更新库存商品表 err = begin.Table("erp_stock_commodity").Where("erp_commodity_id=? and state not in (2,5)", commodityId). Updates(map[string]interface{}{ "retail_price": retailPrice, }).Error if err != nil { return err } return nil } // 导出采购需求excel func demandDataExport(list []DemandData) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := ExportUrl fileName := time.Now().Format(TimeFormat) + "采购需求" + ".xlsx" fmt.Println("url fileName:", url+fileName) // 组合标题栏第一行数据 title1 := []interface{}{"商品编号", "商品名称", "商品分类", "指导零售价", "最近采购价"} storeCount := len(list[0].StoreList) var mergeCells []string // 存储需要合并的单元格范围 for _, v := range list[0].StoreList { for i := 0; i < 3; i++ { title1 = append(title1, v.StoreName) } } for i := 0; i < storeCount; i++ { // 计算每个商户名称的起始和结束单元格 startCol, _ := excelize.ColumnNumberToName(6 + i*3) // 从第6列开始,每个商户占3列 endCol, _ := excelize.ColumnNumberToName(8 + i*3) mergeCell := startCol + "1:" + endCol + "1" mergeCells = append(mergeCells, mergeCell) } title1 = append(title1, "需采购总数量") title1 = append(title1, "需采购总金额") title1 = append(title1, "备注") // 合并单元格 for _, mergeCell := range mergeCells { startCell, endCell := splitMergeCellCoordinates(mergeCell) _ = file.MergeCell(fSheet, startCell, endCell) } // 组合标题栏第二行数据 title2 := []interface{}{"商品编号", "商品名称", "商品分类", "指导零售价", "最近采购价"} for _, _ = range list[0].StoreList { title2 = append(title2, "上月销售数") title2 = append(title2, "库存数量") title2 = append(title2, "需采购数") } title2 = append(title2, "需采购总数量") title2 = append(title2, "需采购总金额") title2 = append(title2, "备注") for i, _ := range title1 { cell, _ := excelize.CoordinatesToCellName(1+i, 1) err := file.SetCellValue(fSheet, cell, title1[i]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } for i, _ := range title2 { cell, _ := excelize.CoordinatesToCellName(1+i, 2) err := file.SetCellValue(fSheet, cell, title2[i]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } var row []interface{} nExcelStartRow := 0 for i := 0; i < len(list); i++ { row = []interface{}{ list[i].ErpCommoditySerialNumber, // 商品编号 list[i].ErpCommodityName, // 商品名称 list[i].ErpCategoryName, // 商品分类名称 list[i].RetailPrice, // 指导零售价 list[i].LastWholesalePrice, // 最近采购价 } for _, v := range list[i].StoreList { row = append(row, v.LastMonthSales) // 上月销售数 row = append(row, v.StockCount) // 库存数量 row = append(row, v.NeedCount) // 需采购数 } row = append(row, list[i].TotalCount) // 需采购总数量 row = append(row, list[i].TotalAmount) // 需采购总金额 row = append(row, list[i].Remark) // 备注 for j, _ := range row { cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+3) err := file.SetCellValue(fSheet, cell, row[j]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } nExcelStartRow++ } // 合并 "需采购总数量","需采购总金额" 和 "备注" 列的单元格 strTotalCountCol, strTotalAmountCol, strRemarkCol := computeExtraColumns("E", storeCount) _ = file.MergeCell(fSheet, strTotalCountCol+"1", strTotalCountCol+"2") _ = file.MergeCell(fSheet, strTotalAmountCol+"1", strTotalAmountCol+"2") _ = file.MergeCell(fSheet, strRemarkCol+"1", strRemarkCol+"2") // 设置所有单元格的样式: 居中、加边框 style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, "border":[{"type":"left","color":"000000","style":1}, {"type":"top","color":"000000","style":1}, {"type":"right","color":"000000","style":1}, {"type":"bottom","color":"000000","style":1}]}`) endRow := fmt.Sprintf(strRemarkCol+"%d", nExcelStartRow+2) // 应用样式到整个表格 _ = file.SetCellStyle("Sheet1", "A1", endRow, style) _ = file.MergeCell(fSheet, "A1", "A2") _ = file.MergeCell(fSheet, "B1", "B2") _ = file.MergeCell(fSheet, "C1", "C2") _ = file.MergeCell(fSheet, "D1", "D2") _ = file.MergeCell(fSheet, "E1", "E2") fmt.Println("save fileName:", config.ExportConfig.Path+fileName) if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { fmt.Println(err) } return url + fileName, nil } // 根据合并单元格的字符串获取左上角和右下角的坐标 func splitMergeCellCoordinates(cell string) (string, string) { split := strings.Split(cell, ":") if len(split) != 2 { return "", "" } return split[0], split[1] } // 计算 "需采购总数量","需采购总金额" 和 "备注" 列的坐标字符串 func computeExtraColumns(startCol string, storeCount int) (string, string, string) { // "需采购总数量" 列在商户列表结束后的下一列 totalCountCol := convertColumnToLetters(convertLettersToColumn(startCol) + storeCount*3 + 1) // "需采购总金额" 列在 "需采购总数量" 列的下一列 totalAmountCol := convertColumnToLetters(convertLettersToColumn(totalCountCol) + 1) // "备注" 列在 "需采购总金额" 列的下一列 remarkCol := convertColumnToLetters(convertLettersToColumn(totalAmountCol) + 1) return totalCountCol, totalAmountCol, remarkCol } // 将列号转换为字母 func convertColumnToLetters(column int) string { var result string for column > 0 { column-- result = string('A'+column%26) + result column /= 26 } return result } // 将字母转换为列号 func convertLettersToColumn(letters string) int { result := 0 for _, letter := range letters { result *= 26 result += int(letter) - 'A' + 1 } return result } // GetReportByOrder 查询采购报表(按单) func GetReportByOrder(req *ErpPurchaseReportByOrderReq, c *gin.Context) (*ErpPurchaseReportByOrderResp, error) { var err error resp := new(ErpPurchaseReportByOrderResp) //if req.ErpCommodityName != "" || req.ErpCategoryID != 0 { // 商品名称、商品分类不为空 // // 先筛选商品入库信息表,然后再补充采购订单信息 // resp, err = getReportByOrderFromCommodityOrCategory(req, c) //} else { // // 先筛选采购订单表,再补充商品入库信息 // resp, err = getReportByOrderFromCommon(req, c) //} resp, err = getReportByOrderFromCommon(req, c) if err != nil { return nil, err } return resp, nil } // 查询采购报表(按单):先筛选商品入库信息表,然后再补充采购订单信息 func getReportByOrderFromCommodityOrCategory(req *ErpPurchaseReportByOrderReq, c *gin.Context) ( *ErpPurchaseReportByOrderResp, error) { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if len(storeList) > 0 { req.StoreId = CompareLists(storeList, req.StoreId) if len(req.StoreId) == 0 { // 没有匹配的数据,表示入参门店不是用户有权限的门店 return &ErpPurchaseReportByOrderResp{}, nil } } else { return nil, errors.New("用户未绑定门店") } } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpPurchaseReportByOrderResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Table("erp_purchase_inventory") if req.ErpCommodityName != "" { qs = qs.Where("erp_commodity_name=?", req.ErpCommodityName) } if req.ErpCategoryID != 0 { qs = qs.Where("erp_category_id=?", req.ErpCategoryID) } var inventoryList []ErpPurchaseInventory var err error if req.IsExport == 1 { // 导出excel err = qs.Order("id DESC").Find(&inventoryList).Error } else { err = qs.Order("id DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&inventoryList).Error } if err != nil && err != RecordNotFound { logger.Error("getReportByOrderFromCommodityOrCategory erp_purchase_inventory err:", logger.Field("err", err)) return resp, err } // 创建一个 map,用于存储不同的 ErpCommodityId commodityMap := make(map[uint32]uint32) // 遍历 inventoryList,将不同的 ErpCommodityId 存入 map 中 for _, inventory := range inventoryList { //commodityMap[inventory.ErpCommodityId] = inventory.ErpPurchaseOrderId commodityMap[inventory.ErpPurchaseOrderId] = inventory.ErpCommodityId } // 计算不同的 ErpCommodityId 的数量 differentCommodityCount := len(commodityMap) var reportOrderDataList []ReportByOrderData var nTotalAmount float64 var nTotalCount int32 // 查询采购订单信息 for k, _ := range commodityMap { var purchaseOrder ErpPurchaseOrder var commodityList []ErpPurchaseCommodityData query := orm.Eloquent.Table("erp_purchase_order") if len(req.ErpSupplierId) > 0 { // 供应商复选 query = query.Where("erp_purchase_order.erp_supplier_id IN (?)", req.ErpSupplierId) } if len(req.StoreId) > 0 { // 门店复选 query = query.Where("erp_purchase_order.store_id IN (?)", req.StoreId) } err = query.Where("id = ?", k).Find(&purchaseOrder).Error if err != nil { return nil, err } var reportByOrderData ReportByOrderData reportByOrderData.ErpPurchaseOrderId = purchaseOrder.ID reportByOrderData.SerialNumber = purchaseOrder.SerialNumber reportByOrderData.PurchaseType = purchaseOrder.PurchaseType reportByOrderData.StoreId = purchaseOrder.StoreId reportByOrderData.StoreName = purchaseOrder.StoreName reportByOrderData.ErpSupplierId = purchaseOrder.ErpSupplierId reportByOrderData.ErpSupplierName = purchaseOrder.ErpSupplierName reportByOrderData.HandlerId = purchaseOrder.HandlerId reportByOrderData.HandlerName = purchaseOrder.HandlerName reportByOrderData.MakerTime = purchaseOrder.MakerTime reportByOrderData.MakerId = purchaseOrder.MakerId reportByOrderData.MakerName = purchaseOrder.MakerName reportByOrderData.AuditorId = purchaseOrder.AuditorId reportByOrderData.AuditTime = purchaseOrder.AuditTime reportByOrderData.AuditorName = purchaseOrder.AuditorName reportByOrderData.State = purchaseOrder.State reportByOrderData.Remark = purchaseOrder.Remark if purchaseOrder.MakerTime != nil && purchaseOrder.MakerTime.IsZero() { reportByOrderData.MakerTime = nil } else { reportByOrderData.MakerTime = purchaseOrder.MakerTime } if purchaseOrder.AuditTime != nil && purchaseOrder.AuditTime.IsZero() { reportByOrderData.AuditTime = nil } else { reportByOrderData.AuditTime = purchaseOrder.AuditTime } var nAmount float64 var nCount int32 // 使用 map 来组合相同 ErpCommodityId 的数据 commodityMapInfo := make(map[uint32]ErpPurchaseCommodityData) for _, inventory := range inventoryList { if inventory.ErpPurchaseOrderId == purchaseOrder.ID { var vCount int32 var vAmount float64 if inventory.PurchaseType == ErpRejectOrder { // 退货单金额和数量取负值 vAmount = -inventory.Amount vCount = -int32(inventory.Count) } else { vCount = int32(inventory.Count) vAmount = inventory.Amount } if existingData, ok := commodityMapInfo[inventory.ErpCommodityId]; ok { // 如果已经存在相同 ErpCommodityId 的数据,则累加 Amount 和 Count existingData.Amount += vAmount existingData.Count += vCount existingData.Price = existingData.Amount / float64(existingData.Count) commodityMapInfo[inventory.ErpCommodityId] = existingData } else { // 否则,创建新的数据 commodityMapInfo[inventory.ErpCommodityId] = ErpPurchaseCommodityData{ CommodityData: CommodityData{ ErpCommodityId: inventory.ErpCommodityId, ErpCommodityName: inventory.ErpCommodityName, ErpCategoryID: inventory.ErpCategoryID, ErpCategoryName: inventory.ErpCategoryName, }, Amount: vAmount, Price: inventory.ImplementationPrice, Count: vCount, } } // 累加总金额和总数量 nAmount += vAmount nCount += vCount } } // 将 map 中的值转换为 slice for _, data := range commodityMapInfo { commodityList = append(commodityList, data) } reportByOrderData.CommodityData = commodityList reportByOrderData.Amount = nAmount reportByOrderData.Count = nCount if nCount != 0 { reportByOrderData.Price = nAmount / float64(nCount) } reportOrderDataList = append(reportOrderDataList, reportByOrderData) nTotalAmount += nAmount nTotalCount += nCount } resp.Total = differentCommodityCount resp.List = reportOrderDataList resp.Amount = nTotalAmount resp.Count = nTotalCount if req.IsExport == 1 { filePath, err := reportByOrderExport(resp) if err != nil { return nil, err } resp = &ErpPurchaseReportByOrderResp{} resp.ExportUrl = filePath } return resp, nil } // 查询采购报表(按单): 先筛选采购订单表,再补充商品入库信息 func getReportByOrderFromCommon(req *ErpPurchaseReportByOrderReq, c *gin.Context) ( *ErpPurchaseReportByOrderResp, error) { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if len(storeList) > 0 { req.StoreId = CompareLists(storeList, req.StoreId) if len(req.StoreId) == 0 { // 没有匹配的数据,表示入参门店不是用户有权限的门店 return &ErpPurchaseReportByOrderResp{}, nil } } else { return nil, errors.New("用户未绑定门店") } } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpPurchaseReportByOrderResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Table("erp_purchase_order").Where("state != ?", ErpPurchaseOrderUnAudit) // 未审核订单不展示 if req.SerialNumber != "" { // 单据编号 qs = qs.Where("serial_number=?", req.SerialNumber) } if req.PurchaseType != "" { // 采购类型 qs = qs.Where("purchase_type=?", req.PurchaseType) } if len(req.ErpSupplierId) > 0 { // 供应商复选 var supplierIDs []uint32 for _, supplier := range req.ErpSupplierId { supplierIDs = append(supplierIDs, supplier) } qs = qs.Where("erp_supplier_id IN (?)", supplierIDs) } if len(req.StoreId) > 0 { // 门店复选 var storeIDs []uint32 for _, store := range req.StoreId { storeIDs = append(storeIDs, store) } qs = qs.Where("store_id IN (?)", storeIDs) } if req.HandlerId != 0 { // 经手人id qs = qs.Where("handler_id=?", req.HandlerId) } if req.State != 0 { // 订单状态 qs = qs.Where("state=?", req.State) } if req.AuditTimeStart != "" { // 审核开始时间 parse, err := time.Parse(QueryTimeFormat, req.AuditTimeStart) if err != nil { logger.Errorf("erpPurchaseOrderList err:", err) return nil, err } qs = qs.Where("audit_time > ?", parse) } if req.AuditTimeEnd != "" { // 审核结束时间 parse, err := time.Parse(QueryTimeFormat, req.AuditTimeEnd) if err != nil { logger.Errorf("erpPurchaseOrderList err:", err) return nil, err } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("audit_time < ?", parse) } var count int64 err := qs.Count(&count).Error if err != nil { logger.Error("getReportByOrderFromCommon count err:", logger.Field("err", err)) return resp, err } resp.Total = int(count) var orders []ErpPurchaseOrder if req.ErpCategoryID != 0 || req.ErpCommodityName != "" || req.IsExport == 1 { err = qs.Order("id DESC").Find(&orders).Error } else { err = qs.Order("id DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&orders).Error } if err != nil && err != RecordNotFound { logger.Error("getReportByOrderFromCommon erp_purchase_order err:", logger.Field("err", err)) return resp, err } var reportByOrderDataList []ReportByOrderData var nTotalAmount float64 var nTotalCount int32 for _, v := range orders { var reportByOrderData ReportByOrderData var nAmount float64 var nCount int32 reportByOrderData.ErpPurchaseOrderId = v.ID reportByOrderData.SerialNumber = v.SerialNumber reportByOrderData.PurchaseType = v.PurchaseType reportByOrderData.StoreId = v.StoreId reportByOrderData.StoreName = v.StoreName reportByOrderData.ErpSupplierId = v.ErpSupplierId reportByOrderData.ErpSupplierName = v.ErpSupplierName reportByOrderData.HandlerId = v.HandlerId reportByOrderData.HandlerName = v.HandlerName //reportByOrderData.MakerTime = v.MakerTime reportByOrderData.MakerId = v.MakerId reportByOrderData.MakerName = v.MakerName reportByOrderData.AuditorId = v.AuditorId //reportByOrderData.AuditTime = v.AuditTime reportByOrderData.AuditorName = v.AuditorName reportByOrderData.State = v.State reportByOrderData.Remark = v.Remark if v.MakerTime != nil && v.MakerTime.IsZero() { reportByOrderData.MakerTime = nil } else { reportByOrderData.MakerTime = v.MakerTime } if v.AuditTime != nil && v.AuditTime.IsZero() { reportByOrderData.AuditTime = nil } else { reportByOrderData.AuditTime = v.AuditTime } reportByOrderData.CommodityData, nAmount, nCount, err = getOrderInventoryInfo(req, v.ID) if err != nil { return nil, err } if reportByOrderData.CommodityData == nil { continue } //if reportByOrderData.CommodityData == nil { // continue //} reportByOrderData.Amount = nAmount reportByOrderData.Count = nCount if nCount != 0 { reportByOrderData.Price = nAmount / float64(nCount) } reportByOrderDataList = append(reportByOrderDataList, reportByOrderData) nTotalAmount += nAmount nTotalCount += nCount } if req.ErpCategoryID != 0 || req.ErpCommodityName != "" { resp.Total = len(reportByOrderDataList) // 计算分页所需的切片索引 startIndex := page * req.PageSize endIndex := (page + 1) * req.PageSize if endIndex > len(reportByOrderDataList) { endIndex = len(reportByOrderDataList) } resp.List = reportByOrderDataList[startIndex:endIndex] } else { resp.List = reportByOrderDataList } resp.Amount = nTotalAmount resp.Count = nTotalCount //resp.Total = int(count) if req.IsExport == 1 { filePath, err := reportByOrderExport(resp) if err != nil { return nil, err } resp = &ErpPurchaseReportByOrderResp{} resp.ExportUrl = filePath } return resp, nil } // 查询采购订单的入库信息 func getOrderInventoryInfo(req *ErpPurchaseReportByOrderReq, erpPurchaseOrderId uint32) ([]ErpPurchaseCommodityData, float64, int32, error) { var inventoryList []ErpPurchaseInventory err := orm.Eloquent.Table("erp_purchase_inventory"). Where("erp_purchase_order_id = ?", erpPurchaseOrderId). Find(&inventoryList).Error if err != nil { logger.Error("getOrderInventoryInfo err:", logger.Field("err", err)) return nil, 0, 0, err } if len(inventoryList) == 0 { //return nil, 0, 0, errors.New("未查询到采购订单的入库信息") return nil, 0, 0, nil } // 使用 map 来组合相同 ErpCommodityId 的数据 commodityMap := make(map[uint32]ErpPurchaseCommodityData) var nAmount float64 var nCount int32 // 遍历库存列表,组合相同 ErpCommodityId 的数据 for _, v := range inventoryList { var vCount int32 var vAmount float64 // 商品名称和分类筛选 if req.ErpCommodityName != "" && v.ErpCommodityName != req.ErpCommodityName { continue } if req.ErpCategoryID != 0 && v.ErpCategoryID != req.ErpCategoryID { continue } if v.PurchaseType == ErpRejectOrder { // 退货单金额和数量取负值 vAmount = -v.Amount vCount = -int32(v.Count) } else { vCount = int32(v.Count) vAmount = v.Amount } if existingData, ok := commodityMap[v.ErpCommodityId]; ok { // 如果已经存在相同 ErpCommodityId 的数据,则累加 Amount 和 Count existingData.Amount += vAmount existingData.Count += vCount existingData.Price = existingData.Amount / float64(existingData.Count) commodityMap[v.ErpCommodityId] = existingData } else { // 否则,创建新的数据 commodityMap[v.ErpCommodityId] = ErpPurchaseCommodityData{ CommodityData: CommodityData{ ErpCommodityId: v.ErpCommodityId, ErpCommodityName: v.ErpCommodityName, ErpCategoryID: v.ErpCategoryID, ErpCategoryName: v.ErpCategoryName, }, Amount: vAmount, Price: v.ImplementationPrice, Count: vCount, } } // 累加总金额和总数量 nAmount += vAmount nCount += vCount } // 将 map 中的值转换为 slice var resp []ErpPurchaseCommodityData for _, data := range commodityMap { resp = append(resp, data) } return resp, nAmount, nCount, nil } // 导出采购报表(按单)excel func reportByOrderExport(req *ErpPurchaseReportByOrderResp) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := ExportUrl fileName := time.Now().Format(TimeFormat) + "采购报表(按单)" + ".xlsx" fmt.Println("url fileName:", url+fileName) // 组合标题栏数据 title := []interface{}{"单据编号", "类型", "店铺名称", "备注", "供应商", "经手人", "制单人", "审核时间", "订单状态", "商品名称", "商品分类", "已执行金额", "执行单价", "已执行数量"} for i, _ := range title { cell, _ := excelize.CoordinatesToCellName(1+i, 1) err := file.SetCellValue(fSheet, cell, title[i]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } var row1 []interface{} var row2 []interface{} nExcelStartRow := 0 for _, orderData := range req.List { var orderType string switch orderData.PurchaseType { case "procure": orderType = "采购入库" case "reject": orderType = "采购退货" } var orderStatus string switch orderData.State { // 1-待审核 2-待入库 3-待退货 4-已完成 5-已终止 case ErpPurchaseOrderUnAudit: orderStatus = "待审核" case ErpPurchaseOrderWaitInventory: orderStatus = "待入库" case ErpPurchaseOrderWaitReject: orderStatus = "待退货" case ErpPurchaseOrderFinished: orderStatus = "已完成" case ErpPurchaseOrderEnd: orderStatus = "已终止" } var strTime string if orderData.AuditTime != nil { strTime = orderData.AuditTime.Format(TimeFormat) } else { strTime = "--" } row1 = []interface{}{ orderData.SerialNumber, // 单据编号 orderType, // 类型 orderData.StoreName, // 店铺名称 orderData.Remark, // 备注 orderData.ErpSupplierName, // 供应商 orderData.HandlerName, // 经手人 orderData.MakerName, // 制单人 strTime, // 审核时间 orderStatus, // 订单状态 "--", // 商品名称 "--", // 商品分类 orderData.Amount, // 已执行金额 orderData.Price, // 执行单价 orderData.Count, // 已执行数量 } for j, _ := range row1 { cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, row1[j]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } nExcelStartRow++ for _, commodity := range orderData.CommodityData { row2 = []interface{}{ "", // 单据编号 "", // 类型 "", // 店铺名称 "", // 备注 "", // 供应商 "", // 经手人 "", // 制单人 "", // 审核时间 "", // 订单状态 commodity.ErpCommodityName, // 商品名称 commodity.ErpCategoryName, // 商品分类 commodity.Amount, // 已执行金额 commodity.Price, // 执行单价 commodity.Count, // 已执行数量 } for j, _ := range row2 { cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, row2[j]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } nExcelStartRow++ } } totalData := "订单数:" + strconv.FormatInt(int64(req.Total), 10) end := []interface{}{totalData, "", "", "", "", "", "", "", "", "", "", req.Amount, "--", req.Count} for i, _ := range end { cell, _ := excelize.CoordinatesToCellName(1+i, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, end[i]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } // 设置所有单元格的样式: 居中、加边框 style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, "border":[{"type":"left","color":"000000","style":1}, {"type":"top","color":"000000","style":1}, {"type":"right","color":"000000","style":1}, {"type":"bottom","color":"000000","style":1}]}`) //设置单元格高度 file.SetRowHeight("Sheet1", 1, 20) // 设置单元格大小 file.SetColWidth("Sheet1", "A", "A", 13) file.SetColWidth("Sheet1", "C", "C", 25) file.SetColWidth("Sheet1", "H", "H", 20) file.SetColWidth("Sheet1", "J", "J", 18) endRow := fmt.Sprintf("N"+"%d", nExcelStartRow+2) // 应用样式到整个表格 _ = file.SetCellStyle("Sheet1", "A1", endRow, style) fmt.Println("save fileName:", config.ExportConfig.Path+fileName) if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { fmt.Println(err) } return url + fileName, nil } // GetReportByCommodity 查询采购报表(按商品) func GetReportByCommodity(req *ErpPurchaseReportByCommodityReq, c *gin.Context) (*ErpPurchaseReportByCommodityResp, error) { var err error resp := new(ErpPurchaseReportByCommodityResp) //if req.ErpCommodityName != "" || req.ErpCategoryID != 0 { // 商品名称、商品分类不为空 // // 先筛选商品入库信息表,然后再补充采购订单信息 // resp, err = getReportByCommodityFromCommodityOrCategory(req, c) //} else { // // 先筛选采购订单表,再补充商品入库信息 // resp, err = getReportByCommodityFromCommon(req, c) //} resp, err = getReportByCommodityFromCommon(req, c) if err != nil { return nil, err } return resp, nil } // 查询采购报表(按商品):先筛选商品入库信息表,然后再补充采购订单信息 func getReportByCommodityFromCommodityOrCategory(req *ErpPurchaseReportByCommodityReq, c *gin.Context) ( *ErpPurchaseReportByCommodityResp, error) { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if len(storeList) > 0 { req.StoreId = CompareLists(storeList, req.StoreId) if len(req.StoreId) == 0 { // 没有匹配的数据,表示入参门店不是用户有权限的门店 return &ErpPurchaseReportByCommodityResp{}, nil } } else { return nil, errors.New("用户未绑定门店") } } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpPurchaseReportByCommodityResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Table("erp_purchase_inventory") if req.ErpCommodityName != "" { qs = qs.Where("erp_commodity_name=?", req.ErpCommodityName) } if req.ErpCategoryID != 0 { qs = qs.Where("erp_category_id=?", req.ErpCategoryID) } var count int64 err := qs.Count(&count).Error if err != nil { logger.Error("getReportByOrderFromCommodityOrCategory count err:", logger.Field("err", err)) return resp, err } var inventoryList []ErpPurchaseInventory err = qs.Order("id DESC").Find(&inventoryList).Error if err != nil && err != RecordNotFound { logger.Error("getReportByOrderFromCommodityOrCategory erp_purchase_inventory err:", logger.Field("err", err)) return resp, err } commodityIDMap := make(map[uint32]map[uint32]ErpPurchaseInventory) // 遍历 inventoryList for _, inventory := range inventoryList { // 如果该商品id已经在map中,则将该订单id加入到对应的set中 if ids, ok := commodityIDMap[inventory.ErpCommodityId]; ok { ids[inventory.ErpPurchaseOrderId] = inventory } else { // 如果该商品id不在map中,则创建一个新的map并加入订单id及其对应的 ErpPurchaseInventory commodityIDMap[inventory.ErpCommodityId] = map[uint32]ErpPurchaseInventory{ inventory.ErpPurchaseOrderId: inventory, } } } resp.Total = len(commodityIDMap) var reportByCommodityDataList []ReportByCommodityData var totalData PurchaseData // 遍历 map 获取去重的 ErpPurchaseOrderId for _, orderIdSet := range commodityIDMap { var reportByCommodityData ReportByCommodityData if len(orderIdSet) != 0 { for _, item := range orderIdSet { reportByCommodityData.ErpCommodityId = item.ErpCommodityId reportByCommodityData.ErpCommodityName = item.ErpCommodityName reportByCommodityData.ErpCategoryID = item.ErpCategoryID reportByCommodityData.ErpCategoryName = item.ErpCategoryName break } } var totalPurchaseData PurchaseData var purchaseOrderDataList []ErpCommodityPurchaseOrderData for orderId := range orderIdSet { purchaseOrderData, err := getPurchaseOrderData(orderId, req) if err != nil { return nil, err } purchaseOrderDataList = append(purchaseOrderDataList, purchaseOrderData) totalPurchaseData.PlanCount += purchaseOrderData.PlanCount totalPurchaseData.PlanPrice += purchaseOrderData.PlanPrice totalPurchaseData.PlanAmount += purchaseOrderData.PlanAmount totalPurchaseData.Amount += purchaseOrderData.Amount totalPurchaseData.Price += purchaseOrderData.Price totalPurchaseData.Count += purchaseOrderData.Count totalPurchaseData.NonExecutionAmount += purchaseOrderData.NonExecutionAmount totalPurchaseData.NonExecutionCount += purchaseOrderData.NonExecutionCount } reportByCommodityData.PlanCount = totalPurchaseData.PlanCount reportByCommodityData.PlanPrice = totalPurchaseData.PlanPrice reportByCommodityData.PlanAmount = totalPurchaseData.PlanAmount reportByCommodityData.Amount = totalPurchaseData.Amount reportByCommodityData.Price = totalPurchaseData.Price reportByCommodityData.Count = totalPurchaseData.Count reportByCommodityData.NonExecutionAmount = totalPurchaseData.NonExecutionAmount reportByCommodityData.NonExecutionCount = totalPurchaseData.NonExecutionCount totalData.PlanCount += reportByCommodityData.PlanCount totalData.PlanAmount += reportByCommodityData.PlanAmount totalData.Amount += reportByCommodityData.Amount totalData.Count += reportByCommodityData.Count totalData.NonExecutionAmount += reportByCommodityData.NonExecutionAmount totalData.NonExecutionCount += reportByCommodityData.NonExecutionCount reportByCommodityData.OrderInfo = purchaseOrderDataList reportByCommodityDataList = append(reportByCommodityDataList, reportByCommodityData) } sortByCommodityIDDesc(reportByCommodityDataList) resp.Total = len(reportByCommodityDataList) // 计算分页所需的切片索引 startIndex := page * req.PageSize endIndex := (page + 1) * req.PageSize if endIndex > len(reportByCommodityDataList) { endIndex = len(reportByCommodityDataList) } resp.List = reportByCommodityDataList[startIndex:endIndex] resp.PlanCount = totalData.PlanCount resp.PlanAmount = totalData.PlanAmount resp.Amount = totalData.Amount resp.Count = totalData.Count resp.NonExecutionAmount = totalData.NonExecutionAmount resp.NonExecutionCount = totalData.NonExecutionCount if req.IsExport == 1 { filePath, err := reportByCommodityExport(resp) if err != nil { return nil, err } resp = &ErpPurchaseReportByCommodityResp{} resp.ExportUrl = filePath } return resp, nil } // 查询采购订单信息 func getPurchaseOrderData(orderID uint32, req *ErpPurchaseReportByCommodityReq) (ErpCommodityPurchaseOrderData, error) { var purchaseOrderData ErpCommodityPurchaseOrderData // 查询采购订单信息 var purchaseOrder ErpPurchaseOrder qs := orm.Eloquent.Table("erp_purchase_order") if len(req.ErpSupplierId) > 0 { // 供应商复选 qs = qs.Where("erp_supplier_id IN (?)", req.ErpSupplierId) } if len(req.StoreId) > 0 { // 门店复选 qs = qs.Where("store_id IN (?)", req.StoreId) } err := qs.Where("id = ?", orderID).Find(&purchaseOrder).Error if err != nil { return purchaseOrderData, err } // 查询采购订单的计划和执行信息 purchaseData, err := getPurchaseData(purchaseOrder.ID) if err != nil { return purchaseOrderData, err } // 组合数据 purchaseOrderData.ErpPurchaseOrderId = purchaseOrder.ID purchaseOrderData.SerialNumber = purchaseOrder.SerialNumber purchaseOrderData.PurchaseType = purchaseOrder.PurchaseType purchaseOrderData.StoreId = purchaseOrder.StoreId purchaseOrderData.StoreName = purchaseOrder.StoreName purchaseOrderData.ErpSupplierId = purchaseOrder.ErpSupplierId purchaseOrderData.ErpSupplierName = purchaseOrder.ErpSupplierName purchaseOrderData.HandlerId = purchaseOrder.HandlerId purchaseOrderData.HandlerName = purchaseOrder.HandlerName //purchaseOrderData.MakerTime = purchaseOrder.MakerTime purchaseOrderData.MakerId = purchaseOrder.MakerId purchaseOrderData.MakerName = purchaseOrder.MakerName purchaseOrderData.AuditorId = purchaseOrder.AuditorId //purchaseOrderData.AuditTime = purchaseOrder.AuditTime purchaseOrderData.AuditorName = purchaseOrder.AuditorName if purchaseOrder.MakerTime != nil && purchaseOrder.MakerTime.IsZero() { purchaseOrderData.MakerTime = nil } else { purchaseOrderData.MakerTime = purchaseOrder.MakerTime } if purchaseOrder.AuditTime != nil && purchaseOrder.AuditTime.IsZero() { purchaseOrderData.AuditTime = nil } else { purchaseOrderData.AuditTime = purchaseOrder.AuditTime } purchaseOrderData.PlanCount = purchaseData.PlanCount purchaseOrderData.PlanPrice = purchaseData.PlanPrice purchaseOrderData.PlanAmount = purchaseData.PlanAmount purchaseOrderData.Amount = purchaseData.Amount purchaseOrderData.Price = purchaseData.Price purchaseOrderData.Count = purchaseData.Count purchaseOrderData.NonExecutionAmount = purchaseData.NonExecutionAmount purchaseOrderData.NonExecutionCount = purchaseData.NonExecutionCount if purchaseOrder.PurchaseType == ErpRejectOrder { // 退货单 purchaseOrderData.PlanCount = -purchaseOrderData.PlanCount purchaseOrderData.PlanAmount = -purchaseOrderData.PlanAmount purchaseOrderData.Amount = -purchaseOrderData.Amount purchaseOrderData.Count = -purchaseOrderData.Count purchaseOrderData.NonExecutionAmount = -purchaseOrderData.NonExecutionAmount purchaseOrderData.NonExecutionCount = -purchaseOrderData.NonExecutionCount } return purchaseOrderData, nil } // getPurchaseData 根据 ErpPurchaseCommodity 表查询采购数据 func getPurchaseData(erpPurchaseOrderId uint32) (PurchaseData, error) { var purchaseData PurchaseData err := orm.Eloquent.Raw(` SELECT SUM(pc.count) AS plan_count, AVG(pc.price) AS plan_price, SUM(pc.amount) AS plan_amount, SUM(pi.count) AS count, AVG(pi.implementation_price) AS price, SUM(pi.amount) AS amount FROM erp_purchase_commodity pc JOIN erp_purchase_inventory pi ON pc.erp_purchase_order_id = pi.erp_purchase_order_id WHERE pc.erp_purchase_order_id = ? GROUP BY pc.erp_purchase_order_id `, erpPurchaseOrderId).Scan(&purchaseData).Error if err != nil { logger.Error("getPurchaseData err:", logger.Field("err", err)) return purchaseData, err } // 计算未执行金额和未执行数量 purchaseData.NonExecutionAmount = purchaseData.PlanAmount - purchaseData.Amount purchaseData.NonExecutionCount = purchaseData.PlanCount - purchaseData.Count return purchaseData, nil } // 查询采购报表(按商品):先筛选采购订单表,再补充商品入库信息 func getReportByCommodityFromCommon(req *ErpPurchaseReportByCommodityReq, c *gin.Context) ( *ErpPurchaseReportByCommodityResp, error) { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if len(storeList) > 0 { req.StoreId = CompareLists(storeList, req.StoreId) if len(req.StoreId) == 0 { // 没有匹配的数据,表示入参门店不是用户有权限的门店 return &ErpPurchaseReportByCommodityResp{}, nil } } else { return nil, errors.New("用户未绑定门店") } } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpPurchaseReportByCommodityResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Table("erp_purchase_order").Where("state != ?", ErpPurchaseOrderUnAudit) if req.SerialNumber != "" { // 单据编号 qs = qs.Where("serial_number=?", req.SerialNumber) } if req.PurchaseType != "" { // 采购类型 qs = qs.Where("purchase_type=?", req.PurchaseType) } if len(req.ErpSupplierId) > 0 { // 供应商复选 qs = qs.Where("erp_supplier_id IN (?)", req.ErpSupplierId) } if len(req.StoreId) > 0 { // 门店复选 qs = qs.Where("store_id IN (?)", req.StoreId) } if req.HandlerId != 0 { // 经手人id qs = qs.Where("handler_id=?", req.HandlerId) } if req.State != 0 { // 订单状态 qs = qs.Where("state=?", req.State) } if req.AuditTimeStart != "" { // 审核开始时间 parse, err := time.Parse(QueryTimeFormat, req.AuditTimeStart) if err != nil { logger.Errorf("erpPurchaseOrderList err:", err) return nil, err } qs = qs.Where("audit_time > ?", parse) } if req.AuditTimeEnd != "" { // 审核结束时间 parse, err := time.Parse(QueryTimeFormat, req.AuditTimeEnd) if err != nil { logger.Errorf("erpPurchaseOrderList err:", err) return nil, err } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("audit_time < ?", parse) } var count int64 err := qs.Count(&count).Error if err != nil { logger.Error("getReportByOrderFromCommon count err:", logger.Field("err", err)) return resp, err } var orders []ErpPurchaseOrder err = qs.Order("id DESC").Find(&orders).Error if err != nil && err != RecordNotFound { logger.Error("getReportByOrderFromCommon erp_purchase_order err:", logger.Field("err", err)) return resp, err } // 查询所有商品id及其对应的orderId commodityAndOrderIdMap, err := getAllOrderCommodity(page, req.PageSize, int(req.IsExport)) if err != nil { return nil, err } tempDataMap := make(map[uint32][]TempData) //// 补充所有采购订单的商品信息和采购执行数量 //for _, v := range orders { // commodityIds := findCommodityIDsByOrderID(v.ID, commodityAndOrderIdMap) // // 获取每个采购订单的每个商品的采购和执行信息 // for _, commodityId := range commodityIds { // purchaseOrderData, commodityData, err := getPurchaseOrderAndCommodityData(v.ID, commodityId) // if err != nil { // return nil, err // } // // tempData := TempData{ // CommodityData: commodityData, // ErpCommodityPurchaseOrderData: purchaseOrderData, // } // tempDataMap[commodityId] = append(tempDataMap[commodityId], tempData) // } //} var wg sync.WaitGroup ch := make(chan TempData, len(orders)*len(commodityAndOrderIdMap)) // 并发查询每个采购订单的每个商品的采购和执行信息 for _, v := range orders { commodityIds := findCommodityIDsByOrderID(v.ID, commodityAndOrderIdMap) for _, commodityID := range commodityIds { wg.Add(1) go getPurchaseOrderAndCommodityDataAsync(v.ID, commodityID, ch, &wg) } } wg.Wait() close(ch) for data := range ch { tempDataMap[data.ErpCommodityId] = append(tempDataMap[data.ErpCommodityId], data) } var dataList []ReportByCommodityData var totalData PurchaseData // tempDataList,按照商品id进行归类 for _, tempDataList := range tempDataMap { var reportData ReportByCommodityData for _, v := range tempDataList { // 商品名称和分类筛选 if req.ErpCommodityName != "" && v.ErpCommodityName != req.ErpCommodityName { continue } if req.ErpCategoryID != 0 && v.ErpCategoryID != req.ErpCategoryID { continue } reportData.ErpCategoryID = v.ErpCategoryID reportData.ErpCategoryName = v.ErpCategoryName reportData.ErpCommodityId = v.ErpCommodityId reportData.ErpCommodityName = v.ErpCommodityName reportData.PlanCount += v.PlanCount reportData.PlanAmount += v.PlanAmount reportData.Amount += v.Amount reportData.Count += v.Count reportData.NonExecutionAmount += v.NonExecutionAmount reportData.NonExecutionCount += v.NonExecutionCount if reportData.PlanCount != 0 { reportData.PlanPrice = reportData.PlanAmount / float64(reportData.PlanCount) reportData.PlanPrice = math.Round(reportData.PlanPrice*100) / 100 } if reportData.Count != 0 { reportData.Price = reportData.Amount / float64(reportData.Count) reportData.Price = math.Round(reportData.Price*100) / 100 } purchaseOrderData := ErpCommodityPurchaseOrderData{ CommonData{ ErpPurchaseOrderId: v.ErpPurchaseOrderId, SerialNumber: v.SerialNumber, PurchaseType: v.PurchaseType, StoreId: v.StoreId, StoreName: v.StoreName, ErpSupplierId: v.ErpSupplierId, ErpSupplierName: v.ErpSupplierName, HandlerId: v.HandlerId, HandlerName: v.HandlerName, //MakerTime: v.MakerTime, MakerId: v.MakerId, MakerName: v.MakerName, AuditorId: v.AuditorId, //AuditTime: v.AuditTime, AuditorName: v.AuditorName, }, PurchaseData{ PlanCount: v.PlanCount, PlanPrice: v.PlanPrice, PlanAmount: v.PlanAmount, Amount: v.Amount, Price: v.Price, Count: v.Count, NonExecutionAmount: v.NonExecutionAmount, NonExecutionCount: v.NonExecutionCount, }, } if v.PurchaseType == ErpRejectOrder { // 退货单 reportData.PlanCount = -reportData.PlanCount reportData.PlanAmount = -reportData.PlanAmount reportData.Amount = -reportData.Amount reportData.Count = -reportData.Count reportData.NonExecutionAmount = -reportData.NonExecutionAmount reportData.NonExecutionCount = -reportData.NonExecutionCount purchaseOrderData.PlanCount = -purchaseOrderData.PlanCount purchaseOrderData.PlanAmount = -purchaseOrderData.PlanAmount purchaseOrderData.Amount = -purchaseOrderData.Amount purchaseOrderData.Count = -purchaseOrderData.Count purchaseOrderData.NonExecutionAmount = -purchaseOrderData.NonExecutionAmount purchaseOrderData.NonExecutionCount = -purchaseOrderData.NonExecutionCount } if v.MakerTime != nil && v.MakerTime.IsZero() { purchaseOrderData.MakerTime = nil } else { purchaseOrderData.MakerTime = v.MakerTime } if v.AuditTime != nil && v.AuditTime.IsZero() { purchaseOrderData.AuditTime = nil } else { purchaseOrderData.AuditTime = v.AuditTime } reportData.OrderInfo = append(reportData.OrderInfo, purchaseOrderData) } if reportData.ErpCategoryID == 0 || len(reportData.OrderInfo) == 0 { continue } dataList = append(dataList, reportData) totalData.PlanCount += reportData.PlanCount totalData.PlanAmount += reportData.PlanAmount totalData.Amount += reportData.Amount totalData.Count += reportData.Count totalData.NonExecutionAmount += reportData.NonExecutionAmount totalData.NonExecutionCount += reportData.NonExecutionCount } sortByCommodityIDDesc(dataList) resp.Total = len(dataList) // 计算分页所需的切片索引 startIndex := page * req.PageSize endIndex := (page + 1) * req.PageSize if endIndex > len(dataList) { endIndex = len(dataList) } resp.List = dataList[startIndex:endIndex] resp.PlanCount = totalData.PlanCount resp.PlanAmount = totalData.PlanAmount resp.Amount = totalData.Amount resp.Count = totalData.Count resp.NonExecutionAmount = totalData.NonExecutionAmount resp.NonExecutionCount = totalData.NonExecutionCount if req.IsExport == 1 { filePath, err := reportByCommodityExport(resp) if err != nil { return nil, err } resp = &ErpPurchaseReportByCommodityResp{} resp.ExportUrl = filePath } return resp, nil } func getPurchaseOrderAndCommodityDataAsync(orderID, commodityID uint32, ch chan<- TempData, wg *sync.WaitGroup) { defer wg.Done() purchaseOrderData, commodityData, err := getPurchaseOrderAndCommodityData(orderID, commodityID) if err != nil { logger.Errorf("getPurchaseOrderAndCommodityDataAsync err:", err) return } tempData := TempData{ CommodityData: commodityData, ErpCommodityPurchaseOrderData: purchaseOrderData, } ch <- tempData } // 按照商品ID进行倒序排序的 less 函数 func sortByCommodityIDDesc(dataList []ReportByCommodityData) { sort.Slice(dataList, func(i, j int) bool { return dataList[i].ErpCommodityId > dataList[j].ErpCommodityId }) } // 查询采购订单信息 func getPurchaseOrderAndCommodityData(orderID, commodityId uint32) (ErpCommodityPurchaseOrderData, CommodityData, error) { var purchaseOrderData ErpCommodityPurchaseOrderData // 查询采购订单信息 var purchaseOrder ErpPurchaseOrder err := orm.Eloquent.Table("erp_purchase_order").Where("id = ?", orderID).Find(&purchaseOrder).Error if err != nil { return ErpCommodityPurchaseOrderData{}, CommodityData{}, err } // 查询采购订单的计划和执行信息 purchaseData, commodityData, err := getSignalPurchaseData(purchaseOrder.ID, commodityId) if err != nil { return ErpCommodityPurchaseOrderData{}, CommodityData{}, err } // 组合数据 purchaseOrderData.ErpPurchaseOrderId = purchaseOrder.ID purchaseOrderData.SerialNumber = purchaseOrder.SerialNumber purchaseOrderData.PurchaseType = purchaseOrder.PurchaseType purchaseOrderData.StoreId = purchaseOrder.StoreId purchaseOrderData.StoreName = purchaseOrder.StoreName purchaseOrderData.ErpSupplierId = purchaseOrder.ErpSupplierId purchaseOrderData.ErpSupplierName = purchaseOrder.ErpSupplierName purchaseOrderData.HandlerId = purchaseOrder.HandlerId purchaseOrderData.HandlerName = purchaseOrder.HandlerName purchaseOrderData.MakerId = purchaseOrder.MakerId purchaseOrderData.MakerName = purchaseOrder.MakerName purchaseOrderData.AuditorId = purchaseOrder.AuditorId purchaseOrderData.AuditTime = purchaseOrder.AuditTime purchaseOrderData.AuditorName = purchaseOrder.AuditorName if purchaseOrder.MakerTime != nil && purchaseOrder.MakerTime.IsZero() { purchaseOrderData.MakerTime = nil } else { purchaseOrderData.MakerTime = purchaseOrder.MakerTime } if purchaseOrder.AuditTime != nil && purchaseOrder.AuditTime.IsZero() { purchaseOrderData.AuditTime = nil } else { purchaseOrderData.AuditTime = purchaseOrder.AuditTime } purchaseOrderData.PlanCount = purchaseData.PlanCount purchaseOrderData.PlanPrice = purchaseData.PlanPrice purchaseOrderData.PlanAmount = purchaseData.PlanAmount purchaseOrderData.Amount = purchaseData.Amount purchaseOrderData.Price = purchaseData.Price purchaseOrderData.Count = purchaseData.Count purchaseOrderData.NonExecutionAmount = purchaseData.NonExecutionAmount purchaseOrderData.NonExecutionCount = purchaseData.NonExecutionCount return purchaseOrderData, commodityData, nil } // getSignalPurchaseData 根据 ErpPurchaseCommodity 表查询采购数据 func getSignalPurchaseData(erpPurchaseOrderId, commodityId uint32) (PurchaseData, CommodityData, error) { var purchaseData PurchaseData var commodityData CommodityData //err := orm.Eloquent.Raw(` // SELECT // SUM(pc.count) AS plan_count, // AVG(pc.price) AS plan_price, // SUM(pc.amount) AS plan_amount, // SUM(pi.count) AS count, // AVG(pi.implementation_price) AS price, // SUM(pi.amount) AS amount // FROM // erp_purchase_commodity pc // JOIN // erp_purchase_inventory pi ON pc.erp_purchase_order_id = pi.erp_purchase_order_id // WHERE // pc.erp_purchase_order_id = ? and pc.erp_commodity_id = ? // GROUP BY // pc.erp_purchase_order_id //`, erpPurchaseOrderId, commodityId).Scan(&purchaseData).Error err := orm.Eloquent.Raw(` SELECT plan.plan_count AS plan_count, plan.plan_price AS plan_price, plan.plan_amount AS plan_amount, inventory.count AS count, inventory.price AS price, inventory.amount AS amount FROM ( SELECT SUM(pc.count) AS plan_count, AVG(pc.price) AS plan_price, SUM(pc.amount) AS plan_amount FROM erp_purchase_commodity pc WHERE pc.erp_purchase_order_id = ? AND pc.erp_commodity_id = ? GROUP BY pc.erp_purchase_order_id ) AS plan JOIN ( SELECT SUM(pi.count) AS count, AVG(pi.implementation_price) AS price, SUM(pi.amount) AS amount FROM erp_purchase_inventory pi WHERE pi.erp_purchase_order_id = ? AND pi.erp_commodity_id = ? GROUP BY pi.erp_purchase_order_id ) AS inventory ON 1 = 1 `, erpPurchaseOrderId, commodityId, erpPurchaseOrderId, commodityId).Scan(&purchaseData).Error if err != nil { logger.Error("getPurchaseData err:", logger.Field("err", err)) return PurchaseData{}, CommodityData{}, err } // 计算未执行金额和未执行数量 purchaseData.NonExecutionAmount = purchaseData.PlanAmount - purchaseData.Amount purchaseData.NonExecutionCount = purchaseData.PlanCount - purchaseData.Count // 查询订单对应的商品信息 commodityInfo, err := GetCommodity(commodityId) if err != nil { return PurchaseData{}, CommodityData{}, err } commodityData.ErpCommodityId = commodityInfo.ID commodityData.ErpCommodityName = commodityInfo.Name commodityData.ErpCategoryID = commodityInfo.ErpCategoryId commodityData.ErpCategoryName = commodityInfo.ErpCategoryName return purchaseData, commodityData, nil } //// 查询所有商品信息 //func getAllOrderCommodity(pageIndex, pageSize int) (map[uint32][]uint32, error) { // qs := orm.Eloquent.Table("erp_purchase_commodity") // // var count int64 // err := qs.Count(&count).Error // if err != nil { // logger.Error("getAllOrderCommodity count err:", logger.Field("err", err)) // return nil, err // } // // var commodityList []ErpPurchaseCommodity // err = qs.Order("id DESC").Offset(pageIndex * pageSize).Limit(pageSize).Find(&commodityList).Error // if err != nil && err != RecordNotFound { // logger.Error("getAllOrderCommodity erp_purchase_commodity err:", logger.Field("err", err)) // return nil, err // } // // commodityMap := make(map[uint32][]uint32) // for _, v := range commodityList { // if !contains(commodityMap[v.ErpCommodityId], v.ErpPurchaseOrderId) { // commodityMap[v.ErpCommodityId] = append(commodityMap[v.ErpCommodityId], v.ErpPurchaseOrderId) // } // } // // return commodityMap, nil //} // //func contains(slice []uint32, item uint32) bool { // for _, value := range slice { // if value == item { // return true // } // } // return false //} // 查询所有商品信息,每页展示不同商品ID及其对应的订单ID func getAllOrderCommodity(pageIndex, pageSize, isExport int) (map[uint32][]uint32, error) { var commodityMap = make(map[uint32][]uint32) var query string // if isExport == 1 { //导出excel // // 执行原生 SQL 查询,联结表格,按照每个商品ID列出所有的订单ID // query = fmt.Sprintf(` // SELECT erp_commodity_id, GROUP_CONCAT(erp_purchase_order_id) AS order_ids FROM erp_purchase_commodity //GROUP BY erp_commodity_id`) // } else { // // 执行原生 SQL 查询,联结表格,按照每个商品ID列出所有的订单ID // query = fmt.Sprintf(` // SELECT erp_commodity_id, GROUP_CONCAT(erp_purchase_order_id) AS order_ids FROM erp_purchase_commodity //GROUP BY erp_commodity_id // LIMIT %d OFFSET %d`, pageSize, pageIndex*pageSize) // } // 执行原生 SQL 查询,联结表格,按照每个商品ID列出所有的订单ID query = fmt.Sprintf(` SELECT erp_commodity_id, GROUP_CONCAT(erp_purchase_order_id) AS order_ids FROM erp_purchase_commodity GROUP BY erp_commodity_id`) rows, err := orm.Eloquent.Raw(query).Rows() if err != nil { logger.Error("getAllOrderCommodity erp_purchase_commodity err:", logger.Field("err", err)) return nil, err } defer rows.Close() for rows.Next() { var commodityID uint32 var orderIDs string if err := rows.Scan(&commodityID, &orderIDs); err != nil { logger.Error("getAllOrderCommodity rows scan err:", logger.Field("err", err)) return nil, err } // 将 orderIDs 字符串拆分为字符串数组 orderIDStrList := strings.Split(orderIDs, ",") // 去重并转换为 uint32 数组 var orderIDList []uint32 seen := make(map[uint32]bool) for _, idStr := range orderIDStrList { id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { logger.Error("getAllOrderCommodity parse orderID err:", logger.Field("err", err)) return nil, err } // 如果该订单ID未被添加过,则添加到列表中 if _, ok := seen[uint32(id)]; !ok { orderIDList = append(orderIDList, uint32(id)) seen[uint32(id)] = true } } // 添加到 map 中 commodityMap[commodityID] = orderIDList } return commodityMap, nil } func findCommodityIDsByOrderID(orderID uint32, commodityMap map[uint32][]uint32) []uint32 { var commodityIDs []uint32 for commodityID, orderIDs := range commodityMap { for _, id := range orderIDs { if id == orderID { commodityIDs = append(commodityIDs, commodityID) break } } } return commodityIDs } // 导出采购报表(按商品)excel func reportByCommodityExport(req *ErpPurchaseReportByCommodityResp) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := ExportUrl fileName := time.Now().Format(TimeFormat) + "采购报表(按商品)" + ".xlsx" fmt.Println("url fileName:", url+fileName) // 组合标题栏数据 title1 := []interface{}{"商品名称", "商品分类", "单据编号", "类型", "店铺名称", "供应商", "经手人", "制单人", "审核时间", "计划采购", "", "", "已执行", "", "", "未执行", ""} title2 := []interface{}{"商品名称", "商品分类", "单据编号", "类型", "店铺名称", "供应商", "经手人", "制单人", "审核时间", "采购金额", "采购单价", "采购数量", "已执行金额", "执行单价", "已执行数量", "未执行金额", "未执行数量"} for i, _ := range title1 { cell, _ := excelize.CoordinatesToCellName(1+i, 1) err := file.SetCellValue(fSheet, cell, title1[i]) if err != nil { logger.Errorf("file set value err:", err) } } for i, _ := range title2 { cell, _ := excelize.CoordinatesToCellName(1+i, 2) err := file.SetCellValue(fSheet, cell, title2[i]) if err != nil { logger.Errorf("file set value err:", err) } } var row1 []interface{} var row2 []interface{} nExcelStartRow := 0 for _, commodityData := range req.List { row1 = []interface{}{ commodityData.ErpCommodityName, // 商品名称 commodityData.ErpCategoryName, // 商品分类 "", // 单据编号 "", // 类型 "", // 店铺名称 "", // 供应商 "", // 经手人 "", // 制单人 "", // 审核时间 commodityData.PlanAmount, // 计划采购金额 commodityData.PlanPrice, // 计划采购单价 commodityData.PlanCount, // 计划采购数量 commodityData.Amount, // 已执行金额 commodityData.Price, // 已执行单价 commodityData.Count, // 已执行数量 commodityData.NonExecutionAmount, // 未执行金额 commodityData.NonExecutionCount, // 未执行数量 } for j, _ := range row1 { cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+3) err := file.SetCellValue(fSheet, cell, row1[j]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } nExcelStartRow++ for _, orderData := range commodityData.OrderInfo { var orderType string switch orderData.PurchaseType { case "procure": orderType = "采购入库" case "reject": orderType = "采购退货" } var strTime string if orderData.AuditTime != nil { strTime = orderData.AuditTime.Format(TimeFormat) } else { strTime = "--" } row2 = []interface{}{ "", // 商品名称 "", // 商品分类 orderData.SerialNumber, // 单据编号 orderType, // 类型 orderData.StoreName, // 店铺名称 orderData.ErpSupplierName, // 供应商 orderData.HandlerName, // 经手人 orderData.MakerName, // 制单人 strTime, // 审核时间 orderData.PlanAmount, // 计划采购金额 orderData.PlanPrice, // 计划采购单价 orderData.PlanCount, // 计划采购数量 orderData.Amount, // 已执行金额 orderData.Price, // 已执行单价 orderData.Count, // 已执行数量 orderData.NonExecutionAmount, // 未执行金额 orderData.NonExecutionCount, // 未执行数量 } for j, _ := range row2 { cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+3) err := file.SetCellValue(fSheet, cell, row2[j]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } nExcelStartRow++ } } totalData := "订单数:" + strconv.FormatInt(int64(req.Total), 10) end := []interface{}{totalData, "", "", "", "", "", "", "", "", req.PlanAmount, // 计划采购金额 "--", // 计划采购单价 req.PlanCount, // 计划采购数量 req.Amount, // 已执行金额 "--", // 已执行单价 req.Count, // 已执行数量 req.NonExecutionAmount, // 未执行金额 req.NonExecutionCount, // 未执行数量 } for i, _ := range end { cell, _ := excelize.CoordinatesToCellName(1+i, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, end[i]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } // 设置所有单元格的样式: 居中、加边框 style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, "border":[{"type":"left","color":"000000","style":1}, {"type":"top","color":"000000","style":1}, {"type":"right","color":"000000","style":1}, {"type":"bottom","color":"000000","style":1}]}`) // 合并单元格 _ = file.MergeCell(fSheet, "A1", "A2") _ = file.MergeCell(fSheet, "B1", "B2") _ = file.MergeCell(fSheet, "C1", "C2") _ = file.MergeCell(fSheet, "D1", "D2") _ = file.MergeCell(fSheet, "E1", "E2") _ = file.MergeCell(fSheet, "F1", "F2") _ = file.MergeCell(fSheet, "G1", "G2") _ = file.MergeCell(fSheet, "H1", "H2") _ = file.MergeCell(fSheet, "I1", "I2") _ = file.MergeCell(fSheet, "J1", "L1") _ = file.MergeCell(fSheet, "M1", "O1") _ = file.MergeCell(fSheet, "P1", "Q1") //设置单元格高度 file.SetRowHeight("Sheet1", 1, 20) file.SetRowHeight("Sheet1", 2, 20) // 设置单元格大小 file.SetColWidth("Sheet1", "A", "A", 15) file.SetColWidth("Sheet1", "B", "B", 10) file.SetColWidth("Sheet1", "C", "C", 13) file.SetColWidth("Sheet1", "E", "E", 25) endRow := fmt.Sprintf("Q"+"%d", nExcelStartRow+2) // 应用样式到整个表格 _ = file.SetCellStyle("Sheet1", "A1", endRow, style) fmt.Println("save fileName:", config.ExportConfig.Path+fileName) if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { fmt.Println(err) } return url + fileName, nil } // GetReportBySupplier 查询供应商采购汇总 func GetReportBySupplier(req *ErpPurchaseReportBySupplierReq, c *gin.Context) ( *ErpPurchaseReportBySupplierResp, error) { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if len(storeList) > 0 { req.StoreList = CompareLists(storeList, req.StoreList) if len(req.StoreList) == 0 { // 没有匹配的数据,表示入参门店不是用户有权限的门店 return &ErpPurchaseReportBySupplierResp{}, nil } } else { return nil, errors.New("用户未绑定门店") } } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpPurchaseReportBySupplierResp{ PageIndex: page + 1, PageSize: req.PageSize, } // 构建查询条件 query := orm.Eloquent.Model(&ErpPurchaseInventory{}). Select("erp_purchase_inventory.erp_purchase_order_id, " + "erp_purchase_inventory.purchase_type, erp_purchase_inventory.serial_number, " + "erp_purchase_order.store_id, erp_purchase_order.store_name, erp_purchase_order.erp_supplier_id, " + "erp_purchase_order.erp_supplier_name, " + "erp_purchase_inventory.erp_commodity_id, erp_purchase_inventory.erp_commodity_name, " + "erp_purchase_inventory.erp_category_id, erp_purchase_inventory.erp_category_name, " + "SUM(erp_purchase_inventory.count) AS count, " + "SUM(erp_purchase_inventory.amount) AS amount"). Joins("JOIN erp_purchase_order ON erp_purchase_order.id = erp_purchase_inventory.erp_purchase_order_id"). Group("erp_purchase_inventory.erp_purchase_order_id, erp_purchase_inventory.erp_commodity_id, " + "erp_purchase_inventory.erp_commodity_name, erp_purchase_inventory.erp_category_id") countQuery := orm.Eloquent.Model(&ErpPurchaseInventory{}). Select("erp_purchase_inventory.erp_purchase_order_id, " + "erp_purchase_inventory.purchase_type, erp_purchase_inventory.serial_number, " + "erp_purchase_order.store_id, erp_purchase_order.store_name, erp_purchase_order.erp_supplier_id, " + "erp_purchase_order.erp_supplier_name, " + "erp_purchase_inventory.erp_commodity_id, erp_purchase_inventory.erp_commodity_name, " + "erp_purchase_inventory.erp_category_id, erp_purchase_inventory.erp_category_name, " + "SUM(erp_purchase_inventory.count) AS count, " + "SUM(erp_purchase_inventory.amount) AS amount"). Joins("JOIN erp_purchase_order ON erp_purchase_order.id = erp_purchase_inventory.erp_purchase_order_id"). Group("erp_purchase_inventory.erp_purchase_order_id, erp_purchase_inventory.erp_commodity_id, " + "erp_purchase_inventory.erp_commodity_name, erp_purchase_inventory.erp_category_id") // 根据请求参数过滤数据 if req.PurchaseType != "" { query = query.Where("erp_purchase_inventory.purchase_type = ?", req.PurchaseType) countQuery = countQuery.Where("erp_purchase_inventory.purchase_type = ?", req.PurchaseType) } if len(req.ErpCommodityName) > 0 { query = query.Where("erp_purchase_inventory.erp_commodity_name IN (?)", req.ErpCommodityName) countQuery = countQuery.Where("erp_purchase_inventory.erp_commodity_name IN (?)", req.ErpCommodityName) } if len(req.ErpCategoryID) > 0 { query = query.Where("erp_purchase_inventory.erp_category_id IN (?)", req.ErpCategoryID) countQuery = countQuery.Where("erp_purchase_inventory.erp_category_id IN (?)", req.ErpCategoryID) } if req.StartTime != "" { // 入/出库开始时间 parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("GetReportDetail err:", err) return nil, err } query = query.Where("erp_purchase_inventory.created_at >= ?", parse) countQuery = countQuery.Where("erp_purchase_inventory.created_at >= ?", parse) } if req.EndTime != "" { // 入/出库结束时间 parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("GetReportDetail err:", err) return nil, err } query = query.Where("erp_purchase_inventory.created_at <= ?", parse) countQuery = countQuery.Where("erp_purchase_inventory.created_at <= ?", parse) } if len(req.StoreList) > 0 { query = query.Where("erp_purchase_order.store_id IN (?)", req.StoreList) countQuery = countQuery.Where("erp_purchase_order.store_id IN (?)", req.StoreList) } //获取总数 var total int64 countQuery.Count(&total) resp.Total = int(total) var err error err = query.Find(&resp.List).Error if err != nil { return nil, err } var esData []PurchaseReportData if len(req.ErpSupplierId) > 0 { var purchaseOrder []ErpPurchaseOrder err = orm.Eloquent.Where("erp_purchase_order.erp_supplier_id IN (?)", req.ErpSupplierId).Find(&purchaseOrder).Error if err != nil { return nil, err } // 循环遍历采购订单 for _, po := range purchaseOrder { // 在resp.List中查找匹配的订单 for _, respItem := range resp.List { if po.ID == respItem.ErpPurchaseOrderId { // 如果找到匹配的订单,将其添加到esData中 data := PurchaseReportData{ ErpPurchaseOrderId: po.ID, StoreId: po.StoreId, StoreName: po.StoreName, PurchaseType: respItem.PurchaseType, ErpSupplierId: po.ErpSupplierId, ErpSupplierName: po.ErpSupplierName, ErpCommodityId: respItem.ErpCommodityId, ErpCommodityName: respItem.ErpCommodityName, ErpCategoryID: respItem.ErpCategoryID, ErpCategoryName: respItem.ErpCategoryName, Count: respItem.Count, Amount: respItem.Amount, RejectAmount: respItem.RejectAmount, Difference: respItem.Difference, } if data.PurchaseType == "reject" { //// 查询退货商品的采购金额 //var oldOrderInfo ErpPurchaseOrder //err = orm.Eloquent.Where("serial_number = ?", po.SerialNumber).First(&oldOrderInfo).Error //if err != nil { // return nil, err //} // 查找对应的采购入库记录 var resultInfo ResultPrice resultInfo, err = GetTotalWholesalePrice(respItem.ErpCommodityId, respItem.SerialNumber) if err != nil { return nil, err } data.Difference = -(resultInfo.TotalWholesalePrice - data.Amount) data.RejectAmount = -data.Amount data.Amount = -resultInfo.TotalWholesalePrice data.Count = -data.Count } esData = append(esData, data) } } } resp.List = esData } else { // 补充关联的供应商和店铺信息 for i := range resp.List { var purchaseOrderInfo ErpPurchaseOrder err = orm.Eloquent.Where("id = ?", resp.List[i].ErpPurchaseOrderId).First(&purchaseOrderInfo).Error if err != nil { return nil, err } resp.List[i].StoreId = purchaseOrderInfo.StoreId resp.List[i].StoreName = purchaseOrderInfo.StoreName resp.List[i].ErpSupplierId = purchaseOrderInfo.ErpSupplierId resp.List[i].ErpSupplierName = purchaseOrderInfo.ErpSupplierName if resp.List[i].PurchaseType == "reject" { //// 查询退货商品的采购金额 //var oldOrderInfo ErpPurchaseOrder //err = orm.Eloquent.Where("serial_number = ?", purchaseOrderInfo.SerialNumber).First(&oldOrderInfo).Error //if err != nil { // return nil, err //} // 查找对应的采购入库记录 var resultInfo ResultPrice resultInfo, err = GetTotalWholesalePrice(resp.List[i].ErpCommodityId, resp.List[i].SerialNumber) if err != nil { return nil, err } resp.List[i].Difference = -(resultInfo.TotalWholesalePrice - resp.List[i].Amount) resp.List[i].RejectAmount = -resp.List[i].Amount resp.List[i].Amount = -resultInfo.TotalWholesalePrice resp.List[i].Count = -resp.List[i].Count } } } mergedData := mergeData(resp.List) // 统计总计数量和金额 var totalAmount float64 var totalCount int32 var totalRejectAmount float64 var totalDifference float64 for _, item := range mergedData { totalAmount += item.Amount totalCount += item.Count totalRejectAmount += item.RejectAmount totalDifference += item.Difference } resp.Amount = totalAmount resp.Count = totalCount resp.RejectAmount = totalRejectAmount resp.Difference = totalDifference resp.Total = len(mergedData) // 排序规则:供应商编号小>商品编号小>店铺编号小>类型为采购入库 sort.Slice(mergedData, func(i, j int) bool { if mergedData[i].ErpSupplierId != mergedData[j].ErpSupplierId { return mergedData[i].ErpSupplierId < mergedData[j].ErpSupplierId } if mergedData[i].ErpCommodityId != mergedData[j].ErpCommodityId { return mergedData[i].ErpCommodityId < mergedData[j].ErpCommodityId } if mergedData[i].StoreId != mergedData[j].StoreId { return mergedData[i].StoreId < mergedData[j].StoreId } if mergedData[i].PurchaseType == ErpProcureOrder && mergedData[j].PurchaseType != ErpProcureOrder { return true } if mergedData[i].PurchaseType != ErpProcureOrder && mergedData[j].PurchaseType == ErpProcureOrder { return false } return false // 默认情况下相等 }) // 分页处理 startIdx := (resp.PageIndex - 1) * resp.PageSize endIdx := resp.PageIndex * resp.PageSize // 确保不超出索引范围 if startIdx >= len(mergedData) { resp.List = []PurchaseReportData{} } else if endIdx > len(mergedData) { resp.List = mergedData[startIdx:] } else { resp.List = mergedData[startIdx:endIdx] } if req.IsExport == 1 { filePath, err := reportBySupplierExport(resp) if err != nil { return nil, err } resp = &ErpPurchaseReportBySupplierResp{} resp.ExportUrl = filePath } return resp, nil } //func mergeData(esData []PurchaseReportData) []PurchaseReportData { // mergedMap := make(map[string]*PurchaseReportData) // // // 遍历 esData 数据 // for _, data := range esData { // key := fmt.Sprintf("%d_%d_%d_%s", data.StoreId, data.ErpSupplierId, data.ErpCommodityId, data.PurchaseType) // if _, ok := mergedMap[key]; !ok { // // 如果键不存在,创建新条目 // mergedMap[key] = &PurchaseReportData{ // StoreId: data.StoreId, // ErpSupplierId: data.ErpSupplierId, // ErpCommodityId: data.ErpCommodityId, // StoreName: data.StoreName, // PurchaseType: data.PurchaseType, // ErpSupplierName: data.ErpSupplierName, // ErpCommodityName: data.ErpCommodityName, // ErpCategoryID: data.ErpCategoryID, // ErpCategoryName: data.ErpCategoryName, // Count: data.Count, // Amount: data.Amount, // RejectAmount: data.RejectAmount, // Difference: data.Difference, // } // } else { // // 如果键已存在,进行累加操作 // mergedMap[key].Count += data.Count // mergedMap[key].Amount += data.Amount // mergedMap[key].RejectAmount += data.RejectAmount // mergedMap[key].Difference += data.Difference // } // } // // // 将合并后的数据转换为数组返回 // var mergedData []PurchaseReportData // for _, v := range mergedMap { // mergedData = append(mergedData, *v) // } // // return mergedData //} func mergeData(esData []PurchaseReportData) []PurchaseReportData { mergedMap := make(map[string]*PurchaseReportData) var keys []string // 用于记录键的顺序 // 遍历 esData 数据 for _, data := range esData { key := fmt.Sprintf("%d_%d_%d_%s", data.StoreId, data.ErpSupplierId, data.ErpCommodityId, data.PurchaseType) if _, ok := mergedMap[key]; !ok { // 如果键不存在,创建新条目 mergedMap[key] = &PurchaseReportData{ StoreId: data.StoreId, ErpSupplierId: data.ErpSupplierId, ErpCommodityId: data.ErpCommodityId, StoreName: data.StoreName, PurchaseType: data.PurchaseType, ErpSupplierName: data.ErpSupplierName, ErpCommodityName: data.ErpCommodityName, ErpCategoryID: data.ErpCategoryID, ErpCategoryName: data.ErpCategoryName, Count: data.Count, Amount: data.Amount, RejectAmount: data.RejectAmount, Difference: data.Difference, } keys = append(keys, key) // 将键添加到有序的键列表中 } else { // 如果键已存在,进行累加操作 mergedMap[key].Count += data.Count mergedMap[key].Amount += data.Amount mergedMap[key].RejectAmount += data.RejectAmount mergedMap[key].Difference += data.Difference } } // 根据有序的键列表顺序构建返回的数组 var mergedData []PurchaseReportData for _, key := range keys { mergedData = append(mergedData, *mergedMap[key]) } return mergedData } // 导出供应商采购汇总excel func reportBySupplierExport(req *ErpPurchaseReportBySupplierResp) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := ExportUrl fileName := time.Now().Format(TimeFormat) + "供应商采购汇总" + ".xlsx" fmt.Println("url fileName:", url+fileName) // 组合标题栏数据 title := []interface{}{"店铺名称", "采购类型", "供应商", "商品名称", "商品分类", "采购数量", "采购金额", "退货金额", "差额"} for i, _ := range title { cell, _ := excelize.CoordinatesToCellName(1+i, 1) err := file.SetCellValue(fSheet, cell, title[i]) if err != nil { logger.Errorf("file set value err:", err) } } var row []interface{} nExcelStartRow := 0 for _, supplierData := range req.List { var orderType string switch supplierData.PurchaseType { case "procure": orderType = "采购入库" case "reject": orderType = "采购退货" } row = []interface{}{ supplierData.StoreName, // 店铺名称 orderType, // 采购类型 supplierData.ErpSupplierName, // 供应商 supplierData.ErpCommodityName, // 商品名称 supplierData.ErpCategoryName, // 商品分类 supplierData.Count, // 采购数量 supplierData.Amount, // 采购金额 supplierData.RejectAmount, // 退货金额 supplierData.Difference, // 差额 } for j, _ := range row { cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, row[j]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } nExcelStartRow++ } totalData := "记录数:" + strconv.FormatInt(int64(req.Total), 10) end := []interface{}{"汇总", totalData, "", "", "", req.Count, // 采购数量 req.Amount, // 采购金额 req.RejectAmount, // 退货金额 req.Difference, // 差额 } for i, _ := range end { cell, _ := excelize.CoordinatesToCellName(1+i, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, end[i]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } // 设置所有单元格的样式: 居中、加边框 style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, "border":[{"type":"left","color":"000000","style":1}, {"type":"top","color":"000000","style":1}, {"type":"right","color":"000000","style":1}, {"type":"bottom","color":"000000","style":1}]}`) //设置单元格高度 file.SetRowHeight("Sheet1", 1, 20) // 设置单元格大小 file.SetColWidth("Sheet1", "A", "A", 25) file.SetColWidth("Sheet1", "D", "D", 15) endRow := fmt.Sprintf("I"+"%d", nExcelStartRow+2) // 应用样式到整个表格 _ = file.SetCellStyle("Sheet1", "A1", endRow, style) fmt.Println("save fileName:", config.ExportConfig.Path+fileName) if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { fmt.Println(err) } return url + fileName, nil } // GetReportDetail 查询采购明细 func GetReportDetail(req *ErpPurchaseReportDetailReq, c *gin.Context) (*ErpPurchaseReportDetailResp, error) { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 返回sysUser未过期的门店id列表 storeList := GetValidStoreIDs(sysUser.StoreData) if len(storeList) > 0 { req.StoreId = CompareLists(storeList, req.StoreId) if len(req.StoreId) == 0 { // 没有匹配的数据,表示入参门店不是用户有权限的门店 return &ErpPurchaseReportDetailResp{}, nil } } else { return nil, errors.New("用户未绑定门店") } } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpPurchaseReportDetailResp{ PageIndex: page + 1, PageSize: req.PageSize, List: make([]struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id"` // 采购订单id OrderSerialNumber string `json:"order_serial_number"` SerialNumber string `json:"serial_number"` PurchaseType string `json:"purchase_type"` ExecuteTime *time.Time `json:"execute_time"` StoreId uint32 `json:"store_id"` StoreName string `json:"store_name"` ErpSupplierId uint32 `json:"erp_supplier_id"` ErpSupplierName string `json:"erp_supplier_name"` ErpCommodityId uint32 `json:"erp_commodity_id"` ErpCommodityName string `json:"erp_commodity_name"` ErpCategoryID uint32 `json:"erp_category_id"` ErpCategoryName string `json:"erp_category_name"` IMEIType uint32 `json:"imei_type"` IMEI string `json:"imei"` Price float64 `json:"price"` EmployeePrice float64 `json:"employee_price"` RejectPrice float64 `json:"reject_price"` DifferencePrice float64 `json:"difference_price"` }, 0), } qs := orm.Eloquent.Debug().Table("erp_purchase_order"). Select("erp_purchase_order.id as erp_purchase_order_id, "+ "erp_purchase_order.serial_number as order_serial_number, "+ "erp_purchase_inventory.serial_number, "+ "erp_purchase_order.purchase_type, "+ "erp_purchase_inventory.created_at as execute_time, "+ "erp_purchase_order.store_id, "+ "erp_purchase_order.store_name, "+ "erp_purchase_order.erp_supplier_id, "+ "erp_purchase_order.erp_supplier_name, "+ "erp_purchase_inventory.erp_commodity_id, "+ "erp_purchase_inventory.erp_commodity_name, "+ "erp_purchase_inventory.erp_category_id, "+ "erp_purchase_inventory.erp_category_name, "+ "erp_purchase_inventory.imei_type, "+ "erp_purchase_inventory.imei, "+ "CASE "+ "WHEN erp_purchase_order.purchase_type = 'procure' THEN erp_purchase_inventory.implementation_price "+ "WHEN erp_purchase_order.purchase_type = 'reject' THEN erp_purchase_inventory.implementation_price "+ "ELSE 0 END AS price, "+ "CASE "+ "WHEN erp_purchase_order.purchase_type = 'procure' THEN 0 "+ "WHEN erp_purchase_order.purchase_type = 'reject' THEN erp_purchase_inventory.implementation_price "+ "ELSE 0 END AS reject_price, "+ "erp_purchase_inventory.employee_price, "+ "(erp_purchase_inventory.implementation_price - erp_purchase_inventory.employee_price) as difference_price"). Joins("JOIN erp_purchase_inventory ON erp_purchase_order.id = erp_purchase_inventory.erp_purchase_order_id"). Where("erp_purchase_order.state <> ?", 5) // 排除已终止的订单 // 创建一个新的查询对象,用于 count 查询 countQuery := orm.Eloquent.Debug().Table("erp_purchase_order"). Joins("JOIN erp_purchase_inventory ON erp_purchase_order.id = erp_purchase_inventory.erp_purchase_order_id"). Where("erp_purchase_order.state <> ?", 5) // 排除已终止的订单 if req.SerialNumber != "" { qs = qs.Where("erp_purchase_order.serial_number = ?", req.SerialNumber) countQuery = countQuery.Where("erp_purchase_order.serial_number = ?", req.SerialNumber) } if req.PurchaseType != "" { qs = qs.Where("erp_purchase_order.purchase_type = ?", req.PurchaseType) countQuery = countQuery.Where("erp_purchase_order.purchase_type = ?", req.PurchaseType) } if req.ErpCommodityName != "" { qs = qs.Where("erp_purchase_inventory.erp_commodity_name LIKE ?", "%"+req.ErpCommodityName+"%") countQuery = countQuery.Where("erp_purchase_inventory.erp_commodity_name LIKE ?", "%"+req.ErpCommodityName+"%") } if req.ErpCategoryID != 0 { qs = qs.Where("erp_purchase_inventory.erp_category_id = ?", req.ErpCategoryID) countQuery = countQuery.Where("erp_purchase_inventory.erp_category_id = ?", req.ErpCategoryID) } if req.ErpSupplierId != 0 { qs = qs.Where("erp_purchase_order.erp_supplier_id = ?", req.ErpSupplierId) countQuery = countQuery.Where("erp_purchase_order.erp_supplier_id = ?", req.ErpSupplierId) } if req.StartTime != "" { // 入/出库开始时间 parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("GetReportDetail err:", err) return nil, err } qs = qs.Where("erp_purchase_inventory.created_at >= ?", parse) countQuery = countQuery.Where("erp_purchase_inventory.created_at >= ?", parse) } if req.EndTime != "" { // 入/出库结束时间 parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("GetReportDetail err:", err) return nil, err } qs = qs.Where("erp_purchase_inventory.created_at <= ?", parse) countQuery = countQuery.Where("erp_purchase_inventory.created_at <= ?", parse) } if len(req.StoreId) > 0 { qs = qs.Where("erp_purchase_order.store_id IN (?)", req.StoreId) countQuery = countQuery.Where("erp_purchase_order.store_id IN (?)", req.StoreId) } var count int64 err := countQuery.Count(&count).Error if err != nil { logger.Errorf("GetReportDetail count err:", err) return nil, err } offset := (req.PageIndex - 1) * req.PageSize err = qs.Order("erp_purchase_inventory.created_at desc"). Offset(offset). Limit(req.PageSize). Scan(&resp.List).Error if err != nil { logger.Errorf("GetReportDetail scan err:", err) return nil, err } // 根据订单类型调整价格的正负性 for i := range resp.List { resp.List[i].DifferencePrice = 0 if resp.List[i].PurchaseType == "reject" { // 采购退货单查询采购价:根据采购入库编号 price, err := getPrice(resp.List[i].SerialNumber) if err != nil { return nil, err } resp.List[i].Price = -price resp.List[i].DifferencePrice = -resp.List[i].DifferencePrice resp.List[i].RejectPrice = -resp.List[i].RejectPrice resp.List[i].EmployeePrice = -resp.List[i].EmployeePrice resp.List[i].DifferencePrice = resp.List[i].Price - resp.List[i].RejectPrice } } resp.Total = int(count) resp.Price, resp.EmployeePrice, resp.RejectPrice, resp.DifferencePrice = calculatePrices(resp.List) if req.IsExport == 1 { filePath, err := reportDetailExport(resp) if err != nil { return nil, err } resp = &ErpPurchaseReportDetailResp{} resp.ExportUrl = filePath } return resp, nil } // 根据采购单据号查询入库采购价 func getPrice(serialNumber string) (float64, error) { var stockCommodity ErpStockCommodity err := orm.Eloquent.Table("erp_stock_commodity").Where("stock_sn = ?", serialNumber). Find(&stockCommodity).Error if err != nil { return 0, err } return stockCommodity.WholesalePrice, nil } // 计算价格信息 func calculatePrices(list []struct { ErpPurchaseOrderId uint32 `json:"erp_purchase_order_id"` // 采购订单id OrderSerialNumber string `json:"order_serial_number"` SerialNumber string `json:"serial_number"` PurchaseType string `json:"purchase_type"` ExecuteTime *time.Time `json:"execute_time"` StoreId uint32 `json:"store_id"` StoreName string `json:"store_name"` ErpSupplierId uint32 `json:"erp_supplier_id"` ErpSupplierName string `json:"erp_supplier_name"` ErpCommodityId uint32 `json:"erp_commodity_id"` ErpCommodityName string `json:"erp_commodity_name"` ErpCategoryID uint32 `json:"erp_category_id"` ErpCategoryName string `json:"erp_category_name"` IMEIType uint32 `json:"imei_type"` IMEI string `json:"imei"` Price float64 `json:"price"` EmployeePrice float64 `json:"employee_price"` RejectPrice float64 `json:"reject_price"` DifferencePrice float64 `json:"difference_price"` }) (price, employeePrice, rejectPrice, differencePrice float64) { for _, item := range list { price += item.Price employeePrice += item.EmployeePrice rejectPrice += item.RejectPrice differencePrice += item.DifferencePrice } return } // 导出采购明细excel func reportDetailExport(req *ErpPurchaseReportDetailResp) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := ExportUrl fileName := time.Now().Format(TimeFormat) + "采购明细" + ".xlsx" fmt.Println("url fileName:", url+fileName) // 组合标题栏数据 title := []interface{}{"单据编号", "单据类型", "出/入库时间", "店铺名称", "供应商", "商品名称", "商品分类", "是否串码", "串码", "采购价", "员工成本价", "退货价", "差价"} for i, _ := range title { cell, _ := excelize.CoordinatesToCellName(1+i, 1) err := file.SetCellValue(fSheet, cell, title[i]) if err != nil { logger.Errorf("file set value err:", err) } } var row []interface{} nExcelStartRow := 0 for _, detailData := range req.List { var orderType string switch detailData.PurchaseType { case "procure": orderType = "采购入库" case "reject": orderType = "采购退货" } var IMEIType string switch detailData.IMEIType { case 1: IMEIType = "非串码" case 2, 3: IMEIType = "串码类" } row = []interface{}{ detailData.OrderSerialNumber, // 单据编号 orderType, // 单据类型 detailData.ExecuteTime.Format(TimeFormat), // 出/入库时间 detailData.StoreName, // 店铺名称 detailData.ErpSupplierName, // 供应商 detailData.ErpCommodityName, // 商品名称 detailData.ErpCategoryName, // 商品分类 IMEIType, // 是否串码 detailData.IMEI, // 串码 detailData.Price, // 采购价 detailData.EmployeePrice, // 员工成本价 detailData.RejectPrice, // 退货价 detailData.DifferencePrice, // 差价 } for j, _ := range row { cell, _ := excelize.CoordinatesToCellName(1+j, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, row[j]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } nExcelStartRow++ } totalData := "记录数:" + strconv.FormatInt(int64(req.Total), 10) end := []interface{}{totalData, "", "", "", "", "", "", "", "", req.Price, // 采购价 req.EmployeePrice, // 员工成本价 req.RejectPrice, // 退货价 req.DifferencePrice, // 差价 } for i, _ := range end { cell, _ := excelize.CoordinatesToCellName(1+i, nExcelStartRow+2) err := file.SetCellValue(fSheet, cell, end[i]) if err != nil { logger.Error("file set value err:", logger.Field("err", err)) } } // 设置所有单元格的样式: 居中、加边框 style, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center"}, "border":[{"type":"left","color":"000000","style":1}, {"type":"top","color":"000000","style":1}, {"type":"right","color":"000000","style":1}, {"type":"bottom","color":"000000","style":1}]}`) //设置单元格高度 file.SetRowHeight("Sheet1", 1, 20) // 设置单元格大小 file.SetColWidth("Sheet1", "A", "A", 15) file.SetColWidth("Sheet1", "C", "C", 20) file.SetColWidth("Sheet1", "D", "D", 26) file.SetColWidth("Sheet1", "F", "F", 18) file.SetColWidth("Sheet1", "I", "I", 18) endRow := fmt.Sprintf("M"+"%d", nExcelStartRow+2) // 应用样式到整个表格 _ = file.SetCellStyle("Sheet1", "A1", endRow, style) fmt.Println("save fileName:", config.ExportConfig.Path+fileName) if err := file.SaveAs(config.ExportConfig.Path + fileName); err != nil { fmt.Println(err) } return url + fileName, nil } // CancelAuditUpdateStock 反审核后更新库存信息 // 查询订单有无入库信息 // 采购单,有入库信息,则退库;如果有已审核的退货单则不能反审核,报错提示 // 退货单,有入库信息,则入库 func CancelAuditUpdateStock(begin *gorm.DB, req ErpPurchaseOrder) error { var purchaseOrder ErpPurchaseOrder err := orm.Eloquent.Table("erp_purchase_order").Where("id=?", req.ID).Find(&purchaseOrder).Error if err != nil { logger.Error("checkPurchaseInventory purchase order err:", logger.Field("err", err)) return err } var inventoryList []ErpPurchaseInventory err = orm.Eloquent.Table("erp_purchase_inventory").Where("erp_purchase_order_id=?", req.ID). Find(&inventoryList).Error if err != nil { logger.Error("purchase commodities err:", logger.Field("err", err)) return err } if len(inventoryList) == 0 { // 还没有入库记录,直接取消审核 return nil } if req.PurchaseType == ErpProcureOrder { // 采购单反审核 // 查询有无退货订单 var count int64 stateList := []int{ErpPurchaseOrderUnAudit, ErpPurchaseOrderEnd} err = orm.Eloquent.Table("erp_purchase_order").Where("rejected_serial_number = ? and state NOT IN (?)", req.SerialNumber, stateList).Count(&count).Error if err != nil { return err } if count > 0 { // 有已审核的退货单 return errors.New("取消审核失败:已存在采购退库订单,请先将其取消") } //// 更新入库的商品为"采购退库" //reqParam := &ErpPurchaseInventoryReq{ // ErpPurchaseOrderId: req.ID, // PurchaseType: ErpRejectOrder, // Inventories: inventoryList, //} // 按照采购退货更新库存 err = InventoryErpPurchaseUpdateRejectStock(begin, inventoryList, purchaseOrder) if err != nil { return fmt.Errorf("取消审核失败:%s", err.Error()) } // 更新采购商品表的入库数量 for _, v := range inventoryList { err = begin.Model(&ErpPurchaseCommodity{}). Where("erp_purchase_order_id = ? and erp_commodity_id = ?", v.ErpPurchaseOrderId, v.ErpCommodityId). UpdateColumn("inventory_count", gorm.Expr("inventory_count - ?", v.Count)).Error if err != nil { logger.Error("update inventory count err:", logger.Field("err", err)) return err } } } else if req.PurchaseType == ErpRejectOrder { // 退货单反审核 // 更新入库的商品为"采购退库" reqParam := &ErpPurchaseInventoryReq{ ErpPurchaseOrderId: req.ID, PurchaseType: ErpProcureOrder, Inventories: inventoryList, } // 采购退货单反审核更新库存 err = ErpRejectOrderCancelAuditUpdateStock(begin, reqParam, purchaseOrder) if err != nil { return fmt.Errorf("取消审核失败:%s", err.Error()) } // 更新采购商品表的入库数量 for _, v := range inventoryList { err = begin.Model(&ErpPurchaseCommodity{}). Where("erp_purchase_order_id = ? and erp_commodity_id = ?", v.ErpPurchaseOrderId, v.ErpCommodityId). UpdateColumn("inventory_count", gorm.Expr("inventory_count + ?", v.Count)).Error if err != nil { logger.Error("update inventory count err:", logger.Field("err", err)) return err } } } else { return errors.New("取消审核失败:采购类型有误") } // 删除采购入库的记录 err = begin.Table("erp_purchase_inventory").Where("erp_purchase_order_id = ? and purchase_type = ?", req.ID, req.PurchaseType).Delete(&ErpPurchaseInventory{}).Error if err != nil { return err } return nil } // ErpRejectOrderCancelAuditUpdateStock 采购退货单反审核更新库存 func ErpRejectOrderCancelAuditUpdateStock(gdb *gorm.DB, req *ErpPurchaseInventoryReq, purchaseOrder ErpPurchaseOrder) error { // 遍历采购入库商品信息 var stockList []ErpStockCommodity for _, v := range req.Inventories { commodityInfo, err := GetCommodity(v.ErpCommodityId) if err != nil { logger.Errorf("GetCommodity err:", err) return err } exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_stock WHERE store_id=%d AND erp_commodity_id=%d", purchaseOrder.StoreId, v.ErpCommodityId)) if err != nil { logger.Errorf("exist err:", err) return err } if exist { err = gdb.Exec(fmt.Sprintf( "UPDATE erp_stock SET count=count+%d WHERE store_id=%d AND erp_commodity_id=%d;", v.Count, purchaseOrder.StoreId, v.ErpCommodityId)).Error if err != nil { logger.Errorf("update stock err:", err) return err } } else { return errors.New("未查询到商品库存信息") } // 更新商品库存信息 // 串码商品直接更新状态为:在库 if v.IMEIType == 2 || v.IMEIType == 3 { // 串码商品 if v.IMEI == "" { return fmt.Errorf("商品[%s]串码为空", v.ErpCommodityName) } err = gdb.Table("erp_stock_commodity").Where("imei = ?", v.IMEI). Update("state", InStock).Error // 状态更新为采购退货 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } } // 非串码商品则新建 nowTime := time.Now() stockCommodity := ErpStockCommodity{ StoreId: purchaseOrder.StoreId, StoreName: purchaseOrder.StoreName, ErpCommodityId: v.ErpCommodityId, ErpCommodityName: v.ErpCommodityName, CommoditySerialNumber: v.CommoditySerialNumber, ErpCategoryId: commodityInfo.ErpCategoryId, ErpCategoryName: commodityInfo.ErpCategoryName, ErpSupplierId: purchaseOrder.ErpSupplierId, ErpSupplierName: purchaseOrder.ErpSupplierName, StaffCostPrice: tools.RoundToTwoDecimalPlaces(v.EmployeePrice - v.ImplementationPrice), WholesalePrice: tools.RoundToTwoDecimalPlaces(v.ImplementationPrice), State: InStock, StorageType: PurchaseInventory, // 采购入库 FirstStockTime: nowTime, StockTime: nowTime, Count: v.Count, ErpBarcode: commodityInfo.ErpBarcode, // 240106新增商品条码 IMEIType: v.IMEIType, IMEI: v.IMEI, Remark: "", MemberDiscount: commodityInfo.MemberDiscount, MinRetailPrice: commodityInfo.MinRetailPrice, RetailPrice: commodityInfo.RetailPrice, OriginalSn: purchaseOrder.SerialNumber, } stockList = append(stockList, stockCommodity) } err := gdb.Debug().Create(&stockList).Error if err != nil { logger.Errorf("create stock commodity err:", err) return err } return nil }