@hartl-services/medusa-base 1.0.0 → 2.0.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/.medusa/server/src/admin/index.js +117 -1
- package/.medusa/server/src/admin/index.mjs +118 -2
- package/.medusa/server/src/documents/plugin-features.js +15 -1
- package/.medusa/server/src/documents/templates/financial-document.js +69 -51
- package/.medusa/server/src/documents/templates/format.js +44 -2
- package/.medusa/server/src/documents/templates/html-factory.js +7 -5
- package/.medusa/server/src/documents/templates/html-utils.js +12 -2
- package/.medusa/server/src/documents/templates/layout.js +115 -70
- package/.medusa/server/src/documents/templates/packing-list.js +10 -8
- package/.medusa/server/src/documents/utils/document-merchant.js +5 -5
- package/.medusa/server/src/documents/utils/document-render-settings.js +59 -0
- package/.medusa/server/src/email/application/handle-invite-event.js +8 -2
- package/.medusa/server/src/email/application/handle-password-reset-event.js +16 -4
- package/.medusa/server/src/email/domain/order-summary.js +103 -0
- package/.medusa/server/src/email/domain/refund-summary.js +43 -0
- package/.medusa/server/src/email/domain/return-summary.js +118 -0
- package/.medusa/server/src/email/domain/shipment-summary.js +74 -0
- package/.medusa/server/src/email/plugin-features.js +9 -1
- package/.medusa/server/src/email/rendering/default-templates.js +693 -65
- package/.medusa/server/src/email/rendering/load-email-shop-details.js +36 -0
- package/.medusa/server/src/email/rendering/render-email.js +16 -3
- package/.medusa/server/src/email/rendering/resolve-shop-configuration.js +20 -1
- package/.medusa/server/src/email/types/template-data.js +155 -2
- package/.medusa/server/src/modules/documents/domain/merchant.js +2 -33
- package/.medusa/server/src/modules/documents/domain/options.js +1 -7
- package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +4 -3
- package/.medusa/server/src/public/descriptor.js +3 -1
- package/.medusa/server/src/public/shop-master-data.js +7 -7
- package/.medusa/server/src/shop-config/build-food-shop-config.js +6 -6
- package/.medusa/server/src/shop-config/options.js +5 -15
- package/.medusa/server/src/workflows/documents/issue-cancellation-after-refund.js +2 -3
- package/.medusa/server/src/workflows/documents/issue-document.js +6 -5
- package/.medusa/server/src/workflows/documents/issue-packing-list.js +9 -3
- package/.medusa/server/src/workflows/email/dispatch-email-message.js +4 -2
- package/.medusa/server/src/workflows/email/prepare-order-canceled-email.js +42 -4
- package/.medusa/server/src/workflows/email/prepare-order-received-email.js +31 -5
- package/.medusa/server/src/workflows/email/prepare-refund-email.js +25 -1
- package/.medusa/server/src/workflows/email/prepare-return-email.js +64 -3
- package/.medusa/server/src/workflows/email/prepare-shipment-email.js +36 -3
- package/README.md +180 -43
- package/dist/admin-components/features/plugin-features/components/ImageSettingInput.d.ts +15 -0
- package/dist/admin-components/features/plugin-features/components/ImageSettingInput.js +42 -0
- package/dist/admin-components/features/plugin-features/components/PluginSettingsForm.js +4 -2
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +1 -1
- package/dist/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +4 -4
- package/dist/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +1 -1
- package/dist/admin-components/features/plugin-features/lib/image-upload.d.ts +6 -0
- package/dist/admin-components/features/plugin-features/lib/image-upload.js +17 -0
- package/dist/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +4 -4
- package/dist/admin-components/i18n/texts.d.ts +6 -0
- package/dist/admin-components/i18n/texts.js +6 -0
- package/dist/documents/templates/financial-document.d.ts +3 -3
- package/dist/documents/templates/financial-document.js +68 -50
- package/dist/documents/templates/format.d.ts +14 -0
- package/dist/documents/templates/format.js +43 -1
- package/dist/documents/templates/html-factory.d.ts +6 -2
- package/dist/documents/templates/html-factory.js +6 -4
- package/dist/documents/templates/html-utils.d.ts +6 -0
- package/dist/documents/templates/html-utils.js +11 -1
- package/dist/documents/templates/layout.d.ts +21 -3
- package/dist/documents/templates/layout.js +114 -69
- package/dist/documents/templates/packing-list.js +9 -7
- package/dist/documents/types/documents.d.ts +11 -16
- package/dist/documents/types/html-factory.d.ts +1 -0
- package/dist/email/types/shop-configuration.d.ts +25 -0
- package/dist/email/types/template-data.d.ts +509 -0
- package/dist/email/types/template-data.js +154 -1
- package/dist/modules/documents/domain/options.d.ts +1 -9
- package/dist/modules/documents/domain/options.js +0 -6
- package/dist/public/descriptor.js +2 -0
- package/dist/public/shop-master-data.d.ts +5 -6
- package/dist/public/shop-master-data.js +6 -6
- package/dist/public/types.d.ts +2 -1
- package/dist/shop-config/build-food-shop-config.js +5 -5
- package/dist/shop-config/options.d.ts +13 -8
- package/dist/shop-config/options.js +4 -14
- package/package.json +2 -2
- package/dist/modules/documents/domain/merchant.d.ts +0 -22
- package/dist/modules/documents/domain/merchant.js +0 -85
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EmailRenderDataSchemas = exports.EmailPersistedDataSchemas = void 0;
|
|
3
|
+
exports.EmailRenderDataSchemas = exports.EmailPersistedDataSchemas = exports.RefundSummarySchema = exports.ReturnReceivedSummarySchema = exports.ReturnSummarySchema = exports.ShipmentSummarySchema = exports.OrderSummarySchema = void 0;
|
|
4
4
|
const zod_1 = require("@medusajs/framework/zod");
|
|
5
5
|
const affiliate_1 = require("@hartl-services/medusa-food-supplements-contracts/schemas/affiliate");
|
|
6
6
|
const NonEmptyStringSchema = zod_1.z.string().trim().min(1);
|
|
@@ -9,15 +9,146 @@ const DocumentsFactFingerprintSchema = zod_1.z
|
|
|
9
9
|
.string()
|
|
10
10
|
.regex(/^[a-f0-9]{64}$/)
|
|
11
11
|
.optional();
|
|
12
|
+
const AmountSchema = zod_1.z.string().regex(/^-?\d+(\.\d+)?$/);
|
|
13
|
+
const OptionalTextSchema = zod_1.z.string().nullable();
|
|
14
|
+
const OrderSummaryAddressSchema = zod_1.z
|
|
15
|
+
.object({
|
|
16
|
+
first_name: OptionalTextSchema,
|
|
17
|
+
last_name: OptionalTextSchema,
|
|
18
|
+
company: OptionalTextSchema,
|
|
19
|
+
address_1: OptionalTextSchema,
|
|
20
|
+
address_2: OptionalTextSchema,
|
|
21
|
+
postal_code: OptionalTextSchema,
|
|
22
|
+
city: OptionalTextSchema,
|
|
23
|
+
country_code: OptionalTextSchema,
|
|
24
|
+
})
|
|
25
|
+
.strict();
|
|
26
|
+
/**
|
|
27
|
+
* Snapshot of an order when it was placed. Amounts are Medusa's gross
|
|
28
|
+
* decimal strings: item and shipping rows before discounts, so
|
|
29
|
+
* `original_item_total + original_shipping_total - discount_total` is the
|
|
30
|
+
* `total`, which already contains `tax_total`.
|
|
31
|
+
*/
|
|
32
|
+
exports.OrderSummarySchema = zod_1.z
|
|
33
|
+
.object({
|
|
34
|
+
display_id: zod_1.z.number().int().nullable(),
|
|
35
|
+
/** The order's e-mail address; `null` when the order has none. */
|
|
36
|
+
email: OptionalTextSchema,
|
|
37
|
+
/** When the order was placed (ISO 8601). */
|
|
38
|
+
placed_at: zod_1.z.string().datetime(),
|
|
39
|
+
currency_code: NonEmptyStringSchema,
|
|
40
|
+
items: zod_1.z.array(zod_1.z
|
|
41
|
+
.object({
|
|
42
|
+
title: NonEmptyStringSchema,
|
|
43
|
+
variant_title: OptionalTextSchema,
|
|
44
|
+
quantity: zod_1.z.number().positive(),
|
|
45
|
+
original_total: AmountSchema,
|
|
46
|
+
})
|
|
47
|
+
.strict()),
|
|
48
|
+
shipping_method_names: zod_1.z.array(NonEmptyStringSchema),
|
|
49
|
+
original_item_total: AmountSchema,
|
|
50
|
+
original_shipping_total: AmountSchema,
|
|
51
|
+
discount_total: AmountSchema,
|
|
52
|
+
tax_total: AmountSchema,
|
|
53
|
+
total: AmountSchema,
|
|
54
|
+
billing_address: OrderSummaryAddressSchema.nullable(),
|
|
55
|
+
shipping_address: OrderSummaryAddressSchema.nullable(),
|
|
56
|
+
})
|
|
57
|
+
.strict();
|
|
58
|
+
/**
|
|
59
|
+
* Snapshot of one shipment when it was shipped: the parcel's items (title
|
|
60
|
+
* of the order line, shipped quantity), tracking numbers and links as
|
|
61
|
+
* entered at shipping, and whether more of the order is still to be shipped.
|
|
62
|
+
*/
|
|
63
|
+
exports.ShipmentSummarySchema = zod_1.z
|
|
64
|
+
.object({
|
|
65
|
+
display_id: zod_1.z.number().int().nullable(),
|
|
66
|
+
shipping_method_name: OptionalTextSchema,
|
|
67
|
+
tracking: zod_1.z.array(zod_1.z
|
|
68
|
+
.object({
|
|
69
|
+
number: OptionalTextSchema,
|
|
70
|
+
url: OptionalTextSchema,
|
|
71
|
+
})
|
|
72
|
+
.strict()),
|
|
73
|
+
items: zod_1.z.array(zod_1.z
|
|
74
|
+
.object({
|
|
75
|
+
title: NonEmptyStringSchema,
|
|
76
|
+
variant_title: OptionalTextSchema,
|
|
77
|
+
quantity: zod_1.z.number().positive(),
|
|
78
|
+
})
|
|
79
|
+
.strict()),
|
|
80
|
+
delivery_address: OrderSummaryAddressSchema.nullable(),
|
|
81
|
+
more_to_follow: zod_1.z.boolean(),
|
|
82
|
+
})
|
|
83
|
+
.strict();
|
|
84
|
+
/**
|
|
85
|
+
* Snapshot of a return when it was requested: the return and order numbers,
|
|
86
|
+
* the items to send back (title of the order line, quantity, reason label),
|
|
87
|
+
* the return shipping option and where to send the parcel.
|
|
88
|
+
*/
|
|
89
|
+
exports.ReturnSummarySchema = zod_1.z
|
|
90
|
+
.object({
|
|
91
|
+
display_id: zod_1.z.number().int().nullable(),
|
|
92
|
+
order_display_id: zod_1.z.number().int().nullable(),
|
|
93
|
+
shipping_method_name: OptionalTextSchema,
|
|
94
|
+
items: zod_1.z.array(zod_1.z
|
|
95
|
+
.object({
|
|
96
|
+
title: NonEmptyStringSchema,
|
|
97
|
+
variant_title: OptionalTextSchema,
|
|
98
|
+
quantity: zod_1.z.number().positive(),
|
|
99
|
+
reason: OptionalTextSchema,
|
|
100
|
+
})
|
|
101
|
+
.strict()),
|
|
102
|
+
return_address: OrderSummaryAddressSchema.nullable(),
|
|
103
|
+
})
|
|
104
|
+
.strict();
|
|
105
|
+
/**
|
|
106
|
+
* Snapshot of a return when it was completely received: the return and
|
|
107
|
+
* order numbers, when it arrived and the received items (title of the order
|
|
108
|
+
* line, received quantity and how much of it arrived damaged).
|
|
109
|
+
*/
|
|
110
|
+
exports.ReturnReceivedSummarySchema = zod_1.z
|
|
111
|
+
.object({
|
|
112
|
+
display_id: zod_1.z.number().int().nullable(),
|
|
113
|
+
order_display_id: zod_1.z.number().int().nullable(),
|
|
114
|
+
received_at: zod_1.z.string().datetime().nullable(),
|
|
115
|
+
items: zod_1.z.array(zod_1.z
|
|
116
|
+
.object({
|
|
117
|
+
title: NonEmptyStringSchema,
|
|
118
|
+
variant_title: OptionalTextSchema,
|
|
119
|
+
quantity: zod_1.z.number().positive(),
|
|
120
|
+
damaged_quantity: zod_1.z.number().nonnegative(),
|
|
121
|
+
})
|
|
122
|
+
.strict()),
|
|
123
|
+
})
|
|
124
|
+
.strict();
|
|
125
|
+
/**
|
|
126
|
+
* Snapshot of one refund when its Documents fact arrived: the order number,
|
|
127
|
+
* the refunded gross amount, when it was refunded and the refund reason
|
|
128
|
+
* chosen in the Admin.
|
|
129
|
+
*/
|
|
130
|
+
exports.RefundSummarySchema = zod_1.z
|
|
131
|
+
.object({
|
|
132
|
+
order_display_id: zod_1.z.number().int().nullable(),
|
|
133
|
+
amount: AmountSchema,
|
|
134
|
+
currency_code: NonEmptyStringSchema,
|
|
135
|
+
refunded_at: zod_1.z.string().datetime().nullable(),
|
|
136
|
+
reason: OptionalTextSchema,
|
|
137
|
+
})
|
|
138
|
+
.strict();
|
|
12
139
|
exports.EmailPersistedDataSchemas = {
|
|
13
140
|
"order.received": zod_1.z
|
|
14
141
|
.object({
|
|
15
142
|
order_id: NonEmptyStringSchema,
|
|
143
|
+
// Optional: messages stored before the summary existed have none.
|
|
144
|
+
summary: exports.OrderSummarySchema.optional(),
|
|
16
145
|
})
|
|
17
146
|
.strict(),
|
|
18
147
|
"order.received.admin": zod_1.z
|
|
19
148
|
.object({
|
|
20
149
|
order_id: NonEmptyStringSchema,
|
|
150
|
+
// Optional: messages stored before the summary existed have none.
|
|
151
|
+
summary: exports.OrderSummarySchema.optional(),
|
|
21
152
|
})
|
|
22
153
|
.strict(),
|
|
23
154
|
"order.shipped": zod_1.z
|
|
@@ -25,24 +156,38 @@ exports.EmailPersistedDataSchemas = {
|
|
|
25
156
|
order_id: NonEmptyStringSchema,
|
|
26
157
|
fulfillment_id: NonEmptyStringSchema,
|
|
27
158
|
documents_fact_fingerprint: DocumentsFactFingerprintSchema,
|
|
159
|
+
// Optional: messages stored before the snapshot existed have none.
|
|
160
|
+
shipment: exports.ShipmentSummarySchema.optional(),
|
|
161
|
+
/** The invoice PDF is attached (Documents chose this shipment). */
|
|
162
|
+
invoice_attached: zod_1.z.boolean().optional(),
|
|
28
163
|
})
|
|
29
164
|
.strict(),
|
|
30
165
|
"order.canceled": zod_1.z
|
|
31
166
|
.object({
|
|
32
167
|
order_id: NonEmptyStringSchema,
|
|
33
168
|
documents_fact_fingerprint: DocumentsFactFingerprintSchema,
|
|
169
|
+
// Optional: messages stored before the snapshot existed have none.
|
|
170
|
+
summary: exports.OrderSummarySchema.optional(),
|
|
171
|
+
/** When the order was canceled (ISO 8601). */
|
|
172
|
+
canceled_at: zod_1.z.string().datetime().optional(),
|
|
173
|
+
/** A cancellation document PDF is attached (from Documents). */
|
|
174
|
+
cancellation_attached: zod_1.z.boolean().optional(),
|
|
34
175
|
})
|
|
35
176
|
.strict(),
|
|
36
177
|
"order.return_requested": zod_1.z
|
|
37
178
|
.object({
|
|
38
179
|
order_id: NonEmptyStringSchema,
|
|
39
180
|
return_id: NonEmptyStringSchema,
|
|
181
|
+
// Optional: messages stored before the snapshot existed have none.
|
|
182
|
+
summary: exports.ReturnSummarySchema.optional(),
|
|
40
183
|
})
|
|
41
184
|
.strict(),
|
|
42
185
|
"order.return_received": zod_1.z
|
|
43
186
|
.object({
|
|
44
187
|
order_id: NonEmptyStringSchema,
|
|
45
188
|
return_id: NonEmptyStringSchema,
|
|
189
|
+
// Optional: messages stored before the snapshot existed have none.
|
|
190
|
+
summary: exports.ReturnReceivedSummarySchema.optional(),
|
|
46
191
|
})
|
|
47
192
|
.strict(),
|
|
48
193
|
"order.refunded": zod_1.z
|
|
@@ -50,6 +195,10 @@ exports.EmailPersistedDataSchemas = {
|
|
|
50
195
|
order_id: NonEmptyStringSchema,
|
|
51
196
|
refund_id: NonEmptyStringSchema,
|
|
52
197
|
documents_fact_fingerprint: DocumentsFactFingerprintSchema,
|
|
198
|
+
// Optional: messages stored before the snapshot existed have none.
|
|
199
|
+
summary: exports.RefundSummarySchema.optional(),
|
|
200
|
+
/** A cancellation document PDF is attached (from Documents). */
|
|
201
|
+
cancellation_attached: zod_1.z.boolean().optional(),
|
|
53
202
|
})
|
|
54
203
|
.strict(),
|
|
55
204
|
"auth.password_reset": zod_1.z
|
|
@@ -71,8 +220,12 @@ exports.EmailRenderDataSchemas = {
|
|
|
71
220
|
...exports.EmailPersistedDataSchemas,
|
|
72
221
|
"auth.password_reset": exports.EmailPersistedDataSchemas["auth.password_reset"].extend({
|
|
73
222
|
reset_url: zod_1.z.string().url(),
|
|
223
|
+
/** How long the reset link is valid, from the token; `undefined` if unknown. */
|
|
224
|
+
valid_minutes: zod_1.z.number().int().positive().optional(),
|
|
74
225
|
}),
|
|
75
226
|
"user.invite": exports.EmailPersistedDataSchemas["user.invite"].extend({
|
|
76
227
|
invite_url: zod_1.z.string().url(),
|
|
228
|
+
/** When the invite expires (ISO 8601), from the current invite. */
|
|
229
|
+
expires_at: zod_1.z.string().datetime().optional(),
|
|
77
230
|
}),
|
|
78
231
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { DocumentHtmlFactory } from "../../../documents/types/html-factory";
|
|
2
|
-
import type {
|
|
3
|
-
import { type DocumentMerchantValues } from "./merchant";
|
|
2
|
+
import type { DocumentPdfOptions } from "../../../documents/types/documents";
|
|
4
3
|
/** Used when the `documents.numbering.timeZone` option is not given. */
|
|
5
4
|
export declare const DEFAULT_NUMBERING_TIME_ZONE = "Europe/Vienna";
|
|
6
5
|
/** The `documents` namespace of the base plugin options. */
|
|
@@ -10,11 +9,6 @@ export type DocumentPluginOptions = {
|
|
|
10
9
|
* `createDefaultDocumentHtmlFactory` with the `numbering.timeZone`.
|
|
11
10
|
*/
|
|
12
11
|
htmlFactory?: DocumentHtmlFactory;
|
|
13
|
-
/**
|
|
14
|
-
* Fallback for the shop master data of the base plugin: used per field
|
|
15
|
-
* only where no value is stored there.
|
|
16
|
-
*/
|
|
17
|
-
merchant?: DocumentMerchantOption;
|
|
18
12
|
numbering?: {
|
|
19
13
|
timeZone?: string;
|
|
20
14
|
};
|
|
@@ -22,8 +16,6 @@ export type DocumentPluginOptions = {
|
|
|
22
16
|
};
|
|
23
17
|
export type ResolvedDocumentPluginOptions = {
|
|
24
18
|
htmlFactory: DocumentHtmlFactory;
|
|
25
|
-
/** The `merchant` option keyed by shop master data field. */
|
|
26
|
-
merchantFallback: DocumentMerchantValues;
|
|
27
19
|
numbering: {
|
|
28
20
|
timeZone: string;
|
|
29
21
|
};
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.resolveDocumentPluginOptions = exports.DEFAULT_NUMBERING_TIME_ZONE = void 0;
|
|
4
4
|
const utils_1 = require("@medusajs/framework/utils");
|
|
5
5
|
const html_factory_1 = require("../../../documents/templates/html-factory");
|
|
6
|
-
const merchant_1 = require("./merchant");
|
|
7
6
|
/** Used when the `documents.numbering.timeZone` option is not given. */
|
|
8
7
|
exports.DEFAULT_NUMBERING_TIME_ZONE = "Europe/Vienna";
|
|
9
8
|
const resolveDocumentPluginOptions = (options) => {
|
|
@@ -24,14 +23,9 @@ const resolveDocumentPluginOptions = (options) => {
|
|
|
24
23
|
typeof options.htmlFactory !== "function") {
|
|
25
24
|
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "The documents 'htmlFactory' option must be a function.");
|
|
26
25
|
}
|
|
27
|
-
if (options?.merchant !== undefined &&
|
|
28
|
-
(typeof options.merchant !== "object" || options.merchant === null)) {
|
|
29
|
-
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "The documents 'merchant' option must be an object.");
|
|
30
|
-
}
|
|
31
26
|
const executablePath = options?.pdf?.executablePath?.trim() || "google-chrome";
|
|
32
27
|
return {
|
|
33
28
|
htmlFactory: options?.htmlFactory ?? (0, html_factory_1.createDefaultDocumentHtmlFactory)({ timeZone }),
|
|
34
|
-
merchantFallback: (0, merchant_1.merchantOptionToFallback)(options?.merchant),
|
|
35
29
|
numbering: { timeZone },
|
|
36
30
|
pdf: { executablePath },
|
|
37
31
|
};
|
|
@@ -11,6 +11,7 @@ const SETTING_TYPES = new Set([
|
|
|
11
11
|
"boolean",
|
|
12
12
|
"secret",
|
|
13
13
|
"select",
|
|
14
|
+
"image",
|
|
14
15
|
]);
|
|
15
16
|
const ACTION_MODES = new Set(["sync", "background"]);
|
|
16
17
|
const ADMIN_PAGE_PREFIX = "/settings/hartl-services/";
|
|
@@ -33,6 +34,7 @@ const defaultMatchesType = (setting) => {
|
|
|
33
34
|
case "string":
|
|
34
35
|
return hasText(value);
|
|
35
36
|
case "url":
|
|
37
|
+
case "image":
|
|
36
38
|
return typeof value === "string" && isHttpUrl(value);
|
|
37
39
|
case "number":
|
|
38
40
|
return typeof value === "number" && Number.isFinite(value);
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { MedusaContainer } from "@medusajs/framework/types";
|
|
2
|
-
import type { PluginSettingsFallback } from "./types";
|
|
3
2
|
/**
|
|
4
3
|
* The base's own descriptor: the shop's master data (Firmenangaben), edited
|
|
5
4
|
* on the generic plugin page like any plugin's settings. It cannot be
|
|
6
5
|
* switched off and declares no features.
|
|
7
6
|
*/
|
|
8
|
-
export declare const shopMasterDataDescriptor: import("./types").PluginFeatureDescriptor<string, "legal_name" | "address_line1" | "address_line2" | "postal_code" | "city" | "
|
|
7
|
+
export declare const shopMasterDataDescriptor: import("./types").PluginFeatureDescriptor<string, "country_code" | "legal_name" | "address_line1" | "address_line2" | "postal_code" | "city" | "vat_number" | "email" | "phone" | "website" | "iban" | "bic" | "company_register_number" | "company_register_court", never>;
|
|
9
8
|
/** Keys of the shop master data settings. */
|
|
10
9
|
export type ShopMasterDataKey = "legal_name" | "address_line1" | "address_line2" | "postal_code" | "city" | "country_code" | "vat_number" | "email" | "phone" | "website" | "iban" | "bic" | "company_register_number" | "company_register_court";
|
|
11
10
|
/** Every key that has a value (stored > fallback). All values are strings. */
|
|
@@ -18,8 +17,8 @@ export type ResolvedShopMasterData = {
|
|
|
18
17
|
missing: ShopMasterDataKey[];
|
|
19
18
|
};
|
|
20
19
|
/**
|
|
21
|
-
* The shop's master data for server-side code
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* The shop's master data for server-side code, exactly as stored in the
|
|
21
|
+
* Admin: there is no config fallback. A missing base plugin fails with
|
|
22
|
+
* `UNEXPECTED_STATE`; read failures propagate.
|
|
24
23
|
*/
|
|
25
|
-
export declare function getShopMasterData(container: MedusaContainer
|
|
24
|
+
export declare function getShopMasterData(container: MedusaContainer): Promise<ResolvedShopMasterData>;
|
|
@@ -12,7 +12,7 @@ const settings_1 = require("./settings");
|
|
|
12
12
|
exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
13
13
|
plugin_key: "shop",
|
|
14
14
|
label: "Shop-Stammdaten",
|
|
15
|
-
description: "Firmenangaben des Shops für Rechnungen, Dokumente und Steuerprüfungen.",
|
|
15
|
+
description: "Firmenangaben des Shops für E-Mails, Rechnungen, Dokumente und Steuerprüfungen.",
|
|
16
16
|
switchable: false,
|
|
17
17
|
features: [],
|
|
18
18
|
settings: [
|
|
@@ -74,12 +74,12 @@ exports.shopMasterDataDescriptor = (0, descriptor_1.definePluginFeatures)({
|
|
|
74
74
|
],
|
|
75
75
|
});
|
|
76
76
|
/**
|
|
77
|
-
* The shop's master data for server-side code
|
|
78
|
-
*
|
|
79
|
-
*
|
|
77
|
+
* The shop's master data for server-side code, exactly as stored in the
|
|
78
|
+
* Admin: there is no config fallback. A missing base plugin fails with
|
|
79
|
+
* `UNEXPECTED_STATE`; read failures propagate.
|
|
80
80
|
*/
|
|
81
|
-
async function getShopMasterData(container
|
|
82
|
-
const resolved = await (0, settings_1.getPluginSettings)(container, exports.shopMasterDataDescriptor
|
|
81
|
+
async function getShopMasterData(container) {
|
|
82
|
+
const resolved = await (0, settings_1.getPluginSettings)(container, exports.shopMasterDataDescriptor);
|
|
83
83
|
const values = {};
|
|
84
84
|
for (const [key, value] of Object.entries(resolved.values)) {
|
|
85
85
|
if (typeof value === "string") {
|
package/dist/public/types.d.ts
CHANGED
|
@@ -30,7 +30,8 @@ export type PluginFeatureDefinition<K extends string = string> = {
|
|
|
30
30
|
readonly default_enabled: boolean;
|
|
31
31
|
readonly parent_key?: K;
|
|
32
32
|
};
|
|
33
|
-
|
|
33
|
+
/** `image`: public http(s) URL of an image the Admin uploads (PNG/JPEG). */
|
|
34
|
+
export type PluginSettingType = "string" | "url" | "number" | "boolean" | "secret" | "select" | "image";
|
|
34
35
|
/** One allowed value of a `"select"` setting. */
|
|
35
36
|
export type PluginSettingOption = {
|
|
36
37
|
readonly value: string;
|
|
@@ -18,7 +18,6 @@ const options_1 = require("./options");
|
|
|
18
18
|
*/
|
|
19
19
|
function buildFoodShopConfig(options, environment = process.env) {
|
|
20
20
|
(0, options_1.assertFoodShopConfigOptions)(options);
|
|
21
|
-
const { merchant } = options;
|
|
22
21
|
if (options.loadEnvFile !== false) {
|
|
23
22
|
(0, utils_1.loadEnv)(process.env.NODE_ENV || "development", process.cwd());
|
|
24
23
|
}
|
|
@@ -144,6 +143,8 @@ function buildFoodShopConfig(options, environment = process.env) {
|
|
|
144
143
|
email: {
|
|
145
144
|
jwtSecret: env.JWT_SECRET,
|
|
146
145
|
shop: {
|
|
146
|
+
// The Admin dashboard mounts under admin.path ("/app").
|
|
147
|
+
adminOrderUrl: ({ orderId }) => new URL(`/app/orders/${encodeURIComponent(orderId)}`, backendOrigin).toString(),
|
|
147
148
|
displayName: options.displayName,
|
|
148
149
|
affiliatePortalUrl: (0, config_1.shopUrl)(storefrontOrigin, `${storefrontPath}/partnerportal`),
|
|
149
150
|
// The Admin dashboard mounts "/invite" under admin.path ("/app").
|
|
@@ -155,12 +156,11 @@ function buildFoodShopConfig(options, environment = process.env) {
|
|
|
155
156
|
orderNotificationEmail: options.orderNotificationEmail,
|
|
156
157
|
// The e-mail module appends "#token=<token>".
|
|
157
158
|
passwordResetUrl: () => (0, config_1.shopUrl)(storefrontOrigin, `${storefrontPath}/reset-password`),
|
|
159
|
+
timeZone: options.timeZone,
|
|
158
160
|
},
|
|
159
161
|
},
|
|
160
162
|
documents: {
|
|
161
|
-
|
|
162
|
-
// Plugins" -> Shop-Stammdaten) wins per field.
|
|
163
|
-
merchant,
|
|
163
|
+
htmlFactory: options.documentHtmlFactory,
|
|
164
164
|
numbering: {
|
|
165
165
|
// Chooses the annual document-number period; matches the date the
|
|
166
166
|
// HTML factory prints.
|
|
@@ -175,7 +175,7 @@ function buildFoodShopConfig(options, environment = process.env) {
|
|
|
175
175
|
},
|
|
176
176
|
{
|
|
177
177
|
resolve: "@hartl-services/medusa-tax-compliance",
|
|
178
|
-
options: {
|
|
178
|
+
options: {},
|
|
179
179
|
},
|
|
180
180
|
{
|
|
181
181
|
resolve: "@hartl-services/medusa-free-shipping",
|
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DocumentHtmlFactory } from "../documents/types";
|
|
2
2
|
/**
|
|
3
3
|
* The shop-specific facts of a food shop. Deployment values and secrets are
|
|
4
4
|
* not options: they come from the environment (see the README).
|
|
5
5
|
*/
|
|
6
6
|
export type FoodShopConfigOptions = Readonly<{
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Shop name in the branding of the default e-mail templates (and the store
|
|
9
|
+
* name of a shop's initial seed). Company name, address and UID are not
|
|
10
|
+
* options: they come only from the Shop-Stammdaten in the Admin.
|
|
11
11
|
*/
|
|
12
|
-
merchant: DocumentMerchantOption;
|
|
13
|
-
/** Shop name in the branding of the default e-mail templates. */
|
|
14
12
|
displayName: string;
|
|
15
13
|
/** Receives the shop's order notification e-mails. */
|
|
16
14
|
orderNotificationEmail: string;
|
|
17
15
|
/**
|
|
18
16
|
* IANA time zone (e.g. "Europe/Vienna"): chooses the annual document-number
|
|
19
|
-
* period, matches the dates printed on documents
|
|
20
|
-
* affiliate consignment reminders.
|
|
17
|
+
* period, matches the dates printed on documents and e-mails, and
|
|
18
|
+
* schedules the affiliate consignment reminders.
|
|
21
19
|
*/
|
|
22
20
|
timeZone: string;
|
|
23
21
|
/**
|
|
@@ -26,6 +24,13 @@ export type FoodShopConfigOptions = Readonly<{
|
|
|
26
24
|
* affiliate referral links to `/<code>`.
|
|
27
25
|
*/
|
|
28
26
|
storefrontCountryCode: string;
|
|
27
|
+
/**
|
|
28
|
+
* Replaces the default document markup (documents `htmlFactory`), e.g.
|
|
29
|
+
* `createDefaultDocumentHtmlFactory({ timeZone, theme })` from
|
|
30
|
+
* `@hartl-services/medusa-base/config` with the shop's own CSS and
|
|
31
|
+
* watermark. Omit it for the plugin's default look.
|
|
32
|
+
*/
|
|
33
|
+
documentHtmlFactory?: DocumentHtmlFactory;
|
|
29
34
|
/**
|
|
30
35
|
* Load `.env.<NODE_ENV>` from the working directory into `process.env`
|
|
31
36
|
* like `medusa-config.ts` does by default. Pass `false` when the
|
|
@@ -45,24 +45,14 @@ const requireObject = (value, name) => {
|
|
|
45
45
|
*/
|
|
46
46
|
function assertFoodShopConfigOptions(options) {
|
|
47
47
|
const candidate = requireObject(options, "options");
|
|
48
|
-
const merchant = requireObject(candidate.merchant, "merchant");
|
|
49
|
-
requireText(merchant.legalName, "merchant.legalName");
|
|
50
|
-
requireCountryCode(merchant.countryCode, "merchant.countryCode");
|
|
51
|
-
if (merchant.vatNumber !== undefined) {
|
|
52
|
-
requireText(merchant.vatNumber, "merchant.vatNumber");
|
|
53
|
-
}
|
|
54
|
-
const address = requireObject(merchant.registeredAddress, "merchant.registeredAddress");
|
|
55
|
-
requireText(address.address1, "merchant.registeredAddress.address1");
|
|
56
|
-
if (address.address2 !== undefined) {
|
|
57
|
-
requireText(address.address2, "merchant.registeredAddress.address2");
|
|
58
|
-
}
|
|
59
|
-
requireText(address.city, "merchant.registeredAddress.city");
|
|
60
|
-
requireText(address.postalCode, "merchant.registeredAddress.postalCode");
|
|
61
|
-
requireCountryCode(address.countryCode, "merchant.registeredAddress.countryCode");
|
|
62
48
|
requireText(candidate.displayName, "displayName");
|
|
63
49
|
requireEmail(candidate.orderNotificationEmail, "orderNotificationEmail");
|
|
64
50
|
requireTimeZone(candidate.timeZone, "timeZone");
|
|
65
51
|
requireCountryCode(candidate.storefrontCountryCode, "storefrontCountryCode");
|
|
52
|
+
if (candidate.documentHtmlFactory !== undefined &&
|
|
53
|
+
typeof candidate.documentHtmlFactory !== "function") {
|
|
54
|
+
fail("documentHtmlFactory", "a function");
|
|
55
|
+
}
|
|
66
56
|
if (candidate.loadEnvFile !== undefined &&
|
|
67
57
|
typeof candidate.loadEnvFile !== "boolean") {
|
|
68
58
|
fail("loadEnvFile", "a boolean");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hartl-services/medusa-base",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Base plugin for Hartl Services Medusa shops: per-plugin feature toggles with a generic Admin settings page, a Store endpoint for storefronts, enforcement helpers, the complete-cart hook composition, the root redirect, cart-promotion link repair, the built-in E-Mail, Documents and S3 file storage modules, config helpers with the turnkey food-shop preset, and the Sentry logger wrapper and instrumentation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"mime-types": "3.0.1",
|
|
89
89
|
"nodemailer": "6.10.1",
|
|
90
90
|
"ulid": "^2.4.0",
|
|
91
|
-
"@hartl-services/medusa-food-supplements-contracts": "^0.
|
|
91
|
+
"@hartl-services/medusa-food-supplements-contracts": "^0.17.0"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
94
|
"@medusajs/admin-sdk": "2.19.0",
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { DocumentMerchant, DocumentMerchantOption } from "../../../documents/types/documents";
|
|
2
|
-
/** Shop master data keys (base plugin, `plugin_key: "shop"`) documents read. */
|
|
3
|
-
export type DocumentMerchantKey = keyof DocumentMerchant;
|
|
4
|
-
export type DocumentMerchantValues = Partial<Record<DocumentMerchantKey, string>>;
|
|
5
|
-
/** Without these a financial document or packing list has no legal sender. */
|
|
6
|
-
export declare const DOCUMENT_MERCHANT_REQUIRED_FIELDS: readonly ["legal_name", "address_line1", "postal_code", "city", "country_code"];
|
|
7
|
-
/** Development placeholder UID: never printed on a document. */
|
|
8
|
-
export declare const MERCHANT_VAT_PLACEHOLDER = "ATU00000000";
|
|
9
|
-
/**
|
|
10
|
-
* The plugin's `merchant` option as a per-field fallback for the shop master
|
|
11
|
-
* data. Empty values are left out so they never shadow a declared default.
|
|
12
|
-
*/
|
|
13
|
-
export declare const merchantOptionToFallback: (option: DocumentMerchantOption | undefined) => DocumentMerchantValues;
|
|
14
|
-
export type DocumentMerchantResolution = {
|
|
15
|
-
merchant: DocumentMerchant;
|
|
16
|
-
missing: [];
|
|
17
|
-
} | {
|
|
18
|
-
merchant: null;
|
|
19
|
-
missing: DocumentMerchantKey[];
|
|
20
|
-
};
|
|
21
|
-
/** Maps resolved shop master data to the render input's `merchant`. */
|
|
22
|
-
export declare const toDocumentMerchant: (values: DocumentMerchantValues) => DocumentMerchantResolution;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toDocumentMerchant = exports.merchantOptionToFallback = exports.MERCHANT_VAT_PLACEHOLDER = exports.DOCUMENT_MERCHANT_REQUIRED_FIELDS = void 0;
|
|
4
|
-
/** Without these a financial document or packing list has no legal sender. */
|
|
5
|
-
exports.DOCUMENT_MERCHANT_REQUIRED_FIELDS = [
|
|
6
|
-
"legal_name",
|
|
7
|
-
"address_line1",
|
|
8
|
-
"postal_code",
|
|
9
|
-
"city",
|
|
10
|
-
"country_code",
|
|
11
|
-
];
|
|
12
|
-
/** Development placeholder UID: never printed on a document. */
|
|
13
|
-
exports.MERCHANT_VAT_PLACEHOLDER = "ATU00000000";
|
|
14
|
-
const text = (value) => {
|
|
15
|
-
if (typeof value !== "string") {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
const trimmed = value.trim();
|
|
19
|
-
return trimmed ? trimmed : null;
|
|
20
|
-
};
|
|
21
|
-
const vatNumber = (value) => {
|
|
22
|
-
const normalized = text(value);
|
|
23
|
-
return normalized &&
|
|
24
|
-
normalized.replace(/\s+/g, "").toUpperCase() === exports.MERCHANT_VAT_PLACEHOLDER
|
|
25
|
-
? null
|
|
26
|
-
: normalized;
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* The plugin's `merchant` option as a per-field fallback for the shop master
|
|
30
|
-
* data. Empty values are left out so they never shadow a declared default.
|
|
31
|
-
*/
|
|
32
|
-
const merchantOptionToFallback = (option) => {
|
|
33
|
-
if (!option) {
|
|
34
|
-
return {};
|
|
35
|
-
}
|
|
36
|
-
const address = option.registeredAddress;
|
|
37
|
-
const entries = [
|
|
38
|
-
["legal_name", option.legalName],
|
|
39
|
-
["address_line1", address?.address1],
|
|
40
|
-
["address_line2", address?.address2],
|
|
41
|
-
["postal_code", address?.postalCode],
|
|
42
|
-
["city", address?.city],
|
|
43
|
-
[
|
|
44
|
-
"country_code",
|
|
45
|
-
text(address?.countryCode ?? option.countryCode)?.toLowerCase(),
|
|
46
|
-
],
|
|
47
|
-
["vat_number", option.vatNumber],
|
|
48
|
-
];
|
|
49
|
-
const fallback = {};
|
|
50
|
-
for (const [key, value] of entries) {
|
|
51
|
-
const normalized = text(value);
|
|
52
|
-
if (normalized) {
|
|
53
|
-
fallback[key] = normalized;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return fallback;
|
|
57
|
-
};
|
|
58
|
-
exports.merchantOptionToFallback = merchantOptionToFallback;
|
|
59
|
-
/** Maps resolved shop master data to the render input's `merchant`. */
|
|
60
|
-
const toDocumentMerchant = (values) => {
|
|
61
|
-
const missing = exports.DOCUMENT_MERCHANT_REQUIRED_FIELDS.filter((key) => !text(values[key]));
|
|
62
|
-
if (missing.length > 0) {
|
|
63
|
-
return { merchant: null, missing };
|
|
64
|
-
}
|
|
65
|
-
return {
|
|
66
|
-
merchant: {
|
|
67
|
-
legal_name: text(values.legal_name),
|
|
68
|
-
address_line1: text(values.address_line1),
|
|
69
|
-
address_line2: text(values.address_line2),
|
|
70
|
-
postal_code: text(values.postal_code),
|
|
71
|
-
city: text(values.city),
|
|
72
|
-
country_code: text(values.country_code).toLowerCase(),
|
|
73
|
-
vat_number: vatNumber(values.vat_number),
|
|
74
|
-
email: text(values.email),
|
|
75
|
-
phone: text(values.phone),
|
|
76
|
-
website: text(values.website),
|
|
77
|
-
iban: text(values.iban),
|
|
78
|
-
bic: text(values.bic),
|
|
79
|
-
company_register_number: text(values.company_register_number),
|
|
80
|
-
company_register_court: text(values.company_register_court),
|
|
81
|
-
},
|
|
82
|
-
missing: [],
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
exports.toDocumentMerchant = toDocumentMerchant;
|