Files
bakery-ia/frontend/src/api/services/transformations.ts
2025-09-17 16:06:30 +02:00

180 lines
5.8 KiB
TypeScript

/**
* Product Transformation Service - Handle transformation operations
*/
import { apiClient } from '../client';
import {
ProductTransformationCreate,
ProductTransformationResponse,
ProductionStage,
} from '../types/inventory';
export class TransformationService {
private readonly baseUrl = '/tenants';
// Product Transformation Operations
async createTransformation(
tenantId: string,
transformationData: ProductTransformationCreate
): Promise<ProductTransformationResponse> {
return apiClient.post<ProductTransformationResponse>(
`${this.baseUrl}/${tenantId}/transformations`,
transformationData
);
}
async getTransformation(
tenantId: string,
transformationId: string
): Promise<ProductTransformationResponse> {
return apiClient.get<ProductTransformationResponse>(
`${this.baseUrl}/${tenantId}/transformations/${transformationId}`
);
}
async getTransformations(
tenantId: string,
options?: {
skip?: number;
limit?: number;
ingredient_id?: string;
source_stage?: ProductionStage;
target_stage?: ProductionStage;
days_back?: number;
}
): Promise<ProductTransformationResponse[]> {
const queryParams = new URLSearchParams();
if (options?.skip !== undefined) queryParams.append('skip', options.skip.toString());
if (options?.limit !== undefined) queryParams.append('limit', options.limit.toString());
if (options?.ingredient_id) queryParams.append('ingredient_id', options.ingredient_id);
if (options?.source_stage) queryParams.append('source_stage', options.source_stage);
if (options?.target_stage) queryParams.append('target_stage', options.target_stage);
if (options?.days_back !== undefined) queryParams.append('days_back', options.days_back.toString());
const url = queryParams.toString()
? `${this.baseUrl}/${tenantId}/transformations?${queryParams.toString()}`
: `${this.baseUrl}/${tenantId}/transformations`;
return apiClient.get<ProductTransformationResponse[]>(url);
}
async getTransformationSummary(
tenantId: string,
daysBack: number = 30
): Promise<any> {
const queryParams = new URLSearchParams();
queryParams.append('days_back', daysBack.toString());
return apiClient.get<any>(
`${this.baseUrl}/${tenantId}/transformations/summary?${queryParams.toString()}`
);
}
// Convenience Methods for Common Transformations
async createParBakeToFreshTransformation(
tenantId: string,
options: {
source_ingredient_id: string;
target_ingredient_id: string;
quantity: number;
target_batch_number?: string;
expiration_hours?: number;
notes?: string;
}
): Promise<{
transformation_id: string;
transformation_reference: string;
source_quantity: number;
target_quantity: number;
expiration_date: string;
message: string;
}> {
const queryParams = new URLSearchParams();
queryParams.append('source_ingredient_id', options.source_ingredient_id);
queryParams.append('target_ingredient_id', options.target_ingredient_id);
queryParams.append('quantity', options.quantity.toString());
if (options.target_batch_number) {
queryParams.append('target_batch_number', options.target_batch_number);
}
if (options.expiration_hours !== undefined) {
queryParams.append('expiration_hours', options.expiration_hours.toString());
}
if (options.notes) {
queryParams.append('notes', options.notes);
}
return apiClient.post<any>(
`${this.baseUrl}/${tenantId}/transformations/par-bake-to-fresh?${queryParams.toString()}`
);
}
async bakeParBakedCroissants(
tenantId: string,
parBakedIngredientId: string,
freshBakedIngredientId: string,
quantity: number,
expirationHours: number = 24,
notes?: string
): Promise<ProductTransformationResponse> {
return this.createTransformation(tenantId, {
source_ingredient_id: parBakedIngredientId,
target_ingredient_id: freshBakedIngredientId,
source_stage: ProductionStage.PAR_BAKED,
target_stage: ProductionStage.FULLY_BAKED,
source_quantity: quantity,
target_quantity: quantity, // Assume 1:1 ratio for croissants
expiration_calculation_method: 'days_from_transformation',
expiration_days_offset: Math.max(1, Math.floor(expirationHours / 24)),
process_notes: notes || `Baked ${quantity} par-baked croissants to fresh croissants`,
});
}
async transformFrozenToPrepared(
tenantId: string,
frozenIngredientId: string,
preparedIngredientId: string,
quantity: number,
notes?: string
): Promise<ProductTransformationResponse> {
return this.createTransformation(tenantId, {
source_ingredient_id: frozenIngredientId,
target_ingredient_id: preparedIngredientId,
source_stage: ProductionStage.FROZEN_PRODUCT,
target_stage: ProductionStage.PREPARED_DOUGH,
source_quantity: quantity,
target_quantity: quantity,
expiration_calculation_method: 'days_from_transformation',
expiration_days_offset: 3, // Prepared dough typically lasts 3 days
process_notes: notes || `Thawed and prepared ${quantity} frozen products`,
});
}
// Analytics and Reporting
async getTransformationsByStage(
tenantId: string,
sourceStage?: ProductionStage,
targetStage?: ProductionStage,
limit: number = 50
): Promise<ProductTransformationResponse[]> {
return this.getTransformations(tenantId, {
source_stage: sourceStage,
target_stage: targetStage,
limit,
});
}
async getTransformationsForIngredient(
tenantId: string,
ingredientId: string,
limit: number = 50
): Promise<ProductTransformationResponse[]> {
return this.getTransformations(tenantId, {
ingredient_id: ingredientId,
limit,
});
}
}
export const transformationService = new TransformationService();