@arela/uploader 1.1.4 → 1.2.0
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/.claude/worktrees/agent-multi-profile/.env.template +224 -0
- package/.claude/worktrees/agent-multi-profile/.prettierrc +13 -0
- package/.claude/worktrees/agent-multi-profile/README.md +405 -0
- package/.claude/worktrees/agent-multi-profile/package-lock.json +7096 -0
- package/.claude/worktrees/agent-multi-profile/package.json +78 -0
- package/.claude/worktrees/agent-multi-profile/scripts/cleanup-ds-store.js +109 -0
- package/.claude/worktrees/agent-multi-profile/scripts/cleanup-system-files.js +69 -0
- package/.claude/worktrees/agent-multi-profile/scripts/scoring-compare.js +243 -0
- package/.claude/worktrees/agent-multi-profile/scripts/scoring-phase4-check.js +96 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/phase-7-features.test.js +415 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/signal-handling.test.js +275 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/smart-watch-integration.test.js +554 -0
- package/.claude/worktrees/agent-multi-profile/scripts/tests/watch-service-integration.test.js +584 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/AgentCommand.js +229 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/AgentInitCommand.js +316 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/DatastageCommand.js +164 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/GDriveSyncCommand.js +475 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/IdentifyCommand.js +708 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PollWorkerCommand.js +169 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PropagateCommand.js +636 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/PushCommand.js +743 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/ScanCommand.js +722 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/UploadCommand.js +587 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/WatchCommand.js +1342 -0
- package/.claude/worktrees/agent-multi-profile/src/commands/WorkerCommand.js +337 -0
- package/.claude/worktrees/agent-multi-profile/src/config/config.js +862 -0
- package/.claude/worktrees/agent-multi-profile/src/document-type-shared.js +131 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/_pedimento-shared-extractors.js +348 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/doda-pdf.js +121 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/doda-xml.js +118 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/factura-inter-agencia.js +186 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/facturas-comerciales.js +233 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-completo-xml.js +372 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-completo.js +108 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/pedimento-simplificado.js +76 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/proforma.js +29 -0
- package/.claude/worktrees/agent-multi-profile/src/document-types/support-document.js +200 -0
- package/.claude/worktrees/agent-multi-profile/src/errors/ErrorHandler.js +278 -0
- package/.claude/worktrees/agent-multi-profile/src/errors/ErrorTypes.js +104 -0
- package/.claude/worktrees/agent-multi-profile/src/file-detection.js +338 -0
- package/.claude/worktrees/agent-multi-profile/src/index.js +890 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/db-matcher-adapter.js +98 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/matchers-seed.js +386 -0
- package/.claude/worktrees/agent-multi-profile/src/scoring/scoring-engine.js +251 -0
- package/.claude/worktrees/agent-multi-profile/src/services/AdvancedFilterService.js +505 -0
- package/.claude/worktrees/agent-multi-profile/src/services/AutoProcessingService.js +749 -0
- package/.claude/worktrees/agent-multi-profile/src/services/BenchmarkingService.js +381 -0
- package/.claude/worktrees/agent-multi-profile/src/services/DatabaseService.js +2173 -0
- package/.claude/worktrees/agent-multi-profile/src/services/DatastageApiService.js +240 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ErrorMonitor.js +275 -0
- package/.claude/worktrees/agent-multi-profile/src/services/GoogleDriveService.js +217 -0
- package/.claude/worktrees/agent-multi-profile/src/services/LoggingService.js +649 -0
- package/.claude/worktrees/agent-multi-profile/src/services/MonitoringService.js +401 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PerformanceOptimizer.js +511 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PipelineApiService.js +274 -0
- package/.claude/worktrees/agent-multi-profile/src/services/PipelineJobRunner.js +389 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ProfileManager.js +164 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ReportingService.js +511 -0
- package/.claude/worktrees/agent-multi-profile/src/services/ScanApiService.js +775 -0
- package/.claude/worktrees/agent-multi-profile/src/services/SignalHandler.js +255 -0
- package/.claude/worktrees/agent-multi-profile/src/services/SmartWatchDatabaseService.js +527 -0
- package/.claude/worktrees/agent-multi-profile/src/services/WatchService.js +783 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/ApiUploadService.js +676 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/BaseUploadService.js +36 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/MultiApiUploadService.js +233 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/SupabaseUploadService.js +148 -0
- package/.claude/worktrees/agent-multi-profile/src/services/upload/UploadServiceFactory.js +100 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/CleanupManager.js +262 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/FileOperations.js +192 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/FileSanitizer.js +99 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/PathDetector.js +198 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/PathNormalizer.js +274 -0
- package/.claude/worktrees/agent-multi-profile/src/utils/WatchEventHandler.js +522 -0
- package/.claude/worktrees/agent-multi-profile/supabase/migrations/001_create_initial_schema.sql +366 -0
- package/.claude/worktrees/agent-multi-profile/supabase/migrations/002_align_with_arela_api_schema.sql +145 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/IdentifyCommand.test.js +570 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/PropagateCommand.test.js +568 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/PushCommand.test.js +754 -0
- package/.claude/worktrees/agent-multi-profile/tests/commands/ScanCommand.test.js +382 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/PathAndTableNameGeneration.test.js +1211 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/factura-inter-agencia.test.js +218 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/pedimento-completo-xml-matcher.test.js +271 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/pedimento-simplificado-matcher.test.js +185 -0
- package/.claude/worktrees/agent-multi-profile/tests/unit/scoring-engine.test.js +221 -0
- package/README.md +65 -0
- package/package.json +1 -1
- package/src/commands/AgentCommand.js +210 -0
- package/src/commands/AgentInitCommand.js +316 -0
- package/src/commands/PollWorkerCommand.js +11 -322
- package/src/config/config.js +44 -6
- package/src/index.js +55 -0
- package/src/services/LoggingService.js +35 -0
- package/src/services/PipelineApiService.js +7 -1
- package/src/services/PipelineJobRunner.js +389 -0
- package/src/services/ProfileManager.js +164 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapt DB matchers (from arela-api `GET /document-matcher/resolved`) into the
|
|
3
|
+
* shape the scoring engine consumes — the HYBRID model:
|
|
4
|
+
*
|
|
5
|
+
* - SELECTION comes from the DB matcher's clues / qualify (per-RFC + globals).
|
|
6
|
+
* - EXTRACTION uses the rich JS extractors keyed by `documentType` when one
|
|
7
|
+
* exists (resolveType, multi-pattern field extractors, pedimento composition);
|
|
8
|
+
* otherwise it falls back to building simple regex extractors from the DB
|
|
9
|
+
* matcher's `fieldExtractors`.
|
|
10
|
+
*
|
|
11
|
+
* This keeps per-client matching configurable from the UI while preserving the
|
|
12
|
+
* robust field extraction that already ships in the uploader.
|
|
13
|
+
*/
|
|
14
|
+
// IMPORTANT: load document-type-shared FIRST so it becomes the root of the
|
|
15
|
+
// shared<->definitions import cycle and fully evaluates before the individual
|
|
16
|
+
// definitions are referenced (otherwise: "Cannot access X before initialization").
|
|
17
|
+
import { FieldResult } from '../document-type-shared.js';
|
|
18
|
+
import { dodaPdfDefinition } from '../document-types/doda-pdf.js';
|
|
19
|
+
import { dodaXmlDefinition } from '../document-types/doda-xml.js';
|
|
20
|
+
import { facturaInterAgenciaDefinition } from '../document-types/factura-inter-agencia.js';
|
|
21
|
+
import { facturasComerciales } from '../document-types/facturas-comerciales.js';
|
|
22
|
+
import { pedimentoCompletoXmlDefinition } from '../document-types/pedimento-completo-xml.js';
|
|
23
|
+
import { pedimentoCompletoDefinition } from '../document-types/pedimento-completo.js';
|
|
24
|
+
import { pedimentoSimplificadoDefinition } from '../document-types/pedimento-simplificado.js';
|
|
25
|
+
import { supportDocumentDefinition } from '../document-types/support-document.js';
|
|
26
|
+
|
|
27
|
+
// documentType -> rich extraction half of the JS definition.
|
|
28
|
+
function extractionOf(def) {
|
|
29
|
+
return {
|
|
30
|
+
extractors: def.extractors,
|
|
31
|
+
resolveType: def.resolveType,
|
|
32
|
+
extractNumPedimento: def.extractNumPedimento,
|
|
33
|
+
extractPedimentoYear: def.extractPedimentoYear,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const EXTRACTION_REGISTRY = {
|
|
38
|
+
pedimento_simplificado: extractionOf(pedimentoSimplificadoDefinition),
|
|
39
|
+
pedimento_completo: extractionOf(pedimentoCompletoDefinition),
|
|
40
|
+
pedimento_completo_xml: extractionOf(pedimentoCompletoXmlDefinition),
|
|
41
|
+
doda_pdf: extractionOf(dodaPdfDefinition),
|
|
42
|
+
doda_xml: extractionOf(dodaXmlDefinition),
|
|
43
|
+
factura_inter_agencia: extractionOf(facturaInterAgenciaDefinition),
|
|
44
|
+
factura_comercial: extractionOf(facturasComerciales),
|
|
45
|
+
support_document: extractionOf(supportDocumentDefinition),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Build a scoring-engine extractor from a DB fieldExtractor (regex + capture).
|
|
49
|
+
function regexExtractor(fe) {
|
|
50
|
+
return {
|
|
51
|
+
field: fe.field,
|
|
52
|
+
extract: (source) => {
|
|
53
|
+
try {
|
|
54
|
+
const m = source.match(new RegExp(fe.extractor, fe.flags || ''));
|
|
55
|
+
return new FieldResult(fe.field, !!m, m ? (m[1] ?? m[0]) : null);
|
|
56
|
+
} catch {
|
|
57
|
+
return new FieldResult(fe.field, false, null);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Convert resolved DB matchers into scoring-engine matchers.
|
|
65
|
+
* @param {Array} dbMatchers - matchers from the API (with clues + fieldExtractors)
|
|
66
|
+
* @returns {Array} scoring matchers
|
|
67
|
+
*/
|
|
68
|
+
export function adaptDbMatchers(dbMatchers) {
|
|
69
|
+
return (dbMatchers || []).map((m) => {
|
|
70
|
+
const rich = EXTRACTION_REGISTRY[m.documentType];
|
|
71
|
+
const extraction = rich
|
|
72
|
+
? rich
|
|
73
|
+
: { extractors: (m.fieldExtractors || []).map(regexExtractor) };
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
documentType: m.documentType,
|
|
77
|
+
extensions: Array.isArray(m.extensions)
|
|
78
|
+
? m.extensions
|
|
79
|
+
: String(m.extensions || '')
|
|
80
|
+
.split(',')
|
|
81
|
+
.map((s) => s.trim())
|
|
82
|
+
.filter(Boolean),
|
|
83
|
+
minScore: m.minScore ?? undefined,
|
|
84
|
+
priority: m.priority ?? 0,
|
|
85
|
+
qualify: m.qualify ?? undefined,
|
|
86
|
+
clues: (m.clues || []).map((c) => ({
|
|
87
|
+
kind: c.kind,
|
|
88
|
+
pattern: c.pattern,
|
|
89
|
+
flags: c.flags || undefined,
|
|
90
|
+
weight: c.weight ?? 1,
|
|
91
|
+
group: c.group || undefined,
|
|
92
|
+
required: !!c.required,
|
|
93
|
+
negative: !!c.negative,
|
|
94
|
+
})),
|
|
95
|
+
...extraction,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed matchers for the scoring engine (PROTOTYPE).
|
|
3
|
+
*
|
|
4
|
+
* Each entry re-expresses the boolean `match()` of an existing
|
|
5
|
+
* `src/document-types/*.js` definition as a set of weighted **clues**, while
|
|
6
|
+
* REUSING that definition's `extractors` / `resolveType` /
|
|
7
|
+
* `extractNumPedimento` / `extractPedimentoYear` verbatim. Only the SELECTION
|
|
8
|
+
* logic is new — field extraction is unchanged, so a comparison against
|
|
9
|
+
* `extractDocumentFields` isolates the first-match-wins → best-match change.
|
|
10
|
+
*
|
|
11
|
+
* Clue → flag mapping used throughout:
|
|
12
|
+
* - strong positive signal → high `weight`
|
|
13
|
+
* - hard exclusion (return false in the original) → `negative: true`
|
|
14
|
+
* - mandatory signature → `required: true`
|
|
15
|
+
* - `minScore` is tuned so the weighted sum reproduces the original boolean
|
|
16
|
+
* on the existing test fixtures.
|
|
17
|
+
*
|
|
18
|
+
* NOTE: a few original conditions are compound (e.g. simplificado's
|
|
19
|
+
* "COVE: present AND PAGO absent", or inter-agencia's "≥2 distinct RFCs from a
|
|
20
|
+
* set"). Where a single clue cannot express the exact boolean, the closest
|
|
21
|
+
* faithful approximation is used and flagged inline — the comparison harness
|
|
22
|
+
* (`scripts/scoring-compare.js`) surfaces any divergence on a real corpus.
|
|
23
|
+
*/
|
|
24
|
+
import { dodaPdfDefinition } from '../document-types/doda-pdf.js';
|
|
25
|
+
import { dodaXmlDefinition } from '../document-types/doda-xml.js';
|
|
26
|
+
import {
|
|
27
|
+
INTER_AGENCIA_RFCS,
|
|
28
|
+
facturaInterAgenciaDefinition,
|
|
29
|
+
} from '../document-types/factura-inter-agencia.js';
|
|
30
|
+
import { facturasComerciales } from '../document-types/facturas-comerciales.js';
|
|
31
|
+
import { pedimentoCompletoXmlDefinition } from '../document-types/pedimento-completo-xml.js';
|
|
32
|
+
import { pedimentoCompletoDefinition } from '../document-types/pedimento-completo.js';
|
|
33
|
+
import { pedimentoSimplificadoDefinition } from '../document-types/pedimento-simplificado.js';
|
|
34
|
+
import { supportDocumentDefinition } from '../document-types/support-document.js';
|
|
35
|
+
|
|
36
|
+
// Pull the extraction half of a definition (reused unchanged).
|
|
37
|
+
function reuse(def) {
|
|
38
|
+
return {
|
|
39
|
+
extractors: def.extractors,
|
|
40
|
+
resolveType: def.resolveType,
|
|
41
|
+
extractNumPedimento: def.extractNumPedimento,
|
|
42
|
+
extractPedimentoYear: def.extractPedimentoYear,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const PEDIMENTO_NUM = /\d{2}\s?\d{2}\s?\d{4}\s?\d{7}/;
|
|
47
|
+
|
|
48
|
+
// --- pedimento_simplificado --------------------------------------------------
|
|
49
|
+
// Original: AVISO/COVE excluded; title "FORMA SIMPLIFICADA DE[L] PEDIMENTO"
|
|
50
|
+
// short-circuits to true; otherwise the header trio (all three) qualifies.
|
|
51
|
+
// Copy markers are NOT made negative here — they let `pedimento_completo`
|
|
52
|
+
// outscore on completo layouts, which reproduces the title short-circuit.
|
|
53
|
+
const simplificado = {
|
|
54
|
+
documentType: 'pedimento_simplificado',
|
|
55
|
+
extensions: ['pdf'],
|
|
56
|
+
minScore: 3, // title(5) OR full header trio(1+1+1)
|
|
57
|
+
priority: 1,
|
|
58
|
+
...reuse(pedimentoSimplificadoDefinition),
|
|
59
|
+
clues: [
|
|
60
|
+
{ kind: 'CONTENT_REGEX', pattern: /AVISO\s+CONSOLIDADO/i, negative: true },
|
|
61
|
+
{
|
|
62
|
+
kind: 'CONTENT_REGEX',
|
|
63
|
+
pattern: /COMPROBANTE\s+DE\s+VALOR\s+ELECTR[ÓO]NICO/i,
|
|
64
|
+
negative: true,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
kind: 'CONTENT_REGEX',
|
|
68
|
+
pattern: /FORMA\s+SIMPLIFICADA\s+DEL?\s+PEDIMENTO/i,
|
|
69
|
+
weight: 5,
|
|
70
|
+
},
|
|
71
|
+
{ kind: 'CONTENT_REGEX', pattern: /NUM\.?\s*PEDIMENTO:/i, weight: 1 },
|
|
72
|
+
{ kind: 'CONTENT_REGEX', pattern: /CVE\.?\s*PEDIMENTO:/i, weight: 1 },
|
|
73
|
+
{ kind: 'CONTENT_REGEX', pattern: /T\.?\s*OPER:?/i, weight: 1 },
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// --- pedimento_completo ------------------------------------------------------
|
|
78
|
+
// Original: exclude FORMA SIMPLIFICADA & AVISO; (header trio AND ≥1 copy marker)
|
|
79
|
+
// OR clue-count heuristic (>25% of ~18 clues ≈ ≥5).
|
|
80
|
+
const completo = {
|
|
81
|
+
documentType: 'pedimento_completo',
|
|
82
|
+
extensions: ['pdf'],
|
|
83
|
+
minScore: 5, // header trio(3) + 1 copy marker(2), or ≥5 fallback clues
|
|
84
|
+
priority: 1,
|
|
85
|
+
...reuse(pedimentoCompletoDefinition),
|
|
86
|
+
clues: [
|
|
87
|
+
{
|
|
88
|
+
kind: 'CONTENT_REGEX',
|
|
89
|
+
pattern: /FORMA\s+SIMPLIFICADA\s+DEL?\s+PEDIMENTO/i,
|
|
90
|
+
negative: true,
|
|
91
|
+
},
|
|
92
|
+
{ kind: 'CONTENT_REGEX', pattern: /AVISO\s+CONSOLIDADO/i, negative: true },
|
|
93
|
+
// header trio
|
|
94
|
+
{ kind: 'CONTENT_REGEX', pattern: /NUM\.?\s*PEDIMENTO:/i, weight: 1 },
|
|
95
|
+
{ kind: 'CONTENT_REGEX', pattern: /CVE\.?\s*PEDIMENTO:/i, weight: 1 },
|
|
96
|
+
{ kind: 'CONTENT_REGEX', pattern: /T\.?\s*OPER:?/i, weight: 1 },
|
|
97
|
+
// copy markers (long-form pedimento signatures)
|
|
98
|
+
{
|
|
99
|
+
kind: 'CONTENT_REGEX',
|
|
100
|
+
pattern: /ORIGINAL:\s*ADMINISTRACION GENERAL DE ADUANAS/i,
|
|
101
|
+
weight: 2,
|
|
102
|
+
},
|
|
103
|
+
{ kind: 'CONTENT_REGEX', pattern: /SEGUNDA\s+COPIA/i, weight: 2 },
|
|
104
|
+
{ kind: 'CONTENT_REGEX', pattern: /TERCERA\s+COPIA/i, weight: 2 },
|
|
105
|
+
{
|
|
106
|
+
kind: 'CONTENT_REGEX',
|
|
107
|
+
pattern: /COPIA\s+(SIMPLIFICAD[AO])?\s*TRANSPORTISTA/i,
|
|
108
|
+
weight: 2,
|
|
109
|
+
},
|
|
110
|
+
{ kind: 'CONTENT_REGEX', pattern: /DEFINITIVO/i, weight: 2 },
|
|
111
|
+
{ kind: 'CONTENT_REGEX', pattern: /ANEXO\s+DEL\s+PEDIMENTO/i, weight: 2 },
|
|
112
|
+
{
|
|
113
|
+
kind: 'CONTENT_REGEX',
|
|
114
|
+
pattern: /\*+FIN\s+DE\s+PEDIMENTO\s*\*+/i,
|
|
115
|
+
weight: 2,
|
|
116
|
+
},
|
|
117
|
+
// exotic-layout fallback clues (weight 1 each)
|
|
118
|
+
{ kind: 'CONTENT_REGEX', pattern: /CERTIFICACIONES/i, weight: 1 },
|
|
119
|
+
{ kind: 'CONTENT_REGEX', pattern: /CUADRO\s+DE\s+LIQUIDACION/i, weight: 1 },
|
|
120
|
+
{
|
|
121
|
+
kind: 'CONTENT_REGEX',
|
|
122
|
+
pattern: /\*\*\*\s+PAGO\s+ELECTRONICO\s+\*\*\*/i,
|
|
123
|
+
weight: 1,
|
|
124
|
+
},
|
|
125
|
+
{ kind: 'CONTENT_REGEX', pattern: /MEDIOS\s+DE\s+TRANSPORTE/i, weight: 1 },
|
|
126
|
+
{
|
|
127
|
+
kind: 'CONTENT_REGEX',
|
|
128
|
+
pattern: /DATOS\s+DEL\s+IMPORTADOR\/EXPORTADOR/i,
|
|
129
|
+
weight: 1,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
kind: 'CONTENT_REGEX',
|
|
133
|
+
pattern: /DATOS\s+DEL\s+PROVEEDOR\s+O\s+COMPRADOR/i,
|
|
134
|
+
weight: 1,
|
|
135
|
+
},
|
|
136
|
+
{ kind: 'CONTENT_REGEX', pattern: /LINEA\s+DE\s+CAPTURA:/i, weight: 1 },
|
|
137
|
+
{
|
|
138
|
+
kind: 'CONTENT_REGEX',
|
|
139
|
+
pattern: /DECLARO\s+BAJO\s+PROTESTA\s+DE\s+DECIR\s+VERDAD/i,
|
|
140
|
+
weight: 1,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
kind: 'CONTENT_REGEX',
|
|
144
|
+
pattern: /PEDIMENTO\s+ELABORADO\s+DE\s+CONFORMIDAD/i,
|
|
145
|
+
weight: 1,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// --- pedimento_completo_xml --------------------------------------------------
|
|
151
|
+
// Original: single condition — the VUCEM response root tag.
|
|
152
|
+
const completoXml = {
|
|
153
|
+
documentType: 'pedimento_completo_xml',
|
|
154
|
+
extensions: ['xml'],
|
|
155
|
+
minScore: 1,
|
|
156
|
+
priority: 2, // authoritative signal — must dominate doda_xml/support on XML
|
|
157
|
+
...reuse(pedimentoCompletoXmlDefinition),
|
|
158
|
+
clues: [
|
|
159
|
+
{
|
|
160
|
+
kind: 'CONTENT_REGEX',
|
|
161
|
+
pattern: /consultarPedimentoCompletoRespuesta/i,
|
|
162
|
+
weight: 10,
|
|
163
|
+
required: true,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// --- doda_pdf ----------------------------------------------------------------
|
|
169
|
+
// Original: primary marker → true; OR (≥2 secondary + pedimento#);
|
|
170
|
+
// OR (doda-context + pedimento# + ≥1 secondary).
|
|
171
|
+
const dodaPdf = {
|
|
172
|
+
documentType: 'doda_pdf',
|
|
173
|
+
extensions: ['pdf'],
|
|
174
|
+
priority: 1,
|
|
175
|
+
qualify: [
|
|
176
|
+
{ primary: 1 },
|
|
177
|
+
{ secondary: 2, pedimento: 1 },
|
|
178
|
+
{ context: 1, pedimento: 1, secondary: 1 },
|
|
179
|
+
],
|
|
180
|
+
...reuse(dodaPdfDefinition),
|
|
181
|
+
clues: [
|
|
182
|
+
{
|
|
183
|
+
kind: 'CONTENT_REGEX',
|
|
184
|
+
pattern: /DOCUMENTO DE OPERACI[OÓ]N PARA DESPACHO ADUANERO/i,
|
|
185
|
+
weight: 5,
|
|
186
|
+
group: 'primary',
|
|
187
|
+
},
|
|
188
|
+
{ kind: 'CONTENT_REGEX', pattern: /DODA/i, weight: 1, group: 'secondary' },
|
|
189
|
+
{ kind: 'CONTENT_REGEX', pattern: /VUCEM/i, weight: 1, group: 'secondary' },
|
|
190
|
+
{
|
|
191
|
+
kind: 'CONTENT_REGEX',
|
|
192
|
+
pattern: PEDIMENTO_NUM,
|
|
193
|
+
weight: 1,
|
|
194
|
+
group: 'pedimento',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
kind: 'CONTENT_REGEX',
|
|
198
|
+
pattern: /despacho aduanero|operaci[oó]n aduanera|validaci[oó]n/i,
|
|
199
|
+
weight: 1,
|
|
200
|
+
group: 'context',
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// --- doda_xml ----------------------------------------------------------------
|
|
206
|
+
// Original: ≥1 doda marker → true; OR (≥3 pedimento markers AND <?xml).
|
|
207
|
+
const dodaXml = {
|
|
208
|
+
documentType: 'doda_xml',
|
|
209
|
+
extensions: ['xml'],
|
|
210
|
+
priority: 1,
|
|
211
|
+
qualify: [{ doda: 1 }, { pedimento: 3, xml: 1 }],
|
|
212
|
+
...reuse(dodaXmlDefinition),
|
|
213
|
+
clues: [
|
|
214
|
+
{
|
|
215
|
+
kind: 'CONTENT_REGEX',
|
|
216
|
+
pattern: /documentoOperacion/i,
|
|
217
|
+
weight: 3,
|
|
218
|
+
group: 'doda',
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
kind: 'CONTENT_REGEX',
|
|
222
|
+
pattern: /despachoAduanero/i,
|
|
223
|
+
weight: 3,
|
|
224
|
+
group: 'doda',
|
|
225
|
+
},
|
|
226
|
+
{ kind: 'CONTENT_REGEX', pattern: /<doda\b/i, weight: 3, group: 'doda' },
|
|
227
|
+
{
|
|
228
|
+
kind: 'CONTENT_REGEX',
|
|
229
|
+
pattern: /xmlns[^"]*doda/i,
|
|
230
|
+
weight: 3,
|
|
231
|
+
group: 'doda',
|
|
232
|
+
},
|
|
233
|
+
{ kind: 'CONTENT_REGEX', pattern: /VUCEM/i, weight: 3, group: 'doda' },
|
|
234
|
+
{
|
|
235
|
+
kind: 'CONTENT_REGEX',
|
|
236
|
+
pattern: /numPedimento/i,
|
|
237
|
+
weight: 1,
|
|
238
|
+
group: 'pedimento',
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
kind: 'CONTENT_REGEX',
|
|
242
|
+
pattern: /patenteAduanal/i,
|
|
243
|
+
weight: 1,
|
|
244
|
+
group: 'pedimento',
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
kind: 'CONTENT_REGEX',
|
|
248
|
+
pattern: /aduanaDespacho/i,
|
|
249
|
+
weight: 1,
|
|
250
|
+
group: 'pedimento',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
kind: 'CONTENT_REGEX',
|
|
254
|
+
pattern: /tipoOperacion/i,
|
|
255
|
+
weight: 1,
|
|
256
|
+
group: 'pedimento',
|
|
257
|
+
},
|
|
258
|
+
// structural gate for the pedimento-markers path (weight 0 = gate only)
|
|
259
|
+
{ kind: 'CONTENT_REGEX', pattern: /<\?xml/i, weight: 0, group: 'xml' },
|
|
260
|
+
],
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// --- factura_inter_agencia ---------------------------------------------------
|
|
264
|
+
// Original: isCfdiContent AND ≥2 distinct configured RFCs AND broker clave.
|
|
265
|
+
// The pair of agency RFCs and the broker-service clave are modelled as REQUIRED
|
|
266
|
+
// clues, which is what lets this win over `factura_comercial` purely by score —
|
|
267
|
+
// no registration-order dependency. (Scope-limited to the configured pair, same
|
|
268
|
+
// as the original; widening means making RFC presence a counting rule.)
|
|
269
|
+
const interAgencia = {
|
|
270
|
+
documentType: 'factura_inter_agencia',
|
|
271
|
+
extensions: ['xml', 'pdf'],
|
|
272
|
+
minScore: 25, // both required RFCs (10+10) + required clave (5)
|
|
273
|
+
priority: 3,
|
|
274
|
+
...reuse(facturaInterAgenciaDefinition),
|
|
275
|
+
clues: [
|
|
276
|
+
...INTER_AGENCIA_RFCS.map((rfc) => ({
|
|
277
|
+
kind: 'CONTENT_REGEX',
|
|
278
|
+
pattern: new RegExp(`\\b${rfc}\\b`, 'i'),
|
|
279
|
+
weight: 10,
|
|
280
|
+
required: true,
|
|
281
|
+
})),
|
|
282
|
+
// BROKER_SERVICE_CLAVE_PROD_SERV (78141502 = servicios de agentes aduaneros)
|
|
283
|
+
{ kind: 'CONTENT_REGEX', pattern: /78141502/, weight: 5, required: true },
|
|
284
|
+
// CFDI content markers (informational positive signal)
|
|
285
|
+
{ kind: 'CONTENT_REGEX', pattern: /cfdi:Comprobante/i, weight: 1 },
|
|
286
|
+
{ kind: 'CONTENT_REGEX', pattern: /xmlns:cfdi/i, weight: 1 },
|
|
287
|
+
{ kind: 'CONTENT_REGEX', pattern: /TipoDeComprobante/i, weight: 1 },
|
|
288
|
+
],
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
// --- factura_comercial -------------------------------------------------------
|
|
292
|
+
// Original: cfdiMatches≥2 OR (invoiceMatches≥1 AND customsMatches≥1).
|
|
293
|
+
// Faithfully expressed with clue groups + qualify (OR-of-ANDs) — a flat
|
|
294
|
+
// minScore could not enforce the "invoice AND customs" pairing and produced
|
|
295
|
+
// false positives on COVE acuses (customs keywords alone reaching the threshold).
|
|
296
|
+
const facturaComercial = {
|
|
297
|
+
documentType: 'factura_comercial',
|
|
298
|
+
extensions: ['pdf', 'xml'],
|
|
299
|
+
priority: 0,
|
|
300
|
+
qualify: [{ cfdi: 2 }, { invoice: 1, customs: 1 }],
|
|
301
|
+
...reuse(facturasComerciales),
|
|
302
|
+
clues: [
|
|
303
|
+
{ kind: 'CONTENT_REGEX', pattern: /cfdi:Comprobante/i, group: 'cfdi' },
|
|
304
|
+
{ kind: 'CONTENT_REGEX', pattern: /xmlns:cfdi/i, group: 'cfdi' },
|
|
305
|
+
{ kind: 'CONTENT_REGEX', pattern: /TipoDeComprobante/i, group: 'cfdi' },
|
|
306
|
+
{ kind: 'CONTENT_REGEX', pattern: /timbreFiscalDigital/i, group: 'cfdi' },
|
|
307
|
+
{ kind: 'CONTENT_REGEX', pattern: /SelloSAT/i, group: 'cfdi' },
|
|
308
|
+
{
|
|
309
|
+
kind: 'CONTENT_REGEX',
|
|
310
|
+
pattern: /factura\s*(comercial|de\s*venta|de\s*exportaci[oó]n)?/i,
|
|
311
|
+
group: 'invoice',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
kind: 'CONTENT_REGEX',
|
|
315
|
+
pattern: /commercial\s*invoice/i,
|
|
316
|
+
group: 'invoice',
|
|
317
|
+
},
|
|
318
|
+
{ kind: 'CONTENT_REGEX', pattern: /invoice\s*number/i, group: 'invoice' },
|
|
319
|
+
{
|
|
320
|
+
kind: 'CONTENT_REGEX',
|
|
321
|
+
pattern: /n[uú]mero\s*de\s*factura/i,
|
|
322
|
+
group: 'invoice',
|
|
323
|
+
},
|
|
324
|
+
{ kind: 'CONTENT_REGEX', pattern: /pedimento/i, group: 'customs' },
|
|
325
|
+
{ kind: 'CONTENT_REGEX', pattern: /aduana/i, group: 'customs' },
|
|
326
|
+
{
|
|
327
|
+
kind: 'CONTENT_REGEX',
|
|
328
|
+
pattern: /importaci[oó]n|exportaci[oó]n/i,
|
|
329
|
+
group: 'customs',
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
kind: 'CONTENT_REGEX',
|
|
333
|
+
pattern: /despacho\s*aduanero/i,
|
|
334
|
+
group: 'customs',
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
kind: 'CONTENT_REGEX',
|
|
338
|
+
pattern: /fracci[oó]n\s*arancelaria/i,
|
|
339
|
+
group: 'customs',
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// --- support_document --------------------------------------------------------
|
|
345
|
+
// Original: soapFound≥2 OR customsFound≥2. Broad fallback → lowest priority so
|
|
346
|
+
// it only wins when no specific matcher qualifies.
|
|
347
|
+
const supportDocument = {
|
|
348
|
+
documentType: 'support_document',
|
|
349
|
+
extensions: ['xml', 'txt', 'json'],
|
|
350
|
+
priority: -1,
|
|
351
|
+
qualify: [{ soap: 2 }, { customs: 2 }],
|
|
352
|
+
...reuse(supportDocumentDefinition),
|
|
353
|
+
clues: [
|
|
354
|
+
{ kind: 'CONTENT_REGEX', pattern: /soapenv:Envelope/i, group: 'soap' },
|
|
355
|
+
{ kind: 'CONTENT_REGEX', pattern: /xmlns:soapenv=/i, group: 'soap' },
|
|
356
|
+
{
|
|
357
|
+
kind: 'CONTENT_REGEX',
|
|
358
|
+
pattern: /solicitarRecibirCoveServicio/i,
|
|
359
|
+
group: 'soap',
|
|
360
|
+
},
|
|
361
|
+
{ kind: 'CONTENT_REGEX', pattern: /tipoOperacion/i, group: 'soap' },
|
|
362
|
+
{ kind: 'CONTENT_REGEX', pattern: /patenteAduanal/i, group: 'soap' },
|
|
363
|
+
// customs metadata fallback — original requires BOTH patterns present
|
|
364
|
+
{ kind: 'CONTENT_REGEX', pattern: /rfc/i, group: 'customs' },
|
|
365
|
+
{
|
|
366
|
+
kind: 'CONTENT_REGEX',
|
|
367
|
+
pattern: /patente|aduana|customs|pedimento/i,
|
|
368
|
+
group: 'customs',
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Default/global seed set. Order is irrelevant — best-match selects the winner.
|
|
375
|
+
* (This becomes the seed for DEFAULT matchers when the model moves to the DB.)
|
|
376
|
+
*/
|
|
377
|
+
export const scoringMatchers = [
|
|
378
|
+
simplificado,
|
|
379
|
+
completo,
|
|
380
|
+
completoXml,
|
|
381
|
+
dodaPdf,
|
|
382
|
+
dodaXml,
|
|
383
|
+
interAgencia,
|
|
384
|
+
facturaComercial,
|
|
385
|
+
supportDocument,
|
|
386
|
+
];
|