includio-cms 0.27.0 → 0.33.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/API.md +58 -14
- package/CHANGELOG.md +59 -0
- package/DOCS.md +1 -1
- package/ROADMAP.md +1 -0
- package/dist/admin/api/handler.js +4 -0
- package/dist/admin/api/integrations.d.ts +13 -0
- package/dist/admin/api/integrations.js +61 -0
- package/dist/admin/api/test-email.d.ts +9 -0
- package/dist/admin/api/test-email.js +39 -0
- package/dist/admin/auth-client.d.ts +543 -543
- package/dist/admin/client/index.d.ts +10 -0
- package/dist/admin/client/index.js +12 -0
- package/dist/admin/client/maintenance/maintenance-page.svelte +210 -0
- package/dist/admin/client/shop/coupon-schema.d.ts +1 -1
- package/dist/admin/client/shop/restore-order-cell.svelte +29 -0
- package/dist/admin/client/shop/restore-order-cell.svelte.d.ts +8 -0
- package/dist/admin/client/shop/shop-order-detail-page.svelte +156 -1
- package/dist/admin/client/shop/shop-orders-list-page.svelte +113 -53
- package/dist/admin/components/layout/app-sidebar.svelte +2 -0
- package/dist/admin/components/layout/nav-custom.svelte +26 -0
- package/dist/admin/components/layout/nav-custom.svelte.d.ts +3 -0
- package/dist/admin/components/layout/page-header.svelte +13 -3
- package/dist/admin/components/layout/page-header.svelte.d.ts +13 -3
- package/dist/admin/remote/admin.remote.d.ts +7 -0
- package/dist/admin/remote/admin.remote.js +10 -0
- package/dist/admin/remote/entry.remote.d.ts +2 -2
- package/dist/admin/remote/index.d.ts +1 -0
- package/dist/admin/remote/index.js +1 -0
- package/dist/admin/remote/invite.d.ts +1 -1
- package/dist/admin/remote/shop.remote.d.ts +125 -40
- package/dist/admin/remote/shop.remote.js +59 -10
- package/dist/admin/types.d.ts +15 -0
- package/dist/admin/utils/csv-export.d.ts +45 -0
- package/dist/admin/utils/csv-export.js +61 -0
- package/dist/cli/scaffold/admin.js +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/dist/core/cms.d.ts +44 -2
- package/dist/core/cms.js +64 -0
- package/dist/core/index.d.ts +2 -4
- package/dist/core/index.js +1 -4
- package/dist/core/server/index.d.ts +4 -1
- package/dist/core/server/index.js +4 -1
- package/dist/db-postgres/schema/shop/index.d.ts +1 -0
- package/dist/db-postgres/schema/shop/index.js +1 -0
- package/dist/db-postgres/schema/shop/invoice.d.ts +254 -0
- package/dist/db-postgres/schema/shop/invoice.js +27 -0
- package/dist/db-postgres/schema/shop/order.d.ts +104 -0
- package/dist/db-postgres/schema/shop/order.js +8 -0
- package/dist/shop/adapters/fakturownia/client.d.ts +33 -0
- package/dist/shop/adapters/fakturownia/client.js +87 -0
- package/dist/shop/adapters/fakturownia/index.d.ts +27 -0
- package/dist/shop/adapters/fakturownia/index.js +47 -0
- package/dist/shop/adapters/fakturownia/payload.d.ts +35 -0
- package/dist/shop/adapters/fakturownia/payload.js +45 -0
- package/dist/shop/adapters/payu/index.js +11 -0
- package/dist/shop/client/index.d.ts +7 -0
- package/dist/shop/http/checkout-handler.js +11 -0
- package/dist/shop/index.d.ts +4 -1
- package/dist/shop/index.js +3 -0
- package/dist/shop/nip.d.ts +12 -0
- package/dist/shop/nip.js +23 -0
- package/dist/shop/server/coupons.d.ts +10 -0
- package/dist/shop/server/coupons.js +19 -0
- package/dist/shop/server/email.d.ts +7 -3
- package/dist/shop/server/email.js +86 -112
- package/dist/shop/server/emailTemplateRegistry.d.ts +47 -0
- package/dist/shop/server/emailTemplateRegistry.js +288 -0
- package/dist/shop/server/invoices.d.ts +64 -0
- package/dist/shop/server/invoices.js +237 -0
- package/dist/shop/server/orders.d.ts +64 -1
- package/dist/shop/server/orders.js +155 -15
- package/dist/shop/templates/_partials/footer.en.html +4 -0
- package/dist/shop/templates/_partials/footer.pl.html +4 -0
- package/dist/shop/templates/_partials/header.en.html +4 -0
- package/dist/shop/templates/_partials/header.pl.html +4 -0
- package/dist/shop/templates/_partials/items.en.html +14 -0
- package/dist/shop/templates/_partials/items.pl.html +14 -0
- package/dist/shop/templates/_partials/tracking.en.html +7 -0
- package/dist/shop/templates/_partials/tracking.pl.html +7 -0
- package/dist/shop/templates/awaiting-payment.en.html +6 -0
- package/dist/shop/templates/awaiting-payment.pl.html +6 -0
- package/dist/shop/templates/cancelled.en.html +6 -0
- package/dist/shop/templates/cancelled.pl.html +6 -0
- package/dist/shop/templates/low-stock.en.html +14 -0
- package/dist/shop/templates/low-stock.pl.html +14 -0
- package/dist/shop/templates/order-completed.en.html +6 -0
- package/dist/shop/templates/order-completed.pl.html +6 -0
- package/dist/shop/templates/order-received.en.html +7 -0
- package/dist/shop/templates/order-received.pl.html +7 -0
- package/dist/shop/templates/payment-received.en.html +7 -0
- package/dist/shop/templates/payment-received.pl.html +7 -0
- package/dist/shop/templates/payment-rejected.en.html +6 -0
- package/dist/shop/templates/payment-rejected.pl.html +6 -0
- package/dist/shop/templates/preparing.en.html +7 -0
- package/dist/shop/templates/preparing.pl.html +7 -0
- package/dist/shop/templates/refunded.en.html +6 -0
- package/dist/shop/templates/refunded.pl.html +6 -0
- package/dist/shop/templates/shipped.en.html +7 -0
- package/dist/shop/templates/shipped.pl.html +7 -0
- package/dist/shop/types.d.ts +130 -1
- package/dist/sveltekit/index.d.ts +0 -1
- package/dist/sveltekit/index.js +0 -1
- package/dist/sveltekit/server/index.d.ts +1 -0
- package/dist/sveltekit/server/index.js +1 -0
- package/dist/types/adapters/email.d.ts +13 -0
- package/dist/types/cms.d.ts +30 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/updates/0.28.0/index.d.ts +2 -0
- package/dist/updates/0.28.0/index.js +38 -0
- package/dist/updates/0.34.0/index.d.ts +2 -0
- package/dist/updates/0.34.0/index.js +17 -0
- package/dist/updates/index.js +5 -1
- package/package.json +7 -2
package/dist/core/cms.js
CHANGED
|
@@ -23,6 +23,7 @@ export class CMS {
|
|
|
23
23
|
sidebarHelp;
|
|
24
24
|
shopConfig;
|
|
25
25
|
cmpConfig;
|
|
26
|
+
adminConfig;
|
|
26
27
|
/**
|
|
27
28
|
* Resolves once the shop's variant-attribute GIN indexes have been applied
|
|
28
29
|
* by `initCMS()`. `null` when the CMS is configured without a shop. Tests
|
|
@@ -47,6 +48,7 @@ export class CMS {
|
|
|
47
48
|
this.sidebarHelp = config.sidebarHelp ?? true;
|
|
48
49
|
this.shopConfig = config.shop ?? null;
|
|
49
50
|
this.cmpConfig = config.cmp ?? null;
|
|
51
|
+
this.adminConfig = config.admin ?? null;
|
|
50
52
|
this.collections = {};
|
|
51
53
|
this.singles = {};
|
|
52
54
|
this.forms = {};
|
|
@@ -134,6 +136,7 @@ export class CMS {
|
|
|
134
136
|
database: drizzleAdapter(drizzleDb, { provider: 'pg', schema: authSchema }),
|
|
135
137
|
secret: this.authConfig.secret,
|
|
136
138
|
baseURL: this.authConfig.baseURL,
|
|
139
|
+
...(this.authConfig.rateLimit ? { rateLimit: this.authConfig.rateLimit } : {}),
|
|
137
140
|
emailAndPassword: {
|
|
138
141
|
enabled: true,
|
|
139
142
|
async sendResetPassword({ user, url }, request) {
|
|
@@ -185,6 +188,18 @@ export function initCMS(config) {
|
|
|
185
188
|
console.warn('[shop] Failed to apply variant attribute indexes:', e);
|
|
186
189
|
});
|
|
187
190
|
}
|
|
191
|
+
// Smoke-test built-in shop email templates — fast lookup-only sweep.
|
|
192
|
+
// Catches corrupt installs (missing files in dist) at boot instead of
|
|
193
|
+
// at first email send. Dynamic import avoids loading shop server code
|
|
194
|
+
// when there is no shop configured.
|
|
195
|
+
import('../shop/server/email.js')
|
|
196
|
+
.then(({ REQUIRED_TEMPLATE_NAMES }) => import('../shop/server/emailTemplateRegistry.js').then(({ validateBuiltinTemplates }) => {
|
|
197
|
+
const defaultLang = cms.languages[0] ?? 'pl';
|
|
198
|
+
validateBuiltinTemplates([...REQUIRED_TEMPLATE_NAMES], defaultLang);
|
|
199
|
+
}))
|
|
200
|
+
.catch((e) => {
|
|
201
|
+
console.error('[shop] Built-in email template validation failed:', e);
|
|
202
|
+
});
|
|
188
203
|
}
|
|
189
204
|
return cms;
|
|
190
205
|
}
|
|
@@ -210,3 +225,52 @@ export function getCMS() {
|
|
|
210
225
|
}
|
|
211
226
|
return cms;
|
|
212
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Returns the configured email adapter from the active CMS singleton, or
|
|
230
|
+
* `null` when no `email` was passed to `defineConfig`. Use this in userland
|
|
231
|
+
* hooks (e.g. `ShopConfig.onOrderPaid`) to reuse the same SMTP transport the
|
|
232
|
+
* CMS already opened — no second nodemailer instance, no duplicated ENV.
|
|
233
|
+
*
|
|
234
|
+
* @returns The current `EmailAdapter` or `null`.
|
|
235
|
+
* @throws {Error} when called before `initCMS()` has run.
|
|
236
|
+
* @public
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* import { getMailer } from 'includio-cms/core';
|
|
240
|
+
*
|
|
241
|
+
* const mailer = getMailer();
|
|
242
|
+
* if (mailer) await mailer.sendMail({ to: 'admin@…', subject: '…', html: '…' });
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
export function getMailer() {
|
|
246
|
+
return getCMS().emailAdapter;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Send an email through the configured CMS email adapter. Thin convenience
|
|
250
|
+
* wrapper around `getCMS().emailAdapter.sendMail` — same options shape, same
|
|
251
|
+
* From address, same SMTP transport as the built-in flows (reset password,
|
|
252
|
+
* shop order status, form notifications).
|
|
253
|
+
*
|
|
254
|
+
* @throws {Error} when no email adapter is configured (matches
|
|
255
|
+
* `defineConfig({ email: ... })` not being set) or when the CMS has not
|
|
256
|
+
* been initialized yet.
|
|
257
|
+
* @public
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* import { sendMail } from 'includio-cms/core';
|
|
261
|
+
*
|
|
262
|
+
* await sendMail({
|
|
263
|
+
* to: process.env.ADMIN_EMAIL!,
|
|
264
|
+
* subject: '[shop] New consent',
|
|
265
|
+
* text: 'Plain fallback for clients that prefer text.',
|
|
266
|
+
* html: '<p>Body…</p>'
|
|
267
|
+
* });
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
export async function sendMail(options) {
|
|
271
|
+
const mailer = getCMS().emailAdapter;
|
|
272
|
+
if (!mailer) {
|
|
273
|
+
throw new Error('[includio-cms] sendMail() called but no `email` adapter is configured in defineConfig.');
|
|
274
|
+
}
|
|
275
|
+
await mailer.sendMail(options);
|
|
276
|
+
}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { getCMS } from './cms.js';
|
|
2
|
-
export { resolveMediaWithStyles, type ResolvedMedia } from './server/fields/utils/resolveMedia.js';
|
|
3
1
|
export { resolveSeo, type ResolvedSeo } from './fields/resolveSeo.js';
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
2
|
+
export { getMailer, sendMail } from './cms.js';
|
|
3
|
+
export type { EmailAdapter, SendMailOptions } from '../types/adapters/email.js';
|
package/dist/core/index.js
CHANGED
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
export { getCMS } from './cms.js';
|
|
2
|
-
export { resolveMediaWithStyles } from './server/fields/utils/resolveMedia.js';
|
|
3
1
|
export { resolveSeo } from './fields/resolveSeo.js';
|
|
4
|
-
export {
|
|
5
|
-
export { getAuth } from '../server/auth.js';
|
|
2
|
+
export { getMailer, sendMail } from './cms.js';
|
|
@@ -7,6 +7,7 @@ export * from './orderStatusHistory.js';
|
|
|
7
7
|
export * from './payment.js';
|
|
8
8
|
export * from './stockReservation.js';
|
|
9
9
|
export * from './refunds.js';
|
|
10
|
+
export * from './invoice.js';
|
|
10
11
|
export * from './webhookEvents.js';
|
|
11
12
|
export * from './coupons.js';
|
|
12
13
|
export * from './couponRedemptions.js';
|
|
@@ -7,6 +7,7 @@ export * from './orderStatusHistory.js';
|
|
|
7
7
|
export * from './payment.js';
|
|
8
8
|
export * from './stockReservation.js';
|
|
9
9
|
export * from './refunds.js';
|
|
10
|
+
export * from './invoice.js';
|
|
10
11
|
export * from './webhookEvents.js';
|
|
11
12
|
export * from './coupons.js';
|
|
12
13
|
export * from './couponRedemptions.js';
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
export type ShopInvoiceStatus = 'pending' | 'issued' | 'sent' | 'failed';
|
|
2
|
+
/**
|
|
3
|
+
* One invoice per order (`order_id` is unique → idempotency guard). `external_id`
|
|
4
|
+
* / `number` / `pdf_url` come from the invoicing provider once issued. The
|
|
5
|
+
* `attempts` / `next_retry_at` columns are reserved for a future automatic-retry
|
|
6
|
+
* mechanism; the current flow only retries manually from the admin.
|
|
7
|
+
*/
|
|
8
|
+
export declare const shopInvoicesTable: import("drizzle-orm/pg-core/table", { with: { "resolution-mode": "require" } }).PgTableWithColumns<{
|
|
9
|
+
name: "shop_invoices";
|
|
10
|
+
schema: undefined;
|
|
11
|
+
columns: {
|
|
12
|
+
id: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
13
|
+
name: "id";
|
|
14
|
+
tableName: "shop_invoices";
|
|
15
|
+
dataType: "string";
|
|
16
|
+
columnType: "PgUUID";
|
|
17
|
+
data: string;
|
|
18
|
+
driverParam: string;
|
|
19
|
+
notNull: true;
|
|
20
|
+
hasDefault: true;
|
|
21
|
+
isPrimaryKey: true;
|
|
22
|
+
isAutoincrement: false;
|
|
23
|
+
hasRuntimeDefault: false;
|
|
24
|
+
enumValues: undefined;
|
|
25
|
+
baseColumn: never;
|
|
26
|
+
identity: undefined;
|
|
27
|
+
generated: undefined;
|
|
28
|
+
}, {}, {}>;
|
|
29
|
+
orderId: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
30
|
+
name: "order_id";
|
|
31
|
+
tableName: "shop_invoices";
|
|
32
|
+
dataType: "string";
|
|
33
|
+
columnType: "PgUUID";
|
|
34
|
+
data: string;
|
|
35
|
+
driverParam: string;
|
|
36
|
+
notNull: true;
|
|
37
|
+
hasDefault: false;
|
|
38
|
+
isPrimaryKey: false;
|
|
39
|
+
isAutoincrement: false;
|
|
40
|
+
hasRuntimeDefault: false;
|
|
41
|
+
enumValues: undefined;
|
|
42
|
+
baseColumn: never;
|
|
43
|
+
identity: undefined;
|
|
44
|
+
generated: undefined;
|
|
45
|
+
}, {}, {}>;
|
|
46
|
+
provider: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
47
|
+
name: "provider";
|
|
48
|
+
tableName: "shop_invoices";
|
|
49
|
+
dataType: "string";
|
|
50
|
+
columnType: "PgText";
|
|
51
|
+
data: string;
|
|
52
|
+
driverParam: string;
|
|
53
|
+
notNull: true;
|
|
54
|
+
hasDefault: false;
|
|
55
|
+
isPrimaryKey: false;
|
|
56
|
+
isAutoincrement: false;
|
|
57
|
+
hasRuntimeDefault: false;
|
|
58
|
+
enumValues: [string, ...string[]];
|
|
59
|
+
baseColumn: never;
|
|
60
|
+
identity: undefined;
|
|
61
|
+
generated: undefined;
|
|
62
|
+
}, {}, {}>;
|
|
63
|
+
externalId: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
64
|
+
name: "external_id";
|
|
65
|
+
tableName: "shop_invoices";
|
|
66
|
+
dataType: "string";
|
|
67
|
+
columnType: "PgText";
|
|
68
|
+
data: string;
|
|
69
|
+
driverParam: string;
|
|
70
|
+
notNull: false;
|
|
71
|
+
hasDefault: false;
|
|
72
|
+
isPrimaryKey: false;
|
|
73
|
+
isAutoincrement: false;
|
|
74
|
+
hasRuntimeDefault: false;
|
|
75
|
+
enumValues: [string, ...string[]];
|
|
76
|
+
baseColumn: never;
|
|
77
|
+
identity: undefined;
|
|
78
|
+
generated: undefined;
|
|
79
|
+
}, {}, {}>;
|
|
80
|
+
number: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
81
|
+
name: "number";
|
|
82
|
+
tableName: "shop_invoices";
|
|
83
|
+
dataType: "string";
|
|
84
|
+
columnType: "PgText";
|
|
85
|
+
data: string;
|
|
86
|
+
driverParam: string;
|
|
87
|
+
notNull: false;
|
|
88
|
+
hasDefault: false;
|
|
89
|
+
isPrimaryKey: false;
|
|
90
|
+
isAutoincrement: false;
|
|
91
|
+
hasRuntimeDefault: false;
|
|
92
|
+
enumValues: [string, ...string[]];
|
|
93
|
+
baseColumn: never;
|
|
94
|
+
identity: undefined;
|
|
95
|
+
generated: undefined;
|
|
96
|
+
}, {}, {}>;
|
|
97
|
+
pdfUrl: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
98
|
+
name: "pdf_url";
|
|
99
|
+
tableName: "shop_invoices";
|
|
100
|
+
dataType: "string";
|
|
101
|
+
columnType: "PgText";
|
|
102
|
+
data: string;
|
|
103
|
+
driverParam: string;
|
|
104
|
+
notNull: false;
|
|
105
|
+
hasDefault: false;
|
|
106
|
+
isPrimaryKey: false;
|
|
107
|
+
isAutoincrement: false;
|
|
108
|
+
hasRuntimeDefault: false;
|
|
109
|
+
enumValues: [string, ...string[]];
|
|
110
|
+
baseColumn: never;
|
|
111
|
+
identity: undefined;
|
|
112
|
+
generated: undefined;
|
|
113
|
+
}, {}, {}>;
|
|
114
|
+
kind: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
115
|
+
name: "kind";
|
|
116
|
+
tableName: "shop_invoices";
|
|
117
|
+
dataType: "string";
|
|
118
|
+
columnType: "PgText";
|
|
119
|
+
data: string;
|
|
120
|
+
driverParam: string;
|
|
121
|
+
notNull: true;
|
|
122
|
+
hasDefault: true;
|
|
123
|
+
isPrimaryKey: false;
|
|
124
|
+
isAutoincrement: false;
|
|
125
|
+
hasRuntimeDefault: false;
|
|
126
|
+
enumValues: [string, ...string[]];
|
|
127
|
+
baseColumn: never;
|
|
128
|
+
identity: undefined;
|
|
129
|
+
generated: undefined;
|
|
130
|
+
}, {}, {}>;
|
|
131
|
+
status: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
132
|
+
name: "status";
|
|
133
|
+
tableName: "shop_invoices";
|
|
134
|
+
dataType: "string";
|
|
135
|
+
columnType: "PgText";
|
|
136
|
+
data: ShopInvoiceStatus;
|
|
137
|
+
driverParam: string;
|
|
138
|
+
notNull: true;
|
|
139
|
+
hasDefault: true;
|
|
140
|
+
isPrimaryKey: false;
|
|
141
|
+
isAutoincrement: false;
|
|
142
|
+
hasRuntimeDefault: false;
|
|
143
|
+
enumValues: [string, ...string[]];
|
|
144
|
+
baseColumn: never;
|
|
145
|
+
identity: undefined;
|
|
146
|
+
generated: undefined;
|
|
147
|
+
}, {}, {
|
|
148
|
+
$type: ShopInvoiceStatus;
|
|
149
|
+
}>;
|
|
150
|
+
attempts: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
151
|
+
name: "attempts";
|
|
152
|
+
tableName: "shop_invoices";
|
|
153
|
+
dataType: "number";
|
|
154
|
+
columnType: "PgInteger";
|
|
155
|
+
data: number;
|
|
156
|
+
driverParam: string | number;
|
|
157
|
+
notNull: true;
|
|
158
|
+
hasDefault: true;
|
|
159
|
+
isPrimaryKey: false;
|
|
160
|
+
isAutoincrement: false;
|
|
161
|
+
hasRuntimeDefault: false;
|
|
162
|
+
enumValues: undefined;
|
|
163
|
+
baseColumn: never;
|
|
164
|
+
identity: undefined;
|
|
165
|
+
generated: undefined;
|
|
166
|
+
}, {}, {}>;
|
|
167
|
+
nextRetryAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
168
|
+
name: "next_retry_at";
|
|
169
|
+
tableName: "shop_invoices";
|
|
170
|
+
dataType: "date";
|
|
171
|
+
columnType: "PgTimestamp";
|
|
172
|
+
data: Date;
|
|
173
|
+
driverParam: string;
|
|
174
|
+
notNull: false;
|
|
175
|
+
hasDefault: false;
|
|
176
|
+
isPrimaryKey: false;
|
|
177
|
+
isAutoincrement: false;
|
|
178
|
+
hasRuntimeDefault: false;
|
|
179
|
+
enumValues: undefined;
|
|
180
|
+
baseColumn: never;
|
|
181
|
+
identity: undefined;
|
|
182
|
+
generated: undefined;
|
|
183
|
+
}, {}, {}>;
|
|
184
|
+
lastError: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
185
|
+
name: "last_error";
|
|
186
|
+
tableName: "shop_invoices";
|
|
187
|
+
dataType: "string";
|
|
188
|
+
columnType: "PgText";
|
|
189
|
+
data: string;
|
|
190
|
+
driverParam: string;
|
|
191
|
+
notNull: false;
|
|
192
|
+
hasDefault: false;
|
|
193
|
+
isPrimaryKey: false;
|
|
194
|
+
isAutoincrement: false;
|
|
195
|
+
hasRuntimeDefault: false;
|
|
196
|
+
enumValues: [string, ...string[]];
|
|
197
|
+
baseColumn: never;
|
|
198
|
+
identity: undefined;
|
|
199
|
+
generated: undefined;
|
|
200
|
+
}, {}, {}>;
|
|
201
|
+
raw: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
202
|
+
name: "raw";
|
|
203
|
+
tableName: "shop_invoices";
|
|
204
|
+
dataType: "json";
|
|
205
|
+
columnType: "PgJsonb";
|
|
206
|
+
data: unknown;
|
|
207
|
+
driverParam: unknown;
|
|
208
|
+
notNull: false;
|
|
209
|
+
hasDefault: false;
|
|
210
|
+
isPrimaryKey: false;
|
|
211
|
+
isAutoincrement: false;
|
|
212
|
+
hasRuntimeDefault: false;
|
|
213
|
+
enumValues: undefined;
|
|
214
|
+
baseColumn: never;
|
|
215
|
+
identity: undefined;
|
|
216
|
+
generated: undefined;
|
|
217
|
+
}, {}, {}>;
|
|
218
|
+
createdAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
219
|
+
name: "created_at";
|
|
220
|
+
tableName: "shop_invoices";
|
|
221
|
+
dataType: "date";
|
|
222
|
+
columnType: "PgTimestamp";
|
|
223
|
+
data: Date;
|
|
224
|
+
driverParam: string;
|
|
225
|
+
notNull: true;
|
|
226
|
+
hasDefault: true;
|
|
227
|
+
isPrimaryKey: false;
|
|
228
|
+
isAutoincrement: false;
|
|
229
|
+
hasRuntimeDefault: false;
|
|
230
|
+
enumValues: undefined;
|
|
231
|
+
baseColumn: never;
|
|
232
|
+
identity: undefined;
|
|
233
|
+
generated: undefined;
|
|
234
|
+
}, {}, {}>;
|
|
235
|
+
updatedAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
236
|
+
name: "updated_at";
|
|
237
|
+
tableName: "shop_invoices";
|
|
238
|
+
dataType: "date";
|
|
239
|
+
columnType: "PgTimestamp";
|
|
240
|
+
data: Date;
|
|
241
|
+
driverParam: string;
|
|
242
|
+
notNull: true;
|
|
243
|
+
hasDefault: true;
|
|
244
|
+
isPrimaryKey: false;
|
|
245
|
+
isAutoincrement: false;
|
|
246
|
+
hasRuntimeDefault: false;
|
|
247
|
+
enumValues: undefined;
|
|
248
|
+
baseColumn: never;
|
|
249
|
+
identity: undefined;
|
|
250
|
+
generated: undefined;
|
|
251
|
+
}, {}, {}>;
|
|
252
|
+
};
|
|
253
|
+
dialect: "pg";
|
|
254
|
+
}>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { integer, jsonb, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { shopOrdersTable } from './order.js';
|
|
3
|
+
/**
|
|
4
|
+
* One invoice per order (`order_id` is unique → idempotency guard). `external_id`
|
|
5
|
+
* / `number` / `pdf_url` come from the invoicing provider once issued. The
|
|
6
|
+
* `attempts` / `next_retry_at` columns are reserved for a future automatic-retry
|
|
7
|
+
* mechanism; the current flow only retries manually from the admin.
|
|
8
|
+
*/
|
|
9
|
+
export const shopInvoicesTable = pgTable('shop_invoices', {
|
|
10
|
+
id: uuid('id').primaryKey().defaultRandom(),
|
|
11
|
+
orderId: uuid('order_id')
|
|
12
|
+
.notNull()
|
|
13
|
+
.unique()
|
|
14
|
+
.references(() => shopOrdersTable.id, { onDelete: 'cascade' }),
|
|
15
|
+
provider: text('provider').notNull(),
|
|
16
|
+
externalId: text('external_id'),
|
|
17
|
+
number: text('number'),
|
|
18
|
+
pdfUrl: text('pdf_url'),
|
|
19
|
+
kind: text('kind').notNull().default('vat'),
|
|
20
|
+
status: text('status').$type().notNull().default('pending'),
|
|
21
|
+
attempts: integer('attempts').notNull().default(0),
|
|
22
|
+
nextRetryAt: timestamp('next_retry_at', { withTimezone: true }),
|
|
23
|
+
lastError: text('last_error'),
|
|
24
|
+
raw: jsonb('raw'),
|
|
25
|
+
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
|
26
|
+
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
|
|
27
|
+
});
|
|
@@ -124,6 +124,40 @@ export declare const shopOrdersTable: import("drizzle-orm/pg-core/table", { with
|
|
|
124
124
|
identity: undefined;
|
|
125
125
|
generated: undefined;
|
|
126
126
|
}, {}, {}>;
|
|
127
|
+
customerNip: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
128
|
+
name: "customer_nip";
|
|
129
|
+
tableName: "shop_orders";
|
|
130
|
+
dataType: "string";
|
|
131
|
+
columnType: "PgText";
|
|
132
|
+
data: string;
|
|
133
|
+
driverParam: string;
|
|
134
|
+
notNull: false;
|
|
135
|
+
hasDefault: false;
|
|
136
|
+
isPrimaryKey: false;
|
|
137
|
+
isAutoincrement: false;
|
|
138
|
+
hasRuntimeDefault: false;
|
|
139
|
+
enumValues: [string, ...string[]];
|
|
140
|
+
baseColumn: never;
|
|
141
|
+
identity: undefined;
|
|
142
|
+
generated: undefined;
|
|
143
|
+
}, {}, {}>;
|
|
144
|
+
customerCompanyName: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
145
|
+
name: "customer_company_name";
|
|
146
|
+
tableName: "shop_orders";
|
|
147
|
+
dataType: "string";
|
|
148
|
+
columnType: "PgText";
|
|
149
|
+
data: string;
|
|
150
|
+
driverParam: string;
|
|
151
|
+
notNull: false;
|
|
152
|
+
hasDefault: false;
|
|
153
|
+
isPrimaryKey: false;
|
|
154
|
+
isAutoincrement: false;
|
|
155
|
+
hasRuntimeDefault: false;
|
|
156
|
+
enumValues: [string, ...string[]];
|
|
157
|
+
baseColumn: never;
|
|
158
|
+
identity: undefined;
|
|
159
|
+
generated: undefined;
|
|
160
|
+
}, {}, {}>;
|
|
127
161
|
shippingAddress: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
128
162
|
name: "shipping_address";
|
|
129
163
|
tableName: "shop_orders";
|
|
@@ -143,6 +177,42 @@ export declare const shopOrdersTable: import("drizzle-orm/pg-core/table", { with
|
|
|
143
177
|
}, {}, {
|
|
144
178
|
$type: Record<string, string>;
|
|
145
179
|
}>;
|
|
180
|
+
billingAddress: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
181
|
+
name: "billing_address";
|
|
182
|
+
tableName: "shop_orders";
|
|
183
|
+
dataType: "json";
|
|
184
|
+
columnType: "PgJsonb";
|
|
185
|
+
data: Record<string, string>;
|
|
186
|
+
driverParam: unknown;
|
|
187
|
+
notNull: false;
|
|
188
|
+
hasDefault: false;
|
|
189
|
+
isPrimaryKey: false;
|
|
190
|
+
isAutoincrement: false;
|
|
191
|
+
hasRuntimeDefault: false;
|
|
192
|
+
enumValues: undefined;
|
|
193
|
+
baseColumn: never;
|
|
194
|
+
identity: undefined;
|
|
195
|
+
generated: undefined;
|
|
196
|
+
}, {}, {
|
|
197
|
+
$type: Record<string, string>;
|
|
198
|
+
}>;
|
|
199
|
+
invoiceRequested: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
200
|
+
name: "invoice_requested";
|
|
201
|
+
tableName: "shop_orders";
|
|
202
|
+
dataType: "boolean";
|
|
203
|
+
columnType: "PgBoolean";
|
|
204
|
+
data: boolean;
|
|
205
|
+
driverParam: boolean;
|
|
206
|
+
notNull: true;
|
|
207
|
+
hasDefault: true;
|
|
208
|
+
isPrimaryKey: false;
|
|
209
|
+
isAutoincrement: false;
|
|
210
|
+
hasRuntimeDefault: false;
|
|
211
|
+
enumValues: undefined;
|
|
212
|
+
baseColumn: never;
|
|
213
|
+
identity: undefined;
|
|
214
|
+
generated: undefined;
|
|
215
|
+
}, {}, {}>;
|
|
146
216
|
totalNet: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
147
217
|
name: "total_net";
|
|
148
218
|
tableName: "shop_orders";
|
|
@@ -495,6 +565,40 @@ export declare const shopOrdersTable: import("drizzle-orm/pg-core/table", { with
|
|
|
495
565
|
identity: undefined;
|
|
496
566
|
generated: undefined;
|
|
497
567
|
}, {}, {}>;
|
|
568
|
+
deletedAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
569
|
+
name: "deleted_at";
|
|
570
|
+
tableName: "shop_orders";
|
|
571
|
+
dataType: "date";
|
|
572
|
+
columnType: "PgTimestamp";
|
|
573
|
+
data: Date;
|
|
574
|
+
driverParam: string;
|
|
575
|
+
notNull: false;
|
|
576
|
+
hasDefault: false;
|
|
577
|
+
isPrimaryKey: false;
|
|
578
|
+
isAutoincrement: false;
|
|
579
|
+
hasRuntimeDefault: false;
|
|
580
|
+
enumValues: undefined;
|
|
581
|
+
baseColumn: never;
|
|
582
|
+
identity: undefined;
|
|
583
|
+
generated: undefined;
|
|
584
|
+
}, {}, {}>;
|
|
585
|
+
deletedBy: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
586
|
+
name: "deleted_by";
|
|
587
|
+
tableName: "shop_orders";
|
|
588
|
+
dataType: "string";
|
|
589
|
+
columnType: "PgText";
|
|
590
|
+
data: string;
|
|
591
|
+
driverParam: string;
|
|
592
|
+
notNull: false;
|
|
593
|
+
hasDefault: false;
|
|
594
|
+
isPrimaryKey: false;
|
|
595
|
+
isAutoincrement: false;
|
|
596
|
+
hasRuntimeDefault: false;
|
|
597
|
+
enumValues: [string, ...string[]];
|
|
598
|
+
baseColumn: never;
|
|
599
|
+
identity: undefined;
|
|
600
|
+
generated: undefined;
|
|
601
|
+
}, {}, {}>;
|
|
498
602
|
createdAt: import("drizzle-orm/pg-core", { with: { "resolution-mode": "require" } }).PgColumn<{
|
|
499
603
|
name: "created_at";
|
|
500
604
|
tableName: "shop_orders";
|
|
@@ -8,7 +8,11 @@ export const shopOrdersTable = pgTable('shop_orders', {
|
|
|
8
8
|
customerEmail: text('customer_email').notNull(),
|
|
9
9
|
customerName: text('customer_name'),
|
|
10
10
|
customerPhone: text('customer_phone'),
|
|
11
|
+
customerNip: text('customer_nip'),
|
|
12
|
+
customerCompanyName: text('customer_company_name'),
|
|
11
13
|
shippingAddress: jsonb('shipping_address').$type(),
|
|
14
|
+
billingAddress: jsonb('billing_address').$type(),
|
|
15
|
+
invoiceRequested: boolean('invoice_requested').default(false).notNull(),
|
|
12
16
|
totalNet: integer('total_net').notNull(),
|
|
13
17
|
totalGross: integer('total_gross').notNull(),
|
|
14
18
|
vatAmount: integer('vat_amount').notNull(),
|
|
@@ -31,6 +35,10 @@ export const shopOrdersTable = pgTable('shop_orders', {
|
|
|
31
35
|
accessToken: uuid('access_token').defaultRandom().notNull(),
|
|
32
36
|
partialPayment: jsonb('partial_payment').$type(),
|
|
33
37
|
balanceOwed: boolean('balance_owed').default(false).notNull(),
|
|
38
|
+
// Soft-delete: admin can hide an order from the admin list without losing
|
|
39
|
+
// the row (accounting/audit safety). NULL = visible. See softDeleteOrder.
|
|
40
|
+
deletedAt: timestamp('deleted_at', { withTimezone: true }),
|
|
41
|
+
deletedBy: text('deleted_by'),
|
|
34
42
|
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
|
35
43
|
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
|
|
36
44
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FakturowniaInvoiceBody } from './payload.js';
|
|
2
|
+
export interface FakturowniaClientOptions {
|
|
3
|
+
/** Account subdomain (`acme`) or full host/URL (`acme.fakturownia.pl`) — normalised either way. */
|
|
4
|
+
domain: string;
|
|
5
|
+
apiToken: string;
|
|
6
|
+
fetch?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
export interface FakturowniaInvoice {
|
|
9
|
+
id: number | string;
|
|
10
|
+
number?: string;
|
|
11
|
+
view_url?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export declare class FakturowniaApiError extends Error {
|
|
15
|
+
readonly status: number;
|
|
16
|
+
readonly body?: unknown | undefined;
|
|
17
|
+
constructor(message: string, status: number, body?: unknown | undefined);
|
|
18
|
+
}
|
|
19
|
+
export declare class FakturowniaClient {
|
|
20
|
+
private readonly base;
|
|
21
|
+
private readonly apiToken;
|
|
22
|
+
private readonly fetchFn;
|
|
23
|
+
constructor(opts: FakturowniaClientOptions);
|
|
24
|
+
createInvoice(invoice: FakturowniaInvoiceBody): Promise<FakturowniaInvoice>;
|
|
25
|
+
sendByEmail(id: number | string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Read-only account lookup — used for connectivity/auth health checks.
|
|
28
|
+
* Does NOT create or modify anything. A 200 means domain + token are valid.
|
|
29
|
+
*/
|
|
30
|
+
getAccount(): Promise<unknown>;
|
|
31
|
+
private postRaw;
|
|
32
|
+
private post;
|
|
33
|
+
}
|