From ef69892ebc36de12866135fc41c6ff2837d0b417 Mon Sep 17 00:00:00 2001 From: xiongshuai <873144595@qq.com> Date: Tue, 11 Nov 2025 10:14:00 +0800 Subject: [PATCH] =?UTF-8?q?=E9=94=80=E5=94=AE=E5=87=BA=E5=BA=93=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../KingDee/SaveModel/SalesOutboundModel.cs | 157 +++++++++ .../Model/YTKJTShopParameter.cs | 16 + .../MyCode.Project.Domain.csproj | 1 + .../IServices/ISalesOutboundService.cs | 8 + .../Implementation/PurchaseStockInService.cs | 2 +- .../Implementation/SalesOutboundService.cs | 333 ++++++++++++++++++ .../MyCode.Project.Services.csproj | 2 + .../Controllers/TestController.cs | 14 +- 8 files changed, 531 insertions(+), 2 deletions(-) create mode 100644 Reportapi/MyCode.Project.Domain/Message/Request/KingDee/SaveModel/SalesOutboundModel.cs create mode 100644 Reportapi/MyCode.Project.Services/IServices/ISalesOutboundService.cs create mode 100644 Reportapi/MyCode.Project.Services/Implementation/SalesOutboundService.cs diff --git a/Reportapi/MyCode.Project.Domain/Message/Request/KingDee/SaveModel/SalesOutboundModel.cs b/Reportapi/MyCode.Project.Domain/Message/Request/KingDee/SaveModel/SalesOutboundModel.cs new file mode 100644 index 0000000..c53825f --- /dev/null +++ b/Reportapi/MyCode.Project.Domain/Message/Request/KingDee/SaveModel/SalesOutboundModel.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; + +namespace MyCode.Project.Domain.Message.Request.KingDee.SaveModel +{ + public class SalesOutboundModel + { + public int? FID { get; set; } + public FBillTypeID FBillTypeID { get; set; } + public string FBillNo { get; set; } + public string FDate { get; set; } + public FSaleOrgId FSaleOrgId { get; set; } + public FCustomerID FCustomerID { get; set; } + public FSaleDeptID FSaleDeptID { get; set; } + public FReceiverID FReceiverID { get; set; } + public FSalesManID FSalesManID { get; set; } + public FStockOrgId FStockOrgId { get; set; } + public FSettleID FSettleID { get; set; } + public FPayerID FPayerID { get; set; } + public string FOwnerTypeIdHead { get; set; } + public FOwnerIdHead FOwnerIdHead { get; set; } + public int? FCDateOffsetValue { get; set; } + public bool? FIsTotalServiceOrCost { get; set; } + public string F_dmi_Combo { get; set; } + public FSubHeadEntity SubHeadEntity { get; set; } + public List FEntity { get; set; } + } + + public class FSubHeadEntity + { + public FSettleCurrID FSettleCurrID { get; set; } + public FSettleOrgID FSettleOrgID { get; set; } + public bool? FIsIncludedTax { get; set; } + public FLocalCurrID FLocalCurrID { get; set; } + public FExchangeTypeID FExchangeTypeID { get; set; } + public decimal? FExchangeRate { get; set; } + public bool? FIsPriceExcludeTax { get; set; } + public decimal? FAllDisCount { get; set; } + } + + public class FSettleCurrID + { + public string FNumber { get; set; } + } + + public class FSettleOrgID + { + public string FNumber { get; set; } + } + + public class FLocalCurrID + { + public string FNumber { get; set; } + } + + public class FExchangeTypeID + { + public string FNumber { get; set; } + } + + public class FSalesOutboundEntryItem + { + public string FRowType { get; set; } + public FMaterialID FMaterialID { get; set; } + public FUnitID FUnitID { get; set; } + public decimal? FInventoryQty { get; set; } + public decimal? FRealQty { get; set; } + public decimal? FDisPriceQty { get; set; } + public decimal? FPrice { get; set; } + public decimal? FTaxPrice { get; set; } + public bool? FIsFree { get; set; } + public string FOwnerTypeID { get; set; } + public FOwnerID FOwnerID { get; set; } + public decimal? FEntryTaxRate { get; set; } + public decimal? FAuxUnitQty { get; set; } + public decimal? FExtAuxUnitQty { get; set; } + public string FSrcType { get; set; } + public string FSrcBillNo { get; set; } + public decimal? FDiscountRate { get; set; } + public decimal? FPriceDiscount { get; set; } + public decimal? FActQty { get; set; } + public FSalUnitID FSalUnitID { get; set; } + public decimal? FSALUNITQTY { get; set; } + public decimal? FSALBASEQTY { get; set; } + public decimal? FPRICEBASEQTY { get; set; } + public bool? FOUTCONTROL { get; set; } + public decimal? FRepairQty { get; set; } + public bool? FIsOverLegalOrg { get; set; } + public decimal? FARNOTJOINQTY { get; set; } + public int? FQmEntryID { get; set; } + public int? FConvertEntryID { get; set; } + public int? FSOEntryId { get; set; } + public decimal? FBeforeDisPriceQty { get; set; } + public decimal? FSignQty { get; set; } + public bool? FCheckDelivery { get; set; } + public decimal? FAllAmountExceptDisCount { get; set; } + public bool? FSettleBySon { get; set; } + public int? FBOMEntryId { get; set; } + public decimal? F_dmi_Amount { get; set; } + public FMaterialID_Sal FMaterialID_Sal { get; set; } + public int? FInStockEntryId { get; set; } + public int? FReceiveEntryId { get; set; } + public bool? FIsReplaceOut { get; set; } + public bool? FVmiBusinessStatus { get; set; } + } + + public class FMaterialID + { + public string FNumber { get; set; } + } + + public class FOwnerID + { + public string FNumber { get; set; } + } + + public class FSalUnitID + { + public string FNumber { get; set; } + } + + public class FMaterialID_Sal + { + public string FNUMBER { get; set; } + } + + public class FCustomerID + { + public string FNumber { get; set; } + } + + public class FSaleDeptID + { + public string FNumber { get; set; } + } + + public class FReceiverID + { + public string FNumber { get; set; } + } + + public class FSalesManID + { + public string FNumber { get; set; } + } + + public class FSettleID + { + public string FNumber { get; set; } + } + + public class FPayerID + { + public string FNumber { get; set; } + } +} + diff --git a/Reportapi/MyCode.Project.Domain/Model/YTKJTShopParameter.cs b/Reportapi/MyCode.Project.Domain/Model/YTKJTShopParameter.cs index 7a83477..8067fde 100644 --- a/Reportapi/MyCode.Project.Domain/Model/YTKJTShopParameter.cs +++ b/Reportapi/MyCode.Project.Domain/Model/YTKJTShopParameter.cs @@ -245,5 +245,21 @@ namespace MyCode.Project.Domain.Model [SugarColumn(ColumnName="F_PURCHASINGWAREHOUSECODE")] public string FPURCHASINGWAREHOUSECODE {get;set;} + /// + /// Desc:采购组织ID + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName="F_PURCHASEORGID")] + public int? FPURCHASEORGID {get;set;} + + /// + /// Desc:采购日期 + /// Default: + /// Nullable:True + /// + [SugarColumn(ColumnName="F_PURCHASE_DATE")] + public DateTime? FPURCHASEDATE {get;set;} + } } diff --git a/Reportapi/MyCode.Project.Domain/MyCode.Project.Domain.csproj b/Reportapi/MyCode.Project.Domain/MyCode.Project.Domain.csproj index 6997beb..1f6ee06 100644 --- a/Reportapi/MyCode.Project.Domain/MyCode.Project.Domain.csproj +++ b/Reportapi/MyCode.Project.Domain/MyCode.Project.Domain.csproj @@ -75,6 +75,7 @@ + diff --git a/Reportapi/MyCode.Project.Services/IServices/ISalesOutboundService.cs b/Reportapi/MyCode.Project.Services/IServices/ISalesOutboundService.cs new file mode 100644 index 0000000..ad11599 --- /dev/null +++ b/Reportapi/MyCode.Project.Services/IServices/ISalesOutboundService.cs @@ -0,0 +1,8 @@ +namespace MyCode.Project.Services.IServices +{ + public interface ISalesOutboundService + { + string PushSalesOutboundToKingDee(string id); + } +} + diff --git a/Reportapi/MyCode.Project.Services/Implementation/PurchaseStockInService.cs b/Reportapi/MyCode.Project.Services/Implementation/PurchaseStockInService.cs index b4aa77f..fc8a838 100644 --- a/Reportapi/MyCode.Project.Services/Implementation/PurchaseStockInService.cs +++ b/Reportapi/MyCode.Project.Services/Implementation/PurchaseStockInService.cs @@ -264,7 +264,7 @@ namespace MyCode.Project.Services.Implementation private PurchaseStockInModel BuildMainModel(PushKingDeeGoodsDocIn goodsDocIn, BusiOrderGoodsDocIn firstDetail, YTKJTShopParameter param, string orgId, List entryList) { - string dateStr = goodsDocIn.InOutDate2?.ToString("yyyy-MM-dd") ?? DateTime.Now.ToString("yyyy-MM-dd"); + string dateStr = goodsDocIn.InOutDate2?.ToString("yyyy-MM-dd HH:mm:ss") ?? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); return new PurchaseStockInModel() { diff --git a/Reportapi/MyCode.Project.Services/Implementation/SalesOutboundService.cs b/Reportapi/MyCode.Project.Services/Implementation/SalesOutboundService.cs new file mode 100644 index 0000000..14958b5 --- /dev/null +++ b/Reportapi/MyCode.Project.Services/Implementation/SalesOutboundService.cs @@ -0,0 +1,333 @@ +using MyCode.Project.Domain.Message.Request.KingDee; +using MyCode.Project.Domain.Message.Request.KingDee.SaveModel; +using MyCode.Project.Domain.Message.Response.KingDee.K3Result; +using MyCode.Project.Domain.Message.Response.KingDee.K3Result.Model; +using MyCode.Project.Domain.Model; +using MyCode.Project.Domain.Repositories; +using MyCode.Project.Infrastructure.Common; +using MyCode.Project.Infrastructure.Exceptions; +using MyCode.Project.OutSideService; +using MyCode.Project.Services.IServices; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MyCode.Project.Services.Implementation +{ + /// + /// 销售出库单服务实现 + /// 功能:处理销售出库单推送到金蝶云星空的业务逻辑 + /// + public class SalesOutboundService : ServiceBase, ISalesOutboundService + { + private const string BILL_TYPE_CODE = "XSCKD01_SYS"; + private const string OWNER_TYPE = "BD_OwnerOrg"; + private const string EXCHANGE_TYPE = "HLTX01_SYS"; + private const string DEFAULT_ORG = "100"; + private const string DEFAULT_CURRENCY = "PRE001"; + private const string DEFAULT_UNIT = "Pcs"; + private const string ROW_TYPE_STANDARD = "Standard"; + private const string ROW_TYPE_SERVICE = "Service"; + private const string FORM_ID = "SAL_OUTSTOCK"; + private const int TRADE_TYPE_SALES_OUTBOUND = 1; + + private IPushKingDeeOrderRepository _pushKingDeeOrderRepository; + private IPushKingDeeOrderItemRepository _pushKingDeeOrderItemRepository; + private IYTKJTShopParameterRepository _yTKJTShopParameterRepository; + private IKingDeeService _kingDeeService; + + public SalesOutboundService( + IPushKingDeeOrderRepository pushKingDeeOrderRepository, + IPushKingDeeOrderItemRepository pushKingDeeOrderItemRepository, + IYTKJTShopParameterRepository yTKJTShopParameterRepository, + IKingDeeService kingDeeService) + { + _pushKingDeeOrderRepository = pushKingDeeOrderRepository; + _pushKingDeeOrderItemRepository = pushKingDeeOrderItemRepository; + _yTKJTShopParameterRepository = yTKJTShopParameterRepository; + _kingDeeService = kingDeeService; + } + + /// + /// 推送销售出库单到金蝶云星空 + /// + /// PushKingDeeOrder表的主键ID(Guid格式的字符串) + /// 返回金蝶API的响应结果(JSON格式) + public string PushSalesOutboundToKingDee(string id) + { + var orderHead = _pushKingDeeOrderRepository + .Queryable() + .Where(t => id == t.Id.ToString()) + .First(); + + if (orderHead == null) + { + throw new BaseException($"未找到ID为 {id} 的销售出库单记录"); + } + + if (orderHead.Status != 0) + { + throw new BaseException($"销售出库单 {orderHead.Sheet} 已经推送过,状态为:{orderHead.Status},不允许重复推送"); + } + + if (orderHead.TradeType != TRADE_TYPE_SALES_OUTBOUND) + { + throw new BaseException($"订单类型 {orderHead.TradeType} 不是销售出库单(TradeType应为1),无法推送"); + } + + var param = _yTKJTShopParameterRepository + .Queryable() + .Where(t => t.FSHOPCODE == orderHead.ShopCode) + .Where(t => t.FDOCUMENTSTATUS == "C") + .First(); + + if (param == null) + { + throw new BaseException($"门店编号:{orderHead.ShopCode},门店名称:{orderHead.ShopName},没有找到对应的门店参数"); + } + + if (param.FSYNCHRONIZEKINGDEE == "0") + { + throw new BaseException($"门店编号:{orderHead.ShopCode},门店名称:{orderHead.ShopName},金蝶同步功能未启用,请联系管理员检查门店参数配置"); + } + + if (param.FSYNCHRONIZEKINGDEE == "1") + { + var response = PushKingdeeSalesOutbound(orderHead, param); + string result = JsonHelper.ToJson(response); + + if (response.IsSuccess) + { + orderHead.Status = 2; + _pushKingDeeOrderRepository.Update(orderHead); + } + + return result; + } + + return ""; + } + + /// + /// 推送到金蝶云星空 + /// + /// 源单信息 + /// 门店配置 + /// + private K3CloudResponseStatus PushKingdeeSalesOutbound(PushKingDeeOrder orderHead, YTKJTShopParameter param) + { + // 从PushKingDeeOrderItem表获取明细数据 + var itemList = _pushKingDeeOrderItemRepository + .Queryable() + .Where(t => orderHead.Id == t.PushKingDeeOrderId) + .ToList(); + + if (itemList == null || itemList.Count == 0) + { + throw new BaseException($"销售出库单 {orderHead.Sheet} 没有找到明细数据,无法推送"); + } + + // 获取销售组织ID + string orgId = param.FSALEORGID?.ToString() ?? DEFAULT_ORG; + + // 获取仓库编码 + string warehouseCode = orderHead.WarehouseCode ?? param.FWAREHOUSECODE ?? ""; + + // 数据校验 + if (string.IsNullOrEmpty(warehouseCode)) + { + throw new BaseException($"销售出库单 {orderHead.Sheet} 仓库编码为空,无法推送"); + } + + // 构建金蝶API的明细行数据 + var entryList = itemList.Select(n => BuildEntryItem(n, param, orgId, warehouseCode)).ToList(); + + if (entryList.Count == 0) + { + throw new BaseException($"销售出库单 {orderHead.Sheet} 明细数据转换失败,无法推送"); + } + + // 构建金蝶API的主表数据 + var model = BuildMainModel(orderHead, param, orgId, entryList); + + // 构建金蝶API请求对象 + BillSave billSave = new BillSave() + { + Model = model, + IsAutoSubmitAndAudit = true, + }; + + // 调用金蝶服务保存单据 + var responseStatus = _kingDeeService.Save(FORM_ID, billSave); + return responseStatus; + } + + /// + /// 构建明细行数据 + /// + private FSalesOutboundEntryItem BuildEntryItem(PushKingDeeOrderItem item, YTKJTShopParameter param, string orgId, string warehouseCode) + { + string rowType = ROW_TYPE_STANDARD; + + // 计算数量(确保为正数) + decimal realQty = Math.Abs(item.SellCount); + + // 计算含税单价 + decimal taxRate = param.FTAXRATE; + decimal taxPrice = item.SellCount != 0 ? Math.Round(Math.Abs(item.DivideSellTotal) / item.SellCount, 10) : 0; + + // 计算无税单价 + decimal price = taxRate > 0 && taxPrice > 0 ? Math.Round(taxPrice / (1 + taxRate / 100), 10) : taxPrice; + + // 计算金额 + decimal allAmountExceptDisCount = Math.Abs(item.DivideSellTotal); + + return new FSalesOutboundEntryItem() + { + FRowType = rowType, + FMaterialID = new FMaterialID() + { + FNumber = item.Barcode ?? "" + }, + FUnitID = new FUnitID() + { + FNumber = item.Unit ?? DEFAULT_UNIT + }, + FInventoryQty = 0, + FRealQty = realQty, + FDisPriceQty = 0, + FPrice = price, + FTaxPrice = taxPrice, + FIsFree = false, + FOwnerTypeID = OWNER_TYPE, + FOwnerID = new FOwnerID() + { + FNumber = orgId + }, + FEntryTaxRate = param.FTAXRATE, + FAuxUnitQty = 0, + FExtAuxUnitQty = 0, + FSrcType = "", + FSrcBillNo = "", + FDiscountRate = 0, + FPriceDiscount = 0, + FActQty = realQty, + FSalUnitID = new FSalUnitID() + { + FNumber = item.Unit ?? DEFAULT_UNIT + }, + FSALUNITQTY = realQty, + FSALBASEQTY = realQty, + FPRICEBASEQTY = realQty, + FOUTCONTROL = false, + FRepairQty = 0, + FIsOverLegalOrg = false, + FARNOTJOINQTY = realQty, + FQmEntryID = 0, + FConvertEntryID = 0, + FSOEntryId = 0, + FBeforeDisPriceQty = 0, + FSignQty = 0, + FCheckDelivery = false, + FAllAmountExceptDisCount = allAmountExceptDisCount, + FSettleBySon = false, + FBOMEntryId = 0, + F_dmi_Amount = allAmountExceptDisCount, + FMaterialID_Sal = new FMaterialID_Sal() + { + FNUMBER = item.Barcode ?? "" + }, + FInStockEntryId = 0, + FReceiveEntryId = 0, + FIsReplaceOut = false, + FVmiBusinessStatus = false + }; + } + + /// + /// 构建主表数据 + /// + private SalesOutboundModel BuildMainModel(PushKingDeeOrder orderHead, YTKJTShopParameter param, string orgId, List entryList) + { + string dateStr = orderHead.ConsignTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + string customerId = param.FSALESCUSTOMERS > 0 ? param.FSALESCUSTOMERS.ToString() : ""; + string salesManId = param.FSALER.HasValue && param.FSALER.Value > 0 ? param.FSALER.Value.ToString() : ""; + + return new SalesOutboundModel() + { + FID = 0, + FBillTypeID = new FBillTypeID() + { + FNUMBER = BILL_TYPE_CODE + }, + FBillNo = orderHead.Sheet > 0 ? orderHead.Sheet.ToString() : "", + FDate = dateStr, + FSaleOrgId = new FSaleOrgId() + { + FNumber = orgId + }, + FCustomerID = new FCustomerID() + { + FNumber = customerId + }, + FSaleDeptID = new FSaleDeptID() + { + FNumber = "02" + }, + FReceiverID = new FReceiverID() + { + FNumber = customerId + }, + FSalesManID = new FSalesManID() + { + FNumber = salesManId + }, + FStockOrgId = new FStockOrgId() + { + FNumber = orgId + }, + FSettleID = new FSettleID() + { + FNumber = customerId + }, + FPayerID = new FPayerID() + { + FNumber = customerId + }, + FOwnerTypeIdHead = OWNER_TYPE, + FOwnerIdHead = new FOwnerIdHead() + { + FNumber = orgId + }, + FCDateOffsetValue = 0, + FIsTotalServiceOrCost = false, + F_dmi_Combo = "标准出库", + SubHeadEntity = new FSubHeadEntity() + { + FSettleCurrID = new FSettleCurrID() + { + FNumber = DEFAULT_CURRENCY + }, + FSettleOrgID = new FSettleOrgID() + { + FNumber = orgId + }, + FIsIncludedTax = false, + FLocalCurrID = new FLocalCurrID() + { + FNumber = DEFAULT_CURRENCY + }, + FExchangeTypeID = new FExchangeTypeID() + { + FNumber = EXCHANGE_TYPE + }, + FExchangeRate = 1m, + FIsPriceExcludeTax = true, + FAllDisCount = 0m + }, + FEntity = entryList + }; + } + } +} + diff --git a/Reportapi/MyCode.Project.Services/MyCode.Project.Services.csproj b/Reportapi/MyCode.Project.Services/MyCode.Project.Services.csproj index 631f7c2..10d8448 100644 --- a/Reportapi/MyCode.Project.Services/MyCode.Project.Services.csproj +++ b/Reportapi/MyCode.Project.Services/MyCode.Project.Services.csproj @@ -120,6 +120,7 @@ + @@ -131,6 +132,7 @@ + diff --git a/Reportapi/MyCode.Project.WebApi/Controllers/TestController.cs b/Reportapi/MyCode.Project.WebApi/Controllers/TestController.cs index 0d22738..ffd833d 100644 --- a/Reportapi/MyCode.Project.WebApi/Controllers/TestController.cs +++ b/Reportapi/MyCode.Project.WebApi/Controllers/TestController.cs @@ -21,16 +21,18 @@ namespace MyCode.Project.WebApi.Controllers private IJackYunStockinService _jackYunStockinService; private IOrderPushService _orderPushService; private IPurchaseStockInService _purchaseStockInService; + private ISalesOutboundService _salesOutboundService; public TestController(IJackYunTaskService jackYunTaskService, IWMSService wMSService , IJackYunStockinService jackYunStockinService, IOrderPushService orderPushService - , IPurchaseStockInService purchaseStockInService) + , IPurchaseStockInService purchaseStockInService, ISalesOutboundService salesOutboundService) { _jackYunTaskService = jackYunTaskService; _wMSService = wMSService; _jackYunStockinService = jackYunStockinService; _orderPushService = orderPushService; _purchaseStockInService = purchaseStockInService; + _salesOutboundService = salesOutboundService; } /// @@ -53,6 +55,16 @@ namespace MyCode.Project.WebApi.Controllers return _purchaseStockInService.PushPurchaseStockInToKingDee(id); } + /// + /// 测试推送销售出库单到金蝶云星空 + /// + [HttpGet] + [AllowAnonymous] + public string TaskSendKingdeeSalesOutboundById(string id) + { + return _salesOutboundService.PushSalesOutboundToKingDee(id); + } + #region 调度运行抓吉客云销售订单