Fas 2 steg 5: Scan-to-scan-diff + konfliktlösning, samt buggfix depleted-transaktion i reconciliation
This commit is contained in:
@@ -4,6 +4,7 @@ import { eq, inArray } from "drizzle-orm";
|
||||
import { buildServer } from "../src/server.js";
|
||||
import { loadConfig } from "../src/config.js";
|
||||
import { createDatabase, closeDatabase, schema } from "@app/database";
|
||||
import { computeBalance } from "@app/inventory-engine";
|
||||
|
||||
describe("quick reconciliation", () => {
|
||||
const testDb = createDatabase(process.env.TEST_DATABASE_URL!);
|
||||
@@ -123,4 +124,56 @@ describe("quick reconciliation", () => {
|
||||
expect(body.quantity).toBe(0.5);
|
||||
expect(body.verifiedByUser).toBe(true);
|
||||
});
|
||||
|
||||
it("transaction invariant holds for exists/depleted/uncertain", async () => {
|
||||
const start = await app.inject({
|
||||
method: "POST",
|
||||
url: "/v1/reconciliations/start",
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: {},
|
||||
});
|
||||
const { candidates } = JSON.parse(start.body) as { candidates: Array<{ itemId: string }> };
|
||||
const itemId = candidates[0]!.itemId;
|
||||
|
||||
async function assertBalanceMatches(itemId: string) {
|
||||
const [item] = await testDb.db
|
||||
.select({ quantity: schema.inventoryItems.quantity })
|
||||
.from(schema.inventoryItems)
|
||||
.where(eq(schema.inventoryItems.id, itemId))
|
||||
.limit(1);
|
||||
const txs = await testDb.db
|
||||
.select({ type: schema.inventoryTransactions.type, quantityDelta: schema.inventoryTransactions.quantityDelta, unit: schema.inventoryTransactions.unit })
|
||||
.from(schema.inventoryTransactions)
|
||||
.where(eq(schema.inventoryTransactions.inventoryItemId, itemId));
|
||||
const balance = computeBalance(txs.map((tx) => ({ type: tx.type, quantityDelta: tx.quantityDelta, unit: tx.unit })));
|
||||
expect(balance.balance).toBeCloseTo(item!.quantity, 5);
|
||||
}
|
||||
|
||||
// exists med justering
|
||||
await app.inject({
|
||||
method: "POST",
|
||||
url: `/v1/reconciliations/items/${itemId}/resolve`,
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: { action: "exists", quantity: 0.7 },
|
||||
});
|
||||
await assertBalanceMatches(itemId);
|
||||
|
||||
// depleted ska alltid skriva transaktion
|
||||
await app.inject({
|
||||
method: "POST",
|
||||
url: `/v1/reconciliations/items/${itemId}/resolve`,
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: { action: "depleted" },
|
||||
});
|
||||
await assertBalanceMatches(itemId);
|
||||
|
||||
// uncertain med justering
|
||||
await app.inject({
|
||||
method: "POST",
|
||||
url: `/v1/reconciliations/items/${itemId}/resolve`,
|
||||
headers: { authorization: `Bearer ${token}` },
|
||||
payload: { action: "uncertain", quantity: 0.2 },
|
||||
});
|
||||
await assertBalanceMatches(itemId);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user