@chenchaolong/plugin-trade-compliance-workbench 1.0.64 → 1.0.65
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.
- package/README.md +22 -3
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/adapters/translation.provider.d.ts +7 -0
- package/dist/lib/adapters/translation.provider.d.ts.map +1 -1
- package/dist/lib/adapters/translation.provider.js +127 -0
- package/dist/lib/adapters/translation.provider.js.map +1 -1
- package/dist/lib/catalog-import/catalog-import.pipeline.d.ts +1 -1
- package/dist/lib/catalog-import/catalog-import.pipeline.d.ts.map +1 -1
- package/dist/lib/catalog-import/catalog-import.pipeline.js +8 -5
- package/dist/lib/catalog-import/catalog-import.pipeline.js.map +1 -1
- package/dist/lib/catalog-import/confidence.js +8 -1
- package/dist/lib/catalog-import/confidence.js.map +1 -1
- package/dist/lib/catalog-import/docx.parser.d.ts.map +1 -1
- package/dist/lib/catalog-import/docx.parser.js +15 -1
- package/dist/lib/catalog-import/docx.parser.js.map +1 -1
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +1 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/domain/catalog-localization.d.ts +1 -1
- package/dist/lib/domain/catalog-localization.d.ts.map +1 -1
- package/dist/lib/domain/catalog-localization.js +3 -1
- package/dist/lib/domain/catalog-localization.js.map +1 -1
- package/dist/lib/import-jobs/agent-handoff.dispatcher.d.ts.map +1 -1
- package/dist/lib/import-jobs/agent-handoff.dispatcher.js +6 -2
- package/dist/lib/import-jobs/agent-handoff.dispatcher.js.map +1 -1
- package/dist/lib/import-jobs/import-command.builder.js +1 -1
- package/dist/lib/import-jobs/import-command.builder.js.map +1 -1
- package/dist/lib/trade-compliance.config.d.ts +52 -0
- package/dist/lib/trade-compliance.config.d.ts.map +1 -1
- package/dist/lib/trade-compliance.config.js +13 -3
- package/dist/lib/trade-compliance.config.js.map +1 -1
- package/dist/lib/workbench.middleware.d.ts +34 -1
- package/dist/lib/workbench.middleware.d.ts.map +1 -1
- package/dist/lib/workbench.middleware.js +10 -2
- package/dist/lib/workbench.middleware.js.map +1 -1
- package/dist/lib/workbench.service.d.ts +8 -0
- package/dist/lib/workbench.service.d.ts.map +1 -1
- package/dist/lib/workbench.service.js +127 -21
- package/dist/lib/workbench.service.js.map +1 -1
- package/package.json +6 -1
- package/scripts/verify-catalog-e2e.mjs +250 -0
- package/scripts/verify-catalog-samples.mjs +61 -0
- package/scripts/verify-qwen-translation.mjs +46 -0
|
@@ -577,7 +577,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
577
577
|
batch.riskRecalculatedAt = result.riskRecalculatedAt ?? new Date();
|
|
578
578
|
return batch;
|
|
579
579
|
}
|
|
580
|
-
async localizeControlledCatalogRows(rows, organizationId, deadline = Date.now() + CONTROLLED_CATALOG_TRANSLATION_TIMEOUT_MS, ownerSignal) {
|
|
580
|
+
async localizeControlledCatalogRows(rows, organizationId, deadline = Date.now() + CONTROLLED_CATALOG_TRANSLATION_TIMEOUT_MS, ownerSignal, allowServiceTranslation = false) {
|
|
581
581
|
const localized = new Array(rows.length);
|
|
582
582
|
const controller = new AbortController();
|
|
583
583
|
const abortFromOwner = () => controller.abort(ownerSignal?.reason);
|
|
@@ -591,7 +591,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
591
591
|
while (nextIndex < rows.length) {
|
|
592
592
|
const index = nextIndex;
|
|
593
593
|
nextIndex += 1;
|
|
594
|
-
localized[index] = await this.localizeControlledCatalogRow(rows[index], organizationId, deadline, controller.signal);
|
|
594
|
+
localized[index] = await this.localizeControlledCatalogRow(rows[index], organizationId, deadline, controller.signal, allowServiceTranslation);
|
|
595
595
|
}
|
|
596
596
|
};
|
|
597
597
|
try {
|
|
@@ -603,14 +603,14 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
603
603
|
ownerSignal?.removeEventListener('abort', abortFromOwner);
|
|
604
604
|
}
|
|
605
605
|
}
|
|
606
|
-
async localizeControlledCatalogRow(row, organizationId, deadline, signal) {
|
|
606
|
+
async localizeControlledCatalogRow(row, organizationId, deadline, signal, allowServiceTranslation) {
|
|
607
607
|
const originalProductName = String(row.originalProductName ?? '').trim() || null;
|
|
608
608
|
const originalControlDescription = String(row.originalControlDescription ?? '').trim() || null;
|
|
609
609
|
let productName = String(row.productName ?? '').trim();
|
|
610
610
|
let controlDescription = String(row.controlDescription ?? '').trim() || null;
|
|
611
611
|
let translationSource = row.translationSource ?? null;
|
|
612
612
|
let serviceTranslated = false;
|
|
613
|
-
const inputIssue = catalogLocalizationInputIssues(row)[0];
|
|
613
|
+
const inputIssue = catalogLocalizationInputIssues(row, allowServiceTranslation)[0];
|
|
614
614
|
if (inputIssue)
|
|
615
615
|
return { productName, controlDescription, originalProductName, originalControlDescription, translationSource, error: inputIssue.message };
|
|
616
616
|
if (!productName) {
|
|
@@ -1071,8 +1071,8 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1071
1071
|
if (decision === 'PUBLISH_VALID_ROWS') {
|
|
1072
1072
|
await this.appendStalledCatalogReviewIssues(manager, scope, task, batch, unresolved);
|
|
1073
1073
|
}
|
|
1074
|
-
|
|
1075
|
-
await reviewRepo.save(
|
|
1074
|
+
for (const block of unresolved)
|
|
1075
|
+
await reviewRepo.save(block);
|
|
1076
1076
|
await this.reconcileCatalogReviewPendingCount(reviewRepo, scope, task);
|
|
1077
1077
|
if (progress.reviewPendingCount !== 0) {
|
|
1078
1078
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '目录恢复后仍存在待复核块');
|
|
@@ -1128,6 +1128,77 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1128
1128
|
}
|
|
1129
1129
|
return result;
|
|
1130
1130
|
}
|
|
1131
|
+
async autoTranslateCatalogReviewBlocks(scope, taskId) {
|
|
1132
|
+
if (!this.translation)
|
|
1133
|
+
return { translated: 0, remaining: await this.countCatalogReviewBlocks(scope, taskId) };
|
|
1134
|
+
let translated = 0;
|
|
1135
|
+
while (true) {
|
|
1136
|
+
const leased = await this.leaseTranslationReviewBlocks(scope, taskId);
|
|
1137
|
+
if (!leased || leased.blocks.length === 0)
|
|
1138
|
+
break;
|
|
1139
|
+
const records = leased.blocks.map(block => ({
|
|
1140
|
+
recordId: block.id,
|
|
1141
|
+
fields: translationFields(block)
|
|
1142
|
+
}));
|
|
1143
|
+
const translatedRecords = await this.translation.translateBatch(records, { organizationId: scope.organizationId });
|
|
1144
|
+
const translatedById = new Map(translatedRecords.map(record => [record.recordId, record]));
|
|
1145
|
+
const accepted = leased.blocks.flatMap(block => {
|
|
1146
|
+
const result = translatedById.get(block.id);
|
|
1147
|
+
const fields = result?.fields;
|
|
1148
|
+
if (!fields)
|
|
1149
|
+
return [];
|
|
1150
|
+
const payload = autoTranslatedPayload(block, fields);
|
|
1151
|
+
return payload ? [{ blockId: block.id, decision: 'ACCEPT', payload }] : [];
|
|
1152
|
+
});
|
|
1153
|
+
const acceptedIds = new Set(accepted.map(item => item.blockId));
|
|
1154
|
+
await this.releaseTranslationReviewBlocks(scope, taskId, leased.leaseToken, leased.blocks.filter(block => !acceptedIds.has(block.id)).map(block => block.id));
|
|
1155
|
+
if (accepted.length === 0)
|
|
1156
|
+
break;
|
|
1157
|
+
const result = await this.submitCatalogReviewBatchInternal(scope, taskId, leased.leaseToken, accepted, true);
|
|
1158
|
+
translated += result.resolved;
|
|
1159
|
+
if (result.resolved === 0 || accepted.length < leased.blocks.length)
|
|
1160
|
+
break;
|
|
1161
|
+
}
|
|
1162
|
+
return { translated, remaining: await this.countCatalogReviewBlocks(scope, taskId) };
|
|
1163
|
+
}
|
|
1164
|
+
async leaseTranslationReviewBlocks(scope, taskId) {
|
|
1165
|
+
return this.dataSource.transaction(async (manager) => {
|
|
1166
|
+
const taskRepo = manager.getRepository(ImportTask);
|
|
1167
|
+
const task = await required(taskRepo.findOne({ where: { ...activeScope(scope), id: taskId }, lock: { mode: 'pessimistic_write' } }), '解析任务不存在');
|
|
1168
|
+
const repo = manager.getRepository(CatalogReviewBlock);
|
|
1169
|
+
const candidates = await repo.find({ where: { ...activeScope(scope), importTaskId: task.id, status: 'PENDING' }, order: { ordinal: 'ASC' }, lock: { mode: 'pessimistic_write' } });
|
|
1170
|
+
const blocks = candidates.filter(block => block.reasons.length === 1 && block.reasons[0] === 'TRANSLATION_REQUIRED').slice(0, CATALOG_REVIEW_BATCH_LIMIT);
|
|
1171
|
+
if (blocks.length === 0)
|
|
1172
|
+
return null;
|
|
1173
|
+
const leaseToken = randomUUID();
|
|
1174
|
+
const leaseExpiresAt = new Date(Date.now() + 10 * 60_000);
|
|
1175
|
+
for (const block of blocks) {
|
|
1176
|
+
block.status = 'LEASED';
|
|
1177
|
+
block.leaseToken = leaseToken;
|
|
1178
|
+
block.leaseExpiresAt = leaseExpiresAt;
|
|
1179
|
+
}
|
|
1180
|
+
await repo.update({ ...activeScope(scope), importTaskId: task.id, id: In(blocks.map(block => block.id)), status: 'PENDING' }, {
|
|
1181
|
+
status: 'LEASED', leaseToken, leaseExpiresAt
|
|
1182
|
+
});
|
|
1183
|
+
if (task.progress) {
|
|
1184
|
+
task.progress.stage = 'TRANSLATING';
|
|
1185
|
+
task.progress.progressVersion += 1;
|
|
1186
|
+
task.progress.lastProgressAt = new Date().toISOString();
|
|
1187
|
+
await taskRepo.save(task);
|
|
1188
|
+
}
|
|
1189
|
+
return { leaseToken, blocks };
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
async releaseTranslationReviewBlocks(scope, taskId, leaseToken, blockIds) {
|
|
1193
|
+
if (blockIds.length === 0)
|
|
1194
|
+
return;
|
|
1195
|
+
await this.dataSource.getRepository(CatalogReviewBlock).update({ ...activeScope(scope), importTaskId: taskId, id: In(blockIds), status: 'LEASED', leaseToken }, {
|
|
1196
|
+
status: 'PENDING', leaseToken: null, leaseExpiresAt: null
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
async countCatalogReviewBlocks(scope, taskId) {
|
|
1200
|
+
return this.dataSource.getRepository(CatalogReviewBlock).count({ where: { ...activeScope(scope), importTaskId: taskId, status: In(['PENDING', 'LEASED']) } });
|
|
1201
|
+
}
|
|
1131
1202
|
async waitImportProgress(scope, taskId, afterProgressVersion = 0, waitMs = 10_000) {
|
|
1132
1203
|
const deadline = Date.now() + Math.max(0, Math.min(waitMs, 10_000));
|
|
1133
1204
|
while (true) {
|
|
@@ -1183,7 +1254,9 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1183
1254
|
block.leaseToken = leaseToken;
|
|
1184
1255
|
block.leaseExpiresAt = leaseExpiresAt;
|
|
1185
1256
|
}
|
|
1186
|
-
await repo.
|
|
1257
|
+
await repo.update({ ...activeScope(scope), importTaskId: task.id, id: In(blocks.map(block => block.id)), status: 'PENDING' }, {
|
|
1258
|
+
status: 'LEASED', leaseToken, leaseExpiresAt
|
|
1259
|
+
});
|
|
1187
1260
|
return {
|
|
1188
1261
|
leaseToken,
|
|
1189
1262
|
leaseExpiresAt: leaseExpiresAt.toISOString(),
|
|
@@ -1196,6 +1269,9 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1196
1269
|
});
|
|
1197
1270
|
}
|
|
1198
1271
|
async submitCatalogReviewBatch(scope, taskId, leaseToken, items) {
|
|
1272
|
+
return this.submitCatalogReviewBatchInternal(scope, taskId, leaseToken, items, false);
|
|
1273
|
+
}
|
|
1274
|
+
async submitCatalogReviewBatchInternal(scope, taskId, leaseToken, items, allowServiceTranslation) {
|
|
1199
1275
|
if (!leaseToken.trim() || items.length === 0 || items.length > CATALOG_REVIEW_BATCH_LIMIT) {
|
|
1200
1276
|
throw new DomainError(DomainErrorCodes.INVALID_INPUT, '复核提交参数无效');
|
|
1201
1277
|
}
|
|
@@ -1254,7 +1330,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1254
1330
|
block.resolutionFingerprint = catalogReviewResolutionFingerprint(item);
|
|
1255
1331
|
}
|
|
1256
1332
|
else {
|
|
1257
|
-
const evaluation = evaluateCatalogReviewAccept(task.documentType, block, item.payload);
|
|
1333
|
+
const evaluation = evaluateCatalogReviewAccept(task.documentType, block, item.payload, allowServiceTranslation);
|
|
1258
1334
|
if (!evaluation.candidate) {
|
|
1259
1335
|
restorePendingReviewBlock(block);
|
|
1260
1336
|
errors.push({ blockId: block.id, reasons: evaluation.reasons });
|
|
@@ -1283,7 +1359,7 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1283
1359
|
const batchRepo = manager.getRepository(ControlledCatalogBatch);
|
|
1284
1360
|
const batch = await required(batchRepo.findOne({ where: { ...activeScope(scope), id: task.resultEntityId } }), '管控目录批次不存在');
|
|
1285
1361
|
const rows = acceptedCandidates.flatMap(candidateToControlledRows);
|
|
1286
|
-
const localized = await this.localizeControlledCatalogRows(rows, scope.organizationId);
|
|
1362
|
+
const localized = await this.localizeControlledCatalogRows(rows, scope.organizationId, undefined, undefined, allowServiceTranslation);
|
|
1287
1363
|
await this.appendControlledCatalogRows(manager, scope, batch, rows, localized);
|
|
1288
1364
|
await batchRepo.save(batch);
|
|
1289
1365
|
task.progress.successCount = batch.successCount;
|
|
@@ -1303,7 +1379,8 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
1303
1379
|
task.progress.invalidCount = rejectedBatch.invalidCount;
|
|
1304
1380
|
task.progress.skippedCount = rejectedBatch.skippedCount;
|
|
1305
1381
|
}
|
|
1306
|
-
|
|
1382
|
+
for (const block of blocks)
|
|
1383
|
+
await reviewRepo.save(block);
|
|
1307
1384
|
task.progress.reviewResolvedCount += resolved;
|
|
1308
1385
|
task.progress.reviewRejectedCount += rejected;
|
|
1309
1386
|
await this.reconcileCatalogReviewPendingCount(reviewRepo, scope, task);
|
|
@@ -3607,12 +3684,10 @@ let TradeComplianceWorkbenchService = class TradeComplianceWorkbenchService {
|
|
|
3607
3684
|
return { scanned: 0, matches: 0, riskRecalculatedAt: sanctionBatch.riskRecalculatedAt };
|
|
3608
3685
|
}
|
|
3609
3686
|
await repo.update({ ...activeScope(scope), deletedAt: IsNull() }, { deletedAt: now, invalidationReason: reason });
|
|
3610
|
-
const
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
manager.getRepository(SanctionedCompanyAlias).find({ where: { ...activeScope(scope), deletedAt: IsNull() } })
|
|
3615
|
-
]);
|
|
3687
|
+
const suppliers = await supplierRepo.find({ where: { ...activeScope(scope), deletedAt: IsNull() } });
|
|
3688
|
+
const customers = await customerRepo.find({ where: { ...activeScope(scope), deletedAt: IsNull() } });
|
|
3689
|
+
const sanctions = await manager.getRepository(SanctionedCompanyRecord).find({ where: { ...activeScope(scope), lifecycleStatus: 'ACTIVE', deletedAt: IsNull() } });
|
|
3690
|
+
const aliases = await manager.getRepository(SanctionedCompanyAlias).find({ where: { ...activeScope(scope), deletedAt: IsNull() } });
|
|
3616
3691
|
const sanctionsById = new Map(sanctions.map(item => [item.id, item]));
|
|
3617
3692
|
const sanctionsByNormalizedName = new Map();
|
|
3618
3693
|
for (const item of sanctions)
|
|
@@ -4394,7 +4469,7 @@ function candidateToControlledRows(candidate) {
|
|
|
4394
4469
|
const hsCodes = Array.isArray(candidate.values.hsCodes) && candidate.values.hsCodes.length
|
|
4395
4470
|
? candidate.values.hsCodes
|
|
4396
4471
|
: [undefined];
|
|
4397
|
-
const translationSource = candidate.values.translationSource === 'AGENT_TRANSLATED' || candidate.values.translationSource === 'SOURCE_CHINESE'
|
|
4472
|
+
const translationSource = candidate.values.translationSource === 'AGENT_TRANSLATED' || candidate.values.translationSource === 'SOURCE_CHINESE' || candidate.values.translationSource === 'SERVICE_TRANSLATED'
|
|
4398
4473
|
? candidate.values.translationSource
|
|
4399
4474
|
: candidate.values.translationRequired || (!candidate.values.productName && candidate.values.originalProductName)
|
|
4400
4475
|
? 'AGENT_TRANSLATED'
|
|
@@ -4402,6 +4477,7 @@ function candidateToControlledRows(candidate) {
|
|
|
4402
4477
|
return hsCodes.map(hsCode => ({
|
|
4403
4478
|
productName: optionalString(candidate.values.productName) ?? undefined,
|
|
4404
4479
|
originalProductName: optionalString(candidate.values.originalProductName) ?? undefined,
|
|
4480
|
+
originalControlDescription: optionalString(candidate.values.originalControlDescription) ?? undefined,
|
|
4405
4481
|
hsCode: typeof hsCode === 'string' ? hsCode : undefined,
|
|
4406
4482
|
controlDescription: optionalString(candidate.values.controlDescription) ?? undefined,
|
|
4407
4483
|
controlCode: optionalString(candidate.values.controlCode) ?? undefined,
|
|
@@ -4498,11 +4574,11 @@ function canonicalJson(value) {
|
|
|
4498
4574
|
}
|
|
4499
4575
|
throw new TypeError('Resolution payload is not JSON serializable');
|
|
4500
4576
|
}
|
|
4501
|
-
function evaluateCatalogReviewAccept(documentType, block, patch) {
|
|
4577
|
+
function evaluateCatalogReviewAccept(documentType, block, patch, allowServiceTranslation = false) {
|
|
4502
4578
|
try {
|
|
4503
4579
|
const payload = mergeReviewPayload(block.payload, patch);
|
|
4504
4580
|
let candidate = reviewCandidate(documentType, block, payload);
|
|
4505
|
-
if (!validCatalogReviewCandidateValues(candidate))
|
|
4581
|
+
if (!validCatalogReviewCandidateValues(candidate, allowServiceTranslation))
|
|
4506
4582
|
return { candidate: null, reasons: ['REVIEW_PAYLOAD_INVALID'] };
|
|
4507
4583
|
candidate = normalizeCatalogCandidate(candidate);
|
|
4508
4584
|
const decision = classifyCandidate(candidate);
|
|
@@ -4520,7 +4596,7 @@ function evaluateCatalogReviewAccept(documentType, block, patch) {
|
|
|
4520
4596
|
const CATALOG_REVIEW_VARCHAR_MAX = 255;
|
|
4521
4597
|
const CATALOG_REVIEW_TEXT_MAX = 100_000;
|
|
4522
4598
|
const CATALOG_REVIEW_ARRAY_MAX = 100;
|
|
4523
|
-
function validCatalogReviewCandidateValues(candidate) {
|
|
4599
|
+
function validCatalogReviewCandidateValues(candidate, allowServiceTranslation = false) {
|
|
4524
4600
|
const values = candidate.values;
|
|
4525
4601
|
const varcharFields = candidate.kind === 'CONTROLLED'
|
|
4526
4602
|
? ['productName', 'hsCode', 'controlCode', 'sourceSection', 'sourceOrdinal']
|
|
@@ -4534,7 +4610,7 @@ function validCatalogReviewCandidateValues(candidate) {
|
|
|
4534
4610
|
return false;
|
|
4535
4611
|
if (values.translationRequired !== undefined && typeof values.translationRequired !== 'boolean')
|
|
4536
4612
|
return false;
|
|
4537
|
-
if (values.translationSource !== undefined && values.translationSource !== 'SOURCE_CHINESE' && values.translationSource !== 'AGENT_TRANSLATED')
|
|
4613
|
+
if (values.translationSource !== undefined && values.translationSource !== 'SOURCE_CHINESE' && values.translationSource !== 'AGENT_TRANSLATED' && !(allowServiceTranslation && values.translationSource === 'SERVICE_TRANSLATED'))
|
|
4538
4614
|
return false;
|
|
4539
4615
|
if (values.hsCodes !== undefined && (!Array.isArray(values.hsCodes) || values.hsCodes.length > CATALOG_REVIEW_ARRAY_MAX ||
|
|
4540
4616
|
values.hsCodes.some(value => typeof value !== 'string' || value.length > CATALOG_REVIEW_VARCHAR_MAX)))
|
|
@@ -4555,6 +4631,36 @@ function validCatalogReviewAlias(value) {
|
|
|
4555
4631
|
return typeof alias === 'string' && alias.length > 0 && alias.length <= CATALOG_REVIEW_VARCHAR_MAX &&
|
|
4556
4632
|
(aliasType === 'AKA' || aliasType === 'LOCAL_LANGUAGE');
|
|
4557
4633
|
}
|
|
4634
|
+
function translationFields(block) {
|
|
4635
|
+
const payload = block.payload ?? {};
|
|
4636
|
+
const fields = block.documentType === 'CONTROLLED_CATALOG'
|
|
4637
|
+
? [['productName', payload.originalProductName ?? payload.productName], ['controlDescription', payload.originalControlDescription ?? payload.controlDescription]]
|
|
4638
|
+
: [['sanctionReason', payload.sanctionReason], ['remarks', payload.remarks]];
|
|
4639
|
+
return Object.fromEntries(fields.filter(([, value]) => typeof value === 'string' && value.trim().length > 0));
|
|
4640
|
+
}
|
|
4641
|
+
function autoTranslatedPayload(block, fields) {
|
|
4642
|
+
const requiredFields = Object.keys(translationFields(block));
|
|
4643
|
+
if (requiredFields.length === 0 || requiredFields.some(field => typeof fields[field] !== 'string' || !fields[field]?.trim()))
|
|
4644
|
+
return null;
|
|
4645
|
+
const payload = { ...block.payload };
|
|
4646
|
+
for (const field of ['productName', 'controlDescription', 'sanctionReason', 'remarks']) {
|
|
4647
|
+
const value = fields[field];
|
|
4648
|
+
if (value !== undefined && value !== null && String(value).trim())
|
|
4649
|
+
payload[field] = String(value).trim();
|
|
4650
|
+
}
|
|
4651
|
+
if (block.documentType === 'CONTROLLED_CATALOG') {
|
|
4652
|
+
if (payload.originalProductName && !payload.productName)
|
|
4653
|
+
return null;
|
|
4654
|
+
if (payload.originalControlDescription && !payload.controlDescription)
|
|
4655
|
+
return null;
|
|
4656
|
+
payload.translationRequired = false;
|
|
4657
|
+
payload.translationSource = 'SERVICE_TRANSLATED';
|
|
4658
|
+
}
|
|
4659
|
+
else {
|
|
4660
|
+
payload.translationRequired = false;
|
|
4661
|
+
}
|
|
4662
|
+
return payload;
|
|
4663
|
+
}
|
|
4558
4664
|
function isRealIsoDate(value) {
|
|
4559
4665
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
4560
4666
|
if (!match)
|