@chenchaolong/plugin-trade-compliance-workbench 1.0.76 → 1.0.78

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.
@@ -12,7 +12,7 @@ import { finished } from 'node:stream/promises';
12
12
  import { createHash, randomUUID } from 'node:crypto';
13
13
  import { BankAccountSettings, CatalogReviewBlock, CompanySanctionMatch, CompanySettings, ControlledCatalogBatch, ControlledCatalogImportIssue, ControlledGoodsRecord, Customer, CustomerContract, CustomerContractAnalysisRun, CustomerContractClause, CustomerContractClauseAnalysis, DerivedSalesOrderManualData, GeneratedSalesFile, ImportTask, ManagedAsset, MonthlySafeExchangeRate, ProductComplianceMatch, ProfitRuleSettings, PurchaseOrder, PurchaseOrderLine, SanctionCatalogBatch, SanctionCatalogImportIssue, SanctionedCompanyAlias, SanctionedCompanyRecord, SettingsHistory, Supplier } from './entities/index.js';
14
14
  import { CATALOG_REVIEW_BATCH_LIMIT } from './constants.js';
15
- import { CatalogTypes, ClauseTypes, CustomerProfitRateStatuses, DomainError, DomainErrorCodes, RefundValidationMethods, SalesPaymentStatuses, SanctionHitStatuses, StalledCatalogImportDecisions } from './types.js';
15
+ import { CatalogTypes, ClauseTypes, CustomerProfitRateStatuses, DomainError, DomainErrorCodes, RefundValidationMethods, RiskLevels, SalesPaymentStatuses, SanctionHitStatuses, StalledCatalogImportDecisions } from './types.js';
16
16
  import { deriveSalesLine, assertReceivedAmount } from './domain/finance.js';
17
17
  import { parseCustomerContractNo, parsePurchaseContractNo } from './domain/contract-number.js';
18
18
  import { isProductNameMatch, normalizeExactName, normalizeForProductMatch, normalizeHsCode, normalizeSanctionSource } from './domain/normalization.js';
@@ -2483,7 +2483,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
2483
2483
  return saved;
2484
2484
  }
2485
2485
  async saveCustomerContractDraft(scope, importTaskId, draft) {
2486
- const normalizedDraft = { ...draft, clauses: draft.clauses.map(normalizeCustomerContractDraftClause) };
2486
+ const normalizedDraft = { ...draft, clauses: draft.clauses.map(clause => normalizeCustomerContractDraftClause(clause)) };
2487
2487
  const effectiveAnalyses = completeCustomerClauseAnalyses(normalizedDraft);
2488
2488
  const contract = await this.dataSource.transaction(async (manager) => {
2489
2489
  const taskRepo = manager.getRepository(ImportTask);
@@ -4206,10 +4206,11 @@ function normalizeCustomerContractAnalysisInput(value, enforceInvariant = true)
4206
4206
  throw new DomainError(DomainErrorCodes.INVALID_INPUT, '未找到条款不能填写人工审核内容');
4207
4207
  return { reviewedText, extractionStatus: value.extractionStatus };
4208
4208
  }
4209
- function normalizeCustomerContractDraftClause(value) {
4209
+ function normalizeCustomerContractDraftClause(value, options = {}) {
4210
4210
  const sourceText = firstStringValue(value, ['sourceText', 'originalText', 'sourceContent']);
4211
4211
  const reviewedText = firstStringValue(value, ['reviewedText', 'translatedText', 'clauseSummary', 'summary', 'originalText']);
4212
- const extractionStatus = value.extractionStatus ?? (sourceText || reviewedText || value.sourceContent ? 'FOUND' : 'NOT_FOUND');
4212
+ const hasReviewableText = Boolean(sourceText || reviewedText || firstStringValue(value, ['sourceContent']));
4213
+ const extractionStatus = options.inferFoundFromText && hasReviewableText ? 'FOUND' : value.extractionStatus ?? (hasReviewableText ? 'FOUND' : 'NOT_FOUND');
4213
4214
  const normalized = normalizeCustomerContractAnalysisInput({ extractionStatus, reviewedText });
4214
4215
  return {
4215
4216
  ...value,
@@ -4220,16 +4221,49 @@ function normalizeCustomerContractDraftClause(value) {
4220
4221
  };
4221
4222
  }
4222
4223
  function normalizeCustomerContractDraftPayload(payload) {
4223
- if (!Array.isArray(payload.clauses))
4224
- return payload;
4225
- return {
4224
+ const normalized = {
4226
4225
  ...payload,
4227
- clauses: payload.clauses.map(clause => {
4226
+ clauses: Array.isArray(payload.clauses) ? payload.clauses.map(clause => {
4228
4227
  if (!clause || typeof clause !== 'object' || Array.isArray(clause))
4229
4228
  return clause;
4230
- return normalizeCustomerContractDraftClause(clause);
4231
- })
4229
+ return normalizeCustomerContractDraftClause(clause, { inferFoundFromText: true });
4230
+ }) : payload.clauses
4232
4231
  };
4232
+ assertCustomerContractImportDraftPayload(normalized);
4233
+ return normalized;
4234
+ }
4235
+ function assertCustomerContractImportDraftPayload(payload) {
4236
+ const customer = payload.customer && typeof payload.customer === 'object' && !Array.isArray(payload.customer) ? payload.customer : {};
4237
+ const contract = payload.contract && typeof payload.contract === 'object' && !Array.isArray(payload.contract) ? payload.contract : {};
4238
+ const customerName = firstStringValue(customer, ['name', 'customerName']);
4239
+ if (!customerName)
4240
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '客户名称为必填项');
4241
+ const contractNo = firstStringValue(contract, ['contractNo']);
4242
+ if (!contractNo)
4243
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '客户合同编码为必填项');
4244
+ const clauses = Array.isArray(payload.clauses) ? payload.clauses : [];
4245
+ if (!clauses.length)
4246
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '客户合同解析结果缺少可审核条款');
4247
+ const analyses = Array.isArray(payload.analyses) ? payload.analyses : [];
4248
+ let foundCount = 0;
4249
+ for (const clause of clauses) {
4250
+ if (!clause || typeof clause !== 'object' || Array.isArray(clause))
4251
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '客户合同条款无效');
4252
+ const item = clause;
4253
+ if (!ClauseTypes.includes(item.clauseType))
4254
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '客户合同条款类型无效');
4255
+ if (item.extractionStatus !== 'FOUND' && item.extractionStatus !== 'NOT_FOUND')
4256
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '客户合同条款提取状态无效');
4257
+ if (item.extractionStatus !== 'FOUND')
4258
+ continue;
4259
+ foundCount += 1;
4260
+ const analysis = analyses.find(value => value && typeof value === 'object' && !Array.isArray(value) && value.clauseType === item.clauseType);
4261
+ if (!analysis || !RiskLevels.includes(analysis.riskLevel) || !firstStringValue(analysis, ['riskMessageZh']) || !firstStringValue(analysis, ['suggestionZh'])) {
4262
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '已找到的关键条款缺少分析结果', { clauseType: item.clauseType });
4263
+ }
4264
+ }
4265
+ if (foundCount === 0)
4266
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '客户合同解析结果缺少可审核条款');
4233
4267
  }
4234
4268
  function customerContractAnalysisInputChanged(currentByType, updates) {
4235
4269
  const seen = new Set();