AdminUser held both panel staff and sales experts, so one mobile could not be both. Staff now live in "Admin" (panel only); sales experts stay in "AdminUser" untouched. Uniqueness is per table. - migration 20261026120000_split_admin_table: additive only. Creates "Admin", copies non-expert rows with their ids, re-points the "who did this" foreign keys (ledger, order status, sms, notification, upload, allocation, workshop, admin session) to "Admin" without moving data. Never writes to "AdminUser". - login, JWT, SSO, branch/workshop supervisors and the users grid use "Admin"; SALES_EXPERT create/edit still goes through admin/users into "AdminUser". - an admin cannot be turned into an expert or the reverse (different tables). - manual/: optional phase-2 script to drop the dormant admin copies from "AdminUser", and a read-only expert-login diagnostic. - docs/ADMIN-TABLE-SPLIT.md: what changed, deploy checklist, rollback. Tested on a copy with 12,000 experts: expert rows byte-identical (md5) before and after; full migration chain applies and matches schema.prisma. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
60 lines
3.2 KiB
SQL
60 lines
3.2 KiB
SQL
-- READ-ONLY diagnostic: "I sign in as a sales expert and the customers menu /
|
|
-- list does not show". Changes nothing.
|
|
--
|
|
-- psql "$DATABASE_URL" -v mobile=09XXXXXXXXX -f diagnose_expert_login.sql
|
|
--
|
|
-- How the app decides (apps/api/src/modules/customer-auth/customer-auth.service.ts):
|
|
-- 1. request-otp: an ACTIVE "AdminUser" with role SALES_EXPERT and exactly this
|
|
-- mobile wins over any Customer with the same number.
|
|
-- 2. That expert needs a SalesExpertProfile and a usable trading Customer
|
|
-- (active, not archived). Created on first sign-in; if a Customer with the
|
|
-- same mobile already exists it is ADOPTED, and when that Customer is
|
|
-- inactive/archived the expert is NOT treated as an expert at all.
|
|
-- 3. verify-otp then issues a "sales-expert" token only if all of the above
|
|
-- hold; GET /api/expert/whoami answers isSalesExpert from that token, and
|
|
-- the apps show «مشتریان من» only when it is true.
|
|
-- 4. «مشتریان من» lists "Customer"."referredByAgentId" = the expert's id.
|
|
|
|
\echo '== 1. the expert row (must be exactly one, active, role SALES_EXPERT)'
|
|
SELECT id, mobile, role, "isActive", length(mobile) AS mobile_len
|
|
FROM "AdminUser" WHERE mobile = :'mobile';
|
|
|
|
\echo '== 1b. near-misses: same digits with spaces / country code / Persian digits'
|
|
SELECT id, mobile, role, "isActive"
|
|
FROM "AdminUser"
|
|
WHERE regexp_replace(mobile, '\D', '', 'g') IN (:'mobile', '98' || substr(:'mobile', 2))
|
|
AND mobile <> :'mobile';
|
|
|
|
\echo '== 2. profile + trading account (tradingCustomerId must be set and usable)'
|
|
SELECT p.id AS profile_id, p."tradingCustomerId",
|
|
c."isActive" AS trading_active, c."isArchived" AS trading_archived
|
|
FROM "AdminUser" a
|
|
LEFT JOIN "SalesExpertProfile" p ON p."adminUserId" = a.id
|
|
LEFT JOIN "Customer" c ON c.id = p."tradingCustomerId"
|
|
WHERE a.mobile = :'mobile';
|
|
|
|
\echo '== 3. Customer rows with this mobile (an inactive/archived one blocks expert mode)'
|
|
SELECT id, kind, "isActive", "isArchived", "verificationStep"
|
|
FROM "Customer" WHERE mobile = :'mobile';
|
|
|
|
\echo '== 4. customers referred by this expert (the «مشتریان من» list)'
|
|
SELECT count(*) AS referred_customers
|
|
FROM "Customer" c JOIN "AdminUser" a ON a.id = c."referredByAgentId"
|
|
WHERE a.mobile = :'mobile';
|
|
|
|
\echo '== 5. experts overall: how many cannot work as experts (for the whole table)'
|
|
SELECT
|
|
count(*) FILTER (WHERE a."isActive") AS active_experts,
|
|
count(*) FILTER (WHERE a."isActive" AND p.id IS NULL) AS no_profile,
|
|
count(*) FILTER (WHERE a."isActive" AND p.id IS NOT NULL
|
|
AND p."tradingCustomerId" IS NULL
|
|
AND EXISTS (SELECT 1 FROM "Customer" c
|
|
WHERE c.mobile = a.mobile
|
|
AND (NOT c."isActive" OR c."isArchived"))) AS blocked_by_inactive_same_mobile_customer,
|
|
count(*) FILTER (WHERE a."isActive" AND c.id IS NOT NULL
|
|
AND (NOT c."isActive" OR c."isArchived")) AS trading_account_disabled
|
|
FROM "AdminUser" a
|
|
LEFT JOIN "SalesExpertProfile" p ON p."adminUserId" = a.id
|
|
LEFT JOIN "Customer" c ON c.id = p."tradingCustomerId"
|
|
WHERE a.role = 'SALES_EXPERT';
|