← Analysis / Análisis
UNCTAD · Accounting-Next · Technical proposal for FrankUNCTAD · Accounting-Next · Propuesta técnica para Frank

Uganda pilot on Accounting-Next: one bug to fix first, then Uganda content and two missing reports Piloto Uganda en Accounting-Next: primero un bug que arreglar, después el contenido de Uganda y dos informes que faltan

Uganda users only enter monthly figures today. In Accounting-Next as built, a user who fills in a month in the questionnaire and records daily operations for the same month sees that month counted twice in every report. We propose to fix that, load Uganda’s chart of accounts, tax rates and institutions, and add ratios and a cash-flow statement. We are asking for a green light to open three pull requests. Nothing has been pushed yet. Hoy los usuarios de Uganda solo cargan cifras mensuales. En Accounting-Next tal como está, si un usuario llena un mes en el cuestionario y registra operaciones diarias de ese mismo mes, ese mes sale contado dos veces en todos los informes. Proponemos arreglar eso, cargar el plan de cuentas, las tasas y las instituciones de Uganda, y agregar ratios y un estado de flujo de caja. Pedimos luz verde para abrir tres pull requests. Todavía no se ha subido nada.

Based on a read-only review of UNCTAD-eRegistrations/Accounting-Next, main @ 69b3aa41 (24 Sep 2026). Findings come from reading the code; nothing was run. Basado en una revisión de solo lectura de UNCTAD-eRegistrations/Accounting-Next, main @ 69b3aa41 (24-sep-2026). Los hallazgos salen de leer el código; no se ejecutó nada.

PR AFix monthly/daily double countingArreglar el doble conteo mensual/diarioCorrectness bug. Must go first.Bug de correctitud. Va primero.
PR BUganda contentContenido de UgandaChart, UGX, VAT 18%, URA / NSSF. Mostly configuration.Plan de cuentas, UGX, IVA 18 %, URA / NSSF. Sobre todo configuración.
PR CRatios + cash flowRatios + flujo de cajaCloses Uganda issues 009–014 and 010 (issue #18).Cierra los reclamos 009–014 y 010 de Uganda (issue #18).

Decisions already takenDecisiones ya tomadas

Nelson decided these on 24 Sep 2026:Nelson las tomó el 24-sep-2026:

  1. The Uganda pilot runs on Accounting-Next, not the legacy KeystoneJS tool.El piloto de Uganda corre en Accounting-Next, no en la herramienta vieja (KeystoneJS).
  2. Monthly vs daily: the declared month total is the truth. Any gap from the daily detail is posted and shown as an “Unsupported amount”.Mensual vs diario: el total mensual declarado es la verdad. La diferencia con el detalle diario se registra y se muestra como “Unsupported amount”.
  3. EFRIS (URA e-invoicing) is out of the pilot, but is presented to URA as the next step.EFRIS (factura electrónica de URA) queda fuera del piloto, pero se presenta a URA como el próximo paso.

PR A — Monthly/daily double countingPR A — Doble conteo mensual/diario

What happens todayQué pasa hoy

FindingHallazgoWhereDónde
A questionnaire actual posts one real Operation per (item, month), dated the last day of the month.Un valor real del cuestionario crea una Operation real por (ítem, mes), con fecha del último día del mes.src/actions/budget-actuals.ts · setBudgetActual
Reports and totals sum all operations on an account, whatever their source.Informes y totales suman todas las operaciones de una cuenta, sin importar el origen.src/actions/reports.ts:368 · src/actions/budget-totals.ts:136
JournalLine.fromBudgetActual is written but never read. “Subsystem B” (spec 2026-06-22 §9) was never built.JournalLine.fromBudgetActual se escribe pero nunca se lee. El “subsistema B” (spec 2026-06-22 §9) nunca se construyó.src/lib/ledger/persist.ts:220
Questionnaire operations carry no businessId, but reports filter by the active business, so monthly figures can drop out of reports.Las operaciones del cuestionario no llevan businessId, pero los informes filtran por negocio activo, así que las cifras mensuales pueden desaparecer de los informes.budget-actuals.ts · src/lib/business/scope.ts:17
Example. Questionnaire “March sales 3,000,000” + the same 40 March sales entered daily (2,800,000) → the March income statement shows 5,800,000.Ejemplo. Cuestionario “Ventas de marzo 3.000.000” + las mismas 40 ventas de marzo cargadas día a día (2.800.000) → el estado de resultados de marzo muestra 5.800.000.

Proposed ruleRegla propuesta

For each (user, business, item/account, month):Para cada (usuario, negocio, ítem/cuenta, mes):

gap = declared total − Σ daily operations

ChangesCambios

ChangeCambioDetailDetalle
Store the declared totalGuardar el total declaradoNew table UserBudgetActualDeclaration(userId, itemId, period, businessId, amount, currency), unique on (user, item, period, business). Additive migration. Backfill from existing questionnaire ops (declared = current op amount).Tabla nueva UserBudgetActualDeclaration(userId, itemId, period, businessId, amount, currency), única por (usuario, ítem, período, negocio). Migración aditiva. Relleno inicial desde las ops del cuestionario existentes (declarado = monto actual de la op).
One helper that recomputes the gapUn helper que recalcula la diferenciareconcileBudgetActualGap(tx, {userId, businessId, accountId, month}), run inside the same transaction as any operation write.reconcileBudgetActualGap(tx, {userId, businessId, accountId, month}), dentro de la misma transacción que cualquier escritura de operación.
Call it from every path that writes an operationLlamarlo desde toda ruta que escribe una operaciónoperations.ts create / update / delete (lines 268, 475, 666), recurring posting, AI-created and aid-created operations, e-invoice acceptance. For an update that changes account or date, both the old and the new (account, month) are reconciled. Open question: hook it centrally in the ledger post path instead?operations.ts crear / editar / borrar (líneas 268, 475, 666), registro de recurrentes, operaciones creadas por IA y por ayudas, aceptación de e-factura. Si una edición cambia la cuenta o la fecha, se reconcilian el (cuenta, mes) viejo y el nuevo. Pregunta abierta: ¿engancharlo centralmente en la ruta de posteo del libro?
Attach the businessPoner el negociosetBudgetActual sets businessId = active business. Backfill: existing questionnaire ops get the user’s active business.setBudgetActual guarda businessId = negocio activo. Relleno: las ops del cuestionario existentes reciben el negocio activo del usuario.
Questionnaire UIPantalla del cuestionarioquestionnaire.tsx: show “Already recorded daily: X” per flow row. The field edits the declared total. Warning on total < daily.questionnaire.tsx: mostrar “Ya registrado día a día: X” en cada fila de flujo. El campo edita el total declarado. Aviso si total < diario.
ReportsInformesACCOUNT_FLOW aggregation (reports.ts:368) groups also by budgetActualItemId IS NULL, and renders the non-null part as an amber sub-line “Unsupported amount”. Totals unchanged. Balance sheet lines can use the existing fromBudgetActual flag.La agregación ACCOUNT_FLOW (reports.ts:368) agrupa también por budgetActualItemId IS NULL y muestra la parte no nula como sub-línea ámbar “Unsupported amount”. Los totales no cambian. El balance puede usar la marca fromBudgetActual que ya existe.
i18nNew keys in src/messages/*.json; must pass npm run i18n:check.Claves nuevas en src/messages/*.json; debe pasar npm run i18n:check.
Tests (Jest)Pruebas (Jest)
  • Declared 3,000,000 + daily 2,800,000 → one gap op of 200,000; income statement = 3,000,000.Declarado 3.000.000 + diario 2.800.000 → una op de diferencia de 200.000; estado de resultados = 3.000.000.
  • Add a daily op of 100,000 → gap becomes 100,000; delete it → back to 200,000.Agregar una op diaria de 100.000 → la diferencia pasa a 100.000; borrarla → vuelve a 200.000.
  • Daily reaches the declared total → gap op deleted, with audit event.Lo diario llega al total declarado → se borra la op de diferencia, con evento de auditoría.
  • Declared < daily on save → refused; daily pushes past declared → no negative op, flag set.Declarado < diario al guardar → rechazado; lo diario supera lo declarado → sin op negativa, marca puesta.
  • Update moving an op to another month → both months reconciled.Edición que mueve una op a otro mes → se reconcilian los dos meses.
  • Two businesses → each reconciles independently; reports filtered by business include questionnaire figures.Dos negocios → cada uno se reconcilia por separado; los informes filtrados por negocio incluyen las cifras del cuestionario.
  • Report splits the “Unsupported amount” sub-line; totals unchanged vs today for users who use only one mode.El informe separa la sub-línea “Unsupported amount”; los totales no cambian respecto de hoy para quien usa un solo modo.

PR B — Uganda contentPR B — Contenido de Uganda

Today the code has no Uganda content at all (no hits for Uganda, UGX, URA, EFRIS or NSSF outside the ISO currency list). The deployment boots one global catalog, so most of this is configuration.Hoy el código no tiene nada de Uganda (cero resultados para Uganda, UGX, URA, EFRIS o NSSF fuera de la lista ISO de monedas). Cada instalación arranca con un solo catálogo global, así que casi todo esto es configuración.

ItemElementoSourceFuenteHowCómo
Chart of accounts (~110 accounts)Plan de cuentas (~110 cuentas)Uganda/ugchart.txtConvert to the default-chart.json shape, including the roles (AR, AP, VAT payable/recoverable, opening equity).Convertir al formato de default-chart.json, con sus roles (cuentas por cobrar y por pagar, IVA por pagar/recuperable, patrimonio de apertura).
Currency UGXMoneda UGXISO 4217Already in iso-4217.json; enable as the default.Ya está en iso-4217.json; activarla como predeterminada.
VAT 18% (standard), 0% and exemptIVA 18 % (general), 0 % y exentoUganda VAT ActLey de IVA de UgandaTaxRate rows(filas)
Institutions: URA (tax), NSSF (social security), KCCA / local government (municipal), URSB (registry)Instituciones: URA (impuestos), NSSF (seguridad social), KCCA / gobierno local (municipal), URSB (registro)—Rename the four global rows (ensure-default-institutions.ts already supports admin renames).Renombrar las cuatro filas globales (ensure-default-institutions.ts ya admite renombrarlas desde admin).
Report models: URA VAT return, NSSF contributions (5% employee + 10% employer)Modelos de informe: declaración de IVA de URA, aportes NSSF (5 % empleado + 10 % empleador)URA / NSSF formsFormularios de URA / NSSFReport definitions (existing engine: IVA_SUM, FORMULA). The exact form layout needs URA input.Definiciones de informe (motor existente: IVA_SUM, FORMULA). El formato exacto necesita datos de URA.
Question for Frank: should country content live as admin configuration on the Uganda instance (no code), or as a country pack in the seed (reproducible, reviewable)? We lean towards a country pack, applied by a script, so El Salvador and others reuse the pattern.Pregunta para Frank: ¿el contenido del país vive como configuración de admin en la instancia de Uganda (sin código), o como paquete de país en el seed (reproducible y revisable)? Nos inclinamos por un paquete de país aplicado por script, para que El Salvador y otros reusen el patrón.

PR C — Ratios and cash flow (issue #18)PR C — Ratios y flujo de caja (issue #18)

EFRIS — next step, not in the pilotEFRIS — próximo paso, fuera del piloto

The existing e-invoice module already has per-jurisdiction formats (EInvoiceFormat) and an emitted/received lifecycle with an institution acknowledgment number. That is the natural hook for EFRIS later. It needs URA sandbox credentials and URA approval, so we propose requesting that access now, in parallel with the pilot.El módulo de e-factura ya tiene formatos por jurisdicción (EInvoiceFormat) y un ciclo emitida/recibida con número de acuse de la institución. Ese es el enganche natural para EFRIS más adelante. Hacen falta credenciales de prueba de URA y su aprobación, así que proponemos pedir ese acceso ya, en paralelo al piloto.

How we would work, and timeCómo trabajaríamos, y tiempos

WorkTrabajoAgent timeTiempo de agenteHuman / external timeTiempo humano / externo
PR A~4–6 h~4–6 hFrank’s team reviewRevisión del equipo de Frank
PR B~2–3 h~2–3 hURA form layouts, NSSF confirmationFormatos de URA, confirmación de NSSF
PR C~1 day~1 díaReview; accountant check of the ratio formulasRevisión; control de un contador sobre las fórmulas

What we need from FrankLo que necesitamos de Frank

  1. Green light to open PRs A, B and C in that order.Luz verde para abrir los PR A, B y C en ese orden.
  2. Gap recompute: call it from each write path, or centrally in the ledger post path?Recalcular la diferencia: ¿desde cada ruta de escritura, o centralmente en la ruta de posteo del libro?
  3. Country content: admin configuration or a country pack in the seed?Contenido del país: ¿configuración de admin o paquete de país en el seed?
  4. Is a Uganda instance planned, and at what URL?¿Hay prevista una instancia de Uganda, y en qué URL?

Visual explanation of the monthly/daily rule: daily-monthly/.Explicación visual de la regla mensual/diario: daily-monthly/.

Nelson Pérez · nelson.perez@unctad.orgDraft proposal · 24 Sep 2026 · nothing pushedBorrador · 24-sep-2026 · nada subido