@infuro/cms-core 1.0.43 → 1.0.44
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.cjs +17 -10
- package/dist/admin.js +17 -10
- package/dist/api.cjs +32 -32
- package/dist/api.js +1 -1
- package/dist/{chunk-SCPKMZG5.cjs → chunk-P2IWWBJV.cjs} +220 -20
- package/dist/{chunk-IX7X4ILO.js → chunk-SQGBHYJF.js} +220 -20
- package/dist/index.cjs +201 -201
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -462,6 +462,41 @@ function applyApprovalStatusSideEffects(updatePayload, opts) {
|
|
|
462
462
|
}
|
|
463
463
|
}
|
|
464
464
|
chunkUSNT2KNT_cjs.__name(applyApprovalStatusSideEffects, "applyApprovalStatusSideEffects");
|
|
465
|
+
async function syncProductVariantsStatusWithProduct(dataSource, entityMap, productId, productStatus) {
|
|
466
|
+
if (!entityMap.product_variants || !Number.isFinite(productId) || productId < 1) return;
|
|
467
|
+
const status = String(productStatus || "draft");
|
|
468
|
+
const repo = dataSource.getRepository(entityMap.product_variants);
|
|
469
|
+
if (status === "available") {
|
|
470
|
+
await repo.createQueryBuilder().update().set({
|
|
471
|
+
status: "available"
|
|
472
|
+
}).where('"productId" = :productId', {
|
|
473
|
+
productId
|
|
474
|
+
}).andWhere("status = :from", {
|
|
475
|
+
from: "draft"
|
|
476
|
+
}).execute();
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (status === "draft") {
|
|
480
|
+
await repo.createQueryBuilder().update().set({
|
|
481
|
+
status: "draft"
|
|
482
|
+
}).where('"productId" = :productId', {
|
|
483
|
+
productId
|
|
484
|
+
}).andWhere("status = :from", {
|
|
485
|
+
from: "available"
|
|
486
|
+
}).execute();
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
chunkUSNT2KNT_cjs.__name(syncProductVariantsStatusWithProduct, "syncProductVariantsStatusWithProduct");
|
|
490
|
+
function coerceVariantStatusForProduct(variantStatus, productStatus, opts) {
|
|
491
|
+
const next = String(variantStatus ?? "draft").trim() || "draft";
|
|
492
|
+
const productLive = String(productStatus ?? "draft") === "available";
|
|
493
|
+
const approvalOk = !opts?.requireApproval || String(opts?.approvalStatus ?? "") === "approved";
|
|
494
|
+
if (!productLive || !approvalOk) {
|
|
495
|
+
return "draft";
|
|
496
|
+
}
|
|
497
|
+
return next;
|
|
498
|
+
}
|
|
499
|
+
chunkUSNT2KNT_cjs.__name(coerceVariantStatusForProduct, "coerceVariantStatusForProduct");
|
|
465
500
|
|
|
466
501
|
// src/lib/event-approval.ts
|
|
467
502
|
var EVENT_APPROVAL_STATUSES = [
|
|
@@ -4604,6 +4639,26 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4604
4639
|
}
|
|
4605
4640
|
}
|
|
4606
4641
|
}
|
|
4642
|
+
if (resource === "product_variants" && entityMap.products) {
|
|
4643
|
+
const productIdRaw = persistBody.productId;
|
|
4644
|
+
const productId = typeof productIdRaw === "number" ? productIdRaw : typeof productIdRaw === "string" && /^\d+$/.test(productIdRaw) ? parseInt(productIdRaw, 10) : NaN;
|
|
4645
|
+
if (Number.isFinite(productId)) {
|
|
4646
|
+
const parent = await dataSource.getRepository(entityMap.products).findOne({
|
|
4647
|
+
where: {
|
|
4648
|
+
id: productId
|
|
4649
|
+
}
|
|
4650
|
+
});
|
|
4651
|
+
const parentStatus = parent?.status;
|
|
4652
|
+
const parentApproval = parent?.approvalStatus;
|
|
4653
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
4654
|
+
persistBody.status = coerceVariantStatusForProduct(persistBody.status, parentStatus, {
|
|
4655
|
+
approvalStatus: parentApproval,
|
|
4656
|
+
requireApproval
|
|
4657
|
+
});
|
|
4658
|
+
} else if (!("status" in persistBody)) {
|
|
4659
|
+
persistBody.status = "draft";
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4607
4662
|
if (resource === "products") {
|
|
4608
4663
|
const scopeForProduct = await resolveScope();
|
|
4609
4664
|
const productFlags = await getVendorCatalogCreateFlags(dataSource);
|
|
@@ -6243,6 +6298,34 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6243
6298
|
delete u.parentId;
|
|
6244
6299
|
}
|
|
6245
6300
|
}
|
|
6301
|
+
if (resource === "product_variants" && entityMap.products) {
|
|
6302
|
+
const currentVariant = await repo.findOne({
|
|
6303
|
+
where: {
|
|
6304
|
+
id: numericId
|
|
6305
|
+
}
|
|
6306
|
+
});
|
|
6307
|
+
if (!currentVariant) return json({
|
|
6308
|
+
message: "Not found"
|
|
6309
|
+
}, {
|
|
6310
|
+
status: 404
|
|
6311
|
+
});
|
|
6312
|
+
const productId = "productId" in updatePayload && updatePayload.productId != null ? Number(updatePayload.productId) : Number(currentVariant.productId);
|
|
6313
|
+
if (Number.isFinite(productId)) {
|
|
6314
|
+
const parent = await dataSource.getRepository(entityMap.products).findOne({
|
|
6315
|
+
where: {
|
|
6316
|
+
id: productId
|
|
6317
|
+
}
|
|
6318
|
+
});
|
|
6319
|
+
const parentStatus = parent?.status;
|
|
6320
|
+
const parentApproval = parent?.approvalStatus;
|
|
6321
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
6322
|
+
const nextStatus = "status" in updatePayload ? updatePayload.status : currentVariant.status;
|
|
6323
|
+
updatePayload.status = coerceVariantStatusForProduct(nextStatus, parentStatus, {
|
|
6324
|
+
approvalStatus: parentApproval,
|
|
6325
|
+
requireApproval
|
|
6326
|
+
});
|
|
6327
|
+
}
|
|
6328
|
+
}
|
|
6246
6329
|
if (resource === "products") {
|
|
6247
6330
|
const currentRow = await repo.findOne({
|
|
6248
6331
|
where: {
|
|
@@ -6598,6 +6681,10 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6598
6681
|
if (reloaded) updated = reloaded;
|
|
6599
6682
|
}
|
|
6600
6683
|
updated = hydrateProductDisplayName(updated);
|
|
6684
|
+
const productStatus = String(updated.status ?? "draft");
|
|
6685
|
+
if (Number.isFinite(updatedId) && updatedId > 0) {
|
|
6686
|
+
await syncProductVariantsStatusWithProduct(dataSource, entityMap, updatedId, productStatus);
|
|
6687
|
+
}
|
|
6601
6688
|
if (getCms) {
|
|
6602
6689
|
const cms = await getCms();
|
|
6603
6690
|
await queueErpProductUpsertIfEnabled(cms, dataSource, entityMap, updated);
|
|
@@ -29815,7 +29902,8 @@ function createStorefrontApiHandler(config) {
|
|
|
29815
29902
|
const repo = dataSource.getRepository(entityMap.product_variants);
|
|
29816
29903
|
const rows = await repo.find({
|
|
29817
29904
|
where: {
|
|
29818
|
-
productId
|
|
29905
|
+
productId,
|
|
29906
|
+
status: "available"
|
|
29819
29907
|
},
|
|
29820
29908
|
order: {
|
|
29821
29909
|
id: "ASC"
|
|
@@ -29848,6 +29936,52 @@ function createStorefrontApiHandler(config) {
|
|
|
29848
29936
|
return result;
|
|
29849
29937
|
}
|
|
29850
29938
|
chunkUSNT2KNT_cjs.__name(loadProductVariantsWithPricing, "loadProductVariantsWithPricing");
|
|
29939
|
+
async function loadApplicableVendorPolicies(productId, vendorId) {
|
|
29940
|
+
if (!entityMap.refund_policies) return [];
|
|
29941
|
+
const policyRepo = dataSource.getRepository(entityMap.refund_policies);
|
|
29942
|
+
const format = /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((row) => ({
|
|
29943
|
+
id: Number(row.id),
|
|
29944
|
+
name: String(row.name ?? "").trim(),
|
|
29945
|
+
desc: row.desc != null ? String(row.desc) : null,
|
|
29946
|
+
refundWindowDays: Number(row.refundWindowDays) || 0,
|
|
29947
|
+
type: String(row.type ?? "percentage"),
|
|
29948
|
+
value: Number(row.value) || 0
|
|
29949
|
+
}), "format");
|
|
29950
|
+
const linkedIds = /* @__PURE__ */ new Set();
|
|
29951
|
+
if (entityMap.product_config) {
|
|
29952
|
+
const configs = await dataSource.getRepository(entityMap.product_config).find({
|
|
29953
|
+
where: {
|
|
29954
|
+
productId
|
|
29955
|
+
}
|
|
29956
|
+
});
|
|
29957
|
+
for (const c of configs) {
|
|
29958
|
+
const id = Number(c.refundPolicyId);
|
|
29959
|
+
if (Number.isFinite(id) && id > 0) linkedIds.add(id);
|
|
29960
|
+
}
|
|
29961
|
+
}
|
|
29962
|
+
if (linkedIds.size > 0) {
|
|
29963
|
+
const rows = await policyRepo.find({
|
|
29964
|
+
where: {
|
|
29965
|
+
id: typeorm.In([
|
|
29966
|
+
...linkedIds
|
|
29967
|
+
])
|
|
29968
|
+
}
|
|
29969
|
+
});
|
|
29970
|
+
return rows.map((r) => format(r)).filter((p) => p.name).sort((a, b) => a.id - b.id);
|
|
29971
|
+
}
|
|
29972
|
+
if (vendorId == null || !Number.isFinite(Number(vendorId))) return [];
|
|
29973
|
+
const vendorRows = await policyRepo.find({
|
|
29974
|
+
where: {
|
|
29975
|
+
vendorId: Number(vendorId),
|
|
29976
|
+
status: "active"
|
|
29977
|
+
},
|
|
29978
|
+
order: {
|
|
29979
|
+
id: "ASC"
|
|
29980
|
+
}
|
|
29981
|
+
});
|
|
29982
|
+
return vendorRows.map((r) => format(r)).filter((p) => p.name);
|
|
29983
|
+
}
|
|
29984
|
+
chunkUSNT2KNT_cjs.__name(loadApplicableVendorPolicies, "loadApplicableVendorPolicies");
|
|
29851
29985
|
return {
|
|
29852
29986
|
async handle(method, path2, req) {
|
|
29853
29987
|
try {
|
|
@@ -29915,13 +30049,11 @@ function createStorefrontApiHandler(config) {
|
|
|
29915
30049
|
const url = new URL(req.url || "", "http://localhost");
|
|
29916
30050
|
const collectionSlug = url.searchParams.get("collection")?.trim();
|
|
29917
30051
|
const collectionId = url.searchParams.get("collectionId");
|
|
30052
|
+
const q = url.searchParams.get("q")?.trim() ?? "";
|
|
29918
30053
|
const limit = Math.min(100, Math.max(1, parseInt(url.searchParams.get("limit") || "20", 10)));
|
|
29919
30054
|
const offset = Math.max(0, parseInt(url.searchParams.get("offset") || "0", 10));
|
|
29920
|
-
const where = {
|
|
29921
|
-
status: "available",
|
|
29922
|
-
deleted: false
|
|
29923
|
-
};
|
|
29924
30055
|
let collectionFilter = null;
|
|
30056
|
+
let collectionIdFilter = null;
|
|
29925
30057
|
if (collectionSlug) {
|
|
29926
30058
|
let col = null;
|
|
29927
30059
|
if (/^\d+$/.test(collectionSlug)) {
|
|
@@ -29948,30 +30080,59 @@ function createStorefrontApiHandler(config) {
|
|
|
29948
30080
|
collection: null
|
|
29949
30081
|
});
|
|
29950
30082
|
}
|
|
29951
|
-
|
|
30083
|
+
collectionIdFilter = Number(col.id);
|
|
29952
30084
|
collectionFilter = {
|
|
29953
30085
|
name: col.name,
|
|
29954
30086
|
slug: col.slug
|
|
29955
30087
|
};
|
|
29956
30088
|
} else if (collectionId) {
|
|
29957
30089
|
const cid = parseInt(collectionId, 10);
|
|
29958
|
-
if (Number.isFinite(cid))
|
|
30090
|
+
if (Number.isFinite(cid)) collectionIdFilter = cid;
|
|
30091
|
+
}
|
|
30092
|
+
let items = [];
|
|
30093
|
+
let total = 0;
|
|
30094
|
+
if (q) {
|
|
30095
|
+
const like = `%${q.replace(/[%_]/g, "\\$&")}%`;
|
|
30096
|
+
const qb = productRepo().createQueryBuilder("p").where("p.status = :status", {
|
|
30097
|
+
status: "available"
|
|
30098
|
+
}).andWhere("p.deleted = :del", {
|
|
30099
|
+
del: false
|
|
30100
|
+
}).andWhere(`(p.name ILIKE :like OR p.slug ILIKE :like OR COALESCE(p.sku, '') ILIKE :like OR COALESCE(p.title, '') ILIKE :like OR COALESCE(p.metadata->>'description', '') ILIKE :like)`, {
|
|
30101
|
+
like
|
|
30102
|
+
}).orderBy("p.id", "ASC").take(limit).skip(offset);
|
|
30103
|
+
if (collectionIdFilter != null) {
|
|
30104
|
+
qb.andWhere("p.collectionId = :cid", {
|
|
30105
|
+
cid: collectionIdFilter
|
|
30106
|
+
});
|
|
30107
|
+
}
|
|
30108
|
+
[items, total] = await qb.getManyAndCount();
|
|
30109
|
+
} else {
|
|
30110
|
+
const where = {
|
|
30111
|
+
status: "available",
|
|
30112
|
+
deleted: false
|
|
30113
|
+
};
|
|
30114
|
+
if (collectionIdFilter != null) where.collectionId = collectionIdFilter;
|
|
30115
|
+
const result = await productRepo().findAndCount({
|
|
30116
|
+
where,
|
|
30117
|
+
order: {
|
|
30118
|
+
id: "ASC"
|
|
30119
|
+
},
|
|
30120
|
+
take: limit,
|
|
30121
|
+
skip: offset
|
|
30122
|
+
});
|
|
30123
|
+
items = result[0];
|
|
30124
|
+
total = result[1];
|
|
29959
30125
|
}
|
|
29960
|
-
const [items, total] = await productRepo().findAndCount({
|
|
29961
|
-
where,
|
|
29962
|
-
order: {
|
|
29963
|
-
id: "ASC"
|
|
29964
|
-
},
|
|
29965
|
-
take: limit,
|
|
29966
|
-
skip: offset
|
|
29967
|
-
});
|
|
29968
30126
|
const products = await Promise.all(items.map((item) => enrichProductPricing(item)));
|
|
29969
30127
|
return json({
|
|
29970
30128
|
products,
|
|
29971
30129
|
total,
|
|
29972
30130
|
...collectionFilter && {
|
|
29973
30131
|
collection: collectionFilter
|
|
29974
|
-
}
|
|
30132
|
+
},
|
|
30133
|
+
...q ? {
|
|
30134
|
+
q
|
|
30135
|
+
} : {}
|
|
29975
30136
|
});
|
|
29976
30137
|
}
|
|
29977
30138
|
if (path2[0] === "products" && path2.length === 2 && method === "GET") {
|
|
@@ -30007,12 +30168,14 @@ function createStorefrontApiHandler(config) {
|
|
|
30007
30168
|
const pricing = await resolveProductEventPricing(Number(p.id));
|
|
30008
30169
|
const enriched = await enrichProductPricing(p, pricing);
|
|
30009
30170
|
const variants = await loadProductVariantsWithPricing(Number(p.id), pricing);
|
|
30171
|
+
const policies = await loadApplicableVendorPolicies(Number(p.id), p.vendorId != null ? Number(p.vendorId) : null);
|
|
30010
30172
|
return json({
|
|
30011
30173
|
...enriched,
|
|
30012
30174
|
attributes: attributeTags,
|
|
30013
30175
|
...variants.length ? {
|
|
30014
30176
|
variants
|
|
30015
|
-
} : {}
|
|
30177
|
+
} : {},
|
|
30178
|
+
policies
|
|
30016
30179
|
});
|
|
30017
30180
|
}
|
|
30018
30181
|
if (path2[0] === "collections" && path2.length === 1 && method === "GET") {
|
|
@@ -30824,24 +30987,61 @@ function createStorefrontApiHandler(config) {
|
|
|
30824
30987
|
}, {
|
|
30825
30988
|
status: 404
|
|
30826
30989
|
});
|
|
30990
|
+
const rawVariantId = body.variantId ?? body.variant_id;
|
|
30991
|
+
const variantIdNum = rawVariantId != null && String(rawVariantId).trim() !== "" ? Number(rawVariantId) : NaN;
|
|
30992
|
+
const hasVariantId = Number.isFinite(variantIdNum) && variantIdNum > 0;
|
|
30993
|
+
const bodyMeta = body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? body.metadata : {};
|
|
30994
|
+
const optionsRaw = body.options && typeof body.options === "object" && !Array.isArray(body.options) ? body.options : bodyMeta.options && typeof bodyMeta.options === "object" && !Array.isArray(bodyMeta.options) ? bodyMeta.options : null;
|
|
30995
|
+
const options = optionsRaw && Object.fromEntries(Object.entries(optionsRaw).map(([k, v]) => [
|
|
30996
|
+
String(k),
|
|
30997
|
+
String(v ?? "").trim()
|
|
30998
|
+
]).filter(([, v]) => v.length > 0));
|
|
30999
|
+
const lineMetadata = {
|
|
31000
|
+
...bodyMeta,
|
|
31001
|
+
...hasVariantId ? {
|
|
31002
|
+
variantId: variantIdNum
|
|
31003
|
+
} : {},
|
|
31004
|
+
...options && Object.keys(options).length ? {
|
|
31005
|
+
options
|
|
31006
|
+
} : {}
|
|
31007
|
+
};
|
|
31008
|
+
if (typeof bodyMeta.imageUrl === "string" && bodyMeta.imageUrl.trim()) {
|
|
31009
|
+
lineMetadata.imageUrl = bodyMeta.imageUrl.trim();
|
|
31010
|
+
}
|
|
31011
|
+
if (typeof bodyMeta.title === "string" && bodyMeta.title.trim()) {
|
|
31012
|
+
lineMetadata.title = bodyMeta.title.trim();
|
|
31013
|
+
}
|
|
31014
|
+
const metadataPayload = Object.keys(lineMetadata).length ? lineMetadata : null;
|
|
30827
31015
|
const { cart, setCookie, err } = await getOrCreateCart(req);
|
|
30828
31016
|
if (err) return err;
|
|
30829
31017
|
const cartId = cart.id;
|
|
30830
|
-
const
|
|
31018
|
+
const sameProductLines = await cartItemRepo().find({
|
|
30831
31019
|
where: {
|
|
30832
31020
|
cartId,
|
|
30833
31021
|
productId
|
|
30834
31022
|
}
|
|
30835
31023
|
});
|
|
31024
|
+
const existing = sameProductLines.find((row) => {
|
|
31025
|
+
const m = row.metadata;
|
|
31026
|
+
const existingVid = m?.variantId ?? m?.variant_id;
|
|
31027
|
+
if (hasVariantId) {
|
|
31028
|
+
return Number(existingVid) === variantIdNum;
|
|
31029
|
+
}
|
|
31030
|
+
return existingVid == null || existingVid === "";
|
|
31031
|
+
});
|
|
30836
31032
|
if (existing) {
|
|
30837
31033
|
await cartItemRepo().update(existing.id, {
|
|
30838
|
-
quantity: existing.quantity + quantity
|
|
31034
|
+
quantity: existing.quantity + quantity,
|
|
31035
|
+
...metadataPayload ? {
|
|
31036
|
+
metadata: metadataPayload
|
|
31037
|
+
} : {}
|
|
30839
31038
|
});
|
|
30840
31039
|
} else {
|
|
30841
31040
|
await cartItemRepo().save(cartItemRepo().create({
|
|
30842
31041
|
cartId,
|
|
30843
31042
|
productId,
|
|
30844
|
-
quantity
|
|
31043
|
+
quantity,
|
|
31044
|
+
metadata: metadataPayload
|
|
30845
31045
|
}));
|
|
30846
31046
|
}
|
|
30847
31047
|
await cartRepo().update(cartId, {
|
|
@@ -452,6 +452,41 @@ function applyApprovalStatusSideEffects(updatePayload, opts) {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
__name(applyApprovalStatusSideEffects, "applyApprovalStatusSideEffects");
|
|
455
|
+
async function syncProductVariantsStatusWithProduct(dataSource, entityMap, productId, productStatus) {
|
|
456
|
+
if (!entityMap.product_variants || !Number.isFinite(productId) || productId < 1) return;
|
|
457
|
+
const status = String(productStatus || "draft");
|
|
458
|
+
const repo = dataSource.getRepository(entityMap.product_variants);
|
|
459
|
+
if (status === "available") {
|
|
460
|
+
await repo.createQueryBuilder().update().set({
|
|
461
|
+
status: "available"
|
|
462
|
+
}).where('"productId" = :productId', {
|
|
463
|
+
productId
|
|
464
|
+
}).andWhere("status = :from", {
|
|
465
|
+
from: "draft"
|
|
466
|
+
}).execute();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (status === "draft") {
|
|
470
|
+
await repo.createQueryBuilder().update().set({
|
|
471
|
+
status: "draft"
|
|
472
|
+
}).where('"productId" = :productId', {
|
|
473
|
+
productId
|
|
474
|
+
}).andWhere("status = :from", {
|
|
475
|
+
from: "available"
|
|
476
|
+
}).execute();
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
__name(syncProductVariantsStatusWithProduct, "syncProductVariantsStatusWithProduct");
|
|
480
|
+
function coerceVariantStatusForProduct(variantStatus, productStatus, opts) {
|
|
481
|
+
const next = String(variantStatus ?? "draft").trim() || "draft";
|
|
482
|
+
const productLive = String(productStatus ?? "draft") === "available";
|
|
483
|
+
const approvalOk = !opts?.requireApproval || String(opts?.approvalStatus ?? "") === "approved";
|
|
484
|
+
if (!productLive || !approvalOk) {
|
|
485
|
+
return "draft";
|
|
486
|
+
}
|
|
487
|
+
return next;
|
|
488
|
+
}
|
|
489
|
+
__name(coerceVariantStatusForProduct, "coerceVariantStatusForProduct");
|
|
455
490
|
|
|
456
491
|
// src/lib/event-approval.ts
|
|
457
492
|
var EVENT_APPROVAL_STATUSES = [
|
|
@@ -4594,6 +4629,26 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4594
4629
|
}
|
|
4595
4630
|
}
|
|
4596
4631
|
}
|
|
4632
|
+
if (resource === "product_variants" && entityMap.products) {
|
|
4633
|
+
const productIdRaw = persistBody.productId;
|
|
4634
|
+
const productId = typeof productIdRaw === "number" ? productIdRaw : typeof productIdRaw === "string" && /^\d+$/.test(productIdRaw) ? parseInt(productIdRaw, 10) : NaN;
|
|
4635
|
+
if (Number.isFinite(productId)) {
|
|
4636
|
+
const parent = await dataSource.getRepository(entityMap.products).findOne({
|
|
4637
|
+
where: {
|
|
4638
|
+
id: productId
|
|
4639
|
+
}
|
|
4640
|
+
});
|
|
4641
|
+
const parentStatus = parent?.status;
|
|
4642
|
+
const parentApproval = parent?.approvalStatus;
|
|
4643
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
4644
|
+
persistBody.status = coerceVariantStatusForProduct(persistBody.status, parentStatus, {
|
|
4645
|
+
approvalStatus: parentApproval,
|
|
4646
|
+
requireApproval
|
|
4647
|
+
});
|
|
4648
|
+
} else if (!("status" in persistBody)) {
|
|
4649
|
+
persistBody.status = "draft";
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4597
4652
|
if (resource === "products") {
|
|
4598
4653
|
const scopeForProduct = await resolveScope();
|
|
4599
4654
|
const productFlags = await getVendorCatalogCreateFlags(dataSource);
|
|
@@ -6233,6 +6288,34 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6233
6288
|
delete u.parentId;
|
|
6234
6289
|
}
|
|
6235
6290
|
}
|
|
6291
|
+
if (resource === "product_variants" && entityMap.products) {
|
|
6292
|
+
const currentVariant = await repo.findOne({
|
|
6293
|
+
where: {
|
|
6294
|
+
id: numericId
|
|
6295
|
+
}
|
|
6296
|
+
});
|
|
6297
|
+
if (!currentVariant) return json({
|
|
6298
|
+
message: "Not found"
|
|
6299
|
+
}, {
|
|
6300
|
+
status: 404
|
|
6301
|
+
});
|
|
6302
|
+
const productId = "productId" in updatePayload && updatePayload.productId != null ? Number(updatePayload.productId) : Number(currentVariant.productId);
|
|
6303
|
+
if (Number.isFinite(productId)) {
|
|
6304
|
+
const parent = await dataSource.getRepository(entityMap.products).findOne({
|
|
6305
|
+
where: {
|
|
6306
|
+
id: productId
|
|
6307
|
+
}
|
|
6308
|
+
});
|
|
6309
|
+
const parentStatus = parent?.status;
|
|
6310
|
+
const parentApproval = parent?.approvalStatus;
|
|
6311
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
6312
|
+
const nextStatus = "status" in updatePayload ? updatePayload.status : currentVariant.status;
|
|
6313
|
+
updatePayload.status = coerceVariantStatusForProduct(nextStatus, parentStatus, {
|
|
6314
|
+
approvalStatus: parentApproval,
|
|
6315
|
+
requireApproval
|
|
6316
|
+
});
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6236
6319
|
if (resource === "products") {
|
|
6237
6320
|
const currentRow = await repo.findOne({
|
|
6238
6321
|
where: {
|
|
@@ -6588,6 +6671,10 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6588
6671
|
if (reloaded) updated = reloaded;
|
|
6589
6672
|
}
|
|
6590
6673
|
updated = hydrateProductDisplayName(updated);
|
|
6674
|
+
const productStatus = String(updated.status ?? "draft");
|
|
6675
|
+
if (Number.isFinite(updatedId) && updatedId > 0) {
|
|
6676
|
+
await syncProductVariantsStatusWithProduct(dataSource, entityMap, updatedId, productStatus);
|
|
6677
|
+
}
|
|
6591
6678
|
if (getCms) {
|
|
6592
6679
|
const cms = await getCms();
|
|
6593
6680
|
await queueErpProductUpsertIfEnabled(cms, dataSource, entityMap, updated);
|
|
@@ -29805,7 +29892,8 @@ function createStorefrontApiHandler(config) {
|
|
|
29805
29892
|
const repo = dataSource.getRepository(entityMap.product_variants);
|
|
29806
29893
|
const rows = await repo.find({
|
|
29807
29894
|
where: {
|
|
29808
|
-
productId
|
|
29895
|
+
productId,
|
|
29896
|
+
status: "available"
|
|
29809
29897
|
},
|
|
29810
29898
|
order: {
|
|
29811
29899
|
id: "ASC"
|
|
@@ -29838,6 +29926,52 @@ function createStorefrontApiHandler(config) {
|
|
|
29838
29926
|
return result;
|
|
29839
29927
|
}
|
|
29840
29928
|
__name(loadProductVariantsWithPricing, "loadProductVariantsWithPricing");
|
|
29929
|
+
async function loadApplicableVendorPolicies(productId, vendorId) {
|
|
29930
|
+
if (!entityMap.refund_policies) return [];
|
|
29931
|
+
const policyRepo = dataSource.getRepository(entityMap.refund_policies);
|
|
29932
|
+
const format = /* @__PURE__ */ __name((row) => ({
|
|
29933
|
+
id: Number(row.id),
|
|
29934
|
+
name: String(row.name ?? "").trim(),
|
|
29935
|
+
desc: row.desc != null ? String(row.desc) : null,
|
|
29936
|
+
refundWindowDays: Number(row.refundWindowDays) || 0,
|
|
29937
|
+
type: String(row.type ?? "percentage"),
|
|
29938
|
+
value: Number(row.value) || 0
|
|
29939
|
+
}), "format");
|
|
29940
|
+
const linkedIds = /* @__PURE__ */ new Set();
|
|
29941
|
+
if (entityMap.product_config) {
|
|
29942
|
+
const configs = await dataSource.getRepository(entityMap.product_config).find({
|
|
29943
|
+
where: {
|
|
29944
|
+
productId
|
|
29945
|
+
}
|
|
29946
|
+
});
|
|
29947
|
+
for (const c of configs) {
|
|
29948
|
+
const id = Number(c.refundPolicyId);
|
|
29949
|
+
if (Number.isFinite(id) && id > 0) linkedIds.add(id);
|
|
29950
|
+
}
|
|
29951
|
+
}
|
|
29952
|
+
if (linkedIds.size > 0) {
|
|
29953
|
+
const rows = await policyRepo.find({
|
|
29954
|
+
where: {
|
|
29955
|
+
id: In([
|
|
29956
|
+
...linkedIds
|
|
29957
|
+
])
|
|
29958
|
+
}
|
|
29959
|
+
});
|
|
29960
|
+
return rows.map((r) => format(r)).filter((p) => p.name).sort((a, b) => a.id - b.id);
|
|
29961
|
+
}
|
|
29962
|
+
if (vendorId == null || !Number.isFinite(Number(vendorId))) return [];
|
|
29963
|
+
const vendorRows = await policyRepo.find({
|
|
29964
|
+
where: {
|
|
29965
|
+
vendorId: Number(vendorId),
|
|
29966
|
+
status: "active"
|
|
29967
|
+
},
|
|
29968
|
+
order: {
|
|
29969
|
+
id: "ASC"
|
|
29970
|
+
}
|
|
29971
|
+
});
|
|
29972
|
+
return vendorRows.map((r) => format(r)).filter((p) => p.name);
|
|
29973
|
+
}
|
|
29974
|
+
__name(loadApplicableVendorPolicies, "loadApplicableVendorPolicies");
|
|
29841
29975
|
return {
|
|
29842
29976
|
async handle(method, path2, req) {
|
|
29843
29977
|
try {
|
|
@@ -29905,13 +30039,11 @@ function createStorefrontApiHandler(config) {
|
|
|
29905
30039
|
const url = new URL(req.url || "", "http://localhost");
|
|
29906
30040
|
const collectionSlug = url.searchParams.get("collection")?.trim();
|
|
29907
30041
|
const collectionId = url.searchParams.get("collectionId");
|
|
30042
|
+
const q = url.searchParams.get("q")?.trim() ?? "";
|
|
29908
30043
|
const limit = Math.min(100, Math.max(1, parseInt(url.searchParams.get("limit") || "20", 10)));
|
|
29909
30044
|
const offset = Math.max(0, parseInt(url.searchParams.get("offset") || "0", 10));
|
|
29910
|
-
const where = {
|
|
29911
|
-
status: "available",
|
|
29912
|
-
deleted: false
|
|
29913
|
-
};
|
|
29914
30045
|
let collectionFilter = null;
|
|
30046
|
+
let collectionIdFilter = null;
|
|
29915
30047
|
if (collectionSlug) {
|
|
29916
30048
|
let col = null;
|
|
29917
30049
|
if (/^\d+$/.test(collectionSlug)) {
|
|
@@ -29938,30 +30070,59 @@ function createStorefrontApiHandler(config) {
|
|
|
29938
30070
|
collection: null
|
|
29939
30071
|
});
|
|
29940
30072
|
}
|
|
29941
|
-
|
|
30073
|
+
collectionIdFilter = Number(col.id);
|
|
29942
30074
|
collectionFilter = {
|
|
29943
30075
|
name: col.name,
|
|
29944
30076
|
slug: col.slug
|
|
29945
30077
|
};
|
|
29946
30078
|
} else if (collectionId) {
|
|
29947
30079
|
const cid = parseInt(collectionId, 10);
|
|
29948
|
-
if (Number.isFinite(cid))
|
|
30080
|
+
if (Number.isFinite(cid)) collectionIdFilter = cid;
|
|
30081
|
+
}
|
|
30082
|
+
let items = [];
|
|
30083
|
+
let total = 0;
|
|
30084
|
+
if (q) {
|
|
30085
|
+
const like = `%${q.replace(/[%_]/g, "\\$&")}%`;
|
|
30086
|
+
const qb = productRepo().createQueryBuilder("p").where("p.status = :status", {
|
|
30087
|
+
status: "available"
|
|
30088
|
+
}).andWhere("p.deleted = :del", {
|
|
30089
|
+
del: false
|
|
30090
|
+
}).andWhere(`(p.name ILIKE :like OR p.slug ILIKE :like OR COALESCE(p.sku, '') ILIKE :like OR COALESCE(p.title, '') ILIKE :like OR COALESCE(p.metadata->>'description', '') ILIKE :like)`, {
|
|
30091
|
+
like
|
|
30092
|
+
}).orderBy("p.id", "ASC").take(limit).skip(offset);
|
|
30093
|
+
if (collectionIdFilter != null) {
|
|
30094
|
+
qb.andWhere("p.collectionId = :cid", {
|
|
30095
|
+
cid: collectionIdFilter
|
|
30096
|
+
});
|
|
30097
|
+
}
|
|
30098
|
+
[items, total] = await qb.getManyAndCount();
|
|
30099
|
+
} else {
|
|
30100
|
+
const where = {
|
|
30101
|
+
status: "available",
|
|
30102
|
+
deleted: false
|
|
30103
|
+
};
|
|
30104
|
+
if (collectionIdFilter != null) where.collectionId = collectionIdFilter;
|
|
30105
|
+
const result = await productRepo().findAndCount({
|
|
30106
|
+
where,
|
|
30107
|
+
order: {
|
|
30108
|
+
id: "ASC"
|
|
30109
|
+
},
|
|
30110
|
+
take: limit,
|
|
30111
|
+
skip: offset
|
|
30112
|
+
});
|
|
30113
|
+
items = result[0];
|
|
30114
|
+
total = result[1];
|
|
29949
30115
|
}
|
|
29950
|
-
const [items, total] = await productRepo().findAndCount({
|
|
29951
|
-
where,
|
|
29952
|
-
order: {
|
|
29953
|
-
id: "ASC"
|
|
29954
|
-
},
|
|
29955
|
-
take: limit,
|
|
29956
|
-
skip: offset
|
|
29957
|
-
});
|
|
29958
30116
|
const products = await Promise.all(items.map((item) => enrichProductPricing(item)));
|
|
29959
30117
|
return json({
|
|
29960
30118
|
products,
|
|
29961
30119
|
total,
|
|
29962
30120
|
...collectionFilter && {
|
|
29963
30121
|
collection: collectionFilter
|
|
29964
|
-
}
|
|
30122
|
+
},
|
|
30123
|
+
...q ? {
|
|
30124
|
+
q
|
|
30125
|
+
} : {}
|
|
29965
30126
|
});
|
|
29966
30127
|
}
|
|
29967
30128
|
if (path2[0] === "products" && path2.length === 2 && method === "GET") {
|
|
@@ -29997,12 +30158,14 @@ function createStorefrontApiHandler(config) {
|
|
|
29997
30158
|
const pricing = await resolveProductEventPricing(Number(p.id));
|
|
29998
30159
|
const enriched = await enrichProductPricing(p, pricing);
|
|
29999
30160
|
const variants = await loadProductVariantsWithPricing(Number(p.id), pricing);
|
|
30161
|
+
const policies = await loadApplicableVendorPolicies(Number(p.id), p.vendorId != null ? Number(p.vendorId) : null);
|
|
30000
30162
|
return json({
|
|
30001
30163
|
...enriched,
|
|
30002
30164
|
attributes: attributeTags,
|
|
30003
30165
|
...variants.length ? {
|
|
30004
30166
|
variants
|
|
30005
|
-
} : {}
|
|
30167
|
+
} : {},
|
|
30168
|
+
policies
|
|
30006
30169
|
});
|
|
30007
30170
|
}
|
|
30008
30171
|
if (path2[0] === "collections" && path2.length === 1 && method === "GET") {
|
|
@@ -30814,24 +30977,61 @@ function createStorefrontApiHandler(config) {
|
|
|
30814
30977
|
}, {
|
|
30815
30978
|
status: 404
|
|
30816
30979
|
});
|
|
30980
|
+
const rawVariantId = body.variantId ?? body.variant_id;
|
|
30981
|
+
const variantIdNum = rawVariantId != null && String(rawVariantId).trim() !== "" ? Number(rawVariantId) : NaN;
|
|
30982
|
+
const hasVariantId = Number.isFinite(variantIdNum) && variantIdNum > 0;
|
|
30983
|
+
const bodyMeta = body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? body.metadata : {};
|
|
30984
|
+
const optionsRaw = body.options && typeof body.options === "object" && !Array.isArray(body.options) ? body.options : bodyMeta.options && typeof bodyMeta.options === "object" && !Array.isArray(bodyMeta.options) ? bodyMeta.options : null;
|
|
30985
|
+
const options = optionsRaw && Object.fromEntries(Object.entries(optionsRaw).map(([k, v]) => [
|
|
30986
|
+
String(k),
|
|
30987
|
+
String(v ?? "").trim()
|
|
30988
|
+
]).filter(([, v]) => v.length > 0));
|
|
30989
|
+
const lineMetadata = {
|
|
30990
|
+
...bodyMeta,
|
|
30991
|
+
...hasVariantId ? {
|
|
30992
|
+
variantId: variantIdNum
|
|
30993
|
+
} : {},
|
|
30994
|
+
...options && Object.keys(options).length ? {
|
|
30995
|
+
options
|
|
30996
|
+
} : {}
|
|
30997
|
+
};
|
|
30998
|
+
if (typeof bodyMeta.imageUrl === "string" && bodyMeta.imageUrl.trim()) {
|
|
30999
|
+
lineMetadata.imageUrl = bodyMeta.imageUrl.trim();
|
|
31000
|
+
}
|
|
31001
|
+
if (typeof bodyMeta.title === "string" && bodyMeta.title.trim()) {
|
|
31002
|
+
lineMetadata.title = bodyMeta.title.trim();
|
|
31003
|
+
}
|
|
31004
|
+
const metadataPayload = Object.keys(lineMetadata).length ? lineMetadata : null;
|
|
30817
31005
|
const { cart, setCookie, err } = await getOrCreateCart(req);
|
|
30818
31006
|
if (err) return err;
|
|
30819
31007
|
const cartId = cart.id;
|
|
30820
|
-
const
|
|
31008
|
+
const sameProductLines = await cartItemRepo().find({
|
|
30821
31009
|
where: {
|
|
30822
31010
|
cartId,
|
|
30823
31011
|
productId
|
|
30824
31012
|
}
|
|
30825
31013
|
});
|
|
31014
|
+
const existing = sameProductLines.find((row) => {
|
|
31015
|
+
const m = row.metadata;
|
|
31016
|
+
const existingVid = m?.variantId ?? m?.variant_id;
|
|
31017
|
+
if (hasVariantId) {
|
|
31018
|
+
return Number(existingVid) === variantIdNum;
|
|
31019
|
+
}
|
|
31020
|
+
return existingVid == null || existingVid === "";
|
|
31021
|
+
});
|
|
30826
31022
|
if (existing) {
|
|
30827
31023
|
await cartItemRepo().update(existing.id, {
|
|
30828
|
-
quantity: existing.quantity + quantity
|
|
31024
|
+
quantity: existing.quantity + quantity,
|
|
31025
|
+
...metadataPayload ? {
|
|
31026
|
+
metadata: metadataPayload
|
|
31027
|
+
} : {}
|
|
30829
31028
|
});
|
|
30830
31029
|
} else {
|
|
30831
31030
|
await cartItemRepo().save(cartItemRepo().create({
|
|
30832
31031
|
cartId,
|
|
30833
31032
|
productId,
|
|
30834
|
-
quantity
|
|
31033
|
+
quantity,
|
|
31034
|
+
metadata: metadataPayload
|
|
30835
31035
|
}));
|
|
30836
31036
|
}
|
|
30837
31037
|
await cartRepo().update(cartId, {
|