feat(flyer): add flyer session and selection system
- Add FlyerSession, FlyerItem, and FlyerSelection models to Prisma schema - Implement session persistence with weekly key generation in FlyerImportService - Add FlyerSelectionModule to AppModule - Extend FlyerImportResponse with sessionId and flyerItemId fields - Create new flyer-selection module directory structure - Add migration for flyer session and selection tables BREAKING CHANGE: Flyer import now persists data to FlyerSession and FlyerItem tables
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
CREATE TABLE `FlyerSession` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`retailer` VARCHAR(191) NOT NULL,
|
||||
`weekKey` VARCHAR(191) NOT NULL,
|
||||
`status` VARCHAR(191) NOT NULL DEFAULT 'draft',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
`expiresAt` DATETIME(3) NULL,
|
||||
|
||||
INDEX `FlyerSession_userId_idx`(`userId`),
|
||||
INDEX `FlyerSession_weekKey_idx`(`weekKey`),
|
||||
INDEX `FlyerSession_status_idx`(`status`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE `FlyerItem` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`sessionId` INTEGER NOT NULL,
|
||||
`rawName` VARCHAR(191) NOT NULL,
|
||||
`normalizedName` VARCHAR(191) NOT NULL,
|
||||
`categoryHint` VARCHAR(191) NULL,
|
||||
`price` DECIMAL(10, 2) NULL,
|
||||
`priceUnit` VARCHAR(191) NULL,
|
||||
`comparisonPrice` DECIMAL(10, 2) NULL,
|
||||
`comparisonUnit` VARCHAR(191) NULL,
|
||||
`offerText` VARCHAR(191) NULL,
|
||||
`parseConfidence` DOUBLE NOT NULL,
|
||||
`parseReasons` JSON NULL,
|
||||
`matchedProductId` INTEGER NULL,
|
||||
`matchedProductName` VARCHAR(191) NULL,
|
||||
`matchedVia` VARCHAR(191) NULL,
|
||||
`matchConfidence` DOUBLE NULL,
|
||||
`matchReasons` JSON NULL,
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
INDEX `FlyerItem_sessionId_idx`(`sessionId`),
|
||||
INDEX `FlyerItem_normalizedName_idx`(`normalizedName`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE `FlyerSelection` (
|
||||
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||
`sessionId` INTEGER NOT NULL,
|
||||
`itemId` INTEGER NOT NULL,
|
||||
`userId` INTEGER NOT NULL,
|
||||
`plannedQuantity` DECIMAL(10, 2) NULL,
|
||||
`plannedUnit` VARCHAR(191) NULL,
|
||||
`priority` VARCHAR(191) NOT NULL DEFAULT 'normal',
|
||||
`note` VARCHAR(191) NULL,
|
||||
`status` VARCHAR(191) NOT NULL DEFAULT 'planned',
|
||||
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updatedAt` DATETIME(3) NOT NULL,
|
||||
|
||||
UNIQUE INDEX `FlyerSelection_sessionId_itemId_key`(`sessionId`, `itemId`),
|
||||
INDEX `FlyerSelection_sessionId_idx`(`sessionId`),
|
||||
INDEX `FlyerSelection_userId_status_idx`(`userId`, `status`),
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `FlyerSession`
|
||||
ADD CONSTRAINT `FlyerSession_userId_fkey`
|
||||
FOREIGN KEY (`userId`) REFERENCES `User`(`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE `FlyerItem`
|
||||
ADD CONSTRAINT `FlyerItem_sessionId_fkey`
|
||||
FOREIGN KEY (`sessionId`) REFERENCES `FlyerSession`(`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE `FlyerSelection`
|
||||
ADD CONSTRAINT `FlyerSelection_sessionId_fkey`
|
||||
FOREIGN KEY (`sessionId`) REFERENCES `FlyerSession`(`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE `FlyerSelection`
|
||||
ADD CONSTRAINT `FlyerSelection_itemId_fkey`
|
||||
FOREIGN KEY (`itemId`) REFERENCES `FlyerItem`(`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE `FlyerSelection`
|
||||
ADD CONSTRAINT `FlyerSelection_userId_fkey`
|
||||
FOREIGN KEY (`userId`) REFERENCES `User`(`id`)
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -27,10 +27,12 @@ model User {
|
||||
ownedProducts Product[]
|
||||
inventoryItems InventoryItem[]
|
||||
pantryItems PantryItem[]
|
||||
mealPlanEntries MealPlanEntry[]
|
||||
receiptAliases ReceiptAlias[]
|
||||
unitMappings UnitMapping[]
|
||||
}
|
||||
mealPlanEntries MealPlanEntry[]
|
||||
receiptAliases ReceiptAlias[]
|
||||
unitMappings UnitMapping[]
|
||||
flyerSessions FlyerSession[]
|
||||
flyerSelections FlyerSelection[]
|
||||
}
|
||||
|
||||
model Product {
|
||||
id Int @id @default(autoincrement())
|
||||
@@ -264,7 +266,7 @@ model UnitMapping {
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model HelpText {
|
||||
model HelpText {
|
||||
id Int @id @default(autoincrement())
|
||||
key String
|
||||
scope String @default("default")
|
||||
@@ -274,6 +276,75 @@ model HelpText {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([key, scope])
|
||||
@@index([key, isActive])
|
||||
}
|
||||
@@unique([key, scope])
|
||||
@@index([key, isActive])
|
||||
}
|
||||
|
||||
model FlyerSession {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
retailer String
|
||||
weekKey String
|
||||
status String @default("draft")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
expiresAt DateTime?
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
items FlyerItem[]
|
||||
selections FlyerSelection[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([weekKey])
|
||||
@@index([status])
|
||||
}
|
||||
|
||||
model FlyerItem {
|
||||
id Int @id @default(autoincrement())
|
||||
sessionId Int
|
||||
rawName String
|
||||
normalizedName String
|
||||
categoryHint String?
|
||||
price Decimal? @db.Decimal(10, 2)
|
||||
priceUnit String?
|
||||
comparisonPrice Decimal? @db.Decimal(10, 2)
|
||||
comparisonUnit String?
|
||||
offerText String?
|
||||
parseConfidence Float
|
||||
parseReasons Json?
|
||||
matchedProductId Int?
|
||||
matchedProductName String?
|
||||
matchedVia String?
|
||||
matchConfidence Float?
|
||||
matchReasons Json?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
session FlyerSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
selections FlyerSelection[]
|
||||
|
||||
@@index([sessionId])
|
||||
@@index([normalizedName])
|
||||
}
|
||||
|
||||
model FlyerSelection {
|
||||
id Int @id @default(autoincrement())
|
||||
sessionId Int
|
||||
itemId Int
|
||||
userId Int
|
||||
plannedQuantity Decimal? @db.Decimal(10, 2)
|
||||
plannedUnit String?
|
||||
priority String @default("normal")
|
||||
note String?
|
||||
status String @default("planned")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
session FlyerSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||||
item FlyerItem @relation(fields: [itemId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([sessionId, itemId])
|
||||
@@index([sessionId])
|
||||
@@index([userId, status])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user