feat: add drawer cash in/out adjustments with balance reconciliation

- New drawer_adjustment table (type: cash_in/cash_out, amount, reason)
- POST/GET /drawer/:id/adjustments endpoints
- Drawer close calculation now includes adjustments: expected = opening + sales + cash_in - cash_out
- DrawerAdjustmentSchema for input validation
- 5 new tests (44 total POS tests passing)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ryan
2026-04-04 20:24:55 +00:00
parent 24ddb17ca8
commit 3ed2707a66
8 changed files with 180 additions and 6 deletions

View File

@@ -0,0 +1,14 @@
DO $$ BEGIN
CREATE TYPE "adjustment_type" AS ENUM ('cash_in', 'cash_out');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
CREATE TABLE IF NOT EXISTS "drawer_adjustment" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"drawer_session_id" uuid NOT NULL REFERENCES "drawer_session"("id"),
"type" "adjustment_type" NOT NULL,
"amount" numeric(10, 2) NOT NULL,
"reason" text NOT NULL,
"created_by" uuid NOT NULL REFERENCES "user"("id"),
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);

View File

@@ -295,6 +295,13 @@
"when": 1775580000000,
"tag": "0041_app_settings",
"breakpoints": true
},
{
"idx": 42,
"version": "7",
"when": 1775590000000,
"tag": "0042_drawer-adjustments",
"breakpoints": true
}
]
}

View File

@@ -68,6 +68,8 @@ export const discounts = pgTable('discount', {
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
})
export const adjustmentTypeEnum = pgEnum('adjustment_type', ['cash_in', 'cash_out'])
export const drawerSessions = pgTable('drawer_session', {
id: uuid('id').primaryKey().defaultRandom(),
locationId: uuid('location_id').references(() => locations.id),
@@ -86,6 +88,20 @@ export const drawerSessions = pgTable('drawer_session', {
closedAt: timestamp('closed_at', { withTimezone: true }),
})
export const drawerAdjustments = pgTable('drawer_adjustment', {
id: uuid('id').primaryKey().defaultRandom(),
drawerSessionId: uuid('drawer_session_id')
.notNull()
.references(() => drawerSessions.id),
type: adjustmentTypeEnum('type').notNull(),
amount: numeric('amount', { precision: 10, scale: 2 }).notNull(),
reason: text('reason').notNull(),
createdBy: uuid('created_by')
.notNull()
.references(() => users.id),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
})
export const transactions = pgTable('transaction', {
id: uuid('id').primaryKey().defaultRandom(),
locationId: uuid('location_id').references(() => locations.id),