feat(db): additive prep migration for the henza data import
Purely additive; nothing dropped, renamed or retyped. Owner rulings of 2026-10-06/07, recorded in docs/DATA-MIGRATION-MAP.md. - Customer.nationalCode is now optional. All 1,595 live legacy customers have none, and fabricating one is a data violation. Still @unique: NULLs do not collide in Postgres, so the import must write NULL, not ''. - Customer.birthDate added, empty. Must not be backfilled until the Jalali-vs-Gregorian check (precheck-3) is in. - VerificationStep.REJECTED plus verificationRejectionReason/RejectedAt. Legacy has a third "rejected" state that the enum could not represent. - CustomerFavorite for legacy `fav` (251 rows), which had no home. accounting-sync customerPayload() now takes `nationalCode: string | null`. validNationalCode() already handled null, so Tahesab/Zarrin receive exactly what they did before. Caught by tsc after the column change. Not touched: accounting* columns, ProductVariant.imageId, the Accounting* tables, SMS (PROTECTED.md §B, §G). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
19d490e790
commit
45633b5aa0
@@ -56,6 +56,13 @@ enum VerificationStep {
|
||||
STEP_ONE_SUBMITTED
|
||||
STEP_TWO_SUBMITTED
|
||||
VERIFIED
|
||||
|
||||
/// Legacy had THREE states per step, not two
|
||||
/// (MajorCustomerController.php:106-135): 1 = awaiting, 2 = approved,
|
||||
/// 3 = REJECTED. Mapping rejected customers to UNVERIFIED would put closed
|
||||
/// cases back in the review queue, so the owner ruled it out (2026-10-07).
|
||||
/// 14 legacy rows land here; see Customer.verificationRejectionReason.
|
||||
REJECTED
|
||||
}
|
||||
|
||||
/// Legacy kept retail and wholesale orders in two SEPARATE tables
|
||||
@@ -367,7 +374,18 @@ model Customer {
|
||||
lastName String?
|
||||
|
||||
mobile String @unique
|
||||
nationalCode String @unique
|
||||
/// Legacy customers.ncode. OPTIONAL since 2026-10-07: not one of the 1,616
|
||||
/// legacy customers has a national code (1595/1595 empty), and fabricating
|
||||
/// one is a data violation that would block later legal identity checks
|
||||
/// (owner ruling). Still @unique — in PostgreSQL NULLs do not collide, so
|
||||
/// the migration tool must write NULL and never '', which would collide on
|
||||
/// the second row.
|
||||
///
|
||||
/// Three service-layer workarounds exist ONLY because this used to be
|
||||
/// required, and they store the MOBILE NUMBER here:
|
||||
/// admin-customers.service.ts:625,685 and customer-auth.service.ts:258.
|
||||
/// Retiring them is a separate change; see docs/DATA-MIGRATION-MAP.md §10.4.
|
||||
nationalCode String? @unique
|
||||
passwordHash String
|
||||
cityId String?
|
||||
city City? @relation(fields: [cityId], references: [id])
|
||||
@@ -429,6 +447,30 @@ model Customer {
|
||||
businessLicenseUrl String?
|
||||
shopImageUrl String?
|
||||
|
||||
/// Why verification was rejected (VerificationStep.REJECTED).
|
||||
///
|
||||
/// Legacy recorded NO reason: none of its 49 `customers` columns is a
|
||||
/// reason/note field, and on rejection it only sent a fixed SMS. So the 14
|
||||
/// rows migrated from henza carry the sentinel
|
||||
/// «رد شده در سیستم قدیم — فاقد شرح علت ثبتشده» instead of NULL, so support
|
||||
/// knows why it is blank (owner ruling 2026-10-07). The migration tool
|
||||
/// writes it only WHERE this IS NULL, so a re-run cannot overwrite a real
|
||||
/// reason entered in the new system.
|
||||
verificationRejectionReason String?
|
||||
|
||||
/// NULL for rows migrated from henza — legacy recorded no rejection time.
|
||||
verificationRejectedAt DateTime?
|
||||
|
||||
/// Legacy customers.birth_date, populated on 917 of 1,595 rows. Kept for the
|
||||
/// birthday SMS / loyalty campaigns (owner ruling 2026-10-07).
|
||||
///
|
||||
/// ⚠ Legacy stores dates as JALALI STRINGS in other tables
|
||||
/// (cashes.recive_date is varchar, 88/89 values starting "14…"), so this
|
||||
/// timestamp may hold mis-entered Jalali years. Confirm the calendar with
|
||||
/// docs/migration-precheck-3.sql BEFORE backfilling: copying them unchanged
|
||||
/// would produce 917 wrong birthdays, worse for an SMS campaign than none.
|
||||
birthDate DateTime?
|
||||
|
||||
/// Legacy `image_id` — the profile photo (عکس پروفایل) on the sales-agent
|
||||
/// form. Legacy stored an FK to its Image table and read it back as a nested
|
||||
/// `image: { id, image }`, so this is a relation, not a bare URL string.
|
||||
@@ -457,12 +499,35 @@ model Customer {
|
||||
notifications NotificationRecipient[]
|
||||
activityLogs CustomerActivityLog[]
|
||||
otpChallenges CustomerOtp[]
|
||||
favorites CustomerFavorite[]
|
||||
|
||||
@@index([referredByAgentId])
|
||||
@@index([firstName])
|
||||
@@index([lastName])
|
||||
}
|
||||
|
||||
/// Legacy `fav` (251 rows) — saved products. The target had no model for this,
|
||||
/// so every customer's favourites would have been lost in the migration; the
|
||||
/// owner confirmed on 2026-10-06 that they matter.
|
||||
///
|
||||
/// Legacy `fav` has two columns and NO primary key. The composite key here
|
||||
/// supplies the constraint the source lacks (the precheck found 0 duplicate
|
||||
/// pairs, but the tool should still SELECT DISTINCT). 14 of the 251 rows point
|
||||
/// at a deleted customer and must be skipped.
|
||||
///
|
||||
/// Legacy's denormalised `product.count_fav` counter is deliberately NOT
|
||||
/// recreated as a column — derive it from this table.
|
||||
model CustomerFavorite {
|
||||
customerId String
|
||||
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
||||
productId String
|
||||
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@id([customerId, productId])
|
||||
@@index([productId])
|
||||
}
|
||||
|
||||
model CustomerAddress {
|
||||
id String @id @default(cuid())
|
||||
customerId String
|
||||
@@ -579,6 +644,7 @@ model Product {
|
||||
comments Comment[]
|
||||
promotions Promotion[]
|
||||
allocationItems BranchAllocationItem[]
|
||||
favoritedBy CustomerFavorite[]
|
||||
}
|
||||
|
||||
model ProductImage {
|
||||
|
||||
Reference in New Issue
Block a user