namespace Admin.NET.Plugin.AiDOP.Supply; /// /// 采购申请转 DO/PO 服务。P4 初版仅处理本次生成的要货令 PR,不扫描历史 PR。 /// public class PurchaseOrderTransferService : ITransient { private readonly ISqlSugarClient _db; private readonly NumberRuleService _numberRuleService; private readonly UserManager _userManager; public PurchaseOrderTransferService(ISqlSugarClient db, NumberRuleService numberRuleService, UserManager userManager) { _db = db; _numberRuleService = numberRuleService; _userManager = userManager; } public async Task TransferGeneratedRequireGoodsAsync(List requests, string account) { var candidates = requests.Where(x => x.IsRequireGoods == 1).ToList(); if (candidates.Count == 0) return new PurchaseOrderTransferResult(); var createdOrders = new List(); var transferredPrIds = new List(); var groups = candidates .GroupBy(x => new { x.TenantId, x.CompanyId, x.FactoryId, x.PrPurchaseId, x.PrPurchaseNumber, x.PrPurchaseName, SupplierType = x.SupplierType ?? string.Empty }) .ToList(); foreach (var tenantGroup in groups.GroupBy(x => x.Key.TenantId.ToString())) { var groupList = tenantGroup.ToList(); var purOrdNumbers = await _numberRuleService.NextBatchInCurrentTransactionAsync("DO", tenantGroup.Key, groupList.Count, account); if (purOrdNumbers.Count < groupList.Count || purOrdNumbers.Any(string.IsNullOrWhiteSpace)) throw Oops.Oh($"当前DO/PO单号生成失败,请检查DO编号规则维护。Domain={tenantGroup.Key}"); for (var i = 0; i < groupList.Count; i++) { var group = groupList[i]; var purOrd = purOrdNumbers[i].Trim(); var now = DateTime.Now; var supplierType = group.Key.SupplierType; var isOutsource = string.Equals(supplierType, "委外", StringComparison.OrdinalIgnoreCase); var poType = isOutsource ? "PW" : "po"; var usage = isOutsource ? "委外加工" : supplierType; var buyer = ResolveBuyer(supplierType); var rows = group.OrderBy(x => x.PrSarriveDate).ThenBy(x => x.PrBillNo).ToList(); await InsertPurchaseOrderMasterAsync(purOrd, poType, usage, buyer, group.Key.PrPurchaseNumber, now, account, group.Key.TenantId); var masterId = await _db.Ado.GetIntAsync( "SELECT IFNULL(MAX(RecID),0) FROM PurOrdMaster WHERE PurOrd=@PurOrd", new SugarParameter("@PurOrd", purOrd)); if (masterId <= 0) throw Oops.Oh("DO/PO主表生成失败。"); for (var lineIndex = 0; lineIndex < rows.Count; lineIndex++) { await InsertPurchaseOrderDetailAsync(purOrd, poType, masterId, lineIndex + 1, rows[lineIndex], account); rows[lineIndex].State = 4; rows[lineIndex].UpdateByName = account; rows[lineIndex].UpdateTime = now; transferredPrIds.Add(rows[lineIndex].Id); } createdOrders.Add(purOrd); } } return new PurchaseOrderTransferResult { CreatedOrderCount = createdOrders.Count, TransferredPrCount = transferredPrIds.Count, CreatedOrders = createdOrders, TransferredPrIds = transferredPrIds }; } private async Task InsertPurchaseOrderMasterAsync( string purOrd, string poType, string? usage, string buyer, string? supplierCode, DateTime now, string account, long tenantId) { await _db.Ado.ExecuteCommandAsync( """ INSERT INTO PurOrdMaster ( Confirming, CreditTermsInt, Disc, ExchRate, EstVal, ExchRate1, ExchRate2, FixedPrice, FixedRate, Frt, PartialOK, AmtPrepaid, PrintPO, PST, Recurr, `Release`, Revision, Scheduled, ServiceCharge, SpecialCharge, Taxable, Tax1, Tax2, Tax3, TransportDays, IsActive, IsConfirm, Potype, IsChanged, TaxIn, Amt, IsPriceChanged, Buyer, Domain, PurOrd, OrdDate, ReqBy, Status, Supp, CreateUser, CreateTime, UpdateUser, UpdateTime, `Usage`, FSTID, Typed, tenant_id ) VALUES ( 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, @PoType, 0, 1, 0, 0, @Buyer, @Domain, @PurOrd, @Now, 'DO', '', @Supp, @CreateUser, @Now, @UpdateUser, @Now, @Usage, @FSTID, @Typed, @TenantId ) """, new SugarParameter("@PoType", poType), new SugarParameter("@Buyer", buyer), new SugarParameter("@Domain", tenantId.ToString()), new SugarParameter("@PurOrd", purOrd), new SugarParameter("@Now", now), new SugarParameter("@Supp", supplierCode), new SugarParameter("@CreateUser", account), new SugarParameter("@UpdateUser", account), new SugarParameter("@Usage", usage), new SugarParameter("@FSTID", string.Equals(usage, "VMI", StringComparison.OrdinalIgnoreCase) ? "3" : string.Empty), new SugarParameter("@Typed", poType == "PW" ? "s" : string.Empty), new SugarParameter("@TenantId", tenantId <= 0 ? null : tenantId)); } private async Task InsertPurchaseOrderDetailAsync(string purOrd, string poType, int masterId, int line, PurchaseRequestMain pr, string account) { var item = (await _db.Ado.SqlQueryAsync( """ SELECT COALESCE(NULLIF(im.ItemNum,''), ic.number) AS ItemNum, COALESCE(NULLIF(im.Descr,''), ic.name) AS Descr, COALESCE(NULLIF(im.UM,''), ic.unit) AS UM, IFNULL(im.Location, '') AS Location, IFNULL(im.Rev, '') AS Rev, IFNULL(im.Drawing, '') AS Drawing FROM ic_item ic LEFT JOIN ItemMaster im ON ic.number = im.ItemNum WHERE ic.Id = @IcitemId LIMIT 1 """, new SugarParameter("@IcitemId", pr.IcitemId))).FirstOrDefault(); await _db.Ado.ExecuteCommandAsync( """ INSERT INTO PurOrdDetail ( QtyBO, RctCost, CreditTermsInt, UpdateCurrentCost, CumReceived1, CumReceived2, CumReceived3, CumReceived4, Disc, FixedPrice, InspectReq, SingleLot, SupplyPer, PurOrd, PST, PackingSlipQty, PayUMConv, PurCost, RctQty, QtyOrded, QtyReceived, QtyReturned, Active, QtyReleased, RctUMConversion, Scheduled, ScheduledChanged, SchedMRPReq, SafetyDays, SafetyHours, StdCost, Taxable, TaxIn, MaxTaxableAmt, TransportHours, UMConversion, VAT, IsActive, IsConfirm, Potype, IsChanged, TaxRate, IsRounding, ReceiptQty, BarCodeQty, IsClosed, QtyReturnedRefund, CumQtyBO, Line, ItemNum, Descr, UM, Rev, Drawing, Location, DueDate, NeedDate, LotSerial, PurOrdRecID, Status, Req, CreateUser, CreateTime, UpdateUser, UpdateTime, tenant_id ) VALUES ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @PurOrd, 0, 0, 1, 0, 0, @QtyOrded, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, @PoType, 0, 0, 0, 0, 0, 0, 0, 0, @Line, @ItemNum, @Descr, @UM, @Rev, @Drawing, @Location, @DueDate, @NeedDate, '', @PurOrdRecID, 'R', @Req, @CreateUser, @Now, @UpdateUser, @Now, @TenantId ) """, new SugarParameter("@PurOrd", purOrd), new SugarParameter("@PoType", poType), new SugarParameter("@QtyOrded", pr.PrAqty ?? pr.PrSqty ?? pr.PrRqty ?? 0), new SugarParameter("@Line", line), new SugarParameter("@ItemNum", item?.ItemNum ?? pr.IcitemName ?? string.Empty), new SugarParameter("@Descr", item?.Descr ?? pr.IcitemName ?? string.Empty), new SugarParameter("@UM", item?.UM ?? pr.PrUnit ?? string.Empty), new SugarParameter("@Rev", item?.Rev ?? string.Empty), new SugarParameter("@Drawing", item?.Drawing ?? string.Empty), new SugarParameter("@Location", string.IsNullOrWhiteSpace(item?.Location) ? "1001" : item.Location), new SugarParameter("@DueDate", pr.PrSarriveDate), new SugarParameter("@NeedDate", pr.PrSarriveDate), new SugarParameter("@PurOrdRecID", masterId), new SugarParameter("@Req", pr.PrBillNo), new SugarParameter("@CreateUser", account), new SugarParameter("@UpdateUser", account), new SugarParameter("@Now", DateTime.Now), new SugarParameter("@TenantId", pr.TenantId <= 0 ? null : pr.TenantId)); } private static string ResolveBuyer(string? supplierType) { return supplierType switch { "研发" => "130", "ECR" => "170", _ => "110" }; } private sealed class ItemLookupRow { public string? ItemNum { get; set; } public string? Descr { get; set; } public string? UM { get; set; } public string? Location { get; set; } public string? Rev { get; set; } public string? Drawing { get; set; } } } public sealed class PurchaseOrderTransferResult { public int CreatedOrderCount { get; set; } public int TransferredPrCount { get; set; } public List CreatedOrders { get; set; } = new(); public List TransferredPrIds { get; set; } = new(); }