R73-A2 收敛 VAT OCR 归一化与质量门槛
这个提交包含在:
@@ -9,7 +9,7 @@ const { compactDateString } = require("../utils/date");
|
||||
const { sha256Hex } = require("../utils/crypto");
|
||||
const { toMoneyString } = require("../utils/workflow");
|
||||
const { findActiveDelegationForDelegate } = require("../utils/delegation");
|
||||
const { isOcrEnabledForType } = require("../services/ocr");
|
||||
const { getMissingRequiredOcrFields, isOcrEnabledForType } = require("../services/ocr");
|
||||
const { registerInvoiceVerificationRoutes } = require("./invoicePool/verification");
|
||||
const { registerInvoicePoolReadRoutes } = require("./invoicePool/read");
|
||||
const { normalizeInvoiceResponse } = require("./invoicePool/shared/mappers");
|
||||
@@ -46,7 +46,7 @@ const invoiceEditBodySchema = z
|
||||
invoiceNumber: z.string().trim().min(1).max(64).nullable().optional(),
|
||||
invoiceDate: z
|
||||
.string()
|
||||
.regex(/^\\d{4}-\\d{2}-\\d{2}$/)
|
||||
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
||||
.nullable()
|
||||
.optional(),
|
||||
sellerName: z.string().trim().min(1).max(256).nullable().optional(),
|
||||
@@ -163,7 +163,7 @@ function normalizeMoneyInput(value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!/^\\d+(?:\\.\\d{1,2})?$/.test(input)) {
|
||||
if (!/^\d+(?:\.\d{1,2})?$/.test(input)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ function parseDateOnlyString(value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -576,6 +576,32 @@ async function invoicePoolRoutes(fastify) {
|
||||
|
||||
if (input.status) {
|
||||
if (input.status === "available" && current.status === "ocr_failed") {
|
||||
const nextSnapshot = {
|
||||
invoiceCode: "invoiceCode" in input ? input.invoiceCode : current.invoice_code,
|
||||
invoiceNumber: "invoiceNumber" in input ? input.invoiceNumber : current.invoice_number,
|
||||
invoiceDate:
|
||||
"invoiceDate" in input
|
||||
? input.invoiceDate
|
||||
: current.invoice_date?.toISOString?.()?.slice(0, 10) ?? null,
|
||||
totalAmount:
|
||||
"totalAmount" in input
|
||||
? normalizeMoneyInput(input.totalAmount)
|
||||
: current.total_amount?.toString?.() ?? null,
|
||||
sellerName: "sellerName" in input ? input.sellerName : current.seller_name,
|
||||
};
|
||||
const missingFields = getMissingRequiredOcrFields(current.ocr_type, nextSnapshot);
|
||||
if (missingFields.length) {
|
||||
throw new AppError({
|
||||
code: "VALID_001",
|
||||
message: "参数校验失败",
|
||||
statusCode: 409,
|
||||
details: {
|
||||
ocr: "missing_required_fields",
|
||||
missingFields,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
updateData.status = "available";
|
||||
updateData.ocr_last_error = null;
|
||||
updateData.ocr_completed_at = new Date();
|
||||
|
||||
在新工单中引用
屏蔽一个用户