@chenchaolong/plugin-trade-compliance-workbench 1.0.161 → 1.0.163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -1
  3. package/dist/lib/remote-components/trade-compliance-workbench/app.js +5 -5
  4. package/dist/lib/remote-ui/app-source.js +1 -1
  5. package/dist/lib/remote-ui/app-source.js.map +1 -1
  6. package/dist/lib/remote-ui/pages/analytics.d.ts.map +1 -1
  7. package/dist/lib/remote-ui/pages/analytics.js +1 -1
  8. package/dist/lib/remote-ui/pages/analytics.js.map +1 -1
  9. package/dist/lib/remote-ui/pages/files.d.ts.map +1 -1
  10. package/dist/lib/remote-ui/pages/files.js +2 -1
  11. package/dist/lib/remote-ui/pages/files.js.map +1 -1
  12. package/dist/lib/remote-ui/pages/home.js +1 -1
  13. package/dist/lib/remote-ui/pages/home.js.map +1 -1
  14. package/dist/lib/remote-ui/pages/sales.d.ts.map +1 -1
  15. package/dist/lib/remote-ui/pages/sales.js +5 -4
  16. package/dist/lib/remote-ui/pages/sales.js.map +1 -1
  17. package/dist/lib/remote-ui/sales-contracts.d.ts +5 -0
  18. package/dist/lib/remote-ui/sales-contracts.d.ts.map +1 -1
  19. package/dist/lib/remote-ui/sales-contracts.js +8 -1
  20. package/dist/lib/remote-ui/sales-contracts.js.map +1 -1
  21. package/dist/lib/remote-ui/styles.css +53 -13
  22. package/dist/lib/sales-file.service.d.ts.map +1 -1
  23. package/dist/lib/sales-file.service.js +10 -7
  24. package/dist/lib/sales-file.service.js.map +1 -1
  25. package/dist/lib/types.d.ts +3 -0
  26. package/dist/lib/types.d.ts.map +1 -1
  27. package/dist/lib/types.js.map +1 -1
  28. package/dist/lib/workbench-view.provider.d.ts +4 -0
  29. package/dist/lib/workbench-view.provider.d.ts.map +1 -1
  30. package/dist/lib/workbench.service.d.ts +11 -0
  31. package/dist/lib/workbench.service.d.ts.map +1 -1
  32. package/dist/lib/workbench.service.js +115 -49
  33. package/dist/lib/workbench.service.js.map +1 -1
  34. package/package.json +1 -1
@@ -2925,7 +2925,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
2925
2925
  async listSalesLines(scope, query) {
2926
2926
  assertOptionalEnum(query.parameters?.complianceCategory, CatalogTypes, '合规分类');
2927
2927
  const rows = await this.salesOrderDtos(scope, await this.listDerivedSalesOrders(scope, query.customerId));
2928
- const order = rows.find(item => item.customerContractId === query.customerContractId && item.purchaseOrderId === query.purchaseOrderId);
2928
+ const order = rows.find(item => item.customerContractId === query.customerContractId);
2929
2929
  if (!order)
2930
2930
  throw new DomainError(DomainErrorCodes.NOT_FOUND, '销售订单不存在');
2931
2931
  const parameters = query.parameters ?? {};
@@ -3438,15 +3438,18 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
3438
3438
  if (!identities.length)
3439
3439
  return [];
3440
3440
  const contractIds = [...new Set(identities.map(identity => identity.customerContractId))];
3441
- const purchaseIds = [...new Set(identities.map(identity => identity.purchaseOrderId))];
3442
- const selectedKeys = new Set(identities.map(identity => derivedOrderKey(identity.customerContractId, identity.purchaseOrderId)));
3443
- const [customers, contracts, purchases, manuals, lines] = await Promise.all([
3441
+ const selectedContracts = new Set(identities.map(identity => identity.customerContractId));
3442
+ const [customers, contracts, purchases, lines] = await Promise.all([
3444
3443
  this.dataSource.getRepository(Customer).find({ where: { ...activeScope(scope), ...(customerId ? { id: customerId } : {}), deletedAt: IsNull() } }),
3445
3444
  this.dataSource.getRepository(CustomerContract).find({ where: { ...activeScope(scope), id: In(contractIds), ...(customerId ? { customerId } : {}), deletedAt: IsNull() } }),
3446
- this.dataSource.getRepository(PurchaseOrder).find({ where: { ...activeScope(scope), id: In(purchaseIds), deletedAt: IsNull() } }),
3447
- this.dataSource.getRepository(DerivedSalesOrderManualData).find({ where: { ...activeScope(scope), customerContractId: In(contractIds), purchaseOrderId: In(purchaseIds), deletedAt: IsNull() } }),
3448
- this.dataSource.getRepository(PurchaseOrderLine).find({ where: { ...activeScope(scope), purchaseOrderId: In(purchaseIds), deletedAt: IsNull() }, order: { lineNumber: 'ASC' } })
3445
+ this.dataSource.getRepository(PurchaseOrder).find({ where: { ...activeScope(scope), deletedAt: IsNull() }, order: { id: 'ASC' } }),
3446
+ this.dataSource.getRepository(PurchaseOrderLine).find({ where: { ...activeScope(scope), deletedAt: IsNull() }, order: { purchaseOrderId: 'ASC', lineNumber: 'ASC' } })
3449
3447
  ]);
3448
+ const associatedPurchaseIds = purchases.filter(purchase => {
3449
+ const baseCode = parsePurchaseContractNo(purchase.contractNo).baseCode;
3450
+ return contracts.some(contract => selectedContracts.has(contract.id) && parseCustomerContractNo(contract.contractNo).baseCode === baseCode);
3451
+ }).map(purchase => purchase.id);
3452
+ const manuals = associatedPurchaseIds.length ? await this.dataSource.getRepository(DerivedSalesOrderManualData).find({ where: { ...activeScope(scope), customerContractId: In(contractIds), purchaseOrderId: In(associatedPurchaseIds), deletedAt: IsNull() } }) : [];
3450
3453
  const customerMap = new Map(customers.map(customer => [customer.id, customer]));
3451
3454
  const manualMap = new Map(manuals.map(manual => [derivedOrderKey(manual.customerContractId, manual.purchaseOrderId), manual]));
3452
3455
  const linesByPurchase = groupBy(lines, line => line.purchaseOrderId);
@@ -3463,7 +3466,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
3463
3466
  continue;
3464
3467
  const baseCode = parseCustomerContractNo(contract.contractNo).baseCode;
3465
3468
  for (const purchase of purchasesByBaseCode.get(baseCode) ?? []) {
3466
- if (!selectedKeys.has(derivedOrderKey(contract.id, purchase.id)))
3469
+ if (!selectedContracts.has(contract.id))
3467
3470
  continue;
3468
3471
  const manual = manualMap.get(derivedOrderKey(contract.id, purchase.id));
3469
3472
  const effectiveRate = manual?.pricingProfitRateOverride ?? (customer.pricingProfitRateStatus === 'CONFIGURED' ? customer.defaultPricingProfitRate : null);
@@ -3492,10 +3495,28 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
3492
3495
  const pricedLines = derivedLines.filter(line => line.pricingStatus === 'PRICED' && line.salesLineAmountCny != null);
3493
3496
  const amount = manual?.orderAmountCnyOverride != null ? new Decimal(manual.orderAmountCnyOverride) : pricedLines.reduce((sum, line) => sum.plus(line.salesLineAmountCny ?? 0), new Decimal(0));
3494
3497
  const received = new Decimal(manual?.receivedAmountCny ?? 0);
3495
- results.push({ customerContractId: contract.id, purchaseOrderId: purchase.id, customerId: customer.id, salesOrderNo: parsePurchaseContractNo(purchase.contractNo).salesOrderNo, customerContractNo: contract.contractNo, purchaseContractNo: purchase.contractNo, orderDate: purchase.orderDate, currencyCode: 'CNY', pricingProfitRateSource: orderPricingSource(derivedLines, manual?.pricingProfitRateOverride, customer.pricingProfitRateStatus === 'CONFIGURED' ? customer.defaultPricingProfitRate : null), effectivePricingProfitRate: effectiveRate, pricingStatus: orderPricingStatus(derivedLines), missingPricingFields: orderMissingPricingFields(derivedLines), orderAmountCny: amount.toFixed(manual?.orderAmountCnyOverride != null ? 4 : 0), receivedAmountCny: received.toFixed(2), unreceivedAmountCny: Decimal.max(amount.minus(received), 0).toFixed(2), lines: derivedLines });
3498
+ results.push({ customerContractId: contract.id, purchaseOrderId: purchase.id, purchaseOrderIds: [purchase.id], purchaseContractNos: [purchase.contractNo], customerId: customer.id, salesOrderNo: contract.contractNo, customerContractNo: contract.contractNo, purchaseContractNo: purchase.contractNo, orderDate: purchase.orderDate, currencyCode: 'CNY', pricingProfitRateSource: orderPricingSource(derivedLines, manual?.pricingProfitRateOverride, customer.pricingProfitRateStatus === 'CONFIGURED' ? customer.defaultPricingProfitRate : null), effectivePricingProfitRate: effectiveRate, pricingStatus: orderPricingStatus(derivedLines), missingPricingFields: orderMissingPricingFields(derivedLines), orderAmountCny: amount.toFixed(manual?.orderAmountCnyOverride != null ? 4 : 0), receivedAmountCny: received.toFixed(2), unreceivedAmountCny: Decimal.max(amount.minus(received), 0).toFixed(2), lines: derivedLines });
3496
3499
  }
3497
3500
  }
3498
- return results.sort(compareDerivedSalesOrders);
3501
+ const grouped = new Map();
3502
+ for (const item of results.sort(compareDerivedSalesOrders)) {
3503
+ const existing = grouped.get(item.customerContractId);
3504
+ if (!existing) {
3505
+ grouped.set(item.customerContractId, item);
3506
+ continue;
3507
+ }
3508
+ existing.purchaseOrderIds = [...(existing.purchaseOrderIds ?? [existing.purchaseOrderId]), item.purchaseOrderId];
3509
+ existing.purchaseContractNos = [...new Set([...(existing.purchaseContractNos ?? [existing.purchaseContractNo]), item.purchaseContractNo])];
3510
+ existing.lines.push(...item.lines);
3511
+ existing.orderAmountCny = new Decimal(existing.orderAmountCny).plus(item.orderAmountCny).toFixed(0);
3512
+ existing.receivedAmountCny = new Decimal(existing.receivedAmountCny).plus(item.receivedAmountCny).toFixed(2);
3513
+ existing.unreceivedAmountCny = Decimal.max(new Decimal(existing.orderAmountCny).minus(existing.receivedAmountCny), 0).toFixed(2);
3514
+ if (item.orderDate && (!existing.orderDate || item.orderDate < existing.orderDate))
3515
+ existing.orderDate = item.orderDate;
3516
+ existing.pricingStatus = existing.pricingStatus === 'UNPRICED' || item.pricingStatus === 'UNPRICED' ? 'UNPRICED' : existing.pricingStatus;
3517
+ existing.missingPricingFields = [...new Set([...existing.missingPricingFields, ...item.missingPricingFields])];
3518
+ }
3519
+ return [...grouped.values()].sort(compareDerivedSalesOrders);
3499
3520
  }
3500
3521
  derivedSalesIdentityQuery(scope, customerId, parameters, includeParties) {
3501
3522
  const qb = this.dataSource.getRepository(CustomerContract).createQueryBuilder('contract')
@@ -3509,7 +3530,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
3509
3530
  qb.andWhere('customer.id = :customerId', { customerId });
3510
3531
  const fuzzy = (expression, key) => { if (hasQueryValue(parameters[key]))
3511
3532
  qb.andWhere(`${expression} ILIKE :${key}`, { [key]: `%${String(parameters[key]).trim()}%` }); };
3512
- fuzzy(`regexp_replace(purchase."contractNo", 'C$', 'X')`, 'salesOrderNo');
3533
+ fuzzy('contract."contractNo"', 'salesOrderNo');
3513
3534
  fuzzy('contract."contractNo"', 'customerContractNo');
3514
3535
  fuzzy('purchase."contractNo"', 'purchaseContractNo');
3515
3536
  if (includeParties) {
@@ -3532,21 +3553,30 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
3532
3553
  return qb;
3533
3554
  }
3534
3555
  async selectAllDerivedSalesOrderIdentities(scope, customerId, parameters, includeParties) {
3535
- return this.derivedSalesIdentityQuery(scope, customerId, parameters, includeParties).orderBy('contract.id', 'ASC').addOrderBy('purchase.id', 'ASC').getRawMany();
3556
+ const rows = await this.derivedSalesIdentityQuery(scope, customerId, parameters, includeParties).orderBy('contract.id', 'ASC').addOrderBy('purchase.id', 'ASC').getRawMany();
3557
+ const seen = new Set();
3558
+ return rows.filter(row => { if (seen.has(row.customerContractId))
3559
+ return false; seen.add(row.customerContractId); return true; });
3536
3560
  }
3537
3561
  async selectDerivedSalesOrderIdentities(scope, customerId, query, includeParties) {
3538
3562
  const pageSize = Math.min(200, Math.max(1, Number(query.pageSize ?? 20)));
3539
3563
  const identityQuery = this.derivedSalesIdentityQuery(scope, customerId, query.parameters ?? {}, includeParties);
3540
3564
  const identitySql = identityQuery.getQuery();
3541
3565
  const identityParameters = identityQuery.getParameters();
3542
- const countRow = await this.dataSource.createQueryBuilder().select('COUNT(*)', 'total').from(`(${identitySql})`, 'identity').setParameters(identityParameters).getRawOne();
3566
+ // 一个客户合同聚合为一条销售订单:按合同去重计数与分页,代表采购单取组内最小 id。
3567
+ const countRow = await this.dataSource.createQueryBuilder()
3568
+ .select('COUNT(*)', 'total')
3569
+ .from(`(SELECT DISTINCT identity."customerContractId" FROM (${identitySql}) identity)`, 'distinct_identity')
3570
+ .setParameters(identityParameters)
3571
+ .getRawOne();
3543
3572
  const total = Number(countRow?.total ?? 0);
3544
3573
  const pageCount = Math.max(1, Math.ceil(total / pageSize));
3545
3574
  const page = Math.min(pageCount, Math.max(1, Number(query.page ?? 1)));
3546
3575
  const items = await this.dataSource.createQueryBuilder()
3547
- .select('identity."customerContractId"', 'customerContractId').addSelect('identity."purchaseOrderId"', 'purchaseOrderId')
3576
+ .select('identity."customerContractId"', 'customerContractId').addSelect('MIN(identity."purchaseOrderId")', 'purchaseOrderId')
3548
3577
  .from(`(${identitySql})`, 'identity').setParameters(identityParameters)
3549
- .orderBy('identity."customerContractId"', 'ASC').addOrderBy('identity."purchaseOrderId"', 'ASC')
3578
+ .groupBy('identity."customerContractId"')
3579
+ .orderBy('identity."customerContractId"', 'ASC')
3550
3580
  .offset((page - 1) * pageSize).limit(pageSize).getRawMany();
3551
3581
  return { items: items, total, page, pageSize };
3552
3582
  }
@@ -3591,44 +3621,78 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
3591
3621
  await this.recalculateCompanyRisks(scope, 'SOURCE_CHANGED');
3592
3622
  return saved;
3593
3623
  }
3624
+ /**
3625
+ * 一个客户合同聚合一笔销售订单后,订单级人工数据(利润率、总额、已收款)按“组”写入:
3626
+ * 利润率覆盖值同步到组内全部采购单;总额/已收款写在代表采购单上、其余清零,保证聚合展示值与录入值一致;
3627
+ * 商品行定价按行自身归属的采购单路由到对应人工数据行。
3628
+ */
3594
3629
  async saveDerivedManualData(scope, customerContractId, purchaseOrderId, input) {
3595
3630
  return this.dataSource.transaction(async (manager) => {
3596
3631
  const contract = await required(manager.getRepository(CustomerContract).findOne({ where: { ...activeScope(scope), id: customerContractId, deletedAt: IsNull() } }), '客户合同不存在');
3597
3632
  const customer = await required(manager.getRepository(Customer).findOne({ where: { ...activeScope(scope), id: contract.customerId, deletedAt: IsNull() }, lock: { mode: 'pessimistic_write' } }), '客户不存在');
3598
- const purchase = await required(manager.getRepository(PurchaseOrder).findOne({ where: { ...activeScope(scope), id: purchaseOrderId, deletedAt: IsNull() }, lock: { mode: 'pessimistic_write' } }), '采购订单不存在');
3599
- if (parseCustomerContractNo(contract.contractNo).baseCode !== parsePurchaseContractNo(purchase.contractNo).baseCode)
3633
+ const representative = await required(manager.getRepository(PurchaseOrder).findOne({ where: { ...activeScope(scope), id: purchaseOrderId, deletedAt: IsNull() }, lock: { mode: 'pessimistic_write' } }), '采购订单不存在');
3634
+ const contractBaseCode = parseCustomerContractNo(contract.contractNo).baseCode;
3635
+ if (parsePurchaseContractNo(representative.contractNo).baseCode !== contractBaseCode)
3600
3636
  throw new DomainError(DomainErrorCodes.DERIVED_LINK_CONFLICT, '客户合同与采购订单未形成派生销售订单');
3601
- const lines = await manager.getRepository(PurchaseOrderLine).find({ where: { ...activeScope(scope), purchaseOrderId, deletedAt: IsNull() }, order: { id: 'ASC' }, lock: { mode: 'pessimistic_write' } });
3637
+ const groupPurchases = (await manager.getRepository(PurchaseOrder).find({ where: { ...activeScope(scope), deletedAt: IsNull() } }))
3638
+ .filter(purchase => parsePurchaseContractNo(purchase.contractNo).baseCode === contractBaseCode)
3639
+ .sort((left, right) => (left.id === purchaseOrderId ? -1 : right.id === purchaseOrderId ? 1 : left.id.localeCompare(right.id)));
3640
+ const lines = await manager.getRepository(PurchaseOrderLine).find({ where: { ...activeScope(scope), purchaseOrderId: In(groupPurchases.map(purchase => purchase.id)), deletedAt: IsNull() }, order: { id: 'ASC' }, lock: { mode: 'pessimistic_write' } });
3641
+ const linesByPurchase = groupBy(lines, line => line.purchaseOrderId);
3602
3642
  const repo = manager.getRepository(DerivedSalesOrderManualData);
3603
- const where = { ...activeScope(scope), customerContractId, purchaseOrderId, deletedAt: IsNull() };
3604
- let row = await repo.findOne({ where, lock: { mode: 'pessimistic_write' } });
3605
- if (!row) {
3606
- await repo.createQueryBuilder().insert().into(DerivedSalesOrderManualData).values({ ...auditScope(scope), customerContractId, purchaseOrderId, pricingProfitRateOverride: null, orderAmountCnyOverride: null, linePricingOverrides: null, receivedAmountCny: '0' }).orIgnore().execute();
3607
- row = await required(repo.findOne({ where, lock: { mode: 'pessimistic_write' } }), '销售订单人工维护数据创建失败');
3608
- }
3609
- const override = input.pricingProfitRateOverride === undefined ? row.pricingProfitRateOverride : normalizeOptionalDecimalInput(input.pricingProfitRateOverride);
3610
- if (override != null && new Decimal(override).isNegative())
3611
- throw new DomainError(DomainErrorCodes.INVALID_INPUT, '订单利润率不能小于 0');
3612
- const orderAmountCnyOverride = input.orderAmountCnyOverride === undefined ? row.orderAmountCnyOverride : normalizeOptionalDecimalInput(input.orderAmountCnyOverride);
3613
- if (orderAmountCnyOverride != null && new Decimal(orderAmountCnyOverride).isNegative())
3614
- throw new DomainError(DomainErrorCodes.INVALID_INPUT, '销售订单总额不能小于 0');
3615
- const nextLineOverrides = input.linePricingOverrides === undefined
3616
- ? row.linePricingOverrides
3617
- : normalizeLinePricingOverrides({ ...linePricingOverrides(row.linePricingOverrides), ...linePricingOverrides(input.linePricingOverrides) });
3618
- const effectiveRate = override ?? (customer.pricingProfitRateStatus === 'CONFIGURED' ? customer.defaultPricingProfitRate : null);
3619
- const received = input.receivedAmountCny ?? row.receivedAmountCny;
3620
- const finalAmount = orderAmountCnyOverride ?? deriveOrderPricingAmount(lines, nextLineOverrides, effectiveRate);
3621
- assertReceivedAmount(finalAmount, received);
3622
- if (input.pricingProfitRateOverride !== undefined)
3623
- row.pricingProfitRateOverride = override;
3624
- if (input.orderAmountCnyOverride !== undefined)
3625
- row.orderAmountCnyOverride = orderAmountCnyOverride;
3626
- if (input.linePricingOverrides !== undefined)
3627
- row.linePricingOverrides = nextLineOverrides;
3628
- if (input.receivedAmountCny !== undefined)
3629
- row.receivedAmountCny = input.receivedAmountCny;
3630
- row.updatedById = scope.userId ?? null;
3631
- return repo.save(row);
3643
+ const rows = new Map();
3644
+ for (const purchaseId of groupPurchases.map(purchase => purchase.id)) {
3645
+ const where = { ...activeScope(scope), customerContractId, purchaseOrderId: purchaseId, deletedAt: IsNull() };
3646
+ let row = await repo.findOne({ where, lock: { mode: 'pessimistic_write' } });
3647
+ if (!row) {
3648
+ await repo.createQueryBuilder().insert().into(DerivedSalesOrderManualData).values({ ...auditScope(scope), customerContractId, purchaseOrderId: purchaseId, pricingProfitRateOverride: null, orderAmountCnyOverride: null, linePricingOverrides: null, receivedAmountCny: '0' }).orIgnore().execute();
3649
+ row = await required(repo.findOne({ where, lock: { mode: 'pessimistic_write' } }), '销售订单人工维护数据创建失败');
3650
+ }
3651
+ rows.set(purchaseId, row);
3652
+ }
3653
+ if (input.pricingProfitRateOverride !== undefined) {
3654
+ const override = normalizeOptionalDecimalInput(input.pricingProfitRateOverride);
3655
+ if (override != null && new Decimal(override).isNegative())
3656
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '订单利润率不能小于 0');
3657
+ for (const row of rows.values())
3658
+ row.pricingProfitRateOverride = override;
3659
+ }
3660
+ if (input.orderAmountCnyOverride !== undefined) {
3661
+ const override = normalizeOptionalDecimalInput(input.orderAmountCnyOverride);
3662
+ if (override != null && new Decimal(override).isNegative())
3663
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '销售订单总额不能小于 0');
3664
+ for (const [purchaseId, row] of rows)
3665
+ row.orderAmountCnyOverride = override == null ? null : purchaseId === purchaseOrderId ? override : '0';
3666
+ }
3667
+ if (input.linePricingOverrides !== undefined) {
3668
+ const incoming = linePricingOverrides(input.linePricingOverrides);
3669
+ const incomingByPurchase = new Map();
3670
+ for (const [lineId, value] of Object.entries(incoming)) {
3671
+ const ownerPurchaseId = lines.find(line => line.id === lineId)?.purchaseOrderId ?? purchaseOrderId;
3672
+ incomingByPurchase.set(ownerPurchaseId, { ...(incomingByPurchase.get(ownerPurchaseId) ?? {}), [lineId]: value });
3673
+ }
3674
+ for (const [purchaseId, row] of rows) {
3675
+ const next = incomingByPurchase.get(purchaseId);
3676
+ if (!next)
3677
+ continue;
3678
+ row.linePricingOverrides = normalizeLinePricingOverrides({ ...linePricingOverrides(row.linePricingOverrides), ...next });
3679
+ }
3680
+ }
3681
+ if (input.receivedAmountCny !== undefined) {
3682
+ for (const [purchaseId, row] of rows)
3683
+ row.receivedAmountCny = purchaseId === purchaseOrderId ? input.receivedAmountCny : '0';
3684
+ }
3685
+ const groupFinalAmount = [...rows.values()].reduce((total, row) => {
3686
+ const effectiveRate = row.pricingProfitRateOverride ?? (customer.pricingProfitRateStatus === 'CONFIGURED' ? customer.defaultPricingProfitRate : null);
3687
+ const purchaseFinal = row.orderAmountCnyOverride != null ? new Decimal(row.orderAmountCnyOverride) : new Decimal(deriveOrderPricingAmount(linesByPurchase.get(row.purchaseOrderId) ?? [], row.linePricingOverrides, effectiveRate));
3688
+ return total.plus(purchaseFinal);
3689
+ }, new Decimal(0));
3690
+ const groupReceived = [...rows.values()].reduce((total, row) => total.plus(row.receivedAmountCny ?? 0), new Decimal(0));
3691
+ assertReceivedAmount(groupFinalAmount, groupReceived);
3692
+ for (const row of rows.values())
3693
+ row.updatedById = scope.userId ?? null;
3694
+ const saved = await repo.save([...rows.values()]);
3695
+ return saved.find(row => row.purchaseOrderId === purchaseOrderId) ?? saved[0];
3632
3696
  });
3633
3697
  }
3634
3698
  async softDelete(scope, entityType, id) {
@@ -4319,7 +4383,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
4319
4383
  async salesOrderDtos(scope, orders) {
4320
4384
  if (!orders.length)
4321
4385
  return [];
4322
- const purchaseIds = [...new Set(orders.map(item => item.purchaseOrderId))];
4386
+ const purchaseIds = [...new Set(orders.flatMap(item => item.purchaseOrderIds?.length ? item.purchaseOrderIds : [item.purchaseOrderId]))];
4323
4387
  const [customers, purchases] = await Promise.all([
4324
4388
  this.dataSource.getRepository(Customer).find({ where: { ...activeScope(scope), id: In([...new Set(orders.map(item => item.customerId))]), deletedAt: IsNull() } }),
4325
4389
  this.dataSource.getRepository(PurchaseOrder).find({ where: { ...activeScope(scope), id: In(purchaseIds), deletedAt: IsNull() } })
@@ -4333,7 +4397,9 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
4333
4397
  const received = new Decimal(order.receivedAmountCny);
4334
4398
  const total = new Decimal(order.orderAmountCny);
4335
4399
  const paymentStatus = received.lte(0) ? 'UNPAID' : received.gte(total) ? 'PAID' : 'PARTIAL';
4336
- return { ...order, customerName: customerNames.get(order.customerId) ?? '', supplierName: supplierNames.get(purchaseSuppliers.get(order.purchaseOrderId) ?? '') ?? '', pricingProfitRateStatus: order.pricingStatus === 'UNPRICED' ? 'MISSING' : 'CONFIGURED', paymentStatus, lines: order.lines.map(line => ({ ...line, complianceCategories: [...new Set(line.complianceMatches.map(match => match.catalogType))] })) };
4400
+ const orderPurchaseIds = order.purchaseOrderIds?.length ? order.purchaseOrderIds : [order.purchaseOrderId];
4401
+ const orderSupplierNames = [...new Set(orderPurchaseIds.map(id => supplierNames.get(purchaseSuppliers.get(id) ?? '') ?? '').filter(Boolean))];
4402
+ return { ...order, customerName: customerNames.get(order.customerId) ?? '', supplierName: orderSupplierNames.join('、'), pricingProfitRateStatus: order.pricingStatus === 'UNPRICED' ? 'MISSING' : 'CONFIGURED', paymentStatus, lines: order.lines.map(line => ({ ...line, complianceCategories: [...new Set(line.complianceMatches.map(match => match.catalogType))] })) };
4337
4403
  });
4338
4404
  }
4339
4405
  async purchaseOrderDtos(scope, orders) {