From 73b6baf6af3d88cdcb0e2df7932a9bd96b0b85c5 Mon Sep 17 00:00:00 2001 From: zhangqian <zhangqian@123.com> Date: 星期一, 01 七月 2024 22:32:34 +0800 Subject: [PATCH] 月度统计出入库按类型汇总报表定时任务和手动跑任务接口 --- proto/product_inventory/server.go | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 307 insertions(+), 2 deletions(-) diff --git a/proto/product_inventory/server.go b/proto/product_inventory/server.go index c99b9f9..41e54d1 100644 --- a/proto/product_inventory/server.go +++ b/proto/product_inventory/server.go @@ -3,13 +3,17 @@ import ( "context" "errors" + "fmt" "github.com/shopspring/decimal" + "gorm.io/gorm" "strconv" "strings" "time" "wms/constvar" "wms/models" + "wms/pkg/logx" "wms/pkg/timex" + "wms/service" ) type Server struct { @@ -101,7 +105,7 @@ products := make([]*ProductInfo, 0) for _, material := range materials { var p ProductInfo - p.Id = material.ID + p.Number = material.ID p.Name = material.Name for _, detail := range details { if material.ID == detail.ProductId { @@ -171,7 +175,8 @@ operation.ReceiverPhone = req.Phone operation.ReceiverAddr = req.Address operation.Source = req.Source - operation.CompanyID = int(req.ClientId) + operation.OperationSource = constvar.OperationSource(req.OperationSource) + operation.CompanyID = strconv.FormatInt(req.ClientId, 10) operation.CompanyName = req.ClientName if req.DeliverType == 1 { for _, product := range req.ProductList { @@ -202,3 +207,303 @@ resp := new(CreateOperationResponse) return resp, err } + +type InputAndOutputDetails struct { + ProductId string `json:"productId"` + Amount decimal.Decimal `json:"amount"` + FromLocationId int `json:"fromLocationId"` + ToLocationId int `json:"toLocationId"` + Number string `json:"number"` + WaybillNumber string `json:"waybillNumber"` + Name string `json:"name"` + BaseOperationType constvar.BaseOperationType `json:"baseOperationType"` + Status constvar.OperationStatus `json:"status"` + CreatedAt time.Time +} + +func (s *Server) GetOrderInputAndOutputInfo(ctx context.Context, req *GetOrderInputAndOutputInfoRequest) (*GetOrderInputAndOutputInfoResponse, error) { + if req.Number == "" { + return nil, errors.New("鍙傛暟涓嶈兘涓虹┖") + } + var details []InputAndOutputDetails + var productIds []string + resp := new(GetOrderInputAndOutputInfoResponse) + + search := models.NewOperationDetailsSearch().Orm.Model(&models.OperationDetails{}). + Select("wms_operation_details.product_id,wms_operation_details.amount,wms_operation_details.from_location_id,wms_operation_details.to_location_id,"+ + "wms_operation.number,wms_operation.waybill_number, logistic_company.name, wms_operation.base_operation_type, wms_operation.status, wms_operation.created_at"). + Joins("left join wms_operation on wms_operation.id = wms_operation_details.operation_id"). + Joins("left join logistic_company on logistic_company.id = wms_operation.logistic_company_id"). + Where("wms_operation.sales_details_number = ?", req.Number). + Where("wms_operation.base_operation_type in ?", []constvar.BaseOperationType{ + constvar.BaseOperationTypeIncoming, constvar.BaseOperationTypeOutgoing}). + Where("wms_operation.status in ?", []constvar.OperationStatus{constvar.OperationStatus_Ready, constvar.OperationStatus_Finish}) + + err := search.Find(&details).Error + if err != nil { + return nil, err + } + if len(details) == 0 { + return resp, nil + } + var locationIds []int + productInputMap := make(map[string]decimal.Decimal) + productOutputMap := make(map[string]decimal.Decimal) + for _, detail := range details { + productIds = append(productIds, detail.ProductId) + if detail.BaseOperationType == constvar.BaseOperationTypeIncoming { + productInputMap[detail.ProductId] = productInputMap[detail.ProductId].Add(detail.Amount) + locationIds = append(locationIds, detail.ToLocationId) //鍏ュ簱浣嶇疆 + } + if detail.BaseOperationType == constvar.BaseOperationTypeOutgoing { + productOutputMap[detail.ProductId] = productOutputMap[detail.ProductId].Add(detail.Amount) + } + } + //鏌ヨ浜у搧淇℃伅 + materials, err := models.NewMaterialSearch().SetIDs(productIds).FindNotTotal() + if err != nil { + return nil, err + } + locationHouseMap, _, _, err := service.GetWarehouseByLocationIds(locationIds) + if err != nil { + return nil, err + } + materialsMap := service.MaterialMap(materials) + + inputList := make([]*InputAndOutputInfo, 0) + outputList := make([]*InputAndOutputInfo, 0) + + for _, detail := range details { + if materialsMap[detail.ProductId] == nil { + continue + } + material := materialsMap[detail.ProductId] + var info InputAndOutputInfo + info.Number = material.ID + info.Name = material.Name + info.OrderAmount = detail.Amount.String() + info.Valorem = detail.Amount.Mul(material.SalePrice).String() + info.Invoice = detail.Number + info.Carrier = detail.Name + info.Waybill = detail.WaybillNumber + info.Unit = material.Unit + info.SalePrice = material.SalePrice.String() + info.Amount = detail.Amount.String() + info.CreateTime = detail.CreatedAt.Format("2006-01-02 15:04") + if detail.Status == constvar.OperationStatus_Finish { //鏄惁瀹屾垚 + info.Status = FinishStatus_Finish + } else { + info.Status = FinishStatus_Ready + } + + if detail.BaseOperationType == constvar.BaseOperationTypeIncoming && detail.Status == constvar.OperationStatus_Finish { + if locationHouseMap[detail.ToLocationId] != nil { + info.Warehouse = locationHouseMap[detail.ToLocationId].Name //鍏ュ簱浠撳簱鍚� + info.LocationID = int64(detail.ToLocationId) + info.WareHouseID = int64(locationHouseMap[detail.ToLocationId].Id) + } + inputList = append(inputList, &info) + } else if detail.BaseOperationType == constvar.BaseOperationTypeOutgoing { + if locationHouseMap[detail.FromLocationId] != nil { + info.Warehouse = locationHouseMap[detail.FromLocationId].Name //鍙戣揣浠撳簱鍚� + info.LocationID = int64(detail.FromLocationId) + info.WareHouseID = int64(locationHouseMap[detail.FromLocationId].Id) + } + outputList = append(outputList, &info) + } + } + resp.InputList = inputList + resp.OutputList = outputList + + return resp, nil +} + +type StoreInfo struct { + Name string `json:"name"` //浜у搧鍚嶇О + Number string `json:"number"` //浜у搧缂栧彿 + StoreAmount decimal.Decimal `json:"storeAmount"` //璁㈠崟鍏ュ簱鏁伴噺 + AvailableAmount decimal.Decimal `json:"availableAmount"` //鍓╀綑鍙敤鏁伴噺 +} + +type OutputSimpleInfo struct { + Number string `json:"number"` //浜у搧缂栧彿 + Amount decimal.Decimal `json:"amount"` //鍦ㄥ簱鏁伴噺 + Status int `json:"status"` //0灏辩华 1瀹屾垚 +} + +func (s *Server) OrderProductOutput(ctx context.Context, req *OrderProductOutputRequest) (resp *OrderProductOutputResponse, err error) { + resp = new(OrderProductOutputResponse) + if req.OrderNumber == "" || len(req.Products) == 0 { + return nil, errors.New("鍙傛暟缂哄け") + } + orderInputAndOutputInfoResponse, err := s.GetOrderInputAndOutputInfo(ctx, &GetOrderInputAndOutputInfoRequest{ + Number: req.OrderNumber, + }) + if err != nil { + logx.Errorf("OrderProductOutput GetOrderInputAndOutputInfo err:%v, req:%v", err, req) + return nil, errors.New("鑾峰彇鍑哄叆搴撲俊鎭け璐�") + } + + outputList := orderInputAndOutputInfoResponse.OutputList + inputList := orderInputAndOutputInfoResponse.InputList + inputProductMap := make(map[string]*StoreInfo) + outputProductMap := make(map[string]*OutputSimpleInfo) + inputLocationAmountMap := make(map[int64]map[string]decimal.Decimal) + outputLocationAmountMap := make(map[int64]map[string]decimal.Decimal) + for _, v := range outputList { + if req.WarehouseId != 0 && v.WareHouseID != req.WarehouseId { + continue + } + if outputProductMap[v.Number] == nil { + simpleInfo := &OutputSimpleInfo{ + Number: v.Number, + } + amount, _ := decimal.NewFromString(v.Amount) + simpleInfo.Amount = amount + outputProductMap[v.Number] = simpleInfo + } else { + amount, _ := decimal.NewFromString(v.Amount) + outputProductMap[v.Number].Amount = outputProductMap[v.Number].Amount.Add(amount) + } + } + for _, v := range inputList { + if req.WarehouseId != 0 && v.WareHouseID != req.WarehouseId { + continue + } + if inputProductMap[v.Number] == nil { + storeInfo := &StoreInfo{ + Number: v.Number, + Name: v.Name, + } + storeAmount, _ := decimal.NewFromString(v.Amount) + storeInfo.StoreAmount = storeAmount + storeInfo.AvailableAmount = storeAmount + inputProductMap[v.Number] = storeInfo + } else { + storeAmount, _ := decimal.NewFromString(v.Amount) + inputProductMap[v.Number].StoreAmount = inputProductMap[v.Number].StoreAmount.Add(storeAmount) + inputProductMap[v.Number].AvailableAmount = inputProductMap[v.Number].StoreAmount + } + } + + for number, inputInfo := range inputProductMap { + outputInfo := outputProductMap[inputInfo.Number] + if outputInfo != nil { + inputProductMap[number].AvailableAmount = inputProductMap[number].AvailableAmount.Sub(outputInfo.Amount) //鍙敤鏁伴噺 = 鍏ュ簱瀹屾垚鏁伴噺 - 宸插彂璐ф暟閲� + } + } + + //鏍¢獙鍙敤鏁伴噺鏄惁瓒冲 + productNeedSendAmount := make(map[string]decimal.Decimal) + for _, product := range req.Products { + sendAmount, _ := decimal.NewFromString(product.Amount) + productNeedSendAmount[product.Number] = sendAmount + if inputProductMap[product.Number] == nil { + return nil, fmt.Errorf("鑾峰彇鍏ュ簱淇℃伅澶辫触锛屼骇鍝佺紪鍙凤細%v", product.Number) + } + if sendAmount.GreaterThan(inputProductMap[product.Number].AvailableAmount) { + return nil, fmt.Errorf("浜у搧鍙敤鏁伴噺涓嶈冻浠ュ彂璐э紝浜у搧缂栧彿锛�%v", product.Number) + } + } + + LocationIDWarehouseIDMap := make(map[int64]int64) + for _, output := range outputList { + if req.WarehouseId != 0 && output.WareHouseID != req.WarehouseId { + continue + } + if outputLocationAmountMap[output.LocationID] == nil { + outputLocationAmountMap[output.LocationID] = make(map[string]decimal.Decimal) + } + outputAmount, _ := decimal.NewFromString(output.Amount) + outputLocationAmountMap[output.LocationID][output.Number] = outputAmount + } + for _, input := range inputList { + if req.WarehouseId != 0 && input.WareHouseID != req.WarehouseId { + continue + } + LocationIDWarehouseIDMap[input.LocationID] = input.WareHouseID + + if inputLocationAmountMap[input.LocationID] == nil { + inputLocationAmountMap[input.LocationID] = make(map[string]decimal.Decimal) + } + storeAmount, _ := decimal.NewFromString(input.Amount) + if outputLocationAmountMap[input.LocationID] != nil { + storeAmount = storeAmount.Sub(outputLocationAmountMap[input.LocationID][input.Number]) + } + inputLocationAmountMap[input.LocationID][input.Number] = storeAmount + } + + productHasSendAmount := make(map[string]decimal.Decimal) //鏈宸插彂璐ф暟閲� + + //find location ID + outputInfoList := make([]*service.OutputInfo, 0) + for locationID, locationProductAmounts := range inputLocationAmountMap { + productInfoList := make([]*service.ProductInfo, 0) + for productNumber, productAmount := range locationProductAmounts { + remainAmount := productNeedSendAmount[productNumber].Sub(productHasSendAmount[productNumber]) + if remainAmount.LessThanOrEqual(decimal.Zero) { + continue + } + var locationSendAmount decimal.Decimal + if productAmount.GreaterThanOrEqual(remainAmount) { + locationSendAmount = remainAmount + } else { + locationSendAmount = productAmount + } + if locationSendAmount.LessThanOrEqual(decimal.Zero) { + continue + } + productInfoList = append(productInfoList, &service.ProductInfo{ + ProductID: productNumber, + Amount: locationSendAmount, + }) + productHasSendAmount[productNumber] = productHasSendAmount[productNumber].Add(locationSendAmount) + } + outputInfoList = append(outputInfoList, &service.OutputInfo{ + LocationID: int(locationID), + WarehouseID: int(LocationIDWarehouseIDMap[locationID]), + Products: productInfoList, + OperationID: 0, + SourceNumber: req.OrderNumber, + SaleDetailsNumber: req.OrderNumber, + }) + } + + err = service.AddOutputOperations(outputInfoList) + + if err != nil { + logx.Errorf("OrderProductOutput AddOutputOperations err:%v", err) + return nil, err + } + resp.Code = 1 + resp.Msg = "success" + return resp, nil +} + +func (s *Server) GetOutputOperationInfo(ctx context.Context, req *GetOutputOperationInfoRequest) (*GetOutputOperationInfoResponse, error) { + if req.Number == "" { + return nil, errors.New("鍙傛暟涓嶈兘涓虹┖") + } + result := new(GetOutputOperationInfoResponse) + first, err := models.NewOperationSearch().SetSourceNumber(req.Number).SetBaseOperationType(constvar.BaseOperationTypeOutgoing).SetStatus(constvar.OperationStatus_Finish).First() + if err != nil { + if err == gorm.ErrRecordNotFound { + return result, nil + } + return nil, err + } + details, err := models.NewOperationDetailsSearch().SetOperationId(first.Id).FindNotTotal() + if err != nil { + return nil, err + } + list := make([]*OutputProduct, 0) + for _, detail := range details { + var op OutputProduct + op.Number = detail.ProductId + op.Amount = detail.Amount.String() + list = append(list, &op) + } + result.Products = list + + return result, nil +} -- Gitblit v1.8.0