150 lines
5.0 KiB
TypeScript
150 lines
5.0 KiB
TypeScript
import { api } from '@/lib/api'
|
|
import type {
|
|
PaginatedResponse,
|
|
PurchaseOrderResponse,
|
|
GrnResponse,
|
|
PurchaseOrderStatus,
|
|
PurchaseReturnResponse,
|
|
} from '@/types/api'
|
|
|
|
import type { PageParams } from './master'
|
|
|
|
// ─── Purchase Orders ──────────────────────────────────────────────────────────
|
|
|
|
export interface PurchaseOrderItemBody {
|
|
itemId: string
|
|
quantity: number
|
|
unitCost: number
|
|
}
|
|
|
|
export interface PurchaseOrderBody {
|
|
supplierId: string
|
|
items: PurchaseOrderItemBody[]
|
|
// TEMPORARY: required while auth doesn't resolve a logged-in user server-side — see testbug.md #28.
|
|
createdByUserId: string
|
|
}
|
|
|
|
export interface UpdatePurchaseOrderLineBody {
|
|
purchaseOrderItemId?: string
|
|
itemId: string
|
|
quantity: number
|
|
unitCost: number
|
|
}
|
|
|
|
export interface UpdatePurchaseOrderBody {
|
|
items: UpdatePurchaseOrderLineBody[]
|
|
}
|
|
|
|
export interface PurchaseOrderListParams extends PageParams {
|
|
supplierId?: string
|
|
status?: PurchaseOrderStatus
|
|
}
|
|
|
|
export const purchaseOrders = {
|
|
list: (params?: PurchaseOrderListParams) =>
|
|
api.get<PaginatedResponse<PurchaseOrderResponse>>('/purchase-orders', { params }).then(r => r.data),
|
|
|
|
get: (id: string) =>
|
|
api.get<PurchaseOrderResponse>(`/purchase-orders/${id}`).then(r => r.data),
|
|
|
|
create: (body: PurchaseOrderBody) =>
|
|
api.post<PurchaseOrderResponse>('/purchase-orders', body).then(r => r.data),
|
|
|
|
update: (id: string, body: UpdatePurchaseOrderBody) =>
|
|
api.put<PurchaseOrderResponse>(`/purchase-orders/${id}`, body).then(r => r.data),
|
|
|
|
cancel: (id: string, reason: string) =>
|
|
api.post<PurchaseOrderResponse>(`/purchase-orders/${id}/cancel`, { reason }).then(r => r.data),
|
|
}
|
|
|
|
// ─── Goods Receipts (GRN) ─────────────────────────────────────────────────────
|
|
|
|
export interface GrnBatchLineBody {
|
|
/** Null for an expiry-only lot (item tracks expiry but not batch numbers). */
|
|
batchNumber: string | null
|
|
expiryDate: string | null
|
|
quantity: number
|
|
}
|
|
|
|
export interface GrnItemBody {
|
|
/** Set for a PO receipt; omitted for a direct receipt (itemId is used instead). */
|
|
purchaseOrderItemId?: string
|
|
/** Set for a direct receipt line. */
|
|
itemId?: string
|
|
acceptedQuantity: number
|
|
rejectedQuantity: number
|
|
/** Only for items with TracksBatches and/or TracksExpiryDate. */
|
|
batches?: GrnBatchLineBody[]
|
|
/** Only for items with TracksSerialNumbers — exactly acceptedQuantity entries. */
|
|
serialNumbers?: string[]
|
|
/** Cost per base-UOM unit. Omitted on a PO line falls back to the PO line's cost. */
|
|
unitCost?: number
|
|
/** Omitted falls back to the GRN-wide percentage. */
|
|
discountPercent?: number
|
|
vatPercent?: number
|
|
/** Free-issue units: stocked but not costed. Serials/batches must cover accepted + free. */
|
|
freeQuantity?: number
|
|
}
|
|
|
|
export interface GrnBody {
|
|
purchaseOrderId: string
|
|
warehouseId: string
|
|
receivedAt: string
|
|
items: GrnItemBody[]
|
|
/** GRN-wide defaults for lines that don't set their own. */
|
|
discountPercent?: number
|
|
vatPercent?: number
|
|
// TEMPORARY: required while auth doesn't resolve a logged-in user server-side — see testbug.md #28.
|
|
receivedByUserId: string
|
|
}
|
|
|
|
export interface DirectGrnBody {
|
|
warehouseId: string
|
|
supplierId?: string
|
|
items: GrnItemBody[]
|
|
discountPercent?: number
|
|
vatPercent?: number
|
|
// TEMPORARY: required while auth doesn't resolve a logged-in user server-side — see testbug.md #28.
|
|
receivedByUserId: string
|
|
}
|
|
|
|
export const grn = {
|
|
list: (params?: PageParams & { purchaseOrderId?: string }) =>
|
|
api.get<PaginatedResponse<GrnResponse>>('/goods-receipts', { params }).then(r => r.data),
|
|
|
|
get: (id: string) =>
|
|
api.get<GrnResponse>(`/goods-receipts/${id}`).then(r => r.data),
|
|
|
|
create: (body: GrnBody) =>
|
|
api.post<GrnResponse>('/goods-receipts', body).then(r => r.data),
|
|
|
|
/** Receive goods with no purchase order. */
|
|
createDirect: (body: DirectGrnBody) =>
|
|
api.post<GrnResponse>('/goods-receipts/direct', body).then(r => r.data),
|
|
}
|
|
|
|
// ─── Purchase Returns ─────────────────────────────────────────────────────────
|
|
|
|
export interface PurchaseReturnItemBody {
|
|
itemId: string
|
|
quantity: number
|
|
}
|
|
|
|
export interface PurchaseReturnBody {
|
|
reason: string
|
|
items: PurchaseReturnItemBody[]
|
|
// TEMPORARY: required while auth doesn't resolve a logged-in user server-side — see testbug.md #28.
|
|
createdByUserId: string
|
|
}
|
|
|
|
export const purchaseReturns = {
|
|
get: (id: string) =>
|
|
api.get<PurchaseReturnResponse>(`/purchase-returns/${id}`).then(r => r.data),
|
|
|
|
listByPurchaseOrder: (purchaseOrderId: string) =>
|
|
api.get<PurchaseReturnResponse[]>(`/purchase-orders/${purchaseOrderId}/returns`).then(r => r.data),
|
|
|
|
create: (purchaseOrderId: string, body: PurchaseReturnBody) =>
|
|
api.post<PurchaseReturnResponse>(`/purchase-orders/${purchaseOrderId}/returns`, body).then(r => r.data),
|
|
}
|