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.
103 lines
5.7 KiB
TypeScript
103 lines
5.7 KiB
TypeScript
/** Back office ↔ store-server client; session token survives reloads. */
|
|
import type { Gstr1, Gstr3b, HsnRow, Reconciliation, TaxAmounts } from '@sims/billing-engine'
|
|
|
|
let token = sessionStorage.getItem('bo.token') ?? ''
|
|
|
|
export function hasSession(): boolean {
|
|
return token !== ''
|
|
}
|
|
|
|
export function clearSession(): void {
|
|
token = ''
|
|
sessionStorage.removeItem('bo.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)')
|
|
if (res.status === 401) clearSession()
|
|
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 async function login(username: string, password: string): Promise<string> {
|
|
const s = await call<{ token: string; displayName: string }>('/auth/login', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password }),
|
|
})
|
|
token = s.token
|
|
sessionStorage.setItem('bo.token', s.token)
|
|
return s.displayName
|
|
}
|
|
|
|
/** End the session server-side (M4) then clear it locally. Best-effort on the network call. */
|
|
export async function logout(): Promise<void> {
|
|
try { await call('/auth/logout', { method: 'POST' }) } catch { /* server down: clear locally anyway */ }
|
|
clearSession()
|
|
}
|
|
|
|
export const getItems = (q?: string): Promise<Record<string, unknown>[]> =>
|
|
call(`/items${q !== undefined && q !== '' ? `?q=${encodeURIComponent(q)}` : ''}`)
|
|
export const createItem = (body: Record<string, unknown>): Promise<Record<string, unknown>> =>
|
|
call('/items', { method: 'POST', body: JSON.stringify(body) })
|
|
export const getParties = (kind?: string): Promise<Record<string, unknown>[]> =>
|
|
call(`/parties${kind !== undefined ? `?kind=${kind}` : ''}`)
|
|
export const getBills = (date?: string, limit?: number, offset?: number): Promise<Record<string, unknown>[]> => {
|
|
const qs = new URLSearchParams()
|
|
if (date !== undefined) qs.set('date', date)
|
|
if (limit !== undefined) qs.set('limit', String(limit))
|
|
if (offset !== undefined) qs.set('offset', String(offset))
|
|
return call(`/bills${qs.toString() !== '' ? `?${qs}` : ''}`)
|
|
}
|
|
export const getStock = (): Promise<Record<string, unknown>[]> => call('/stock')
|
|
/** Credit notes (SALE_RETURN docs) — tenant-scoped, paginated; feeds the Returns page. */
|
|
export const getReturns = (limit?: number, offset?: number): Promise<Record<string, unknown>[]> => {
|
|
const qs = new URLSearchParams()
|
|
if (limit !== undefined) qs.set('limit', String(limit))
|
|
if (offset !== undefined) qs.set('offset', String(offset))
|
|
return call(`/returns${qs.toString() !== '' ? `?${qs}` : ''}`)
|
|
}
|
|
/** S5: per-batch on-hand + value inputs joined to the item — feeds the expiry
|
|
* dashboard and the Stock page's batch breakdown. */
|
|
export const getExpiry = (): Promise<Record<string, unknown>[]> => call('/expiry')
|
|
export const getAudit = (limit?: number, offset?: number): Promise<Record<string, unknown>[]> => {
|
|
const qs = new URLSearchParams()
|
|
if (limit !== undefined) qs.set('limit', String(limit))
|
|
if (offset !== undefined) qs.set('offset', String(offset))
|
|
return call(`/audit${qs.toString() !== '' ? `?${qs}` : ''}`)
|
|
}
|
|
|
|
export const getPurchases = (): Promise<Record<string, unknown>[]> => call('/purchases')
|
|
export const getLastCosts = (supplierId?: string): Promise<Record<string, { last?: number; supplierLast?: number }>> =>
|
|
call(`/purchases/last-costs${supplierId !== undefined ? `?supplierId=${supplierId}` : ''}`)
|
|
export const postPurchase = (body: Record<string, unknown>): Promise<{ id: string; totalPaise: number }> =>
|
|
call('/purchases', { method: 'POST', body: JSON.stringify(body) })
|
|
export const getCostHistory = (itemId: string, supplierId?: string): Promise<{ cost: number; date: string; supplier: string }[]> =>
|
|
call(`/purchases/cost-history?itemId=${itemId}${supplierId !== undefined ? `&supplierId=${supplierId}` : ''}`)
|
|
export const getBootstrapPublic = (): Promise<{ taxClasses: { classCode: string; ratePctBp: number; cessPctBp: number; effectiveFrom: string; effectiveTo?: string }[] }> =>
|
|
call('/bootstrap?counter=C1')
|
|
|
|
export interface Seller { id: string; name: string; storeCode: string; stateCode: string; gstin?: string }
|
|
/** Seller header for GST invoices/labels — store name, GSTIN, state (from bootstrap). */
|
|
export const getSeller = (): Promise<Seller> =>
|
|
call<{ store: Seller }>('/bootstrap?counter=C1').then((b) => b.store)
|
|
|
|
// GST returns — READ-ONLY reporting over a period's immutable documents. The server folds them
|
|
// through the shared engine and returns each section plus a reconciliation proof (see repos-gst).
|
|
export interface Gstr1Response extends Gstr1 { period: string; storeStateCode: string; reconciliation: Reconciliation }
|
|
export interface Gstr3bResponse extends Gstr3b { period: string; storeStateCode: string; reconciliation: Reconciliation }
|
|
export interface HsnResponse { period: string; storeStateCode: string; rows: HsnRow[]; totals: TaxAmounts; reconciliation: Reconciliation }
|
|
|
|
export const getGstr1 = (period: string): Promise<Gstr1Response> => call(`/gst/gstr1?period=${encodeURIComponent(period)}`)
|
|
export const getGstr3b = (period: string): Promise<Gstr3bResponse> => call(`/gst/gstr3b?period=${encodeURIComponent(period)}`)
|
|
export const getGstHsn = (period: string): Promise<HsnResponse> => call(`/gst/hsn?period=${encodeURIComponent(period)}`)
|
|
/** The pragmatic GSTN-shaped JSON export (documented in-payload as NOT a certified filing). */
|
|
export const getGstr1Json = (period: string): Promise<Record<string, unknown>> => call(`/gst/gstr1.json?period=${encodeURIComponent(period)}`)
|