336 lines
11 KiB
TypeScript
336 lines
11 KiB
TypeScript
|
|
// ================================================================
|
||
|
|
// frontend/src/api/services/procurement-service.ts
|
||
|
|
// ================================================================
|
||
|
|
/**
|
||
|
|
* Procurement Service - Fully aligned with backend Procurement Service API
|
||
|
|
*
|
||
|
|
* Backend API: services/procurement/app/api/
|
||
|
|
* - procurement_plans.py: Plan CRUD and generation
|
||
|
|
* - analytics.py: Analytics and dashboard
|
||
|
|
* - purchase_orders.py: PO creation from plans
|
||
|
|
*
|
||
|
|
* Base URL: /api/v1/tenants/{tenant_id}/procurement/*
|
||
|
|
*
|
||
|
|
* Last Updated: 2025-10-31
|
||
|
|
* Status: ✅ Complete - 100% backend alignment
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { apiClient } from '../client/apiClient';
|
||
|
|
import {
|
||
|
|
// Procurement Plan types
|
||
|
|
ProcurementPlanResponse,
|
||
|
|
ProcurementPlanCreate,
|
||
|
|
ProcurementPlanUpdate,
|
||
|
|
PaginatedProcurementPlans,
|
||
|
|
|
||
|
|
// Procurement Requirement types
|
||
|
|
ProcurementRequirementResponse,
|
||
|
|
ProcurementRequirementUpdate,
|
||
|
|
|
||
|
|
// Dashboard & Analytics types
|
||
|
|
ProcurementDashboardData,
|
||
|
|
ProcurementTrendsData,
|
||
|
|
|
||
|
|
// Request/Response types
|
||
|
|
GeneratePlanRequest,
|
||
|
|
GeneratePlanResponse,
|
||
|
|
AutoGenerateProcurementRequest,
|
||
|
|
AutoGenerateProcurementResponse,
|
||
|
|
CreatePOsResult,
|
||
|
|
LinkRequirementToPORequest,
|
||
|
|
UpdateDeliveryStatusRequest,
|
||
|
|
ApprovalRequest,
|
||
|
|
RejectionRequest,
|
||
|
|
|
||
|
|
// Query parameter types
|
||
|
|
GetProcurementPlansParams,
|
||
|
|
GetPlanRequirementsParams,
|
||
|
|
UpdatePlanStatusParams,
|
||
|
|
} from '../types/procurement';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Procurement Service
|
||
|
|
* All methods use the standalone Procurement Service backend API
|
||
|
|
*/
|
||
|
|
export class ProcurementService {
|
||
|
|
|
||
|
|
// ===================================================================
|
||
|
|
// ANALYTICS & DASHBOARD
|
||
|
|
// Backend: services/procurement/app/api/analytics.py
|
||
|
|
// ===================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get procurement analytics dashboard data
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/analytics/procurement
|
||
|
|
*/
|
||
|
|
static async getProcurementAnalytics(tenantId: string): Promise<ProcurementDashboardData> {
|
||
|
|
return apiClient.get<ProcurementDashboardData>(`/tenants/${tenantId}/procurement/analytics/procurement`);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get procurement time-series trends for charts
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/analytics/procurement/trends
|
||
|
|
*/
|
||
|
|
static async getProcurementTrends(tenantId: string, days: number = 7): Promise<ProcurementTrendsData> {
|
||
|
|
return apiClient.get<ProcurementTrendsData>(`/tenants/${tenantId}/procurement/analytics/procurement/trends?days=${days}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================================================================
|
||
|
|
// PROCUREMENT PLAN GENERATION
|
||
|
|
// Backend: services/procurement/app/api/procurement_plans.py
|
||
|
|
// ===================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Auto-generate procurement plan from forecast data (Orchestrator integration)
|
||
|
|
* POST /api/v1/tenants/{tenant_id}/procurement/auto-generate
|
||
|
|
*
|
||
|
|
* Called by Orchestrator Service to create procurement plans based on forecast data
|
||
|
|
*/
|
||
|
|
static async autoGenerateProcurement(
|
||
|
|
tenantId: string,
|
||
|
|
request: AutoGenerateProcurementRequest
|
||
|
|
): Promise<AutoGenerateProcurementResponse> {
|
||
|
|
return apiClient.post<AutoGenerateProcurementResponse>(
|
||
|
|
`/tenants/${tenantId}/procurement/auto-generate`,
|
||
|
|
request
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Generate a new procurement plan (manual/UI-driven)
|
||
|
|
* POST /api/v1/tenants/{tenant_id}/procurement/plans/generate
|
||
|
|
*/
|
||
|
|
static async generateProcurementPlan(
|
||
|
|
tenantId: string,
|
||
|
|
request: GeneratePlanRequest
|
||
|
|
): Promise<GeneratePlanResponse> {
|
||
|
|
return apiClient.post<GeneratePlanResponse>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/generate`,
|
||
|
|
request
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================================================================
|
||
|
|
// PROCUREMENT PLAN CRUD
|
||
|
|
// Backend: services/procurement/app/api/procurement_plans.py
|
||
|
|
// ===================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the current day's procurement plan
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/plans/current
|
||
|
|
*/
|
||
|
|
static async getCurrentProcurementPlan(tenantId: string): Promise<ProcurementPlanResponse | null> {
|
||
|
|
return apiClient.get<ProcurementPlanResponse | null>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/current`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get procurement plan by ID
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}
|
||
|
|
*/
|
||
|
|
static async getProcurementPlanById(
|
||
|
|
tenantId: string,
|
||
|
|
planId: string
|
||
|
|
): Promise<ProcurementPlanResponse | null> {
|
||
|
|
return apiClient.get<ProcurementPlanResponse | null>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/${planId}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get procurement plan for a specific date
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/plans/date/{plan_date}
|
||
|
|
*/
|
||
|
|
static async getProcurementPlanByDate(
|
||
|
|
tenantId: string,
|
||
|
|
planDate: string
|
||
|
|
): Promise<ProcurementPlanResponse | null> {
|
||
|
|
return apiClient.get<ProcurementPlanResponse | null>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/date/${planDate}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* List all procurement plans for tenant with pagination and filtering
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/plans
|
||
|
|
*/
|
||
|
|
static async getProcurementPlans(params: GetProcurementPlansParams): Promise<PaginatedProcurementPlans> {
|
||
|
|
const { tenant_id, status, start_date, end_date, limit = 50, offset = 0 } = params;
|
||
|
|
|
||
|
|
const queryParams = new URLSearchParams({
|
||
|
|
limit: limit.toString(),
|
||
|
|
offset: offset.toString(),
|
||
|
|
});
|
||
|
|
|
||
|
|
if (status) queryParams.append('status', status);
|
||
|
|
if (start_date) queryParams.append('start_date', start_date);
|
||
|
|
if (end_date) queryParams.append('end_date', end_date);
|
||
|
|
|
||
|
|
return apiClient.get<PaginatedProcurementPlans>(
|
||
|
|
`/tenants/${tenant_id}/procurement/plans?${queryParams.toString()}`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update procurement plan status
|
||
|
|
* PATCH /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}/status
|
||
|
|
*/
|
||
|
|
static async updateProcurementPlanStatus(params: UpdatePlanStatusParams): Promise<ProcurementPlanResponse> {
|
||
|
|
const { tenant_id, plan_id, status, notes } = params;
|
||
|
|
|
||
|
|
const queryParams = new URLSearchParams({ status });
|
||
|
|
if (notes) queryParams.append('notes', notes);
|
||
|
|
|
||
|
|
return apiClient.patch<ProcurementPlanResponse>(
|
||
|
|
`/tenants/${tenant_id}/procurement/plans/${plan_id}/status?${queryParams.toString()}`,
|
||
|
|
{}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================================================================
|
||
|
|
// PROCUREMENT REQUIREMENTS
|
||
|
|
// Backend: services/procurement/app/api/procurement_plans.py
|
||
|
|
// ===================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get all requirements for a procurement plan
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}/requirements
|
||
|
|
*/
|
||
|
|
static async getPlanRequirements(params: GetPlanRequirementsParams): Promise<ProcurementRequirementResponse[]> {
|
||
|
|
const { tenant_id, plan_id, status, priority } = params;
|
||
|
|
|
||
|
|
const queryParams = new URLSearchParams();
|
||
|
|
if (status) queryParams.append('status', status);
|
||
|
|
if (priority) queryParams.append('priority', priority);
|
||
|
|
|
||
|
|
const url = `/tenants/${tenant_id}/procurement/plans/${plan_id}/requirements${
|
||
|
|
queryParams.toString() ? `?${queryParams.toString()}` : ''
|
||
|
|
}`;
|
||
|
|
|
||
|
|
return apiClient.get<ProcurementRequirementResponse[]>(url);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get critical requirements across all plans
|
||
|
|
* GET /api/v1/tenants/{tenant_id}/procurement/requirements/critical
|
||
|
|
*/
|
||
|
|
static async getCriticalRequirements(tenantId: string): Promise<ProcurementRequirementResponse[]> {
|
||
|
|
return apiClient.get<ProcurementRequirementResponse[]>(
|
||
|
|
`/tenants/${tenantId}/procurement/requirements/critical`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Link a procurement requirement to a purchase order
|
||
|
|
* POST /api/v1/tenants/{tenant_id}/procurement/requirements/{requirement_id}/link-purchase-order
|
||
|
|
*/
|
||
|
|
static async linkRequirementToPurchaseOrder(
|
||
|
|
tenantId: string,
|
||
|
|
requirementId: string,
|
||
|
|
request: LinkRequirementToPORequest
|
||
|
|
): Promise<{ success: boolean; message: string; requirement_id: string; purchase_order_id: string }> {
|
||
|
|
return apiClient.post<{
|
||
|
|
success: boolean;
|
||
|
|
message: string;
|
||
|
|
requirement_id: string;
|
||
|
|
purchase_order_id: string;
|
||
|
|
}>(
|
||
|
|
`/tenants/${tenantId}/procurement/requirements/${requirementId}/link-purchase-order`,
|
||
|
|
request
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update delivery status for a requirement
|
||
|
|
* PUT /api/v1/tenants/{tenant_id}/procurement/requirements/{requirement_id}/delivery-status
|
||
|
|
*/
|
||
|
|
static async updateRequirementDeliveryStatus(
|
||
|
|
tenantId: string,
|
||
|
|
requirementId: string,
|
||
|
|
request: UpdateDeliveryStatusRequest
|
||
|
|
): Promise<{ success: boolean; message: string; requirement_id: string; delivery_status: string }> {
|
||
|
|
return apiClient.put<{
|
||
|
|
success: boolean;
|
||
|
|
message: string;
|
||
|
|
requirement_id: string;
|
||
|
|
delivery_status: string;
|
||
|
|
}>(
|
||
|
|
`/tenants/${tenantId}/procurement/requirements/${requirementId}/delivery-status`,
|
||
|
|
request
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================================================================
|
||
|
|
// ADVANCED PROCUREMENT OPERATIONS
|
||
|
|
// Backend: services/procurement/app/api/procurement_plans.py
|
||
|
|
// ===================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Recalculate an existing procurement plan
|
||
|
|
* POST /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}/recalculate
|
||
|
|
*/
|
||
|
|
static async recalculateProcurementPlan(
|
||
|
|
tenantId: string,
|
||
|
|
planId: string
|
||
|
|
): Promise<GeneratePlanResponse> {
|
||
|
|
return apiClient.post<GeneratePlanResponse>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/${planId}/recalculate`,
|
||
|
|
{}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Approve a procurement plan with optional notes
|
||
|
|
* POST /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}/approve
|
||
|
|
*/
|
||
|
|
static async approveProcurementPlan(
|
||
|
|
tenantId: string,
|
||
|
|
planId: string,
|
||
|
|
request?: ApprovalRequest
|
||
|
|
): Promise<ProcurementPlanResponse> {
|
||
|
|
return apiClient.post<ProcurementPlanResponse>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/${planId}/approve`,
|
||
|
|
request || {}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Reject a procurement plan with optional notes
|
||
|
|
* POST /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}/reject
|
||
|
|
*/
|
||
|
|
static async rejectProcurementPlan(
|
||
|
|
tenantId: string,
|
||
|
|
planId: string,
|
||
|
|
request?: RejectionRequest
|
||
|
|
): Promise<ProcurementPlanResponse> {
|
||
|
|
return apiClient.post<ProcurementPlanResponse>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/${planId}/reject`,
|
||
|
|
request || {}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ===================================================================
|
||
|
|
// PURCHASE ORDERS
|
||
|
|
// Backend: services/procurement/app/api/purchase_orders.py
|
||
|
|
// ===================================================================
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create purchase orders from procurement plan requirements
|
||
|
|
* Groups requirements by supplier and creates POs
|
||
|
|
* POST /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}/create-purchase-orders
|
||
|
|
*/
|
||
|
|
static async createPurchaseOrdersFromPlan(
|
||
|
|
tenantId: string,
|
||
|
|
planId: string,
|
||
|
|
autoApprove: boolean = false
|
||
|
|
): Promise<CreatePOsResult> {
|
||
|
|
return apiClient.post<CreatePOsResult>(
|
||
|
|
`/tenants/${tenantId}/procurement/plans/${planId}/create-purchase-orders`,
|
||
|
|
{ auto_approve: autoApprove }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export default ProcurementService;
|