Fix issues 4
This commit is contained in:
@@ -547,6 +547,9 @@ export class InventoryService {
|
|||||||
*/
|
*/
|
||||||
async getProductsList(tenantId: string): Promise<ProductInfo[]> {
|
async getProductsList(tenantId: string): Promise<ProductInfo[]> {
|
||||||
try {
|
try {
|
||||||
|
console.log('🔍 Fetching products for forecasting...', { tenantId });
|
||||||
|
|
||||||
|
// First try to get finished products (preferred for forecasting)
|
||||||
const response = await apiClient.get(`/tenants/${tenantId}/ingredients`, {
|
const response = await apiClient.get(`/tenants/${tenantId}/ingredients`, {
|
||||||
params: {
|
params: {
|
||||||
limit: 100,
|
limit: 100,
|
||||||
@@ -606,7 +609,47 @@ export class InventoryService {
|
|||||||
}))
|
}))
|
||||||
.filter(product => product.inventory_product_id && product.name);
|
.filter(product => product.inventory_product_id && product.name);
|
||||||
|
|
||||||
console.log('📋 Processed inventory products:', products);
|
console.log('📋 Processed finished products:', products);
|
||||||
|
|
||||||
|
// If no finished products found, try to get all products as fallback
|
||||||
|
if (products.length === 0) {
|
||||||
|
console.log('⚠️ No finished products found, trying to get all products as fallback...');
|
||||||
|
|
||||||
|
const fallbackResponse = await apiClient.get(`/tenants/${tenantId}/ingredients`, {
|
||||||
|
params: {
|
||||||
|
limit: 100,
|
||||||
|
// No product_type filter to get all products
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('🔍 Fallback API Response:', fallbackResponse);
|
||||||
|
|
||||||
|
const fallbackDataToProcess = fallbackResponse?.data || fallbackResponse;
|
||||||
|
let fallbackProductsArray: any[] = [];
|
||||||
|
|
||||||
|
if (Array.isArray(fallbackDataToProcess)) {
|
||||||
|
fallbackProductsArray = fallbackDataToProcess;
|
||||||
|
} else if (fallbackDataToProcess && typeof fallbackDataToProcess === 'object') {
|
||||||
|
const keys = Object.keys(fallbackDataToProcess);
|
||||||
|
if (keys.length > 0 && keys.every(key => !isNaN(Number(key)))) {
|
||||||
|
fallbackProductsArray = Object.values(fallbackDataToProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackProducts: ProductInfo[] = fallbackProductsArray
|
||||||
|
.map((product: any) => ({
|
||||||
|
inventory_product_id: product.id || product.inventory_product_id,
|
||||||
|
name: product.name || product.product_name || `Product ${product.id || ''}`,
|
||||||
|
category: product.category,
|
||||||
|
current_stock: product.current_stock,
|
||||||
|
unit: product.unit,
|
||||||
|
cost_per_unit: product.cost_per_unit
|
||||||
|
}))
|
||||||
|
.filter(product => product.inventory_product_id && product.name);
|
||||||
|
|
||||||
|
console.log('📋 Processed fallback products (all inventory items):', fallbackProducts);
|
||||||
|
return fallbackProducts;
|
||||||
|
}
|
||||||
|
|
||||||
return products;
|
return products;
|
||||||
|
|
||||||
|
|||||||
@@ -87,12 +87,10 @@ const ForecastPage: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
const products = await getProductsList(tenantId);
|
const products = await getProductsList(tenantId);
|
||||||
|
|
||||||
// If no finished products found, use fallback products with trained models
|
// Always use real inventory products - no hardcoded fallbacks
|
||||||
if (products.length === 0) {
|
if (products.length === 0) {
|
||||||
setInventoryItems([
|
console.warn('⚠️ No finished products found in inventory for forecasting');
|
||||||
{ id: '3ae0afca-3e75-4f93-b6af-2d24c24bfcd5', name: 'Croissants' },
|
setInventoryItems([]);
|
||||||
{ id: 'b2341c5d-db5d-418e-a978-6d24cd9f039e', name: 'Pan de molde' }
|
|
||||||
]);
|
|
||||||
} else {
|
} else {
|
||||||
// Map products to the expected format
|
// Map products to the expected format
|
||||||
setInventoryItems(products.map(p => ({
|
setInventoryItems(products.map(p => ({
|
||||||
@@ -102,11 +100,8 @@ const ForecastPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load products:', error);
|
console.error('Failed to load products:', error);
|
||||||
// Use fallback products with trained models
|
// Don't use fake fallback products - show empty state instead
|
||||||
setInventoryItems([
|
setInventoryItems([]);
|
||||||
{ id: '3ae0afca-3e75-4f93-b6af-2d24c24bfcd5', name: 'Croissants' },
|
|
||||||
{ id: 'b2341c5d-db5d-418e-a978-6d24cd9f039e', name: 'Pan de molde' }
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -462,7 +457,19 @@ const ForecastPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{forecastData.length === 0 && !isLoading && !isGenerating && (
|
{inventoryItems.length === 0 && !isLoading && !isGenerating && (
|
||||||
|
<div className="mt-4 p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<AlertTriangle className="h-5 w-5 text-yellow-600 mr-2" />
|
||||||
|
<span className="text-yellow-800 text-sm">
|
||||||
|
<strong>No hay productos disponibles para predicciones.</strong>
|
||||||
|
<br />Para usar esta funcionalidad, necesitas crear productos terminados (como pan, croissants, etc.) en tu inventario con tipo "Producto terminado".
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{inventoryItems.length > 0 && forecastData.length === 0 && !isLoading && !isGenerating && (
|
||||||
<div className="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
<div className="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Info className="h-5 w-5 text-blue-600 mr-2" />
|
<Info className="h-5 w-5 text-blue-600 mr-2" />
|
||||||
|
|||||||
@@ -30,24 +30,61 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
|
|||||||
create_data = ingredient_data.model_dump()
|
create_data = ingredient_data.model_dump()
|
||||||
create_data['tenant_id'] = tenant_id
|
create_data['tenant_id'] = tenant_id
|
||||||
|
|
||||||
# Map 'category' from schema to appropriate model fields
|
# Handle product_type enum conversion
|
||||||
|
product_type_value = create_data.get('product_type', 'ingredient')
|
||||||
|
if 'product_type' in create_data:
|
||||||
|
from app.models.inventory import ProductType
|
||||||
|
try:
|
||||||
|
# Convert string to enum object
|
||||||
|
if isinstance(product_type_value, str):
|
||||||
|
for enum_member in ProductType:
|
||||||
|
if enum_member.value == product_type_value or enum_member.name == product_type_value:
|
||||||
|
create_data['product_type'] = enum_member
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# If not found, default to INGREDIENT
|
||||||
|
create_data['product_type'] = ProductType.INGREDIENT
|
||||||
|
# If it's already an enum, keep it
|
||||||
|
except Exception:
|
||||||
|
# Fallback to INGREDIENT if any issues
|
||||||
|
create_data['product_type'] = ProductType.INGREDIENT
|
||||||
|
|
||||||
|
# Handle category mapping based on product type
|
||||||
if 'category' in create_data:
|
if 'category' in create_data:
|
||||||
category_value = create_data.pop('category')
|
category_value = create_data.pop('category')
|
||||||
# For now, assume all items are ingredients and map to ingredient_category
|
|
||||||
# Convert string to enum object
|
if product_type_value == 'finished_product' or product_type_value == 'FINISHED_PRODUCT':
|
||||||
from app.models.inventory import IngredientCategory
|
# Map to product_category for finished products
|
||||||
try:
|
from app.models.inventory import ProductCategory
|
||||||
# Find the enum member by value
|
if category_value:
|
||||||
for enum_member in IngredientCategory:
|
try:
|
||||||
if enum_member.value == category_value:
|
# Find the enum member by value
|
||||||
create_data['ingredient_category'] = enum_member
|
for enum_member in ProductCategory:
|
||||||
break
|
if enum_member.value == category_value:
|
||||||
else:
|
create_data['product_category'] = enum_member
|
||||||
# If not found, default to OTHER
|
break
|
||||||
create_data['ingredient_category'] = IngredientCategory.OTHER
|
else:
|
||||||
except Exception:
|
# If not found, default to OTHER
|
||||||
# Fallback to OTHER if any issues
|
create_data['product_category'] = ProductCategory.OTHER_PRODUCTS
|
||||||
create_data['ingredient_category'] = IngredientCategory.OTHER
|
except Exception:
|
||||||
|
# Fallback to OTHER if any issues
|
||||||
|
create_data['product_category'] = ProductCategory.OTHER_PRODUCTS
|
||||||
|
else:
|
||||||
|
# Map to ingredient_category for ingredients
|
||||||
|
from app.models.inventory import IngredientCategory
|
||||||
|
if category_value:
|
||||||
|
try:
|
||||||
|
# Find the enum member by value
|
||||||
|
for enum_member in IngredientCategory:
|
||||||
|
if enum_member.value == category_value:
|
||||||
|
create_data['ingredient_category'] = enum_member
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# If not found, default to OTHER
|
||||||
|
create_data['ingredient_category'] = IngredientCategory.OTHER
|
||||||
|
except Exception:
|
||||||
|
# Fallback to OTHER if any issues
|
||||||
|
create_data['ingredient_category'] = IngredientCategory.OTHER
|
||||||
|
|
||||||
# Convert unit_of_measure string to enum object
|
# Convert unit_of_measure string to enum object
|
||||||
if 'unit_of_measure' in create_data:
|
if 'unit_of_measure' in create_data:
|
||||||
@@ -81,6 +118,92 @@ class IngredientRepository(BaseRepository[Ingredient, IngredientCreate, Ingredie
|
|||||||
logger.error("Failed to create ingredient", error=str(e), tenant_id=tenant_id)
|
logger.error("Failed to create ingredient", error=str(e), tenant_id=tenant_id)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
async def update(self, record_id: Any, obj_in: IngredientUpdate, **kwargs) -> Optional[Ingredient]:
|
||||||
|
"""Override update to handle product_type and category enum conversions"""
|
||||||
|
try:
|
||||||
|
# Prepare data and map schema fields to model fields
|
||||||
|
update_data = obj_in.model_dump(exclude_unset=True)
|
||||||
|
|
||||||
|
# Handle product_type enum conversion
|
||||||
|
if 'product_type' in update_data:
|
||||||
|
product_type_value = update_data['product_type']
|
||||||
|
from app.models.inventory import ProductType
|
||||||
|
try:
|
||||||
|
# Convert string to enum object
|
||||||
|
if isinstance(product_type_value, str):
|
||||||
|
for enum_member in ProductType:
|
||||||
|
if enum_member.value == product_type_value or enum_member.name == product_type_value:
|
||||||
|
update_data['product_type'] = enum_member
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# If not found, keep original value (don't update)
|
||||||
|
del update_data['product_type']
|
||||||
|
# If it's already an enum, keep it
|
||||||
|
except Exception:
|
||||||
|
# Remove invalid product_type to avoid update
|
||||||
|
del update_data['product_type']
|
||||||
|
|
||||||
|
# Handle category mapping based on product type
|
||||||
|
if 'category' in update_data:
|
||||||
|
category_value = update_data.pop('category')
|
||||||
|
product_type_value = update_data.get('product_type', 'ingredient')
|
||||||
|
|
||||||
|
# Get current product if we need to determine type
|
||||||
|
if 'product_type' not in update_data:
|
||||||
|
current_record = await self.get_by_id(record_id)
|
||||||
|
if current_record:
|
||||||
|
product_type_value = current_record.product_type.value if current_record.product_type else 'ingredient'
|
||||||
|
|
||||||
|
if product_type_value == 'finished_product' or product_type_value == 'FINISHED_PRODUCT':
|
||||||
|
# Map to product_category for finished products
|
||||||
|
from app.models.inventory import ProductCategory
|
||||||
|
if category_value:
|
||||||
|
try:
|
||||||
|
for enum_member in ProductCategory:
|
||||||
|
if enum_member.value == category_value:
|
||||||
|
update_data['product_category'] = enum_member
|
||||||
|
# Clear ingredient_category when setting product_category
|
||||||
|
update_data['ingredient_category'] = None
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Map to ingredient_category for ingredients
|
||||||
|
from app.models.inventory import IngredientCategory
|
||||||
|
if category_value:
|
||||||
|
try:
|
||||||
|
for enum_member in IngredientCategory:
|
||||||
|
if enum_member.value == category_value:
|
||||||
|
update_data['ingredient_category'] = enum_member
|
||||||
|
# Clear product_category when setting ingredient_category
|
||||||
|
update_data['product_category'] = None
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Handle unit_of_measure enum conversion
|
||||||
|
if 'unit_of_measure' in update_data:
|
||||||
|
unit_value = update_data['unit_of_measure']
|
||||||
|
from app.models.inventory import UnitOfMeasure
|
||||||
|
try:
|
||||||
|
if isinstance(unit_value, str):
|
||||||
|
for enum_member in UnitOfMeasure:
|
||||||
|
if enum_member.value == unit_value:
|
||||||
|
update_data['unit_of_measure'] = enum_member
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# If not found, keep original value
|
||||||
|
del update_data['unit_of_measure']
|
||||||
|
except Exception:
|
||||||
|
del update_data['unit_of_measure']
|
||||||
|
|
||||||
|
# Call parent update method
|
||||||
|
return await super().update(record_id, update_data, **kwargs)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Failed to update ingredient", error=str(e), record_id=record_id)
|
||||||
|
raise
|
||||||
|
|
||||||
async def get_ingredients_by_tenant(
|
async def get_ingredients_by_tenant(
|
||||||
self,
|
self,
|
||||||
tenant_id: UUID,
|
tenant_id: UUID,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from pydantic import BaseModel, Field, validator
|
|||||||
from typing import Generic, TypeVar
|
from typing import Generic, TypeVar
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from app.models.inventory import UnitOfMeasure, IngredientCategory, StockMovementType
|
from app.models.inventory import UnitOfMeasure, IngredientCategory, StockMovementType, ProductType, ProductCategory
|
||||||
|
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
|
||||||
@@ -32,11 +32,12 @@ class InventoryBaseSchema(BaseModel):
|
|||||||
# ===== INGREDIENT SCHEMAS =====
|
# ===== INGREDIENT SCHEMAS =====
|
||||||
|
|
||||||
class IngredientCreate(InventoryBaseSchema):
|
class IngredientCreate(InventoryBaseSchema):
|
||||||
"""Schema for creating ingredients"""
|
"""Schema for creating ingredients and finished products"""
|
||||||
name: str = Field(..., max_length=255, description="Ingredient name")
|
name: str = Field(..., max_length=255, description="Product name")
|
||||||
|
product_type: ProductType = Field(ProductType.INGREDIENT, description="Type of product (ingredient or finished_product)")
|
||||||
sku: Optional[str] = Field(None, max_length=100, description="SKU code")
|
sku: Optional[str] = Field(None, max_length=100, description="SKU code")
|
||||||
barcode: Optional[str] = Field(None, max_length=50, description="Barcode")
|
barcode: Optional[str] = Field(None, max_length=50, description="Barcode")
|
||||||
category: IngredientCategory = Field(..., description="Ingredient category")
|
category: Optional[str] = Field(None, description="Product category (ingredient or finished product category)")
|
||||||
subcategory: Optional[str] = Field(None, max_length=100, description="Subcategory")
|
subcategory: Optional[str] = Field(None, max_length=100, description="Subcategory")
|
||||||
description: Optional[str] = Field(None, description="Ingredient description")
|
description: Optional[str] = Field(None, description="Ingredient description")
|
||||||
brand: Optional[str] = Field(None, max_length=100, description="Brand name")
|
brand: Optional[str] = Field(None, max_length=100, description="Brand name")
|
||||||
@@ -83,11 +84,12 @@ class IngredientCreate(InventoryBaseSchema):
|
|||||||
|
|
||||||
|
|
||||||
class IngredientUpdate(InventoryBaseSchema):
|
class IngredientUpdate(InventoryBaseSchema):
|
||||||
"""Schema for updating ingredients"""
|
"""Schema for updating ingredients and finished products"""
|
||||||
name: Optional[str] = Field(None, max_length=255, description="Ingredient name")
|
name: Optional[str] = Field(None, max_length=255, description="Product name")
|
||||||
|
product_type: Optional[ProductType] = Field(None, description="Type of product (ingredient or finished_product)")
|
||||||
sku: Optional[str] = Field(None, max_length=100, description="SKU code")
|
sku: Optional[str] = Field(None, max_length=100, description="SKU code")
|
||||||
barcode: Optional[str] = Field(None, max_length=50, description="Barcode")
|
barcode: Optional[str] = Field(None, max_length=50, description="Barcode")
|
||||||
category: Optional[IngredientCategory] = Field(None, description="Ingredient category")
|
category: Optional[str] = Field(None, description="Product category")
|
||||||
subcategory: Optional[str] = Field(None, max_length=100, description="Subcategory")
|
subcategory: Optional[str] = Field(None, max_length=100, description="Subcategory")
|
||||||
description: Optional[str] = Field(None, description="Ingredient description")
|
description: Optional[str] = Field(None, description="Ingredient description")
|
||||||
brand: Optional[str] = Field(None, max_length=100, description="Brand name")
|
brand: Optional[str] = Field(None, max_length=100, description="Brand name")
|
||||||
@@ -122,13 +124,14 @@ class IngredientUpdate(InventoryBaseSchema):
|
|||||||
|
|
||||||
|
|
||||||
class IngredientResponse(InventoryBaseSchema):
|
class IngredientResponse(InventoryBaseSchema):
|
||||||
"""Schema for ingredient API responses"""
|
"""Schema for ingredient and finished product API responses"""
|
||||||
id: str
|
id: str
|
||||||
tenant_id: str
|
tenant_id: str
|
||||||
name: str
|
name: str
|
||||||
|
product_type: ProductType
|
||||||
sku: Optional[str]
|
sku: Optional[str]
|
||||||
barcode: Optional[str]
|
barcode: Optional[str]
|
||||||
category: IngredientCategory
|
category: Optional[str] # Will be populated from ingredient_category or product_category
|
||||||
subcategory: Optional[str]
|
subcategory: Optional[str]
|
||||||
description: Optional[str]
|
description: Optional[str]
|
||||||
brand: Optional[str]
|
brand: Optional[str]
|
||||||
|
|||||||
@@ -61,7 +61,15 @@ class InventoryService:
|
|||||||
ingredient = await repository.create_ingredient(ingredient_data, tenant_id)
|
ingredient = await repository.create_ingredient(ingredient_data, tenant_id)
|
||||||
|
|
||||||
# Convert to response schema
|
# Convert to response schema
|
||||||
response = IngredientResponse(**ingredient.to_dict())
|
ingredient_dict = ingredient.to_dict()
|
||||||
|
|
||||||
|
# Map category field based on product type
|
||||||
|
if ingredient.product_type and ingredient.product_type.value == 'finished_product':
|
||||||
|
ingredient_dict['category'] = ingredient.product_category.value if ingredient.product_category else None
|
||||||
|
else:
|
||||||
|
ingredient_dict['category'] = ingredient.ingredient_category.value if ingredient.ingredient_category else None
|
||||||
|
|
||||||
|
response = IngredientResponse(**ingredient_dict)
|
||||||
|
|
||||||
# Add computed fields
|
# Add computed fields
|
||||||
response.current_stock = 0.0
|
response.current_stock = 0.0
|
||||||
@@ -90,7 +98,15 @@ class InventoryService:
|
|||||||
stock_totals = await stock_repo.get_total_stock_by_ingredient(tenant_id, ingredient_id)
|
stock_totals = await stock_repo.get_total_stock_by_ingredient(tenant_id, ingredient_id)
|
||||||
|
|
||||||
# Convert to response schema
|
# Convert to response schema
|
||||||
response = IngredientResponse(**ingredient.to_dict())
|
ingredient_dict = ingredient.to_dict()
|
||||||
|
|
||||||
|
# Map category field based on product type
|
||||||
|
if ingredient.product_type and ingredient.product_type.value == 'finished_product':
|
||||||
|
ingredient_dict['category'] = ingredient.product_category.value if ingredient.product_category else None
|
||||||
|
else:
|
||||||
|
ingredient_dict['category'] = ingredient.ingredient_category.value if ingredient.ingredient_category else None
|
||||||
|
|
||||||
|
response = IngredientResponse(**ingredient_dict)
|
||||||
response.current_stock = stock_totals['total_available']
|
response.current_stock = stock_totals['total_available']
|
||||||
response.is_low_stock = stock_totals['total_available'] <= ingredient.low_stock_threshold
|
response.is_low_stock = stock_totals['total_available'] <= ingredient.low_stock_threshold
|
||||||
response.needs_reorder = stock_totals['total_available'] <= ingredient.reorder_point
|
response.needs_reorder = stock_totals['total_available'] <= ingredient.reorder_point
|
||||||
@@ -138,7 +154,15 @@ class InventoryService:
|
|||||||
stock_totals = await stock_repo.get_total_stock_by_ingredient(tenant_id, ingredient_id)
|
stock_totals = await stock_repo.get_total_stock_by_ingredient(tenant_id, ingredient_id)
|
||||||
|
|
||||||
# Convert to response schema
|
# Convert to response schema
|
||||||
response = IngredientResponse(**updated_ingredient.to_dict())
|
ingredient_dict = updated_ingredient.to_dict()
|
||||||
|
|
||||||
|
# Map category field based on product type
|
||||||
|
if updated_ingredient.product_type and updated_ingredient.product_type.value == 'finished_product':
|
||||||
|
ingredient_dict['category'] = updated_ingredient.product_category.value if updated_ingredient.product_category else None
|
||||||
|
else:
|
||||||
|
ingredient_dict['category'] = updated_ingredient.ingredient_category.value if updated_ingredient.ingredient_category else None
|
||||||
|
|
||||||
|
response = IngredientResponse(**ingredient_dict)
|
||||||
response.current_stock = stock_totals['total_available']
|
response.current_stock = stock_totals['total_available']
|
||||||
response.is_low_stock = stock_totals['total_available'] <= updated_ingredient.low_stock_threshold
|
response.is_low_stock = stock_totals['total_available'] <= updated_ingredient.low_stock_threshold
|
||||||
response.needs_reorder = stock_totals['total_available'] <= updated_ingredient.reorder_point
|
response.needs_reorder = stock_totals['total_available'] <= updated_ingredient.reorder_point
|
||||||
@@ -173,7 +197,15 @@ class InventoryService:
|
|||||||
stock_totals = await stock_repo.get_total_stock_by_ingredient(tenant_id, ingredient.id)
|
stock_totals = await stock_repo.get_total_stock_by_ingredient(tenant_id, ingredient.id)
|
||||||
|
|
||||||
# Convert to response schema
|
# Convert to response schema
|
||||||
response = IngredientResponse(**ingredient.to_dict())
|
ingredient_dict = ingredient.to_dict()
|
||||||
|
|
||||||
|
# Map category field based on product type
|
||||||
|
if ingredient.product_type and ingredient.product_type.value == 'finished_product':
|
||||||
|
ingredient_dict['category'] = ingredient.product_category.value if ingredient.product_category else None
|
||||||
|
else:
|
||||||
|
ingredient_dict['category'] = ingredient.ingredient_category.value if ingredient.ingredient_category else None
|
||||||
|
|
||||||
|
response = IngredientResponse(**ingredient_dict)
|
||||||
response.current_stock = stock_totals['total_available']
|
response.current_stock = stock_totals['total_available']
|
||||||
response.is_low_stock = stock_totals['total_available'] <= ingredient.low_stock_threshold
|
response.is_low_stock = stock_totals['total_available'] <= ingredient.low_stock_threshold
|
||||||
response.needs_reorder = stock_totals['total_available'] <= ingredient.reorder_point
|
response.needs_reorder = stock_totals['total_available'] <= ingredient.reorder_point
|
||||||
|
|||||||
@@ -627,13 +627,14 @@ class EnhancedTrainingService:
|
|||||||
"status": status or "pending",
|
"status": status or "pending",
|
||||||
"progress": progress or 0,
|
"progress": progress or 0,
|
||||||
"current_step": current_step or "initializing",
|
"current_step": current_step or "initializing",
|
||||||
"start_time": datetime.utcnow()
|
"start_time": datetime.now(timezone.utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
if error_message:
|
if error_message:
|
||||||
log_data["error_message"] = error_message
|
log_data["error_message"] = error_message
|
||||||
if results:
|
if results:
|
||||||
log_data["results"] = results
|
# Ensure results are JSON-serializable before storing
|
||||||
|
log_data["results"] = make_json_serializable(results)
|
||||||
|
|
||||||
await self.training_log_repo.create_training_log(log_data)
|
await self.training_log_repo.create_training_log(log_data)
|
||||||
logger.info("Created initial training log", job_id=job_id, tenant_id=tenant_id)
|
logger.info("Created initial training log", job_id=job_id, tenant_id=tenant_id)
|
||||||
@@ -655,9 +656,10 @@ class EnhancedTrainingService:
|
|||||||
if error_message:
|
if error_message:
|
||||||
update_data["error_message"] = error_message
|
update_data["error_message"] = error_message
|
||||||
if results:
|
if results:
|
||||||
update_data["results"] = results
|
# Ensure results are JSON-serializable before storing
|
||||||
|
update_data["results"] = make_json_serializable(results)
|
||||||
if status in ["completed", "failed"]:
|
if status in ["completed", "failed"]:
|
||||||
update_data["end_time"] = datetime.utcnow()
|
update_data["end_time"] = datetime.now(timezone.utc)
|
||||||
|
|
||||||
if update_data:
|
if update_data:
|
||||||
await self.training_log_repo.update(existing_log.id, update_data)
|
await self.training_log_repo.update(existing_log.id, update_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user