|
|
@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Order;
|
|
|
/// </summary>
|
|
|
public class MaterialRequirementCalculator : ITransient
|
|
|
{
|
|
|
- private const int MaxBomDepth = 6;
|
|
|
+ private const int MaxBomDepth = 20;
|
|
|
private readonly ISqlSugarClient _db;
|
|
|
|
|
|
public MaterialRequirementCalculator(ISqlSugarClient db)
|
|
|
@@ -50,7 +50,7 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
if (bomId is null)
|
|
|
{
|
|
|
warnings.Add($"订单行 {entry.EntrySeq}({itemNum})未匹配到 BOM,仅写入成品行资源检查结果");
|
|
|
- await ApplySupplyAsync(lines, entry.TenantId, bangId);
|
|
|
+ await ApplySupplyAsync(lines, entry.TenantId, bangId, entry.Id);
|
|
|
return lines;
|
|
|
}
|
|
|
|
|
|
@@ -58,7 +58,7 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
if (children.Count == 0)
|
|
|
{
|
|
|
warnings.Add($"订单行 {entry.EntrySeq} BOM {entry.BomNumber ?? bomId.ToString()} 无子件明细");
|
|
|
- await ApplySupplyAsync(lines, entry.TenantId, bangId);
|
|
|
+ await ApplySupplyAsync(lines, entry.TenantId, bangId, entry.Id);
|
|
|
return lines;
|
|
|
}
|
|
|
|
|
|
@@ -69,7 +69,7 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
lines, rootFid, "1", 1, orderQty, child, entry, needTime, warnings, seq, depth: 1);
|
|
|
}
|
|
|
|
|
|
- await ApplySupplyAsync(lines, entry.TenantId, bangId);
|
|
|
+ await ApplySupplyAsync(lines, entry.TenantId, bangId, entry.Id);
|
|
|
return lines;
|
|
|
}
|
|
|
|
|
|
@@ -101,59 +101,51 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
var fid = YitIdHelper.NextId();
|
|
|
var nextSeq = seq + 1;
|
|
|
|
|
|
- if (!isVirtual)
|
|
|
+ // 虚拟件(erp_cls=4)也写入资源检查记录,IsUse=0 标识为虚拟件,不参与缺料计算
|
|
|
+ lines.Add(new ResourceCheckBomLine
|
|
|
{
|
|
|
- lines.Add(new ResourceCheckBomLine
|
|
|
- {
|
|
|
- Fid = fid,
|
|
|
- ParentFid = parentFid,
|
|
|
- BomChildId = child.Id,
|
|
|
- Num = num,
|
|
|
- Level = parentLevel + 1,
|
|
|
- Type = child.Type,
|
|
|
- ItemNumber = child.ItemNumber ?? string.Empty,
|
|
|
- ItemName = child.ItemName,
|
|
|
- Model = child.Model,
|
|
|
- Unit = child.Unit,
|
|
|
- BomNumber = child.BomNumber,
|
|
|
- ErpCls = child.ErpCls,
|
|
|
- ErpClsName = MapErpClsName(child.ErpCls),
|
|
|
- Backflush = child.Backflush,
|
|
|
- Qty = unitQty,
|
|
|
- Wastage = child.Wastage,
|
|
|
- Scrap = child.Scrap,
|
|
|
- NeedCount = needCount,
|
|
|
- NeedCountNoLoss = RoundQty(needNoLoss),
|
|
|
- KittingTime = needTime,
|
|
|
- SatisfyTime = needTime,
|
|
|
- IsBom = child.IsBom,
|
|
|
- HaveIcSubs = child.HaveIcSubs,
|
|
|
- SubstituteCode = child.SubstituteCode,
|
|
|
- IsUse = 1
|
|
|
- });
|
|
|
- }
|
|
|
+ Fid = fid,
|
|
|
+ ParentFid = parentFid,
|
|
|
+ BomChildId = child.Id,
|
|
|
+ Num = num,
|
|
|
+ Level = parentLevel + 1,
|
|
|
+ Type = child.Type,
|
|
|
+ ItemNumber = child.ItemNumber ?? string.Empty,
|
|
|
+ ItemName = child.ItemName,
|
|
|
+ Model = child.Model,
|
|
|
+ Unit = child.Unit,
|
|
|
+ BomNumber = child.BomNumber,
|
|
|
+ ErpCls = child.ErpCls,
|
|
|
+ ErpClsName = MapErpClsName(child.ErpCls),
|
|
|
+ Backflush = child.Backflush,
|
|
|
+ Qty = unitQty,
|
|
|
+ Wastage = child.Wastage,
|
|
|
+ Scrap = child.Scrap,
|
|
|
+ NeedCount = needCount,
|
|
|
+ NeedCountNoLoss = RoundQty(needNoLoss),
|
|
|
+ KittingTime = needTime,
|
|
|
+ SatisfyTime = needTime,
|
|
|
+ IsBom = child.IsBom,
|
|
|
+ HaveIcSubs = child.HaveIcSubs,
|
|
|
+ SubstituteCode = child.SubstituteCode,
|
|
|
+ IsUse = isVirtual ? 0 : 1
|
|
|
+ });
|
|
|
|
|
|
seq = nextSeq;
|
|
|
var expandParentFid = isVirtual ? parentFid : fid;
|
|
|
var expandParentNum = num;
|
|
|
var expandNeed = needCount;
|
|
|
|
|
|
- if (child.IsBom == 1 && depth < MaxBomDepth && !string.IsNullOrWhiteSpace(child.ItemNumber))
|
|
|
+ // 仅虚拟件(erp_cls=4)继续向下展开子阶BOM,其他类型作为叶子节点不再下钻
|
|
|
+ if (isVirtual && depth < MaxBomDepth && !string.IsNullOrWhiteSpace(child.ItemNumber))
|
|
|
{
|
|
|
var subBomId = await ResolveBomIdByItemAsync(child.ItemNumber!, entry.TenantId);
|
|
|
if (subBomId is null)
|
|
|
- {
|
|
|
- if (!isVirtual)
|
|
|
- warnings.Add($"物料 {child.ItemNumber} 标记为 BOM 但未找到子阶 BOM,按单层处理");
|
|
|
return seq;
|
|
|
- }
|
|
|
|
|
|
var grandchildren = await LoadBomChildrenAsync(subBomId.Value, entry.TenantId);
|
|
|
if (grandchildren.Count == 0)
|
|
|
- {
|
|
|
- warnings.Add($"物料 {child.ItemNumber} 子阶 BOM 无明细");
|
|
|
return seq;
|
|
|
- }
|
|
|
|
|
|
var childSeq = 1;
|
|
|
foreach (var gc in grandchildren)
|
|
|
@@ -172,7 +164,7 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
depth + 1);
|
|
|
}
|
|
|
}
|
|
|
- else if (child.IsBom == 1 && depth >= MaxBomDepth)
|
|
|
+ else if (depth >= MaxBomDepth)
|
|
|
{
|
|
|
warnings.Add($"物料 {child.ItemNumber} 超过最大 BOM 展开层级 {MaxBomDepth},停止下钻");
|
|
|
}
|
|
|
@@ -252,7 +244,7 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
new SugarParameter("@TenantId", tenantId));
|
|
|
}
|
|
|
|
|
|
- private async Task ApplySupplyAsync(List<ResourceCheckBomLine> lines, long tenantId, long bangId = 0)
|
|
|
+ private async Task ApplySupplyAsync(List<ResourceCheckBomLine> lines, long tenantId, long bangId = 0, long entryId = 0)
|
|
|
{
|
|
|
var stockMap = new Dictionary<string, decimal>(StringComparer.OrdinalIgnoreCase);
|
|
|
var transitMap = new Dictionary<string, decimal>(StringComparer.OrdinalIgnoreCase);
|
|
|
@@ -321,6 +313,18 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
line.MakeQty = line.ErpCls == 1 ? line.LackQty : 0;
|
|
|
line.PurchaseQty = line.ErpCls == 3 ? line.LackQty : 0;
|
|
|
|
|
|
+ // 虚拟件(erp_cls=4)仅展示BOM结构,不参与库存消耗与缺料计算
|
|
|
+ if (line.ErpCls == 4)
|
|
|
+ {
|
|
|
+ line.Sqty = 0;
|
|
|
+ line.LackQty = 0;
|
|
|
+ line.SelfLackQty = 0;
|
|
|
+ line.UseQty = 0;
|
|
|
+ line.MakeQty = 0;
|
|
|
+ line.PurchaseQty = 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
var consumedFromStock = RoundQty(Math.Min(stock, line.Sqty));
|
|
|
stockMap[line.ItemNumber] = RoundQty(Math.Max(0m, stock - consumedFromStock));
|
|
|
var remainingSqty = RoundQty(Math.Max(0m, line.Sqty - consumedFromStock));
|
|
|
@@ -343,10 +347,10 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
"""
|
|
|
INSERT INTO ic_item_stockoccupy (
|
|
|
Id, icitem_number, quantity, morder_mo, bang_id,
|
|
|
- tenant_id, occupy_time, create_time, update_time
|
|
|
+ orderentry_id, tenant_id, occupy_time, create_time, update_time
|
|
|
) VALUES (
|
|
|
@Id, @ItemNumber, @Qty, @WorkOrd, @BangId,
|
|
|
- @TenantId, @Now, @Now, @Now
|
|
|
+ @EntryId, @TenantId, @Now, @Now, @Now
|
|
|
)
|
|
|
""",
|
|
|
new SugarParameter("@Id", YitIdHelper.NextId()),
|
|
|
@@ -354,6 +358,7 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
new SugarParameter("@Qty", stockUsed),
|
|
|
new SugarParameter("@WorkOrd", ""),
|
|
|
new SugarParameter("@BangId", bangId),
|
|
|
+ new SugarParameter("@EntryId", entryId),
|
|
|
new SugarParameter("@TenantId", tenantId),
|
|
|
new SugarParameter("@Now", DateTime.Now));
|
|
|
}
|
|
|
@@ -365,10 +370,10 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
"""
|
|
|
INSERT INTO srm_po_occupy (
|
|
|
Id, ItemNumber, OccupyQty, morder_mo, bang_id,
|
|
|
- tenant_id, create_time, update_time
|
|
|
+ polist_id, entry_id, tenant_id, create_time, update_time
|
|
|
) VALUES (
|
|
|
@Id, @ItemNumber, @Qty, @WorkOrd, @BangId,
|
|
|
- @TenantId, @Now, @Now
|
|
|
+ @PolistId, @EntryId, @TenantId, @Now, @Now
|
|
|
)
|
|
|
""",
|
|
|
new SugarParameter("@Id", YitIdHelper.NextId()),
|
|
|
@@ -376,6 +381,8 @@ public class MaterialRequirementCalculator : ITransient
|
|
|
new SugarParameter("@Qty", transitUsed),
|
|
|
new SugarParameter("@WorkOrd", ""),
|
|
|
new SugarParameter("@BangId", bangId),
|
|
|
+ new SugarParameter("@PolistId", 0),
|
|
|
+ new SugarParameter("@EntryId", entryId),
|
|
|
new SugarParameter("@TenantId", tenantId),
|
|
|
new SugarParameter("@Now", DateTime.Now));
|
|
|
}
|