@hartl-services/medusa-base 2.11.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 +39 -39
- package/.medusa/server/src/admin/index.mjs +39 -39
- 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/jobs/tax-compliance-state-sync.js +26 -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 +6 -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 +344 -22
- 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 +5 -8
- package/package.json +3 -2
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.readAddressVatNumber = void 0;
|
|
19
|
+
const lib_1 = require("../../shared/lib");
|
|
20
|
+
const tax_compliance_1 = require("@hartl-services/medusa-food-supplements-contracts/paths/tax-compliance");
|
|
21
|
+
const modules_sdk_1 = require("@medusajs/framework/modules-sdk");
|
|
22
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
23
|
+
const registry_1 = require("../../tax-compliance/integrations/registry");
|
|
24
|
+
const validation_policy_1 = require("./domain/validation-policy");
|
|
25
|
+
const tax_compliance_state_1 = __importDefault(require("./models/tax-compliance-state"));
|
|
26
|
+
const tax_compliance_validation_1 = __importDefault(require("./models/tax-compliance-validation"));
|
|
27
|
+
const complete_cart_hooks_1 = require("../../tax-compliance/complete-cart-hooks");
|
|
28
|
+
const plugin_features_1 = require("../../tax-compliance/plugin-features");
|
|
29
|
+
const runtime_settings_1 = require("./runtime-settings");
|
|
30
|
+
const subjectFilter = (subject) => subject.kind === "cart"
|
|
31
|
+
? { cart_id: subject.cartId }
|
|
32
|
+
: { customer_address_id: subject.customerAddressId };
|
|
33
|
+
const subjectColumns = (subject) => subject.kind === "cart"
|
|
34
|
+
? { cart_id: subject.cartId, customer_address_id: null, customer_id: null }
|
|
35
|
+
: {
|
|
36
|
+
cart_id: null,
|
|
37
|
+
customer_address_id: subject.customerAddressId,
|
|
38
|
+
customer_id: subject.customerId,
|
|
39
|
+
};
|
|
40
|
+
// The cart key keeps its original format so running validations and new ones
|
|
41
|
+
// share one lock across a deploy.
|
|
42
|
+
const subjectLockKey = (subject) => subject.kind === "cart"
|
|
43
|
+
? `tax-compliance-validation:${subject.cartId}`
|
|
44
|
+
: `tax-compliance-validation:customer-address:${subject.customerAddressId}`;
|
|
45
|
+
/** `eligible` and `ineligible` both mean: the UID is real and matches company and address. */
|
|
46
|
+
const isVerifiedStatus = (status) => status === "eligible" || status === "ineligible";
|
|
47
|
+
const STATE_SCOPE = "global";
|
|
48
|
+
const normalizeWhitespace = (value) => value.trim().replace(/\s+/g, " ");
|
|
49
|
+
const normalizeCountryCode = (value) => normalizeWhitespace(value).toLowerCase();
|
|
50
|
+
const normalizeAddress = (address) => ({
|
|
51
|
+
address1: normalizeWhitespace(address.address1),
|
|
52
|
+
...(address.address2
|
|
53
|
+
? { address2: normalizeWhitespace(address.address2) }
|
|
54
|
+
: {}),
|
|
55
|
+
city: normalizeWhitespace(address.city),
|
|
56
|
+
countryCode: normalizeCountryCode(address.countryCode),
|
|
57
|
+
postalCode: normalizeWhitespace(address.postalCode),
|
|
58
|
+
});
|
|
59
|
+
const normalizeFacts = (input) => ({
|
|
60
|
+
company_name: normalizeWhitespace(input.company_name),
|
|
61
|
+
customer_vat_number: (0, lib_1.normalizeVatNumber)(input.customer_vat_number),
|
|
62
|
+
billing_address: normalizeAddress(input.billing_address),
|
|
63
|
+
delivery_country_code: normalizeCountryCode(input.delivery_country_code),
|
|
64
|
+
merchant_country_code: normalizeCountryCode(input.merchant_country_code),
|
|
65
|
+
merchant_vat_number: (0, lib_1.normalizeVatNumber)(input.merchant_vat_number),
|
|
66
|
+
});
|
|
67
|
+
const fingerprint = (value) => (0, lib_1.sha256Hex)(JSON.stringify(value));
|
|
68
|
+
const createCartFingerprint = (input) => fingerprint({
|
|
69
|
+
billing_address: input.billing_address,
|
|
70
|
+
company_name: input.company_name.toLocaleLowerCase("de-AT"),
|
|
71
|
+
customer_vat_number: input.customer_vat_number,
|
|
72
|
+
delivery_country_code: input.delivery_country_code,
|
|
73
|
+
});
|
|
74
|
+
const createMerchantProfileFingerprint = (input) => fingerprint({
|
|
75
|
+
merchant_country_code: input.merchant_country_code,
|
|
76
|
+
merchant_vat_number: input.merchant_vat_number,
|
|
77
|
+
});
|
|
78
|
+
/** Only an Austrian merchant with a real UID queries FinanzOnline. */
|
|
79
|
+
const needsFinanzOnlineCredentials = (input) => input.merchant_country_code === "at" &&
|
|
80
|
+
(0, lib_1.normalizeVatNumber)(input.merchant_vat_number) !==
|
|
81
|
+
lib_1.VAT_NUMBER_PLACEHOLDER;
|
|
82
|
+
const safeProviderFacts = (facts) => {
|
|
83
|
+
if (!facts) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
valid: facts.valid,
|
|
88
|
+
name: facts.name,
|
|
89
|
+
addressLines: [...facts.addressLines],
|
|
90
|
+
reference: facts.reference,
|
|
91
|
+
...(typeof facts.returnCode === "number"
|
|
92
|
+
? { returnCode: facts.returnCode }
|
|
93
|
+
: {}),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
const asRecord = (value) => value;
|
|
97
|
+
/** The UID an address carries in its metadata; `null` while it has none. */
|
|
98
|
+
const readAddressVatNumber = (metadata) => {
|
|
99
|
+
const value = metadata?.[tax_compliance_1.TAX_IDENTIFICATION_METADATA_KEY];
|
|
100
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
101
|
+
};
|
|
102
|
+
exports.readAddressVatNumber = readAddressVatNumber;
|
|
103
|
+
/**
|
|
104
|
+
* The facts a saved address carries now, as the address check submitted
|
|
105
|
+
* them; `null` while company, UID or a required address part is missing.
|
|
106
|
+
* Merchant fields are left empty: they are not part of the facts fingerprint.
|
|
107
|
+
*/
|
|
108
|
+
const factsFromCustomerAddress = (address) => {
|
|
109
|
+
const vatNumber = (0, exports.readAddressVatNumber)(address.metadata);
|
|
110
|
+
if (!vatNumber ||
|
|
111
|
+
!address.company?.trim() ||
|
|
112
|
+
!address.address_1 ||
|
|
113
|
+
!address.city ||
|
|
114
|
+
!address.country_code ||
|
|
115
|
+
!address.postal_code) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return normalizeFacts({
|
|
119
|
+
company_name: address.company,
|
|
120
|
+
customer_vat_number: vatNumber,
|
|
121
|
+
billing_address: {
|
|
122
|
+
address1: address.address_1,
|
|
123
|
+
address2: address.address_2,
|
|
124
|
+
city: address.city,
|
|
125
|
+
countryCode: address.country_code,
|
|
126
|
+
postalCode: address.postal_code,
|
|
127
|
+
},
|
|
128
|
+
delivery_country_code: address.country_code,
|
|
129
|
+
merchant_country_code: "",
|
|
130
|
+
merchant_vat_number: "",
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
class TaxComplianceService extends (0, utils_1.MedusaService)({
|
|
134
|
+
TaxComplianceState: tax_compliance_state_1.default,
|
|
135
|
+
TaxComplianceValidation: tax_compliance_validation_1.default,
|
|
136
|
+
}) {
|
|
137
|
+
/** Discovered by @hartl-services/medusa-base, which runs these workflows. */
|
|
138
|
+
// eslint-disable-next-line @medusajs/service-methods-must-be-async -- The base discovers the hooks synchronously while it composes completeCartWorkflow (see CompleteCartParticipant).
|
|
139
|
+
completeCartHooks() {
|
|
140
|
+
return complete_cart_hooks_1.taxComplianceCompleteCartHooks;
|
|
141
|
+
}
|
|
142
|
+
constructor(container, options = {}) {
|
|
143
|
+
super(container);
|
|
144
|
+
/** Discovered by @hartl-services/medusa-base for the feature toggles. */
|
|
145
|
+
this.pluginFeatures = plugin_features_1.taxCompliancePluginFeatures;
|
|
146
|
+
this.injectedValidator_ = options.validator;
|
|
147
|
+
this.logger_ = (0, lib_1.resolveOptionalLogger)(container);
|
|
148
|
+
this.now_ = options.now ?? (() => new Date());
|
|
149
|
+
this.currentCartLoader_ = options.currentCartLoader;
|
|
150
|
+
this.customerAddressesLoader_ = options.customerAddressesLoader;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Whether `validateCart` needs the FinanzOnline credentials for this input:
|
|
154
|
+
* only an Austrian merchant with a real UID queries FinanzOnline (and never
|
|
155
|
+
* with an injected test validator). The caller reads them only then.
|
|
156
|
+
*/
|
|
157
|
+
async requiresFinanzOnlineCredentials(rawInput) {
|
|
158
|
+
return (!this.injectedValidator_ &&
|
|
159
|
+
needsFinanzOnlineCredentials(normalizeFacts({
|
|
160
|
+
company_name: "",
|
|
161
|
+
customer_vat_number: "",
|
|
162
|
+
billing_address: {
|
|
163
|
+
address1: "",
|
|
164
|
+
city: "",
|
|
165
|
+
countryCode: "",
|
|
166
|
+
postalCode: "",
|
|
167
|
+
},
|
|
168
|
+
delivery_country_code: "",
|
|
169
|
+
...rawInput,
|
|
170
|
+
})));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* `credentials` are the FinanzOnline credentials from the plugin settings,
|
|
174
|
+
* read by the caller (`undefined` while not configured: the registry then
|
|
175
|
+
* answers "unavailable" without calling FinanzOnline).
|
|
176
|
+
*/
|
|
177
|
+
async validateCart(rawInput, credentials) {
|
|
178
|
+
const { cart_id, ...facts } = rawInput;
|
|
179
|
+
return await this.validateSubject({ kind: "cart", cartId: normalizeWhitespace(cart_id) }, normalizeFacts(facts), credentials);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Checks the UID of a saved customer address. The same same-day reuse and
|
|
183
|
+
* locks as for carts apply, so a UID is queried at most once per day across
|
|
184
|
+
* carts and addresses.
|
|
185
|
+
*/
|
|
186
|
+
async validateCustomerAddress(rawInput, credentials) {
|
|
187
|
+
const { customer_address_id, customer_id, ...facts } = rawInput;
|
|
188
|
+
return await this.validateSubject({
|
|
189
|
+
kind: "customer_address",
|
|
190
|
+
customerAddressId: normalizeWhitespace(customer_address_id),
|
|
191
|
+
customerId: normalizeWhitespace(customer_id),
|
|
192
|
+
}, normalizeFacts({
|
|
193
|
+
...facts,
|
|
194
|
+
delivery_country_code: facts.billing_address.countryCode,
|
|
195
|
+
}), credentials);
|
|
196
|
+
}
|
|
197
|
+
async validateSubject(subject, input, credentials) {
|
|
198
|
+
const now = this.now_();
|
|
199
|
+
try {
|
|
200
|
+
return await this.validateWithDatabaseLock(subject, input, now, (await this.requiresFinanzOnlineCredentials(input))
|
|
201
|
+
? credentials
|
|
202
|
+
: undefined);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
if (!(0, lib_1.isUniqueConstraintViolation)(error)) {
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
const winner = await this.reuseMatchingUniqueConflict(subject, input, now);
|
|
209
|
+
if (winner) {
|
|
210
|
+
return winner;
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async validateWithDatabaseLock(subject, input, now, credentials, sharedContext = {}) {
|
|
216
|
+
await this.acquireSubjectValidationLock(subject, sharedContext);
|
|
217
|
+
await this.acquireUidValidationLock(input, now, sharedContext);
|
|
218
|
+
return await this.validateNormalized(subject, input, now, credentials, sharedContext);
|
|
219
|
+
}
|
|
220
|
+
async reuseMatchingUniqueConflict(subject, input, now, sharedContext = {}) {
|
|
221
|
+
await this.acquireSubjectValidationLock(subject, sharedContext);
|
|
222
|
+
await this.acquireUidValidationLock(input, now, sharedContext);
|
|
223
|
+
const matching = await this.findMatchingValidation(subject, input, now, sharedContext);
|
|
224
|
+
if (!matching) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return asRecord(await this.updateTaxComplianceValidations({
|
|
228
|
+
id: matching.id,
|
|
229
|
+
cache_used: true,
|
|
230
|
+
reuse_reason: "unique_conflict_matching_input",
|
|
231
|
+
}, sharedContext));
|
|
232
|
+
}
|
|
233
|
+
async validateNormalized(subject, input, now, credentials, sharedContext) {
|
|
234
|
+
const matching = await this.findMatchingValidation(subject, input, now, sharedContext);
|
|
235
|
+
// Same rule as the cross-subject reuse below: an "unavailable" without a
|
|
236
|
+
// provider answer is checked again instead of being returned all day.
|
|
237
|
+
if (matching && matching.provider_facts !== null) {
|
|
238
|
+
return asRecord(await this.updateTaxComplianceValidations({
|
|
239
|
+
id: matching.id,
|
|
240
|
+
cache_used: true,
|
|
241
|
+
reuse_reason: "same_day_matching_input",
|
|
242
|
+
}, sharedContext));
|
|
243
|
+
}
|
|
244
|
+
const reusable = await this.findReusableUidValidation(input, now, sharedContext);
|
|
245
|
+
if (reusable) {
|
|
246
|
+
return await this.replaceCurrentEvidence(subject, input, now, reusable.provider, this.evaluateReusableProviderResult(reusable, input), true, "same_day_uid_provider_result", sharedContext);
|
|
247
|
+
}
|
|
248
|
+
const validator = this.injectedValidator_ ??
|
|
249
|
+
(0, registry_1.selectQualifiedVatValidator)({
|
|
250
|
+
merchantCountryCode: input.merchant_country_code,
|
|
251
|
+
merchantVatNumber: input.merchant_vat_number,
|
|
252
|
+
credentials,
|
|
253
|
+
logger: this.logger_,
|
|
254
|
+
});
|
|
255
|
+
const result = await validator.validate({
|
|
256
|
+
billingAddress: input.billing_address,
|
|
257
|
+
companyName: input.company_name,
|
|
258
|
+
customerVatNumber: input.customer_vat_number,
|
|
259
|
+
deliveryCountryCode: input.delivery_country_code,
|
|
260
|
+
merchantCountryCode: input.merchant_country_code,
|
|
261
|
+
merchantVatNumber: input.merchant_vat_number,
|
|
262
|
+
});
|
|
263
|
+
return await this.replaceCurrentEvidence(subject, input, now, validator.provider, result, false, null, sharedContext);
|
|
264
|
+
}
|
|
265
|
+
async replaceCurrentEvidence(subject, input, now, provider, result, cacheUsed, reuseReason, sharedContext) {
|
|
266
|
+
const validationDate = (0, lib_1.toIsoDate)(now);
|
|
267
|
+
const cartFingerprint = createCartFingerprint(input);
|
|
268
|
+
const merchantProfileFingerprint = createMerchantProfileFingerprint(input);
|
|
269
|
+
// Retire the current record with an immediate statement: MikroORM flushes
|
|
270
|
+
// inserts before updates, so an ORM update would reach the database
|
|
271
|
+
// after the insert below and trip the "one current record" unique index.
|
|
272
|
+
await this.retireCurrentEvidence(subject, sharedContext);
|
|
273
|
+
return asRecord(await this.createTaxComplianceValidations({
|
|
274
|
+
...subjectColumns(subject),
|
|
275
|
+
order_id: null,
|
|
276
|
+
status: result.status,
|
|
277
|
+
company_name: input.company_name,
|
|
278
|
+
customer_vat_number: input.customer_vat_number,
|
|
279
|
+
billing_address: input.billing_address,
|
|
280
|
+
delivery_country_code: input.delivery_country_code,
|
|
281
|
+
merchant_country_code: input.merchant_country_code,
|
|
282
|
+
merchant_vat_number: input.merchant_vat_number,
|
|
283
|
+
provider,
|
|
284
|
+
provider_facts: safeProviderFacts(result.provider),
|
|
285
|
+
validation_reason: result.reason,
|
|
286
|
+
cart_fingerprint: cartFingerprint,
|
|
287
|
+
merchant_profile_fingerprint: merchantProfileFingerprint,
|
|
288
|
+
validated_at: now,
|
|
289
|
+
validation_date: validationDate,
|
|
290
|
+
is_current: true,
|
|
291
|
+
cache_used: cacheUsed,
|
|
292
|
+
reuse_reason: reuseReason,
|
|
293
|
+
}, sharedContext));
|
|
294
|
+
}
|
|
295
|
+
async retireCurrentEvidence(subject, sharedContext) {
|
|
296
|
+
const manager = this.requireManager(sharedContext);
|
|
297
|
+
// The column is chosen from the subject kind, never from input.
|
|
298
|
+
const column = subject.kind === "cart" ? "cart_id" : "customer_address_id";
|
|
299
|
+
const id = subject.kind === "cart" ? subject.cartId : subject.customerAddressId;
|
|
300
|
+
await manager.execute(`update "tax_compliance_validation"
|
|
301
|
+
set "is_current" = false, "updated_at" = now()
|
|
302
|
+
where "${column}" = ? and "is_current" = true and "deleted_at" is null`, [id], "run");
|
|
303
|
+
}
|
|
304
|
+
async findMatchingValidation(subject, input, now, sharedContext) {
|
|
305
|
+
const records = (await this.listTaxComplianceValidations({
|
|
306
|
+
...subjectFilter(subject),
|
|
307
|
+
customer_vat_number: input.customer_vat_number,
|
|
308
|
+
cart_fingerprint: createCartFingerprint(input),
|
|
309
|
+
merchant_profile_fingerprint: createMerchantProfileFingerprint(input),
|
|
310
|
+
validation_date: (0, lib_1.toIsoDate)(now),
|
|
311
|
+
is_current: true,
|
|
312
|
+
}, { take: 1, order: { created_at: "DESC" } }, sharedContext));
|
|
313
|
+
return records[0] ?? null;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Only a record FinanzOnline actually answered for (provider facts, valid
|
|
317
|
+
* or not) is reused, keeping every UID at one query per day. An
|
|
318
|
+
* "unavailable" without an answer (missing credentials, placeholder UID,
|
|
319
|
+
* outage, login failure) is not: it must not block the UID until midnight.
|
|
320
|
+
*/
|
|
321
|
+
async findReusableUidValidation(input, now, sharedContext) {
|
|
322
|
+
const records = (await this.listTaxComplianceValidations({
|
|
323
|
+
customer_vat_number: input.customer_vat_number,
|
|
324
|
+
merchant_country_code: input.merchant_country_code,
|
|
325
|
+
merchant_vat_number: input.merchant_vat_number,
|
|
326
|
+
validation_date: (0, lib_1.toIsoDate)(now),
|
|
327
|
+
}, { order: { created_at: "DESC" } }, sharedContext));
|
|
328
|
+
return records.find((record) => record.provider_facts !== null) ?? null;
|
|
329
|
+
}
|
|
330
|
+
evaluateReusableProviderResult(source, input) {
|
|
331
|
+
if (source.status === "unavailable" || !source.provider_facts) {
|
|
332
|
+
return {
|
|
333
|
+
status: "unavailable",
|
|
334
|
+
provider: null,
|
|
335
|
+
reason: source.status === "unavailable"
|
|
336
|
+
? source.validation_reason
|
|
337
|
+
: "cached_provider_facts_unavailable",
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
const provider = safeProviderFacts(source.provider_facts);
|
|
341
|
+
const decision = (0, validation_policy_1.evaluateEligibility)({
|
|
342
|
+
billingAddress: input.billing_address,
|
|
343
|
+
companyName: input.company_name,
|
|
344
|
+
customerVatNumber: input.customer_vat_number,
|
|
345
|
+
deliveryCountryCode: input.delivery_country_code,
|
|
346
|
+
merchantCountryCode: input.merchant_country_code,
|
|
347
|
+
provider,
|
|
348
|
+
});
|
|
349
|
+
return {
|
|
350
|
+
status: decision.status,
|
|
351
|
+
provider,
|
|
352
|
+
reason: decision.reason,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async acquireSubjectValidationLock(subject, sharedContext) {
|
|
356
|
+
const manager = this.requireManager(sharedContext);
|
|
357
|
+
await (0, lib_1.acquireAdvisoryXactLock)(manager, subjectLockKey(subject));
|
|
358
|
+
}
|
|
359
|
+
async acquireUidValidationLock(input, now, sharedContext) {
|
|
360
|
+
const manager = this.requireManager(sharedContext);
|
|
361
|
+
await (0, lib_1.acquireAdvisoryXactLock)(manager, `tax-compliance-uid-validation:${input.merchant_vat_number}:${input.customer_vat_number}:${(0, lib_1.toIsoDate)(now)}`);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* The stored mirror of the `b2b` and `shipping_tax` toggles. Before the
|
|
365
|
+
* first sync there is no row; both count as on then, as before the toggles
|
|
366
|
+
* existed. Read errors propagate (see readTaxComplianceState).
|
|
367
|
+
*/
|
|
368
|
+
async getTaxComplianceState() {
|
|
369
|
+
const rows = (await this.listTaxComplianceStates({ scope: STATE_SCOPE }, { take: 1 }));
|
|
370
|
+
const row = rows[0];
|
|
371
|
+
return {
|
|
372
|
+
b2b_active: row?.b2b_active !== false,
|
|
373
|
+
shipping_tax_active: row?.shipping_tax_active !== false,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Writes the mirror. The partial unique `scope` index keeps a single row:
|
|
378
|
+
* concurrent writers race on the INSERT and the loser updates instead.
|
|
379
|
+
*/
|
|
380
|
+
async storeTaxComplianceState(state, sharedContext = {}) {
|
|
381
|
+
await this.requireManager(sharedContext).execute(`insert into "tax_compliance_state"
|
|
382
|
+
("id", "scope", "b2b_active", "shipping_tax_active", "created_at", "updated_at")
|
|
383
|
+
values (
|
|
384
|
+
'tcstate_' || substr(md5(random()::text || clock_timestamp()::text), 1, 26),
|
|
385
|
+
?, ?, ?, now(), now()
|
|
386
|
+
)
|
|
387
|
+
on conflict ("scope") where "deleted_at" is null
|
|
388
|
+
do update set
|
|
389
|
+
"b2b_active" = excluded."b2b_active",
|
|
390
|
+
"shipping_tax_active" = excluded."shipping_tax_active",
|
|
391
|
+
"updated_at" = now()`, [STATE_SCOPE, state.b2b_active, state.shipping_tax_active], "run");
|
|
392
|
+
}
|
|
393
|
+
requireManager(sharedContext) {
|
|
394
|
+
return (0, lib_1.requireTransactionManager)(sharedContext, "Tax compliance validation requires a database transaction.");
|
|
395
|
+
}
|
|
396
|
+
async getCurrentCartValidation(cartId, cartFingerprint) {
|
|
397
|
+
const validationDate = (0, lib_1.toIsoDate)(this.now_());
|
|
398
|
+
const records = (await this.listTaxComplianceValidations({
|
|
399
|
+
cart_id: cartId,
|
|
400
|
+
cart_fingerprint: cartFingerprint,
|
|
401
|
+
validation_date: validationDate,
|
|
402
|
+
is_current: true,
|
|
403
|
+
}, { take: 1, order: { created_at: "DESC" } }));
|
|
404
|
+
return records[0] ?? null;
|
|
405
|
+
}
|
|
406
|
+
/** Whether the cart has a current check record, i.e. may be zero-rated. */
|
|
407
|
+
async hasCurrentCartEvidence(cartId) {
|
|
408
|
+
const records = await this.listTaxComplianceValidations({ cart_id: cartId, is_current: true }, { take: 1, select: ["id"] });
|
|
409
|
+
return records.length > 0;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Cart tax path (tax-context hook): the caller passes a fail-safe loader,
|
|
413
|
+
* so a failing master data read answers "no evidence" and the cart keeps
|
|
414
|
+
* ordinary B2C rates. The loader is only called for a cart with a current
|
|
415
|
+
* validation record, so B2C carts never touch the base plugin.
|
|
416
|
+
*/
|
|
417
|
+
async getCurrentValidationId(cart, loadMerchant) {
|
|
418
|
+
return await this.currentValidationIdFor(cart, loadMerchant);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Reads the validation record first and calls `loadMerchant` only when one
|
|
422
|
+
* exists; its failures propagate to the caller.
|
|
423
|
+
*/
|
|
424
|
+
async currentValidationIdFor(cart, loadMerchant) {
|
|
425
|
+
const records = (await this.listTaxComplianceValidations({ cart_id: cart.id, is_current: true }, { take: 1, order: { created_at: "DESC" } }));
|
|
426
|
+
const record = records[0];
|
|
427
|
+
if (!record) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
const loaded = await loadMerchant();
|
|
431
|
+
if (!loaded) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
const merchant = (0, runtime_settings_1.normalizeMerchantProfile)(loaded);
|
|
435
|
+
// The tax-line hooks pass a cart without billing address; the current
|
|
436
|
+
// cart is loaded then, so its billing company, address and UID count.
|
|
437
|
+
let currentInput = this.inputFromCart(cart, merchant);
|
|
438
|
+
if (!currentInput) {
|
|
439
|
+
try {
|
|
440
|
+
const currentCart = await this.loadCurrentCart(cart.id);
|
|
441
|
+
currentInput = currentCart
|
|
442
|
+
? this.inputFromCart(currentCart, merchant)
|
|
443
|
+
: null;
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (!currentInput) {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
return record.validation_date === (0, lib_1.toIsoDate)(this.now_()) &&
|
|
453
|
+
record.cart_fingerprint === createCartFingerprint(currentInput) &&
|
|
454
|
+
record.merchant_profile_fingerprint ===
|
|
455
|
+
createMerchantProfileFingerprint({
|
|
456
|
+
...currentInput,
|
|
457
|
+
merchant_country_code: merchant.countryCode,
|
|
458
|
+
merchant_vat_number: merchant.vatNumber,
|
|
459
|
+
})
|
|
460
|
+
? record.id
|
|
461
|
+
: null;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Tax provider path: `merchant` is the profile the cart tax-context hook
|
|
465
|
+
* put into the tax calculation context (`null` while it had none, e.g.
|
|
466
|
+
* because the master data read failed): no merchant, no zero rate.
|
|
467
|
+
*/
|
|
468
|
+
async isValidationEligibleForTax(validationId, rawDeliveryCountryCode, rawMerchant) {
|
|
469
|
+
if (!rawMerchant) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
const merchant = (0, runtime_settings_1.normalizeMerchantProfile)(rawMerchant);
|
|
473
|
+
const records = (await this.listTaxComplianceValidations({ id: validationId, is_current: true }, { take: 1 }));
|
|
474
|
+
const record = records[0];
|
|
475
|
+
// Only cart evidence can zero-rate a cart; address evidence never reaches
|
|
476
|
+
// the tax provider directly.
|
|
477
|
+
if (!record || record.status !== "eligible" || !record.cart_id) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
const currentCart = await this.loadCurrentCart(record.cart_id);
|
|
482
|
+
if (!currentCart ||
|
|
483
|
+
(await this.currentValidationIdFor(currentCart, async () => merchant)) !==
|
|
484
|
+
validationId) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
const deliveryCountryCode = normalizeCountryCode(rawDeliveryCountryCode);
|
|
492
|
+
const customerCountryPrefix = record.customer_vat_number.slice(0, 2);
|
|
493
|
+
return (record.validation_date === (0, lib_1.toIsoDate)(this.now_()) &&
|
|
494
|
+
record.delivery_country_code === deliveryCountryCode &&
|
|
495
|
+
customerCountryPrefix === deliveryCountryCode.toUpperCase() &&
|
|
496
|
+
record.merchant_country_code === merchant.countryCode &&
|
|
497
|
+
record.merchant_vat_number === merchant.vatNumber &&
|
|
498
|
+
merchant.countryCode !== deliveryCountryCode &&
|
|
499
|
+
record.merchant_profile_fingerprint ===
|
|
500
|
+
createMerchantProfileFingerprint({
|
|
501
|
+
merchant_country_code: merchant.countryCode,
|
|
502
|
+
merchant_vat_number: merchant.vatNumber,
|
|
503
|
+
}));
|
|
504
|
+
}
|
|
505
|
+
async isOrderValidationEligibleForTax(validationId, orderId, rawDeliveryCountryCode) {
|
|
506
|
+
const records = (await this.listTaxComplianceValidations({ id: validationId, order_id: orderId }, { take: 1 }));
|
|
507
|
+
const record = records[0];
|
|
508
|
+
if (!record || record.status !== "eligible" || !record.provider_facts) {
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
const deliveryCountryCode = normalizeCountryCode(rawDeliveryCountryCode);
|
|
512
|
+
const frozenInput = normalizeFacts({
|
|
513
|
+
company_name: record.company_name,
|
|
514
|
+
customer_vat_number: record.customer_vat_number,
|
|
515
|
+
billing_address: record.billing_address,
|
|
516
|
+
delivery_country_code: record.delivery_country_code,
|
|
517
|
+
merchant_country_code: record.merchant_country_code,
|
|
518
|
+
merchant_vat_number: record.merchant_vat_number,
|
|
519
|
+
});
|
|
520
|
+
const decision = (0, validation_policy_1.evaluateEligibility)({
|
|
521
|
+
billingAddress: frozenInput.billing_address,
|
|
522
|
+
companyName: frozenInput.company_name,
|
|
523
|
+
customerVatNumber: frozenInput.customer_vat_number,
|
|
524
|
+
deliveryCountryCode: frozenInput.delivery_country_code,
|
|
525
|
+
merchantCountryCode: frozenInput.merchant_country_code,
|
|
526
|
+
provider: record.provider_facts,
|
|
527
|
+
});
|
|
528
|
+
return (frozenInput.delivery_country_code === deliveryCountryCode &&
|
|
529
|
+
record.cart_fingerprint === createCartFingerprint(frozenInput) &&
|
|
530
|
+
record.merchant_profile_fingerprint ===
|
|
531
|
+
createMerchantProfileFingerprint(frozenInput) &&
|
|
532
|
+
decision.status === "eligible");
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Checkout guard: the caller passes a strict loader, so a read failure
|
|
536
|
+
* propagates and the freeze step fails a zero-rated order while an
|
|
537
|
+
* ordinarily taxed one continues.
|
|
538
|
+
*/
|
|
539
|
+
async freezeOrderEvidence(orderId, cart, loadMerchant) {
|
|
540
|
+
const validationId = await this.currentValidationIdFor(cart, loadMerchant);
|
|
541
|
+
if (!validationId) {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
const records = (await this.listTaxComplianceValidations({ id: validationId }, { take: 1 }));
|
|
545
|
+
const current = records[0];
|
|
546
|
+
if (!current) {
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
const previousOrderId = current.order_id;
|
|
550
|
+
return {
|
|
551
|
+
evidence: asRecord(await this.updateTaxComplianceValidations({
|
|
552
|
+
id: validationId,
|
|
553
|
+
order_id: orderId,
|
|
554
|
+
})),
|
|
555
|
+
previousOrderId,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
async restoreOrderEvidence(validationId, previousOrderId) {
|
|
559
|
+
await this.updateTaxComplianceValidations({
|
|
560
|
+
id: validationId,
|
|
561
|
+
order_id: previousOrderId,
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
async getOrderValidationId(orderId) {
|
|
565
|
+
const records = (await this.listTaxComplianceValidations({ order_id: orderId }, { take: 1, order: { created_at: "DESC" } }));
|
|
566
|
+
return records[0]?.id ?? null;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* The current evidence of each of the customer's saved addresses, but only
|
|
570
|
+
* where it still matches the address as it is now (company, UID in
|
|
571
|
+
* metadata, address). A changed address has no status until checked again.
|
|
572
|
+
*/
|
|
573
|
+
async listCustomerAddressIdentifications(customerId) {
|
|
574
|
+
const addresses = await this.loadCustomerAddresses(customerId);
|
|
575
|
+
if (addresses.length === 0) {
|
|
576
|
+
return [];
|
|
577
|
+
}
|
|
578
|
+
const records = (await this.listTaxComplianceValidations({
|
|
579
|
+
customer_address_id: addresses.map(({ id }) => id),
|
|
580
|
+
customer_id: customerId,
|
|
581
|
+
is_current: true,
|
|
582
|
+
}));
|
|
583
|
+
return addresses.flatMap((address) => {
|
|
584
|
+
const record = records.find(({ customer_address_id }) => customer_address_id === address.id);
|
|
585
|
+
const facts = factsFromCustomerAddress(address);
|
|
586
|
+
if (!record ||
|
|
587
|
+
!facts ||
|
|
588
|
+
record.cart_fingerprint !== createCartFingerprint(facts)) {
|
|
589
|
+
return [];
|
|
590
|
+
}
|
|
591
|
+
return [
|
|
592
|
+
{
|
|
593
|
+
customer_address_id: address.id,
|
|
594
|
+
status: record.status,
|
|
595
|
+
customer_vat_number: record.customer_vat_number,
|
|
596
|
+
company_name: record.company_name,
|
|
597
|
+
validated_at: record.validated_at,
|
|
598
|
+
verified: isVerifiedStatus(record.status),
|
|
599
|
+
billing_address: record.billing_address,
|
|
600
|
+
},
|
|
601
|
+
];
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* For other plugins (resolved by key, e.g. the Affiliate registration): the
|
|
606
|
+
* verified identification of one of the customer's addresses, or `null`
|
|
607
|
+
* when the address is not theirs, has changed since the check, or its UID
|
|
608
|
+
* is not verified.
|
|
609
|
+
*/
|
|
610
|
+
async getVerifiedCustomerAddressIdentification(customerId, customerAddressId) {
|
|
611
|
+
const identifications = await this.listCustomerAddressIdentifications(customerId);
|
|
612
|
+
const identification = identifications.find(({ customer_address_id }) => customer_address_id === customerAddressId);
|
|
613
|
+
return identification?.verified ? identification : null;
|
|
614
|
+
}
|
|
615
|
+
async loadCustomerAddresses(customerId) {
|
|
616
|
+
if (this.customerAddressesLoader_) {
|
|
617
|
+
return await this.customerAddressesLoader_(customerId);
|
|
618
|
+
}
|
|
619
|
+
const loadedModule = modules_sdk_1.MedusaModule.getModuleInstance(utils_1.Modules.CUSTOMER);
|
|
620
|
+
const customerService = loadedModule?.[utils_1.Modules.CUSTOMER];
|
|
621
|
+
if (!customerService) {
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
return (await customerService.listCustomerAddresses({
|
|
625
|
+
customer_id: customerId,
|
|
626
|
+
}));
|
|
627
|
+
}
|
|
628
|
+
async loadCurrentCart(cartId) {
|
|
629
|
+
if (this.currentCartLoader_) {
|
|
630
|
+
return await this.currentCartLoader_(cartId);
|
|
631
|
+
}
|
|
632
|
+
const loadedModule = modules_sdk_1.MedusaModule.getModuleInstance(utils_1.Modules.CART);
|
|
633
|
+
const cartService = loadedModule?.[utils_1.Modules.CART];
|
|
634
|
+
if (!cartService) {
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
return (await cartService.retrieveCart(cartId, {
|
|
638
|
+
relations: ["billing_address", "shipping_address"],
|
|
639
|
+
}));
|
|
640
|
+
}
|
|
641
|
+
/** The facts the cart carries now; `null` while company, UID or an address part is missing. */
|
|
642
|
+
inputFromCart(cart, merchant) {
|
|
643
|
+
const billing = cart.billing_address;
|
|
644
|
+
const deliveryCountryCode = cart.shipping_address?.country_code;
|
|
645
|
+
const customerVatNumber = (0, exports.readAddressVatNumber)(billing?.metadata);
|
|
646
|
+
if (!customerVatNumber ||
|
|
647
|
+
!billing?.address_1 ||
|
|
648
|
+
!billing.city ||
|
|
649
|
+
!billing.company ||
|
|
650
|
+
!billing.country_code ||
|
|
651
|
+
!billing.postal_code ||
|
|
652
|
+
!deliveryCountryCode) {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
return normalizeFacts({
|
|
656
|
+
company_name: billing.company,
|
|
657
|
+
customer_vat_number: customerVatNumber,
|
|
658
|
+
billing_address: {
|
|
659
|
+
address1: billing.address_1,
|
|
660
|
+
address2: billing.address_2,
|
|
661
|
+
city: billing.city,
|
|
662
|
+
countryCode: billing.country_code,
|
|
663
|
+
postalCode: billing.postal_code,
|
|
664
|
+
},
|
|
665
|
+
delivery_country_code: deliveryCountryCode,
|
|
666
|
+
merchant_country_code: merchant.countryCode,
|
|
667
|
+
merchant_vat_number: merchant.vatNumber,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
__decorate([
|
|
672
|
+
(0, utils_1.InjectTransactionManager)(),
|
|
673
|
+
__param(4, (0, utils_1.MedusaContext)()),
|
|
674
|
+
__metadata("design:type", Function),
|
|
675
|
+
__metadata("design:paramtypes", [Object, Object, Date, Object, Object]),
|
|
676
|
+
__metadata("design:returntype", Promise)
|
|
677
|
+
], TaxComplianceService.prototype, "validateWithDatabaseLock", null);
|
|
678
|
+
__decorate([
|
|
679
|
+
(0, utils_1.InjectTransactionManager)(),
|
|
680
|
+
__param(3, (0, utils_1.MedusaContext)()),
|
|
681
|
+
__metadata("design:type", Function),
|
|
682
|
+
__metadata("design:paramtypes", [Object, Object, Date, Object]),
|
|
683
|
+
__metadata("design:returntype", Promise)
|
|
684
|
+
], TaxComplianceService.prototype, "reuseMatchingUniqueConflict", null);
|
|
685
|
+
__decorate([
|
|
686
|
+
(0, utils_1.InjectTransactionManager)(),
|
|
687
|
+
__param(1, (0, utils_1.MedusaContext)()),
|
|
688
|
+
__metadata("design:type", Function),
|
|
689
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
690
|
+
__metadata("design:returntype", Promise)
|
|
691
|
+
], TaxComplianceService.prototype, "storeTaxComplianceState", null);
|
|
692
|
+
exports.default = TaxComplianceService;
|
|
693
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL3RheC1jb21wbGlhbmNlL3NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQ0EsMENBVXlCO0FBQ3pCLDJHQUF3SDtBQU94SCxpRUFBOEQ7QUFDOUQscURBTWtDO0FBRWxDLHlFQUdtRDtBQUVuRCxrRUFBZ0U7QUFXaEUseUZBQThEO0FBQzlELG1HQUF3RTtBQUN4RSxrRkFBeUY7QUFDekYsMEVBQWtGO0FBQ2xGLHlEQUcyQjtBQThFM0IsTUFBTSxhQUFhLEdBQUcsQ0FBQyxPQUF3QixFQUFFLEVBQUUsQ0FDakQsT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNO0lBQ3JCLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsTUFBTSxFQUFFO0lBQzdCLENBQUMsQ0FBQyxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxpQkFBaUIsRUFBRSxDQUFBO0FBRXhELE1BQU0sY0FBYyxHQUFHLENBQUMsT0FBd0IsRUFBRSxFQUFFLENBQ2xELE9BQU8sQ0FBQyxJQUFJLEtBQUssTUFBTTtJQUNyQixDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLE1BQU0sRUFBRSxtQkFBbUIsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRTtJQUMzRSxDQUFDLENBQUM7UUFDRSxPQUFPLEVBQUUsSUFBSTtRQUNiLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxpQkFBaUI7UUFDOUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxVQUFVO0tBQ2hDLENBQUE7QUFFUCw2RUFBNkU7QUFDN0Usa0NBQWtDO0FBQ2xDLE1BQU0sY0FBYyxHQUFHLENBQUMsT0FBd0IsRUFBVSxFQUFFLENBQzFELE9BQU8sQ0FBQyxJQUFJLEtBQUssTUFBTTtJQUNyQixDQUFDLENBQUMsNkJBQTZCLE9BQU8sQ0FBQyxNQUFNLEVB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
|