package controllers
|
|
import (
|
"context"
|
"encoding/json"
|
"errors"
|
"fmt"
|
"github.com/gin-gonic/gin"
|
"github.com/shopspring/decimal"
|
"github.com/xuri/excelize/v2"
|
"google.golang.org/grpc"
|
"google.golang.org/grpc/credentials/insecure"
|
"gorm.io/gorm"
|
"os"
|
"sort"
|
"strconv"
|
"strings"
|
"time"
|
"wms/conf"
|
"wms/constvar"
|
"wms/extend/code"
|
"wms/extend/util"
|
"wms/models"
|
"wms/opa"
|
"wms/pkg/logx"
|
"wms/pkg/structx"
|
"wms/proto/client"
|
"wms/proto/product_inventory"
|
"wms/proto/purchase_wms"
|
"wms/proto/supplier"
|
"wms/request"
|
"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
|
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 {
|
if location, err := models.NewLocationSearch().SetType(int(constvar.LocationTypeVendor)).First(); err != nil {
|
return err
|
} else {
|
params.FromLocationID = location.Id
|
}
|
if params.ToLocationID == 0 {
|
return errors.New("请选择目标位置")
|
}
|
}
|
if operationType.BaseOperationType == constvar.BaseOperationTypeOutgoing {
|
if location, err := models.NewLocationSearch().SetType(int(constvar.LocationTypeCustomer)).First(); err != nil {
|
return err
|
} else {
|
params.ToLocationID = location.Id
|
}
|
if params.FromLocationID == 0 {
|
return errors.New("请选择源位置")
|
}
|
}
|
if operationType.BaseOperationType == constvar.BaseOperationTypeInternal {
|
if params.ToLocationID == 0 {
|
return errors.New("请选择目标位置")
|
}
|
if params.FromLocationID == 0 {
|
return errors.New("请选择源位置")
|
}
|
}
|
return nil
|
}
|
|
func (slf OperationController) CheckParams(params models.Operation) error {
|
if params.SourceNumber == "" {
|
return errors.New("请填入源单号")
|
}
|
|
if params.OperationTypeId == 0 && int(params.BaseOperationType) == 0 {
|
return errors.New("未识别该记录类型")
|
}
|
|
if params.OperationDate == "" {
|
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
|
}
|
if 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
|
}
|
|
var listProdtId []string
|
var listProdt []*models.Material
|
mapProdt := make(map[string]decimal.Decimal)
|
listDetails, err := models.NewOperationDetailsSearch().SetOperationId(operation.Id).FindAll()
|
if err != nil {
|
return err
|
}
|
for _, v := range listDetails {
|
listProdtId = append(listProdtId, v.ProductId)
|
mapProdt[v.ProductId] = v.Amount
|
}
|
|
if err := models.NewMaterialSearch().Orm.Where("id IN ?", listProdtId).Find(&listProdt).Error; err != nil {
|
return err
|
}
|
|
if operation.BaseOperationType == constvar.BaseOperationTypeIncoming {
|
var operationInputs []*models.Operation
|
for k, v := range listProdt {
|
value, ok := mapProdt[v.ID]
|
if !ok {
|
return errors.New("产品种类异常")
|
}
|
|
listProdt[k].Amount = listProdt[k].Amount.Add(value)
|
if err := tx.Save(listProdt[k]).Error; err != nil {
|
return err
|
}
|
|
locationRule, err := models.NewLocationProductSearch().SetProductId(v.ID).SetAreaId(operation.ToLocationID).First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
|
if err == nil {
|
operationTransfer := &models.Operation{
|
Number: operation.Number,
|
SourceNumber: operation.SourceNumber,
|
OperationTypeId: 0,
|
OperationTypeName: operation.OperationTypeName,
|
Status: constvar.OperationStatus_Finish,
|
FromLocationID: locationRule.AreaId,
|
ToLocationID: locationRule.LocationId,
|
OperationDate: operation.OperationDate,
|
ContacterID: operation.ContacterID,
|
ContacterName: operation.ContacterName,
|
CompanyID: operation.CompanyID,
|
CompanyName: operation.CompanyName,
|
Comment: operation.Comment,
|
BaseOperationType: constvar.BaseOperationTypeInternal,
|
Details: []*models.OperationDetails{
|
{
|
ProductId: v.ID,
|
Amount: value,
|
},
|
},
|
}
|
operationInputs = append(operationInputs, operationTransfer)
|
|
//if err := tx.Create(&operationTransfer).Error; err != nil {
|
// return err
|
//}
|
|
locAmount, err := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(locationRule.LocationId).
|
First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
locAmount.LocationId = locationRule.LocationId
|
locAmount.ProductId = v.ID
|
locAmount.CreateDate = time.Now().Format("2006-01-02 15:04:05")
|
locAmount.Amount = locAmount.Amount.Add(value)
|
locAmount.ProductCategoryID = v.CategoryId
|
if res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
} else {
|
locationRule, err = models.NewLocationProductSearch().SetProductCategoryId(v.CategoryId).SetAreaId(operation.ToLocationID).First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
if err == nil {
|
operationTransfer := &models.Operation{
|
Number: operation.Number,
|
SourceNumber: operation.SourceNumber,
|
OperationTypeId: 0,
|
OperationTypeName: operation.OperationTypeName,
|
Status: constvar.OperationStatus_Finish,
|
FromLocationID: locationRule.AreaId,
|
ToLocationID: locationRule.LocationId,
|
OperationDate: operation.OperationDate,
|
ContacterID: operation.ContacterID,
|
ContacterName: operation.ContacterName,
|
CompanyID: operation.CompanyID,
|
CompanyName: operation.CompanyName,
|
Comment: operation.Comment,
|
BaseOperationType: constvar.BaseOperationTypeInternal,
|
Details: []*models.OperationDetails{
|
{
|
ProductId: v.ID,
|
Amount: value,
|
},
|
},
|
}
|
operationInputs = append(operationInputs, operationTransfer)
|
|
//if err := tx.Create(&operationTransfer).Error; err != nil {
|
// return err
|
//}
|
|
locAmount, err := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(locationRule.LocationId).
|
First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
locAmount.LocationId = locationRule.LocationId
|
locAmount.ProductId = v.ID
|
locAmount.CreateDate = time.Now().Format("2006-01-02 15:04:05")
|
locAmount.Amount = locAmount.Amount.Add(value)
|
locAmount.ProductCategoryID = v.CategoryId
|
if res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
} else {
|
locAmount, err := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(operation.ToLocationID).
|
First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
locAmount.LocationId = operation.ToLocationID
|
locAmount.ProductId = v.ID
|
locAmount.CreateDate = time.Now().Format("2006-01-02 15:04:05")
|
locAmount.Amount = locAmount.Amount.Add(value)
|
locAmount.ProductCategoryID = v.CategoryId
|
if res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
}
|
}
|
}
|
if len(operationInputs) > 0 {
|
if err := tx.Create(&operationInputs).Error; err != nil {
|
return err
|
}
|
}
|
if operation.SourceNumber != "" {
|
go UpdatePurchaseStatus(operation.SourceNumber)
|
}
|
|
}
|
|
if operation.BaseOperationType == constvar.BaseOperationTypeOutgoing {
|
for k, v := range listProdt {
|
value, ok := mapProdt[v.ID]
|
if !ok {
|
return errors.New("产品种类异常")
|
}
|
//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.Amount.LessThan(value) {
|
return errors.New(fmt.Sprintf("产品:%v,库存:%v,出库:%v,数量不够,无法完成出库操作", v.Name, v.Amount.String(), value.String()))
|
}
|
}
|
listProdt[k].Amount = listProdt[k].Amount.Sub(value)
|
if err := tx.Save(listProdt[k]).Error; err != nil {
|
return err
|
}
|
locAmount, res := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(operation.ToLocationID).
|
FirstRes()
|
if res.Error != nil {
|
return err
|
}
|
if locAmount.Amount.LessThan(value) {
|
return errors.New(fmt.Sprintf("产品:%v,库存:%v,出库:%v,数量不够,无法完成出库操作", v.Name, v.Amount.String(), value.String()))
|
}
|
locAmount.Amount = locAmount.Amount.Sub(value)
|
if err := models.NewLocationProductAmountSearch().SetID(locAmount.Id).Update(locAmount); err != nil {
|
return err
|
}
|
}
|
if operation.SourceNumber != "" {
|
go UpdateSalesDetailStatus(operation.SourceNumber)
|
}
|
}
|
|
if operation.BaseOperationType == constvar.BaseOperationTypeInternal {
|
var operationInputs []*models.Operation
|
for _, v := range listProdt {
|
value, ok := mapProdt[v.ID]
|
if !ok {
|
return errors.New("产品种类异常")
|
}
|
//listProdt[k].Amount = listProdt[k].Amount.Add(value)
|
//if err := tx.Save(listProdt[k]).Error; err != nil {
|
// return err
|
//}
|
fromLocAmount, res := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(operation.FromLocationID).
|
FirstRes()
|
if res.Error != nil {
|
return err
|
}
|
if fromLocAmount.Amount.LessThan(value) {
|
return errors.New(fmt.Sprintf("产品:%v,库存:%v,调拨:%v,数量不够,无法完成调拨操作", v.Name, v.Amount.String(), value.String()))
|
}
|
fromLocAmount.Amount = fromLocAmount.Amount.Sub(value)
|
if err := models.NewLocationProductAmountSearch().SetID(fromLocAmount.Id).Update(fromLocAmount); err != nil {
|
return err
|
}
|
|
locationRule, err := models.NewLocationProductSearch().SetProductId(v.ID).SetAreaId(operation.ToLocationID).First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
|
if err == nil {
|
operationTransfer := &models.Operation{
|
Number: operation.Number,
|
SourceNumber: operation.SourceNumber,
|
OperationTypeId: 0,
|
OperationTypeName: operation.OperationTypeName,
|
Status: constvar.OperationStatus_Finish,
|
FromLocationID: locationRule.AreaId,
|
ToLocationID: locationRule.LocationId,
|
OperationDate: operation.OperationDate,
|
ContacterID: operation.ContacterID,
|
ContacterName: operation.ContacterName,
|
CompanyID: operation.CompanyID,
|
CompanyName: operation.CompanyName,
|
Comment: operation.Comment,
|
BaseOperationType: constvar.BaseOperationTypeInternal,
|
Details: []*models.OperationDetails{
|
{
|
ProductId: v.ID,
|
Amount: value,
|
},
|
},
|
}
|
operationInputs = append(operationInputs, operationTransfer)
|
|
//if err := tx.Create(&operationTransfer).Error; err != nil {
|
// return err
|
//}
|
|
locAmount, err := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(locationRule.LocationId).
|
First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
locAmount.LocationId = locationRule.LocationId
|
locAmount.ProductId = v.ID
|
locAmount.CreateDate = time.Now().Format("2006-01-02 15:04:05")
|
locAmount.Amount = locAmount.Amount.Add(value)
|
locAmount.ProductCategoryID = v.CategoryId
|
if res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
} else {
|
locationRule, err = models.NewLocationProductSearch().SetProductCategoryId(v.CategoryId).SetAreaId(operation.ToLocationID).First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
if err == nil {
|
operationTransfer := &models.Operation{
|
Number: operation.Number,
|
SourceNumber: operation.SourceNumber,
|
OperationTypeId: 0,
|
OperationTypeName: operation.OperationTypeName,
|
Status: constvar.OperationStatus_Finish,
|
FromLocationID: locationRule.AreaId,
|
ToLocationID: locationRule.LocationId,
|
OperationDate: operation.OperationDate,
|
ContacterID: operation.ContacterID,
|
ContacterName: operation.ContacterName,
|
CompanyID: operation.CompanyID,
|
CompanyName: operation.CompanyName,
|
Comment: operation.Comment,
|
BaseOperationType: constvar.BaseOperationTypeInternal,
|
Details: []*models.OperationDetails{
|
{
|
ProductId: v.ID,
|
Amount: value,
|
},
|
},
|
}
|
operationInputs = append(operationInputs, operationTransfer)
|
|
//if err := tx.Create(&operationTransfer).Error; err != nil {
|
// return err
|
//}
|
|
locAmount, err := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(locationRule.LocationId).
|
First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
locAmount.LocationId = locationRule.LocationId
|
locAmount.ProductId = v.ID
|
locAmount.CreateDate = time.Now().Format("2006-01-02 15:04:05")
|
locAmount.Amount = locAmount.Amount.Add(value)
|
locAmount.ProductCategoryID = v.CategoryId
|
if res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
} else {
|
locAmount, err := models.NewLocationProductAmountSearch().
|
SetProductId(v.ID).
|
SetLocationId(operation.ToLocationID).
|
First()
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
return err
|
}
|
locAmount.LocationId = operation.ToLocationID
|
locAmount.ProductId = v.ID
|
locAmount.CreateDate = time.Now().Format("2006-01-02 15:04:05")
|
locAmount.Amount = locAmount.Amount.Add(value)
|
if res := models.NewLocationProductAmountSearch().Orm.Where("id=?", locAmount.ID).Save(locAmount); res.Error != nil {
|
return res.Error
|
}
|
}
|
}
|
}
|
if len(operationInputs) > 0 {
|
if err := tx.Create(&operationInputs).Error; err != nil {
|
return err
|
}
|
}
|
}
|
return nil
|
}); 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
|
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: operation.FromLocationID,
|
FromLocation: operation.FromLocation.Name,
|
ToLocationId: operation.ToLocationID,
|
ToLocation: operation.ToLocation.Name,
|
}
|
histories = append(histories, history)
|
}
|
}
|
if err := db.Model(&models.MoveHistory{}).Create(&histories).Error; err != nil {
|
return err
|
}
|
for _, history := range histories {
|
service.AddNewHistoryReportRecord(history.Id)
|
}
|
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 UpdateSalesDetailStatus(number string) {
|
client := product_inventory.NewProductInventoryServiceClient(ProductInventoryServiceConn)
|
_, err := client.UpdateSalesDetailStatus(context.Background(), &product_inventory.UpdateSalesDetailStatusRequest{
|
Number: number,
|
SalesDetailStatus: "已出库",
|
})
|
if err != nil {
|
logx.Errorf("grpc dial UpdateSalesDetailStatus service error: %v", err)
|
}
|
}
|
|
func UpdatePurchaseStatus(number string) {
|
client := purchase_wms.NewPurchaseServiceClient(PurchaseServiceConn)
|
_, err := client.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
|
}
|
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()
|
|
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), operation.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), "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.FromLocation.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()
|
|
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), "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(supplier.SupplierConn)
|
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(client.ClientConn)
|
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)
|
}
|