R111/A3-A6 强化发票查重和状态治理
这个提交包含在:
+12
-31
@@ -1,6 +1,7 @@
|
||||
const cron = require("node-cron");
|
||||
|
||||
const { buildMissingRequiredOcrError, runOcrForInvoice } = require("../services/ocr");
|
||||
const { resolveInvoiceDedupBlocker } = require("../services/invoiceGovernance");
|
||||
const { readFileBuffer } = require("../services/storage");
|
||||
|
||||
const OCR_DUPLICATE_ERROR = "duplicate_invoice_code_number";
|
||||
@@ -69,35 +70,10 @@ async function claimPendingInvoices(prisma, config, workerId) {
|
||||
ocr_lock_owner = ${workerId}
|
||||
FROM candidates
|
||||
WHERE i.id = candidates.id
|
||||
RETURNING i.id, i.file_sha256, i.ocr_provider, i.ocr_type, i.storage_path, i.mime_type, i.ocr_attempts
|
||||
RETURNING i.id, i.owner_employee_id, i.file_sha256, i.ocr_provider, i.ocr_type, i.storage_path, i.mime_type, i.ocr_attempts
|
||||
`;
|
||||
}
|
||||
|
||||
async function findDuplicateInvoice(prisma, invoiceId, invoiceCode, invoiceNumber) {
|
||||
if (!invoiceCode || !invoiceNumber) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return prisma.invoicePool.findFirst({
|
||||
where: {
|
||||
id: { not: invoiceId },
|
||||
invoice_code: invoiceCode,
|
||||
invoice_number: invoiceNumber,
|
||||
is_duplicate_override: false,
|
||||
OR: [
|
||||
{ status: "bound" },
|
||||
{
|
||||
status: "archived",
|
||||
archived_reason: "used_complete",
|
||||
},
|
||||
],
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function getOrCreateOcrResult(prisma, config, claimedInvoice, imageBase64) {
|
||||
const ocrProvider = getEffectiveOcrProvider(config);
|
||||
const where = {
|
||||
@@ -303,11 +279,15 @@ async function processClaimedInvoice(prisma, config, claimedInvoice, workerId, l
|
||||
return { outcome: "failed" };
|
||||
}
|
||||
|
||||
const duplicate = await findDuplicateInvoice(
|
||||
const duplicate = await resolveInvoiceDedupBlocker(
|
||||
prisma,
|
||||
claimedInvoice.id,
|
||||
normalized.invoiceCode,
|
||||
normalized.invoiceNumber,
|
||||
{
|
||||
excludeInvoiceId: claimedInvoice.id,
|
||||
fileSha256: claimedInvoice.file_sha256,
|
||||
invoiceCode: normalized.invoiceCode,
|
||||
invoiceNumber: normalized.invoiceNumber,
|
||||
ownerEmployeeId: claimedInvoice.owner_employee_id,
|
||||
},
|
||||
);
|
||||
|
||||
if (duplicate) {
|
||||
@@ -315,7 +295,8 @@ async function processClaimedInvoice(prisma, config, claimedInvoice, workerId, l
|
||||
logger.warn(
|
||||
{
|
||||
invoiceId: claimedInvoice.id,
|
||||
duplicateInvoiceId: duplicate.id,
|
||||
duplicateInvoiceId: duplicate.conflict.id,
|
||||
duplicateReason: duplicate.reason,
|
||||
mimeType: claimedInvoice.mime_type,
|
||||
transportMode: normalized?.transportMode ?? normalized?.raw?.transportMode ?? null,
|
||||
},
|
||||
|
||||
在新工单中引用
屏蔽一个用户