@infuro/cms-core 1.0.44 → 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 +541 -407
- package/dist/admin.d.cts +1 -1
- package/dist/admin.d.ts +1 -1
- package/dist/admin.js +541 -407
- 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-P2IWWBJV.cjs → chunk-F5XFHYTG.cjs} +703 -269
- package/dist/{chunk-SQGBHYJF.js → chunk-MVXLRANV.js} +700 -271
- 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];
|
|
@@ -1090,11 +1120,118 @@ function pgErrorCode(err) {
|
|
|
1090
1120
|
return driver?.code ?? err.code;
|
|
1091
1121
|
}
|
|
1092
1122
|
__name(pgErrorCode, "pgErrorCode");
|
|
1093
|
-
function
|
|
1123
|
+
function normalizeCustomerPhone(phone) {
|
|
1094
1124
|
const p = typeof phone === "string" ? phone.trim() : "";
|
|
1095
|
-
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);
|
|
1096
1138
|
}
|
|
1097
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");
|
|
1098
1235
|
async function restoreCustomerRow(repo, row, user, name, email, phone) {
|
|
1099
1236
|
const id = row.id;
|
|
1100
1237
|
await repo.update(id, {
|
|
@@ -1116,7 +1253,7 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1116
1253
|
const repo = dsOrEm.getRepository(customerEntity);
|
|
1117
1254
|
const email = normalizeEmail(user.email);
|
|
1118
1255
|
const name = String(user.name ?? "").trim() || email.split("@")[0] || "User";
|
|
1119
|
-
const phone =
|
|
1256
|
+
const phone = normalizeCustomerPhone(overrides?.phone ?? user.phone);
|
|
1120
1257
|
let row = await repo.findOne({
|
|
1121
1258
|
where: {
|
|
1122
1259
|
userId: user.id,
|
|
@@ -1124,10 +1261,11 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1124
1261
|
}
|
|
1125
1262
|
});
|
|
1126
1263
|
if (row) {
|
|
1264
|
+
const nextPhone = resolveNextPhone(phone, row.phone);
|
|
1127
1265
|
await repo.update(row.id, {
|
|
1128
1266
|
name,
|
|
1129
1267
|
email,
|
|
1130
|
-
phone,
|
|
1268
|
+
phone: nextPhone,
|
|
1131
1269
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1132
1270
|
});
|
|
1133
1271
|
return {
|
|
@@ -1145,10 +1283,11 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1145
1283
|
if (existingUserId != null && existingUserId !== user.id) {
|
|
1146
1284
|
return null;
|
|
1147
1285
|
}
|
|
1286
|
+
const nextPhone = resolveNextPhone(phone, row.phone);
|
|
1148
1287
|
await repo.update(row.id, {
|
|
1149
1288
|
userId: user.id,
|
|
1150
1289
|
name,
|
|
1151
|
-
phone,
|
|
1290
|
+
phone: nextPhone,
|
|
1152
1291
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1153
1292
|
});
|
|
1154
1293
|
return {
|
|
@@ -1163,21 +1302,23 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1163
1302
|
if (deletedByEmail) {
|
|
1164
1303
|
const existingUserId = deletedByEmail.userId;
|
|
1165
1304
|
if (existingUserId != null && existingUserId !== user.id) return null;
|
|
1166
|
-
return restoreCustomerRow(repo, deletedByEmail, user, name, email, phone);
|
|
1305
|
+
return restoreCustomerRow(repo, deletedByEmail, user, name, email, resolveNextPhone(phone, deletedByEmail.phone));
|
|
1167
1306
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
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);
|
|
1179
1321
|
}
|
|
1180
|
-
return restoreCustomerRow(repo, deletedByPhone, user, name, email, phone);
|
|
1181
1322
|
}
|
|
1182
1323
|
try {
|
|
1183
1324
|
const created = await repo.save(repo.create({
|
|
@@ -1198,34 +1339,22 @@ async function ensureCustomerForUser(dsOrEm, customerEntity, user, overrides) {
|
|
|
1198
1339
|
userId: user.id
|
|
1199
1340
|
}
|
|
1200
1341
|
});
|
|
1201
|
-
if (row)
|
|
1342
|
+
if (row) {
|
|
1343
|
+
return restoreCustomerRow(repo, row, user, name, email, resolveNextPhone(phone, row.phone));
|
|
1344
|
+
}
|
|
1202
1345
|
row = await repo.findOne({
|
|
1203
1346
|
where: {
|
|
1204
1347
|
email
|
|
1205
1348
|
}
|
|
1206
1349
|
});
|
|
1207
1350
|
if (row && (row.userId ?? user.id) === user.id) {
|
|
1208
|
-
return restoreCustomerRow(repo, row, user, name, email, phone);
|
|
1351
|
+
return restoreCustomerRow(repo, row, user, name, email, resolveNextPhone(phone, row.phone));
|
|
1209
1352
|
}
|
|
1210
1353
|
if (code === "23505") return null;
|
|
1211
1354
|
throw err;
|
|
1212
1355
|
}
|
|
1213
1356
|
}
|
|
1214
1357
|
__name(ensureCustomerForUser, "ensureCustomerForUser");
|
|
1215
|
-
async function linkUnclaimedContactToUser(dataSource, contactsEntity, userId, email) {
|
|
1216
|
-
const repo = dataSource.getRepository(contactsEntity);
|
|
1217
|
-
const found = await repo.findOne({
|
|
1218
|
-
where: {
|
|
1219
|
-
email,
|
|
1220
|
-
userId: IsNull(),
|
|
1221
|
-
deleted: false
|
|
1222
|
-
}
|
|
1223
|
-
});
|
|
1224
|
-
if (found) await repo.update(found.id, {
|
|
1225
|
-
userId
|
|
1226
|
-
});
|
|
1227
|
-
}
|
|
1228
|
-
__name(linkUnclaimedContactToUser, "linkUnclaimedContactToUser");
|
|
1229
1358
|
|
|
1230
1359
|
// src/lib/vendor-customer-contacts.ts
|
|
1231
1360
|
function isCustomerTypeContact(type) {
|
|
@@ -1318,8 +1447,27 @@ function resolveVendorIdForContactCheck(scope, bodyOrRow) {
|
|
|
1318
1447
|
return Number.isFinite(n) ? n : null;
|
|
1319
1448
|
}
|
|
1320
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");
|
|
1321
1462
|
async function ensureVendorCustomerForOrderContact(dataSource, entityMap, vendorId, contactId, details) {
|
|
1322
|
-
|
|
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;
|
|
1323
1471
|
const vcEntity = entityMap.vendor_customers;
|
|
1324
1472
|
const contactEntity = entityMap.contacts;
|
|
1325
1473
|
if (!vcEntity || !contactEntity) return;
|
|
@@ -1345,96 +1493,61 @@ async function ensureVendorCustomerForOrderContact(dataSource, entityMap, vendor
|
|
|
1345
1493
|
}
|
|
1346
1494
|
let customerId = null;
|
|
1347
1495
|
if (entityMap.customer) {
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
}
|
|
1496
|
+
const ensured = await ensureCustomerRecord(dataSource, entityMap.customer, {
|
|
1497
|
+
name,
|
|
1498
|
+
email,
|
|
1499
|
+
phone,
|
|
1500
|
+
userId: null
|
|
1354
1501
|
});
|
|
1355
|
-
if (
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1502
|
+
if (ensured) customerId = ensured.id;
|
|
1503
|
+
}
|
|
1504
|
+
for (const vendorId of vendors) {
|
|
1505
|
+
if (customerId != null) {
|
|
1506
|
+
const byCustomer = await vcRepo.findOne({
|
|
1360
1507
|
where: {
|
|
1361
|
-
|
|
1362
|
-
|
|
1508
|
+
vendorId,
|
|
1509
|
+
customerId
|
|
1363
1510
|
}
|
|
1364
1511
|
});
|
|
1365
|
-
if (
|
|
1366
|
-
|
|
1367
|
-
if (
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
where: {
|
|
1371
|
-
name: "Customer",
|
|
1372
|
-
deleted: false
|
|
1373
|
-
}
|
|
1512
|
+
if (byCustomer) {
|
|
1513
|
+
const existingContactId = byCustomer.contactId;
|
|
1514
|
+
if (existingContactId == null) {
|
|
1515
|
+
await vcRepo.update(byCustomer.id, {
|
|
1516
|
+
contactId
|
|
1374
1517
|
});
|
|
1375
|
-
if (customerGroup) groupId = Number(customerGroup.id);
|
|
1376
1518
|
}
|
|
1377
|
-
|
|
1378
|
-
name,
|
|
1379
|
-
email,
|
|
1380
|
-
phone,
|
|
1381
|
-
password: null,
|
|
1382
|
-
blocked: false,
|
|
1383
|
-
groupId,
|
|
1384
|
-
adminAccess: false
|
|
1385
|
-
}));
|
|
1519
|
+
continue;
|
|
1386
1520
|
}
|
|
1387
|
-
const userId = Number(user.id);
|
|
1388
|
-
await linkUnclaimedContactToUser(dataSource, contactEntity, userId, email);
|
|
1389
|
-
const ensured = await ensureCustomerForUser(dataSource, entityMap.customer, {
|
|
1390
|
-
id: userId,
|
|
1391
|
-
name,
|
|
1392
|
-
email,
|
|
1393
|
-
phone
|
|
1394
|
-
}, {
|
|
1395
|
-
phone
|
|
1396
|
-
});
|
|
1397
|
-
if (ensured) customerId = ensured.id;
|
|
1398
1521
|
}
|
|
1399
|
-
|
|
1400
|
-
if (customerId != null) {
|
|
1401
|
-
const byCustomer = await vcRepo.findOne({
|
|
1522
|
+
const byContact = await vcRepo.findOne({
|
|
1402
1523
|
where: {
|
|
1403
1524
|
vendorId,
|
|
1404
|
-
|
|
1525
|
+
contactId
|
|
1405
1526
|
}
|
|
1406
1527
|
});
|
|
1407
|
-
if (
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
contactId
|
|
1528
|
+
if (byContact) {
|
|
1529
|
+
if (customerId != null && byContact.customerId == null) {
|
|
1530
|
+
await vcRepo.update(byContact.id, {
|
|
1531
|
+
customerId
|
|
1412
1532
|
});
|
|
1413
1533
|
}
|
|
1414
|
-
|
|
1534
|
+
continue;
|
|
1415
1535
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
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
|
+
}));
|
|
1421
1547
|
}
|
|
1422
|
-
});
|
|
1423
|
-
if (byContact) return;
|
|
1424
|
-
if (customerId != null) {
|
|
1425
|
-
await vcRepo.save(vcRepo.create({
|
|
1426
|
-
vendorId,
|
|
1427
|
-
customerId,
|
|
1428
|
-
contactId
|
|
1429
|
-
}));
|
|
1430
|
-
return;
|
|
1431
1548
|
}
|
|
1432
|
-
await vcRepo.save(vcRepo.create({
|
|
1433
|
-
vendorId,
|
|
1434
|
-
contactId
|
|
1435
|
-
}));
|
|
1436
1549
|
}
|
|
1437
|
-
__name(
|
|
1550
|
+
__name(ensureVendorCustomersForOrder, "ensureVendorCustomersForOrder");
|
|
1438
1551
|
|
|
1439
1552
|
// src/lib/currency-prices.ts
|
|
1440
1553
|
function normalizeCurrencyCode(code) {
|
|
@@ -3800,23 +3913,24 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
3800
3913
|
if (resource === "vendor_customers" && entityMap["customer"]) {
|
|
3801
3914
|
const scope = await resolveVendorCustomersScope(dataSource, entityMap, resolveScope);
|
|
3802
3915
|
const repo2 = dataSource.getRepository(entity);
|
|
3803
|
-
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);
|
|
3804
3917
|
applyVendorScopeToQueryBuilder(qb, "vc", scope);
|
|
3805
3918
|
if (search && typeof search === "string" && search.trim()) {
|
|
3806
3919
|
const term = `%${search.trim()}%`;
|
|
3807
|
-
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)", {
|
|
3808
3921
|
term
|
|
3809
3922
|
});
|
|
3810
3923
|
}
|
|
3811
3924
|
const [rows, total2] = await qb.getManyAndCount();
|
|
3812
3925
|
const data2 = rows.map((row) => {
|
|
3813
3926
|
const customer = row.customer;
|
|
3927
|
+
const contact = row.contact;
|
|
3814
3928
|
return {
|
|
3815
3929
|
...row,
|
|
3816
|
-
name: customer?.name ?? null,
|
|
3817
|
-
email: customer?.email ?? null,
|
|
3818
|
-
phone: customer?.phone ?? null,
|
|
3819
|
-
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
|
|
3820
3934
|
};
|
|
3821
3935
|
});
|
|
3822
3936
|
return json({
|
|
@@ -4150,7 +4264,11 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4150
4264
|
} else if (resource === "collections") {
|
|
4151
4265
|
if (scope.type === "vendor") {
|
|
4152
4266
|
where = mergeVendorScopeIntoWhere(where, scope, resource, catalogFlags);
|
|
4153
|
-
} 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") {
|
|
4154
4272
|
where = mergeListWhereAnd(where, {
|
|
4155
4273
|
isCatalog: false
|
|
4156
4274
|
});
|
|
@@ -4347,41 +4465,17 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4347
4465
|
status: 503
|
|
4348
4466
|
});
|
|
4349
4467
|
}
|
|
4350
|
-
if (!entityMap["
|
|
4351
|
-
return json({
|
|
4352
|
-
error: "Users entity not configured"
|
|
4353
|
-
}, {
|
|
4354
|
-
status: 503
|
|
4355
|
-
});
|
|
4356
|
-
}
|
|
4357
|
-
let customerGroupId = null;
|
|
4358
|
-
if (entityMap["user_groups"]) {
|
|
4359
|
-
const userGroupRepo = dataSource.getRepository(entityMap["user_groups"]);
|
|
4360
|
-
const customerGroup = await userGroupRepo.findOne({
|
|
4361
|
-
where: {
|
|
4362
|
-
name: "Customer",
|
|
4363
|
-
deleted: false
|
|
4364
|
-
}
|
|
4365
|
-
});
|
|
4366
|
-
if (!customerGroup) {
|
|
4367
|
-
return json({
|
|
4368
|
-
error: "User group 'customer' not found"
|
|
4369
|
-
}, {
|
|
4370
|
-
status: 500
|
|
4371
|
-
});
|
|
4372
|
-
}
|
|
4373
|
-
customerGroupId = Number(customerGroup.id);
|
|
4374
|
-
} else {
|
|
4468
|
+
if (!entityMap["contacts"]) {
|
|
4375
4469
|
return json({
|
|
4376
|
-
error: "
|
|
4470
|
+
error: "Contacts entity not configured"
|
|
4377
4471
|
}, {
|
|
4378
4472
|
status: 503
|
|
4379
4473
|
});
|
|
4380
4474
|
}
|
|
4381
4475
|
const name = String(body.name ?? "").trim();
|
|
4382
|
-
const email = String(body.email ?? "").trim();
|
|
4383
|
-
const
|
|
4384
|
-
const
|
|
4476
|
+
const email = String(body.email ?? "").trim().toLowerCase();
|
|
4477
|
+
const phoneRaw = String(body.phone ?? "").trim();
|
|
4478
|
+
const phone = phoneRaw || null;
|
|
4385
4479
|
if (!name) return json({
|
|
4386
4480
|
error: "name is required"
|
|
4387
4481
|
}, {
|
|
@@ -4392,25 +4486,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4392
4486
|
}, {
|
|
4393
4487
|
status: 400
|
|
4394
4488
|
});
|
|
4395
|
-
if (!phone) return json({
|
|
4396
|
-
error: "phone is required"
|
|
4397
|
-
}, {
|
|
4398
|
-
status: 400
|
|
4399
|
-
});
|
|
4400
|
-
if (!rawPw) return json({
|
|
4401
|
-
error: "password is required"
|
|
4402
|
-
}, {
|
|
4403
|
-
status: 400
|
|
4404
|
-
});
|
|
4405
|
-
if (rawPw.length < 6) {
|
|
4406
|
-
return json({
|
|
4407
|
-
error: "Password must be at least 6 characters"
|
|
4408
|
-
}, {
|
|
4409
|
-
status: 400
|
|
4410
|
-
});
|
|
4411
|
-
}
|
|
4412
4489
|
const customerRepo = dataSource.getRepository(entityMap["customer"]);
|
|
4413
|
-
const userRepo = dataSource.getRepository(entityMap["users"]);
|
|
4414
4490
|
const dupCustEmail = await customerRepo.findOne({
|
|
4415
4491
|
where: {
|
|
4416
4492
|
email,
|
|
@@ -4424,49 +4500,26 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4424
4500
|
status: 409
|
|
4425
4501
|
});
|
|
4426
4502
|
}
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
if (dupCustPhone) {
|
|
4434
|
-
return json({
|
|
4435
|
-
error: "A customer with this phone number already exists"
|
|
4436
|
-
}, {
|
|
4437
|
-
status: 409
|
|
4503
|
+
if (phone) {
|
|
4504
|
+
const dupCustPhone = await customerRepo.findOne({
|
|
4505
|
+
where: {
|
|
4506
|
+
phone,
|
|
4507
|
+
deleted: false
|
|
4508
|
+
}
|
|
4438
4509
|
});
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4510
|
+
if (dupCustPhone) {
|
|
4511
|
+
return json({
|
|
4512
|
+
error: "A customer with this phone number already exists"
|
|
4513
|
+
}, {
|
|
4514
|
+
status: 409
|
|
4515
|
+
});
|
|
4445
4516
|
}
|
|
4446
|
-
});
|
|
4447
|
-
if (dupUser) {
|
|
4448
|
-
userId = Number(dupUser.id);
|
|
4449
|
-
} else {
|
|
4450
|
-
const bcrypt = await import('bcryptjs');
|
|
4451
|
-
const hashedPassword = await bcrypt.hash(rawPw, 10);
|
|
4452
|
-
const savedUser = await userRepo.save(userRepo.create({
|
|
4453
|
-
name,
|
|
4454
|
-
email,
|
|
4455
|
-
phone,
|
|
4456
|
-
password: hashedPassword,
|
|
4457
|
-
groupId: customerGroupId,
|
|
4458
|
-
adminAccess: false,
|
|
4459
|
-
blocked: false
|
|
4460
|
-
}));
|
|
4461
|
-
userId = Number(savedUser.id);
|
|
4462
4517
|
}
|
|
4463
|
-
const savedCustomer = await
|
|
4464
|
-
id: userId,
|
|
4518
|
+
const savedCustomer = await ensureCustomerRecord(dataSource, entityMap["customer"], {
|
|
4465
4519
|
name,
|
|
4466
4520
|
email,
|
|
4467
|
-
phone
|
|
4468
|
-
|
|
4469
|
-
phone
|
|
4521
|
+
phone,
|
|
4522
|
+
userId: null
|
|
4470
4523
|
});
|
|
4471
4524
|
if (!savedCustomer) {
|
|
4472
4525
|
return json({
|
|
@@ -4475,37 +4528,37 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4475
4528
|
status: 409
|
|
4476
4529
|
});
|
|
4477
4530
|
}
|
|
4478
|
-
const
|
|
4479
|
-
|
|
4531
|
+
const contactRepo = dataSource.getRepository(entityMap["contacts"]);
|
|
4532
|
+
let contact = await contactRepo.findOne({
|
|
4480
4533
|
where: {
|
|
4481
|
-
|
|
4534
|
+
email,
|
|
4482
4535
|
deleted: false
|
|
4483
4536
|
}
|
|
4484
|
-
})
|
|
4485
|
-
if (
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
}
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
}
|
|
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
|
+
};
|
|
4508
4560
|
}
|
|
4561
|
+
const contactId = Number(contact.id);
|
|
4509
4562
|
const scope = await resolveVendorCustomersScope(dataSource, entityMap, resolveScope);
|
|
4510
4563
|
let vendorId = null;
|
|
4511
4564
|
if (scope.type === "vendor") {
|
|
@@ -4521,19 +4574,25 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4521
4574
|
status: 400
|
|
4522
4575
|
});
|
|
4523
4576
|
}
|
|
4524
|
-
|
|
4525
|
-
|
|
4577
|
+
await ensureVendorCustomersForOrder(dataSource, entityMap, [
|
|
4578
|
+
vendorId
|
|
4579
|
+
], contactId, {
|
|
4580
|
+
name,
|
|
4581
|
+
email,
|
|
4582
|
+
phone
|
|
4583
|
+
});
|
|
4584
|
+
const customerRow = await customerRepo.findOne({
|
|
4526
4585
|
where: {
|
|
4527
|
-
|
|
4586
|
+
id: savedCustomer.id,
|
|
4587
|
+
deleted: false
|
|
4528
4588
|
}
|
|
4529
4589
|
});
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
}
|
|
4536
|
-
return json(customerRow, {
|
|
4590
|
+
return json(customerRow ?? {
|
|
4591
|
+
id: savedCustomer.id,
|
|
4592
|
+
name,
|
|
4593
|
+
email,
|
|
4594
|
+
phone
|
|
4595
|
+
}, {
|
|
4537
4596
|
status: 201
|
|
4538
4597
|
});
|
|
4539
4598
|
}
|
|
@@ -4981,6 +5040,7 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4981
5040
|
}
|
|
4982
5041
|
const nameRaw = String(body["contact.name"] ?? contact.name ?? "").trim();
|
|
4983
5042
|
const phoneRaw = body["contact.phone"] ?? contact.phone;
|
|
5043
|
+
const emailForVc = (emailRaw || String(contact.email ?? "")).trim().toLowerCase();
|
|
4984
5044
|
const phoneToSave = phoneRaw == null || phoneRaw === "" ? null : String(phoneRaw).trim();
|
|
4985
5045
|
if (phoneToSave && contact.phone !== phoneToSave) {
|
|
4986
5046
|
await contactRepo.update(contact.id, {
|
|
@@ -4989,21 +5049,45 @@ function createCrudHandler(dataSource, entityMap, options) {
|
|
|
4989
5049
|
contact.phone = phoneToSave;
|
|
4990
5050
|
}
|
|
4991
5051
|
const vendorIdForCustomer = resolveVendorIdForContactCheck(scopeCreate, persistBody);
|
|
4992
|
-
const
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
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,
|
|
4997
5073
|
phone: phoneRaw == null || phoneRaw === "" ? null : String(phoneRaw)
|
|
4998
5074
|
});
|
|
4999
|
-
}
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
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
|
+
}));
|
|
5007
5091
|
}
|
|
5008
5092
|
}
|
|
5009
5093
|
const randomPart = Math.floor(1e4 + Math.random() * 9e4);
|
|
@@ -6783,6 +6867,14 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6783
6867
|
}
|
|
6784
6868
|
}
|
|
6785
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
|
+
}
|
|
6786
6878
|
return json({
|
|
6787
6879
|
message: "Deleted successfully"
|
|
6788
6880
|
}, {
|
|
@@ -6813,6 +6905,20 @@ function createCrudByIdHandler(dataSource, entityMap, options) {
|
|
|
6813
6905
|
};
|
|
6814
6906
|
}
|
|
6815
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");
|
|
6816
6922
|
var VENDOR_INVITE_METADATA_TOKEN = "inviteToken";
|
|
6817
6923
|
var VENDOR_INVITE_METADATA_EXPIRES = "inviteExpiresAt";
|
|
6818
6924
|
var VENDOR_INVITE_METADATA_SENT = "inviteSentAt";
|
|
@@ -7545,6 +7651,50 @@ async function findLlmAgentByScope(dataSource, entityMap, scope, options = {}) {
|
|
|
7545
7651
|
}
|
|
7546
7652
|
__name(findLlmAgentByScope, "findLlmAgentByScope");
|
|
7547
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
|
+
|
|
7548
7698
|
// src/lib/media-folder-path.ts
|
|
7549
7699
|
function sanitizeMediaFolderPath(input) {
|
|
7550
7700
|
if (input == null) return "";
|
|
@@ -8938,6 +9088,17 @@ function createFormSaveHandlers(config) {
|
|
|
8938
9088
|
});
|
|
8939
9089
|
const fields = Array.isArray(body.fields) ? body.fields : [];
|
|
8940
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
|
+
}
|
|
8941
9102
|
const form = await formRepo().save(formRepo().create(formRow));
|
|
8942
9103
|
for (let i = 0; i < fields.length; i++) {
|
|
8943
9104
|
const row = normalizeFieldRow(fields[i], form.id);
|
|
@@ -8961,6 +9122,14 @@ function createFormSaveHandlers(config) {
|
|
|
8961
9122
|
status: 201
|
|
8962
9123
|
});
|
|
8963
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
|
+
}
|
|
8964
9133
|
return json({
|
|
8965
9134
|
error: "Server Error"
|
|
8966
9135
|
}, {
|
|
@@ -9009,6 +9178,20 @@ function createFormSaveHandlers(config) {
|
|
|
9009
9178
|
]) {
|
|
9010
9179
|
if (body[key] !== void 0) formRow[key] = body[key];
|
|
9011
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
|
+
}
|
|
9012
9195
|
if (Object.keys(formRow).length > 0) await formRepo().update(formId, formRow);
|
|
9013
9196
|
await fieldRepo().delete({
|
|
9014
9197
|
formId
|
|
@@ -9037,6 +9220,14 @@ function createFormSaveHandlers(config) {
|
|
|
9037
9220
|
status: 404
|
|
9038
9221
|
});
|
|
9039
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
|
+
}
|
|
9040
9231
|
return json({
|
|
9041
9232
|
error: "Server Error"
|
|
9042
9233
|
}, {
|
|
@@ -9618,6 +9809,7 @@ function createUsersApiHandlers(config) {
|
|
|
9618
9809
|
"id",
|
|
9619
9810
|
"name",
|
|
9620
9811
|
"email",
|
|
9812
|
+
"phone",
|
|
9621
9813
|
"blocked",
|
|
9622
9814
|
"createdAt",
|
|
9623
9815
|
"updatedAt",
|
|
@@ -9647,6 +9839,11 @@ function createUsersApiHandlers(config) {
|
|
|
9647
9839
|
}
|
|
9648
9840
|
try {
|
|
9649
9841
|
const uid = parseInt(id, 10);
|
|
9842
|
+
if (!Number.isFinite(uid)) return json({
|
|
9843
|
+
error: "Invalid id"
|
|
9844
|
+
}, {
|
|
9845
|
+
status: 400
|
|
9846
|
+
});
|
|
9650
9847
|
const existing = await userRepo().findOne({
|
|
9651
9848
|
where: {
|
|
9652
9849
|
id: uid,
|
|
@@ -9659,8 +9856,59 @@ function createUsersApiHandlers(config) {
|
|
|
9659
9856
|
status: 404
|
|
9660
9857
|
});
|
|
9661
9858
|
const body = await req.json();
|
|
9662
|
-
const
|
|
9663
|
-
|
|
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);
|
|
9664
9912
|
const updated = await userRepo().findOne({
|
|
9665
9913
|
where: {
|
|
9666
9914
|
id: uid,
|
|
@@ -9673,6 +9921,7 @@ function createUsersApiHandlers(config) {
|
|
|
9673
9921
|
"id",
|
|
9674
9922
|
"name",
|
|
9675
9923
|
"email",
|
|
9924
|
+
"phone",
|
|
9676
9925
|
"blocked",
|
|
9677
9926
|
"createdAt",
|
|
9678
9927
|
"updatedAt",
|
|
@@ -9684,7 +9933,8 @@ function createUsersApiHandlers(config) {
|
|
|
9684
9933
|
}, {
|
|
9685
9934
|
status: 404
|
|
9686
9935
|
});
|
|
9687
|
-
} catch {
|
|
9936
|
+
} catch (err) {
|
|
9937
|
+
console.error("[users.update]", err);
|
|
9688
9938
|
return json({
|
|
9689
9939
|
error: "Server Error"
|
|
9690
9940
|
}, {
|
|
@@ -12732,6 +12982,39 @@ function slugify(input) {
|
|
|
12732
12982
|
return input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
12733
12983
|
}
|
|
12734
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");
|
|
12735
13018
|
function vendorOnboardErrorResponse(json, err) {
|
|
12736
13019
|
const msg = err instanceof Error ? err.message : String(err);
|
|
12737
13020
|
console.error("[vendor-onboard]", err);
|
|
@@ -12830,14 +13113,80 @@ function createVendorOnboardHandlers(config) {
|
|
|
12830
13113
|
__name(gateAdmin, "gateAdmin");
|
|
12831
13114
|
async function resolveActiveVendorId(u) {
|
|
12832
13115
|
const scope = resolveVendorScopeFromSessionUser(u);
|
|
12833
|
-
|
|
12834
|
-
if (scope.type === "
|
|
12835
|
-
|
|
12836
|
-
|
|
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
|
+
}
|
|
12837
13158
|
}
|
|
12838
13159
|
return null;
|
|
12839
13160
|
}
|
|
12840
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");
|
|
12841
13190
|
async function gateVendorTeam() {
|
|
12842
13191
|
const u = await getSessionUser();
|
|
12843
13192
|
if (!u?.email) return json({
|
|
@@ -12891,6 +13240,40 @@ function createVendorOnboardHandlers(config) {
|
|
|
12891
13240
|
}
|
|
12892
13241
|
__name(trySendVendorOnboardEmails, "trySendVendorOnboardEmails");
|
|
12893
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
|
+
},
|
|
12894
13277
|
/** POST /api/admin/vendors/onboard — create vendor + owner user + vendor_users + invite */
|
|
12895
13278
|
async onboard(req) {
|
|
12896
13279
|
const err = await gateAdmin();
|
|
@@ -12959,6 +13342,7 @@ function createVendorOnboardHandlers(config) {
|
|
|
12959
13342
|
}
|
|
12960
13343
|
});
|
|
12961
13344
|
if (dupVendor) throw new Error("VENDOR_SLUG_EXISTS");
|
|
13345
|
+
await retireSoftDeletedUniqueValue(vendorRepo, "slug", slug);
|
|
12962
13346
|
let ownerGroup = await groupRepo.findOne({
|
|
12963
13347
|
where: {
|
|
12964
13348
|
name: VENDOR_OWNER_GROUP_NAME,
|
|
@@ -12987,8 +13371,15 @@ function createVendorOnboardHandlers(config) {
|
|
|
12987
13371
|
email: userEmail
|
|
12988
13372
|
}
|
|
12989
13373
|
});
|
|
12990
|
-
if (existingUser && !existingUser.deleted)
|
|
12991
|
-
|
|
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 () => {
|
|
12992
13383
|
await userRepo.update(existingUser.id, {
|
|
12993
13384
|
deleted: false,
|
|
12994
13385
|
deletedAt: null,
|
|
@@ -16071,10 +16462,12 @@ var Customer = class {
|
|
|
16071
16462
|
__name(this, "Customer");
|
|
16072
16463
|
}
|
|
16073
16464
|
id;
|
|
16465
|
+
/** Set only when the customer can log in (linked `users` row). Admin/guest customers stay null. */
|
|
16074
16466
|
userId;
|
|
16075
16467
|
user;
|
|
16076
16468
|
name;
|
|
16077
16469
|
email;
|
|
16470
|
+
/** Optional; multiple customers may have null (PostgreSQL UNIQUE allows multiple NULLs). */
|
|
16078
16471
|
phone;
|
|
16079
16472
|
createdAt;
|
|
16080
16473
|
updatedAt;
|
|
@@ -16089,8 +16482,10 @@ _ts_decorate18([
|
|
|
16089
16482
|
_ts_metadata18("design:type", Number)
|
|
16090
16483
|
], Customer.prototype, "id", void 0);
|
|
16091
16484
|
_ts_decorate18([
|
|
16092
|
-
Column("int"
|
|
16093
|
-
|
|
16485
|
+
Column("int", {
|
|
16486
|
+
nullable: true
|
|
16487
|
+
}),
|
|
16488
|
+
_ts_metadata18("design:type", Object)
|
|
16094
16489
|
], Customer.prototype, "userId", void 0);
|
|
16095
16490
|
_ts_decorate18([
|
|
16096
16491
|
ManyToOne(() => User, {
|
|
@@ -16113,9 +16508,10 @@ _ts_decorate18([
|
|
|
16113
16508
|
], Customer.prototype, "email", void 0);
|
|
16114
16509
|
_ts_decorate18([
|
|
16115
16510
|
Column("varchar", {
|
|
16116
|
-
unique: true
|
|
16511
|
+
unique: true,
|
|
16512
|
+
nullable: true
|
|
16117
16513
|
}),
|
|
16118
|
-
_ts_metadata18("design:type",
|
|
16514
|
+
_ts_metadata18("design:type", Object)
|
|
16119
16515
|
], Customer.prototype, "phone", void 0);
|
|
16120
16516
|
_ts_decorate18([
|
|
16121
16517
|
Column({
|
|
@@ -26699,6 +27095,14 @@ function createCmsApiHandler(config) {
|
|
|
26699
27095
|
});
|
|
26700
27096
|
return vendorHandlers.switchVendor(req);
|
|
26701
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
|
+
}
|
|
26702
27106
|
if (path2[0] === "admin" && path2[1] === "vendor" && path2[2] === "roles" && vendorRolesHandlers) {
|
|
26703
27107
|
if (path2.length === 3 && m === "GET") return vendorRolesHandlers.list();
|
|
26704
27108
|
if (path2.length === 3 && m === "POST") return vendorRolesHandlers.create(req);
|
|
@@ -29168,13 +29572,36 @@ function createStorefrontApiHandler(config) {
|
|
|
29168
29572
|
status: 400
|
|
29169
29573
|
};
|
|
29170
29574
|
}
|
|
29575
|
+
const list = [
|
|
29576
|
+
...vendorIds
|
|
29577
|
+
];
|
|
29171
29578
|
return {
|
|
29172
|
-
vendorId: [
|
|
29173
|
-
|
|
29174
|
-
][0]
|
|
29579
|
+
vendorId: list[0],
|
|
29580
|
+
vendorIds: list
|
|
29175
29581
|
};
|
|
29176
29582
|
}
|
|
29177
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");
|
|
29178
29605
|
function roundMoney3(n) {
|
|
29179
29606
|
return Math.round(n * 100) / 100;
|
|
29180
29607
|
}
|
|
@@ -31496,6 +31923,7 @@ function createStorefrontApiHandler(config) {
|
|
|
31496
31923
|
taxCode: line.taxCode
|
|
31497
31924
|
}));
|
|
31498
31925
|
}
|
|
31926
|
+
await linkOrderContactToVendors(contactId, vendorRes.vendorIds);
|
|
31499
31927
|
fireOrderPlacedNotification(oid);
|
|
31500
31928
|
return json({
|
|
31501
31929
|
orderId: oid,
|
|
@@ -31568,6 +31996,7 @@ function createStorefrontApiHandler(config) {
|
|
|
31568
31996
|
taxCode: line.taxCode
|
|
31569
31997
|
}));
|
|
31570
31998
|
}
|
|
31999
|
+
await linkOrderContactToVendors(contactId, vendorResChk.vendorIds);
|
|
31571
32000
|
await cartItemRepo().delete({
|
|
31572
32001
|
cartId: cart.id
|
|
31573
32002
|
});
|
|
@@ -31814,4 +32243,4 @@ function createStorefrontApiHandler(config) {
|
|
|
31814
32243
|
}
|
|
31815
32244
|
__name(createStorefrontApiHandler, "createStorefrontApiHandler");
|
|
31816
32245
|
|
|
31817
|
-
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 };
|