265 of 268 aggregations are written in a vocabulary OpenRegister does not read
AggregationRunner::run() reads exactly nine keys off an aggregation spec.
Measured from source, not from docs:
$ grep -oE "spec\['[a-zA-Z]+'\]" lib/Service/Aggregation/AggregationRunner.php | sort -u
spec['field'] spec['filter'] spec['from'] spec['groupBy'] spec['join']
spec['metric'] spec['metrics'] spec['select'] spec['where']
metric / metrics is the only way the engine computes a value. A spec
without one returns "metric": "", "field": null and "groups": [] — an empty
result with HTTP 200, which every page renders as its own empty state and reads
as "no data yet".
Observed live on the rig, before and after removing the @self placeholder:
{"name":"byCostCenter","metric":"","field":null,"backend":"php-fallback",
"truncated":false,"groups":[], ...}
This is #1216's first layer, unswept
#1216 had four causes
stacked on one page. The first was "the declaration spoke a grammar the engine
does not read" — it used source and a sum list. That was fixed for one
aggregation. It is still true of 265 others.
It also explains why nobody noticed #1255's @self.administrationId problem:
these aggregations were already returning nothing for a second, independent
reason, so removing the placeholder changes an empty result into a different
empty result.
Total aggregations declared: 268
Declaring metric or metrics (the only way the engine computes a value): 3
Cannot produce a value: 265
Those that CAN:
CurrencyBalance.balanceByCurrency
CommitmentLine.committedVsRealisedPerBudgetLine
APInvoice.apAging
Keys the engine never reads, by frequency
| key |
occurrences |
example |
source |
112 |
IBWinstOpgave.revenueExcludingVat (30-bookkeeping-ib-aangifte-zzp.json) |
operations |
92 |
IB47Record.ib47TotalsPerTaxYear (add-shillinq-detachering-payroll-administratie.json) |
operation |
73 |
Appointment.countByStatus (10-bookings-create-appointment.json) |
sum |
26 |
IBWinstOpgave.revenueExcludingVat (30-bookkeeping-ib-aangifte-zzp.json) |
expression |
19 |
StockMove.netQuantityForLocation (inventory-stock-movement-ledger.json) |
type |
15 |
CommitmentBudget.outstanding_commitments (bookkeeping-verplichtingenadministratie.json) |
sourceSchema |
14 |
GLLine.iv3DetailByTaakveldCategorie (shillinq_register.json) |
label |
12 |
ARInvoice.arAging (add-shillinq-bookkeeping-compliance.json) |
register |
12 |
ARInvoice.arAging (add-shillinq-bookkeeping-compliance.json) |
schema |
12 |
ARInvoice.arAging (add-shillinq-bookkeeping-compliance.json) |
sourceRegister |
10 |
Iv3Export.buckets (shillinq_register.json) |
computedFields |
7 |
ARInvoice.arAging (add-shillinq-bookkeeping-compliance.json) |
params |
6 |
GLTransaction.nivraBericht (add-shillinq-rekenkamer-audit-pack.json) |
output |
6 |
VpbBalansLink.vpbBalansFiltered (bookkeeping-vpb-corporate-tax-balans.json) |
meta |
5 |
GLTransaction.nivraBericht (add-shillinq-rekenkamer-audit-pack.json) |
orderBy |
5 |
APTransaction.agedPayablesDetail (bookkeeping-accounts-payable-core.json) |
aggregate |
5 |
APTransaction.agedPayablesSummary (bookkeeping-accounts-payable-core.json) |
sources |
5 |
ExpenseClaimEntry.settlementTotals (expense-reimbursement-or-passthrough.json) |
What this costs
Every page reading one of these renders an empty state over live data. That is
not a hypothetical: it is exactly what #1216 was, and it took a live instance to
find, because every unit test, validator and gate was green throughout — a
test that asserts the declaration's own shape compares it to itself.
Fixing it
Each declaration needs translating into the engine's grammar with its semantics
preserved — source → from, sum: [x] → metric: "sum", field: "x",
operation → metric, and the operations list → metrics. Mechanical in
shape, but 265 of them, and each one's meaning has to be checked rather than
pattern-matched. It should be done in batches with a live instance to confirm
each batch actually returns rows.
Some are not translations at all and need redesign:
TaxEstimate.ytdByCategory declares its filter as a SQL-like string;
- four
SBRDocumentType aggregations use join.schema + join.condition (a SQL
string) plus a check block — an entire invented sub-language.
Preventing more
tests/validate-registers.js now ratchets this: a new aggregation that declares
neither metric nor metrics pushes the count above its baseline and fails the
build. The baseline can only fall.
The alternative — implementing source/sum/operation as aliases in
OpenRegister — would make 265 declarations start working at once, with no
per-declaration check that the result is the intended one. That seems like the
more dangerous direction, but it is a real option and worth a decision rather
than an assumption.
265 of 268 aggregations are written in a vocabulary OpenRegister does not read
AggregationRunner::run()reads exactly nine keys off an aggregation spec.Measured from source, not from docs:
metric/metricsis the only way the engine computes a value. A specwithout one returns
"metric": "", "field": nulland"groups": []— an emptyresult with HTTP 200, which every page renders as its own empty state and reads
as "no data yet".
Observed live on the rig, before and after removing the
@selfplaceholder:{"name":"byCostCenter","metric":"","field":null,"backend":"php-fallback", "truncated":false,"groups":[], ...}This is #1216's first layer, unswept
#1216 had four causes
stacked on one page. The first was "the declaration spoke a grammar the engine
does not read" — it used
sourceand asumlist. That was fixed for oneaggregation. It is still true of 265 others.
It also explains why nobody noticed #1255's
@self.administrationIdproblem:these aggregations were already returning nothing for a second, independent
reason, so removing the placeholder changes an empty result into a different
empty result.
Total aggregations declared: 268
Declaring
metricormetrics(the only way the engine computes a value): 3Cannot produce a value: 265
Those that CAN:
CurrencyBalance.balanceByCurrencyCommitmentLine.committedVsRealisedPerBudgetLineAPInvoice.apAgingKeys the engine never reads, by frequency
sourceIBWinstOpgave.revenueExcludingVat (30-bookkeeping-ib-aangifte-zzp.json)operationsIB47Record.ib47TotalsPerTaxYear (add-shillinq-detachering-payroll-administratie.json)operationAppointment.countByStatus (10-bookings-create-appointment.json)sumIBWinstOpgave.revenueExcludingVat (30-bookkeeping-ib-aangifte-zzp.json)expressionStockMove.netQuantityForLocation (inventory-stock-movement-ledger.json)typeCommitmentBudget.outstanding_commitments (bookkeeping-verplichtingenadministratie.json)sourceSchemaGLLine.iv3DetailByTaakveldCategorie (shillinq_register.json)labelARInvoice.arAging (add-shillinq-bookkeeping-compliance.json)registerARInvoice.arAging (add-shillinq-bookkeeping-compliance.json)schemaARInvoice.arAging (add-shillinq-bookkeeping-compliance.json)sourceRegisterIv3Export.buckets (shillinq_register.json)computedFieldsARInvoice.arAging (add-shillinq-bookkeeping-compliance.json)paramsGLTransaction.nivraBericht (add-shillinq-rekenkamer-audit-pack.json)outputVpbBalansLink.vpbBalansFiltered (bookkeeping-vpb-corporate-tax-balans.json)metaGLTransaction.nivraBericht (add-shillinq-rekenkamer-audit-pack.json)orderByAPTransaction.agedPayablesDetail (bookkeeping-accounts-payable-core.json)aggregateAPTransaction.agedPayablesSummary (bookkeeping-accounts-payable-core.json)sourcesExpenseClaimEntry.settlementTotals (expense-reimbursement-or-passthrough.json)What this costs
Every page reading one of these renders an empty state over live data. That is
not a hypothetical: it is exactly what #1216 was, and it took a live instance to
find, because every unit test, validator and gate was green throughout — a
test that asserts the declaration's own shape compares it to itself.
Fixing it
Each declaration needs translating into the engine's grammar with its semantics
preserved —
source→from,sum: [x]→metric: "sum", field: "x",operation→metric, and theoperationslist →metrics. Mechanical inshape, but 265 of them, and each one's meaning has to be checked rather than
pattern-matched. It should be done in batches with a live instance to confirm
each batch actually returns rows.
Some are not translations at all and need redesign:
TaxEstimate.ytdByCategorydeclares itsfilteras a SQL-like string;SBRDocumentTypeaggregations usejoin.schema+join.condition(a SQLstring) plus a
checkblock — an entire invented sub-language.Preventing more
tests/validate-registers.jsnow ratchets this: a new aggregation that declaresneither
metricnormetricspushes the count above its baseline and fails thebuild. The baseline can only fall.
The alternative — implementing
source/sum/operationas aliases inOpenRegister — would make 265 declarations start working at once, with no
per-declaration check that the result is the intended one. That seems like the
more dangerous direction, but it is a real option and worth a decision rather
than an assumption.