Add POI feature and imporve the overall backend implementation

This commit is contained in:
Urtzi Alfaro
2025-11-12 15:34:10 +01:00
parent e8096cd979
commit 5783c7ed05
173 changed files with 16862 additions and 9078 deletions

View File

@@ -87,6 +87,7 @@ class ApiClient {
'/auth/me', // User profile endpoints
'/auth/register', // Registration
'/auth/login', // Login
'/geocoding', // Geocoding/address search - utility service, no tenant context
];
const isPublicEndpoint = publicEndpoints.some(endpoint =>

View File

@@ -169,11 +169,11 @@ export const useCreateOrder = (
queryKey: ordersKeys.orders(),
predicate: (query) => {
const queryKey = query.queryKey as string[];
return queryKey.includes('list') &&
return queryKey.includes('list') &&
JSON.stringify(queryKey).includes(variables.tenant_id);
},
});
// Invalidate dashboard
queryClient.invalidateQueries({
queryKey: ordersKeys.dashboard(variables.tenant_id),
@@ -189,6 +189,39 @@ export const useCreateOrder = (
});
};
export const useUpdateOrder = (
options?: UseMutationOptions<OrderResponse, ApiError, { tenantId: string; orderId: string; data: OrderUpdate }>
) => {
const queryClient = useQueryClient();
return useMutation<OrderResponse, ApiError, { tenantId: string; orderId: string; data: OrderUpdate }>({
mutationFn: ({ tenantId, orderId, data }) => OrdersService.updateOrder(tenantId, orderId, data),
onSuccess: (data, variables) => {
// Update the specific order in cache
queryClient.setQueryData(
ordersKeys.order(variables.tenantId, variables.orderId),
data
);
// Invalidate orders list for this tenant
queryClient.invalidateQueries({
queryKey: ordersKeys.orders(),
predicate: (query) => {
const queryKey = query.queryKey as string[];
return queryKey.includes('list') &&
JSON.stringify(queryKey).includes(variables.tenantId);
},
});
// Invalidate dashboard
queryClient.invalidateQueries({
queryKey: ordersKeys.dashboard(variables.tenantId),
});
},
...options,
});
};
export const useUpdateOrderStatus = (
options?: UseMutationOptions<OrderResponse, ApiError, UpdateOrderStatusParams>
) => {

View File

@@ -10,7 +10,9 @@ import type {
PurchaseOrderDetail,
PurchaseOrderSearchParams,
PurchaseOrderUpdateData,
PurchaseOrderStatus
PurchaseOrderStatus,
CreateDeliveryInput,
DeliveryResponse
} from '../services/purchase_orders';
import {
listPurchaseOrders,
@@ -21,7 +23,8 @@ import {
approvePurchaseOrder,
rejectPurchaseOrder,
bulkApprovePurchaseOrders,
deletePurchaseOrder
deletePurchaseOrder,
createDelivery
} from '../services/purchase_orders';
// Query Keys
@@ -257,3 +260,33 @@ export const useDeletePurchaseOrder = (
...options,
});
};
/**
* Hook to create a delivery for a purchase order
*/
export const useCreateDelivery = (
options?: UseMutationOptions<
DeliveryResponse,
ApiError,
{ tenantId: string; poId: string; deliveryData: CreateDeliveryInput }
>
) => {
const queryClient = useQueryClient();
return useMutation<
DeliveryResponse,
ApiError,
{ tenantId: string; poId: string; deliveryData: CreateDeliveryInput }
>({
mutationFn: ({ tenantId, poId, deliveryData }) => createDelivery(tenantId, poId, deliveryData),
onSuccess: (data, variables) => {
// Invalidate all PO queries to refresh status
queryClient.invalidateQueries({ queryKey: purchaseOrderKeys.all });
// Invalidate detail for this specific PO
queryClient.invalidateQueries({
queryKey: purchaseOrderKeys.detail(variables.tenantId, variables.poId)
});
},
...options,
});
};

View File

@@ -629,6 +629,7 @@ export {
useBusinessModelDetection,
useOrdersServiceStatus,
useCreateOrder,
useUpdateOrder,
useUpdateOrderStatus,
useCreateCustomer,
useUpdateCustomer,

View File

@@ -10,11 +10,12 @@ export const BACKEND_ONBOARDING_STEPS = [
'user_registered', // Phase 0: User account created (auto-completed)
'bakery-type-selection', // Phase 1: Choose bakery type
'setup', // Phase 2: Basic bakery setup and tenant creation
'upload-sales-data', // Phase 2a: File upload, validation, AI classification
'inventory-review', // Phase 2a: Review AI-detected products with type selection
'initial-stock-entry', // Phase 2a: Capture initial stock levels
'product-categorization', // Phase 2b: Advanced categorization (optional)
'suppliers-setup', // Phase 2c: Suppliers configuration
'poi-detection', // Phase 2a: POI Detection (Location Context)
'upload-sales-data', // Phase 2b: File upload, validation, AI classification
'inventory-review', // Phase 2b: Review AI-detected products with type selection
'initial-stock-entry', // Phase 2b: Capture initial stock levels
'product-categorization', // Phase 2c: Advanced categorization (optional)
'suppliers-setup', // Phase 2d: Suppliers configuration
'recipes-setup', // Phase 3: Production recipes (optional)
'production-processes', // Phase 3: Finishing processes (optional)
'quality-setup', // Phase 3: Quality standards (optional)
@@ -28,11 +29,12 @@ export const BACKEND_ONBOARDING_STEPS = [
export const FRONTEND_STEP_ORDER = [
'bakery-type-selection', // Phase 1: Choose bakery type
'setup', // Phase 2: Basic bakery setup and tenant creation
'upload-sales-data', // Phase 2a: File upload and AI classification
'inventory-review', // Phase 2a: Review AI-detected products
'initial-stock-entry', // Phase 2a: Initial stock levels
'product-categorization', // Phase 2b: Advanced categorization (optional)
'suppliers-setup', // Phase 2c: Suppliers configuration
'poi-detection', // Phase 2a: POI Detection (Location Context)
'upload-sales-data', // Phase 2b: File upload and AI classification
'inventory-review', // Phase 2b: Review AI-detected products
'initial-stock-entry', // Phase 2b: Initial stock levels
'product-categorization', // Phase 2c: Advanced categorization (optional)
'suppliers-setup', // Phase 2d: Suppliers configuration
'recipes-setup', // Phase 3: Production recipes (optional)
'production-processes', // Phase 3: Finishing processes (optional)
'quality-setup', // Phase 3: Quality standards (optional)

View File

@@ -103,20 +103,28 @@ export class OrdersService {
return apiClient.get<OrderResponse[]>(`/tenants/${tenant_id}/orders?${queryParams.toString()}`);
}
/**
* Update order details
* PUT /tenants/{tenant_id}/orders/{order_id}
*/
static async updateOrder(tenantId: string, orderId: string, orderData: OrderUpdate): Promise<OrderResponse> {
return apiClient.put<OrderResponse>(`/tenants/${tenantId}/orders/${orderId}`, orderData);
}
/**
* Update order status
* PUT /tenants/{tenant_id}/orders/{order_id}/status
*/
static async updateOrderStatus(params: UpdateOrderStatusParams): Promise<OrderResponse> {
const { tenant_id, order_id, new_status, reason } = params;
const queryParams = new URLSearchParams();
if (reason) {
queryParams.append('reason', reason);
}
const url = `/tenants/${tenant_id}/orders/${order_id}/status${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
return apiClient.put<OrderResponse>(url, { status: new_status });
}

View File

@@ -242,3 +242,68 @@ export async function deletePurchaseOrder(
`/tenants/${tenantId}/procurement/purchase-orders/${poId}`
);
}
// ================================================================
// DELIVERY TYPES AND METHODS
// ================================================================
export interface DeliveryItemInput {
purchase_order_item_id: string;
inventory_product_id: string;
ordered_quantity: number;
delivered_quantity: number;
accepted_quantity: number;
rejected_quantity: number;
batch_lot_number?: string;
expiry_date?: string;
quality_grade?: string;
quality_issues?: string;
rejection_reason?: string;
item_notes?: string;
}
export interface CreateDeliveryInput {
purchase_order_id: string;
supplier_id: string;
supplier_delivery_note?: string;
scheduled_date?: string;
estimated_arrival?: string;
carrier_name?: string;
tracking_number?: string;
inspection_passed?: boolean;
inspection_notes?: string;
notes?: string;
items: DeliveryItemInput[];
}
export interface DeliveryResponse {
id: string;
tenant_id: string;
purchase_order_id: string;
supplier_id: string;
delivery_number: string;
status: string;
scheduled_date?: string;
estimated_arrival?: string;
actual_arrival?: string;
completed_at?: string;
inspection_passed?: boolean;
inspection_notes?: string;
notes?: string;
created_at: string;
updated_at: string;
}
/**
* Create delivery for purchase order
*/
export async function createDelivery(
tenantId: string,
poId: string,
deliveryData: CreateDeliveryInput
): Promise<DeliveryResponse> {
return apiClient.post<DeliveryResponse>(
`/tenants/${tenantId}/procurement/purchase-orders/${poId}/deliveries`,
deliveryData
);
}

View File

@@ -120,6 +120,13 @@ export interface ProductionBatchResponse {
actual_duration_minutes: number | null;
status: ProductionStatus;
priority: ProductionPriority;
// Process stage tracking (replaces frontend mock data)
current_process_stage?: string | null;
process_stage_history?: Array<Record<string, any>> | null;
pending_quality_checks?: Array<Record<string, any>> | null;
completed_quality_checks?: Array<Record<string, any>> | null;
estimated_cost: number | null;
actual_cost: number | null;
yield_percentage: number | null;