Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions client/src/components/TransactionForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import { checkDispensingEligibility, type LocalSettings, type GuardResult } from '../utils/dispensingGuard';
import { type PatientDocType } from '../db/schema';
import { TRANSACTION_TYPES, type TransactionType } from '../constants';

// Mock Patient Data (Since we don't have a patient picker UI yet)
const MOCK_PATIENTS: (PatientDocType & { last_sync_date: string })[] = [
Expand All @@ -15,7 +16,7 @@ const LOCAL_SETTINGS: LocalSettings = {
};

interface TransactionFormProps {
onAdd: (type: 'DISPENSE' | 'RECEIVE' | 'ADJUST', qty: number, batchId: string) => void;
onAdd: (type: TransactionType, qty: number, batchId: string) => void;
sku: string;
}

Expand All @@ -34,7 +35,7 @@ export const TransactionForm: React.FC<TransactionFormProps> = ({ onAdd, sku })
setGuardResult(result);

if (result.allowed) {
onAdd('DISPENSE', qty, batchId);
onAdd(TRANSACTION_TYPES.DISPENSE, qty, batchId);
setQty(0);
}
};
Expand Down Expand Up @@ -103,13 +104,13 @@ export const TransactionForm: React.FC<TransactionFormProps> = ({ onAdd, sku })
Dispense ( - )
</button>
<button
onClick={() => { onAdd('RECEIVE', qty, batchId); setQty(0); }}
onClick={() => { onAdd(TRANSACTION_TYPES.RECEIVE, qty, batchId); setQty(0); }}
style={{ backgroundColor: '#ccffcc', padding: '8px 16px', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
>
Receive ( + )
</button>
<button
onClick={() => { onAdd('ADJUST', qty, batchId); setQty(0); }}
onClick={() => { onAdd(TRANSACTION_TYPES.ADJUST, qty, batchId); setQty(0); }}
style={{ backgroundColor: '#ffffcc', padding: '8px 16px', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
>
Adjust ( +/- )
Expand Down
7 changes: 7 additions & 0 deletions client/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const TRANSACTION_TYPES = {
DISPENSE: 'DISPENSE',
RECEIVE: 'RECEIVE',
ADJUST: 'ADJUST'
} as const;

export type TransactionType = typeof TRANSACTION_TYPES[keyof typeof TRANSACTION_TYPES];
13 changes: 9 additions & 4 deletions client/src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
toTypedRxJsonSchema,
type ExtractDocumentTypeFromTypedRxJsonSchema
} from 'rxdb';
import { TRANSACTION_TYPES } from '../constants';

// --- Transaction Event (Write Model) ---
export const TRANSACTION_SCHEMA_LITERAL = {
Expand All @@ -16,7 +17,11 @@ export const TRANSACTION_SCHEMA_LITERAL = {
},
type: {
type: 'string',
enum: ['DISPENSE', 'RECEIVE', 'ADJUST'],
enum: [
TRANSACTION_TYPES.DISPENSE,
TRANSACTION_TYPES.RECEIVE,
TRANSACTION_TYPES.ADJUST
],
maxLength: 20
},
sku: {
Expand Down Expand Up @@ -48,7 +53,7 @@ export const TRANSACTION_SCHEMA_LITERAL = {
required: ['id', 'type', 'sku', 'qty', 'timestamp', 'sync_status']
} as const;

const schemaTypedTransaction = toTypedRxJsonSchema(TRANSACTION_SCHEMA_LITERAL);
export const schemaTypedTransaction = toTypedRxJsonSchema(TRANSACTION_SCHEMA_LITERAL);
export type TransactionDocType = ExtractDocumentTypeFromTypedRxJsonSchema<typeof schemaTypedTransaction>;

export const TransactionSchema: RxJsonSchema<TransactionDocType> = TRANSACTION_SCHEMA_LITERAL;
Expand Down Expand Up @@ -79,7 +84,7 @@ export const INVENTORY_SCHEMA_LITERAL = {
required: ['sku', 'batch_id', 'current_stock']
} as const;

const schemaTypedInventory = toTypedRxJsonSchema(INVENTORY_SCHEMA_LITERAL);
export const schemaTypedInventory = toTypedRxJsonSchema(INVENTORY_SCHEMA_LITERAL);
export type InventoryDocType = ExtractDocumentTypeFromTypedRxJsonSchema<typeof schemaTypedInventory>;

export const InventorySchema: RxJsonSchema<InventoryDocType> = INVENTORY_SCHEMA_LITERAL;
Expand Down Expand Up @@ -110,7 +115,7 @@ export const PATIENT_SCHEMA_LITERAL = {
required: ['id', 'name', 'municipality']
} as const;

const schemaTypedPatient = toTypedRxJsonSchema(PATIENT_SCHEMA_LITERAL);
export const schemaTypedPatient = toTypedRxJsonSchema(PATIENT_SCHEMA_LITERAL);
export type PatientDocType = ExtractDocumentTypeFromTypedRxJsonSchema<typeof schemaTypedPatient>;

export const PatientSchema: RxJsonSchema<PatientDocType> = PATIENT_SCHEMA_LITERAL;
7 changes: 4 additions & 3 deletions client/src/hooks/useInventory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react';
import { getDatabase, type MyDatabase } from '../db/database';
import { type TransactionDocType } from '../db/schema';
import { TRANSACTION_TYPES, type TransactionType } from '../constants';

export function useInventory(sku: string | null) {
const [currentStock, setCurrentStock] = useState<number>(0);
Expand Down Expand Up @@ -43,13 +44,13 @@ export function useInventory(sku: string | null) {
return () => subscription.unsubscribe();
}, [db, sku]);

const addTransaction = async (type: 'DISPENSE' | 'RECEIVE' | 'ADJUST', qty: number, batch_id: string) => {
const addTransaction = async (type: TransactionType, qty: number, batch_id: string) => {
if (!db || !sku) return;

// Ensure negative for Dispense if user passes positive
let finalQty = qty;
if (type === 'DISPENSE' && qty > 0) finalQty = -qty;
if (type === 'RECEIVE' && qty < 0) finalQty = -qty; // Receive should be positive
if (type === TRANSACTION_TYPES.DISPENSE && qty > 0) finalQty = -qty;
if (type === TRANSACTION_TYPES.RECEIVE && qty < 0) finalQty = -qty; // Receive should be positive

await db.transactions.insert({
id: crypto.randomUUID(),
Expand Down
Binary file added verification/transactions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions verification/verify_transactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from playwright.sync_api import sync_playwright

def verify_transactions():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()

print("Navigating to app...")
try:
page.goto("http://localhost:3000")
except Exception as e:
print(f"Failed to navigate: {e}")
return

# Wait for app to load
print("Waiting for app to load...")
try:
page.wait_for_selector("h1:has-text('YAKAP-Link')", timeout=10000)
except Exception as e:
print(f"App didn't load in time: {e}")
page.screenshot(path="verification/error.png")
return

# Add Dispense Transaction
print("Adding Dispense transaction...")
page.fill("input[type=number]", "5")
page.click("button:has-text('Dispense ( - )')")

# Add Receive Transaction
print("Adding Receive transaction...")
page.fill("input[type=number]", "10")
page.click("button:has-text('Receive ( + )')")

# Add Adjust Transaction
print("Adding Adjust transaction...")
page.fill("input[type=number]", "2")
page.click("button:has-text('Adjust ( +/- )')")

# Verify Ledger
print("Verifying ledger...")
# Check if 'DISPENSE', 'RECEIVE', 'ADJUST' text exists in the ledger area
try:
page.wait_for_selector("text=DISPENSE", timeout=5000)
page.wait_for_selector("text=RECEIVE", timeout=5000)
page.wait_for_selector("text=ADJUST", timeout=5000)
except Exception as e:
print(f"Ledger verification failed: {e}")

print("Taking screenshot...")
page.screenshot(path="verification/transactions.png")

browser.close()

if __name__ == "__main__":
verify_transactions()