Fas 2 steg 5: Scan-to-scan-diff + konfliktlösning, samt buggfix depleted-transaktion i reconciliation

This commit is contained in:
Sven (AAMOS AI)
2026-08-07 01:30:31 +07:00
parent 218bc44d08
commit 4a4f448e1c
26 changed files with 10590 additions and 13 deletions
+39
View File
@@ -108,3 +108,42 @@ export const inventoryTransactions = pgTable(
index("inventory_tx_type_idx").on(t.type),
],
);
/** Konflikter vid scan-to-scan-diff (Fas 2 §5.6): kvitto vs bild, två medlemmar, offline vs server. */
export const inventoryConflicts = pgTable(
"inventory_conflicts",
{
id: uuid("id").primaryKey().defaultRandom(),
householdId: uuid("household_id")
.notNull()
.references(() => households.id, { onDelete: "cascade" }),
inventoryItemId: uuid("inventory_item_id").references(() => inventoryItems.id, { onDelete: "cascade" }),
scanJobId: uuid("scan_job_id"),
/** Beskrivande typ: receipt_vs_image, member_vs_member, offline_vs_server, model_disagreement. */
conflictType: text("conflict_type").notNull(),
/** Källor med prioritet. Lägst nummer = högst förtroende. */
sourceA: text("source_a").notNull(),
sourceAPriority: integer("source_a_priority").notNull().default(0),
sourceB: text("source_b").notNull(),
sourceBPriority: integer("source_b_priority").notNull().default(0),
/** Konkreta värden som strider. */
payloadA: jsonb("payload_a").notNull().$type<Record<string, unknown>>(),
payloadB: jsonb("payload_b").notNull().$type<Record<string, unknown>>(),
/** proposed_resolution beräknas utifrån källprioritet. */
proposedResolution: jsonb("proposed_resolution").$type<Record<string, unknown>>(),
status: text("status").notNull().default("open"),
/** Användaren som löste konflikten, eller NULL om system-förslag. */
resolvedByUserId: uuid("resolved_by_user_id").references(() => users.id, { onDelete: "set null" }),
resolution: jsonb("resolution").$type<Record<string, unknown>>(),
/** Modell- och promptversion som användes när konflikten upptäcktes. */
modelVersion: text("model_version"),
promptVersion: text("prompt_version"),
createdAt: createdAt(),
resolvedAt: timestamp("resolved_at", { withTimezone: true }),
},
(t) => [
index("inventory_conflicts_household_idx").on(t.householdId),
index("inventory_conflicts_item_idx").on(t.inventoryItemId),
index("inventory_conflicts_status_idx").on(t.status),
],
);