domma-cms 0.55.1 → 0.66.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/CLAUDE.md +130 -2
- package/README.md +4 -4
- package/admin/css/admin.css +1 -1
- package/admin/dist/domma/domma-tools.css +3 -3
- package/admin/dist/domma/domma-tools.min.js +3 -3
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +3 -3
- package/admin/js/lib/page-picker.js +1 -0
- package/admin/js/lib/plugin-accent.js +1 -0
- package/admin/js/lib/plugin-chrome.js +1 -0
- package/admin/js/lib/shortcode-context-menu.js +2 -2
- package/admin/js/lib/sidebar-grouping.js +1 -1
- package/admin/js/lib/sidebar-grouping.test.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +4 -4
- package/admin/js/lib/slideover-resizable.js +1 -0
- package/admin/js/templates/context-menu-editor.html +212 -0
- package/admin/js/templates/context-menus.html +16 -0
- package/admin/js/templates/plugin-code.html +1 -1
- package/{plugins/site-search/admin/templates/site-search.html → admin/js/templates/search.html} +50 -4
- package/admin/js/templates/settings.html +16 -97
- package/admin/js/templates/theme.html +173 -0
- package/admin/js/views/context-menu-editor.js +55 -0
- package/admin/js/views/context-menus.js +5 -0
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/plugin-marketplace.js +1 -1
- package/admin/js/views/plugins.js +25 -23
- package/admin/js/views/search.js +1 -0
- package/admin/js/views/settings.js +3 -3
- package/admin/js/views/theme.js +1 -0
- package/bin/cli.js +2 -0
- package/bin/update.js +13 -2
- package/config/menus/admin-sidebar.json +129 -23
- package/config/plugins.json +5 -5
- package/config/search.json +13 -0
- package/config/theme.json +18 -0
- package/package.json +12 -4
- package/plugins/_lib/admin/mail/compose-window.js +914 -0
- package/plugins/_lib/admin/mail/contacts.js +301 -0
- package/plugins/_lib/admin/mail/diagnostics-section.js +133 -0
- package/plugins/_lib/admin/mail/folder-tree.js +254 -0
- package/plugins/_lib/admin/mail/identity.js +480 -0
- package/plugins/_lib/admin/mail/image-senders-section.js +131 -0
- package/plugins/_lib/admin/mail/keyboard.js +136 -0
- package/plugins/_lib/admin/mail/mail.css +1 -0
- package/plugins/_lib/admin/mail/mail.html +71 -0
- package/plugins/_lib/admin/mail/panes.js +253 -0
- package/plugins/_lib/admin/mail/reader-view.js +4453 -0
- package/plugins/_lib/admin/mail/resizable.js +26 -0
- package/plugins/_lib/admin/mail/rules.js +343 -0
- package/plugins/_lib/admin/mail/scheduling.js +203 -0
- package/plugins/_lib/admin/mail/section-kit.js +277 -0
- package/plugins/_lib/admin/mail/templates.js +238 -0
- package/plugins/_lib/admin/mail/threads.js +200 -0
- package/plugins/_lib/admin/mail/vacation.js +269 -0
- package/plugins/_lib/admin/ui/help.css +1 -0
- package/plugins/_lib/admin/ui/help.js +174 -0
- package/plugins/_lib/admin/ui/resizable.js +151 -0
- package/plugins/_lib/dataStore.js +117 -0
- package/plugins/_lib/mail/accounts.js +919 -0
- package/plugins/_lib/mail/bodyTokens.js +101 -0
- package/plugins/_lib/mail/compose.js +256 -0
- package/plugins/_lib/mail/defaults.js +57 -0
- package/plugins/_lib/mail/diagnostics.js +154 -0
- package/plugins/_lib/mail/envelope.js +161 -0
- package/plugins/_lib/mail/folders.js +192 -0
- package/plugins/_lib/mail/handoff.js +274 -0
- package/plugins/_lib/mail/imapPool.js +293 -0
- package/plugins/_lib/mail/mbox.js +74 -0
- package/plugins/_lib/mail/pollSchedule.js +79 -0
- package/plugins/_lib/mail/poller.js +135 -0
- package/plugins/_lib/mail/priority.js +138 -0
- package/plugins/_lib/mail/readRoutes.js +680 -0
- package/plugins/_lib/mail/render.js +291 -0
- package/plugins/_lib/mail/ruleRunner.js +152 -0
- package/plugins/_lib/mail/scheduler.js +254 -0
- package/plugins/_lib/mail/secretbox.js +229 -0
- package/plugins/_lib/mail/send.js +396 -0
- package/plugins/_lib/mail/store.js +1002 -0
- package/plugins/_lib/mail/sync.js +292 -0
- package/plugins/_lib/mail/syncPlan.js +126 -0
- package/plugins/_lib/mail/syncSelection.js +82 -0
- package/plugins/_lib/mail/unsubscribe.js +183 -0
- package/plugins/_lib/mail/vacationRunner.js +114 -0
- package/plugins/_lib/mail/write.js +473 -0
- package/plugins/_lib/schemaSync.js +83 -0
- package/plugins/_template/admin/css/index.css +0 -0
- package/plugins/_template/admin/templates/index.html +4 -4
- package/plugins/_template/admin/views/index.js +7 -0
- package/plugins/analytics/admin/css/index.css +1 -0
- package/plugins/analytics/admin/templates/analytics.html +22 -13
- package/plugins/analytics/plugin.json +3 -0
- package/plugins/blog/admin/css/index.css +1 -0
- package/plugins/blog/admin/templates/blog.html +30 -18
- package/plugins/blog/admin/templates/categories.html +2 -2
- package/plugins/blog/admin/templates/comments.html +2 -2
- package/plugins/blog/admin/templates/post-editor.html +34 -34
- package/plugins/blog/admin/templates/settings.html +6 -3
- package/plugins/blog/admin/views/blog.js +8 -5
- package/plugins/blog/admin/views/categories.js +5 -10
- package/plugins/blog/admin/views/comments.js +5 -5
- package/plugins/blog/admin/views/post-editor.js +39 -20
- package/plugins/blog/admin/views/settings.js +52 -50
- package/plugins/blog/collections/categories/schema.json +7 -6
- package/plugins/blog/collections/comments/schema.json +11 -10
- package/plugins/blog/collections/posts/schema.json +14 -13
- package/plugins/blog/plugin.js +36 -13
- package/plugins/blog/plugin.json +13 -5
- package/plugins/blog/plugin.public.js +312 -0
- package/plugins/contacts/admin/css/index.css +1 -0
- package/plugins/contacts/admin/templates/contacts.html +128 -0
- package/plugins/contacts/admin/views/contacts.js +237 -4
- package/plugins/contacts/collections/user-contacts/schema.json +108 -0
- package/plugins/contacts/plugin.js +214 -27
- package/plugins/contacts/plugin.json +4 -1
- package/plugins/invoice/admin/css/index.css +1 -0
- package/plugins/invoice/admin/templates/editor.html +140 -49
- package/plugins/invoice/admin/templates/index.html +153 -23
- package/plugins/invoice/admin/templates/issuers.html +2 -5
- package/plugins/invoice/admin/templates/receivers.html +2 -5
- package/plugins/invoice/admin/views/contacts-source.js +266 -0
- package/plugins/invoice/admin/views/editor.js +366 -199
- package/plugins/invoice/admin/views/export.js +199 -0
- package/plugins/invoice/admin/views/help-content.js +61 -0
- package/plugins/invoice/admin/views/index.js +582 -94
- package/plugins/invoice/admin/views/issuers.js +24 -17
- package/plugins/invoice/admin/views/media.js +172 -0
- package/plugins/invoice/admin/views/party-view.js +305 -67
- package/plugins/invoice/admin/views/payments.js +127 -0
- package/plugins/invoice/admin/views/print.js +130 -0
- package/plugins/invoice/admin/views/receivers.js +49 -16
- package/plugins/invoice/admin/views/send.js +212 -0
- package/plugins/invoice/admin/views/settings.js +594 -0
- package/plugins/invoice/admin/views/view-lifecycle.js +33 -0
- package/plugins/invoice/collections/invoice-issuers/schema.json +77 -11
- package/plugins/invoice/collections/invoice-receivers/schema.json +10 -9
- package/plugins/invoice/collections/invoices/schema.json +19 -13
- package/plugins/invoice/config.js +27 -6
- package/plugins/invoice/pdf.js +164 -0
- package/plugins/invoice/plugin.js +1217 -44
- package/plugins/invoice/plugin.json +10 -9
- package/plugins/invoice/templates/_base.css +1 -0
- package/plugins/invoice/templates/classic-nologo.html +100 -0
- package/plugins/invoice/templates/classic.html +91 -0
- package/plugins/invoice/templates/invoice-print.html +24 -0
- package/plugins/invoice/templates/minimal.html +99 -0
- package/plugins/invoice/templates/modern-nologo.html +114 -0
- package/plugins/invoice/templates/modern.html +113 -0
- package/plugins/invoice/templates/templates.json +11 -0
- package/plugins/mail-reader/admin/views/mail.js +19 -0
- package/plugins/mail-reader/config.js +7 -0
- package/plugins/mail-reader/plugin.js +48 -0
- package/plugins/mail-reader/plugin.json +33 -0
- package/plugins/notes/admin/views/notes.js +1 -1
- package/plugins/notes/plugin.json +2 -2
- package/plugins/surveys/lib/audience.js +37 -0
- package/plugins/surveys/lib/campaigns.js +43 -0
- package/plugins/surveys/lib/ledger.js +110 -0
- package/plugins/surveys/lib/sending.js +106 -0
- package/plugins/surveys/lib/stats.js +62 -0
- package/plugins/surveys/lib/submit.js +95 -0
- package/plugins/surveys/lib/tokens.js +28 -0
- package/plugins/surveys/plugin.public.js +149 -0
- package/plugins/theme-switcher/admin/templates/theme-switcher.html +1 -1
- package/public/css/forms.css +1 -1
- package/public/css/search.css +1 -0
- package/public/css/site.css +1 -1
- package/public/js/collection-context.js +2 -2
- package/public/js/context-menus.js +1 -0
- package/public/js/form-logic-engine.js +1 -1
- package/public/js/forms.js +2 -2
- package/public/js/search.js +1 -0
- package/public/js/site.js +1 -1
- package/scripts/build.js +37 -3
- package/scripts/copy-domma.js +48 -0
- package/scripts/seed.js +1996 -0
- package/server/routes/api/collections.js +34 -0
- package/server/routes/api/context-menus.js +104 -0
- package/server/routes/api/forms.js +42 -3
- package/server/routes/api/notifications.js +69 -19
- package/server/routes/api/plugins.js +50 -6
- package/server/routes/api/search.js +43 -0
- package/server/routes/api/theme.js +69 -0
- package/server/routes/public.js +42 -7
- package/server/server.js +74 -0
- package/server/services/adapters/FileAdapter.js +6 -1
- package/server/services/content.js +26 -0
- package/server/services/contextMenus.js +477 -0
- package/server/services/markdown.js +70 -9
- package/server/services/permissionRegistry.js +24 -0
- package/server/services/pluginFiles.js +52 -11
- package/server/services/plugins.js +229 -6
- package/server/services/renderer.js +144 -22
- package/server/services/roles.js +1 -1
- package/server/services/search-migration.js +82 -0
- package/server/services/search.js +413 -0
- package/server/services/sidebar-migration.js +1 -0
- package/server/services/themeSettings.js +541 -0
- package/server/services/users.js +8 -0
- package/server/templates/page.html +4 -2
- package/plugins/contacts/data/contacts.json +0 -20
- package/plugins/notes/data/notes.json +0 -1
- package/plugins/site-search/admin/views/site-search.js +0 -116
- package/plugins/site-search/config.js +0 -15
- package/plugins/site-search/plugin.js +0 -188
- package/plugins/site-search/plugin.json +0 -40
- package/plugins/site-search/public/inject-body.html +0 -17
- package/plugins/site-search/public/inject-head.html +0 -1
- package/plugins/site-search/public/search.css +0 -1
- package/plugins/site-search/public/search.js +0 -1
- package/plugins/todo/data/todos.json +0 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoice plugin - saving invoices as a file.
|
|
3
|
+
*
|
|
4
|
+
* Domma's table can export what it is showing, but it exports the RENDERED
|
|
5
|
+
* columns: a receiver id instead of a name, an HTML badge instead of a status,
|
|
6
|
+
* and no line items at all, because they are not in the table. An accountant
|
|
7
|
+
* asking for "the invoices as a spreadsheet" wants neither of those, so the
|
|
8
|
+
* rows are built here from the data and the table's own exporter is left off.
|
|
9
|
+
*
|
|
10
|
+
* Excel is an HTML table served as `application/vnd.ms-excel`, which is what
|
|
11
|
+
* Domma's own exporter does and what Excel and LibreOffice both open without
|
|
12
|
+
* a converter. It is not a real .xlsx, and it does not need to be - nothing
|
|
13
|
+
* here has formulas, merged cells or more than one sheet.
|
|
14
|
+
*/
|
|
15
|
+
function escapeHtml(s) {
|
|
16
|
+
return String(s ?? '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {Array<Array<string|number>>} rows
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
function toCsv(rows) {
|
|
24
|
+
return rows.map((row) => row.map((cell) => {
|
|
25
|
+
const text = cell === null || cell === undefined ? '' : String(cell);
|
|
26
|
+
return `"${text.replace(/"/g, '""')}"`;
|
|
27
|
+
}).join(',')).join('\r\n');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} title
|
|
32
|
+
* @param {Array<Array<string|number>>} rows
|
|
33
|
+
* @param {number} headerRows - how many leading rows to render as <th>
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
function toExcel(title, rows, headerRows = 1) {
|
|
37
|
+
const body = rows.map((row, index) => {
|
|
38
|
+
const tag = index < headerRows ? 'th' : 'td';
|
|
39
|
+
return '<tr>' + row.map((cell) => `<${tag}>${escapeHtml(cell)}</${tag}>`).join('') + '</tr>';
|
|
40
|
+
}).join('');
|
|
41
|
+
|
|
42
|
+
return `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel">
|
|
43
|
+
<head><meta charset="UTF-8">
|
|
44
|
+
<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
|
|
45
|
+
<x:Name>${escapeHtml(title).slice(0, 31)}</x:Name>
|
|
46
|
+
<x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions>
|
|
47
|
+
</x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
|
|
48
|
+
<style>table{border-collapse:collapse}th,td{border:1px solid #999;padding:6px}th{background:#eee;font-weight:bold;text-align:left}</style>
|
|
49
|
+
</head><body><table>${body}</table></body></html>`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} content
|
|
54
|
+
* @param {string} mime
|
|
55
|
+
* @param {string} filename
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
function save(content, mime, filename) {
|
|
59
|
+
// A BOM, because Excel reads a CSV without one as the system codepage and
|
|
60
|
+
// turns every pound sign into £.
|
|
61
|
+
const blob = new Blob([mime.startsWith('text/csv') ? '' + content : content], { type: mime });
|
|
62
|
+
const url = URL.createObjectURL(blob);
|
|
63
|
+
const a = document.createElement('a');
|
|
64
|
+
a.href = url;
|
|
65
|
+
a.download = filename;
|
|
66
|
+
a.rel = 'noopener';
|
|
67
|
+
document.body.appendChild(a);
|
|
68
|
+
a.click();
|
|
69
|
+
a.remove();
|
|
70
|
+
// Revoked on the next tick: revoking synchronously races the download in
|
|
71
|
+
// Firefox, which has not necessarily read the blob by the time click()
|
|
72
|
+
// returns.
|
|
73
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** A filename component that no filesystem will argue about. */
|
|
77
|
+
function safeName(text, fallback) {
|
|
78
|
+
const cleaned = String(text ?? '').replace(/[^\w.-]+/g, '_').replace(/^_+|_+$/g, '');
|
|
79
|
+
return cleaned || fallback;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function money(amount) {
|
|
83
|
+
return (Number(amount) || 0).toFixed(2);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function dateText(value) {
|
|
87
|
+
return value ? D(value).format('DD MMM YYYY') : '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* One invoice as a small worksheet: the facts, then the lines, then the totals.
|
|
92
|
+
*
|
|
93
|
+
* @param {object} invoice
|
|
94
|
+
* @param {{issuerName: string, receiverName: string}} names
|
|
95
|
+
* @returns {Array<Array<string|number>>}
|
|
96
|
+
*/
|
|
97
|
+
export function invoiceRows(invoice, { issuerName = '', receiverName = '' } = {}) {
|
|
98
|
+
const currency = invoice.currency || 'GBP';
|
|
99
|
+
const totals = invoice.totals ?? {};
|
|
100
|
+
const rows = [
|
|
101
|
+
['Invoice', invoice.number || ''],
|
|
102
|
+
['Status', invoice.overdue ? 'overdue' : (invoice.status || 'draft')],
|
|
103
|
+
['From', issuerName],
|
|
104
|
+
['To', receiverName],
|
|
105
|
+
['Issued', dateText(invoice.issueDate)],
|
|
106
|
+
['Due', dateText(invoice.dueDate)],
|
|
107
|
+
['Currency', currency],
|
|
108
|
+
[],
|
|
109
|
+
['Description', 'Quantity', 'Unit price', 'Line total']
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (const line of invoice.lineItems ?? []) {
|
|
113
|
+
const quantity = Number(line.quantity) || 0;
|
|
114
|
+
const unit = Number(line.unitPrice) || 0;
|
|
115
|
+
rows.push([line.description || '', quantity, money(unit), money(quantity * unit)]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
rows.push([]);
|
|
119
|
+
rows.push(['', '', 'Subtotal', money(totals.subtotal)]);
|
|
120
|
+
if (invoice.vatEnabled) rows.push(['', '', `VAT (${totals.vatRate ?? invoice.vatRate ?? 0}%)`, money(totals.vat)]);
|
|
121
|
+
rows.push(['', '', 'Total', money(totals.total)]);
|
|
122
|
+
if (Number(totals.paid) > 0) {
|
|
123
|
+
rows.push(['', '', 'Paid', money(totals.paid)]);
|
|
124
|
+
rows.push(['', '', 'Balance', money(Math.max(0, Number(totals.balance) || 0))]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (invoice.notes) {
|
|
128
|
+
rows.push([]);
|
|
129
|
+
rows.push(['Notes', invoice.notes]);
|
|
130
|
+
}
|
|
131
|
+
return rows;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A list of invoices, one per row.
|
|
136
|
+
*
|
|
137
|
+
* @param {object[]} invoices
|
|
138
|
+
* @param {(invoice: object) => {issuerName: string, receiverName: string}} names
|
|
139
|
+
* @returns {Array<Array<string|number>>}
|
|
140
|
+
*/
|
|
141
|
+
export function listRows(invoices, names) {
|
|
142
|
+
const rows = [[
|
|
143
|
+
'Number', 'Status', 'From', 'To', 'Issued', 'Due',
|
|
144
|
+
'Currency', 'Subtotal', 'VAT', 'Total', 'Paid', 'Balance'
|
|
145
|
+
]];
|
|
146
|
+
|
|
147
|
+
for (const invoice of invoices) {
|
|
148
|
+
const totals = invoice.totals ?? {};
|
|
149
|
+
const { issuerName = '', receiverName = '' } = names(invoice) ?? {};
|
|
150
|
+
rows.push([
|
|
151
|
+
invoice.number || '',
|
|
152
|
+
invoice.overdue ? 'overdue' : (invoice.status || 'draft'),
|
|
153
|
+
issuerName,
|
|
154
|
+
receiverName,
|
|
155
|
+
dateText(invoice.issueDate),
|
|
156
|
+
dateText(invoice.dueDate),
|
|
157
|
+
invoice.currency || 'GBP',
|
|
158
|
+
money(totals.subtotal),
|
|
159
|
+
money(totals.vat),
|
|
160
|
+
money(totals.total),
|
|
161
|
+
money(totals.paid),
|
|
162
|
+
money(totals.balance)
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
return rows;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {object} invoice
|
|
170
|
+
* @param {'csv'|'excel'} format
|
|
171
|
+
* @param {object} names
|
|
172
|
+
* @returns {void}
|
|
173
|
+
*/
|
|
174
|
+
export function exportInvoice(invoice, format, names) {
|
|
175
|
+
const rows = invoiceRows(invoice, names);
|
|
176
|
+
const stem = safeName(invoice.number, 'invoice');
|
|
177
|
+
if (format === 'excel') {
|
|
178
|
+
save(toExcel(stem, rows, 0), 'application/vnd.ms-excel', `${stem}.xls`);
|
|
179
|
+
} else {
|
|
180
|
+
save(toCsv(rows), 'text/csv;charset=utf-8', `${stem}.csv`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @param {object[]} invoices
|
|
186
|
+
* @param {'csv'|'excel'} format
|
|
187
|
+
* @param {Function} names
|
|
188
|
+
* @param {string} [stem]
|
|
189
|
+
* @returns {void}
|
|
190
|
+
*/
|
|
191
|
+
export function exportList(invoices, format, names, stem = 'invoices') {
|
|
192
|
+
const rows = listRows(invoices, names);
|
|
193
|
+
const filename = `${safeName(stem, 'invoices')}-${D().format('YYYY-MM-DD')}`;
|
|
194
|
+
if (format === 'excel') {
|
|
195
|
+
save(toExcel('Invoices', rows, 1), 'application/vnd.ms-excel', `${filename}.xls`);
|
|
196
|
+
} else {
|
|
197
|
+
save(toCsv(rows), 'text/csv;charset=utf-8', `${filename}.csv`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the Invoices plugin's Help panel says.
|
|
3
|
+
*
|
|
4
|
+
* Kept as data rather than markup so the same words can be shown in the panel,
|
|
5
|
+
* quoted in a tooltip, or lifted into the docs without being re-typed - and so
|
|
6
|
+
* that changing how help LOOKS never means editing what it SAYS.
|
|
7
|
+
*
|
|
8
|
+
* Written for someone who has not used the screen before, in the order they
|
|
9
|
+
* will meet it: make an issuer, make a receiver, make an invoice, send it, get
|
|
10
|
+
* paid, file it away.
|
|
11
|
+
*/
|
|
12
|
+
export const INVOICE_HELP = {
|
|
13
|
+
title: 'Invoices - how it works',
|
|
14
|
+
intro: 'An invoice is raised BY an issuer (you) FOR a receiver (your customer). Both live in Settings, behind the cog.',
|
|
15
|
+
sections: [
|
|
16
|
+
{
|
|
17
|
+
title: 'Before your first invoice',
|
|
18
|
+
body: [
|
|
19
|
+
'Add an issuer: your trading name, address, VAT number and the account you are paid into. Add one per business you invoice as; "Invoice as" picks which one a new invoice opens with.',
|
|
20
|
+
'Add a receiver: who you are billing. If you use the Contacts plugin, Import from Contacts brings the company name, billing address, VAT number and reference across for you.',
|
|
21
|
+
'Until there is at least one of each, New Invoice stays switched off - an invoice with nobody on either end cannot be saved.'
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
title: 'Numbers, dates and VAT',
|
|
26
|
+
body: [
|
|
27
|
+
'Numbers are allocated when an invoice is created, per prefix and per year, and are never reused. Typing your own number still wins.',
|
|
28
|
+
'The due date is filled in from your payment terms, and an invoice past its due date reports itself overdue without anything having to run - the stored status stays whatever you set.',
|
|
29
|
+
'VAT is one rate per invoice, applied to the total. Line item prices are net.'
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: 'Sending and getting paid',
|
|
34
|
+
body: [
|
|
35
|
+
'Send emails the invoice, attaching a PDF where the server can render one. Recipients can be typed, or picked from your contacts - including everybody in a group.',
|
|
36
|
+
'Record a payment against an invoice and the balance follows; settle it in full and the status moves to paid on its own.'
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
title: 'Filing',
|
|
41
|
+
body: [
|
|
42
|
+
'Archive is a shelf, not a status: an archived invoice keeps whatever it was and simply stops appearing in the list. Show archived swaps the list over to the shelf.',
|
|
43
|
+
'Delete is permanent. For anything you might need again - and an invoice you have sent is exactly that - archive it instead.'
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
title: 'How it looks',
|
|
48
|
+
body: [
|
|
49
|
+
'Settings > Templates chooses the layout, with a worked example in your own name. An issuer with no logo gets a layout built for having none rather than the same one with a gap in it.',
|
|
50
|
+
'Settings > Branding holds the house logo, used for any issuer that has not got one of its own.'
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
title: 'Getting it out',
|
|
55
|
+
body: [
|
|
56
|
+
'Right-click any row (or use the ⋯ button) to print, send, download a PDF, export that one invoice as CSV or Excel, duplicate it, change its status, archive or delete it.',
|
|
57
|
+
'The CSV and Excel buttons above the table export what the filters are currently showing, not everything.'
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
};
|