销售出库单

This commit is contained in:
熊帅 2025-11-11 10:14:00 +08:00
parent a8c6c37db1
commit ef69892ebc
8 changed files with 531 additions and 2 deletions

View File

@ -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<FSalesOutboundEntryItem> 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; }
}
}

View File

@ -245,5 +245,21 @@ namespace MyCode.Project.Domain.Model
[SugarColumn(ColumnName="F_PURCHASINGWAREHOUSECODE")]
public string FPURCHASINGWAREHOUSECODE {get;set;}
/// <summary>
/// Desc:采购组织ID
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="F_PURCHASEORGID")]
public int? FPURCHASEORGID {get;set;}
/// <summary>
/// Desc:采购日期
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="F_PURCHASE_DATE")]
public DateTime? FPURCHASEDATE {get;set;}
}
}

View File

@ -75,6 +75,7 @@
<Compile Include="Message\Request\KingDee\BillSave.cs" />
<Compile Include="Message\Request\KingDee\SaveModel\PurchaseStockInModel.cs" />
<Compile Include="Message\Request\KingDee\SaveModel\SaleOrderModel.cs" />
<Compile Include="Message\Request\KingDee\SaveModel\SalesOutboundModel.cs" />
<Compile Include="Message\Request\TimeRange.cs" />
<Compile Include="Message\Request\WMS\SaleOrder.cs" />
<Compile Include="Message\Response\Common\HeadFieldItem.cs" />

View File

@ -0,0 +1,8 @@
namespace MyCode.Project.Services.IServices
{
public interface ISalesOutboundService
{
string PushSalesOutboundToKingDee(string id);
}
}

View File

@ -264,7 +264,7 @@ namespace MyCode.Project.Services.Implementation
private PurchaseStockInModel BuildMainModel(PushKingDeeGoodsDocIn goodsDocIn, BusiOrderGoodsDocIn firstDetail, YTKJTShopParameter param, string orgId, List<FPurchaseStockInEntryItem> 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()
{

View File

@ -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
{
/// <summary>
/// 销售出库单服务实现
/// 功能:处理销售出库单推送到金蝶云星空的业务逻辑
/// </summary>
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;
}
/// <summary>
/// 推送销售出库单到金蝶云星空
/// </summary>
/// <param name="id">PushKingDeeOrder表的主键IDGuid格式的字符串</param>
/// <returns>返回金蝶API的响应结果JSON格式</returns>
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 "";
}
/// <summary>
/// 推送到金蝶云星空
/// </summary>
/// <param name="orderHead">源单信息</param>
/// <param name="param">门店配置</param>
/// <returns></returns>
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;
}
/// <summary>
/// 构建明细行数据
/// </summary>
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
};
}
/// <summary>
/// 构建主表数据
/// </summary>
private SalesOutboundModel BuildMainModel(PushKingDeeOrder orderHead, YTKJTShopParameter param, string orgId, List<FSalesOutboundEntryItem> 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
};
}
}
}

View File

@ -120,6 +120,7 @@
<Compile Include="Implementation\JackYunStockinService.cs" />
<Compile Include="Implementation\OrderPushService.cs" />
<Compile Include="Implementation\PurchaseStockInService.cs" />
<Compile Include="Implementation\SalesOutboundService.cs" />
<Compile Include="Implementation\WMSService.cs" />
<Compile Include="Implementation\JackYunService.cs" />
<Compile Include="Implementation\JackYunTaskService.cs" />
@ -131,6 +132,7 @@
<Compile Include="IServices\IJackYunStockinService.cs" />
<Compile Include="IServices\IOrderPushService.cs" />
<Compile Include="IServices\IPurchaseStockInService.cs" />
<Compile Include="IServices\ISalesOutboundService.cs" />
<Compile Include="IServices\IWMSService.cs" />
<Compile Include="IServices\IJackYunTaskService.cs" />
<Compile Include="IServices\IJackYunService.cs" />

View File

@ -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;
}
/// <summary>
@ -53,6 +55,16 @@ namespace MyCode.Project.WebApi.Controllers
return _purchaseStockInService.PushPurchaseStockInToKingDee(id);
}
/// <summary>
/// 测试推送销售出库单到金蝶云星空
/// </summary>
[HttpGet]
[AllowAnonymous]
public string TaskSendKingdeeSalesOutboundById(string id)
{
return _salesOutboundService.PushSalesOutboundToKingDee(id);
}
#region