@aglyn/plugins-commerce 1.0.0-beta.289 → 1.0.0-beta.291
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/README.md +4 -2
- package/package.json +13 -13
- package/src/lib/components/commerce-console-page.js +2 -1
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/commerce-pdf-download.d.ts +30 -0
- package/src/lib/components/console/commerce-pdf-download.js +53 -0
- package/src/lib/components/console/commerce-pdf-download.js.map +1 -0
- package/src/lib/components/console/host-orders-card.component.d.ts +6 -0
- package/src/lib/components/console/host-orders-card.component.js +3 -2
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/host-quotes-card.component.js +20 -4
- package/src/lib/components/console/host-quotes-card.component.js.map +1 -1
- package/src/lib/components/console/invoice-send.component.d.ts +44 -0
- package/src/lib/components/console/invoice-send.component.js +237 -0
- package/src/lib/components/console/invoice-send.component.js.map +1 -0
- package/src/lib/components/console/order-detail-dialog.component.d.ts +5 -0
- package/src/lib/components/console/order-detail-dialog.component.js +9 -2
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-invoice.component.d.ts +15 -4
- package/src/lib/components/console/order-invoice.component.js +93 -12
- package/src/lib/components/console/order-invoice.component.js.map +1 -1
- package/src/lib/components/console/quote-editor-dialog.component.d.ts +29 -0
- package/src/lib/components/console/quote-editor-dialog.component.js +154 -5
- package/src/lib/components/console/quote-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/quote-proofs.component.d.ts +48 -0
- package/src/lib/components/console/quote-proofs.component.js +694 -0
- package/src/lib/components/console/quote-proofs.component.js.map +1 -0
- package/src/lib/components/console/quote-send.component.d.ts +42 -0
- package/src/lib/components/console/quote-send.component.js +307 -0
- package/src/lib/components/console/quote-send.component.js.map +1 -0
- package/src/lib/components/console/quote-start.component.d.ts +45 -0
- package/src/lib/components/console/quote-start.component.js +182 -0
- package/src/lib/components/console/quote-start.component.js.map +1 -0
- package/src/lib/components/product-options-field-body.d.ts +44 -0
- package/src/lib/components/product-options-field-body.js +260 -0
- package/src/lib/components/product-options-field-body.js.map +1 -0
- package/src/lib/components/product-options-field.d.ts +32 -0
- package/src/lib/components/product-options-field.js +128 -0
- package/src/lib/components/product-options-field.js.map +1 -0
- package/src/lib/components/quote-page-body.js +67 -20
- package/src/lib/components/quote-page-body.js.map +1 -1
- package/src/lib/components/quote-proof-panel.d.ts +42 -0
- package/src/lib/components/quote-proof-panel.js +436 -0
- package/src/lib/components/quote-proof-panel.js.map +1 -0
- package/src/lib/model/commerce-invoices.d.ts +132 -0
- package/src/lib/model/commerce-invoices.js +332 -0
- package/src/lib/model/commerce-invoices.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +29 -2
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-product-field.d.ts +23 -0
- package/src/lib/model/commerce-product-field.js +26 -0
- package/src/lib/model/commerce-product-field.js.map +1 -0
- package/src/lib/model/commerce-quote-email.d.ts +67 -0
- package/src/lib/model/commerce-quote-email.js +154 -0
- package/src/lib/model/commerce-quote-email.js.map +1 -0
- package/src/lib/model/commerce-quote-pay.d.ts +76 -6
- package/src/lib/model/commerce-quote-pay.js +105 -39
- package/src/lib/model/commerce-quote-pay.js.map +1 -1
- package/src/lib/model/commerce-quote-proofs.d.ts +159 -0
- package/src/lib/model/commerce-quote-proofs.js +348 -0
- package/src/lib/model/commerce-quote-proofs.js.map +1 -0
- package/src/lib/model/commerce-quote-request.d.ts +93 -0
- package/src/lib/model/commerce-quote-request.js +207 -0
- package/src/lib/model/commerce-quote-request.js.map +1 -0
- package/src/lib/model/commerce-quotes.d.ts +140 -3
- package/src/lib/model/commerce-quotes.js.map +1 -1
- package/src/lib/model/index.d.ts +4 -0
- package/src/lib/model/index.js +4 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/plugin.js +19 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +115 -70
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/commerce-pdf.d.ts +104 -0
- package/src/lib/server/commerce-pdf.js +310 -0
- package/src/lib/server/commerce-pdf.js.map +1 -0
- package/src/lib/server/draft-order.js +23 -3
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/invoice-email.d.ts +76 -0
- package/src/lib/server/invoice-email.js +183 -0
- package/src/lib/server/invoice-email.js.map +1 -0
- package/src/lib/server/order-invoice-send.d.ts +43 -0
- package/src/lib/server/order-invoice-send.js +218 -0
- package/src/lib/server/order-invoice-send.js.map +1 -0
- package/src/lib/server/order-print.d.ts +6 -0
- package/src/lib/server/order-print.js +22 -5
- package/src/lib/server/order-print.js.map +1 -1
- package/src/lib/server/order-status.d.ts +11 -0
- package/src/lib/server/order-status.js +44 -8
- package/src/lib/server/order-status.js.map +1 -1
- package/src/lib/server/pdf-response.d.ts +31 -0
- package/src/lib/server/pdf-response.js +30 -0
- package/src/lib/server/pdf-response.js.map +1 -0
- package/src/lib/server/person-eraser.d.ts +3 -2
- package/src/lib/server/person-eraser.js +25 -6
- package/src/lib/server/person-eraser.js.map +1 -1
- package/src/lib/server/pos-reader-splash.d.ts +2 -18
- package/src/lib/server/pos-reader-splash.js +8 -93
- package/src/lib/server/pos-reader-splash.js.map +1 -1
- package/src/lib/server/quote-email.d.ts +85 -0
- package/src/lib/server/quote-email.js +211 -0
- package/src/lib/server/quote-email.js.map +1 -0
- package/src/lib/server/quote-link-token.d.ts +11 -0
- package/src/lib/server/quote-link-token.js +21 -0
- package/src/lib/server/quote-link-token.js.map +1 -1
- package/src/lib/server/quote-pay.d.ts +38 -6
- package/src/lib/server/quote-pay.js +115 -53
- package/src/lib/server/quote-pay.js.map +1 -1
- package/src/lib/server/quote-proofs.d.ts +51 -0
- package/src/lib/server/quote-proofs.js +500 -0
- package/src/lib/server/quote-proofs.js.map +1 -0
- package/src/lib/server/quote-uploads.d.ts +33 -0
- package/src/lib/server/quote-uploads.js +29 -0
- package/src/lib/server/quote-uploads.js.map +1 -0
- package/src/lib/server/quote.d.ts +25 -0
- package/src/lib/server/quote.js +638 -8
- package/src/lib/server/quote.js.map +1 -1
- package/src/lib/server/site-image-source.d.ts +38 -0
- package/src/lib/server/site-image-source.js +130 -0
- package/src/lib/server/site-image-source.js.map +1 -0
- package/src/lib/server/site-page-resolver.js +2 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server.js +24 -1
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +8 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/tenant-emails.js +243 -0
- package/src/lib/tenant-emails.js.map +1 -1
- package/src/lib/utils/commerce-document-pdf.d.ts +82 -0
- package/src/lib/utils/commerce-document-pdf.js +613 -0
- package/src/lib/utils/commerce-document-pdf.js.map +1 -0
- package/src/lib/utils/invoice-email-content.d.ts +45 -0
- package/src/lib/utils/invoice-email-content.js +126 -0
- package/src/lib/utils/invoice-email-content.js.map +1 -0
- package/src/lib/utils/order-invoice.d.ts +52 -3
- package/src/lib/utils/order-invoice.js +124 -64
- package/src/lib/utils/order-invoice.js.map +1 -1
- package/src/lib/utils/quote-email-content.d.ts +62 -0
- package/src/lib/utils/quote-email-content.js +154 -0
- package/src/lib/utils/quote-email-content.js.map +1 -0
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { buildEmailPalette, emailAccent, resolveEmailColor } from "@aglyn/shared-util-email";
|
|
18
|
+
import { relativeLuminanceOfRgb, rgbChannelsOfHex } from "@aglyn/shared-util-tools/contrast";
|
|
19
|
+
import { PdfWriter, pdfRgbFromHex } from "@aglyn/shared-util-tools/pdf/pdf-writer";
|
|
20
|
+
import { buildOrderInvoiceData, invoiceAddressLines, invoiceMoney } from "./order-invoice.js";
|
|
21
|
+
const NEUTRAL_PALETTE = buildEmailPalette({
|
|
22
|
+
base: 'tenant'
|
|
23
|
+
});
|
|
24
|
+
const neutral = (path)=>{
|
|
25
|
+
var _resolveEmailColor;
|
|
26
|
+
return (_resolveEmailColor = resolveEmailColor(path, NEUTRAL_PALETTE)) != null ? _resolveEmailColor : '';
|
|
27
|
+
};
|
|
28
|
+
/** The colors a document wears where its site's theme gives none: the default site theme's greys. */ export const COMMERCE_PDF_NEUTRAL_COLORS = Object.freeze({
|
|
29
|
+
accent: neutral('grey.800'),
|
|
30
|
+
onAccent: neutral('background.paper'),
|
|
31
|
+
accentText: neutral('grey.800'),
|
|
32
|
+
tint: neutral('grey.100'),
|
|
33
|
+
ink: neutral('text.primary'),
|
|
34
|
+
muted: neutral('text.secondary'),
|
|
35
|
+
rule: neutral('divider')
|
|
36
|
+
});
|
|
37
|
+
/** WCAG contrast between two `#rrggbb` colors; 1 when either cannot be read. */ function contrast(one, other) {
|
|
38
|
+
const a = rgbChannelsOfHex(one);
|
|
39
|
+
const b = rgbChannelsOfHex(other);
|
|
40
|
+
if (!a || !b) return 1;
|
|
41
|
+
const la = relativeLuminanceOfRgb(...a);
|
|
42
|
+
const lb = relativeLuminanceOfRgb(...b);
|
|
43
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A document's colors from a site's resolved palette: the primary color for
|
|
47
|
+
* the accent, the theme's own text and divider colors, and text on the
|
|
48
|
+
* accent that reads at 4.5:1 (the theme's contrast text when it does, else
|
|
49
|
+
* the theme's dark ink or white, whichever reads better).
|
|
50
|
+
*/ export function commercePdfColorsFromPalette(palette) {
|
|
51
|
+
const accent = emailAccent(palette);
|
|
52
|
+
const read = (path, fallback)=>{
|
|
53
|
+
var _resolveEmailColor;
|
|
54
|
+
return (_resolveEmailColor = resolveEmailColor(path, palette)) != null ? _resolveEmailColor : fallback;
|
|
55
|
+
};
|
|
56
|
+
const ink = read('text.primary', COMMERCE_PDF_NEUTRAL_COLORS.ink);
|
|
57
|
+
const paper = read('background.paper', COMMERCE_PDF_NEUTRAL_COLORS.onAccent);
|
|
58
|
+
const onAccent = contrast(accent.label, accent.fill) >= 4.5 ? accent.label : contrast(ink, accent.fill) >= contrast(paper, accent.fill) ? ink : paper;
|
|
59
|
+
return {
|
|
60
|
+
accent: accent.fill,
|
|
61
|
+
onAccent,
|
|
62
|
+
accentText: contrast(accent.link, paper) >= 4.5 ? accent.link : ink,
|
|
63
|
+
tint: read('tint.primary', COMMERCE_PDF_NEUTRAL_COLORS.tint),
|
|
64
|
+
ink,
|
|
65
|
+
muted: read('text.secondary', COMMERCE_PDF_NEUTRAL_COLORS.muted),
|
|
66
|
+
rule: read('divider', COMMERCE_PDF_NEUTRAL_COLORS.rule)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** A date the American way, in the site's time zone: `October 28, 2026`. */ export function formatPdfDate(ms, timeZone) {
|
|
70
|
+
if (!ms || !Number.isFinite(ms)) return '';
|
|
71
|
+
const options = {
|
|
72
|
+
year: 'numeric',
|
|
73
|
+
month: 'long',
|
|
74
|
+
day: 'numeric'
|
|
75
|
+
};
|
|
76
|
+
try {
|
|
77
|
+
return new Intl.DateTimeFormat('en-US', _extends({}, options, timeZone ? {
|
|
78
|
+
timeZone
|
|
79
|
+
} : {})).format(new Date(ms));
|
|
80
|
+
} catch (unused) {
|
|
81
|
+
return new Intl.DateTimeFormat('en-US', _extends({}, options, {
|
|
82
|
+
timeZone: 'UTC'
|
|
83
|
+
})).format(new Date(ms));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const minus = (text)=>`-${text}`;
|
|
87
|
+
function quoteAddressLines(address) {
|
|
88
|
+
return invoiceAddressLines(address, {
|
|
89
|
+
phone: false
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function quoteDocument(input) {
|
|
93
|
+
var _view_customerName, _view_company;
|
|
94
|
+
const { view, brand } = input;
|
|
95
|
+
const money = (cents)=>invoiceMoney(cents, view.currency);
|
|
96
|
+
const date = (ms)=>formatPdfDate(ms, brand.timeZone);
|
|
97
|
+
const customer = [
|
|
98
|
+
(_view_customerName = view.customerName) != null ? _view_customerName : '',
|
|
99
|
+
(_view_company = view.company) != null ? _view_company : ''
|
|
100
|
+
].filter(Boolean);
|
|
101
|
+
const billTo = quoteAddressLines(view.billTo);
|
|
102
|
+
const parties = [];
|
|
103
|
+
if (billTo.length) {
|
|
104
|
+
// The bill-to's own name leads; the customer's name and company are added when it does not carry them.
|
|
105
|
+
const named = customer.filter((part)=>!billTo.includes(part));
|
|
106
|
+
parties.push({
|
|
107
|
+
label: 'Bill to',
|
|
108
|
+
lines: [
|
|
109
|
+
...named,
|
|
110
|
+
...billTo
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
} else if (customer.length) {
|
|
114
|
+
parties.push({
|
|
115
|
+
label: 'Prepared for',
|
|
116
|
+
lines: customer
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const shipTo = quoteAddressLines(view.shipTo);
|
|
120
|
+
if (shipTo.length) parties.push({
|
|
121
|
+
label: 'Ship to',
|
|
122
|
+
lines: shipTo
|
|
123
|
+
});
|
|
124
|
+
const summary = [
|
|
125
|
+
{
|
|
126
|
+
label: 'Subtotal',
|
|
127
|
+
value: money(view.totals.itemsCents)
|
|
128
|
+
}
|
|
129
|
+
];
|
|
130
|
+
if (view.totals.discountCents > 0) {
|
|
131
|
+
summary.push({
|
|
132
|
+
label: view.discountLabel ? `Discount (${view.discountLabel})` : 'Discount',
|
|
133
|
+
value: minus(money(view.totals.discountCents))
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (view.totals.shippingCents > 0) summary.push({
|
|
137
|
+
label: 'Shipping',
|
|
138
|
+
value: money(view.totals.shippingCents)
|
|
139
|
+
});
|
|
140
|
+
if (view.taxAtPayment) summary.push({
|
|
141
|
+
label: 'Tax is added at payment',
|
|
142
|
+
value: '',
|
|
143
|
+
note: true
|
|
144
|
+
});
|
|
145
|
+
else if (view.totals.taxCents > 0) summary.push({
|
|
146
|
+
label: view.taxLabel || 'Tax',
|
|
147
|
+
value: money(view.totals.taxCents)
|
|
148
|
+
});
|
|
149
|
+
else if (view.taxLabel && view.taxLabel !== 'Tax') summary.push({
|
|
150
|
+
label: view.taxLabel,
|
|
151
|
+
value: '',
|
|
152
|
+
note: true
|
|
153
|
+
});
|
|
154
|
+
summary.push({
|
|
155
|
+
label: view.taxAtPayment ? 'Total before tax' : 'Total',
|
|
156
|
+
value: money(view.totals.totalCents),
|
|
157
|
+
emphasis: true
|
|
158
|
+
});
|
|
159
|
+
const stamp = view.status === 'paid' ? `Paid${view.paidAtMs ? ` ${date(view.paidAtMs)}` : ''}` : view.status === 'canceled' ? 'Canceled' : view.status === 'refunded' ? 'Refunded' : null;
|
|
160
|
+
const facts = [];
|
|
161
|
+
if (view.dateMs) facts.push([
|
|
162
|
+
'Date',
|
|
163
|
+
date(view.dateMs)
|
|
164
|
+
]);
|
|
165
|
+
if (input.revision && input.revision > 0) facts.push([
|
|
166
|
+
'Revision',
|
|
167
|
+
String(input.revision)
|
|
168
|
+
]);
|
|
169
|
+
const sections = [];
|
|
170
|
+
if (view.notes) sections.push({
|
|
171
|
+
heading: 'Notes',
|
|
172
|
+
text: view.notes
|
|
173
|
+
});
|
|
174
|
+
if (view.terms) sections.push({
|
|
175
|
+
heading: 'Terms',
|
|
176
|
+
text: view.terms
|
|
177
|
+
});
|
|
178
|
+
const payUrl = view.status === 'unpaid' && input.payUrl ? input.payUrl : null;
|
|
179
|
+
return {
|
|
180
|
+
kind: 'QUOTE',
|
|
181
|
+
title: view.number,
|
|
182
|
+
facts,
|
|
183
|
+
stamp,
|
|
184
|
+
parties,
|
|
185
|
+
lines: view.lines.map((line)=>{
|
|
186
|
+
var _line_variantLabel, _line_description;
|
|
187
|
+
return {
|
|
188
|
+
name: line.name,
|
|
189
|
+
details: [
|
|
190
|
+
(_line_variantLabel = line.variantLabel) != null ? _line_variantLabel : '',
|
|
191
|
+
(_line_description = line.description) != null ? _line_description : '',
|
|
192
|
+
line.sku ? `SKU ${line.sku}` : ''
|
|
193
|
+
].filter(Boolean),
|
|
194
|
+
quantity: line.quantity,
|
|
195
|
+
unit: line.included ? '' : money(line.unitAmountCents),
|
|
196
|
+
amount: line.included ? 'Included' : money(line.amountCents)
|
|
197
|
+
};
|
|
198
|
+
}),
|
|
199
|
+
summary,
|
|
200
|
+
sections,
|
|
201
|
+
pay: payUrl ? {
|
|
202
|
+
url: payUrl,
|
|
203
|
+
label: 'Pay this quote',
|
|
204
|
+
intro: view.approvalRequired ? `${view.storeName || 'We'} will ask you to approve the proof first, at this link:` : 'Open the quote on our site to pay it securely by card:'
|
|
205
|
+
} : null
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/** The quote as a PDF. */ export function renderQuotePdf(input) {
|
|
209
|
+
return renderCommerceDocument(input.brand, quoteDocument(input));
|
|
210
|
+
}
|
|
211
|
+
function invoiceDocument(input) {
|
|
212
|
+
const data = buildOrderInvoiceData(input);
|
|
213
|
+
const money = (cents)=>invoiceMoney(cents, data.currency);
|
|
214
|
+
const parties = [];
|
|
215
|
+
if (data.billTo.length) parties.push({
|
|
216
|
+
label: 'Bill to',
|
|
217
|
+
lines: data.billTo
|
|
218
|
+
});
|
|
219
|
+
if (data.shipTo.length) parties.push({
|
|
220
|
+
label: 'Ship to',
|
|
221
|
+
lines: data.shipTo
|
|
222
|
+
});
|
|
223
|
+
const facts = [];
|
|
224
|
+
if (data.dateMs) facts.push([
|
|
225
|
+
'Order date',
|
|
226
|
+
formatPdfDate(data.dateMs, input.brand.timeZone)
|
|
227
|
+
]);
|
|
228
|
+
const sections = [];
|
|
229
|
+
if (data.footer) sections.push({
|
|
230
|
+
heading: '',
|
|
231
|
+
text: data.footer
|
|
232
|
+
});
|
|
233
|
+
if (data.termsUrl) sections.push({
|
|
234
|
+
heading: 'Terms',
|
|
235
|
+
text: data.termsUrl,
|
|
236
|
+
link: data.termsUrl
|
|
237
|
+
});
|
|
238
|
+
const payUrl = input.order.status === 'pending' && input.payUrl ? input.payUrl : null;
|
|
239
|
+
return {
|
|
240
|
+
kind: 'INVOICE',
|
|
241
|
+
title: `Invoice ${data.number}`,
|
|
242
|
+
facts,
|
|
243
|
+
stamp: data.paymentStatus,
|
|
244
|
+
parties,
|
|
245
|
+
lines: data.lines.map((line)=>({
|
|
246
|
+
name: line.name,
|
|
247
|
+
details: [
|
|
248
|
+
line.detail,
|
|
249
|
+
line.description
|
|
250
|
+
].filter(Boolean),
|
|
251
|
+
quantity: line.quantity,
|
|
252
|
+
unit: line.included ? '' : money(line.unitCents),
|
|
253
|
+
amount: line.included ? 'Included' : money(line.amountCents)
|
|
254
|
+
})),
|
|
255
|
+
summary: data.summary.map((row)=>({
|
|
256
|
+
label: row.label,
|
|
257
|
+
value: row.minus ? minus(money(row.cents)) : money(row.cents),
|
|
258
|
+
emphasis: row.emphasis
|
|
259
|
+
})),
|
|
260
|
+
sections,
|
|
261
|
+
pay: payUrl ? {
|
|
262
|
+
url: payUrl,
|
|
263
|
+
label: 'Pay this invoice',
|
|
264
|
+
intro: 'Open the invoice on our site to pay it securely by card:'
|
|
265
|
+
} : null
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/** Any order's invoice as a PDF, a paid quote's included. */ export function renderOrderInvoicePdf(input) {
|
|
269
|
+
return renderCommerceDocument(input.brand, invoiceDocument(input));
|
|
270
|
+
}
|
|
271
|
+
/*==========================================
|
|
272
|
+
* THE LAYOUT
|
|
273
|
+
*=========================================*/ const PAGE_MARGIN = {
|
|
274
|
+
top: 48,
|
|
275
|
+
right: 48,
|
|
276
|
+
bottom: 60,
|
|
277
|
+
left: 48
|
|
278
|
+
};
|
|
279
|
+
const BAND_HEIGHT = 10;
|
|
280
|
+
const LOGO_BOX = {
|
|
281
|
+
width: 180,
|
|
282
|
+
height: 50
|
|
283
|
+
};
|
|
284
|
+
function renderCommerceDocument(brand, document) {
|
|
285
|
+
var _brand_colors;
|
|
286
|
+
const palette = _extends({}, COMMERCE_PDF_NEUTRAL_COLORS, (_brand_colors = brand.colors) != null ? _brand_colors : {});
|
|
287
|
+
const color = (hex, fallback)=>{
|
|
288
|
+
var _ref, _pdfRgbFromHex;
|
|
289
|
+
return (_ref = (_pdfRgbFromHex = pdfRgbFromHex(hex)) != null ? _pdfRgbFromHex : pdfRgbFromHex(fallback)) != null ? _ref : [
|
|
290
|
+
0,
|
|
291
|
+
0,
|
|
292
|
+
0
|
|
293
|
+
];
|
|
294
|
+
};
|
|
295
|
+
const colors = {
|
|
296
|
+
accent: color(palette.accent, COMMERCE_PDF_NEUTRAL_COLORS.accent),
|
|
297
|
+
onAccent: color(palette.onAccent, COMMERCE_PDF_NEUTRAL_COLORS.onAccent),
|
|
298
|
+
accentText: color(palette.accentText, COMMERCE_PDF_NEUTRAL_COLORS.accentText),
|
|
299
|
+
tint: color(palette.tint, COMMERCE_PDF_NEUTRAL_COLORS.tint),
|
|
300
|
+
ink: color(palette.ink, COMMERCE_PDF_NEUTRAL_COLORS.ink),
|
|
301
|
+
muted: color(palette.muted, COMMERCE_PDF_NEUTRAL_COLORS.muted),
|
|
302
|
+
rule: color(palette.rule, COMMERCE_PDF_NEUTRAL_COLORS.rule)
|
|
303
|
+
};
|
|
304
|
+
const businessName = brand.businessName.trim();
|
|
305
|
+
const documentTitle = businessName ? `${document.title} - ${businessName}` : document.title;
|
|
306
|
+
const writer = new PdfWriter({
|
|
307
|
+
title: documentTitle,
|
|
308
|
+
margin: PAGE_MARGIN,
|
|
309
|
+
onNewPage: (page)=>{
|
|
310
|
+
page.fillRect(0, page.pageHeight - BAND_HEIGHT / 2, page.pageWidth, BAND_HEIGHT / 2, colors.accent);
|
|
311
|
+
const baseline = page.top + 14;
|
|
312
|
+
if (businessName) page.textIn(page.left, page.contentWidth, baseline, businessName, {
|
|
313
|
+
size: 8.5,
|
|
314
|
+
font: 'bold',
|
|
315
|
+
color: colors.muted
|
|
316
|
+
});
|
|
317
|
+
page.textIn(page.left, page.contentWidth, baseline, `${document.title} (continued)`, {
|
|
318
|
+
size: 8.5,
|
|
319
|
+
color: colors.muted
|
|
320
|
+
}, 'right');
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
const { left, right } = writer;
|
|
324
|
+
// The band across the top of the first page.
|
|
325
|
+
writer.fillRect(0, writer.pageHeight - BAND_HEIGHT, writer.pageWidth, BAND_HEIGHT, colors.accent);
|
|
326
|
+
// Left: logo, business name, contact.
|
|
327
|
+
const leftWidth = writer.contentWidth * 0.55;
|
|
328
|
+
let leftY = writer.top;
|
|
329
|
+
if (brand.logo) {
|
|
330
|
+
const drawn = writer.image(brand.logo, {
|
|
331
|
+
x: left,
|
|
332
|
+
y: leftY - LOGO_BOX.height,
|
|
333
|
+
width: LOGO_BOX.width,
|
|
334
|
+
height: LOGO_BOX.height
|
|
335
|
+
});
|
|
336
|
+
leftY = drawn.y - 12;
|
|
337
|
+
}
|
|
338
|
+
writer.y = leftY;
|
|
339
|
+
if (businessName) writer.paragraph(businessName, {
|
|
340
|
+
width: leftWidth,
|
|
341
|
+
style: {
|
|
342
|
+
font: 'bold',
|
|
343
|
+
size: 14,
|
|
344
|
+
color: colors.ink
|
|
345
|
+
},
|
|
346
|
+
leading: 1.25,
|
|
347
|
+
after: 2
|
|
348
|
+
});
|
|
349
|
+
for (const line of brand.contactLines){
|
|
350
|
+
writer.paragraph(line, {
|
|
351
|
+
width: leftWidth,
|
|
352
|
+
style: {
|
|
353
|
+
size: 9,
|
|
354
|
+
color: colors.muted
|
|
355
|
+
},
|
|
356
|
+
leading: 1.3
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
leftY = writer.y;
|
|
360
|
+
// Right: the document's name, number, facts and standing.
|
|
361
|
+
const factsWidth = 210;
|
|
362
|
+
const factsX = right - factsWidth;
|
|
363
|
+
let rightY = writer.top - 22;
|
|
364
|
+
writer.textIn(factsX, factsWidth, rightY, document.kind, {
|
|
365
|
+
font: 'bold',
|
|
366
|
+
size: 24,
|
|
367
|
+
color: colors.accentText
|
|
368
|
+
}, 'right');
|
|
369
|
+
rightY -= 20;
|
|
370
|
+
writer.textIn(factsX, factsWidth, rightY, document.title, {
|
|
371
|
+
font: 'bold',
|
|
372
|
+
size: 11,
|
|
373
|
+
color: colors.ink
|
|
374
|
+
}, 'right');
|
|
375
|
+
for (const [label, value] of document.facts){
|
|
376
|
+
rightY -= 14;
|
|
377
|
+
const valueWidth = writer.measure(value, {
|
|
378
|
+
size: 9.5
|
|
379
|
+
});
|
|
380
|
+
writer.textIn(factsX, factsWidth, rightY, value, {
|
|
381
|
+
size: 9.5,
|
|
382
|
+
color: colors.ink
|
|
383
|
+
}, 'right');
|
|
384
|
+
writer.textIn(factsX, factsWidth - valueWidth - 8, rightY, label, {
|
|
385
|
+
size: 9.5,
|
|
386
|
+
color: colors.muted
|
|
387
|
+
}, 'right');
|
|
388
|
+
}
|
|
389
|
+
if (document.stamp) {
|
|
390
|
+
rightY -= 24;
|
|
391
|
+
const style = {
|
|
392
|
+
font: 'bold',
|
|
393
|
+
size: 8.5,
|
|
394
|
+
color: colors.onAccent
|
|
395
|
+
};
|
|
396
|
+
const width = writer.measure(document.stamp, style) + 16;
|
|
397
|
+
writer.fillRect(right - width, rightY - 5, width, 17, colors.accent);
|
|
398
|
+
writer.textIn(right - width, width, rightY, document.stamp, style, 'center');
|
|
399
|
+
rightY -= 5;
|
|
400
|
+
}
|
|
401
|
+
writer.y = Math.min(leftY, rightY) - 22;
|
|
402
|
+
// Who it is for.
|
|
403
|
+
if (document.parties.length) {
|
|
404
|
+
const columnWidth = Math.min(220, writer.contentWidth / document.parties.length);
|
|
405
|
+
const top = writer.y;
|
|
406
|
+
let lowest = top;
|
|
407
|
+
document.parties.forEach((party, index)=>{
|
|
408
|
+
const x = left + index * (columnWidth + 16);
|
|
409
|
+
writer.y = top;
|
|
410
|
+
writer.paragraph(party.label.toUpperCase(), {
|
|
411
|
+
x,
|
|
412
|
+
width: columnWidth,
|
|
413
|
+
style: {
|
|
414
|
+
font: 'bold',
|
|
415
|
+
size: 8,
|
|
416
|
+
color: colors.muted
|
|
417
|
+
},
|
|
418
|
+
after: 2
|
|
419
|
+
});
|
|
420
|
+
party.lines.forEach((line, lineIndex)=>{
|
|
421
|
+
writer.paragraph(line, {
|
|
422
|
+
x,
|
|
423
|
+
width: columnWidth,
|
|
424
|
+
style: {
|
|
425
|
+
size: 9.5,
|
|
426
|
+
color: colors.ink,
|
|
427
|
+
font: lineIndex === 0 ? 'bold' : 'regular'
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
lowest = Math.min(lowest, writer.y);
|
|
432
|
+
});
|
|
433
|
+
writer.y = lowest - 18;
|
|
434
|
+
}
|
|
435
|
+
// The lines.
|
|
436
|
+
const rows = document.lines.map((line)=>[
|
|
437
|
+
[
|
|
438
|
+
{
|
|
439
|
+
text: line.name,
|
|
440
|
+
style: {
|
|
441
|
+
font: 'bold',
|
|
442
|
+
size: 9.5,
|
|
443
|
+
color: colors.ink
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
...line.details.map((detail)=>({
|
|
447
|
+
text: detail,
|
|
448
|
+
style: {
|
|
449
|
+
size: 8.5,
|
|
450
|
+
color: colors.muted
|
|
451
|
+
}
|
|
452
|
+
}))
|
|
453
|
+
],
|
|
454
|
+
String(line.quantity),
|
|
455
|
+
line.unit,
|
|
456
|
+
line.amount
|
|
457
|
+
]);
|
|
458
|
+
writer.table({
|
|
459
|
+
columns: [
|
|
460
|
+
{
|
|
461
|
+
header: 'Item',
|
|
462
|
+
width: 0
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
header: 'Qty',
|
|
466
|
+
width: 48,
|
|
467
|
+
align: 'right'
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
header: 'Unit price',
|
|
471
|
+
width: 86,
|
|
472
|
+
align: 'right'
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
header: 'Amount',
|
|
476
|
+
width: 90,
|
|
477
|
+
align: 'right'
|
|
478
|
+
}
|
|
479
|
+
],
|
|
480
|
+
rows,
|
|
481
|
+
headerFill: colors.accent,
|
|
482
|
+
headerStyle: {
|
|
483
|
+
font: 'bold',
|
|
484
|
+
size: 8.5,
|
|
485
|
+
color: colors.onAccent
|
|
486
|
+
},
|
|
487
|
+
bodyStyle: {
|
|
488
|
+
size: 9.5,
|
|
489
|
+
color: colors.ink
|
|
490
|
+
},
|
|
491
|
+
padding: {
|
|
492
|
+
x: 8,
|
|
493
|
+
y: 6
|
|
494
|
+
},
|
|
495
|
+
ruleColor: colors.rule
|
|
496
|
+
});
|
|
497
|
+
// The totals on the right; beside them on the left, paying it.
|
|
498
|
+
const summaryWidth = 250;
|
|
499
|
+
const summaryX = right - summaryWidth;
|
|
500
|
+
const payWidth = writer.contentWidth - summaryWidth - 28;
|
|
501
|
+
const rowHeight = 17;
|
|
502
|
+
const summaryHeight = document.summary.reduce((sum, row)=>sum + (row.emphasis ? rowHeight + 8 : rowHeight), 0);
|
|
503
|
+
const pay = document.pay;
|
|
504
|
+
const buttonStyle = {
|
|
505
|
+
font: 'bold',
|
|
506
|
+
size: 10.5,
|
|
507
|
+
color: colors.onAccent
|
|
508
|
+
};
|
|
509
|
+
const buttonHeight = 26;
|
|
510
|
+
const payParts = pay ? {
|
|
511
|
+
intro: writer.wrap(pay.intro, payWidth, {
|
|
512
|
+
size: 9
|
|
513
|
+
}),
|
|
514
|
+
url: writer.wrap(pay.url, payWidth, {
|
|
515
|
+
size: 8
|
|
516
|
+
})
|
|
517
|
+
} : null;
|
|
518
|
+
const payHeight = payParts ? 18 + payParts.intro.length * 12 + 6 + buttonHeight + 8 + payParts.url.length * 11 : 0;
|
|
519
|
+
writer.down(12);
|
|
520
|
+
writer.ensure(Math.max(summaryHeight, payHeight));
|
|
521
|
+
const blockTop = writer.y;
|
|
522
|
+
for (const row of document.summary){
|
|
523
|
+
const height = row.emphasis ? rowHeight + 8 : rowHeight;
|
|
524
|
+
if (row.emphasis) writer.fillRect(summaryX, writer.y - height, summaryWidth, height, colors.tint);
|
|
525
|
+
const size = row.emphasis ? 11.5 : 9.5;
|
|
526
|
+
const baseline = writer.y - height / 2 - size * 0.35;
|
|
527
|
+
const style = row.emphasis ? {
|
|
528
|
+
font: 'bold',
|
|
529
|
+
size,
|
|
530
|
+
color: colors.ink
|
|
531
|
+
} : _extends({
|
|
532
|
+
size,
|
|
533
|
+
color: row.note ? colors.muted : colors.ink
|
|
534
|
+
}, row.note ? {
|
|
535
|
+
font: 'italic'
|
|
536
|
+
} : {});
|
|
537
|
+
writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.label, style, 'left');
|
|
538
|
+
if (row.value) writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.value, style, 'right');
|
|
539
|
+
writer.down(height);
|
|
540
|
+
}
|
|
541
|
+
const summaryBottom = writer.y;
|
|
542
|
+
if (pay && payParts) {
|
|
543
|
+
let y = blockTop - 12;
|
|
544
|
+
writer.text(left, y, 'Pay online', {
|
|
545
|
+
font: 'bold',
|
|
546
|
+
size: 11,
|
|
547
|
+
color: colors.ink
|
|
548
|
+
});
|
|
549
|
+
y -= 6;
|
|
550
|
+
for (const line of payParts.intro){
|
|
551
|
+
y -= 12;
|
|
552
|
+
writer.text(left, y + 2, line, {
|
|
553
|
+
size: 9,
|
|
554
|
+
color: colors.muted
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
y -= 6;
|
|
558
|
+
const buttonWidth = writer.measure(pay.label, buttonStyle) + 36;
|
|
559
|
+
writer.fillRect(left, y - buttonHeight, buttonWidth, buttonHeight, colors.accent);
|
|
560
|
+
writer.textIn(left, buttonWidth, y - buttonHeight / 2 - 10.5 * 0.35, pay.label, buttonStyle, 'center');
|
|
561
|
+
writer.link(left, y - buttonHeight, buttonWidth, buttonHeight, pay.url);
|
|
562
|
+
y -= buttonHeight + 8;
|
|
563
|
+
for (const line of payParts.url){
|
|
564
|
+
y -= 11;
|
|
565
|
+
writer.text(left, y + 2, line, {
|
|
566
|
+
size: 8,
|
|
567
|
+
color: colors.accentText
|
|
568
|
+
});
|
|
569
|
+
writer.link(left, y, writer.measure(line, {
|
|
570
|
+
size: 8
|
|
571
|
+
}), 11, pay.url);
|
|
572
|
+
}
|
|
573
|
+
writer.y = Math.min(summaryBottom, y);
|
|
574
|
+
}
|
|
575
|
+
writer.down(22);
|
|
576
|
+
// Notes, terms, the store's footer.
|
|
577
|
+
for (const section of document.sections){
|
|
578
|
+
if (section.heading) {
|
|
579
|
+
writer.ensure(36);
|
|
580
|
+
writer.paragraph(section.heading, {
|
|
581
|
+
style: {
|
|
582
|
+
font: 'bold',
|
|
583
|
+
size: 10,
|
|
584
|
+
color: colors.ink
|
|
585
|
+
},
|
|
586
|
+
after: 1
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
writer.paragraph(section.text, _extends({
|
|
590
|
+
style: {
|
|
591
|
+
size: 9,
|
|
592
|
+
color: section.link ? colors.accentText : colors.muted
|
|
593
|
+
}
|
|
594
|
+
}, section.link ? {
|
|
595
|
+
link: section.link
|
|
596
|
+
} : {}, {
|
|
597
|
+
after: 12
|
|
598
|
+
}));
|
|
599
|
+
}
|
|
600
|
+
return writer.finish({
|
|
601
|
+
footer: (page, pages)=>({
|
|
602
|
+
left: businessName ? `${businessName} · ${document.title}` : document.title,
|
|
603
|
+
right: `Page ${page} of ${pages}`
|
|
604
|
+
}),
|
|
605
|
+
footerStyle: {
|
|
606
|
+
size: 8,
|
|
607
|
+
color: colors.muted
|
|
608
|
+
},
|
|
609
|
+
footerRule: colors.rule
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
//# sourceMappingURL=commerce-document-pdf.js.map
|