invoicehn 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 633195e813e47b48266e5c5dd70026b7fb22f3a3c839d53a9c79e37cb4861cd9
4
+ data.tar.gz: 4970c4b593fb55f38ee90819b063444664bb07e0a00bc8b039f0e1b4d6c340c3
5
+ SHA512:
6
+ metadata.gz: fbcecd6f82d560df0dd182e1cd0d57439a3c2c4f4de2cf5ac2980e838ad2746831d5775d54c894a2e474113f67ed7096610cb1f1a184aa2a0512851deab6fb40
7
+ data.tar.gz: 52a36d88796c7699fd29d193df20a16fb85651da782963bdd243e38557b6a73221478ce25b711083fdea4fa19d3535cfbfbe4d53090670d387dba70435f20a9d
data/CHANGELOG.md ADDED
@@ -0,0 +1,59 @@
1
+ # Changelog
2
+
3
+ Formato basado en [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/).
4
+ Este proyecto sigue [Versionado Semántico](https://semver.org/lang/es/).
5
+
6
+ ## [Sin publicar]
7
+
8
+ ## [0.1.0] — 2026-08-28
9
+
10
+ Primera versión. Emisión de Facturas (Comprobante Fiscal tipo 01) conforme al
11
+ Reglamento del Régimen de Facturación, Acuerdo 481-2017 y sus reformas
12
+ (609-2017, 725-2018 y 817-2018).
13
+
14
+ ### Añadido
15
+
16
+ - **Requisitos del formato (Art. 10)**: datos de identificación del emisor,
17
+ denominación "Factura", CAI, fecha límite de emisión, rango autorizado,
18
+ destino de los ejemplares, correlativo de 16 dígitos
19
+ `NNN-NNN-NN-NNNNNNNN`, datos del adquirente exonerado, y la línea de
20
+ descuentos y rebajas del numeral 10.
21
+ - **Requisitos al momento de la emisión (Art. 11)**: identificación del cliente
22
+ según su tipo, discriminación de valores exentos, exonerados y gravados,
23
+ subtotales e impuestos por tarifa, descuentos, denominación de la moneda,
24
+ importe total en números y letras, umbral de L 10,000.00 para consumidores
25
+ finales, y tasa de cambio vigente en facturas en moneda extranjera.
26
+ - **Redondeo estatutario** del Artículo 9 de la Ley del Impuesto Sobre Ventas:
27
+ medio hacia arriba en 0.005 de Lempira. `Float` está prohibido en todo el
28
+ camino del dinero, con una prueba que recorre el código fuente y falla si
29
+ aparece.
30
+ - **Los descuentos reducen la base gravable**, conforme al Artículo 3 de la Ley
31
+ del ISV.
32
+ - **Tasas del ISV** del Decreto 278-2013: 15% general y 18% especial.
33
+ - **Control de autorizaciones**: un identificador acumula autorizaciones; el
34
+ asignador elige la vigente que cubra el correlativo siguiente. La emisión se
35
+ bloquea si la autorización venció (Art. 62) o el número queda fuera del rango.
36
+ - **Numeración sin huecos ni reutilización**, bajo candado exclusivo de archivo;
37
+ la asignación y el guardado ocurren en un solo paso atómico.
38
+ - **Anulación (Art. 41)** que conserva el correlativo consumido.
39
+ - **Libro contable (Art. 53 num. 1)** con interfaz `Invoicehn::Ledger` e
40
+ implementación JSONL incluida, y exportación en texto (Art. 53 num. 5).
41
+ - **Interfaz de terminal** con subcomandos y asistente interactivo, en español o
42
+ inglés. El documento fiscal se emite siempre en español.
43
+ - **Salidas** en texto, PDF y JSON/CSV.
44
+
45
+ ### Notas
46
+
47
+ - La gema cubre el contenido del documento. La inscripción al Régimen de
48
+ Facturación (Art. 45), la inscripción como autoimpresor (Art. 47), la
49
+ Declaración Jurada del sistema computarizado (Art. 53) y la obtención del CAI,
50
+ el rango y la fecha límite (Arts. 59-61) siguen siendo obligación del obligado
51
+ tributario.
52
+ - El RTN se valida por longitud y dígitos únicamente: no existe un dígito
53
+ verificador documentado públicamente. El CAI se almacena e imprime tal cual,
54
+ porque ninguna norma fija su formato.
55
+ - La leyenda "La factura es beneficio de todos, exíjala" no está exigida por
56
+ ninguna norma; se admite como texto opcional.
57
+
58
+ [Sin publicar]: https://github.com/JorgePadilla/invoicehn/compare/v0.1.0...HEAD
59
+ [0.1.0]: https://github.com/JorgePadilla/invoicehn/releases/tag/v0.1.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jorge Padilla
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,430 @@
1
+ # invoicehn
2
+
3
+ [![CI](https://github.com/JorgePadilla/invoicehn/actions/workflows/ci.yml/badge.svg)](https://github.com/JorgePadilla/invoicehn/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/invoicehn.svg)](https://rubygems.org/gems/invoicehn)
5
+
6
+ Facturación para Honduras conforme al **Reglamento del Régimen de Facturación,
7
+ Otros Documentos Fiscales y Registro Fiscal de Imprentas** (Acuerdo No. 481-2017
8
+ y sus reformas). Biblioteca Ruby más una interfaz de terminal.
9
+
10
+ ```
11
+ invoicehn setup # datos del emisor (Art. 10 num. 1)
12
+ invoicehn auth add # el CAI, el rango y la fecha límite que otorga el SAR
13
+ invoicehn new # emitir una factura (asistente interactivo)
14
+ invoicehn check # ¿se puede emitir ahora mismo?
15
+ ```
16
+
17
+ ---
18
+
19
+ ## Lo que esta gema hace y lo que no
20
+
21
+ Esto es lo primero que debe quedar claro, porque el software sólo cubre una mitad
22
+ de lo que la ley exige.
23
+
24
+ **Cubre el contenido del documento.** Todos los campos que exigen los Artículos
25
+ 10 (requisitos del formato) y 11 (requisitos al momento de la emisión): el
26
+ correlativo de 16 dígitos, el control del CAI, del rango autorizado y de la fecha
27
+ límite de emisión, la discriminación del ISV por tarifa, los descuentos, el total
28
+ en números y letras, y el redondeo estatutario.
29
+
30
+ **No otorga autorización.** Siguen siendo obligación del obligado tributario:
31
+
32
+ | Obligación | Artículo |
33
+ |---|---|
34
+ | Inscribirse en el Régimen de Facturación | Art. 45 |
35
+ | Inscribirse como autoimpresor | Art. 47 |
36
+ | Presentar la Declaración Jurada del sistema computarizado | Art. 53 |
37
+ | Obtener el CAI, el rango autorizado y la fecha límite | Arts. 59-61 |
38
+ | Comunicar los documentos no utilizados | Art. 42 |
39
+ | Custodiar los documentos por el plazo de prescripción | Art. 43 |
40
+
41
+ La gema **consume** el CAI, el rango y la fecha límite; nunca los inventa.
42
+ Una factura generada aquí sin una autorización real del SAR no es un documento
43
+ fiscal válido.
44
+
45
+ ---
46
+
47
+ ## Instalación
48
+
49
+ ```ruby
50
+ # Gemfile
51
+ gem "invoicehn"
52
+ ```
53
+
54
+ o `gem install invoicehn`. Requiere Ruby >= 3.1.
55
+
56
+ ---
57
+
58
+ ## Uso desde la terminal
59
+
60
+ ### 1. Configurar el emisor
61
+
62
+ ```
63
+ invoicehn setup
64
+ ```
65
+
66
+ Pide los siete datos del Art. 10 num. 1: RTN, nombre o razón social, nombre
67
+ comercial, dirección de la casa matriz, dirección del establecimiento del punto
68
+ de emisión, teléfono y correo electrónico. Todos son obligatorios; la emisión se
69
+ bloquea si falta alguno.
70
+
71
+ También acepta banderas, para instalaciones desatendidas:
72
+
73
+ ```
74
+ invoicehn setup --rtn 08011990123456 \
75
+ --legal-name "Comercial Ejemplo, S. de R.L." \
76
+ --trade-name "Ferretería Ejemplo" \
77
+ --address "Col. Palmira, Tegucigalpa M.D.C." \
78
+ --phone "2222-3333" --email "facturacion@ejemplo.hn"
79
+ ```
80
+
81
+ ### 2. Registrar la autorización del SAR
82
+
83
+ Copie los valores del documento de autorización:
84
+
85
+ ```
86
+ invoicehn auth add --cai "ABCD12-345678-9ABCDE-F01234-567890-AB" \
87
+ --from "000-001-01-00000001" --to "000-001-01-00000500" \
88
+ --limit "2027-06-30"
89
+ ```
90
+
91
+ Un mismo identificador acumula autorizaciones con el tiempo: cuando un rango se
92
+ agota y el SAR concede otro que continúa la numeración, se registra el nuevo sin
93
+ borrar el anterior. El asignador elige la autorización vigente que cubra el
94
+ correlativo que sigue.
95
+
96
+ ### 3. Emitir
97
+
98
+ Interactivo:
99
+
100
+ ```
101
+ invoicehn new
102
+ ```
103
+
104
+ O desde un archivo, para integrarlo con otro sistema:
105
+
106
+ ```json
107
+ {
108
+ "customer": {
109
+ "kind": "taxpayer",
110
+ "name": "Distribuidora del Norte, S.A.",
111
+ "rtn": "05019005123456"
112
+ },
113
+ "items": [
114
+ { "description": "Cemento gris bolsa 42.5 kg", "quantity": 20,
115
+ "unit_price": "235.00", "discount": "200.00", "treatment": "gravado_15" },
116
+ { "description": "Cerveza nacional caja 24 unidades", "quantity": 3,
117
+ "unit_price": "480.00", "treatment": "gravado_18" },
118
+ { "description": "Medicamento esencial", "quantity": 10,
119
+ "unit_price": "12.50", "treatment": "exento" }
120
+ ]
121
+ }
122
+ ```
123
+
124
+ ```
125
+ invoicehn issue -f venta.json
126
+ ```
127
+
128
+ ### 4. Consultar, anular, exportar
129
+
130
+ ```
131
+ invoicehn show 000-001-01-00000001 # texto
132
+ invoicehn show 000-001-01-00000001 -f json # JSON
133
+ invoicehn pdf 000-001-01-00000001 -o f.pdf
134
+ invoicehn list --from 2026-01-01 --to 2026-12-31
135
+ invoicehn annul 000-001-01-00000002 -r "Error en la cantidad"
136
+ invoicehn export --format csv -o ventas.csv # Art. 53 num. 5
137
+ invoicehn check
138
+ ```
139
+
140
+ ### Idioma
141
+
142
+ La interfaz está en español por defecto y en inglés con `--lang en` o
143
+ `INVOICEHN_LANG=en`. **El documento fiscal siempre se emite en español**: sus
144
+ leyendas ("FACTURA", "Original: Cliente", "CONSUMIDOR FINAL", "ANULADA") son
145
+ texto legal fijado por el Reglamento, no texto de interfaz, y viven como
146
+ constantes fuera del catálogo de traducciones.
147
+
148
+ ---
149
+
150
+ ## Uso como biblioteca
151
+
152
+ ```ruby
153
+ require "invoicehn"
154
+
155
+ emisor = Invoicehn::Issuer.new(
156
+ rtn: "08011990123456",
157
+ legal_name: "Comercial Ejemplo, S. de R.L.",
158
+ trade_name: "Ferretería Ejemplo",
159
+ headquarters_address: "Col. Palmira, Tegucigalpa M.D.C.",
160
+ phone: "2222-3333",
161
+ email: "facturacion@ejemplo.hn"
162
+ )
163
+
164
+ autorizacion = Invoicehn::Authorization.new(
165
+ cai: "ABCD12-345678-9ABCDE-F01234-567890-AB",
166
+ range_start: "000-001-01-00000001",
167
+ range_end: "000-001-01-00000500",
168
+ limit_date: Date.new(2027, 6, 30)
169
+ )
170
+
171
+ factura = Invoicehn::Invoice.new(
172
+ correlative: "000-001-01-00000042",
173
+ issuer: emisor,
174
+ customer: Invoicehn::Customer::ConsumidorFinal.new,
175
+ authorization: autorizacion,
176
+ line_items: [
177
+ Invoicehn::LineItem.new(
178
+ description: "Servicio de consultoría",
179
+ quantity: 2,
180
+ unit_price: Invoicehn::Money.new("1500.00"),
181
+ treatment: :gravado_15
182
+ )
183
+ ]
184
+ )
185
+
186
+ factura.validate! # lanza ComplianceError con la lista
187
+ puts factura.total # => L 3,450.00
188
+ puts factura.total_in_words # => TRES MIL CUATROCIENTOS ... CON 00/100
189
+ puts Invoicehn::Renderers::Text.new(factura).render
190
+ ```
191
+
192
+ Para emitir con numeración y persistencia, use el servicio:
193
+
194
+ ```ruby
195
+ issuance = Invoicehn::Issuance.new
196
+ factura = issuance.issue(customer: cliente, line_items: lineas)
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Decisiones que conviene conocer
202
+
203
+ ### El redondeo es estatutario, y equivocarse es delito
204
+
205
+ **Ley del Impuesto Sobre Ventas (Decreto-Ley 24), Artículo 9**, redactado por el
206
+ Decreto 135-94:
207
+
208
+ > Cuando al calcular dicho gravamen, resulte una fracción menor de 0.005 de
209
+ > Lempira, deberá reducirse el recargo hasta la cifra de centavos próxima
210
+ > inferior, en cambio, si la fracción citada es igual o mayor de 0.005 de
211
+ > Lempira, entonces podrá subirse el cómputo hasta la cifra de centavos próxima
212
+ > superior. **El recargo del impuesto al consumidor fuera de la regla establecida
213
+ > en el párrafo anterior, se considerará como hurto.**
214
+
215
+ Por eso todo importe es `BigDecimal` y **`Float` está prohibido** en el camino
216
+ del dinero: `Money.new(10.5)` lanza una excepción. Una prueba recorre el código
217
+ fuente y falla si aparece `Float` o `to_f` fuera de su propio rechazo.
218
+
219
+ ### El ISV se calcula sobre el subtotal de cada tarifa
220
+
221
+ El Art. 11 num. 1 literales h) e i) exigen mostrar *subtotales por tarifa* y
222
+ *impuestos por tarifa*. Redondear línea por línea y sumar puede producir un total
223
+ de ISV que no coincida con la tarifa aplicada al subtotal impreso — justo la
224
+ discrepancia que un auditor cuestionaría. La norma no resuelve el punto (el
225
+ Art. 9 de la Ley del ISV habla del recargo "sobre el precio del artículo vendido
226
+ o servicio prestado", que se lee por ítem), así que la lectura elegida está
227
+ documentada en el código y cubierta por pruebas.
228
+
229
+ ### Los descuentos reducen la base gravable
230
+
231
+ **Ley del ISV, Artículo 3**: *"No forman parte de la base gravable los descuentos
232
+ efectivos que consten en la factura o documento equivalente, siempre que resulten
233
+ normales según la costumbre comercial."* Mostrar el descuento pero gravar el valor
234
+ bruto le cobraría de más al cliente.
235
+
236
+ La línea de descuentos aparece siempre, con o sin descuento: el Acuerdo 725-2018
237
+ la agregó como **Art. 10 num. 10** ("Descuentos y rebajas otorgados"), es decir
238
+ como requisito **del formato**, además de los literales l) y j) del Art. 11.
239
+
240
+ ### El CAI y el RTN se validan con prudencia
241
+
242
+ El Art. 4 num. 7 define el CAI únicamente como *"una serie alfanumérica generada
243
+ electrónicamente"*. Ninguna norma fija su longitud ni su agrupación, y el SAR no
244
+ publica un formato. Se almacena e imprime **tal cual**.
245
+
246
+ El RTN son 14 dígitos, para personas naturales y jurídicas. **No existe un dígito
247
+ verificador documentado públicamente**: la palabra "dígito" no aparece en el
248
+ Código Tributario, el Acuerdo 481-2017 exige el RTN sin especificar su
249
+ estructura, y el SAR no publica algoritmo alguno. Validar contra un esquema
250
+ adivinado — un módulo 11 tomado del RUT chileno o del RFC mexicano — rechazaría
251
+ RTN reales. Se valida longitud y dígitos, nada más.
252
+
253
+ ### La numeración no se reutiliza ni se salta
254
+
255
+ El asignador se lleva por la terna (establecimiento, punto de emisión, tipo de
256
+ documento) — el *identificador del documento* del Art. 10 num. 7 — y trabaja bajo
257
+ un candado exclusivo de archivo, de modo que dos procesos simultáneos no pueden
258
+ repetir ni omitir un número. La asignación y el guardado ocurren dentro del mismo
259
+ candado: si el guardado falla, el correlativo no se consume.
260
+
261
+ Un documento emitido es inmutable. La única corrección es la anulación
262
+ (Art. 41), que marca el registro con la leyenda **ANULADA** y **conserva el
263
+ correlativo consumido**.
264
+
265
+ ### La fecha de emisión es la del sistema
266
+
267
+ No hay bandera para retrofechar. El Art. 43 obliga a custodiar los documentos en
268
+ orden cronológico, y un libro cuya numeración no concuerde con sus fechas es lo
269
+ primero que una auditoría cuestiona.
270
+
271
+ ### La tasa del 18% está redactada de forma ambigua
272
+
273
+ El Decreto 278-2013 Art. 16 fija la tasa general en 15% y en 18% la de *"las
274
+ bebidas alcohólicas, cerveza y cigarrillos al igual que los boletos aéreos de
275
+ clase ejecutiva"*. La enumeración del Art. 6 que reforma agrega *"otros productos
276
+ elaborados de tabaco"*, y las guías tributarias incluyen además primera clase.
277
+ Decidir si un producto concreto califica es del obligado tributario; la gema
278
+ calcula una vez tomada esa decisión.
279
+
280
+ ### Lo que NO es obligatorio, pese a lo que se repite
281
+
282
+ La leyenda **"La factura es beneficio de todos, exíjala"** **no** está exigida por
283
+ ninguna norma. Se verificó su ausencia en el texto completo del Acuerdo 481-2017,
284
+ en su texto consolidado y en su antecesor el Acuerdo 189-2014. Se admite como
285
+ texto opcional, nunca como regla de cumplimiento.
286
+
287
+ Las leyendas que sí exige el Reglamento son `CONSUMIDOR FINAL` (Art. 11 num. 2
288
+ lit. a), `ANULADA` (Art. 41) y el destino de los ejemplares (Art. 10 num. 6).
289
+
290
+ ---
291
+
292
+ ## Matriz de cumplimiento
293
+
294
+ Cada fila corresponde a pruebas automatizadas.
295
+
296
+ | Artículo | Requisito | Implementado en |
297
+ |---|---|---|
298
+ | 10 num. 1 | Datos de identificación del emisor | `Issuer` |
299
+ | 10 num. 2 | Denominación "Factura" | `Renderers::Text` |
300
+ | 10 num. 3-5 | CAI, fecha límite y rango vigentes | `Authorization` |
301
+ | 10 num. 6 | Destino de los ejemplares | `Renderers::Text` |
302
+ | 10 num. 7 | Correlativo de 16 dígitos | `Correlative` |
303
+ | 10 num. 8 | Datos del adquirente exonerado | `Customer::Exonerado` |
304
+ | 10 num. 10 | Descuentos y rebajas (formato) | `Renderers::Text` |
305
+ | 11 num. 1 | Requisitos para crédito fiscal | `Compliance::Validator` |
306
+ | 11 num. 1 g-i | Discriminación por tratamiento y tarifa | `TaxSummary` |
307
+ | 11 num. 1 k | Importe total en números y letras | `SpanishNumerals` |
308
+ | 11 num. 1 l / 2 j | Discriminación de descuentos | `LineItem`, `TaxSummary` |
309
+ | 11 num. 2 | CONSUMIDOR FINAL; datos sobre L 10,000.00 | `Customer::ConsumidorFinal` |
310
+ | 11 num. 3 | Crédito fiscal sólo por ventas gravadas | `TaxSummary#credito_fiscal_base` |
311
+ | 11 párrafo final | Tasa de cambio a la fecha de emisión | `ExchangeRate` |
312
+ | 12 | Exportaciones con tasa cero | `TaxTreatment::GRAVADO_0` |
313
+ | 41 | Leyenda ANULADA; correlativo conservado | `Invoice#annul` |
314
+ | 42 | Aviso de autorizaciones vencidas sin usar | `invoicehn check` |
315
+ | 43 | Custodia cronológica | `Storage::JsonStore` |
316
+ | 53 num. 1 | Integración contable o de inventarios | `Ledger` |
317
+ | 53 num. 5 | Exportación en archivos de texto | `invoicehn export` |
318
+ | 62 | La autorización vencida bloquea la emisión | `Authorization#expired?` |
319
+ | ISV Art. 3 | Los descuentos no forman base gravable | `LineItem#taxable_base` |
320
+ | ISV Art. 9 | Redondeo a centavos, medio hacia arriba | `Money#round_statutory` |
321
+
322
+ ### Art. 9 del Reglamento: por qué no hay consolidación de fin de día
323
+
324
+ El Art. 9 permite no extender comprobante en el acto para ventas a consumidores
325
+ finales que no excedan L 50.00. Ese alivio **no aplica** a los autoimpresores por
326
+ máquina registradora ni por sistema computarizado — y esta gema es un sistema
327
+ computarizado. Por eso emite siempre un documento por venta: la ausencia de esa
328
+ función es deliberada.
329
+
330
+ ---
331
+
332
+ ## Integración contable (Art. 53 num. 1)
333
+
334
+ El Art. 53 num. 1 condiciona la autorización como autoimpresor a que el sistema
335
+ de facturación esté integrado al menos a un sistema contable o de inventarios.
336
+ `Invoicehn::Ledger` es esa interfaz; la gema incluye una implementación en JSONL
337
+ que cumple el requisito por sí sola.
338
+
339
+ ```ruby
340
+ class MiContabilidad < Invoicehn::Ledger
341
+ def record(invoice, event: :emision)
342
+ MiERP::Asiento.crear!(invoice.to_h)
343
+ invoice
344
+ end
345
+
346
+ def entries(from: nil, to: nil) = MiERP::Asiento.entre(from, to)
347
+ end
348
+
349
+ Invoicehn::Issuance.new(
350
+ ledger: Invoicehn::Ledger::Multi.new(
351
+ Invoicehn::Ledger::JsonlLedger.new,
352
+ MiContabilidad.new
353
+ )
354
+ )
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Almacenamiento
360
+
361
+ Por defecto en `~/.invoicehn` (configurable con `INVOICEHN_HOME` o `--data-dir`):
362
+
363
+ ```
364
+ ~/.invoicehn/
365
+ ├── issuer.json # datos del emisor
366
+ ├── authorizations.json # los CAI registrados
367
+ ├── sequences.json # contadores por identificador
368
+ ├── ledger.jsonl # libro append-only
369
+ └── documentos/AAAA/MM/000-001-01-00000001.json
370
+ ```
371
+
372
+ Archivos JSON planos, legibles sin esta gema — algo que importa en registros que
373
+ deben sobrevivir al software que los escribió.
374
+
375
+ ---
376
+
377
+ ## Fuentes
378
+
379
+ Textos primarios, por número de *La Gaceta*:
380
+
381
+ | Gaceta | Norma |
382
+ |---|---|
383
+ | 34,413 | Acuerdo 481-2017 (original) |
384
+ | 34,457 | Acuerdo 609-2017 (primera reforma) |
385
+ | 34,792 | Acuerdo 725-2018 (segunda reforma — agrega los descuentos) |
386
+ | 34,811 | Acuerdo 817-2018 (tercera reforma) |
387
+ | 33,316 | Decreto 278-2013 (tasas del ISV) |
388
+
389
+ Más la Ley del Impuesto Sobre Ventas (Decreto-Ley 24, Arts. 3 y 9) y el Código
390
+ Tributario (Decreto 170-2016, Art. 66). Los PDF y las extracciones están en
391
+ `doc/fuentes/`.
392
+
393
+ > Dos trampas encontradas al investigar, por si le ahorran tiempo: la copia de la
394
+ > Ley del ISV que publica el TSC es una consolidación que se detiene en el
395
+ > Decreto 171-98 y todavía muestra 12%/15% — no sirve para citar tasas, aunque sí
396
+ > es buena fuente del Art. 9. Y el texto de 2017 del Acuerdo 481-2017 fue
397
+ > reformado tres veces: hay que trabajar sobre el *texto consolidado* del SAR.
398
+
399
+ ---
400
+
401
+ ## Desarrollo
402
+
403
+ ```
404
+ bundle install
405
+ bundle exec rake # pruebas y linter
406
+ bundle exec rake test
407
+ bundle exec rake rubocop
408
+ ```
409
+
410
+ ### Publicar una versión
411
+
412
+ Un número de versión publicado en RubyGems **no se puede reutilizar**: `gem yank`
413
+ retira el paquete pero deja el número quemado para siempre. Por eso `rake build`
414
+ y `rake release` ejecutan las pruebas y el linter antes de hacer nada.
415
+
416
+ ```
417
+ gem signin # una sola vez por máquina; requiere MFA
418
+ # subir la versión en lib/invoicehn/version.rb
419
+ # anotar los cambios en CHANGELOG.md
420
+ bundle exec rake release # etiqueta vX.Y.Z, la sube y publica la gema
421
+ ```
422
+
423
+ `rake release` se niega a continuar si el árbol de trabajo tiene cambios sin
424
+ confirmar o si la etiqueta ya existe.
425
+
426
+ ## Licencia
427
+
428
+ MIT. El texto de la licencia incluye la exención de garantías habitual: esta
429
+ gema ayuda a cumplir los requisitos de contenido del Reglamento, pero la
430
+ responsabilidad tributaria sigue siendo del obligado tributario.
@@ -0,0 +1,89 @@
1
+ # Command-line interface strings.
2
+ #
3
+ # IMPORTANT: this catalog does NOT contain the fiscal document's text. The
4
+ # invoice legends ("FACTURA", "Original: Cliente", "CONSUMIDOR FINAL",
5
+ # "ANULADA") are legal text fixed by the Reglamento and live as constants in
6
+ # Invoicehn::Renderers::Text. Translating them would produce an invalid
7
+ # document — an English CLI still issues a Spanish invoice.
8
+ en:
9
+ setup:
10
+ title: "Issuer configuration (Art. 10 num. 1)"
11
+ intro: "These details are printed on every invoice. All are mandatory."
12
+ rtn: "Issuer RTN (14 digits)"
13
+ legal_name: "Legal name or razón social"
14
+ trade_name: "Trade name"
15
+ headquarters: "Head office address"
16
+ branch: "Establishment address (leave blank if head office)"
17
+ phone: "Phone number"
18
+ email: "Email address"
19
+ saved: "Issuer saved to %{path}"
20
+
21
+ auth:
22
+ title: "Register a SAR authorization"
23
+ intro: |
24
+ The CAI, authorized range and issue deadline are granted by SAR
25
+ (Arts. 59-61). Copy them from the authorization document; this program
26
+ does not generate them.
27
+ cai: "CAI (Clave de Autorización de Impresión)"
28
+ establishment: "Establishment (3 digits; head office = 000)"
29
+ emission_point: "Emission point (3 digits)"
30
+ range_start: "First authorized number (8 digits)"
31
+ range_end: "Last authorized number (8 digits)"
32
+ limit_date: "Issue deadline (YYYY-MM-DD)"
33
+ saved: "Authorization registered: %{range}"
34
+ none: "No authorizations on file. Use `invoicehn auth add`."
35
+ list_header: "Registered authorizations"
36
+
37
+ invoice:
38
+ customer_kind: "Customer type"
39
+ kind_consumidor_final: "Final consumer"
40
+ kind_taxpayer: "Taxpayer (claims crédito fiscal)"
41
+ kind_exonerado: "Exonerated purchaser"
42
+ customer_name: "Customer name or legal name"
43
+ customer_rtn: "Customer RTN"
44
+ id_type: "Identification document type"
45
+ id_number: "Identification document number"
46
+ purchase_order: "Orden de Compra Exenta number"
47
+ exoneration_registry: "Constancia del Registro de Exonerados number"
48
+ sag_registry: "SAG registry number"
49
+ add_line: "Add a line item?"
50
+ description: "Detailed description of the good or service"
51
+ quantity: "Quantity"
52
+ unit_price: "Unit price"
53
+ discount: "Discount granted (0 if none)"
54
+ treatment: "ISV treatment"
55
+ currency: "Currency"
56
+ exchange_rate: "Exchange rate in force on the issue date"
57
+ exchange_source: "Exchange rate source"
58
+ notes: "Additional notes (optional)"
59
+ confirm: "Issue this invoice?"
60
+ cancelled: "Issuance cancelled. No number was consumed."
61
+ issued: "Invoice issued: %{correlative}"
62
+
63
+ check:
64
+ title: "Billing system status"
65
+ issuer_ok: "Issuer configured and complete"
66
+ issuer_missing: "Issuer details missing: %{fields}"
67
+ issuer_absent: "No issuer configured. Run `invoicehn setup`."
68
+ auth_active: "Current authorization: %{cai}"
69
+ auth_absent: "No current authorization for %{identifier}"
70
+ next: "Next number: %{correlative}"
71
+ remaining: "Documents available: %{count}"
72
+ days: "Days until the deadline: %{days}"
73
+ lapsed: |
74
+ Warning: %{count} authorization(s) expired holding unused documents.
75
+ Art. 42 requires reporting this to SAR within the first 10 business days
76
+ of the following month.
77
+ ready: "Ready to issue."
78
+ not_ready: "Not ready to issue."
79
+
80
+ errors:
81
+ not_initialized: "No issuer configured. Run `invoicehn setup` first."
82
+ not_found: "Invoice %{correlative} not found"
83
+ compliance: "The invoice does not meet the legal requirements:"
84
+
85
+ common:
86
+ yes: "Yes"
87
+ no: "No"
88
+ total: "Total"
89
+ cancel: "Cancel"