You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
3.8 KiB
TypeScript
89 lines
3.8 KiB
TypeScript
import type { Item } from '@sims/domain'
|
|
import type { LineInput, TaxClassRow } from '@sims/billing-engine'
|
|
|
|
/** POS ↔ store-server client. Same origin in production (served under /pos/). */
|
|
|
|
export interface BootstrapStore {
|
|
id: string; name: string; storeCode: string; stateCode: string
|
|
gstin: string; counterId: string; counterCode: string
|
|
}
|
|
export interface PosUser { id: string; name: string; role: string }
|
|
export interface Bootstrap { store: BootstrapStore; users: PosUser[]; taxClasses: TaxClassRow[] }
|
|
|
|
let token = ''
|
|
|
|
async function call<T>(path: string, init?: RequestInit): Promise<T> {
|
|
const res = await fetch(`/api${path}`, {
|
|
...init,
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
...(token !== '' ? { authorization: `Bearer ${token}` } : {}),
|
|
},
|
|
}).catch(() => undefined)
|
|
if (res === undefined) throw new Error('Store-server unreachable (E-3301)')
|
|
const body = (await res.json().catch(() => ({}))) as T & { error?: string }
|
|
if (!res.ok) throw new Error(body.error ?? `Server error HTTP ${res.status}`)
|
|
return body
|
|
}
|
|
|
|
export const fetchBootstrap = (counter: string): Promise<Bootstrap> =>
|
|
call(`/bootstrap?counter=${encodeURIComponent(counter)}`)
|
|
|
|
export async function pinLogin(userId: string, pin: string): Promise<void> {
|
|
const s = await call<{ token: string }>('/auth/pin', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ userId, pin }),
|
|
})
|
|
token = s.token
|
|
}
|
|
|
|
/**
|
|
* In-flow supervisor gate (spec P0-2): verify a supervisor's PIN without touching
|
|
* the module-level `token` — the cashier stays logged in and no session is created.
|
|
* Distinct from `pinLogin` on purpose; reusing that would hijack the cashier's session.
|
|
*/
|
|
export const verifySupervisorPin = (userId: string, pin: string): Promise<{ ok: true; userId: string; displayName: string }> =>
|
|
call('/auth/verify-pin', { method: 'POST', body: JSON.stringify({ userId, pin }) })
|
|
|
|
// The counter caches the whole sellable catalog (no silent cap — R13); ?all=1
|
|
// returns every non-inactive item unpaginated.
|
|
export const fetchItemsCache = (): Promise<Item[]> => call('/items?all=1')
|
|
|
|
export const fetchStock = (): Promise<{ code: string; on_hand: number }[]> => call('/stock')
|
|
|
|
export interface CommitResult { id: string; docNo: string; payablePaise: number }
|
|
|
|
/** Supervisor-approved override, audited server-side in the commit transaction (spec P0-2). */
|
|
export interface BillOverride {
|
|
itemId: string; kind: 'price' | 'qty' | 'discount'; before: number; after: number; approvedByUserId?: string
|
|
}
|
|
|
|
export const postBill = (body: {
|
|
storeId: string; counterId: string; shiftId: string; customerId?: string
|
|
businessDate: string; lines: LineInput[]
|
|
payments: { mode: string; amountPaise: number }[]
|
|
clientPayablePaise: number
|
|
overrides?: BillOverride[]
|
|
}): Promise<CommitResult> => call('/bills', { method: 'POST', body: JSON.stringify(body) })
|
|
|
|
export const fetchBills = (date: string, counterId: string): Promise<Record<string, unknown>[]> =>
|
|
call(`/bills?date=${date}&counterId=${encodeURIComponent(counterId)}`)
|
|
|
|
export const createDraftItem = (body: {
|
|
code: string; name: string; hsn: string; taxClassCode: string
|
|
unitCode: string; unitDecimals: number; salePricePaise: number
|
|
barcode?: string; status: string
|
|
}): Promise<Item> => call('/items', { method: 'POST', body: JSON.stringify(body) })
|
|
|
|
/** Attached-customer identity the counter needs: name plus B2B place-of-supply inputs. */
|
|
export interface PosCustomer { id: string; name: string; phone?: string; gstin?: string; stateCode?: string }
|
|
|
|
export const findCustomers = (phone: string): Promise<PosCustomer[]> =>
|
|
call(`/parties?phone=${encodeURIComponent(phone)}`)
|
|
|
|
export const createCustomer = (name: string, phone: string, gstin?: string): Promise<PosCustomer> =>
|
|
call('/parties', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ name, phone, ...(gstin !== undefined && gstin !== '' ? { gstin } : {}) }),
|
|
})
|