80 lines
2.8 KiB
TypeScript
80 lines
2.8 KiB
TypeScript
import {
|
||
boolean,
|
||
date,
|
||
doublePrecision,
|
||
index,
|
||
pgTable,
|
||
text,
|
||
uuid,
|
||
integer,
|
||
} from "drizzle-orm/pg-core";
|
||
import { createdAt, unitEnum } from "./_shared.js";
|
||
import { households } from "./households.js";
|
||
import { canonicalIngredients } from "./ingredients.js";
|
||
import { products } from "./products.js";
|
||
|
||
/** Kvitton (spec §12): lager + budget + prishistorik + matsvinnsvärde. */
|
||
export const receipts = pgTable(
|
||
"receipts",
|
||
{
|
||
id: uuid("id").primaryKey().defaultRandom(),
|
||
householdId: uuid("household_id")
|
||
.notNull()
|
||
.references(() => households.id, { onDelete: "cascade" }),
|
||
storeName: text("store_name"),
|
||
purchaseDate: date("purchase_date"),
|
||
totalMinor: integer("total_minor"),
|
||
discountMinor: integer("discount_minor"),
|
||
imageUrl: text("image_url"),
|
||
scanJobId: uuid("scan_job_id"),
|
||
status: text("status").notNull().default("pending"),
|
||
createdAt: createdAt(),
|
||
},
|
||
(t) => [index("receipts_household_idx").on(t.householdId, t.purchaseDate)],
|
||
);
|
||
|
||
export const receiptLines = pgTable(
|
||
"receipt_lines",
|
||
{
|
||
id: uuid("id").primaryKey().defaultRandom(),
|
||
receiptId: uuid("receipt_id")
|
||
.notNull()
|
||
.references(() => receipts.id, { onDelete: "cascade" }),
|
||
rawText: text("raw_text").notNull(),
|
||
normalizedName: text("normalized_name"),
|
||
canonicalIngredientId: text("canonical_ingredient_id").references(
|
||
() => canonicalIngredients.id,
|
||
),
|
||
productId: uuid("product_id").references(() => products.id),
|
||
quantity: doublePrecision("quantity"),
|
||
unit: unitEnum("unit"),
|
||
unitPriceMinor: integer("unit_price_minor"),
|
||
totalPriceMinor: integer("total_price_minor"),
|
||
confidence: doublePrecision("confidence").notNull().default(0),
|
||
verifiedByUser: boolean("verified_by_user").notNull().default(false),
|
||
addedToInventory: boolean("added_to_inventory").notNull().default(false),
|
||
},
|
||
(t) => [index("receipt_lines_receipt_idx").on(t.receiptId)],
|
||
);
|
||
|
||
/** Prishistorik per ingrediens/produkt – grund för budget och prognoser (spec §12, §26). */
|
||
export const priceObservations = pgTable(
|
||
"price_observations",
|
||
{
|
||
id: uuid("id").primaryKey().defaultRandom(),
|
||
canonicalIngredientId: text("canonical_ingredient_id").references(
|
||
() => canonicalIngredients.id,
|
||
),
|
||
productId: uuid("product_id").references(() => products.id),
|
||
storeName: text("store_name"),
|
||
priceMinor: integer("price_minor").notNull(),
|
||
quantity: doublePrecision("quantity"),
|
||
unit: unitEnum("unit"),
|
||
pricePerKgMinor: integer("price_per_kg_minor"),
|
||
observedAt: date("observed_at").notNull(),
|
||
source: text("source").notNull().default("receipt"),
|
||
createdAt: createdAt(),
|
||
},
|
||
(t) => [index("price_observations_ingredient_idx").on(t.canonicalIngredientId, t.observedAt)],
|
||
);
|