@infuro/cms-core 1.0.43 → 1.0.45
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 +557 -416
- package/dist/admin.d.cts +1 -1
- package/dist/admin.d.ts +1 -1
- package/dist/admin.js +557 -416
- package/dist/api.cjs +32 -32
- package/dist/api.d.cts +2 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +1 -1
- package/dist/{chunk-SCPKMZG5.cjs → chunk-F5XFHYTG.cjs} +924 -290
- package/dist/{chunk-IX7X4ILO.js → chunk-MVXLRANV.js} +921 -292
- package/dist/index.cjs +241 -201
- package/dist/index.d.cts +42 -7
- package/dist/index.d.ts +42 -7
- package/dist/index.js +21 -2
- package/dist/migrations/1781800000000-CustomerPhoneNullable.ts +53 -0
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@ import { permissionRowsToRecord, logRbac, isSuperAdmin, vendorPortalFlagsFromUse
|
|
|
10
10
|
import { queueErp } from './chunk-SYBOCAWB.js';
|
|
11
11
|
import { isErpIntegrationEnabled } from './chunk-JC6DLWTE.js';
|
|
12
12
|
import { __name } from './chunk-SHUYVCID.js';
|
|
13
|
-
import { PrimaryGeneratedColumn, Column, Entity, ManyToOne, JoinColumn, OneToMany, Index, ManyToMany, JoinTable, Unique, CreateDateColumn, UpdateDateColumn, In,
|
|
13
|
+
import { PrimaryGeneratedColumn, Column, Entity, ManyToOne, JoinColumn, OneToMany, Index, ManyToMany, JoinTable, Unique, CreateDateColumn, UpdateDateColumn, In, ILike, Between, MoreThanOrEqual, LessThanOrEqual, Not, MoreThan, IsNull } from 'typeorm';
|
|
14
14
|
import { Country, State, City } from 'country-state-city';
|
|
15
15
|
import crypto2, { randomBytes, randomUUID, createHmac, timingSafeEqual, randomInt } from 'crypto';
|
|
16
16
|
import Parser from 'rss-parser';
|
|
@@ -36,6 +36,7 @@ async function queryVendorLinks(dataSource, userId) {
|
|
|
36
36
|
vr."name" AS "vendorRoleName",
|
|
37
37
|
vr."isOwnerRole" AS "isOwnerRole"
|
|
38
38
|
FROM "vendor_users" vu
|
|
39
|
+
INNER JOIN "vendors" v ON v.id = vu."vendorId" AND v.deleted = false
|
|
39
40
|
LEFT JOIN "vendor_roles" vr ON vr.id = vu."vendorRoleId" AND vr.deleted = false
|
|
40
41
|
WHERE vu."userId" = $1
|
|
41
42
|
ORDER BY vu.id ASC
|
|
@@ -45,10 +46,11 @@ async function queryVendorLinks(dataSource, userId) {
|
|
|
45
46
|
} catch (err) {
|
|
46
47
|
if (!isMissingVendorRoleSchemaError(err)) throw err;
|
|
47
48
|
const legacy = await dataSource.query(`
|
|
48
|
-
SELECT "vendorId", "role"
|
|
49
|
-
FROM "vendor_users"
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
SELECT vu."vendorId" AS "vendorId", vu."role" AS "role"
|
|
50
|
+
FROM "vendor_users" vu
|
|
51
|
+
INNER JOIN "vendors" v ON v.id = vu."vendorId" AND v.deleted = false
|
|
52
|
+
WHERE vu."userId" = $1
|
|
53
|
+
ORDER BY vu.id ASC
|
|
52
54
|
`, [
|
|
53
55
|
userId
|
|
54
56
|
]);
|
|
@@ -62,8 +64,36 @@ async function queryVendorLinks(dataSource, userId) {
|
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
66
|
__name(queryVendorLinks, "queryVendorLinks");
|
|
67
|
+
async function queryOwnedVendorIds(dataSource, userId) {
|
|
68
|
+
try {
|
|
69
|
+
const rows = await dataSource.query(`
|
|
70
|
+
SELECT id
|
|
71
|
+
FROM "vendors"
|
|
72
|
+
WHERE "userId" = $1 AND deleted = false
|
|
73
|
+
ORDER BY id ASC
|
|
74
|
+
`, [
|
|
75
|
+
userId
|
|
76
|
+
]);
|
|
77
|
+
return rows.map((r) => Number(r.id)).filter((id) => Number.isFinite(id) && id > 0);
|
|
78
|
+
} catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
__name(queryOwnedVendorIds, "queryOwnedVendorIds");
|
|
65
83
|
async function loadUserVendorContext(dataSource, userId, preferredVendorId) {
|
|
66
84
|
const rows = await queryVendorLinks(dataSource, userId);
|
|
85
|
+
const linkIds = new Set(rows.map((r) => Number(r.vendorId)));
|
|
86
|
+
for (const ownedId of await queryOwnedVendorIds(dataSource, userId)) {
|
|
87
|
+
if (linkIds.has(ownedId)) continue;
|
|
88
|
+
rows.push({
|
|
89
|
+
vendorId: ownedId,
|
|
90
|
+
role: "owner",
|
|
91
|
+
vendorRoleId: null,
|
|
92
|
+
vendorRoleName: null,
|
|
93
|
+
isOwnerRole: true
|
|
94
|
+
});
|
|
95
|
+
linkIds.add(ownedId);
|
|
96
|
+
}
|
|
67
97
|
const vendorIds = rows.map((r) => Number(r.vendorId));
|
|
68
98
|
const activeRow = preferredVendorId != null ? rows.find((r) => Number(r.vendorId) === preferredVendorId) : void 0;
|
|
69
99
|
const primary = activeRow ?? rows[0];
|
|
@@ -452,6 +482,41 @@ function applyApprovalStatusSideEffects(updatePayload, opts) {
|
|
|
452
482
|
}
|
|
453
483
|
}
|
|
454
484
|
__name(applyApprovalStatusSideEffects, "applyApprovalStatusSideEffects");
|
|
485
|
+
async function syncProductVariantsStatusWithProduct(dataSource, entityMap, productId, productStatus) {
|
|
486
|
+
if (!entityMap.product_variants || !Number.isFinite(productId) || productId < 1) return;
|
|
487
|
+
const status = String(productStatus || "draft");
|
|
488
|
+
const repo = dataSource.getRepository(entityMap.product_variants);
|
|
489
|
+
if (status === "available") {
|
|
490
|
+
await repo.createQueryBuilder().update().set({
|
|
491
|
+
status: "available"
|
|
492
|
+
}).where('"productId" = :productId', {
|
|
493
|
+
productId
|
|
494
|
+
}).andWhere("status = :from", {
|
|
495
|
+
from: "draft"
|
|
496
|
+
}).execute();
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (status === "draft") {
|
|
500
|
+
await repo.createQueryBuilder().update().set({
|
|
501
|
+
status: "draft"
|
|
502
|
+
}).where('"productId" = :productId', {
|
|
503
|
+
productId
|
|
504
|
+
}).andWhere("status = :from", {
|
|
505
|
+
from: "available"
|
|
506
|
+
}).execute();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
__name(syncProductVariantsStatusWithProduct, "syncProductVariantsStatusWithProduct");
|
|
510
|
+
function coerceVariantStatusForProduct(variantStatus, productStatus, opts) {
|
|
511
|
+
const next = String(variantStatus ?? "draft").trim() || "draft";
|
|
512
|
+
const productLive = String(productStatus ?? "draft") === "available";
|
|
513
|
+
const approvalOk = !opts?.requireApproval || String(opts?.approvalStatus ?? "") === "approved";
|
|
514
|
+
if (!productLive || !approvalOk) {
|
|
515
|
+
return "draft";
|
|
516
|
+
}
|
|
517
|
+
return next;
|
|
518
|
+
}
|
|
519
|
+
__name(coerceVariantStatusForProduct, "coerceVariantStatusForProduct");
|
|
455
520
|
|
|
456
521
|
// src/lib/event-approval.ts
|
|
457
522
|
var EVENT_APPROVAL_STATUSES = [
|
|
@@ -1055,11 +1120,118 @@ function pgErrorCode(err) {
|
|
|
1055
1120
|
return driver?.code ?? err.code;
|
|
1056
1121
|
}
|
|
1057
1122
|
__name(pgErrorCode, "pgErrorCode");
|
|
1058
|
-
function
|
|
1123
|
+
function normalizeCustomerPhone(phone) {
|
|
1059
1124
|
const p = typeof phone === "string" ? phone.trim() : "";
|
|
1060
|
-
return p ||
|
|
1125
|
+
return p || null;
|
|
1126
|
+
}
|
|
1127
|
+
__name(normalizeCustomerPhone, "normalizeCustomerPhone");
|
|
1128
|
+
function isSyntheticCustomerPhone(phone) {
|
|
1129
|
+
const p = String(phone ?? "").trim();
|
|
1130
|
+
if (!p) return true;
|
|
1131
|
+
if (p.startsWith("e-") || p.startsWith("u-")) return true;
|
|
1132
|
+
if (p.includes("@")) return true;
|
|
1133
|
+
return false;
|
|
1134
|
+
}
|
|
1135
|
+
__name(isSyntheticCustomerPhone, "isSyntheticCustomerPhone");
|
|
1136
|
+
function customerPhoneForUser(_userId, phone) {
|
|
1137
|
+
return normalizeCustomerPhone(phone);
|
|
1061
1138
|
}
|
|
1062
1139
|
__name(customerPhoneForUser, "customerPhoneForUser");
|
|
1140
|
+
function customerPhoneForEmail(_email, phone) {
|
|
1141
|
+
return normalizeCustomerPhone(phone);
|
|
1142
|
+
}
|
|
1143
|
+
__name(customerPhoneForEmail, "customerPhoneForEmail");
|
|
1144
|
+
function resolveNextPhone(inputPhone, existingPhone) {
|
|
1145
|
+
if (inputPhone) return inputPhone;
|
|
1146
|
+
const existing = normalizeCustomerPhone(existingPhone);
|
|
1147
|
+
if (!existing || isSyntheticCustomerPhone(existing)) return null;
|
|
1148
|
+
return existing;
|
|
1149
|
+
}
|
|
1150
|
+
__name(resolveNextPhone, "resolveNextPhone");
|
|
1151
|
+
async function ensureCustomerRecord(dsOrEm, customerEntity, input) {
|
|
1152
|
+
const repo = dsOrEm.getRepository(customerEntity);
|
|
1153
|
+
const email = normalizeEmail(input.email);
|
|
1154
|
+
if (!email) return null;
|
|
1155
|
+
const name = String(input.name ?? "").trim() || email.split("@")[0] || "Customer";
|
|
1156
|
+
const userId = input.userId != null && Number.isFinite(Number(input.userId)) && Number(input.userId) > 0 ? Number(input.userId) : null;
|
|
1157
|
+
const phone = normalizeCustomerPhone(input.phone);
|
|
1158
|
+
let row = await repo.findOne({
|
|
1159
|
+
where: {
|
|
1160
|
+
email,
|
|
1161
|
+
deleted: false
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
if (!row) {
|
|
1165
|
+
row = await repo.findOne({
|
|
1166
|
+
where: {
|
|
1167
|
+
email
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
if (row) {
|
|
1172
|
+
const existingUserId = row.userId;
|
|
1173
|
+
if (userId != null && existingUserId != null && existingUserId !== userId) {
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
const nextPhone = resolveNextPhone(phone, row.phone);
|
|
1177
|
+
const patch = {
|
|
1178
|
+
name,
|
|
1179
|
+
phone: nextPhone,
|
|
1180
|
+
deleted: false,
|
|
1181
|
+
deletedAt: null,
|
|
1182
|
+
deletedBy: null,
|
|
1183
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1184
|
+
};
|
|
1185
|
+
if (userId != null && existingUserId == null) {
|
|
1186
|
+
patch.userId = userId;
|
|
1187
|
+
}
|
|
1188
|
+
await repo.update(row.id, patch);
|
|
1189
|
+
return {
|
|
1190
|
+
id: row.id
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
try {
|
|
1194
|
+
const created = await repo.save(repo.create({
|
|
1195
|
+
userId,
|
|
1196
|
+
name,
|
|
1197
|
+
email,
|
|
1198
|
+
phone,
|
|
1199
|
+
deleted: false
|
|
1200
|
+
}));
|
|
1201
|
+
return {
|
|
1202
|
+
id: created.id
|
|
1203
|
+
};
|
|
1204
|
+
} catch (err) {
|
|
1205
|
+
const code = pgErrorCode(err);
|
|
1206
|
+
if (code === "25P02") throw err;
|
|
1207
|
+
row = await repo.findOne({
|
|
1208
|
+
where: {
|
|
1209
|
+
email
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
if (row) {
|
|
1213
|
+
const existingUserId = row.userId;
|
|
1214
|
+
if (userId != null && existingUserId != null && existingUserId !== userId) return null;
|
|
1215
|
+
await repo.update(row.id, {
|
|
1216
|
+
name,
|
|
1217
|
+
phone: resolveNextPhone(phone, row.phone),
|
|
1218
|
+
...userId != null && existingUserId == null ? {
|
|
1219
|
+
userId
|
|
1220
|
+
} : {},
|
|
1221
|
+
deleted: false,
|
|
1222
|
+
deletedAt: null,
|
|
1223
|
+
deletedBy: null,
|
|
1224
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1225
|
+
});
|
|
1226
|
+
return {
|
|
1227
|
+
id: row.id
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
if (code === "23505") return null;
|
|
1231
|
+
throw err;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
__name(ensureCustomerRecord, "ensureCustomerRecord");
|
|
1063
1235
|
async function restoreCustomerRow(repo, row, user, name, email, phone) {
|
|
1064
1236
|
const id = row.id;
|
|
1065
1237
|
await repo.update(id, {
|
|
@@ -1081,7 +1253,7 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1081
1253
|
const repo = dsOrEm.getRepository(customerEntity);
|
|
1082
1254
|
const email = normalizeEmail(user.email);
|
|
1083
1255
|
const name = String(user.name ?? "").trim() || email.split("@")[0] || "User";
|
|
1084
|
-
const phone =
|
|
1256
|
+
const phone = normalizeCustomerPhone(overrides?.phone ?? user.phone);
|
|
1085
1257
|
let row = await repo.findOne({
|
|
1086
1258
|
where: {
|
|
1087
1259
|
userId: user.id,
|
|
@@ -1089,10 +1261,11 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1089
1261
|
}
|
|
1090
1262
|
});
|
|
1091
1263
|
if (row) {
|
|
1264
|
+
const nextPhone = resolveNextPhone(phone, row.phone);
|
|
1092
1265
|
await repo.update(row.id, {
|
|
1093
1266
|
name,
|
|
1094
1267
|
email,
|
|
1095
|
-
phone,
|
|
1268
|
+
phone: nextPhone,
|
|
1096
1269
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1097
1270
|
});
|
|
1098
1271
|
return {
|
|
@@ -1110,10 +1283,11 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1110
1283
|
if (existingUserId != null && existingUserId !== user.id) {
|
|
1111
1284
|
return null;
|
|
1112
1285
|
}
|
|
1286
|
+
const nextPhone = resolveNextPhone(phone, row.phone);
|
|
1113
1287
|
await repo.update(row.id, {
|
|
1114
1288
|
userId: user.id,
|
|
1115
1289
|
name,
|
|
1116
|
-
phone,
|
|
1290
|
+
phone: nextPhone,
|
|
1117
1291
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1118
1292
|
});
|
|
1119
1293
|
return {
|
|
@@ -1128,21 +1302,23 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1128
1302
|
if (deletedByEmail) {
|
|
1129
1303
|
const existingUserId = deletedByEmail.userId;
|
|
1130
1304
|
if (existingUserId != null && existingUserId !== user.id) return null;
|
|
1131
|
-
return restoreCustomerRow(repo, deletedByEmail, user, name, email, phone);
|
|
1305
|
+
return restoreCustomerRow(repo, deletedByEmail, user, name, email, resolveNextPhone(phone, deletedByEmail.phone));
|
|
1132
1306
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1307
|
+
if (phone) {
|
|
1308
|
+
const deletedByPhone = await repo.findOne({
|
|
1309
|
+
where: {
|
|
1310
|
+
phone
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
if (deletedByPhone) {
|
|
1314
|
+
const existingUserId = deletedByPhone.userId;
|
|
1315
|
+
if (existingUserId != null && existingUserId !== user.id) {
|
|
1316
|
+
return ensureCustomerForUser(dsOrEm, customerEntity, user, {
|
|
1317
|
+
phone: null
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
return restoreCustomerRow(repo, deletedByPhone, user, name, email, phone);
|
|
1144
1321
|
}
|
|
1145
|
-
return restoreCustomerRow(repo, deletedByPhone, user, name, email, phone);
|
|
1146
1322
|
}
|
|
1147
1323
|
try {
|
|
1148
1324
|
const created = await repo.save(repo.create({
|
|
@@ -1163,34 +1339,22 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1163
1339
|
userId: user.id
|
|
1164
1340
|
}
|
|
1165
1341
|
});
|
|
1166
|
-
if (row)
|
|
1342
|
+
if (row) {
|
|
1343
|
+
return restoreCustomerRow(repo, row, user, name, email, resolveNextPhone(phone, row.phone));
|
|
1344
|
+
}
|
|
1167
1345
|
row = await repo.findOne({
|
|
1168
1346
|
where: {
|
|
1169
1347
|
email
|
|
1170
1348
|
}
|
|
1171
1349
|
});
|
|
1172
1350
|
if (row && (row.userId ?? user.id) === user.id) {
|
|
1173
|
-
return restoreCustomerRow(repo, row, user, name, email, phone);
|
|
1351
|
+
return restoreCustomerRow(repo, row, user, name, email, resolveNextPhone(phone, row.phone));
|
|
1174
1352
|
}
|
|
1175
1353
|
if (code === "23505") return null;
|
|
1176
1354
|
throw err;
|
|
1177
1355
|
}
|
|
1178
1356
|
}
|
|
1179
1357
|
__name(ensureCustomerForUser, "ensureCustomerForUser");
|
|
1180
|
-
async function linkUnclaimedContactToUser(dataSource, contactsEntity, userId, email) {
|
|
1181
|
-
const repo = dataSource.getRepository(contactsEntity);
|
|
1182
|
-
const found = await repo.findOne({
|
|
1183
|
-
where: {
|
|
1184
|
-
email,
|
|
1185
|
-
userId: IsNull(),
|
|
1186
|
-
deleted: false
|
|
1187
|
-
}
|
|
1188
|
-
});
|
|
1189
|
-
if (found) await repo.update(found.id, {
|
|
1190
|
-
userId
|
|
1191
|
-
});
|
|
1192
|
-
}
|
|
1193
|
-
__name(linkUnclaimedContactToUser, "linkUnclaimedContactToUser");
|
|
1194
1358
|
|
|
1195
1359
|
// src/lib/vendor-customer-contacts.ts
|
|
1196
1360
|
function isCustomerTypeContact(type) {
|
|
@@ -1283,8 +1447,27 @@ function resolveVendorIdForContactCheck(scope, bodyOrRow) {
|
|
|
1283
1447
|
return Number.isFinite(n) ? n : null;
|
|
1284
1448
|
}
|
|
1285
1449
|
__name(resolveVendorIdForContactCheck, "resolveVendorIdForContactCheck");
|
|
1450
|
+
function uniquePositiveIds(ids) {
|
|
1451
|
+
const out = [];
|
|
1452
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1453
|
+
for (const raw of ids) {
|
|
1454
|
+
const n = Number(raw);
|
|
1455
|
+
if (!Number.isFinite(n) || n <= 0 || seen.has(n)) continue;
|
|
1456
|
+
seen.add(n);
|
|
1457
|
+
out.push(n);
|
|
1458
|
+
}
|
|
1459
|
+
return out;
|
|
1460
|
+
}
|
|
1461
|
+
__name(uniquePositiveIds, "uniquePositiveIds");
|
|
1286
1462
|
async function ensureVendorCustomerForOrderContact(dataSource, entityMap, vendorId, contactId, details) {
|
|
1287
|
-
|
|
1463
|
+
await ensureVendorCustomersForOrder(dataSource, entityMap, [
|
|
1464
|
+
vendorId
|
|
1465
|
+
], contactId, details);
|
|
1466
|
+
}
|
|
1467
|
+
__name(ensureVendorCustomerForOrderContact, "ensureVendorCustomerForOrderContact");
|
|
1468
|
+
async function ensureVendorCustomersForOrder(dataSource, entityMap, vendorIds, contactId, details) {
|
|
1469
|
+
const vendors = uniquePositiveIds(vendorIds);
|
|
1470
|
+
if (vendors.length === 0 || !Number.isFinite(contactId) || contactId <= 0) return;
|
|
1288
1471
|
const vcEntity = entityMap.vendor_customers;
|
|
1289
1472
|
const contactEntity = entityMap.contacts;
|
|
1290
1473
|
if (!vcEntity || !contactEntity) return;
|
|
@@ -1310,96 +1493,61 @@ async function ensureVendorCustomerForOrderContact(dataSource, entityMap, vendor
|
|
|
1310
1493
|
}
|
|
1311
1494
|
let customerId = null;
|
|
1312
1495
|
if (entityMap.customer) {
|
|
1313
|
-
const
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1496
|
+
const ensured = await ensureCustomerRecord(dataSource, entityMap.customer, {
|
|
1497
|
+
name,
|
|
1498
|
+
email,
|
|
1499
|
+
phone,
|
|
1500
|
+
userId: null
|
|
1319
1501
|
});
|
|
1320
|
-
if (
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1502
|
+
if (ensured) customerId = ensured.id;
|
|
1503
|
+
}
|
|
1504
|
+
for (const vendorId of vendors) {
|
|
1505
|
+
if (customerId != null) {
|
|
1506
|
+
const byCustomer = await vcRepo.findOne({
|
|
1325
1507
|
where: {
|
|
1326
|
-
|
|
1327
|
-
|
|
1508
|
+
vendorId,
|
|
1509
|
+
customerId
|
|
1328
1510
|
}
|
|
1329
1511
|
});
|
|
1330
|
-
if (
|
|
1331
|
-
|
|
1332
|
-
if (
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
where: {
|
|
1336
|
-
name: "Customer",
|
|
1337
|
-
deleted: false
|
|
1338
|
-
}
|
|
1512
|
+
if (byCustomer) {
|
|
1513
|
+
const existingContactId = byCustomer.contactId;
|
|
1514
|
+
if (existingContactId == null) {
|
|
1515
|
+
await vcRepo.update(byCustomer.id, {
|
|
1516
|
+
contactId
|
|
1339
1517
|
});
|
|
1340
|
-
if (customerGroup) groupId = Number(customerGroup.id);
|
|
1341
1518
|
}
|
|
1342
|
-
|
|
1343
|
-
name,
|
|
1344
|
-
email,
|
|
1345
|
-
phone,
|
|
1346
|
-
password: null,
|
|
1347
|
-
blocked: false,
|
|
1348
|
-
groupId,
|
|
1349
|
-
adminAccess: false
|
|
1350
|
-
}));
|
|
1519
|
+
continue;
|
|
1351
1520
|
}
|
|
1352
|
-
const userId = Number(user.id);
|
|
1353
|
-
await linkUnclaimedContactToUser(dataSource, contactEntity, userId, email);
|
|
1354
|
-
const ensured = await ensureCustomerForUser(dataSource, entityMap.customer, {
|
|
1355
|
-
id: userId,
|
|
1356
|
-
name,
|
|
1357
|
-
email,
|
|
1358
|
-
phone
|
|
1359
|
-
}, {
|
|
1360
|
-
phone
|
|
1361
|
-
});
|
|
1362
|
-
if (ensured) customerId = ensured.id;
|
|
1363
1521
|
}
|
|
1364
|
-
|
|
1365
|
-
if (customerId != null) {
|
|
1366
|
-
const byCustomer = await vcRepo.findOne({
|
|
1522
|
+
const byContact = await vcRepo.findOne({
|
|
1367
1523
|
where: {
|
|
1368
1524
|
vendorId,
|
|
1369
|
-
|
|
1525
|
+
contactId
|
|
1370
1526
|
}
|
|
1371
1527
|
});
|
|
1372
|
-
if (
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
contactId
|
|
1528
|
+
if (byContact) {
|
|
1529
|
+
if (customerId != null && byContact.customerId == null) {
|
|
1530
|
+
await vcRepo.update(byContact.id, {
|
|
1531
|
+
customerId
|
|
1377
1532
|
});
|
|
1378
1533
|
}
|
|
1379
|
-
|
|
1534
|
+
continue;
|
|
1380
1535
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1536
|
+
if (customerId != null) {
|
|
1537
|
+
await vcRepo.save(vcRepo.create({
|
|
1538
|
+
vendorId,
|
|
1539
|
+
customerId,
|
|
1540
|
+
contactId
|
|
1541
|
+
}));
|
|
1542
|
+
} else {
|
|
1543
|
+
await vcRepo.save(vcRepo.create({
|
|
1544
|
+
vendorId,
|
|
1545
|
+
contactId
|
|
1546
|
+
}));
|
|
1386
1547
|
}
|
|
1387
|
-
});
|
|
1388
|
-
if (byContact) return;
|
|
1389
|
-
if (customerId != null) {
|
|
1390
|
-
await vcRepo.save(vcRepo.create({
|
|
1391
|
-
vendorId,
|
|
1392
|
-
customerId,
|
|
1393
|
-
contactId
|
|
1394
|
-
}));
|
|
1395
|
-
return;
|
|
1396
1548
|
}
|
|
1397
|
-
await vcRepo.save(vcRepo.create({
|
|
1398
|
-
vendorId,
|
|
1399
|
-
contactId
|
|
1400
|
-
}));
|
|
1401
1549
|
}
|
|
1402
|
-
__name(
|
|
1550
|
+
__name(ensureVendorCustomersForOrder, "ensureVendorCustomersForOrder");
|
|
1403
1551
|
|
|
1404
1552
|
// src/lib/currency-prices.ts
|
|
1405
1553
|
function normalizeCurrencyCode(code) {
|
|
@@ -3765,23 +3913,24 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3765
3913
|
if (resource === "vendor_customers" && entityMap["customer"]) {
|
|
3766
3914
|
const scope = await resolveVendorCustomersScope(dataSource, entityMap, resolveScope);
|
|
3767
3915
|
const repo2 = dataSource.getRepository(entity);
|
|
3768
|
-
const qb = repo2.createQueryBuilder("vc").leftJoinAndSelect("vc.customer", "customer").leftJoinAndSelect("customer.user", "user").orderBy("vc.createdAt", sortOrder === "DESC" ? "DESC" : "ASC").skip(skip).take(limit);
|
|
3916
|
+
const qb = repo2.createQueryBuilder("vc").leftJoinAndSelect("vc.customer", "customer").leftJoinAndSelect("customer.user", "user").leftJoinAndSelect("vc.contact", "contact").orderBy("vc.createdAt", sortOrder === "DESC" ? "DESC" : "ASC").skip(skip).take(limit);
|
|
3769
3917
|
applyVendorScopeToQueryBuilder(qb, "vc", scope);
|
|
3770
3918
|
if (search && typeof search === "string" && search.trim()) {
|
|
3771
3919
|
const term = `%${search.trim()}%`;
|
|
3772
|
-
qb.andWhere("(customer.name ILIKE :term OR customer.email ILIKE :term OR customer.phone ILIKE :term)", {
|
|
3920
|
+
qb.andWhere("(customer.name ILIKE :term OR customer.email ILIKE :term OR customer.phone ILIKE :term OR contact.name ILIKE :term OR contact.email ILIKE :term OR contact.phone ILIKE :term)", {
|
|
3773
3921
|
term
|
|
3774
3922
|
});
|
|
3775
3923
|
}
|
|
3776
3924
|
const [rows, total2] = await qb.getManyAndCount();
|
|
3777
3925
|
const data2 = rows.map((row) => {
|
|
3778
3926
|
const customer = row.customer;
|
|
3927
|
+
const contact = row.contact;
|
|
3779
3928
|
return {
|
|
3780
3929
|
...row,
|
|
3781
|
-
name: customer?.name ?? null,
|
|
3782
|
-
email: customer?.email ?? null,
|
|
3783
|
-
phone: customer?.phone ?? null,
|
|
3784
|
-
company: customer?.company ?? null
|
|
3930
|
+
name: customer?.name ?? contact?.name ?? null,
|
|
3931
|
+
email: customer?.email ?? contact?.email ?? null,
|
|
3932
|
+
phone: customer?.phone ?? contact?.phone ?? null,
|
|
3933
|
+
company: customer?.company ?? contact?.company ?? null
|
|
3785
3934
|
};
|
|
3786
3935
|
});
|
|
3787
3936
|
return json({
|
|
@@ -4115,7 +4264,11 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4115
4264
|
} else if (resource === "collections") {
|
|
4116
4265
|
if (scope.type === "vendor") {
|
|
4117
4266
|
where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
|
|
4118
|
-
} else if (searchParams.get("isCatalog")
|
|
4267
|
+
} else if (searchParams.get("isCatalog") === "true") {
|
|
4268
|
+
where = mergeListWhereAnd(where, {
|
|
4269
|
+
isCatalog: true
|
|
4270
|
+
});
|
|
4271
|
+
} else if (searchParams.get("isCatalog") === "false") {
|
|
4119
4272
|
where = mergeListWhereAnd(where, {
|
|
4120
4273
|
isCatalog: false
|
|
4121
4274
|
});
|
|
@@ -4312,41 +4465,17 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4312
4465
|
status: 503
|
|
4313
4466
|
});
|
|
4314
4467
|
}
|
|
4315
|
-
if (!entityMap["
|
|
4316
|
-
return json({
|
|
4317
|
-
error: "Users entity not configured"
|
|
4318
|
-
}, {
|
|
4319
|
-
status: 503
|
|
4320
|
-
});
|
|
4321
|
-
}
|
|
4322
|
-
let customerGroupId = null;
|
|
4323
|
-
if (entityMap["user_groups"]) {
|
|
4324
|
-
const userGroupRepo = dataSource.getRepository(entityMap["user_groups"]);
|
|
4325
|
-
const customerGroup = await userGroupRepo.findOne({
|
|
4326
|
-
where: {
|
|
4327
|
-
name: "Customer",
|
|
4328
|
-
deleted: false
|
|
4329
|
-
}
|
|
4330
|
-
});
|
|
4331
|
-
if (!customerGroup) {
|
|
4332
|
-
return json({
|
|
4333
|
-
error: "User group 'customer' not found"
|
|
4334
|
-
}, {
|
|
4335
|
-
status: 500
|
|
4336
|
-
});
|
|
4337
|
-
}
|
|
4338
|
-
customerGroupId = Number(customerGroup.id);
|
|
4339
|
-
} else {
|
|
4468
|
+
if (!entityMap["contacts"]) {
|
|
4340
4469
|
return json({
|
|
4341
|
-
error: "
|
|
4470
|
+
error: "Contacts entity not configured"
|
|
4342
4471
|
}, {
|
|
4343
4472
|
status: 503
|
|
4344
4473
|
});
|
|
4345
4474
|
}
|
|
4346
4475
|
const name = String(body.name ?? "").trim();
|
|
4347
|
-
const email = String(body.email ?? "").trim();
|
|
4348
|
-
const
|
|
4349
|
-
const
|
|
4476
|
+
const email = String(body.email ?? "").trim().toLowerCase();
|
|
4477
|
+
const phoneRaw = String(body.phone ?? "").trim();
|
|
4478
|
+
const phone = phoneRaw || null;
|
|
4350
4479
|
if (!name) return json({
|
|
4351
4480
|
error: "name is required"
|
|
4352
4481
|
}, {
|
|
@@ -4357,25 +4486,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4357
4486
|
}, {
|
|
4358
4487
|
status: 400
|
|
4359
4488
|
});
|
|
4360
|
-
if (!phone) return json({
|
|
4361
|
-
error: "phone is required"
|
|
4362
|
-
}, {
|
|
4363
|
-
status: 400
|
|
4364
|
-
});
|
|
4365
|
-
if (!rawPw) return json({
|
|
4366
|
-
error: "password is required"
|
|
4367
|
-
}, {
|
|
4368
|
-
status: 400
|
|
4369
|
-
});
|
|
4370
|
-
if (rawPw.length < 6) {
|
|
4371
|
-
return json({
|
|
4372
|
-
error: "Password must be at least 6 characters"
|
|
4373
|
-
}, {
|
|
4374
|
-
status: 400
|
|
4375
|
-
});
|
|
4376
|
-
}
|
|
4377
4489
|
const customerRepo = dataSource.getRepository(entityMap["customer"]);
|
|
4378
|
-
const userRepo = dataSource.getRepository(entityMap["users"]);
|
|
4379
4490
|
const dupCustEmail = await customerRepo.findOne({
|
|
4380
4491
|
where: {
|
|
4381
4492
|
email,
|
|
@@ -4389,49 +4500,26 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4389
4500
|
status: 409
|
|
4390
4501
|
});
|
|
4391
4502
|
}
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
if (dupCustPhone) {
|
|
4399
|
-
return json({
|
|
4400
|
-
error: "A customer with this phone number already exists"
|
|
4401
|
-
}, {
|
|
4402
|
-
status: 409
|
|
4503
|
+
if (phone) {
|
|
4504
|
+
const dupCustPhone = await customerRepo.findOne({
|
|
4505
|
+
where: {
|
|
4506
|
+
phone,
|
|
4507
|
+
deleted: false
|
|
4508
|
+
}
|
|
4403
4509
|
});
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4510
|
+
if (dupCustPhone) {
|
|
4511
|
+
return json({
|
|
4512
|
+
error: "A customer with this phone number already exists"
|
|
4513
|
+
}, {
|
|
4514
|
+
status: 409
|
|
4515
|
+
});
|
|
4410
4516
|
}
|
|
4411
|
-
});
|
|
4412
|
-
if (dupUser) {
|
|
4413
|
-
userId = Number(dupUser.id);
|
|
4414
|
-
} else {
|
|
4415
|
-
const bcrypt = await import('bcryptjs');
|
|
4416
|
-
const hashedPassword = await bcrypt.hash(rawPw, 10);
|
|
4417
|
-
const savedUser = await userRepo.save(userRepo.create({
|
|
4418
|
-
name,
|
|
4419
|
-
email,
|
|
4420
|
-
phone,
|
|
4421
|
-
password: hashedPassword,
|
|
4422
|
-
groupId: customerGroupId,
|
|
4423
|
-
adminAccess: false,
|
|
4424
|
-
blocked: false
|
|
4425
|
-
}));
|
|
4426
|
-
userId = Number(savedUser.id);
|
|
4427
4517
|
}
|
|
4428
|
-
const savedCustomer = await
|
|
4429
|
-
id: userId,
|
|
4518
|
+
const savedCustomer = await ensureCustomerRecord(dataSource, entityMap["customer"], {
|
|
4430
4519
|
name,
|
|
4431
4520
|
email,
|
|
4432
|
-
phone
|
|
4433
|
-
|
|
4434
|
-
phone
|
|
4521
|
+
phone,
|
|
4522
|
+
userId: null
|
|
4435
4523
|
});
|
|
4436
4524
|
if (!savedCustomer) {
|
|
4437
4525
|
return json({
|
|
@@ -4440,37 +4528,37 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4440
4528
|
status: 409
|
|
4441
4529
|
});
|
|
4442
4530
|
}
|
|
4443
|
-
const
|
|
4444
|
-
|
|
4531
|
+
const contactRepo = dataSource.getRepository(entityMap["contacts"]);
|
|
4532
|
+
let contact = await contactRepo.findOne({
|
|
4445
4533
|
where: {
|
|
4446
|
-
|
|
4534
|
+
email,
|
|
4447
4535
|
deleted: false
|
|
4448
4536
|
}
|
|
4449
|
-
})
|
|
4450
|
-
if (
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
}
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
}
|
|
4537
|
+
});
|
|
4538
|
+
if (!contact) {
|
|
4539
|
+
contact = await contactRepo.save(contactRepo.create({
|
|
4540
|
+
name,
|
|
4541
|
+
email,
|
|
4542
|
+
phone: phone || null,
|
|
4543
|
+
type: "customer"
|
|
4544
|
+
}));
|
|
4545
|
+
} else {
|
|
4546
|
+
const t = contact.type;
|
|
4547
|
+
const patch = {
|
|
4548
|
+
name,
|
|
4549
|
+
phone: phone || contact.phone || null
|
|
4550
|
+
};
|
|
4551
|
+
if (t == null || t === "" || String(t).toLowerCase() !== "customer") {
|
|
4552
|
+
patch.type = "customer";
|
|
4553
|
+
}
|
|
4554
|
+
await contactRepo.update(contact.id, patch);
|
|
4555
|
+
contact = {
|
|
4556
|
+
...contact,
|
|
4557
|
+
...patch,
|
|
4558
|
+
id: contact.id
|
|
4559
|
+
};
|
|
4473
4560
|
}
|
|
4561
|
+
const contactId = Number(contact.id);
|
|
4474
4562
|
const scope = await resolveVendorCustomersScope(dataSource, entityMap, resolveScope);
|
|
4475
4563
|
let vendorId = null;
|
|
4476
4564
|
if (scope.type === "vendor") {
|
|
@@ -4486,19 +4574,25 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4486
4574
|
status: 400
|
|
4487
4575
|
});
|
|
4488
4576
|
}
|
|
4489
|
-
|
|
4490
|
-
|
|
4577
|
+
await ensureVendorCustomersForOrder(dataSource, entityMap, [
|
|
4578
|
+
vendorId
|
|
4579
|
+
], contactId, {
|
|
4580
|
+
name,
|
|
4581
|
+
email,
|
|
4582
|
+
phone
|
|
4583
|
+
});
|
|
4584
|
+
const customerRow = await customerRepo.findOne({
|
|
4491
4585
|
where: {
|
|
4492
|
-
|
|
4586
|
+
id: savedCustomer.id,
|
|
4587
|
+
deleted: false
|
|
4493
4588
|
}
|
|
4494
4589
|
});
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
}
|
|
4501
|
-
return json(customerRow, {
|
|
4590
|
+
return json(customerRow ?? {
|
|
4591
|
+
id: savedCustomer.id,
|
|
4592
|
+
name,
|
|
4593
|
+
email,
|
|
4594
|
+
phone
|
|
4595
|
+
}, {
|
|
4502
4596
|
status: 201
|
|
4503
4597
|
});
|
|
4504
4598
|
}
|
|
@@ -4594,6 +4688,26 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4594
4688
|
}
|
|
4595
4689
|
}
|
|
4596
4690
|
}
|
|
4691
|
+
if (resource === "product_variants" && entityMap.products) {
|
|
4692
|
+
const productIdRaw = persistBody.productId;
|
|
4693
|
+
const productId = typeof productIdRaw === "number" ? productIdRaw : typeof productIdRaw === "string" && /^\d+$/.test(productIdRaw) ? parseInt(productIdRaw, 10) : NaN;
|
|
4694
|
+
if (Number.isFinite(productId)) {
|
|
4695
|
+
const parent = await dataSource.getRepository(entityMap.products).findOne({
|
|
4696
|
+
where: {
|
|
4697
|
+
id: productId
|
|
4698
|
+
}
|
|
4699
|
+
});
|
|
4700
|
+
const parentStatus = parent?.status;
|
|
4701
|
+
const parentApproval = parent?.approvalStatus;
|
|
4702
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
4703
|
+
persistBody.status = coerceVariantStatusForProduct(persistBody.status, parentStatus, {
|
|
4704
|
+
approvalStatus: parentApproval,
|
|
4705
|
+
requireApproval
|
|
4706
|
+
});
|
|
4707
|
+
} else if (!("status" in persistBody)) {
|
|
4708
|
+
persistBody.status = "draft";
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4597
4711
|
if (resource === "products") {
|
|
4598
4712
|
const scopeForProduct = await resolveScope();
|
|
4599
4713
|
const productFlags = await getVendorCatalogCreateFlags(dataSource);
|
|
@@ -4926,6 +5040,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4926
5040
|
}
|
|
4927
5041
|
const nameRaw = String(body["contact.name"] ?? contact.name ?? "").trim();
|
|
4928
5042
|
const phoneRaw = body["contact.phone"] ?? contact.phone;
|
|
5043
|
+
const emailForVc = (emailRaw || String(contact.email ?? "")).trim().toLowerCase();
|
|
4929
5044
|
const phoneToSave = phoneRaw == null || phoneRaw === "" ? null : String(phoneRaw).trim();
|
|
4930
5045
|
if (phoneToSave && contact.phone !== phoneToSave) {
|
|
4931
5046
|
await contactRepo.update(contact.id, {
|
|
@@ -4934,21 +5049,45 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4934
5049
|
contact.phone = phoneToSave;
|
|
4935
5050
|
}
|
|
4936
5051
|
const vendorIdForCustomer = resolveVendorIdForContactCheck(scopeCreate, persistBody);
|
|
4937
|
-
const
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
5052
|
+
const orderLinesForVendors = normalizeOrderLinesInput(body.orderLines);
|
|
5053
|
+
const vendorIdsForVc = [];
|
|
5054
|
+
if (vendorIdForCustomer != null) vendorIdsForVc.push(vendorIdForCustomer);
|
|
5055
|
+
if (orderLinesForVendors?.length && entityMap.products) {
|
|
5056
|
+
const productRepoForVc = dataSource.getRepository(entityMap.products);
|
|
5057
|
+
for (const line of orderLinesForVendors) {
|
|
5058
|
+
const pid = Number(line.productId);
|
|
5059
|
+
if (!Number.isFinite(pid)) continue;
|
|
5060
|
+
const product = await productRepoForVc.findOne({
|
|
5061
|
+
where: {
|
|
5062
|
+
id: pid
|
|
5063
|
+
}
|
|
5064
|
+
});
|
|
5065
|
+
const pVid = Number(product?.vendorId);
|
|
5066
|
+
if (Number.isFinite(pVid) && pVid > 0) vendorIdsForVc.push(pVid);
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
if (vendorIdsForVc.length > 0 && emailForVc) {
|
|
5070
|
+
await ensureVendorCustomersForOrder(dataSource, entityMap, vendorIdsForVc, contact.id, {
|
|
5071
|
+
name: nameRaw || emailForVc.split("@")[0] || "Customer",
|
|
5072
|
+
email: emailForVc,
|
|
4942
5073
|
phone: phoneRaw == null || phoneRaw === "" ? null : String(phoneRaw)
|
|
4943
5074
|
});
|
|
4944
|
-
}
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
5075
|
+
}
|
|
5076
|
+
const accountCustomerId = Number(body.accountCustomerId);
|
|
5077
|
+
if (entityMap.customer_contacts && Number.isFinite(accountCustomerId) && accountCustomerId > 0) {
|
|
5078
|
+
const ccRepo = dataSource.getRepository(entityMap.customer_contacts);
|
|
5079
|
+
const orderContactId = contact.id;
|
|
5080
|
+
const existingLink = await ccRepo.findOne({
|
|
5081
|
+
where: {
|
|
5082
|
+
customerId: accountCustomerId,
|
|
5083
|
+
contactId: orderContactId
|
|
5084
|
+
}
|
|
5085
|
+
});
|
|
5086
|
+
if (!existingLink) {
|
|
5087
|
+
await ccRepo.save(ccRepo.create({
|
|
5088
|
+
customerId: accountCustomerId,
|
|
5089
|
+
contactId: orderContactId
|
|
5090
|
+
}));
|
|
4952
5091
|
}
|
|
4953
5092
|
}
|
|
4954
5093
|
const randomPart = Math.floor(1e4 + Math.random() * 9e4);
|
|
@@ -6233,6 +6372,34 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6233
6372
|
delete u.parentId;
|
|
6234
6373
|
}
|
|
6235
6374
|
}
|
|
6375
|
+
if (resource === "product_variants" && entityMap.products) {
|
|
6376
|
+
const currentVariant = await repo.findOne({
|
|
6377
|
+
where: {
|
|
6378
|
+
id: numericId
|
|
6379
|
+
}
|
|
6380
|
+
});
|
|
6381
|
+
if (!currentVariant) return json({
|
|
6382
|
+
message: "Not found"
|
|
6383
|
+
}, {
|
|
6384
|
+
status: 404
|
|
6385
|
+
});
|
|
6386
|
+
const productId = "productId" in updatePayload && updatePayload.productId != null ? Number(updatePayload.productId) : Number(currentVariant.productId);
|
|
6387
|
+
if (Number.isFinite(productId)) {
|
|
6388
|
+
const parent = await dataSource.getRepository(entityMap.products).findOne({
|
|
6389
|
+
where: {
|
|
6390
|
+
id: productId
|
|
6391
|
+
}
|
|
6392
|
+
});
|
|
6393
|
+
const parentStatus = parent?.status;
|
|
6394
|
+
const parentApproval = parent?.approvalStatus;
|
|
6395
|
+
const requireApproval = await getRequireProductApproval(dataSource);
|
|
6396
|
+
const nextStatus = "status" in updatePayload ? updatePayload.status : currentVariant.status;
|
|
6397
|
+
updatePayload.status = coerceVariantStatusForProduct(nextStatus, parentStatus, {
|
|
6398
|
+
approvalStatus: parentApproval,
|
|
6399
|
+
requireApproval
|
|
6400
|
+
});
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
6236
6403
|
if (resource === "products") {
|
|
6237
6404
|
const currentRow = await repo.findOne({
|
|
6238
6405
|
where: {
|
|
@@ -6588,6 +6755,10 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6588
6755
|
if (reloaded) updated = reloaded;
|
|
6589
6756
|
}
|
|
6590
6757
|
updated = hydrateProductDisplayName(updated);
|
|
6758
|
+
const productStatus = String(updated.status ?? "draft");
|
|
6759
|
+
if (Number.isFinite(updatedId) && updatedId > 0) {
|
|
6760
|
+
await syncProductVariantsStatusWithProduct(dataSource, entityMap, updatedId, productStatus);
|
|
6761
|
+
}
|
|
6591
6762
|
if (getCms) {
|
|
6592
6763
|
const cms = await getCms();
|
|
6593
6764
|
await queueErpProductUpsertIfEnabled(cms, dataSource, entityMap, updated);
|
|
@@ -6696,6 +6867,14 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6696
6867
|
}
|
|
6697
6868
|
}
|
|
6698
6869
|
await repo.update(numericId, buildSoftDeletePayload(repo.metadata, deletedBy));
|
|
6870
|
+
if ((resource === "forms" || resource === "vendors") && existing && typeof existing.slug === "string") {
|
|
6871
|
+
const slug = String(existing.slug).trim();
|
|
6872
|
+
if (slug && !slug.includes("__deleted_")) {
|
|
6873
|
+
await repo.update(numericId, {
|
|
6874
|
+
slug: `${slug}__deleted_${numericId}`
|
|
6875
|
+
});
|
|
6876
|
+
}
|
|
6877
|
+
}
|
|
6699
6878
|
return json({
|
|
6700
6879
|
message: "Deleted successfully"
|
|
6701
6880
|
}, {
|
|
@@ -6725,7 +6904,21 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6725
6904
|
}
|
|
6726
6905
|
};
|
|
6727
6906
|
}
|
|
6728
|
-
__name(createCrudByIdHandler, "createCrudByIdHandler");
|
|
6907
|
+
__name(createCrudByIdHandler, "createCrudByIdHandler");
|
|
6908
|
+
async function linkUnclaimedContactToUser(dataSource, contactsEntity, userId, email) {
|
|
6909
|
+
const repo = dataSource.getRepository(contactsEntity);
|
|
6910
|
+
const found = await repo.findOne({
|
|
6911
|
+
where: {
|
|
6912
|
+
email,
|
|
6913
|
+
userId: IsNull(),
|
|
6914
|
+
deleted: false
|
|
6915
|
+
}
|
|
6916
|
+
});
|
|
6917
|
+
if (found) await repo.update(found.id, {
|
|
6918
|
+
userId
|
|
6919
|
+
});
|
|
6920
|
+
}
|
|
6921
|
+
__name(linkUnclaimedContactToUser, "linkUnclaimedContactToUser");
|
|
6729
6922
|
var VENDOR_INVITE_METADATA_TOKEN = "inviteToken";
|
|
6730
6923
|
var VENDOR_INVITE_METADATA_EXPIRES = "inviteExpiresAt";
|
|
6731
6924
|
var VENDOR_INVITE_METADATA_SENT = "inviteSentAt";
|
|
@@ -7458,6 +7651,50 @@ async function findLlmAgentByScope(dataSource, entityMap, scope, options = {}) {
|
|
|
7458
7651
|
}
|
|
7459
7652
|
__name(findLlmAgentByScope, "findLlmAgentByScope");
|
|
7460
7653
|
|
|
7654
|
+
// src/lib/retire-soft-deleted-unique.ts
|
|
7655
|
+
async function retireSoftDeletedUniqueValue(repo, column, value) {
|
|
7656
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
7657
|
+
if (!trimmed) return;
|
|
7658
|
+
const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
|
|
7659
|
+
if (!cols.has(column) || !cols.has("deleted")) return;
|
|
7660
|
+
const rows = await repo.find({
|
|
7661
|
+
where: {
|
|
7662
|
+
[column]: trimmed,
|
|
7663
|
+
deleted: true
|
|
7664
|
+
}
|
|
7665
|
+
});
|
|
7666
|
+
for (const row of rows) {
|
|
7667
|
+
const id = row.id;
|
|
7668
|
+
if (!Number.isFinite(id)) continue;
|
|
7669
|
+
const current = String(row[column] ?? "");
|
|
7670
|
+
if (current.includes("__deleted_")) continue;
|
|
7671
|
+
await repo.update(id, {
|
|
7672
|
+
[column]: `${trimmed}__deleted_${id}`
|
|
7673
|
+
});
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
__name(retireSoftDeletedUniqueValue, "retireSoftDeletedUniqueValue");
|
|
7677
|
+
async function activeUniqueValueExists(repo, column, value, excludeId) {
|
|
7678
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
7679
|
+
if (!trimmed) return false;
|
|
7680
|
+
const cols = new Set(repo.metadata.columns.map((c) => c.propertyName));
|
|
7681
|
+
if (!cols.has(column)) return false;
|
|
7682
|
+
const qb = repo.createQueryBuilder("row").where(`row.${column} = :value`, {
|
|
7683
|
+
value: trimmed
|
|
7684
|
+
});
|
|
7685
|
+
if (cols.has("deleted")) {
|
|
7686
|
+
qb.andWhere("row.deleted = false");
|
|
7687
|
+
}
|
|
7688
|
+
if (excludeId != null && Number.isFinite(excludeId) && excludeId > 0) {
|
|
7689
|
+
qb.andWhere("row.id != :excludeId", {
|
|
7690
|
+
excludeId
|
|
7691
|
+
});
|
|
7692
|
+
}
|
|
7693
|
+
const hit = await qb.getOne();
|
|
7694
|
+
return !!hit;
|
|
7695
|
+
}
|
|
7696
|
+
__name(activeUniqueValueExists, "activeUniqueValueExists");
|
|
7697
|
+
|
|
7461
7698
|
// src/lib/media-folder-path.ts
|
|
7462
7699
|
function sanitizeMediaFolderPath(input) {
|
|
7463
7700
|
if (input == null) return "";
|
|
@@ -8851,6 +9088,17 @@ function createFormSaveHandlers(config) {
|
|
|
8851
9088
|
});
|
|
8852
9089
|
const fields = Array.isArray(body.fields) ? body.fields : [];
|
|
8853
9090
|
const { fields: _f, ...formRow } = body;
|
|
9091
|
+
const slug = typeof formRow.slug === "string" ? formRow.slug.trim() : "";
|
|
9092
|
+
if (slug) {
|
|
9093
|
+
if (await activeUniqueValueExists(formRepo(), "slug", slug)) {
|
|
9094
|
+
return json({
|
|
9095
|
+
error: "A form with this slug already exists"
|
|
9096
|
+
}, {
|
|
9097
|
+
status: 400
|
|
9098
|
+
});
|
|
9099
|
+
}
|
|
9100
|
+
await retireSoftDeletedUniqueValue(formRepo(), "slug", slug);
|
|
9101
|
+
}
|
|
8854
9102
|
const form = await formRepo().save(formRepo().create(formRow));
|
|
8855
9103
|
for (let i = 0; i < fields.length; i++) {
|
|
8856
9104
|
const row = normalizeFieldRow(fields[i], form.id);
|
|
@@ -8874,6 +9122,14 @@ function createFormSaveHandlers(config) {
|
|
|
8874
9122
|
status: 201
|
|
8875
9123
|
});
|
|
8876
9124
|
} catch (e) {
|
|
9125
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9126
|
+
if (/duplicate key|unique constraint/i.test(msg) && /slug/i.test(msg)) {
|
|
9127
|
+
return json({
|
|
9128
|
+
error: "A form with this slug already exists"
|
|
9129
|
+
}, {
|
|
9130
|
+
status: 400
|
|
9131
|
+
});
|
|
9132
|
+
}
|
|
8877
9133
|
return json({
|
|
8878
9134
|
error: "Server Error"
|
|
8879
9135
|
}, {
|
|
@@ -8922,6 +9178,20 @@ function createFormSaveHandlers(config) {
|
|
|
8922
9178
|
]) {
|
|
8923
9179
|
if (body[key] !== void 0) formRow[key] = body[key];
|
|
8924
9180
|
}
|
|
9181
|
+
if (typeof formRow.slug === "string") {
|
|
9182
|
+
const slug = formRow.slug.trim();
|
|
9183
|
+
formRow.slug = slug;
|
|
9184
|
+
if (slug) {
|
|
9185
|
+
if (await activeUniqueValueExists(formRepo(), "slug", slug, formId)) {
|
|
9186
|
+
return json({
|
|
9187
|
+
error: "A form with this slug already exists"
|
|
9188
|
+
}, {
|
|
9189
|
+
status: 400
|
|
9190
|
+
});
|
|
9191
|
+
}
|
|
9192
|
+
await retireSoftDeletedUniqueValue(formRepo(), "slug", slug);
|
|
9193
|
+
}
|
|
9194
|
+
}
|
|
8925
9195
|
if (Object.keys(formRow).length > 0) await formRepo().update(formId, formRow);
|
|
8926
9196
|
await fieldRepo().delete({
|
|
8927
9197
|
formId
|
|
@@ -8950,6 +9220,14 @@ function createFormSaveHandlers(config) {
|
|
|
8950
9220
|
status: 404
|
|
8951
9221
|
});
|
|
8952
9222
|
} catch (e) {
|
|
9223
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9224
|
+
if (/duplicate key|unique constraint/i.test(msg) && /slug/i.test(msg)) {
|
|
9225
|
+
return json({
|
|
9226
|
+
error: "A form with this slug already exists"
|
|
9227
|
+
}, {
|
|
9228
|
+
status: 400
|
|
9229
|
+
});
|
|
9230
|
+
}
|
|
8953
9231
|
return json({
|
|
8954
9232
|
error: "Server Error"
|
|
8955
9233
|
}, {
|
|
@@ -9531,6 +9809,7 @@ function createUsersApiHandlers(config) {
|
|
|
9531
9809
|
"id",
|
|
9532
9810
|
"name",
|
|
9533
9811
|
"email",
|
|
9812
|
+
"phone",
|
|
9534
9813
|
"blocked",
|
|
9535
9814
|
"createdAt",
|
|
9536
9815
|
"updatedAt",
|
|
@@ -9560,6 +9839,11 @@ function createUsersApiHandlers(config) {
|
|
|
9560
9839
|
}
|
|
9561
9840
|
try {
|
|
9562
9841
|
const uid = parseInt(id, 10);
|
|
9842
|
+
if (!Number.isFinite(uid)) return json({
|
|
9843
|
+
error: "Invalid id"
|
|
9844
|
+
}, {
|
|
9845
|
+
status: 400
|
|
9846
|
+
});
|
|
9563
9847
|
const existing = await userRepo().findOne({
|
|
9564
9848
|
where: {
|
|
9565
9849
|
id: uid,
|
|
@@ -9572,8 +9856,59 @@ function createUsersApiHandlers(config) {
|
|
|
9572
9856
|
status: 404
|
|
9573
9857
|
});
|
|
9574
9858
|
const body = await req.json();
|
|
9575
|
-
const
|
|
9576
|
-
|
|
9859
|
+
const patch = {
|
|
9860
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
9861
|
+
};
|
|
9862
|
+
if (typeof body.name === "string") patch.name = body.name.trim();
|
|
9863
|
+
if (typeof body.email === "string") patch.email = body.email.trim().toLowerCase();
|
|
9864
|
+
if (body.blocked !== void 0) {
|
|
9865
|
+
patch.blocked = body.blocked === true || body.blocked === "true" || body.blocked === 1 || body.blocked === "1";
|
|
9866
|
+
}
|
|
9867
|
+
if (body.adminAccess !== void 0) {
|
|
9868
|
+
patch.adminAccess = body.adminAccess === true || body.adminAccess === "true" || body.adminAccess === 1 || body.adminAccess === "1";
|
|
9869
|
+
}
|
|
9870
|
+
if (body.groupId !== void 0) {
|
|
9871
|
+
if (body.groupId === null || body.groupId === "") {
|
|
9872
|
+
patch.groupId = null;
|
|
9873
|
+
} else {
|
|
9874
|
+
const gid = Number(body.groupId);
|
|
9875
|
+
if (!Number.isFinite(gid)) {
|
|
9876
|
+
return json({
|
|
9877
|
+
error: "Invalid groupId"
|
|
9878
|
+
}, {
|
|
9879
|
+
status: 400
|
|
9880
|
+
});
|
|
9881
|
+
}
|
|
9882
|
+
patch.groupId = gid;
|
|
9883
|
+
}
|
|
9884
|
+
}
|
|
9885
|
+
if (body.phone !== void 0) {
|
|
9886
|
+
const phone = body.phone == null ? null : String(body.phone).trim();
|
|
9887
|
+
patch.phone = phone || null;
|
|
9888
|
+
}
|
|
9889
|
+
if (Object.keys(patch).length <= 1) {
|
|
9890
|
+
return json({
|
|
9891
|
+
error: "No valid fields to update"
|
|
9892
|
+
}, {
|
|
9893
|
+
status: 400
|
|
9894
|
+
});
|
|
9895
|
+
}
|
|
9896
|
+
if (typeof patch.email === "string" && patch.email !== existing.email) {
|
|
9897
|
+
const emailTaken = await userRepo().findOne({
|
|
9898
|
+
where: {
|
|
9899
|
+
email: patch.email,
|
|
9900
|
+
deleted: false
|
|
9901
|
+
}
|
|
9902
|
+
});
|
|
9903
|
+
if (emailTaken && Number(emailTaken.id) !== uid) {
|
|
9904
|
+
return json({
|
|
9905
|
+
error: "Email already in use"
|
|
9906
|
+
}, {
|
|
9907
|
+
status: 400
|
|
9908
|
+
});
|
|
9909
|
+
}
|
|
9910
|
+
}
|
|
9911
|
+
await userRepo().update(uid, patch);
|
|
9577
9912
|
const updated = await userRepo().findOne({
|
|
9578
9913
|
where: {
|
|
9579
9914
|
id: uid,
|
|
@@ -9586,6 +9921,7 @@ function createUsersApiHandlers(config) {
|
|
|
9586
9921
|
"id",
|
|
9587
9922
|
"name",
|
|
9588
9923
|
"email",
|
|
9924
|
+
"phone",
|
|
9589
9925
|
"blocked",
|
|
9590
9926
|
"createdAt",
|
|
9591
9927
|
"updatedAt",
|
|
@@ -9597,7 +9933,8 @@ function createUsersApiHandlers(config) {
|
|
|
9597
9933
|
}, {
|
|
9598
9934
|
status: 404
|
|
9599
9935
|
});
|
|
9600
|
-
} catch {
|
|
9936
|
+
} catch (err) {
|
|
9937
|
+
console.error("[users.update]", err);
|
|
9601
9938
|
return json({
|
|
9602
9939
|
error: "Server Error"
|
|
9603
9940
|
}, {
|
|
@@ -12645,6 +12982,39 @@ function slugify(input) {
|
|
|
12645
12982
|
return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
12646
12983
|
}
|
|
12647
12984
|
__name(slugify, "slugify");
|
|
12985
|
+
async function userHasActiveVendor(em, entityMap, userId) {
|
|
12986
|
+
const vendorRepo = em.getRepository(entityMap.vendors);
|
|
12987
|
+
const owned = await vendorRepo.findOne({
|
|
12988
|
+
where: {
|
|
12989
|
+
userId,
|
|
12990
|
+
deleted: false
|
|
12991
|
+
}
|
|
12992
|
+
});
|
|
12993
|
+
if (owned) return true;
|
|
12994
|
+
if (!entityMap.vendor_users) return false;
|
|
12995
|
+
const link = await em.getRepository(entityMap.vendor_users).createQueryBuilder("vu").innerJoin("vendors", "v", 'v.id = vu."vendorId" AND v.deleted = false').where('vu."userId" = :userId', {
|
|
12996
|
+
userId
|
|
12997
|
+
}).getOne();
|
|
12998
|
+
return !!link;
|
|
12999
|
+
}
|
|
13000
|
+
__name(userHasActiveVendor, "userHasActiveVendor");
|
|
13001
|
+
async function userHadAnyVendor(em, entityMap, userId) {
|
|
13002
|
+
const vendorRepo = em.getRepository(entityMap.vendors);
|
|
13003
|
+
const owned = await vendorRepo.findOne({
|
|
13004
|
+
where: {
|
|
13005
|
+
userId
|
|
13006
|
+
}
|
|
13007
|
+
});
|
|
13008
|
+
if (owned) return true;
|
|
13009
|
+
if (!entityMap.vendor_users) return false;
|
|
13010
|
+
const link = await em.getRepository(entityMap.vendor_users).findOne({
|
|
13011
|
+
where: {
|
|
13012
|
+
userId
|
|
13013
|
+
}
|
|
13014
|
+
});
|
|
13015
|
+
return !!link;
|
|
13016
|
+
}
|
|
13017
|
+
__name(userHadAnyVendor, "userHadAnyVendor");
|
|
12648
13018
|
function vendorOnboardErrorResponse(json, err) {
|
|
12649
13019
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12650
13020
|
console.error("[vendor-onboard]", err);
|
|
@@ -12743,14 +13113,80 @@ function createVendorOnboardHandlers(config) {
|
|
|
12743
13113
|
__name(gateAdmin, "gateAdmin");
|
|
12744
13114
|
async function resolveActiveVendorId(u) {
|
|
12745
13115
|
const scope = resolveVendorScopeFromSessionUser(u);
|
|
12746
|
-
|
|
12747
|
-
if (scope.type === "
|
|
12748
|
-
|
|
12749
|
-
|
|
13116
|
+
const candidates = [];
|
|
13117
|
+
if (scope.type === "vendor") candidates.push(scope.vendorId);
|
|
13118
|
+
if (scope.type === "all" || scope.type === "vendor") {
|
|
13119
|
+
const preferred = u.activeVendorId ?? u.vendorIds?.[0];
|
|
13120
|
+
if (preferred != null && Number.isFinite(preferred)) candidates.push(Number(preferred));
|
|
13121
|
+
for (const id of u.vendorIds ?? []) {
|
|
13122
|
+
if (Number.isFinite(id)) candidates.push(Number(id));
|
|
13123
|
+
}
|
|
13124
|
+
}
|
|
13125
|
+
if (entityMap.vendors && candidates.length > 0) {
|
|
13126
|
+
const unique = [
|
|
13127
|
+
...new Set(candidates.filter((id) => id > 0))
|
|
13128
|
+
];
|
|
13129
|
+
for (const id of unique) {
|
|
13130
|
+
try {
|
|
13131
|
+
const row = await dataSource.getRepository(entityMap.vendors).findOne({
|
|
13132
|
+
where: {
|
|
13133
|
+
id,
|
|
13134
|
+
deleted: false
|
|
13135
|
+
}
|
|
13136
|
+
});
|
|
13137
|
+
if (row) return id;
|
|
13138
|
+
} catch {
|
|
13139
|
+
}
|
|
13140
|
+
}
|
|
13141
|
+
}
|
|
13142
|
+
const uid = u.id != null ? Number(u.id) : NaN;
|
|
13143
|
+
if (Number.isFinite(uid) && entityMap.vendors) {
|
|
13144
|
+
try {
|
|
13145
|
+
const owned = await dataSource.getRepository(entityMap.vendors).findOne({
|
|
13146
|
+
where: {
|
|
13147
|
+
userId: uid,
|
|
13148
|
+
deleted: false
|
|
13149
|
+
},
|
|
13150
|
+
order: {
|
|
13151
|
+
id: "ASC"
|
|
13152
|
+
}
|
|
13153
|
+
});
|
|
13154
|
+
const vid = owned ? Number(owned.id) : NaN;
|
|
13155
|
+
if (Number.isFinite(vid) && vid > 0) return vid;
|
|
13156
|
+
} catch {
|
|
13157
|
+
}
|
|
12750
13158
|
}
|
|
12751
13159
|
return null;
|
|
12752
13160
|
}
|
|
12753
13161
|
__name(resolveActiveVendorId, "resolveActiveVendorId");
|
|
13162
|
+
async function gateVendorPortal() {
|
|
13163
|
+
const u = await getSessionUser();
|
|
13164
|
+
if (!u?.email) return json({
|
|
13165
|
+
error: "Unauthorized"
|
|
13166
|
+
}, {
|
|
13167
|
+
status: 401
|
|
13168
|
+
});
|
|
13169
|
+
if (!isVendorPortalUser(u) && !isPlatformAdministrator(u)) {
|
|
13170
|
+
return json({
|
|
13171
|
+
error: "Forbidden"
|
|
13172
|
+
}, {
|
|
13173
|
+
status: 403
|
|
13174
|
+
});
|
|
13175
|
+
}
|
|
13176
|
+
const vendorId = await resolveActiveVendorId(u);
|
|
13177
|
+
if (vendorId == null) {
|
|
13178
|
+
return json({
|
|
13179
|
+
error: "No vendor is linked to your account."
|
|
13180
|
+
}, {
|
|
13181
|
+
status: 400
|
|
13182
|
+
});
|
|
13183
|
+
}
|
|
13184
|
+
return {
|
|
13185
|
+
user: u,
|
|
13186
|
+
vendorId
|
|
13187
|
+
};
|
|
13188
|
+
}
|
|
13189
|
+
__name(gateVendorPortal, "gateVendorPortal");
|
|
12754
13190
|
async function gateVendorTeam() {
|
|
12755
13191
|
const u = await getSessionUser();
|
|
12756
13192
|
if (!u?.email) return json({
|
|
@@ -12804,6 +13240,40 @@ function createVendorOnboardHandlers(config) {
|
|
|
12804
13240
|
}
|
|
12805
13241
|
__name(trySendVendorOnboardEmails, "trySendVendorOnboardEmails");
|
|
12806
13242
|
return {
|
|
13243
|
+
/** GET /api/admin/vendor/profile — current user's store (server-resolved vendor id). */
|
|
13244
|
+
async getProfile() {
|
|
13245
|
+
const gated = await gateVendorPortal();
|
|
13246
|
+
if (gated instanceof Response) return gated;
|
|
13247
|
+
const { vendorId } = gated;
|
|
13248
|
+
if (!entityMap.vendors) {
|
|
13249
|
+
return json({
|
|
13250
|
+
error: "Vendors not configured"
|
|
13251
|
+
}, {
|
|
13252
|
+
status: 500
|
|
13253
|
+
});
|
|
13254
|
+
}
|
|
13255
|
+
try {
|
|
13256
|
+
const row = await dataSource.getRepository(entityMap.vendors).findOne({
|
|
13257
|
+
where: {
|
|
13258
|
+
id: vendorId,
|
|
13259
|
+
deleted: false
|
|
13260
|
+
}
|
|
13261
|
+
});
|
|
13262
|
+
if (!row) return json({
|
|
13263
|
+
error: "Vendor not found"
|
|
13264
|
+
}, {
|
|
13265
|
+
status: 404
|
|
13266
|
+
});
|
|
13267
|
+
return json(row);
|
|
13268
|
+
} catch (e) {
|
|
13269
|
+
console.error("[vendor.profile]", e);
|
|
13270
|
+
return json({
|
|
13271
|
+
error: "Failed to load vendor profile"
|
|
13272
|
+
}, {
|
|
13273
|
+
status: 500
|
|
13274
|
+
});
|
|
13275
|
+
}
|
|
13276
|
+
},
|
|
12807
13277
|
/** POST /api/admin/vendors/onboard — create vendor + owner user + vendor_users + invite */
|
|
12808
13278
|
async onboard(req) {
|
|
12809
13279
|
const err = await gateAdmin();
|
|
@@ -12872,6 +13342,7 @@ function createVendorOnboardHandlers(config) {
|
|
|
12872
13342
|
}
|
|
12873
13343
|
});
|
|
12874
13344
|
if (dupVendor) throw new Error("VENDOR_SLUG_EXISTS");
|
|
13345
|
+
await retireSoftDeletedUniqueValue(vendorRepo, "slug", slug);
|
|
12875
13346
|
let ownerGroup = await groupRepo.findOne({
|
|
12876
13347
|
where: {
|
|
12877
13348
|
name: VENDOR_OWNER_GROUP_NAME,
|
|
@@ -12900,8 +13371,15 @@ function createVendorOnboardHandlers(config) {
|
|
|
12900
13371
|
email: userEmail
|
|
12901
13372
|
}
|
|
12902
13373
|
});
|
|
12903
|
-
if (existingUser && !existingUser.deleted)
|
|
12904
|
-
|
|
13374
|
+
if (existingUser && !existingUser.deleted) {
|
|
13375
|
+
if (await userHasActiveVendor(em, entityMap, existingUser.id)) {
|
|
13376
|
+
throw new Error("USER_EMAIL_EXISTS");
|
|
13377
|
+
}
|
|
13378
|
+
if (!await userHadAnyVendor(em, entityMap, existingUser.id)) {
|
|
13379
|
+
throw new Error("USER_EMAIL_EXISTS");
|
|
13380
|
+
}
|
|
13381
|
+
}
|
|
13382
|
+
const newUser = existingUser ? await (async () => {
|
|
12905
13383
|
await userRepo.update(existingUser.id, {
|
|
12906
13384
|
deleted: false,
|
|
12907
13385
|
deletedAt: null,
|
|
@@ -15984,10 +16462,12 @@ var Customer = class {
|
|
|
15984
16462
|
__name(this, "Customer");
|
|
15985
16463
|
}
|
|
15986
16464
|
id;
|
|
16465
|
+
/** Set only when the customer can log in (linked `users` row). Admin/guest customers stay null. */
|
|
15987
16466
|
userId;
|
|
15988
16467
|
user;
|
|
15989
16468
|
name;
|
|
15990
16469
|
email;
|
|
16470
|
+
/** Optional; multiple customers may have null (PostgreSQL UNIQUE allows multiple NULLs). */
|
|
15991
16471
|
phone;
|
|
15992
16472
|
createdAt;
|
|
15993
16473
|
updatedAt;
|
|
@@ -16002,8 +16482,10 @@ _ts_decorate18([
|
|
|
16002
16482
|
_ts_metadata18("design:type", Number)
|
|
16003
16483
|
], Customer.prototype, "id", void 0);
|
|
16004
16484
|
_ts_decorate18([
|
|
16005
|
-
Column("int"
|
|
16006
|
-
|
|
16485
|
+
Column("int", {
|
|
16486
|
+
nullable: true
|
|
16487
|
+
}),
|
|
16488
|
+
_ts_metadata18("design:type", Object)
|
|
16007
16489
|
], Customer.prototype, "userId", void 0);
|
|
16008
16490
|
_ts_decorate18([
|
|
16009
16491
|
ManyToOne(() => User, {
|
|
@@ -16026,9 +16508,10 @@ _ts_decorate18([
|
|
|
16026
16508
|
], Customer.prototype, "email", void 0);
|
|
16027
16509
|
_ts_decorate18([
|
|
16028
16510
|
Column("varchar", {
|
|
16029
|
-
unique: true
|
|
16511
|
+
unique: true,
|
|
16512
|
+
nullable: true
|
|
16030
16513
|
}),
|
|
16031
|
-
_ts_metadata18("design:type",
|
|
16514
|
+
_ts_metadata18("design:type", Object)
|
|
16032
16515
|
], Customer.prototype, "phone", void 0);
|
|
16033
16516
|
_ts_decorate18([
|
|
16034
16517
|
Column({
|
|
@@ -26612,6 +27095,14 @@ function createCmsApiHandler(config) {
|
|
|
26612
27095
|
});
|
|
26613
27096
|
return vendorHandlers.switchVendor(req);
|
|
26614
27097
|
}
|
|
27098
|
+
if (path2[0] === "admin" && path2[1] === "vendor" && path2[2] === "profile" && path2.length === 3 && m === "GET") {
|
|
27099
|
+
if (!vendorHandlers) return config.json({
|
|
27100
|
+
error: "Not found"
|
|
27101
|
+
}, {
|
|
27102
|
+
status: 404
|
|
27103
|
+
});
|
|
27104
|
+
return vendorHandlers.getProfile();
|
|
27105
|
+
}
|
|
26615
27106
|
if (path2[0] === "admin" && path2[1] === "vendor" && path2[2] === "roles" && vendorRolesHandlers) {
|
|
26616
27107
|
if (path2.length === 3 && m === "GET") return vendorRolesHandlers.list();
|
|
26617
27108
|
if (path2.length === 3 && m === "POST") return vendorRolesHandlers.create(req);
|
|
@@ -29081,13 +29572,36 @@ function createStorefrontApiHandler(config) {
|
|
|
29081
29572
|
status: 400
|
|
29082
29573
|
};
|
|
29083
29574
|
}
|
|
29575
|
+
const list = [
|
|
29576
|
+
...vendorIds
|
|
29577
|
+
];
|
|
29084
29578
|
return {
|
|
29085
|
-
vendorId: [
|
|
29086
|
-
|
|
29087
|
-
][0]
|
|
29579
|
+
vendorId: list[0],
|
|
29580
|
+
vendorIds: list
|
|
29088
29581
|
};
|
|
29089
29582
|
}
|
|
29090
29583
|
__name(resolveSingleVendorIdFromCart, "resolveSingleVendorIdFromCart");
|
|
29584
|
+
async function linkOrderContactToVendors(contactId, vendorIds) {
|
|
29585
|
+
try {
|
|
29586
|
+
const contact = await contactRepo().findOne({
|
|
29587
|
+
where: {
|
|
29588
|
+
id: contactId,
|
|
29589
|
+
deleted: false
|
|
29590
|
+
}
|
|
29591
|
+
});
|
|
29592
|
+
if (!contact) return;
|
|
29593
|
+
const email = String(contact.email ?? "").trim().toLowerCase();
|
|
29594
|
+
if (!email) return;
|
|
29595
|
+
await ensureVendorCustomersForOrder(dataSource, entityMap, vendorIds, contactId, {
|
|
29596
|
+
name: String(contact.name ?? "").trim() || email.split("@")[0] || "Customer",
|
|
29597
|
+
email,
|
|
29598
|
+
phone: contact.phone ?? null
|
|
29599
|
+
});
|
|
29600
|
+
} catch (err) {
|
|
29601
|
+
console.error("[storefront] vendor_customers link failed", err);
|
|
29602
|
+
}
|
|
29603
|
+
}
|
|
29604
|
+
__name(linkOrderContactToVendors, "linkOrderContactToVendors");
|
|
29091
29605
|
function roundMoney3(n) {
|
|
29092
29606
|
return Math.round(n * 100) / 100;
|
|
29093
29607
|
}
|
|
@@ -29805,7 +30319,8 @@ function createStorefrontApiHandler(config) {
|
|
|
29805
30319
|
const repo = dataSource.getRepository(entityMap.product_variants);
|
|
29806
30320
|
const rows = await repo.find({
|
|
29807
30321
|
where: {
|
|
29808
|
-
productId
|
|
30322
|
+
productId,
|
|
30323
|
+
status: "available"
|
|
29809
30324
|
},
|
|
29810
30325
|
order: {
|
|
29811
30326
|
id: "ASC"
|
|
@@ -29838,6 +30353,52 @@ function createStorefrontApiHandler(config) {
|
|
|
29838
30353
|
return result;
|
|
29839
30354
|
}
|
|
29840
30355
|
__name(loadProductVariantsWithPricing, "loadProductVariantsWithPricing");
|
|
30356
|
+
async function loadApplicableVendorPolicies(productId, vendorId) {
|
|
30357
|
+
if (!entityMap.refund_policies) return [];
|
|
30358
|
+
const policyRepo = dataSource.getRepository(entityMap.refund_policies);
|
|
30359
|
+
const format = /* @__PURE__ */ __name((row) => ({
|
|
30360
|
+
id: Number(row.id),
|
|
30361
|
+
name: String(row.name ?? "").trim(),
|
|
30362
|
+
desc: row.desc != null ? String(row.desc) : null,
|
|
30363
|
+
refundWindowDays: Number(row.refundWindowDays) || 0,
|
|
30364
|
+
type: String(row.type ?? "percentage"),
|
|
30365
|
+
value: Number(row.value) || 0
|
|
30366
|
+
}), "format");
|
|
30367
|
+
const linkedIds = /* @__PURE__ */ new Set();
|
|
30368
|
+
if (entityMap.product_config) {
|
|
30369
|
+
const configs = await dataSource.getRepository(entityMap.product_config).find({
|
|
30370
|
+
where: {
|
|
30371
|
+
productId
|
|
30372
|
+
}
|
|
30373
|
+
});
|
|
30374
|
+
for (const c of configs) {
|
|
30375
|
+
const id = Number(c.refundPolicyId);
|
|
30376
|
+
if (Number.isFinite(id) && id > 0) linkedIds.add(id);
|
|
30377
|
+
}
|
|
30378
|
+
}
|
|
30379
|
+
if (linkedIds.size > 0) {
|
|
30380
|
+
const rows = await policyRepo.find({
|
|
30381
|
+
where: {
|
|
30382
|
+
id: In([
|
|
30383
|
+
...linkedIds
|
|
30384
|
+
])
|
|
30385
|
+
}
|
|
30386
|
+
});
|
|
30387
|
+
return rows.map((r) => format(r)).filter((p) => p.name).sort((a, b) => a.id - b.id);
|
|
30388
|
+
}
|
|
30389
|
+
if (vendorId == null || !Number.isFinite(Number(vendorId))) return [];
|
|
30390
|
+
const vendorRows = await policyRepo.find({
|
|
30391
|
+
where: {
|
|
30392
|
+
vendorId: Number(vendorId),
|
|
30393
|
+
status: "active"
|
|
30394
|
+
},
|
|
30395
|
+
order: {
|
|
30396
|
+
id: "ASC"
|
|
30397
|
+
}
|
|
30398
|
+
});
|
|
30399
|
+
return vendorRows.map((r) => format(r)).filter((p) => p.name);
|
|
30400
|
+
}
|
|
30401
|
+
__name(loadApplicableVendorPolicies, "loadApplicableVendorPolicies");
|
|
29841
30402
|
return {
|
|
29842
30403
|
async handle(method, path2, req) {
|
|
29843
30404
|
try {
|
|
@@ -29905,13 +30466,11 @@ function createStorefrontApiHandler(config) {
|
|
|
29905
30466
|
const url = new URL(req.url || "", "http://localhost");
|
|
29906
30467
|
const collectionSlug = url.searchParams.get("collection")?.trim();
|
|
29907
30468
|
const collectionId = url.searchParams.get("collectionId");
|
|
30469
|
+
const q = url.searchParams.get("q")?.trim() ?? "";
|
|
29908
30470
|
const limit = Math.min(100, Math.max(1, parseInt(url.searchParams.get("limit") || "20", 10)));
|
|
29909
30471
|
const offset = Math.max(0, parseInt(url.searchParams.get("offset") || "0", 10));
|
|
29910
|
-
const where = {
|
|
29911
|
-
status: "available",
|
|
29912
|
-
deleted: false
|
|
29913
|
-
};
|
|
29914
30472
|
let collectionFilter = null;
|
|
30473
|
+
let collectionIdFilter = null;
|
|
29915
30474
|
if (collectionSlug) {
|
|
29916
30475
|
let col = null;
|
|
29917
30476
|
if (/^\d+$/.test(collectionSlug)) {
|
|
@@ -29938,30 +30497,59 @@ function createStorefrontApiHandler(config) {
|
|
|
29938
30497
|
collection: null
|
|
29939
30498
|
});
|
|
29940
30499
|
}
|
|
29941
|
-
|
|
30500
|
+
collectionIdFilter = Number(col.id);
|
|
29942
30501
|
collectionFilter = {
|
|
29943
30502
|
name: col.name,
|
|
29944
30503
|
slug: col.slug
|
|
29945
30504
|
};
|
|
29946
30505
|
} else if (collectionId) {
|
|
29947
30506
|
const cid = parseInt(collectionId, 10);
|
|
29948
|
-
if (Number.isFinite(cid))
|
|
30507
|
+
if (Number.isFinite(cid)) collectionIdFilter = cid;
|
|
30508
|
+
}
|
|
30509
|
+
let items = [];
|
|
30510
|
+
let total = 0;
|
|
30511
|
+
if (q) {
|
|
30512
|
+
const like = `%${q.replace(/[%_]/g, "\\$&")}%`;
|
|
30513
|
+
const qb = productRepo().createQueryBuilder("p").where("p.status = :status", {
|
|
30514
|
+
status: "available"
|
|
30515
|
+
}).andWhere("p.deleted = :del", {
|
|
30516
|
+
del: false
|
|
30517
|
+
}).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)`, {
|
|
30518
|
+
like
|
|
30519
|
+
}).orderBy("p.id", "ASC").take(limit).skip(offset);
|
|
30520
|
+
if (collectionIdFilter != null) {
|
|
30521
|
+
qb.andWhere("p.collectionId = :cid", {
|
|
30522
|
+
cid: collectionIdFilter
|
|
30523
|
+
});
|
|
30524
|
+
}
|
|
30525
|
+
[items, total] = await qb.getManyAndCount();
|
|
30526
|
+
} else {
|
|
30527
|
+
const where = {
|
|
30528
|
+
status: "available",
|
|
30529
|
+
deleted: false
|
|
30530
|
+
};
|
|
30531
|
+
if (collectionIdFilter != null) where.collectionId = collectionIdFilter;
|
|
30532
|
+
const result = await productRepo().findAndCount({
|
|
30533
|
+
where,
|
|
30534
|
+
order: {
|
|
30535
|
+
id: "ASC"
|
|
30536
|
+
},
|
|
30537
|
+
take: limit,
|
|
30538
|
+
skip: offset
|
|
30539
|
+
});
|
|
30540
|
+
items = result[0];
|
|
30541
|
+
total = result[1];
|
|
29949
30542
|
}
|
|
29950
|
-
const [items, total] = await productRepo().findAndCount({
|
|
29951
|
-
where,
|
|
29952
|
-
order: {
|
|
29953
|
-
id: "ASC"
|
|
29954
|
-
},
|
|
29955
|
-
take: limit,
|
|
29956
|
-
skip: offset
|
|
29957
|
-
});
|
|
29958
30543
|
const products = await Promise.all(items.map((item) => enrichProductPricing(item)));
|
|
29959
30544
|
return json({
|
|
29960
30545
|
products,
|
|
29961
30546
|
total,
|
|
29962
30547
|
...collectionFilter && {
|
|
29963
30548
|
collection: collectionFilter
|
|
29964
|
-
}
|
|
30549
|
+
},
|
|
30550
|
+
...q ? {
|
|
30551
|
+
q
|
|
30552
|
+
} : {}
|
|
29965
30553
|
});
|
|
29966
30554
|
}
|
|
29967
30555
|
if (path2[0] === "products" && path2.length === 2 && method === "GET") {
|
|
@@ -29997,12 +30585,14 @@ function createStorefrontApiHandler(config) {
|
|
|
29997
30585
|
const pricing = await resolveProductEventPricing(Number(p.id));
|
|
29998
30586
|
const enriched = await enrichProductPricing(p, pricing);
|
|
29999
30587
|
const variants = await loadProductVariantsWithPricing(Number(p.id), pricing);
|
|
30588
|
+
const policies = await loadApplicableVendorPolicies(Number(p.id), p.vendorId != null ? Number(p.vendorId) : null);
|
|
30000
30589
|
return json({
|
|
30001
30590
|
...enriched,
|
|
30002
30591
|
attributes: attributeTags,
|
|
30003
30592
|
...variants.length ? {
|
|
30004
30593
|
variants
|
|
30005
|
-
} : {}
|
|
30594
|
+
} : {},
|
|
30595
|
+
policies
|
|
30006
30596
|
});
|
|
30007
30597
|
}
|
|
30008
30598
|
if (path2[0] === "collections" && path2.length === 1 && method === "GET") {
|
|
@@ -30814,24 +31404,61 @@ function createStorefrontApiHandler(config) {
|
|
|
30814
31404
|
}, {
|
|
30815
31405
|
status: 404
|
|
30816
31406
|
});
|
|
31407
|
+
const rawVariantId = body.variantId ?? body.variant_id;
|
|
31408
|
+
const variantIdNum = rawVariantId != null && String(rawVariantId).trim() !== "" ? Number(rawVariantId) : NaN;
|
|
31409
|
+
const hasVariantId = Number.isFinite(variantIdNum) && variantIdNum > 0;
|
|
31410
|
+
const bodyMeta = body.metadata && typeof body.metadata === "object" && !Array.isArray(body.metadata) ? body.metadata : {};
|
|
31411
|
+
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;
|
|
31412
|
+
const options = optionsRaw && Object.fromEntries(Object.entries(optionsRaw).map(([k, v]) => [
|
|
31413
|
+
String(k),
|
|
31414
|
+
String(v ?? "").trim()
|
|
31415
|
+
]).filter(([, v]) => v.length > 0));
|
|
31416
|
+
const lineMetadata = {
|
|
31417
|
+
...bodyMeta,
|
|
31418
|
+
...hasVariantId ? {
|
|
31419
|
+
variantId: variantIdNum
|
|
31420
|
+
} : {},
|
|
31421
|
+
...options && Object.keys(options).length ? {
|
|
31422
|
+
options
|
|
31423
|
+
} : {}
|
|
31424
|
+
};
|
|
31425
|
+
if (typeof bodyMeta.imageUrl === "string" && bodyMeta.imageUrl.trim()) {
|
|
31426
|
+
lineMetadata.imageUrl = bodyMeta.imageUrl.trim();
|
|
31427
|
+
}
|
|
31428
|
+
if (typeof bodyMeta.title === "string" && bodyMeta.title.trim()) {
|
|
31429
|
+
lineMetadata.title = bodyMeta.title.trim();
|
|
31430
|
+
}
|
|
31431
|
+
const metadataPayload = Object.keys(lineMetadata).length ? lineMetadata : null;
|
|
30817
31432
|
const { cart, setCookie, err } = await getOrCreateCart(req);
|
|
30818
31433
|
if (err) return err;
|
|
30819
31434
|
const cartId = cart.id;
|
|
30820
|
-
const
|
|
31435
|
+
const sameProductLines = await cartItemRepo().find({
|
|
30821
31436
|
where: {
|
|
30822
31437
|
cartId,
|
|
30823
31438
|
productId
|
|
30824
31439
|
}
|
|
30825
31440
|
});
|
|
31441
|
+
const existing = sameProductLines.find((row) => {
|
|
31442
|
+
const m = row.metadata;
|
|
31443
|
+
const existingVid = m?.variantId ?? m?.variant_id;
|
|
31444
|
+
if (hasVariantId) {
|
|
31445
|
+
return Number(existingVid) === variantIdNum;
|
|
31446
|
+
}
|
|
31447
|
+
return existingVid == null || existingVid === "";
|
|
31448
|
+
});
|
|
30826
31449
|
if (existing) {
|
|
30827
31450
|
await cartItemRepo().update(existing.id, {
|
|
30828
|
-
quantity: existing.quantity + quantity
|
|
31451
|
+
quantity: existing.quantity + quantity,
|
|
31452
|
+
...metadataPayload ? {
|
|
31453
|
+
metadata: metadataPayload
|
|
31454
|
+
} : {}
|
|
30829
31455
|
});
|
|
30830
31456
|
} else {
|
|
30831
31457
|
await cartItemRepo().save(cartItemRepo().create({
|
|
30832
31458
|
cartId,
|
|
30833
31459
|
productId,
|
|
30834
|
-
quantity
|
|
31460
|
+
quantity,
|
|
31461
|
+
metadata: metadataPayload
|
|
30835
31462
|
}));
|
|
30836
31463
|
}
|
|
30837
31464
|
await cartRepo().update(cartId, {
|
|
@@ -31296,6 +31923,7 @@ function createStorefrontApiHandler(config) {
|
|
|
31296
31923
|
taxCode: line.taxCode
|
|
31297
31924
|
}));
|
|
31298
31925
|
}
|
|
31926
|
+
await linkOrderContactToVendors(contactId, vendorRes.vendorIds);
|
|
31299
31927
|
fireOrderPlacedNotification(oid);
|
|
31300
31928
|
return json({
|
|
31301
31929
|
orderId: oid,
|
|
@@ -31368,6 +31996,7 @@ function createStorefrontApiHandler(config) {
|
|
|
31368
31996
|
taxCode: line.taxCode
|
|
31369
31997
|
}));
|
|
31370
31998
|
}
|
|
31999
|
+
await linkOrderContactToVendors(contactId, vendorResChk.vendorIds);
|
|
31371
32000
|
await cartItemRepo().delete({
|
|
31372
32001
|
cartId: cart.id
|
|
31373
32002
|
});
|
|
@@ -31614,4 +32243,4 @@ function createStorefrontApiHandler(config) {
|
|
|
31614
32243
|
}
|
|
31615
32244
|
__name(createStorefrontApiHandler, "createStorefrontApiHandler");
|
|
31616
32245
|
|
|
31617
|
-
export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyApprovalStatusSideEffects, applyEventApprovalStatusSideEffects, applyRotatingVendorInvite, applyVendorCustomersContactFilter, applyVendorEventCreateApproval, applyVendorProductCreateApproval, assertCaptchaOk, assertContactAllowedForVendorOrder, assertEventApprovalUpdate, assertProductApprovalUpdate, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkEventsEnabled, checkMultiVendorEnabled, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, findActiveRefundPolicyForVendor, findVendorByInviteToken, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRequireEventApproval, getRequireProductApproval, getRssArticleSummaryFromItem, getVendorCatalogCreateFlags, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, invalidateRequireEventApprovalCache, invalidateRequireProductApprovalCache, invalidateVendorCatalogCreateFlagsCache, isCustomerTypeContact, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging };
|
|
32246
|
+
export { Address, Attendee, Attribute, BLOG_GENERATOR_AGENT_NAME, BLOG_GENERATOR_DEFAULT_SYSTEM_INSTRUCTION, BLOG_GENERATOR_DEFAULT_VALIDATION_RULES, BLOG_GENERATOR_LLM_AGENT_SLUG, BLOG_GENERATOR_MARKDOWN_ARTICLE_SEPARATOR, BLOG_METADATA_ENRICHER_AGENT_NAME, BLOG_METADATA_ENRICHER_DEFAULT_SYSTEM_INSTRUCTION, BLOG_METADATA_ENRICHER_DEFAULT_VALIDATION_RULES, BLOG_METADATA_ENRICHER_LLM_AGENT_SLUG, Blog, BlogGeneratorService, Brand, CMS_ENTITY_MAP, Cart, CartItem, Category, ChatConversation, ChatMessage, Collection, Combo, ComboItem, Comment, Config, Contact, Currency, CurrencyExchange, Customer, Customer_Contacts, Discount, DiscountRules, Event, EventProduct, Form, FormField, FormSubmission, JobSchedule, JobScheduleRun, KnowledgeBaseChunk, KnowledgeBaseDocument, LlmAgent, Media, MessageTemplate, Order, OrderAddresses, OrderDiscounts, OrderItem, OrderNotificationBinding, OrderNotificationTrigger, OtpChallenge, Page, PasswordResetToken, Payment, Permission, Product, ProductAttribute, ProductCategory, ProductConfig, ProductVariant, RefundPolicy, RefundRequest, RssArticle, RssFeed, Seo, Tag, Tax, User, UserGroup, Vendor, VendorCustomer, VendorRole, VendorRolePermission, VendorUser, Wishlist, WishlistItem, ZIP_MIME_TYPES, applyApprovalStatusSideEffects, applyEventApprovalStatusSideEffects, applyRotatingVendorInvite, applyVendorCustomersContactFilter, applyVendorEventCreateApproval, applyVendorProductCreateApproval, assertCaptchaOk, assertContactAllowedForVendorOrder, assertEventApprovalUpdate, assertProductApprovalUpdate, buildBlogMetadataUserPrompt, buildCronFromSchedule, buildRssUserPromptFromFeeds, buildVendorInviteLink, calculateOrderRefundPreview, calculateRefundFromPolicy, checkEventsEnabled, checkMultiVendorEnabled, contactIsVendorCustomer, countRecentOtpSends, createAnalyticsHandlers, createBlogBySlugHandler, createChangePasswordHandler, createCmsApiHandler, createCmsApp, createCmsAppWithMessaging, createCrudByIdHandler, createCrudHandler, createDashboardStatsHandler, createEcommerceAnalyticsHandler, createEventOrderMessageTemplateHandlers, createForgotPasswordHandler, createFormBySlugHandler, createInviteAcceptHandler, createJobScheduleHandlers, createLlmAgentKnowledgeHandlers, createMediaZipExtractHandler, createMessageTemplateRowLoader, createOtpChallenge, createSetPasswordHandler, createSettingsApiHandlers, createSocialMediaHandlers, createStorefrontApiHandler, createUploadHandler, createUserAuthApiRouter, createUserAvatarHandler, createUserProfileHandler, createUsersApiHandlers, createVendorDashboardHandler, createVendorOnboardHandlers, customerPhoneForEmail, customerPhoneForUser, daysBeforeEventStart, describeEventTierPolicy, ensureCustomerForUser, ensureCustomerRecord, ensureMessagingPluginsOnCms, ensureScheduleQueueWorker, ensureVendorCustomerForOrderContact, ensureVendorCustomersForOrder, findActiveRefundPolicyForVendor, findVendorByInviteToken, formatTierRange, generateNumericOtp, getPublicSettingsGroup, getRequireEventApproval, getRequireProductApproval, getRssArticleSummaryFromItem, getVendorCatalogCreateFlags, hashOtpCode, hydrateVendorSessionUser, invalidateEventsCache, invalidateMultiVendorCache, invalidateRequireEventApprovalCache, invalidateRequireProductApprovalCache, invalidateVendorCatalogCreateFlagsCache, isCustomerTypeContact, isSyntheticCustomerPhone, isZipMedia, linkUnclaimedContactToUser, llmAgentToChatAgentOptions, loadSettingsGroupFromDb, loadUserVendorContext, mergeGuardrailsIntoSystemPrompt, messagingPlugins, metaFetchUserManagedPages, metaPostPageFeed, metaPostPagePhoto, metaResolvePageAccessToken, normalizeCustomerPhone, normalizePhoneE164, normalizeRefundTiers, overlayCmsPlugins, parseBlogGeneratorAgentContent, parseBlogGeneratorModelOutput, parseBlogMetadataEnrichmentJson, parseLlmAgentValidationRules, pgBossScheduleNameForId, queueErpCreateContactIfEnabled, queueJobScheduleNow, queuePlugin, queueSms, registerJobRunnerWorker, registerMessagingQueueProcessors, registerSmsQueueProcessor, relativePathFromMediaParentId, resolveBlogCategoryIdByName, resolveSettingsEncryptionKey, resolveVendorIdForContactCheck, sanitizeMediaFolderPath, sanitizeStorageSegment, sendVendorOnboardEmails, simpleDecrypt, simpleEncrypt, syncJobScheduleToPgBoss, validateRefundTiers, validateScheduleInput, validateUserMessageAgainstAgentRules, validateUserMessageAgainstStructuredRules, verifyAndConsumeOtpChallenge, verifyOtpCodeHash, whatsappPlugin, wrapGetCmsWithMessaging };
|