build: rebuild API release bundle for the admin/expert table split
app.js, seed.js, the Prisma client in release/vendor and the new migration 20261026120000_split_admin_table, built with `pnpm build:release` (API only; the admin panel and user apps are unchanged by this work). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
06c8a5f434
commit
e2ec30f241
+211
-101
@@ -90343,7 +90343,7 @@ var require_admin_uploads_service = __commonJS({
|
|||||||
mimeType: file2.mimetype,
|
mimeType: file2.mimetype,
|
||||||
sizeBytes: file2.size,
|
sizeBytes: file2.size,
|
||||||
purpose: purpose ?? null,
|
purpose: purpose ?? null,
|
||||||
uploadedByAdminUserId: actorAdminUserId
|
uploadedByAdminId: actorAdminUserId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -91521,7 +91521,7 @@ var require_ledger_service = __commonJS({
|
|||||||
note: input.note,
|
note: input.note,
|
||||||
confirmed,
|
confirmed,
|
||||||
confirmedAt: confirmed ? /* @__PURE__ */ new Date() : null,
|
confirmedAt: confirmed ? /* @__PURE__ */ new Date() : null,
|
||||||
postedByAdminUserId: input.postedByAdminUserId
|
postedByAdminId: input.postedByAdminId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
@@ -91546,7 +91546,7 @@ var require_ledger_service = __commonJS({
|
|||||||
return entry;
|
return entry;
|
||||||
const updated = await tx.ledgerEntry.update({
|
const updated = await tx.ledgerEntry.update({
|
||||||
where: { id: entryId },
|
where: { id: entryId },
|
||||||
data: { confirmed: true, confirmedAt: /* @__PURE__ */ new Date(), postedByAdminUserId: adminUserId ?? entry.postedByAdminUserId }
|
data: { confirmed: true, confirmedAt: /* @__PURE__ */ new Date(), postedByAdminId: adminUserId ?? entry.postedByAdminId }
|
||||||
});
|
});
|
||||||
await this.applyToBalance(entry.customerId, entry.currency, entry.direction, entry.amount, tx);
|
await this.applyToBalance(entry.customerId, entry.currency, entry.direction, entry.amount, tx);
|
||||||
return updated;
|
return updated;
|
||||||
@@ -91660,7 +91660,7 @@ var require_ledger_service = __commonJS({
|
|||||||
note: params.note,
|
note: params.note,
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
// pending until an admin confirms — intentional deviation from legacy, which auto-confirms on creation
|
// pending until an admin confirms — intentional deviation from legacy, which auto-confirms on creation
|
||||||
postedByAdminUserId: params.postedByAdminUserId
|
postedByAdminId: params.postedByAdminId
|
||||||
}, tx);
|
}, tx);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -91677,7 +91677,7 @@ var require_ledger_service = __commonJS({
|
|||||||
amount: params.amount,
|
amount: params.amount,
|
||||||
note: params.note,
|
note: params.note,
|
||||||
confirmed: false,
|
confirmed: false,
|
||||||
postedByAdminUserId: params.postedByAdminUserId
|
postedByAdminId: params.postedByAdminId
|
||||||
}, tx);
|
}, tx);
|
||||||
}
|
}
|
||||||
async postAdjustment(params, tx = this.prisma) {
|
async postAdjustment(params, tx = this.prisma) {
|
||||||
@@ -91689,7 +91689,7 @@ var require_ledger_service = __commonJS({
|
|||||||
amount: params.amount,
|
amount: params.amount,
|
||||||
note: params.note,
|
note: params.note,
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
postedByAdminUserId: params.postedByAdminUserId
|
postedByAdminId: params.postedByAdminId
|
||||||
}, tx);
|
}, tx);
|
||||||
}
|
}
|
||||||
async getBalance(customerId) {
|
async getBalance(customerId) {
|
||||||
@@ -92254,7 +92254,7 @@ var require_orders_service = __commonJS({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
await tx.orderStatusEvent.create({
|
await tx.orderStatusEvent.create({
|
||||||
data: { orderId, fromStatus: order.status, toStatus: newStatus, note, actorAdminUserId }
|
data: { orderId, fromStatus: order.status, toStatus: newStatus, note, actorAdminId: actorAdminUserId }
|
||||||
});
|
});
|
||||||
await this.ledger.syncOrderDebt(orderId, tx);
|
await this.ledger.syncOrderDebt(orderId, tx);
|
||||||
if (newStatus === client_1.OrderStatus.CANCELLED && order.status !== client_1.OrderStatus.CANCELLED) {
|
if (newStatus === client_1.OrderStatus.CANCELLED && order.status !== client_1.OrderStatus.CANCELLED) {
|
||||||
@@ -93827,7 +93827,7 @@ var require_admin_session_service = __commonJS({
|
|||||||
await this.prisma.adminSession.create({
|
await this.prisma.adminSession.create({
|
||||||
data: {
|
data: {
|
||||||
jti: input.jti,
|
jti: input.jti,
|
||||||
adminUserId: input.adminUserId,
|
adminId: input.adminId,
|
||||||
iamSessionId: input.iamSessionId ?? null,
|
iamSessionId: input.iamSessionId ?? null,
|
||||||
iamSub: input.iamSub ?? null,
|
iamSub: input.iamSub ?? null,
|
||||||
ipAddress: input.ipAddress ?? null,
|
ipAddress: input.ipAddress ?? null,
|
||||||
@@ -93863,8 +93863,8 @@ var require_admin_session_service = __commonJS({
|
|||||||
return this.revokeWhere({ iamSub }, reason);
|
return this.revokeWhere({ iamSub }, reason);
|
||||||
}
|
}
|
||||||
/** Revoke every session of a local admin (account disabled, password changed). */
|
/** Revoke every session of a local admin (account disabled, password changed). */
|
||||||
async revokeAllForAdmin(adminUserId, reason = "revoked") {
|
async revokeAllForAdmin(adminId, reason = "revoked") {
|
||||||
return this.revokeWhere({ adminUserId }, reason);
|
return this.revokeWhere({ adminId }, reason);
|
||||||
}
|
}
|
||||||
async revokeWhere(where, reason) {
|
async revokeWhere(where, reason) {
|
||||||
if (!this.enabled)
|
if (!this.enabled)
|
||||||
@@ -94003,7 +94003,7 @@ var require_auth_service = __commonJS({
|
|||||||
if (isStaticAdmin) {
|
if (isStaticAdmin) {
|
||||||
await this.ensureStaticAdmin();
|
await this.ensureStaticAdmin();
|
||||||
}
|
}
|
||||||
const user = await this.prisma.adminUser.findUnique({
|
const user = await this.prisma.admin.findUnique({
|
||||||
where: { mobile: isStaticAdmin ? STATIC_ADMIN_MOBILE : dto.phone },
|
where: { mobile: isStaticAdmin ? STATIC_ADMIN_MOBILE : dto.phone },
|
||||||
include: { userType: { include: { permissions: true } } }
|
include: { userType: { include: { permissions: true } } }
|
||||||
});
|
});
|
||||||
@@ -94023,7 +94023,7 @@ var require_auth_service = __commonJS({
|
|||||||
if (jti) {
|
if (jti) {
|
||||||
await this.sessions.record({
|
await this.sessions.record({
|
||||||
jti,
|
jti,
|
||||||
adminUserId: user.id,
|
adminId: user.id,
|
||||||
iamSessionId: verified.identity?.sessionId ?? null,
|
iamSessionId: verified.identity?.sessionId ?? null,
|
||||||
iamSub: verified.identity?.sub ?? null,
|
iamSub: verified.identity?.sub ?? null,
|
||||||
ipAddress: clientIp ?? null,
|
ipAddress: clientIp ?? null,
|
||||||
@@ -94084,7 +94084,7 @@ var require_auth_service = __commonJS({
|
|||||||
if (!claims.phoneNumber) {
|
if (!claims.phoneNumber) {
|
||||||
throw new common_12.UnauthorizedException("\u0647\u0648\u06CC\u062A \u0645\u0634\u062A\u0631\u06A9 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0646\u062F\u0627\u0631\u062F");
|
throw new common_12.UnauthorizedException("\u0647\u0648\u06CC\u062A \u0645\u0634\u062A\u0631\u06A9 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0646\u062F\u0627\u0631\u062F");
|
||||||
}
|
}
|
||||||
const user = await this.prisma.adminUser.findFirst({
|
const user = await this.prisma.admin.findFirst({
|
||||||
where: { mobile: { in: mobileVariants(claims.phoneNumber) }, isActive: true },
|
where: { mobile: { in: mobileVariants(claims.phoneNumber) }, isActive: true },
|
||||||
include: { userType: { include: { permissions: true } } }
|
include: { userType: { include: { permissions: true } } }
|
||||||
});
|
});
|
||||||
@@ -94128,7 +94128,7 @@ var require_auth_service = __commonJS({
|
|||||||
if (jti) {
|
if (jti) {
|
||||||
await this.sessions.record({
|
await this.sessions.record({
|
||||||
jti,
|
jti,
|
||||||
adminUserId: user.id,
|
adminId: user.id,
|
||||||
iamSessionId: identity?.sessionId ?? null,
|
iamSessionId: identity?.sessionId ?? null,
|
||||||
iamSub: identity?.sub ?? null,
|
iamSub: identity?.sub ?? null,
|
||||||
ipAddress: ip ?? null,
|
ipAddress: ip ?? null,
|
||||||
@@ -94152,7 +94152,7 @@ var require_auth_service = __commonJS({
|
|||||||
const fullAccess = await this.prisma.userType.findUnique({ where: { id: 85 } });
|
const fullAccess = await this.prisma.userType.findUnique({ where: { id: 85 } });
|
||||||
const userTypeId = fullAccess ? 85 : null;
|
const userTypeId = fullAccess ? 85 : null;
|
||||||
const passwordHash = await bcrypt.hash(STATIC_ADMIN_MOBILE, 10);
|
const passwordHash = await bcrypt.hash(STATIC_ADMIN_MOBILE, 10);
|
||||||
await this.prisma.adminUser.upsert({
|
await this.prisma.admin.upsert({
|
||||||
where: { mobile: STATIC_ADMIN_MOBILE },
|
where: { mobile: STATIC_ADMIN_MOBILE },
|
||||||
update: { role: client_1.AdminRole.SUPER_ADMIN, userTypeId, branchId: null, isActive: true },
|
update: { role: client_1.AdminRole.SUPER_ADMIN, userTypeId, branchId: null, isActive: true },
|
||||||
create: {
|
create: {
|
||||||
@@ -94230,7 +94230,7 @@ var require_auth_service = __commonJS({
|
|||||||
* legacy. The guard's own checks use the pre-expanded list from JwtStrategy.
|
* legacy. The guard's own checks use the pre-expanded list from JwtStrategy.
|
||||||
*/
|
*/
|
||||||
async showData(adminId) {
|
async showData(adminId) {
|
||||||
const user = await this.prisma.adminUser.findUnique({
|
const user = await this.prisma.admin.findUnique({
|
||||||
where: { id: adminId },
|
where: { id: adminId },
|
||||||
include: { userType: { include: { permissions: true } } }
|
include: { userType: { include: { permissions: true } } }
|
||||||
});
|
});
|
||||||
@@ -94644,7 +94644,7 @@ var require_jwt_strategy = __commonJS({
|
|||||||
if (!await this.sessions.isActive(payload.jti)) {
|
if (!await this.sessions.isActive(payload.jti)) {
|
||||||
throw new common_12.UnauthorizedException();
|
throw new common_12.UnauthorizedException();
|
||||||
}
|
}
|
||||||
const user = await this.prisma.adminUser.findUnique({
|
const user = await this.prisma.admin.findUnique({
|
||||||
where: { id: payload.sub },
|
where: { id: payload.sub },
|
||||||
include: { userType: { include: { permissions: true } } }
|
include: { userType: { include: { permissions: true } } }
|
||||||
});
|
});
|
||||||
@@ -95418,7 +95418,7 @@ var require_admin_orders_service = __commonJS({
|
|||||||
customer: true,
|
customer: true,
|
||||||
branch: true,
|
branch: true,
|
||||||
items: { include: { productVariant: { include: { product: true } } } },
|
items: { include: { productVariant: { include: { product: true } } } },
|
||||||
statusEvents: { include: { actorAdminUser: true }, orderBy: { createdAt: "asc" } }
|
statusEvents: { include: { actorAdmin: true }, orderBy: { createdAt: "asc" } }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!order)
|
if (!order)
|
||||||
@@ -96336,7 +96336,7 @@ var require_admin_branches_service = __commonJS({
|
|||||||
/** Legacy `POST branch/table`. */
|
/** Legacy `POST branch/table`. */
|
||||||
async listBranches(query) {
|
async listBranches(query) {
|
||||||
this.table.assertNotExport(query);
|
this.table.assertNotExport(query);
|
||||||
const aliases = { "user.name": "adminUsers.some.fullName" };
|
const aliases = { "user.name": "admins.some.fullName" };
|
||||||
const { where, orderBy, skip, take, page, limit } = this.table.build(query, {
|
const { where, orderBy, skip, take, page, limit } = this.table.build(query, {
|
||||||
searchable: ["name", "address", "phone"],
|
searchable: ["name", "address", "phone"],
|
||||||
allowed: ["id", "name", "address", "phone", "isActive", "cityId", "city.name"],
|
allowed: ["id", "name", "address", "phone", "isActive", "cityId", "city.name"],
|
||||||
@@ -96351,8 +96351,8 @@ var require_admin_branches_service = __commonJS({
|
|||||||
take,
|
take,
|
||||||
include: {
|
include: {
|
||||||
city: true,
|
city: true,
|
||||||
adminUsers: { where: { isActive: true }, take: 1 },
|
admins: { where: { isActive: true }, take: 1 },
|
||||||
_count: { select: { adminUsers: true, orders: true } }
|
_count: { select: { admins: true, orders: true } }
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
this.prisma.branch.count({ where })
|
this.prisma.branch.count({ where })
|
||||||
@@ -96367,15 +96367,15 @@ var require_admin_branches_service = __commonJS({
|
|||||||
city_id: b.cityId,
|
city_id: b.cityId,
|
||||||
city: b.city ? { id: b.city.id, title: b.city.name } : null,
|
city: b.city ? { id: b.city.id, title: b.city.name } : null,
|
||||||
// legacy column key is `user.name` — the branch manager
|
// legacy column key is `user.name` — the branch manager
|
||||||
user: b.adminUsers[0] ? { id: b.adminUsers[0].id, name: b.adminUsers[0].fullName } : null,
|
user: b.admins[0] ? { id: b.admins[0].id, name: b.admins[0].fullName } : null,
|
||||||
users_count: b._count.adminUsers,
|
users_count: b._count.admins,
|
||||||
orders_count: b._count.orders
|
orders_count: b._count.orders
|
||||||
})), total, query, page, limit);
|
})), total, query, page, limit);
|
||||||
}
|
}
|
||||||
async findBranch(id) {
|
async findBranch(id) {
|
||||||
const b = await this.prisma.branch.findUnique({
|
const b = await this.prisma.branch.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: { city: true, adminUsers: { where: { isActive: true }, take: 1 } }
|
include: { city: true, admins: { where: { isActive: true }, take: 1 } }
|
||||||
});
|
});
|
||||||
if (!b)
|
if (!b)
|
||||||
throw new common_12.NotFoundException("\u0634\u0639\u0628\u0647 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
throw new common_12.NotFoundException("\u0634\u0639\u0628\u0647 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
@@ -96386,8 +96386,8 @@ var require_admin_branches_service = __commonJS({
|
|||||||
phone: b.phone,
|
phone: b.phone,
|
||||||
is_active: b.isActive,
|
is_active: b.isActive,
|
||||||
city_id: b.cityId,
|
city_id: b.cityId,
|
||||||
user_id: b.adminUsers[0]?.id ?? null,
|
user_id: b.admins[0]?.id ?? null,
|
||||||
user: b.adminUsers[0] ? { id: b.adminUsers[0].id, name: b.adminUsers[0].fullName } : null
|
user: b.admins[0] ? { id: b.admins[0].id, name: b.admins[0].fullName } : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/** Legacy `POST branch` — the manager is an EXISTING admin picked by id. */
|
/** Legacy `POST branch` — the manager is an EXISTING admin picked by id. */
|
||||||
@@ -96436,12 +96436,12 @@ var require_admin_branches_service = __commonJS({
|
|||||||
*/
|
*/
|
||||||
async assignBranchManager(branchId, userId) {
|
async assignBranchManager(branchId, userId) {
|
||||||
await this.prisma.$transaction(async (tx) => {
|
await this.prisma.$transaction(async (tx) => {
|
||||||
await tx.adminUser.updateMany({ where: { branchId }, data: { branchId: null } });
|
await tx.admin.updateMany({ where: { branchId }, data: { branchId: null } });
|
||||||
if (userId) {
|
if (userId) {
|
||||||
const user = await tx.adminUser.findUnique({ where: { id: userId } });
|
const user = await tx.admin.findUnique({ where: { id: userId } });
|
||||||
if (!user)
|
if (!user)
|
||||||
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u0627\u0646\u062A\u062E\u0627\u0628\u200C\u0634\u062F\u0647 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u0627\u0646\u062A\u062E\u0627\u0628\u200C\u0634\u062F\u0647 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
await tx.adminUser.update({ where: { id: userId }, data: { branchId } });
|
await tx.admin.update({ where: { id: userId }, data: { branchId } });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -96564,25 +96564,25 @@ var require_admin_branches_service = __commonJS({
|
|||||||
throw new common_12.BadRequestException("\u0645\u0648\u0628\u0627\u06CC\u0644 \u0633\u0631\u067E\u0631\u0633\u062A \u06A9\u0627\u0631\u06AF\u0627\u0647 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A");
|
throw new common_12.BadRequestException("\u0645\u0648\u0628\u0627\u06CC\u0644 \u0633\u0631\u067E\u0631\u0633\u062A \u06A9\u0627\u0631\u06AF\u0627\u0647 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A");
|
||||||
const fullName = `${fname} ${lname}`.trim();
|
const fullName = `${fname} ${lname}`.trim();
|
||||||
if (currentId) {
|
if (currentId) {
|
||||||
const current = await this.prisma.adminUser.findUnique({ where: { id: currentId } });
|
const current = await this.prisma.admin.findUnique({ where: { id: currentId } });
|
||||||
if (current) {
|
if (current) {
|
||||||
if (phone !== current.mobile) {
|
if (phone !== current.mobile) {
|
||||||
const clash = await this.prisma.adminUser.findUnique({ where: { mobile: phone } });
|
const clash = await this.prisma.admin.findUnique({ where: { mobile: phone } });
|
||||||
if (clash && clash.id !== currentId) {
|
if (clash && clash.id !== currentId) {
|
||||||
throw new common_12.BadRequestException("\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A");
|
throw new common_12.BadRequestException("\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.prisma.adminUser.update({
|
await this.prisma.admin.update({
|
||||||
where: { id: currentId },
|
where: { id: currentId },
|
||||||
data: { fullName, mobile: phone }
|
data: { fullName, mobile: phone }
|
||||||
});
|
});
|
||||||
return currentId;
|
return currentId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const existing = await this.prisma.adminUser.findUnique({ where: { mobile: phone } });
|
const existing = await this.prisma.admin.findUnique({ where: { mobile: phone } });
|
||||||
if (existing)
|
if (existing)
|
||||||
return existing.id;
|
return existing.id;
|
||||||
const created = await this.prisma.adminUser.create({
|
const created = await this.prisma.admin.create({
|
||||||
data: {
|
data: {
|
||||||
fullName,
|
fullName,
|
||||||
mobile: phone,
|
mobile: phone,
|
||||||
@@ -96605,7 +96605,7 @@ var require_admin_branches_service = __commonJS({
|
|||||||
}
|
}
|
||||||
/** The admin picker the branch form uses — legacy `GET user/list`. */
|
/** The admin picker the branch form uses — legacy `GET user/list`. */
|
||||||
async userList() {
|
async userList() {
|
||||||
const rows = await this.prisma.adminUser.findMany({
|
const rows = await this.prisma.admin.findMany({
|
||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
orderBy: { fullName: "asc" },
|
orderBy: { fullName: "asc" },
|
||||||
select: { id: true, fullName: true, mobile: true, branchId: true }
|
select: { id: true, fullName: true, mobile: true, branchId: true }
|
||||||
@@ -100173,7 +100173,7 @@ var require_admin_documents_service = __commonJS({
|
|||||||
orderBy,
|
orderBy,
|
||||||
skip,
|
skip,
|
||||||
take,
|
take,
|
||||||
include: { customer: true, postedByAdminUser: true }
|
include: { customer: true, postedByAdmin: true }
|
||||||
}),
|
}),
|
||||||
this.prisma.ledgerEntry.count({ where: scoped })
|
this.prisma.ledgerEntry.count({ where: scoped })
|
||||||
]);
|
]);
|
||||||
@@ -100212,7 +100212,7 @@ var require_admin_documents_service = __commonJS({
|
|||||||
lname: e.customer.lastName ?? "",
|
lname: e.customer.lastName ?? "",
|
||||||
mobile: e.customer.mobile
|
mobile: e.customer.mobile
|
||||||
},
|
},
|
||||||
posted_by: e.postedByAdminUser ? e.postedByAdminUser.fullName : null,
|
posted_by: e.postedByAdmin ? e.postedByAdmin.fullName : null,
|
||||||
caj: e.createdAt,
|
caj: e.createdAt,
|
||||||
uaj: e.createdAt
|
uaj: e.createdAt
|
||||||
};
|
};
|
||||||
@@ -100221,7 +100221,7 @@ var require_admin_documents_service = __commonJS({
|
|||||||
async findOne(id) {
|
async findOne(id) {
|
||||||
const e = await this.prisma.ledgerEntry.findUnique({
|
const e = await this.prisma.ledgerEntry.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: { customer: true, postedByAdminUser: true }
|
include: { customer: true, postedByAdmin: true }
|
||||||
});
|
});
|
||||||
if (!e)
|
if (!e)
|
||||||
throw new common_12.NotFoundException("\u0633\u0646\u062F \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
throw new common_12.NotFoundException("\u0633\u0646\u062F \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
@@ -100250,7 +100250,7 @@ var require_admin_documents_service = __commonJS({
|
|||||||
karatPerMille: data.ayar ? Number(data.ayar) : void 0,
|
karatPerMille: data.ayar ? Number(data.ayar) : void 0,
|
||||||
pricePerGramRial: data.price_per_gram ? String(data.price_per_gram) : void 0,
|
pricePerGramRial: data.price_per_gram ? String(data.price_per_gram) : void 0,
|
||||||
note: data.note ?? void 0,
|
note: data.note ?? void 0,
|
||||||
postedByAdminUserId: actorAdminUserId
|
postedByAdminId: actorAdminUserId
|
||||||
});
|
});
|
||||||
await this.prisma.ledgerEntry.update({
|
await this.prisma.ledgerEntry.update({
|
||||||
where: { id: entry.id },
|
where: { id: entry.id },
|
||||||
@@ -101292,7 +101292,7 @@ var require_admin_users_service = __commonJS({
|
|||||||
caj: "createdAt",
|
caj: "createdAt",
|
||||||
active: "isActive"
|
active: "isActive"
|
||||||
};
|
};
|
||||||
/** Legacy `GET users` — the admin user grid. */
|
/** Legacy `GET users` — the admin user grid (panel staff only). */
|
||||||
async list(query) {
|
async list(query) {
|
||||||
this.table.assertNotExport(query);
|
this.table.assertNotExport(query);
|
||||||
const mapField = (f) => f ? AdminUsersService_1.FIELD_ALIASES[f] ?? f : f;
|
const mapField = (f) => f ? AdminUsersService_1.FIELD_ALIASES[f] ?? f : f;
|
||||||
@@ -101319,14 +101319,14 @@ var require_admin_users_service = __commonJS({
|
|||||||
AND: [where, { mobile: { not: super_admin_1.SUPER_ADMIN_MOBILE } }]
|
AND: [where, { mobile: { not: super_admin_1.SUPER_ADMIN_MOBILE } }]
|
||||||
};
|
};
|
||||||
const [rows, total] = await Promise.all([
|
const [rows, total] = await Promise.all([
|
||||||
this.prisma.adminUser.findMany({
|
this.prisma.admin.findMany({
|
||||||
where: visible,
|
where: visible,
|
||||||
orderBy,
|
orderBy,
|
||||||
skip,
|
skip,
|
||||||
take,
|
take,
|
||||||
include: { userType: true, branch: true, avatarFile: true }
|
include: { userType: true, branch: true }
|
||||||
}),
|
}),
|
||||||
this.prisma.adminUser.count({ where: visible })
|
this.prisma.admin.count({ where: visible })
|
||||||
]);
|
]);
|
||||||
return this.table.respond(rows.map((r) => this.toRow(r)), total, query, page, limit);
|
return this.table.respond(rows.map((r) => this.toRow(r)), total, query, page, limit);
|
||||||
}
|
}
|
||||||
@@ -101345,20 +101345,20 @@ var require_admin_users_service = __commonJS({
|
|||||||
user_type: u.userType ? { id: u.userType.id, title: u.userType.title } : null,
|
user_type: u.userType ? { id: u.userType.id, title: u.userType.title } : null,
|
||||||
branch_id: u.branchId,
|
branch_id: u.branchId,
|
||||||
branch: u.branch ? { id: u.branch.id, name: u.branch.name } : null,
|
branch: u.branch ? { id: u.branch.id, name: u.branch.name } : null,
|
||||||
percent: u.wagePercent,
|
percent: u.wagePercent ?? null,
|
||||||
active: u.isActive ? 1 : -1,
|
active: u.isActive ? 1 : -1,
|
||||||
is_active: u.isActive,
|
is_active: u.isActive,
|
||||||
caj: u.createdAt,
|
caj: u.createdAt,
|
||||||
// Sales-agent fields (ReagentCustomer.vue) — set only for role=SALES_EXPERT,
|
// Sales-agent fields (ReagentCustomer.vue) — set only for role=SALES_EXPERT,
|
||||||
// null for every other admin. Same shapes admin-customers.service.ts used
|
// null for every admin. Same shapes admin-customers.service.ts used
|
||||||
// when this data still lived on Customer, so the form binds unchanged.
|
// when this data still lived on Customer, so the form binds unchanged.
|
||||||
address: u.address,
|
address: u.address ?? null,
|
||||||
store_name: u.storeName,
|
store_name: u.storeName ?? null,
|
||||||
pasazh_name: u.pasazhName,
|
pasazh_name: u.pasazhName ?? null,
|
||||||
mcode: u.memberCode,
|
mcode: u.memberCode ?? null,
|
||||||
image_id: u.avatarFileId,
|
image_id: u.avatarFileId ?? null,
|
||||||
image: u.avatarFile ? { id: u.avatarFile.id, image: u.avatarFile.url } : null,
|
image: u.avatarFile ? { id: u.avatarFile.id, image: u.avatarFile.url } : null,
|
||||||
// D17 — from SalesExpertProfile. Null for a non-expert, and null-safe for
|
// D17 — from SalesExpertProfile. Null for an admin, and null-safe for
|
||||||
// an expert whose profile row is somehow missing.
|
// an expert whose profile row is somehow missing.
|
||||||
city_id: u.salesExpertProfile?.cityId ?? null,
|
city_id: u.salesExpertProfile?.cityId ?? null,
|
||||||
city: u.salesExpertProfile?.city ? { id: u.salesExpertProfile.city.id, title: u.salesExpertProfile.city.name } : null,
|
city: u.salesExpertProfile?.city ? { id: u.salesExpertProfile.city.id, title: u.salesExpertProfile.city.name } : null,
|
||||||
@@ -101367,9 +101367,22 @@ var require_admin_users_service = __commonJS({
|
|||||||
ncode: u.salesExpertProfile?.nationalCode ?? null
|
ncode: u.salesExpertProfile?.nationalCode ?? null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
async findOne(id) {
|
/** A panel admin by id, or null. The support super admin is never found. */
|
||||||
const u = await this.prisma.adminUser.findUnique({
|
async findAdmin(id) {
|
||||||
|
const admin = await this.prisma.admin.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
|
include: { userType: true, branch: true }
|
||||||
|
});
|
||||||
|
return admin && !(0, super_admin_1.isSuperAdmin)(admin.mobile) ? admin : null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* A sales expert by id, or null. `role` is part of the lookup on purpose:
|
||||||
|
* "AdminUser" still holds the pre-split copies of the admins (dormant), and
|
||||||
|
* those must never be reachable as experts either.
|
||||||
|
*/
|
||||||
|
async findExpert(id) {
|
||||||
|
const expert = await this.prisma.adminUser.findFirst({
|
||||||
|
where: { id, role: client_1.AdminRole.SALES_EXPERT },
|
||||||
// D17 — the edit form needs the sales-expert fields too.
|
// D17 — the edit form needs the sales-expert fields too.
|
||||||
include: {
|
include: {
|
||||||
userType: true,
|
userType: true,
|
||||||
@@ -101378,9 +101391,16 @@ var require_admin_users_service = __commonJS({
|
|||||||
salesExpertProfile: { include: { city: true } }
|
salesExpertProfile: { include: { city: true } }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!u || (0, super_admin_1.isSuperAdmin)(u.mobile))
|
return expert && !(0, super_admin_1.isSuperAdmin)(expert.mobile) ? expert : null;
|
||||||
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
}
|
||||||
return this.toRow(u);
|
async findOne(id) {
|
||||||
|
const admin = await this.findAdmin(id);
|
||||||
|
if (admin)
|
||||||
|
return this.toRow(admin);
|
||||||
|
const expert = await this.findExpert(id);
|
||||||
|
if (expert)
|
||||||
|
return this.toRow(expert);
|
||||||
|
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Legacy `POST users`.
|
* Legacy `POST users`.
|
||||||
@@ -101389,6 +101409,8 @@ var require_admin_users_service = __commonJS({
|
|||||||
* caller omitted it, which silently turned every admin created without an
|
* caller omitted it, which silently turned every admin created without an
|
||||||
* explicit role into a sales agent regardless of the access level ("نوع
|
* explicit role into a sales agent regardless of the access level ("نوع
|
||||||
* کاربری") actually chosen. UserCreate.vue now always sends one.
|
* کاربری") actually chosen. UserCreate.vue now always sends one.
|
||||||
|
*
|
||||||
|
* SALES_EXPERT goes to the `AdminUser` table, every other role to `Admin`.
|
||||||
*/
|
*/
|
||||||
async create(data) {
|
async create(data) {
|
||||||
const firstName = String(data.fname ?? data.name ?? "").trim();
|
const firstName = String(data.fname ?? data.name ?? "").trim();
|
||||||
@@ -101405,6 +101427,31 @@ var require_admin_users_service = __commonJS({
|
|||||||
throw new common_12.BadRequestException("\u0631\u0645\u0632 \u0639\u0628\u0648\u0631 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A");
|
throw new common_12.BadRequestException("\u0631\u0645\u0632 \u0639\u0628\u0648\u0631 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A");
|
||||||
if (!role)
|
if (!role)
|
||||||
throw new common_12.BadRequestException("\u0646\u0642\u0634 \u06A9\u0627\u0631\u0628\u0631 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A");
|
throw new common_12.BadRequestException("\u0646\u0642\u0634 \u06A9\u0627\u0631\u0628\u0631 \u0627\u0644\u0632\u0627\u0645\u06CC \u0627\u0633\u062A");
|
||||||
|
if (!Object.values(client_1.AdminRole).includes(role))
|
||||||
|
throw new common_12.BadRequestException("\u0646\u0642\u0634 \u06A9\u0627\u0631\u0628\u0631 \u0645\u0639\u062A\u0628\u0631 \u0646\u06CC\u0633\u062A");
|
||||||
|
if (role === client_1.AdminRole.SALES_EXPERT) {
|
||||||
|
return this.createExpert({ data, firstName, lastName, fullName, mobile, password });
|
||||||
|
}
|
||||||
|
const existing = await this.prisma.admin.findUnique({ where: { mobile } });
|
||||||
|
if (existing)
|
||||||
|
throw new common_12.BadRequestException("\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A");
|
||||||
|
return this.prisma.admin.create({
|
||||||
|
data: {
|
||||||
|
fullName,
|
||||||
|
firstName: firstName || null,
|
||||||
|
lastName: lastName || null,
|
||||||
|
mobile,
|
||||||
|
passwordHash: await bcrypt.hash(password, 10),
|
||||||
|
role,
|
||||||
|
userTypeId: data.ut !== void 0 && data.ut !== null ? Number(data.ut) : null,
|
||||||
|
branchId: data.branch_id || null,
|
||||||
|
isActive: data.is_active !== void 0 ? Boolean(data.is_active) : true
|
||||||
|
},
|
||||||
|
include: { userType: true, branch: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async createExpert(input) {
|
||||||
|
const { data, firstName, lastName, fullName, mobile, password } = input;
|
||||||
const existing = await this.prisma.adminUser.findUnique({ where: { mobile } });
|
const existing = await this.prisma.adminUser.findUnique({ where: { mobile } });
|
||||||
if (existing)
|
if (existing)
|
||||||
throw new common_12.BadRequestException("\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A");
|
throw new common_12.BadRequestException("\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A");
|
||||||
@@ -101415,13 +101462,12 @@ var require_admin_users_service = __commonJS({
|
|||||||
lastName: lastName || null,
|
lastName: lastName || null,
|
||||||
mobile,
|
mobile,
|
||||||
passwordHash: await bcrypt.hash(password, 10),
|
passwordHash: await bcrypt.hash(password, 10),
|
||||||
role,
|
role: client_1.AdminRole.SALES_EXPERT,
|
||||||
userTypeId: data.ut !== void 0 && data.ut !== null ? Number(data.ut) : null,
|
userTypeId: data.ut !== void 0 && data.ut !== null ? Number(data.ut) : null,
|
||||||
branchId: data.branch_id || null,
|
branchId: data.branch_id || null,
|
||||||
wagePercent: data.percent !== void 0 && data.percent !== null ? new client_1.Prisma.Decimal(String(data.percent)) : null,
|
wagePercent: data.percent !== void 0 && data.percent !== null ? new client_1.Prisma.Decimal(String(data.percent)) : null,
|
||||||
isActive: data.is_active !== void 0 ? Boolean(data.is_active) : true,
|
isActive: data.is_active !== void 0 ? Boolean(data.is_active) : true,
|
||||||
// Sales-agent-only fields (ReagentCustomer.vue); undefined for every
|
// Sales-agent-only fields (ReagentCustomer.vue).
|
||||||
// other role, which Prisma leaves at column default (null).
|
|
||||||
address: data.address || null,
|
address: data.address || null,
|
||||||
storeName: data.store_name || null,
|
storeName: data.store_name || null,
|
||||||
pasazhName: data.pasazh_name || null,
|
pasazhName: data.pasazh_name || null,
|
||||||
@@ -101429,7 +101475,7 @@ var require_admin_users_service = __commonJS({
|
|||||||
avatarFileId: data.image_id || null,
|
avatarFileId: data.image_id || null,
|
||||||
// D17 — a sales expert always gets its profile row, so the grid's city
|
// D17 — a sales expert always gets its profile row, so the grid's city
|
||||||
// and credit-limit columns have somewhere to read from immediately.
|
// and credit-limit columns have somewhere to read from immediately.
|
||||||
...role === "SALES_EXPERT" ? { salesExpertProfile: { create: this.salesExpertProfileData(data) } } : {}
|
salesExpertProfile: { create: this.salesExpertProfileData(data) }
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
userType: true,
|
userType: true,
|
||||||
@@ -101456,9 +101502,65 @@ var require_admin_users_service = __commonJS({
|
|||||||
}
|
}
|
||||||
/** Legacy `PUT users/{id}`. */
|
/** Legacy `PUT users/{id}`. */
|
||||||
async update(id, data) {
|
async update(id, data) {
|
||||||
const existing = await this.prisma.adminUser.findUnique({ where: { id } });
|
const admin = await this.findAdmin(id);
|
||||||
if (!existing || (0, super_admin_1.isSuperAdmin)(existing.mobile))
|
if (admin)
|
||||||
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
return this.updateAdmin(admin, data);
|
||||||
|
const expert = await this.findExpert(id);
|
||||||
|
if (expert)
|
||||||
|
return this.updateExpert(expert, data);
|
||||||
|
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
|
}
|
||||||
|
async updateAdmin(existing, data) {
|
||||||
|
const id = existing.id;
|
||||||
|
const patch = {};
|
||||||
|
if (data.fname !== void 0 || data.lname !== void 0) {
|
||||||
|
const firstName = String(data.fname ?? existing.firstName ?? "").trim();
|
||||||
|
const lastName = String(data.lname ?? existing.lastName ?? "").trim();
|
||||||
|
patch.firstName = firstName || null;
|
||||||
|
patch.lastName = lastName || null;
|
||||||
|
} else if (data.name !== void 0) {
|
||||||
|
patch.fullName = String(data.name);
|
||||||
|
}
|
||||||
|
if (data.mobile !== void 0) {
|
||||||
|
const mobile = (0, digits_1.faToEn)(String(data.mobile));
|
||||||
|
if (mobile !== existing.mobile) {
|
||||||
|
const clash = await this.prisma.admin.findUnique({ where: { mobile } });
|
||||||
|
if (clash)
|
||||||
|
throw new common_12.BadRequestException("\u0627\u06CC\u0646 \u0634\u0645\u0627\u0631\u0647 \u0645\u0648\u0628\u0627\u06CC\u0644 \u0642\u0628\u0644\u0627\u064B \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A");
|
||||||
|
}
|
||||||
|
patch.mobile = mobile;
|
||||||
|
}
|
||||||
|
if (data.password)
|
||||||
|
patch.passwordHash = await bcrypt.hash(String(data.password), 10);
|
||||||
|
if (data.role !== void 0) {
|
||||||
|
const role = data.role;
|
||||||
|
if (!Object.values(client_1.AdminRole).includes(role))
|
||||||
|
throw new common_12.BadRequestException("\u0646\u0642\u0634 \u06A9\u0627\u0631\u0628\u0631 \u0645\u0639\u062A\u0628\u0631 \u0646\u06CC\u0633\u062A");
|
||||||
|
if (role === client_1.AdminRole.SALES_EXPERT) {
|
||||||
|
throw new common_12.BadRequestException("\u0646\u0642\u0634 \u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u0628\u0631\u0627\u06CC \u0627\u062F\u0645\u06CC\u0646 \u0642\u0627\u0628\u0644 \u062A\u0646\u0638\u06CC\u0645 \u0646\u06CC\u0633\u062A\u061B \u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0631\u0627 \u0627\u0632 \u0628\u062E\u0634 \u06A9\u0627\u0631\u0634\u0646\u0627\u0633\u0627\u0646 \u0628\u0633\u0627\u0632\u06CC\u062F");
|
||||||
|
}
|
||||||
|
patch.role = role;
|
||||||
|
}
|
||||||
|
if (data.ut !== void 0) {
|
||||||
|
patch.userType = data.ut ? { connect: { id: Number(data.ut) } } : { disconnect: true };
|
||||||
|
}
|
||||||
|
if (data.branch_id !== void 0) {
|
||||||
|
patch.branch = data.branch_id ? { connect: { id: String(data.branch_id) } } : { disconnect: true };
|
||||||
|
}
|
||||||
|
if (data.is_active !== void 0)
|
||||||
|
patch.isActive = Boolean(data.is_active);
|
||||||
|
const updated = await this.prisma.admin.update({
|
||||||
|
where: { id },
|
||||||
|
data: patch,
|
||||||
|
include: { userType: true, branch: true }
|
||||||
|
});
|
||||||
|
if (patch.isActive === false || patch.passwordHash !== void 0) {
|
||||||
|
await this.sessions.revokeAllForAdmin(id, patch.isActive === false ? "account_disabled" : "password_changed");
|
||||||
|
}
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
async updateExpert(existing, data) {
|
||||||
|
const id = existing.id;
|
||||||
const patch = {};
|
const patch = {};
|
||||||
if (data.fname !== void 0 || data.lname !== void 0) {
|
if (data.fname !== void 0 || data.lname !== void 0) {
|
||||||
const firstName = String(data.fname ?? existing.firstName ?? "").trim();
|
const firstName = String(data.fname ?? existing.firstName ?? "").trim();
|
||||||
@@ -101479,8 +101581,9 @@ var require_admin_users_service = __commonJS({
|
|||||||
}
|
}
|
||||||
if (data.password)
|
if (data.password)
|
||||||
patch.passwordHash = await bcrypt.hash(String(data.password), 10);
|
patch.passwordHash = await bcrypt.hash(String(data.password), 10);
|
||||||
if (data.role !== void 0)
|
if (data.role !== void 0 && data.role !== client_1.AdminRole.SALES_EXPERT) {
|
||||||
patch.role = data.role;
|
throw new common_12.BadRequestException("\u0646\u0642\u0634 \u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634 \u0642\u0627\u0628\u0644 \u062A\u063A\u06CC\u06CC\u0631 \u0646\u06CC\u0633\u062A");
|
||||||
|
}
|
||||||
if (data.ut !== void 0) {
|
if (data.ut !== void 0) {
|
||||||
patch.userType = data.ut ? { connect: { id: Number(data.ut) } } : { disconnect: true };
|
patch.userType = data.ut ? { connect: { id: Number(data.ut) } } : { disconnect: true };
|
||||||
}
|
}
|
||||||
@@ -101503,9 +101606,8 @@ var require_admin_users_service = __commonJS({
|
|||||||
if (data.image_id !== void 0) {
|
if (data.image_id !== void 0) {
|
||||||
patch.avatarFile = data.image_id ? { connect: { id: String(data.image_id) } } : { disconnect: true };
|
patch.avatarFile = data.image_id ? { connect: { id: String(data.image_id) } } : { disconnect: true };
|
||||||
}
|
}
|
||||||
const effectiveRole = patch.role ?? existing.role;
|
|
||||||
const touchesProfile = data.city_id !== void 0 || data.gram_level !== void 0 || data.phone !== void 0 || data.ncode !== void 0;
|
const touchesProfile = data.city_id !== void 0 || data.gram_level !== void 0 || data.phone !== void 0 || data.ncode !== void 0;
|
||||||
if (effectiveRole === "SALES_EXPERT" && (touchesProfile || patch.role !== void 0)) {
|
if (touchesProfile) {
|
||||||
const profile = this.salesExpertProfileData(data);
|
const profile = this.salesExpertProfileData(data);
|
||||||
patch.salesExpertProfile = {
|
patch.salesExpertProfile = {
|
||||||
upsert: {
|
upsert: {
|
||||||
@@ -101521,7 +101623,7 @@ var require_admin_users_service = __commonJS({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const updated = await this.prisma.adminUser.update({
|
return this.prisma.adminUser.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: patch,
|
data: patch,
|
||||||
include: {
|
include: {
|
||||||
@@ -101531,10 +101633,6 @@ var require_admin_users_service = __commonJS({
|
|||||||
salesExpertProfile: { include: { city: true } }
|
salesExpertProfile: { include: { city: true } }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (patch.isActive === false || patch.passwordHash !== void 0) {
|
|
||||||
await this.sessions.revokeAllForAdmin(id, patch.isActive === false ? "account_disabled" : "password_changed");
|
|
||||||
}
|
|
||||||
return updated;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Legacy `DELETE users/{id}`.
|
* Legacy `DELETE users/{id}`.
|
||||||
@@ -101544,21 +101642,28 @@ var require_admin_users_service = __commonJS({
|
|||||||
* audit trail. Deactivation is offered instead. Legacy deleted regardless.
|
* audit trail. Deactivation is offered instead. Legacy deleted regardless.
|
||||||
*/
|
*/
|
||||||
async remove(id, actorId) {
|
async remove(id, actorId) {
|
||||||
const u = await this.prisma.adminUser.findUnique({
|
const admin = await this.prisma.admin.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: {
|
include: {
|
||||||
_count: { select: { postedLedgerEntries: true, orderStatusEvents: true } }
|
_count: { select: { postedLedgerEntries: true, orderStatusEvents: true } }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!u || (0, super_admin_1.isSuperAdmin)(u.mobile))
|
if (admin) {
|
||||||
|
if ((0, super_admin_1.isSuperAdmin)(admin.mobile))
|
||||||
|
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
|
if (id === actorId) {
|
||||||
|
throw new common_12.BadRequestException("\u0646\u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u06CC\u062F \u062D\u0633\u0627\u0628 \u06A9\u0627\u0631\u0628\u0631\u06CC \u062E\u0648\u062F\u062A\u0627\u0646 \u0631\u0627 \u062D\u0630\u0641 \u06A9\u0646\u06CC\u062F");
|
||||||
|
}
|
||||||
|
const refs = admin._count.postedLedgerEntries + admin._count.orderStatusEvents;
|
||||||
|
if (refs > 0) {
|
||||||
|
throw new common_12.BadRequestException(`\u062D\u0630\u0641 \u0627\u06CC\u0646 \u06A9\u0627\u0631\u0628\u0631 \u0645\u0645\u06A9\u0646 \u0646\u06CC\u0633\u062A\u060C \u0686\u0648\u0646 ${refs} \u0633\u0646\u062F \u06CC\u0627 \u062A\u063A\u06CC\u06CC\u0631 \u0648\u0636\u0639\u06CC\u062A \u0628\u0647 \u0646\u0627\u0645 \u0627\u0648 \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A. \u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u06CC\u062F \u063A\u06CC\u0631\u0641\u0639\u0627\u0644\u0634 \u06A9\u0646\u06CC\u062F.`);
|
||||||
|
}
|
||||||
|
await this.prisma.admin.delete({ where: { id } });
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
const expert = await this.findExpert(id);
|
||||||
|
if (!expert)
|
||||||
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
if (id === actorId) {
|
|
||||||
throw new common_12.BadRequestException("\u0646\u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u06CC\u062F \u062D\u0633\u0627\u0628 \u06A9\u0627\u0631\u0628\u0631\u06CC \u062E\u0648\u062F\u062A\u0627\u0646 \u0631\u0627 \u062D\u0630\u0641 \u06A9\u0646\u06CC\u062F");
|
|
||||||
}
|
|
||||||
const refs = u._count.postedLedgerEntries + u._count.orderStatusEvents;
|
|
||||||
if (refs > 0) {
|
|
||||||
throw new common_12.BadRequestException(`\u062D\u0630\u0641 \u0627\u06CC\u0646 \u06A9\u0627\u0631\u0628\u0631 \u0645\u0645\u06A9\u0646 \u0646\u06CC\u0633\u062A\u060C \u0686\u0648\u0646 ${refs} \u0633\u0646\u062F \u06CC\u0627 \u062A\u063A\u06CC\u06CC\u0631 \u0648\u0636\u0639\u06CC\u062A \u0628\u0647 \u0646\u0627\u0645 \u0627\u0648 \u062B\u0628\u062A \u0634\u062F\u0647 \u0627\u0633\u062A. \u0645\u06CC\u200C\u062A\u0648\u0627\u0646\u06CC\u062F \u063A\u06CC\u0631\u0641\u0639\u0627\u0644\u0634 \u06A9\u0646\u06CC\u062F.`);
|
|
||||||
}
|
|
||||||
await this.prisma.adminUser.delete({ where: { id } });
|
await this.prisma.adminUser.delete({ where: { id } });
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
@@ -101567,12 +101672,12 @@ var require_admin_users_service = __commonJS({
|
|||||||
async listUserTypes() {
|
async listUserTypes() {
|
||||||
const rows = await this.prisma.userType.findMany({
|
const rows = await this.prisma.userType.findMany({
|
||||||
orderBy: { id: "asc" },
|
orderBy: { id: "asc" },
|
||||||
include: { _count: { select: { adminUsers: true, permissions: true } } }
|
include: { _count: { select: { admins: true, adminUsers: true, permissions: true } } }
|
||||||
});
|
});
|
||||||
return rows.map((t) => ({
|
return rows.map((t) => ({
|
||||||
id: t.id,
|
id: t.id,
|
||||||
title: t.title,
|
title: t.title,
|
||||||
users_count: t._count.adminUsers,
|
users_count: t._count.admins + t._count.adminUsers,
|
||||||
permissions_count: t._count.permissions
|
permissions_count: t._count.permissions
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -101645,14 +101750,19 @@ var require_admin_users_service = __commonJS({
|
|||||||
}
|
}
|
||||||
/** Legacy `GET users/show` equivalent for the "اطلاعات من" screen. */
|
/** Legacy `GET users/show` equivalent for the "اطلاعات من" screen. */
|
||||||
async myData(adminId) {
|
async myData(adminId) {
|
||||||
return this.findOne(adminId);
|
const admin = await this.findAdmin(adminId);
|
||||||
|
if (!admin)
|
||||||
|
throw new common_12.NotFoundException("\u06A9\u0627\u0631\u0628\u0631 \u06CC\u0627\u0641\u062A \u0646\u0634\u062F");
|
||||||
|
return this.toRow(admin);
|
||||||
}
|
}
|
||||||
/** The AdminRole picker for the user form. */
|
/**
|
||||||
|
* The AdminRole picker for the staff form. SALES_EXPERT is not offered: an
|
||||||
|
* expert is a different table and is created from the reagent form.
|
||||||
|
*/
|
||||||
roleOptions() {
|
roleOptions() {
|
||||||
return [
|
return [
|
||||||
{ value: client_1.AdminRole.SUPER_ADMIN, title: "\u0645\u062F\u06CC\u0631 \u0627\u0631\u0634\u062F" },
|
{ value: client_1.AdminRole.SUPER_ADMIN, title: "\u0645\u062F\u06CC\u0631 \u0627\u0631\u0634\u062F" },
|
||||||
{ value: client_1.AdminRole.SYSTEM_APPROVER, title: "\u062A\u0627\u06CC\u06CC\u062F \u0645\u062F\u06CC\u0631 \u0633\u06CC\u0633\u062A\u0645" },
|
{ value: client_1.AdminRole.SYSTEM_APPROVER, title: "\u062A\u0627\u06CC\u06CC\u062F \u0645\u062F\u06CC\u0631 \u0633\u06CC\u0633\u062A\u0645" },
|
||||||
{ value: client_1.AdminRole.SALES_EXPERT, title: "\u06A9\u0627\u0631\u0634\u0646\u0627\u0633 \u0641\u0631\u0648\u0634" },
|
|
||||||
{ value: client_1.AdminRole.WORKSHOP_SUPERVISOR, title: "\u0633\u0631\u067E\u0631\u0633\u062A \u06A9\u0627\u0631\u06AF\u0627\u0647" },
|
{ value: client_1.AdminRole.WORKSHOP_SUPERVISOR, title: "\u0633\u0631\u067E\u0631\u0633\u062A \u06A9\u0627\u0631\u06AF\u0627\u0647" },
|
||||||
{ value: client_1.AdminRole.ACCOUNTANT, title: "\u062D\u0633\u0627\u0628\u062F\u0627\u0631" }
|
{ value: client_1.AdminRole.ACCOUNTANT, title: "\u062D\u0633\u0627\u0628\u062F\u0627\u0631" }
|
||||||
];
|
];
|
||||||
@@ -101915,7 +102025,7 @@ var require_admin_invoices_service = __commonJS({
|
|||||||
"createdAt",
|
"createdAt",
|
||||||
"branchId",
|
"branchId",
|
||||||
"branch.name",
|
"branch.name",
|
||||||
"allocatorAdminUser.fullName",
|
"allocatorAdmin.fullName",
|
||||||
// legacy filter keys
|
// legacy filter keys
|
||||||
"name",
|
"name",
|
||||||
"caj"
|
"caj"
|
||||||
@@ -101923,7 +102033,7 @@ var require_admin_invoices_service = __commonJS({
|
|||||||
static FIELD_ALIASES = {
|
static FIELD_ALIASES = {
|
||||||
name: "branch.name",
|
name: "branch.name",
|
||||||
caj: "createdAt",
|
caj: "createdAt",
|
||||||
"allocator_user.name": "allocatorAdminUser.fullName"
|
"allocator_user.name": "allocatorAdmin.fullName"
|
||||||
};
|
};
|
||||||
/** Legacy `POST branch/product/table`. */
|
/** Legacy `POST branch/product/table`. */
|
||||||
async list(query) {
|
async list(query) {
|
||||||
@@ -101947,7 +102057,7 @@ var require_admin_invoices_service = __commonJS({
|
|||||||
take,
|
take,
|
||||||
include: {
|
include: {
|
||||||
branch: true,
|
branch: true,
|
||||||
allocatorAdminUser: true,
|
allocatorAdmin: true,
|
||||||
items: { include: { product: true } }
|
items: { include: { product: true } }
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -101966,7 +102076,7 @@ var require_admin_invoices_service = __commonJS({
|
|||||||
sum_weight: sumWeight,
|
sum_weight: sumWeight,
|
||||||
count_item: a.items.length,
|
count_item: a.items.length,
|
||||||
note: a.note,
|
note: a.note,
|
||||||
allocator_user: a.allocatorAdminUser ? { id: a.allocatorAdminUser.id, name: a.allocatorAdminUser.fullName } : null,
|
allocator_user: a.allocatorAdmin ? { id: a.allocatorAdmin.id, name: a.allocatorAdmin.fullName } : null,
|
||||||
caj: a.createdAt,
|
caj: a.createdAt,
|
||||||
products: a.items.map((it) => ({
|
products: a.items.map((it) => ({
|
||||||
id: it.productId,
|
id: it.productId,
|
||||||
@@ -101983,7 +102093,7 @@ var require_admin_invoices_service = __commonJS({
|
|||||||
where: { id },
|
where: { id },
|
||||||
include: {
|
include: {
|
||||||
branch: true,
|
branch: true,
|
||||||
allocatorAdminUser: true,
|
allocatorAdmin: true,
|
||||||
items: { include: { product: true } }
|
items: { include: { product: true } }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -102017,7 +102127,7 @@ var require_admin_invoices_service = __commonJS({
|
|||||||
const allocation = await this.prisma.branchAllocation.create({
|
const allocation = await this.prisma.branchAllocation.create({
|
||||||
data: {
|
data: {
|
||||||
branchId,
|
branchId,
|
||||||
allocatorAdminUserId: actorId,
|
allocatorAdminId: actorId,
|
||||||
note: data.note ?? null,
|
note: data.note ?? null,
|
||||||
items: {
|
items: {
|
||||||
create: products.map((p) => ({
|
create: products.map((p) => ({
|
||||||
@@ -102446,7 +102556,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
orderBy,
|
orderBy,
|
||||||
skip,
|
skip,
|
||||||
take,
|
take,
|
||||||
include: { sentByAdminUser: true }
|
include: { sentByAdmin: true }
|
||||||
}),
|
}),
|
||||||
this.prisma.smsMessage.count({ where })
|
this.prisma.smsMessage.count({ where })
|
||||||
]);
|
]);
|
||||||
@@ -102456,7 +102566,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
text: s.body,
|
text: s.body,
|
||||||
mobile: s.recipients,
|
mobile: s.recipients,
|
||||||
sent_count: s.sentCount,
|
sent_count: s.sentCount,
|
||||||
sender: s.sentByAdminUser?.fullName ?? null,
|
sender: s.sentByAdmin?.fullName ?? null,
|
||||||
caj: s.createdAt
|
caj: s.createdAt
|
||||||
})), total, query, page, limit);
|
})), total, query, page, limit);
|
||||||
}
|
}
|
||||||
@@ -102483,7 +102593,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
recipients: recipients.join(","),
|
recipients: recipients.join(","),
|
||||||
body,
|
body,
|
||||||
sentCount: recipients.length,
|
sentCount: recipients.length,
|
||||||
sentByAdminUserId: actorId
|
sentByAdminId: actorId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -102566,7 +102676,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
audience,
|
audience,
|
||||||
sentByAdminUserId: actorId,
|
sentByAdminId: actorId,
|
||||||
recipients: { create: customers.map((c) => ({ customerId: c.id })) }
|
recipients: { create: customers.map((c) => ({ customerId: c.id })) }
|
||||||
},
|
},
|
||||||
include: { _count: { select: { recipients: true } } }
|
include: { _count: { select: { recipients: true } } }
|
||||||
@@ -102589,7 +102699,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
audience: client_1.MessageAudience.SINGLE_NUMBER,
|
audience: client_1.MessageAudience.SINGLE_NUMBER,
|
||||||
sentByAdminUserId: actorId,
|
sentByAdminId: actorId,
|
||||||
recipients: { create: [{ customerId }] }
|
recipients: { create: [{ customerId }] }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -102599,7 +102709,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
const n = await this.prisma.notification.findUnique({
|
const n = await this.prisma.notification.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: {
|
include: {
|
||||||
sentByAdminUser: true,
|
sentByAdmin: true,
|
||||||
recipients: { include: { customer: true } }
|
recipients: { include: { customer: true } }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -102611,7 +102721,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
body: n.body,
|
body: n.body,
|
||||||
text: n.body,
|
text: n.body,
|
||||||
audience: n.audience,
|
audience: n.audience,
|
||||||
sender: n.sentByAdminUser?.fullName ?? null,
|
sender: n.sentByAdmin?.fullName ?? null,
|
||||||
caj: n.createdAt,
|
caj: n.createdAt,
|
||||||
recipients: n.recipients.map((r) => ({
|
recipients: n.recipients.map((r) => ({
|
||||||
id: r.id,
|
id: r.id,
|
||||||
@@ -102638,7 +102748,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
skip,
|
skip,
|
||||||
take,
|
take,
|
||||||
include: {
|
include: {
|
||||||
sentByAdminUser: true,
|
sentByAdmin: true,
|
||||||
_count: { select: { recipients: true } }
|
_count: { select: { recipients: true } }
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -102649,7 +102759,7 @@ var require_admin_misc_service = __commonJS({
|
|||||||
title: n.title,
|
title: n.title,
|
||||||
text: n.body,
|
text: n.body,
|
||||||
audience: n.audience,
|
audience: n.audience,
|
||||||
sender: n.sentByAdminUser?.fullName ?? null,
|
sender: n.sentByAdmin?.fullName ?? null,
|
||||||
total_count: n._count.recipients,
|
total_count: n._count.recipients,
|
||||||
caj: n.createdAt
|
caj: n.createdAt
|
||||||
})), total, query, page, limit);
|
})), total, query, page, limit);
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
-- Split panel staff out of "AdminUser" into their own table, "Admin".
|
||||||
|
--
|
||||||
|
-- WHY
|
||||||
|
-- ---
|
||||||
|
-- "AdminUser" held two unrelated kinds of person: panel staff and sales
|
||||||
|
-- experts (role SALES_EXPERT). Because the mobile is unique per table, one
|
||||||
|
-- number could not be both an admin and an expert. Staff belong to the ADMIN
|
||||||
|
-- PANEL only; experts belong to the apps. They are now separate tables, each
|
||||||
|
-- unique on its own.
|
||||||
|
--
|
||||||
|
-- SAFETY — READ BEFORE RUNNING ON PRODUCTION
|
||||||
|
-- ------------------------------------------
|
||||||
|
-- This migration NEVER modifies, deletes or locks "AdminUser" (the sales-expert
|
||||||
|
-- table; ~12,000 rows in production). It only
|
||||||
|
-- 1. creates "Admin" and its indexes/foreign keys,
|
||||||
|
-- 2. COPIES the non-expert rows from "AdminUser" into it, keeping their ids,
|
||||||
|
-- 3. re-points the "who did this" foreign keys from "AdminUser" to "Admin".
|
||||||
|
--
|
||||||
|
-- * Ids are preserved, so every issued panel token (its `sub` is the id) and
|
||||||
|
-- every existing ledger / status / SMS / upload row keeps resolving.
|
||||||
|
-- * The old non-expert rows are LEFT in "AdminUser", dormant. The code no
|
||||||
|
-- longer reads them as admins. Remove them later, in a separate release, only
|
||||||
|
-- once the split has been verified.
|
||||||
|
-- * Re-runnable: every statement is guarded (IF NOT EXISTS / ON CONFLICT / drop
|
||||||
|
-- then add), so a half-applied run can simply be run again.
|
||||||
|
-- * No data is moved on the big tables (LedgerEntry, OrderStatusEvent, …):
|
||||||
|
-- the existing columns keep their names and values; only the constraint's
|
||||||
|
-- target changes.
|
||||||
|
--
|
||||||
|
-- The re-pointed foreign keys are added NOT VALID (checked for new/changed rows
|
||||||
|
-- immediately, existing rows not scanned under a heavy lock) and then validated
|
||||||
|
-- at the end. A row that references an id that is not an admin — only possible
|
||||||
|
-- if a sales expert once appears as an actor — leaves that one constraint
|
||||||
|
-- NOT VALID and prints a NOTICE instead of failing the deploy. See
|
||||||
|
-- docs/ADMIN-TABLE-SPLIT.md for the check query.
|
||||||
|
--
|
||||||
|
-- ROLLBACK: deploy the previous code. Nothing was deleted, so the old code
|
||||||
|
-- still finds every admin in "AdminUser". Admins created AFTER the split exist
|
||||||
|
-- only in "Admin" and would need re-creating in the old system.
|
||||||
|
|
||||||
|
-- ── 1. The table ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "Admin" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"fullName" TEXT NOT NULL,
|
||||||
|
"firstName" TEXT,
|
||||||
|
"lastName" TEXT,
|
||||||
|
"mobile" TEXT NOT NULL,
|
||||||
|
"passwordHash" TEXT NOT NULL,
|
||||||
|
"role" "AdminRole" NOT NULL,
|
||||||
|
"userTypeId" INTEGER,
|
||||||
|
"branchId" TEXT,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "Admin_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS "Admin_mobile_key" ON "Admin" ("mobile");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Admin_role_idx" ON "Admin" ("role");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Admin_userTypeId_idx" ON "Admin" ("userTypeId");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Admin_firstName_idx" ON "Admin" ("firstName");
|
||||||
|
CREATE INDEX IF NOT EXISTS "Admin_lastName_idx" ON "Admin" ("lastName");
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "Admin" ADD CONSTRAINT "Admin_userTypeId_fkey"
|
||||||
|
FOREIGN KEY ("userTypeId") REFERENCES "user_type"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "Admin" ADD CONSTRAINT "Admin_branchId_fkey"
|
||||||
|
FOREIGN KEY ("branchId") REFERENCES "Branch"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ── 2. Copy the staff (everyone who is not a sales expert), ids preserved ──
|
||||||
|
--
|
||||||
|
-- Done BEFORE the name-sync trigger exists, so the stored names are copied
|
||||||
|
-- exactly as they are rather than rebuilt.
|
||||||
|
|
||||||
|
INSERT INTO "Admin"
|
||||||
|
("id", "fullName", "firstName", "lastName", "mobile", "passwordHash",
|
||||||
|
"role", "userTypeId", "branchId", "isActive", "createdAt")
|
||||||
|
SELECT
|
||||||
|
"id", "fullName", "firstName", "lastName", "mobile", "passwordHash",
|
||||||
|
"role", "userTypeId", "branchId", "isActive", "createdAt"
|
||||||
|
FROM "AdminUser"
|
||||||
|
WHERE "role" <> 'SALES_EXPERT'
|
||||||
|
ON CONFLICT ("id") DO NOTHING;
|
||||||
|
|
||||||
|
-- Same two-way fullName <-> firstName/lastName sync as AdminUser, sharing its
|
||||||
|
-- function (created in 20260922180000_sales_expert_profile_and_admin_name_parts).
|
||||||
|
DROP TRIGGER IF EXISTS admin_sync_full_name_trg ON "Admin";
|
||||||
|
CREATE TRIGGER admin_sync_full_name_trg
|
||||||
|
BEFORE INSERT OR UPDATE ON "Admin"
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION admin_user_sync_full_name();
|
||||||
|
|
||||||
|
-- ── 3. Re-point the foreign keys that mean "an admin did this" ─────────────
|
||||||
|
--
|
||||||
|
-- Same constraint names, same column names, same ON DELETE / ON UPDATE actions;
|
||||||
|
-- only the referenced table changes. "Order"."salesAgentId",
|
||||||
|
-- "Customer"."referredByAgentId" and "SalesExpertProfile"."adminUserId" are
|
||||||
|
-- about SALES EXPERTS and keep pointing at "AdminUser".
|
||||||
|
|
||||||
|
ALTER TABLE "Workshop" DROP CONSTRAINT IF EXISTS "Workshop_supervisorId_fkey";
|
||||||
|
ALTER TABLE "Workshop" ADD CONSTRAINT "Workshop_supervisorId_fkey"
|
||||||
|
FOREIGN KEY ("supervisorId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
ALTER TABLE "OrderStatusEvent" DROP CONSTRAINT IF EXISTS "OrderStatusEvent_actorAdminUserId_fkey";
|
||||||
|
ALTER TABLE "OrderStatusEvent" ADD CONSTRAINT "OrderStatusEvent_actorAdminUserId_fkey"
|
||||||
|
FOREIGN KEY ("actorAdminUserId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
ALTER TABLE "PreOrderStatusEvent" DROP CONSTRAINT IF EXISTS "PreOrderStatusEvent_actorAdminUserId_fkey";
|
||||||
|
ALTER TABLE "PreOrderStatusEvent" ADD CONSTRAINT "PreOrderStatusEvent_actorAdminUserId_fkey"
|
||||||
|
FOREIGN KEY ("actorAdminUserId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
ALTER TABLE "LedgerEntry" DROP CONSTRAINT IF EXISTS "LedgerEntry_postedByAdminUserId_fkey";
|
||||||
|
ALTER TABLE "LedgerEntry" ADD CONSTRAINT "LedgerEntry_postedByAdminUserId_fkey"
|
||||||
|
FOREIGN KEY ("postedByAdminUserId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
ALTER TABLE "BranchAllocation" DROP CONSTRAINT IF EXISTS "BranchAllocation_allocatorAdminUserId_fkey";
|
||||||
|
ALTER TABLE "BranchAllocation" ADD CONSTRAINT "BranchAllocation_allocatorAdminUserId_fkey"
|
||||||
|
FOREIGN KEY ("allocatorAdminUserId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
ALTER TABLE "SmsMessage" DROP CONSTRAINT IF EXISTS "SmsMessage_sentByAdminUserId_fkey";
|
||||||
|
ALTER TABLE "SmsMessage" ADD CONSTRAINT "SmsMessage_sentByAdminUserId_fkey"
|
||||||
|
FOREIGN KEY ("sentByAdminUserId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
ALTER TABLE "Notification" DROP CONSTRAINT IF EXISTS "Notification_sentByAdminUserId_fkey";
|
||||||
|
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_sentByAdminUserId_fkey"
|
||||||
|
FOREIGN KEY ("sentByAdminUserId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
ALTER TABLE "UploadedFile" DROP CONSTRAINT IF EXISTS "UploadedFile_uploadedByAdminUserId_fkey";
|
||||||
|
ALTER TABLE "UploadedFile" ADD CONSTRAINT "UploadedFile_uploadedByAdminUserId_fkey"
|
||||||
|
FOREIGN KEY ("uploadedByAdminUserId") REFERENCES "Admin"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
-- Panel tokens are issued to staff only. CASCADE as before: deleting an admin
|
||||||
|
-- ends their sessions.
|
||||||
|
ALTER TABLE "AdminSession" DROP CONSTRAINT IF EXISTS "AdminSession_adminUserId_fkey";
|
||||||
|
ALTER TABLE "AdminSession" ADD CONSTRAINT "AdminSession_adminUserId_fkey"
|
||||||
|
FOREIGN KEY ("adminUserId") REFERENCES "Admin"("id") ON DELETE CASCADE ON UPDATE CASCADE NOT VALID;
|
||||||
|
|
||||||
|
-- ── 4. Validate; never fail the deploy over an old orphan ──────────────────
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
c record;
|
||||||
|
BEGIN
|
||||||
|
FOR c IN
|
||||||
|
SELECT * FROM (VALUES
|
||||||
|
('Workshop', 'Workshop_supervisorId_fkey'),
|
||||||
|
('OrderStatusEvent', 'OrderStatusEvent_actorAdminUserId_fkey'),
|
||||||
|
('PreOrderStatusEvent', 'PreOrderStatusEvent_actorAdminUserId_fkey'),
|
||||||
|
('LedgerEntry', 'LedgerEntry_postedByAdminUserId_fkey'),
|
||||||
|
('BranchAllocation', 'BranchAllocation_allocatorAdminUserId_fkey'),
|
||||||
|
('SmsMessage', 'SmsMessage_sentByAdminUserId_fkey'),
|
||||||
|
('Notification', 'Notification_sentByAdminUserId_fkey'),
|
||||||
|
('UploadedFile', 'UploadedFile_uploadedByAdminUserId_fkey'),
|
||||||
|
('AdminSession', 'AdminSession_adminUserId_fkey')
|
||||||
|
) AS t(tbl, con)
|
||||||
|
LOOP
|
||||||
|
BEGIN
|
||||||
|
EXECUTE format('ALTER TABLE %I VALIDATE CONSTRAINT %I', c.tbl, c.con);
|
||||||
|
EXCEPTION WHEN foreign_key_violation THEN
|
||||||
|
RAISE NOTICE '% on % left NOT VALID: some existing rows reference an id that is not in "Admin" (see docs/ADMIN-TABLE-SPLIT.md)', c.con, c.tbl;
|
||||||
|
END;
|
||||||
|
END LOOP;
|
||||||
|
END $$;
|
||||||
@@ -1846,7 +1846,7 @@ async function main() {
|
|||||||
update: {},
|
update: {},
|
||||||
create: { id: "workshop-central", name: "\u06A9\u0627\u0631\u06AF\u0627\u0647 \u0645\u0631\u06A9\u0632\u06CC \u0633\u0627\u062E\u062A \u0637\u0644\u0627" }
|
create: { id: "workshop-central", name: "\u06A9\u0627\u0631\u06AF\u0627\u0647 \u0645\u0631\u06A9\u0632\u06CC \u0633\u0627\u062E\u062A \u0637\u0644\u0627" }
|
||||||
});
|
});
|
||||||
await prisma.adminUser.upsert({
|
await prisma.admin.upsert({
|
||||||
where: { mobile: DEV_TEST_PHONE },
|
where: { mobile: DEV_TEST_PHONE },
|
||||||
update: { passwordHash: DEV_TEST_PHONE_PASSWORD_HASH, userTypeId: 85, branchId: null },
|
update: { passwordHash: DEV_TEST_PHONE_PASSWORD_HASH, userTypeId: 85, branchId: null },
|
||||||
create: {
|
create: {
|
||||||
@@ -1858,7 +1858,7 @@ async function main() {
|
|||||||
branchId: null
|
branchId: null
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await prisma.adminUser.upsert({
|
await prisma.admin.upsert({
|
||||||
where: { mobile: "09120000001" },
|
where: { mobile: "09120000001" },
|
||||||
update: { passwordHash: PLACEHOLDER_PASSWORD_HASH, userTypeId: 85, branchId: null },
|
update: { passwordHash: PLACEHOLDER_PASSWORD_HASH, userTypeId: 85, branchId: null },
|
||||||
create: {
|
create: {
|
||||||
@@ -1899,7 +1899,7 @@ async function main() {
|
|||||||
wagePercent: 1.2
|
wagePercent: 1.2
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await prisma.adminUser.upsert({
|
await prisma.admin.upsert({
|
||||||
where: { mobile: "09123334444" },
|
where: { mobile: "09123334444" },
|
||||||
update: { passwordHash: PLACEHOLDER_PASSWORD_HASH, userTypeId: 88 },
|
update: { passwordHash: PLACEHOLDER_PASSWORD_HASH, userTypeId: 88 },
|
||||||
create: {
|
create: {
|
||||||
@@ -1910,7 +1910,7 @@ async function main() {
|
|||||||
userTypeId: 88
|
userTypeId: 88
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await prisma.adminUser.upsert({
|
await prisma.admin.upsert({
|
||||||
where: { mobile: "09125556666" },
|
where: { mobile: "09125556666" },
|
||||||
update: { passwordHash: PLACEHOLDER_PASSWORD_HASH, userTypeId: 87, branchId: null },
|
update: { passwordHash: PLACEHOLDER_PASSWORD_HASH, userTypeId: 87, branchId: null },
|
||||||
create: {
|
create: {
|
||||||
|
|||||||
+26
-11
File diff suppressed because one or more lines are too long
+23
-8
@@ -130,6 +130,20 @@ exports.Prisma.CityScalarFieldEnum = {
|
|||||||
percentAux: 'percentAux'
|
percentAux: 'percentAux'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.Prisma.AdminScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
fullName: 'fullName',
|
||||||
|
firstName: 'firstName',
|
||||||
|
lastName: 'lastName',
|
||||||
|
mobile: 'mobile',
|
||||||
|
passwordHash: 'passwordHash',
|
||||||
|
role: 'role',
|
||||||
|
userTypeId: 'userTypeId',
|
||||||
|
branchId: 'branchId',
|
||||||
|
isActive: 'isActive',
|
||||||
|
createdAt: 'createdAt'
|
||||||
|
};
|
||||||
|
|
||||||
exports.Prisma.AdminUserScalarFieldEnum = {
|
exports.Prisma.AdminUserScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
fullName: 'fullName',
|
fullName: 'fullName',
|
||||||
@@ -170,7 +184,7 @@ exports.Prisma.AppSessionScalarFieldEnum = {
|
|||||||
exports.Prisma.AdminSessionScalarFieldEnum = {
|
exports.Prisma.AdminSessionScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
jti: 'jti',
|
jti: 'jti',
|
||||||
adminUserId: 'adminUserId',
|
adminId: 'adminId',
|
||||||
iamSessionId: 'iamSessionId',
|
iamSessionId: 'iamSessionId',
|
||||||
iamSub: 'iamSub',
|
iamSub: 'iamSub',
|
||||||
ipAddress: 'ipAddress',
|
ipAddress: 'ipAddress',
|
||||||
@@ -370,7 +384,7 @@ exports.Prisma.OrderStatusEventScalarFieldEnum = {
|
|||||||
fromStatus: 'fromStatus',
|
fromStatus: 'fromStatus',
|
||||||
toStatus: 'toStatus',
|
toStatus: 'toStatus',
|
||||||
note: 'note',
|
note: 'note',
|
||||||
actorAdminUserId: 'actorAdminUserId',
|
actorAdminId: 'actorAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -397,7 +411,7 @@ exports.Prisma.PreOrderStatusEventScalarFieldEnum = {
|
|||||||
id: 'id',
|
id: 'id',
|
||||||
preOrderId: 'preOrderId',
|
preOrderId: 'preOrderId',
|
||||||
changes: 'changes',
|
changes: 'changes',
|
||||||
actorAdminUserId: 'actorAdminUserId',
|
actorAdminId: 'actorAdminId',
|
||||||
ipAddress: 'ipAddress',
|
ipAddress: 'ipAddress',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
@@ -414,7 +428,7 @@ exports.Prisma.LedgerEntryScalarFieldEnum = {
|
|||||||
orderId: 'orderId',
|
orderId: 'orderId',
|
||||||
confirmed: 'confirmed',
|
confirmed: 'confirmed',
|
||||||
confirmedAt: 'confirmedAt',
|
confirmedAt: 'confirmedAt',
|
||||||
postedByAdminUserId: 'postedByAdminUserId',
|
postedByAdminId: 'postedByAdminId',
|
||||||
labName: 'labName',
|
labName: 'labName',
|
||||||
hallmark: 'hallmark',
|
hallmark: 'hallmark',
|
||||||
accountName: 'accountName',
|
accountName: 'accountName',
|
||||||
@@ -549,7 +563,7 @@ exports.Prisma.PromotionExclusionScalarFieldEnum = {
|
|||||||
exports.Prisma.BranchAllocationScalarFieldEnum = {
|
exports.Prisma.BranchAllocationScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
branchId: 'branchId',
|
branchId: 'branchId',
|
||||||
allocatorAdminUserId: 'allocatorAdminUserId',
|
allocatorAdminId: 'allocatorAdminId',
|
||||||
note: 'note',
|
note: 'note',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
@@ -567,7 +581,7 @@ exports.Prisma.SmsMessageScalarFieldEnum = {
|
|||||||
recipients: 'recipients',
|
recipients: 'recipients',
|
||||||
body: 'body',
|
body: 'body',
|
||||||
sentCount: 'sentCount',
|
sentCount: 'sentCount',
|
||||||
sentByAdminUserId: 'sentByAdminUserId',
|
sentByAdminId: 'sentByAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -576,7 +590,7 @@ exports.Prisma.NotificationScalarFieldEnum = {
|
|||||||
title: 'title',
|
title: 'title',
|
||||||
body: 'body',
|
body: 'body',
|
||||||
audience: 'audience',
|
audience: 'audience',
|
||||||
sentByAdminUserId: 'sentByAdminUserId',
|
sentByAdminId: 'sentByAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -609,7 +623,7 @@ exports.Prisma.UploadedFileScalarFieldEnum = {
|
|||||||
mimeType: 'mimeType',
|
mimeType: 'mimeType',
|
||||||
sizeBytes: 'sizeBytes',
|
sizeBytes: 'sizeBytes',
|
||||||
purpose: 'purpose',
|
purpose: 'purpose',
|
||||||
uploadedByAdminUserId: 'uploadedByAdminUserId',
|
uploadedByAdminId: 'uploadedByAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -782,6 +796,7 @@ exports.MessageAudience = exports.$Enums.MessageAudience = {
|
|||||||
|
|
||||||
exports.Prisma.ModelName = {
|
exports.Prisma.ModelName = {
|
||||||
City: 'City',
|
City: 'City',
|
||||||
|
Admin: 'Admin',
|
||||||
AdminUser: 'AdminUser',
|
AdminUser: 'AdminUser',
|
||||||
AppSession: 'AppSession',
|
AppSession: 'AppSession',
|
||||||
AdminSession: 'AdminSession',
|
AdminSession: 'AdminSession',
|
||||||
|
|||||||
+4830
-3253
File diff suppressed because it is too large
Load Diff
+26
-11
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "prisma-client-8945b25ad1891f9eaff8d38ca7f3c1b19a3f2d11c0c8521549c219186ff7566b",
|
"name": "prisma-client-d98bddf3599c950eb3fcd511efa7f226c4ef4c5c3cbc261683fb1118a763a924",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
"browser": "index-browser.js",
|
"browser": "index-browser.js",
|
||||||
|
|||||||
+92
-48
@@ -140,6 +140,61 @@ model City {
|
|||||||
@@index([parentId])
|
@@index([parentId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A staff account for the ADMIN PANEL — and nothing else.
|
||||||
|
///
|
||||||
|
/// Split out of AdminUser (which now holds sales experts only) so that:
|
||||||
|
/// * the same mobile can be an admin AND a sales expert — they are two
|
||||||
|
/// separate roles, each unique only within its own table;
|
||||||
|
/// * the expert table (production: ~12,000 rows) is never written by panel
|
||||||
|
/// staff management.
|
||||||
|
/// Migration 20261026120000_split_admin_table copies the existing non-expert
|
||||||
|
/// AdminUser rows here WITH THEIR IDS, so issued panel tokens and every
|
||||||
|
/// "who did this" column keep resolving. The old rows are left in AdminUser,
|
||||||
|
/// dormant; no code reads them as admins any more.
|
||||||
|
///
|
||||||
|
/// ⚠️ fullName/firstName/lastName are kept in sync by the DB trigger
|
||||||
|
/// `admin_sync_full_name_trg` (it shares the function admin_user_sync_full_name) — same semantics as AdminUser: the parts win.
|
||||||
|
model Admin {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
|
||||||
|
fullName String
|
||||||
|
firstName String?
|
||||||
|
lastName String?
|
||||||
|
|
||||||
|
mobile String @unique
|
||||||
|
passwordHash String
|
||||||
|
/// Never SALES_EXPERT — that role lives in AdminUser. Enforced in
|
||||||
|
/// AdminUsersService, not in the database, so the enum stays shared.
|
||||||
|
role AdminRole
|
||||||
|
userTypeId Int?
|
||||||
|
userType UserType? @relation(fields: [userTypeId], references: [id])
|
||||||
|
branchId String?
|
||||||
|
branch Branch? @relation(fields: [branchId], references: [id])
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
supervisedWorkshops Workshop[] @relation("WorkshopSupervisor")
|
||||||
|
branchAllocations BranchAllocation[] @relation("AllocationAuthor")
|
||||||
|
postedLedgerEntries LedgerEntry[]
|
||||||
|
orderStatusEvents OrderStatusEvent[]
|
||||||
|
preOrderStatusEvents PreOrderStatusEvent[]
|
||||||
|
sentSmsMessages SmsMessage[] @relation("SmsAuthor")
|
||||||
|
sentNotifications Notification[] @relation("NotificationAuthor")
|
||||||
|
uploadedFiles UploadedFile[] @relation("FileUploader")
|
||||||
|
|
||||||
|
/// Issued admin-panel tokens, recorded only while session tracking is on
|
||||||
|
/// (AUTH_SESSION_TRACKING, or implicitly whenever the IAM is enabled) so a
|
||||||
|
/// token can be revoked before it expires. See AdminSession.
|
||||||
|
sessions AdminSession[]
|
||||||
|
|
||||||
|
@@index([role])
|
||||||
|
@@index([userTypeId])
|
||||||
|
@@index([firstName])
|
||||||
|
@@index([lastName])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A SALES EXPERT (role SALES_EXPERT). Panel staff live in `Admin`; rows here with
|
||||||
|
/// any other role are pre-split leftovers and are ignored by the code.
|
||||||
model AdminUser {
|
model AdminUser {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
|
|
||||||
@@ -185,26 +240,13 @@ model AdminUser {
|
|||||||
pasazhName String? // legacy pasazh_name
|
pasazhName String? // legacy pasazh_name
|
||||||
memberCode String? // legacy mcode
|
memberCode String? // legacy mcode
|
||||||
|
|
||||||
referredCustomers Customer[] @relation("ReferralAgent")
|
referredCustomers Customer[] @relation("ReferralAgent")
|
||||||
/// Orders credited to this sales expert — see Order.salesAgentId.
|
/// Orders credited to this sales expert — see Order.salesAgentId.
|
||||||
creditedOrders Order[] @relation("OrderSalesAgent")
|
creditedOrders Order[] @relation("OrderSalesAgent")
|
||||||
supervisedWorkshops Workshop[] @relation("WorkshopSupervisor")
|
|
||||||
branchAllocations BranchAllocation[] @relation("AllocationAuthor")
|
|
||||||
postedLedgerEntries LedgerEntry[]
|
|
||||||
orderStatusEvents OrderStatusEvent[]
|
|
||||||
preOrderStatusEvents PreOrderStatusEvent[]
|
|
||||||
sentSmsMessages SmsMessage[] @relation("SmsAuthor")
|
|
||||||
sentNotifications Notification[] @relation("NotificationAuthor")
|
|
||||||
uploadedFiles UploadedFile[] @relation("FileUploader")
|
|
||||||
|
|
||||||
/// Only ever set for role = SALES_EXPERT (D17).
|
/// Only ever set for role = SALES_EXPERT (D17).
|
||||||
salesExpertProfile SalesExpertProfile?
|
salesExpertProfile SalesExpertProfile?
|
||||||
|
|
||||||
/// Issued admin-panel tokens, recorded only while session tracking is on
|
|
||||||
/// (AUTH_SESSION_TRACKING, or implicitly whenever the IAM is enabled) so a
|
|
||||||
/// token can be revoked before it expires. See AdminSession.
|
|
||||||
sessions AdminSession[]
|
|
||||||
|
|
||||||
@@index([role])
|
@@index([role])
|
||||||
@@index([userTypeId])
|
@@index([userTypeId])
|
||||||
@@index([avatarFileId])
|
@@index([avatarFileId])
|
||||||
@@ -274,8 +316,8 @@ model AdminSession {
|
|||||||
/// The token's `jti` claim. Unique: one row per issued token.
|
/// The token's `jti` claim. Unique: one row per issued token.
|
||||||
jti String @unique
|
jti String @unique
|
||||||
|
|
||||||
adminUserId String
|
adminId String @map("adminUserId")
|
||||||
adminUser AdminUser @relation(fields: [adminUserId], references: [id], onDelete: Cascade)
|
admin Admin @relation(fields: [adminId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
/// Set when the login was verified by the IAM: its session id (`sid`) and the
|
/// Set when the login was verified by the IAM: its session id (`sid`) and the
|
||||||
/// stable user id (`sub`) there. Back-channel logout arrives keyed by these —
|
/// stable user id (`sub`) there. Back-channel logout arrives keyed by these —
|
||||||
@@ -294,7 +336,7 @@ model AdminSession {
|
|||||||
revokedAt DateTime?
|
revokedAt DateTime?
|
||||||
revokedReason String?
|
revokedReason String?
|
||||||
|
|
||||||
@@index([adminUserId])
|
@@index([adminId], map: "AdminSession_adminUserId_idx")
|
||||||
@@index([iamSessionId])
|
@@index([iamSessionId])
|
||||||
@@index([iamSub])
|
@@index([iamSub])
|
||||||
@@index([expiresAt])
|
@@index([expiresAt])
|
||||||
@@ -571,6 +613,7 @@ model Branch {
|
|||||||
cityId String?
|
cityId String?
|
||||||
city City? @relation(fields: [cityId], references: [id])
|
city City? @relation(fields: [cityId], references: [id])
|
||||||
adminUsers AdminUser[]
|
adminUsers AdminUser[]
|
||||||
|
admins Admin[]
|
||||||
orders Order[]
|
orders Order[]
|
||||||
allocations BranchAllocation[]
|
allocations BranchAllocation[]
|
||||||
}
|
}
|
||||||
@@ -583,7 +626,7 @@ model Workshop {
|
|||||||
phone String?
|
phone String?
|
||||||
/// The supervisor created alongside the workshop by the legacy Add form.
|
/// The supervisor created alongside the workshop by the legacy Add form.
|
||||||
supervisorId String?
|
supervisorId String?
|
||||||
supervisor AdminUser? @relation("WorkshopSupervisor", fields: [supervisorId], references: [id])
|
supervisor Admin? @relation("WorkshopSupervisor", fields: [supervisorId], references: [id])
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
preOrders PreOrder[]
|
preOrders PreOrder[]
|
||||||
}
|
}
|
||||||
@@ -826,15 +869,15 @@ model OrderItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model OrderStatusEvent {
|
model OrderStatusEvent {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
orderId String
|
orderId String
|
||||||
order Order @relation(fields: [orderId], references: [id])
|
order Order @relation(fields: [orderId], references: [id])
|
||||||
fromStatus OrderStatus?
|
fromStatus OrderStatus?
|
||||||
toStatus OrderStatus
|
toStatus OrderStatus
|
||||||
note String?
|
note String?
|
||||||
actorAdminUserId String?
|
actorAdminId String? @map("actorAdminUserId")
|
||||||
actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])
|
actorAdmin Admin? @relation(fields: [actorAdminId], references: [id])
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
@@index([orderId])
|
@@index([orderId])
|
||||||
}
|
}
|
||||||
@@ -875,15 +918,15 @@ model PreOrder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model PreOrderStatusEvent {
|
model PreOrderStatusEvent {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
preOrderId String
|
preOrderId String
|
||||||
preOrder PreOrder @relation(fields: [preOrderId], references: [id])
|
preOrder PreOrder @relation(fields: [preOrderId], references: [id])
|
||||||
/// Field-level diff, e.g. {"status": {"old": "PLACED", "new": "ADMIN_APPROVED"}}
|
/// Field-level diff, e.g. {"status": {"old": "PLACED", "new": "ADMIN_APPROVED"}}
|
||||||
changes Json
|
changes Json
|
||||||
actorAdminUserId String?
|
actorAdminId String? @map("actorAdminUserId")
|
||||||
actorAdminUser AdminUser? @relation(fields: [actorAdminUserId], references: [id])
|
actorAdmin Admin? @relation(fields: [actorAdminId], references: [id])
|
||||||
ipAddress String?
|
ipAddress String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
@@index([preOrderId])
|
@@index([preOrderId])
|
||||||
}
|
}
|
||||||
@@ -912,10 +955,10 @@ model LedgerEntry {
|
|||||||
order Order? @relation(fields: [orderId], references: [id])
|
order Order? @relation(fields: [orderId], references: [id])
|
||||||
|
|
||||||
/// Pending entries don't count toward the cached customer balance until confirmed.
|
/// Pending entries don't count toward the cached customer balance until confirmed.
|
||||||
confirmed Boolean @default(false)
|
confirmed Boolean @default(false)
|
||||||
confirmedAt DateTime?
|
confirmedAt DateTime?
|
||||||
postedByAdminUserId String?
|
postedByAdminId String? @map("postedByAdminUserId")
|
||||||
postedByAdminUser AdminUser? @relation(fields: [postedByAdminUserId], references: [id])
|
postedByAdmin Admin? @relation(fields: [postedByAdminId], references: [id])
|
||||||
|
|
||||||
/// Instrument metadata from the legacy "cash" document form. Which fields are
|
/// Instrument metadata from the legacy "cash" document form. Which fields are
|
||||||
/// filled depends on sourceType: a melted-gold receipt carries lab/hallmark and
|
/// filled depends on sourceType: a melted-gold receipt carries lab/hallmark and
|
||||||
@@ -997,6 +1040,7 @@ model UserType {
|
|||||||
|
|
||||||
permissions Permission[]
|
permissions Permission[]
|
||||||
adminUsers AdminUser[]
|
adminUsers AdminUser[]
|
||||||
|
admins Admin[]
|
||||||
|
|
||||||
@@map("user_type")
|
@@map("user_type")
|
||||||
}
|
}
|
||||||
@@ -1251,8 +1295,8 @@ model BranchAllocation {
|
|||||||
branch Branch @relation(fields: [branchId], references: [id])
|
branch Branch @relation(fields: [branchId], references: [id])
|
||||||
|
|
||||||
/// Legacy `allocator_user` — the admin who filed the allocation.
|
/// Legacy `allocator_user` — the admin who filed the allocation.
|
||||||
allocatorAdminUserId String?
|
allocatorAdminId String? @map("allocatorAdminUserId")
|
||||||
allocatorAdminUser AdminUser? @relation("AllocationAuthor", fields: [allocatorAdminUserId], references: [id])
|
allocatorAdmin Admin? @relation("AllocationAuthor", fields: [allocatorAdminId], references: [id])
|
||||||
|
|
||||||
note String?
|
note String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
@@ -1299,8 +1343,8 @@ model SmsMessage {
|
|||||||
/// How many numbers the send actually resolved to.
|
/// How many numbers the send actually resolved to.
|
||||||
sentCount Int @default(0)
|
sentCount Int @default(0)
|
||||||
|
|
||||||
sentByAdminUserId String?
|
sentByAdminId String? @map("sentByAdminUserId")
|
||||||
sentByAdminUser AdminUser? @relation("SmsAuthor", fields: [sentByAdminUserId], references: [id])
|
sentByAdmin Admin? @relation("SmsAuthor", fields: [sentByAdminId], references: [id])
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
@@ -1313,8 +1357,8 @@ model Notification {
|
|||||||
body String
|
body String
|
||||||
audience MessageAudience @default(ALL_CUSTOMERS)
|
audience MessageAudience @default(ALL_CUSTOMERS)
|
||||||
|
|
||||||
sentByAdminUserId String?
|
sentByAdminId String? @map("sentByAdminUserId")
|
||||||
sentByAdminUser AdminUser? @relation("NotificationAuthor", fields: [sentByAdminUserId], references: [id])
|
sentByAdmin Admin? @relation("NotificationAuthor", fields: [sentByAdminId], references: [id])
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
@@ -1379,8 +1423,8 @@ model UploadedFile {
|
|||||||
/// 'customer.nationalId'. Kept loose so a new form needs no schema change.
|
/// 'customer.nationalId'. Kept loose so a new form needs no schema change.
|
||||||
purpose String?
|
purpose String?
|
||||||
|
|
||||||
uploadedByAdminUserId String?
|
uploadedByAdminId String? @map("uploadedByAdminUserId")
|
||||||
uploadedByAdminUser AdminUser? @relation("FileUploader", fields: [uploadedByAdminUserId], references: [id])
|
uploadedByAdmin Admin? @relation("FileUploader", fields: [uploadedByAdminId], references: [id])
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
customerAvatars Customer[] @relation("CustomerAvatar")
|
customerAvatars Customer[] @relation("CustomerAvatar")
|
||||||
|
|||||||
+23
-8
@@ -130,6 +130,20 @@ exports.Prisma.CityScalarFieldEnum = {
|
|||||||
percentAux: 'percentAux'
|
percentAux: 'percentAux'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.Prisma.AdminScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
fullName: 'fullName',
|
||||||
|
firstName: 'firstName',
|
||||||
|
lastName: 'lastName',
|
||||||
|
mobile: 'mobile',
|
||||||
|
passwordHash: 'passwordHash',
|
||||||
|
role: 'role',
|
||||||
|
userTypeId: 'userTypeId',
|
||||||
|
branchId: 'branchId',
|
||||||
|
isActive: 'isActive',
|
||||||
|
createdAt: 'createdAt'
|
||||||
|
};
|
||||||
|
|
||||||
exports.Prisma.AdminUserScalarFieldEnum = {
|
exports.Prisma.AdminUserScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
fullName: 'fullName',
|
fullName: 'fullName',
|
||||||
@@ -170,7 +184,7 @@ exports.Prisma.AppSessionScalarFieldEnum = {
|
|||||||
exports.Prisma.AdminSessionScalarFieldEnum = {
|
exports.Prisma.AdminSessionScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
jti: 'jti',
|
jti: 'jti',
|
||||||
adminUserId: 'adminUserId',
|
adminId: 'adminId',
|
||||||
iamSessionId: 'iamSessionId',
|
iamSessionId: 'iamSessionId',
|
||||||
iamSub: 'iamSub',
|
iamSub: 'iamSub',
|
||||||
ipAddress: 'ipAddress',
|
ipAddress: 'ipAddress',
|
||||||
@@ -370,7 +384,7 @@ exports.Prisma.OrderStatusEventScalarFieldEnum = {
|
|||||||
fromStatus: 'fromStatus',
|
fromStatus: 'fromStatus',
|
||||||
toStatus: 'toStatus',
|
toStatus: 'toStatus',
|
||||||
note: 'note',
|
note: 'note',
|
||||||
actorAdminUserId: 'actorAdminUserId',
|
actorAdminId: 'actorAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -397,7 +411,7 @@ exports.Prisma.PreOrderStatusEventScalarFieldEnum = {
|
|||||||
id: 'id',
|
id: 'id',
|
||||||
preOrderId: 'preOrderId',
|
preOrderId: 'preOrderId',
|
||||||
changes: 'changes',
|
changes: 'changes',
|
||||||
actorAdminUserId: 'actorAdminUserId',
|
actorAdminId: 'actorAdminId',
|
||||||
ipAddress: 'ipAddress',
|
ipAddress: 'ipAddress',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
@@ -414,7 +428,7 @@ exports.Prisma.LedgerEntryScalarFieldEnum = {
|
|||||||
orderId: 'orderId',
|
orderId: 'orderId',
|
||||||
confirmed: 'confirmed',
|
confirmed: 'confirmed',
|
||||||
confirmedAt: 'confirmedAt',
|
confirmedAt: 'confirmedAt',
|
||||||
postedByAdminUserId: 'postedByAdminUserId',
|
postedByAdminId: 'postedByAdminId',
|
||||||
labName: 'labName',
|
labName: 'labName',
|
||||||
hallmark: 'hallmark',
|
hallmark: 'hallmark',
|
||||||
accountName: 'accountName',
|
accountName: 'accountName',
|
||||||
@@ -549,7 +563,7 @@ exports.Prisma.PromotionExclusionScalarFieldEnum = {
|
|||||||
exports.Prisma.BranchAllocationScalarFieldEnum = {
|
exports.Prisma.BranchAllocationScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
branchId: 'branchId',
|
branchId: 'branchId',
|
||||||
allocatorAdminUserId: 'allocatorAdminUserId',
|
allocatorAdminId: 'allocatorAdminId',
|
||||||
note: 'note',
|
note: 'note',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
@@ -567,7 +581,7 @@ exports.Prisma.SmsMessageScalarFieldEnum = {
|
|||||||
recipients: 'recipients',
|
recipients: 'recipients',
|
||||||
body: 'body',
|
body: 'body',
|
||||||
sentCount: 'sentCount',
|
sentCount: 'sentCount',
|
||||||
sentByAdminUserId: 'sentByAdminUserId',
|
sentByAdminId: 'sentByAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -576,7 +590,7 @@ exports.Prisma.NotificationScalarFieldEnum = {
|
|||||||
title: 'title',
|
title: 'title',
|
||||||
body: 'body',
|
body: 'body',
|
||||||
audience: 'audience',
|
audience: 'audience',
|
||||||
sentByAdminUserId: 'sentByAdminUserId',
|
sentByAdminId: 'sentByAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -609,7 +623,7 @@ exports.Prisma.UploadedFileScalarFieldEnum = {
|
|||||||
mimeType: 'mimeType',
|
mimeType: 'mimeType',
|
||||||
sizeBytes: 'sizeBytes',
|
sizeBytes: 'sizeBytes',
|
||||||
purpose: 'purpose',
|
purpose: 'purpose',
|
||||||
uploadedByAdminUserId: 'uploadedByAdminUserId',
|
uploadedByAdminId: 'uploadedByAdminId',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt'
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -782,6 +796,7 @@ exports.MessageAudience = exports.$Enums.MessageAudience = {
|
|||||||
|
|
||||||
exports.Prisma.ModelName = {
|
exports.Prisma.ModelName = {
|
||||||
City: 'City',
|
City: 'City',
|
||||||
|
Admin: 'Admin',
|
||||||
AdminUser: 'AdminUser',
|
AdminUser: 'AdminUser',
|
||||||
AppSession: 'AppSession',
|
AppSession: 'AppSession',
|
||||||
AdminSession: 'AdminSession',
|
AdminSession: 'AdminSession',
|
||||||
|
|||||||
Reference in New Issue
Block a user