phase-1: packages/rules complete, 100% covered
Every algorithm in docs/RULES.md, implemented exactly as written: the RUC check digit, the calendario perpetuo with weekend and holiday roll forward, CDC and KUDE QR parsing, keyword classification, computeF120, computeF515 and the dashboard projections, plus the two form definitions. Both worked examples reproduce verbatim on the first implementation: F120 at Gs. 277.273 to pay with the Gs. 350.000 flip case, F515 at Gs. 11.290.000 on a 5,65% effective rate. 162 tests over the package, coverage enforced at 100% statements, branches, functions and lines; the only exclusions are three bounded-loop guards marked v8 ignore with a comment saying why. No tax rule was invented. All six TODO-TAX-VERIFY items from RULES.md have a test pinning today's behaviour and a row in the DECISIONS.md register, so verification later is a red/green diff. Where RULES.md was silent the choice is marked SPEC-GAP in the code and listed too, the notable one being that taxpayer.hasIrp gates the deduction amount. No floats anywhere in a money path: money is a branded Pyg of whole guaranies and percentages go through integer arithmetic rounded half up. Also: contracts now takes IRP_CATEGORIES from rules rather than declaring the eight strings twice; classification returns reason codes with catalog strings in both locales, so the detail sheet localizes; apps/api/.env.example now names the same web port as apps/web/.env.example, without which a fresh checkout fails sign in on the origin check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ae2ea20b7e
commit
80b10c958e
@@ -219,6 +219,17 @@ export const en: Record<MessageKey, string> = {
|
||||
"auth.login.failed": "That email and password do not match. Try again.",
|
||||
"auth.login.noAccount": "Do not have an account yet?",
|
||||
|
||||
// Classification reasons (es.extra.ts)
|
||||
"classification.reason.iva_credit_eligible": "You can claim the IVA on this factura as credit.",
|
||||
"classification.reason.iva_credit_not_iva_taxpayer": "You are not registered for IVA, so this factura earns no credit.",
|
||||
"classification.reason.iva_credit_sale_document": "This is a sale of yours: it adds to debit, not to credit.",
|
||||
"classification.reason.iva_credit_resimple_supplier": "The supplier is on RESIMPLE, so it earns no IVA credit.",
|
||||
"classification.reason.iva_credit_no_iva_amount": "The factura shows no IVA separately.",
|
||||
"classification.reason.irp_category_keyword": "Category suggested from the issuer name.",
|
||||
"classification.reason.irp_no_category": "No category suggested",
|
||||
"classification.reason.irp_sale_is_income": "This is income of yours, not a deductible expense.",
|
||||
"classification.reason.irp_not_irp_taxpayer": "You are not registered for IRP, so it is not deducted.",
|
||||
|
||||
// Chrome (es.extra.ts)
|
||||
"common.language": "Language",
|
||||
"common.languageEs": "Español",
|
||||
|
||||
@@ -19,6 +19,18 @@ export const esExtra = {
|
||||
"auth.login.failed": "Ese email y esa contraseña no coinciden. Probá de nuevo.",
|
||||
"auth.login.noAccount": "¿Todavia no tenés cuenta?",
|
||||
|
||||
// Why a document was classified the way it was. packages/rules emits reason codes, not
|
||||
// sentences, so the detail sheet can show them in the reader's language.
|
||||
"classification.reason.iva_credit_eligible": "Podés usar el IVA de esta factura como credito.",
|
||||
"classification.reason.iva_credit_not_iva_taxpayer": "No tenés IVA activo, asi que esta factura no genera credito.",
|
||||
"classification.reason.iva_credit_sale_document": "Es una venta tuya: suma al debito, no al credito.",
|
||||
"classification.reason.iva_credit_resimple_supplier": "El proveedor esta en RESIMPLE, asi que no genera credito de IVA.",
|
||||
"classification.reason.iva_credit_no_iva_amount": "La factura no tiene IVA discriminado.",
|
||||
"classification.reason.irp_category_keyword": "Categoria sugerida por el nombre del emisor.",
|
||||
"classification.reason.irp_no_category": "Sin categoria sugerida",
|
||||
"classification.reason.irp_sale_is_income": "Es un ingreso tuyo, no un gasto deducible.",
|
||||
"classification.reason.irp_not_irp_taxpayer": "No tenés IRP activo, asi que no se descuenta.",
|
||||
|
||||
// Chrome
|
||||
"common.language": "Idioma",
|
||||
"common.languageEs": "Español",
|
||||
|
||||
Reference in New Issue
Block a user