ADD new frontend

This commit is contained in:
Urtzi Alfaro
2025-08-28 10:41:04 +02:00
parent 9c247a5f99
commit 0fd273cfce
492 changed files with 114979 additions and 1632 deletions

View 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();