@hartl-services/medusa-base 2.10.0 → 3.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 +44 -40
- package/.medusa/server/src/admin/index.mjs +44 -40
- package/.medusa/server/src/api/admin/tax-compliance/customer-addresses/[address_id]/tax-identification/route.js +15 -0
- package/.medusa/server/src/api/check-customer-address-tax-identification.js +93 -0
- package/.medusa/server/src/api/customer-address-identification-response.js +14 -0
- package/.medusa/server/src/api/middlewares.js +55 -1
- package/.medusa/server/src/api/store/carts/[id]/tax-identification/route.js +179 -0
- package/.medusa/server/src/api/store/customers/me/addresses/[address_id]/tax-identification/route.js +12 -0
- package/.medusa/server/src/api/store/customers/me/tax-identifications/route.js +14 -0
- package/.medusa/server/src/api/store/tax-identification-policy/route.js +21 -0
- package/.medusa/server/src/api/tax-identification-message.js +9 -0
- package/.medusa/server/src/api/to-response-amount.js +19 -0
- package/.medusa/server/src/email/application/deliver-ephemeral-auth-mail.js +2 -4
- package/.medusa/server/src/email/application/handle-password-reset-event.js +75 -27
- package/.medusa/server/src/email/rendering/default-templates.js +2 -1
- package/.medusa/server/src/email/rendering/render-email.js +2 -1
- package/.medusa/server/src/email/rendering/resolve-shop-configuration.js +5 -1
- package/.medusa/server/src/email/templates/blocks/dynamic-blocks.js +5 -2
- package/.medusa/server/src/email/templates/defaults/documents.js +8 -1
- package/.medusa/server/src/email/templates/defaults/subjects.js +3 -1
- package/.medusa/server/src/email/templates/defaults/texts.js +4 -2
- package/.medusa/server/src/email/templates/render.js +2 -1
- package/.medusa/server/src/email/templates/sample-data.js +5 -1
- package/.medusa/server/src/email/types/email-kinds.js +3 -1
- package/.medusa/server/src/email/types/template-data.js +8 -1
- package/.medusa/server/src/jobs/tax-compliance-state-sync.js +26 -0
- package/.medusa/server/src/modules/email/migrations/Migration20261013120000.js +20 -0
- package/.medusa/server/src/modules/tax-compliance/domain/validation-policy.js +48 -0
- package/.medusa/server/src/modules/tax-compliance/index.js +15 -0
- package/.medusa/server/src/modules/tax-compliance/migrations/Migration20260814120459.js +18 -0
- package/.medusa/server/src/modules/tax-compliance/migrations/Migration20260814121724.js +14 -0
- package/.medusa/server/src/modules/tax-compliance/migrations/Migration20260814153410.js +14 -0
- package/.medusa/server/src/modules/tax-compliance/migrations/Migration20260929091620.js +27 -0
- package/.medusa/server/src/modules/tax-compliance/migrations/Migration20261011090147.js +16 -0
- package/.medusa/server/src/modules/tax-compliance/models/tax-compliance-state.js +29 -0
- package/.medusa/server/src/modules/tax-compliance/models/tax-compliance-validation.js +74 -0
- package/.medusa/server/src/modules/tax-compliance/runtime-settings.js +43 -0
- package/.medusa/server/src/modules/tax-compliance/service.js +693 -0
- package/.medusa/server/src/providers/tax-compliance/index.js +11 -0
- package/.medusa/server/src/providers/tax-compliance/service.js +179 -0
- package/.medusa/server/src/public/constants.js +4 -2
- package/.medusa/server/src/public/index.js +6 -2
- package/.medusa/server/src/public/tax-compliance.js +22 -0
- package/.medusa/server/src/shared/lib/index.js +2 -1
- package/.medusa/server/src/shared/lib/optional-registration.js +34 -0
- package/.medusa/server/src/shop-config/build-food-shop-config.js +12 -9
- package/.medusa/server/src/subscribers/tax-compliance-plugin-features-updated.js +25 -0
- package/.medusa/server/src/tax-compliance/complete-cart-hooks.js +26 -0
- package/.medusa/server/src/tax-compliance/integrations/finanzonline/client.js +222 -0
- package/.medusa/server/src/tax-compliance/integrations/finanzonline/validator.js +50 -0
- package/.medusa/server/src/tax-compliance/integrations/registry.js +26 -0
- package/.medusa/server/src/tax-compliance/integrations/validator.js +19 -0
- package/.medusa/server/src/tax-compliance/lib/order-tax-context.js +100 -0
- package/.medusa/server/src/tax-compliance/lib/tax-compliance-state.js +59 -0
- package/.medusa/server/src/tax-compliance/lib/tax-runtime-settings.js +79 -0
- package/.medusa/server/src/tax-compliance/plugin-features.js +73 -0
- package/.medusa/server/src/tax-compliance/resolve-services.js +7 -0
- package/.medusa/server/src/tax-compliance/types/tax-compliance.js +6 -0
- package/.medusa/server/src/workflows/documents/issue-invoice-after-shipment.js +25 -30
- package/.medusa/server/src/workflows/documents/issue-packing-list.js +3 -14
- package/.medusa/server/src/workflows/hooks/cart-tax-context.js +78 -0
- package/.medusa/server/src/workflows/hooks/order-tax-context.js +7 -0
- package/.medusa/server/src/workflows/hooks/shipping-tax-apportionment.js +145 -0
- package/.medusa/server/src/workflows/tax-compliance/apportion-order-shipping-tax.js +72 -0
- package/.medusa/server/src/workflows/tax-compliance/domain/requires-frozen-tax-evidence.js +27 -0
- package/.medusa/server/src/workflows/tax-compliance/freeze-order-evidence.js +125 -0
- package/.medusa/server/src/workflows/tax-compliance/submit-customer-address-tax-identification.js +18 -0
- package/.medusa/server/src/workflows/tax-compliance/submit-tax-identification.js +18 -0
- package/README.md +369 -27
- package/dist/email/types/email-kinds.d.ts +3 -2
- package/dist/email/types/email-kinds.js +2 -0
- package/dist/email/types/shop-configuration.d.ts +8 -0
- package/dist/email/types/template-data.d.ts +6 -1
- package/dist/email/types/template-data.js +7 -0
- package/dist/public/constants.d.ts +2 -0
- package/dist/public/constants.js +3 -1
- package/dist/public/index.d.ts +2 -1
- package/dist/public/index.js +5 -1
- package/dist/public/tax-compliance.d.ts +30 -0
- package/dist/public/tax-compliance.js +21 -0
- package/dist/shared/lib/index.d.ts +1 -0
- package/dist/shared/lib/index.js +1 -0
- package/dist/shared/lib/optional-registration.d.ts +18 -0
- package/dist/shared/lib/optional-registration.js +33 -0
- package/dist/shop-config/build-food-shop-config.d.ts +2 -2
- package/dist/shop-config/build-food-shop-config.js +11 -8
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -6,8 +6,10 @@ Admin hub under **Settings → Hartl Services Plugins** with one page per
|
|
|
6
6
|
plugin, a Store endpoint for storefronts and helpers that plugins use to
|
|
7
7
|
enforce their toggles and read their settings. Since 1.0.0 it also contains
|
|
8
8
|
the shop's transactional E-Mail, order Documents (invoices, cancellations,
|
|
9
|
-
packing lists) and S3 file storage, and the turnkey `medusa-config.ts` preset
|
|
10
|
-
(
|
|
9
|
+
packing lists) and S3 file storage, and the turnkey `medusa-config.ts` preset,
|
|
10
|
+
and since 3.0.0 the EU tax compliance (UID check, Tax provider) (see "E-Mail",
|
|
11
|
+
"Dokumente", "Steuer-Compliance", "Dateispeicher (S3)" and
|
|
12
|
+
"Shop-Config-Preset").
|
|
11
13
|
|
|
12
14
|
## What it does
|
|
13
15
|
|
|
@@ -54,6 +56,10 @@ packing lists) and S3 file storage, and the turnkey `medusa-config.ts` preset
|
|
|
54
56
|
"E-Mail"), issues invoices, cancellation documents and packing lists as
|
|
55
57
|
private PDFs (module `documents`, see "Dokumente") and stores files in
|
|
56
58
|
S3-compatible storage behind the `/files` gateway (see "Dateispeicher (S3)").
|
|
59
|
+
- Checks B2B customers' VAT IDs (FinanzOnline), provides the Medusa Tax
|
|
60
|
+
provider `tp_tax_compliance` for reverse-charge and apportioned shipping tax
|
|
61
|
+
and freezes the tax evidence of an order at checkout (module
|
|
62
|
+
`tax_compliance`, see "Steuer-Compliance").
|
|
57
63
|
- Declares which login methods customers may use (module `customer_auth`:
|
|
58
64
|
e-mail + password and a one-time code by e-mail), provides the `email-otp`
|
|
59
65
|
auth provider and the code request route (see "Kunden-Login").
|
|
@@ -116,6 +122,17 @@ exports from the matching `@hartl-services/medusa-base` subpath. The
|
|
|
116
122
|
`documentsEnabled` option is gone; Documents is always installed, and the
|
|
117
123
|
e-mail module follows the Documents `invoices` toggle.
|
|
118
124
|
|
|
125
|
+
Upgrading to 3.0.0 (Steuer-Compliance merged): remove
|
|
126
|
+
`@hartl-services/medusa-tax-compliance` from `package.json` and from the
|
|
127
|
+
`plugins` list and change the Tax provider resolve path to
|
|
128
|
+
`@hartl-services/medusa-base/providers/tax-compliance` (the provider id
|
|
129
|
+
`tp_tax_compliance`, the module key `tax_compliance`, its tables and
|
|
130
|
+
migrations are unchanged, so no data migration is needed). Plugins that
|
|
131
|
+
reached `tax_compliance` by container key keep working; the typed helper is
|
|
132
|
+
`listCustomerAddressTaxIdentifications` (see "Steuer-Compliance"). The other
|
|
133
|
+
Hartl Services plugins need the versions released with 3.0.0 (their peer
|
|
134
|
+
range is `^3`).
|
|
135
|
+
|
|
119
136
|
Peer dependencies are the host's Medusa 2.19 packages (`@medusajs/framework`,
|
|
120
137
|
`@medusajs/medusa`, `@medusajs/admin-sdk`, `@medusajs/js-sdk`, `@medusajs/ui`, `@medusajs/icons`)
|
|
121
138
|
plus React, React Router and TanStack Query in the versions the Medusa 2.19
|
|
@@ -927,7 +944,7 @@ class MyModuleService extends MedusaService({ /* … */ }) {
|
|
|
927
944
|
|
|
928
945
|
**Ordering.** Ascending `order`, then module key, then workflow id. In use:
|
|
929
946
|
Affiliate 100 (`affiliate-validate-complete-cart`,
|
|
930
|
-
`finalize-affiliate-order-attribution`),
|
|
947
|
+
`finalize-affiliate-order-attribution`), the base's Steuer-Compliance 200
|
|
931
948
|
(`freeze-tax-compliance-order-evidence`, `orderCreated` only).
|
|
932
949
|
|
|
933
950
|
**Rollback semantics.**
|
|
@@ -1436,6 +1453,11 @@ Options of the `email` namespace:
|
|
|
1436
1453
|
`orderNotificationEmail` mailbox. `verifyAccountUrl` (optional builder,
|
|
1437
1454
|
like `passwordResetUrl`; the mail appends `#token=`) is the storefront page
|
|
1438
1455
|
that confirms an account, needed while `customer_verification` is on.
|
|
1456
|
+
`adminPasswordResetUrl` (optional builder, receives `{ token }`) is the
|
|
1457
|
+
Medusa Admin's "choose a new password" page; without it an Admin user's
|
|
1458
|
+
password reset fails with `render_failed`. `buildFoodShopConfig` sets
|
|
1459
|
+
`<MEDUSA_BACKEND_URL>/app/reset-password?token=<token>` (the dashboard reads
|
|
1460
|
+
the token from the query).
|
|
1439
1461
|
`adminAffiliateApplicationsUrl` (optional builder, receives
|
|
1440
1462
|
`{ applicationId }`) is the Admin page of the partner applications; the
|
|
1441
1463
|
notification about a new application shows its "Im Admin prüfen" button
|
|
@@ -1539,7 +1561,7 @@ feature is switched on again.
|
|
|
1539
1561
|
| --------------------- | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- |
|
|
1540
1562
|
| (plugin switch) | E-Mail | on | every route under `/admin/email/*` (mail log, resends, attachments; 404); jobs `email-reconcile-attempts`, `email-reconcile-pending-messages`, `email-retry-messages`; every feature below | checkout and orders; already queued messages stay stored |
|
|
1541
1563
|
| `order_mails` | Bestell-Mails | on | subscribers `order-placed`, `email-shipment-created`, `email-order-canceled`, `return-requested`, `return-received`, `documents-shipment-invoice-assignment-decided`, `documents-order-cancellation-ready`, `documents-refund-ready`; route `/admin/draft-orders/:id/order-confirmation` (404) and its draft-order widget | jobs, Admin routes, already created messages |
|
|
1542
|
-
| `account_mails` | Konto-Mails | on | subscribers `password-reset
|
|
1564
|
+
| `account_mails` | Konto-Mails | on | subscribers `password-reset` (customers and Admin users), `user-invite`, `customer-verification` (the last one only with `customer_verification` on as well); not the login code mail, which depends on neither this feature nor the plugin switch (see "Kunden-Login") | jobs, Admin routes, already created messages |
|
|
1543
1565
|
| `customer_verification` | Kontobestätigung per E-Mail | off | Medusa's e-mail verification of customer logins (see "Kontobestätigung per E-Mail"), route `POST /store/customer-verification/resend` (404 while off), subscriber `customer-verification`; child of `account_mails` | checkout and orders; confirmed accounts stay confirmed, already created messages |
|
|
1544
1566
|
| `affiliate_reminders` | Affiliate-Mails | on | subscribers `affiliate-consignment-reminder-due` (reminders to affiliates) and `affiliate-application-submitted` (notification to the shop about a new partner application) | jobs, Admin routes, already created messages |
|
|
1545
1567
|
|
|
@@ -1826,9 +1848,10 @@ has at most 35 characters. Email runtime-validates every contract before use.
|
|
|
1826
1848
|
|
|
1827
1849
|
- The accepted kinds are exactly `order.received`, `order.received.admin`, `order.shipped`,
|
|
1828
1850
|
`order.canceled`, `order.return_requested`, `order.return_received`,
|
|
1829
|
-
`order.refunded`, `auth.password_reset`, `auth.
|
|
1830
|
-
`auth.login_code`, `affiliate.consignment_report_due`,
|
|
1831
|
-
`affiliate.application_submitted.admin
|
|
1851
|
+
`order.refunded`, `auth.password_reset`, `auth.admin_password_reset`,
|
|
1852
|
+
`auth.verification`, `auth.login_code`, `affiliate.consignment_report_due`,
|
|
1853
|
+
`affiliate.application_submitted.admin`, `user.invite` and
|
|
1854
|
+
`system.credential_expiry.admin`.
|
|
1832
1855
|
- Every automatic message has a deterministic business key.
|
|
1833
1856
|
- Every placed Order creates independent customer and Shop intents. The customer
|
|
1834
1857
|
intent honors `no_notification`; the Shop intent always targets the static
|
|
@@ -1914,7 +1937,7 @@ an info box lists number, customer, e-mail and order time, followed by an
|
|
|
1914
1937
|
button is left out without it) and `timeZone` (IANA; the order time is left
|
|
1915
1938
|
out without it). `buildFoodShopConfig` sets both (`<MEDUSA_BACKEND_URL>/app/
|
|
1916
1939
|
orders/<id>` and its `timeZone` option). The layout receives `audience`
|
|
1917
|
-
(`"shop"` for this kind, `affiliate.application_submitted.admin` and `
|
|
1940
|
+
(`"shop"` for this kind, `affiliate.application_submitted.admin`, `system.credential_expiry.admin`, `user.invite` and `auth.admin_password_reset`, `"customer"` for all others) and
|
|
1918
1941
|
shows the contact line only to customers.
|
|
1919
1942
|
|
|
1920
1943
|
The shipping confirmation (`order.shipped`, one per fulfillment) receives a
|
|
@@ -1978,6 +2001,20 @@ without `iat`) and a note that the mail can be ignored. The lifetime is
|
|
|
1978
2001
|
passed as the ephemeral `valid_minutes` next to `reset_url` and is never
|
|
1979
2002
|
persisted.
|
|
1980
2003
|
|
|
2004
|
+
The same Medusa event with `actor_type: "user"` (the Admin's "Passwort
|
|
2005
|
+
zurücksetzen" at `/app/reset-password`) becomes its own kind,
|
|
2006
|
+
`auth.admin_password_reset`, with its own Admin template: subject "Neues
|
|
2007
|
+
Passwort für den Admin-Bereich von <displayName>", the same button, link text,
|
|
2008
|
+
lifetime and ignore note, and the link from `adminPasswordResetUrl` (the token
|
|
2009
|
+
in the query). It is sent only when exactly one Admin user has the address:
|
|
2010
|
+
the `emailpass` identity is shared by all actor types, so a customer could ask
|
|
2011
|
+
for an Admin reset of their own identity, and that request is ignored
|
|
2012
|
+
(`no_admin_user`). A token whose `actor_type` differs from the event's is
|
|
2013
|
+
ignored as `invalid_token`; every other actor type stays `invalid_event`. Like
|
|
2014
|
+
the customer reset it is gated by `account_mails`, ephemeral (never resent or
|
|
2015
|
+
retried by the generic job) and stored with `resource_type` `"user"`; its
|
|
2016
|
+
layout audience is `"shop"`.
|
|
2017
|
+
|
|
1981
2018
|
The account confirmation (`auth.verification`) has the subject "Bestätige
|
|
1982
2019
|
deine E-Mail-Adresse", a "E-Mail-Adresse bestätigen" button, the link as
|
|
1983
2020
|
copyable text, a note that the link is only valid for a limited time (a new
|
|
@@ -2433,13 +2470,9 @@ separate documents package since 1.0.0.
|
|
|
2433
2470
|
|
|
2434
2471
|
### Configuration
|
|
2435
2472
|
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
`tax_compliance` module with no fallback; a shop that does not register Tax
|
|
2440
|
-
Compliance fails at invoice issuance time (the first `shipment.created`
|
|
2441
|
-
event) with an `UNEXPECTED_STATE` error naming the missing plugin, not at
|
|
2442
|
-
startup.
|
|
2473
|
+
The invoice workflow reads the order's frozen B2B evidence from the base's own
|
|
2474
|
+
`tax_compliance` module (see "Steuer-Compliance"); register the Tax provider
|
|
2475
|
+
`@hartl-services/medusa-base/providers/tax-compliance` as shown there.
|
|
2443
2476
|
|
|
2444
2477
|
```ts
|
|
2445
2478
|
export default defineConfig({
|
|
@@ -2448,10 +2481,7 @@ export default defineConfig({
|
|
|
2448
2481
|
resolve: "@medusajs/medusa/tax",
|
|
2449
2482
|
options: {
|
|
2450
2483
|
providers: [
|
|
2451
|
-
{
|
|
2452
|
-
resolve:
|
|
2453
|
-
"@hartl-services/medusa-tax-compliance/providers/tax-compliance",
|
|
2454
|
-
},
|
|
2484
|
+
{ resolve: "@hartl-services/medusa-base/providers/tax-compliance" },
|
|
2455
2485
|
],
|
|
2456
2486
|
},
|
|
2457
2487
|
},
|
|
@@ -2470,10 +2500,6 @@ export default defineConfig({
|
|
|
2470
2500
|
},
|
|
2471
2501
|
},
|
|
2472
2502
|
},
|
|
2473
|
-
{
|
|
2474
|
-
resolve: "@hartl-services/medusa-tax-compliance",
|
|
2475
|
-
options: {},
|
|
2476
|
-
},
|
|
2477
2503
|
],
|
|
2478
2504
|
});
|
|
2479
2505
|
```
|
|
@@ -2588,7 +2614,8 @@ medusa-config fallback: what is stored in the Admin is what is printed.
|
|
|
2588
2614
|
|
|
2589
2615
|
Requirements/couplings:
|
|
2590
2616
|
|
|
2591
|
-
-
|
|
2617
|
+
- The Tax provider `tp_tax_compliance` of the base's Steuer-Compliance on the
|
|
2618
|
+
regions, so orders carry UID-based exemptions.
|
|
2592
2619
|
- A Chromium/Chrome executable and a Medusa File provider that supports
|
|
2593
2620
|
private files (see "Development" below for the sandbox/non-root note).
|
|
2594
2621
|
Documents stores PDFs through `Modules.FILE` and streams them only through
|
|
@@ -3058,6 +3085,322 @@ const response = await sdk.client.fetch<ListAdminOrderPackingListsResponse>(
|
|
|
3058
3085
|
);
|
|
3059
3086
|
```
|
|
3060
3087
|
|
|
3088
|
+
## Steuer-Compliance
|
|
3089
|
+
|
|
3090
|
+
There is no separate `@hartl-services/medusa-tax-compliance` package since
|
|
3091
|
+
3.0.0: the EU tax compliance (module `tax_compliance`, the Tax Module provider
|
|
3092
|
+
`tp_tax_compliance`, the UID check routes, the order-evidence freeze and the
|
|
3093
|
+
apportioned shipping tax) is part of the base. The module owns the durable
|
|
3094
|
+
validation evidence; a Storefront never imports its implementation, only
|
|
3095
|
+
`@hartl-services/medusa-food-supplements-contracts`.
|
|
3096
|
+
|
|
3097
|
+
### Configuration
|
|
3098
|
+
|
|
3099
|
+
Register the Tax Module provider in `medusa-config.ts` (the Shop-Config-Preset
|
|
3100
|
+
does this); the module itself boots with the base and has no options:
|
|
3101
|
+
|
|
3102
|
+
```ts
|
|
3103
|
+
modules: [
|
|
3104
|
+
{
|
|
3105
|
+
resolve: "@medusajs/medusa/tax",
|
|
3106
|
+
options: {
|
|
3107
|
+
providers: [
|
|
3108
|
+
{ resolve: "@hartl-services/medusa-base/providers/tax-compliance" },
|
|
3109
|
+
],
|
|
3110
|
+
},
|
|
3111
|
+
},
|
|
3112
|
+
],
|
|
3113
|
+
```
|
|
3114
|
+
|
|
3115
|
+
The provider is configured without a secondary `id`, so Medusa registers its
|
|
3116
|
+
class identifier `tax_compliance` as exactly `tp_tax_compliance`; regions
|
|
3117
|
+
select it as their tax provider. `medusa db:migrate` creates the tables
|
|
3118
|
+
`tax_compliance_validation` and `tax_compliance_state` (unchanged names and
|
|
3119
|
+
migrations of the former package, so an upgraded shop needs no data
|
|
3120
|
+
migration).
|
|
3121
|
+
|
|
3122
|
+
The merchant's own country and VAT number come only from the Shop-Stammdaten
|
|
3123
|
+
(fields `country_code` and `vat_number`); there is no config fallback. They
|
|
3124
|
+
select the qualified VAT validator (currently only the Austrian FinanzOnline
|
|
3125
|
+
adapter). While either is missing, carts get ordinary B2C tax.
|
|
3126
|
+
|
|
3127
|
+
### Runtime settings
|
|
3128
|
+
|
|
3129
|
+
Declared on the Steuer-Compliance descriptor and edited on its Admin page
|
|
3130
|
+
(Settings → Hartl Services Plugins → Steuer-Compliance):
|
|
3131
|
+
|
|
3132
|
+
| Key | Label | Type | Required |
|
|
3133
|
+
| ----------------------------- | -------------- | ------ | -------- |
|
|
3134
|
+
| `finanzonline_participant_id` | Teilnehmer-ID | string | yes |
|
|
3135
|
+
| `finanzonline_user_id` | Benutzer-ID | string | yes |
|
|
3136
|
+
| `finanzonline_password` | PIN | secret | yes |
|
|
3137
|
+
|
|
3138
|
+
All three sit in the group "FinanzOnline" ("Webservice-Benutzer für die
|
|
3139
|
+
qualifizierte UID-Abfrage.").
|
|
3140
|
+
|
|
3141
|
+
- Read per validation, only for an Austrian merchant with a real UID. There
|
|
3142
|
+
is no environment or medusa-config fallback; `FINANZONLINE_*` variables are
|
|
3143
|
+
ignored.
|
|
3144
|
+
- All three are needed as a set. While any is missing the plugin page shows
|
|
3145
|
+
"nicht konfiguriert", one warning is logged per process and not-configured
|
|
3146
|
+
state, and the qualified check answers `unavailable`
|
|
3147
|
+
(`missing_credentials`) without calling FinanzOnline.
|
|
3148
|
+
- A failing settings read is logged (`logger.error`, reported to Sentry by
|
|
3149
|
+
the base logger wrapper) and handled like missing credentials.
|
|
3150
|
+
- Merchant country and UID are read from the shop master data. On the cart
|
|
3151
|
+
tax path (tax-context hook) and in the Store route a failing read is logged
|
|
3152
|
+
and treated as "no merchant": ordinary B2C tax, and the route answers 500
|
|
3153
|
+
as before for a missing merchant profile. The cart tax path and the
|
|
3154
|
+
order-evidence freeze read the master data only for a cart that has a
|
|
3155
|
+
current UID validation record, so B2C carts never touch the settings and
|
|
3156
|
+
an outage does not log an error per tax calculation. The order-evidence
|
|
3157
|
+
freeze at checkout lets a read failure propagate, so a zero-rated order
|
|
3158
|
+
fails and an ordinarily taxed one continues.
|
|
3159
|
+
- Both are read at app level, never by the module service (a module does not
|
|
3160
|
+
reach into other modules): `src/tax-compliance/lib/tax-runtime-settings.ts`
|
|
3161
|
+
provides `resolveMerchantProfile(container, { failSafe })` and
|
|
3162
|
+
`resolveFinanzOnlineCredentials(container)`, called with the application
|
|
3163
|
+
container of the Store route, the validation and freeze workflow steps and
|
|
3164
|
+
the cart tax-context hook. The service takes the results as parameters:
|
|
3165
|
+
`validateCart(input, credentials)` (the step reads the credentials only
|
|
3166
|
+
when `requiresFinanzOnlineCredentials(input)` says so, and never passes
|
|
3167
|
+
them as workflow input), `getCurrentValidationId(cart, loadMerchant)` and
|
|
3168
|
+
`freezeOrderEvidence(orderId, cart, loadMerchant)` (the loader runs only
|
|
3169
|
+
after a current validation record was found) and
|
|
3170
|
+
`isValidationEligibleForTax(validationId, countryCode, merchant)`.
|
|
3171
|
+
- The cart tax-context hook puts the merchant profile it checked the
|
|
3172
|
+
evidence against into `additional_context.tax_compliance_merchant`, next to
|
|
3173
|
+
the validation ID. The tax provider runs in the Tax Module's isolated
|
|
3174
|
+
container and reads the merchant only from that context; without one it
|
|
3175
|
+
answers ordinary B2C rates. The order tax path needs no merchant: it checks
|
|
3176
|
+
the evidence frozen under the order ID.
|
|
3177
|
+
|
|
3178
|
+
### Feature toggles
|
|
3179
|
+
|
|
3180
|
+
Steuer-Compliance declares its switch and three features under the plugin key
|
|
3181
|
+
`tax_compliance` (`TAX_COMPLIANCE_PLUGIN_KEY` / `TAX_COMPLIANCE_FEATURES` from
|
|
3182
|
+
the Contracts `/paths/tax-compliance` subpath). An Admin switches them under
|
|
3183
|
+
Settings → "Hartl Services Plugins"; Storefronts read the effective values
|
|
3184
|
+
from `GET /store/plugin-features/tax_compliance`. All features default to on.
|
|
3185
|
+
The plugin switch off counts as all three features off.
|
|
3186
|
+
|
|
3187
|
+
| Key | Label | Default | Off means |
|
|
3188
|
+
| ---------------- | ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3189
|
+
| `vat_validation` | UID-Prüfung erlauben | on | `POST /store/customers/me/addresses/:address_id/tax-identification`, `GET /store/customers/me/tax-identifications` and the Admin re-check answer 404; Affiliate partner registration takes VAT IDs unchecked. Also switches `b2b` off. The FinanzOnline settings are dimmed. |
|
|
3190
|
+
| `b2b` | B2B-Verkauf im Checkout | on | `POST /store/carts/:id/tax-identification` answers 404 (before body validation); carts get no reverse charge, even with evidence checked earlier today; completing an ordinarily taxed order freezes no evidence. Child of `vat_validation`. Storefronts keep the company name and hide the VAT id. |
|
|
3191
|
+
| `shipping_tax` | Versandsteuer-Compliance | on | Shipping is taxed at the shipping option's own rate (Medusa's default) instead of the net-value-weighted item rate, for carts and order recalculations. |
|
|
3192
|
+
|
|
3193
|
+
With `shipping_tax` on, a cart with items at several rates gets one shipping
|
|
3194
|
+
tax line "Anteilige Versandsteuer" (code `APPORTIONED_SHIPPING_TAX_CODE` from
|
|
3195
|
+
`/lib`) at the items' rates weighted by their net amount after discounts
|
|
3196
|
+
(computed like Medusa's line totals), which yields the same tax as splitting
|
|
3197
|
+
the shipping by that amount onto each rate. Medusa taxes a cart before it
|
|
3198
|
+
applies promotions and a code redeemed through `POST /store/carts/:id/promotions`
|
|
3199
|
+
recalculates no tax, so the cart's rate can lag behind its discounts; the
|
|
3200
|
+
`orderCreated` participant therefore sets the order's rate again from its
|
|
3201
|
+
final discounts (tax-inclusive shipping only, whose paid gross amount stays
|
|
3202
|
+
the same; on error the order keeps the cart's rate and a warning is logged).
|
|
3203
|
+
Medusa stores the rate with six decimals. The invoice (see "Dokumente")
|
|
3204
|
+
splits that line back onto the item rates by the same key.
|
|
3205
|
+
|
|
3206
|
+
`GET /store/tax-identification-policy` follows only the plugin switch.
|
|
3207
|
+
Evidence frozen under an order that was zero-rated keeps counting for that
|
|
3208
|
+
order whatever the toggles say now.
|
|
3209
|
+
|
|
3210
|
+
The tax hooks and the order-evidence freeze never read a toggle. The
|
|
3211
|
+
`tax_compliance_state` row mirrors the effective `b2b` and `shipping_tax`
|
|
3212
|
+
values; the `plugin-features.updated.v1` subscriber and the
|
|
3213
|
+
`tax-compliance-state-sync` job (every 5 minutes) write it. Before the first
|
|
3214
|
+
sync (no row) both count as on; when the row cannot be read both count as
|
|
3215
|
+
off (ordinary B2C rates, Medusa's shipping tax).
|
|
3216
|
+
|
|
3217
|
+
### UID check in the checkout
|
|
3218
|
+
|
|
3219
|
+
Public Store request, response, schema, and path imports belong to
|
|
3220
|
+
`@hartl-services/medusa-food-supplements-contracts`:
|
|
3221
|
+
|
|
3222
|
+
```ts
|
|
3223
|
+
import type { SubmitTaxIdentificationResponse } from "@hartl-services/medusa-food-supplements-contracts/types/tax-compliance-store";
|
|
3224
|
+
import { taxCompliancePaths } from "@hartl-services/medusa-food-supplements-contracts/paths/tax-compliance";
|
|
3225
|
+
|
|
3226
|
+
const result = await sdk.client.fetch<SubmitTaxIdentificationResponse>(
|
|
3227
|
+
taxCompliancePaths.store.carts.taxIdentification(cartId),
|
|
3228
|
+
{ method: "POST", body: {} },
|
|
3229
|
+
);
|
|
3230
|
+
```
|
|
3231
|
+
|
|
3232
|
+
The module persists normalized cart validation evidence and selects qualified
|
|
3233
|
+
VAT validators by merchant country. Austrian merchants use the isolated
|
|
3234
|
+
FinanzOnline stage-2 adapter. Missing credentials, the development UID
|
|
3235
|
+
placeholder `ATU00000000`, technical/rate-limit responses, and unsupported
|
|
3236
|
+
merchant countries produce local `unavailable` evidence. Tax calculation
|
|
3237
|
+
interprets missing, stale, invalid, ineligible, and unavailable evidence as the
|
|
3238
|
+
normal B2C outcome. Cart tax calculation only accepts same-day `eligible`
|
|
3239
|
+
evidence whose current cart and merchant fingerprints still match, whose UID
|
|
3240
|
+
prefix matches the delivery country, and whose delivery country is
|
|
3241
|
+
cross-border. Once evidence is frozen, order tax calculation instead verifies
|
|
3242
|
+
the exact evidence-ID/order-ID binding and its frozen facts; it deliberately
|
|
3243
|
+
does not reopen current-cart or current-day eligibility. Eligible calculations
|
|
3244
|
+
receive explicit zero-percent item and shipping tax lines.
|
|
3245
|
+
|
|
3246
|
+
`ATU00000000` is a development-only Austrian UID placeholder: the registry
|
|
3247
|
+
treats it as missing (`merchant_uid_sentinel`, never sent to FinanzOnline)
|
|
3248
|
+
and Documents never prints it. Enter the real UID in the Shop-Stammdaten
|
|
3249
|
+
before issuing production invoices.
|
|
3250
|
+
|
|
3251
|
+
The Store route is:
|
|
3252
|
+
|
|
3253
|
+
```text
|
|
3254
|
+
POST /store/carts/:id/tax-identification
|
|
3255
|
+
```
|
|
3256
|
+
|
|
3257
|
+
The UID belongs to the cart's billing address: company plus
|
|
3258
|
+
`metadata.vat_number` (`TAX_IDENTIFICATION_METADATA_KEY`), set by the
|
|
3259
|
+
storefront like any other address field, for guests too. The cart evidence
|
|
3260
|
+
fingerprint reads the UID from the current billing address, so changing or
|
|
3261
|
+
removing it ends the exemption. Medusa itself recomputes taxes only when the
|
|
3262
|
+
shipping address changes, so a storefront calls this route (empty body) after
|
|
3263
|
+
the addresses changed and before it initiates payment.
|
|
3264
|
+
|
|
3265
|
+
It requires a publishable key scoped to the cart's sales channel. With a UID
|
|
3266
|
+
on a billing address outside the merchant country (Shop-Stammdaten) it
|
|
3267
|
+
performs one qualified validation; without one, or for a company in the
|
|
3268
|
+
merchant country, it answers `status: null` without any query and only
|
|
3269
|
+
recomputes when the cart still has a current check record (an earlier
|
|
3270
|
+
exemption falls away). It then forces tax-line recomputation, reconciles an
|
|
3271
|
+
existing payment collection with the new cart total, and returns only the
|
|
3272
|
+
public status, optional user message, and safe cart totals. The deprecated
|
|
3273
|
+
body (`company_name` and `vat_number` together) is still accepted: the route
|
|
3274
|
+
writes both into the billing address first. Provider facts, evidence IDs,
|
|
3275
|
+
credentials, sessions, and SOAP data are never part of the response. When the
|
|
3276
|
+
total changed, the reconciliation deletes the cart's payment sessions, so a
|
|
3277
|
+
storefront re-initiates payment after the call.
|
|
3278
|
+
|
|
3279
|
+
The adapter requires a dedicated FinanzOnline web-service user whose
|
|
3280
|
+
credentials are entered as runtime settings (see above). The PIN is stored
|
|
3281
|
+
encrypted and never sent to the Admin or a Storefront. SOAP clients are
|
|
3282
|
+
validation-scoped, scrub their node-soap transport state after every
|
|
3283
|
+
operation, and always attempt logout after a successful login. Every
|
|
3284
|
+
`unavailable` answer from the adapter logs one warning with the failing step
|
|
3285
|
+
(login or UID query) and FinanzOnline's return code and message, or the
|
|
3286
|
+
exception message (PIN masked), so a rejected web-service user shows up in
|
|
3287
|
+
the server log while the shopper only sees the generic message. Same-day
|
|
3288
|
+
provider facts are serialized and reused by normalized merchant UID, customer
|
|
3289
|
+
UID, and UTC date across carts and saved addresses. Every subject still
|
|
3290
|
+
receives its own evidence row and fingerprint; changed company or address
|
|
3291
|
+
submissions are re-evaluated locally against the structured provider facts
|
|
3292
|
+
without another remote request. Only records FinanzOnline actually answered
|
|
3293
|
+
for (provider facts, valid or not) are reused: an `unavailable` without an
|
|
3294
|
+
answer (missing credentials, the placeholder UID, an outage or login failure)
|
|
3295
|
+
is checked again on the next submission instead of blocking the UID for the
|
|
3296
|
+
rest of the day. Transaction-scoped PostgreSQL advisory locks cover both the
|
|
3297
|
+
normalized subject and the UID/day reuse key. Partial unique indexes permit
|
|
3298
|
+
only one current evidence row per cart and per saved address, while a
|
|
3299
|
+
separate lookup index supports cross-subject reuse.
|
|
3300
|
+
|
|
3301
|
+
A domestic company never qualifies for a tax-free intra-Community supply, so
|
|
3302
|
+
its UID is neither checked nor stored as evidence. Storefronts ask for the UID
|
|
3303
|
+
only outside the merchant country and read that country from:
|
|
3304
|
+
|
|
3305
|
+
```text
|
|
3306
|
+
GET /store/tax-identification-policy → { merchant_country_code: "at" | null }
|
|
3307
|
+
```
|
|
3308
|
+
|
|
3309
|
+
`null` while the master data name no country (or cannot be read): the
|
|
3310
|
+
storefront then asks for every country.
|
|
3311
|
+
|
|
3312
|
+
```ts
|
|
3313
|
+
import type { GetTaxIdentificationPolicyResponse } from "@hartl-services/medusa-food-supplements-contracts/types/tax-compliance-store";
|
|
3314
|
+
import { taxCompliancePaths } from "@hartl-services/medusa-food-supplements-contracts/paths/tax-compliance";
|
|
3315
|
+
|
|
3316
|
+
const { merchant_country_code } = await sdk.client.fetch<GetTaxIdentificationPolicyResponse>(
|
|
3317
|
+
taxCompliancePaths.store.taxIdentificationPolicy,
|
|
3318
|
+
{ method: "GET" },
|
|
3319
|
+
);
|
|
3320
|
+
```
|
|
3321
|
+
|
|
3322
|
+
Login and UID query share a 10 s budget (WSDL downloads included) and logout
|
|
3323
|
+
gets 3 s, so the request stays below a storefront's 15 s checkout wait and the
|
|
3324
|
+
database transaction holding the UID lock never waits on a hanging
|
|
3325
|
+
FinanzOnline call; a timeout answers `unavailable` (`technical_error`).
|
|
3326
|
+
|
|
3327
|
+
### Saved customer addresses
|
|
3328
|
+
|
|
3329
|
+
A saved customer address carries its UID in `metadata.vat_number`
|
|
3330
|
+
(`TAX_IDENTIFICATION_METADATA_KEY` in the Contracts). A logged-in customer
|
|
3331
|
+
checks it with
|
|
3332
|
+
|
|
3333
|
+
```text
|
|
3334
|
+
POST /store/customers/me/addresses/:address_id/tax-identification
|
|
3335
|
+
GET /store/customers/me/tax-identifications
|
|
3336
|
+
```
|
|
3337
|
+
|
|
3338
|
+
Company, UID and address are read from the stored address, never from the
|
|
3339
|
+
request; the address's own country stands in for the delivery country.
|
|
3340
|
+
`verified` (status `eligible` or `ineligible`) means the UID is real and
|
|
3341
|
+
matches company and address. The GET route only reports evidence whose
|
|
3342
|
+
fingerprint still matches the address as it is now: editing company, UID or
|
|
3343
|
+
address drops the status until the address is checked again. Metadata alone
|
|
3344
|
+
never proves a check. Both routes follow the `vat_validation` feature (404
|
|
3345
|
+
while off) and require a customer login.
|
|
3346
|
+
|
|
3347
|
+
Other plugins read the same current evidence, whatever its status (also
|
|
3348
|
+
`unavailable` and `invalid`), through
|
|
3349
|
+
`listCustomerAddressTaxIdentifications(container, customerId)` from
|
|
3350
|
+
`@hartl-services/medusa-base` (typed `CustomerAddressTaxIdentification` with
|
|
3351
|
+
the checked `billing_address`); the caller decides with
|
|
3352
|
+
`isPluginFeatureEnabledByKey(container, "tax_compliance", "vat_validation")`
|
|
3353
|
+
whether the check is on. The Affiliate partner registration uses it.
|
|
3354
|
+
|
|
3355
|
+
An Admin checks any customer's saved address again with
|
|
3356
|
+
|
|
3357
|
+
```text
|
|
3358
|
+
POST /admin/tax-compliance/customer-addresses/:address_id/tax-identification
|
|
3359
|
+
```
|
|
3360
|
+
|
|
3361
|
+
(admin login, `vat_validation` feature; answers like the Store route,
|
|
3362
|
+
`SubmitCustomerAddressTaxIdentificationResponse`, path
|
|
3363
|
+
`taxCompliancePaths.admin.customerAddressTaxIdentification(addressId)`). The
|
|
3364
|
+
Affiliate plugin uses it to re-check a partner application whose check was
|
|
3365
|
+
unavailable.
|
|
3366
|
+
|
|
3367
|
+
All three FinanzOnline settings are required as a set. Otherwise the service
|
|
3368
|
+
stores `unavailable` evidence and checkout continues with B2C tax; the Store
|
|
3369
|
+
response returns the checkout warning "Die UID-Prüfung ist derzeit nicht
|
|
3370
|
+
verfügbar. Bitte versuchen Sie es später erneut." Invalid or ineligible B2B
|
|
3371
|
+
facts likewise receive ordinary B2C tax, without a warning. The registry is
|
|
3372
|
+
country-based: it currently provides only the Austrian FinanzOnline adapter;
|
|
3373
|
+
an unsupported merchant country deliberately resolves to `unavailable`.
|
|
3374
|
+
|
|
3375
|
+
### Frozen order evidence
|
|
3376
|
+
|
|
3377
|
+
Cart tax workflows pass only the opaque current evidence ID and the merchant
|
|
3378
|
+
profile (country and UID) to the provider. Order tax workflows use evidence
|
|
3379
|
+
frozen under the order ID by the workflow
|
|
3380
|
+
`freeze-tax-compliance-order-evidence`, which the base runs as an
|
|
3381
|
+
`orderCreated` participant with `order` 200 (see "Complete-cart hook
|
|
3382
|
+
composition"), after Affiliate's attribution (100) when Affiliate is
|
|
3383
|
+
installed. After the freeze the same workflow sets the order's apportioned
|
|
3384
|
+
shipping rate from its final discounts (see "Feature toggles" above).
|
|
3385
|
+
Compensation runs in reverse (tax, then Affiliate); if the freeze fails, the
|
|
3386
|
+
base cancels Affiliate's already finished attribution. Tax-only module lookup
|
|
3387
|
+
and context failures remain fail-closed to ordinary B2C. The freeze step
|
|
3388
|
+
decides itself whether a failure is fatal: an auxiliary freeze failure is
|
|
3389
|
+
logged and contained for an ordinarily taxed B2C cart, but a zero-rated cart
|
|
3390
|
+
cannot complete unless its evidence was durably frozen under the order ID.
|
|
3391
|
+
Zero-rated means the provider set a line to 0 % although its configured tax
|
|
3392
|
+
rate is not 0 % (UID-based exemption); a line that is 0 % because its
|
|
3393
|
+
configured tax rate is 0 % (e.g. the small-business exemption) is ordinary
|
|
3394
|
+
taxation and needs no evidence. Freeze-compensation persistence failures are
|
|
3395
|
+
logged and propagated so workflow cancellation stays failed instead of
|
|
3396
|
+
silently leaving a stale order binding.
|
|
3397
|
+
|
|
3398
|
+
Documents reads that frozen eligible B2B evidence when an invoice is issued,
|
|
3399
|
+
so the document snapshot contains the company, customer UID and exemption
|
|
3400
|
+
wording from the completed order (see "Dokumente"). A later cart change or
|
|
3401
|
+
validation cannot alter an already issued document; B2C, invalid, ineligible
|
|
3402
|
+
and unavailable evidence adds no tax-proof wording.
|
|
3403
|
+
|
|
3061
3404
|
## Dateispeicher (S3)
|
|
3062
3405
|
|
|
3063
3406
|
The base provides a Medusa File Module provider for durable S3-compatible
|
|
@@ -3299,15 +3642,14 @@ loads the Sentry logger wrapper and with it `@sentry/node`; `/config` stays
|
|
|
3299
3642
|
free of it.
|
|
3300
3643
|
|
|
3301
3644
|
The preset registers the base (with its E-Mail, Documents and `/files`
|
|
3302
|
-
options) plus
|
|
3303
|
-
`medusa-
|
|
3645
|
+
options, and its Tax provider) plus four feature plugins, in this order:
|
|
3646
|
+
`medusa-base`, `medusa-free-shipping`, `medusa-batch-management`,
|
|
3304
3647
|
`medusa-affiliate`, `medusa-payload-sync`.
|
|
3305
3648
|
|
|
3306
3649
|
### Installation in a shop
|
|
3307
3650
|
|
|
3308
3651
|
```bash
|
|
3309
3652
|
pnpm add @hartl-services/medusa-base \
|
|
3310
|
-
@hartl-services/medusa-tax-compliance \
|
|
3311
3653
|
@hartl-services/medusa-free-shipping \
|
|
3312
3654
|
@hartl-services/medusa-batch-management \
|
|
3313
3655
|
@hartl-services/medusa-affiliate \
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "@medusajs/framework/zod";
|
|
2
|
-
export declare const EMAIL_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "affiliate.consignment_report_due", "affiliate.application_submitted.admin", "user.invite", "auth.verification", "auth.login_code", "system.credential_expiry.admin"];
|
|
2
|
+
export declare const EMAIL_KINDS: readonly ["order.received", "order.received.admin", "order.shipped", "order.canceled", "order.return_requested", "order.return_received", "order.refunded", "auth.password_reset", "affiliate.consignment_report_due", "affiliate.application_submitted.admin", "user.invite", "auth.verification", "auth.login_code", "system.credential_expiry.admin", "auth.admin_password_reset"];
|
|
3
3
|
export declare const EmailKindSchema: z.ZodEnum<{
|
|
4
4
|
"order.received": "order.received";
|
|
5
5
|
"order.received.admin": "order.received.admin";
|
|
@@ -15,6 +15,7 @@ export declare const EmailKindSchema: z.ZodEnum<{
|
|
|
15
15
|
"auth.verification": "auth.verification";
|
|
16
16
|
"auth.login_code": "auth.login_code";
|
|
17
17
|
"system.credential_expiry.admin": "system.credential_expiry.admin";
|
|
18
|
+
"auth.admin_password_reset": "auth.admin_password_reset";
|
|
18
19
|
}>;
|
|
19
20
|
export type EmailKind = (typeof EMAIL_KINDS)[number];
|
|
20
21
|
/**
|
|
@@ -22,5 +23,5 @@ export type EmailKind = (typeof EMAIL_KINDS)[number];
|
|
|
22
23
|
* subscriber builds the mail from the event, so neither the generic retry
|
|
23
24
|
* job, the automatic dispatch nor an Admin resend may touch them.
|
|
24
25
|
*/
|
|
25
|
-
export declare const EPHEMERAL_EMAIL_KINDS: readonly ["auth.password_reset", "auth.verification", "auth.login_code"];
|
|
26
|
+
export declare const EPHEMERAL_EMAIL_KINDS: readonly ["auth.password_reset", "auth.admin_password_reset", "auth.verification", "auth.login_code"];
|
|
26
27
|
export declare const isEphemeralEmailKind: (kind: EmailKind) => boolean;
|
|
@@ -17,6 +17,7 @@ exports.EMAIL_KINDS = [
|
|
|
17
17
|
"auth.verification",
|
|
18
18
|
"auth.login_code",
|
|
19
19
|
"system.credential_expiry.admin",
|
|
20
|
+
"auth.admin_password_reset",
|
|
20
21
|
];
|
|
21
22
|
exports.EmailKindSchema = zod_1.z.enum(exports.EMAIL_KINDS);
|
|
22
23
|
/**
|
|
@@ -26,6 +27,7 @@ exports.EmailKindSchema = zod_1.z.enum(exports.EMAIL_KINDS);
|
|
|
26
27
|
*/
|
|
27
28
|
exports.EPHEMERAL_EMAIL_KINDS = [
|
|
28
29
|
"auth.password_reset",
|
|
30
|
+
"auth.admin_password_reset",
|
|
29
31
|
"auth.verification",
|
|
30
32
|
"auth.login_code",
|
|
31
33
|
];
|
|
@@ -48,6 +48,14 @@ export type ShopEmailConfiguration = Readonly<{
|
|
|
48
48
|
adminAffiliateApplicationsUrl?: (input: Readonly<{
|
|
49
49
|
applicationId: string;
|
|
50
50
|
}>) => string;
|
|
51
|
+
/**
|
|
52
|
+
* The Medusa Admin's "choose a new password" page with the reset token
|
|
53
|
+
* (`/app/reset-password?token=<token>`). Without it an Admin user's
|
|
54
|
+
* password reset sends no mail.
|
|
55
|
+
*/
|
|
56
|
+
adminPasswordResetUrl?: (input: Readonly<{
|
|
57
|
+
token: string;
|
|
58
|
+
}>) => string;
|
|
51
59
|
affiliatePortalUrl: string;
|
|
52
60
|
displayName: string;
|
|
53
61
|
inviteUrl: (input: Readonly<{
|
|
@@ -381,6 +381,7 @@ export declare const EmailPersistedDataSchemas: {
|
|
|
381
381
|
client_id: z.ZodString;
|
|
382
382
|
expires_on: z.ZodString;
|
|
383
383
|
}, z.core.$strict>;
|
|
384
|
+
readonly "auth.admin_password_reset": z.ZodObject<{}, z.core.$strict>;
|
|
384
385
|
};
|
|
385
386
|
export declare const EmailRenderDataSchemas: {
|
|
386
387
|
readonly "auth.password_reset": z.ZodObject<{
|
|
@@ -388,6 +389,10 @@ export declare const EmailRenderDataSchemas: {
|
|
|
388
389
|
reset_url: z.ZodString;
|
|
389
390
|
valid_minutes: z.ZodOptional<z.ZodNumber>;
|
|
390
391
|
}, z.core.$strict>;
|
|
392
|
+
readonly "auth.admin_password_reset": z.ZodObject<{
|
|
393
|
+
reset_url: z.ZodString;
|
|
394
|
+
valid_minutes: z.ZodOptional<z.ZodNumber>;
|
|
395
|
+
}, z.core.$strict>;
|
|
391
396
|
readonly "user.invite": z.ZodObject<{
|
|
392
397
|
invite_url: z.ZodString;
|
|
393
398
|
expires_at: z.ZodOptional<z.ZodString>;
|
|
@@ -635,7 +640,7 @@ export type EmailPersistedDataMap = {
|
|
|
635
640
|
};
|
|
636
641
|
type EmptyRenderContext = Readonly<Record<never, never>>;
|
|
637
642
|
export type EmailEphemeralRenderContextMap = {
|
|
638
|
-
readonly [Kind in EmailKind]: Kind extends "auth.password_reset" ? Readonly<{
|
|
643
|
+
readonly [Kind in EmailKind]: Kind extends "auth.password_reset" | "auth.admin_password_reset" ? Readonly<{
|
|
639
644
|
reset_url: string;
|
|
640
645
|
valid_minutes?: number;
|
|
641
646
|
}> : Kind extends "user.invite" ? Readonly<{
|
|
@@ -240,6 +240,8 @@ exports.EmailPersistedDataSchemas = {
|
|
|
240
240
|
expires_on: affiliate_1.CalendarDateSchema,
|
|
241
241
|
})
|
|
242
242
|
.strict(),
|
|
243
|
+
// The Admin dashboard is German only, so no locale; the link is ephemeral.
|
|
244
|
+
"auth.admin_password_reset": zod_1.z.object({}).strict(),
|
|
243
245
|
};
|
|
244
246
|
exports.EmailRenderDataSchemas = {
|
|
245
247
|
...exports.EmailPersistedDataSchemas,
|
|
@@ -248,6 +250,11 @@ exports.EmailRenderDataSchemas = {
|
|
|
248
250
|
/** How long the reset link is valid, from the token; `undefined` if unknown. */
|
|
249
251
|
valid_minutes: zod_1.z.number().int().positive().optional(),
|
|
250
252
|
}),
|
|
253
|
+
"auth.admin_password_reset": exports.EmailPersistedDataSchemas["auth.admin_password_reset"].extend({
|
|
254
|
+
reset_url: zod_1.z.string().url(),
|
|
255
|
+
/** How long the reset link is valid, from the token; `undefined` if unknown. */
|
|
256
|
+
valid_minutes: zod_1.z.number().int().positive().optional(),
|
|
257
|
+
}),
|
|
251
258
|
"user.invite": exports.EmailPersistedDataSchemas["user.invite"].extend({
|
|
252
259
|
invite_url: zod_1.z.string().url(),
|
|
253
260
|
/** When the invite expires (ISO 8601), from the current invite. */
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
/** Container key of the base plugin's module (`Module("plugin_features", …)`). */
|
|
2
2
|
export declare const PLUGIN_FEATURES_MODULE: "plugin_features";
|
|
3
|
+
/** Container key of the base plugin's Steuer-Compliance module (`Module("tax_compliance", …)`). */
|
|
4
|
+
export declare const TAX_COMPLIANCE_MODULE: "tax_compliance";
|
package/dist/public/constants.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PLUGIN_FEATURES_MODULE = void 0;
|
|
3
|
+
exports.TAX_COMPLIANCE_MODULE = exports.PLUGIN_FEATURES_MODULE = void 0;
|
|
4
4
|
/** Container key of the base plugin's module (`Module("plugin_features", …)`). */
|
|
5
5
|
exports.PLUGIN_FEATURES_MODULE = "plugin_features";
|
|
6
|
+
/** Container key of the base plugin's Steuer-Compliance module (`Module("tax_compliance", …)`). */
|
|
7
|
+
exports.TAX_COMPLIANCE_MODULE = "tax_compliance";
|
package/dist/public/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { PLUGIN_FEATURES_MODULE } from "./constants";
|
|
1
|
+
export { PLUGIN_FEATURES_MODULE, TAX_COMPLIANCE_MODULE } from "./constants";
|
|
2
|
+
export { listCustomerAddressTaxIdentifications, TAX_COMPLIANCE_STATUSES, type AddressSnapshot, type CustomerAddressTaxIdentification, type TaxComplianceStatus, } from "./tax-compliance";
|
|
2
3
|
export { definePluginFeatures } from "./descriptor";
|
|
3
4
|
export { resolveEffectivePluginFeatures } from "./effective-features";
|
|
4
5
|
export { isOwnPluginEvent, isPluginFeatureEnabled, isPluginFeatureEnabledByKey, pluginFeatureGate, requirePluginFeature, } from "./enforcement";
|
package/dist/public/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.reportPluginActionProgress = exports.shopMasterDataDescriptor = exports.getShopMasterData = exports.getShopAppearance = exports.getPluginSettings = exports.PLUGIN_ICON_NAMES = exports.resolveBaseModule = exports.isBasePluginRegistered = exports.requirePluginFeature = exports.pluginFeatureGate = exports.isPluginFeatureEnabledByKey = exports.isPluginFeatureEnabled = exports.isOwnPluginEvent = exports.resolveEffectivePluginFeatures = exports.definePluginFeatures = exports.PLUGIN_FEATURES_MODULE = void 0;
|
|
3
|
+
exports.reportPluginActionProgress = exports.shopMasterDataDescriptor = exports.getShopMasterData = exports.getShopAppearance = exports.getPluginSettings = exports.PLUGIN_ICON_NAMES = exports.resolveBaseModule = exports.isBasePluginRegistered = exports.requirePluginFeature = exports.pluginFeatureGate = exports.isPluginFeatureEnabledByKey = exports.isPluginFeatureEnabled = exports.isOwnPluginEvent = exports.resolveEffectivePluginFeatures = exports.definePluginFeatures = exports.TAX_COMPLIANCE_STATUSES = exports.listCustomerAddressTaxIdentifications = exports.TAX_COMPLIANCE_MODULE = exports.PLUGIN_FEATURES_MODULE = void 0;
|
|
4
4
|
var constants_1 = require("./constants");
|
|
5
5
|
Object.defineProperty(exports, "PLUGIN_FEATURES_MODULE", { enumerable: true, get: function () { return constants_1.PLUGIN_FEATURES_MODULE; } });
|
|
6
|
+
Object.defineProperty(exports, "TAX_COMPLIANCE_MODULE", { enumerable: true, get: function () { return constants_1.TAX_COMPLIANCE_MODULE; } });
|
|
7
|
+
var tax_compliance_1 = require("./tax-compliance");
|
|
8
|
+
Object.defineProperty(exports, "listCustomerAddressTaxIdentifications", { enumerable: true, get: function () { return tax_compliance_1.listCustomerAddressTaxIdentifications; } });
|
|
9
|
+
Object.defineProperty(exports, "TAX_COMPLIANCE_STATUSES", { enumerable: true, get: function () { return tax_compliance_1.TAX_COMPLIANCE_STATUSES; } });
|
|
6
10
|
var descriptor_1 = require("./descriptor");
|
|
7
11
|
Object.defineProperty(exports, "definePluginFeatures", { enumerable: true, get: function () { return descriptor_1.definePluginFeatures; } });
|
|
8
12
|
var effective_features_1 = require("./effective-features");
|