invoice_extractor_arca 0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +49 -0
- data/LICENSE.txt +21 -0
- data/README.md +631 -0
- data/exe/arca-invoice-extract +6 -0
- data/lib/invoice_extractor_arca/catalogs/copy_types.json +5 -0
- data/lib/invoice_extractor_arca/catalogs/currencies.json +65 -0
- data/lib/invoice_extractor_arca/catalogs/document_types.json +40 -0
- data/lib/invoice_extractor_arca/catalogs/iva_types.json +13 -0
- data/lib/invoice_extractor_arca/catalogs/payment_methods.json +10 -0
- data/lib/invoice_extractor_arca/catalogs/units_measure.json +47 -0
- data/lib/invoice_extractor_arca/catalogs/voucher_types.json +99 -0
- data/lib/invoice_extractor_arca/catalogs.rb +34 -0
- data/lib/invoice_extractor_arca/cli.rb +247 -0
- data/lib/invoice_extractor_arca/command_runner.rb +18 -0
- data/lib/invoice_extractor_arca/configuration.rb +42 -0
- data/lib/invoice_extractor_arca/dependencies/resolver.rb +118 -0
- data/lib/invoice_extractor_arca/error.rb +6 -0
- data/lib/invoice_extractor_arca/error_record.rb +39 -0
- data/lib/invoice_extractor_arca/errors.rb +34 -0
- data/lib/invoice_extractor_arca/extractor.rb +381 -0
- data/lib/invoice_extractor_arca/final_payload_builder.rb +67 -0
- data/lib/invoice_extractor_arca/ocr/rapidocr_extract.py +198 -0
- data/lib/invoice_extractor_arca/ocr/rapidocr_text_extractor.rb +117 -0
- data/lib/invoice_extractor_arca/pdf/ocr_text_extractor.rb +131 -0
- data/lib/invoice_extractor_arca/pdf/renderer.rb +97 -0
- data/lib/invoice_extractor_arca/pdf/text_extractor.rb +182 -0
- data/lib/invoice_extractor_arca/qr/payload_parser.rb +124 -0
- data/lib/invoice_extractor_arca/qr/reader.rb +53 -0
- data/lib/invoice_extractor_arca/qr/result_builder.rb +229 -0
- data/lib/invoice_extractor_arca/qr_extractor.rb +156 -0
- data/lib/invoice_extractor_arca/result.rb +94 -0
- data/lib/invoice_extractor_arca/scanners/factory.rb +88 -0
- data/lib/invoice_extractor_arca/scanners/qreader_scan.py +102 -0
- data/lib/invoice_extractor_arca/scanners/qreader_scanner.rb +131 -0
- data/lib/invoice_extractor_arca/scanners/scanner_chain.rb +73 -0
- data/lib/invoice_extractor_arca/scanners/zbar_scanner.rb +52 -0
- data/lib/invoice_extractor_arca/text/invoice_parser.rb +517 -0
- data/lib/invoice_extractor_arca/text/layout_table_parser.rb +218 -0
- data/lib/invoice_extractor_arca/text/locale_value_normalizer.rb +53 -0
- data/lib/invoice_extractor_arca/text/normalizer.rb +31 -0
- data/lib/invoice_extractor_arca/validation/reconciler.rb +53 -0
- data/lib/invoice_extractor_arca/version.rb +5 -0
- data/lib/invoice_extractor_arca/voucher_validator.rb +169 -0
- data/lib/invoice_extractor_arca.rb +117 -0
- data/requirements-ocr.txt +2 -0
- data/sig/invoice_extractor_arca.rbs +4 -0
- metadata +135 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"000": "OTRAS MONEDAS",
|
|
3
|
+
"PES": "PESOS",
|
|
4
|
+
"ARS": "PESOS ARGENTINOS",
|
|
5
|
+
"DOL": "Dólar ESTADOUNIDENSE",
|
|
6
|
+
"002": "Dólar EEUU LIBRE",
|
|
7
|
+
"003": "FRANCOS FRANCESES",
|
|
8
|
+
"004": "LIRAS ITALIANAS",
|
|
9
|
+
"005": "PESETAS",
|
|
10
|
+
"006": "MARCOS ALEMANES",
|
|
11
|
+
"007": "FLORINES HOLANDESES",
|
|
12
|
+
"008": "FRANCOS BELGAS",
|
|
13
|
+
"009": "FRANCOS SUIZOS",
|
|
14
|
+
"010": "PESOS MEJICANOS",
|
|
15
|
+
"011": "PESOS URUGUAYOS",
|
|
16
|
+
"012": "REAL",
|
|
17
|
+
"013": "ESCUDOS PORTUGUESES",
|
|
18
|
+
"014": "CORONAS DANESAS",
|
|
19
|
+
"015": "CORONAS NORUEGAS",
|
|
20
|
+
"016": "CORONAS SUECAS",
|
|
21
|
+
"017": "CHELINES AUTRIACOS",
|
|
22
|
+
"018": "Dólar CANADIENSE",
|
|
23
|
+
"019": "YENS",
|
|
24
|
+
"021": "LIBRA ESTERLINA",
|
|
25
|
+
"022": "MARCOS FINLANDESES",
|
|
26
|
+
"023": "BOLIVAR (VENEZOLANO)",
|
|
27
|
+
"024": "CORONA CHECA",
|
|
28
|
+
"025": "DINAR (YUGOSLAVO)",
|
|
29
|
+
"026": "Dólar AUSTRALIANO",
|
|
30
|
+
"027": "DRACMA (GRIEGO)",
|
|
31
|
+
"028": "FLORIN (ANTILLAS HOLA",
|
|
32
|
+
"029": "GUARANI",
|
|
33
|
+
"030": "SHEKEL (ISRAEL)",
|
|
34
|
+
"031": "PESO BOLIVIANO",
|
|
35
|
+
"032": "PESO COLOMBIANO",
|
|
36
|
+
"033": "PESO CHILENO",
|
|
37
|
+
"034": "RAND (SUDAFRICANO)",
|
|
38
|
+
"035": "NUEVO SOL PERUANO",
|
|
39
|
+
"036": "SUCRE (ECUATORIANO)",
|
|
40
|
+
"040": "LEI RUMANOS",
|
|
41
|
+
"041": "DERECHOS ESPECIALES DE GIRO",
|
|
42
|
+
"042": "PESOS DOMINICANOS",
|
|
43
|
+
"043": "BALBOAS PANAMEÑAS",
|
|
44
|
+
"044": "CORDOBAS NICARAGÛENSES",
|
|
45
|
+
"045": "DIRHAM MARROQUÍES",
|
|
46
|
+
"046": "LIBRAS EGIPCIAS",
|
|
47
|
+
"047": "RIYALS SAUDITAS",
|
|
48
|
+
"048": "BRANCOS BELGAS FINANCIERAS",
|
|
49
|
+
"049": "GRAMOS DE ORO FINO",
|
|
50
|
+
"050": "LIBRAS IRLANDESAS",
|
|
51
|
+
"051": "Dólar DE HONG KONG",
|
|
52
|
+
"052": "Dólar DE SINGAPUR",
|
|
53
|
+
"053": "Dólar DE JAMAICA",
|
|
54
|
+
"054": "Dólar DE TAIWAN",
|
|
55
|
+
"055": "QUETZAL (GUATEMALTECOS)",
|
|
56
|
+
"056": "FORINT (HUNGRIA)",
|
|
57
|
+
"057": "BAHT (TAILANDIA)",
|
|
58
|
+
"058": "ECU",
|
|
59
|
+
"059": "DINAR KUWAITI",
|
|
60
|
+
"060": "EURO",
|
|
61
|
+
"061": "ZLTYS POLACOS",
|
|
62
|
+
"062": "RUPIAS HINDÚES",
|
|
63
|
+
"063": "LEMPIRAS HONDUREÑAS",
|
|
64
|
+
"064": "YUAN (Rep. Pop. China)"
|
|
65
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"0" : "CI Policía Federal",
|
|
3
|
+
"1" : "CI Buenos Aires",
|
|
4
|
+
"2" : "CI Catamarca",
|
|
5
|
+
"3" : "CI Córdoba",
|
|
6
|
+
"4" : "CI Corrientes",
|
|
7
|
+
"5" : "CI Entre Ríos",
|
|
8
|
+
"6" : "CI Jujuy",
|
|
9
|
+
"7" : "CI Mendoza",
|
|
10
|
+
"8" : "CI La Rioja",
|
|
11
|
+
"9" : "CI Salta",
|
|
12
|
+
"10" : "CI San Juan",
|
|
13
|
+
"11" : "CI San Luis",
|
|
14
|
+
"12" : "CI Santa Fe",
|
|
15
|
+
"13" : "CI Santiago del Estero",
|
|
16
|
+
"14" : "CI Tucumán",
|
|
17
|
+
"16" : "CI Chaco",
|
|
18
|
+
"17" : "CI Chubut",
|
|
19
|
+
"18" : "CI Formosa",
|
|
20
|
+
"19" : "CI Misiones",
|
|
21
|
+
"20" : "CI Neuquén",
|
|
22
|
+
"21" : "CI La Pampa",
|
|
23
|
+
"22" : "CI Río Negro",
|
|
24
|
+
"23" : "CI Santa Cruz",
|
|
25
|
+
"24" : "CI Tierra del Fuego",
|
|
26
|
+
"30" : "Certificado de Migración",
|
|
27
|
+
"80" : "CUIT",
|
|
28
|
+
"86" : "CUIL",
|
|
29
|
+
"87" : "CDI",
|
|
30
|
+
"88" : "Usado por Anses para Padrón",
|
|
31
|
+
"89" : "LE",
|
|
32
|
+
"90" : "LC",
|
|
33
|
+
"91" : "CI extranjera",
|
|
34
|
+
"92" : "en trámite",
|
|
35
|
+
"93" : "Acta nacimiento",
|
|
36
|
+
"94" : "Pasaporte",
|
|
37
|
+
"95" : "CI Bs. As. RNP",
|
|
38
|
+
"96" : "DNI",
|
|
39
|
+
"99" : "Sin identificar/venta global diaria"
|
|
40
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"01" : "IVA Responsable Inscripto",
|
|
3
|
+
"04" : "IVA Sujeto Exento",
|
|
4
|
+
"05" : "Consumidor Final",
|
|
5
|
+
"06" : "Responsable Monotributo" ,
|
|
6
|
+
"07" : "Sujeto no Categorizado",
|
|
7
|
+
"08" : "Proveedor del Exterior",
|
|
8
|
+
"09" : "Cliente del Exterior",
|
|
9
|
+
"10" : "IVA Liberado – Ley Nº 19.640",
|
|
10
|
+
"13" : "Monotributista Social",
|
|
11
|
+
"15" : "IVA No Alcanzado",
|
|
12
|
+
"16" : "Monotributo Trabajador Independiente Promovido"
|
|
13
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"01": "kilogramos",
|
|
3
|
+
"02": "metros",
|
|
4
|
+
"03": "metros cuadrados",
|
|
5
|
+
"04": "metros cúbicos",
|
|
6
|
+
"05": "litros",
|
|
7
|
+
"06": "1000 kWh",
|
|
8
|
+
"07": "unidades",
|
|
9
|
+
"08": "pares",
|
|
10
|
+
"09": "docenas",
|
|
11
|
+
"10": "quilates",
|
|
12
|
+
"11": "millares",
|
|
13
|
+
"12": "gramos",
|
|
14
|
+
"13": "milimetros",
|
|
15
|
+
"14": "mm cúbicos",
|
|
16
|
+
"15": "kilómetros",
|
|
17
|
+
"16": "hectolitros",
|
|
18
|
+
"17": "centímetros",
|
|
19
|
+
"18": "jgo. pqt. mazo naipes",
|
|
20
|
+
"19": "cm cúbicos",
|
|
21
|
+
"20": "toneladas",
|
|
22
|
+
"21": "dam cúbicos",
|
|
23
|
+
"22": "hm cúbicos",
|
|
24
|
+
"23": "km cúbicos",
|
|
25
|
+
"24": "microgramos",
|
|
26
|
+
"25": "nanogramos",
|
|
27
|
+
"26": "picogramos",
|
|
28
|
+
"27": "miligramos",
|
|
29
|
+
"28": "mililitros",
|
|
30
|
+
"29": "curie",
|
|
31
|
+
"30": "milicurie",
|
|
32
|
+
"31": "microcurie",
|
|
33
|
+
"32": "uiacthor",
|
|
34
|
+
"33": "muiacthor",
|
|
35
|
+
"34": "kg base",
|
|
36
|
+
"35": "gruesa",
|
|
37
|
+
"36": "kg bruto",
|
|
38
|
+
"37": "uiactant",
|
|
39
|
+
"38": "muiactant",
|
|
40
|
+
"39": "uiactig",
|
|
41
|
+
"40": "muiactig",
|
|
42
|
+
"41": "kg activo",
|
|
43
|
+
"42": "gramo activo",
|
|
44
|
+
"43": "gramo base",
|
|
45
|
+
"44": "packs",
|
|
46
|
+
"45": "otras unidades"
|
|
47
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"001": "FACTURA A",
|
|
3
|
+
"002": "NOTA DE DEBITO A",
|
|
4
|
+
"003": "NOTA DE CREDITO A",
|
|
5
|
+
"004": "RECIBO A",
|
|
6
|
+
"005": "NOTAS DE VENTA AL CONTADO A",
|
|
7
|
+
"006": "FACTURA B",
|
|
8
|
+
"007": "NOTA DE DEBITO B",
|
|
9
|
+
"008": "NOTA DE CREDITO B",
|
|
10
|
+
"009": "RECIBO B",
|
|
11
|
+
"010": "NOTAS DE VENTA AL CONTADO B",
|
|
12
|
+
"011": "FACTURA C",
|
|
13
|
+
"012": "NOTA DE DEBITO C",
|
|
14
|
+
"013": "NOTA DE CREDITO C",
|
|
15
|
+
"015": "RECIBO C",
|
|
16
|
+
"016": "NOTAS DE VENTA AL CONTADO C",
|
|
17
|
+
"017": "LIQUIDACION DE SERVICIOS PUBLICOS CLASE A",
|
|
18
|
+
"018": "LIQUIDACION DE SERVICIOS PUBLICOS CLASE B",
|
|
19
|
+
"019": "FACTURA POR OPERACIONES CON EL EXTERIOR",
|
|
20
|
+
"020": "NOTA DE DEBITO POR OPERACIONES CON EL EXTERIOR",
|
|
21
|
+
"021": "NOTA DE CREDITO POR OPERACIONES CON EL EXTERIOR",
|
|
22
|
+
"022": "FACTURA PERMISO EXPORTACION SIMPLIFICADO - DTO. 855/97",
|
|
23
|
+
"023": "COMPROBANTES CLASE “A” DE COMPRA PRIMARIA PARA EL SECTOR PESQUERO MARITIMO",
|
|
24
|
+
"024": "COMPROBANTES CLASE “A” DE CONSIGNACION PRIMARIA PARA EL SECTOR PESQUERO MARITIMO",
|
|
25
|
+
"025": "COMPROBANTES CLASE “B” DE COMPRA PRIMARIA PARA EL SECTOR PESQUERO MARITIMO",
|
|
26
|
+
"026": "COMPROBANTES CLASE “B” DE CONSIGNACION PRIMARIA PARA EL SECTOR PESQUERO MARITIMO",
|
|
27
|
+
"027": "LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE A",
|
|
28
|
+
"028": "LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE B",
|
|
29
|
+
"029": "LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE C",
|
|
30
|
+
"030": "COMPROBANTE DE COMPRA DE BIENES USADOS",
|
|
31
|
+
"031": "MANDATO - CONSIGNACION",
|
|
32
|
+
"032": "COMPROBANTE DE COMPRA DE MATERIALES A RECICLAR PROVENIENTE DE RESIDUOS",
|
|
33
|
+
"033": "LIQUIDACION PRIMARIA DE GRANOS",
|
|
34
|
+
"034": "COMPROBANTES A DEL ANEXO I, APARTADO A, INCISO F) R.G. N° 1415",
|
|
35
|
+
"035": "COMPROBANTES B DEL ANEXO I, APARTADO A, INC. F), R.G. N° 1415",
|
|
36
|
+
"036": "COMPROBANTES C DEL ANEXO I, APARTADO A, INC.F), R.G. N° 1415",
|
|
37
|
+
"037": "NOTAS DE DEBITO O DOCUMENTOS EQUIVALENTES QUE CUMPLAN CON LA R.G. N° 1415",
|
|
38
|
+
"038": "NOTAS DE CREDITO O DOCUMENTOS EQUIVALENTES QUE CUMPLAN CON LA R.G. N° 1415",
|
|
39
|
+
"039": "OTROS COMPROBANTES A QUE CUMPLEN CON LA R G N° 1415",
|
|
40
|
+
"040": "OTROS COMPROBANTES B QUE CUMPLAN CON LA R.G. N° 1415",
|
|
41
|
+
"041": "OTROS COMPROBANTES C QUE CUMPLAN CON LA R.G. N° 1415",
|
|
42
|
+
"043": "NOTA DE CREDITO LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE B",
|
|
43
|
+
"044": "NOTA DE CREDITO LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE C",
|
|
44
|
+
"045": "NOTA DE DEBITO LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE A",
|
|
45
|
+
"046": "NOTA DE DEBITO LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE B",
|
|
46
|
+
"047": "NOTA DE DEBITO LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE C",
|
|
47
|
+
"048": "NOTA DE CREDITO LIQUIDACION UNICA COMERCIAL IMPOSITIVA CLASE A",
|
|
48
|
+
"049": "COMPROBANTES DE COMPRA DE BIENES USADOS NO REGISTRABLES A CONSUMIDORES FINALES",
|
|
49
|
+
"051": "FACTURA A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”",
|
|
50
|
+
"052": "NOTA DE DEBITO A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”",
|
|
51
|
+
"053": "NOTA DE CREDITO A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”",
|
|
52
|
+
"054": "RECIBO A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”",
|
|
53
|
+
"055": "NOTAS DE VENTA AL CONTADO A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”",
|
|
54
|
+
"056": "COMPROBANTES A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN” DEL ANEXO I, APARTADO A, INC. F), R.G. N° 1.415",
|
|
55
|
+
"057": "OTROS COMPROBANTES A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”QUE CUMPLAN CON LA R.G. N° 1.415",
|
|
56
|
+
"058": "CUENTA DE VENTA Y LIQUIDO PRODUCTO A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”",
|
|
57
|
+
"059": "LIQUIDACION A CON LEYENDA “OPERACIÓN SUJETA A RETENCIÓN”",
|
|
58
|
+
"060": "CUENTAS DE VENTA Y LIQUIDO PRODUCTO A",
|
|
59
|
+
"061": "CUENTAS DE VENTA Y LIQUIDO PRODUCTO B",
|
|
60
|
+
"063": "LIQUIDACIONES A",
|
|
61
|
+
"064": "LIQUIDACIONES B",
|
|
62
|
+
"066": "DESPACHO DE IMPORTACION",
|
|
63
|
+
"080": "INFORME DIARIO DE CIERRE (ZETA) - CONTROLADORES FISCALES (*)",
|
|
64
|
+
"081": "TIQUE FACTURA A (*)",
|
|
65
|
+
"082": "TIQUE FACTURA B (*)",
|
|
66
|
+
"083": "TIQUE (*)",
|
|
67
|
+
"088": "REMITO ELECTRONICO",
|
|
68
|
+
"089": "RESUMEN DE DATOS",
|
|
69
|
+
"090": "OTROS COMPROBANTES - DOCUMENTOS EXCEPTUADOS - NOTAS DE CREDITO",
|
|
70
|
+
"091": "REMITOS R",
|
|
71
|
+
"099": "OTROS COMPROBANTES QUE NO CUMPLEN O ESTÁN EXCEPTUADOS DE LA R.G. 1415 Y SUS MODIF",
|
|
72
|
+
"109": "TIQUE C",
|
|
73
|
+
"110": "TIQUE NOTA DE CREDITO (*)",
|
|
74
|
+
"111": "TIQUE FACTURA C (*)",
|
|
75
|
+
"112": "TIQUE NOTA DE CREDITO A (*)",
|
|
76
|
+
"113": "TIQUE NOTA DE CREDITO B (*)",
|
|
77
|
+
"114": "TIQUE NOTA DE CREDITO C (*)",
|
|
78
|
+
"115": "TIQUE NOTA DE DEBITO A (*)",
|
|
79
|
+
"116": "TIQUE NOTA DE DEBITO B (*)",
|
|
80
|
+
"117": "TIQUE NOTA DE DEBITO C (*)",
|
|
81
|
+
"201": "FACTURA DE CRÉDITO ELECTRÓNICA MiPyMEs (FCE) A",
|
|
82
|
+
"202": "NOTA DE DEBITO ELECTRÓNICA MiPyMEs (FCE) A",
|
|
83
|
+
"203": "NOTA DE CREDITO ELECTRÓNICA MiPyMEs (FCE) A",
|
|
84
|
+
"206": "FACTURA DE CRÉDITO ELECTRÓNICA MiPyMEs (FCE) B",
|
|
85
|
+
"207": "NOTA DE DEBITO ELECTRÓNICA MiPyMEs (FCE) B",
|
|
86
|
+
"208": "NOTA DE CREDITO ELECTRÓNICA MiPyMEs (FCE) B",
|
|
87
|
+
"211": "FACTURA DE CRÉDITO ELECTRÓNICA MiPyMEs (FCE) C",
|
|
88
|
+
"212": "NOTA DE DEBITO ELECTRÓNICA MiPyMEs (FCE) C",
|
|
89
|
+
"213": "NOTA DE CREDITO ELECTRÓNICA MiPyMEs (FCE) C",
|
|
90
|
+
"331": "LIQUIDACION SECUNDARIA DE GRANOS",
|
|
91
|
+
"332": "CERTIFICACION ELECTRONICA (GRANOS)",
|
|
92
|
+
"991": "REMITO ELECTRÓNICO DE TABACO EN HEBRA",
|
|
93
|
+
"992": "RESUMEN DE DATOS DE TABACO EN HEBRA",
|
|
94
|
+
"993": "REMITO ELECTRÓNICO HARINERO - AUTOMOTOR",
|
|
95
|
+
"994": "REMITO ELECTRÓNICO HARINERO - FERROVIARIO",
|
|
96
|
+
"995": "REMITO ELECTRÓNICO CÁRNICO",
|
|
97
|
+
"997": "Remito Electrónico para Azúcar, Alcohol y Subproductos -Mercado Interno-",
|
|
98
|
+
"998": "Remito Electrónico para Azúcar, Alcohol y Subproductos -Exportación-"
|
|
99
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module InvoiceExtractorArca
|
|
6
|
+
module Catalogs
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def fetch(catalog, code)
|
|
10
|
+
data(catalog).fetch(code)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def data(catalog)
|
|
14
|
+
@data ||= {}
|
|
15
|
+
|
|
16
|
+
@data[catalog] ||= begin
|
|
17
|
+
path = File.expand_path(
|
|
18
|
+
"catalogs/#{catalog}.json",
|
|
19
|
+
__dir__
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
JSON.parse(File.read(path))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
VOUCHER_TYPES = data(:voucher_types)
|
|
27
|
+
CURRENCIES = data(:currencies)
|
|
28
|
+
DOCUMENT_TYPES = data(:document_types)
|
|
29
|
+
IVA_TYPES = data(:iva_types)
|
|
30
|
+
PAYMENT_METHODS = data(:payment_methods)
|
|
31
|
+
UNITS_MEASURE = data(:units_measure)
|
|
32
|
+
COPY_TYPES = data(:copy_types)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "optparse"
|
|
5
|
+
|
|
6
|
+
module InvoiceExtractorArca
|
|
7
|
+
class CLI
|
|
8
|
+
EXIT_SUCCESS = 0
|
|
9
|
+
EXIT_EXTRACTION_FAILED = 1
|
|
10
|
+
EXIT_USAGE_ERROR = 2
|
|
11
|
+
EXIT_RUNTIME_ERROR = 3
|
|
12
|
+
|
|
13
|
+
def initialize(
|
|
14
|
+
argv,
|
|
15
|
+
stdout: $stdout,
|
|
16
|
+
stderr: $stderr,
|
|
17
|
+
extractor: InvoiceExtractorArca.method(:extract)
|
|
18
|
+
)
|
|
19
|
+
@argv = argv.dup
|
|
20
|
+
@stdout = stdout
|
|
21
|
+
@stderr = stderr
|
|
22
|
+
@extractor = extractor
|
|
23
|
+
@options = default_options
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run
|
|
27
|
+
parser = build_parser
|
|
28
|
+
parser.parse!(@argv)
|
|
29
|
+
|
|
30
|
+
if @options.delete(:help)
|
|
31
|
+
@stdout.puts parser
|
|
32
|
+
return EXIT_SUCCESS
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
input_path = @argv.shift
|
|
36
|
+
raise OptionParser::MissingArgument, "invoice file" unless input_path
|
|
37
|
+
|
|
38
|
+
unless @argv.empty?
|
|
39
|
+
raise OptionParser::InvalidArgument,
|
|
40
|
+
"unexpected arguments: #{@argv.join(" ")}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
result = @extractor.call(input_path, **extractor_options)
|
|
44
|
+
|
|
45
|
+
print_result(result)
|
|
46
|
+
|
|
47
|
+
result.success? ? EXIT_SUCCESS : EXIT_EXTRACTION_FAILED
|
|
48
|
+
rescue OptionParser::ParseError => e
|
|
49
|
+
@stderr.puts "Error: #{e.message}"
|
|
50
|
+
@stderr.puts
|
|
51
|
+
@stderr.puts build_parser
|
|
52
|
+
EXIT_USAGE_ERROR
|
|
53
|
+
rescue InvoiceExtractorArca::Error => e
|
|
54
|
+
print_exception(e)
|
|
55
|
+
EXIT_EXTRACTION_FAILED
|
|
56
|
+
rescue SystemCallError => e
|
|
57
|
+
@stderr.puts JSON.generate(
|
|
58
|
+
success: false,
|
|
59
|
+
error: {
|
|
60
|
+
code: "system_error",
|
|
61
|
+
message: e.message
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
EXIT_RUNTIME_ERROR
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def default_options
|
|
70
|
+
{
|
|
71
|
+
pretty: false,
|
|
72
|
+
strict: false,
|
|
73
|
+
enrichment: true,
|
|
74
|
+
page_range: nil,
|
|
75
|
+
resolution: 300,
|
|
76
|
+
multiple_qr: :first_valid,
|
|
77
|
+
qr_scanner: :zbar_then_qreader,
|
|
78
|
+
dependency_paths: {}
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_parser
|
|
83
|
+
OptionParser.new do |parser|
|
|
84
|
+
parser.banner =
|
|
85
|
+
"Usage: arca-invoice-extract [options] INVOICE_FILE"
|
|
86
|
+
|
|
87
|
+
parser.separator ""
|
|
88
|
+
parser.separator "Extract ARCA invoice data as JSON."
|
|
89
|
+
parser.separator ""
|
|
90
|
+
|
|
91
|
+
parser.on(
|
|
92
|
+
"--pretty",
|
|
93
|
+
"Pretty-print JSON output"
|
|
94
|
+
) do
|
|
95
|
+
@options[:pretty] = true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
parser.on(
|
|
99
|
+
"--strict",
|
|
100
|
+
"Raise extraction errors instead of returning them"
|
|
101
|
+
) do
|
|
102
|
+
@options[:strict] = true
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
parser.on(
|
|
106
|
+
"--[no-]enrichment",
|
|
107
|
+
"Enable or disable text/OCR enrichment"
|
|
108
|
+
) do |value|
|
|
109
|
+
@options[:enrichment] = value
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
parser.on(
|
|
113
|
+
"--page PAGE",
|
|
114
|
+
Integer,
|
|
115
|
+
"Process one PDF page"
|
|
116
|
+
) do |page|
|
|
117
|
+
positive_integer!(page, "--page")
|
|
118
|
+
@options[:page_range] = page
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
parser.on(
|
|
122
|
+
"--pages RANGE",
|
|
123
|
+
"Process a page range, for example 1-5"
|
|
124
|
+
) do |value|
|
|
125
|
+
@options[:page_range] = parse_page_range(value)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
parser.on(
|
|
129
|
+
"--resolution DPI",
|
|
130
|
+
Integer,
|
|
131
|
+
"PDF rendering resolution, default: 300"
|
|
132
|
+
) do |resolution|
|
|
133
|
+
positive_integer!(resolution, "--resolution")
|
|
134
|
+
@options[:resolution] = resolution
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
parser.on(
|
|
138
|
+
"--qr-scanner NAME",
|
|
139
|
+
%w[zbar qreader zbar_then_qreader qreader_then_zbar all],
|
|
140
|
+
"QR scanner backend"
|
|
141
|
+
) do |scanner|
|
|
142
|
+
@options[:qr_scanner] = scanner.to_sym
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
parser.on(
|
|
146
|
+
"--multiple-qr MODE",
|
|
147
|
+
%w[first_valid error],
|
|
148
|
+
"Multiple-QR behavior"
|
|
149
|
+
) do |mode|
|
|
150
|
+
@options[:multiple_qr] = mode.to_sym
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
parser.on(
|
|
154
|
+
"--dependency NAME=PATH",
|
|
155
|
+
"Set an external dependency path; repeatable"
|
|
156
|
+
) do |value|
|
|
157
|
+
name, path = value.split("=", 2)
|
|
158
|
+
|
|
159
|
+
if name.to_s.empty? || path.to_s.empty?
|
|
160
|
+
raise OptionParser::InvalidArgument,
|
|
161
|
+
"dependency must use NAME=PATH"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
@options[:dependency_paths][name.to_sym] = path
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
parser.on(
|
|
168
|
+
"-h",
|
|
169
|
+
"--help",
|
|
170
|
+
"Show this help"
|
|
171
|
+
) do
|
|
172
|
+
@options[:help] = true
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def parse_page_range(value)
|
|
178
|
+
match = value.match(/\A(\d+)-(\d+)\z/)
|
|
179
|
+
|
|
180
|
+
unless match
|
|
181
|
+
raise OptionParser::InvalidArgument,
|
|
182
|
+
"page range must use FIRST-LAST"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
first = Integer(match[1])
|
|
186
|
+
last = Integer(match[2])
|
|
187
|
+
|
|
188
|
+
if first.zero? || last.zero? || first > last
|
|
189
|
+
raise OptionParser::InvalidArgument,
|
|
190
|
+
"page range must be positive and ordered"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
first..last
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def positive_integer!(value, option)
|
|
197
|
+
return if value.positive?
|
|
198
|
+
|
|
199
|
+
raise OptionParser::InvalidArgument,
|
|
200
|
+
"#{option} must be positive"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def extractor_options
|
|
204
|
+
@options.reject do |name, value|
|
|
205
|
+
name == :pretty || value.nil?
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def print_result(result)
|
|
210
|
+
payload = result.to_h
|
|
211
|
+
|
|
212
|
+
json =
|
|
213
|
+
if @options[:pretty]
|
|
214
|
+
JSON.pretty_generate(payload)
|
|
215
|
+
else
|
|
216
|
+
JSON.generate(payload)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
@stdout.puts json
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def print_exception(exception)
|
|
223
|
+
payload = {
|
|
224
|
+
success: false,
|
|
225
|
+
error: {
|
|
226
|
+
code: underscore(exception.class.name.split("::").last),
|
|
227
|
+
message: exception.message
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
json =
|
|
232
|
+
if @options[:pretty]
|
|
233
|
+
JSON.pretty_generate(payload)
|
|
234
|
+
else
|
|
235
|
+
JSON.generate(payload)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
@stderr.puts json
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def underscore(value)
|
|
242
|
+
value
|
|
243
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
244
|
+
.downcase
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
5
|
+
module InvoiceExtractorArca
|
|
6
|
+
class CommandRunner
|
|
7
|
+
def run(*command)
|
|
8
|
+
stdout, stderr, status = Open3.capture3(*command)
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
stdout: stdout,
|
|
12
|
+
stderr: stderr,
|
|
13
|
+
status: status.exitstatus,
|
|
14
|
+
success: status.success?
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module InvoiceExtractorArca
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_reader :strict,
|
|
6
|
+
:dependency_paths,
|
|
7
|
+
:page_range,
|
|
8
|
+
:resolution,
|
|
9
|
+
:multiple_qr,
|
|
10
|
+
:qr_scanner,
|
|
11
|
+
:python_command,
|
|
12
|
+
:enrichment
|
|
13
|
+
|
|
14
|
+
def initialize(
|
|
15
|
+
strict: false,
|
|
16
|
+
dependency_paths: {},
|
|
17
|
+
page_range: nil,
|
|
18
|
+
resolution: 300,
|
|
19
|
+
multiple_qr: :first_valid,
|
|
20
|
+
qr_scanner: :zbar_then_qreader,
|
|
21
|
+
python_command: :python3,
|
|
22
|
+
enrichment: true
|
|
23
|
+
)
|
|
24
|
+
@strict = strict
|
|
25
|
+
@dependency_paths = dependency_paths || {}
|
|
26
|
+
@page_range = page_range
|
|
27
|
+
@resolution = resolution
|
|
28
|
+
@multiple_qr = multiple_qr
|
|
29
|
+
@qr_scanner = qr_scanner
|
|
30
|
+
@python_command = python_command
|
|
31
|
+
@enrichment = enrichment
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def strict?
|
|
35
|
+
strict == true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def enrichment?
|
|
39
|
+
enrichment == true
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|