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 = "包"; 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 == 2) { 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 ?? ""; string 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 = false, }; // 调用金蝶服务保存单据 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() { Fcode= item.Barcode ?? "", 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() { FOrgId = orgId }, FStockID=new FStockID() { FNumber = param.FWAREHOUSECODE }, 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 = GenerateBillNo(orderHead.Sheet, orderHead.ConsignTime.Value), FDate = dateStr, FSaleOrgId = new FSaleOrgId() { FOrgId = orgId }, FCustomerID = new FCustomerID() { FCustId = customerId }, //FSaleDeptID = new FSaleDeptID() //{ // FNumber = "02" //}, FReceiverID = new FReceiverID() { FNumber = customerId }, FSalesManID = new FSalesManID() { FNumber = salesManId }, FStockOrgId = new FStockOrgId() { FOrgId = orgId }, FSettleID = new FSettleID() { FNumber = customerId }, FPayerID = new FPayerID() { FNumber = customerId }, FOwnerTypeIdHead = OWNER_TYPE, FOwnerIdHead = new FOwnerIdHead() { FOrgId = orgId }, FCDateOffsetValue = 0, FIsTotalServiceOrCost = false, //F_dmi_Combo = "标准出库", SubHeadEntity = new FSubHeadEntity() { FSettleCurrID = new FSettleCurrID() { FNumber = DEFAULT_CURRENCY }, FSettleOrgID = new FSettleOrgID() { FOrgId = orgId }, FIsIncludedTax = false, FLocalCurrID = new FLocalCurrID() { FNumber = DEFAULT_CURRENCY }, FExchangeTypeID = new FExchangeTypeID() { FNumber = EXCHANGE_TYPE }, FExchangeRate = 1m, FIsPriceExcludeTax = true, FAllDisCount = 0m }, FEntity = entryList }; } /** * JKXC+年月日+自增ID(三位)--销售出库单 JKXT+年月日+自增ID(三位)--销售退货单 JKCR+年月日+自增ID(三位)--采购入库单 JKCT+年月日+自增ID(三位)--采购退货单 **/ private string GenerateBillNo(int Sheet, DateTime date) { string prefix = "JKXC" + date.ToString("yyyyMMdd"); string numberPart = (Sheet > 0 ? Sheet.ToString() : "").PadLeft(3, '0'); numberPart = numberPart.Length > 3 ? numberPart.Substring(numberPart.Length - 3) : numberPart; // 组合并确保只取最后3位数字 string combined = prefix + numberPart; return combined; } } }