|
|
@@ -100,20 +100,53 @@ public class ProcurementPipelineService : IDynamicApiController, ITransient
|
|
|
try
|
|
|
{
|
|
|
var newRequests = new List<PurchaseRequestMain>();
|
|
|
+ var deleteVouchers = new List<long>();
|
|
|
if (createFromShortage)
|
|
|
{
|
|
|
- newRequests = await _shortageService.BuildFromResourceCheckAsync(tenantId, account);
|
|
|
- result.ShortageItemCount = newRequests.Count;
|
|
|
+ var candidates = await _shortageService.BuildFromResourceCheckAsync(tenantId, account);
|
|
|
+ result.ShortageItemCount = candidates.Count;
|
|
|
+
|
|
|
+ // 幂等处理:对比已有 PR,更新/删除/保留
|
|
|
+ var (toAdd, toDelete) = await _shortageService.ReconcileAsync(tenantId, candidates, account);
|
|
|
+ newRequests = toAdd;
|
|
|
+ deleteVouchers = toDelete;
|
|
|
+ result.PrUpdatedCount = candidates.Count - toAdd.Count - toDelete.Count;
|
|
|
}
|
|
|
|
|
|
if (newRequests.Count > 0)
|
|
|
{
|
|
|
- var mergeNew = _mergeService.MergeGeneratedRequests(newRequests);
|
|
|
- newRequests = mergeNew.Requests;
|
|
|
- result.PrMergeReducedCount += mergeNew.ReducedCount;
|
|
|
- await AssignPrNumbersAsync(newRequests, account);
|
|
|
- await InsertPurchaseRequestsAsync(newRequests);
|
|
|
- result.PrCreatedCount = newRequests.Count;
|
|
|
+ // 保留各工单独立 PR + 创建合并 PR(数量重新计算)
|
|
|
+ var split = await _mergeService.SplitMergeWithRecalcAsync(newRequests, account);
|
|
|
+
|
|
|
+ var individualPrs = split.IndividualRequests;
|
|
|
+ var mergedPrs = split.MergedRequests;
|
|
|
+
|
|
|
+ // 分配编号
|
|
|
+ await AssignPrNumbersAsync(individualPrs, account);
|
|
|
+ await AssignPrNumbersAsync(mergedPrs, account);
|
|
|
+
|
|
|
+ // 独立 PR: state=5(参考,不参与 DO/PO 转单)
|
|
|
+ foreach (var pr in individualPrs) pr.State = 5;
|
|
|
+ // 关联独立 PR 到合并 PR 编号
|
|
|
+ foreach (var group in individualPrs.GroupBy(x => new { x.IcitemId, x.PrPurchaseId }))
|
|
|
+ {
|
|
|
+ var merged = mergedPrs.FirstOrDefault(m =>
|
|
|
+ m.IcitemId == group.Key.IcitemId && m.PrPurchaseId == group.Key.PrPurchaseId);
|
|
|
+ if (merged is not null)
|
|
|
+ {
|
|
|
+ foreach (var ind in group)
|
|
|
+ ind.IndividualPrReferBillNo = merged.PrBillNo;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 插入独立 PR(参考)
|
|
|
+ await InsertIndividualPurchaseRequestsAsync(individualPrs);
|
|
|
+ // 插入合并 PR(可转单)
|
|
|
+ await InsertPurchaseRequestsAsync(mergedPrs);
|
|
|
+
|
|
|
+ result.PrCreatedCount = mergedPrs.Count;
|
|
|
+ result.IndividualPrCount = individualPrs.Count;
|
|
|
+ result.PrMergeReducedCount = Math.Max(0, individualPrs.Count - mergedPrs.Count);
|
|
|
}
|
|
|
|
|
|
if (mergeHistorical)
|
|
|
@@ -124,6 +157,24 @@ public class ProcurementPipelineService : IDynamicApiController, ITransient
|
|
|
result.PrCreatedCount += historicalMerge.CreatedPrCount;
|
|
|
}
|
|
|
|
|
|
+ // 删除已无缺料的旧 PR
|
|
|
+ if (deleteVouchers.Count > 0)
|
|
|
+ {
|
|
|
+ foreach (var v in deleteVouchers)
|
|
|
+ {
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ UPDATE srm_pr_main
|
|
|
+ SET IsDeleted = 1, update_by_name = @User, update_time = @Now
|
|
|
+ WHERE voucher = @Voucher
|
|
|
+ """,
|
|
|
+ new SugarParameter("@User", account),
|
|
|
+ new SugarParameter("@Now", DateTime.Now),
|
|
|
+ new SugarParameter("@Voucher", v));
|
|
|
+ }
|
|
|
+ result.PrDeletedCount = deleteVouchers.Count;
|
|
|
+ }
|
|
|
+
|
|
|
var pending = await LoadPendingPurchaseRequestsAsync(tenantId);
|
|
|
result.PendingPrCount = pending.Count;
|
|
|
|
|
|
@@ -140,9 +191,6 @@ public class ProcurementPipelineService : IDynamicApiController, ITransient
|
|
|
|
|
|
var pushCandidates = pending
|
|
|
.Where(x => x.IsRequireGoods == 0 && (x.State ?? 0) == 1)
|
|
|
- .Concat(newRequests.Where(x => x.IsRequireGoods == 0))
|
|
|
- .GroupBy(x => x.Id)
|
|
|
- .Select(g => g.First())
|
|
|
.ToList();
|
|
|
|
|
|
if (enableExternalPush && pushCandidates.Count > 0)
|
|
|
@@ -217,16 +265,18 @@ public class ProcurementPipelineService : IDynamicApiController, ITransient
|
|
|
await _db.Ado.ExecuteCommandAsync(
|
|
|
"""
|
|
|
INSERT INTO srm_pr_main
|
|
|
- (Id,pr_billno,pr_purchaseid,pr_purchasenumber,pr_purchasename,pr_purchaser,pr_purchaser_num,
|
|
|
+ (Id,pr_billno,pr_mono,entity_id,pr_purchaseid,pr_purchasenumber,pr_purchasename,pr_purchaser,pr_purchaser_num,
|
|
|
pr_rqty,pr_aqty,pr_sqty,icitem_id,icitem_name,pr_ssend_date,pr_sarrive_date,pr_unit,state,pr_type,currencytype,
|
|
|
create_by_name,create_time,update_by_name,update_time,tenant_id,factory_id,org_id,IsDeleted,company_id,IsRequireGoods,supplier_type)
|
|
|
VALUES
|
|
|
- (@Id,@PrBillNo,@PrPurchaseId,@PrPurchaseNumber,@PrPurchaseName,@PrPurchaser,@PrPurchaserNum,
|
|
|
+ (@Id,@PrBillNo,@PrMono,@EntityId,@PrPurchaseId,@PrPurchaseNumber,@PrPurchaseName,@PrPurchaser,@PrPurchaserNum,
|
|
|
@PrRqty,@PrAqty,@PrSqty,@IcitemId,@IcitemName,@PrSsendDate,@PrSarriveDate,@PrUnit,@State,@PrType,@CurrencyType,
|
|
|
@CreateByName,@CreateTime,@UpdateByName,@UpdateTime,@TenantId,@FactoryId,@OrgId,0,@CompanyId,@IsRequireGoods,@SupplierType)
|
|
|
""",
|
|
|
new SugarParameter("@Id", pr.Id),
|
|
|
new SugarParameter("@PrBillNo", pr.PrBillNo),
|
|
|
+ new SugarParameter("@PrMono", pr.PrMono ?? (object)DBNull.Value),
|
|
|
+ new SugarParameter("@EntityId", pr.EntityId ?? (object)DBNull.Value),
|
|
|
new SugarParameter("@PrPurchaseId", pr.PrPurchaseId),
|
|
|
new SugarParameter("@PrPurchaseNumber", pr.PrPurchaseNumber),
|
|
|
new SugarParameter("@PrPurchaseName", pr.PrPurchaseName),
|
|
|
@@ -256,6 +306,57 @@ public class ProcurementPipelineService : IDynamicApiController, ITransient
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private async Task InsertIndividualPurchaseRequestsAsync(List<PurchaseRequestMain> requests)
|
|
|
+ {
|
|
|
+ foreach (var pr in requests)
|
|
|
+ {
|
|
|
+ await _db.Ado.ExecuteCommandAsync(
|
|
|
+ """
|
|
|
+ INSERT INTO srm_pr_main
|
|
|
+ (Id,pr_billno,pr_mono,entity_id,pr_purchaseid,pr_purchasenumber,pr_purchasename,pr_purchaser,pr_purchaser_num,
|
|
|
+ pr_rqty,pr_aqty,pr_sqty,icitem_id,icitem_name,pr_ssend_date,pr_sarrive_date,pr_unit,state,pr_type,currencytype,
|
|
|
+ create_by_name,create_time,update_by_name,update_time,tenant_id,factory_id,org_id,IsDeleted,company_id,
|
|
|
+ IsRequireGoods,supplier_type,refer_pr_billno)
|
|
|
+ VALUES
|
|
|
+ (@Id,@PrBillNo,@PrMono,@EntityId,@PrPurchaseId,@PrPurchaseNumber,@PrPurchaseName,@PrPurchaser,@PrPurchaserNum,
|
|
|
+ @PrRqty,@PrAqty,@PrSqty,@IcitemId,@IcitemName,@PrSsendDate,@PrSarriveDate,@PrUnit,@State,@PrType,@CurrencyType,
|
|
|
+ @CreateByName,@CreateTime,@UpdateByName,@UpdateTime,@TenantId,@FactoryId,@OrgId,0,@CompanyId,
|
|
|
+ @IsRequireGoods,@SupplierType,@ReferPrBillNo)
|
|
|
+ """,
|
|
|
+ new SugarParameter("@Id", pr.Id),
|
|
|
+ new SugarParameter("@PrBillNo", pr.PrBillNo),
|
|
|
+ new SugarParameter("@PrMono", pr.PrMono ?? (object)DBNull.Value),
|
|
|
+ new SugarParameter("@EntityId", pr.EntityId ?? (object)DBNull.Value),
|
|
|
+ new SugarParameter("@PrPurchaseId", pr.PrPurchaseId),
|
|
|
+ new SugarParameter("@PrPurchaseNumber", pr.PrPurchaseNumber),
|
|
|
+ new SugarParameter("@PrPurchaseName", pr.PrPurchaseName),
|
|
|
+ new SugarParameter("@PrPurchaser", pr.PrPurchaser),
|
|
|
+ new SugarParameter("@PrPurchaserNum", pr.PrPurchaserNum),
|
|
|
+ new SugarParameter("@PrRqty", pr.PrRqty),
|
|
|
+ new SugarParameter("@PrAqty", pr.PrAqty),
|
|
|
+ new SugarParameter("@PrSqty", pr.PrSqty),
|
|
|
+ new SugarParameter("@IcitemId", pr.IcitemId),
|
|
|
+ new SugarParameter("@IcitemName", pr.IcitemName),
|
|
|
+ new SugarParameter("@PrSsendDate", pr.PrSsendDate),
|
|
|
+ new SugarParameter("@PrSarriveDate", pr.PrSarriveDate),
|
|
|
+ new SugarParameter("@PrUnit", pr.PrUnit),
|
|
|
+ new SugarParameter("@State", pr.State ?? 5),
|
|
|
+ new SugarParameter("@PrType", pr.PrType ?? 3),
|
|
|
+ new SugarParameter("@CurrencyType", pr.CurrencyType),
|
|
|
+ new SugarParameter("@CreateByName", pr.CreateByName),
|
|
|
+ new SugarParameter("@CreateTime", pr.CreateTime),
|
|
|
+ new SugarParameter("@UpdateByName", pr.UpdateByName),
|
|
|
+ new SugarParameter("@UpdateTime", pr.UpdateTime),
|
|
|
+ new SugarParameter("@TenantId", pr.TenantId),
|
|
|
+ new SugarParameter("@FactoryId", pr.FactoryId),
|
|
|
+ new SugarParameter("@OrgId", pr.OrgId),
|
|
|
+ new SugarParameter("@CompanyId", pr.CompanyId ?? 1000),
|
|
|
+ new SugarParameter("@IsRequireGoods", pr.IsRequireGoods),
|
|
|
+ new SugarParameter("@SupplierType", pr.SupplierType),
|
|
|
+ new SugarParameter("@ReferPrBillNo", pr.IndividualPrReferBillNo ?? (object)DBNull.Value));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private async Task PersistPrStateUpdatesAsync(IReadOnlyList<long> prIds, int state, string account)
|
|
|
{
|
|
|
if (prIds.Count == 0) return;
|
|
|
@@ -303,7 +404,10 @@ public class ProcurementPipelineService : IDynamicApiController, ITransient
|
|
|
private static string BuildMessage(ProcurementPipelineResult r)
|
|
|
{
|
|
|
var parts = new List<string>();
|
|
|
- if (r.PrCreatedCount > 0) parts.Add($"新建 PR {r.PrCreatedCount}");
|
|
|
+ if (r.IndividualPrCount > 0) parts.Add($"独立 PR {r.IndividualPrCount}");
|
|
|
+ if (r.PrCreatedCount > 0) parts.Add($"合并 PR {r.PrCreatedCount}");
|
|
|
+ if (r.PrUpdatedCount > 0) parts.Add($"更新 PR {r.PrUpdatedCount}");
|
|
|
+ if (r.PrDeletedCount > 0) parts.Add($"删除 PR {r.PrDeletedCount}");
|
|
|
if (r.PoCreatedCount > 0) parts.Add($"转 DO/PO {r.PoCreatedCount}");
|
|
|
if (r.QadTrackingCount > 0) parts.Add($"外部推送 {r.QadTrackingCount}");
|
|
|
return parts.Count > 0 ? string.Join(",", parts) : "无待处理采购申请";
|
|
|
@@ -313,7 +417,10 @@ public class ProcurementPipelineService : IDynamicApiController, ITransient
|
|
|
public sealed class ProcurementPipelineResult
|
|
|
{
|
|
|
public int ShortageItemCount { get; set; }
|
|
|
+ public int IndividualPrCount { get; set; }
|
|
|
public int PrCreatedCount { get; set; }
|
|
|
+ public int PrUpdatedCount { get; set; }
|
|
|
+ public int PrDeletedCount { get; set; }
|
|
|
public int PrMergeReducedCount { get; set; }
|
|
|
public int HistoricalPrMergedGroups { get; set; }
|
|
|
public int PendingPrCount { get; set; }
|