From 73b6baf6af3d88cdcb0e2df7932a9bd96b0b85c5 Mon Sep 17 00:00:00 2001
From: zhangqian <zhangqian@123.com>
Date: 星期一, 01 七月 2024 22:32:34 +0800
Subject: [PATCH] 月度统计出入库按类型汇总报表定时任务和手动跑任务接口

---
 service/operation.go |  389 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 363 insertions(+), 26 deletions(-)

diff --git a/service/operation.go b/service/operation.go
index 5d7304b..3b9c43b 100644
--- a/service/operation.go
+++ b/service/operation.go
@@ -1,18 +1,26 @@
 package service
 
 import (
+	"errors"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/mitchellh/mapstructure"
 	"github.com/shopspring/decimal"
+	"gorm.io/gorm"
+	"strconv"
 	"time"
 	"wms/constvar"
+	"wms/middleware"
 	"wms/models"
 )
 
 type OutputInfo struct {
-	LocationID   int
-	WarehouseID  int
-	Products     []*ProductInfo
-	OperationID  int
-	SourceNumber string
+	LocationID        int
+	WarehouseID       int
+	Products          []*ProductInfo
+	OperationID       int
+	SourceNumber      string
+	SaleDetailsNumber string
 }
 
 type ProductInfo struct {
@@ -60,27 +68,29 @@
 			continue
 		}
 		operation := &models.Operation{
-			Id:                0,
-			Number:            autoCode,
-			SourceNumber:      output.SourceNumber,
-			OperationTypeId:   opTypeMap[output.WarehouseID].Id,
-			OperationTypeName: opTypeMap[output.WarehouseID].Name,
-			Status:            constvar.OperationStatus_Ready,
-			OperationDate:     time.Now().Format("2006-01-02 15:04:05"),
-			ContacterID:       0,
-			ContacterName:     "",
-			CompanyID:         0,
-			CompanyName:       "",
-			Comment:           "crm鍙戣揣鐢宠",
-			LogisticCompanyId: "",
-			LogisticCompany:   models.LogisticCompany{},
-			WaybillNumber:     "",
-			Weight:            decimal.Decimal{},
-			LogisticWeight:    decimal.Decimal{},
-			Source:            "crm",
-			Details:           details,
-			BaseOperationType: constvar.BaseOperationTypeOutgoing,
-			LocationID:        output.LocationID,
+			Id:                 0,
+			Number:             autoCode,
+			SourceNumber:       output.SourceNumber,
+			OperationTypeId:    opTypeMap[output.WarehouseID].Id,
+			OperationTypeName:  opTypeMap[output.WarehouseID].Name,
+			Status:             constvar.OperationStatus_Ready,
+			OperationDate:      time.Now().Format("2006-01-02 15:04:05"),
+			ContacterID:        0,
+			ContacterName:      "",
+			CompanyID:          "",
+			CompanyName:        "",
+			Comment:            "crm鍙戣揣鐢宠",
+			LogisticCompanyId:  "",
+			LogisticCompany:    models.LogisticCompany{},
+			WaybillNumber:      "",
+			Weight:             decimal.Decimal{},
+			LogisticWeight:     decimal.Decimal{},
+			Source:             "crm",
+			Details:            details,
+			BaseOperationType:  constvar.BaseOperationTypeOutgoing,
+			LocationID:         output.LocationID,
+			OperationSource:    constvar.OperationSourceSaleDelivery,
+			SalesDetailsNumber: output.SaleDetailsNumber,
 		}
 		operations = append(operations, operation)
 		autoCode = models.GetAutoCode(maxAutoIncr, codeStandard)
@@ -89,3 +99,330 @@
 
 	return models.NewOperationSearch().CreateBatch(operations)
 }
+
+// FinishOperationInput 瀹屾垚鍏ュ簱
+func FinishOperationInput(c *gin.Context, tx *gorm.DB, operation *models.Operation, listDetails []*models.OperationDetails, mapLocAmount map[string]*models.LocationProductAmount) (err error) {
+	userInfo := middleware.GetUserInfo(c)
+	locationRoleList, err := models.NewLocationProductSearch().Find()
+	if err != nil {
+		return errors.New("鑾峰彇涓婃灦瑙勫垯淇℃伅澶辫触")
+	}
+	var mapLocationRoleProduct, mapLocationRoleCategory map[string]*models.LocationProduct
+	if len(locationRoleList) > 0 {
+		mapLocationRoleProduct = make(map[string]*models.LocationProduct)
+		mapLocationRoleCategory = make(map[string]*models.LocationProduct)
+		for _, v := range locationRoleList {
+			if v.RuleType == constvar.RuleType_Product {
+				mapLocationRoleProduct[strconv.Itoa(v.AreaId)+v.ProductId] = v
+			}
+			if v.RuleType == constvar.RuleType_ProductCategory {
+				mapLocationRoleCategory[strconv.Itoa(v.AreaId)+strconv.Itoa(v.ProductCategoryID)] = v
+			}
+		}
+	}
+
+	var details []*models.OperationDetails
+	for k, v := range listDetails {
+		listDetails[k].Product.Amount = listDetails[k].Product.Amount.Add(v.Amount)
+		if err := tx.Save(&listDetails[k].Product).Error; err != nil {
+			return err
+		}
+
+		if roleProduct, ok := mapLocationRoleProduct[strconv.Itoa(operation.LocationID)+v.ProductId]; ok {
+			detail := &models.OperationDetails{
+				ProductId:      v.ProductId,
+				Amount:         v.Amount,
+				FromLocationID: roleProduct.AreaId,
+				ToLocationID:   roleProduct.LocationId,
+			}
+			details = append(details, detail)
+
+			if locAmount, aok := mapLocAmount[strconv.Itoa(roleProduct.LocationId)+v.ProductId]; aok {
+				locAmount.Amount = locAmount.Amount.Add(v.Amount)
+				if err := models.NewLocationProductAmountSearch().SetOrm(tx).SetID(int(locAmount.ID)).Save(locAmount); err != nil {
+					return err
+				}
+			} else {
+				if err := models.NewLocationProductAmountSearch().SetOrm(tx).Create(&models.LocationProductAmount{
+					LocationId:        roleProduct.LocationId,
+					ProductCategoryID: v.Product.CategoryId,
+					ProductId:         v.ProductId,
+					Amount:            v.Amount,
+					CreateDate:        time.Now().Format("2006-01-02 15:04:05"),
+				}); err != nil {
+					return err
+				}
+			}
+		} else {
+			if roleCategory, cok := mapLocationRoleCategory[strconv.Itoa(operation.LocationID)+strconv.Itoa(v.Product.CategoryId)]; cok {
+				detail := &models.OperationDetails{
+					ProductId:      v.ProductId,
+					Amount:         v.Amount,
+					FromLocationID: roleCategory.AreaId,
+					ToLocationID:   roleCategory.LocationId,
+				}
+				details = append(details, detail)
+
+				if locAmount, aok := mapLocAmount[strconv.Itoa(roleCategory.LocationId)+v.ProductId]; aok {
+					locAmount.Amount = locAmount.Amount.Add(v.Amount)
+					if err := models.NewLocationProductAmountSearch().SetOrm(tx).SetID(int(locAmount.ID)).Save(locAmount); err != nil {
+						return err
+					}
+				} else {
+					if err := models.NewLocationProductAmountSearch().SetOrm(tx).Create(&models.LocationProductAmount{
+						LocationId:        roleCategory.LocationId,
+						ProductCategoryID: v.Product.CategoryId,
+						ProductId:         v.ProductId,
+						Amount:            v.Amount,
+						CreateDate:        time.Now().Format("2006-01-02 15:04:05"),
+					}); err != nil {
+						return err
+					}
+				}
+			} else {
+				if locAmount, aok := mapLocAmount[strconv.Itoa(operation.LocationID)+v.ProductId]; aok {
+					locAmount.Amount = locAmount.Amount.Add(v.Amount)
+					if err := models.NewLocationProductAmountSearch().SetOrm(tx).SetID(int(locAmount.ID)).Save(locAmount); err != nil {
+						return err
+					}
+				} else {
+					if err := models.NewLocationProductAmountSearch().SetOrm(tx).Create(&models.LocationProductAmount{
+						LocationId:        operation.LocationID,
+						ProductCategoryID: v.Product.CategoryId,
+						ProductId:         v.ProductId,
+						Amount:            v.Amount,
+						CreateDate:        time.Now().Format("2006-01-02 15:04:05"),
+					}); err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+	if len(details) > 0 {
+		if err := tx.Create(&models.Operation{
+			Number:            operation.Number,
+			SourceNumber:      operation.SourceNumber,
+			OperationTypeId:   0,
+			OperationTypeName: operation.OperationTypeName,
+			Status:            constvar.OperationStatus_Finish,
+			OperationDate:     operation.OperationDate,
+			ContacterID:       operation.ContacterID,
+			ContacterName:     operation.ContacterName,
+			CompanyID:         operation.CompanyID,
+			CompanyName:       operation.CompanyName,
+			Comment:           operation.Comment,
+			BaseOperationType: constvar.BaseOperationTypeInternal,
+			Details:           details,
+			CheckedBy:         userInfo.Username,
+		}).Error; err != nil {
+			return err
+		}
+	}
+	return nil
+
+}
+
+// FinishOperationOutput 瀹屾垚鍑哄簱鎴栨姤搴�
+func FinishOperationOutput(tx *gorm.DB, listDetails []*models.OperationDetails, mapLocAmount map[string]*models.LocationProductAmount, originOperation *models.Operation) (err error) {
+	var internalInputDetails []*models.OperationDetails //鍐呴儴璋冩嫧浜х敓鐨勫嚭搴撻獙璇佸悗锛岀敓鎴愬叆搴撳崟
+	for k, v := range listDetails {
+		if v.Product.Amount.LessThan(v.Amount) {
+			return errors.New(fmt.Sprintf("浜у搧锛�%v,搴撳瓨锛�%v,鍑哄簱锛�%v,鏁伴噺涓嶅锛屾棤娉曞畬鎴愬嚭搴撴搷浣�", v.Product.Name, v.Product.Amount.String(), v.Amount.String()))
+		}
+		listDetails[k].Product.Amount = listDetails[k].Product.Amount.Sub(v.Amount)
+		if err := models.NewMaterialSearch().SetOrm(tx).Save(&listDetails[k].Product); err != nil {
+			return err
+		}
+
+		if locAmount, aok := mapLocAmount[strconv.Itoa(v.FromLocationID)+v.ProductId]; aok {
+			if locAmount.Amount.LessThan(v.Amount) {
+				return errors.New(fmt.Sprintf("浜у搧锛�%v,搴撳瓨锛�%v,鍑哄簱锛�%v,鏁伴噺涓嶅锛屾棤娉曞畬鎴愬嚭搴撴搷浣�", v.Product.Name, locAmount.Amount.String(), v.Amount.String()))
+			}
+			locAmount.Amount = locAmount.Amount.Sub(v.Amount)
+			if err := models.NewLocationProductAmountSearch().SetOrm(tx).SetID(locAmount.Id).Save(locAmount); err != nil {
+				return err
+			}
+		} else {
+			return errors.New("褰撳墠浠撳簱娌℃湁璇ヤ骇鍝�,璇峰厛鍏ュ簱")
+		}
+		if v.IsInternalOutput {
+			var inputDetail models.OperationDetails
+			mapstructure.Decode(v, &inputDetail)
+			inputDetail.Id = 0
+			inputDetail.OperationID = 0
+			internalInputDetails = append(internalInputDetails, &inputDetail)
+		}
+	}
+	if len(internalInputDetails) > 0 {
+		opTypeId, err := GetTargetOperationTypeIdByOperation(originOperation, constvar.BaseOperationTypeIncoming)
+		if err != nil {
+			return err
+		}
+		operation := &models.Operation{
+			OperationTypeId:   opTypeId,
+			Number:            strconv.FormatInt(time.Now().Unix(), 10),
+			Status:            constvar.OperationStatus_Ready,
+			OperationDate:     time.Now().Format("2006-01-02 15:04:05"),
+			Comment:           "搴撳瓨璋冩嫧鍏ュ簱",
+			BaseOperationType: constvar.BaseOperationTypeIncoming,
+			Details:           internalInputDetails,
+			LocationID:        internalInputDetails[0].ToLocationID,
+			OperationTypeName: "搴撳瓨璋冩嫧鍏ュ簱",
+		}
+		if err := models.NewOperationSearch().SetOrm(tx).Create(operation); err != nil {
+			return err
+		}
+	}
+	return nil
+
+}
+
+// FinishOperationInternal 楠岃瘉鍐呴儴璋冩嫧鐢熸垚鍑哄簱鍗�
+func FinishOperationInternal(tx *gorm.DB, listDetails []*models.OperationDetails, originOperation *models.Operation) (err error) {
+	var outputDetails []*models.OperationDetails
+	for _, v := range listDetails {
+		outputDetails = append(outputDetails, &models.OperationDetails{
+			ProductId:        v.ProductId,
+			Amount:           v.Amount,
+			FromLocationID:   v.FromLocationID,
+			ToLocationID:     v.ToLocationID,
+			TotalGrossWeight: v.TotalGrossWeight,
+			TotalNetWeight:   v.TotalNetWeight,
+			AuxiliaryAmount:  v.AuxiliaryAmount,
+			AuxiliaryUnit:    v.AuxiliaryUnit,
+			Remark:           v.Remark,
+			IsInternalOutput: true,
+			Cost:             v.Cost,
+			SalePrice:        v.SalePrice,
+		})
+	}
+	if len(outputDetails) > 0 {
+		opTypeId, err := GetTargetOperationTypeIdByOperation(originOperation, constvar.BaseOperationTypeOutgoing)
+		if err != nil {
+			return err
+		}
+		operation := &models.Operation{
+			OperationTypeId:   opTypeId,
+			Number:            strconv.FormatInt(time.Now().Unix(), 10),
+			Status:            constvar.OperationStatus_Ready,
+			OperationDate:     time.Now().Format("2006-01-02 15:04:05"),
+			Comment:           "搴撳瓨璋冩嫧鍑哄簱",
+			BaseOperationType: constvar.BaseOperationTypeOutgoing,
+			Details:           outputDetails,
+			LocationID:        outputDetails[0].FromLocationID,
+			OperationTypeName: "搴撳瓨璋冩嫧鍑哄簱",
+			IsInternalOutput:  true,
+		}
+		if err := models.NewOperationSearch().SetOrm(tx).Create(operation); err != nil {
+			return err
+		}
+	}
+	return nil
+
+}
+
+// FinishOperationAdjust 瀹屾垚搴撳瓨璋冩暣
+// 楠岃瘉鍚庣敓鎴愬叆搴撳崟鎴栧嚭搴撳崟锛堝簱瀛樺噺灏戠敓鎴愬嚭搴撳崟锛屽簱瀛樺鍔犵敓鎴愬叆搴撳崟锛�
+func FinishOperationAdjust(tx *gorm.DB, listDetails []*models.OperationDetails, mapLocAmount map[string]*models.LocationProductAmount, originOperation *models.Operation) (err error) {
+	var inputDetails []*models.OperationDetails
+	var outputDetails []*models.OperationDetails
+	for _, v := range listDetails {
+		if locAmount, aok := mapLocAmount[strconv.Itoa(v.ToLocationID)+v.ProductId]; aok {
+			if locAmount.Amount.Equal(v.Amount) {
+				continue
+			}
+			if v.Amount.GreaterThan(locAmount.Amount) {
+				inputDetails = append(inputDetails, &models.OperationDetails{
+					ProductId:        v.ProductId,
+					Amount:           v.Amount.Sub(locAmount.Amount),
+					FromLocationID:   v.FromLocationID,
+					ToLocationID:     v.ToLocationID,
+					TotalGrossWeight: v.TotalGrossWeight,
+					TotalNetWeight:   v.TotalNetWeight,
+					AuxiliaryAmount:  v.AuxiliaryAmount,
+					AuxiliaryUnit:    v.AuxiliaryUnit,
+					Remark:           v.Remark,
+					Cost:             v.Cost,
+					SalePrice:        v.SalePrice,
+				})
+			} else {
+				outputDetails = append(outputDetails, &models.OperationDetails{
+					ProductId:        v.ProductId,
+					Amount:           locAmount.Amount.Sub(v.Amount),
+					FromLocationID:   v.ToLocationID,
+					ToLocationID:     v.FromLocationID,
+					TotalGrossWeight: v.TotalGrossWeight,
+					TotalNetWeight:   v.TotalNetWeight,
+					AuxiliaryAmount:  v.AuxiliaryAmount,
+					AuxiliaryUnit:    v.AuxiliaryUnit,
+					Remark:           v.Remark,
+					Cost:             v.Cost,
+					SalePrice:        v.SalePrice,
+				})
+			}
+		} else {
+			inputDetails = append(inputDetails, &models.OperationDetails{
+				ProductId:      v.ProductId,
+				Amount:         v.Amount,
+				FromLocationID: v.FromLocationID,
+				ToLocationID:   v.ToLocationID,
+				Remark:         v.Remark,
+			})
+		}
+	}
+	if len(inputDetails) > 0 {
+		opTypeId, err := GetTargetOperationTypeIdByOperation(originOperation, constvar.BaseOperationTypeIncoming)
+		if err != nil {
+			return err
+		}
+		operation := &models.Operation{
+			OperationTypeId:   opTypeId,
+			Number:            strconv.FormatInt(time.Now().Unix(), 10),
+			Status:            constvar.OperationStatus_Ready,
+			OperationDate:     time.Now().Format("2006-01-02 15:04:05"),
+			Comment:           "搴撳瓨璋冩暣鍏ュ簱",
+			BaseOperationType: constvar.BaseOperationTypeIncoming,
+			Details:           inputDetails,
+			LocationID:        inputDetails[0].FromLocationID,
+			OperationTypeName: "搴撳瓨璋冩暣鍏ュ簱",
+		}
+		if err := models.NewOperationSearch().SetOrm(tx).Create(operation); err != nil {
+			return err
+		}
+	}
+	if len(outputDetails) > 0 {
+		opTypeId, err := GetTargetOperationTypeIdByOperation(originOperation, constvar.BaseOperationTypeOutgoing)
+		if err != nil {
+			return err
+		}
+		operation := &models.Operation{
+			OperationTypeId:   opTypeId,
+			Number:            strconv.FormatInt(time.Now().Unix(), 10),
+			Status:            constvar.OperationStatus_Ready,
+			OperationDate:     time.Now().Format("2006-01-02 15:04:05"),
+			Comment:           "搴撳瓨璋冩暣鍑哄簱",
+			BaseOperationType: constvar.BaseOperationTypeOutgoing,
+			Details:           outputDetails,
+			LocationID:        outputDetails[0].FromLocationID,
+			OperationTypeName: "搴撳瓨璋冩暣鍑哄簱",
+		}
+		if err := models.NewOperationSearch().SetOrm(tx).Create(operation); err != nil {
+			return err
+		}
+	}
+	return nil
+
+}
+
+func GetTargetOperationTypeIdByOperation(operation *models.Operation, baseOT constvar.BaseOperationType) (operationTypeId int, err error) {
+	oT, err := models.NewOperationTypeSearch().SetID(uint(operation.OperationTypeId)).First()
+	if err != nil {
+		return 0, err
+	}
+	targetOT, err := models.NewOperationTypeSearch().SetBaseOperationType(baseOT).SetWarehouseId(oT.WarehouseId).First()
+	if err != nil {
+		return 0, err
+	}
+	return targetOT.Id, nil
+}

--
Gitblit v1.8.0