How the schedule is calculated
Live formulas from src/lib/deferred.ts — any change to those functions updates the rules, source snippets, and example results shown here.
1. Eligibility — which invoice lines are processed
Filtering is based on GL Account. Stock Codes / Tax Codes are not used for eligibility.
- The GL Account comes from the Sales Invoice detail row (
AccountId/ expandedAccount), not from the invoice header. - Codes are normalized (trim + uppercase) before matching.
- A line is processed only when its GL Account exists in the setup list with active = true.
- Lines without a GL Account (comments, sub-totals, descriptive rows) are skipped silently. Lines with an unknown / inactive GL are counted as Ignored.
function isProcessable(glAccount, rules) {
if (!glAccount) return false;
const c = normalizeCode(glAccount);
const r = rules.find((x) => x.code === c);
return !!r && r.active;
}function lineGLAccount(d) {
const code = d.account?.code ?? d.glAccount?.code ?? "";
if (code) return code;
if (d.accountId != null) return String(d.accountId);
if (d.glAccountId != null) return String(d.glAccountId);
return "";
}Example: normalizeCode(" 4200-0000 ") → 4200-0000
2. Revenue amount — Ex Tax from the Sales Invoice Register
Revenue is taken directly from the N3 Sales Invoice Register's Ex Tax value. No tax rate is applied by this app.
revenue = amountLocal ?? amount
// Amount in the N3 Sales Invoice Detail is the Before Tax / Ex Tax value.
// Net Amount is ignored — Before Tax is never derived from it.
// Tax Amount is display-only and never enters any calculation.function lineExTaxAmount(d) {
const explicit = d.taxableAmountLocal ?? d.taxableAmount ?? d.beforeTaxAmountLocal ?? d.beforeTaxAmount ?? d.taxExclusiveAmountLocal ?? d.taxExclusiveAmount;
if (explicit != null) return round2(explicit);
const gross = d.amountLocal ?? d.amount ?? d.netAmountLocal ?? d.netAmount ?? 0;
const tax = lineTaxAmount(d);
return round2(tax > 0 && gross > tax ? gross - tax : gross);
}Cancelled invoices (isCancelled = true) are excluded at the fetch layer and never reach the schedule.
3. Service period — DateRef1 / DateRef2
The service period is taken strictly from the Sales Invoice detail's DateRef1 (Start Date) and DateRef2 (End Date). There is no fallback to the invoice date or Qty × UOM.
start = dateRef1 ?? manualCorrection.start
end = dateRef2 ?? manualCorrection.end
if (!start || !end || end < start):
→ Missing Service Period — the line is NOT processed
(Accounting enters the Start/End Date in the app)
months = months_inclusive(start, end)Lines with an incomplete service period are listed under Missing Service Period on the Revenue Recognition Schedule. Once the Start Date and End Date are entered there, the line is scheduled on the next calculation.
4. Monthly allocation
Revenue is divided evenly across months; rounding remainder lands on the final month so totals reconcile to the cent.
months_inclusive = (end.year - start.year) × 12 + (end.month - start.month)
+ (end.day >= start.day ? 1 : 0)
monthly = round2(revenue / months)
for i in 0..months-1:
amount[i] = monthly (i < months-1)
amount[last] = revenue - sum(previous) // absorbs rounding5. Journal Entry — consolidated by GL Account
One journal line per GL Account per side. Descriptions are auto-generated from the selected accounting period.
To Set Up Deferred Revenue for the month of {accounting period}
Dr Sales (per GL Account)
Cr Deferred Revenue
To Recognize Revenue for the month of {accounting period}
Dr Deferred Revenue
Cr Sales (per GL Account)- Amounts are consolidated by GL Account across all supporting invoice lines.
- Set Up Deferred Revenue uses only invoiced amounts in the period.
- Recognize Revenue uses system-scheduled recognition for the month.
- Each journal amount is clickable and drills down to the underlying invoice breakdown.
- Posting is blocked when Debit ≠ Credit, when Deferred Revenue would go negative, or when recognition exceeds the available deferred revenue for a GL.
- After a successful post, the accounting period is locked and further sync / posting for that month is prevented until the period is unlocked.
6. Dashboard KPIs
What each dashboard card means for the selected accounting period.
- Sales — total Ex Tax sales recorded in N3 for the selected accounting period.
- Deferred Revenue to Set Up — total of the To Set Up Deferred Revenue journal side for the period.
- Revenue to Recognize — total of the To Recognize Revenue journal side, i.e. system-scheduled recognition in the period.
- Ending Deferred Revenue Balance — the prior deferred balance plus the period's deferred-revenue movement, less the revenue recognized from DateRef1/DateRef2 schedules.
Worked example (live)
Computed by running the real buildSchedule on a sample invoice — these numbers update if the formula changes.
Invoice INV-DEMO · 06/15/2026
Line: GL 4200-0000 · Ex Tax 1,200.00
DateRef1: 06/15/2026 · DateRef2: 06/14/2027| Month | Amount |
|---|---|
| Jun 2026 | 88.00 |
| Jul 2026 | 88.00 |
| Aug 2026 | 88.00 |
| Sep 2026 | 88.00 |
| Oct 2026 | 88.00 |
| Nov 2026 | 88.00 |
| Dec 2026 | 88.00 |
| Jan 2027 | 88.00 |
| Feb 2027 | 88.00 |
| Mar 2027 | 88.00 |
| Apr 2027 | 88.00 |
| May 2027 | 88.00 |
| Total | 1,056.00 |
Source of truth: src/lib/deferred.ts. Edit the functions there and this page reflects the new rules on next load.
