@rovela-ai/sdk 0.19.8 → 0.21.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/dist/admin/api/index.d.ts +4 -2
- package/dist/admin/api/index.d.ts.map +1 -1
- package/dist/admin/api/index.js +4 -2
- package/dist/admin/api/index.js.map +1 -1
- package/dist/admin/api/integrations.d.ts +60 -0
- package/dist/admin/api/integrations.d.ts.map +1 -0
- package/dist/admin/api/integrations.js +337 -0
- package/dist/admin/api/integrations.js.map +1 -0
- package/dist/admin/api/orders.d.ts +49 -0
- package/dist/admin/api/orders.d.ts.map +1 -1
- package/dist/admin/api/orders.js +293 -30
- package/dist/admin/api/orders.js.map +1 -1
- package/dist/admin/api/settings.d.ts.map +1 -1
- package/dist/admin/api/settings.js +6 -1
- package/dist/admin/api/settings.js.map +1 -1
- package/dist/admin/api/shipping.d.ts +49 -0
- package/dist/admin/api/shipping.d.ts.map +1 -1
- package/dist/admin/api/shipping.js +144 -0
- package/dist/admin/api/shipping.js.map +1 -1
- package/dist/admin/components/AdminNav.d.ts.map +1 -1
- package/dist/admin/components/AdminNav.js +3 -1
- package/dist/admin/components/AdminNav.js.map +1 -1
- package/dist/admin/components/AdminSelect.d.ts.map +1 -1
- package/dist/admin/components/AdminSelect.js +2 -0
- package/dist/admin/components/AdminSelect.js.map +1 -1
- package/dist/admin/components/DiscountsManager.d.ts.map +1 -1
- package/dist/admin/components/DiscountsManager.js +2 -2
- package/dist/admin/components/DiscountsManager.js.map +1 -1
- package/dist/admin/components/IntegrationsManager.d.ts +5 -0
- package/dist/admin/components/IntegrationsManager.d.ts.map +1 -0
- package/dist/admin/components/IntegrationsManager.js +138 -0
- package/dist/admin/components/IntegrationsManager.js.map +1 -0
- package/dist/admin/components/OrderDetails.d.ts.map +1 -1
- package/dist/admin/components/OrderDetails.js +75 -3
- package/dist/admin/components/OrderDetails.js.map +1 -1
- package/dist/admin/components/ShippingSettings.d.ts.map +1 -1
- package/dist/admin/components/ShippingSettings.js +316 -137
- package/dist/admin/components/ShippingSettings.js.map +1 -1
- package/dist/admin/components/UsersTable.d.ts.map +1 -1
- package/dist/admin/components/UsersTable.js +2 -0
- package/dist/admin/components/UsersTable.js.map +1 -1
- package/dist/admin/components/VariantManager.d.ts.map +1 -1
- package/dist/admin/components/VariantManager.js +8 -2
- package/dist/admin/components/VariantManager.js.map +1 -1
- package/dist/admin/components/index.d.ts +2 -0
- package/dist/admin/components/index.d.ts.map +1 -1
- package/dist/admin/components/index.js +1 -0
- package/dist/admin/components/index.js.map +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +2 -0
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/integrations/brevo-logo.d.ts +11 -0
- package/dist/admin/integrations/brevo-logo.d.ts.map +1 -0
- package/dist/admin/integrations/brevo-logo.js +11 -0
- package/dist/admin/integrations/brevo-logo.js.map +1 -0
- package/dist/admin/integrations/registry.d.ts +116 -0
- package/dist/admin/integrations/registry.d.ts.map +1 -0
- package/dist/admin/integrations/registry.js +389 -0
- package/dist/admin/integrations/registry.js.map +1 -0
- package/dist/admin/types.d.ts +13 -0
- package/dist/admin/types.d.ts.map +1 -1
- package/dist/checkout/api/colissimo-relays.d.ts +24 -0
- package/dist/checkout/api/colissimo-relays.d.ts.map +1 -0
- package/dist/checkout/api/colissimo-relays.js +60 -0
- package/dist/checkout/api/colissimo-relays.js.map +1 -0
- package/dist/checkout/api/manual.d.ts.map +1 -1
- package/dist/checkout/api/manual.js +21 -5
- package/dist/checkout/api/manual.js.map +1 -1
- package/dist/checkout/api/shipping.d.ts.map +1 -1
- package/dist/checkout/api/shipping.js +60 -82
- package/dist/checkout/api/shipping.js.map +1 -1
- package/dist/checkout/components/CheckoutFlow.d.ts.map +1 -1
- package/dist/checkout/components/CheckoutFlow.js +141 -3
- package/dist/checkout/components/CheckoutFlow.js.map +1 -1
- package/dist/checkout/components/ShippingOptions.d.ts +8 -1
- package/dist/checkout/components/ShippingOptions.d.ts.map +1 -1
- package/dist/checkout/components/ShippingOptions.js +40 -11
- package/dist/checkout/components/ShippingOptions.js.map +1 -1
- package/dist/checkout/components/colissimo-logo.d.ts +12 -0
- package/dist/checkout/components/colissimo-logo.d.ts.map +1 -0
- package/dist/checkout/components/colissimo-logo.js +12 -0
- package/dist/checkout/components/colissimo-logo.js.map +1 -0
- package/dist/checkout/server/create-checkout-session.d.ts.map +1 -1
- package/dist/checkout/server/create-checkout-session.js +55 -13
- package/dist/checkout/server/create-checkout-session.js.map +1 -1
- package/dist/checkout/server/handle-webhook.js +7 -3
- package/dist/checkout/server/handle-webhook.js.map +1 -1
- package/dist/checkout/server/order-service.d.ts.map +1 -1
- package/dist/checkout/server/order-service.js +25 -0
- package/dist/checkout/server/order-service.js.map +1 -1
- package/dist/checkout/server/pricing.d.ts +20 -0
- package/dist/checkout/server/pricing.d.ts.map +1 -1
- package/dist/checkout/server/pricing.js +51 -5
- package/dist/checkout/server/pricing.js.map +1 -1
- package/dist/checkout/types.d.ts +57 -1
- package/dist/checkout/types.d.ts.map +1 -1
- package/dist/core/db/queries.d.ts +143 -21
- package/dist/core/db/queries.d.ts.map +1 -1
- package/dist/core/db/queries.js +228 -1
- package/dist/core/db/queries.js.map +1 -1
- package/dist/core/db/schema.d.ts +323 -5
- package/dist/core/db/schema.d.ts.map +1 -1
- package/dist/core/db/schema.js +70 -0
- package/dist/core/db/schema.js.map +1 -1
- package/dist/core/i18n/messages/en.d.ts.map +1 -1
- package/dist/core/i18n/messages/en.js +132 -0
- package/dist/core/i18n/messages/en.js.map +1 -1
- package/dist/core/i18n/messages/fr.d.ts.map +1 -1
- package/dist/core/i18n/messages/fr.js +132 -0
- package/dist/core/i18n/messages/fr.js.map +1 -1
- package/dist/products/api/products.d.ts +3 -1
- package/dist/products/api/products.d.ts.map +1 -1
- package/dist/products/api/products.js +29 -3
- package/dist/products/api/products.js.map +1 -1
- package/dist/products/components/VariantSelector.d.ts.map +1 -1
- package/dist/products/components/VariantSelector.js +4 -1
- package/dist/products/components/VariantSelector.js.map +1 -1
- package/dist/products/hooks/useProducts.d.ts +3 -1
- package/dist/products/hooks/useProducts.d.ts.map +1 -1
- package/dist/products/hooks/useProducts.js +4 -2
- package/dist/products/hooks/useProducts.js.map +1 -1
- package/dist/shipping/colissimo.d.ts +166 -0
- package/dist/shipping/colissimo.d.ts.map +1 -0
- package/dist/shipping/colissimo.js +424 -0
- package/dist/shipping/colissimo.js.map +1 -0
- package/dist/shipping/index.d.ts +4 -1
- package/dist/shipping/index.d.ts.map +1 -1
- package/dist/shipping/index.js +3 -1
- package/dist/shipping/index.js.map +1 -1
- package/docs/changelog/epoch-07-shipping-carriers.md +125 -0
- package/docs/changelog/epoch-08-integrations.md +125 -0
- package/package.json +6 -1
- package/templates/store-template/.claude/skills/build-feature/SKILL.md +19 -0
- package/templates/store-template/.claude/skills/manage-products/SKILL.md +1 -1
- package/templates/store-template/app/admin/(dashboard)/integrations/page.tsx +17 -0
- package/templates/store-template/app/api/admin/integrations/route.ts +9 -0
- package/templates/store-template/app/api/admin/orders/[id]/colissimo-defaults/route.ts +6 -0
- package/templates/store-template/app/api/admin/orders/[id]/label/route.ts +6 -0
- package/templates/store-template/app/api/admin/orders/[id]/ship-with-colissimo/route.ts +6 -0
- package/templates/store-template/app/api/admin/shipping/colissimo/route.ts +10 -0
- package/templates/store-template/app/api/checkout/colissimo-relays/route.ts +4 -0
- package/templates/store-template/app/faq/page.tsx +42 -56
- package/templates/store-template/app/privacy/page.tsx +65 -105
- package/templates/store-template/app/products/[slug]/ProductDetailClient.tsx +6 -3
- package/templates/store-template/app/products/page.tsx +63 -1
- package/templates/store-template/app/terms/page.tsx +70 -198
- package/templates/store-template/package.json +1 -1
package/dist/core/db/queries.js
CHANGED
|
@@ -167,6 +167,11 @@ export async function countProducts(options = {}) {
|
|
|
167
167
|
if (options.outOfStock) {
|
|
168
168
|
conditions.push(buildOutOfStockCondition());
|
|
169
169
|
}
|
|
170
|
+
// Keep in lockstep with findProducts' onSale condition — the public
|
|
171
|
+
// products API uses this count for pagination.
|
|
172
|
+
if (options.onSale) {
|
|
173
|
+
conditions.push(sql `${schema.products.comparePrice} IS NOT NULL AND ${schema.products.comparePrice} > ${schema.products.price}`);
|
|
174
|
+
}
|
|
170
175
|
const result = await db
|
|
171
176
|
.select({ count: sql `count(*)` })
|
|
172
177
|
.from(schema.products)
|
|
@@ -1003,6 +1008,87 @@ export async function updateOrderShipping(orderId, shippingData) {
|
|
|
1003
1008
|
.returning();
|
|
1004
1009
|
return order || null;
|
|
1005
1010
|
}
|
|
1011
|
+
// =============================================================================
|
|
1012
|
+
// Shipping Labels (carrier label bytes, SDK 0.20.0)
|
|
1013
|
+
// =============================================================================
|
|
1014
|
+
/**
|
|
1015
|
+
* Self-heal for the shipping_labels table (Colissimo labels are returned as
|
|
1016
|
+
* bytes exactly once — they must be persisted at purchase time). Idempotent;
|
|
1017
|
+
* cached in-process.
|
|
1018
|
+
*/
|
|
1019
|
+
let shippingLabelsSchemaEnsured = false;
|
|
1020
|
+
export async function ensureShippingLabelsSchema() {
|
|
1021
|
+
if (shippingLabelsSchemaEnsured)
|
|
1022
|
+
return;
|
|
1023
|
+
const db = getDb();
|
|
1024
|
+
try {
|
|
1025
|
+
await db.execute(sql `
|
|
1026
|
+
CREATE TABLE IF NOT EXISTS shipping_labels (
|
|
1027
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
1028
|
+
order_id uuid NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
|
|
1029
|
+
carrier varchar(50) NOT NULL,
|
|
1030
|
+
format varchar(30) NOT NULL,
|
|
1031
|
+
data text NOT NULL,
|
|
1032
|
+
created_at timestamp NOT NULL DEFAULT now()
|
|
1033
|
+
)
|
|
1034
|
+
`);
|
|
1035
|
+
await db.execute(sql `CREATE INDEX IF NOT EXISTS shipping_labels_order_id_idx ON shipping_labels (order_id)`);
|
|
1036
|
+
shippingLabelsSchemaEnsured = true;
|
|
1037
|
+
}
|
|
1038
|
+
catch (err) {
|
|
1039
|
+
console.error('[ensureShippingLabelsSchema] migration failed:', err);
|
|
1040
|
+
throw err;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
/** Persist a carrier label document (base64 bytes). */
|
|
1044
|
+
export async function saveShippingLabel(params) {
|
|
1045
|
+
await ensureShippingLabelsSchema();
|
|
1046
|
+
const db = getDb();
|
|
1047
|
+
await db.insert(schema.shippingLabels).values({
|
|
1048
|
+
orderId: params.orderId,
|
|
1049
|
+
carrier: params.carrier,
|
|
1050
|
+
format: params.format,
|
|
1051
|
+
data: params.dataBase64,
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
/** Latest stored label for an order (admin-gated download route). */
|
|
1055
|
+
export async function findShippingLabelByOrderId(orderId) {
|
|
1056
|
+
await ensureShippingLabelsSchema();
|
|
1057
|
+
const db = getDb();
|
|
1058
|
+
const [label] = await db
|
|
1059
|
+
.select()
|
|
1060
|
+
.from(schema.shippingLabels)
|
|
1061
|
+
.where(eq(schema.shippingLabels.orderId, orderId))
|
|
1062
|
+
.orderBy(desc(schema.shippingLabels.createdAt))
|
|
1063
|
+
.limit(1);
|
|
1064
|
+
return label || null;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Per-line weights for parcel-weight estimation (Colissimo). Product weight
|
|
1068
|
+
* lives in the `weight` metafield (kg — the PrestaShop-import convention);
|
|
1069
|
+
* lines whose product has no weight metafield return null so the caller can
|
|
1070
|
+
* apply the store's default item weight.
|
|
1071
|
+
*/
|
|
1072
|
+
export async function getOrderItemWeights(orderId) {
|
|
1073
|
+
const db = getDb();
|
|
1074
|
+
const rows = await db
|
|
1075
|
+
.select({
|
|
1076
|
+
quantity: schema.orderItems.quantity,
|
|
1077
|
+
metafields: schema.products.metafields,
|
|
1078
|
+
})
|
|
1079
|
+
.from(schema.orderItems)
|
|
1080
|
+
.leftJoin(schema.products, eq(schema.products.id, schema.orderItems.productId))
|
|
1081
|
+
.where(eq(schema.orderItems.orderId, orderId));
|
|
1082
|
+
return rows.map((r) => {
|
|
1083
|
+
const list = r.metafields ?? [];
|
|
1084
|
+
const raw = list.find((m) => m?.key === 'weight')?.value;
|
|
1085
|
+
const parsed = raw !== undefined ? Number.parseFloat(String(raw)) : NaN;
|
|
1086
|
+
return {
|
|
1087
|
+
quantity: r.quantity,
|
|
1088
|
+
weightKg: Number.isFinite(parsed) && parsed > 0 ? parsed : null,
|
|
1089
|
+
};
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1006
1092
|
/**
|
|
1007
1093
|
* Find order by Stripe Payment Intent ID
|
|
1008
1094
|
* Used for idempotency in webhook handling
|
|
@@ -1807,6 +1893,10 @@ export async function ensurePaymentMethodsSchema() {
|
|
|
1807
1893
|
// Email locale (i18n B3): the locale the order was placed in — drives its
|
|
1808
1894
|
// lifecycle emails. customers.locale rides ensureCustomerGroupsSchema.
|
|
1809
1895
|
await db.execute(sql `ALTER TABLE orders ADD COLUMN IF NOT EXISTS locale varchar(5)`);
|
|
1896
|
+
// Carrier relay delivery (SDK 0.20.0 — Colissimo Point Retrait): the
|
|
1897
|
+
// carrier pickup-point the customer chose. shippingAddress snapshots the
|
|
1898
|
+
// relay's address; this keeps the carrier point id/type for labels.
|
|
1899
|
+
await db.execute(sql `ALTER TABLE orders ADD COLUMN IF NOT EXISTS relay jsonb`);
|
|
1810
1900
|
// Group gating (Phase 4) — payment_methods reads all pass through here.
|
|
1811
1901
|
await ensureVisibilityColumns();
|
|
1812
1902
|
paymentMethodsSchemaEnsured = true;
|
|
@@ -2447,6 +2537,19 @@ export async function ensureStoreSettingsSchema() {
|
|
|
2447
2537
|
await db.execute(sql `
|
|
2448
2538
|
ALTER TABLE store_settings
|
|
2449
2539
|
ADD COLUMN IF NOT EXISTS public_metafield_keys jsonb NOT NULL DEFAULT '[]'::jsonb
|
|
2540
|
+
`);
|
|
2541
|
+
// Colissimo carrier integration (SDK 0.20.0). Credentials are server-only
|
|
2542
|
+
// secrets — excluded from the public settings allowlist by construction.
|
|
2543
|
+
// Relay PRICING deliberately has no settings columns: relay offers are
|
|
2544
|
+
// ordinary shipping_rates rows (fulfillment='relay') under a colissimo
|
|
2545
|
+
// carrier — one pricing engine for every carrier.
|
|
2546
|
+
await db.execute(sql `
|
|
2547
|
+
ALTER TABLE store_settings
|
|
2548
|
+
ADD COLUMN IF NOT EXISTS colissimo_contract_number varchar(20),
|
|
2549
|
+
ADD COLUMN IF NOT EXISTS colissimo_password varchar(64),
|
|
2550
|
+
ADD COLUMN IF NOT EXISTS colissimo_enabled boolean DEFAULT false,
|
|
2551
|
+
ADD COLUMN IF NOT EXISTS colissimo_sender_address jsonb,
|
|
2552
|
+
ADD COLUMN IF NOT EXISTS colissimo_default_weight_kg decimal(8,2)
|
|
2450
2553
|
`);
|
|
2451
2554
|
storeSettingsSchemaEnsured = true;
|
|
2452
2555
|
}
|
|
@@ -2642,6 +2745,14 @@ export async function findSettings() {
|
|
|
2642
2745
|
shippoTestMode: settings.shippoTestMode,
|
|
2643
2746
|
shippoFromAddress: settings.shippoFromAddress,
|
|
2644
2747
|
shippoDefaultParcel: settings.shippoDefaultParcel,
|
|
2748
|
+
// Colissimo fields (SDK 0.20.0)
|
|
2749
|
+
colissimoContractNumber: settings.colissimoContractNumber,
|
|
2750
|
+
colissimoPassword: settings.colissimoPassword,
|
|
2751
|
+
colissimoEnabled: settings.colissimoEnabled,
|
|
2752
|
+
colissimoSenderAddress: settings.colissimoSenderAddress,
|
|
2753
|
+
colissimoDefaultWeightKg: settings.colissimoDefaultWeightKg
|
|
2754
|
+
? Number(settings.colissimoDefaultWeightKg)
|
|
2755
|
+
: null,
|
|
2645
2756
|
// Discount/promotion configuration
|
|
2646
2757
|
promotionCodesEnabled: settings.promotionCodesEnabled,
|
|
2647
2758
|
// Cookie consent banner (R021)
|
|
@@ -2722,6 +2833,15 @@ export async function upsertSettings(data) {
|
|
|
2722
2833
|
shippoTestMode: data.shippoTestMode ?? undefined,
|
|
2723
2834
|
shippoFromAddress: data.shippoFromAddress ?? undefined,
|
|
2724
2835
|
shippoDefaultParcel: data.shippoDefaultParcel ?? undefined,
|
|
2836
|
+
// Colissimo (SDK 0.20.0). Credentials map explicit null -> NULL (the
|
|
2837
|
+
// disconnect route clears them), like the branding pointers above.
|
|
2838
|
+
colissimoContractNumber: data.colissimoContractNumber !== undefined ? data.colissimoContractNumber : undefined,
|
|
2839
|
+
colissimoPassword: data.colissimoPassword !== undefined ? data.colissimoPassword : undefined,
|
|
2840
|
+
colissimoEnabled: data.colissimoEnabled ?? undefined,
|
|
2841
|
+
colissimoSenderAddress: data.colissimoSenderAddress ?? undefined,
|
|
2842
|
+
colissimoDefaultWeightKg: data.colissimoDefaultWeightKg !== null && data.colissimoDefaultWeightKg !== undefined
|
|
2843
|
+
? String(data.colissimoDefaultWeightKg)
|
|
2844
|
+
: undefined,
|
|
2725
2845
|
// Discount/promotion configuration
|
|
2726
2846
|
promotionCodesEnabled: data.promotionCodesEnabled ?? undefined,
|
|
2727
2847
|
// Cookie consent banner (R021)
|
|
@@ -2773,6 +2893,14 @@ export async function upsertSettings(data) {
|
|
|
2773
2893
|
shippoTestMode: result.shippoTestMode,
|
|
2774
2894
|
shippoFromAddress: result.shippoFromAddress,
|
|
2775
2895
|
shippoDefaultParcel: result.shippoDefaultParcel,
|
|
2896
|
+
// Colissimo fields (SDK 0.20.0)
|
|
2897
|
+
colissimoContractNumber: result.colissimoContractNumber,
|
|
2898
|
+
colissimoPassword: result.colissimoPassword,
|
|
2899
|
+
colissimoEnabled: result.colissimoEnabled,
|
|
2900
|
+
colissimoSenderAddress: result.colissimoSenderAddress,
|
|
2901
|
+
colissimoDefaultWeightKg: result.colissimoDefaultWeightKg
|
|
2902
|
+
? Number(result.colissimoDefaultWeightKg)
|
|
2903
|
+
: null,
|
|
2776
2904
|
// Discount/promotion configuration
|
|
2777
2905
|
promotionCodesEnabled: result.promotionCodesEnabled,
|
|
2778
2906
|
// Cookie consent banner (R021)
|
|
@@ -2781,10 +2909,72 @@ export async function upsertSettings(data) {
|
|
|
2781
2909
|
publicMetafieldKeys: result.publicMetafieldKeys ?? [],
|
|
2782
2910
|
};
|
|
2783
2911
|
}
|
|
2912
|
+
/**
|
|
2913
|
+
* Self-heal for the carrier-registry columns (SDK 0.20.0):
|
|
2914
|
+
* shipping_carriers.provider/logo_url + shipping_rates.fulfillment. Drizzle
|
|
2915
|
+
* `select()` enumerates every declared column, so every carrier/rate read
|
|
2916
|
+
* chokepoint calls this first. Idempotent; cached in-process.
|
|
2917
|
+
*/
|
|
2918
|
+
let shippingCarrierSchemaEnsured = false;
|
|
2919
|
+
export async function ensureShippingCarrierSchema() {
|
|
2920
|
+
if (shippingCarrierSchemaEnsured)
|
|
2921
|
+
return;
|
|
2922
|
+
const db = getDb();
|
|
2923
|
+
try {
|
|
2924
|
+
await db.execute(sql `
|
|
2925
|
+
ALTER TABLE shipping_carriers
|
|
2926
|
+
ADD COLUMN IF NOT EXISTS provider varchar(20) NOT NULL DEFAULT 'manual',
|
|
2927
|
+
ADD COLUMN IF NOT EXISTS logo_url varchar(500)
|
|
2928
|
+
`);
|
|
2929
|
+
await db.execute(sql `
|
|
2930
|
+
ALTER TABLE shipping_rates
|
|
2931
|
+
ADD COLUMN IF NOT EXISTS fulfillment varchar(20) NOT NULL DEFAULT 'home'
|
|
2932
|
+
`);
|
|
2933
|
+
shippingCarrierSchemaEnsured = true;
|
|
2934
|
+
}
|
|
2935
|
+
catch (err) {
|
|
2936
|
+
console.error('[ensureShippingCarrierSchema] migration failed:', err);
|
|
2937
|
+
throw err;
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
/** Best-effort variant — never throws (the subsequent read surfaces real errors). */
|
|
2941
|
+
export async function ensureShippingCarrierSchemaSafe() {
|
|
2942
|
+
try {
|
|
2943
|
+
await ensureShippingCarrierSchema();
|
|
2944
|
+
}
|
|
2945
|
+
catch {
|
|
2946
|
+
// Swallow — the caller's own query reports the real failure.
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
/**
|
|
2950
|
+
* Legacy-mode self-heal: a store that ran `shipping_mode = 'shippo'` (the
|
|
2951
|
+
* retired global either/or) gets a shippo carrier ROW in the registry so its
|
|
2952
|
+
* live quotes keep flowing under the multi-carrier model. Idempotent
|
|
2953
|
+
* (INSERT … WHERE NOT EXISTS); cached in-process.
|
|
2954
|
+
*/
|
|
2955
|
+
let legacyShippoCarrierEnsured = false;
|
|
2956
|
+
export async function ensureLegacyShippoCarrier(shippingMode) {
|
|
2957
|
+
if (legacyShippoCarrierEnsured || shippingMode !== 'shippo')
|
|
2958
|
+
return;
|
|
2959
|
+
try {
|
|
2960
|
+
await ensureShippingCarrierSchema();
|
|
2961
|
+
const db = getDb();
|
|
2962
|
+
await db.execute(sql `
|
|
2963
|
+
INSERT INTO shipping_carriers (name, code, provider, is_active, sort_order)
|
|
2964
|
+
SELECT 'Carrier rates', 'shippo', 'shippo', true, 0
|
|
2965
|
+
WHERE NOT EXISTS (SELECT 1 FROM shipping_carriers WHERE provider = 'shippo')
|
|
2966
|
+
`);
|
|
2967
|
+
legacyShippoCarrierEnsured = true;
|
|
2968
|
+
}
|
|
2969
|
+
catch (err) {
|
|
2970
|
+
console.error('[ensureLegacyShippoCarrier] failed (non-fatal):', err);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2784
2973
|
/**
|
|
2785
2974
|
* Find all shipping carriers
|
|
2786
2975
|
*/
|
|
2787
2976
|
export async function findShippingCarriers() {
|
|
2977
|
+
await ensureShippingCarrierSchemaSafe();
|
|
2788
2978
|
const db = getDb();
|
|
2789
2979
|
const carriers = await db
|
|
2790
2980
|
.select()
|
|
@@ -2795,6 +2985,8 @@ export async function findShippingCarriers() {
|
|
|
2795
2985
|
name: carrier.name,
|
|
2796
2986
|
code: carrier.code,
|
|
2797
2987
|
description: carrier.description,
|
|
2988
|
+
provider: carrier.provider ?? 'manual',
|
|
2989
|
+
logoUrl: carrier.logoUrl ?? null,
|
|
2798
2990
|
isActive: carrier.isActive,
|
|
2799
2991
|
sortOrder: carrier.sortOrder,
|
|
2800
2992
|
createdAt: carrier.createdAt,
|
|
@@ -2805,6 +2997,7 @@ export async function findShippingCarriers() {
|
|
|
2805
2997
|
* Find active shipping carriers only
|
|
2806
2998
|
*/
|
|
2807
2999
|
export async function findActiveShippingCarriers() {
|
|
3000
|
+
await ensureShippingCarrierSchemaSafe();
|
|
2808
3001
|
const db = getDb();
|
|
2809
3002
|
const carriers = await db
|
|
2810
3003
|
.select()
|
|
@@ -2816,6 +3009,8 @@ export async function findActiveShippingCarriers() {
|
|
|
2816
3009
|
name: carrier.name,
|
|
2817
3010
|
code: carrier.code,
|
|
2818
3011
|
description: carrier.description,
|
|
3012
|
+
provider: carrier.provider ?? 'manual',
|
|
3013
|
+
logoUrl: carrier.logoUrl ?? null,
|
|
2819
3014
|
isActive: carrier.isActive,
|
|
2820
3015
|
sortOrder: carrier.sortOrder,
|
|
2821
3016
|
createdAt: carrier.createdAt,
|
|
@@ -2826,6 +3021,7 @@ export async function findActiveShippingCarriers() {
|
|
|
2826
3021
|
* Find a shipping carrier by ID
|
|
2827
3022
|
*/
|
|
2828
3023
|
export async function findShippingCarrierById(id) {
|
|
3024
|
+
await ensureShippingCarrierSchemaSafe();
|
|
2829
3025
|
const db = getDb();
|
|
2830
3026
|
const [carrier] = await db
|
|
2831
3027
|
.select()
|
|
@@ -2839,6 +3035,8 @@ export async function findShippingCarrierById(id) {
|
|
|
2839
3035
|
name: carrier.name,
|
|
2840
3036
|
code: carrier.code,
|
|
2841
3037
|
description: carrier.description,
|
|
3038
|
+
provider: carrier.provider ?? 'manual',
|
|
3039
|
+
logoUrl: carrier.logoUrl ?? null,
|
|
2842
3040
|
isActive: carrier.isActive,
|
|
2843
3041
|
sortOrder: carrier.sortOrder,
|
|
2844
3042
|
createdAt: carrier.createdAt,
|
|
@@ -2849,6 +3047,7 @@ export async function findShippingCarrierById(id) {
|
|
|
2849
3047
|
* Create a new shipping carrier
|
|
2850
3048
|
*/
|
|
2851
3049
|
export async function createShippingCarrier(data) {
|
|
3050
|
+
await ensureShippingCarrierSchema();
|
|
2852
3051
|
const db = getDb();
|
|
2853
3052
|
const [carrier] = await db
|
|
2854
3053
|
.insert(schema.shippingCarriers)
|
|
@@ -2856,6 +3055,8 @@ export async function createShippingCarrier(data) {
|
|
|
2856
3055
|
name: data.name,
|
|
2857
3056
|
code: data.code,
|
|
2858
3057
|
description: data.description || null,
|
|
3058
|
+
provider: data.provider || 'manual',
|
|
3059
|
+
logoUrl: data.logoUrl ?? null,
|
|
2859
3060
|
isActive: data.isActive ?? true,
|
|
2860
3061
|
sortOrder: data.sortOrder ?? 0,
|
|
2861
3062
|
})
|
|
@@ -2866,6 +3067,7 @@ export async function createShippingCarrier(data) {
|
|
|
2866
3067
|
* Update a shipping carrier
|
|
2867
3068
|
*/
|
|
2868
3069
|
export async function updateShippingCarrier(id, data) {
|
|
3070
|
+
await ensureShippingCarrierSchemaSafe();
|
|
2869
3071
|
const db = getDb();
|
|
2870
3072
|
const [carrier] = await db
|
|
2871
3073
|
.update(schema.shippingCarriers)
|
|
@@ -2909,8 +3111,10 @@ export async function findCarriersWithZonesAndRates() {
|
|
|
2909
3111
|
* Find all shipping zones with their rates and carrier info
|
|
2910
3112
|
*/
|
|
2911
3113
|
export async function findShippingZones() {
|
|
2912
|
-
// Rate SELECTs enumerate visible_to_groups — self-heal older
|
|
3114
|
+
// Rate SELECTs enumerate visible_to_groups + fulfillment — self-heal older
|
|
3115
|
+
// stores first.
|
|
2913
3116
|
await ensureVisibilityColumnsSafe();
|
|
3117
|
+
await ensureShippingCarrierSchemaSafe();
|
|
2914
3118
|
const db = getDb();
|
|
2915
3119
|
// Get all zones with carrier info via join
|
|
2916
3120
|
const zonesWithCarriers = await db
|
|
@@ -2920,6 +3124,9 @@ export async function findShippingZones() {
|
|
|
2920
3124
|
id: schema.shippingCarriers.id,
|
|
2921
3125
|
name: schema.shippingCarriers.name,
|
|
2922
3126
|
code: schema.shippingCarriers.code,
|
|
3127
|
+
provider: schema.shippingCarriers.provider,
|
|
3128
|
+
logoUrl: schema.shippingCarriers.logoUrl,
|
|
3129
|
+
isActive: schema.shippingCarriers.isActive,
|
|
2923
3130
|
},
|
|
2924
3131
|
})
|
|
2925
3132
|
.from(schema.shippingZones)
|
|
@@ -2945,6 +3152,9 @@ export async function findShippingZones() {
|
|
|
2945
3152
|
id: carrier.id,
|
|
2946
3153
|
name: carrier.name,
|
|
2947
3154
|
code: carrier.code,
|
|
3155
|
+
provider: carrier.provider ?? 'manual',
|
|
3156
|
+
logoUrl: carrier.logoUrl ?? null,
|
|
3157
|
+
isActive: carrier.isActive ?? true,
|
|
2948
3158
|
} : undefined,
|
|
2949
3159
|
name: zone.name,
|
|
2950
3160
|
countries: zone.countries || [],
|
|
@@ -2963,6 +3173,7 @@ export async function findShippingZones() {
|
|
|
2963
3173
|
isActive: rate.isActive,
|
|
2964
3174
|
sortOrder: rate.sortOrder,
|
|
2965
3175
|
visibleToGroups: Array.isArray(rate.visibleToGroups) ? rate.visibleToGroups : [],
|
|
3176
|
+
fulfillment: rate.fulfillment === 'relay' ? 'relay' : 'home',
|
|
2966
3177
|
})),
|
|
2967
3178
|
}));
|
|
2968
3179
|
}
|
|
@@ -3025,6 +3236,7 @@ export async function deleteShippingZone(id) {
|
|
|
3025
3236
|
*/
|
|
3026
3237
|
export async function createShippingRate(data) {
|
|
3027
3238
|
await ensureVisibilityColumnsSafe();
|
|
3239
|
+
await ensureShippingCarrierSchemaSafe();
|
|
3028
3240
|
const db = getDb();
|
|
3029
3241
|
const [rate] = await db
|
|
3030
3242
|
.insert(schema.shippingRates)
|
|
@@ -3039,6 +3251,7 @@ export async function createShippingRate(data) {
|
|
|
3039
3251
|
isActive: data.isActive ?? true,
|
|
3040
3252
|
sortOrder: data.sortOrder ?? 0,
|
|
3041
3253
|
visibleToGroups: data.visibleToGroups ?? [],
|
|
3254
|
+
fulfillment: data.fulfillment ?? 'home',
|
|
3042
3255
|
})
|
|
3043
3256
|
.returning();
|
|
3044
3257
|
return rate;
|
|
@@ -3048,6 +3261,7 @@ export async function createShippingRate(data) {
|
|
|
3048
3261
|
*/
|
|
3049
3262
|
export async function updateShippingRate(id, data) {
|
|
3050
3263
|
await ensureVisibilityColumnsSafe();
|
|
3264
|
+
await ensureShippingCarrierSchemaSafe();
|
|
3051
3265
|
const db = getDb();
|
|
3052
3266
|
const updateData = {};
|
|
3053
3267
|
if (data.name !== undefined)
|
|
@@ -3068,6 +3282,8 @@ export async function updateShippingRate(id, data) {
|
|
|
3068
3282
|
updateData.sortOrder = data.sortOrder;
|
|
3069
3283
|
if (data.visibleToGroups !== undefined)
|
|
3070
3284
|
updateData.visibleToGroups = data.visibleToGroups;
|
|
3285
|
+
if (data.fulfillment !== undefined)
|
|
3286
|
+
updateData.fulfillment = data.fulfillment;
|
|
3071
3287
|
const [rate] = await db
|
|
3072
3288
|
.update(schema.shippingRates)
|
|
3073
3289
|
.set(updateData)
|
|
@@ -3101,6 +3317,10 @@ export async function findShippingRatesForCountry(country, orderAmount, groupId)
|
|
|
3101
3317
|
// Skip zones without a carrier (shouldn't happen, but defensive)
|
|
3102
3318
|
if (!zone.carrier)
|
|
3103
3319
|
continue;
|
|
3320
|
+
// A deactivated CARRIER withdraws all its offers (registry semantics —
|
|
3321
|
+
// pre-0.20.0 the flag was cosmetic in the quote path).
|
|
3322
|
+
if (!zone.carrier.isActive)
|
|
3323
|
+
continue;
|
|
3104
3324
|
// Check if zone covers this country
|
|
3105
3325
|
// '*' means all countries (international catch-all)
|
|
3106
3326
|
const coversCountry = zone.countries.includes('*') || zone.countries.includes(country.toUpperCase());
|
|
@@ -3124,11 +3344,14 @@ export async function findShippingRatesForCountry(country, orderAmount, groupId)
|
|
|
3124
3344
|
carrierId: zone.carrierId,
|
|
3125
3345
|
carrierName: zone.carrier.name,
|
|
3126
3346
|
carrierCode: zone.carrier.code,
|
|
3347
|
+
carrierProvider: zone.carrier.provider,
|
|
3348
|
+
carrierLogoUrl: zone.carrier.logoUrl,
|
|
3127
3349
|
zoneName: zone.name,
|
|
3128
3350
|
name: rate.name,
|
|
3129
3351
|
description: rate.description,
|
|
3130
3352
|
price: rate.price,
|
|
3131
3353
|
estimatedDays: rate.estimatedDays,
|
|
3354
|
+
fulfillment: rate.fulfillment,
|
|
3132
3355
|
});
|
|
3133
3356
|
}
|
|
3134
3357
|
}
|
|
@@ -3154,6 +3377,8 @@ export async function findShippingOptionsForCountry(country, orderAmount, groupI
|
|
|
3154
3377
|
id: rate.carrierId,
|
|
3155
3378
|
name: rate.carrierName,
|
|
3156
3379
|
code: rate.carrierCode,
|
|
3380
|
+
provider: rate.carrierProvider,
|
|
3381
|
+
logoUrl: rate.carrierLogoUrl,
|
|
3157
3382
|
},
|
|
3158
3383
|
options: [],
|
|
3159
3384
|
};
|
|
@@ -3169,6 +3394,8 @@ export async function findShippingOptionsForCountry(country, orderAmount, groupI
|
|
|
3169
3394
|
price: rate.price,
|
|
3170
3395
|
estimatedDays: rate.estimatedDays ?? undefined,
|
|
3171
3396
|
source: 'manual',
|
|
3397
|
+
provider: rate.carrierProvider,
|
|
3398
|
+
fulfillment: rate.fulfillment,
|
|
3172
3399
|
});
|
|
3173
3400
|
}
|
|
3174
3401
|
// Convert to array and sort by carrier name
|