@final-commerce/common 2.8.0 → 2.8.1-online.2

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/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  BookingResourceStatus: () => BookingResourceStatus,
37
37
  BookingRulesError: () => BookingRulesError,
38
38
  BookingType: () => BookingType,
39
+ CANADA_PROVINCES: () => CANADA_PROVINCES,
39
40
  CONDITION_OPERATORS: () => CONDITION_OPERATORS,
40
41
  COUNTRY_CURRENCIES: () => COUNTRY_CURRENCIES,
41
42
  CURRENCY_CONFIG: () => CURRENCY_CONFIG,
@@ -52,6 +53,7 @@ __export(index_exports, {
52
53
  DualWriteStrategy: () => DualWriteStrategy,
53
54
  EUFixedRates: () => EUFixedRates,
54
55
  EUROPE: () => EUROPE,
56
+ EVERYWHERE_KEY: () => EVERYWHERE_KEY,
55
57
  EXPENSE_DEFAULT_CATEGORIES: () => EXPENSE_DEFAULT_CATEGORIES,
56
58
  EXPENSE_NAME_MAX_LENGTH: () => EXPENSE_NAME_MAX_LENGTH,
57
59
  EXPENSE_PAYMENT_METHOD_PRESETS: () => EXPENSE_PAYMENT_METHOD_PRESETS,
@@ -76,6 +78,7 @@ __export(index_exports, {
76
78
  OtherFixedRates: () => OtherFixedRates,
77
79
  OwnerType: () => OwnerType,
78
80
  PATH_MODES: () => PATH_MODES,
81
+ PAYMENT_CHANNELS: () => PAYMENT_CHANNELS,
79
82
  PAYMENT_STATES: () => PAYMENT_STATES,
80
83
  POS_VALID_INITIAL_STATES: () => POS_VALID_INITIAL_STATES,
81
84
  PaymentMethod: () => PaymentMethod,
@@ -85,6 +88,7 @@ __export(index_exports, {
85
88
  PricingLevel: () => PricingLevel,
86
89
  PricingPlanEnum: () => PricingPlanEnum,
87
90
  PricingRegionEnum: () => PricingRegionEnum,
91
+ ProcessorType: () => ProcessorType,
88
92
  ProductStatus: () => ProductStatus,
89
93
  ProductType: () => ProductType,
90
94
  PublicationAvailability: () => PublicationAvailability,
@@ -103,6 +107,12 @@ __export(index_exports, {
103
107
  SYSTEM_FAMILY_IDS: () => SYSTEM_FAMILY_IDS,
104
108
  SYSTEM_UNITS: () => SYSTEM_UNITS,
105
109
  SYSTEM_UNIT_IDS: () => SYSTEM_UNIT_IDS,
110
+ ShippingChannel: () => ShippingChannel,
111
+ ShippingNoOptionReason: () => ShippingNoOptionReason,
112
+ ShippingOptionType: () => ShippingOptionType,
113
+ ShippingRateType: () => ShippingRateType,
114
+ ShippingZoneError: () => ShippingZoneError,
115
+ ShippingZoneKind: () => ShippingZoneKind,
106
116
  StationStatus: () => StationStatus,
107
117
  StripeCurrencyCode: () => StripeCurrencyCode,
108
118
  StripeStatus: () => StripeStatus,
@@ -110,6 +120,7 @@ __export(index_exports, {
110
120
  TopMetericsSort: () => TopMetericsSort,
111
121
  UserTypes: () => UserTypes,
112
122
  addAmounts: () => addAmounts,
123
+ addressPlaceKeys: () => addressPlaceKeys,
113
124
  adyenLegalEntityRequiresStateOrProvince: () => adyenLegalEntityRequiresStateOrProvince,
114
125
  adyenRequiresJobTitle: () => adyenRequiresJobTitle,
115
126
  adyenStoreRequiresStateOrProvince: () => adyenStoreRequiresStateOrProvince,
@@ -188,25 +199,38 @@ __export(index_exports, {
188
199
  isSystemFamilyCode: () => isSystemFamilyCode,
189
200
  isValidQuantity: () => isValidQuantity,
190
201
  isWhole: () => isWhole,
202
+ lineStockMoves: () => lineStockMoves,
203
+ listCountries: () => listCountries,
204
+ listProvinces: () => listProvinces,
191
205
  mapToLegacyStatus: () => mapToLegacyStatus,
192
206
  maxPrecisionFor: () => maxPrecisionFor,
193
207
  mergeFragment: () => mergeFragment,
194
208
  modifiersSellable: () => modifiersSellable,
195
209
  multiplyAmount: () => multiplyAmount,
210
+ normalizeAddress: () => normalizeAddress,
211
+ normalizeCityKey: () => normalizeCityKey,
212
+ normalizeCountry: () => normalizeCountry,
196
213
  normalizeMongoIdString: () => normalizeMongoIdString,
214
+ normalizePostCode: () => normalizePostCode,
215
+ normalizeProvince: () => normalizeProvince,
197
216
  occupancyIn: () => occupancyIn,
198
217
  parseAmountToMinorUnits: () => parseAmountToMinorUnits,
199
218
  parseCurrencyCode: () => parseCurrencyCode,
200
219
  parseStripeCurrencyCode: () => parseStripeCurrencyCode,
220
+ placeKey: () => placeKey,
201
221
  planWindow: () => planWindow,
202
222
  poolDemand: () => poolDemand,
203
223
  poolOf: () => poolOf,
204
224
  poolShortfall: () => poolShortfall,
205
225
  poolStockAt: () => poolStockAt,
226
+ postCodeMatchesProvince: () => postCodeMatchesProvince,
206
227
  presentAt: () => presentAt,
228
+ priceForTotal: () => priceForTotal,
229
+ provincesForPostCode: () => provincesForPostCode,
207
230
  removeFragment: () => removeFragment,
208
231
  resolveFamily: () => resolveFamily,
209
232
  resolveModifiersForProduct: () => resolveModifiersForProduct,
233
+ resolveShipping: () => resolveShipping,
210
234
  resolveTemplate: () => resolveTemplate,
211
235
  resolveUnit: () => resolveUnit,
212
236
  roundToPrecision: () => roundToPrecision,
@@ -223,6 +247,7 @@ __export(index_exports, {
223
247
  validateConfig: () => validateConfig,
224
248
  validateFragmentCompatibility: () => validateFragmentCompatibility,
225
249
  validateOrderStatuses: () => validateOrderStatuses,
250
+ validateShippingZone: () => validateShippingZone,
226
251
  violatesFinancialInvariant: () => violatesFinancialInvariant,
227
252
  windowEndsAt: () => windowEndsAt,
228
253
  withinPresence: () => withinPresence
@@ -431,6 +456,7 @@ var OrderPlatform = /* @__PURE__ */ ((OrderPlatform2) => {
431
456
  OrderPlatform2["POS"] = "in-store";
432
457
  OrderPlatform2["WOO_COMMERCE"] = "woo-commerce";
433
458
  OrderPlatform2["PUBLIC_API"] = "public-api";
459
+ OrderPlatform2["ONLINE_STORE"] = "online-store";
434
460
  return OrderPlatform2;
435
461
  })(OrderPlatform || {});
436
462
  var CustomerPlatform = /* @__PURE__ */ ((CustomerPlatform2) => {
@@ -717,6 +743,50 @@ var ResourceAvailabilityState = /* @__PURE__ */ ((ResourceAvailabilityState2) =>
717
743
  return ResourceAvailabilityState2;
718
744
  })(ResourceAvailabilityState || {});
719
745
 
746
+ // enums/shipping.enum.ts
747
+ var ShippingZoneKind = /* @__PURE__ */ ((ShippingZoneKind2) => {
748
+ ShippingZoneKind2["COUNTRY"] = "country";
749
+ ShippingZoneKind2["PROVINCE"] = "province";
750
+ ShippingZoneKind2["CITY"] = "city";
751
+ ShippingZoneKind2["EVERYWHERE"] = "everywhere";
752
+ return ShippingZoneKind2;
753
+ })(ShippingZoneKind || {});
754
+ var ShippingOptionType = /* @__PURE__ */ ((ShippingOptionType2) => {
755
+ ShippingOptionType2["SHIP"] = "ship";
756
+ ShippingOptionType2["PICKUP"] = "pickup";
757
+ return ShippingOptionType2;
758
+ })(ShippingOptionType || {});
759
+ var ShippingRateType = /* @__PURE__ */ ((ShippingRateType2) => {
760
+ ShippingRateType2["FLAT"] = "flat";
761
+ return ShippingRateType2;
762
+ })(ShippingRateType || {});
763
+ var ShippingChannel = /* @__PURE__ */ ((ShippingChannel2) => {
764
+ ShippingChannel2["WEB"] = "web";
765
+ ShippingChannel2["POS"] = "pos";
766
+ return ShippingChannel2;
767
+ })(ShippingChannel || {});
768
+ var ShippingNoOptionReason = /* @__PURE__ */ ((ShippingNoOptionReason2) => {
769
+ ShippingNoOptionReason2["INCOMPLETE"] = "INCOMPLETE";
770
+ ShippingNoOptionReason2["ADDRESS_MISMATCH"] = "ADDRESS_MISMATCH";
771
+ ShippingNoOptionReason2["NO_COVERAGE"] = "NO_COVERAGE";
772
+ ShippingNoOptionReason2["BLOCKED"] = "BLOCKED";
773
+ ShippingNoOptionReason2["NO_OPTION"] = "NO_OPTION";
774
+ return ShippingNoOptionReason2;
775
+ })(ShippingNoOptionReason || {});
776
+ var ShippingZoneError = /* @__PURE__ */ ((ShippingZoneError2) => {
777
+ ShippingZoneError2["NO_PLACES"] = "NO_PLACES";
778
+ ShippingZoneError2["INVALID_PLACE"] = "INVALID_PLACE";
779
+ ShippingZoneError2["DUPLICATE_PLACE"] = "DUPLICATE_PLACE";
780
+ ShippingZoneError2["NO_WEBSITE_OPTION"] = "NO_WEBSITE_OPTION";
781
+ ShippingZoneError2["INVALID_PRICE"] = "INVALID_PRICE";
782
+ ShippingZoneError2["INVALID_DAYS"] = "INVALID_DAYS";
783
+ ShippingZoneError2["INVALID_TIERS"] = "INVALID_TIERS";
784
+ ShippingZoneError2["NO_CHANNEL"] = "NO_CHANNEL";
785
+ ShippingZoneError2["PICKUP_WITHOUT_STORE"] = "PICKUP_WITHOUT_STORE";
786
+ ShippingZoneError2["MISSING_NAME"] = "MISSING_NAME";
787
+ return ShippingZoneError2;
788
+ })(ShippingZoneError || {});
789
+
720
790
  // utils/currency.util.ts
721
791
  var CURRENCY_CONFIG = {
722
792
  // Major currencies with 2 decimal places
@@ -1892,6 +1962,48 @@ function compositeReservationMismatches(order) {
1892
1962
  });
1893
1963
  }
1894
1964
 
1965
+ // utils/line-stock-moves.util.ts
1966
+ function lineStockMoves(item, quantity, reservations = []) {
1967
+ const sign = Math.sign(quantity);
1968
+ const pool = (stockVariantId) => stockVariantId ? { stockVariantId: stockVariantId.toString() } : {};
1969
+ if (!item.components?.length)
1970
+ return [
1971
+ {
1972
+ productId: item.productId.toString(),
1973
+ variantId: item.variantId.toString(),
1974
+ fields: {
1975
+ quantity,
1976
+ ...item.stockQuantity != null ? { stockQuantity: sign * item.stockQuantity, unitId: item.unitId?.toString() } : {},
1977
+ ...pool(item.stockVariantId)
1978
+ }
1979
+ }
1980
+ ];
1981
+ const booked = new Set(
1982
+ reservations.filter((reservation) => reservation.lineItemInternalId && reservation.lineItemInternalId === item.internalId).map((reservation) => reservation.componentIndex)
1983
+ );
1984
+ return item.components.flatMap((component, componentIndex) => {
1985
+ if (booked.has(componentIndex)) return [];
1986
+ if (component.unitId && component.stockQuantity == null)
1987
+ throw new Error(
1988
+ `Composite ${item.productId?.toString()} component ${component.variantId.toString()} is sold in ${component.unitId.toString()} but carries no stockQuantity`
1989
+ );
1990
+ return [
1991
+ {
1992
+ productId: component.productId.toString(),
1993
+ variantId: component.variantId.toString(),
1994
+ componentIndex,
1995
+ fields: {
1996
+ quantity: component.quantity * quantity,
1997
+ ...component.stockQuantity != null ? { stockQuantity: component.stockQuantity * quantity, unitId: component.unitId?.toString() } : {},
1998
+ ...pool(component.stockVariantId),
1999
+ ...item.productId ? { compositeProductId: item.productId.toString() } : {},
2000
+ ...item.name ? { compositeName: item.name } : {}
2001
+ }
2002
+ }
2003
+ ];
2004
+ });
2005
+ }
2006
+
1895
2007
  // units/family-code.ts
1896
2008
  var MAX_SLUG_LENGTH = 32;
1897
2009
  var FALLBACK_SLUG = "family";
@@ -3795,6 +3907,321 @@ function planWindow({ rule, timeZone, presence, startAt, endAt, now, busy = [] }
3795
3907
  }
3796
3908
  var holdExpiresAt = (rule, now) => new Date(now.getTime() + (rule.window.holdMinutes ?? DEFAULT_HOLD_MINUTES) * 6e4);
3797
3909
 
3910
+ // shipping/fold-text.ts
3911
+ function foldText(value) {
3912
+ return value.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/&/g, " and ").replace(/[^a-z0-9]+/g, " ").trim();
3913
+ }
3914
+
3915
+ // shipping/countries.ts
3916
+ var NOT_COUNTRIES = /* @__PURE__ */ new Set(["AC", "CP", "CQ", "DG", "EA", "EU", "EZ", "IC", "QO", "TA", "UN", "XA", "XB", "ZZ"]);
3917
+ var ALIASES = {
3918
+ canada: "CA",
3919
+ "united states": "US",
3920
+ usa: "US",
3921
+ "united states of america": "US",
3922
+ uk: "GB",
3923
+ "great britain": "GB",
3924
+ england: "GB",
3925
+ uae: "AE",
3926
+ can: "CA",
3927
+ mex: "MX",
3928
+ aus: "AU",
3929
+ gbr: "GB"
3930
+ };
3931
+ var NAME_LOCALES = ["en", "fr"];
3932
+ function regionNames(locale) {
3933
+ try {
3934
+ return new Intl.DisplayNames([locale], { type: "region", fallback: "none" });
3935
+ } catch {
3936
+ return null;
3937
+ }
3938
+ }
3939
+ function canonicalCode(code) {
3940
+ try {
3941
+ return new Intl.Locale(`und-${code}`).maximize().region ?? code;
3942
+ } catch {
3943
+ return code;
3944
+ }
3945
+ }
3946
+ function allCodes() {
3947
+ const names = regionNames("en");
3948
+ if (!names) return [];
3949
+ const codes2 = [];
3950
+ for (let a = 65; a <= 90; a++) {
3951
+ for (let b = 65; b <= 90; b++) {
3952
+ const code = String.fromCharCode(a, b);
3953
+ if (!NOT_COUNTRIES.has(code) && names.of(code) && canonicalCode(code) === code) codes2.push(code);
3954
+ }
3955
+ }
3956
+ return codes2;
3957
+ }
3958
+ var lookup = null;
3959
+ var codes = null;
3960
+ function countryCodes() {
3961
+ codes ??= allCodes();
3962
+ return codes;
3963
+ }
3964
+ function countryLookup() {
3965
+ if (lookup) return lookup;
3966
+ lookup = new Map(Object.entries(ALIASES));
3967
+ for (const locale of NAME_LOCALES) {
3968
+ const names = regionNames(locale);
3969
+ if (!names) continue;
3970
+ for (const code of countryCodes()) {
3971
+ const name = names.of(code);
3972
+ if (name) lookup.set(foldText(name), code);
3973
+ }
3974
+ }
3975
+ return lookup;
3976
+ }
3977
+ function normalizeCountry(value) {
3978
+ const text = (value ?? "").trim();
3979
+ if (!text) return void 0;
3980
+ const upper = text.toUpperCase();
3981
+ if (upper.length === 2) {
3982
+ if (NOT_COUNTRIES.has(upper)) return void 0;
3983
+ if (countryCodes().length === 0) return /^[A-Z]{2}$/.test(upper) ? upper : void 0;
3984
+ const code = canonicalCode(upper);
3985
+ if (countryCodes().includes(code)) return code;
3986
+ }
3987
+ return countryLookup().get(foldText(text));
3988
+ }
3989
+ function listCountries(locale = "en") {
3990
+ const names = regionNames(locale) ?? regionNames("en");
3991
+ return countryCodes().map((code) => ({ code, name: names?.of(code) ?? code })).sort((a, b) => a.name.localeCompare(b.name, locale));
3992
+ }
3993
+
3994
+ // shipping/provinces.ts
3995
+ var CANADA_PROVINCES = [
3996
+ { code: "AB", name: "Alberta", nameFr: "Alberta", postalLetters: ["T"] },
3997
+ { code: "BC", name: "British Columbia", nameFr: "Colombie-Britannique", postalLetters: ["V"] },
3998
+ { code: "MB", name: "Manitoba", nameFr: "Manitoba", postalLetters: ["R"] },
3999
+ { code: "NB", name: "New Brunswick", nameFr: "Nouveau-Brunswick", postalLetters: ["E"] },
4000
+ {
4001
+ code: "NL",
4002
+ name: "Newfoundland and Labrador",
4003
+ nameFr: "Terre-Neuve-et-Labrador",
4004
+ postalLetters: ["A"],
4005
+ aliases: ["NF", "LB", "Newfoundland", "Labrador"]
4006
+ },
4007
+ { code: "NS", name: "Nova Scotia", nameFr: "Nouvelle-\xC9cosse", postalLetters: ["B"] },
4008
+ { code: "NT", name: "Northwest Territories", nameFr: "Territoires du Nord-Ouest", postalLetters: ["X"] },
4009
+ { code: "NU", name: "Nunavut", nameFr: "Nunavut", postalLetters: ["X"] },
4010
+ { code: "ON", name: "Ontario", nameFr: "Ontario", postalLetters: ["K", "L", "M", "N", "P"] },
4011
+ {
4012
+ code: "PE",
4013
+ name: "Prince Edward Island",
4014
+ nameFr: "\xCEle-du-Prince-\xC9douard",
4015
+ postalLetters: ["C"],
4016
+ aliases: ["PEI"]
4017
+ },
4018
+ { code: "QC", name: "Quebec", nameFr: "Qu\xE9bec", postalLetters: ["G", "H", "J"], aliases: ["PQ"] },
4019
+ { code: "SK", name: "Saskatchewan", nameFr: "Saskatchewan", postalLetters: ["S"] },
4020
+ { code: "YT", name: "Yukon", nameFr: "Yukon", postalLetters: ["Y"], aliases: ["YK"] }
4021
+ ];
4022
+ var PROVINCES_BY_COUNTRY = { CA: CANADA_PROVINCES };
4023
+ var lookups = /* @__PURE__ */ new Map();
4024
+ function provinceLookup(country) {
4025
+ const provinces = PROVINCES_BY_COUNTRY[country];
4026
+ if (!provinces) return void 0;
4027
+ let lookup2 = lookups.get(country);
4028
+ if (!lookup2) {
4029
+ lookup2 = /* @__PURE__ */ new Map();
4030
+ for (const p of provinces) {
4031
+ for (const label of [p.code, p.name, p.nameFr, ...p.aliases ?? []]) lookup2.set(foldText(label), p.code);
4032
+ }
4033
+ lookups.set(country, lookup2);
4034
+ }
4035
+ return lookup2;
4036
+ }
4037
+ function normalizeProvince(country, value) {
4038
+ if (!country || !value?.trim()) return void 0;
4039
+ return provinceLookup(country)?.get(foldText(value));
4040
+ }
4041
+ function listProvinces(country) {
4042
+ return PROVINCES_BY_COUNTRY[country] ?? [];
4043
+ }
4044
+ function provincesForPostCode(country, postCode) {
4045
+ const letter = postCode?.charAt(0);
4046
+ if (!country || !letter) return [];
4047
+ return listProvinces(country).filter((p) => p.postalLetters.includes(letter)).map((p) => p.code);
4048
+ }
4049
+
4050
+ // shipping/normalize.ts
4051
+ var EVERYWHERE_KEY = "*";
4052
+ function normalizeCityKey(value) {
4053
+ const words = foldText(value ?? "").split(" ").filter(Boolean).map((word) => word === "saint" ? "st" : word === "sainte" ? "ste" : word);
4054
+ return words.length ? words.join("") : void 0;
4055
+ }
4056
+ function normalizePostCode(value) {
4057
+ const compact = (value ?? "").replace(/[^0-9a-z]/gi, "").toUpperCase();
4058
+ return compact || void 0;
4059
+ }
4060
+ function normalizeAddress(address) {
4061
+ if (!address) return {};
4062
+ const country = normalizeCountry(address.country);
4063
+ const postCode = normalizePostCode(address.postCode ?? address.postalCode ?? address.zip);
4064
+ const fromPostCode = provincesForPostCode(country, postCode);
4065
+ const province = normalizeProvince(country, address.state ?? address.province) ?? (fromPostCode.length === 1 ? fromPostCode[0] : void 0);
4066
+ return { country, province, cityKey: normalizeCityKey(address.city), postCode };
4067
+ }
4068
+ function postCodeMatchesProvince(address) {
4069
+ const candidates = provincesForPostCode(address.country, address.postCode);
4070
+ if (!address.province || candidates.length === 0) return true;
4071
+ return candidates.includes(address.province);
4072
+ }
4073
+ function placeKey(place) {
4074
+ const country = normalizeCountry(place.country);
4075
+ if (!country) return void 0;
4076
+ if (!place.province) return country;
4077
+ const province = normalizeProvince(country, place.province);
4078
+ if (!province) return void 0;
4079
+ if (!place.city) return `${country}-${province}`;
4080
+ const city = normalizeCityKey(place.city);
4081
+ return city ? `${country}-${province}/${city}` : void 0;
4082
+ }
4083
+ function addressPlaceKeys(address) {
4084
+ const keys = [];
4085
+ if (address.country && address.province && address.cityKey) {
4086
+ keys.push(`${address.country}-${address.province}/${address.cityKey}`);
4087
+ }
4088
+ if (address.country && address.province) keys.push(`${address.country}-${address.province}`);
4089
+ if (address.country) keys.push(address.country);
4090
+ keys.push(EVERYWHERE_KEY);
4091
+ return keys;
4092
+ }
4093
+
4094
+ // shipping/resolve.ts
4095
+ function resolveShipping({ zones, address, itemsTotal, channel }) {
4096
+ const shipsToCountries = countriesShippedTo(zones);
4097
+ const none = (reason, zone2) => ({
4098
+ zoneId: zone2?.id ?? null,
4099
+ options: [],
4100
+ reason,
4101
+ message: zone2?.message?.trim() || null,
4102
+ shipsToCountries
4103
+ });
4104
+ const normalized = normalizeAddress(address);
4105
+ if (!normalized.country) return none("INCOMPLETE" /* INCOMPLETE */);
4106
+ if (!postCodeMatchesProvince(normalized)) return none("ADDRESS_MISMATCH" /* ADDRESS_MISMATCH */);
4107
+ const zone = findZone(zones, addressPlaceKeys(normalized));
4108
+ if (!zone) return none("NO_COVERAGE" /* NO_COVERAGE */);
4109
+ const offered = zone.options.filter((option) => isOffered(option, zone, channel));
4110
+ if (offered.length === 0) {
4111
+ return none(zone.ships ? "NO_OPTION" /* NO_OPTION */ : "BLOCKED" /* BLOCKED */, zone);
4112
+ }
4113
+ const quotes = offered.map((option) => quote(option, zone.id, itemsTotal));
4114
+ const ship = quotes.filter((q) => q.type === "ship" /* SHIP */).sort((a, b) => a.amount - b.amount);
4115
+ const pickup = quotes.filter((q) => q.type === "pickup" /* PICKUP */).sort((a, b) => a.amount - b.amount);
4116
+ return {
4117
+ zoneId: zone.id,
4118
+ options: [...ship, ...pickup],
4119
+ reason: null,
4120
+ message: zone.ships ? null : zone.message?.trim() || null,
4121
+ shipsToCountries
4122
+ };
4123
+ }
4124
+ function findZone(zones, keys) {
4125
+ const byKey = /* @__PURE__ */ new Map();
4126
+ for (const zone of zones) {
4127
+ for (const key of zoneKeys(zone)) if (!byKey.has(key)) byKey.set(key, zone);
4128
+ }
4129
+ for (const key of keys) {
4130
+ const zone = byKey.get(key);
4131
+ if (zone) return zone;
4132
+ }
4133
+ return void 0;
4134
+ }
4135
+ function zoneKeys(zone) {
4136
+ if (zone.kind === "everywhere" /* EVERYWHERE */) return [EVERYWHERE_KEY];
4137
+ return zone.places.map(placeKey).filter((key) => Boolean(key));
4138
+ }
4139
+ function isOffered(option, zone, channel) {
4140
+ if (!option.channels.some((c) => c === channel)) return false;
4141
+ if (option.type === "pickup" /* PICKUP */) return channel !== "pos" /* POS */;
4142
+ return zone.ships;
4143
+ }
4144
+ function priceForTotal(option, itemsTotal) {
4145
+ const tiers = [...option.priceByTotal ?? []].sort((a, b) => a.from - b.from);
4146
+ let price = option.price;
4147
+ for (const tier of tiers) if (itemsTotal >= tier.from) price = tier.price;
4148
+ if (option.freeOver != null && itemsTotal >= option.freeOver) return 0;
4149
+ return price;
4150
+ }
4151
+ function quote(option, zoneId, itemsTotal) {
4152
+ const amount = priceForTotal(option, itemsTotal);
4153
+ const freeOver = option.freeOver ?? null;
4154
+ return {
4155
+ optionId: option.id,
4156
+ zoneId,
4157
+ type: option.type,
4158
+ name: option.name?.trim() || null,
4159
+ amount,
4160
+ amountToFree: freeOver != null && amount > 0 ? freeOver - itemsTotal : null,
4161
+ minDays: option.minDays ?? null,
4162
+ maxDays: option.maxDays ?? null,
4163
+ outletId: option.outletId ?? null
4164
+ };
4165
+ }
4166
+ function countriesShippedTo(zones) {
4167
+ const countries = /* @__PURE__ */ new Set();
4168
+ for (const zone of zones) {
4169
+ if (!zone.ships) continue;
4170
+ for (const key of zoneKeys(zone)) if (key !== EVERYWHERE_KEY) countries.add(key.slice(0, 2));
4171
+ }
4172
+ return [...countries].sort();
4173
+ }
4174
+
4175
+ // shipping/validate.ts
4176
+ function validateShippingZone(zone) {
4177
+ const problems = [...placeProblems(zone)];
4178
+ for (const option of zone.options) problems.push(...optionProblems(option));
4179
+ const reachesWebsite = zone.options.some((option) => option.channels.includes("web" /* WEB */));
4180
+ if (zone.ships && !reachesWebsite) problems.push({ code: "NO_WEBSITE_OPTION" /* NO_WEBSITE_OPTION */ });
4181
+ return problems;
4182
+ }
4183
+ function placeProblems(zone) {
4184
+ if (zone.kind === "everywhere" /* EVERYWHERE */) {
4185
+ return zone.places.length ? [{ code: "INVALID_PLACE" /* INVALID_PLACE */ }] : [];
4186
+ }
4187
+ if (zone.places.length === 0) return [{ code: "NO_PLACES" /* NO_PLACES */ }];
4188
+ if (!zone.places.every((place) => fitsKind(place, zone.kind))) return [{ code: "INVALID_PLACE" /* INVALID_PLACE */ }];
4189
+ const keys = zone.places.map(placeKey);
4190
+ if (keys.some((key) => !key)) return [{ code: "INVALID_PLACE" /* INVALID_PLACE */ }];
4191
+ if (new Set(keys).size !== keys.length) return [{ code: "DUPLICATE_PLACE" /* DUPLICATE_PLACE */ }];
4192
+ return [];
4193
+ }
4194
+ function fitsKind(place, kind) {
4195
+ const hasProvince = Boolean(place.province?.trim());
4196
+ const hasCity = Boolean(place.city?.trim());
4197
+ if (kind === "country" /* COUNTRY */) return !hasProvince && !hasCity;
4198
+ if (kind === "province" /* PROVINCE */) return hasProvince && !hasCity;
4199
+ if (kind === "city" /* CITY */) return hasProvince && hasCity;
4200
+ return false;
4201
+ }
4202
+ var isAmount = (value) => Number.isInteger(value) && value >= 0;
4203
+ var isDays = (value) => value == null || isAmount(value);
4204
+ function optionProblems(option) {
4205
+ const problems = [];
4206
+ const isPickup = option.type === "pickup" /* PICKUP */;
4207
+ if (!isAmount(option.price) || option.freeOver != null && !isAmount(option.freeOver)) {
4208
+ problems.push("INVALID_PRICE" /* INVALID_PRICE */);
4209
+ }
4210
+ if (!isPickup && !option.name?.trim()) problems.push("MISSING_NAME" /* MISSING_NAME */);
4211
+ if (!isDays(option.minDays) || !isDays(option.maxDays) || (option.minDays ?? 0) > (option.maxDays ?? Infinity)) {
4212
+ problems.push("INVALID_DAYS" /* INVALID_DAYS */);
4213
+ }
4214
+ if (!tiersAreValid(option)) problems.push("INVALID_TIERS" /* INVALID_TIERS */);
4215
+ if (option.channels.length === 0) problems.push("NO_CHANNEL" /* NO_CHANNEL */);
4216
+ if (isPickup && !option.outletId) problems.push("PICKUP_WITHOUT_STORE" /* PICKUP_WITHOUT_STORE */);
4217
+ return problems.map((code) => ({ code, optionId: option.id }));
4218
+ }
4219
+ function tiersAreValid(option) {
4220
+ const tiers = option.priceByTotal ?? [];
4221
+ const froms = tiers.map((tier) => tier.from);
4222
+ return tiers.every((tier) => Number.isInteger(tier.from) && tier.from > 0 && isAmount(tier.price)) && new Set(froms).size === froms.length;
4223
+ }
4224
+
3798
4225
  // constants/adyen.constants.ts
3799
4226
  var ADYEN_ROLLOUT_COUNTRY_CODES = /* @__PURE__ */ new Set(["CA"]);
3800
4227
  function isAdyenRolloutCountry(alpha2) {
@@ -3844,6 +4271,27 @@ var BNPL_SKIP_REASON = {
3844
4271
  NO_BUSINESS_LINE: "no_business_line"
3845
4272
  };
3846
4273
 
4274
+ // pos-types/active/ProcessorType.ts
4275
+ var ProcessorType = /* @__PURE__ */ ((ProcessorType2) => {
4276
+ ProcessorType2["FINAL_PAY"] = "Terminal";
4277
+ ProcessorType2["TAP_TO_PAY"] = "TapToPay";
4278
+ ProcessorType2["MOTO"] = "Moto";
4279
+ ProcessorType2["CLOUD"] = "Cloud";
4280
+ ProcessorType2["BLUETOOTH"] = "Bluetooth";
4281
+ ProcessorType2["CASH"] = "Cash";
4282
+ ProcessorType2["CUSTOM_PAY"] = "CustomPay";
4283
+ ProcessorType2["VENDARA"] = "Vendara";
4284
+ ProcessorType2["REDEEM"] = "Redeem";
4285
+ ProcessorType2["INTEGRATION"] = "Integration";
4286
+ ProcessorType2["PAYMENT_LINK"] = "PaymentLink";
4287
+ ProcessorType2["ONLINE_PAYMENT"] = "OnlinePayment";
4288
+ ProcessorType2["EXTERNAL"] = "External";
4289
+ return ProcessorType2;
4290
+ })(ProcessorType || {});
4291
+
4292
+ // constants/payment-channel.constants.ts
4293
+ var PAYMENT_CHANNELS = ["inPerson", "online"];
4294
+
3847
4295
  // constants/expense.constants.ts
3848
4296
  var EXPENSE_PAYMENT_METHOD_PRESETS = [
3849
4297
  "Cash",
@@ -3886,6 +4334,7 @@ var EXPENSE_DEFAULT_CATEGORIES = [
3886
4334
  BookingResourceStatus,
3887
4335
  BookingRulesError,
3888
4336
  BookingType,
4337
+ CANADA_PROVINCES,
3889
4338
  CONDITION_OPERATORS,
3890
4339
  COUNTRY_CURRENCIES,
3891
4340
  CURRENCY_CONFIG,
@@ -3902,6 +4351,7 @@ var EXPENSE_DEFAULT_CATEGORIES = [
3902
4351
  DualWriteStrategy,
3903
4352
  EUFixedRates,
3904
4353
  EUROPE,
4354
+ EVERYWHERE_KEY,
3905
4355
  EXPENSE_DEFAULT_CATEGORIES,
3906
4356
  EXPENSE_NAME_MAX_LENGTH,
3907
4357
  EXPENSE_PAYMENT_METHOD_PRESETS,
@@ -3926,6 +4376,7 @@ var EXPENSE_DEFAULT_CATEGORIES = [
3926
4376
  OtherFixedRates,
3927
4377
  OwnerType,
3928
4378
  PATH_MODES,
4379
+ PAYMENT_CHANNELS,
3929
4380
  PAYMENT_STATES,
3930
4381
  POS_VALID_INITIAL_STATES,
3931
4382
  PaymentMethod,
@@ -3935,6 +4386,7 @@ var EXPENSE_DEFAULT_CATEGORIES = [
3935
4386
  PricingLevel,
3936
4387
  PricingPlanEnum,
3937
4388
  PricingRegionEnum,
4389
+ ProcessorType,
3938
4390
  ProductStatus,
3939
4391
  ProductType,
3940
4392
  PublicationAvailability,
@@ -3953,6 +4405,12 @@ var EXPENSE_DEFAULT_CATEGORIES = [
3953
4405
  SYSTEM_FAMILY_IDS,
3954
4406
  SYSTEM_UNITS,
3955
4407
  SYSTEM_UNIT_IDS,
4408
+ ShippingChannel,
4409
+ ShippingNoOptionReason,
4410
+ ShippingOptionType,
4411
+ ShippingRateType,
4412
+ ShippingZoneError,
4413
+ ShippingZoneKind,
3956
4414
  StationStatus,
3957
4415
  StripeCurrencyCode,
3958
4416
  StripeStatus,
@@ -3960,6 +4418,7 @@ var EXPENSE_DEFAULT_CATEGORIES = [
3960
4418
  TopMetericsSort,
3961
4419
  UserTypes,
3962
4420
  addAmounts,
4421
+ addressPlaceKeys,
3963
4422
  adyenLegalEntityRequiresStateOrProvince,
3964
4423
  adyenRequiresJobTitle,
3965
4424
  adyenStoreRequiresStateOrProvince,
@@ -4038,25 +4497,38 @@ var EXPENSE_DEFAULT_CATEGORIES = [
4038
4497
  isSystemFamilyCode,
4039
4498
  isValidQuantity,
4040
4499
  isWhole,
4500
+ lineStockMoves,
4501
+ listCountries,
4502
+ listProvinces,
4041
4503
  mapToLegacyStatus,
4042
4504
  maxPrecisionFor,
4043
4505
  mergeFragment,
4044
4506
  modifiersSellable,
4045
4507
  multiplyAmount,
4508
+ normalizeAddress,
4509
+ normalizeCityKey,
4510
+ normalizeCountry,
4046
4511
  normalizeMongoIdString,
4512
+ normalizePostCode,
4513
+ normalizeProvince,
4047
4514
  occupancyIn,
4048
4515
  parseAmountToMinorUnits,
4049
4516
  parseCurrencyCode,
4050
4517
  parseStripeCurrencyCode,
4518
+ placeKey,
4051
4519
  planWindow,
4052
4520
  poolDemand,
4053
4521
  poolOf,
4054
4522
  poolShortfall,
4055
4523
  poolStockAt,
4524
+ postCodeMatchesProvince,
4056
4525
  presentAt,
4526
+ priceForTotal,
4527
+ provincesForPostCode,
4057
4528
  removeFragment,
4058
4529
  resolveFamily,
4059
4530
  resolveModifiersForProduct,
4531
+ resolveShipping,
4060
4532
  resolveTemplate,
4061
4533
  resolveUnit,
4062
4534
  roundToPrecision,
@@ -4073,6 +4545,7 @@ var EXPENSE_DEFAULT_CATEGORIES = [
4073
4545
  validateConfig,
4074
4546
  validateFragmentCompatibility,
4075
4547
  validateOrderStatuses,
4548
+ validateShippingZone,
4076
4549
  violatesFinancialInvariant,
4077
4550
  windowEndsAt,
4078
4551
  withinPresence