v0.3.3
28 September 2026 LatestPOST /business-credit/dunning/runanswers202 { started: true }at once and runs the pass in the background (a full pass on a large install outlived reverse-proxy timeouts). The admin *Run now* button pollsdunning/statusevery 3 s and shows the report whenrunningturns false;dunning/statusgainedlastRun(completed/locked/failed).{ wait: true }keeps the synchronous response for scripts and tests.GET /business-credit/dashboardnow aggregates exposure, overdue (honouring each account's grace days), due-in-7-days, the aging buckets and the top-10 exposures in SQL instead of loading up to 20 000 invoice rows, and also returnspendingInvitations,activePlansand the channel'sloyaltystats. The admin UI loads the Overview with one call instead of five, keeps a 30 s freshness window per list (Refresh, a channel change or any mutation bypasses it), tracks table rows by id and runs withOnPushchange detection.- Loyalty monthly expiry finds the accounts with something to expire in one aggregate query per batch of 500 (
GROUP BY … HAVING) instead of one query per account. - Daily run fan-out: the storefront base URL is resolved once per channel; exposure and held credit for every account with a reminder due come from one
GROUP BY accountIdquery; newly-overdue invoices are marked with one bulkUPDATEand one multi-row eventINSERTper channel (same event payloads); on the statement day only accounts with ledger activity in the month or a non-zero opening balance are considered, five at a time. - Statement periods longer than 400 days are cut back to 400 days from
from(X-Statement-Clampedheader; the response'stosays where it ends); statement emails use the run's clock.
- Loyalty expiry maths: a redeem that was later released (order abandoned) no longer counts as consumption, so the points it briefly held expire when their earnings expire instead of being sheltered.
- PostgreSQL: every column of Vendure's own tables the plugin reads (
customer.firstName/lastName/emailAddress/deletedAt,order.subTotalWithTax/shippingWithTax/customerId,order_channels_channelandcustomer_channels_channelkeys,channel.defaultCurrencyCode) is now quoted; TypeORM creates them camelCase on Postgres, so the unquoted reads failed there. Found by the newsrc/pg-corpus.test.ts, which runs every raw SQL statement of the package against a real PostgreSQL (skipped unlessHULO_PG_URLis set).
- Calendar-day arithmetic (due dates, grace, reminder days, days overdue, late-fee days, statement day and period) is UTC — now said so in the README.