package models import ( "encoding/json" "errors" "fmt" "github.com/gin-gonic/gin" "github.com/xuri/excelize/v2" "go-admin/app/admin/apis/pay" 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 ( ErpOrderStateUnAudit = "un_audit" // 未审核 ErpOrderStateAudited = "audited" // 已审核 ErpOrderMemberTypeGeneral = "general" // 普通 ErpOrderMemberTypeMember = "member" // 会员 RetailTypeSale = "sale" // 零售订单 RetailTypeRejected = "rejected" // 零售退货订单 NoCreatePayOrder = 0 // 未创建 WaitForPaying = 1 // 待支付 HavePaid = 2 // 已支付 NoPrint = 1 // 未打印 HavePrinted = 2 // 已打印 OnlinePay = 1 // 线上支付(微信/支付宝/云闪付扫码) NoPayOrder = "no_pay_order" PayInit = "pay_init" Paying = "paying" PayOk = "pay_ok" PayFailed = "pay_failed" PayUnknown = "pay_unknown" ) // ErpOrder 零售订单表 type ErpOrder struct { Model BillSn string `json:"bill_sn" gorm:"index"` // 单据编号 RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 Uid int `json:"uid"` // 用户id UserType int `json:"user_type"` // 会员类别:1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 9-黑金&尊享 Tel string `json:"tel" gorm:"index"` // 客户手机号 StoreId uint32 `json:"store_id" gorm:"index"` // 门店id StoreName string `json:"store_name"` // 门店名称 MakerId uint32 `json:"maker_id" gorm:"index"` // 制单人id MakerName string `json:"maker_name"` // 制单人名称 MakerTime time.Time `json:"maker_time"` // 制单时间 AuditorId uint32 `json:"auditor_id" gorm:"index"` // 审核人id AuditorName string `json:"auditor_name"` // 审核人姓名 AuditTime *time.Time `json:"audit_time"` // 审核时间 CashierList string `json:"cashier_list" gorm:"type:text"` // 付款方式,存储json数据 SalesmanList string `json:"salesman_list" gorm:"type:text"` // 销售员信息,存储json数据 MemberType string `json:"member_type"` // 会员类型:general 普通; member 会员 State string `json:"state" gorm:"index"` // 订单状态:un_audit 待审核; audited 已审核 TotalRetailPrice float64 `json:"total_retail_price"` // 订单总指导零售价 TotalAmount float64 `json:"total_amount"` // 订单实收金额 TotalCount int32 `json:"total_count"` // 订单商品数量 TotalSalesProfit float64 `json:"total_sales_profit"` // 订单总销售毛利 TotalStaffProfit float64 `json:"total_staff_profit"` // 订单总员工毛利 VmCount uint32 `json:"vm_count"` // 使用会员积分 SaleOrderId uint32 `json:"sale_order_id"` // 销售订单id PayStatus uint32 `json:"pay_status"` // 支付状态 0-未创建 ;1-待支付; 2-已支付 IsPrint uint32 `json:"is_print"` // 是否打印小票 1-未打印 2-已打印 PrintCount uint32 `json:"print_count"` // 小票打印次数 InvoiceCode string `json:"invoice_code"` // 发票代码 InvoiceNumber string `json:"invoice_number"` // 发票编码 RejectedTotalAmount float64 `json:"rejected_total_amount" gorm:"-"` // 订单总退货金额 RejectedTotalCount uint32 `json:"rejected_total_count" gorm:"-"` // 订单总退货数量 StorePer float64 `json:"store_per"` // 门店提成:订单总员工毛利X该门店设置好的提成比例,保留到小数后两位多余舍去 TotalDiscount float64 `json:"total_discount"` // 订单总优惠:订单所有商品零售优惠+会员优惠+会员积分抵扣之和 BankTrxNo string `json:"bank_trx_no" gorm:"-"` // 银行流水号 Commodities []ErpOrderCommodity `json:"commodities" gorm:"-"` // 零售订单商品信息 Cashiers []ErpOrderCashier `json:"cashiers" gorm:"-"` // 收付款方式 Salesman []ErpOrderSales `json:"salesman" gorm:"-"` // 销售员信息 } // ErpOrderCommodity 零售订单商品表 type ErpOrderCommodity struct { Model ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id(后端生成) ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id ErpCategoryName string `json:"erp_category_name"` // 分类名称 ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpSupplierId uint32 `json:"erp_supplier_id" gorm:"index"` // 主供应商id ErpSupplierName string `json:"erp_supplier_name"` // 主供应商名称 IMEIType uint32 `json:"imei_type"` // 1-无串码 2-串码 IMEI string `json:"imei" gorm:"index"` // 串码 PresentType uint32 `json:"present_type"` // 赠送类型:1-非赠送 2-赠送 RetailPrice float64 `json:"retail_price"` // 指导零售价 SalePrice float64 `json:"sale_price"` // 零售价 Count int32 `json:"count"` // 销售数量 SaleDiscount float64 `json:"sale_discount"` // 零售优惠 MemberDiscount float64 `json:"member_discount"` // 会员优惠 VmDiscount float64 `json:"vm_discount"` // 会员积分抵扣 CouponID uint32 `json:"coupon_id"` // 优惠券ID CouponDiscount float64 `json:"coupon_discount"` // 优惠券抵扣 CouponCode string `json:"coupon_code"` // 优惠券券码 CouponName string `json:"coupon_name"` // 优惠券名称 Amount float64 `json:"amount"` // 实际零售价 ReceivedAmount float64 `json:"received_amount"` // 商品实收金额 Remark string `json:"remark"` // 销售备注 RejectedRemark string `json:"rejected_remark"` // 退货备注 RejectedPrice float64 `json:"rejected_price"` // 退货单价 RejectedCount uint32 `json:"rejected_count"` // 退货数量 RejectedAmount float64 `json:"rejected_amount"` // 退货金额 RejectedOrderCommodityId uint32 `json:"rejected_order_commodity_id"` // 退货订单商品id StaffCostPrice float64 `json:"staff_cost_price"` // 员工成本价加价(如:加价50,不是加价后的价格) WholesalePrice float64 `json:"wholesale_price"` // 指导采购价 SalesProfit float64 `json:"sales_profit"` // 销售毛利:实际零售价-采购单价;如果为退货订单,则为实际退货价-采购单价 StaffProfit float64 `json:"staff_profit"` // 员工毛利:实际零售价-员工成本价;如果为退货订单,则为实际退货价-员工成本价 ErpStockCommodityID string `json:"erp_stock_commodity_id"` // 库存商品表主键id StaffPrice float64 `json:"staff_price" gorm:"-"` // 员工成本价 CommoditySerialNumber string `json:"commodity_serial_number" gorm:"-"` // 商品编号 CategoryNumber string `json:"category_number" gorm:"-"` // 商品分类编号 } // ErpOrderCashier 订单收款方式 type ErpOrderCashier struct { CashierId uint32 `json:"cashier_id"` // 收付款方式id Name string `json:"name"` // 收付款方式名称 Amount float64 `json:"amount"` // 金额 } // ErpOrderPayWay 订单支付方式记录 type ErpOrderPayWay struct { Model ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id(后端生成) CashierId uint32 `json:"cashier_id"` // 收付款方式id Name string `json:"name"` // 收付款方式名称 Amount float64 `json:"amount"` // 金额 } // ErpOrderSales 销售员信息 type ErpOrderSales struct { Model ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id(后端生成) Uid uint32 `json:"userId" binding:"required"` // 销售员用户ID(20240322:更换为系统用户表主键id) Name string `json:"name"` // 销售员用户姓名 SalesProfitPer float64 `json:"sales_profit_per"` // 销售毛利提成:每个商品销售毛利X其对应的提成比例后求和;如果是两个销售员参与则分别除以2 ,保留到小数后两位多余舍去 StaffProfitPer float64 `json:"staff_profit_per"` // 员工毛利提成:每个商品员工毛利X其对应的提成比例后求和;如果是两个销售员参与则分别除以2 ,保留到小数后两位多余舍去 SalesmanPer float64 `json:"salesman_per"` // 销售员提成:订单总员工毛利X该销售员设置好的提成比例;如果是两个销售员参与,那么两个人算出的提成均除以2,保留到小数后两位多余舍去 } // ErpOrderRecord 订单支付记录表 type ErpOrderRecord struct { Model ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id BillSn string `json:"bill_sn" gorm:"index"` // 单据编号 OutOrderNo string `json:"out_order_no"` // 商户订单号 PlatTrxNo string `json:"plat_trx_no"` // 平台交易流水号 BankOrderNo string `json:"bank_order_no"` // 银行订单号 BankTrxNo string `json:"bank_trx_no"` // 银行流水号 TotalAmount float64 `json:"total_amount"` // 订单总金额 PayWay string `json:"pay_way"` // 支付方式 Status string `json:"status"` // 支付状态 } // ErpOrderCreateReq 订单创建入参 type ErpOrderCreateReq struct { BillSn string `json:"bill_sn"` // 单据编号 StoreId uint32 `json:"store_id" binding:"required"` // 门店id StoreName string `json:"store_name" binding:"required"` // 门店名称 RetailType string `json:"retail_type" binding:"required"` // 销售类型:sale 零售销售; rejected 零售退货 Tel string `json:"tel"` // 会员手机号 MemberType string `json:"member_type"` // 会员类型:general 普通; member 会员 TotalRetailPrice float64 `json:"total_retail_price"` // 订单总指导零售价:如果是赠送商品,金额可以为0 TotalAmount float64 `json:"total_amount"` // 订单实收金额:如果只有1个赠送商品,金额可以为0 TotalCount uint32 `json:"total_count" binding:"required"` // 订单商品数量 VmAmount uint32 `json:"vm_count"` // 使用会员积分 Cashiers []ErpOrderCashier `json:"cashiers" binding:"required"` // 收付款方式 ErpOrderCommodities []ErpOrderCommodity `json:"erp_order_commodities" binding:"required"` // 零售订单商品信息 Salesman []ErpOrderSales `json:"salesman" binding:"required"` // 销售员信息 } // ErpOrderListReq 查询零售订单列表入参 type ErpOrderListReq struct { ScanCode string `json:"scan_code"` // 扫码枪扫码数据:串码 BillSn string `json:"bill_sn"` // 单据编号 RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 CommodityName string `json:"commodity_name"` // 商品名称 Uid int `json:"uid"` // 用户ID Tel string `json:"tel"` // 客户手机号 Salesman uint32 `json:"salesman"` // 销售人员ID StoreId uint32 `json:"store_id"` // 门店ID State string `json:"state"` // 订单状态 PayStatus uint32 `json:"pay_status"` // 支付状态 0-未创建 ;1-待支付; 2-已支付 MakeTimeStart string `json:"make_time_start"` // 制单开始时间 MakeTimeEnd string `json:"make_time_end"` // 制单结束时间 AuditTimeStart string `json:"audit_time_start"` // 审核开始时间 AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // ErpOrderListResp 查询零售订单列表出参 type ErpOrderListResp struct { List []ErpOrder `json:"list"` Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 ShowAll string `json:"show_all" binding:"required"` // 展示所有订单配置:ON-打开,OFF-关闭 ExportUrl string `json:"export_url"` } // ErpOrderDeleteReq 删除零售订单入参 type ErpOrderDeleteReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 } // ErpOrderPayReq 收款入参 type ErpOrderPayReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 AuthCode string `json:"auth_code" binding:"required"` // 用户付款码 } // ErpOrderPayResp 收款出参 type ErpOrderPayResp struct { Status string `json:"status"` // 支付成功:pay_ok;支付失败:pay_failed ;等待支付:paying; 未知状态:pay_unknown; 未创建支付订单:no_pay_order } // ErpOrderAuditReq 审核零售订单入参 type ErpOrderAuditReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 State int `json:"state" binding:"required"` // 审核操作: 1-审核 2-取消审核 } // ErpOrderAddInvoiceReq 开发票入参 type ErpOrderAddInvoiceReq struct { BillSn string `json:"bill_sn" binding:"required"` // 单据编号 InvoiceCode string `json:"invoice_code" binding:"required"` // 发票代码 InvoiceNumber string `json:"invoice_number" binding:"required"` // 发票编码 } // ErpOrderStoreManageDataReq 查询门店经营入参 type ErpOrderStoreManageDataReq struct { StoreId []uint32 `json:"store_id"` // 门店ID StartTime string `json:"start_time"` // 开始时间 EndTime string `json:"end_time"` // 结束时间 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 IsExport uint32 `json:"is_export"` // 1-导出 SortType string `json:"sort_type"` // 排序类型:desc 降序、asc 升序 } // ErpOrderStoreManageDataResp 查询门店经营出参 type ErpOrderStoreManageDataResp struct { List []StoreManageData `json:"list"` Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 ExportUrl string `json:"export_url"` TotalSalesAmount float64 `json:"total_sales_amount"` // 总销售额 TotalPromotionFee float64 `json:"total_promotion_fee"` // 总推广费 TotalSalesProfit float64 `json:"total_sales_profit"` // 总销售毛利 TotalStaffProfit float64 `json:"total_staff_profit"` // 总员工毛利 TotalCount int64 `json:"total_count"` // 总销售数量 } // StoreManageData 门店经营数据 type StoreManageData struct { Date string `json:"date"` // 时间,如:"2023-12-25" TotalSalesAmount float64 `json:"total_sales_amount"` // 销售额 PromotionFee float64 `json:"promotion_fee"` // 推广费 SalesProfit float64 `json:"sales_profit"` // 销售毛利 StaffProfit float64 `json:"staff_profit"` // 员工毛利 Count int64 `json:"count"` // 销售数量 } // ErpOrderRetailMarginReq 查询商品零售毛利汇总入参 type ErpOrderRetailMarginReq struct { StoreId []uint32 `json:"store_id"` // 门店ID RetailType []string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 ErpCommodityName []string `json:"erp_commodity_name"` // 商品名称 ErpCategoryId []uint32 `json:"erp_category_id" gorm:"index"` // 分类id StartTime string `json:"start_time"` // 开始时间 EndTime string `json:"end_time"` // 结束时间 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 IsExport uint32 `json:"is_export"` // 1-导出 } // ErpOrderRetailMarginResp 查询商品零售毛利汇总出参 type ErpOrderRetailMarginResp struct { List []RetailMarginData `json:"list"` Total int `json:"total"` // 总条数 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 TotalCount int32 `json:"total_count"` // 总销售数量 TotalSalesAmount float64 `json:"total_sales_amount"` // 总销售/退货金额 TotalSalesCost float64 `json:"total_sales_cost"` // 总销售成本:销售采购价之和 TotalEmployeeCost float64 `json:"total_employee_cost"` // 总员工成本:商品员工成本价之和 TotalSalesMargin float64 `json:"total_sales_margin"` // 总销售毛利:销售/退货金额-销售成本 TotalGrossMargins string `json:"total_gross_margins"` // 销售毛利率:销售毛利/销售/退货金额 TotalEmployeeMargin float64 `json:"total_employee_margin"` // 总员工毛利:销售金额/实际退货金额-员工成本 TotalEmployeeGrossMargins string `json:"total_employee_gross_margins"` // 总员工毛利率: 员工毛利/销售/退货金额 ExportUrl string `json:"export_url"` } // RetailMarginData 商品零售毛利数据 type RetailMarginData struct { StoreId uint32 `json:"store_id" gorm:"index"` // 门店id StoreName string `json:"store_name"` // 门店名称 RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 ErpCommodityId uint32 `json:"erp_commodity_id" gorm:"index"` // 商品id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id ErpCategoryName string `json:"erp_category_name"` // 分类名称 Count int32 `json:"count"` // 销售数量 SalesAmount float64 `json:"sales_amount"` // 销售/退货金额 SalesCost float64 `json:"sales_cost"` // 销售成本:销售采购价之和 EmployeeCost float64 `json:"employee_cost"` // 员工成本:商品员工成本价之和 SalesMargin float64 `json:"sales_margin"` // 销售毛利:销售/退货金额-销售成本 GrossMargins string `json:"gross_margins"` // 销售毛利率:销售毛利/销售/退货金额 EmployeeMargin float64 `json:"employee_margin"` // 员工毛利:零售销售时:销售金额-员工成本 零售退货时:实际退货金额-员工成本 EmployeeGrossMargins string `json:"employee_gross_margins"` // 员工毛利率: 零售销售时:员工毛利/销售金额 零售退货时:员工毛利/退货金额 } // ErpOrderRetailDetailReq 零售明细汇总入参 type ErpOrderRetailDetailReq struct { BillSn string `json:"bill_sn"` // 单据编号 RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 Uid int `json:"uid"` // 用户ID Tel string `json:"tel"` // 客户手机号 StoreId uint32 `json:"store_id"` // 门店ID ErpCategoryId []uint32 `json:"erp_category_id"` // 分类id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 Salesman uint32 `json:"salesman"` // 销售人员ID IMEI string `json:"imei"` // 串码 StartTime string `json:"start_time"` // 开始时间 EndTime string `json:"end_time"` // 结束时间 BankTrxNo string `json:"bank_trx_no"` // 银联流水号 CashierId uint32 `json:"cashier_id"` // 收付款方式id PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 IsExport uint32 `json:"is_export"` // 1-导出 } // ErpOrderRetailDetailResp 零售明细汇总出参 type ErpOrderRetailDetailResp struct { Total int `json:"total"` // 总条数(总订单数) PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 ExportUrl string `json:"export_url"` // 导出excel地址 List []ErpOrder `json:"list"` // 零售明细 SumData RetailDetailTotalData `json:"sumData"` // 汇总数据 } // RetailDetailTotalData 零售明细相关金额汇总 type RetailDetailTotalData struct { Count int32 `json:"count"` // 销售数量 RetailPrice float64 `json:"retail_price"` // 指导零售价 SalePrice float64 `json:"sale_price"` // 零售价 SaleDiscount float64 `json:"sale_discount"` // 零售优惠 MemberDiscount float64 `json:"member_discount"` // 会员优惠 VmDiscount float64 `json:"vm_discount"` // 会员积分抵扣 CouponDiscount float64 `json:"coupon_discount"` // 优惠券抵扣 Amount float64 `json:"amount"` // 实际零售价 WholesalePrice float64 `json:"wholesale_price"` // 采购单价 StaffPrice float64 `json:"staff_price"` // 员工成本价 SalesProfit float64 `json:"sales_profit"` // 销售毛利 StaffProfit float64 `json:"staff_profit"` // 员工毛利 TotalRetailPrice float64 `json:"total_retail_price"` // 订单总指导零售价 TotalDiscount float64 `json:"total_discount"` // 订单总优惠:订单所有商品零售优惠+会员优惠+会员积分抵扣之和 TotalAmount float64 `json:"total_amount"` // 订单实收金额 //TotalCashierData TotalCashierData []ErpOrderCashier `json:"total_cashier_data" gorm:"-"` // 订单实收金额 TotalSalesProfit float64 `json:"total_sales_profit"` // 订单总销售毛利 TotalStaffProfit float64 `json:"total_staff_profit"` // 订单总员工毛利 TotalPerData StorePer float64 `json:"store_per"` // 门店提成 } // TotalPerData 员工提成 type TotalPerData struct { TotalSalesProfitPer float64 `json:"total_sales_profit_per"` // 销售毛利提成 TotalStaffProfitPer float64 `json:"total_staff_profit_per"` // 员工毛利提成 SalesmanPer float64 `json:"salesman_per"` // 销售员提成 } // TotalCashierData 支付方式金额汇总 type TotalCashierData struct { ScanAmount float64 `json:"scan_amount"` // 扫码付 CashAmount float64 `json:"cash_amount"` // 现金收款 PosAmount float64 `json:"pos_amount"` // pos机收款 StoreVmAmount float64 `json:"store_vm_amount"` // 商场积分抵扣 OtherAmount float64 `json:"other_amount"` // 其他付款方式 } // ErpOrderReceiptDataResp 查询小票数据出参 type ErpOrderReceiptDataResp struct { StoreName string `json:"storeName"` // 门店名称 Barcode string `json:"barcode"` // 单据编号:条码 OddNum string `json:"oddNum" ` // 单据编号 Time time.Time `json:"time"` // 审核时间 CollectS string `json:"collectS"` // 收银人员:制单人 ChandiseObj map[string]TableData `json:"chandiseObj"` // 商品信息 TotalRetailP float64 `json:"totalRetailP"` // 零售价合计 TotalNum uint32 `json:"totalNum"` // 数量合计 TotalAmount float64 `json:"totalAmount"` // 零售优惠总金额 MembersAmount float64 `json:"membersAmount"` // 会员优惠总金额 CouponAmount float64 `json:"couponAmount"` // 优惠券优惠总金额 IntegrationAmount float64 `json:"integrationAmount"` // 积分抵扣总金额 ToDealWith float64 `json:"toDealWith"` // 零售价合计 - 零售优惠总额 - 会员优惠总和 - 积分抵扣总额 ModeOfPayment map[string]ErpOrderCashier `json:"modeOfPayment"` // 支付信息 ActualPayment float64 `json:"actualPayment"` // 所有支付方式金额总和 Tel string `json:"tel"` // 买家电话 StoreTel string `json:"storeTel"` // 卖家电话 StoreAddress string `json:"storeAddress"` // 店铺地址 Uid int `json:"uid"` // 用户id IsShowImei int `json:"is_show_imei"` // 是否打印商品串码,1-打印串码,0-不打印 } type TableData struct { Name string `json:"name"` SL uint32 `json:"SL"` // 销售数量 DJ float64 `json:"DJ"` // 商品指导零售价 JE float64 `json:"JE"` // 商品指导零售价乘以销售数量 IMEI string `json:"imei"` // 串码 } type ErpOrderBatchImportResp struct { Commodities []ErpOrderCommodity `json:"commodities"` // 零售订单商品信息 } // Contains 判断id是否在list中 func Contains(list []uint32, id uint32) bool { for _, item := range list { if item == id { return true } } return false } // CompareLists 返回共有的数据 func CompareLists(list1 []uint32, list2 []uint32) []uint32 { if len(list2) == 0 { // 如果list2为空,则直接使用list1的数据 return list1 } // 创建一个 map 用于存储 list1 中的数据 list1Map := make(map[uint32]bool) for _, id := range list1 { list1Map[id] = true } var commonIds []uint32 // 遍历 list2,如果在 list1Map 中找到相同的元素,则加入到 commonIds 中 for _, id := range list2 { if list1Map[id] { commonIds = append(commonIds, id) } } return commonIds } // GetValidStoreIDs 返回未过期门店的ID列表 func GetValidStoreIDs(storeData string) []uint32 { // 解析门店数据 var stores []StoreInfo if err := json.Unmarshal([]byte(storeData), &stores); err != nil { return nil } var validStoreIDs []uint32 // 遍历每个门店,检查是否过期 for _, store := range stores { expireTime, err := time.Parse(StoreDateTimeFormat, store.ExpireTime) if err != nil { continue } expireTime = expireTime.Add(24*time.Hour - time.Second) // 如果过期时间在当前时间之后,则未过期,将门店ID添加到列表中 if expireTime.After(time.Now()) { validStoreIDs = append(validStoreIDs, uint32(store.StoreID)) } } return validStoreIDs } // List 查询零售订单列表 func (m *ErpOrderListReq) List(c *gin.Context) (*ErpOrderListResp, error) { showConfig, err := GetErpOrderShowConfig() if err != nil { logger.Errorf("List err:", err) showConfig.ShowAll = "ON" } resp := &ErpOrderListResp{ PageIndex: m.PageIndex, PageSize: m.PageSize, } page := m.PageIndex - 1 if page < 0 { page = 0 } if m.PageSize == 0 { m.PageSize = 10 } if m.ScanCode != "" { // 扫描了串码,需要查询已售的商品数据 return QueryListByScanCode(m.StoreId, m.ScanCode, showConfig.ShowAll, c) } if m.CommodityName != "" { // 输入了商品名称进行查询 return QueryListByCommodityName(m, showConfig.ShowAll, c) } qs := orm.Eloquent.Table("erp_order") if showConfig.ShowAll == "OFF" { // 关闭后未开小票的零售销售订单隐藏 qs = qs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) } // 非管理员才判断所属门店 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 { if len(storeList) == 1 { qs = qs.Where("store_id = ?", storeList[0]) } else { qs = qs.Where("store_id IN (?)", storeList) } } else { return nil, errors.New("用户未绑定门店") } } if m.BillSn != "" { qs = qs.Where("bill_sn=?", m.BillSn) } else { if m.RetailType != "" { qs = qs.Where("retail_type=?", m.RetailType) } if m.Uid != 0 { qs = qs.Where("uid=?", m.Uid) } if m.Tel != "" { qs = qs.Where("tel=?", m.Tel) } if m.StoreId != 0 { qs = qs.Where("store_id=?", m.StoreId) } if m.Salesman != 0 { qs = qs.Where("JSON_CONTAINS(salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, m.Salesman)) } if m.PayStatus != 0 { qs = qs.Where("pay_status=?", m.PayStatus) } if m.State != "" { qs = qs.Where("state=?", m.State) } if m.MakeTimeStart != "" { parse, err := time.Parse(QueryTimeFormat, m.MakeTimeStart) if err != nil { logger.Errorf("err:", err) } qs = qs.Where("maker_time > ?", parse) } if m.MakeTimeEnd != "" { parse, err := time.Parse(QueryTimeFormat, m.MakeTimeEnd) if err != nil { logger.Errorf("err:", err) } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("maker_time < ?", parse) } if m.AuditTimeStart != "" { parse, err := time.Parse(QueryTimeFormat, m.AuditTimeStart) if err != nil { logger.Errorf("err:", err) } qs = qs.Where("audit_time > ?", parse) } if m.AuditTimeEnd != "" { parse, err := time.Parse(QueryTimeFormat, m.AuditTimeEnd) if err != nil { logger.Errorf("err:", 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("count err:", logger.Field("err", err)) return resp, err } var orders []ErpOrder err = qs.Debug().Order("maker_time 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 } // 添加付款、销售员、商品信息 erpOrderListSetCommodity(orders) erpOrderListSetCashier(orders) erpOrderListSetSalesman(orders) erpOrderSetBankTrxNo(orders) resp.List = orders //跟之前保持一致 resp.Total = int(count) resp.PageIndex = page + 1 resp.PageSize = m.PageSize resp.ShowAll = showConfig.ShowAll return resp, nil } // QueryListByScanCode 通过扫描串码查询列表 func QueryListByScanCode(storeId uint32, scanCode, showConfig string, c *gin.Context) (*ErpOrderListResp, error) { resp := &ErpOrderListResp{} // 查询扫码串码的零售销售订单的商品信息 var commodity []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Where("imei = ? and rejected_count = 0", scanCode).Find(&commodity).Error if err != nil && err != RecordNotFound { logger.Error("get erp_order_commodity err:", logger.Field("err", err)) return resp, err } if len(commodity) == 0 { return &ErpOrderListResp{}, nil } // 判断该串码商品是否已经退货退回库存列表 var stockCount int64 err = orm.Eloquent.Table("erp_stock_commodity"). Where("state = 1 AND imei = ?", scanCode).Count(&stockCount).Error if err != nil { return nil, err } if stockCount > 0 { return nil, errors.New("库存已有该串码商品") } var orders []ErpOrder for _, item := range commodity { if showConfig == "OFF" { err = orm.Eloquent.Table("erp_order"). Where("id = ? and pay_status = ? and is_print != ?", item.ErpOrderId, HavePaid, NoPrint). Order("audit_time DESC").Find(&orders).Error } else { err = orm.Eloquent.Table("erp_order"). Where("id = ? and pay_status = ?", item.ErpOrderId, HavePaid). Order("audit_time DESC").Find(&orders).Error } if err != nil && err != RecordNotFound { logger.Error("get erp_order err:", logger.Field("err", err)) return resp, err } if len(orders) == 0 { continue } else { break } } if len(orders) != 0 { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("查询失败:" + err.Error()) } if !CheckUserStore(orders[0].StoreId, sysUser) { return &ErpOrderListResp{}, errors.New("您没有该门店权限") } } if orders[0].StoreId != storeId && storeId != 0 { return &ErpOrderListResp{}, errors.New("非当前门店所售商品,需前往对应门店退货") } } // 添加付款、销售员、商品信息 erpOrderListSetCommodity(orders) erpOrderListSetCashier(orders) erpOrderListSetSalesman(orders) orders[0].Commodities = commodity if len(orders) != 0 { // 查询该串码商品是否已经退过货 resp.List = append(resp.List, orders[0]) } else { resp.List = orders } //跟之前保持一致 resp.Total = len(resp.List) resp.PageIndex = 1 resp.PageSize = 10 return resp, nil } // QueryListByCommodityName 通过商品名称查询列表 func QueryListByCommodityName(req *ErpOrderListReq, showConfig string, c *gin.Context) (*ErpOrderListResp, error) { resp := &ErpOrderListResp{ 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_order_commodity"). Select("erp_order_commodity.*, erp_order.*"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") if req.CommodityName != "" { // 商品名称 qs = qs.Where("erp_order_commodity.erp_commodity_name like ?", "%"+req.CommodityName+"%") } if showConfig == "OFF" { qs = qs.Where("invoice_code != ?", 0) } if req.BillSn != "" { qs = qs.Where("erp_order.bill_sn=?", req.BillSn) } if req.Tel != "" { // 用户手机号 qs = qs.Where("erp_order.tel=?", req.Tel) } if req.StoreId != 0 { // 门店ID qs = qs.Where("erp_order.store_id=?", req.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 { if len(storeList) == 1 { qs = qs.Where("erp_order.store_id = ?", storeList[0]) } else { qs = qs.Where("erp_order.store_id IN (?)", storeList) } } else { return nil, errors.New("用户未绑定门店") } } qs.Where("erp_order.pay_status = ?", HavePaid) es := qs var result []RetailDetailByJoin err := qs.Order("erp_order.audit_time DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&result).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } var count int64 err = es.Count(&count).Error if err != nil { logger.Errorf("QueryRetailMargin count err:", err.Error()) return nil, err } orders := packData(result) erpOrderListSetCashier(orders) erpOrderListSetSalesman(orders) resp.List = orders //跟之前保持一致 resp.Total = int(count) resp.PageIndex = page + 1 resp.PageSize = req.PageSize return resp, nil } // 数组转字符串 func intArrayToString(arr []uint32) string { var strArr []string for _, num := range arr { strArr = append(strArr, fmt.Sprintf("%d", num)) } return strings.Join(strArr, ",") } // 字符串转数组 func stringToIntArray(str string) ([]uint32, error) { var arr []uint32 strArr := strings.Split(str, ",") for _, s := range strArr { num, err := strconv.Atoi(s) if err != nil { return nil, err } arr = append(arr, uint32(num)) } return arr, nil } // 更新零售订单商品表的库存商品表主键id字段 func updateErpStockCommodityID(gdb *gorm.DB, commodity ErpOrderCommodity, nIdList []uint32) error { fmt.Println("nIdList is:", nIdList) strId := intArrayToString(nIdList) // 查找在库的非串码商品 var stockCommodity ErpStockCommodity err := orm.Eloquent.Table("erp_stock_commodity").Where("id = ?", strId).First(&stockCommodity).Error if err != nil { logger.Error("get erp_stock_commodity err:", logger.Field("err", err)) return err } err = gdb.Table("erp_order_commodity").Where("id = ?", commodity.ID). Updates(map[string]interface{}{ "erp_stock_commodity_id": strId, "retail_price": stockCommodity.RetailPrice, "wholesale_price": stockCommodity.WholesalePrice, "staff_cost_price": stockCommodity.StaffCostPrice, "sales_profit": commodity.ReceivedAmount - stockCommodity.WholesalePrice, "staff_profit": commodity.ReceivedAmount - stockCommodity.WholesalePrice - stockCommodity.StaffCostPrice, }).Error //Update("erp_stock_commodity_id", strId).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } return nil } // FindRightErpStockCommodityId 找一个可用的库存商品表主键ID func FindRightErpStockCommodityId(idList map[uint32][]uint32, commodityId uint32, stockCommodity []ErpStockCommodity) (uint32, error) { // 获取给定商品ID对应的ID列表 existingIds, ok := idList[commodityId] if !ok || len(existingIds) == 0 { // 如果对应的ID列表不存在或为空,直接返回第一个库存商品的ID if len(stockCommodity) > 0 { return stockCommodity[0].ID, nil } return 0, fmt.Errorf("empty stock commodity list") } // 创建一个 map 用于快速查找已有的 ID existingIdMap := make(map[uint32]bool) for _, id := range existingIds { existingIdMap[id] = true } // 遍历库存商品,找到第一个不在已有ID列表中的ID并返回 for _, item := range stockCommodity { if _, found := existingIdMap[item.ID]; !found { return item.ID, nil } } // 如果所有库存商品的ID都在已有的ID列表中,则返回错误 return 0, fmt.Errorf("no available ID found for commodity ID: %d", commodityId) } // UpdateStock 扣减or添加库存 // 零售订单: // 有串码,通过串码查找库存详情表,然后更改对应库存的状态为"已售"; // 非串码,通过门店id、商品id、商品名称查找库存详情表,找到库存时间最长的,然后更改其状态为"已售"。 // 同时扣减库存表对应的数量,-1 // 零售退货: // 有串码,通过串码查找库存详情表,然后更改对应库存的状态为"在库"; // 非串码,通过门店id、商品id、商品名称查找库存详情表,找到状态为"已售"且时间最近的单,将其状态改为"在库" // 同时扣减库存表对应的数量,+1 func UpdateStock(gdb *gorm.DB, erpOrder ErpOrder, state, payStatus, auditState int) error { var commodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Where("erp_order_id = ?", erpOrder.ID). Find(&commodities).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } usedStockCommodityIdList := make(map[uint32][]uint32) // 记录非串码商品已使用的商品库存表主键id if erpOrder.RetailType == RetailTypeSale { // 零售订单 for i, _ := range commodities { if commodities[i].IMEIType == 2 || commodities[i].IMEIType == 3 { // 串码商品 if commodities[i].IMEI == "" { return errors.New("串码为空") } err = gdb.Table("erp_stock_commodity").Where("imei = ?", commodities[i].IMEI). Updates(map[string]interface{}{ "state": state, "updated_at": time.Now(), }).Error // 状态更新为已销售;反审核则更新为在库 if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } if state == SoldOut { // 已售的订单才更新库存数量 err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count - ?", 1), "updated_at": time.Now(), }).Error // 库存数量-1 if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } } else { // 反审核时则库存数量+1 err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count + ?", 1), "updated_at": time.Now(), }).Error // 库存数量+1 if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } } } else { // 非串码商品 if state == InStock { // 零售订单反审核 err = gdb.Table("erp_stock_commodity").Where("id = ?", commodities[i].ErpStockCommodityID). Updates(map[string]interface{}{ "state": state, "updated_at": time.Now(), }).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 = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count + ?", 1), "updated_at": time.Now(), }).Error // 库存数量+1 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } err = gdb.Table("erp_order_commodity").Where("id = ?", commodities[i].ID). Updates(map[string]interface{}{ "erp_stock_commodity_id": "", }).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } } else { // 正常销售 if commodities[i].ErpStockCommodityID != "" { // 零售退货单反审核,默认当作销售单 err = gdb.Table("erp_stock_commodity").Where("id = ?", commodities[i].ErpStockCommodityID). Updates(map[string]interface{}{ "state": SoldOut, "updated_at": time.Now(), }).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 = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count - ?", 1), "updated_at": time.Now(), }).Error // 库存数量-1 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } } else { // 正常销售 var stockCommodity []ErpStockCommodity // 添加的一条零售商品包含的非串码商品可能不止1个 var stockCommodityIdList []uint32 // 通过门店id,商品id,查找状态为1-在库的非串码商品 err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and store_id = ? "+ "and state = ? and imei_type = ?", commodities[i].ErpCommodityId, erpOrder.StoreId, InStock, 1). Order("first_stock_time ASC").Find(&stockCommodity).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } if stockCommodity == nil || len(stockCommodity) == 0 { return errors.New("find commodity no stock ") } // 找一个可用的库存商品表主键ID,使用零售商品表的主键ID作为key rightId, err := FindRightErpStockCommodityId(usedStockCommodityIdList, commodities[i].ErpCommodityId, stockCommodity) if err != nil { return err } logger.Info("rightId is:", logger.Field("rightId", rightId)) if rightId == 0 { logger.Error("rightId is 0") return errors.New("查询商品库存出错") } // 优先出库库存时间最长的数据 err = gdb.Table("erp_stock_commodity").Where("id = ?", rightId). Updates(map[string]interface{}{ "state": state, "updated_at": time.Now(), }).Error // 状态更新为销售锁定中;反审核则更新为在库 if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } if state == SoldOut { // 已售的订单才更新库存数量 err = gdb.Table("erp_stock").Where("store_id = ? and erp_commodity_id = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count - ?", 1), "updated_at": time.Now(), }).Error // 库存数量-1 if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } } stockCommodityIdList = append(stockCommodityIdList, rightId) usedStockCommodityIdList[commodities[i].ErpCommodityId] = append(usedStockCommodityIdList[commodities[i].ErpCommodityId], rightId) err = updateErpStockCommodityID(gdb, commodities[i], stockCommodityIdList) if err != nil { return err } } } } } } else if erpOrder.RetailType == RetailTypeRejected { // 零售退货订单 for i, _ := range commodities { if commodities[i].IMEIType == 2 || commodities[i].IMEIType == 3 { // 串码商品 if commodities[i].IMEI == "" { return errors.New("串码为空") } err = gdb.Table("erp_stock_commodity").Where("imei = ?", commodities[i].IMEI). Updates(map[string]interface{}{ "state": InStock, "updated_at": Now(), }).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 = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count + ?", 1), "updated_at": time.Now(), }).Error // 库存数量+1 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } } else { // 非串码商品 var stockCommodity []ErpStockCommodity var stockCommodityIdList []uint32 stockCommodityID, _ := tools.StringToInt(commodities[i].ErpStockCommodityID) if stockCommodityID > 0 { err = gdb.Table("erp_stock_commodity").Where("id = ?", commodities[i].ErpStockCommodityID). Updates(map[string]interface{}{ "state": InStock, "updated_at": Now(), }).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 = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count + ?", 1), "updated_at": time.Now(), }).Error // 库存数量+1 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } } else { // 添加的一条零售商品包含的非串码商品可能不止1个 for j := 0; j < int(commodities[i].Count); j++ { // 通过门店id,商品id,查找状态为2-已售的非串码商品 err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id = ? and store_id = ? "+ "and state = ? and imei_type = ?", commodities[i].ErpCommodityId, erpOrder.StoreId, SoldOut, NoIMEICommodity). Order("first_stock_time 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") } // 找一个可用的库存商品表主键ID rightId, err := FindRightErpStockCommodityId(usedStockCommodityIdList, commodities[i].ErpCommodityId, stockCommodity) if err != nil { return err } err = gdb.Table("erp_stock_commodity").Where("id = ?", rightId). Updates(map[string]interface{}{ "state": InStock, "updated_at": Now(), }).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 = ?", erpOrder.StoreId, commodities[i].ErpCommodityId). Updates(map[string]interface{}{ "count": gorm.Expr("count + ?", 1), "updated_at": time.Now(), }).Error // 库存数量+1 if err != nil { logger.Error("RetailTypeRejected commodities err:", logger.Field("err", err)) return err } stockCommodityIdList = append(stockCommodityIdList, rightId) usedStockCommodityIdList[commodities[i].ID] = append(usedStockCommodityIdList[commodities[i].ID], rightId) } err = updateErpStockCommodityID(gdb, commodities[i], stockCommodityIdList) if err != nil { return err } } } } } else { return errors.New("订单类型错误") } // 判断用户是不是会员,只有会员才会积分,否则不积分 userInfo, err := GetUserInfoByUid(uint32(erpOrder.Uid)) if err != nil { logger.Error("UpdateStock GetUserInfoByUid err:", logger.Field("err", err)) return err } if IsInMemberLevels(userInfo.MemberLevel) { // 更新用户积分 var vmCount int var describe, event string if erpOrder.RetailType == RetailTypeSale && state == SoldOut && payStatus == HavePaid { // 零售订单,而且订单已支付,更新用户积分 if auditState == 2 { describe = "零售退货反审核获得积分" } else { describe = "零售销售获得积分" } event = VmEventErpOrderSale vmCount = tools.RoundFloat64(erpOrder.TotalAmount) if vmCount == 0 { // 0元无需积分 return nil } } else if erpOrder.RetailType == RetailTypeRejected { // 退货订单,扣减用户积分(需校验购物时已积分,有则扣除) // 查询退货订单对应销售订单信息 var erpSaleOrder ErpOrder err = orm.Eloquent.Table("erp_order").Where("id = ?", erpOrder.SaleOrderId).Find(&erpSaleOrder).Error if err != nil { logger.Errorf("query erpSaleOrder error, id is:", erpOrder.SaleOrderId) } var count int64 err = orm.Eloquent.Table("user_vm_record").Where("bill_sn = ?", erpSaleOrder.BillSn). Count(&count).Error if err != nil { logger.Errorf("query user_vm_record error, bill_sn is:", erpSaleOrder.BillSn) } // 积过分才扣除 if count > 0 { describe = "零售退货扣除积分" event = VmEventErpOrderReject vmCount = 0 - tools.RoundFloat64(erpOrder.TotalAmount) } else { return nil } } else { return nil } err = UserVmUpdate(gdb, erpOrder.BillSn, uint32(erpOrder.Uid), vmCount, event, describe) if err != nil { logger.Errorf("err:", err) return err } } return nil } // CheckIsOnlinePay 检查是否包含线上支付 func CheckIsOnlinePay(jCashier []ErpOrderCashier) bool { for _, cashier := range jCashier { //if cashier.CashierId == OnlinePay || strings.Contains(cashier.Name, "微信") || strings.Contains(cashier.Name, "支付宝") { if cashier.CashierId == OnlinePay { return true } } return false } // UpdateCoupon 更新对应优惠券的状态为已使用状态 func UpdateCoupon(gdb *gorm.DB, erpOrder ErpOrder, state int) error { var commodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Where("erp_order_id = ?", erpOrder.ID). Find(&commodities).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return err } for i, _ := range commodities { var userCoupon UserCoupon err := orm.Eloquent.Table("user_coupon").Where("id = ?", commodities[i].CouponID). Find(&userCoupon).Error if err != nil || err == RecordNotFound { return errors.New("未查询到优惠券") } var coupon Coupon err = orm.Eloquent.Table("coupon").Where("id = ?", userCoupon.CouponId).Find(&coupon).Error if err != nil || err == RecordNotFound { return errors.New("审核失败,未查询到优惠券信息:" + err.Error()) } couponState := userCoupon.State // 优惠券状态 if erpOrder.RetailType == RetailTypeSale { // 零售销售订单 if state == InStock { // 反审核,在库状态,此时优惠券应该更新为:未使用/已过期 if userCoupon.ActiveEnd.Before(time.Now()) { couponState = 3 // 已过期 } else { couponState = 1 // 未使用 } } else if state == SoldOut { // 审核,已售状态,此时优惠券应该更新为:已使用 switch userCoupon.State { case 2: // 已使用 return errors.New(fmt.Sprintf("审核失败:优惠券[%s]已使用,请重新选择", coupon.Name)) case 3: // 已过期 return errors.New(fmt.Sprintf("审核失败:优惠券[%s]已过期,请重新选择", coupon.Name)) } couponState = 2 // 已使用 } } else if erpOrder.RetailType == RetailTypeRejected { // 零售退货订单 if state == InStock { // 审核,在库状态,此时优惠券应该更新为:未使用/已过期 if userCoupon.ActiveEnd.Before(time.Now()) { couponState = 3 // 已过期 } else { couponState = 1 // 未使用 } } else if state == SoldOut { // 反审核,已售状态,此时优惠券应该更新为:已使用 switch userCoupon.State { case 2: // 已使用 return errors.New(fmt.Sprintf("反审核失败:优惠券[%s]已使用,请重新选择", coupon.Name)) case 3: // 已过期 return errors.New(fmt.Sprintf("反审核失败:优惠券[%s]已过期,请重新选择", coupon.Name)) } couponState = 2 // 已使用 } } err = gdb.Table("user_coupon").Where("id = ?", commodities[i].CouponID).Updates(map[string]interface{}{ "state": couponState, "updated_at": time.Now(), "use_time": time.Now(), }).Error if err != nil { logger.Error("UpdateCoupon err:", logger.Field("err", err)) return err } } return nil } // NewErpBillSn 生成零售订单号 func NewErpBillSn() string { nowTime := time.Now() rand.Seed(nowTime.UnixNano()) max := 1 for { if max > 5 { logger.Error("create sn err") return "" } random := rand.Int31n(9999) + 1000 sn := fmt.Sprintf("%s%d", nowTime.Format("060102"), random) exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM erp_order WHERE bill_sn='%s'", sn)) if err != nil { logger.Error("exist sn err") } if !exist { return sn } max++ } } // 添加零售订单的银行流水号 func erpOrderSetBankTrxNoAI(list []ErpOrder) { if len(list) == 0 { return // 如果列表为空,直接返回 } // 提取所有的 BillSn billSnList := make([]string, len(list)) for i, order := range list { billSnList[i] = order.BillSn } // 批量查询 var orderPayWays []ErpOrderRecord err := orm.Eloquent.Table("erp_order_record"). Where("bill_sn IN (?) AND status = ?", billSnList, PayOk). Find(&orderPayWays).Error if err != nil { logger.Error("SetBankTrxNo query erp_order_record err:", logger.Field("err", err)) return } // 将银行流水号映射到订单 bankTrxNoMap := make(map[string]string) for _, record := range orderPayWays { bankTrxNoMap[record.BillSn] = record.BankTrxNo } // 设置每个订单的银行流水号 for i := range list { list[i].BankTrxNo = bankTrxNoMap[list[i].BillSn] } } // 添加零售订单的银行流水号 func erpOrderSetBankTrxNo(list []ErpOrder) { for i, _ := range list { list[i].SetBankTrxNo() } } func (m *ErpOrder) SetBankTrxNo() { var orderPayWay ErpOrderRecord err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", m.BillSn, PayOk).Find(&orderPayWay).Error if err != nil { logger.Error("SetBankTrxNo query erp_order_record err:", logger.Field("err", err)) } m.BankTrxNo = orderPayWay.BankTrxNo } // ErpOrderRetailDetailSetCommodity 添加零售明细中订单的商品信息 func ErpOrderRetailDetailSetCommodity(list []ErpOrder) { for i, _ := range list { list[i].SetRetailDetailCommodity() list[i].StorePer = tools.RoundToTwoDecimalPlaces(list[i].StorePer) if list[i].RetailType == RetailTypeRejected { // 退货订单,金额需要转换为负值 list[i].TotalRetailPrice = -math.Abs(list[i].TotalRetailPrice) list[i].TotalAmount = -math.Abs(list[i].TotalAmount) list[i].TotalCount = -int32(math.Abs(float64(list[i].TotalCount))) //list[i].TotalSalesProfit = -math.Abs(list[i].TotalSalesProfit) //list[i].TotalStaffProfit = -math.Abs(list[i].TotalStaffProfit) list[i].TotalSalesProfit = -list[i].TotalSalesProfit list[i].TotalStaffProfit = -list[i].TotalStaffProfit list[i].TotalDiscount = -math.Abs(list[i].TotalDiscount) list[i].VmCount = -uint32(math.Abs(float64(list[i].VmCount))) if list[i].TotalStaffProfit > 0 { list[i].StorePer = math.Abs(list[i].StorePer) } else { list[i].StorePer = -math.Abs(list[i].StorePer) } } } } func (m *ErpOrder) SetRetailDetailCommodity() { var orderCommodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Where("erp_order_id = ?", m.ID).Find(&orderCommodities).Error if err != nil { logger.Error("SetCommodity query erp_order_commodity err:", logger.Field("err", err)) } var respOrderCommodities []ErpOrderCommodity for _, item := range orderCommodities { if m.RetailType == RetailTypeRejected { // 退货订单,金额需要转换为负值 item.Count = -item.Count item.RetailPrice = -item.RetailPrice item.SalePrice = -item.SalePrice item.Amount = -item.Amount item.SaleDiscount = -item.SaleDiscount item.MemberDiscount = -item.MemberDiscount item.VmDiscount = -item.VmDiscount item.ReceivedAmount = -item.ReceivedAmount item.RejectedAmount = -item.RejectedAmount item.SalesProfit = -item.SalesProfit item.StaffProfit = -item.StaffProfit item.StaffCostPrice = -item.StaffCostPrice item.WholesalePrice = -item.WholesalePrice item.CouponDiscount = -item.CouponDiscount } item.StaffPrice = item.StaffCostPrice + item.WholesalePrice if item.IMEIType == 2 || item.IMEIType == 3 || item.IMEI != "" { // 串码 respOrderCommodities = append(respOrderCommodities, item) } else { // 非串码 idList, err := stringToIntArray(item.ErpStockCommodityID) if err != nil { respOrderCommodities = append(respOrderCommodities, item) continue } for _, stockCommodityId := range idList { var orderCommodity ErpOrderCommodity orderCommodity = item if m.RetailType == RetailTypeRejected { // 退货订单,数量需要转换为负值 orderCommodity.Count = -1 } else { orderCommodity.Count = 1 } nCount := math.Abs(float64(item.Count)) orderCommodity.SaleDiscount = item.SaleDiscount / nCount orderCommodity.VmDiscount = item.VmDiscount / nCount orderCommodity.ReceivedAmount = item.ReceivedAmount / nCount orderCommodity.RejectedAmount = item.RejectedAmount / nCount orderCommodity.SalesProfit = item.SalesProfit / nCount orderCommodity.StaffProfit = item.StaffProfit / nCount // 查询库存商品信息 var stockCommodity ErpStockCommodity err = orm.Eloquent.Table("erp_stock_commodity"). Where("id = ?", stockCommodityId).Find(&stockCommodity).Error if err != nil { respOrderCommodities = append(respOrderCommodities, item) continue } orderCommodity.ErpSupplierId = stockCommodity.ErpSupplierId orderCommodity.ErpSupplierName = stockCommodity.ErpSupplierName orderCommodity.WholesalePrice = stockCommodity.WholesalePrice orderCommodity.StaffCostPrice = stockCommodity.StaffCostPrice orderCommodity.StaffPrice = orderCommodity.WholesalePrice + orderCommodity.StaffCostPrice if m.RetailType == RetailTypeRejected { // 退货订单,数量需要转换为负值 orderCommodity.WholesalePrice = -orderCommodity.WholesalePrice orderCommodity.StaffCostPrice = -orderCommodity.StaffCostPrice orderCommodity.StaffPrice = -orderCommodity.StaffPrice } respOrderCommodities = append(respOrderCommodities, orderCommodity) } } } m.Commodities = respOrderCommodities } // ErpOrderRetailDetailSetCommodityAI 添加零售明细中订单的商品信息 func ErpOrderRetailDetailSetCommodityAI(list []ErpOrder) { // 构建订单ID的切片 var orderIDs []uint32 for _, order := range list { orderIDs = append(orderIDs, order.ID) } // 批量查询所有商品信息 var orderCommodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Find(&orderCommodities).Error if err != nil { logger.Error("Batch query erp_order_commodity err:", logger.Field("err", err)) return } // 创建一个映射,便于根据订单ID查找商品信息 commodityMap := make(map[uint32][]ErpOrderCommodity) for _, commodity := range orderCommodities { commodityMap[commodity.ErpOrderId] = append(commodityMap[commodity.ErpOrderId], commodity) } // 批量查询所有已售的非串码商品信息 var stockCommodities []ErpStockCommodity err = orm.Eloquent.Table("erp_stock_commodity").Where("state = ? and imei = ?", SoldOut, "").Find(&stockCommodities).Error if err != nil { logger.Error("Batch query erp_stock_commodity err:", logger.Field("err", err)) return } // 创建一个映射,便于根据库存商品ID查找商品信息 stockMap := make(map[uint32]ErpStockCommodity) for _, stock := range stockCommodities { stockMap[stock.ID] = stock } // 批量查询销售员信息 var salesmanInfo []ErpOrderSales err = orm.Eloquent.Model(&ErpOrderSales{}).Find(&salesmanInfo).Error if err != nil { logger.Error("Batch query ErpOrderSales err:", logger.Field("err", err)) return } // 创建用户信息的映射,便于快速查找 salesMap := make(map[uint32][]ErpOrderSales) for _, user := range salesmanInfo { salesMap[user.ErpOrderId] = append(salesMap[user.ErpOrderId], user) } // 批量查询系统用户信息 userMap, err := GetSysUserInfoByIds() if err != nil { return } // 批量查询商品信息 var erpCommodities []ErpCommodity err = orm.Eloquent.Table("erp_commodity").Find(&erpCommodities).Error if err != nil { logger.Error("Batch query erp_commodity err:", logger.Field("err", err)) return } erpCommodityMap := make(map[uint32]ErpCommodity) for _, commodity := range erpCommodities { erpCommodityMap[commodity.ID] = commodity } for i := range list { list[i].SetRetailDetailCommodityAI(commodityMap[list[i].ID], stockMap) list[i].StorePer = tools.RoundToTwoDecimalPlaces(list[i].StorePer) if list[i].RetailType == RetailTypeRejected { // 退货订单,金额需要转换为负值 list[i].TotalRetailPrice = -math.Abs(list[i].TotalRetailPrice) list[i].TotalAmount = -math.Abs(list[i].TotalAmount) list[i].TotalCount = -int32(math.Abs(float64(list[i].TotalCount))) list[i].TotalSalesProfit = -list[i].TotalSalesProfit list[i].TotalStaffProfit = -list[i].TotalStaffProfit list[i].TotalDiscount = -math.Abs(list[i].TotalDiscount) list[i].VmCount = -uint32(math.Abs(float64(list[i].VmCount))) if list[i].TotalStaffProfit > 0 { list[i].StorePer = math.Abs(list[i].StorePer) } else { list[i].StorePer = -math.Abs(list[i].StorePer) } } _ = list[i].SetOrderSalesmanAI(erpCommodityMap, salesMap, userMap) } } func (m *ErpOrder) SetRetailDetailCommodityAI(orderCommodities []ErpOrderCommodity, stockMap map[uint32]ErpStockCommodity) { var respOrderCommodities []ErpOrderCommodity for _, item := range orderCommodities { fmt.Println("orderId is:", item.ErpOrderId) if m.RetailType == RetailTypeRejected { // 退货订单,金额需要转换为负值 item.Count = -item.Count item.RetailPrice = -item.RetailPrice item.SalePrice = -item.SalePrice item.Amount = -item.Amount item.SaleDiscount = -item.SaleDiscount item.MemberDiscount = -item.MemberDiscount item.VmDiscount = -item.VmDiscount item.ReceivedAmount = -item.ReceivedAmount item.RejectedAmount = -item.RejectedAmount item.SalesProfit = -item.SalesProfit item.StaffProfit = -item.StaffProfit item.StaffCostPrice = -item.StaffCostPrice item.WholesalePrice = -item.WholesalePrice item.CouponDiscount = -item.CouponDiscount } item.StaffPrice = item.StaffCostPrice + item.WholesalePrice if item.IMEIType == 2 || item.IMEIType == 3 || item.IMEI != "" { // 串码 respOrderCommodities = append(respOrderCommodities, item) } else { // 非串码 idList, err := stringToIntArray(item.ErpStockCommodityID) if err != nil { respOrderCommodities = append(respOrderCommodities, item) continue } for _, stockCommodityId := range idList { fmt.Println("stockCommodityId is:", stockCommodityId) var orderCommodity ErpOrderCommodity orderCommodity = item if m.RetailType == RetailTypeRejected { // 退货订单,数量需要转换为负值 orderCommodity.Count = -1 } else { orderCommodity.Count = 1 } nCount := math.Abs(float64(item.Count)) orderCommodity.SaleDiscount = item.SaleDiscount / nCount orderCommodity.VmDiscount = item.VmDiscount / nCount orderCommodity.ReceivedAmount = item.ReceivedAmount / nCount orderCommodity.RejectedAmount = item.RejectedAmount / nCount orderCommodity.SalesProfit = item.SalesProfit / nCount orderCommodity.StaffProfit = item.StaffProfit / nCount // 获取库存商品信息 stockCommodity, exists := stockMap[stockCommodityId] if !exists { respOrderCommodities = append(respOrderCommodities, item) continue } orderCommodity.ErpSupplierId = stockCommodity.ErpSupplierId orderCommodity.ErpSupplierName = stockCommodity.ErpSupplierName orderCommodity.WholesalePrice = stockCommodity.WholesalePrice orderCommodity.StaffCostPrice = stockCommodity.StaffCostPrice orderCommodity.StaffPrice = orderCommodity.WholesalePrice + orderCommodity.StaffCostPrice if m.RetailType == RetailTypeRejected { // 退货订单,数量需要转换为负值 orderCommodity.WholesalePrice = -orderCommodity.WholesalePrice orderCommodity.StaffCostPrice = -orderCommodity.StaffCostPrice orderCommodity.StaffPrice = -orderCommodity.StaffPrice } respOrderCommodities = append(respOrderCommodities, orderCommodity) } } } m.Commodities = respOrderCommodities } // 添加订单的商品信息 func erpOrderListSetCommodity(list []ErpOrder) { for i, _ := range list { list[i].SetCommodity() } } func (m *ErpOrder) SetCommodity() { var orderCommodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Where("erp_order_id = ?", m.ID).Find(&orderCommodities).Error if err != nil { logger.Error("SetCommodity query erp_order_commodity err:", logger.Field("err", err)) } commodities := mergeOrderCommodities(orderCommodities) for i, _ := range commodities { categoryInfo, _ := GetErpCategory(commodities[i].ErpCategoryId) if categoryInfo != nil { commodities[i].CategoryNumber = categoryInfo.Number } commodityInfo, _ := GetCommodity(commodities[i].ErpCommodityId) if categoryInfo != nil { commodities[i].CommoditySerialNumber = commodityInfo.SerialNumber } } m.Commodities = commodities } // 合并重复的商品数据 func mergeOrderCommodities(orderCommodities []ErpOrderCommodity) []ErpOrderCommodity { // 创建一个map来存储每个(ErpOrderId, ErpCommodityId)组合的合并结果 type key struct { ErpOrderId uint32 ErpCommodityId uint32 IMEI string CouponID uint32 SalePrice float64 } commodityMap := make(map[key]*ErpOrderCommodity) // 遍历orderCommodities,将相同ErpCommodityId的数量累加 for _, commodity := range orderCommodities { comboKey := key{ ErpOrderId: commodity.ErpOrderId, ErpCommodityId: commodity.ErpCommodityId, IMEI: commodity.IMEI, CouponID: commodity.CouponID, SalePrice: commodity.SalePrice, } if existingCommodity, exists := commodityMap[comboKey]; exists { existingCommodity.Count += commodity.Count existingCommodity.SalesProfit += commodity.SalesProfit existingCommodity.StaffProfit += commodity.StaffProfit existingCommodity.SaleDiscount += commodity.SaleDiscount existingCommodity.MemberDiscount += commodity.MemberDiscount existingCommodity.ReceivedAmount += commodity.ReceivedAmount existingCommodity.RejectedCount += commodity.RejectedCount existingCommodity.RejectedAmount += commodity.RejectedAmount stockCommodityID, _ := tools.StringToInt(commodity.ErpStockCommodityID) if stockCommodityID > 0 { existingCommodity.ErpStockCommodityID = strings.Join([]string{existingCommodity.ErpStockCommodityID, commodity.ErpStockCommodityID}, ",") } } else { // 使用commodity的副本避免对原始数据的修改 commodityCopy := commodity commodityMap[comboKey] = &commodityCopy } } // 将map转换回orderCommodities列表 var mergedCommodities []ErpOrderCommodity for _, commodity := range commodityMap { // 对float64类型的字段进行四舍五入处理 commodity.SalesProfit = tools.RoundFloat(commodity.SalesProfit) commodity.StaffProfit = tools.RoundFloat(commodity.StaffProfit) commodity.SaleDiscount = tools.RoundFloat(commodity.SaleDiscount) commodity.MemberDiscount = tools.RoundFloat(commodity.MemberDiscount) commodity.ReceivedAmount = tools.RoundFloat(commodity.ReceivedAmount) commodity.RejectedPrice = tools.RoundFloat(commodity.RejectedPrice) commodity.RejectedAmount = tools.RoundFloat(commodity.RejectedAmount) commodity.StaffCostPrice = tools.RoundFloat(commodity.StaffCostPrice) commodity.WholesalePrice = tools.RoundFloat(commodity.WholesalePrice) mergedCommodities = append(mergedCommodities, *commodity) } // 对mergedCommodities按ErpCommodityId进行排序 sort.Slice(mergedCommodities, func(i, j int) bool { return mergedCommodities[i].ID < mergedCommodities[j].ID }) return mergedCommodities } // 添加订单的销售员信息 func erpOrderListSetSalesmanByRetailDetail(userId uint32, list []ErpOrder) { for i, _ := range list { _ = list[i].SetOrderSalesmanRetailDetail(userId) } } func (m *ErpOrder) SetOrderSalesmanRetailDetail(userId uint32) error { var salesProfit, staffProfit, totalStaffProfit float64 //获取销售毛利、员工毛利数据 for _, item := range m.Commodities { erpCommodity, err := GetCommodity(item.ErpCommodityId) if err != nil { logger.Error("GetCommodity err:", logger.Field("err", err)) } salesProfit += item.SalesProfit * erpCommodity.Brokerage1 * 0.01 staffProfit += item.StaffProfit * erpCommodity.Brokerage2 * 0.01 totalStaffProfit += item.StaffProfit } // 四舍五入并保留两位小数 salesProfit = math.Round(salesProfit*100) / 100 staffProfit = math.Round(staffProfit*100) / 100 totalStaffProfit = math.Round(totalStaffProfit*100) / 100 var salesmanInfo []ErpOrderSales err := orm.Eloquent.Model(&ErpOrderSales{}).Where("erp_order_id = ?", m.ID).Find(&salesmanInfo).Error if err != nil { return err } var salesmanList []ErpOrderSales for _, item := range salesmanInfo { item.SalesProfitPer = salesProfit / float64(len(salesmanInfo)) item.StaffProfitPer = staffProfit / float64(len(salesmanInfo)) // 获取员工毛利 userInfo, err := GetSysUserInfoById(item.Uid) if err != nil { logger.Error("GetSysUserInfoByUid err:", logger.Field("err", err)) } item.Name = userInfo.NickName item.SalesmanPer = totalStaffProfit * userInfo.SalesCommRate * 0.01 / float64(len(salesmanInfo)) //if m.RetailType == RetailTypeRejected { // item.SalesProfitPer = -item.SalesProfitPer // item.StaffProfitPer = -item.StaffProfitPer // item.SalesmanPer = -item.SalesmanPer //} if userId != item.Uid { item.SalesProfitPer = 0 item.StaffProfitPer = 0 item.SalesmanPer = 0 } salesmanList = append(salesmanList, item) } if len(salesmanList) == 0 { m.Salesman = []ErpOrderSales{} } else { m.Salesman = salesmanList } m.SalesmanList = "" return nil } // GetSysUserInfoByIds 批量获取用户信息 func GetSysUserInfoByIds() (map[uint32]SysUser, error) { var userInfos []SysUser err := orm.Eloquent.Debug().Table("sys_user").Find(&userInfos).Error if err != nil { logger.Error("Batch query sys_user err:", logger.Field("err", err)) return nil, err } // 创建用户信息的映射,便于快速查找 userMap := make(map[uint32]SysUser) for _, user := range userInfos { if countDigits(user.Uid) == 8 { userMap[user.Uid] = user } userMap[uint32(user.UserId)] = user } return userMap, nil } func (m *ErpOrder) SetOrderSalesmanAI(erpCommodityMap map[uint32]ErpCommodity, salesMap map[uint32][]ErpOrderSales, userMap map[uint32]SysUser) error { var salesProfit, staffProfit, totalStaffProfit float64 //获取销售毛利、员工毛利数据 for _, item := range m.Commodities { erpCommodity := erpCommodityMap[item.ErpCommodityId] salesProfit += item.SalesProfit * erpCommodity.Brokerage1 * 0.01 staffProfit += item.StaffProfit * erpCommodity.Brokerage2 * 0.01 totalStaffProfit += item.StaffProfit } // 四舍五入并保留两位小数 salesProfit = math.Round(salesProfit*100) / 100 staffProfit = math.Round(staffProfit*100) / 100 totalStaffProfit = math.Round(totalStaffProfit*100) / 100 var salesmanInfo []ErpOrderSales salesmanInfo = salesMap[m.ID] var salesmanList []ErpOrderSales for _, item := range salesmanInfo { item.SalesProfitPer = salesProfit / float64(len(salesmanInfo)) item.StaffProfitPer = staffProfit / float64(len(salesmanInfo)) // 获取员工毛利 userInfo := userMap[item.Uid] item.Name = userInfo.NickName item.SalesmanPer = totalStaffProfit * userInfo.SalesCommRate * 0.01 / float64(len(salesmanInfo)) salesmanList = append(salesmanList, item) } if len(salesmanList) == 0 { m.Salesman = []ErpOrderSales{} } else { m.Salesman = salesmanList } m.SalesmanList = "" return nil } // 添加订单的销售员信息 func erpOrderListSetSalesman(list []ErpOrder) { for i, _ := range list { _ = list[i].SetOrderSalesman() } } func (m *ErpOrder) SetOrderSalesman() error { var salesProfit, staffProfit, totalStaffProfit float64 //获取销售毛利、员工毛利数据 for _, item := range m.Commodities { erpCommodity, err := GetCommodity(item.ErpCommodityId) if err != nil { logger.Error("GetCommodity err:", logger.Field("err", err)) } salesProfit += item.SalesProfit * erpCommodity.Brokerage1 * 0.01 staffProfit += item.StaffProfit * erpCommodity.Brokerage2 * 0.01 totalStaffProfit += item.StaffProfit } // 四舍五入并保留两位小数 salesProfit = math.Round(salesProfit*100) / 100 staffProfit = math.Round(staffProfit*100) / 100 totalStaffProfit = math.Round(totalStaffProfit*100) / 100 var salesmanInfo []ErpOrderSales err := orm.Eloquent.Model(&ErpOrderSales{}).Where("erp_order_id = ?", m.ID).Find(&salesmanInfo).Error if err != nil { return err } var salesmanList []ErpOrderSales for _, item := range salesmanInfo { item.SalesProfitPer = salesProfit / float64(len(salesmanInfo)) item.StaffProfitPer = staffProfit / float64(len(salesmanInfo)) // 获取员工毛利 userInfo, err := GetSysUserInfoById(item.Uid) if err != nil { logger.Error("GetSysUserInfoByUid err:", logger.Field("err", err)) } item.Name = userInfo.NickName item.SalesmanPer = totalStaffProfit * userInfo.SalesCommRate * 0.01 / float64(len(salesmanInfo)) //if m.RetailType == RetailTypeRejected { // item.SalesProfitPer = -item.SalesProfitPer // item.StaffProfitPer = -item.StaffProfitPer // item.SalesmanPer = -item.SalesmanPer //} salesmanList = append(salesmanList, item) } if len(salesmanList) == 0 { m.Salesman = []ErpOrderSales{} } else { m.Salesman = salesmanList } m.SalesmanList = "" return nil } func (m *ErpOrder) SetSalesman() { if m.SalesmanList != "" { var salesman []ErpOrderSales err := json.Unmarshal([]byte(m.SalesmanList), &salesman) if err != nil { logger.Error("unmarshal err:", logger.Field("err", err)) } m.Salesman = salesman m.SalesmanList = "" } } func (m *ErpOrderCreateReq) GetSalesmanList() (string, error) { var salesProfit, staffProfit, totalStaffProfit float64 //获取销售毛利、员工毛利数据 for _, item := range m.ErpOrderCommodities { erpCommodity, err := GetCommodity(item.ErpCommodityId) if err != nil { logger.Error("GetCommodity err:", logger.Field("err", err)) } salesProfit += item.SalesProfit * erpCommodity.Brokerage1 * 0.01 staffProfit += item.StaffProfit * erpCommodity.Brokerage2 * 0.01 totalStaffProfit += item.StaffProfit } var salesmanList []ErpOrderSales for _, item := range m.Salesman { item.SalesProfitPer = salesProfit / float64(len(m.Salesman)) item.StaffProfitPer = staffProfit / float64(len(m.Salesman)) // 获取员工毛利 userInfo, err := GetSysUserInfoById(item.Uid) if err != nil { logger.Error("GetSysUserInfoByUid err:", logger.Field("err", err)) } item.Name = userInfo.NickName //item.SalesmanPer = staffProfit * userInfo.SalesCommRate * 0.01 / float64(len(m.Salesman)) item.SalesmanPer = totalStaffProfit * userInfo.SalesCommRate * 0.01 / float64(len(m.Salesman)) // 四舍五入并保留两位小数 item.SalesProfitPer = math.Round(item.SalesProfitPer*100) / 100 item.StaffProfitPer = math.Round(item.StaffProfitPer*100) / 100 item.SalesmanPer = math.Round(item.SalesmanPer*100) / 100 salesmanList = append(salesmanList, item) } m.Salesman = salesmanList jSalesman, err := json.Marshal(m.Salesman) if err != nil { logger.Error("salesman marshal err:", logger.Field("err", err)) return "", err } return string(jSalesman), nil } // 添加订单的付款信息 func erpOrderListSetCashier(list []ErpOrder) { for i, _ := range list { list[i].SetErpCashier() } } func (m *ErpOrder) SetErpCashier() { //if m.CashierList != "" && m.RetailType == RetailTypeSale { // 临时限制,退货订单不展示具体付款方式 if m.CashierList != "" { var cashiers []ErpOrderCashier err := json.Unmarshal([]byte(m.CashierList), &cashiers) if err != nil { logger.Error("unmarshal err:", logger.Field("err", err)) } if m.RetailType == RetailTypeRejected { for i, _ := range cashiers { cashiers[i].Amount = -cashiers[i].Amount } } m.Cashiers = cashiers m.CashierList = "" } } // 添加订单的付款信息 func erpRetailDetailSetCashier(list []ErpOrder) { for i, _ := range list { list[i].SetErpRetailDetailCashier() } } func (m *ErpOrder) SetErpRetailDetailCashier() { if m.CashierList != "" { var cashiers []ErpOrderCashier err := json.Unmarshal([]byte(m.CashierList), &cashiers) if err != nil { logger.Error("unmarshal err:", logger.Field("err", err)) } if m.RetailType == RetailTypeRejected { for i, _ := range cashiers { cashiers[i].Amount = -cashiers[i].Amount } } var nTotalOtherAmount float64 var newCashiers []ErpOrderCashier for _, item := range cashiers { if item.CashierId > 4 { nTotalOtherAmount += item.Amount } else { newCashiers = append(newCashiers, item) } } // 将所有其他类型的支付方式合并到一起 newCashiers = append(newCashiers, ErpOrderCashier{8090, "", nTotalOtherAmount}) m.Cashiers = newCashiers m.CashierList = "" } } func GetErpOrderCommodityMap(ids []uint32, req *ErpOrderCreateReq) (map[uint32]ErpOrderCommodity, error) { commodityMap := make(map[uint32]ErpOrderCommodity, 0) if len(ids) == 0 { return commodityMap, nil } var commodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id "). Where("erp_order_commodity.erp_commodity_id IN (?) and erp_order.store_id = ? "+ "and erp_order.retail_type = ? and erp_order.pay_status = ? and erp_order.tel = ?", ids, req.StoreId, RetailTypeSale, HavePaid, req.Tel). Find(&commodities).Order("audit_time DESC").Error //var commodities []ErpOrderCommodity //err := orm.Eloquent.Table("erp_order_commodity").Where("erp_commodity_id IN (?)", ids).Find(&commodities).Error if err != nil { logger.Error("commodities err:", logger.Field("err", err)) return commodityMap, err } // 合并数据 commodities = mergeOrderCommodities(commodities) for i, _ := range commodities { commodityMap[commodities[i].ErpCommodityId] = commodities[i] } return commodityMap, nil } func SetUserInfo(tel string) { var user UserInfo err := orm.Eloquent.Table("user").Where("tel=?", tel).Find(&user).Error if err != nil { logger.Error("user err:", logger.Field("err", err)) return } if user.FirstRetailOrder == nil { err := orm.Eloquent.Table("user").Where("uid=?", user.Uid). Updates(map[string]interface{}{"first_retail_order": time.Now()}).Error if err != nil { logger.Error("update user err:", logger.Field("err", err)) } } } // SetInvoice 设置发票内容 func SetInvoice(req *ErpOrderAddInvoiceReq, c *gin.Context) error { sysUser, err := GetSysUserByCtx(c) if err != nil { logger.Errorf("err:%#v", err) return err } var orderInfo ErpOrder err = orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Find(&orderInfo).Error if err != nil { logger.Error("操作失败,未查询到订单", logger.Field("err", err)) return errors.New("未查询到订单") } if orderInfo.State == ErpOrderStateUnAudit { logger.Errorf("order delete err, state is:", orderInfo.State) return errors.New("订单未审核") } if orderInfo.PayStatus != 2 { logger.Errorf("order delete err, state is:", orderInfo.State) return errors.New("订单未支付") } if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { // 校验入参门店是否包含在用户所有门店中,是否过期 if !CheckUserStore(orderInfo.StoreId, sysUser) { return errors.New("操作失败:您没有该门店权限") } } err = orm.Eloquent.Table("erp_order").Where("id = ?", orderInfo.ID).Updates(map[string]interface{}{ "invoice_code": req.InvoiceCode, "invoice_number": req.InvoiceNumber, }).Error if err != nil { logger.Error("AddInvoice err:", logger.Field("err", err)) return err } return nil } // DeleteOrder 删除订单 func DeleteOrder(req *ErpOrderDeleteReq, c *gin.Context) error { sysUser, err := GetSysUserByCtx(c) if err != nil { logger.Errorf("err:%#v", err) return err } var orderInfo ErpOrder err = orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Find(&orderInfo).Error if err != nil { logger.Error("order delete err:", logger.Field("err", err)) return errors.New("未查询到订单") } if orderInfo.State != ErpOrderStateUnAudit { logger.Errorf("order delete err, state is:", orderInfo.State) return errors.New("已审核订单不能删除") } if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { // 校验入参门店是否包含在用户所有门店中,是否过期 if !CheckUserStore(orderInfo.StoreId, sysUser) { return errors.New("操作失败:您没有该门店权限") } } var orderCommodities []ErpOrderCommodity err = orm.Eloquent.Table("erp_order_commodity").Where("erp_order_id = ?", orderInfo.ID).Find(&orderCommodities).Error if err != nil { logger.Error("order delete err:", logger.Field("err", err)) return errors.New("未查询到订单商品信息") } begin := orm.Eloquent.Begin() err = begin.Delete(&orderCommodities).Error if err != nil { begin.Rollback() logger.Error("order delete1 err:", logger.Field("err", err)) return err } err = begin.Delete(orderInfo).Error if err != nil { begin.Rollback() logger.Error("order delete2 err:", logger.Field("err", err)) return err } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("order delete3 err:", logger.Field("err", err)) return err } return nil } // 生成序列号 func genErpOrderSerialNo() string { currentDate := time.Now().Format("20060102") tradeNO := "sale" + currentDate + pay.RandomNumString(10000000, 99999999) return tradeNO } // GetErpOrderSn 生成零售订单号 func GetErpOrderSn() string { var erpOrderSn string for { erpOrderSn = genErpOrderSerialNo() var count int64 err := orm.Eloquent.Table("erp_order_record").Where("out_order_no=?", erpOrderSn).Count(&count) if err != nil { logger.Error("err:", logger.Field("err", err)) } if count == 0 { break } } return erpOrderSn } // 查询是否有已支付的订单 func checkIsPayOk(billSn string) bool { var count int64 err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, PayOk). Count(&count).Error if err != nil { logger.Error("checkIsPayOk err:", logger.Field("err", err)) } return count > 0 } // 查询是否有待支付的订单 func checkIsPaying(billSn string) bool { var count int64 err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, Paying). Count(&count).Error if err != nil { logger.Error("checkIsPayOk err:", logger.Field("err", err)) } return count > 0 } // 查询是否有刚初始化的订单 func checkIsPayInit(billSn string) bool { var count int64 err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", billSn, PayInit). Count(&count).Error if err != nil { logger.Error("checkIsPayOk err:", logger.Field("err", err)) } return count > 0 } // ErpOrderPay 零售订单支付 func ErpOrderPay(req *ErpOrderPayReq, c *gin.Context) (*ErpOrderPayResp, error) { sysUser, err := GetSysUserByCtx(c) if err != nil { logger.Errorf("err:%#v", err) return nil, err } resp := &ErpOrderPayResp{ Status: PayFailed, } //通过单据号查询收款金额 var orderInfo ErpOrder err = orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Find(&orderInfo).Error if err != nil { logger.Error("未查询到订单:", logger.Field("err", err)) return resp, err } if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { // 校验入参门店是否包含在用户所有门店中,是否过期 if !CheckUserStore(orderInfo.StoreId, sysUser) { return nil, errors.New("操作失败:您没有该门店权限") } } if orderInfo.PayStatus == HavePaid { logger.Error("ErpOrderPay err, 订单已支付") return resp, errors.New("订单已支付") } if orderInfo.PayStatus != WaitForPaying { logger.Error("ErpOrderPay err, 订单不是待支付状态") return resp, errors.New("订单不是待支付状态") } var cashiers []ErpOrderCashier err = json.Unmarshal([]byte(orderInfo.CashierList), &cashiers) if err != nil { logger.Error("unmarshal CashierList err:", logger.Field("err", err)) } var amount float64 for i, _ := range cashiers { if cashiers[i].CashierId == OnlinePay { //线上支付 amount = cashiers[i].Amount break } } if amount == 0 { logger.Error("ErpOrderPay err, 订单金额为0") return resp, errors.New("订单金额为0") } // dev环境测试默认金额都为0.1元 if config.ApplicationConfig.Mode == "dev" { amount = 0.1 } // 查询是否有已支付的订单 if checkIsPayOk(req.BillSn) { // 更新零售订单表 err = orm.Eloquent.Model(&ErpOrder{}).Where("id = ?", orderInfo.ID). Updates(map[string]interface{}{"pay_status": HavePaid}).Error if err != nil { logger.Error("ErpOrderPay update erp_order_record err:", logger.Field("err", err)) } resp.Status = PayOk logger.Error("ErpOrderPay err2, 订单已支付") return resp, errors.New("订单已支付") } // 查询是否有待支付订单 if checkIsPaying(req.BillSn) { queryResp, err := QueryErpOrderPayStatus(req.BillSn) if err != nil { logger.Error("查询订单失败", logger.Field("err", err)) return resp, err } // 等待支付或成功支付则返回,否则新建订单 if queryResp.Status == Paying || queryResp.Status == PayOk { resp.Status = queryResp.Status return resp, nil } } // 查询是否有刚初始化的订单 var orderSn string if checkIsPayInit(req.BillSn) { var orderRecordInfo ErpOrderRecord err := orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status = ?", req.BillSn, PayInit). Find(&orderRecordInfo).Error if err != nil { logger.Error("未查询到订单:", logger.Field("err", err)) return resp, errors.New("未查询到待支付订单") } orderSn = orderRecordInfo.OutOrderNo } else { // 创建支付订单号,并记录到支付记录表,关联零售订单号 orderSn = GetErpOrderSn() var erpOrderRecord ErpOrderRecord erpOrderRecord.ErpOrderId = orderInfo.ID erpOrderRecord.BillSn = orderInfo.BillSn erpOrderRecord.OutOrderNo = orderSn erpOrderRecord.Status = PayInit err = orm.Eloquent.Table("erp_order_record").Create(&erpOrderRecord).Error if err != nil { logger.Error("Create erp_order_record err:", logger.Field("err", err)) return resp, err } } resp.Status = Paying // 发起支付扣款 hmPayResp, err := pay.HmJsPayBToCOrder(orderInfo.StoreId, orderSn, amount, req.AuthCode, "") if err != nil { logger.Error("HmJsPayBToCOrder err") return resp, err } // 更新支付状态 var payStatus string switch hmPayResp.SubCode { case "SUCCESS": payStatus = PayOk case "FAILED": payStatus = PayFailed case "WAITING_PAYMENT": payStatus = Paying //default: // payStatus = PayUnknown } updateInfo := map[string]interface{}{ "bank_order_no": hmPayResp.BankOrderNo, "plat_trx_no": hmPayResp.PlatTrxNo, "bank_trx_no": hmPayResp.BankTrxNo, "total_amount": hmPayResp.TotalAmount, "pay_way": hmPayResp.PayWay, "status": payStatus, } begin := orm.Eloquent.Begin() err = begin.Model(&ErpOrderRecord{}).Where("out_order_no = ?", orderSn). Updates(updateInfo).Error if err != nil { begin.Rollback() logger.Error("update erp_order_record err:", logger.Field("err", err)) return resp, err } if payStatus == PayOk { // 支付成功 // 更新零售订单表 err = begin.Model(&ErpOrder{}).Where("id = ?", orderInfo.ID). Updates(map[string]interface{}{"pay_status": HavePaid}).Error if err != nil { begin.Rollback() logger.Error("update erp_order_record err:", logger.Field("err", err)) return resp, err } //// 更新库存订单表 //err = updateErpStockCommodity(begin, req.BillSn) //if err != nil { // logger.Error("ErpOrderPay updateErpStockCommodity err:", logger.Field("err", err)) // return resp, err //} } err = begin.Commit().Error if err != nil { begin.Rollback() return resp, fmt.Errorf("ErpOrderPay[commit err]:%v", err) } resp.Status = payStatus // 判断用户是不是会员,只有会员才会积分,否则不积分 userInfo, err := GetUserInfoByUid(uint32(orderInfo.Uid)) if err != nil { logger.Error("UpdateStock GetUserInfoByUid err:", logger.Field("err", err)) } if IsInMemberLevels(userInfo.MemberLevel) { // 更新用户积分 var vmCount int var describe, event string if orderInfo.RetailType == RetailTypeSale { // 零售订单,而且订单已支付,更新用户积分 describe = "零售销售获得积分" event = VmEventErpOrderSale vmCount = tools.RoundFloat64(orderInfo.TotalAmount) if vmCount == 0 { // 0元无需积分 return resp, nil } } else { return resp, nil } err = UserVmUpdate(nil, orderInfo.BillSn, uint32(orderInfo.Uid), vmCount, event, describe) if err != nil { logger.Errorf("UserVmUpdate err:", err) } } return resp, nil } // QueryErpOrderPayStatus 查询零售订单支付状态 func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) { resp := &ErpOrderPayResp{ Status: Paying, } // 查询订单信息 var orderInfo ErpOrder err := orm.Eloquent.Table("erp_order").Where("bill_sn = ?", billSn).Find(&orderInfo).Error if err != nil { logger.Error("未查询到订单:", logger.Field("err", err)) resp.Status = PayFailed return resp, errors.New("未查询到待支付订单") } // 查询待支付订单 var orderRecordInfo ErpOrderRecord err = orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status in (?)", billSn, []string{Paying, PayOk}). Find(&orderRecordInfo).Error if err != nil { logger.Error("未查询到订单:", logger.Field("err", err)) resp.Status = PayFailed return resp, errors.New("未查询到待支付订单") } if orderRecordInfo.OutOrderNo == "" { logger.Info("还未创建支付订单") resp.Status = NoPayOrder return resp, nil } if orderRecordInfo.Status == PayOk { logger.Info("订单已支付") resp.Status = PayOk return resp, nil } // 查询支付状态 hmQueryResp, err := pay.HmQueryOrder(orderRecordInfo.OutOrderNo, orderInfo.StoreId) if err != nil { logger.Error("查询失败:", logger.Field("err", err)) return resp, err } // 更新订单状态 var payStatus string switch hmQueryResp.SubCode { case "SUCCESS": payStatus = PayOk case "FAILED": payStatus = PayFailed case "WAITING_PAYMENT": payStatus = Paying //default: // payStatus = PayUnknown } updateInfo := map[string]interface{}{ "bank_order_no": hmQueryResp.BankOrderNo, "plat_trx_no": hmQueryResp.PlatTrxNo, "bank_trx_no": hmQueryResp.BankTrxNo, "total_amount": hmQueryResp.TotalAmount, "pay_way": hmQueryResp.PayWayCode, "status": payStatus, "updated_at": time.Now(), } begin := orm.Eloquent.Begin() err = begin.Model(&ErpOrderRecord{}).Where("out_order_no = ?", orderRecordInfo.OutOrderNo). Updates(updateInfo).Error if err != nil { begin.Rollback() logger.Error("query update erp_order_record err:", logger.Field("err", err)) return resp, err } if payStatus == PayOk { // 支付成功 // 更新零售订单表 err = begin.Model(&ErpOrder{}).Where("id = ?", orderRecordInfo.ErpOrderId). Updates(map[string]interface{}{ "pay_status": HavePaid, "updated_at": time.Now(), }).Error if err != nil { begin.Rollback() logger.Error("query update erp_order_record err:", logger.Field("err", err)) return resp, err } //// 更新库存订单表 //err = updateErpStockCommodity(begin, billSn) //if err != nil { // logger.Error("ErpOrderPay updateErpStockCommodity err:", logger.Field("err", err)) // return resp, err //} } err = begin.Commit().Error if err != nil { begin.Rollback() return resp, fmt.Errorf("QueryErpOrderPayStatus[commit err]:%v", err) } resp.Status = payStatus return resp, nil } // 更新库存订单表 func updateErpStockCommodity(gdb *gorm.DB, billSn string) error { //通过单据号查询订单 var erpOrderInfo []ErpOrder err := orm.Eloquent.Table("erp_order").Where("bill_sn = ?", billSn).Find(&erpOrderInfo).Error if err != nil { logger.Error("erp_order 未查询到订单:", logger.Field("err", err)) return err } if len(erpOrderInfo) == 0 { return errors.New("未查询到订单") } err = UpdateStock(gdb, erpOrderInfo[0], SoldOut, HavePaid, 0) if err != nil { gdb.Rollback() logger.Error("updateErpStockCommodity UpdateStock err:", logger.Field("err", err)) return errors.New("审核失败:" + err.Error()) } return nil } // CommodityIsHaveStock 查询商品是否有库存 func CommodityIsHaveStock(req ErpOrderCommodity, storeId uint32) bool { qs := orm.Eloquent.Table("erp_stock_commodity") if req.IMEI != "" { // 串码商品 qs.Where("imei = ? and state = ?", req.IMEI, InStock) } else { // 非串码商品 qs.Where("erp_commodity_id = ? and store_id = ? and state = ? and imei_type = ?", req.ErpCommodityId, storeId, InStock, NoIMEICommodity) } var count int64 err := qs.Count(&count).Error if err != nil { logger.Error("CommodityIsHaveStock err:", logger.Field("err", err)) } return count > 0 } // QueryStoreManageData 查询门店经营数据 func QueryStoreManageData(req *ErpOrderStoreManageDataReq, c *gin.Context) (*ErpOrderStoreManageDataResp, error) { showConfig, err := GetErpOrderShowConfig() if err != nil { logger.Errorf("List err:", err) showConfig.ShowAll = "ON" } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpOrderStoreManageDataResp{ PageIndex: req.PageIndex, PageSize: req.PageSize, } var storeManageDataList []StoreManageData // 构建查询条件 qs := orm.Eloquent.Model(&ErpOrder{}) if len(req.StoreId) != 0 { qs.Where("erp_order.store_id in ?", req.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 { if len(storeList) == 1 { qs = qs.Where("store_id = ?", storeList[0]) } else { qs = qs.Where("store_id IN (?)", storeList) } } else { return nil, errors.New("用户未绑定门店") } } if showConfig.ShowAll == "OFF" { qs = qs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) } if req.StartTime != "" && req.EndTime != "" { startTime, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Error("startTime parse err") } endTime, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Error("endTime parse err") } //startTime, _ := time.Parse("2006-01-02", req.StartTime) //endTime, _ := time.Parse("2006-01-02", req.EndTime) qs = qs.Where("maker_time BETWEEN ? AND ?", startTime, endTime) } else { qs = qs.Where("maker_time IS NOT NULL") } qs.Where("state = ?", ErpOrderStateAudited) // 查询汇总数据 var summary struct { TotalSalesAmount float64 TotalPromotionFee float64 TotalSalesProfit float64 TotalStaffProfit float64 TotalCount int64 } err = qs.Select("SUM(CASE WHEN retail_type = 'sale' THEN total_amount ELSE -total_amount END) AS total_sales_amount, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_discount ELSE -total_discount END) AS total_promotion_fee, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_sales_profit ELSE -total_sales_profit END) AS total_sales_profit, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_staff_profit ELSE -total_staff_profit END) AS total_staff_profit, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_count ELSE -total_count END) AS total_count"). Find(&summary).Error if err != nil { logger.Error("QueryStoreManageData summary err:", logger.Field("err", err)) return nil, err } // 查询分页数据 if req.SortType == "asc" { err = qs.Select("DATE_FORMAT(maker_time, '%Y-%m-%d') AS date, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_discount ELSE -total_discount END) AS promotion_fee, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_amount ELSE -total_amount END) AS total_sales_amount, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_sales_profit ELSE -total_sales_profit END) AS sales_profit, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_staff_profit ELSE -total_staff_profit END) AS staff_profit, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_count ELSE -total_count END) AS count"). Group("date"). Order("date ASC"). Find(&storeManageDataList).Error } else { err = qs.Select("DATE_FORMAT(maker_time, '%Y-%m-%d') AS date, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_discount ELSE -total_discount END) AS promotion_fee, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_amount ELSE -total_amount END) AS total_sales_amount, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_sales_profit ELSE -total_sales_profit END) AS sales_profit, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_staff_profit ELSE -total_staff_profit END) AS staff_profit, " + "SUM(CASE WHEN retail_type = 'sale' THEN total_count ELSE -total_count END) AS count"). Group("date"). Order("date DESC"). Find(&storeManageDataList).Error } if err != nil { logger.Error("QueryStoreManageData err:", logger.Field("err", err)) return nil, err } finalStoreManageDataList := constructFinalStoreManageDataList(storeManageDataList, req) // 汇总数据赋值给响应 resp.TotalSalesAmount = math.Round(summary.TotalSalesAmount*100) / 100 resp.TotalPromotionFee = math.Round(summary.TotalPromotionFee*100) / 100 resp.TotalSalesProfit = math.Round(summary.TotalSalesProfit*100) / 100 resp.TotalStaffProfit = math.Round(summary.TotalStaffProfit*100) / 100 resp.TotalCount = summary.TotalCount if req.IsExport == 1 { //导出excel //storeName := "所有门店" //if req.StoreId != 0 { // storeInfo, err := GetStore(req.StoreId) // if err != nil { // logger.Error("GetStore err:", logger.Field("err", err)) // return nil, err // } // storeName = storeInfo.Name //} finalStoreManageDataList = constructFinalStoreManageDataList(storeManageDataList, req) filePath, err := storeManageDataExport(finalStoreManageDataList, "门店", summary, c) if err != nil { logger.Error("StoreManageDataExport err:", logger.Field("err", err)) return nil, err } resp.ExportUrl = filePath } else { // 根据页码和页面条数截取结果 startIndex := (req.PageIndex - 1) * req.PageSize endIndex := startIndex + req.PageSize if endIndex > len(finalStoreManageDataList) { endIndex = len(finalStoreManageDataList) } resp.List = finalStoreManageDataList[startIndex:endIndex] resp.Total = len(finalStoreManageDataList) } return resp, nil } // 查询门店经营数据,为空的日期默认填充数据为0 func constructFinalStoreManageDataList(storeManageDataList []StoreManageData, req *ErpOrderStoreManageDataReq) []StoreManageData { // Create a map to store data by date for easier processing storeDataMap := make(map[string]StoreManageData) for _, data := range storeManageDataList { data.TotalSalesAmount = math.Round(data.TotalSalesAmount*100) / 100 data.PromotionFee = math.Round(data.PromotionFee*100) / 100 data.SalesProfit = math.Round(data.SalesProfit*100) / 100 data.StaffProfit = math.Round(data.StaffProfit*100) / 100 storeDataMap[data.Date] = data } // Construct the final response with consecutive dates and defaulting to 0 for missing data var finalStoreManageDataList []StoreManageData startDate, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Error("constructFinalStoreManageDataList time Parse err:", logger.Field("err", err)) return storeManageDataList } endDate, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Error("constructFinalStoreManageDataList time Parse err:", logger.Field("err", err)) return storeManageDataList } for d := endDate; d.After(startDate) || d.Equal(startDate); d = d.AddDate(0, 0, -1) { dateStr := d.Format("2006-01-02") data, found := storeDataMap[dateStr] if !found { data = StoreManageData{ Date: dateStr, TotalSalesAmount: 0, PromotionFee: 0, SalesProfit: 0, StaffProfit: 0, Count: 0, } } finalStoreManageDataList = append(finalStoreManageDataList, data) } sortType := strings.ToLower(req.SortType) switch sortType { case "asc": sort.Slice(finalStoreManageDataList, func(i, j int) bool { return finalStoreManageDataList[i].Date < finalStoreManageDataList[j].Date }) case "desc": sort.Slice(finalStoreManageDataList, func(i, j int) bool { return finalStoreManageDataList[i].Date > finalStoreManageDataList[j].Date }) } return finalStoreManageDataList } // StoreManageDataExport 导出门店经营数据 func storeManageDataExport(list []StoreManageData, storeName string, summary struct { TotalSalesAmount float64 TotalPromotionFee float64 TotalSalesProfit float64 TotalStaffProfit float64 TotalCount int64 }, c *gin.Context) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := config.ExportConfig.Url fileName := time.Now().Format(TimeFormat) + storeName + "经营数据" + ".xlsx" fmt.Println("url fileName:", url+fileName) // 判断是否有入销售毛利、员工毛利的权限 flag1, _ := checkRoleMenu(c, SalesProfitMenu) flag2, _ := checkRoleMenu(c, StaffProfitMenu) fmt.Println("flag1 is:", flag1) fmt.Println("flag2 is:", flag2) logger.Info("flag1 is:", logger.Field("flag1", flag1)) logger.Info("flag2 is:", logger.Field("flag2", flag2)) nEndCount := 0 title := []interface{}{"时间", "销售额", "推广费"} if flag1 { // 销售毛利 title = append(title, "销售毛利") nEndCount += 1 } if flag2 { // 员工毛利 title = append(title, "员工毛利") nEndCount += 1 } title = append(title, "销售数量") 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 rowId := 0; rowId < len(list); rowId++ { row = []interface{}{ list[rowId].Date, list[rowId].TotalSalesAmount, list[rowId].PromotionFee, } // 控制是否导出 if flag1 { // 销售毛利 row = append(row, list[rowId].SalesProfit) } if flag2 { // 员工毛利 row = append(row, list[rowId].StaffProfit) } row = append(row, list[rowId].Count) 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(len(list)), 10) end := []interface{}{totalData, summary.TotalSalesAmount, summary.TotalPromotionFee} if flag1 { // 销售毛利 end = append(end, summary.TotalSalesProfit) } if flag2 { // 员工毛利 end = append(end, summary.TotalStaffProfit) } end = append(end, summary.TotalCount) 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", 18) var endRow string switch nEndCount { case 1: endRow = fmt.Sprintf("E"+"%d", nExcelStartRow+2) case 2: endRow = fmt.Sprintf("F"+"%d", nExcelStartRow+2) default: endRow = fmt.Sprintf("D"+"%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 } // retailMarginDataExport 导出商品零售毛利汇总数据 func retailMarginDataExport(req *ErpOrderRetailMarginResp, c *gin.Context) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := config.ExportConfig.Url fileName := time.Now().Format(TimeFormat) + "商品零售毛利汇总" + ".xlsx" fmt.Println("url fileName:", url+fileName) nEndCount := 0 title := []interface{}{"店铺名称", "零售类型", "商品名称", "商品分类", "数量", "销售/退货金额"} // 判断是否有权限 flag1, _ := checkRoleMenu(c, SalesCostMenu) flag2, _ := checkRoleMenu(c, EmployeeCostMenu) flag3, _ := checkRoleMenu(c, SalesMarginMenu) flag4, _ := checkRoleMenu(c, EmployeeMarginMenu) flag5, _ := checkRoleMenu(c, GrossMarginsMenu) flag6, _ := checkRoleMenu(c, EmployeeGrossMarginsMenu) if flag1 { title = append(title, "销售成本") nEndCount += 1 } if flag2 { title = append(title, "员工成本") nEndCount += 1 } if flag3 { title = append(title, "销售毛利") nEndCount += 1 } if flag4 { title = append(title, "员工毛利") nEndCount += 1 } if flag5 { title = append(title, "销售毛利率") nEndCount += 1 } if flag6 { title = append(title, "员工毛利率") nEndCount += 1 } 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 rowId := 0; rowId < len(req.List); rowId++ { var saleType string if req.List[rowId].RetailType == RetailTypeSale { saleType = "零售销售" } else if req.List[rowId].RetailType == RetailTypeRejected { saleType = "零售退货" } else { logger.Error("订单类型异常") return "", errors.New("RetailMarginDataExport, 订单类型异常:" + req.List[rowId].RetailType) } row = []interface{}{ req.List[rowId].StoreName, saleType, req.List[rowId].ErpCommodityName, req.List[rowId].ErpCategoryName, req.List[rowId].Count, req.List[rowId].SalesAmount, } if flag1 { // 销售成本 row = append(row, req.List[rowId].SalesCost) } if flag2 { // 员工成本 row = append(row, req.List[rowId].EmployeeCost) } if flag3 { // 销售毛利 row = append(row, req.List[rowId].SalesMargin) } if flag4 { // 员工毛利 row = append(row, req.List[rowId].EmployeeMargin) } if flag5 { // 销售毛利率 row = append(row, req.List[rowId].GrossMargins) } if flag6 { // 员工毛利率 row = append(row, req.List[rowId].EmployeeGrossMargins) } 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.TotalCount), 10) end := []interface{}{totalData, "", "", "", req.TotalCount, fmt.Sprintf("%.2f", req.TotalSalesAmount)} if flag1 { // 销售成本 end = append(end, req.TotalSalesCost) } if flag2 { // 员工成本 end = append(end, req.TotalEmployeeCost) } if flag3 { // 销售毛利 end = append(end, req.TotalSalesMargin) } if flag4 { // 员工毛利 end = append(end, req.TotalEmployeeMargin) } if flag5 { // 销售毛利率 end = append(end, req.TotalGrossMargins) } if flag6 { // 员工毛利率 end = append(end, req.TotalEmployeeGrossMargins) } 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, 30) // 设置单元格大小 file.SetColWidth("Sheet1", "A", "A", 26) file.SetColWidth("Sheet1", "C", "C", 45) file.SetColWidth("Sheet1", "F", "F", 15) var endRow string switch nEndCount { case 1: endRow = fmt.Sprintf("G"+"%d", nExcelStartRow+2) case 2: endRow = fmt.Sprintf("H"+"%d", nExcelStartRow+2) case 3: endRow = fmt.Sprintf("I"+"%d", nExcelStartRow+2) case 4: endRow = fmt.Sprintf("J"+"%d", nExcelStartRow+2) case 5: endRow = fmt.Sprintf("K"+"%d", nExcelStartRow+2) case 6: endRow = fmt.Sprintf("L"+"%d", nExcelStartRow+2) default: endRow = fmt.Sprintf("F"+"%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 } // QueryRetailMargin 查询零售毛利汇总数据 func QueryRetailMargin(req *ErpOrderRetailMarginReq, c *gin.Context) (*ErpOrderRetailMarginResp, 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 &ErpOrderRetailMarginResp{}, nil } } else { return nil, errors.New("用户未绑定门店") } } showConfig, err := GetErpOrderShowConfig() if err != nil { logger.Errorf("List err:", err) showConfig.ShowAll = "ON" } resp := &ErpOrderRetailMarginResp{ 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_order_commodity"). Select("erp_order_commodity.*, erp_order.store_id, erp_order.store_name, erp_order.retail_type"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") if len(req.StoreId) != 0 { qs.Where("erp_order.store_id in ?", req.StoreId) } if len(req.RetailType) != 0 { qs.Where("erp_order.retail_type in ?", req.RetailType) } if len(req.ErpCommodityName) != 0 { qs.Where("erp_order_commodity.erp_commodity_name in ?", req.ErpCommodityName) } if len(req.ErpCategoryId) != 0 { categoryList, err := TransformErpCategoryIds(req.ErpCategoryId) if err != nil { return nil, err } qs.Where("erp_order_commodity.erp_category_id in ?", categoryList) } if req.StartTime != "" { startTime, err := time.Parse(QueryTimeFormat, req.StartTime) if err == nil { qs = qs.Where("erp_order.audit_time>?", startTime) } else { logger.Errorf("QueryRetailMargin time start parse err:", err.Error()) } } if req.EndTime != "" { endTime, err := time.Parse(QueryTimeFormat, req.EndTime) if err == nil { qs = qs.Where("erp_order.audit_time len(list) { endIndex = len(list) } resp.List = list[startIndex:endIndex] resp.Total = len(list) resp.PageIndex = req.PageIndex resp.PageSize = req.PageSize } return resp, nil } // 将float64转换为百分比 func float64ToPercentage(value float64) string { // 将浮点数乘以100,然后格式化为字符串 percentage := fmt.Sprintf("%.2f%%", value*100) return percentage } // QueryRetailDetail 查询零售明细 func QueryRetailDetail(req *ErpOrderRetailDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { resp := &ErpOrderRetailDetailResp{} // 通过银行流水号查询 if req.BankTrxNo != "" { var orderPayWay ErpOrderRecord err := orm.Eloquent.Table("erp_order_record").Where("bank_trx_no = ? and status = ?", req.BankTrxNo, PayOk).Find(&orderPayWay).Error if err != nil { logger.Error("query erp_order_record err:", logger.Field("err", err)) return nil, err } req.BillSn = orderPayWay.BillSn } var err error if (len(req.ErpCategoryId) != 0 || req.ErpCommodityName != "" || req.IMEI != "") && req.BillSn == "" { // 商品分类or商品名称不为空且订单编号为空 // 联表查询 resp, err = queryRetailDetailByJoin(req, c) } else { // 普通单表查询,然后补充收款数据和商品数据 resp, err = queryRetailDetailCommon(req, c) } if err != nil { logger.Error("queryRetailDetailCommon err") return nil, err } return resp, nil } //// 导出零售明细报表excel-合并单元格版本 //func retailDetailExportBack(list []ErpOrder, sumData RetailDetailTotalData) (string, error) { // file := excelize.NewFile() // fSheet := "Sheet1" // // url := config.ExportConfig.Url // fileName := time.Now().Format(TimeFormat) + "零售明细" + ".xlsx" // fmt.Println("url fileName:", url+fileName) // // title := []interface{}{"订单编号", "订单类型", "用户ID", "客户手机号", "审核时间", "店铺", "销售员", "商品分类", "商品名称", // "供应商", "是否串码", "商品串码", "是否赠送", "销售数量", "指导零售价", "零售价", "零售优惠", "会员优惠", "实际零售价/退货价", // "采购单价", "员工成本价", "销售毛利", "员工毛利", "订单总指导零售价", "订单总优惠", "订单实收", "【扫码付", "现金收款", "pos机收款", // "商场积分抵扣", "其他付款方式】", "订单总销售毛利", "订单总员工毛利", "销售毛利提成", "员工毛利提成", "销售员提成", "门店提成", "备注"} // 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 row []interface{} // nAmount := 0.0 // nExcelStartRow := 0 // for i := 0; i < len(list); i++ { // var saleType string // if list[i].RetailType == RetailTypeSale { // saleType = "零售销售" // } else if list[i].RetailType == RetailTypeRejected { // saleType = "零售退货" // } else { // logger.Error("订单类型异常") // return "", errors.New("RetailMarginDataExport, 订单类型异常:" + list[i].RetailType) // } // // commodityIdMap := make(map[uint32]bool) // // 先判断商品数量,确定要写几行数据 // for rowId := 0; rowId < len(list[i].Commodities); rowId++ { // if list[i].RetailType == RetailTypeSale { // nAmount = list[i].Commodities[rowId].Amount // } else if list[i].RetailType == RetailTypeRejected { // nAmount = list[i].Commodities[rowId].RejectedAmount // list[i].CashierList = "" // 目前零售退货订单暂时不展示各个方式的付款金额 // } // // isIMEIType := "是" // if list[i].Commodities[rowId].IMEIType == 1 { // isIMEIType = "否" // } // // strPresentType := "非赠送" // if list[i].Commodities[rowId].PresentType == 2 { // strPresentType = "赠送" // } // // // 组合销售员名称,提成数据 // salesMan := "" // strSalesProfitPer := "" // strStaffProfitPer := "" // strSalesmanPer := "" // //var salesList []ErpOrderSales // //err := json.Unmarshal([]byte(list[i].SalesmanList), &salesList) // //if err != nil { // // logger.Error("unmarshal err:", logger.Field("err", err)) // //} // for j, item := range list[i].Salesman { // salesMan += item.Name // // 将浮点数转换为字符串,保留两位小数 // strNumber1 := strconv.FormatFloat(item.SalesProfitPer, 'f', 2, 64) // strSalesProfitPer += strNumber1 // strNumber2 := strconv.FormatFloat(item.StaffProfitPer, 'f', 2, 64) // strStaffProfitPer += strNumber2 // strNumber3 := strconv.FormatFloat(item.SalesmanPer, 'f', 2, 64) // strSalesmanPer += strNumber3 // // if j < len(list[i].Salesman)-1 { // salesMan += "\n" // strSalesProfitPer += "\n" // strStaffProfitPer += "\n" // strSalesmanPer += "\n" // } // } // // // 组合支付相关信息 // var cashierData TotalCashierData // var cashiers []ErpOrderCashier // err := json.Unmarshal([]byte(list[i].CashierList), &cashiers) // if err != nil { // logger.Error("unmarshal err:", logger.Field("err", err)) // } // for _, item := range cashiers { // switch item.CashierId { // case 1: // cashierData.ScanAmount = item.Amount // case 2: // cashierData.CashAmount = item.Amount // case 3: // cashierData.PosAmount = item.Amount // case 4: // cashierData.StoreVmAmount = item.Amount // default: // cashierData.OtherAmount += item.Amount // } // } // // // 单个订单的汇总数据只记录一次 // var temp RetailDetailTotalData // if !commodityIdMap[list[i].Commodities[rowId].ErpCommodityId] { // commodityIdMap[list[i].Commodities[rowId].ErpCommodityId] = true // temp.TotalRetailPrice = list[i].TotalRetailPrice // temp.TotalDiscount = list[i].TotalDiscount // temp.TotalAmount = list[i].TotalAmount // temp.TotalSalesProfit = list[i].TotalSalesProfit // temp.TotalStaffProfit = list[i].TotalStaffProfit // temp.StorePer = list[i].StorePer // } // // row = []interface{}{ // list[i].BillSn, // saleType, // list[i].Uid, // list[i].Tel, // list[i].AuditTime, // list[i].StoreName, // salesMan, //销售员 // list[i].Commodities[rowId].ErpCategoryName, // list[i].Commodities[rowId].ErpCommodityName, // list[i].Commodities[rowId].ErpSupplierName, //供应商 // isIMEIType, // list[i].Commodities[rowId].IMEI, // strPresentType, // list[i].Commodities[rowId].Count, // list[i].Commodities[rowId].RetailPrice, // list[i].Commodities[rowId].SalePrice, // list[i].Commodities[rowId].SaleDiscount, // list[i].Commodities[rowId].MemberDiscount, // nAmount, // list[i].Commodities[rowId].WholesalePrice, // list[i].Commodities[rowId].WholesalePrice + list[i].Commodities[rowId].StaffCostPrice, // list[i].Commodities[rowId].SalesProfit, // list[i].Commodities[rowId].StaffProfit, // temp.TotalRetailPrice, // temp.TotalDiscount, // temp.TotalAmount, // cashierData.ScanAmount, // 扫码付 // cashierData.CashAmount, // 现金收款 // cashierData.PosAmount, // pos机收款 // cashierData.StoreVmAmount, // 商场积分抵扣 // cashierData.OtherAmount, // 其他付款方式 // temp.TotalSalesProfit, // temp.TotalStaffProfit, // strSalesProfitPer, // 销售毛利提成 // strStaffProfitPer, // 员工毛利提成 // strSalesmanPer, // 销售员提成 // temp.StorePer, // list[i].Commodities[rowId].Remark, // } // // 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++ // } // // //合并单元格 // nRow := len(list[i].Commodities) // if nRow > 1 { // for j := 'A'; j <= 'G'; j++ { // colName := string(j) // str1 := fmt.Sprintf("%s%d", colName, nExcelStartRow-nRow+2) // str2 := fmt.Sprintf("%s%d", colName, nExcelStartRow+1) // _ = file.MergeCell(fSheet, str1, str2) // } // // for j := 'X'; j <= 'Z'; j++ { // colName := string(j) // str1 := fmt.Sprintf("%s%d", colName, nExcelStartRow-nRow+2) // str2 := fmt.Sprintf("%s%d", colName, nExcelStartRow+1) // _ = file.MergeCell(fSheet, str1, str2) // } // // for j := 'A'; j <= 'L'; j++ { // colName := string(j) // str1 := fmt.Sprintf("A%s%d", colName, nExcelStartRow-nRow+2) // str2 := fmt.Sprintf("A%s%d", colName, nExcelStartRow+1) // _ = file.MergeCell(fSheet, str1, str2) // } // } // } // // totalData := "订单数:" + strconv.FormatInt(int64(sumData.Count), 10) // end := []interface{}{totalData, "", "", "", "", "", "", "", "", "", "", "", "", // sumData.Count, // sumData.RetailPrice, // sumData.SalePrice, // sumData.SaleDiscount, // sumData.MemberDiscount, // sumData.Amount, // sumData.WholesalePrice, // sumData.StaffPrice, // sumData.SalesProfit, // sumData.StaffProfit, // sumData.TotalRetailPrice, // sumData.TotalDiscount, // sumData.TotalAmount, // sumData.ScanAmount, // 扫码付 // sumData.CashAmount, // 现金收款 // sumData.PosAmount, // pos机收款 // sumData.StoreVmAmount, // 商场积分抵扣 // sumData.OtherAmount, // 其他付款方式 // sumData.TotalSalesProfit, // sumData.TotalStaffProfit, // sumData.TotalSalesProfitPer, // 销售毛利提成 // sumData.TotalStaffProfitPer, // 员工毛利提成 // sumData.SalesmanPer, // 销售员提成 // sumData.StorePer, // ""} // // 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}]}`) // // // 设置单元格的样式: 居中、加边框、自动换行 // style1, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center","wrap_text":true}, // "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("AL%d", nExcelStartRow+2) // // 应用样式到整个表格 // _ = file.SetCellStyle("Sheet1", "A1", endRow, style) // // //需要自动换行的列 // endRow1 := fmt.Sprintf("G%d", nExcelStartRow+2) // endRow2 := fmt.Sprintf("AH%d", nExcelStartRow+2) // endRow3 := fmt.Sprintf("AI%d", nExcelStartRow+2) // endRow4 := fmt.Sprintf("AJ%d", nExcelStartRow+2) // _ = file.SetCellStyle("Sheet1", "A1", "AL1", style1) // _ = file.SetCellStyle("Sheet1", "G2", endRow1, style1) // _ = file.SetCellStyle("Sheet1", "AH2", endRow2, style1) // _ = file.SetCellStyle("Sheet1", "AI2", endRow3, style1) // _ = file.SetCellStyle("Sheet1", "AJ2", endRow4, style1) // // 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 //} // 导出零售明细报表excel-不合并 func retailDetailExport(list []ErpOrder, sumData RetailDetailTotalData, c *gin.Context) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := config.ExportConfig.Url fileName := time.Now().Format(TimeFormat) + "零售明细" + ".xlsx" fmt.Println("url fileName:", url+fileName) // 查询所有支付方式 var sysCashierList []ErpCashier err := orm.Eloquent.Table("erp_cashier").Order("id ASC").Find(&sysCashierList).Error if err != nil { return "", err } // 判断是否有权限 flag1, _ := checkRoleMenu(c, DetailWholesalePriceMenu) flag2, _ := checkRoleMenu(c, DetailEmployeeCostMenu) flag3, _ := checkRoleMenu(c, DetailSalesProfitMenu) flag4, _ := checkRoleMenu(c, DetailStaffProfitMenu) flag5, _ := checkRoleMenu(c, DetailTotalSalesProfitMenu) flag6, _ := checkRoleMenu(c, DetailTotalStaffProfitMenu) flag7, _ := checkRoleMenu(c, DetailSalesProfitPerMenu) flag8, _ := checkRoleMenu(c, DetailStaffProfitPerMenu) flag9, _ := checkRoleMenu(c, DetailSalesmanPerMenu) flag10, _ := checkRoleMenu(c, DetailStorePerMenu) nEndCount := 0 title := []interface{}{"订单编号", "订单类型", "用户ID", "客户手机号", "日期", "审核时间", "店铺", "银联流水号", "销售员1", "销售员2", "一级分类", "二级分类", "三级分类", "商品名称", "供应商", "是否串码", "商品串码", "是否赠送", "销售数量", "指导零售价", "零售价", "零售优惠", "会员优惠", "实际零售价/退货价"} if flag1 { // 采购单价 title = append(title, "采购单价") nEndCount += 1 } if flag2 { // 员工成本价 title = append(title, "员工成本价") nEndCount += 1 } if flag3 { // 销售毛利 title = append(title, "销售毛利") nEndCount += 1 } if flag4 { // 员工毛利 title = append(title, "员工毛利") nEndCount += 1 } title = append(title, "订单总指导零售价", "订单总优惠", "订单实收") for i, value := range sysCashierList { if value.ID == 1 { title = append(title, "【扫码付") } else if i+1 == len(sysCashierList) { title = append(title, value.Name+"】") } else { title = append(title, value.Name) } nEndCount += 1 } if flag5 { // 订单总销售毛利 title = append(title, "订单总销售毛利") nEndCount += 1 } if flag6 { // 订单总员工毛利 title = append(title, "订单总员工毛利") nEndCount += 1 } if flag7 { // 销售毛利提成 title = append(title, "销售毛利提成") nEndCount += 1 } if flag8 { // 员工毛利提成 title = append(title, "员工毛利提成") nEndCount += 1 } if flag9 { // 销售员提成 title = append(title, "销售员提成") nEndCount += 1 } if flag10 { // 门店提成 title = append(title, "门店提成") nEndCount += 1 } title = append(title, "备注") 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)) } } categoryMap, err := GetAllCategories() if err != nil { // 处理错误 logger.Error("GetAllCategories err:", logger.Field("err", err)) } var row []interface{} nAmount := 0.0 nExcelStartRow := 0 for i := 0; i < len(list); i++ { var saleType string if list[i].RetailType == RetailTypeSale { saleType = "零售销售" } else if list[i].RetailType == RetailTypeRejected { saleType = "零售退货" } else { logger.Error("订单类型异常") return "", errors.New("RetailMarginDataExport, 订单类型异常:" + list[i].RetailType) } orderFlag := false salesMan1 := "" salesMan2 := "" // 先判断商品数量,确定要写几行数据 for rowId := 0; rowId < len(list[i].Commodities); rowId++ { if list[i].RetailType == RetailTypeSale { nAmount = list[i].Commodities[rowId].Amount } else if list[i].RetailType == RetailTypeRejected { nAmount = list[i].Commodities[rowId].RejectedAmount //list[i].CashierList = "" // 目前零售退货订单暂时不展示各个方式的付款金额 } // 用户id var userId string if list[i].Uid != 0 { userId = strconv.Itoa(list[i].Uid) } else { userId = "" } // 审核时间 var auditTime, auditDate string if list[i].AuditTime != nil { auditTime = list[i].AuditTime.Format(ExcelTimeFormat) auditDate = list[i].AuditTime.Format(DateTimeFormat) // Extract date only } else { auditTime = "" auditDate = "" } // 商品分类 categoryID := list[i].Commodities[rowId].ErpCategoryId categoryLevels := GetCategoryLevelsFromMap(categoryID, categoryMap) isIMEIType := "是" if list[i].Commodities[rowId].IMEIType == 1 { isIMEIType = "否" } strPresentType := "非赠送" if list[i].Commodities[rowId].PresentType == 2 { strPresentType = "赠送" } // 组合支付相关信息 var cashiers []ErpOrderCashier err := json.Unmarshal([]byte(list[i].CashierList), &cashiers) if err != nil { logger.Error("unmarshal err:", logger.Field("err", err)) } var cashierDataList []ErpOrderCashier for _, value := range sysCashierList { var orderCashier ErpOrderCashier orderCashier.CashierId = value.ID orderCashier.Name = value.Name for _, item := range cashiers { if value.ID == item.CashierId { if list[i].RetailType == RetailTypeRejected { orderCashier.Amount = -item.Amount } else { orderCashier.Amount = item.Amount } } else { continue } } cashierDataList = append(cashierDataList, orderCashier) } //for _, item := range cashiers { // switch item.CashierId { // case 1: // cashierData.ScanAmount = item.Amount // case 2: // cashierData.CashAmount = item.Amount // case 3: // cashierData.PosAmount = item.Amount // case 4: // cashierData.StoreVmAmount = item.Amount // default: // cashierData.OtherAmount += item.Amount // } //} // 单个订单的汇总数据只记录一次 if !orderFlag { orderFlag = true nSalesProfitPer := 0.0 nStaffProfitPer := 0.0 nSalesmanPer := 0.0 if len(list[i].Salesman) > 0 { salesMan1 = list[i].Salesman[0].Name if len(list[i].Salesman) == 2 { salesMan2 = list[i].Salesman[1].Name } nSalesProfitPer = math.Round(list[i].Salesman[0].SalesProfitPer*100) / 100 nStaffProfitPer = math.Round(list[i].Salesman[0].StaffProfitPer*100) / 100 nSalesmanPer = math.Round(list[i].Salesman[0].SalesmanPer*100) / 100 } row = []interface{}{ list[i].BillSn, saleType, userId, list[i].Tel, auditDate, auditTime, list[i].StoreName, list[i].BankTrxNo, salesMan1, //销售员1 salesMan2, //销售员2 categoryLevels.Level1.Name, // 一级分类 categoryLevels.Level2.Name, // 二级分类 categoryLevels.Level3.Name, // 三级分类 list[i].Commodities[rowId].ErpCommodityName, list[i].Commodities[rowId].ErpSupplierName, //供应商 isIMEIType, list[i].Commodities[rowId].IMEI, strPresentType, list[i].Commodities[rowId].Count, list[i].Commodities[rowId].RetailPrice, list[i].Commodities[rowId].SalePrice, list[i].Commodities[rowId].SaleDiscount, list[i].Commodities[rowId].MemberDiscount, nAmount, //list[i].Commodities[rowId].WholesalePrice, //list[i].Commodities[rowId].WholesalePrice + list[i].Commodities[rowId].StaffCostPrice, //list[i].Commodities[rowId].SalesProfit, //list[i].Commodities[rowId].StaffProfit, //list[i].TotalRetailPrice, //list[i].TotalDiscount, //list[i].TotalAmount, //cashierData.ScanAmount, // 扫码付 //cashierData.CashAmount, // 现金收款 //cashierData.PosAmount, // pos机收款 //cashierData.StoreVmAmount, // 商场积分抵扣 //cashierData.OtherAmount, // 其他付款方式 //list[i].TotalSalesProfit, //list[i].TotalStaffProfit, //nSalesProfitPer, // 销售毛利提成 //nStaffProfitPer, // 员工毛利提成 //nSalesmanPer, // 销售员提成 //list[i].StorePer, //list[i].Commodities[rowId].Remark, } // 控制是否导出 if flag1 { // 采购单价 row = append(row, list[i].Commodities[rowId].WholesalePrice) } if flag2 { // 员工成本价 row = append(row, list[i].Commodities[rowId].WholesalePrice+list[i].Commodities[rowId].StaffCostPrice) } if flag3 { // 销售毛利 row = append(row, list[i].Commodities[rowId].SalesProfit) } if flag4 { // 员工毛利 row = append(row, list[i].Commodities[rowId].StaffProfit) } row = append(row, list[i].TotalRetailPrice) row = append(row, list[i].TotalDiscount) row = append(row, list[i].TotalAmount) //row = append(row, cashierData.ScanAmount) //row = append(row, cashierData.CashAmount) //row = append(row, cashierData.PosAmount) //row = append(row, cashierData.StoreVmAmount) //row = append(row, cashierData.OtherAmount) for _, value := range cashierDataList { row = append(row, value.Amount) } if flag5 { // 订单总销售毛利 row = append(row, list[i].TotalSalesProfit) } if flag6 { // 订单总员工毛利 row = append(row, list[i].TotalStaffProfit) } if flag7 { // 销售毛利提成 row = append(row, nSalesProfitPer) } if flag8 { // 员工毛利提成 row = append(row, nStaffProfitPer) } if flag9 { // 销售员提成 row = append(row, nSalesmanPer) } if flag10 { // 门店提成 row = append(row, list[i].StorePer) } row = append(row, list[i].Commodities[rowId].Remark) } else { if len(list[i].Salesman) == 2 && rowId == 1 { row = []interface{}{ list[i].BillSn, saleType, userId, list[i].Tel, auditDate, auditTime, list[i].StoreName, list[i].BankTrxNo, salesMan1, //销售员1 salesMan2, //销售员2 categoryLevels.Level1.Name, // 一级分类 categoryLevels.Level2.Name, // 二级分类 categoryLevels.Level3.Name, // 三级分类 list[i].Commodities[rowId].ErpCommodityName, list[i].Commodities[rowId].ErpSupplierName, //供应商 isIMEIType, list[i].Commodities[rowId].IMEI, strPresentType, list[i].Commodities[rowId].Count, list[i].Commodities[rowId].RetailPrice, list[i].Commodities[rowId].SalePrice, list[i].Commodities[rowId].SaleDiscount, list[i].Commodities[rowId].MemberDiscount, nAmount, //list[i].Commodities[rowId].WholesalePrice, //list[i].Commodities[rowId].WholesalePrice + list[i].Commodities[rowId].StaffCostPrice, //list[i].Commodities[rowId].SalesProfit, //list[i].Commodities[rowId].StaffProfit, //"", //"", //"", //"", //"", //"", //"", //"", //"", //"", //math.Round(list[i].Salesman[1].SalesProfitPer*100) / 100, // 销售毛利提成 //math.Round(list[i].Salesman[1].StaffProfitPer*100) / 100, // 员工毛利提成 //math.Round(list[i].Salesman[1].SalesmanPer*100) / 100, // 销售员提成 //"", //"", } // 控制是否导出 if flag1 { // 采购单价 row = append(row, list[i].Commodities[rowId].WholesalePrice) } if flag2 { // 员工成本价 row = append(row, list[i].Commodities[rowId].WholesalePrice+list[i].Commodities[rowId].StaffCostPrice) } if flag3 { // 销售毛利 row = append(row, list[i].Commodities[rowId].SalesProfit) } if flag4 { // 员工毛利 row = append(row, list[i].Commodities[rowId].StaffProfit) } row = append(row, "") row = append(row, "") row = append(row, "") for _, value := range sysCashierList { fmt.Println("cashier name:", value.Name) row = append(row, "") } //row = append(row, "") //row = append(row, "") //row = append(row, "") //row = append(row, "") //row = append(row, "") if flag5 { // 订单总销售毛利 row = append(row, "") } if flag6 { // 订单总员工毛利 row = append(row, "") } if flag7 { // 销售毛利提成 row = append(row, math.Round(list[i].Salesman[1].SalesProfitPer*100)/100) } if flag8 { // 员工毛利提成 row = append(row, math.Round(list[i].Salesman[1].StaffProfitPer*100)/100) } if flag9 { // 销售员提成 row = append(row, math.Round(list[i].Salesman[1].SalesmanPer*100)/100) } if flag10 { // 门店提成 row = append(row, "") } row = append(row, "") } else { row = []interface{}{ list[i].BillSn, saleType, userId, list[i].Tel, auditDate, auditTime, list[i].StoreName, list[i].BankTrxNo, salesMan1, //销售员1 salesMan2, //销售员2 categoryLevels.Level1.Name, // 一级分类 categoryLevels.Level2.Name, // 二级分类 categoryLevels.Level3.Name, // 三级分类 list[i].Commodities[rowId].ErpCommodityName, list[i].Commodities[rowId].ErpSupplierName, //供应商 isIMEIType, list[i].Commodities[rowId].IMEI, strPresentType, list[i].Commodities[rowId].Count, list[i].Commodities[rowId].RetailPrice, list[i].Commodities[rowId].SalePrice, list[i].Commodities[rowId].SaleDiscount, list[i].Commodities[rowId].MemberDiscount, nAmount, //list[i].Commodities[rowId].WholesalePrice, //list[i].Commodities[rowId].WholesalePrice + list[i].Commodities[rowId].StaffCostPrice, //list[i].Commodities[rowId].SalesProfit, //list[i].Commodities[rowId].StaffProfit, //"", //"", //"", //"", //"", //"", //"", //"", //"", //"", //"", // 销售毛利提成 //"", // 员工毛利提成 //"", // 销售员提成 //"", //"", } // 控制是否导出 if flag1 { // 采购单价 row = append(row, list[i].Commodities[rowId].WholesalePrice) } if flag2 { // 员工成本价 row = append(row, list[i].Commodities[rowId].WholesalePrice+list[i].Commodities[rowId].StaffCostPrice) } if flag3 { // 销售毛利 row = append(row, list[i].Commodities[rowId].SalesProfit) } if flag4 { // 员工毛利 row = append(row, list[i].Commodities[rowId].StaffProfit) } row = append(row, "") row = append(row, "") row = append(row, "") for _, value := range sysCashierList { fmt.Println("cashier name:", value.Name) row = append(row, "") } //row = append(row, "") //row = append(row, "") //row = append(row, "") //row = append(row, "") //row = append(row, "") if flag5 { // 订单总销售毛利 row = append(row, "") } if flag6 { // 订单总员工毛利 row = append(row, "") } if flag7 { // 销售毛利提成 row = append(row, "") } if flag8 { // 员工毛利提成 row = append(row, "") } if flag9 { // 销售员提成 row = append(row, "") } if flag10 { // 门店提成 row = append(row, "") } row = append(row, "") } } 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++ //if len(list[i].Commodities) == 1 && len(list[i].Salesman) == 2 { // row = []interface{}{ // "", // "", // "", // "", // "", // "", // "", // list[i].Salesman[1].Name, //销售员 // "", // "", // "", // "", // "", // "", // "", // "", // "", // "", // "", // "", // // //"", // //"", // //"", // //"", // // //"", // //"", // //"", // //"", // //"", // //"", // //"", // //"", // // //"", // //"", // //math.Round(list[i].Salesman[1].SalesProfitPer*100) / 100, // 销售毛利提成 // //math.Round(list[i].Salesman[1].StaffProfitPer*100) / 100, // 员工毛利提成 // //math.Round(list[i].Salesman[1].SalesmanPer*100) / 100, // 销售员提成 // //"", // // //"", // } // // 控制是否导出 // if flag1 { // 采购单价 // row = append(row, "") // } // if flag2 { // 员工成本价 // row = append(row, "") // } // if flag3 { // 销售毛利 // row = append(row, "") // } // if flag4 { // 员工毛利 // row = append(row, "") // } // // row = append(row, "") // row = append(row, "") // row = append(row, "") // row = append(row, "") // row = append(row, "") // row = append(row, "") // row = append(row, "") // row = append(row, "") // // if flag5 { // 订单总销售毛利 // row = append(row, "") // } // if flag6 { // 订单总员工毛利 // row = append(row, "") // } // if flag7 { // 销售毛利提成 // row = append(row, math.Round(list[i].Salesman[1].SalesProfitPer*100)/100) // } // if flag8 { // 员工毛利提成 // row = append(row, math.Round(list[i].Salesman[1].StaffProfitPer*100)/100) // } // if flag9 { // 销售员提成 // row = append(row, math.Round(list[i].Salesman[1].SalesmanPer*100)/100) // } // if flag10 { // 门店提成 // row = append(row, "") // } // // row = append(row, "") // // 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(len(list)), 10) end := []interface{}{totalData, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", sumData.Count, sumData.RetailPrice, sumData.SalePrice, sumData.SaleDiscount, sumData.MemberDiscount, sumData.Amount, //sumData.WholesalePrice, //sumData.StaffPrice, //sumData.SalesProfit, //sumData.StaffProfit, //sumData.TotalRetailPrice, //sumData.TotalDiscount, //sumData.TotalAmount, //sumData.ScanAmount, // 扫码付 //sumData.CashAmount, // 现金收款 //sumData.PosAmount, // pos机收款 //sumData.StoreVmAmount, // 商场积分抵扣 //sumData.OtherAmount, // 其他付款方式 //sumData.TotalSalesProfit, //sumData.TotalStaffProfit, //sumData.TotalSalesProfitPer, // 销售毛利提成 //sumData.TotalStaffProfitPer, // 员工毛利提成 //sumData.SalesmanPer, // 销售员提成 //sumData.StorePer, //"" } // 控制是否导出 if flag1 { // 采购单价 end = append(end, sumData.WholesalePrice) } if flag2 { // 员工成本价 end = append(end, sumData.StaffPrice) } if flag3 { // 销售毛利 end = append(end, sumData.SalesProfit) } if flag4 { // 员工毛利 end = append(end, sumData.StaffProfit) } end = append(end, sumData.TotalRetailPrice) end = append(end, sumData.TotalDiscount) end = append(end, sumData.TotalAmount) for _, value := range sumData.TotalCashierData { end = append(end, value.Amount) } //end = append(end, sumData.ScanAmount) //end = append(end, sumData.CashAmount) //end = append(end, sumData.PosAmount) //end = append(end, sumData.StoreVmAmount) //end = append(end, sumData.OtherAmount) if flag5 { // 订单总销售毛利 end = append(end, sumData.TotalSalesProfit) } if flag6 { // 订单总员工毛利 end = append(end, sumData.TotalStaffProfit) } if flag7 { // 销售毛利提成 end = append(end, sumData.TotalSalesProfitPer) } if flag8 { // 员工毛利提成 end = append(end, sumData.TotalStaffProfitPer) } if flag9 { // 销售员提成 end = append(end, sumData.SalesmanPer) } if flag10 { // 门店提成 end = append(end, sumData.StorePer) } end = append(end, "") 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"}}`) 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 := getEndRow(nEndCount, nExcelStartRow) fmt.Println(endRow) //endRow := fmt.Sprintf("AL%d", nExcelStartRow+2) // 应用样式到整个表格 _ = file.SetCellStyle("Sheet1", "A1", endRow, style) // 设置单元格的样式: 居中、加边框、自动换行 //style1, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center","wrap_text":true}}`) style1, _ := file.NewStyle(`{"alignment":{"horizontal":"center","vertical":"center","wrap_text":true}, "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}]}`) //需要自动换行的列 endRow2 := fmt.Sprintf("AI%d", nExcelStartRow+2) endRow3 := fmt.Sprintf("AJ%d", nExcelStartRow+2) endRow4 := fmt.Sprintf("AK%d", nExcelStartRow+2) _ = file.SetCellStyle("Sheet1", "A1", "AN1", style1) _ = file.SetCellStyle("Sheet1", "AL2", endRow2, style1) _ = file.SetCellStyle("Sheet1", "AJ2", endRow3, style1) _ = file.SetCellStyle("Sheet1", "AK2", endRow4, style1) // 设置单元格大小 file.SetColWidth("Sheet1", "A", "A", 15) file.SetColWidth("Sheet1", "D", "D", 15) file.SetColWidth("Sheet1", "E", "E", 12) file.SetColWidth("Sheet1", "F", "F", 18) file.SetColWidth("Sheet1", "G", "G", 25) file.SetColWidth("Sheet1", "H", "H", 25) 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 getEndRow(nEndCount, nExcelStartRow int) string { // Excel列是26进制的,列A为1,B为2,... Z为26,AA为27,以此类推 columnIndex := 28 + nEndCount // "AH" 是第33列 // 计算Excel列标 columnLetter := "" for columnIndex > 0 { // 获取当前列的字母,并且列标从1开始,所以要减去1 columnLetter = string(rune((columnIndex-1)%26+'A')) + columnLetter columnIndex = (columnIndex - 1) / 26 } // 生成最终的单元格位置 endRow := fmt.Sprintf("%s%d", columnLetter, nExcelStartRow+2) return endRow } // 生成Excel列名 func getExcelColumnName(index int) string { columnName := "" for index >= 0 { columnName = string(rune(index%26+'A')) + columnName index = index/26 - 1 } return columnName } // RetailDetailByJoin 联表查询扫描参数 type RetailDetailByJoin struct { ErpOrderCommodity ErpOrder } // 联表查询 func queryRetailDetailByJoin(req *ErpOrderRetailDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { showConfig, err := GetErpOrderShowConfig() if err != nil { logger.Errorf("List err:", err) showConfig.ShowAll = "ON" } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpOrderRetailDetailResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Debug().Table("erp_order_commodity"). Select("erp_order_commodity.*, erp_order.*"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") es := orm.Eloquent.Debug().Table("erp_order_commodity"). Select("erp_order_commodity.*, erp_order.*"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") orderSumQs := orm.Eloquent.Table("erp_order_commodity AS oc").Select("oc.*, eo.*"). Joins("JOIN erp_order AS eo ON oc.erp_order_id = eo.id") rejectedOrderSumQs := orm.Eloquent.Table("erp_order_commodity AS oc").Select("oc.*, eo.*"). Joins("JOIN erp_order AS eo ON oc.erp_order_id = eo.id") if len(req.ErpCategoryId) != 0 { // 商品分类 categoryList, err := TransformErpCategoryIds(req.ErpCategoryId) if err != nil { return nil, err } qs = qs.Where("erp_order_commodity.erp_category_id in ?", categoryList) es = es.Where("erp_order_commodity.erp_category_id in ?", categoryList) orderSumQs = orderSumQs.Where("oc.erp_category_id in ?", categoryList) rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.erp_category_id in ?", categoryList) } if req.ErpCommodityName != "" { // 商品名称 qs = qs.Where("erp_order_commodity.erp_commodity_name = ?", req.ErpCommodityName) es = es.Where("erp_order_commodity.erp_commodity_name = ?", req.ErpCommodityName) orderSumQs = orderSumQs.Where("oc.erp_commodity_name = ?", req.ErpCommodityName) rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.erp_commodity_name = ?", req.ErpCommodityName) } if req.RetailType != "" { // 销售类型 qs = qs.Where("erp_order.retail_type=?", req.RetailType) es = es.Where("erp_order.retail_type=?", req.RetailType) orderSumQs = orderSumQs.Where("eo.retail_type=?", req.RetailType) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.retail_type=?", req.RetailType) } if req.Uid != 0 { // 用户ID qs = qs.Where("erp_order.uid=?", req.Uid) es = es.Where("erp_order.uid=?", req.Uid) orderSumQs = orderSumQs.Where("eo.uid=?", req.Uid) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.uid=?", req.Uid) } if req.Tel != "" { // 用户手机号 qs = qs.Where("erp_order.tel=?", req.Tel) es = es.Where("erp_order.tel=?", req.Tel) orderSumQs = orderSumQs.Where("eo.tel=?", req.Tel) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.tel=?", req.Tel) } if req.StoreId != 0 { // 门店ID qs = qs.Where("erp_order.store_id=?", req.StoreId) es = es.Where("erp_order.store_id=?", req.StoreId) orderSumQs = orderSumQs.Where("eo.store_id=?", req.StoreId) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id=?", req.StoreId) } if req.IMEI != "" { // 串码 qs = qs.Where("erp_order_commodity.imei=?", req.IMEI) es = es.Where("erp_order_commodity.imei=?", req.IMEI) orderSumQs = orderSumQs.Where("oc.imei=?", req.IMEI) rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.imei=?", req.IMEI) } // 非管理员才判断所属门店 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 { if len(storeList) == 1 { qs = qs.Where("erp_order.store_id = ?", storeList[0]) es = es.Where("erp_order.store_id = ?", storeList[0]) orderSumQs = orderSumQs.Where("eo.store_id = ?", storeList[0]) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id = ?", storeList[0]) } else { qs = qs.Where("erp_order.store_id IN (?)", storeList) es = es.Where("erp_order.store_id IN (?)", storeList) orderSumQs = orderSumQs.Where("eo.store_id IN (?)", storeList) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id IN (?)", storeList) } } else { return nil, errors.New("用户未绑定门店") } } if req.Salesman != 0 { // 销售员 qs = qs.Where("JSON_CONTAINS(erp_order.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) es = es.Where("JSON_CONTAINS(erp_order.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) orderSumQs = orderSumQs.Where("JSON_CONTAINS(eo.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) rejectedOrderSumQs = rejectedOrderSumQs.Where("JSON_CONTAINS(eo.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) } if req.StartTime != "" { // 审核开始时间 parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("err:", err) } qs = qs.Where("erp_order.audit_time > ?", parse) es = es.Where("erp_order.audit_time > ?", parse) orderSumQs = orderSumQs.Where("eo.audit_time > ?", parse) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.audit_time > ?", parse) } if req.EndTime != "" { // 审核结束时间 parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("err:", err) } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("erp_order.audit_time < ?", parse) es = es.Where("erp_order.audit_time < ?", parse) orderSumQs = orderSumQs.Where("eo.audit_time < ?", parse) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.audit_time < ?", parse) } if showConfig.ShowAll == "OFF" { qs = qs.Where("erp_order.is_print = ? or erp_order.retail_type = ?", HavePrinted, RetailTypeRejected) es = es.Where("erp_order.is_print = ? or erp_order.retail_type = ?", HavePrinted, RetailTypeRejected) orderSumQs = orderSumQs.Where("eo.is_print = ? or eo.retail_type = ?", HavePrinted, RetailTypeRejected) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.is_print = ? or eo.retail_type = ?", HavePrinted, RetailTypeRejected) } qs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) es.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) orderSumQs.Where("eo.pay_status = ? or (eo.retail_type = ? and eo.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) rejectedOrderSumQs.Where("eo.pay_status = ? or (eo.retail_type = ? and eo.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) // 销售订单的汇总数据 var sumData RetailDetailTotalData sumData, err = getRetailDetailTotalDataJoinErpOrderSale(orderSumQs, RetailTypeSale) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 退货订单的汇总数据 var rejectedSumData RetailDetailTotalData rejectedSumData, err = getRetailDetailTotalDataJoinErpOrderRejected(rejectedOrderSumQs, RetailTypeRejected) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的销售数据 sumData = subtractRetailData(sumData, rejectedSumData) // 销售订单提成汇总 var totalPerData TotalPerData totalPerQs := qs totalPerData, err = getTotalPerData(totalPerQs, RetailTypeSale) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 退货订单提成汇总 var rejectedTotalPerData TotalPerData rejectedTotalPerQs := qs rejectedTotalPerData, err = getTotalPerData(rejectedTotalPerQs, RetailTypeRejected) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的提成数据 totalPerData = subtractTotalPerData(totalPerData, rejectedTotalPerData) //// 销售订单支持汇总 //var cashier TotalCashierData //cashierQs := qs //cashier, err = getTotalCashierData(cashierQs, RetailTypeSale) //if err != nil { // logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) // return resp, err //} // //// 支付方式筛选 //if req.CashierId != 0 { // var tempCashier TotalCashierData // switch req.CashierId { // case 1: // tempCashier.ScanAmount = cashier.ScanAmount // case 2: // tempCashier.CashAmount = cashier.CashAmount // case 3: // tempCashier.PosAmount = cashier.PosAmount // case 4: // tempCashier.StoreVmAmount = cashier.StoreVmAmount // default: // tempCashier = cashier // } // cashier = tempCashier //} // //// 退货订单支付汇总:目前零售退货订单暂时不展示各个方式的付款金额 //var rejectedCashier TotalCashierData //rejectedCashierQs := qs //rejectedCashier, err = getTotalCashierData(rejectedCashierQs, RetailTypeRejected) //if err != nil { // logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) // return resp, err //} //// 计算销售订单和退货订单汇总后的支付数据 //cashier = subtractCashierData(cashier, rejectedCashier) // //// 处理汇总数据,四舍五入保留2位小数 //roundValues(&sumData, &totalPerData, &cashier) // 销售订单支付汇总 var cashierData []ErpOrderCashier cashierQs := qs cashierData, err = getTotalCashierData(cashierQs, RetailTypeSale) if err != nil { logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) return resp, err } // 支付方式筛选 if req.CashierId != 0 { var filteredData []ErpOrderCashier for _, cashier := range cashierData { if cashier.CashierId == req.CashierId { filteredData = append(filteredData, cashier) } } cashierData = filteredData } // 退货订单支付汇总:目前零售退货订单暂时不展示各个方式的付款金额 var rejectedCashierData []ErpOrderCashier rejectedCashierQs := qs rejectedCashierData, err = getTotalCashierData(rejectedCashierQs, RetailTypeRejected) if err != nil { logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的支付数据 cashierData = subtractCashierData(cashierData, rejectedCashierData) // 处理汇总数据,四舍五入保留2位小数 roundValues(&sumData, &totalPerData) sumData.TotalSalesProfit = 0 // 订单总销售毛利 sumData.TotalStaffProfit = 0 // 订单总员工毛利 sumData.TotalRetailPrice = 0 // 订单总指导零售价 sumData.TotalDiscount = 0 // 订单总优惠 sumData.TotalAmount = 0 // 订单实收金额 sumData.StorePer = 0 // 门店提成 sumData.TotalCashierData = nil //sumData.ScanAmount = cashier.ScanAmount //sumData.CashAmount = cashier.CashAmount //sumData.PosAmount = cashier.PosAmount //sumData.StoreVmAmount = cashier.StoreVmAmount //sumData.OtherAmount = cashier.OtherAmount RoundRetailDetailTotalData(&sumData) var result []RetailDetailByJoin if req.IsExport == 1 { //导出excel err = qs.Order("erp_order.audit_time DESC").Find(&result).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } var count int64 err = es.Count(&count).Error if err != nil { logger.Errorf("QueryRetailMargin count err:", err.Error()) return nil, err } orders := packData(result) erpOrderSetBankTrxNo(orders) fileUrl, err := retailDetailExport(orders, sumData, c) if err != nil { logger.Error("retailDetailExport err:", logger.Field("err", err)) return resp, err } resp.ExportUrl = fileUrl } else { //err = qs.Order("erp_order.audit_time DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&result).Error err = qs.Order("erp_order.audit_time DESC").Find(&result).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } var count int64 err = es.Count(&count).Error if err != nil { logger.Errorf("QueryRetailMargin count err:", err.Error()) return nil, err } orders := packData(result) erpOrderListSetCashier(orders) erpOrderListSetSalesman(orders) erpOrderSetBankTrxNo(orders) pagedOrders := paginate(orders, page, req.PageSize) resp.List = pagedOrders //跟之前保持一致 resp.Total = len(orders) resp.PageIndex = page + 1 resp.PageSize = req.PageSize resp.SumData = sumData } return resp, nil } // 分页函数 func paginate(orders []ErpOrder, page int, pageSize int) []ErpOrder { start := page * pageSize end := start + pageSize if start >= len(orders) { return []ErpOrder{} } if end > len(orders) { end = len(orders) } return orders[start:end] } // 组合数据 func packData(result []RetailDetailByJoin) []ErpOrder { var orders []ErpOrder orderIdMap := make(map[uint32]ErpOrder) for _, item := range result { var order ErpOrder var commodity ErpOrderCommodity // 判断是否有重复数据 if len(orderIdMap) != 0 && orderIdMap[item.ErpOrderId].BillSn != "" { order = orderIdMap[item.ErpOrderId] } else { // 订单数据 order.CreatedAt = item.ErpOrderCommodity.CreatedAt order.ID = item.ErpOrderId order.BillSn = item.BillSn order.RetailType = item.RetailType order.Uid = item.Uid order.Tel = item.Tel order.StoreId = item.StoreId order.StoreName = item.StoreName order.MakerId = item.MakerId order.MakerName = item.MakerName order.MakerTime = item.MakerTime order.AuditorId = item.AuditorId order.AuditorName = item.AuditorName order.AuditTime = item.AuditTime order.CashierList = item.CashierList order.SalesmanList = item.SalesmanList order.MemberType = item.MemberType order.State = item.State order.TotalRetailPrice = item.TotalRetailPrice order.TotalAmount = item.TotalAmount order.TotalCount = item.TotalCount order.TotalSalesProfit = item.TotalSalesProfit order.TotalStaffProfit = item.TotalStaffProfit order.VmCount = item.VmCount order.SaleOrderId = item.SaleOrderId order.PayStatus = item.PayStatus order.IsPrint = item.IsPrint order.PrintCount = item.PrintCount order.InvoiceCode = item.InvoiceCode order.InvoiceNumber = item.InvoiceNumber order.RejectedTotalAmount = item.RejectedTotalAmount order.RejectedTotalCount = item.RejectedTotalCount if order.RetailType == RetailTypeRejected { // 退货订单,金额需要转换为负值 order.TotalRetailPrice = -math.Abs(order.TotalRetailPrice) order.TotalAmount = -math.Abs(order.TotalAmount) order.TotalCount = -int32(math.Abs(float64(order.TotalCount))) order.TotalSalesProfit = -order.TotalSalesProfit order.TotalStaffProfit = -order.TotalStaffProfit order.TotalDiscount = -math.Abs(order.TotalDiscount) order.VmCount = -uint32(math.Abs(float64(order.VmCount))) order.StorePer = -math.Abs(order.StorePer) } } if order.RetailType == RetailTypeRejected { // 退货订单,金额需要转换为负值 item.Count = -item.Count item.RetailPrice = -item.RetailPrice item.SalePrice = -item.SalePrice item.Amount = -item.Amount item.SaleDiscount = -item.SaleDiscount item.MemberDiscount = -item.MemberDiscount item.VmDiscount = -item.VmDiscount item.ReceivedAmount = -item.ReceivedAmount item.RejectedAmount = -item.RejectedAmount item.SalesProfit = -item.SalesProfit item.StaffProfit = -item.StaffProfit item.StaffCostPrice = -item.StaffCostPrice item.WholesalePrice = -item.WholesalePrice } // 订单商品数据 commodity.CreatedAt = item.ErpOrderCommodity.CreatedAt commodity.ErpOrderId = item.ErpOrderId commodity.ErpCategoryId = item.ErpCategoryId commodity.ErpCategoryName = item.ErpCategoryName commodity.ErpCommodityId = item.ErpCommodityId commodity.ErpCommodityName = item.ErpCommodityName commodity.IMEIType = item.IMEIType commodity.IMEI = item.IMEI commodity.PresentType = item.PresentType commodity.RetailPrice = item.RetailPrice commodity.SalePrice = item.SalePrice commodity.Count = item.Count commodity.SaleDiscount = item.SaleDiscount commodity.MemberDiscount = item.MemberDiscount commodity.VmDiscount = item.VmDiscount commodity.Amount = item.Amount commodity.ReceivedAmount = item.ReceivedAmount commodity.Remark = item.Remark commodity.RejectedPrice = item.RejectedPrice commodity.RejectedCount = item.RejectedCount commodity.RejectedAmount = item.RejectedAmount commodity.RejectedOrderCommodityId = item.RejectedOrderCommodityId commodity.StaffCostPrice = item.StaffCostPrice commodity.WholesalePrice = item.WholesalePrice commodity.ErpSupplierId = item.ErpSupplierId commodity.ErpSupplierName = item.ErpSupplierName commodity.SalesProfit = item.SalesProfit commodity.StaffProfit = item.StaffProfit commodity.StaffPrice = item.WholesalePrice + item.StaffCostPrice order.Commodities = append(order.Commodities, commodity) orderIdMap[item.ErpOrderId] = order } for _, item := range orderIdMap { orders = append(orders, item) } // 根据AuditTime进行降序排序 sort.SliceStable(orders, func(i, j int) bool { return orders[i].AuditTime.After(*orders[j].AuditTime) }) return orders } // 普通单表查询,然后补充收款数据和商品数据 func queryRetailDetailCommon(req *ErpOrderRetailDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { showConfig, err := GetErpOrderShowConfig() if err != nil { logger.Errorf("List err:", err) showConfig.ShowAll = "ON" } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpOrderRetailDetailResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Table("erp_order") totalPerQs := orm.Eloquent.Table("erp_order") if showConfig.ShowAll == "OFF" { qs = qs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) totalPerQs = totalPerQs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) } if req.BillSn != "" { // 订单编号 qs = qs.Where("bill_sn=?", req.BillSn) totalPerQs = totalPerQs.Where("bill_sn=?", req.BillSn) } else { if req.RetailType != "" { // 销售类型 qs = qs.Where("retail_type=?", req.RetailType) totalPerQs = totalPerQs.Where("retail_type=?", req.RetailType) } if req.Uid != 0 { // 用户ID qs = qs.Where("erp_order.uid=?", req.Uid) totalPerQs = totalPerQs.Where("erp_order.uid=?", req.Uid) } if req.Tel != "" { // 用户手机号 qs = qs.Where("tel=?", req.Tel) totalPerQs = totalPerQs.Where("tel=?", req.Tel) } if req.StoreId != 0 { // 门店ID qs = qs.Where("store_id=?", req.StoreId) totalPerQs = totalPerQs.Where("store_id=?", req.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 { if len(storeList) == 1 { qs = qs.Where("store_id = ?", storeList[0]) totalPerQs = totalPerQs.Where("store_id = ?", storeList[0]) } else { qs = qs.Where("store_id IN (?)", storeList) totalPerQs = totalPerQs.Where("store_id IN (?)", storeList) } } else { return nil, errors.New("用户未绑定门店") } } if tools.GetRoleName(c) == "shopper" { qs = qs.Where("JSON_CONTAINS(salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, tools.GetUserId(c))) totalPerQs = totalPerQs.Where("erp_order_sales.uid = ?", tools.GetUserId(c)) } else { if req.Salesman != 0 { // 销售员 qs = qs.Where("JSON_CONTAINS(salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) totalPerQs = totalPerQs.Where("erp_order_sales.uid = ?", req.Salesman) } } if req.CashierId != 0 { // 支付方式 qs = qs.Where("JSON_CONTAINS(cashier_list, ?)", fmt.Sprintf(`{"cashier_id":%d}`, req.CashierId)) qs = qs.Where("retail_type=?", RetailTypeSale) //totalPerQs = totalPerQs.Where("erp_order_pay_way.cashier_id = ?", req.CashierId) } if req.StartTime != "" { // 审核开始时间 parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("err:", err) } qs = qs.Where("audit_time > ?", parse) totalPerQs = totalPerQs.Where("audit_time > ?", parse) } if req.EndTime != "" { // 审核结束时间 parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("err:", err) } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("audit_time < ?", parse) totalPerQs = totalPerQs.Where("audit_time < ?", parse) } } qs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) totalPerQs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) es := qs rejectedTotalPerQs := totalPerQs // 销售订单的汇总数据 orderSumQs := qs var sumData RetailDetailTotalData sumData, err = getRetailDetailTotalDataSale(orderSumQs, RetailTypeSale) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 退货订单的汇总数据 var rejectedSumData RetailDetailTotalData rejectedOrderSumQs := qs rejectedSumData, err = getRetailDetailTotalDataRejected(rejectedOrderSumQs, RetailTypeRejected) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的销售数据 sumData = subtractRetailData(sumData, rejectedSumData) // 销售订单提成汇总 var totalPerData TotalPerData totalPerData, err = getTotalPerData(totalPerQs, RetailTypeSale) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 退货订单提成汇总 var rejectedTotalPerData TotalPerData rejectedTotalPerData, err = getTotalPerData(rejectedTotalPerQs, RetailTypeRejected) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的提成数据 totalPerData = subtractTotalPerData(totalPerData, rejectedTotalPerData) //// 销售订单支付汇总 //var cashier TotalCashierData //cashierQs := qs //cashier, err = getTotalCashierData(cashierQs, RetailTypeSale) //if err != nil { // logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) // return resp, err //} // //// 支付方式筛选 //if req.CashierId != 0 { // var tempCashier TotalCashierData // switch req.CashierId { // case 1: // tempCashier.ScanAmount = cashier.ScanAmount // case 2: // tempCashier.CashAmount = cashier.CashAmount // case 3: // tempCashier.PosAmount = cashier.PosAmount // case 4: // tempCashier.StoreVmAmount = cashier.StoreVmAmount // default: // tempCashier = cashier // } // cashier = tempCashier //} // //// 退货订单支付汇总:目前零售退货订单暂时不展示各个方式的付款金额 //var rejectedCashier TotalCashierData //rejectedCashierQs := qs //rejectedCashier, err = getTotalCashierData(rejectedCashierQs, RetailTypeRejected) //if err != nil { // logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) // return resp, err //} //// 计算销售订单和退货订单汇总后的支付数据 //cashier = subtractCashierData(cashier, rejectedCashier) // //// 处理汇总数据,四舍五入保留2位小数 //roundValues(&sumData, &totalPerData, &cashier) // 销售订单支付汇总 var cashierData []ErpOrderCashier cashierQs := qs cashierData, err = getTotalCashierData(cashierQs, RetailTypeSale) if err != nil { logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) return resp, err } // 支付方式筛选 if req.CashierId != 0 { var filteredData []ErpOrderCashier for _, cashier := range cashierData { if cashier.CashierId == req.CashierId { filteredData = append(filteredData, cashier) } } cashierData = filteredData } // 退货订单支付汇总:目前零售退货订单暂时不展示各个方式的付款金额 var rejectedCashierData []ErpOrderCashier rejectedCashierQs := qs rejectedCashierData, err = getTotalCashierData(rejectedCashierQs, RetailTypeRejected) if err != nil { logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的支付数据 cashierData = subtractCashierData(cashierData, rejectedCashierData) // 处理汇总数据,四舍五入保留2位小数 roundValues(&sumData, &totalPerData) sumData.TotalSalesProfitPer = totalPerData.TotalSalesProfitPer sumData.TotalStaffProfitPer = totalPerData.TotalStaffProfitPer sumData.SalesmanPer = totalPerData.SalesmanPer sumData.TotalCashierData = cashierData RoundRetailDetailTotalData(&sumData) if req.IsExport == 1 { // 导出excel var orders []ErpOrder err = qs.Order("audit_time DESC").Find(&orders).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } // 进行批量处理 var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() ErpOrderRetailDetailSetCommodityAI(orders) // 商品信息处理 }() go func() { defer wg.Done() erpOrderSetBankTrxNoAI(orders) // 银行交易号处理 }() wg.Wait() // 等待所有处理完成 fileUrl, err := retailDetailExport(orders, sumData, c) if err != nil { logger.Error("retailDetailExport err:", logger.Field("err", err)) return resp, err } resp.ExportUrl = fileUrl } else { var count int64 err = es.Count(&count).Error if err != nil { logger.Error("count err:", logger.Field("err", err)) return resp, err } var orders []ErpOrder err = qs.Order("audit_time DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&orders).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } // 添加付款、销售员、商品信息 ErpOrderRetailDetailSetCommodity(orders) erpOrderListSetSalesman(orders) erpOrderListSetCashier(orders) erpOrderSetBankTrxNo(orders) //erpOrderListSetSalesmanByRetailDetail(req.Salesman, orders) resp.List = orders //跟之前保持一致 resp.Total = int(count) resp.PageIndex = page + 1 resp.PageSize = req.PageSize resp.SumData = sumData } return resp, nil } // 四舍五入保留2位小数 func roundValues(data *RetailDetailTotalData, totalPerData *TotalPerData) { roundMap := map[*float64]*float64{ &data.StorePer: &data.StorePer, &totalPerData.TotalSalesProfitPer: &totalPerData.TotalSalesProfitPer, &totalPerData.TotalStaffProfitPer: &totalPerData.TotalStaffProfitPer, &totalPerData.SalesmanPer: &totalPerData.SalesmanPer, } for original, rounded := range roundMap { *rounded = tools.RoundToTwoDecimalPlaces(*original) } } //// 四舍五入保留2位小数 //func roundValues(data *RetailDetailTotalData, totalPerData *TotalPerData, cashier *TotalCashierData) { // roundMap := map[*float64]*float64{ // &data.StorePer: &data.StorePer, // &totalPerData.TotalSalesProfitPer: &totalPerData.TotalSalesProfitPer, // &totalPerData.TotalStaffProfitPer: &totalPerData.TotalStaffProfitPer, // &totalPerData.SalesmanPer: &totalPerData.SalesmanPer, // &cashier.ScanAmount: &cashier.ScanAmount, // &cashier.CashAmount: &cashier.CashAmount, // &cashier.PosAmount: &cashier.PosAmount, // &cashier.StoreVmAmount: &cashier.StoreVmAmount, // &cashier.OtherAmount: &cashier.OtherAmount, // } // // for original, rounded := range roundMap { // *rounded = tools.RoundToTwoDecimalPlaces(*original) // } //} // RoundRetailDetailTotalData 将 RetailDetailTotalData 中所有 float64 字段保留两位小数 func RoundRetailDetailTotalData(data *RetailDetailTotalData) { data.RetailPrice = tools.RoundToTwoDecimalPlaces(data.RetailPrice) data.SalePrice = tools.RoundToTwoDecimalPlaces(data.SalePrice) data.SaleDiscount = tools.RoundToTwoDecimalPlaces(data.SaleDiscount) data.MemberDiscount = tools.RoundToTwoDecimalPlaces(data.MemberDiscount) data.VmDiscount = tools.RoundToTwoDecimalPlaces(data.VmDiscount) data.CouponDiscount = tools.RoundToTwoDecimalPlaces(data.CouponDiscount) data.Amount = tools.RoundToTwoDecimalPlaces(data.Amount) data.WholesalePrice = tools.RoundToTwoDecimalPlaces(data.WholesalePrice) data.StaffPrice = tools.RoundToTwoDecimalPlaces(data.StaffPrice) data.SalesProfit = tools.RoundToTwoDecimalPlaces(data.SalesProfit) data.StaffProfit = tools.RoundToTwoDecimalPlaces(data.StaffProfit) data.TotalRetailPrice = tools.RoundToTwoDecimalPlaces(data.TotalRetailPrice) data.TotalDiscount = tools.RoundToTwoDecimalPlaces(data.TotalDiscount) data.TotalAmount = tools.RoundToTwoDecimalPlaces(data.TotalAmount) data.TotalSalesProfit = tools.RoundToTwoDecimalPlaces(data.TotalSalesProfit) data.TotalStaffProfit = tools.RoundToTwoDecimalPlaces(data.TotalStaffProfit) data.StorePer = tools.RoundToTwoDecimalPlaces(data.StorePer) // 处理TotalCashierData中的float64 //data.ScanAmount = tools.RoundToTwoDecimalPlaces(data.ScanAmount) //data.CashAmount = tools.RoundToTwoDecimalPlaces(data.CashAmount) //data.PosAmount = tools.RoundToTwoDecimalPlaces(data.PosAmount) //data.StoreVmAmount = tools.RoundToTwoDecimalPlaces(data.StoreVmAmount) //data.OtherAmount = tools.RoundToTwoDecimalPlaces(data.OtherAmount) for i, value := range data.TotalCashierData { data.TotalCashierData[i].Amount = tools.RoundToTwoDecimalPlaces(value.Amount) } // 处理TotalPerData中的float64 data.TotalSalesProfitPer = tools.RoundToTwoDecimalPlaces(data.TotalSalesProfitPer) data.TotalStaffProfitPer = tools.RoundToTwoDecimalPlaces(data.TotalStaffProfitPer) data.SalesmanPer = tools.RoundToTwoDecimalPlaces(data.SalesmanPer) } // 查询零售订单的汇总数据 func getRetailDetailTotalDataSale(qs *gorm.DB, retailType string) (RetailDetailTotalData, error) { var sumData RetailDetailTotalData err := qs.Debug(). Select("SUM(cs.count) as count, "+ "SUM(cs.retail_price) as retail_price, "+ "SUM(cs.sale_price) as sale_price, "+ "SUM(cs.sale_discount) as sale_discount, "+ "SUM(cs.member_discount) as member_discount, "+ "SUM(cs.coupon_discount) as coupon_discount, "+ "SUM(cs.amount) as Amount, "+ "SUM(cs.wholesale_price) as wholesale_price, "+ "(SUM(cs.wholesale_price) + SUM(cs.staff_cost_price)) as staff_price, "+ "(SUM(cs.amount) - SUM(cs.wholesale_price)) as sales_profit, "+ "(SUM(cs.amount) - SUM(cs.wholesale_price) - SUM(cs.staff_cost_price)) as staff_profit, "+ "SUM(erp_order.total_retail_price) as total_retail_price, "+ "(SUM(erp_order.total_retail_price) - SUM(erp_order.total_amount)) as total_discount, "+ "SUM(erp_order.total_amount) as total_amount, "+ "SUM(erp_order.total_sales_profit) as total_sales_profit, "+ "SUM(erp_order.total_staff_profit) as total_staff_profit, "+ "SUM(erp_order.store_per) as store_per"). Joins("JOIN (SELECT erp_order_id, "+ "SUM(count) as count, "+ "SUM(retail_price) as retail_price, "+ "SUM(sale_price) as sale_price, "+ "SUM(sale_discount) as sale_discount, "+ "SUM(member_discount) as member_discount, "+ "SUM(coupon_discount) as coupon_discount, "+ "SUM(amount) as amount, "+ "SUM(wholesale_price) as wholesale_price, "+ "SUM(staff_cost_price) as staff_cost_price "+ "FROM erp_order_commodity "+ "GROUP BY erp_order_id) as cs ON cs.erp_order_id = erp_order.id"). Where("erp_order.retail_type = ?", retailType). Scan(&sumData).Error if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return sumData, err } return sumData, nil } // 查询零售退货订单的汇总数据 func getRetailDetailTotalDataRejected(qs *gorm.DB, retailType string) (RetailDetailTotalData, error) { var sumData RetailDetailTotalData err := qs.Debug(). Select("SUM(cs.count) as count, "+ "SUM(cs.retail_price) as retail_price, "+ "SUM(cs.sale_price) as sale_price, "+ "SUM(cs.sale_discount) as sale_discount, "+ "SUM(cs.member_discount) as member_discount, "+ "SUM(cs.coupon_discount) as coupon_discount, "+ "SUM(cs.amount) as Amount, "+ "SUM(cs.wholesale_price) as wholesale_price, "+ "(SUM(cs.wholesale_price) + SUM(cs.staff_cost_price)) as staff_price, "+ "(SUM(cs.amount) - SUM(cs.wholesale_price)) as sales_profit, "+ "(SUM(cs.amount) - SUM(cs.wholesale_price) - SUM(cs.staff_cost_price)) as staff_profit, "+ "SUM(erp_order.total_retail_price) as total_retail_price, "+ "(SUM(erp_order.total_retail_price) - SUM(erp_order.total_amount)) as total_discount, "+ "SUM(erp_order.total_amount) as total_amount, "+ "SUM(erp_order.total_sales_profit) as total_sales_profit, "+ "SUM(erp_order.total_staff_profit) as total_staff_profit, "+ "SUM(erp_order.store_per) as store_per"). Joins("JOIN (SELECT erp_order_id, "+ "SUM(count) as count, "+ "SUM(retail_price) as retail_price, "+ "SUM(sale_price) as sale_price, "+ "SUM(sale_discount) as sale_discount, "+ "SUM(member_discount) as member_discount, "+ "SUM(coupon_discount) as coupon_discount, "+ "SUM(rejected_amount) as amount, "+ "SUM(wholesale_price) as wholesale_price, "+ "SUM(staff_cost_price) as staff_cost_price "+ "FROM erp_order_commodity "+ "GROUP BY erp_order_id) as cs ON cs.erp_order_id = erp_order.id"). Where("erp_order.retail_type = ?", retailType). Scan(&sumData).Error if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return sumData, err } return sumData, nil } // 查询零售订单的汇总数据 func getRetailDetailTotalDataJoinErpOrderSale(qs *gorm.DB, retailType string) (RetailDetailTotalData, error) { var sumData RetailDetailTotalData qs = qs.Where("eo.retail_type = ?", retailType) // 添加额外的条件 err := qs.Debug().Select("SUM(oc.count) as count, " + "SUM(oc.retail_price) as retail_price, " + "SUM(oc.sale_price) as sale_price, " + "SUM(oc.sale_discount) as sale_discount, " + "SUM(oc.member_discount) as member_discount, " + "SUM(oc.amount) as Amount, " + "SUM(oc.wholesale_price) as wholesale_price, " + "(SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) as staff_price, " + "(SUM(oc.Amount) - SUM(oc.wholesale_price)) as sales_profit, " + "(SUM(oc.Amount) - SUM(oc.wholesale_price) - SUM(oc.staff_cost_price)) as staff_profit"). Scan(&sumData).Error if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return sumData, err } return sumData, nil } // 查询零售订单的汇总数据 func getRetailDetailTotalDataJoinErpOrderRejected(qs *gorm.DB, retailType string) (RetailDetailTotalData, error) { var sumData RetailDetailTotalData qs = qs.Where("eo.retail_type = ?", retailType) // 添加额外的条件 err := qs.Debug().Select("SUM(oc.count) as count, " + "SUM(oc.retail_price) as retail_price, " + "SUM(oc.sale_price) as sale_price, " + "SUM(oc.sale_discount) as sale_discount, " + "SUM(oc.member_discount) as member_discount, " + "SUM(oc.rejected_amount) as Amount, " + "SUM(oc.wholesale_price) as wholesale_price, " + "(SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) as staff_price, " + "SUM(oc.sales_profit) as sales_profit, " + "SUM(oc.staff_profit) as staff_profit"). Scan(&sumData).Error if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return sumData, err } return sumData, nil } // 查询零售订单的提成汇总数据 func getTotalPerData(qs *gorm.DB, retailType string) (TotalPerData, error) { var totalPerData TotalPerData err := qs.Debug().Select("SUM(erp_order_sales.sales_profit_per) as total_sales_profit_per, "+ "SUM(erp_order_sales.staff_profit_per) as total_staff_profit_per, "+ "SUM(erp_order_sales.salesman_per) as salesman_per"). Joins("JOIN erp_order_sales ON erp_order_sales.erp_order_id = erp_order.id and erp_order.retail_type = ?", retailType). Scan(&totalPerData).Error if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return totalPerData, err } return totalPerData, nil } //// 查询零售订单的支付汇总数据 //func getTotalCashierData(qs *gorm.DB, retailType string) (TotalCashierData, error) { // var cashier TotalCashierData // err := qs.Debug().Select( // "SUM(CASE WHEN erp_order_pay_way.cashier_id = 1 THEN erp_order_pay_way.amount ELSE 0 END) AS scan_amount,"+ // "SUM(CASE WHEN erp_order_pay_way.cashier_id = 2 THEN erp_order_pay_way.amount ELSE 0 END) AS cash_amount,"+ // "SUM(CASE WHEN erp_order_pay_way.cashier_id = 3 THEN erp_order_pay_way.amount ELSE 0 END) AS pos_amount,"+ // "SUM(CASE WHEN erp_order_pay_way.cashier_id = 4 THEN erp_order_pay_way.amount ELSE 0 END) AS store_vm_amount,"+ // "SUM(CASE WHEN erp_order_pay_way.cashier_id not in (1,2,3,4) THEN erp_order_pay_way.amount ELSE 0 END) AS other_amount"). // Joins("JOIN erp_order_pay_way ON erp_order_pay_way.erp_order_id = erp_order.id and erp_order.retail_type = ?", retailType). // Scan(&cashier).Error // if err != nil { // logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) // return cashier, err // } // return cashier, nil //} // 查询零售订单的支付汇总数据 func getTotalCashierData(qs *gorm.DB, retailType string) ([]ErpOrderCashier, error) { var results []ErpOrderCashier err := qs.Debug(). Select("erp_order_pay_way.cashier_id AS cashier_id, erp_cashier.name AS name, SUM(erp_order_pay_way.amount) AS amount"). Joins("JOIN erp_order_pay_way ON erp_order_pay_way.erp_order_id = erp_order.id AND erp_order.retail_type = ?", retailType). Joins("JOIN erp_cashier ON erp_cashier.id = erp_order_pay_way.cashier_id"). Group("erp_order_pay_way.cashier_id, erp_cashier.name"). Scan(&results).Error if err != nil { logger.Error("query erp_order_pay_way sum data err:", logger.Field("err", err)) return nil, err } // 查询所有支付方式 var sysCashierList []ErpCashier err = orm.Eloquent.Table("erp_cashier").Order("id ASC").Find(&sysCashierList).Error if err != nil { return nil, err } var resp []ErpOrderCashier for _, value := range sysCashierList { var data ErpOrderCashier data.CashierId = value.ID data.Name = value.Name for _, item := range results { if value.ID == item.CashierId { data.Amount = item.Amount } else { continue } } resp = append(resp, data) } return resp, nil } // 计算销售订单和退货订单汇总后的销售数据 func subtractRetailData(sumData, rejectedSumData RetailDetailTotalData) RetailDetailTotalData { result := RetailDetailTotalData{ Count: sumData.Count - rejectedSumData.Count, RetailPrice: sumData.RetailPrice - rejectedSumData.RetailPrice, SalePrice: sumData.SalePrice - rejectedSumData.SalePrice, SaleDiscount: sumData.SaleDiscount - rejectedSumData.SaleDiscount, MemberDiscount: sumData.MemberDiscount - rejectedSumData.MemberDiscount, VmDiscount: sumData.VmDiscount - rejectedSumData.VmDiscount, Amount: sumData.Amount - rejectedSumData.Amount, WholesalePrice: sumData.WholesalePrice - rejectedSumData.WholesalePrice, StaffPrice: sumData.StaffPrice - rejectedSumData.StaffPrice, SalesProfit: sumData.SalesProfit - rejectedSumData.SalesProfit, StaffProfit: sumData.StaffProfit - rejectedSumData.StaffProfit, TotalRetailPrice: sumData.TotalRetailPrice - rejectedSumData.TotalRetailPrice, TotalDiscount: sumData.TotalDiscount - rejectedSumData.TotalDiscount, TotalAmount: sumData.TotalAmount - rejectedSumData.TotalAmount, TotalSalesProfit: sumData.TotalSalesProfit - rejectedSumData.TotalSalesProfit, TotalStaffProfit: sumData.TotalStaffProfit - rejectedSumData.TotalStaffProfit, StorePer: sumData.StorePer - rejectedSumData.StorePer, CouponDiscount: sumData.CouponDiscount - rejectedSumData.CouponDiscount, } //if sumData.TotalSalesProfit != 0 { // result.TotalSalesProfit = sumData.TotalSalesProfit - rejectedSumData.TotalSalesProfit //} else { // result.TotalSalesProfit = rejectedSumData.TotalSalesProfit //} //if sumData.TotalStaffProfit != 0 { // result.TotalStaffProfit = sumData.TotalStaffProfit - rejectedSumData.TotalStaffProfit //} else { // result.TotalStaffProfit = rejectedSumData.TotalStaffProfit //} return result } // 计算销售订单和退货订单汇总后的提成数据 func subtractTotalPerData(totalPerData, rejectedTotalPerData TotalPerData) TotalPerData { result := TotalPerData{ TotalSalesProfitPer: totalPerData.TotalSalesProfitPer - rejectedTotalPerData.TotalSalesProfitPer, TotalStaffProfitPer: totalPerData.TotalStaffProfitPer - rejectedTotalPerData.TotalStaffProfitPer, SalesmanPer: totalPerData.SalesmanPer - rejectedTotalPerData.SalesmanPer, } return result } //// 计算销售订单和退货订单汇总后的支付数据 //func subtractCashierData(cashier, rejectedCashier TotalCashierData) TotalCashierData { // result := TotalCashierData{ // ScanAmount: cashier.ScanAmount - rejectedCashier.ScanAmount, // CashAmount: cashier.CashAmount - rejectedCashier.CashAmount, // PosAmount: cashier.PosAmount - rejectedCashier.PosAmount, // StoreVmAmount: cashier.StoreVmAmount - rejectedCashier.StoreVmAmount, // OtherAmount: cashier.OtherAmount - rejectedCashier.OtherAmount, // } // return result //} // subtractCashierData 计算销售订单和退货订单汇总后的支付数据 func subtractCashierData(cashierData, rejectedCashierData []ErpOrderCashier) []ErpOrderCashier { // 将退货数据转换为 map 便于查找 rejectedMap := make(map[uint32]float64) for _, rejected := range rejectedCashierData { rejectedMap[rejected.CashierId] = rejected.Amount } // 减去退货数据的金额 var result []ErpOrderCashier for _, cashier := range cashierData { adjustedAmount := cashier.Amount - rejectedMap[cashier.CashierId] // 如果金额为负数则视为零 if adjustedAmount < 0 { adjustedAmount = 0 } result = append(result, ErpOrderCashier{ CashierId: cashier.CashierId, Name: cashier.Name, Amount: adjustedAmount, }) } return result } // QueryReceiptData 查询小票数据 func QueryReceiptData(req *ErpOrderDeleteReq, c *gin.Context) (*ErpOrderReceiptDataResp, error) { sysUser, err := GetSysUserByCtx(c) if err != nil { logger.Errorf("err:%#v", err) return nil, err } var orders []ErpOrder err = orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Find(&orders).Error if err != nil { logger.Error("erp_order find err:", logger.Field("err", err)) return nil, err } if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { // 校验入参门店是否包含在用户所有门店中,是否过期 if !CheckUserStore(orders[0].StoreId, sysUser) { return nil, errors.New("操作失败:您没有该门店权限") } } if orders[0].PayStatus != HavePaid { logger.Error("订单未支付") return nil, errors.New("该订单未支付,不支持打印小票") } // 添加付款、销售员、商品信息 erpOrderListSetCommodity(orders) erpOrderListSetCashier(orders) erpOrderListSetSalesman(orders) // 查询门店信息 storeInfo, err := GetStore(orders[0].StoreId) if err != nil { logger.Error("QueryReceiptData GetStore err:", logger.Field("err", err)) return nil, err } // 记录打印信息 if orders[0].IsPrint != HavePrinted { err = orm.Eloquent.Table("erp_order").Where("bill_sn = ?", req.BillSn).Updates(map[string]interface{}{ "is_print": HavePrinted, }).Error if err != nil { return nil, err } } err = orm.Eloquent.Model(&ErpOrder{}). Where("bill_sn = ? ", req.BillSn). UpdateColumn("print_count", gorm.Expr("print_count + ?", 1)).Error if err != nil { return nil, err } order := orders[0] var salesName string if len(order.Salesman) != 0 { // 遍历销售员信息,拼接名称 var names []string for _, item := range order.Salesman { names = append(names, item.Name) } salesName = strings.Join(names, ",") // 使用中文逗号拼接 } resp := new(ErpOrderReceiptDataResp) resp.StoreName = order.StoreName resp.Barcode = order.BillSn resp.OddNum = order.BillSn resp.Time = orders[0].UpdatedAt resp.CollectS = salesName if order.StoreId == 29 { // 大运哈曼店才打印商品串码 resp.IsShowImei = 1 } var categoryList []uint32 if sysUser.CooperativeBusinessId == 1 { // 迪为特殊处理 // 获取任天堂游戏的子分类 categoryList, _ = GetSubcategoryIds(4) } var totalCouponAmount float64 commodityMap := make(map[string]TableData, 0) for i, item := range order.Commodities { var tableData TableData if Contains(categoryList, item.ErpCategoryId) { category, err := GetCategoryById(4) if err != nil { tableData.Name = "任天堂游戏" } else { tableData.Name = category.Name } } else { tableData.Name = item.ErpCommodityName } tableData.SL = uint32(item.Count) tableData.DJ = item.RetailPrice tableData.JE = float64(item.Count) * item.RetailPrice tableData.IMEI = item.IMEI key := fmt.Sprintf("commodity_%d", i) commodityMap[key] = tableData resp.TotalAmount += item.SaleDiscount resp.IntegrationAmount += item.VmDiscount if order.MemberType != ErpOrderMemberTypeGeneral { resp.MembersAmount += item.MemberDiscount } totalCouponAmount += item.CouponDiscount } resp.ChandiseObj = commodityMap resp.TotalRetailP = order.TotalRetailPrice resp.TotalNum = uint32(order.TotalCount) resp.ToDealWith = order.TotalAmount resp.CouponAmount = totalCouponAmount cashierMap := make(map[string]ErpOrderCashier, 0) for i, item := range order.Cashiers { key := fmt.Sprintf("payment_%d", i) cashierMap[key] = item } resp.ModeOfPayment = cashierMap resp.ActualPayment = order.TotalAmount resp.Tel = order.Tel if order.MemberType != ErpOrderMemberTypeGeneral { resp.Uid = order.Uid } resp.StoreTel = storeInfo.Tel resp.StoreAddress = storeInfo.Address return resp, nil } // CreateErpOrder 创建零售订单 func CreateErpOrder(req *ErpOrderCreateReq, c *gin.Context) error { for i, _ := range req.ErpOrderCommodities { req.ErpOrderCommodities[i].ID = 0 } // 校验订单数据 erpOrder, err := checkOrderData(req, c) if err != nil { return err } // 四舍五入 tools.RoundFloatFields(req) begin := orm.Eloquent.Begin() if req.Tel != "" { // 0-创建用户信息 userInfo, err := GetUserInfoByTel(req.Tel) if err != nil { logger.Error("checkOrderData GetUserInfoByTel err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } if userInfo.Uid == 0 { user := UserInfo{ Uid: uint32(erpOrder.Uid), Tel: req.Tel, StoreId: uint64(req.StoreId), UserType: UserTypeConsumer, // 用户 OpenMemberLevel: MemberLevelConsumer, // 普通用户 MemberLevel: MemberLevelConsumer, // 普通用户 CooperativeBusinessId: 1, // 合作商默认为1 } err = begin.Create(&user).Error if err != nil { begin.Rollback() logger.Error("create user err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } } } // 记录用户会员类型 if erpOrder.Uid != 0 { erpOrder.UserType, _ = GetUserType(erpOrder.Uid) } // 1-创建零售订单 err = begin.Create(erpOrder).Error if err != nil { begin.Rollback() logger.Error("create erp_order err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } // 销售信息添加零售订单id for i, _ := range req.Salesman { req.Salesman[i].ErpOrderId = erpOrder.ID } // 2-创建销售员信息记录 err = begin.Create(&req.Salesman).Error if err != nil { begin.Rollback() logger.Error("Create erp_order_sales err") return errors.New("操作失败:" + err.Error()) } // 支付方式添加零售订单id var orderPayWay ErpOrderPayWay var orderPayWayList []ErpOrderPayWay for _, item := range req.Cashiers { orderPayWay.ErpOrderId = erpOrder.ID orderPayWay.CashierId = item.CashierId orderPayWay.Name = item.Name orderPayWay.Amount = item.Amount orderPayWayList = append(orderPayWayList, orderPayWay) } // 3-创建支付方式记录 err = begin.Create(&orderPayWayList).Error if err != nil { begin.Rollback() logger.Error("Create erp_order_pay_way err") return errors.New("操作失败:" + err.Error()) } // 订单商品表信息添加零售订单id for i, _ := range req.ErpOrderCommodities { req.ErpOrderCommodities[i].ErpOrderId = erpOrder.ID } // 4-创建商品订单信息 err = begin.Create(&req.ErpOrderCommodities).Error if err != nil { begin.Rollback() logger.Error("Create erp_order_commodity err") return errors.New("操作失败:" + err.Error()) } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } return nil } // EditErpOrder 编辑订单 func EditErpOrder(req *ErpOrderCreateReq, c *gin.Context) error { var orderInfo ErpOrder err := orm.Eloquent.Table("erp_order").Where("bill_sn=?", req.BillSn).Find(&orderInfo).Error if err != nil { logger.Error("query erp_order err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } if orderInfo.State != ErpOrderStateUnAudit { logger.Error("EditErpOrder err:", logger.Field("err", err)) return errors.New("订单状态不是待审核,操作失败") } // 校验订单数据 erpOrder, err := checkOrderData(req, c) if err != nil { return err } erpOrder.ID = orderInfo.ID erpOrder.BillSn = orderInfo.BillSn // 编辑的订单单据号不能改变 begin := orm.Eloquent.Begin() // 1-更新零售订单 //err = begin.Model(&ErpOrder{}).Where("id = ?", orderInfo.ID).Updates(erpOrder).Error err = begin.Model(&ErpOrder{}).Where("id = ?", orderInfo.ID).Omit("created_at").Save(&erpOrder).Error if err != nil { begin.Rollback() logger.Error("update erp_order err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } // 2-更新零售订单商品信息 err = updateCommodityData(begin, orderInfo.ID, req) if err != nil { begin.Rollback() logger.Error("update erp_order_commodity err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } // 3-更新销售员信息记录 err = updateSalesData(begin, orderInfo.ID, req) if err != nil { begin.Rollback() logger.Error("update erp_order_sales err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } // 4-更新支付方式记录 err = updatePayWayData(begin, orderInfo.ID, req) if err != nil { begin.Rollback() logger.Error("update erp_order_pay_way err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("commit err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } return nil } // CheckUserStore 校验用户门店是否包含在门店数据中且未过期 func CheckUserStore(userStoreId uint32, sysUser *SysUser) bool { // 解析门店数据 var stores []StoreInfo if err := json.Unmarshal([]byte(sysUser.StoreData), &stores); err != nil { return false } // 查找用户门店并检查是否过期 for _, store := range stores { if store.StoreID == int(userStoreId) { expireTime, err := time.Parse(StoreDateTimeFormat, store.ExpireTime) if err != nil { return false } expireTime = expireTime.Add(24*time.Hour - time.Second) // 如果过期时间在当前时间之后,则未过期 if expireTime.After(time.Now()) { return true } return false } } // 没有找到对应的门店 return false } // AuthUserStore 校验是否有某个门店的权限 func AuthUserStore(c *gin.Context, storeId uint32) error { // 非管理员才判断所属门店 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { sysUser, err := GetSysUserByCtx(c) if err != nil { return err } // 校验入参门店是否包含在用户所有门店中,是否过期 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { if !CheckUserStore(storeId, sysUser) { return errors.New("操作失败:您没有该门店权限") } } } return nil } // checkOrderData 校验订单数据 func checkOrderData(req *ErpOrderCreateReq, c *gin.Context) (*ErpOrder, error) { sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } // 校验入参门店是否包含在用户所有门店中,是否过期 if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") { if !CheckUserStore(req.StoreId, sysUser) { return nil, errors.New("操作失败:您没有该门店权限") } } var rejectedOrderSaleId uint32 if req.RetailType == RetailTypeSale { // 校验商品是否有库存,是否是对应门店库存商品 err = CheckOrderCommodityStock(req) if err != nil { return nil, err } } else { // 零售退货单,需校验退货商品是否是本门店售卖商品,同一个退货单中只能添加同一销售订单的商品 rejectedOrderSaleId, err = checkRejectedOrderRule(req) if err != nil { return nil, err } } jCashier, err := json.Marshal(req.Cashiers) if err != nil { logger.Error("cashiers marshal err:", logger.Field("err", err)) return nil, errors.New("操作失败:" + err.Error()) } var userUid uint32 if req.Tel != "" { // 通过手机号查询用户id,如果没有,则新建一个用户id userInfo, err := GetUserInfoByTel(req.Tel) if err != nil { logger.Error("checkOrderData GetUserInfoByTel err:", logger.Field("err", err)) return nil, errors.New("操作失败:" + err.Error()) } if userInfo.Uid == 0 { userUid = CreateUid() // 没有用户则新建 } else { userUid = userInfo.Uid } } erpOrder := &ErpOrder{ BillSn: NewErpBillSn(), RetailType: req.RetailType, Uid: int(userUid), Tel: req.Tel, StoreId: req.StoreId, StoreName: req.StoreName, MakerId: uint32(sysUser.UserId), MakerName: sysUser.NickName, MakerTime: time.Now(), CashierList: string(jCashier), MemberType: req.MemberType, State: ErpOrderStateUnAudit, PayStatus: NoCreatePayOrder, IsPrint: NoPrint, // 未打印 PrintCount: 0, // 打印次数 TotalRetailPrice: req.TotalRetailPrice, VmCount: req.VmAmount, } if req.RetailType == RetailTypeRejected { erpOrder.SaleOrderId = rejectedOrderSaleId } commodityMap := make(map[uint32]ErpStockCommodity) imeiCommodityMap := make(map[string]ErpStockCommodity) if req.RetailType == RetailTypeSale { // 零售订单,查商品表信息 commodityIds := make([]uint32, 0, len(req.ErpOrderCommodities)) // 非串码商品的商品id列表 imeiList := make([]string, 0, len(req.ErpOrderCommodities)) // 串码商品的串码列表 for i, _ := range req.ErpOrderCommodities { if req.ErpOrderCommodities[i].IMEIType == 1 { // 非串码商品 commodityIds = append(commodityIds, req.ErpOrderCommodities[i].ErpCommodityId) } else { // 串码商品 imeiList = append(imeiList, req.ErpOrderCommodities[i].IMEI) } } // 商品的相关价格以库存表为准,商品表的价格除了最低零售价,其他只是参考 // 串码商品直接查询;非串码商品查询库存时间最久的商品 var commodities []ErpStockCommodity if len(imeiList) != 0 { err := orm.Eloquent.Table("erp_stock_commodity").Where("imei IN (?) and state = ? "+ "and store_id = ?", imeiList, InStock, req.StoreId). Find(&commodities).Error if err != nil { logger.Error("get commodities err:", logger.Field("err", err)) return nil, errors.New("操作失败:" + err.Error()) } for i, _ := range commodities { // 串码 imeiCommodityMap[commodities[i].IMEI] = commodities[i] } } if len(commodityIds) != 0 { commodities = nil err = orm.Eloquent.Table("erp_stock_commodity").Where("erp_commodity_id IN (?) and state = ? "+ "and store_id = ?", commodityIds, InStock, req.StoreId). Find(&commodities).Error if err != nil { logger.Error("get commodities err:", logger.Field("err", err)) return nil, errors.New("操作失败:" + err.Error()) } for _, commodity := range commodities { // 非串码 v, ok := commodityMap[commodity.ErpCommodityId] if !ok { // 没有则直接添加 commodityMap[commodity.ErpCommodityId] = commodity } else { // 已有则比较非串码的入库时间,添加入库时间最早的商品 if commodity.FirstStockTime.Before(v.FirstStockTime) { commodityMap[commodity.ErpCommodityId] = commodity } } } } // 更新用户信息表,添加首次零售订单时间 if req.Tel != "" { SetUserInfo(req.Tel) } } bCheckFlag := false // 校验商品相关金额是否符合要求 var respErpOrderCommodities []ErpOrderCommodity for i, _ := range req.ErpOrderCommodities { if req.RetailType == RetailTypeRejected { // 零售退货订单 if req.ErpOrderCommodities[i].RejectedPrice > req.ErpOrderCommodities[i].Amount { // 退货单价不能大于实际零售价 logger.Error("rejected price gt retail price ") return nil, errors.New("退货单价大于实际零售价") } if int32(req.ErpOrderCommodities[i].RejectedCount) > req.ErpOrderCommodities[i].Count { // 退货数量不能大于销售数量 logger.Error("rejected count gt retail count ") return nil, errors.New("退货数量大于销售数量") } // 更新订单表总退款金额和数量 备注:2024-03-12 订单表没有退款金额的字段,默认都使用订单金额字段,根据订单类型判断是零售或退款 //erpOrder.RejectedTotalAmount += req.ErpOrderCommodities[i].RejectedAmount //erpOrder.RejectedTotalCount += req.ErpOrderCommodities[i].RejectedCount erpOrder.TotalAmount += req.ErpOrderCommodities[i].RejectedAmount erpOrder.TotalCount += int32(req.ErpOrderCommodities[i].Count) // 订单总优惠 erpOrder.TotalDiscount += req.ErpOrderCommodities[i].MemberDiscount + req.ErpOrderCommodities[i].SaleDiscount // 销售毛利:实际退货金额-采购金额(需要先取负值,然后再计算),结果等同于:采购金额-实际退货金额 salesProfit := req.ErpOrderCommodities[i].RejectedAmount - req.ErpOrderCommodities[i].WholesalePrice*float64(req.ErpOrderCommodities[i].Count) // 员工毛利 StaffProfit := salesProfit - req.ErpOrderCommodities[i].StaffCostPrice*float64(req.ErpOrderCommodities[i].Count) req.ErpOrderCommodities[i].SalesProfit = salesProfit // 销售毛利 req.ErpOrderCommodities[i].StaffProfit = StaffProfit // 员工毛利 erpOrder.TotalSalesProfit += salesProfit erpOrder.TotalStaffProfit += StaffProfit // 单个商品的销售毛利 req.ErpOrderCommodities[i].SalesProfit = salesProfit / float64(req.ErpOrderCommodities[i].Count) // 单个商品的员工毛利 req.ErpOrderCommodities[i].StaffProfit = StaffProfit / float64(req.ErpOrderCommodities[i].Count) // 单个商品的零售优惠 req.ErpOrderCommodities[i].SaleDiscount = req.ErpOrderCommodities[i].SaleDiscount / float64(req.ErpOrderCommodities[i].Count) // 单个商品的会员优惠 req.ErpOrderCommodities[i].MemberDiscount = req.ErpOrderCommodities[i].MemberDiscount / float64(req.ErpOrderCommodities[i].Count) // 单个商品实收金额 req.ErpOrderCommodities[i].ReceivedAmount = req.ErpOrderCommodities[i].ReceivedAmount / float64(req.ErpOrderCommodities[i].Count) //// 单个商品退货金额 //req.ErpOrderCommodities[i].RejectedAmount = req.ErpOrderCommodities[i].RejectedAmount / float64(req.ErpOrderCommodities[i].Count) for j := 0; j < int(req.ErpOrderCommodities[i].RejectedCount); j++ { stockIdList, _ := stringToIntArray(req.ErpOrderCommodities[i].ErpStockCommodityID) temp := req.ErpOrderCommodities[i] temp.RejectedAmount = req.ErpOrderCommodities[i].RejectedAmount / float64(req.ErpOrderCommodities[i].RejectedCount) temp.Count = 1 temp.RejectedCount = 1 if len(stockIdList) > j { temp.ErpStockCommodityID = fmt.Sprintf("%d", stockIdList[j]) } respErpOrderCommodities = append(respErpOrderCommodities, temp) } } else if req.RetailType == RetailTypeSale { // 零售订单 // 校验是否使用优惠券,如果使用判断是否符合规则 if req.ErpOrderCommodities[i].CouponID != 0 { // 判断优惠券抵扣金额是否正确 var userCoupon UserCoupon err = orm.Eloquent.Table("user_coupon").Where("id = ?", req.ErpOrderCommodities[i].CouponID). Find(&userCoupon).Error if err != nil || err == RecordNotFound { return nil, errors.New("操作失败,未查询到优惠券信息:" + err.Error()) } var coupon Coupon err = orm.Eloquent.Table("coupon").Where("id = ?", userCoupon.CouponId).Find(&coupon).Error if err != nil || err == RecordNotFound { return nil, errors.New("操作失败,未查询到优惠券信息:" + err.Error()) } switch userCoupon.State { case 2: // 已使用 return nil, errors.New(fmt.Sprintf("操作失败:优惠券[%s]已使用,请重新选择", coupon.Name)) case 3: // 已过期 return nil, errors.New(fmt.Sprintf("操作失败:优惠券[%s]已过期,请重新选择", coupon.Name)) } if userCoupon.Value/100 != uint32(req.ErpOrderCommodities[i].CouponDiscount) { return nil, errors.New("操作失败:当前抵扣金额与优惠券金额不符") } // 判断优惠券可用商品分类是否跟当前商品分类是否符合 if !(CheckCouponCategory(userCoupon.CategoryNumber, req.ErpOrderCommodities[i].ErpCategoryId)) { return nil, errors.New(fmt.Sprintf("操作失败:当前商品[%s]与优惠券可用商品不符", req.ErpOrderCommodities[i].ErpCommodityName)) } } var v ErpStockCommodity var ok bool if req.ErpOrderCommodities[i].IMEIType == 1 { // 非串码商品 v, ok = commodityMap[req.ErpOrderCommodities[i].ErpCommodityId] if !ok { return nil, errors.New("遍历商品信息报错") } } else { // 串码商品 v, ok = imeiCommodityMap[req.ErpOrderCommodities[i].IMEI] if !ok { return nil, errors.New("遍历商品信息报错") } } // 赠送类型商品进行校验,最低零售价为0才能赠送 if req.ErpOrderCommodities[i].PresentType == 2 && v.MinRetailPrice != 0 { logger.Error("PresentType is 2, MinRetailPrice no equal 0") return nil, errors.New("赠送商品最低零售价不为0,不符合赠送条件,请检查") } if req.ErpOrderCommodities[i].SalePrice < v.MinRetailPrice { //零售价不能低于最低零售价 logger.Error("SalePrice less than MinRetailPrice") return nil, errors.New("零售价不能低于最低零售价,请检查") } if req.ErpOrderCommodities[i].PresentType == 1 && req.ErpOrderCommodities[i].CouponDiscount < req.ErpOrderCommodities[i].Amount { // 有非赠送商品 bCheckFlag = true } req.ErpOrderCommodities[i].ErpCommodityId = v.ErpCommodityId // 商品id req.ErpOrderCommodities[i].ErpCommodityName = v.ErpCommodityName // 商品名称 req.ErpOrderCommodities[i].ErpCategoryId = v.ErpCategoryId // 分类id req.ErpOrderCommodities[i].ErpCategoryName = v.ErpCategoryName // 分类名称 req.ErpOrderCommodities[i].ErpSupplierId = v.ErpSupplierId // 供应商id req.ErpOrderCommodities[i].ErpSupplierName = v.ErpSupplierName // 供应商名称 req.ErpOrderCommodities[i].RetailPrice = v.RetailPrice // 指导零售价 req.ErpOrderCommodities[i].StaffCostPrice = v.StaffCostPrice // 员工成本价加价 req.ErpOrderCommodities[i].WholesalePrice = v.WholesalePrice // 指导采购价 // 销售毛利 备注:产品说有亏本销售的情况,不用判断毛利是否<0 salesProfit := req.ErpOrderCommodities[i].ReceivedAmount - req.ErpOrderCommodities[i].WholesalePrice*float64(req.ErpOrderCommodities[i].Count) // 员工毛利 StaffProfit := salesProfit - req.ErpOrderCommodities[i].StaffCostPrice*float64(req.ErpOrderCommodities[i].Count) // 单个商品的销售毛利 req.ErpOrderCommodities[i].SalesProfit = salesProfit / float64(req.ErpOrderCommodities[i].Count) // 单个商品的员工毛利 req.ErpOrderCommodities[i].StaffProfit = StaffProfit / float64(req.ErpOrderCommodities[i].Count) // 订单总销售毛利 erpOrder.TotalSalesProfit += salesProfit // 订单总员工毛利 erpOrder.TotalStaffProfit += StaffProfit // 更新订单表总金额和数量 erpOrder.TotalAmount += req.ErpOrderCommodities[i].ReceivedAmount erpOrder.TotalCount += int32(req.ErpOrderCommodities[i].Count) // 单个商品的零售优惠 req.ErpOrderCommodities[i].SaleDiscount = req.ErpOrderCommodities[i].SaleDiscount / float64(req.ErpOrderCommodities[i].Count) // 单个商品的会员优惠 req.ErpOrderCommodities[i].MemberDiscount = req.ErpOrderCommodities[i].MemberDiscount / float64(req.ErpOrderCommodities[i].Count) // 单个商品实收金额 req.ErpOrderCommodities[i].ReceivedAmount = req.ErpOrderCommodities[i].ReceivedAmount / float64(req.ErpOrderCommodities[i].Count) if req.ErpOrderCommodities[i].Amount == 0 && req.ErpOrderCommodities[i].CouponDiscount > req.ErpOrderCommodities[i].SalePrice { // 实际零售金额为0且使用了优惠券 req.ErpOrderCommodities[i].MemberDiscount = 0 req.ErpOrderCommodities[i].SaleDiscount = 0 req.ErpOrderCommodities[i].CouponDiscount = req.ErpOrderCommodities[i].SalePrice } for j := 0; j < int(req.ErpOrderCommodities[i].Count); j++ { temp := req.ErpOrderCommodities[i] temp.Count = 1 respErpOrderCommodities = append(respErpOrderCommodities, temp) } } } if erpOrder.TotalAmount == 0 && bCheckFlag { return nil, errors.New("订单包含非赠送商品,实收金额不能为0") } req.ErpOrderCommodities = respErpOrderCommodities tools.RoundFloatFields(erpOrder) // 判断线上支付金额是否>0 if req.RetailType == RetailTypeSale { erpOrder.TotalAmount = erpOrder.TotalAmount - float64(erpOrder.VmCount/100) onlinePayFlag := false onlinePayAmount := erpOrder.TotalAmount var nWaitPayAmount float64 for _, item := range req.Cashiers { if item.CashierId == 1 { // 线上支付 onlinePayFlag = true } else { onlinePayAmount -= item.Amount } nWaitPayAmount += item.Amount } if onlinePayFlag && onlinePayAmount <= 0 { // 线上支付且金额小于0则报错 logger.Error("线上支付金额小于0") return nil, errors.New("扫码付金额需>0") } if nWaitPayAmount > erpOrder.TotalAmount { // 收款金额大于订单金额 logger.Error("收款金额大于订单金额") return nil, errors.New("收款金额大于订单金额") } if nWaitPayAmount < erpOrder.TotalAmount { // 收款金额小于订单金额 logger.Error("收款金额小于订单金额") return nil, errors.New("收款金额小于订单金额") } } // 添加销售员相关信息 jSalesman, err := req.GetSalesmanList() if err != nil { return nil, errors.New("操作失败:" + err.Error()) } erpOrder.SalesmanList = jSalesman // 添加门店提成 store, err := GetStore(req.StoreId) if err != nil { return nil, errors.New("操作失败:" + err.Error()) } erpOrder.StorePer = erpOrder.TotalStaffProfit * (store.SalesCommRate / 100) erpOrder.StorePer = math.Round(erpOrder.StorePer*100) / 100 // 订单总优惠 if req.RetailType == RetailTypeSale { erpOrder.TotalDiscount = math.Round((erpOrder.TotalRetailPrice-erpOrder.TotalAmount)*100) / 100 } return erpOrder, nil } // CheckOrderCommodityStock 校验商品是否有库存 func CheckOrderCommodityStock(req *ErpOrderCreateReq) error { if len(req.ErpOrderCommodities) != 0 { // 统计串码和非串码商品信息 commodityMap := make(map[uint32]uint32) // 记录非串码商品id及其数量 imeiCommodityMap := make(map[uint32]string) // 记录串码商品id及其串码 commodityNameMap := make(map[uint32]string) // 记录商品名称 for _, commodity := range req.ErpOrderCommodities { if commodity.IMEIType == 1 { _, ok := commodityMap[commodity.ErpCommodityId] if !ok { // 没有则直接添加 commodityMap[commodity.ErpCommodityId] = uint32(commodity.Count) } else { commodityMap[commodity.ErpCommodityId] += uint32(commodity.Count) } commodityNameMap[commodity.ErpCommodityId] = commodity.ErpCommodityName } else { imeiCommodityMap[commodity.ErpCommodityId] = commodity.IMEI commodityNameMap[commodity.ErpCommodityId] = commodity.ErpCommodityName } } // 查询库存表,确认是否有库存 if len(commodityMap) != 0 { // 查询非串码商品库存 var count int64 for commodityId, nCount := range commodityMap { err := orm.Eloquent.Table("erp_stock_commodity"). Where("erp_commodity_id = ? and store_id = ? and state = ? and imei_type = ?", commodityId, req.StoreId, InStock, NoIMEICommodity). Count(&count).Error if err != nil { return err } if count < int64(nCount) { // 获取商品名称 return errors.New("商品" + "[" + commodityNameMap[commodityId] + "]库存不足") } } } if len(imeiCommodityMap) != 0 { // 查询串码商品库存 for commodityId, imei := range imeiCommodityMap { var imeiStockCommodity ErpStockCommodity err := orm.Eloquent.Table("erp_stock_commodity").Where("imei = ? and state = ?", imei, InStock). Find(&imeiStockCommodity).Error if err != nil { return err } if imeiStockCommodity.ID == 0 { // 获取商品名称 return errors.New("商品" + "[" + commodityNameMap[commodityId] + "]库存不足") } if imeiStockCommodity.StoreId != req.StoreId { return errors.New(commodityNameMap[commodityId] + "商品非所选门店库存,请检查") } } } } else { return errors.New("该零售订单未添加商品") } return nil } // 退货校验 // (1)校验退货商品销售时的手机号与当前填写的手机号是否相同,不匹配报错:所退商品的历史订单手机号与当前不符。 // (2)校验商品是否是当前用户购买 // (3)校验退货商品是否是本门店售卖商品 // (4)同一个退货单中只能添加同一销售订单的商品 func checkRejectedOrderRule(req *ErpOrderCreateReq) (uint32, error) { if req.RetailType != RetailTypeRejected { return 0, errors.New("订单类型有误,非退货订单") } if len(req.ErpOrderCommodities) == 0 { return 0, errors.New("未添加退货商品") } var salesId uint32 rejectOrderSalesMap := make(map[string]bool) for _, item := range req.ErpOrderCommodities { // 查询退货商品之前的销售情况 if item.IMEIType == NoIMEICommodity { var saleOrderId uint32 if req.Tel != "" { //非串码商品,且手机号不为空才校验 var commodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id "). Where("erp_order_commodity.erp_commodity_id = ? and erp_order.store_id = ? "+ "and erp_order.retail_type = ? and erp_order.pay_status = ? and erp_order.tel = ?", item.ErpCommodityId, req.StoreId, RetailTypeSale, HavePaid, req.Tel). Find(&commodities).Order("audit_time DESC").Error if err != nil { logger.Error("query erp_order_commodity err:", logger.Field("err", err)) return 0, errors.New("操作失败:" + err.Error()) } if len(commodities) == 0 { return 0, errors.New(fmt.Sprintf("未查询到商品[%s]销售情况", item.ErpCommodityName)) } saleOrderId = commodities[0].ErpOrderId } else { saleOrderId = item.ErpOrderId } var orderInfo ErpOrder err := orm.Eloquent.Table("erp_order").Where("id = ?", saleOrderId). Find(&orderInfo).Error if err != nil { logger.Error("query erp_order err:", logger.Field("err", err)) return 0, errors.New("操作失败:" + err.Error()) } if orderInfo.BillSn == "" { return 0, errors.New(fmt.Sprintf("未查询到商品[%s]销售订单", item.ErpCommodityName)) } rejectOrderSalesMap[orderInfo.BillSn] = true if len(rejectOrderSalesMap) > 1 { return 0, errors.New("只可添加相同零售订单的商品") } salesId = orderInfo.ID } else { // 串码商品 if item.IMEI == "" { return 0, errors.New("退货商品串码为空") } var commodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id "). Where("erp_order_commodity.imei = ? and erp_order.retail_type = ? and erp_order.pay_status = ?", item.IMEI, RetailTypeSale, HavePaid). Order("erp_order_id DESC").Find(&commodities).Error if err != nil { logger.Error("query erp_order_commodity err:", logger.Field("err", err)) return 0, errors.New("操作失败:" + err.Error()) } if len(commodities) == 0 { return 0, errors.New(fmt.Sprintf("未查询到商品[%s]销售情况", item.ErpCommodityName)) } var orderInfo ErpOrder err = orm.Eloquent.Table("erp_order").Where("id = ?", commodities[0].ErpOrderId). Find(&orderInfo).Error if err != nil { logger.Error("query erp_order err:", logger.Field("err", err)) return 0, errors.New("操作失败:" + err.Error()) } if orderInfo.BillSn == "" { return 0, errors.New(fmt.Sprintf("未查询到商品[%s]销售订单", item.ErpCommodityName)) } if orderInfo.Tel != req.Tel { return 0, errors.New(fmt.Sprintf("所退商品[%s]的历史订单手机号与当前不符", item.ErpCommodityName)) } if orderInfo.StoreId != req.StoreId { return 0, errors.New(fmt.Sprintf("[%s]非当前门店所售商品,需前往对应门店退货", item.ErpCommodityName)) } rejectOrderSalesMap[orderInfo.BillSn] = true if len(rejectOrderSalesMap) > 1 { return 0, errors.New("只可添加相同零售订单的商品") } salesId = orderInfo.ID } } return salesId, nil } // updateCommodityData 更新订单商品信息 func updateCommodityData(gdb *gorm.DB, orderId uint32, req *ErpOrderCreateReq) error { // 查询现有的零售订单信息 var commodities []ErpOrderCommodity err := orm.Eloquent.Table("erp_order_commodity").Where("erp_order_id = ?", orderId).Find(&commodities).Error if err != nil { logger.Error("query erp_order_commodity err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } // 1-删除所有的商品信息 if len(commodities) != 0 { err = gdb.Delete(&commodities).Error if err != nil { logger.Error("更新商品订单信息-删除 error") return errors.New("操作失败:" + err.Error()) } } for i, _ := range req.ErpOrderCommodities { req.ErpOrderCommodities[i].ID = 0 if req.ErpOrderCommodities[i].ErpOrderId == 0 || req.ErpOrderCommodities[i].ErpOrderId != orderId { //发现前端有时会没传,后端打补丁 req.ErpOrderCommodities[i].ErpOrderId = orderId } } // 2-更新商品订单信息-新增 err = gdb.Create(&req.ErpOrderCommodities).Error if err != nil { logger.Error("更新商品订单信息-新增 error") return errors.New("操作失败:" + err.Error()) } return nil } // updateSalesData 更新订单销售员信息 func updateSalesData(gdb *gorm.DB, orderId uint32, req *ErpOrderCreateReq) error { // 查询现有的零售订单销售员信息 var orderSales []ErpOrderSales err := orm.Eloquent.Table("erp_order_sales").Where("erp_order_id = ?", orderId).Find(&orderSales).Error if err != nil { logger.Error("query erp_order_sales err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } // 1-删除所有的零售订单支付方式 if len(orderSales) != 0 { err = gdb.Delete(&orderSales).Error if err != nil { logger.Error("更新订单销售员信息-删除 error") return errors.New("操作失败:" + err.Error()) } } var newOrderSales []ErpOrderSales for _, reqSales := range req.Salesman { reqSales.ID = 0 if reqSales.ErpOrderId == 0 { reqSales.ErpOrderId = orderId } newOrderSales = append(newOrderSales, reqSales) } // 2-新增所有的零售订单支付方式 if len(newOrderSales) != 0 { err = gdb.Create(&newOrderSales).Error if err != nil { logger.Error("更新订单销售员信息-新增 error") return errors.New("操作失败:" + err.Error()) } } return nil } // updatePayWayData 更新零售订单支付方式记录表 func updatePayWayData(gdb *gorm.DB, orderId uint32, req *ErpOrderCreateReq) error { // 查询现有的零售订单销售员信息 var orderPayWay []ErpOrderPayWay err := orm.Eloquent.Table("erp_order_pay_way").Where("erp_order_id = ?", orderId).Find(&orderPayWay).Error if err != nil { logger.Error("query erp_order_sales err:", logger.Field("err", err)) return errors.New("操作失败:" + err.Error()) } // 1-删除所有的零售订单支付方式 if len(orderPayWay) != 0 { err = gdb.Delete(&orderPayWay).Error if err != nil { logger.Error("更新零售订单支付方式-删除 error") return errors.New("操作失败:" + err.Error()) } } var newOrderPayWay []ErpOrderPayWay for _, reqPayWay := range req.Cashiers { payWay := ErpOrderPayWay{ ErpOrderId: orderId, CashierId: reqPayWay.CashierId, Name: reqPayWay.Name, Amount: reqPayWay.Amount, } if payWay.ErpOrderId == 0 { payWay.ErpOrderId = orderId } newOrderPayWay = append(newOrderPayWay, payWay) } // 2-新增所有的零售订单支付方式 if len(newOrderPayWay) != 0 { err = gdb.Create(&newOrderPayWay).Error if err != nil { logger.Error("更新零售订单支付方式-新增 error") return errors.New("操作失败:" + err.Error()) } } return nil } // ErpOrderDailyReportReq 经营日报表入参 type ErpOrderDailyReportReq struct { StartTime string `json:"start_time"` // 开始时间 EndTime string `json:"end_time"` // 结束时间 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 } // ErpOrderDailyReportResp 经营日报表出参 type ErpOrderDailyReportResp struct { Total int `json:"total"` // 总条数(总订单数) PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 每页展示条数 TotalCount int32 `json:"total_count"` // 合计总销售数量 TotalAmount float64 `json:"total_amount"` // 合计总金额 TotalSalesProfit float64 `json:"total_sales_profit"` // 合计总毛利(销售毛利;店员看员工毛利) List []DailyReport `json:"list"` // 零售明细 } // DailyReport 经营日报表 type DailyReport struct { StoreId uint32 `json:"store_id"` // 门店ID StoreName string `json:"store_name"` // 门店名称 StoreAmount float64 `json:"store_amount"` // 门店销售金额 StoreSalesProfit float64 `json:"store_sales_profit"` // 门店销售毛利(销售毛利;店员看员工毛利) CategorySales []CategorySalesData `json:"category_sales"` // 各分类的销售数据 } // CategorySalesData 门店分类销售数据 type CategorySalesData struct { CategoryID uint32 `json:"category_id"` // 一级分类ID CategoryName string `json:"category_name"` // 一级分类名称 SaleTitle string `json:"sale_title"` // 金额标题 (例如: "主机金额") TotalSaleAmount float64 `json:"total_sale_amount"` // 销售金额 CostTitle string `json:"cost_title"` // 成本标题 (例如: "主机成本") TotalCostAmount float64 `json:"total_cost_amount"` // 销售成本 ProfitTitle string `json:"profit_title"` // 毛利标题 (例如: "主机毛利") TotalProfitAmount float64 `json:"total_profit_amount"` // 销售利润 } // QueryOrderDailyReport 生成经营日报表 func QueryOrderDailyReport(req *ErpOrderDailyReportReq, c *gin.Context) (*ErpOrderDailyReportResp, error) { page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpOrderDailyReportResp{ PageIndex: page + 1, PageSize: req.PageSize, } // 查询用户所属门店 var storeList []uint32 sysUser, err := GetSysUserByCtx(c) if err != nil { return nil, err } roleName := tools.GetRoleName(c) if !(roleName == "admin" || roleName == "系统管理员") { // 返回sysUser未过期的门店id列表 storeList = GetValidStoreIDs(sysUser.StoreData) } else { storeList, err = GetAllStoreIdsByCooperativeBusinessId(sysUser.CooperativeBusinessId) if err != nil { return nil, errors.New("用户未绑定门店") } } if len(storeList) == 0 { return nil, errors.New("用户未绑定门店") } // 判断是否为店长和店员的账户,店长或员工展示员工成本和员工毛利 showFlag := false if roleName == "store-manager" || roleName == "shopper" { showFlag = true } storeMap, err := GetAllStoreData() if err != nil { return nil, err } // 查询所有门店和分类的零售信息 var sumData []RetailDetailSumData if roleName == "shopper" { //店员只查询自己的业绩 sumData, err = getSalesmanSumDataByStoreAndCategory(req, c) } else { sumData, err = getSumDataByStoreAndCategory(req) } if err != nil { return nil, err } var nTotalAmount, nTotalSalesProfit float64 var nTotalCount int32 var storeDataList []DailyReport for _, store := range storeList { var categoryDataList []CategorySalesData var nStoreAmount, nStoreSalesProfit float64 // 判断门店名称中是否包含"哈曼" displayCategories := getDisplayCategories(storeMap[store].Name) for _, item := range sumData { // 仅当门店ID匹配且分类ID在展示分类中时,才添加分类数据 if item.StoreId == store && containsCategory(displayCategories, item.ErpCategoryId) { nTotalAmount += item.Amount nStoreAmount += item.Amount nTotalCount += item.Count if showFlag { // 员工毛利 nTotalSalesProfit += item.StaffProfit nStoreSalesProfit += item.StaffProfit } else { // 销售毛利 nTotalSalesProfit += item.SalesProfit nStoreSalesProfit += item.SalesProfit } // 处理单个分类数据 categoryData := processCategoryData(item, showFlag) categoryDataList = append(categoryDataList, categoryData) } } // 如果分类数据不足4个,补充缺失的分类,金额为0 categoryDataList = appendMissingCategories(categoryDataList, displayCategories) // 排序分类数据列表 sort.Slice(categoryDataList, func(i, j int) bool { return categoryDataList[i].CategoryID < categoryDataList[j].CategoryID }) // 构建门店数据 storeData := DailyReport{ StoreId: store, StoreName: storeMap[store].Name, StoreAmount: tools.RoundToTwoDecimalPlaces(nStoreAmount), StoreSalesProfit: tools.RoundToTwoDecimalPlaces(nStoreSalesProfit), CategorySales: categoryDataList, } storeDataList = append(storeDataList, storeData) } sort.Slice(storeDataList, func(i, j int) bool { return storeDataList[i].StoreAmount > storeDataList[j].StoreAmount }) // 计算分页所需的切片索引 startIndex := page * req.PageSize endIndex := (page + 1) * req.PageSize if endIndex > len(storeDataList) { endIndex = len(storeDataList) } resp.List = storeDataList[startIndex:endIndex] resp.Total = len(storeDataList) resp.PageIndex = req.PageIndex resp.PageSize = req.PageSize resp.TotalCount = nTotalCount resp.TotalAmount = tools.RoundToTwoDecimalPlaces(nTotalAmount) resp.TotalSalesProfit = tools.RoundToTwoDecimalPlaces(nTotalSalesProfit) return resp, nil } // 获取分类展示列表 func getDisplayCategories(storeName string) []uint32 { if strings.Contains(storeName, "哈曼") { return []uint32{27, 34} // 如果包含"哈曼",只显示分类27和34 } return []uint32{1, 4, 7, 13} // 否则显示分类1、4、7、13 } // 处理单个分类数据 func processCategoryData(item RetailDetailSumData, flag bool) CategorySalesData { var categoryName string switch item.ErpCategoryId { case 1: categoryName = "主机" case 4: categoryName = "游戏" case 7: categoryName = "配件" case 13: categoryName = "周边" default: categoryName = item.ErpCategoryName } var saleData CategorySalesData saleData.CategoryID = item.ErpCategoryId saleData.CategoryName = item.ErpCategoryName saleData.SaleTitle = categoryName + "销售金额" saleData.TotalSaleAmount = tools.RoundToTwoDecimalPlaces(item.Amount) if flag { // 店长或员工展示员工成本和员工毛利 saleData.CostTitle = categoryName + "销售成本" saleData.TotalCostAmount = tools.RoundToTwoDecimalPlaces(item.StaffPrice) saleData.ProfitTitle = categoryName + "销售利润" saleData.TotalProfitAmount = tools.RoundToTwoDecimalPlaces(item.StaffProfit) } else { saleData.CostTitle = categoryName + "销售成本" saleData.TotalCostAmount = tools.RoundToTwoDecimalPlaces(item.WholesalePrice) saleData.ProfitTitle = categoryName + "销售利润" saleData.TotalProfitAmount = tools.RoundToTwoDecimalPlaces(item.SalesProfit) } return saleData } // 补充缺失的分类数据 func appendMissingCategories(categoryDataList []CategorySalesData, displayCategories []uint32) []CategorySalesData { for _, categoryId := range displayCategories { if !containsCategorySalesData(categoryDataList, categoryId) { var categoryName string switch categoryId { case 1: categoryName = "主机" case 4: categoryName = "游戏" case 7: categoryName = "配件" case 13: categoryName = "周边" case 27: categoryName = "哈曼卡顿" case 34: categoryName = "JBL" } categoryDataList = append(categoryDataList, CategorySalesData{ CategoryID: categoryId, CategoryName: categoryName, SaleTitle: categoryName + "销售金额", TotalSaleAmount: 0, // 补充分类金额为0 CostTitle: categoryName + "销售成本", TotalCostAmount: 0, // 补充分类成本为0 ProfitTitle: categoryName + "销售利润", TotalProfitAmount: 0, // 补充分类利润为0 }) } } return categoryDataList } // RetailDetailSumData 零售明细相关金额汇总 type RetailDetailSumData struct { StoreId uint32 `json:"store_id" gorm:"index"` // 门店id StoreName string `json:"store_name"` // 门店名称 ErpCategoryId uint32 `json:"erp_category_id" gorm:"index"` // 分类id ErpCategoryName string `json:"erp_category_name"` // 分类名称 Count int32 `json:"count"` // 销售数量 RetailPrice float64 `json:"retail_price"` // 指导零售价 SalePrice float64 `json:"sale_price"` // 零售价 SaleDiscount float64 `json:"sale_discount"` // 零售优惠 MemberDiscount float64 `json:"member_discount"` // 会员优惠 VmDiscount float64 `json:"vm_discount"` // 会员积分抵扣 CouponDiscount float64 `json:"coupon_discount"` // 优惠券抵扣 Amount float64 `json:"amount"` // 实际零售价 WholesalePrice float64 `json:"wholesale_price"` // 采购单价 StaffPrice float64 `json:"staff_price"` // 员工成本价 SalesProfit float64 `json:"sales_profit"` // 销售毛利 StaffProfit float64 `json:"staff_profit"` // 员工毛利 TotalRetailPrice float64 `json:"total_retail_price"` // 订单总指导零售价 TotalDiscount float64 `json:"total_discount"` // 订单总优惠:订单所有商品零售优惠+会员优惠+会员积分抵扣之和 TotalAmount float64 `json:"total_amount"` // 订单实收金额 TotalCashierData TotalSalesProfit float64 `json:"total_sales_profit"` // 订单总销售毛利 TotalStaffProfit float64 `json:"total_staff_profit"` // 订单总员工毛利 TotalPerData StorePer float64 `json:"store_per"` // 门店提成 } // 查询零售和退货订单的汇总并直接做减法,增加时间筛选条件 func getSumDataByStoreAndCategory(req *ErpOrderDailyReportReq) ([]RetailDetailSumData, error) { var result []RetailDetailSumData // 解析开始时间 startTimeCondition := "" if req.StartTime != "" { parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("开始时间解析错误: %v", err) return result, err } startTimeCondition = fmt.Sprintf("AND eo.audit_time > '%s'", parse.Format(QueryTimeFormat)) } // 解析结束时间 endTimeCondition := "" if req.EndTime != "" { parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("结束时间解析错误: %v", err) return result, err } endTimeCondition = fmt.Sprintf("AND eo.audit_time < '%s'", parse.Format(QueryTimeFormat)) } // 获取所有1级分类 firstCategories, err := GetAllFirstCategories() if err != nil { logger.Error("获取一级分类错误", logger.Field("err", err)) return result, err } // 获取所有一级分类对应的子分类IDs categorySubIds := make(map[uint32][]uint32) for _, category := range firstCategories { subCategoryIDs, err := GetSubcategoryIds(category.ID) if err != nil { logger.Error("获取子分类ID错误", logger.Field("err", err), logger.Field("categoryID", category.ID)) return result, err } categorySubIds[category.ID] = subCategoryIDs } err = orm.Eloquent.Raw(fmt.Sprintf(` SELECT retail.store_id, retail.store_name, retail.erp_category_id, retail.erp_category_name, COALESCE(retail.count, 0) - COALESCE(rejected.count, 0) AS count, COALESCE(retail.retail_price, 0) - COALESCE(rejected.retail_price, 0) AS retail_price, COALESCE(retail.sale_price, 0) - COALESCE(rejected.sale_price, 0) AS sale_price, COALESCE(retail.sale_discount, 0) - COALESCE(rejected.sale_discount, 0) AS sale_discount, COALESCE(retail.member_discount, 0) - COALESCE(rejected.member_discount, 0) AS member_discount, COALESCE(retail.amount, 0) - COALESCE(rejected.amount, 0) AS amount, COALESCE(retail.wholesale_price, 0) - COALESCE(rejected.wholesale_price, 0) AS wholesale_price, COALESCE(retail.staff_price, 0) - COALESCE(rejected.staff_price, 0) AS staff_price, COALESCE(retail.sales_profit, 0) - COALESCE(rejected.sales_profit, 0) AS sales_profit, COALESCE(retail.staff_profit, 0) - COALESCE(rejected.staff_profit, 0) AS staff_profit, COALESCE(retail.total_amount, 0) - COALESCE(rejected.total_amount, 0) AS total_amount, COALESCE(retail.total_sales_profit, 0) - COALESCE(rejected.total_sales_profit, 0) AS total_sales_profit, COALESCE(retail.total_staff_profit, 0) - COALESCE(rejected.total_staff_profit, 0) AS total_staff_profit FROM ( SELECT eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name, SUM(oc.count) AS count, SUM(oc.retail_price) AS retail_price, SUM(oc.sale_price) AS sale_price, SUM(oc.sale_discount) AS sale_discount, SUM(oc.member_discount) AS member_discount, SUM(oc.amount) AS amount, SUM(oc.wholesale_price) AS wholesale_price, (SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) AS staff_price, (SUM(oc.amount) - SUM(oc.wholesale_price)) AS sales_profit, (SUM(oc.amount) - SUM(oc.wholesale_price) - SUM(oc.staff_cost_price)) AS staff_profit, SUM(eo.total_amount) as total_amount, SUM(eo.total_sales_profit) as total_sales_profit, SUM(eo.total_staff_profit) as total_staff_profit FROM erp_order eo JOIN erp_order_commodity oc ON eo.id = oc.erp_order_id WHERE eo.retail_type = ? %s %s GROUP BY eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name ) AS retail LEFT JOIN ( SELECT eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name, SUM(oc.count) AS count, SUM(oc.retail_price) AS retail_price, SUM(oc.sale_price) AS sale_price, SUM(oc.sale_discount) AS sale_discount, SUM(oc.member_discount) AS member_discount, SUM(oc.rejected_amount) AS amount, SUM(oc.wholesale_price) AS wholesale_price, (SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) AS staff_price, SUM(oc.sales_profit) AS sales_profit, SUM(oc.staff_profit) AS staff_profit, SUM(eo.total_amount) as total_amount, SUM(eo.total_sales_profit) as total_sales_profit, SUM(eo.total_staff_profit) as total_staff_profit FROM erp_order eo JOIN erp_order_commodity oc ON eo.id = oc.erp_order_id WHERE eo.retail_type = ? %s %s GROUP BY eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name ) AS rejected ON retail.store_id = rejected.store_id AND retail.erp_category_id = rejected.erp_category_id `, startTimeCondition, endTimeCondition, startTimeCondition, endTimeCondition), RetailTypeSale, RetailTypeRejected).Scan(&result).Error if err != nil { logger.Error("query data with subtraction err:", logger.Field("err", err)) return result, err } // 合并子分类数据(根据门店ID和父分类合并,并累加金额) mergedResult := make(map[string]RetailDetailSumData) for i := range result { for parentID, subCategories := range categorySubIds { if containsCategory(subCategories, result[i].ErpCategoryId) { key := fmt.Sprintf("%d-%d", result[i].StoreId, parentID) if existing, exists := mergedResult[key]; exists { existing.Count += result[i].Count existing.RetailPrice += result[i].RetailPrice existing.SalePrice += result[i].SalePrice existing.SaleDiscount += result[i].SaleDiscount existing.MemberDiscount += result[i].MemberDiscount existing.Amount += result[i].Amount existing.WholesalePrice += result[i].WholesalePrice existing.StaffPrice += result[i].StaffPrice existing.SalesProfit += result[i].SalesProfit existing.StaffProfit += result[i].StaffProfit mergedResult[key] = existing } else { result[i].ErpCategoryId = parentID //result[i].ErpCategoryName = getCategoryNameByCategoryID(parentID) mergedResult[key] = result[i] } break } } } // 转换合并结果为切片 finalResult := make([]RetailDetailSumData, 0, len(mergedResult)) for _, v := range mergedResult { finalResult = append(finalResult, v) } return finalResult, nil } func getSalesmanSumDataByStoreAndCategory(req *ErpOrderDailyReportReq, c *gin.Context) ([]RetailDetailSumData, error) { var result []RetailDetailSumData // 解析开始时间 startTimeCondition := "" if req.StartTime != "" { parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("开始时间解析错误: %v", err) return result, err } startTimeCondition = fmt.Sprintf("AND eo.audit_time > '%s'", parse.Format(QueryTimeFormat)) } // 解析结束时间 endTimeCondition := "" if req.EndTime != "" { parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("结束时间解析错误: %v", err) return result, err } endTimeCondition = fmt.Sprintf("AND eo.audit_time < '%s'", parse.Format(QueryTimeFormat)) } // 销售员条件 salesmanCondition := fmt.Sprintf("AND JSON_CONTAINS(eo.salesman_list, '%s')", fmt.Sprintf(`{"userId":%d}`, tools.GetUserId(c))) // 获取所有1级分类 firstCategories, err := GetAllFirstCategories() if err != nil { logger.Error("获取一级分类错误", logger.Field("err", err)) return result, err } // 获取所有一级分类对应的子分类IDs categorySubIds := make(map[uint32][]uint32) for _, category := range firstCategories { subCategoryIDs, err := GetSubcategoryIds(category.ID) if err != nil { logger.Error("获取子分类ID错误", logger.Field("err", err), logger.Field("categoryID", category.ID)) return result, err } categorySubIds[category.ID] = subCategoryIDs } err = orm.Eloquent.Raw(fmt.Sprintf(` SELECT retail.store_id, retail.store_name, retail.erp_category_id, retail.erp_category_name, COALESCE(retail.count, 0) - COALESCE(rejected.count, 0) AS count, COALESCE(retail.retail_price, 0) - COALESCE(rejected.retail_price, 0) AS retail_price, COALESCE(retail.sale_price, 0) - COALESCE(rejected.sale_price, 0) AS sale_price, COALESCE(retail.sale_discount, 0) - COALESCE(rejected.sale_discount, 0) AS sale_discount, COALESCE(retail.member_discount, 0) - COALESCE(rejected.member_discount, 0) AS member_discount, COALESCE(retail.amount, 0) - COALESCE(rejected.amount, 0) AS amount, COALESCE(retail.wholesale_price, 0) - COALESCE(rejected.wholesale_price, 0) AS wholesale_price, COALESCE(retail.staff_price, 0) - COALESCE(rejected.staff_price, 0) AS staff_price, COALESCE(retail.sales_profit, 0) - COALESCE(rejected.sales_profit, 0) AS sales_profit, COALESCE(retail.staff_profit, 0) - COALESCE(rejected.staff_profit, 0) AS staff_profit, COALESCE(retail.total_amount, 0) - COALESCE(rejected.total_amount, 0) AS total_amount, COALESCE(retail.total_sales_profit, 0) - COALESCE(rejected.total_sales_profit, 0) AS total_sales_profit, COALESCE(retail.total_staff_profit, 0) - COALESCE(rejected.total_staff_profit, 0) AS total_staff_profit FROM ( SELECT eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name, SUM(oc.count) AS count, SUM(oc.retail_price) AS retail_price, SUM(oc.sale_price) AS sale_price, SUM(oc.sale_discount) AS sale_discount, SUM(oc.member_discount) AS member_discount, SUM(oc.amount) AS amount, SUM(oc.wholesale_price) AS wholesale_price, (SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) AS staff_price, (SUM(oc.amount) - SUM(oc.wholesale_price)) AS sales_profit, (SUM(oc.amount) - SUM(oc.wholesale_price) - SUM(oc.staff_cost_price)) AS staff_profit, SUM(eo.total_amount) as total_amount, SUM(eo.total_sales_profit) as total_sales_profit, SUM(eo.total_staff_profit) as total_staff_profit FROM erp_order eo JOIN erp_order_commodity oc ON eo.id = oc.erp_order_id WHERE eo.retail_type = ? %s %s %s GROUP BY eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name ) AS retail LEFT JOIN ( SELECT eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name, SUM(oc.count) AS count, SUM(oc.retail_price) AS retail_price, SUM(oc.sale_price) AS sale_price, SUM(oc.sale_discount) AS sale_discount, SUM(oc.member_discount) AS member_discount, SUM(oc.rejected_amount) AS amount, SUM(oc.wholesale_price) AS wholesale_price, (SUM(oc.wholesale_price) + SUM(oc.staff_cost_price)) AS staff_price, SUM(oc.sales_profit) AS sales_profit, SUM(oc.staff_profit) AS staff_profit, SUM(eo.total_amount) as total_amount, SUM(eo.total_sales_profit) as total_sales_profit, SUM(eo.total_staff_profit) as total_staff_profit FROM erp_order eo JOIN erp_order_commodity oc ON eo.id = oc.erp_order_id WHERE eo.retail_type = ? %s %s %s GROUP BY eo.store_id, eo.store_name, oc.erp_category_id, oc.erp_category_name ) AS rejected ON retail.store_id = rejected.store_id AND retail.erp_category_id = rejected.erp_category_id `, startTimeCondition, endTimeCondition, salesmanCondition, startTimeCondition, endTimeCondition, salesmanCondition), RetailTypeSale, RetailTypeRejected).Scan(&result).Error if err != nil { logger.Error("query data with subtraction err:", logger.Field("err", err)) return result, err } // 合并子分类数据(根据门店ID和父分类合并,并累加金额) mergedResult := make(map[string]RetailDetailSumData) for i := range result { for parentID, subCategories := range categorySubIds { if containsCategory(subCategories, result[i].ErpCategoryId) { key := fmt.Sprintf("%d-%d", result[i].StoreId, parentID) if existing, exists := mergedResult[key]; exists { existing.Count += result[i].Count existing.RetailPrice += result[i].RetailPrice existing.SalePrice += result[i].SalePrice existing.SaleDiscount += result[i].SaleDiscount existing.MemberDiscount += result[i].MemberDiscount existing.Amount += result[i].Amount existing.WholesalePrice += result[i].WholesalePrice existing.StaffPrice += result[i].StaffPrice existing.SalesProfit += result[i].SalesProfit existing.StaffProfit += result[i].StaffProfit mergedResult[key] = existing } else { result[i].ErpCategoryId = parentID //result[i].ErpCategoryName = getCategoryNameByCategoryID(parentID) mergedResult[key] = result[i] } break } } } // 转换合并结果为切片 finalResult := make([]RetailDetailSumData, 0, len(mergedResult)) for _, v := range mergedResult { finalResult = append(finalResult, v) } return finalResult, nil } // containsCategory 检查分类ID是否在子分类IDs列表中 func containsCategory(subCategoryIds []uint32, categoryId uint32) bool { for _, id := range subCategoryIds { if id == categoryId { return true } } return false } // 辅助函数,用于检查分类是否已经存在于categoryDataList中 func containsCategorySalesData(categoryDataList []CategorySalesData, categoryId uint32) bool { for _, category := range categoryDataList { if category.CategoryID == categoryId { return true } } return false } // ErpOrderSaleDetailReq 销售明细汇总入参 type ErpOrderSaleDetailReq struct { BillSn string `json:"bill_sn"` // 单据编号 RetailType string `json:"retail_type"` // 销售类型:sale 零售销售; rejected 零售退货 UserType []uint32 `json:"user_type"` // 会员类别:0-未注册 1-普通会员 2-黄金会员 4-白金会员 5-黑金会员 6-尊享会员 7-黄金&尊享 8-白金&尊享 9-黑金&尊享 Uid int `json:"uid"` // 用户ID Tel string `json:"tel"` // 客户手机号 StoreId uint32 `json:"store_id"` // 门店ID ErpCategoryId []uint32 `json:"erp_category_id"` // 分类id ErpCommodityName string `json:"erp_commodity_name"` // 商品名称 Salesman uint32 `json:"salesman"` // 销售人员ID IMEI string `json:"imei"` // 串码 StartTime string `json:"start_time"` // 开始时间 EndTime string `json:"end_time"` // 结束时间 BankTrxNo string `json:"bank_trx_no"` // 银联流水号 PageIndex int `json:"pageIndex"` // 页码 PageSize int `json:"pageSize"` // 页面条数 IsExport uint32 `json:"is_export"` // 1-导出 } // QuerySaleDetail 查询销售明细 func QuerySaleDetail(req *ErpOrderSaleDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { resp := &ErpOrderRetailDetailResp{} // 通过银行流水号查询 if req.BankTrxNo != "" { var orderPayWay ErpOrderRecord err := orm.Eloquent.Table("erp_order_record").Where("bank_trx_no = ? and status = ?", req.BankTrxNo, PayOk).Find(&orderPayWay).Error if err != nil { logger.Error("query erp_order_record err:", logger.Field("err", err)) return nil, err } req.BillSn = orderPayWay.BillSn } var err error if (len(req.ErpCategoryId) != 0 || req.ErpCommodityName != "" || req.IMEI != "") && req.BillSn == "" { // 商品分类or商品名称不为空且订单编号为空 // 联表查询 resp, err = querySaleDetailByJoin(req, c) } else { // 普通单表查询,然后补充收款数据和商品数据 resp, err = querySaleDetailCommon(req, c) } if err != nil { logger.Error("queryRetailDetailCommon err") return nil, err } return resp, nil } // 联表查询 func querySaleDetailByJoin(req *ErpOrderSaleDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { showConfig, err := GetErpOrderShowConfig() if err != nil { logger.Errorf("List err:", err) showConfig.ShowAll = "ON" } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpOrderRetailDetailResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Debug().Table("erp_order_commodity"). Select("erp_order_commodity.*, erp_order.*"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") es := orm.Eloquent.Debug().Table("erp_order_commodity"). Select("erp_order_commodity.*, erp_order.*"). Joins("JOIN erp_order ON erp_order_commodity.erp_order_id = erp_order.id") orderSumQs := orm.Eloquent.Table("erp_order_commodity AS oc").Select("oc.*, eo.*"). Joins("JOIN erp_order AS eo ON oc.erp_order_id = eo.id") rejectedOrderSumQs := orm.Eloquent.Table("erp_order_commodity AS oc").Select("oc.*, eo.*"). Joins("JOIN erp_order AS eo ON oc.erp_order_id = eo.id") if len(req.ErpCategoryId) != 0 { // 商品分类 categoryList, err := TransformErpCategoryIds(req.ErpCategoryId) if err != nil { return nil, err } qs = qs.Where("erp_order_commodity.erp_category_id in ?", categoryList) es = es.Where("erp_order_commodity.erp_category_id in ?", categoryList) orderSumQs = orderSumQs.Where("oc.erp_category_id in ?", categoryList) rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.erp_category_id in ?", categoryList) } if req.ErpCommodityName != "" { // 商品名称 qs = qs.Where("erp_order_commodity.erp_commodity_name = ?", req.ErpCommodityName) es = es.Where("erp_order_commodity.erp_commodity_name = ?", req.ErpCommodityName) orderSumQs = orderSumQs.Where("oc.erp_commodity_name = ?", req.ErpCommodityName) rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.erp_commodity_name = ?", req.ErpCommodityName) } if req.RetailType != "" { // 销售类型 qs = qs.Where("erp_order.retail_type=?", req.RetailType) es = es.Where("erp_order.retail_type=?", req.RetailType) orderSumQs = orderSumQs.Where("eo.retail_type=?", req.RetailType) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.retail_type=?", req.RetailType) } if req.Uid != 0 { // 用户ID qs = qs.Where("erp_order.uid=?", req.Uid) es = es.Where("erp_order.uid=?", req.Uid) orderSumQs = orderSumQs.Where("eo.uid=?", req.Uid) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.uid=?", req.Uid) } if len(req.UserType) != 0 { // 会员类别 qs = qs.Where("user_type in ?", req.UserType) es = es.Where("user_type in ?", req.UserType) orderSumQs = orderSumQs.Where("user_type in ?", req.UserType) rejectedOrderSumQs = rejectedOrderSumQs.Where("user_type in ?", req.UserType) } if req.Tel != "" { // 用户手机号 qs = qs.Where("erp_order.tel=?", req.Tel) es = es.Where("erp_order.tel=?", req.Tel) orderSumQs = orderSumQs.Where("eo.tel=?", req.Tel) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.tel=?", req.Tel) } if req.StoreId != 0 { // 门店ID qs = qs.Where("erp_order.store_id=?", req.StoreId) es = es.Where("erp_order.store_id=?", req.StoreId) orderSumQs = orderSumQs.Where("eo.store_id=?", req.StoreId) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id=?", req.StoreId) } if req.IMEI != "" { // 串码 qs = qs.Where("erp_order_commodity.imei=?", req.IMEI) es = es.Where("erp_order_commodity.imei=?", req.IMEI) orderSumQs = orderSumQs.Where("oc.imei=?", req.IMEI) rejectedOrderSumQs = rejectedOrderSumQs.Where("oc.imei=?", req.IMEI) } // 非管理员才判断所属门店 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 { if len(storeList) == 1 { qs = qs.Where("erp_order.store_id = ?", storeList[0]) es = es.Where("erp_order.store_id = ?", storeList[0]) orderSumQs = orderSumQs.Where("eo.store_id = ?", storeList[0]) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id = ?", storeList[0]) } else { qs = qs.Where("erp_order.store_id IN (?)", storeList) es = es.Where("erp_order.store_id IN (?)", storeList) orderSumQs = orderSumQs.Where("eo.store_id IN (?)", storeList) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.store_id IN (?)", storeList) } } else { return nil, errors.New("用户未绑定门店") } } if req.Salesman != 0 { // 销售员 qs = qs.Where("JSON_CONTAINS(erp_order.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) es = es.Where("JSON_CONTAINS(erp_order.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) orderSumQs = orderSumQs.Where("JSON_CONTAINS(eo.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) rejectedOrderSumQs = rejectedOrderSumQs.Where("JSON_CONTAINS(eo.salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) } if req.StartTime != "" { // 审核开始时间 parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("err:", err) } qs = qs.Where("erp_order.audit_time > ?", parse) es = es.Where("erp_order.audit_time > ?", parse) orderSumQs = orderSumQs.Where("eo.audit_time > ?", parse) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.audit_time > ?", parse) } if req.EndTime != "" { // 审核结束时间 parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("err:", err) } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("erp_order.audit_time < ?", parse) es = es.Where("erp_order.audit_time < ?", parse) orderSumQs = orderSumQs.Where("eo.audit_time < ?", parse) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.audit_time < ?", parse) } if showConfig.ShowAll == "OFF" { qs = qs.Where("erp_order.is_print = ? or erp_order.retail_type = ?", HavePrinted, RetailTypeRejected) es = es.Where("erp_order.is_print = ? or erp_order.retail_type = ?", HavePrinted, RetailTypeRejected) orderSumQs = orderSumQs.Where("eo.is_print = ? or eo.retail_type = ?", HavePrinted, RetailTypeRejected) rejectedOrderSumQs = rejectedOrderSumQs.Where("eo.is_print = ? or eo.retail_type = ?", HavePrinted, RetailTypeRejected) } qs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) es.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) orderSumQs.Where("eo.pay_status = ? or (eo.retail_type = ? and eo.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) rejectedOrderSumQs.Where("eo.pay_status = ? or (eo.retail_type = ? and eo.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) // 销售订单的汇总数据 var sumData RetailDetailTotalData sumData, err = getRetailDetailTotalDataJoinErpOrderSale(orderSumQs, RetailTypeSale) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 退货订单的汇总数据 var rejectedSumData RetailDetailTotalData rejectedSumData, err = getRetailDetailTotalDataJoinErpOrderRejected(rejectedOrderSumQs, RetailTypeRejected) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的销售数据 sumData = subtractRetailData(sumData, rejectedSumData) // 销售订单提成汇总 var totalPerData TotalPerData totalPerQs := qs totalPerData, err = getTotalPerData(totalPerQs, RetailTypeSale) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 退货订单提成汇总 var rejectedTotalPerData TotalPerData rejectedTotalPerQs := qs rejectedTotalPerData, err = getTotalPerData(rejectedTotalPerQs, RetailTypeRejected) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的提成数据 totalPerData = subtractTotalPerData(totalPerData, rejectedTotalPerData) // 处理汇总数据,四舍五入保留2位小数 saleDetailRoundValues(&sumData, &totalPerData) sumData.TotalSalesProfit = 0 // 订单总销售毛利 sumData.TotalStaffProfit = 0 // 订单总员工毛利 sumData.TotalRetailPrice = 0 // 订单总指导零售价 sumData.TotalDiscount = 0 // 订单总优惠 sumData.TotalAmount = 0 // 订单实收金额 sumData.StorePer = 0 // 门店提成 sumData.TotalCashierData = nil //sumData.ScanAmount = 0 //sumData.CashAmount = 0 //sumData.PosAmount = 0 //sumData.StoreVmAmount = 0 //sumData.OtherAmount = 0 RoundRetailDetailTotalData(&sumData) var result []RetailDetailByJoin if req.IsExport == 1 { //导出excel err = qs.Order("erp_order.audit_time DESC").Find(&result).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } var count int64 err = es.Count(&count).Error if err != nil { logger.Errorf("QueryRetailMargin count err:", err.Error()) return nil, err } orders := packData(result) erpOrderSetBankTrxNo(orders) fileUrl, err := saleDetailExport(orders, sumData, c) if err != nil { logger.Error("retailDetailExport err:", logger.Field("err", err)) return resp, err } resp.ExportUrl = fileUrl } else { //err = qs.Order("erp_order.audit_time DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&result).Error err = qs.Order("erp_order.audit_time DESC").Find(&result).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } var count int64 err = es.Count(&count).Error if err != nil { logger.Errorf("QueryRetailMargin count err:", err.Error()) return nil, err } orders := packData(result) erpOrderListSetSalesman(orders) erpOrderSetBankTrxNo(orders) pagedOrders := paginate(orders, page, req.PageSize) resp.List = pagedOrders //跟之前保持一致 resp.Total = len(orders) resp.PageIndex = page + 1 resp.PageSize = req.PageSize resp.SumData = sumData } return resp, nil } // 普通单表查询,然后补充收款数据和商品数据 func querySaleDetailCommon(req *ErpOrderSaleDetailReq, c *gin.Context) (*ErpOrderRetailDetailResp, error) { showConfig, err := GetErpOrderShowConfig() if err != nil { logger.Errorf("List err:", err) showConfig.ShowAll = "ON" } page := req.PageIndex - 1 if page < 0 { page = 0 } if req.PageSize == 0 { req.PageSize = 10 } resp := &ErpOrderRetailDetailResp{ PageIndex: page + 1, PageSize: req.PageSize, } qs := orm.Eloquent.Table("erp_order") totalPerQs := orm.Eloquent.Table("erp_order") if showConfig.ShowAll == "OFF" { qs = qs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) totalPerQs = totalPerQs.Where("is_print = ? or retail_type = ?", HavePrinted, RetailTypeRejected) } if req.BillSn != "" { // 订单编号 qs = qs.Where("bill_sn=?", req.BillSn) totalPerQs = totalPerQs.Where("bill_sn=?", req.BillSn) } else { if req.RetailType != "" { // 销售类型 qs = qs.Where("retail_type=?", req.RetailType) totalPerQs = totalPerQs.Where("retail_type=?", req.RetailType) } if req.Uid != 0 { // 用户ID qs = qs.Where("erp_order.uid=?", req.Uid) totalPerQs = totalPerQs.Where("erp_order.uid=?", req.Uid) } if req.Tel != "" { // 用户手机号 qs = qs.Where("tel=?", req.Tel) totalPerQs = totalPerQs.Where("tel=?", req.Tel) } if req.StoreId != 0 { // 门店ID qs = qs.Where("store_id=?", req.StoreId) totalPerQs = totalPerQs.Where("store_id=?", req.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 { if len(storeList) == 1 { qs = qs.Where("store_id = ?", storeList[0]) totalPerQs = totalPerQs.Where("store_id = ?", storeList[0]) } else { qs = qs.Where("store_id IN (?)", storeList) totalPerQs = totalPerQs.Where("store_id IN (?)", storeList) } } else { return nil, errors.New("用户未绑定门店") } } if req.Salesman != 0 { // 销售员 qs = qs.Where("JSON_CONTAINS(salesman_list, ?)", fmt.Sprintf(`{"userId":%d}`, req.Salesman)) totalPerQs = totalPerQs.Where("erp_order_sales.uid = ?", req.Salesman) } if req.StartTime != "" { // 审核开始时间 parse, err := time.Parse(QueryTimeFormat, req.StartTime) if err != nil { logger.Errorf("err:", err) } qs = qs.Where("audit_time > ?", parse) totalPerQs = totalPerQs.Where("audit_time > ?", parse) } if req.EndTime != "" { // 审核结束时间 parse, err := time.Parse(QueryTimeFormat, req.EndTime) if err != nil { logger.Errorf("err:", err) } //parse = parse.AddDate(0, 0, 1) qs = qs.Where("audit_time < ?", parse) totalPerQs = totalPerQs.Where("audit_time < ?", parse) } if len(req.UserType) != 0 { qs = qs.Where("user_type in ?", req.UserType) totalPerQs = totalPerQs.Where("user_type in ?", req.UserType) } } qs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) totalPerQs.Where("erp_order.pay_status = ? or (erp_order.retail_type = ? and erp_order.state != ?)", HavePaid, RetailTypeRejected, ErpOrderStateUnAudit) es := qs rejectedTotalPerQs := totalPerQs // 销售订单的汇总数据 orderSumQs := qs var sumData RetailDetailTotalData sumData, err = getRetailDetailTotalDataSale(orderSumQs, RetailTypeSale) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 退货订单的汇总数据 var rejectedSumData RetailDetailTotalData rejectedOrderSumQs := qs rejectedSumData, err = getRetailDetailTotalDataRejected(rejectedOrderSumQs, RetailTypeRejected) if err != nil { logger.Error("query sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的销售数据 sumData = subtractRetailData(sumData, rejectedSumData) // 销售订单提成汇总 var totalPerData TotalPerData totalPerData, err = getTotalPerData(totalPerQs, RetailTypeSale) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 退货订单提成汇总 var rejectedTotalPerData TotalPerData rejectedTotalPerData, err = getTotalPerData(rejectedTotalPerQs, RetailTypeRejected) if err != nil { logger.Error("query erp_order_sales sum data err:", logger.Field("err", err)) return resp, err } // 计算销售订单和退货订单汇总后的提成数据 totalPerData = subtractTotalPerData(totalPerData, rejectedTotalPerData) // 处理汇总数据,四舍五入保留2位小数 saleDetailRoundValues(&sumData, &totalPerData) sumData.TotalSalesProfitPer = totalPerData.TotalSalesProfitPer sumData.TotalStaffProfitPer = totalPerData.TotalStaffProfitPer sumData.SalesmanPer = totalPerData.SalesmanPer RoundRetailDetailTotalData(&sumData) if req.IsExport == 1 { // 导出excel var orders []ErpOrder err = qs.Order("audit_time DESC").Find(&orders).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } // 进行批量处理 var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() ErpOrderRetailDetailSetCommodityAI(orders) // 商品信息处理 }() go func() { defer wg.Done() erpOrderSetBankTrxNoAI(orders) // 银行交易号处理 }() wg.Wait() // 等待所有处理完成 fileUrl, err := saleDetailExport(orders, sumData, c) if err != nil { logger.Error("retailDetailExport err:", logger.Field("err", err)) return resp, err } resp.ExportUrl = fileUrl } else { var count int64 err = es.Count(&count).Error if err != nil { logger.Error("count err:", logger.Field("err", err)) return resp, err } var orders []ErpOrder err = qs.Order("audit_time DESC").Offset(page * req.PageSize).Limit(req.PageSize).Find(&orders).Error if err != nil && err != RecordNotFound { logger.Error("erp commodity list err:", logger.Field("err", err)) return resp, err } // 添加付款、销售员、商品信息 ErpOrderRetailDetailSetCommodity(orders) erpOrderListSetSalesman(orders) erpOrderSetBankTrxNo(orders) resp.List = orders //跟之前保持一致 resp.Total = int(count) resp.PageIndex = page + 1 resp.PageSize = req.PageSize resp.SumData = sumData } return resp, nil } // 四舍五入保留2位小数 func saleDetailRoundValues(data *RetailDetailTotalData, totalPerData *TotalPerData) { roundMap := map[*float64]*float64{ &data.StorePer: &data.StorePer, &totalPerData.TotalSalesProfitPer: &totalPerData.TotalSalesProfitPer, &totalPerData.TotalStaffProfitPer: &totalPerData.TotalStaffProfitPer, &totalPerData.SalesmanPer: &totalPerData.SalesmanPer, } for original, rounded := range roundMap { *rounded = tools.RoundToTwoDecimalPlaces(*original) } } // 导出销售明细报表excel func saleDetailExport(list []ErpOrder, sumData RetailDetailTotalData, c *gin.Context) (string, error) { file := excelize.NewFile() fSheet := "Sheet1" url := config.ExportConfig.Url fileName := time.Now().Format(TimeFormat) + "销售明细" + ".xlsx" fmt.Println("url fileName:", url+fileName) title := []interface{}{"订单编号", "订单类型", "店铺", "会员类别", "用户ID", "日期", "审核时间", "销售员1", "销售员2", "一级分类", "二级分类", "三级分类", "商品名称", "供应商", "商品串码", "是否赠送", "销售数量", "指导价", "零售价", "零售优惠", "会员优惠", "销售金额", "采购单价", "员工成本", "销售毛利", "员工毛利", "银联流水", "联系电话", "备注"} 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)) } } categoryMap, err := GetAllCategories() if err != nil { // 处理错误 logger.Error("GetAllCategories err:", logger.Field("err", err)) } var row []interface{} nAmount := 0.0 nExcelStartRow := 0 for i := 0; i < len(list); i++ { var saleType string if list[i].RetailType == RetailTypeSale { saleType = "零售销售" } else if list[i].RetailType == RetailTypeRejected { saleType = "零售退货" } else { logger.Error("订单类型异常") return "", errors.New("RetailMarginDataExport, 订单类型异常:" + list[i].RetailType) } var salesMan1, salesMan2 string // 先判断商品数量,确定要写几行数据 for rowId := 0; rowId < len(list[i].Commodities); rowId++ { if list[i].RetailType == RetailTypeSale { nAmount = list[i].Commodities[rowId].Amount } else if list[i].RetailType == RetailTypeRejected { nAmount = list[i].Commodities[rowId].RejectedAmount list[i].CashierList = "" // 目前零售退货订单暂时不展示各个方式的付款金额 } // 用户id,会员类别 var userId, userType string if list[i].Uid != 0 { userId = strconv.Itoa(list[i].Uid) userType = GetUserTypeString(list[i].UserType) } else { userId = "" } // 审核时间 var auditTime, auditDate string if list[i].AuditTime != nil { auditTime = list[i].AuditTime.Format(ExcelTimeFormat) auditDate = list[i].AuditTime.Format(DateTimeFormat) // Extract date only } else { auditTime = "" auditDate = "" } // 商品分类 categoryID := list[i].Commodities[rowId].ErpCategoryId categoryLevels := GetCategoryLevelsFromMap(categoryID, categoryMap) // 是否赠送 strPresentType := "非赠送" if list[i].Commodities[rowId].PresentType == 2 { strPresentType = "赠送" } if len(list[i].Salesman) > 0 { salesMan1 = list[i].Salesman[0].Name if len(list[i].Salesman) == 2 { salesMan2 = list[i].Salesman[1].Name } } row = []interface{}{ list[i].BillSn, // 订单编号 saleType, // 订单类型 list[i].StoreName, // 店铺 userType, // 会员类别 userId, // 用户ID auditDate, // 日期 auditTime, // 审核时间 salesMan1, // 销售员1 salesMan2, // 销售员2 categoryLevels.Level1.Name, // 一级分类 categoryLevels.Level2.Name, // 二级分类 categoryLevels.Level3.Name, // 三级分类 list[i].Commodities[rowId].ErpCommodityName, // 商品名称 list[i].Commodities[rowId].ErpSupplierName, // 供应商 list[i].Commodities[rowId].IMEI, // 商品串码 strPresentType, // 是否赠送 list[i].Commodities[rowId].Count, // 销售数量 list[i].Commodities[rowId].RetailPrice, // 指导价 list[i].Commodities[rowId].SalePrice, // 零售价 list[i].Commodities[rowId].SaleDiscount, // 零售优惠 list[i].Commodities[rowId].MemberDiscount, // 会员优惠 nAmount, // 销售金额 list[i].Commodities[rowId].WholesalePrice, // 采购单价 list[i].Commodities[rowId].WholesalePrice + list[i].Commodities[rowId].StaffCostPrice, // 员工成本 list[i].Commodities[rowId].SalesProfit, // 销售毛利 list[i].Commodities[rowId].StaffProfit, // 员工毛利 list[i].BankTrxNo, // 银联流水 list[i].Tel, // 联系电话 list[i].Commodities[rowId].Remark, // 备注 } 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(len(list)), 10) end := []interface{}{totalData, "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", sumData.Count, sumData.RetailPrice, sumData.SalePrice, sumData.SaleDiscount, sumData.MemberDiscount, sumData.Amount, "", } 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}]}`) endRow := fmt.Sprintf("AC"+"%d", nExcelStartRow+2) // 应用样式到整个表格 _ = file.SetCellStyle("Sheet1", "A1", endRow, style) // 设置单元格大小 file.SetColWidth("Sheet1", "A", "A", 15) file.SetColWidth("Sheet1", "C", "C", 25) file.SetColWidth("Sheet1", "D", "D", 19) file.SetColWidth("Sheet1", "F", "F", 15) file.SetColWidth("Sheet1", "G", "G", 19) file.SetColWidth("Sheet1", "M", "M", 25) file.SetColWidth("Sheet1", "O", "O", 18) file.SetColWidth("Sheet1", "AA", "AA", 30) file.SetColWidth("Sheet1", "AB", "AB", 15) 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 }