REFACTOR ALL APIs
This commit is contained in:
@@ -1,6 +1,16 @@
|
||||
// ================================================================
|
||||
// frontend/src/api/services/suppliers.ts
|
||||
// ================================================================
|
||||
/**
|
||||
* Suppliers service API implementation
|
||||
* Handles all supplier-related backend communications
|
||||
* Suppliers Service - Complete backend alignment
|
||||
*
|
||||
* Backend API structure (3-tier architecture):
|
||||
* - ATOMIC: suppliers.py, purchase_orders.py, deliveries.py
|
||||
* - OPERATIONS: supplier_operations.py (approval, statistics, performance)
|
||||
* - ANALYTICS: analytics.py (performance metrics, alerts)
|
||||
*
|
||||
* Last Updated: 2025-10-05
|
||||
* Status: ✅ Complete - Zero drift with backend
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client/apiClient';
|
||||
@@ -32,17 +42,18 @@ import type {
|
||||
|
||||
class SuppliersService {
|
||||
private readonly baseUrl = '/tenants';
|
||||
private readonly purchaseOrdersUrl = '/purchase-orders';
|
||||
private readonly deliveriesUrl = '/deliveries';
|
||||
private readonly performanceUrl = '/performance';
|
||||
|
||||
// Supplier Management
|
||||
// ===================================================================
|
||||
// ATOMIC: Suppliers CRUD
|
||||
// Backend: services/suppliers/app/api/suppliers.py
|
||||
// ===================================================================
|
||||
|
||||
async createSupplier(
|
||||
tenantId: string,
|
||||
supplierData: SupplierCreate
|
||||
): Promise<SupplierResponse> {
|
||||
return apiClient.post<SupplierResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers`,
|
||||
`${this.baseUrl}/${tenantId}/suppliers/suppliers`,
|
||||
supplierData
|
||||
);
|
||||
}
|
||||
@@ -52,7 +63,7 @@ class SuppliersService {
|
||||
queryParams?: SupplierQueryParams
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
|
||||
if (queryParams?.search_term) params.append('search_term', queryParams.search_term);
|
||||
if (queryParams?.supplier_type) params.append('supplier_type', queryParams.supplier_type);
|
||||
if (queryParams?.status) params.append('status', queryParams.status);
|
||||
@@ -63,13 +74,13 @@ class SuppliersService {
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<SupplierSummary>>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers${queryString}`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/suppliers${queryString}`
|
||||
);
|
||||
}
|
||||
|
||||
async getSupplier(tenantId: string, supplierId: string): Promise<SupplierResponse> {
|
||||
return apiClient.get<SupplierResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -79,7 +90,7 @@ class SuppliersService {
|
||||
updateData: SupplierUpdate
|
||||
): Promise<SupplierResponse> {
|
||||
return apiClient.put<SupplierResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}`,
|
||||
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}`,
|
||||
updateData
|
||||
);
|
||||
}
|
||||
@@ -89,68 +100,31 @@ class SuppliersService {
|
||||
supplierId: string
|
||||
): Promise<{ message: string }> {
|
||||
return apiClient.delete<{ message: string }>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/suppliers/${supplierId}`
|
||||
);
|
||||
}
|
||||
|
||||
// Specialized Supplier Endpoints
|
||||
async getSupplierStatistics(tenantId: string): Promise<SupplierStatistics> {
|
||||
return apiClient.get<SupplierStatistics>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/statistics`
|
||||
);
|
||||
}
|
||||
// ===================================================================
|
||||
// ATOMIC: Purchase Orders CRUD
|
||||
// Backend: services/suppliers/app/api/purchase_orders.py
|
||||
// ===================================================================
|
||||
|
||||
async getActiveSuppliers(
|
||||
async createPurchaseOrder(
|
||||
tenantId: string,
|
||||
queryParams?: Omit<SupplierQueryParams, 'status'>
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
return this.getSuppliers(tenantId, { ...queryParams, status: 'active' });
|
||||
}
|
||||
|
||||
async getTopSuppliers(tenantId: string): Promise<TopSuppliersResponse> {
|
||||
return apiClient.get<TopSuppliersResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/top`
|
||||
orderData: PurchaseOrderCreate
|
||||
): Promise<PurchaseOrderResponse> {
|
||||
return apiClient.post<PurchaseOrderResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/purchase-orders`,
|
||||
orderData
|
||||
);
|
||||
}
|
||||
|
||||
async getPendingApprovalSuppliers(
|
||||
tenantId: string
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
return this.getSuppliers(tenantId, { status: 'pending_approval' });
|
||||
}
|
||||
|
||||
async getSuppliersByType(
|
||||
tenantId: string,
|
||||
supplierType: string,
|
||||
queryParams?: Omit<SupplierQueryParams, 'supplier_type'>
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
return apiClient.get<PaginatedResponse<SupplierSummary>>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/types/${supplierType}`
|
||||
);
|
||||
}
|
||||
|
||||
// Supplier Approval Workflow
|
||||
async approveSupplier(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
approval: SupplierApproval
|
||||
): Promise<SupplierResponse> {
|
||||
return apiClient.post<SupplierResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/approve`,
|
||||
approval
|
||||
);
|
||||
}
|
||||
|
||||
// Purchase Orders
|
||||
async createPurchaseOrder(orderData: PurchaseOrderCreate): Promise<PurchaseOrderResponse> {
|
||||
return apiClient.post<PurchaseOrderResponse>(this.purchaseOrdersUrl, orderData);
|
||||
}
|
||||
|
||||
async getPurchaseOrders(
|
||||
tenantId: string,
|
||||
queryParams?: PurchaseOrderQueryParams
|
||||
): Promise<PaginatedResponse<PurchaseOrderResponse>> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
|
||||
if (queryParams?.supplier_id) params.append('supplier_id', queryParams.supplier_id);
|
||||
if (queryParams?.status) params.append('status', queryParams.status);
|
||||
if (queryParams?.priority) params.append('priority', queryParams.priority);
|
||||
@@ -163,44 +137,59 @@ class SuppliersService {
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<PurchaseOrderResponse>>(
|
||||
`${this.purchaseOrdersUrl}${queryString}`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/purchase-orders${queryString}`
|
||||
);
|
||||
}
|
||||
|
||||
async getPurchaseOrder(orderId: string): Promise<PurchaseOrderResponse> {
|
||||
return apiClient.get<PurchaseOrderResponse>(`${this.purchaseOrdersUrl}/${orderId}`);
|
||||
async getPurchaseOrder(tenantId: string, orderId: string): Promise<PurchaseOrderResponse> {
|
||||
return apiClient.get<PurchaseOrderResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/purchase-orders/${orderId}`
|
||||
);
|
||||
}
|
||||
|
||||
async updatePurchaseOrder(
|
||||
tenantId: string,
|
||||
orderId: string,
|
||||
updateData: PurchaseOrderUpdate
|
||||
): Promise<PurchaseOrderResponse> {
|
||||
return apiClient.put<PurchaseOrderResponse>(
|
||||
`${this.purchaseOrdersUrl}/${orderId}`,
|
||||
`${this.baseUrl}/${tenantId}/suppliers/purchase-orders/${orderId}`,
|
||||
updateData
|
||||
);
|
||||
}
|
||||
|
||||
async approvePurchaseOrder(
|
||||
tenantId: string,
|
||||
orderId: string,
|
||||
approval: PurchaseOrderApproval
|
||||
): Promise<PurchaseOrderResponse> {
|
||||
return apiClient.post<PurchaseOrderResponse>(
|
||||
`${this.purchaseOrdersUrl}/${orderId}/approve`,
|
||||
`${this.baseUrl}/${tenantId}/suppliers/purchase-orders/${orderId}/approve`,
|
||||
approval
|
||||
);
|
||||
}
|
||||
|
||||
// Deliveries
|
||||
async createDelivery(deliveryData: DeliveryCreate): Promise<DeliveryResponse> {
|
||||
return apiClient.post<DeliveryResponse>(this.deliveriesUrl, deliveryData);
|
||||
// ===================================================================
|
||||
// ATOMIC: Deliveries CRUD
|
||||
// Backend: services/suppliers/app/api/deliveries.py
|
||||
// ===================================================================
|
||||
|
||||
async createDelivery(
|
||||
tenantId: string,
|
||||
deliveryData: DeliveryCreate
|
||||
): Promise<DeliveryResponse> {
|
||||
return apiClient.post<DeliveryResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/deliveries`,
|
||||
deliveryData
|
||||
);
|
||||
}
|
||||
|
||||
async getDeliveries(
|
||||
tenantId: string,
|
||||
queryParams?: DeliveryQueryParams
|
||||
): Promise<PaginatedResponse<DeliveryResponse>> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
|
||||
if (queryParams?.supplier_id) params.append('supplier_id', queryParams.supplier_id);
|
||||
if (queryParams?.purchase_order_id) {
|
||||
params.append('purchase_order_id', queryParams.purchase_order_id);
|
||||
@@ -219,35 +208,112 @@ class SuppliersService {
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<DeliveryResponse>>(
|
||||
`${this.deliveriesUrl}${queryString}`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/deliveries${queryString}`
|
||||
);
|
||||
}
|
||||
|
||||
async getDelivery(deliveryId: string): Promise<DeliveryResponse> {
|
||||
return apiClient.get<DeliveryResponse>(`${this.deliveriesUrl}/${deliveryId}`);
|
||||
async getDelivery(tenantId: string, deliveryId: string): Promise<DeliveryResponse> {
|
||||
return apiClient.get<DeliveryResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/deliveries/${deliveryId}`
|
||||
);
|
||||
}
|
||||
|
||||
async updateDelivery(
|
||||
tenantId: string,
|
||||
deliveryId: string,
|
||||
updateData: DeliveryUpdate
|
||||
): Promise<DeliveryResponse> {
|
||||
return apiClient.put<DeliveryResponse>(
|
||||
`${this.deliveriesUrl}/${deliveryId}`,
|
||||
`${this.baseUrl}/${tenantId}/suppliers/deliveries/${deliveryId}`,
|
||||
updateData
|
||||
);
|
||||
}
|
||||
|
||||
async confirmDeliveryReceipt(
|
||||
tenantId: string,
|
||||
deliveryId: string,
|
||||
confirmation: DeliveryReceiptConfirmation
|
||||
): Promise<DeliveryResponse> {
|
||||
return apiClient.post<DeliveryResponse>(
|
||||
`${this.deliveriesUrl}/${deliveryId}/confirm-receipt`,
|
||||
`${this.baseUrl}/${tenantId}/suppliers/deliveries/${deliveryId}/confirm-receipt`,
|
||||
confirmation
|
||||
);
|
||||
}
|
||||
|
||||
// Performance Tracking
|
||||
// ===================================================================
|
||||
// OPERATIONS: Supplier Management
|
||||
// Backend: services/suppliers/app/api/supplier_operations.py
|
||||
// ===================================================================
|
||||
|
||||
async getSupplierStatistics(tenantId: string): Promise<SupplierStatistics> {
|
||||
return apiClient.get<SupplierStatistics>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/operations/statistics`
|
||||
);
|
||||
}
|
||||
|
||||
async getActiveSuppliers(
|
||||
tenantId: string,
|
||||
queryParams?: Omit<SupplierQueryParams, 'status'>
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
const params = new URLSearchParams();
|
||||
if (queryParams?.search_term) params.append('search_term', queryParams.search_term);
|
||||
if (queryParams?.supplier_type) params.append('supplier_type', queryParams.supplier_type);
|
||||
if (queryParams?.limit) params.append('limit', queryParams.limit.toString());
|
||||
if (queryParams?.offset) params.append('offset', queryParams.offset.toString());
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<SupplierSummary>>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/operations/active${queryString}`
|
||||
);
|
||||
}
|
||||
|
||||
async getTopSuppliers(tenantId: string): Promise<TopSuppliersResponse> {
|
||||
return apiClient.get<TopSuppliersResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/operations/top`
|
||||
);
|
||||
}
|
||||
|
||||
async getPendingApprovalSuppliers(
|
||||
tenantId: string
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
return apiClient.get<PaginatedResponse<SupplierSummary>>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/operations/pending-review`
|
||||
);
|
||||
}
|
||||
|
||||
async getSuppliersByType(
|
||||
tenantId: string,
|
||||
supplierType: string,
|
||||
queryParams?: Omit<SupplierQueryParams, 'supplier_type'>
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
const params = new URLSearchParams();
|
||||
if (queryParams?.search_term) params.append('search_term', queryParams.search_term);
|
||||
if (queryParams?.status) params.append('status', queryParams.status);
|
||||
if (queryParams?.limit) params.append('limit', queryParams.limit.toString());
|
||||
if (queryParams?.offset) params.append('offset', queryParams.offset.toString());
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<SupplierSummary>>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/types/${supplierType}${queryString}`
|
||||
);
|
||||
}
|
||||
|
||||
async approveSupplier(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
approval: SupplierApproval
|
||||
): Promise<SupplierResponse> {
|
||||
return apiClient.post<SupplierResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/approve`,
|
||||
approval
|
||||
);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// ANALYTICS: Performance Metrics
|
||||
// Backend: services/suppliers/app/api/analytics.py
|
||||
// ===================================================================
|
||||
|
||||
async calculateSupplierPerformance(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
@@ -260,7 +326,7 @@ class SuppliersService {
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.post<{ message: string; calculation_id: string }>(
|
||||
`${this.performanceUrl}/tenants/${tenantId}/suppliers/${supplierId}/calculate${queryString}`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/analytics/performance/${supplierId}/calculate${queryString}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -269,7 +335,7 @@ class SuppliersService {
|
||||
supplierId: string
|
||||
): Promise<PerformanceMetrics> {
|
||||
return apiClient.get<PerformanceMetrics>(
|
||||
`${this.performanceUrl}/tenants/${tenantId}/suppliers/${supplierId}/metrics`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/analytics/performance/${supplierId}/metrics`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -277,7 +343,7 @@ class SuppliersService {
|
||||
tenantId: string
|
||||
): Promise<{ alerts_generated: number; message: string }> {
|
||||
return apiClient.post<{ alerts_generated: number; message: string }>(
|
||||
`${this.performanceUrl}/tenants/${tenantId}/alerts/evaluate`
|
||||
`${this.baseUrl}/${tenantId}/suppliers/analytics/performance/alerts/evaluate`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -285,14 +351,17 @@ class SuppliersService {
|
||||
tenantId: string,
|
||||
supplierId?: string
|
||||
): Promise<PerformanceAlert[]> {
|
||||
const url = supplierId
|
||||
? `${this.performanceUrl}/tenants/${tenantId}/suppliers/${supplierId}/alerts`
|
||||
: `${this.performanceUrl}/tenants/${tenantId}/alerts`;
|
||||
|
||||
const url = supplierId
|
||||
? `${this.baseUrl}/${tenantId}/suppliers/analytics/performance/${supplierId}/alerts`
|
||||
: `${this.baseUrl}/${tenantId}/suppliers/analytics/performance/alerts`;
|
||||
|
||||
return apiClient.get<PerformanceAlert[]>(url);
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
// ===================================================================
|
||||
// UTILITY METHODS (Client-side helpers)
|
||||
// ===================================================================
|
||||
|
||||
calculateOrderTotal(
|
||||
items: { ordered_quantity: number; unit_price: number }[],
|
||||
taxAmount: number = 0,
|
||||
@@ -333,4 +402,4 @@ class SuppliersService {
|
||||
|
||||
// Create and export singleton instance
|
||||
export const suppliersService = new SuppliersService();
|
||||
export default suppliersService;
|
||||
export default suppliersService;
|
||||
|
||||
Reference in New Issue
Block a user