Improve the frontend 4
This commit is contained in:
@@ -28,24 +28,6 @@ import {
|
||||
GetCustomersParams,
|
||||
UpdateOrderStatusParams,
|
||||
GetDemandRequirementsParams,
|
||||
// Procurement types
|
||||
ProcurementPlanResponse,
|
||||
ProcurementPlanCreate,
|
||||
ProcurementPlanUpdate,
|
||||
ProcurementRequirementResponse,
|
||||
ProcurementRequirementUpdate,
|
||||
ProcurementDashboardData,
|
||||
GeneratePlanRequest,
|
||||
GeneratePlanResponse,
|
||||
PaginatedProcurementPlans,
|
||||
GetProcurementPlansParams,
|
||||
GetPlanRequirementsParams,
|
||||
UpdatePlanStatusParams,
|
||||
CreatePOsResult,
|
||||
LinkRequirementToPORequest,
|
||||
UpdateDeliveryStatusRequest,
|
||||
ApprovalRequest,
|
||||
RejectionRequest,
|
||||
} from '../types/orders';
|
||||
|
||||
export class OrdersService {
|
||||
@@ -209,209 +191,6 @@ export class OrdersService {
|
||||
return apiClient.get<ServiceStatus>(`/tenants/${tenantId}/orders/operations/status`);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// OPERATIONS: Procurement Planning
|
||||
// Backend: services/orders/app/api/procurement_operations.py
|
||||
// ===================================================================
|
||||
|
||||
/**
|
||||
* Get current procurement plan for today
|
||||
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/current
|
||||
*/
|
||||
static async getCurrentProcurementPlan(tenantId: string): Promise<ProcurementPlanResponse | null> {
|
||||
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/operations/procurement/plans/current`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get procurement plan by specific date
|
||||
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/date/{plan_date}
|
||||
*/
|
||||
static async getProcurementPlanByDate(tenantId: string, planDate: string): Promise<ProcurementPlanResponse | null> {
|
||||
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/operations/procurement/plans/date/${planDate}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get procurement plan by ID
|
||||
* GET /tenants/{tenant_id}/orders/operations/procurement/plans/id/{plan_id}
|
||||
*/
|
||||
static async getProcurementPlanById(tenantId: string, planId: string): Promise<ProcurementPlanResponse | null> {
|
||||
return apiClient.get<ProcurementPlanResponse | null>(`/tenants/${tenantId}/orders/operations/procurement/plans/id/${planId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* List procurement plans with filtering
|
||||
* GET /tenants/{tenant_id}/orders/operations/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}/orders/operations/procurement/plans?${queryParams.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new procurement plan
|
||||
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/generate
|
||||
*/
|
||||
static async generateProcurementPlan(tenantId: string, request: GeneratePlanRequest): Promise<GeneratePlanResponse> {
|
||||
return apiClient.post<GeneratePlanResponse>(`/tenants/${tenantId}/orders/operations/procurement/plans/generate`, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update procurement plan status
|
||||
* PUT /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/status
|
||||
*/
|
||||
static async updateProcurementPlanStatus(params: UpdatePlanStatusParams): Promise<ProcurementPlanResponse> {
|
||||
const { tenant_id, plan_id, status } = params;
|
||||
|
||||
const queryParams = new URLSearchParams({ status });
|
||||
|
||||
return apiClient.put<ProcurementPlanResponse>(
|
||||
`/tenants/${tenant_id}/orders/operations/procurement/plans/${plan_id}/status?${queryParams.toString()}`,
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get procurement dashboard data
|
||||
* GET /tenants/{tenant_id}/orders/dashboard/procurement
|
||||
*/
|
||||
static async getProcurementDashboard(tenantId: string): Promise<ProcurementDashboardData | null> {
|
||||
return apiClient.get<ProcurementDashboardData | null>(`/tenants/${tenantId}/orders/dashboard/procurement`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get requirements for a specific plan
|
||||
* GET /tenants/{tenant_id}/orders/operations/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}/orders/operations/procurement/plans/${plan_id}/requirements${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
|
||||
|
||||
return apiClient.get<ProcurementRequirementResponse[]>(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get critical requirements across all plans
|
||||
* GET /tenants/{tenant_id}/orders/operations/procurement/requirements/critical
|
||||
*/
|
||||
static async getCriticalRequirements(tenantId: string): Promise<ProcurementRequirementResponse[]> {
|
||||
return apiClient.get<ProcurementRequirementResponse[]>(`/tenants/${tenantId}/orders/operations/procurement/requirements/critical`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger daily scheduler manually
|
||||
* POST /tenants/{tenant_id}/orders/operations/procurement/scheduler/trigger
|
||||
*/
|
||||
static async triggerDailyScheduler(tenantId: string): Promise<{ success: boolean; message: string; tenant_id: string }> {
|
||||
return apiClient.post<{ success: boolean; message: string; tenant_id: string }>(
|
||||
`/tenants/${tenantId}/orders/operations/procurement/scheduler/trigger`,
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get procurement service health
|
||||
* GET /tenants/{tenant_id}/orders/base/procurement/health
|
||||
*/
|
||||
static async getProcurementHealth(tenantId: string): Promise<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }> {
|
||||
return apiClient.get<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }>(`/tenants/${tenantId}/orders/base/procurement/health`);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// OPERATIONS: Advanced Procurement Features
|
||||
// Backend: services/orders/app/api/procurement_operations.py
|
||||
// ===================================================================
|
||||
|
||||
/**
|
||||
* Recalculate an existing procurement plan
|
||||
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/recalculate
|
||||
*/
|
||||
static async recalculateProcurementPlan(tenantId: string, planId: string): Promise<GeneratePlanResponse> {
|
||||
return apiClient.post<GeneratePlanResponse>(
|
||||
`/tenants/${tenantId}/orders/operations/procurement/plans/${planId}/recalculate`,
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve a procurement plan with notes
|
||||
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/approve
|
||||
*/
|
||||
static async approveProcurementPlan(tenantId: string, planId: string, request?: ApprovalRequest): Promise<ProcurementPlanResponse> {
|
||||
return apiClient.post<ProcurementPlanResponse>(
|
||||
`/tenants/${tenantId}/orders/operations/procurement/plans/${planId}/approve`,
|
||||
request || {}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject a procurement plan with notes
|
||||
* POST /tenants/{tenant_id}/orders/operations/procurement/plans/{plan_id}/reject
|
||||
*/
|
||||
static async rejectProcurementPlan(tenantId: string, planId: string, request?: RejectionRequest): Promise<ProcurementPlanResponse> {
|
||||
return apiClient.post<ProcurementPlanResponse>(
|
||||
`/tenants/${tenantId}/orders/operations/procurement/plans/${planId}/reject`,
|
||||
request || {}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create purchase orders automatically from procurement plan
|
||||
* POST /tenants/{tenant_id}/orders/operations/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}/orders/operations/procurement/plans/${planId}/create-purchase-orders`,
|
||||
{ auto_approve: autoApprove }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a procurement requirement to a purchase order
|
||||
* POST /tenants/{tenant_id}/orders/operations/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}/orders/operations/procurement/requirements/${requirementId}/link-purchase-order`,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update delivery status for a requirement
|
||||
* PUT /tenants/{tenant_id}/orders/operations/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}/orders/operations/procurement/requirements/${requirementId}/delivery-status`,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default OrdersService;
|
||||
|
||||
335
frontend/src/api/services/procurement-service.ts
Normal file
335
frontend/src/api/services/procurement-service.ts
Normal file
@@ -0,0 +1,335 @@
|
||||
// ================================================================
|
||||
// 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;
|
||||
@@ -1,317 +0,0 @@
|
||||
/**
|
||||
* Procurement Service API Client
|
||||
* Handles procurement planning and purchase order management
|
||||
*
|
||||
* NEW in Sprint 3: Procurement Service now owns all procurement operations
|
||||
* Previously these were split between Orders Service and Suppliers Service
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client';
|
||||
|
||||
// ============================================================================
|
||||
// PROCUREMENT PLAN TYPES
|
||||
// ============================================================================
|
||||
|
||||
export interface ProcurementRequirement {
|
||||
id: string;
|
||||
ingredient_id: string;
|
||||
ingredient_name?: string;
|
||||
ingredient_sku?: string;
|
||||
required_quantity: number;
|
||||
current_stock: number;
|
||||
quantity_to_order: number;
|
||||
unit_of_measure: string;
|
||||
estimated_cost: string; // Decimal as string
|
||||
priority: 'urgent' | 'high' | 'normal' | 'low';
|
||||
reason: string;
|
||||
supplier_id?: string;
|
||||
supplier_name?: string;
|
||||
expected_delivery_date?: string;
|
||||
// NEW: Local production support
|
||||
is_locally_produced?: boolean;
|
||||
recipe_id?: string;
|
||||
parent_requirement_id?: string;
|
||||
bom_explosion_level?: number;
|
||||
}
|
||||
|
||||
export interface ProcurementPlanSummary {
|
||||
id: string;
|
||||
plan_date: string;
|
||||
status: 'DRAFT' | 'PENDING_APPROVAL' | 'APPROVED' | 'IN_PROGRESS' | 'COMPLETED' | 'CANCELLED';
|
||||
total_requirements: number;
|
||||
total_estimated_cost: string; // Decimal as string
|
||||
planning_horizon_days: number;
|
||||
auto_generated: boolean;
|
||||
// NEW: Orchestrator integration
|
||||
forecast_id?: string;
|
||||
production_schedule_id?: string;
|
||||
created_at: string;
|
||||
created_by?: string;
|
||||
}
|
||||
|
||||
export interface ProcurementPlanDetail extends ProcurementPlanSummary {
|
||||
requirements: ProcurementRequirement[];
|
||||
notes?: string;
|
||||
approved_by?: string;
|
||||
approved_at?: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// AUTO-GENERATE PROCUREMENT TYPES (Orchestrator Integration)
|
||||
// ============================================================================
|
||||
|
||||
export interface AutoGenerateProcurementRequest {
|
||||
forecast_data: Record<string, any>; // From Forecasting Service
|
||||
production_schedule_id?: string;
|
||||
target_date?: string; // YYYY-MM-DD
|
||||
planning_horizon_days?: number; // Default: 14
|
||||
safety_stock_percentage?: number; // Default: 20.00
|
||||
auto_create_pos?: boolean; // Default: true
|
||||
auto_approve_pos?: boolean; // Default: false
|
||||
}
|
||||
|
||||
export interface AutoGenerateProcurementResponse {
|
||||
success: boolean;
|
||||
plan?: ProcurementPlanDetail;
|
||||
purchase_orders_created?: number;
|
||||
purchase_orders_auto_approved?: number;
|
||||
purchase_orders_pending_approval?: number;
|
||||
recipe_explosion_applied?: boolean;
|
||||
recipe_explosion_metadata?: {
|
||||
total_requirements_before: number;
|
||||
total_requirements_after: number;
|
||||
explosion_levels: number;
|
||||
locally_produced_ingredients: number;
|
||||
};
|
||||
warnings?: string[];
|
||||
errors?: string[];
|
||||
execution_time_ms?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PROCUREMENT PLAN API FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Get list of procurement plans with optional filters
|
||||
*/
|
||||
export async function listProcurementPlans(
|
||||
tenantId: string,
|
||||
params?: {
|
||||
status?: ProcurementPlanSummary['status'];
|
||||
date_from?: string;
|
||||
date_to?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
): Promise<ProcurementPlanSummary[]> {
|
||||
return apiClient.get<ProcurementPlanSummary[]>(
|
||||
`/tenants/${tenantId}/procurement/plans`,
|
||||
{ params }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single procurement plan by ID with full details
|
||||
*/
|
||||
export async function getProcurementPlan(
|
||||
tenantId: string,
|
||||
planId: string
|
||||
): Promise<ProcurementPlanDetail> {
|
||||
return apiClient.get<ProcurementPlanDetail>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new procurement plan (manual)
|
||||
*/
|
||||
export async function createProcurementPlan(
|
||||
tenantId: string,
|
||||
data: {
|
||||
plan_date: string;
|
||||
planning_horizon_days?: number;
|
||||
include_safety_stock?: boolean;
|
||||
safety_stock_percentage?: number;
|
||||
notes?: string;
|
||||
}
|
||||
): Promise<ProcurementPlanDetail> {
|
||||
return apiClient.post<ProcurementPlanDetail>(
|
||||
`/tenants/${tenantId}/procurement/plans`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update procurement plan
|
||||
*/
|
||||
export async function updateProcurementPlan(
|
||||
tenantId: string,
|
||||
planId: string,
|
||||
data: {
|
||||
status?: ProcurementPlanSummary['status'];
|
||||
notes?: string;
|
||||
}
|
||||
): Promise<ProcurementPlanDetail> {
|
||||
return apiClient.put<ProcurementPlanDetail>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete procurement plan
|
||||
*/
|
||||
export async function deleteProcurementPlan(
|
||||
tenantId: string,
|
||||
planId: string
|
||||
): Promise<{ message: string }> {
|
||||
return apiClient.delete<{ message: string }>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve procurement plan
|
||||
*/
|
||||
export async function approveProcurementPlan(
|
||||
tenantId: string,
|
||||
planId: string,
|
||||
notes?: string
|
||||
): Promise<ProcurementPlanDetail> {
|
||||
return apiClient.post<ProcurementPlanDetail>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/approve`,
|
||||
{ notes }
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// AUTO-GENERATE PROCUREMENT (ORCHESTRATOR INTEGRATION)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Auto-generate procurement plan from forecast data
|
||||
* This is the main entry point for orchestrated procurement planning
|
||||
*
|
||||
* NEW in Sprint 3: Called by Orchestrator Service to create procurement plans
|
||||
* based on forecast data and production schedules
|
||||
*
|
||||
* Features:
|
||||
* - Receives forecast data from Forecasting Service (via Orchestrator)
|
||||
* - Calculates procurement requirements using smart calculator
|
||||
* - Applies Recipe Explosion for locally-produced ingredients
|
||||
* - Optionally creates purchase orders
|
||||
* - Optionally auto-approves qualifying POs
|
||||
*/
|
||||
export async function autoGenerateProcurement(
|
||||
tenantId: string,
|
||||
request: AutoGenerateProcurementRequest
|
||||
): Promise<AutoGenerateProcurementResponse> {
|
||||
return apiClient.post<AutoGenerateProcurementResponse>(
|
||||
`/tenants/${tenantId}/procurement/auto-generate`,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test auto-generate with sample forecast data (for development/testing)
|
||||
*/
|
||||
export async function testAutoGenerateProcurement(
|
||||
tenantId: string,
|
||||
targetDate?: string
|
||||
): Promise<AutoGenerateProcurementResponse> {
|
||||
return apiClient.post<AutoGenerateProcurementResponse>(
|
||||
`/tenants/${tenantId}/procurement/auto-generate/test`,
|
||||
{ target_date: targetDate }
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PROCUREMENT REQUIREMENTS API FUNCTIONS
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Add requirement to procurement plan
|
||||
*/
|
||||
export async function addProcurementRequirement(
|
||||
tenantId: string,
|
||||
planId: string,
|
||||
requirement: {
|
||||
ingredient_id: string;
|
||||
required_quantity: number;
|
||||
quantity_to_order: number;
|
||||
priority: ProcurementRequirement['priority'];
|
||||
reason: string;
|
||||
supplier_id?: string;
|
||||
expected_delivery_date?: string;
|
||||
}
|
||||
): Promise<ProcurementRequirement> {
|
||||
return apiClient.post<ProcurementRequirement>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/requirements`,
|
||||
requirement
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update procurement requirement
|
||||
*/
|
||||
export async function updateProcurementRequirement(
|
||||
tenantId: string,
|
||||
planId: string,
|
||||
requirementId: string,
|
||||
data: {
|
||||
quantity_to_order?: number;
|
||||
priority?: ProcurementRequirement['priority'];
|
||||
supplier_id?: string;
|
||||
expected_delivery_date?: string;
|
||||
}
|
||||
): Promise<ProcurementRequirement> {
|
||||
return apiClient.put<ProcurementRequirement>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/requirements/${requirementId}`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete procurement requirement
|
||||
*/
|
||||
export async function deleteProcurementRequirement(
|
||||
tenantId: string,
|
||||
planId: string,
|
||||
requirementId: string
|
||||
): Promise<{ message: string }> {
|
||||
return apiClient.delete<{ message: string }>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/requirements/${requirementId}`
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PURCHASE ORDERS FROM PLAN
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Create purchase orders from procurement plan
|
||||
* Groups requirements by supplier and creates POs
|
||||
*/
|
||||
export async function createPurchaseOrdersFromPlan(
|
||||
tenantId: string,
|
||||
planId: string,
|
||||
options?: {
|
||||
auto_approve?: boolean;
|
||||
group_by_supplier?: boolean;
|
||||
delivery_date?: string;
|
||||
}
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
purchase_orders_created: number;
|
||||
purchase_orders_auto_approved?: number;
|
||||
purchase_orders_pending_approval?: number;
|
||||
purchase_order_ids: string[];
|
||||
message?: string;
|
||||
}> {
|
||||
return apiClient.post(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/create-purchase-orders`,
|
||||
options
|
||||
);
|
||||
}
|
||||
@@ -168,6 +168,21 @@ export class TenantService {
|
||||
return apiClient.delete<{ success: boolean; message: string }>(`${this.baseUrl}/${tenantId}/members/${memberUserId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer tenant ownership to another admin
|
||||
* Backend: services/tenant/app/api/tenant_members.py - transfer_ownership endpoint
|
||||
*
|
||||
* @param tenantId - The tenant ID
|
||||
* @param newOwnerId - The user ID of the new owner (must be an existing admin)
|
||||
* @returns Updated tenant with new owner
|
||||
*/
|
||||
async transferOwnership(tenantId: string, newOwnerId: string): Promise<TenantResponse> {
|
||||
return apiClient.post<TenantResponse>(
|
||||
`${this.baseUrl}/${tenantId}/transfer-ownership`,
|
||||
{ new_owner_id: newOwnerId }
|
||||
);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// OPERATIONS: Statistics & Admin
|
||||
// Backend: services/tenant/app/api/tenant_operations.py
|
||||
|
||||
Reference in New Issue
Block a user