tallyd 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1493 -2
- package/dist/accounts/chart.d.ts +96 -0
- package/dist/accounts/chart.js +244 -0
- package/dist/accounts/index.d.ts +3 -0
- package/dist/accounts/index.js +3 -0
- package/dist/accounts/standard.d.ts +12 -0
- package/dist/accounts/standard.js +95 -0
- package/dist/accounts/types.d.ts +20 -0
- package/dist/accounts/types.js +40 -0
- package/dist/cli/args.d.ts +45 -0
- package/dist/cli/args.js +147 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +25 -0
- package/dist/cli/run.d.ts +34 -0
- package/dist/cli/run.js +1660 -0
- package/dist/dashboard/charts.d.ts +38 -0
- package/dist/dashboard/charts.js +173 -0
- package/dist/dashboard/index.d.ts +3 -0
- package/dist/dashboard/index.js +3 -0
- package/dist/dashboard/model.d.ts +137 -0
- package/dist/dashboard/model.js +190 -0
- package/dist/dashboard/render.d.ts +23 -0
- package/dist/dashboard/render.js +231 -0
- package/dist/dashboard/script.d.ts +28 -0
- package/dist/dashboard/script.js +704 -0
- package/dist/dashboard/styles.d.ts +14 -0
- package/dist/dashboard/styles.js +726 -0
- package/dist/demo/disposal.d.ts +80 -0
- package/dist/demo/disposal.js +215 -0
- package/dist/demo/evidence.d.ts +153 -0
- package/dist/demo/evidence.js +348 -0
- package/dist/demo/foreign.d.ts +29 -0
- package/dist/demo/foreign.js +113 -0
- package/dist/demo/foreignReport.d.ts +10 -0
- package/dist/demo/foreignReport.js +89 -0
- package/dist/demo/generator.d.ts +108 -0
- package/dist/demo/generator.js +445 -0
- package/dist/demo/group.d.ts +70 -0
- package/dist/demo/group.js +195 -0
- package/dist/demo/groupPeriods.d.ts +27 -0
- package/dist/demo/groupPeriods.js +125 -0
- package/dist/demo/groupReport.d.ts +13 -0
- package/dist/demo/groupReport.js +87 -0
- package/dist/demo/ingest.d.ts +6 -0
- package/dist/demo/ingest.js +61 -0
- package/dist/demo/main.d.ts +6 -0
- package/dist/demo/main.js +7 -0
- package/dist/demo/mainDisposal.d.ts +1 -0
- package/dist/demo/mainDisposal.js +2 -0
- package/dist/demo/mainEvidence.d.ts +8 -0
- package/dist/demo/mainEvidence.js +9 -0
- package/dist/demo/mainForeign.d.ts +6 -0
- package/dist/demo/mainForeign.js +7 -0
- package/dist/demo/mainGroup.d.ts +6 -0
- package/dist/demo/mainGroup.js +7 -0
- package/dist/demo/mainGroupPeriods.d.ts +6 -0
- package/dist/demo/mainGroupPeriods.js +7 -0
- package/dist/demo/mainIngest.d.ts +6 -0
- package/dist/demo/mainIngest.js +7 -0
- package/dist/demo/mainMidYear.d.ts +6 -0
- package/dist/demo/mainMidYear.js +7 -0
- package/dist/demo/mainReconcile.d.ts +6 -0
- package/dist/demo/mainReconcile.js +7 -0
- package/dist/demo/mainStatements.d.ts +6 -0
- package/dist/demo/mainStatements.js +7 -0
- package/dist/demo/midYear.d.ts +51 -0
- package/dist/demo/midYear.js +162 -0
- package/dist/demo/month.d.ts +13 -0
- package/dist/demo/month.js +142 -0
- package/dist/demo/random.d.ts +48 -0
- package/dist/demo/random.js +94 -0
- package/dist/demo/receivables.d.ts +36 -0
- package/dist/demo/receivables.js +104 -0
- package/dist/demo/reconcile.d.ts +20 -0
- package/dist/demo/reconcile.js +104 -0
- package/dist/demo/report.d.ts +6 -0
- package/dist/demo/report.js +26 -0
- package/dist/demo/statement.d.ts +13 -0
- package/dist/demo/statement.js +51 -0
- package/dist/demo/statements.d.ts +6 -0
- package/dist/demo/statements.js +29 -0
- package/dist/demo/supplierRun.d.ts +46 -0
- package/dist/demo/supplierRun.js +94 -0
- package/dist/demo/unrealisedEvidence.d.ts +51 -0
- package/dist/demo/unrealisedEvidence.js +183 -0
- package/dist/fx/average.d.ts +60 -0
- package/dist/fx/average.js +139 -0
- package/dist/fx/document.d.ts +60 -0
- package/dist/fx/document.js +205 -0
- package/dist/fx/exposure.d.ts +108 -0
- package/dist/fx/exposure.js +262 -0
- package/dist/fx/index.d.ts +8 -0
- package/dist/fx/index.js +8 -0
- package/dist/fx/rate.d.ts +77 -0
- package/dist/fx/rate.js +200 -0
- package/dist/fx/revaluation.d.ts +92 -0
- package/dist/fx/revaluation.js +197 -0
- package/dist/fx/settlement.d.ts +84 -0
- package/dist/fx/settlement.js +162 -0
- package/dist/fx/table.d.ts +120 -0
- package/dist/fx/table.js +271 -0
- package/dist/fx/translate.d.ts +126 -0
- package/dist/fx/translate.js +258 -0
- package/dist/group/accounts.d.ts +74 -0
- package/dist/group/accounts.js +179 -0
- package/dist/group/acquisition.d.ts +324 -0
- package/dist/group/acquisition.js +416 -0
- package/dist/group/aggregate.d.ts +143 -0
- package/dist/group/aggregate.js +288 -0
- package/dist/group/comparative.d.ts +102 -0
- package/dist/group/comparative.js +166 -0
- package/dist/group/consolidate.d.ts +196 -0
- package/dist/group/consolidate.js +635 -0
- package/dist/group/disposal.d.ts +181 -0
- package/dist/group/disposal.js +233 -0
- package/dist/group/document.d.ts +41 -0
- package/dist/group/document.js +326 -0
- package/dist/group/index.d.ts +12 -0
- package/dist/group/index.js +12 -0
- package/dist/group/intercompany.d.ts +136 -0
- package/dist/group/intercompany.js +238 -0
- package/dist/group/interest.d.ts +109 -0
- package/dist/group/interest.js +224 -0
- package/dist/group/movement.d.ts +206 -0
- package/dist/group/movement.js +432 -0
- package/dist/group/structure.d.ts +151 -0
- package/dist/group/structure.js +327 -0
- package/dist/group/timeline.d.ts +109 -0
- package/dist/group/timeline.js +169 -0
- package/dist/group/unrealised.d.ts +165 -0
- package/dist/group/unrealised.js +314 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/ledger/close.d.ts +75 -0
- package/dist/ledger/close.js +142 -0
- package/dist/ledger/date.d.ts +48 -0
- package/dist/ledger/date.js +141 -0
- package/dist/ledger/entry.d.ts +150 -0
- package/dist/ledger/entry.js +264 -0
- package/dist/ledger/index.d.ts +6 -0
- package/dist/ledger/index.js +6 -0
- package/dist/ledger/ledger.d.ts +101 -0
- package/dist/ledger/ledger.js +308 -0
- package/dist/ledger/serialise.d.ts +68 -0
- package/dist/ledger/serialise.js +204 -0
- package/dist/ledger/trialBalance.d.ts +49 -0
- package/dist/ledger/trialBalance.js +103 -0
- package/dist/money/currency.d.ts +37 -0
- package/dist/money/currency.js +67 -0
- package/dist/money/index.d.ts +3 -0
- package/dist/money/index.js +3 -0
- package/dist/money/money.d.ts +106 -0
- package/dist/money/money.js +300 -0
- package/dist/money/rounding.d.ts +41 -0
- package/dist/money/rounding.js +117 -0
- package/dist/reconcile/accuracy.d.ts +65 -0
- package/dist/reconcile/accuracy.js +162 -0
- package/dist/reconcile/assignment.d.ts +83 -0
- package/dist/reconcile/assignment.js +326 -0
- package/dist/reconcile/bankView.d.ts +65 -0
- package/dist/reconcile/bankView.js +86 -0
- package/dist/reconcile/bridge.d.ts +76 -0
- package/dist/reconcile/bridge.js +129 -0
- package/dist/reconcile/candidates.d.ts +84 -0
- package/dist/reconcile/candidates.js +174 -0
- package/dist/reconcile/decisions.d.ts +116 -0
- package/dist/reconcile/decisions.js +168 -0
- package/dist/reconcile/index.d.ts +12 -0
- package/dist/reconcile/index.js +12 -0
- package/dist/reconcile/matcher.d.ts +89 -0
- package/dist/reconcile/matcher.js +306 -0
- package/dist/reconcile/memory.d.ts +120 -0
- package/dist/reconcile/memory.js +281 -0
- package/dist/reconcile/posting.d.ts +185 -0
- package/dist/reconcile/posting.js +481 -0
- package/dist/reconcile/scoring.d.ts +98 -0
- package/dist/reconcile/scoring.js +319 -0
- package/dist/reconcile/similarity.d.ts +88 -0
- package/dist/reconcile/similarity.js +272 -0
- package/dist/reconcile/subsetSum.d.ts +61 -0
- package/dist/reconcile/subsetSum.js +136 -0
- package/dist/reports/ageing.d.ts +75 -0
- package/dist/reports/ageing.js +149 -0
- package/dist/reports/balanceSheet.d.ts +57 -0
- package/dist/reports/balanceSheet.js +107 -0
- package/dist/reports/incomeStatement.d.ts +59 -0
- package/dist/reports/incomeStatement.js +132 -0
- package/dist/reports/index.d.ts +4 -0
- package/dist/reports/index.js +4 -0
- package/dist/reports/period.d.ts +37 -0
- package/dist/reports/period.js +78 -0
- package/dist/statement/amount.d.ts +55 -0
- package/dist/statement/amount.js +211 -0
- package/dist/statement/columns.d.ts +39 -0
- package/dist/statement/columns.js +221 -0
- package/dist/statement/csv.d.ts +50 -0
- package/dist/statement/csv.js +185 -0
- package/dist/statement/dates.d.ts +52 -0
- package/dist/statement/dates.js +204 -0
- package/dist/statement/duplicates.d.ts +51 -0
- package/dist/statement/duplicates.js +101 -0
- package/dist/statement/import.d.ts +52 -0
- package/dist/statement/import.js +172 -0
- package/dist/statement/index.d.ts +20 -0
- package/dist/statement/index.js +32 -0
- package/dist/statement/line.d.ts +60 -0
- package/dist/statement/line.js +73 -0
- package/dist/statement/ofx.d.ts +47 -0
- package/dist/statement/ofx.js +199 -0
- package/package.json +72 -4
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assembling the page.
|
|
3
|
+
*
|
|
4
|
+
* One file, no network. Styles and script are inlined, the data is embedded as
|
|
5
|
+
* JSON, and nothing is fetched — because the person who most needs to look at a
|
|
6
|
+
* reconciliation is often the one who cannot install anything, and a dashboard
|
|
7
|
+
* that needs a CDN is a dashboard that fails on a train.
|
|
8
|
+
*
|
|
9
|
+
* Escaping is done here rather than trusted anywhere: bank descriptors are
|
|
10
|
+
* attacker-adjacent text in the sense that matters, which is that they contain
|
|
11
|
+
* whatever the payer typed into a reference field.
|
|
12
|
+
*/
|
|
13
|
+
import { STYLES } from "./styles.js";
|
|
14
|
+
import { CLIENT_SCRIPT } from "./script.js";
|
|
15
|
+
import { cashPositionChart, confidenceChart } from "./charts.js";
|
|
16
|
+
export function escapeHtml(value) {
|
|
17
|
+
return value
|
|
18
|
+
.replace(/&/g, "&")
|
|
19
|
+
.replace(/</g, "<")
|
|
20
|
+
.replace(/>/g, ">")
|
|
21
|
+
.replace(/"/g, """)
|
|
22
|
+
.replace(/'/g, "'");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* JSON safe to sit inside a `<script>` element.
|
|
26
|
+
*
|
|
27
|
+
* `</script>` anywhere in the data would close the block early — a narration
|
|
28
|
+
* reading "see </script> for details" is unlikely but the failure is total, and
|
|
29
|
+
* the fix is three replacements.
|
|
30
|
+
*/
|
|
31
|
+
export function embedJson(value) {
|
|
32
|
+
return JSON.stringify(value)
|
|
33
|
+
.replace(/</g, "\\u003c")
|
|
34
|
+
.replace(/>/g, "\\u003e")
|
|
35
|
+
.replace(/\u2028/g, "\\u2028")
|
|
36
|
+
.replace(/\u2029/g, "\\u2029");
|
|
37
|
+
}
|
|
38
|
+
const amountClass = (line) => line.amount.minor < 0 ? "how-much negative" : "how-much";
|
|
39
|
+
function pairingLine(line) {
|
|
40
|
+
return `<div class="pairing-line">
|
|
41
|
+
<span class="who">${line.side === "book" ? "Ledger" : "Bank"}</span>
|
|
42
|
+
<span class="when">${escapeHtml(line.date)}</span>
|
|
43
|
+
<span class="what">${escapeHtml(line.description)}</span>
|
|
44
|
+
<span class="${amountClass(line)}">${escapeHtml(line.amount.text)}</span>
|
|
45
|
+
</div>`;
|
|
46
|
+
}
|
|
47
|
+
const KIND_LABEL = {
|
|
48
|
+
"one-to-one": "1:1",
|
|
49
|
+
"one-to-many": "1:N",
|
|
50
|
+
"many-to-one": "N:1",
|
|
51
|
+
};
|
|
52
|
+
function matchCard(match, actionable) {
|
|
53
|
+
const reasons = match.reasons.length === 0
|
|
54
|
+
? ""
|
|
55
|
+
: `<ul class="reasons">${match.reasons
|
|
56
|
+
.map((reason) => `<li>${escapeHtml(reason)}</li>`)
|
|
57
|
+
.join("")}</ul>`;
|
|
58
|
+
const actions = actionable
|
|
59
|
+
? `<div class="actions">
|
|
60
|
+
<button type="button" class="primary" data-action="accept" data-match-id="${escapeHtml(match.id)}">Accept match</button>
|
|
61
|
+
<button type="button" data-action="reject" data-match-id="${escapeHtml(match.id)}">Not a match</button>
|
|
62
|
+
</div>`
|
|
63
|
+
: "";
|
|
64
|
+
return `<article class="match" data-match-id="${escapeHtml(match.id)}">
|
|
65
|
+
<div class="match-head">
|
|
66
|
+
<span class="badge" data-confidence="${escapeHtml(match.confidence)}">${escapeHtml(match.confidence)}</span>
|
|
67
|
+
<span class="badge badge-kind">${escapeHtml(KIND_LABEL[match.kind])}</span>
|
|
68
|
+
<span class="score">${match.score.toFixed(3)}</span>
|
|
69
|
+
<span class="match-amount">${escapeHtml(match.amount.text)}</span>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="pairing">
|
|
72
|
+
${match.statement.map(pairingLine).join("")}
|
|
73
|
+
${match.book.map(pairingLine).join("")}
|
|
74
|
+
</div>
|
|
75
|
+
${reasons}
|
|
76
|
+
${actions}
|
|
77
|
+
</article>`;
|
|
78
|
+
}
|
|
79
|
+
function accountsTable(data) {
|
|
80
|
+
if (data.accounts.length === 0) {
|
|
81
|
+
return `<div class="empty"><strong>No chart</strong>This ledger was loaded without a chart of accounts.</div>`;
|
|
82
|
+
}
|
|
83
|
+
const rows = data.accounts
|
|
84
|
+
.map((account) => `<tr data-account="${escapeHtml(account.code)}" data-clickable="true" tabindex="0" role="button" aria-selected="false">
|
|
85
|
+
<td class="when">${escapeHtml(account.code)}</td>
|
|
86
|
+
<td style="padding-left:${account.depth * 14}px">${escapeHtml(account.name)}</td>
|
|
87
|
+
<td class="when">${escapeHtml(account.type)}</td>
|
|
88
|
+
<td class="num${account.balance.minor < 0 ? " negative" : ""}">${escapeHtml(account.balance.text)}</td>
|
|
89
|
+
</tr>`)
|
|
90
|
+
.join("");
|
|
91
|
+
return `<table>
|
|
92
|
+
<thead><tr><th>Code</th><th>Account</th><th>Type</th><th class="num">Balance</th></tr></thead>
|
|
93
|
+
<tbody>${rows}</tbody>
|
|
94
|
+
</table>`;
|
|
95
|
+
}
|
|
96
|
+
export function renderDashboard(data) {
|
|
97
|
+
const title = `Bank reconciliation — ${data.account} ${data.accountName}`;
|
|
98
|
+
const matchedCards = data.matched.length === 0
|
|
99
|
+
? `<div class="empty"><strong>Nothing matched yet</strong>The matcher found no pair confident enough to accept on its own.</div>`
|
|
100
|
+
: data.matched.map((match) => matchCard(match, false)).join("");
|
|
101
|
+
const queueCards = data.suggested.map((match) => matchCard(match, true)).join("");
|
|
102
|
+
return `<!doctype html>
|
|
103
|
+
<html lang="en">
|
|
104
|
+
<head>
|
|
105
|
+
<meta charset="utf-8">
|
|
106
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
107
|
+
<meta name="color-scheme" content="light dark">
|
|
108
|
+
<title>${escapeHtml(title)}</title>
|
|
109
|
+
<style>${STYLES}</style>
|
|
110
|
+
</head>
|
|
111
|
+
<body>
|
|
112
|
+
<div class="shell">
|
|
113
|
+
|
|
114
|
+
<header class="masthead">
|
|
115
|
+
<div>
|
|
116
|
+
<h1>Bank reconciliation</h1>
|
|
117
|
+
<p>${escapeHtml(data.account)} ${escapeHtml(data.accountName)} · ${escapeHtml(data.period.from)} to ${escapeHtml(data.period.to)}</p>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="masthead-meta">
|
|
120
|
+
<div><span>Currency</span><strong>${escapeHtml(data.currency)}</strong></div>
|
|
121
|
+
<div><span>Statement</span><strong>${escapeHtml(data.statementFormat.toUpperCase())}</strong></div>
|
|
122
|
+
<div><span>Trial balance</span><strong>${data.trialBalanceBalanced ? "Agrees" : "Does not agree"}</strong></div>
|
|
123
|
+
</div>
|
|
124
|
+
</header>
|
|
125
|
+
|
|
126
|
+
<p id="live-region" class="visually-hidden" role="status" aria-live="polite"></p>
|
|
127
|
+
|
|
128
|
+
<section>
|
|
129
|
+
<h2>Where it stands</h2>
|
|
130
|
+
<p class="section-note">The bridge recomputes as you work: an undecided suggestion counts as outstanding on both sides, because until someone confirms it, it is not evidence.</p>
|
|
131
|
+
<div class="split">
|
|
132
|
+
<div class="panel bridge">
|
|
133
|
+
<dl id="bridge-list"></dl>
|
|
134
|
+
<div class="bridge-verdict" id="bridge-verdict" data-state="off">
|
|
135
|
+
<span class="label">Reconciled</span>
|
|
136
|
+
<span class="figure">0.00</span>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div>
|
|
140
|
+
<dl class="counts">
|
|
141
|
+
<div><dt>Matched</dt><dd id="count-matched">0</dd></div>
|
|
142
|
+
<div><dt>To review</dt><dd id="count-review">0</dd></div>
|
|
143
|
+
<div><dt>Ledger only</dt><dd id="count-book">0</dd></div>
|
|
144
|
+
<div><dt>Bank only</dt><dd id="count-bank">0</dd></div>
|
|
145
|
+
</dl>
|
|
146
|
+
<div class="panel" style="margin-top:20px">
|
|
147
|
+
<h2 style="font-size:0.875rem">Match confidence</h2>
|
|
148
|
+
<p class="section-note" style="margin-bottom:12px">How much of this the matcher actually knew.</p>
|
|
149
|
+
${confidenceChart(data.confidence)}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</section>
|
|
154
|
+
|
|
155
|
+
<section>
|
|
156
|
+
<h2>Review queue</h2>
|
|
157
|
+
<p class="section-note">Pairs the matcher believes but will not post on its own. Accepting one moves it into the matched set and updates the bridge above.</p>
|
|
158
|
+
<div class="queue" id="queue">${queueCards}</div>
|
|
159
|
+
</section>
|
|
160
|
+
|
|
161
|
+
<section id="decided-section" hidden>
|
|
162
|
+
<h2>Decided</h2>
|
|
163
|
+
<p class="section-note">What you have said so far. Nothing is written anywhere until you export it, and any of these can be taken back until then.</p>
|
|
164
|
+
<div class="panel"><div class="decided-list" id="decided-list"></div></div>
|
|
165
|
+
</section>
|
|
166
|
+
|
|
167
|
+
<section>
|
|
168
|
+
<h2>Leftovers</h2>
|
|
169
|
+
<p class="section-note">What each side has that the other does not. These are the timing differences and the entries nobody has booked.</p>
|
|
170
|
+
<div class="split">
|
|
171
|
+
<div class="panel">
|
|
172
|
+
<h2 style="font-size:0.875rem">In the books, not on the statement</h2>
|
|
173
|
+
<div id="leftover-book" style="margin-top:12px"></div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="panel">
|
|
176
|
+
<h2 style="font-size:0.875rem">On the statement, not in the books</h2>
|
|
177
|
+
<div id="leftover-bank" style="margin-top:12px"></div>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</section>
|
|
181
|
+
|
|
182
|
+
<section id="implied-section">
|
|
183
|
+
<h2>What this implies</h2>
|
|
184
|
+
<p class="section-note">The entries these statement lines would need if nothing in the books explains them. Rejecting a suggestion adds one here; accepting it takes one away. Run <code>tallyd post</code> to book them.</p>
|
|
185
|
+
<div class="panel" id="implied"></div>
|
|
186
|
+
</section>
|
|
187
|
+
|
|
188
|
+
<section>
|
|
189
|
+
<h2>Cash position</h2>
|
|
190
|
+
<p class="section-note">The balance of ${escapeHtml(data.account)} after every day it moved.</p>
|
|
191
|
+
<div class="panel">${cashPositionChart(data.cashPosition, { exponent: data.exponent, symbol: data.currencySymbol })}</div>
|
|
192
|
+
</section>
|
|
193
|
+
|
|
194
|
+
<section>
|
|
195
|
+
<h2>Accepted matches</h2>
|
|
196
|
+
<details>
|
|
197
|
+
<summary><span class="chevron"></span>Matched outright<span class="tally" id="matched-tally">0</span></summary>
|
|
198
|
+
<div class="queue">${matchedCards}</div>
|
|
199
|
+
</details>
|
|
200
|
+
</section>
|
|
201
|
+
|
|
202
|
+
<section>
|
|
203
|
+
<h2>The ledger</h2>
|
|
204
|
+
<p class="section-note">Every account that has been posted to. Choose one to read its postings.</p>
|
|
205
|
+
<div class="split">
|
|
206
|
+
<div class="panel">${accountsTable(data)}</div>
|
|
207
|
+
<div class="panel" id="postings"></div>
|
|
208
|
+
</div>
|
|
209
|
+
</section>
|
|
210
|
+
|
|
211
|
+
<footer>
|
|
212
|
+
<span>Generated by ${escapeHtml(data.generatedFor)} — this file is self-contained and works offline.</span>
|
|
213
|
+
<span>Export decisions, then <code>tallyd learn</code> to teach the matcher and <code>tallyd post</code> to book what is left.</span>
|
|
214
|
+
</footer>
|
|
215
|
+
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div class="decision-bar" id="decision-bar" data-open="false" role="region" aria-label="Decisions to export">
|
|
219
|
+
<span class="tally-text" id="decision-tally">No decisions yet</span>
|
|
220
|
+
<span class="bar-actions">
|
|
221
|
+
<button type="button" data-action="undo-last">Undo last</button>
|
|
222
|
+
<button type="button" data-action="copy-decisions">Copy</button>
|
|
223
|
+
<button type="button" class="primary" data-action="export-decisions">Export decisions</button>
|
|
224
|
+
</span>
|
|
225
|
+
</div>
|
|
226
|
+
<script>window.__TALLYD__ = ${embedJson(data)};</script>
|
|
227
|
+
<script>${CLIENT_SCRIPT}</script>
|
|
228
|
+
</body>
|
|
229
|
+
</html>
|
|
230
|
+
`;
|
|
231
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client script, inlined.
|
|
3
|
+
*
|
|
4
|
+
* It does one thing that matters: accepting or rejecting a suggestion changes
|
|
5
|
+
* the reconciliation and the bridge recomputes in front of you. A dashboard
|
|
6
|
+
* that renders the same static figures the CLI already prints is a worse CLI —
|
|
7
|
+
* the reason to have a screen is that a decision can have an effect.
|
|
8
|
+
*
|
|
9
|
+
* The arithmetic runs entirely in integer minor units. JavaScript numbers are
|
|
10
|
+
* exact for integers well past any sum a small company's bank account will
|
|
11
|
+
* reach, and the one thing this page must never do is show a reconciliation
|
|
12
|
+
* that is out by a penny because a float rounded.
|
|
13
|
+
*
|
|
14
|
+
* ## What leaves the page
|
|
15
|
+
*
|
|
16
|
+
* Decisions are exported as the decisions document, which `tallyd learn` reads.
|
|
17
|
+
* The payload for each suggestion was built in TypeScript and baked into
|
|
18
|
+
* `window.__TALLYD__`; this script stamps a verdict and today's date onto it
|
|
19
|
+
* and serialises the result. Nothing here knows the shape of the format beyond
|
|
20
|
+
* those two fields, which is the point — a format definition living in a
|
|
21
|
+
* string of hand-written client JavaScript is a format definition nobody can
|
|
22
|
+
* test.
|
|
23
|
+
*
|
|
24
|
+
* A decision is reversible until it is exported. A queue card is hidden rather
|
|
25
|
+
* than destroyed when it is decided, so undo is a matter of putting it back
|
|
26
|
+
* where it was rather than rebuilding it from data.
|
|
27
|
+
*/
|
|
28
|
+
export declare const CLIENT_SCRIPT: string;
|