346 lines
8.1 KiB
TypeScript
346 lines
8.1 KiB
TypeScript
/**
|
|
* Purchase Orders API Client
|
|
* Handles all API calls for purchase orders
|
|
*
|
|
* UPDATED in Sprint 3: Purchase orders now managed by Procurement Service
|
|
* Previously: Suppliers Service (/tenants/{id}/purchase-orders)
|
|
* Now: Procurement Service (/tenants/{id}/procurement/purchase-orders)
|
|
*/
|
|
|
|
import { apiClient } from '../client';
|
|
|
|
export type PurchaseOrderStatus =
|
|
| 'DRAFT'
|
|
| 'PENDING_APPROVAL'
|
|
| 'APPROVED'
|
|
| 'SENT_TO_SUPPLIER'
|
|
| 'CONFIRMED'
|
|
| 'RECEIVED'
|
|
| 'COMPLETED'
|
|
| 'CANCELLED'
|
|
| 'DISPUTED';
|
|
|
|
export type PurchaseOrderPriority = 'urgent' | 'high' | 'normal' | 'low';
|
|
|
|
export interface PurchaseOrderItem {
|
|
id: string;
|
|
inventory_product_id: string;
|
|
product_code?: string;
|
|
product_name?: string;
|
|
ordered_quantity: number;
|
|
unit_of_measure: string;
|
|
unit_price: string; // Decimal as string
|
|
line_total: string; // Decimal as string
|
|
received_quantity: number;
|
|
remaining_quantity: number;
|
|
quality_requirements?: string;
|
|
item_notes?: string;
|
|
}
|
|
|
|
export interface SupplierSummary {
|
|
id: string;
|
|
name: string;
|
|
supplier_code: string;
|
|
supplier_type: string;
|
|
status: string;
|
|
contact_person?: string;
|
|
email?: string;
|
|
phone?: string;
|
|
}
|
|
|
|
export interface PurchaseOrderSummary {
|
|
id: string;
|
|
po_number: string;
|
|
supplier_id: string;
|
|
supplier_name?: string;
|
|
status: PurchaseOrderStatus;
|
|
priority: PurchaseOrderPriority;
|
|
order_date: string;
|
|
required_delivery_date?: string;
|
|
total_amount: string; // Decimal as string
|
|
currency: string;
|
|
created_at: string;
|
|
reasoning_data?: any; // AI reasoning data for dashboard display
|
|
ai_reasoning_summary?: string; // Human-readable summary
|
|
}
|
|
|
|
export interface PurchaseOrderDetail extends PurchaseOrderSummary {
|
|
reference_number?: string;
|
|
estimated_delivery_date?: string;
|
|
|
|
// Financial information
|
|
subtotal: string;
|
|
tax_amount: string;
|
|
shipping_cost: string;
|
|
discount_amount: string;
|
|
|
|
// Delivery information
|
|
delivery_address?: string;
|
|
delivery_instructions?: string;
|
|
delivery_contact?: string;
|
|
delivery_phone?: string;
|
|
|
|
// Approval workflow
|
|
requires_approval: boolean;
|
|
approved_by?: string;
|
|
approved_at?: string;
|
|
rejection_reason?: string;
|
|
|
|
// Communication tracking
|
|
sent_to_supplier_at?: string;
|
|
supplier_confirmation_date?: string;
|
|
supplier_reference?: string;
|
|
|
|
// Additional information
|
|
notes?: string;
|
|
internal_notes?: string;
|
|
terms_and_conditions?: string;
|
|
|
|
// Audit fields
|
|
updated_at: string;
|
|
created_by: string;
|
|
updated_by: string;
|
|
|
|
// Related data
|
|
supplier?: SupplierSummary;
|
|
items?: PurchaseOrderItem[];
|
|
}
|
|
|
|
export interface PurchaseOrderSearchParams {
|
|
supplier_id?: string;
|
|
status?: PurchaseOrderStatus;
|
|
priority?: PurchaseOrderPriority;
|
|
date_from?: string; // YYYY-MM-DD
|
|
date_to?: string; // YYYY-MM-DD
|
|
search_term?: string;
|
|
limit?: number;
|
|
skip?: number; // ✅ Changed from "offset" to "skip" to match backend
|
|
}
|
|
|
|
export interface PurchaseOrderUpdateData {
|
|
status?: PurchaseOrderStatus;
|
|
priority?: PurchaseOrderPriority;
|
|
notes?: string;
|
|
rejection_reason?: string;
|
|
internal_notes?: string;
|
|
}
|
|
|
|
export interface PurchaseOrderItemCreate {
|
|
inventory_product_id: string;
|
|
ordered_quantity: number;
|
|
unit_price: string; // Decimal as string
|
|
unit_of_measure: string;
|
|
quality_requirements?: string;
|
|
item_notes?: string;
|
|
}
|
|
|
|
export interface PurchaseOrderCreateData {
|
|
supplier_id: string;
|
|
required_delivery_date?: string;
|
|
priority?: PurchaseOrderPriority;
|
|
tax_amount?: number;
|
|
shipping_cost?: number;
|
|
discount_amount?: number;
|
|
notes?: string;
|
|
procurement_plan_id?: string;
|
|
items: PurchaseOrderItemCreate[];
|
|
}
|
|
|
|
/**
|
|
* Create a new purchase order
|
|
*/
|
|
export async function createPurchaseOrder(
|
|
tenantId: string,
|
|
data: PurchaseOrderCreateData
|
|
): Promise<PurchaseOrderDetail> {
|
|
return apiClient.post<PurchaseOrderDetail>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders`,
|
|
data
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get list of purchase orders with optional filters
|
|
*/
|
|
export async function listPurchaseOrders(
|
|
tenantId: string,
|
|
params?: PurchaseOrderSearchParams
|
|
): Promise<PurchaseOrderSummary[]> {
|
|
return apiClient.get<PurchaseOrderSummary[]>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders`,
|
|
{ params }
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get purchase orders by status
|
|
*/
|
|
export async function getPurchaseOrdersByStatus(
|
|
tenantId: string,
|
|
status: PurchaseOrderStatus,
|
|
limit: number = 50
|
|
): Promise<PurchaseOrderSummary[]> {
|
|
return listPurchaseOrders(tenantId, { status, limit });
|
|
}
|
|
|
|
/**
|
|
* Get pending approval purchase orders
|
|
*/
|
|
export async function getPendingApprovalPurchaseOrders(
|
|
tenantId: string,
|
|
limit: number = 50
|
|
): Promise<PurchaseOrderSummary[]> {
|
|
return getPurchaseOrdersByStatus(tenantId, 'PENDING_APPROVAL', limit);
|
|
}
|
|
|
|
/**
|
|
* Get a single purchase order by ID with full details
|
|
*/
|
|
export async function getPurchaseOrder(
|
|
tenantId: string,
|
|
poId: string
|
|
): Promise<PurchaseOrderDetail> {
|
|
return apiClient.get<PurchaseOrderDetail>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders/${poId}`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Update purchase order
|
|
*/
|
|
export async function updatePurchaseOrder(
|
|
tenantId: string,
|
|
poId: string,
|
|
data: PurchaseOrderUpdateData
|
|
): Promise<PurchaseOrderDetail> {
|
|
return apiClient.put<PurchaseOrderDetail>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders/${poId}`,
|
|
data
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Approve a purchase order
|
|
*/
|
|
export async function approvePurchaseOrder(
|
|
tenantId: string,
|
|
poId: string,
|
|
notes?: string
|
|
): Promise<PurchaseOrderDetail> {
|
|
return apiClient.post<PurchaseOrderDetail>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders/${poId}/approve`,
|
|
{
|
|
action: 'approve',
|
|
notes: notes || 'Approved from dashboard'
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Reject a purchase order
|
|
*/
|
|
export async function rejectPurchaseOrder(
|
|
tenantId: string,
|
|
poId: string,
|
|
reason: string
|
|
): Promise<PurchaseOrderDetail> {
|
|
return apiClient.post<PurchaseOrderDetail>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders/${poId}/approve`,
|
|
{
|
|
action: 'reject',
|
|
notes: reason
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Bulk approve purchase orders
|
|
*/
|
|
export async function bulkApprovePurchaseOrders(
|
|
tenantId: string,
|
|
poIds: string[],
|
|
notes?: string
|
|
): Promise<PurchaseOrderDetail[]> {
|
|
const approvalPromises = poIds.map(poId =>
|
|
approvePurchaseOrder(tenantId, poId, notes)
|
|
);
|
|
return Promise.all(approvalPromises);
|
|
}
|
|
|
|
/**
|
|
* Delete purchase order
|
|
*/
|
|
export async function deletePurchaseOrder(
|
|
tenantId: string,
|
|
poId: string
|
|
): Promise<{ message: string }> {
|
|
return apiClient.delete<{ message: string }>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders/${poId}`
|
|
);
|
|
}
|
|
|
|
// ================================================================
|
|
// DELIVERY TYPES AND METHODS
|
|
// ================================================================
|
|
|
|
export interface DeliveryItemInput {
|
|
purchase_order_item_id: string;
|
|
inventory_product_id: string;
|
|
ordered_quantity: number;
|
|
delivered_quantity: number;
|
|
accepted_quantity: number;
|
|
rejected_quantity: number;
|
|
batch_lot_number?: string;
|
|
expiry_date?: string;
|
|
quality_grade?: string;
|
|
quality_issues?: string;
|
|
rejection_reason?: string;
|
|
item_notes?: string;
|
|
}
|
|
|
|
export interface CreateDeliveryInput {
|
|
purchase_order_id: string;
|
|
supplier_id: string;
|
|
supplier_delivery_note?: string;
|
|
scheduled_date?: string;
|
|
estimated_arrival?: string;
|
|
carrier_name?: string;
|
|
tracking_number?: string;
|
|
inspection_passed?: boolean;
|
|
inspection_notes?: string;
|
|
notes?: string;
|
|
items: DeliveryItemInput[];
|
|
}
|
|
|
|
export interface DeliveryResponse {
|
|
id: string;
|
|
tenant_id: string;
|
|
purchase_order_id: string;
|
|
supplier_id: string;
|
|
delivery_number: string;
|
|
status: string;
|
|
scheduled_date?: string;
|
|
estimated_arrival?: string;
|
|
actual_arrival?: string;
|
|
completed_at?: string;
|
|
inspection_passed?: boolean;
|
|
inspection_notes?: string;
|
|
notes?: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
/**
|
|
* Create delivery for purchase order
|
|
*/
|
|
export async function createDelivery(
|
|
tenantId: string,
|
|
poId: string,
|
|
deliveryData: CreateDeliveryInput
|
|
): Promise<DeliveryResponse> {
|
|
return apiClient.post<DeliveryResponse>(
|
|
`/tenants/${tenantId}/procurement/purchase-orders/${poId}/deliveries`,
|
|
deliveryData
|
|
);
|
|
}
|