ADD new frontend
This commit is contained in:
408
frontend/src/services/api/procurement.service.ts
Normal file
408
frontend/src/services/api/procurement.service.ts
Normal file
@@ -0,0 +1,408 @@
|
||||
import { apiClient, ApiResponse } from './client';
|
||||
|
||||
// Enums
|
||||
export enum PurchaseOrderStatus {
|
||||
DRAFT = 'draft',
|
||||
PENDING = 'pending',
|
||||
APPROVED = 'approved',
|
||||
SENT = 'sent',
|
||||
PARTIALLY_RECEIVED = 'partially_received',
|
||||
RECEIVED = 'received',
|
||||
CANCELLED = 'cancelled',
|
||||
}
|
||||
|
||||
export enum DeliveryStatus {
|
||||
SCHEDULED = 'scheduled',
|
||||
IN_TRANSIT = 'in_transit',
|
||||
DELIVERED = 'delivered',
|
||||
FAILED = 'failed',
|
||||
RETURNED = 'returned',
|
||||
}
|
||||
|
||||
// Request/Response Types
|
||||
export interface PurchaseOrderItem {
|
||||
ingredient_id: string;
|
||||
ingredient_name: string;
|
||||
quantity: number;
|
||||
unit_price: number;
|
||||
total_price: number;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface PurchaseOrderCreate {
|
||||
supplier_id: string;
|
||||
items: PurchaseOrderItem[];
|
||||
delivery_date?: string;
|
||||
notes?: string;
|
||||
priority?: 'low' | 'normal' | 'high' | 'urgent';
|
||||
}
|
||||
|
||||
export interface PurchaseOrderUpdate {
|
||||
supplier_id?: string;
|
||||
delivery_date?: string;
|
||||
notes?: string;
|
||||
priority?: 'low' | 'normal' | 'high' | 'urgent';
|
||||
status?: PurchaseOrderStatus;
|
||||
}
|
||||
|
||||
export interface PurchaseOrderResponse {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
order_number: string;
|
||||
supplier_id: string;
|
||||
supplier_name: string;
|
||||
status: PurchaseOrderStatus;
|
||||
items: PurchaseOrderItem[];
|
||||
subtotal: number;
|
||||
tax_amount: number;
|
||||
total_amount: number;
|
||||
delivery_date?: string;
|
||||
expected_delivery_date?: string;
|
||||
actual_delivery_date?: string;
|
||||
notes?: string;
|
||||
priority: 'low' | 'normal' | 'high' | 'urgent';
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
created_by: string;
|
||||
approved_by?: string;
|
||||
approved_at?: string;
|
||||
}
|
||||
|
||||
export interface Supplier {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
name: string;
|
||||
contact_name?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
address: string;
|
||||
tax_id?: string;
|
||||
payment_terms?: string;
|
||||
delivery_terms?: string;
|
||||
rating?: number;
|
||||
is_active: boolean;
|
||||
performance_metrics: {
|
||||
on_time_delivery_rate: number;
|
||||
quality_score: number;
|
||||
total_orders: number;
|
||||
average_delivery_time: number;
|
||||
};
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface DeliveryResponse {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
purchase_order_id: string;
|
||||
delivery_number: string;
|
||||
supplier_id: string;
|
||||
status: DeliveryStatus;
|
||||
scheduled_date: string;
|
||||
actual_delivery_date?: string;
|
||||
delivery_items: Array<{
|
||||
ingredient_id: string;
|
||||
ingredient_name: string;
|
||||
ordered_quantity: number;
|
||||
delivered_quantity: number;
|
||||
unit_price: number;
|
||||
batch_number?: string;
|
||||
expiration_date?: string;
|
||||
quality_notes?: string;
|
||||
}>;
|
||||
total_items: number;
|
||||
delivery_notes?: string;
|
||||
quality_check_notes?: string;
|
||||
received_by?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
class ProcurementService {
|
||||
private readonly baseUrl = '/procurement';
|
||||
|
||||
// Purchase Order management
|
||||
async getPurchaseOrders(params?: {
|
||||
page?: number;
|
||||
size?: number;
|
||||
status?: PurchaseOrderStatus;
|
||||
supplier_id?: string;
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
}): Promise<ApiResponse<{ items: PurchaseOrderResponse[]; total: number; page: number; size: number; pages: number }>> {
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
queryParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const url = queryParams.toString()
|
||||
? `${this.baseUrl}/purchase-orders?${queryParams.toString()}`
|
||||
: `${this.baseUrl}/purchase-orders`;
|
||||
|
||||
return apiClient.get(url);
|
||||
}
|
||||
|
||||
async getPurchaseOrder(orderId: string): Promise<ApiResponse<PurchaseOrderResponse>> {
|
||||
return apiClient.get(`${this.baseUrl}/purchase-orders/${orderId}`);
|
||||
}
|
||||
|
||||
async createPurchaseOrder(orderData: PurchaseOrderCreate): Promise<ApiResponse<PurchaseOrderResponse>> {
|
||||
return apiClient.post(`${this.baseUrl}/purchase-orders`, orderData);
|
||||
}
|
||||
|
||||
async updatePurchaseOrder(orderId: string, orderData: PurchaseOrderUpdate): Promise<ApiResponse<PurchaseOrderResponse>> {
|
||||
return apiClient.put(`${this.baseUrl}/purchase-orders/${orderId}`, orderData);
|
||||
}
|
||||
|
||||
async approvePurchaseOrder(orderId: string): Promise<ApiResponse<PurchaseOrderResponse>> {
|
||||
return apiClient.post(`${this.baseUrl}/purchase-orders/${orderId}/approve`);
|
||||
}
|
||||
|
||||
async sendPurchaseOrder(orderId: string, sendEmail: boolean = true): Promise<ApiResponse<{ message: string; sent_at: string }>> {
|
||||
return apiClient.post(`${this.baseUrl}/purchase-orders/${orderId}/send`, { send_email: sendEmail });
|
||||
}
|
||||
|
||||
async cancelPurchaseOrder(orderId: string, reason?: string): Promise<ApiResponse<PurchaseOrderResponse>> {
|
||||
return apiClient.post(`${this.baseUrl}/purchase-orders/${orderId}/cancel`, { reason });
|
||||
}
|
||||
|
||||
// Supplier management
|
||||
async getSuppliers(params?: {
|
||||
page?: number;
|
||||
size?: number;
|
||||
is_active?: boolean;
|
||||
search?: string;
|
||||
}): Promise<ApiResponse<{ items: Supplier[]; total: number; page: number; size: number; pages: number }>> {
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
queryParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const url = queryParams.toString()
|
||||
? `${this.baseUrl}/suppliers?${queryParams.toString()}`
|
||||
: `${this.baseUrl}/suppliers`;
|
||||
|
||||
return apiClient.get(url);
|
||||
}
|
||||
|
||||
async getSupplier(supplierId: string): Promise<ApiResponse<Supplier>> {
|
||||
return apiClient.get(`${this.baseUrl}/suppliers/${supplierId}`);
|
||||
}
|
||||
|
||||
async createSupplier(supplierData: Omit<Supplier, 'id' | 'tenant_id' | 'performance_metrics' | 'created_at' | 'updated_at'>): Promise<ApiResponse<Supplier>> {
|
||||
return apiClient.post(`${this.baseUrl}/suppliers`, supplierData);
|
||||
}
|
||||
|
||||
async updateSupplier(supplierId: string, supplierData: Partial<Supplier>): Promise<ApiResponse<Supplier>> {
|
||||
return apiClient.put(`${this.baseUrl}/suppliers/${supplierId}`, supplierData);
|
||||
}
|
||||
|
||||
async deleteSupplier(supplierId: string): Promise<ApiResponse<{ message: string }>> {
|
||||
return apiClient.delete(`${this.baseUrl}/suppliers/${supplierId}`);
|
||||
}
|
||||
|
||||
async getSupplierPerformance(supplierId: string): Promise<ApiResponse<{
|
||||
supplier: Supplier;
|
||||
performance_history: Array<{
|
||||
month: string;
|
||||
on_time_delivery_rate: number;
|
||||
quality_score: number;
|
||||
order_count: number;
|
||||
total_value: number;
|
||||
}>;
|
||||
recent_deliveries: DeliveryResponse[];
|
||||
}>> {
|
||||
return apiClient.get(`${this.baseUrl}/suppliers/${supplierId}/performance`);
|
||||
}
|
||||
|
||||
// Delivery management
|
||||
async getDeliveries(params?: {
|
||||
page?: number;
|
||||
size?: number;
|
||||
status?: DeliveryStatus;
|
||||
supplier_id?: string;
|
||||
purchase_order_id?: string;
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
}): Promise<ApiResponse<{ items: DeliveryResponse[]; total: number; page: number; size: number; pages: number }>> {
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
queryParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const url = queryParams.toString()
|
||||
? `${this.baseUrl}/deliveries?${queryParams.toString()}`
|
||||
: `${this.baseUrl}/deliveries`;
|
||||
|
||||
return apiClient.get(url);
|
||||
}
|
||||
|
||||
async getDelivery(deliveryId: string): Promise<ApiResponse<DeliveryResponse>> {
|
||||
return apiClient.get(`${this.baseUrl}/deliveries/${deliveryId}`);
|
||||
}
|
||||
|
||||
async receiveDelivery(deliveryId: string, deliveryData: {
|
||||
delivered_items: Array<{
|
||||
ingredient_id: string;
|
||||
delivered_quantity: number;
|
||||
batch_number?: string;
|
||||
expiration_date?: string;
|
||||
quality_notes?: string;
|
||||
}>;
|
||||
delivery_notes?: string;
|
||||
quality_check_notes?: string;
|
||||
}): Promise<ApiResponse<DeliveryResponse>> {
|
||||
return apiClient.post(`${this.baseUrl}/deliveries/${deliveryId}/receive`, deliveryData);
|
||||
}
|
||||
|
||||
async reportDeliveryIssue(deliveryId: string, issue: {
|
||||
issue_type: 'late_delivery' | 'quality_issue' | 'quantity_mismatch' | 'damaged_goods' | 'other';
|
||||
description: string;
|
||||
affected_items?: string[];
|
||||
severity: 'low' | 'medium' | 'high';
|
||||
}): Promise<ApiResponse<{ message: string; issue_id: string }>> {
|
||||
return apiClient.post(`${this.baseUrl}/deliveries/${deliveryId}/report-issue`, issue);
|
||||
}
|
||||
|
||||
// Analytics and reporting
|
||||
async getProcurementAnalytics(params?: {
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
supplier_id?: string;
|
||||
}): Promise<ApiResponse<{
|
||||
total_purchase_value: number;
|
||||
total_orders: number;
|
||||
average_order_value: number;
|
||||
on_time_delivery_rate: number;
|
||||
quality_score: number;
|
||||
cost_savings: number;
|
||||
top_suppliers: Array<{
|
||||
supplier_name: string;
|
||||
total_value: number;
|
||||
order_count: number;
|
||||
performance_score: number;
|
||||
}>;
|
||||
spending_trends: Array<{
|
||||
month: string;
|
||||
total_spending: number;
|
||||
order_count: number;
|
||||
}>;
|
||||
}>> {
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
queryParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const url = queryParams.toString()
|
||||
? `${this.baseUrl}/analytics?${queryParams.toString()}`
|
||||
: `${this.baseUrl}/analytics`;
|
||||
|
||||
return apiClient.get(url);
|
||||
}
|
||||
|
||||
async getSpendingByCategory(params?: {
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
}): Promise<ApiResponse<Array<{
|
||||
category: string;
|
||||
total_spending: number;
|
||||
percentage: number;
|
||||
trend: 'up' | 'down' | 'stable';
|
||||
}>>> {
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
queryParams.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const url = queryParams.toString()
|
||||
? `${this.baseUrl}/spending/by-category?${queryParams.toString()}`
|
||||
: `${this.baseUrl}/spending/by-category`;
|
||||
|
||||
return apiClient.get(url);
|
||||
}
|
||||
|
||||
// Automated procurement
|
||||
async getReorderSuggestions(): Promise<ApiResponse<Array<{
|
||||
ingredient_id: string;
|
||||
ingredient_name: string;
|
||||
current_stock: number;
|
||||
reorder_point: number;
|
||||
suggested_quantity: number;
|
||||
preferred_supplier: {
|
||||
id: string;
|
||||
name: string;
|
||||
last_price: number;
|
||||
lead_time_days: number;
|
||||
};
|
||||
urgency: 'low' | 'medium' | 'high' | 'critical';
|
||||
}>>> {
|
||||
return apiClient.get(`${this.baseUrl}/reorder-suggestions`);
|
||||
}
|
||||
|
||||
async createReorderFromSuggestions(suggestions: Array<{
|
||||
ingredient_id: string;
|
||||
supplier_id: string;
|
||||
quantity: number;
|
||||
}>): Promise<ApiResponse<PurchaseOrderResponse[]>> {
|
||||
return apiClient.post(`${this.baseUrl}/auto-reorder`, { suggestions });
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
getPurchaseOrderStatusOptions(): { value: PurchaseOrderStatus; label: string; color: string }[] {
|
||||
return [
|
||||
{ value: PurchaseOrderStatus.DRAFT, label: 'Draft', color: 'gray' },
|
||||
{ value: PurchaseOrderStatus.PENDING, label: 'Pending', color: 'yellow' },
|
||||
{ value: PurchaseOrderStatus.APPROVED, label: 'Approved', color: 'blue' },
|
||||
{ value: PurchaseOrderStatus.SENT, label: 'Sent', color: 'purple' },
|
||||
{ value: PurchaseOrderStatus.PARTIALLY_RECEIVED, label: 'Partially Received', color: 'orange' },
|
||||
{ value: PurchaseOrderStatus.RECEIVED, label: 'Received', color: 'green' },
|
||||
{ value: PurchaseOrderStatus.CANCELLED, label: 'Cancelled', color: 'red' },
|
||||
];
|
||||
}
|
||||
|
||||
getDeliveryStatusOptions(): { value: DeliveryStatus; label: string; color: string }[] {
|
||||
return [
|
||||
{ value: DeliveryStatus.SCHEDULED, label: 'Scheduled', color: 'blue' },
|
||||
{ value: DeliveryStatus.IN_TRANSIT, label: 'In Transit', color: 'yellow' },
|
||||
{ value: DeliveryStatus.DELIVERED, label: 'Delivered', color: 'green' },
|
||||
{ value: DeliveryStatus.FAILED, label: 'Failed', color: 'red' },
|
||||
{ value: DeliveryStatus.RETURNED, label: 'Returned', color: 'orange' },
|
||||
];
|
||||
}
|
||||
|
||||
getPriorityOptions(): { value: string; label: string; color: string }[] {
|
||||
return [
|
||||
{ value: 'low', label: 'Low', color: 'gray' },
|
||||
{ value: 'normal', label: 'Normal', color: 'blue' },
|
||||
{ value: 'high', label: 'High', color: 'orange' },
|
||||
{ value: 'urgent', label: 'Urgent', color: 'red' },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export const procurementService = new ProcurementService();
|
||||
Reference in New Issue
Block a user