package controllers
|
|
import (
|
"context"
|
"encoding/json"
|
"errors"
|
"fmt"
|
"github.com/gin-gonic/gin"
|
uuid "github.com/satori/go.uuid"
|
"github.com/shopspring/decimal"
|
"github.com/xuri/excelize/v2"
|
"gorm.io/gorm"
|
"os"
|
"sort"
|
"strconv"
|
"strings"
|
"time"
|
"wms/constvar"
|
"wms/extend/code"
|
"wms/extend/util"
|
"wms/models"
|
"wms/opa"
|
"wms/pkg/logx"
|
"wms/pkg/mysqlx"
|
"wms/pkg/structx"
|
"wms/proto/client"
|
"wms/proto/init_client"
|
"wms/proto/inventory_order"
|
"wms/proto/product_inventory"
|
"wms/proto/purchase_wms"
|
"wms/proto/supplier"
|
"wms/request"
|
"wms/response"
|
"wms/service"
|
"wms/utils/http"
|
"wms/utils/upload"
|
)
|
|
type OperationController struct {
|
}
|
|
// Add
|
// @Tags 入库/出库
|
// @Summary 添加入库/出库
|
// @Produce application/json
|
// @Param object body request.AddOperation true "入库/出库信息"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/operation [post]
|
func (slf OperationController) Add(c *gin.Context) {
|
var reqParams request.AddOperation
|
var params models.Operation
|
if err := c.BindJSON(&reqParams); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误")
|
return
|
}
|
if err := structx.AssignTo(reqParams, ¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "数据转换错误"+err.Error())
|
return
|
}
|
if err := slf.CheckParams(params); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
|
if err := slf.FormatLocation(¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
|
if CheckDetailsRepeat(params.Details) {
|
util.ResponseFormat(c, code.RequestParamError, "明细中不能存在重复的产品")
|
return
|
}
|
|
operationType, err := models.NewOperationTypeSearch().SetID(uint(params.OperationTypeId)).First()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
|
params.Status = constvar.OperationStatus_Ready
|
//params.Number = strconv.FormatInt(time.Now().Unix(), 10)
|
params.BaseOperationType = operationType.BaseOperationType
|
|
var numberNum int64
|
if err := mysqlx.GetDB().Model(&models.Operation{}).Where("number=?", params.Number).Count(&numberNum).Error; err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
if numberNum > 0 {
|
util.ResponseFormat(c, code.RequestParamError, "单号已存在")
|
return
|
}
|
|
if err := models.NewOperationSearch().Create(¶ms); err != nil {
|
logx.Errorf("Operation create err: %v", err)
|
util.ResponseFormat(c, code.SaveFail, "添加失败:"+err.Error())
|
return
|
}
|
util.ResponseFormat(c, code.Success, "添加成功")
|
}
|
|
func CheckDetailsRepeat(details []*models.OperationDetails) bool {
|
var detailIDs []string
|
var tempID string
|
for _, v := range details {
|
detailIDs = append(detailIDs, v.ProductId)
|
}
|
sort.Strings(detailIDs)
|
for _, v := range detailIDs {
|
if v != tempID {
|
tempID = v
|
} else {
|
return true
|
}
|
}
|
return false
|
}
|
|
func (slf OperationController) FormatLocation(params *models.Operation) error {
|
operationType, err := models.NewOperationTypeSearch().SetID(uint(params.OperationTypeId)).First()
|
if err != nil {
|
return err
|
}
|
if operationType.BaseOperationType == constvar.BaseOperationTypeIncoming {
|
location, err := models.NewLocationSearch().SetType(int(constvar.LocationTypeVendor)).First()
|
if err != nil {
|
return err
|
}
|
for k, v := range params.Details {
|
params.Details[k].FromLocationID = location.Id
|
if v.ToLocationID == 0 {
|
params.Details[k].ToLocationID = params.LocationID
|
}
|
}
|
|
}
|
if operationType.BaseOperationType == constvar.BaseOperationTypeOutgoing {
|
location, err := models.NewLocationSearch().SetType(int(constvar.LocationTypeCustomer)).First()
|
if err != nil {
|
return err
|
}
|
for k, v := range params.Details {
|
params.Details[k].ToLocationID = location.Id
|
if v.FromLocationID == 0 {
|
return errors.New("请选择出库位置")
|
}
|
}
|
|
}
|
if operationType.BaseOperationType == constvar.BaseOperationTypeInternal {
|
for _, v := range params.Details {
|
if v.ToLocationID == 0 {
|
return errors.New("请选择目标位置")
|
}
|
if v.FromLocationID == 0 {
|
return errors.New("请选择源位置")
|
}
|
}
|
|
}
|
return nil
|
}
|
|
func (slf OperationController) CheckParams(params models.Operation) error {
|
if params.SourceNumber == "" {
|
return errors.New("请填入源单号")
|
}
|
|
if params.Number == "" {
|
return errors.New("请填入单号")
|
}
|
|
if params.OperationTypeId == 0 && int(params.BaseOperationType) == 0 {
|
return errors.New("未识别该记录类型")
|
}
|
|
if params.OperationDate == "" {
|
return errors.New("请选择安排日期")
|
}
|
|
if params.LocationID == 0 {
|
return errors.New("请选择源位置")
|
}
|
|
if len(params.Details) <= 0 {
|
return errors.New("请添加明细信息")
|
}
|
|
//检查明细部分
|
for _, v := range params.Details {
|
if v.ProductId == "" {
|
return errors.New("productID为空")
|
}
|
//if v.ProductName == "" {
|
// return errors.New("产品名称异常")
|
//}
|
if v.Amount.IsNegative() {
|
return errors.New("产品数量出错")
|
}
|
}
|
|
return nil
|
}
|
|
// GetOperationInfo
|
// @Tags 入库/出库
|
// @Summary 入库/出库列表
|
// @Produce application/json
|
//
|
// @Param id path string true "id"
|
//
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/getOperationInfo/{id} [get]
|
func (slf OperationController) GetOperationInfo(c *gin.Context) {
|
number := c.Param("id")
|
if number == "" {
|
util.ResponseFormat(c, code.RequestParamError, "参数解析失败,参数不能为空")
|
return
|
}
|
id, _ := strconv.Atoi(number)
|
operation, err := models.NewOperationSearch().SetID(id).SetPreload(true).First()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
util.ResponseFormat(c, code.Success, operation)
|
}
|
|
// List
|
// @Tags 入库/出库
|
// @Summary 入库/出库列表
|
// @Produce application/json
|
// @Param object body request.OperationList true "查询参数"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/list [post]
|
func (slf OperationController) List(c *gin.Context) {
|
var params request.OperationList
|
if err := c.BindJSON(¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error())
|
return
|
}
|
if err := slf.CheckListParams(¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
search := models.NewOperationSearch()
|
search.SetPage(params.Page, params.PageSize)
|
if params.Number != "" {
|
search.SetKeyword(params.Number)
|
}
|
if int(params.Status) != 0 {
|
search.SetStatus(params.Status)
|
}
|
list, total, err := search.SetOperationTypeId(params.OperationTypeId).SetPreload(true).SetOrder("created_at desc").Find()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error())
|
return
|
}
|
|
util.ResponseFormatListWithPage(c, code.Success, list, int(total), params.Page, params.PageSize)
|
}
|
|
func (slf OperationController) CheckListParams(params *request.OperationList) error {
|
if !params.PageInfo.Check() {
|
return errors.New("数据分页信息错误")
|
}
|
if params.OperationTypeId == 0 {
|
return errors.New("operationTypeId为0")
|
}
|
return nil
|
}
|
|
// Update
|
// @Tags 入库/出库
|
// @Summary 修改入库/出库信息
|
// @Produce application/json
|
// @Param object body request.UpdateOperation true "入库信息"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/update [post]
|
func (slf OperationController) Update(c *gin.Context) {
|
var reqParams request.UpdateOperation
|
var params models.Operation
|
if err := c.BindJSON(&reqParams); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error())
|
return
|
}
|
if reqParams.Status != constvar.OperationStatus_Ready {
|
util.ResponseFormat(c, code.RequestParamError, "该入库信息已完成,无法进行修改")
|
return
|
}
|
if err := structx.AssignTo(reqParams, ¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "数据转换错误"+err.Error())
|
return
|
}
|
if err := slf.CheckParams(params); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
if CheckDetailsRepeat(params.Details) {
|
util.ResponseFormat(c, code.RequestParamError, "明细中不能存在重复的产品")
|
return
|
}
|
if err := slf.FormatLocation(¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
if err := models.WithTransaction(func(tx *gorm.DB) error {
|
if err := models.NewOperationDetailsSearch().SetOrm(tx).SetOperationId(params.Id).Delete(); err != nil {
|
return err
|
}
|
operationSearch := models.NewOperationSearch().SetOrm(tx)
|
if err := operationSearch.Orm.Model(¶ms).Association("Details").Replace(params.Details); err != nil {
|
return err
|
}
|
if err := models.NewOperationSearch().SetOrm(tx).SetID(params.Id).Save(¶ms); err != nil {
|
return err
|
}
|
return nil
|
}); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "修改失败:"+err.Error())
|
return
|
}
|
|
util.ResponseFormat(c, code.Success, "修改成功")
|
}
|
|
// Delete
|
//
|
// @Tags 入库/出库
|
// @Summary 删除入库/出库信息
|
// @Produce application/json
|
// @Param id path int true "id"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/operation/{id} [delete]
|
func (slf OperationController) Delete(c *gin.Context) {
|
id, err := strconv.Atoi(c.Param("id"))
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "错误的id值")
|
return
|
}
|
if id == 0 {
|
util.ResponseFormat(c, code.RequestParamError, "id为0")
|
return
|
}
|
operation, err := models.NewOperationSearch().SetID(id).First()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库信息:"+err.Error())
|
return
|
}
|
if operation.Status != constvar.OperationStatus_Ready {
|
util.ResponseFormat(c, code.RequestError, "该入库信息无法进行删除")
|
return
|
}
|
if err := models.WithTransaction(func(tx *gorm.DB) error {
|
if err := models.NewOperationDetailsSearch().SetOrm(tx).SetOperationId(id).Delete(); err != nil {
|
return err
|
}
|
if err := models.NewOperationSearch().SetOrm(tx).SetID(id).Delete(); err != nil {
|
return err
|
}
|
return nil
|
}); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "修改失败:"+err.Error())
|
return
|
}
|
util.ResponseFormat(c, code.Success, "删除成功")
|
}
|
|
// Finish
|
//
|
// @Tags 入库/出库
|
// @Summary 更改记录状态
|
// @Produce application/json
|
// @Param id path int true "id"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/finish/{id} [put]
|
func (slf OperationController) Finish(c *gin.Context) {
|
id, err := strconv.Atoi(c.Param("id"))
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "错误的id值")
|
return
|
}
|
if id == 0 {
|
util.ResponseFormat(c, code.RequestParamError, "id为0")
|
return
|
}
|
operation, err := models.NewOperationSearch().SetPreload(true).SetID(id).First()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库信息:"+err.Error())
|
return
|
}
|
if operation.Status != constvar.OperationStatus_Ready {
|
util.ResponseFormat(c, code.RequestError, "该出入库信息无法完成")
|
return
|
}
|
|
listDetails, err := models.NewOperationDetailsSearch().SetOperationId(operation.Id).SetPreload(true).FindAll()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestError, err.Error())
|
return
|
}
|
|
var mapLocAmount map[string]*models.LocationProductAmount
|
locAmountList, err := models.NewLocationProductAmountSearch().Find()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestError, err.Error())
|
return
|
}
|
if len(locAmountList) > 0 {
|
for _, v := range locAmountList {
|
mapLocAmount[strconv.Itoa(v.LocationId)+v.ProductId] = v
|
}
|
}
|
|
err = models.WithTransaction(func(tx *gorm.DB) error {
|
if err := models.NewOperationSearch().SetOrm(tx).SetID(id).Update(&models.Operation{Status: constvar.OperationStatus_Finish, AuditDate: time.Now().Format("2006-01-02 15:04:05")}); err != nil {
|
return err
|
}
|
if err := AddMoveHistory([]*models.Operation{operation}, tx); err != nil {
|
return err
|
}
|
|
if operation.BaseOperationType == constvar.BaseOperationTypeIncoming {
|
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 res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
} else {
|
if err := models.NewLocationProductAmountSearch().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 res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
} else {
|
if err := models.NewLocationProductAmountSearch().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 res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
} else {
|
if err := models.NewLocationProductAmountSearch().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,
|
}).Error; err != nil {
|
return err
|
}
|
}
|
if operation.Source != "" {
|
go UpdatePurchaseStatus(operation.Source, operation.SourceNumber)
|
}
|
|
}
|
|
if operation.BaseOperationType == constvar.BaseOperationTypeOutgoing {
|
for k, v := range listDetails {
|
//todo 演示测试数据
|
data, err := os.ReadFile("conf/input.json")
|
if err != nil {
|
return errors.New("文件读取失败")
|
}
|
m := make(map[string]interface{})
|
err = json.Unmarshal(data, &m)
|
if err != nil {
|
return errors.New("格式转换失败")
|
}
|
if opa.OpaCheck(c, m, "operation") {
|
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()))
|
}
|
}
|
//todo ================end===============================
|
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 := tx.Save(&listDetails[k].Product).Error; 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().SetID(locAmount.Id).Update(locAmount); err != nil {
|
return err
|
}
|
} else {
|
return errors.New("当前仓库没有该产品,请先入库")
|
}
|
}
|
if operation.Source != "" {
|
go UpdateOutStatus(operation.Source, operation.SourceNumber, 4)
|
}
|
}
|
|
if operation.BaseOperationType == constvar.BaseOperationTypeInternal {
|
for _, v := range listDetails {
|
if fromLocAmount, aok := mapLocAmount[strconv.Itoa(v.FromLocationID)+v.ProductId]; aok {
|
if fromLocAmount.Amount.LessThan(v.Amount) {
|
return errors.New(fmt.Sprintf("产品:%v,库存:%v,出库:%v,数量不够,无法完成出库操作", v.Product.Name, fromLocAmount.Amount.String(), v.Amount.String()))
|
}
|
fromLocAmount.Amount = fromLocAmount.Amount.Sub(v.Amount)
|
if err := models.NewLocationProductAmountSearch().SetID(fromLocAmount.Id).Update(fromLocAmount); err != nil {
|
return err
|
}
|
} else {
|
return errors.New("当前仓库没有该产品,请先入库")
|
}
|
|
if toLocAmount, aok := mapLocAmount[strconv.Itoa(v.ToLocationID)+v.ProductId]; aok {
|
toLocAmount.Amount = toLocAmount.Amount.Add(v.Amount)
|
if err := models.NewLocationProductAmountSearch().SetID(toLocAmount.Id).Update(toLocAmount); err != nil {
|
return err
|
}
|
} else {
|
if err := models.NewLocationProductAmountSearch().Create(&models.LocationProductAmount{
|
LocationId: v.ToLocationID,
|
ProductCategoryID: v.Product.CategoryId,
|
ProductId: v.ProductId,
|
Amount: v.Amount,
|
CreateDate: time.Now().Format("2006-01-02 15:04:05"),
|
}); err != nil {
|
return err
|
}
|
}
|
}
|
}
|
return nil
|
})
|
if err != nil {
|
util.ResponseFormat(c, code.RequestError, err.Error())
|
return
|
}
|
|
util.ResponseFormat(c, code.Success, "操作成功")
|
}
|
|
func AddMoveHistory(operationList []*models.Operation, db *gorm.DB) error {
|
var histories []*models.MoveHistory
|
operationMap := make(map[int]*models.Operation, len(operationList))
|
for _, operation := range operationList {
|
for _, v := range operation.Details {
|
history := &models.MoveHistory{
|
Number: operation.Number,
|
BaseOperationType: operation.BaseOperationType,
|
OperationTypeId: operation.OperationTypeId,
|
OperationTypeName: operation.OperationTypeName,
|
OperationId: operation.Id,
|
ProductId: v.ProductId,
|
ProductName: v.Product.Name,
|
Amount: v.Amount,
|
Unit: v.Product.Unit,
|
Weight: operation.Weight,
|
FromLocationId: v.FromLocationID,
|
FromLocation: v.FromLocation.Name,
|
ToLocationId: v.ToLocationID,
|
ToLocation: v.ToLocation.Name,
|
}
|
histories = append(histories, history)
|
}
|
operationMap[operation.Id] = operation
|
}
|
if err := db.Model(&models.MoveHistory{}).Create(&histories).Error; err != nil {
|
return err
|
}
|
for _, history := range histories {
|
service.AddNewHistoryReportRecord(history, operationMap[history.OperationId])
|
}
|
return nil
|
}
|
|
//var (
|
// ProductInventoryServiceConn *grpc.ClientConn
|
// PurchaseServiceConn *grpc.ClientConn
|
//)
|
//
|
//func InitProductInventoryServiceConn() {
|
// var err error
|
// ProductInventoryServiceConn, err = grpc.Dial(conf.GrpcServerConf.CrmAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
// if err != nil {
|
// logx.Errorf("grpc dial product service error: %v", err.Error())
|
// return
|
// }
|
// PurchaseServiceConn, err = grpc.Dial(conf.GrpcServerConf.SrmAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
// if err != nil {
|
// logx.Errorf("grpc dial product service error: %v", err.Error())
|
// return
|
// }
|
//}
|
//
|
//func CloseProductInventoryServiceConn() {
|
// if ProductInventoryServiceConn != nil {
|
// ProductInventoryServiceConn.Close()
|
// }
|
// if PurchaseServiceConn != nil {
|
// PurchaseServiceConn.Close()
|
// }
|
//}
|
|
func UpdateOutStatus(source, number string, status int64) {
|
if source == "CRM" {
|
cl := product_inventory.NewProductInventoryServiceClient(init_client.CrmConn)
|
_, err := cl.UpdateSalesDetailStatus(context.Background(), &product_inventory.UpdateSalesDetailStatusRequest{
|
Number: number,
|
SalesDetailStatus: "已出库",
|
})
|
if err != nil {
|
logx.Errorf("grpc dial UpdateSalesDetailStatus service error: %v", err)
|
}
|
}
|
if source == "APS_APPLY_MATERIAL" {
|
cl := inventory_order.NewInventoryOrderServiceClient(init_client.ApsConn)
|
_, err := cl.UpdateMaterialApplyStatus(context.Background(), &inventory_order.UpdateMaterialApplyStatusRequest{
|
Number: number,
|
Status: status,
|
})
|
if err != nil {
|
logx.Errorf("grpc dial UpdateSalesDetailStatus service error: %v", err)
|
}
|
}
|
}
|
|
func UpdatePurchaseStatus(source, number string) {
|
if source == "SRM_PURCHASE" {
|
cl := purchase_wms.NewPurchaseServiceClient(init_client.SrmConn)
|
_, err := cl.UpdatePurchaseStatus(context.Background(), &purchase_wms.UpdatePurchaseStatusRequest{Number: number})
|
if err != nil {
|
logx.Errorf("grpc dial UpdatePurchaseStatus service error: %v", err)
|
}
|
}
|
}
|
|
// ListTransfer
|
// @Tags 入库/出库
|
// @Summary 库存调拨列表
|
// @Produce application/json
|
// @Param object body request.OperationAllList true "参数"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/listTransfer [post]
|
func (slf OperationController) ListTransfer(c *gin.Context) {
|
var params request.OperationAllList
|
if err := c.BindJSON(¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error())
|
return
|
}
|
if !params.PageInfo.Check() {
|
util.ResponseFormat(c, code.RequestParamError, "数据分页信息错误")
|
return
|
}
|
search := models.NewOperationSearch()
|
search.SetPage(params.Page, params.PageSize)
|
search.SetPage(params.Page, params.PageSize)
|
if params.Number != "" {
|
search.SetKeyword(params.Number)
|
}
|
list, total, err := search.SetPreload(true).SetBaseOperationType(constvar.BaseOperationTypeInternal).SetOrder("created_at desc").Find()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error())
|
return
|
}
|
util.ResponseFormatListWithPage(c, code.Success, list, int(total), params.Page, params.PageSize)
|
}
|
|
// GetLogisticCompanyList
|
// @Tags 入库/出库
|
// @Summary 获取物流公司列表
|
// @Produce application/json
|
// @Success 200 {object} util.ResponseList{data=[]models.LogisticCompany} "成功"
|
// @Router /api-wms/v1/operation/getLogisticCompanyList [get]
|
func (slf OperationController) GetLogisticCompanyList(c *gin.Context) {
|
companies, err := models.NewLogisticCompanySearch().FindNotTotal()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error())
|
return
|
}
|
util.ResponseFormat(c, code.Success, companies)
|
}
|
|
// Cancel
|
//
|
// @Tags 入库/出库
|
// @Summary 取消
|
// @Produce application/json
|
// @Param id path int true "id"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/cancel/{id} [put]
|
func (slf OperationController) Cancel(c *gin.Context) {
|
id, err := strconv.Atoi(c.Param("id"))
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "错误的id值")
|
return
|
}
|
if id == 0 {
|
util.ResponseFormat(c, code.RequestParamError, "id为0")
|
return
|
}
|
operation, err := models.NewOperationSearch().SetPreload(true).SetID(id).First()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库信息:"+err.Error())
|
return
|
}
|
if operation.Status != constvar.OperationStatus_Ready {
|
util.ResponseFormat(c, code.RequestError, "该出入库信息无法取消")
|
return
|
}
|
operation.Status = constvar.OperationStatus_Cancel
|
operation.AuditDate = time.Now().Format("2006-01-02 15:04:05")
|
if err := models.NewOperationSearch().SetID(operation.Id).Save(operation); err != nil {
|
util.ResponseFormat(c, code.SaveFail, err.Error())
|
return
|
}
|
//更新aps物料申请状态
|
if operation.Source == "APS_APPLY_MATERIAL" {
|
go UpdateOutStatus(operation.Source, operation.SourceNumber, 3)
|
}
|
util.ResponseFormat(c, code.Success, "操作成功")
|
}
|
|
// outputOperation
|
//
|
// @Tags 入库/出库
|
// @Summary 打印
|
// @Produce application/json
|
// @Param id path int true "id"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/outputOperation/{id} [put]
|
func (slf OperationController) OutputOperation(c *gin.Context) {
|
id, err := strconv.Atoi(c.Param("id"))
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "错误的id值")
|
return
|
}
|
if id == 0 {
|
util.ResponseFormat(c, code.RequestParamError, "id为0")
|
return
|
}
|
operation, err := models.NewOperationSearch().SetPreload(true).SetID(id).First()
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "未找到相关出入库信息:"+err.Error())
|
return
|
}
|
if len(operation.Details) == 0 {
|
util.ResponseFormat(c, code.RequestParamError, "没有相关产品明细的信息")
|
return
|
}
|
|
var fileUrl string
|
if operation.BaseOperationType == constvar.BaseOperationTypeIncoming {
|
fileUrl, err = ExportInputSelfmade(constvar.FileTemplateCategory_Selfmade, operation)
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
} else if operation.BaseOperationType == constvar.BaseOperationTypeOutgoing {
|
fileUrl, err = ExportOutputOperation(constvar.FileTemplateCategory_Output, operation)
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, err.Error())
|
return
|
}
|
} else {
|
util.ResponseFormat(c, code.RequestParamError, "该记录不支持打印")
|
return
|
}
|
|
util.ResponseFormat(c, code.Success, fileUrl)
|
}
|
|
func ExportInputSelfmade(category constvar.FileTemplateCategory, operation *models.Operation) (string, error) {
|
template, err := models.NewFileTemplateAttachmentSearch().SetPreload(true).SetCategory(category).First()
|
if err != nil {
|
return "", errors.New("获取模版记录失败:" + err.Error())
|
}
|
readerCloser, err := http.HttpGetWithReadCloser(template.Attachment.FileUrl)
|
if err != nil {
|
return "", errors.New("获取模版失败:" + err.Error())
|
}
|
|
f, err := excelize.OpenReader(readerCloser)
|
if err != nil {
|
return "", errors.New("读取excel模版失败:" + err.Error())
|
}
|
readerCloser.Close()
|
defer f.Close()
|
|
style, _ := f.NewStyle(&excelize.Style{
|
Border: []excelize.Border{
|
{Type: "left", Color: "#000000", Style: 2},
|
{Type: "top", Color: "#000000", Style: 2},
|
{Type: "bottom", Color: "#000000", Style: 2},
|
{Type: "right", Color: "#000000", Style: 2},
|
},
|
Alignment: &excelize.Alignment{
|
Horizontal: "center",
|
Vertical: "center",
|
},
|
})
|
styleLeft, _ := f.NewStyle(&excelize.Style{
|
Alignment: &excelize.Alignment{
|
Horizontal: "left",
|
Vertical: "center",
|
},
|
})
|
|
f.SetCellValue("Sheet1", "B3", operation.CompanyName)
|
f.SetCellValue("Sheet1", "E3", operation.OperationDate)
|
f.SetCellValue("Sheet1", "H3", operation.Number)
|
|
rowIndex := 5
|
totalAmount := decimal.NewFromInt(0)
|
for _, v := range operation.Details {
|
f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), v.ProductId)
|
f.SetCellValue("Sheet1", "B"+strconv.Itoa(rowIndex), v.Product.Name)
|
f.SetCellValue("Sheet1", "C"+strconv.Itoa(rowIndex), v.Product.Specs)
|
f.SetCellValue("Sheet1", "D"+strconv.Itoa(rowIndex), v.Product.Unit)
|
f.SetCellValue("Sheet1", "E"+strconv.Itoa(rowIndex), v.Amount.String())
|
f.SetCellValue("Sheet1", "H"+strconv.Itoa(rowIndex), v.ToLocation.Name)
|
f.SetCellValue("Sheet1", "I"+strconv.Itoa(rowIndex), operation.Comment)
|
rowIndex++
|
totalAmount = totalAmount.Add(v.Amount)
|
}
|
if err := f.MergeCell("Sheet1", "A"+strconv.Itoa(rowIndex), "C"+strconv.Itoa(rowIndex)); err != nil {
|
return "", err
|
}
|
f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), "合计")
|
f.SetCellValue("Sheet1", "E"+strconv.Itoa(rowIndex), totalAmount.String())
|
f.SetCellStyle("Sheet1", "A4", "I"+strconv.Itoa(rowIndex), style)
|
rowIndex++
|
|
f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), "审核:")
|
f.SetCellValue("Sheet1", "D"+strconv.Itoa(rowIndex), "检验:")
|
f.SetCellValue("Sheet1", "G"+strconv.Itoa(rowIndex), "保管:")
|
f.SetCellStyle("Sheet1", "A"+strconv.Itoa(rowIndex), "G"+strconv.Itoa(rowIndex), styleLeft)
|
|
buf, err := f.WriteToBuffer()
|
if err != nil {
|
return "", err
|
}
|
|
fileUrl, err := upload.UploadFileToSeaWeed(string(constvar.FileType_File), uuid.NewV4().String()+".xlsx", buf.Bytes())
|
if err != nil {
|
logx.Errorf("file upload err: %v", err)
|
return "", err
|
}
|
|
return fileUrl, nil
|
}
|
|
func ExportOutputOperation(category constvar.FileTemplateCategory, operation *models.Operation) (string, error) {
|
repositoryLevels := strings.Split(operation.Location.JointName, "/")
|
template, err := models.NewFileTemplateAttachmentSearch().SetPreload(true).SetCategory(category).First()
|
if err != nil {
|
return "", errors.New("获取模版记录失败:" + err.Error())
|
}
|
readerCloser, err := http.HttpGetWithReadCloser(template.Attachment.FileUrl)
|
if err != nil {
|
return "", errors.New("获取模版失败:" + err.Error())
|
}
|
|
f, err := excelize.OpenReader(readerCloser)
|
if err != nil {
|
return "", errors.New("读取excel模版失败:" + err.Error())
|
}
|
readerCloser.Close()
|
defer f.Close()
|
|
style, _ := f.NewStyle(&excelize.Style{
|
Border: []excelize.Border{
|
{Type: "left", Color: "#000000", Style: 1},
|
{Type: "top", Color: "#000000", Style: 1},
|
{Type: "bottom", Color: "#000000", Style: 1},
|
{Type: "right", Color: "#000000", Style: 1},
|
},
|
Alignment: &excelize.Alignment{
|
Horizontal: "center",
|
Vertical: "center",
|
},
|
})
|
|
f.SetCellValue("Sheet1", "C2", repositoryLevels[0])
|
f.SetCellValue("Sheet1", "H2", operation.WaybillNumber)
|
f.SetCellValue("Sheet1", "K2", operation.AuditDate)
|
f.SetCellValue("Sheet1", "H3", operation.SourceNumber)
|
f.SetCellValue("Sheet1", "K3", operation.Number)
|
f.SetCellValue("Sheet1", "C4", operation.CompanyName)
|
f.SetCellValue("Sheet1", "I4", operation.ReceiverAddr)
|
f.SetCellValue("Sheet1", "C5", operation.ReceiverName)
|
f.SetCellValue("Sheet1", "H5", operation.ReceiverPhone)
|
|
rowIndex := 8
|
totalAmount := decimal.NewFromInt(0)
|
totalWeight := decimal.NewFromInt(0)
|
|
addRows := len(operation.Details) - 1
|
if addRows > 0 {
|
f.InsertRows("Sheet1", 9, addRows)
|
f.SetCellStyle("Sheet1", "A9", "K"+strconv.Itoa(rowIndex+addRows), style)
|
}
|
|
for k, v := range operation.Details {
|
productTotalWeight := v.Amount.Mul(v.Product.Weight).Div(decimal.NewFromInt(1000))
|
f.SetCellValue("Sheet1", "A"+strconv.Itoa(rowIndex), k+1)
|
f.SetCellValue("Sheet1", "B"+strconv.Itoa(rowIndex), v.Product.Name)
|
f.SetCellValue("Sheet1", "H"+strconv.Itoa(rowIndex), v.Amount)
|
f.SetCellValue("Sheet1", "I"+strconv.Itoa(rowIndex), v.Product.Weight)
|
f.SetCellValue("Sheet1", "J"+strconv.Itoa(rowIndex), productTotalWeight)
|
rowIndex++
|
totalAmount = totalAmount.Add(v.Amount)
|
totalWeight = totalWeight.Add(productTotalWeight)
|
}
|
|
buf, err := f.WriteToBuffer()
|
if err != nil {
|
return "", err
|
}
|
|
fileUrl, err := upload.UploadFileToSeaWeed(string(constvar.FileType_File), uuid.NewV4().String()+".xlsx", buf.Bytes())
|
if err != nil {
|
logx.Errorf("file upload err: %v", err)
|
return "", err
|
}
|
|
return fileUrl, nil
|
|
}
|
|
// GetSupplierList
|
// @Tags 入库/出库
|
// @Summary 获取物流公司列表
|
// @Produce application/json
|
//
|
// @Success 200 {object} util.Response "成功"
|
//
|
// @Router /api-wms/v1/operation/getSupplierList [get]
|
func (slf OperationController) GetSupplierList(c *gin.Context) {
|
cli := supplier.NewSupplierServiceClient(init_client.SrmConn)
|
resp, err := cli.GetSupplierList(c, &supplier.SupplierListRequest{Status: 1})
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "grpc调用失败:"+err.Error())
|
return
|
}
|
util.ResponseFormat(c, code.Success, resp.List)
|
}
|
|
// GetClientList
|
// @Tags 入库/出库
|
// @Summary 获取物流公司列表
|
// @Produce application/json
|
//
|
// @Success 200 {object} util.Response "成功"
|
//
|
// @Router /api-wms/v1/operation/getClientList [get]
|
func (slf OperationController) GetClientList(c *gin.Context) {
|
cli := client.NewClientServiceClient(init_client.CrmConn)
|
resp, err := cli.GetClientList(c, &client.ClientListRequest{})
|
if err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "grpc调用失败:"+err.Error())
|
return
|
}
|
util.ResponseFormat(c, code.Success, resp.List)
|
}
|
|
// ListByCondition
|
// @Tags 入库/出库
|
// @Summary 出入库明细
|
// @Produce application/json
|
// @Param object body request.OperationCondition true "参数"
|
// @Success 200 {object} util.Response "成功"
|
// @Router /api-wms/v1/operation/listByCondition [post]
|
func (slf OperationController) ListByCondition(c *gin.Context) {
|
var params request.OperationCondition
|
if err := c.BindJSON(¶ms); err != nil {
|
util.ResponseFormat(c, code.RequestParamError, "参数解析失败,数据类型错误:"+err.Error())
|
return
|
}
|
if !params.PageInfo.Check() {
|
util.ResponseFormat(c, code.RequestParamError, "数据分页信息错误")
|
return
|
}
|
|
db := mysqlx.GetDB().Table("wms_operation").
|
Select("wms_operation.id as operation_id,wms_operation.number,wms_operation.base_operation_type,material.id AS product_id,material.`name` AS product_name,wms_operation_details.amount,material.unit,wms_operation_details.from_location_id,from_location.`name` AS from_location,wms_operation_details.to_location_id,to_location.`name` AS to_location,wms_operation.operation_date as date,wms_operation.`status`").
|
InnerJoins("inner join wms_operation_details ON wms_operation_details.operation_id = wms_operation.id").
|
InnerJoins("INNER JOIN material ON material.id = wms_operation_details.product_id").
|
InnerJoins("INNER JOIN wms_location AS from_location ON from_location.id = wms_operation_details.from_location_id").
|
InnerJoins("INNER JOIN wms_location AS to_location ON to_location.id = wms_operation_details.to_location_id")
|
if params.Condition != "" {
|
db = db.Where("wms_operation.number like ? or wms_operation.source_number like ? or from_location.`name` like ? or to_location.`name` like ? or material.`name` like ? ", "%"+params.Condition+"%", "%"+params.Condition+"%", "%"+params.Condition+"%", "%"+params.Condition+"%", "%"+params.Condition+"%")
|
}
|
var (
|
records = make([]*response.InventoryHistory, 0)
|
total int64
|
)
|
|
if err := db.Count(&total).Error; err != nil {
|
util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error())
|
return
|
}
|
|
if err := db.Offset((params.Page - 1) * params.PageSize).Limit(params.PageSize).Find(&records).Error; err != nil {
|
util.ResponseFormat(c, code.RequestError, "查找失败:"+err.Error())
|
return
|
}
|
util.ResponseFormatListWithPage(c, code.Success, records, int(total), params.Page, params.PageSize)
|
}
|