Add POI feature and imporve the overall backend implementation
This commit is contained in:
@@ -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 =>
|
||||
|
||||
@@ -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>
|
||||
) => {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -629,6 +629,7 @@ export {
|
||||
useBusinessModelDetection,
|
||||
useOrdersServiceStatus,
|
||||
useCreateOrder,
|
||||
useUpdateOrder,
|
||||
useUpdateOrderStatus,
|
||||
useCreateCustomer,
|
||||
useUpdateCustomer,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user