@aglyn/plugins-commerce 1.0.0-beta.288 → 1.0.0-beta.290

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.
Files changed (129) hide show
  1. package/README.md +2 -2
  2. package/package.json +13 -13
  3. package/src/lib/components/commerce-console-page.js +29 -2
  4. package/src/lib/components/commerce-console-page.js.map +1 -1
  5. package/src/lib/components/console/commerce-analytics-card.component.js +4 -2
  6. package/src/lib/components/console/commerce-analytics-card.component.js.map +1 -1
  7. package/src/lib/components/console/commerce-pdf-download.d.ts +30 -0
  8. package/src/lib/components/console/commerce-pdf-download.js +53 -0
  9. package/src/lib/components/console/commerce-pdf-download.js.map +1 -0
  10. package/src/lib/components/console/host-orders-card.component.js +5 -1
  11. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  12. package/src/lib/components/console/host-quotes-card.component.d.ts +34 -0
  13. package/src/lib/components/console/host-quotes-card.component.js +263 -0
  14. package/src/lib/components/console/host-quotes-card.component.js.map +1 -0
  15. package/src/lib/components/console/order-invoice.component.d.ts +6 -4
  16. package/src/lib/components/console/order-invoice.component.js +67 -11
  17. package/src/lib/components/console/order-invoice.component.js.map +1 -1
  18. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +33 -28
  19. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
  20. package/src/lib/components/console/quote-editor-dialog.component.d.ts +45 -0
  21. package/src/lib/components/console/quote-editor-dialog.component.js +1317 -0
  22. package/src/lib/components/console/quote-editor-dialog.component.js.map +1 -0
  23. package/src/lib/components/console/quote-pay-link.component.d.ts +38 -0
  24. package/src/lib/components/console/quote-pay-link.component.js +213 -0
  25. package/src/lib/components/console/quote-pay-link.component.js.map +1 -0
  26. package/src/lib/components/console/quote-proofs.component.d.ts +37 -0
  27. package/src/lib/components/console/quote-proofs.component.js +626 -0
  28. package/src/lib/components/console/quote-proofs.component.js.map +1 -0
  29. package/src/lib/components/console/quote-settings-card.component.d.ts +29 -0
  30. package/src/lib/components/console/quote-settings-card.component.js +121 -0
  31. package/src/lib/components/console/quote-settings-card.component.js.map +1 -0
  32. package/src/lib/components/console/tax-settings-card.component.js +4 -1
  33. package/src/lib/components/console/tax-settings-card.component.js.map +1 -1
  34. package/src/lib/components/quote-page-body.d.ts +33 -0
  35. package/src/lib/components/quote-page-body.js +724 -0
  36. package/src/lib/components/quote-page-body.js.map +1 -0
  37. package/src/lib/components/quote-page.d.ts +29 -0
  38. package/src/lib/components/quote-page.js +96 -0
  39. package/src/lib/components/quote-page.js.map +1 -0
  40. package/src/lib/components/quote-proof-panel.d.ts +42 -0
  41. package/src/lib/components/quote-proof-panel.js +436 -0
  42. package/src/lib/components/quote-proof-panel.js.map +1 -0
  43. package/src/lib/components/storefront-payment-element.d.ts +7 -1
  44. package/src/lib/components/storefront-payment-element.js +8 -3
  45. package/src/lib/components/storefront-payment-element.js.map +1 -1
  46. package/src/lib/constants/orders-list-query.js +6 -1
  47. package/src/lib/constants/orders-list-query.js.map +1 -1
  48. package/src/lib/constants/quote-page.d.ts +30 -0
  49. package/src/lib/constants/quote-page.js +35 -0
  50. package/src/lib/constants/quote-page.js.map +1 -0
  51. package/src/lib/model/commerce-orders.d.ts +25 -2
  52. package/src/lib/model/commerce-orders.js +10 -1
  53. package/src/lib/model/commerce-orders.js.map +1 -1
  54. package/src/lib/model/commerce-quote-pay.d.ts +256 -0
  55. package/src/lib/model/commerce-quote-pay.js +426 -0
  56. package/src/lib/model/commerce-quote-pay.js.map +1 -0
  57. package/src/lib/model/commerce-quote-proofs.d.ts +159 -0
  58. package/src/lib/model/commerce-quote-proofs.js +348 -0
  59. package/src/lib/model/commerce-quote-proofs.js.map +1 -0
  60. package/src/lib/model/commerce-quotes.d.ts +335 -0
  61. package/src/lib/model/commerce-quotes.js +522 -0
  62. package/src/lib/model/commerce-quotes.js.map +1 -0
  63. package/src/lib/model/index.d.ts +3 -0
  64. package/src/lib/model/index.js +3 -0
  65. package/src/lib/model/index.js.map +1 -1
  66. package/src/lib/model/plugin-permissions.d.ts +1 -1
  67. package/src/lib/model/plugin-permissions.js +20 -0
  68. package/src/lib/model/plugin-permissions.js.map +1 -1
  69. package/src/lib/server/api-v1/orders-and-products.js +6 -2
  70. package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
  71. package/src/lib/server/billing-webhook.js +127 -52
  72. package/src/lib/server/billing-webhook.js.map +1 -1
  73. package/src/lib/server/checkout-session-expiry.d.ts +36 -0
  74. package/src/lib/server/checkout-session-expiry.js +63 -0
  75. package/src/lib/server/checkout-session-expiry.js.map +1 -0
  76. package/src/lib/server/commerce-pdf.d.ts +100 -0
  77. package/src/lib/server/commerce-pdf.js +283 -0
  78. package/src/lib/server/commerce-pdf.js.map +1 -0
  79. package/src/lib/server/draft-order.js +16 -8
  80. package/src/lib/server/draft-order.js.map +1 -1
  81. package/src/lib/server/order-print.d.ts +6 -0
  82. package/src/lib/server/order-print.js +22 -5
  83. package/src/lib/server/order-print.js.map +1 -1
  84. package/src/lib/server/pdf-response.d.ts +31 -0
  85. package/src/lib/server/pdf-response.js +30 -0
  86. package/src/lib/server/pdf-response.js.map +1 -0
  87. package/src/lib/server/person-eraser.d.ts +7 -0
  88. package/src/lib/server/person-eraser.js +24 -2
  89. package/src/lib/server/person-eraser.js.map +1 -1
  90. package/src/lib/server/pos-reader-splash.d.ts +2 -18
  91. package/src/lib/server/pos-reader-splash.js +8 -93
  92. package/src/lib/server/pos-reader-splash.js.map +1 -1
  93. package/src/lib/server/pos-return.js +3 -1
  94. package/src/lib/server/pos-return.js.map +1 -1
  95. package/src/lib/server/quote-link-token.d.ts +39 -0
  96. package/src/lib/server/quote-link-token.js +97 -0
  97. package/src/lib/server/quote-link-token.js.map +1 -0
  98. package/src/lib/server/quote-pay.d.ts +110 -0
  99. package/src/lib/server/quote-pay.js +606 -0
  100. package/src/lib/server/quote-pay.js.map +1 -0
  101. package/src/lib/server/quote-proofs.d.ts +51 -0
  102. package/src/lib/server/quote-proofs.js +500 -0
  103. package/src/lib/server/quote-proofs.js.map +1 -0
  104. package/src/lib/server/quote-uploads.d.ts +33 -0
  105. package/src/lib/server/quote-uploads.js +29 -0
  106. package/src/lib/server/quote-uploads.js.map +1 -0
  107. package/src/lib/server/quote.d.ts +63 -0
  108. package/src/lib/server/quote.js +846 -0
  109. package/src/lib/server/quote.js.map +1 -0
  110. package/src/lib/server/related.js +2 -0
  111. package/src/lib/server/related.js.map +1 -1
  112. package/src/lib/server/site-image-source.d.ts +38 -0
  113. package/src/lib/server/site-image-source.js +130 -0
  114. package/src/lib/server/site-image-source.js.map +1 -0
  115. package/src/lib/server/site-page-resolver.d.ts +2 -0
  116. package/src/lib/server/site-page-resolver.js +75 -0
  117. package/src/lib/server/site-page-resolver.js.map +1 -1
  118. package/src/lib/server.js +33 -1
  119. package/src/lib/server.js.map +1 -1
  120. package/src/lib/site.js +6 -0
  121. package/src/lib/site.js.map +1 -1
  122. package/src/lib/transfer/tracking.server.js +6 -0
  123. package/src/lib/transfer/tracking.server.js.map +1 -1
  124. package/src/lib/utils/commerce-document-pdf.d.ts +77 -0
  125. package/src/lib/utils/commerce-document-pdf.js +608 -0
  126. package/src/lib/utils/commerce-document-pdf.js.map +1 -0
  127. package/src/lib/utils/order-invoice.d.ts +52 -3
  128. package/src/lib/utils/order-invoice.js +116 -64
  129. package/src/lib/utils/order-invoice.js.map +1 -1
@@ -0,0 +1,608 @@
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
+ return {
239
+ kind: 'INVOICE',
240
+ title: `Invoice ${data.number}`,
241
+ facts,
242
+ stamp: data.paymentStatus,
243
+ parties,
244
+ lines: data.lines.map((line)=>({
245
+ name: line.name,
246
+ details: [
247
+ line.detail,
248
+ line.description
249
+ ].filter(Boolean),
250
+ quantity: line.quantity,
251
+ unit: line.included ? '' : money(line.unitCents),
252
+ amount: line.included ? 'Included' : money(line.amountCents)
253
+ })),
254
+ summary: data.summary.map((row)=>({
255
+ label: row.label,
256
+ value: row.minus ? minus(money(row.cents)) : money(row.cents),
257
+ emphasis: row.emphasis
258
+ })),
259
+ sections,
260
+ pay: null
261
+ };
262
+ }
263
+ /** Any order's invoice as a PDF, a paid quote's included. */ export function renderOrderInvoicePdf(input) {
264
+ return renderCommerceDocument(input.brand, invoiceDocument(input));
265
+ }
266
+ /*==========================================
267
+ * THE LAYOUT
268
+ *=========================================*/ const PAGE_MARGIN = {
269
+ top: 48,
270
+ right: 48,
271
+ bottom: 60,
272
+ left: 48
273
+ };
274
+ const BAND_HEIGHT = 10;
275
+ const LOGO_BOX = {
276
+ width: 180,
277
+ height: 50
278
+ };
279
+ function renderCommerceDocument(brand, document) {
280
+ var _brand_colors;
281
+ const palette = _extends({}, COMMERCE_PDF_NEUTRAL_COLORS, (_brand_colors = brand.colors) != null ? _brand_colors : {});
282
+ const color = (hex, fallback)=>{
283
+ var _ref, _pdfRgbFromHex;
284
+ return (_ref = (_pdfRgbFromHex = pdfRgbFromHex(hex)) != null ? _pdfRgbFromHex : pdfRgbFromHex(fallback)) != null ? _ref : [
285
+ 0,
286
+ 0,
287
+ 0
288
+ ];
289
+ };
290
+ const colors = {
291
+ accent: color(palette.accent, COMMERCE_PDF_NEUTRAL_COLORS.accent),
292
+ onAccent: color(palette.onAccent, COMMERCE_PDF_NEUTRAL_COLORS.onAccent),
293
+ accentText: color(palette.accentText, COMMERCE_PDF_NEUTRAL_COLORS.accentText),
294
+ tint: color(palette.tint, COMMERCE_PDF_NEUTRAL_COLORS.tint),
295
+ ink: color(palette.ink, COMMERCE_PDF_NEUTRAL_COLORS.ink),
296
+ muted: color(palette.muted, COMMERCE_PDF_NEUTRAL_COLORS.muted),
297
+ rule: color(palette.rule, COMMERCE_PDF_NEUTRAL_COLORS.rule)
298
+ };
299
+ const businessName = brand.businessName.trim();
300
+ const documentTitle = businessName ? `${document.title} - ${businessName}` : document.title;
301
+ const writer = new PdfWriter({
302
+ title: documentTitle,
303
+ margin: PAGE_MARGIN,
304
+ onNewPage: (page)=>{
305
+ page.fillRect(0, page.pageHeight - BAND_HEIGHT / 2, page.pageWidth, BAND_HEIGHT / 2, colors.accent);
306
+ const baseline = page.top + 14;
307
+ if (businessName) page.textIn(page.left, page.contentWidth, baseline, businessName, {
308
+ size: 8.5,
309
+ font: 'bold',
310
+ color: colors.muted
311
+ });
312
+ page.textIn(page.left, page.contentWidth, baseline, `${document.title} (continued)`, {
313
+ size: 8.5,
314
+ color: colors.muted
315
+ }, 'right');
316
+ }
317
+ });
318
+ const { left, right } = writer;
319
+ // The band across the top of the first page.
320
+ writer.fillRect(0, writer.pageHeight - BAND_HEIGHT, writer.pageWidth, BAND_HEIGHT, colors.accent);
321
+ // Left: logo, business name, contact.
322
+ const leftWidth = writer.contentWidth * 0.55;
323
+ let leftY = writer.top;
324
+ if (brand.logo) {
325
+ const drawn = writer.image(brand.logo, {
326
+ x: left,
327
+ y: leftY - LOGO_BOX.height,
328
+ width: LOGO_BOX.width,
329
+ height: LOGO_BOX.height
330
+ });
331
+ leftY = drawn.y - 12;
332
+ }
333
+ writer.y = leftY;
334
+ if (businessName) writer.paragraph(businessName, {
335
+ width: leftWidth,
336
+ style: {
337
+ font: 'bold',
338
+ size: 14,
339
+ color: colors.ink
340
+ },
341
+ leading: 1.25,
342
+ after: 2
343
+ });
344
+ for (const line of brand.contactLines){
345
+ writer.paragraph(line, {
346
+ width: leftWidth,
347
+ style: {
348
+ size: 9,
349
+ color: colors.muted
350
+ },
351
+ leading: 1.3
352
+ });
353
+ }
354
+ leftY = writer.y;
355
+ // Right: the document's name, number, facts and standing.
356
+ const factsWidth = 210;
357
+ const factsX = right - factsWidth;
358
+ let rightY = writer.top - 22;
359
+ writer.textIn(factsX, factsWidth, rightY, document.kind, {
360
+ font: 'bold',
361
+ size: 24,
362
+ color: colors.accentText
363
+ }, 'right');
364
+ rightY -= 20;
365
+ writer.textIn(factsX, factsWidth, rightY, document.title, {
366
+ font: 'bold',
367
+ size: 11,
368
+ color: colors.ink
369
+ }, 'right');
370
+ for (const [label, value] of document.facts){
371
+ rightY -= 14;
372
+ const valueWidth = writer.measure(value, {
373
+ size: 9.5
374
+ });
375
+ writer.textIn(factsX, factsWidth, rightY, value, {
376
+ size: 9.5,
377
+ color: colors.ink
378
+ }, 'right');
379
+ writer.textIn(factsX, factsWidth - valueWidth - 8, rightY, label, {
380
+ size: 9.5,
381
+ color: colors.muted
382
+ }, 'right');
383
+ }
384
+ if (document.stamp) {
385
+ rightY -= 24;
386
+ const style = {
387
+ font: 'bold',
388
+ size: 8.5,
389
+ color: colors.onAccent
390
+ };
391
+ const width = writer.measure(document.stamp, style) + 16;
392
+ writer.fillRect(right - width, rightY - 5, width, 17, colors.accent);
393
+ writer.textIn(right - width, width, rightY, document.stamp, style, 'center');
394
+ rightY -= 5;
395
+ }
396
+ writer.y = Math.min(leftY, rightY) - 22;
397
+ // Who it is for.
398
+ if (document.parties.length) {
399
+ const columnWidth = Math.min(220, writer.contentWidth / document.parties.length);
400
+ const top = writer.y;
401
+ let lowest = top;
402
+ document.parties.forEach((party, index)=>{
403
+ const x = left + index * (columnWidth + 16);
404
+ writer.y = top;
405
+ writer.paragraph(party.label.toUpperCase(), {
406
+ x,
407
+ width: columnWidth,
408
+ style: {
409
+ font: 'bold',
410
+ size: 8,
411
+ color: colors.muted
412
+ },
413
+ after: 2
414
+ });
415
+ party.lines.forEach((line, lineIndex)=>{
416
+ writer.paragraph(line, {
417
+ x,
418
+ width: columnWidth,
419
+ style: {
420
+ size: 9.5,
421
+ color: colors.ink,
422
+ font: lineIndex === 0 ? 'bold' : 'regular'
423
+ }
424
+ });
425
+ });
426
+ lowest = Math.min(lowest, writer.y);
427
+ });
428
+ writer.y = lowest - 18;
429
+ }
430
+ // The lines.
431
+ const rows = document.lines.map((line)=>[
432
+ [
433
+ {
434
+ text: line.name,
435
+ style: {
436
+ font: 'bold',
437
+ size: 9.5,
438
+ color: colors.ink
439
+ }
440
+ },
441
+ ...line.details.map((detail)=>({
442
+ text: detail,
443
+ style: {
444
+ size: 8.5,
445
+ color: colors.muted
446
+ }
447
+ }))
448
+ ],
449
+ String(line.quantity),
450
+ line.unit,
451
+ line.amount
452
+ ]);
453
+ writer.table({
454
+ columns: [
455
+ {
456
+ header: 'Item',
457
+ width: 0
458
+ },
459
+ {
460
+ header: 'Qty',
461
+ width: 48,
462
+ align: 'right'
463
+ },
464
+ {
465
+ header: 'Unit price',
466
+ width: 86,
467
+ align: 'right'
468
+ },
469
+ {
470
+ header: 'Amount',
471
+ width: 90,
472
+ align: 'right'
473
+ }
474
+ ],
475
+ rows,
476
+ headerFill: colors.accent,
477
+ headerStyle: {
478
+ font: 'bold',
479
+ size: 8.5,
480
+ color: colors.onAccent
481
+ },
482
+ bodyStyle: {
483
+ size: 9.5,
484
+ color: colors.ink
485
+ },
486
+ padding: {
487
+ x: 8,
488
+ y: 6
489
+ },
490
+ ruleColor: colors.rule
491
+ });
492
+ // The totals on the right; beside them on the left, paying it.
493
+ const summaryWidth = 250;
494
+ const summaryX = right - summaryWidth;
495
+ const payWidth = writer.contentWidth - summaryWidth - 28;
496
+ const rowHeight = 17;
497
+ const summaryHeight = document.summary.reduce((sum, row)=>sum + (row.emphasis ? rowHeight + 8 : rowHeight), 0);
498
+ const pay = document.pay;
499
+ const buttonStyle = {
500
+ font: 'bold',
501
+ size: 10.5,
502
+ color: colors.onAccent
503
+ };
504
+ const buttonHeight = 26;
505
+ const payParts = pay ? {
506
+ intro: writer.wrap(pay.intro, payWidth, {
507
+ size: 9
508
+ }),
509
+ url: writer.wrap(pay.url, payWidth, {
510
+ size: 8
511
+ })
512
+ } : null;
513
+ const payHeight = payParts ? 18 + payParts.intro.length * 12 + 6 + buttonHeight + 8 + payParts.url.length * 11 : 0;
514
+ writer.down(12);
515
+ writer.ensure(Math.max(summaryHeight, payHeight));
516
+ const blockTop = writer.y;
517
+ for (const row of document.summary){
518
+ const height = row.emphasis ? rowHeight + 8 : rowHeight;
519
+ if (row.emphasis) writer.fillRect(summaryX, writer.y - height, summaryWidth, height, colors.tint);
520
+ const size = row.emphasis ? 11.5 : 9.5;
521
+ const baseline = writer.y - height / 2 - size * 0.35;
522
+ const style = row.emphasis ? {
523
+ font: 'bold',
524
+ size,
525
+ color: colors.ink
526
+ } : _extends({
527
+ size,
528
+ color: row.note ? colors.muted : colors.ink
529
+ }, row.note ? {
530
+ font: 'italic'
531
+ } : {});
532
+ writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.label, style, 'left');
533
+ if (row.value) writer.textIn(summaryX + 8, summaryWidth - 16, baseline, row.value, style, 'right');
534
+ writer.down(height);
535
+ }
536
+ const summaryBottom = writer.y;
537
+ if (pay && payParts) {
538
+ let y = blockTop - 12;
539
+ writer.text(left, y, 'Pay online', {
540
+ font: 'bold',
541
+ size: 11,
542
+ color: colors.ink
543
+ });
544
+ y -= 6;
545
+ for (const line of payParts.intro){
546
+ y -= 12;
547
+ writer.text(left, y + 2, line, {
548
+ size: 9,
549
+ color: colors.muted
550
+ });
551
+ }
552
+ y -= 6;
553
+ const buttonWidth = writer.measure(pay.label, buttonStyle) + 36;
554
+ writer.fillRect(left, y - buttonHeight, buttonWidth, buttonHeight, colors.accent);
555
+ writer.textIn(left, buttonWidth, y - buttonHeight / 2 - 10.5 * 0.35, pay.label, buttonStyle, 'center');
556
+ writer.link(left, y - buttonHeight, buttonWidth, buttonHeight, pay.url);
557
+ y -= buttonHeight + 8;
558
+ for (const line of payParts.url){
559
+ y -= 11;
560
+ writer.text(left, y + 2, line, {
561
+ size: 8,
562
+ color: colors.accentText
563
+ });
564
+ writer.link(left, y, writer.measure(line, {
565
+ size: 8
566
+ }), 11, pay.url);
567
+ }
568
+ writer.y = Math.min(summaryBottom, y);
569
+ }
570
+ writer.down(22);
571
+ // Notes, terms, the store's footer.
572
+ for (const section of document.sections){
573
+ if (section.heading) {
574
+ writer.ensure(36);
575
+ writer.paragraph(section.heading, {
576
+ style: {
577
+ font: 'bold',
578
+ size: 10,
579
+ color: colors.ink
580
+ },
581
+ after: 1
582
+ });
583
+ }
584
+ writer.paragraph(section.text, _extends({
585
+ style: {
586
+ size: 9,
587
+ color: section.link ? colors.accentText : colors.muted
588
+ }
589
+ }, section.link ? {
590
+ link: section.link
591
+ } : {}, {
592
+ after: 12
593
+ }));
594
+ }
595
+ return writer.finish({
596
+ footer: (page, pages)=>({
597
+ left: businessName ? `${businessName} · ${document.title}` : document.title,
598
+ right: `Page ${page} of ${pages}`
599
+ }),
600
+ footerStyle: {
601
+ size: 8,
602
+ color: colors.muted
603
+ },
604
+ footerRule: colors.rule
605
+ });
606
+ }
607
+
608
+ //# sourceMappingURL=commerce-document-pdf.js.map