Fix UI for inventory page 3
This commit is contained in:
@@ -174,9 +174,9 @@ export const useStockMovements = (
|
||||
ingredientId?: string,
|
||||
limit: number = 50,
|
||||
offset: number = 0,
|
||||
options?: Omit<UseQueryOptions<PaginatedResponse<StockMovementResponse>, ApiError>, 'queryKey' | 'queryFn'>
|
||||
options?: Omit<UseQueryOptions<StockMovementResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<PaginatedResponse<StockMovementResponse>, ApiError>({
|
||||
return useQuery<StockMovementResponse[], ApiError>({
|
||||
queryKey: inventoryKeys.stock.movements(tenantId, ingredientId),
|
||||
queryFn: () => inventoryService.getStockMovements(tenantId, ingredientId, limit, offset),
|
||||
enabled: !!tenantId,
|
||||
@@ -339,34 +339,117 @@ export const useConsumeStock = (
|
||||
|
||||
export const useCreateStockMovement = (
|
||||
options?: UseMutationOptions<
|
||||
StockMovementResponse,
|
||||
ApiError,
|
||||
StockMovementResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; movementData: StockMovementCreate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
return useMutation<
|
||||
StockMovementResponse,
|
||||
ApiError,
|
||||
StockMovementResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; movementData: StockMovementCreate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, movementData }) => inventoryService.createStockMovement(tenantId, movementData),
|
||||
onSuccess: (data, { tenantId, movementData }) => {
|
||||
// Invalidate movement queries
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId) });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: inventoryKeys.stock.movements(tenantId, movementData.ingredient_id)
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: inventoryKeys.stock.movements(tenantId, movementData.ingredient_id)
|
||||
});
|
||||
// Invalidate stock queries if this affects stock levels
|
||||
if (['in', 'out', 'adjustment'].includes(movementData.movement_type)) {
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.lists() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: inventoryKeys.stock.byIngredient(tenantId, movementData.ingredient_id)
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: inventoryKeys.stock.byIngredient(tenantId, movementData.ingredient_id)
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.ingredients.lists() });
|
||||
}
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Custom hooks for stock management operations
|
||||
export const useStockOperations = (tenantId: string) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const addStock = useMutation({
|
||||
mutationFn: async ({ ingredientId, quantity, unit_cost, notes }: {
|
||||
ingredientId: string;
|
||||
quantity: number;
|
||||
unit_cost?: number;
|
||||
notes?: string;
|
||||
}) => {
|
||||
// Create stock entry via backend API
|
||||
const stockData: StockCreate = {
|
||||
ingredient_id: ingredientId,
|
||||
quantity,
|
||||
unit_price: unit_cost || 0,
|
||||
notes
|
||||
};
|
||||
return inventoryService.addStock(tenantId, stockData);
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate relevant queries
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.ingredients.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId) });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId, variables.ingredientId) });
|
||||
}
|
||||
});
|
||||
|
||||
const consumeStock = useMutation({
|
||||
mutationFn: async ({ ingredientId, quantity, reference_number, notes, fifo = true }: {
|
||||
ingredientId: string;
|
||||
quantity: number;
|
||||
reference_number?: string;
|
||||
notes?: string;
|
||||
fifo?: boolean;
|
||||
}) => {
|
||||
const consumptionData: StockConsumptionRequest = {
|
||||
ingredient_id: ingredientId,
|
||||
quantity,
|
||||
reference_number,
|
||||
notes,
|
||||
fifo
|
||||
};
|
||||
return inventoryService.consumeStock(tenantId, consumptionData);
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate relevant queries
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.ingredients.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId) });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId, variables.ingredientId) });
|
||||
}
|
||||
});
|
||||
|
||||
const adjustStock = useMutation({
|
||||
mutationFn: async ({ ingredientId, quantity, notes }: {
|
||||
ingredientId: string;
|
||||
quantity: number;
|
||||
notes?: string;
|
||||
}) => {
|
||||
// Create adjustment movement via backend API
|
||||
const movementData: StockMovementCreate = {
|
||||
ingredient_id: ingredientId,
|
||||
movement_type: 'adjustment',
|
||||
quantity,
|
||||
notes
|
||||
};
|
||||
return inventoryService.createStockMovement(tenantId, movementData);
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate relevant queries
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.ingredients.lists() });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId) });
|
||||
queryClient.invalidateQueries({ queryKey: inventoryKeys.stock.movements(tenantId, variables.ingredientId) });
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
addStock,
|
||||
consumeStock,
|
||||
adjustStock
|
||||
};
|
||||
};
|
||||
@@ -172,15 +172,23 @@ export class InventoryService {
|
||||
ingredientId?: string,
|
||||
limit: number = 50,
|
||||
offset: number = 0
|
||||
): Promise<PaginatedResponse<StockMovementResponse>> {
|
||||
): Promise<StockMovementResponse[]> {
|
||||
const queryParams = new URLSearchParams();
|
||||
if (ingredientId) queryParams.append('ingredient_id', ingredientId);
|
||||
queryParams.append('limit', limit.toString());
|
||||
queryParams.append('offset', offset.toString());
|
||||
queryParams.append('skip', offset.toString()); // Backend expects 'skip' not 'offset'
|
||||
|
||||
return apiClient.get<PaginatedResponse<StockMovementResponse>>(
|
||||
`${this.baseUrl}/${tenantId}/stock/movements?${queryParams.toString()}`
|
||||
);
|
||||
const url = `${this.baseUrl}/${tenantId}/stock/movements?${queryParams.toString()}`;
|
||||
console.log('🔍 Frontend calling API:', url);
|
||||
|
||||
try {
|
||||
const result = await apiClient.get<StockMovementResponse[]>(url);
|
||||
console.log('✅ Frontend API response:', result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ Frontend API error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Expiry Management
|
||||
|
||||
@@ -183,27 +183,36 @@ export interface StockResponse {
|
||||
|
||||
export interface StockMovementCreate {
|
||||
ingredient_id: string;
|
||||
movement_type: 'in' | 'out' | 'adjustment' | 'transfer' | 'waste';
|
||||
stock_id?: string;
|
||||
movement_type: 'purchase' | 'production_use' | 'adjustment' | 'waste' | 'transfer' | 'return' | 'initial_stock';
|
||||
quantity: number;
|
||||
unit_price?: number;
|
||||
unit_cost?: number;
|
||||
reference_number?: string;
|
||||
supplier_id?: string;
|
||||
notes?: string;
|
||||
related_stock_id?: string;
|
||||
reason_code?: string;
|
||||
movement_date?: string;
|
||||
}
|
||||
|
||||
export interface StockMovementResponse {
|
||||
id: string;
|
||||
ingredient_id: string;
|
||||
tenant_id: string;
|
||||
movement_type: 'in' | 'out' | 'adjustment' | 'transfer' | 'waste';
|
||||
ingredient_id: string;
|
||||
stock_id?: string;
|
||||
movement_type: 'purchase' | 'production_use' | 'adjustment' | 'waste' | 'transfer' | 'return' | 'initial_stock';
|
||||
quantity: number;
|
||||
unit_price?: number;
|
||||
total_value?: number;
|
||||
unit_cost?: number;
|
||||
total_cost?: number;
|
||||
quantity_before?: number;
|
||||
quantity_after?: number;
|
||||
reference_number?: string;
|
||||
supplier_id?: string;
|
||||
notes?: string;
|
||||
related_stock_id?: string;
|
||||
reason_code?: string;
|
||||
movement_date: string;
|
||||
created_at: string;
|
||||
created_by?: string;
|
||||
ingredient?: IngredientResponse;
|
||||
}
|
||||
|
||||
// Filter and Query Types
|
||||
|
||||
Reference in New Issue
Block a user