Initial commit (unpacked platform)
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import {
|
||||
boolean,
|
||||
date,
|
||||
doublePrecision,
|
||||
index,
|
||||
jsonb,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
uuid,
|
||||
integer,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import type { NutritionDeclaration } from "@app/shared-types";
|
||||
import {
|
||||
createdAt,
|
||||
dateKindEnum,
|
||||
expiryStatusEnum,
|
||||
inventorySourceEnum,
|
||||
inventoryTransactionTypeEnum,
|
||||
unitEnum,
|
||||
updatedAt,
|
||||
} from "./_shared.js";
|
||||
import { households, storageLocations } from "./households.js";
|
||||
import { canonicalIngredients } from "./ingredients.js";
|
||||
import { products } from "./products.js";
|
||||
import { users } from "./users.js";
|
||||
|
||||
/**
|
||||
* Food Twin (spec §8): lagerposter med fullständig spårbarhet.
|
||||
* `quantity` är ett cachat saldo – sanningen är inventory_transactions.
|
||||
*/
|
||||
export const inventoryItems = pgTable(
|
||||
"inventory_items",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
householdId: uuid("household_id")
|
||||
.notNull()
|
||||
.references(() => households.id, { onDelete: "cascade" }),
|
||||
canonicalIngredientId: text("canonical_ingredient_id").references(
|
||||
() => canonicalIngredients.id,
|
||||
),
|
||||
productId: uuid("product_id").references(() => products.id),
|
||||
displayName: text("display_name").notNull(),
|
||||
brand: text("brand"),
|
||||
quantity: doublePrecision("quantity").notNull().default(0),
|
||||
unit: unitEnum("unit").notNull(),
|
||||
storageLocationId: uuid("storage_location_id")
|
||||
.notNull()
|
||||
.references(() => storageLocations.id),
|
||||
sublocation: text("sublocation"),
|
||||
purchasedAt: date("purchased_at"),
|
||||
openedAt: date("opened_at"),
|
||||
bestBeforeDate: date("best_before_date"),
|
||||
useByDate: date("use_by_date"),
|
||||
dateKind: dateKindEnum("date_kind"),
|
||||
frozenAt: date("frozen_at"),
|
||||
thawedAt: date("thawed_at"),
|
||||
priceMinor: integer("price_minor"),
|
||||
nutritionPer100: jsonb("nutrition_per_100").$type<NutritionDeclaration>(),
|
||||
source: inventorySourceEnum("source").notNull(),
|
||||
confidence: doublePrecision("confidence").notNull().default(1),
|
||||
verifiedByUser: boolean("verified_by_user").notNull().default(false),
|
||||
lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }),
|
||||
expiryStatus: expiryStatusEnum("expiry_status").notNull().default("unknown"),
|
||||
/** Sätts när saldot nått 0 och posten arkiverats. */
|
||||
depletedAt: timestamp("depleted_at", { withTimezone: true }),
|
||||
modelVersion: text("model_version"),
|
||||
promptVersion: text("prompt_version"),
|
||||
createdAt: createdAt(),
|
||||
updatedAt: updatedAt(),
|
||||
},
|
||||
(t) => [
|
||||
index("inventory_items_household_idx").on(t.householdId),
|
||||
index("inventory_items_household_bb_idx").on(t.householdId, t.bestBeforeDate),
|
||||
index("inventory_items_location_idx").on(t.storageLocationId),
|
||||
index("inventory_items_canonical_idx").on(t.canonicalIngredientId),
|
||||
],
|
||||
);
|
||||
|
||||
/** Transaktionsloggen är källan till sanning (spec §8): + inköp, − använt, − kasserat … */
|
||||
export const inventoryTransactions = pgTable(
|
||||
"inventory_transactions",
|
||||
{
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
householdId: uuid("household_id")
|
||||
.notNull()
|
||||
.references(() => households.id, { onDelete: "cascade" }),
|
||||
inventoryItemId: uuid("inventory_item_id")
|
||||
.notNull()
|
||||
.references(() => inventoryItems.id, { onDelete: "cascade" }),
|
||||
type: inventoryTransactionTypeEnum("type").notNull(),
|
||||
quantityDelta: doublePrecision("quantity_delta").notNull(),
|
||||
unit: unitEnum("unit").notNull(),
|
||||
refType: text("ref_type"),
|
||||
refId: uuid("ref_id"),
|
||||
actorUserId: uuid("actor_user_id").references(() => users.id, { onDelete: "set null" }),
|
||||
note: text("note"),
|
||||
/** Värde i SEK för matsvinnsberäkning vid discard (spec §12, §26). */
|
||||
valueMinor: integer("value_minor"),
|
||||
createdAt: createdAt(),
|
||||
},
|
||||
(t) => [
|
||||
index("inventory_tx_item_idx").on(t.inventoryItemId),
|
||||
index("inventory_tx_household_time_idx").on(t.householdId, t.createdAt),
|
||||
index("inventory_tx_type_idx").on(t.type),
|
||||
],
|
||||
);
|
||||
Reference in New Issue
Block a user