mh_goadmin_server/app/admin/models/purchase.go
chenlin e45772ab7c 1.修复编辑商品资料后更新创建时间为空的缺陷;
2.修复进销存报表通过分类、名称查询报错的问题;
3.零售订单开单优化,允许非会员手机号为空的情况;
4.修复库存调拨、库存变动、采购订单、供应商等功能编辑时部分数据不能置为空,且创建时间会更新为空的问题;
2024-08-16 17:42:37 +08:00

5868 lines
208 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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