@fayz-ai/shop 0.7.0 → 0.8.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/README.md +2 -0
- package/dist/index.cjs +185 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -28
- package/dist/index.js.map +1 -1
- package/dist/mock-provider.d.ts.map +1 -1
- package/dist/mock.cjs +5 -0
- package/dist/mock.cjs.map +1 -1
- package/dist/mock.js +5 -0
- package/dist/mock.js.map +1 -1
- package/dist/provider.d.ts +42 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/runtime.cjs +5 -0
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +5 -0
- package/dist/runtime.js.map +1 -1
- package/dist/supabase-provider.d.ts +20 -1
- package/dist/supabase-provider.d.ts.map +1 -1
- package/dist/tables.d.ts +11 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/types.d.ts +103 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -4
- package/src/index.ts +4 -0
- package/src/mock-provider.ts +5 -0
- package/src/provider.ts +45 -0
- package/src/supabase-provider.ts +226 -34
- package/src/tables.ts +16 -0
- package/src/types.ts +110 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type { Product, ProductImage, Category, ProductEnquiry, ProductEnquiryStatus, Order, OrderItem, OrderStatus, FinancialStatus, FulfillmentStatus, ShopCustomer, Discount, DiscountType, DiscountMethod, DiscountStatus, ProductStatus, CreateProductInput, UpdateProductInput, ListProductsOptions, CreateProductEnquiryInput, ListProductEnquiriesOptions, CreateCategoryInput, UpdateCategoryInput, CreateOrderInput, UpdateOrderInput, ListOrdersOptions, CreateCustomerInput, UpdateCustomerInput, ListCustomersOptions, ResolveCustomerInput, CreateDiscountInput, UpdateDiscountInput, ListDiscountsOptions, PlaceOrderInput, PlaceOrderLine, DiscountValidation, InventoryLine, InventoryIssue, InventoryCheck, CartTotalLine, CartTotals, } from './types';
|
|
2
|
+
export type { ShippingZone, CreateShippingZoneInput, UpdateShippingZoneInput, ShippingQuoteOption, CustomerAddress, } from './types';
|
|
2
3
|
export type { ShopProvider } from './provider';
|
|
3
4
|
export { validateDiscount, applyDiscount, checkInventory, computeCartTotals, getCustomerOrders, } from './commerce';
|
|
4
5
|
export { SupabaseShopProvider, createSupabaseShopProvider } from './supabase-provider';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EACrE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EACjE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EACpE,aAAa,EACb,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAC3D,yBAAyB,EAAE,2BAA2B,EACtD,mBAAmB,EAAE,mBAAmB,EACxC,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EACrD,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,oBAAoB,EACpF,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAC9D,eAAe,EAAE,cAAc,EAC/B,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,GAC7F,MAAM,SAAS,CAAA;AAEhB,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EACL,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,GACtF,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AACjE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAG/E,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,oBAAoB,EACrE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EACjE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EACpE,aAAa,EACb,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAC3D,yBAAyB,EAAE,2BAA2B,EACtD,mBAAmB,EAAE,mBAAmB,EACxC,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,EACrD,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,oBAAoB,EACpF,mBAAmB,EAAE,mBAAmB,EAAE,oBAAoB,EAC9D,eAAe,EAAE,cAAc,EAC/B,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,GAC7F,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,YAAY,EAAE,uBAAuB,EAAE,uBAAuB,EAC9D,mBAAmB,EAAE,eAAe,GACrC,MAAM,SAAS,CAAA;AAChB,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EACL,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,GACtF,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAA;AACtF,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AACjE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAG/E,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -388,6 +388,11 @@ var MockShopProvider = class {
|
|
|
388
388
|
customerEmail: input.customerEmail ?? null,
|
|
389
389
|
discountCode: input.discountCode ?? null,
|
|
390
390
|
notes: input.notes ?? null,
|
|
391
|
+
shippingAddress: null,
|
|
392
|
+
// CreateOrderInput has no payment method — that is a PlaceOrderInput
|
|
393
|
+
// concern; the mock records none rather than inventing one.
|
|
394
|
+
paymentMethodKind: null,
|
|
395
|
+
paidAt: null,
|
|
391
396
|
items: input.items.map((i) => ({ id: uid(), orderId, productId: i.productId ?? null, name: i.name, sku: i.sku ?? null, quantity: i.quantity, unitPrice: i.unitPrice, total: Math.round(i.quantity * i.unitPrice * 100) / 100, imageUrl: i.imageUrl ?? null })),
|
|
392
397
|
createdAt: now(),
|
|
393
398
|
updatedAt: now()
|
|
@@ -649,6 +654,21 @@ function getShopTenantId() {
|
|
|
649
654
|
}
|
|
650
655
|
}
|
|
651
656
|
|
|
657
|
+
// src/tables.ts
|
|
658
|
+
var T = {
|
|
659
|
+
products: "plg_shop_products",
|
|
660
|
+
productImages: "plg_shop_product_images",
|
|
661
|
+
categories: "plg_shop_categories",
|
|
662
|
+
orders: "plg_shop_orders",
|
|
663
|
+
orderItems: "plg_shop_order_items",
|
|
664
|
+
customers: "plg_shop_customers",
|
|
665
|
+
discounts: "plg_shop_discounts",
|
|
666
|
+
// Core, not plg_shop_*: a delivery zone is a fact about the business, and
|
|
667
|
+
// fulfilment and financial read it too. Same reasoning as addresses and
|
|
668
|
+
// orders — see migration 0021.
|
|
669
|
+
shippingZones: "shipping_zones"
|
|
670
|
+
};
|
|
671
|
+
|
|
652
672
|
// src/supabase-provider.ts
|
|
653
673
|
function getDb() {
|
|
654
674
|
const supabase = getSupabaseClientOptional();
|
|
@@ -663,6 +683,40 @@ function asUuid(value) {
|
|
|
663
683
|
if (!value) return null;
|
|
664
684
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value) ? value : null;
|
|
665
685
|
}
|
|
686
|
+
function toPostalDigits(value) {
|
|
687
|
+
const digits = String(value ?? "").replace(/\D/g, "");
|
|
688
|
+
return digits ? digits.padStart(8, "0").slice(0, 8) : void 0;
|
|
689
|
+
}
|
|
690
|
+
function rowToShippingZone(row) {
|
|
691
|
+
return {
|
|
692
|
+
id: row.id,
|
|
693
|
+
tenantId: row.tenant_id,
|
|
694
|
+
name: row.name,
|
|
695
|
+
carrier: row.carrier ?? null,
|
|
696
|
+
postalFrom: String(row.postal_from ?? "").trim(),
|
|
697
|
+
postalTo: String(row.postal_to ?? "").trim(),
|
|
698
|
+
rate: Number(row.rate ?? 0),
|
|
699
|
+
freeAbove: row.free_above == null ? null : Number(row.free_above),
|
|
700
|
+
etaMinDays: row.eta_min_days ?? null,
|
|
701
|
+
etaMaxDays: row.eta_max_days ?? null,
|
|
702
|
+
active: row.active ?? true,
|
|
703
|
+
sortOrder: row.sort_order ?? 0
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function shippingZonePayload(input) {
|
|
707
|
+
const payload = {};
|
|
708
|
+
if (input.name !== void 0) payload.name = input.name;
|
|
709
|
+
if (input.carrier !== void 0) payload.carrier = input.carrier || null;
|
|
710
|
+
if (input.postalFrom !== void 0) payload.postal_from = toPostalDigits(input.postalFrom);
|
|
711
|
+
if (input.postalTo !== void 0) payload.postal_to = toPostalDigits(input.postalTo);
|
|
712
|
+
if (input.rate !== void 0) payload.rate = input.rate;
|
|
713
|
+
if (input.freeAbove !== void 0) payload.free_above = input.freeAbove ?? null;
|
|
714
|
+
if (input.etaMinDays !== void 0) payload.eta_min_days = input.etaMinDays ?? null;
|
|
715
|
+
if (input.etaMaxDays !== void 0) payload.eta_max_days = input.etaMaxDays ?? null;
|
|
716
|
+
if (input.active !== void 0) payload.active = input.active;
|
|
717
|
+
if (input.sortOrder !== void 0) payload.sort_order = input.sortOrder;
|
|
718
|
+
return payload;
|
|
719
|
+
}
|
|
666
720
|
function rowToProduct(row) {
|
|
667
721
|
return {
|
|
668
722
|
id: row.id,
|
|
@@ -711,6 +765,19 @@ function rowToCategory(row) {
|
|
|
711
765
|
createdAt: row.created_at
|
|
712
766
|
};
|
|
713
767
|
}
|
|
768
|
+
function rowToOrderItem(row) {
|
|
769
|
+
return {
|
|
770
|
+
id: row.id,
|
|
771
|
+
orderId: row.order_id,
|
|
772
|
+
productId: row.product_id ?? null,
|
|
773
|
+
name: row.name,
|
|
774
|
+
sku: row.sku ?? null,
|
|
775
|
+
quantity: row.quantity ?? 0,
|
|
776
|
+
unitPrice: row.unit_price ?? 0,
|
|
777
|
+
total: row.total ?? 0,
|
|
778
|
+
imageUrl: row.image_url ?? null
|
|
779
|
+
};
|
|
780
|
+
}
|
|
714
781
|
function rowToOrder(row) {
|
|
715
782
|
return {
|
|
716
783
|
id: row.id,
|
|
@@ -730,7 +797,10 @@ function rowToOrder(row) {
|
|
|
730
797
|
customerEmail: row.customer_email,
|
|
731
798
|
discountCode: row.discount_code,
|
|
732
799
|
notes: row.notes,
|
|
733
|
-
|
|
800
|
+
shippingAddress: row.shipping_address ?? null,
|
|
801
|
+
paymentMethodKind: row.payment_method_kind ?? null,
|
|
802
|
+
paidAt: row.paid_at ?? null,
|
|
803
|
+
items: Array.isArray(row.items) ? row.items.map(rowToOrderItem) : [],
|
|
734
804
|
createdAt: row.created_at,
|
|
735
805
|
updatedAt: row.updated_at
|
|
736
806
|
};
|
|
@@ -776,7 +846,7 @@ var SupabaseShopProvider = class {
|
|
|
776
846
|
async listProducts(options) {
|
|
777
847
|
const db = getDb();
|
|
778
848
|
const tenantId = getTenantId();
|
|
779
|
-
let q = db.from(
|
|
849
|
+
let q = db.from(T.products).select(`*, images:${T.productImages}(*)`);
|
|
780
850
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
781
851
|
if (options?.status) q = q.eq("status", options.status);
|
|
782
852
|
if (options?.categoryId) q = q.eq("category_id", options.categoryId);
|
|
@@ -791,14 +861,14 @@ var SupabaseShopProvider = class {
|
|
|
791
861
|
}
|
|
792
862
|
async getProduct(id) {
|
|
793
863
|
const db = getDb();
|
|
794
|
-
const { data, error } = await db.from(
|
|
864
|
+
const { data, error } = await db.from(T.products).select(`*, images:${T.productImages}(*)`).eq("id", id).maybeSingle();
|
|
795
865
|
if (error) throw error;
|
|
796
866
|
return data ? rowToProduct(data) : null;
|
|
797
867
|
}
|
|
798
868
|
async createProduct(input) {
|
|
799
869
|
const db = getDb();
|
|
800
870
|
const tenantId = getTenantId();
|
|
801
|
-
const { data, error } = await db.from(
|
|
871
|
+
const { data, error } = await db.from(T.products).insert({
|
|
802
872
|
tenant_id: tenantId,
|
|
803
873
|
name: input.name,
|
|
804
874
|
slug: input.slug ?? slugify(input.name),
|
|
@@ -815,7 +885,7 @@ var SupabaseShopProvider = class {
|
|
|
815
885
|
is_physical: input.isPhysical ?? true,
|
|
816
886
|
weight: input.weight ?? null,
|
|
817
887
|
weight_unit: input.weightUnit ?? "kg"
|
|
818
|
-
}).select(
|
|
888
|
+
}).select(`*, images:${T.productImages}(*)`).single();
|
|
819
889
|
if (error) throw error;
|
|
820
890
|
return rowToProduct(data);
|
|
821
891
|
}
|
|
@@ -837,12 +907,12 @@ var SupabaseShopProvider = class {
|
|
|
837
907
|
if (input.categoryId !== void 0) updates.category_id = input.categoryId;
|
|
838
908
|
if (input.isPhysical !== void 0) updates.is_physical = input.isPhysical;
|
|
839
909
|
if (input.weight !== void 0) updates.weight = input.weight;
|
|
840
|
-
const { data, error } = await db.from(
|
|
910
|
+
const { data, error } = await db.from(T.products).update(updates).eq("id", id).select(`*, images:${T.productImages}(*)`).single();
|
|
841
911
|
if (error) throw error;
|
|
842
912
|
return rowToProduct(data);
|
|
843
913
|
}
|
|
844
914
|
async deleteProduct(id) {
|
|
845
|
-
const { error } = await getDb().from(
|
|
915
|
+
const { error } = await getDb().from(T.products).delete().eq("id", id);
|
|
846
916
|
if (error) throw error;
|
|
847
917
|
}
|
|
848
918
|
async uploadProductImage(productId, file) {
|
|
@@ -852,12 +922,37 @@ var SupabaseShopProvider = class {
|
|
|
852
922
|
const { error: uploadErr } = await db.storage.from("shop-images").upload(path, file);
|
|
853
923
|
if (uploadErr) throw uploadErr;
|
|
854
924
|
const { data: { publicUrl } } = db.storage.from("shop-images").getPublicUrl(path);
|
|
855
|
-
const { data
|
|
925
|
+
const { data: existing } = await db.from(T.productImages).select("id,sort_order").eq("product_id", productId).order("sort_order", { ascending: false }).limit(1);
|
|
926
|
+
const first = !existing || existing.length === 0;
|
|
927
|
+
const nextOrder = first ? 0 : (existing[0].sort_order ?? 0) + 1;
|
|
928
|
+
const { data, error } = await db.from(T.productImages).insert({
|
|
929
|
+
product_id: productId,
|
|
930
|
+
tenant_id: tenantId,
|
|
931
|
+
url: publicUrl,
|
|
932
|
+
sort_order: nextOrder,
|
|
933
|
+
is_primary: first
|
|
934
|
+
}).select().single();
|
|
935
|
+
if (error) throw error;
|
|
936
|
+
return rowToImage(data);
|
|
937
|
+
}
|
|
938
|
+
async updateProductImage(imageId, input) {
|
|
939
|
+
const db = getDb();
|
|
940
|
+
const updates = {};
|
|
941
|
+
if (input.sortOrder !== void 0) updates.sort_order = input.sortOrder;
|
|
942
|
+
if (input.altText !== void 0) updates.alt_text = input.altText;
|
|
943
|
+
if (input.isPrimary !== void 0) updates.is_primary = input.isPrimary;
|
|
944
|
+
if (input.isPrimary) {
|
|
945
|
+
const { data: target } = await db.from(T.productImages).select("product_id").eq("id", imageId).single();
|
|
946
|
+
if (target?.product_id) {
|
|
947
|
+
await db.from(T.productImages).update({ is_primary: false }).eq("product_id", target.product_id).neq("id", imageId);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
const { data, error } = await db.from(T.productImages).update(updates).eq("id", imageId).select().single();
|
|
856
951
|
if (error) throw error;
|
|
857
952
|
return rowToImage(data);
|
|
858
953
|
}
|
|
859
954
|
async deleteProductImage(imageId) {
|
|
860
|
-
const { error } = await getDb().from(
|
|
955
|
+
const { error } = await getDb().from(T.productImages).delete().eq("id", imageId);
|
|
861
956
|
if (error) throw error;
|
|
862
957
|
}
|
|
863
958
|
// ---------------------------------------------------------------------------
|
|
@@ -866,7 +961,7 @@ var SupabaseShopProvider = class {
|
|
|
866
961
|
async listCategories() {
|
|
867
962
|
const db = getDb();
|
|
868
963
|
const tenantId = getTenantId();
|
|
869
|
-
let q = db.from(
|
|
964
|
+
let q = db.from(T.categories).select("*").order("sort_order", { ascending: true });
|
|
870
965
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
871
966
|
const { data, error } = await q;
|
|
872
967
|
if (error) throw error;
|
|
@@ -874,7 +969,7 @@ var SupabaseShopProvider = class {
|
|
|
874
969
|
}
|
|
875
970
|
async createCategory(input) {
|
|
876
971
|
const db = getDb();
|
|
877
|
-
const { data, error } = await db.from(
|
|
972
|
+
const { data, error } = await db.from(T.categories).insert({ tenant_id: getTenantId(), name: input.name, slug: input.slug ?? slugify(input.name), description: input.description ?? null, parent_id: input.parentId ?? null, sort_order: input.sortOrder ?? 0 }).select().single();
|
|
878
973
|
if (error) throw error;
|
|
879
974
|
return rowToCategory(data);
|
|
880
975
|
}
|
|
@@ -886,12 +981,12 @@ var SupabaseShopProvider = class {
|
|
|
886
981
|
if (input.description !== void 0) updates.description = input.description;
|
|
887
982
|
if (input.parentId !== void 0) updates.parent_id = input.parentId;
|
|
888
983
|
if (input.sortOrder !== void 0) updates.sort_order = input.sortOrder;
|
|
889
|
-
const { data, error } = await db.from(
|
|
984
|
+
const { data, error } = await db.from(T.categories).update(updates).eq("id", id).select().single();
|
|
890
985
|
if (error) throw error;
|
|
891
986
|
return rowToCategory(data);
|
|
892
987
|
}
|
|
893
988
|
async deleteCategory(id) {
|
|
894
|
-
const { error } = await getDb().from(
|
|
989
|
+
const { error } = await getDb().from(T.categories).delete().eq("id", id);
|
|
895
990
|
if (error) throw error;
|
|
896
991
|
}
|
|
897
992
|
// ---------------------------------------------------------------------------
|
|
@@ -900,7 +995,7 @@ var SupabaseShopProvider = class {
|
|
|
900
995
|
async listOrders(options) {
|
|
901
996
|
const db = getDb();
|
|
902
997
|
const tenantId = getTenantId();
|
|
903
|
-
let q = db.from(
|
|
998
|
+
let q = db.from(T.orders).select(`*, items:${T.orderItems}(*)`);
|
|
904
999
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
905
1000
|
if (options?.status) q = q.eq("status", options.status);
|
|
906
1001
|
if (options?.financialStatus) q = q.eq("financial_status", options.financialStatus);
|
|
@@ -915,7 +1010,7 @@ var SupabaseShopProvider = class {
|
|
|
915
1010
|
return (data ?? []).map(rowToOrder);
|
|
916
1011
|
}
|
|
917
1012
|
async getOrder(id) {
|
|
918
|
-
const { data, error } = await getDb().from(
|
|
1013
|
+
const { data, error } = await getDb().from(T.orders).select(`*, items:${T.orderItems}(*)`).eq("id", id).maybeSingle();
|
|
919
1014
|
if (error) throw error;
|
|
920
1015
|
return data ? rowToOrder(data) : null;
|
|
921
1016
|
}
|
|
@@ -926,10 +1021,10 @@ var SupabaseShopProvider = class {
|
|
|
926
1021
|
const discountTotal = input.discountTotal ?? 0;
|
|
927
1022
|
const shippingTotal = input.shippingTotal ?? 0;
|
|
928
1023
|
const total = Math.round((subtotal - discountTotal + shippingTotal) * 100) / 100;
|
|
929
|
-
const { data: order, error: orderErr } = await db.from(
|
|
1024
|
+
const { data: order, error: orderErr } = await db.from(T.orders).insert({ tenant_id: tenantId, customer_id: input.customerId ?? null, customer_name: input.customerName ?? null, customer_email: input.customerEmail ?? null, currency: input.currency ?? "BRL", notes: input.notes ?? null, subtotal, discount_total: discountTotal, shipping_total: shippingTotal, discount_code: input.discountCode ?? null, total, status: "open", financial_status: "pending", fulfillment_status: "unfulfilled" }).select().single();
|
|
930
1025
|
if (orderErr) throw orderErr;
|
|
931
1026
|
const items = input.items.map((i) => ({ order_id: order.id, product_id: asUuid(i.productId), name: i.name, sku: i.sku ?? null, quantity: i.quantity, unit_price: i.unitPrice, total: i.quantity * i.unitPrice, image_url: i.imageUrl ?? null }));
|
|
932
|
-
await db.from(
|
|
1027
|
+
await db.from(T.orderItems).insert(items);
|
|
933
1028
|
return this.getOrder(order.id);
|
|
934
1029
|
}
|
|
935
1030
|
async updateOrder(id, input) {
|
|
@@ -938,7 +1033,53 @@ var SupabaseShopProvider = class {
|
|
|
938
1033
|
if (input.financialStatus !== void 0) updates.financial_status = input.financialStatus;
|
|
939
1034
|
if (input.fulfillmentStatus !== void 0) updates.fulfillment_status = input.fulfillmentStatus;
|
|
940
1035
|
if (input.notes !== void 0) updates.notes = input.notes;
|
|
941
|
-
const { error } = await getDb().from(
|
|
1036
|
+
const { error } = await getDb().from(T.orders).update(updates).eq("id", id);
|
|
1037
|
+
if (error) throw error;
|
|
1038
|
+
return this.getOrder(id);
|
|
1039
|
+
}
|
|
1040
|
+
// -------------------------------------------------------------------------
|
|
1041
|
+
// Delivery zones
|
|
1042
|
+
//
|
|
1043
|
+
// Postal codes are stored as 8 bare digits so the range test in
|
|
1044
|
+
// shop_quote_shipping is a plain BETWEEN. Accepting '22.041-001' from the
|
|
1045
|
+
// form and normalising here means the merchant never has to think about it.
|
|
1046
|
+
// -------------------------------------------------------------------------
|
|
1047
|
+
async listShippingZones() {
|
|
1048
|
+
const tenantId = getTenantId();
|
|
1049
|
+
const { data, error } = await getDb().from(T.shippingZones).select("*").eq("tenant_id", tenantId).order("sort_order", { ascending: true }).order("postal_from", { ascending: true });
|
|
1050
|
+
if (error) throw error;
|
|
1051
|
+
return (data ?? []).map(rowToShippingZone);
|
|
1052
|
+
}
|
|
1053
|
+
async createShippingZone(input) {
|
|
1054
|
+
const tenantId = getTenantId();
|
|
1055
|
+
if (!tenantId) throw new Error("@fayz-ai/shop: createShippingZone requires a tenant id.");
|
|
1056
|
+
const { data, error } = await getDb().from(T.shippingZones).insert({ tenant_id: tenantId, ...shippingZonePayload(input) }).select().single();
|
|
1057
|
+
if (error) throw error;
|
|
1058
|
+
return rowToShippingZone(data);
|
|
1059
|
+
}
|
|
1060
|
+
async updateShippingZone(id, input) {
|
|
1061
|
+
const { data, error } = await getDb().from(T.shippingZones).update(shippingZonePayload(input)).eq("id", id).select().single();
|
|
1062
|
+
if (error) throw error;
|
|
1063
|
+
return rowToShippingZone(data);
|
|
1064
|
+
}
|
|
1065
|
+
async deleteShippingZone(id) {
|
|
1066
|
+
const { error } = await getDb().from(T.shippingZones).delete().eq("id", id);
|
|
1067
|
+
if (error) throw error;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* Settle a pending order: the merchant confirming that the money arrived.
|
|
1071
|
+
*
|
|
1072
|
+
* Goes through the RPC rather than an UPDATE so the payment ledger row in
|
|
1073
|
+
* public.transactions closes in the same transaction as the order — set
|
|
1074
|
+
* financial_status by hand and the order says paid while the ledger still
|
|
1075
|
+
* says pending. Since 0019 the RPC also enforces who may call it: a member of
|
|
1076
|
+
* the owning tenant, or a PSP webhook with the service role.
|
|
1077
|
+
*/
|
|
1078
|
+
async confirmPayment(id, reference) {
|
|
1079
|
+
const { error } = await getDb().rpc("shop_confirm_payment", {
|
|
1080
|
+
p_order_id: id,
|
|
1081
|
+
p_reference: reference ?? null
|
|
1082
|
+
});
|
|
942
1083
|
if (error) throw error;
|
|
943
1084
|
return this.getOrder(id);
|
|
944
1085
|
}
|
|
@@ -957,6 +1098,22 @@ var SupabaseShopProvider = class {
|
|
|
957
1098
|
p_currency: input.currency ?? "BRL",
|
|
958
1099
|
p_discount_code: input.discountCode ?? null,
|
|
959
1100
|
p_shipping_total: input.shippingTotal ?? 0,
|
|
1101
|
+
// snake_case keys on purpose: the RPC freezes this jsonb onto the
|
|
1102
|
+
// order and public.addresses uses these exact column names.
|
|
1103
|
+
p_shipping_address: input.shippingAddress ? {
|
|
1104
|
+
postal_code: input.shippingAddress.postalCode,
|
|
1105
|
+
street: input.shippingAddress.street,
|
|
1106
|
+
number: input.shippingAddress.number ?? null,
|
|
1107
|
+
complement: input.shippingAddress.complement ?? null,
|
|
1108
|
+
district: input.shippingAddress.district ?? null,
|
|
1109
|
+
city: input.shippingAddress.city,
|
|
1110
|
+
state: input.shippingAddress.state,
|
|
1111
|
+
country: input.shippingAddress.country ?? "BR",
|
|
1112
|
+
recipient: input.shippingAddress.recipient ?? null,
|
|
1113
|
+
phone: input.shippingAddress.phone ?? null,
|
|
1114
|
+
label: input.shippingAddress.label ?? null
|
|
1115
|
+
} : null,
|
|
1116
|
+
p_payment_method: input.paymentMethod ?? null,
|
|
960
1117
|
p_notes: input.notes ?? null
|
|
961
1118
|
});
|
|
962
1119
|
if (error) throw error;
|
|
@@ -971,7 +1128,7 @@ var SupabaseShopProvider = class {
|
|
|
971
1128
|
async listCustomers(options) {
|
|
972
1129
|
const db = getDb();
|
|
973
1130
|
const tenantId = getTenantId();
|
|
974
|
-
let q = db.from(
|
|
1131
|
+
let q = db.from(T.customers).select("*").order("first_name", { ascending: true });
|
|
975
1132
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
976
1133
|
if (options?.search) q = q.or(`first_name.ilike.%${options.search}%,last_name.ilike.%${options.search}%,email.ilike.%${options.search}%`);
|
|
977
1134
|
if (options?.limit) q = q.limit(options.limit);
|
|
@@ -980,12 +1137,12 @@ var SupabaseShopProvider = class {
|
|
|
980
1137
|
return (data ?? []).map(rowToCustomer);
|
|
981
1138
|
}
|
|
982
1139
|
async getCustomer(id) {
|
|
983
|
-
const { data, error } = await getDb().from(
|
|
1140
|
+
const { data, error } = await getDb().from(T.customers).select("*").eq("id", id).maybeSingle();
|
|
984
1141
|
if (error) throw error;
|
|
985
1142
|
return data ? rowToCustomer(data) : null;
|
|
986
1143
|
}
|
|
987
1144
|
async createCustomer(input) {
|
|
988
|
-
const { data, error } = await getDb().from(
|
|
1145
|
+
const { data, error } = await getDb().from(T.customers).insert({ tenant_id: getTenantId(), first_name: input.firstName, last_name: input.lastName ?? "", email: input.email ?? null, phone: input.phone ?? null, notes: input.notes ?? null }).select().single();
|
|
989
1146
|
if (error) throw error;
|
|
990
1147
|
return rowToCustomer(data);
|
|
991
1148
|
}
|
|
@@ -996,12 +1153,12 @@ var SupabaseShopProvider = class {
|
|
|
996
1153
|
if (input.email !== void 0) updates.email = input.email;
|
|
997
1154
|
if (input.phone !== void 0) updates.phone = input.phone;
|
|
998
1155
|
if (input.notes !== void 0) updates.notes = input.notes;
|
|
999
|
-
const { data, error } = await getDb().from(
|
|
1156
|
+
const { data, error } = await getDb().from(T.customers).update(updates).eq("id", id).select().single();
|
|
1000
1157
|
if (error) throw error;
|
|
1001
1158
|
return rowToCustomer(data);
|
|
1002
1159
|
}
|
|
1003
1160
|
async deleteCustomer(id) {
|
|
1004
|
-
const { error } = await getDb().from(
|
|
1161
|
+
const { error } = await getDb().from(T.customers).delete().eq("id", id);
|
|
1005
1162
|
if (error) throw error;
|
|
1006
1163
|
}
|
|
1007
1164
|
// Find-or-create + link to auth.uid server-side (SECURITY DEFINER RPC), so the
|
|
@@ -1026,7 +1183,7 @@ var SupabaseShopProvider = class {
|
|
|
1026
1183
|
async listDiscounts(options) {
|
|
1027
1184
|
const db = getDb();
|
|
1028
1185
|
const tenantId = getTenantId();
|
|
1029
|
-
let q = db.from(
|
|
1186
|
+
let q = db.from(T.discounts).select("*").order("created_at", { ascending: false });
|
|
1030
1187
|
if (tenantId) q = q.eq("tenant_id", tenantId);
|
|
1031
1188
|
if (options?.status) q = q.eq("status", options.status);
|
|
1032
1189
|
if (options?.type) q = q.eq("type", options.type);
|
|
@@ -1037,12 +1194,12 @@ var SupabaseShopProvider = class {
|
|
|
1037
1194
|
return (data ?? []).map(rowToDiscount);
|
|
1038
1195
|
}
|
|
1039
1196
|
async getDiscount(id) {
|
|
1040
|
-
const { data, error } = await getDb().from(
|
|
1197
|
+
const { data, error } = await getDb().from(T.discounts).select("*").eq("id", id).maybeSingle();
|
|
1041
1198
|
if (error) throw error;
|
|
1042
1199
|
return data ? rowToDiscount(data) : null;
|
|
1043
1200
|
}
|
|
1044
1201
|
async createDiscount(input) {
|
|
1045
|
-
const { data, error } = await getDb().from(
|
|
1202
|
+
const { data, error } = await getDb().from(T.discounts).insert({ tenant_id: getTenantId(), title: input.title, code: input.code ?? null, type: input.type, method: input.method ?? "code", value: input.value, usage_limit: input.usageLimit ?? null, once_per_customer: input.oncePerCustomer ?? false, starts_at: input.startsAt ?? (/* @__PURE__ */ new Date()).toISOString(), ends_at: input.endsAt ?? null, status: input.status ?? "active", times_used: 0 }).select().single();
|
|
1046
1203
|
if (error) throw error;
|
|
1047
1204
|
return rowToDiscount(data);
|
|
1048
1205
|
}
|
|
@@ -1055,12 +1212,12 @@ var SupabaseShopProvider = class {
|
|
|
1055
1212
|
if (input.usageLimit !== void 0) updates.usage_limit = input.usageLimit;
|
|
1056
1213
|
if (input.status !== void 0) updates.status = input.status;
|
|
1057
1214
|
if (input.endsAt !== void 0) updates.ends_at = input.endsAt;
|
|
1058
|
-
const { data, error } = await getDb().from(
|
|
1215
|
+
const { data, error } = await getDb().from(T.discounts).update(updates).eq("id", id).select().single();
|
|
1059
1216
|
if (error) throw error;
|
|
1060
1217
|
return rowToDiscount(data);
|
|
1061
1218
|
}
|
|
1062
1219
|
async deleteDiscount(id) {
|
|
1063
|
-
const { error } = await getDb().from(
|
|
1220
|
+
const { error } = await getDb().from(T.discounts).delete().eq("id", id);
|
|
1064
1221
|
if (error) throw error;
|
|
1065
1222
|
}
|
|
1066
1223
|
};
|