Improve the frontend and repository layer

This commit is contained in:
Urtzi Alfaro
2025-10-23 07:44:54 +02:00
parent 8d30172483
commit 07c33fa578
112 changed files with 14726 additions and 2733 deletions

View File

@@ -0,0 +1,155 @@
"""add tenant_settings
Revision ID: 20251022_0000
Revises: 20251017_0000
Create Date: 2025-10-22
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from uuid import uuid4
# revision identifiers, used by Alembic.
revision = '20251022_0000'
down_revision = '20251017_0000'
branch_labels = None
depends_on = None
def get_default_settings():
"""Get default settings for all categories"""
return {
"procurement_settings": {
"auto_approve_enabled": True,
"auto_approve_threshold_eur": 500.0,
"auto_approve_min_supplier_score": 0.80,
"require_approval_new_suppliers": True,
"require_approval_critical_items": True,
"procurement_lead_time_days": 3,
"demand_forecast_days": 14,
"safety_stock_percentage": 20.0,
"po_approval_reminder_hours": 24,
"po_critical_escalation_hours": 12
},
"inventory_settings": {
"low_stock_threshold": 10,
"reorder_point": 20,
"reorder_quantity": 50,
"expiring_soon_days": 7,
"expiration_warning_days": 3,
"quality_score_threshold": 8.0,
"temperature_monitoring_enabled": True,
"refrigeration_temp_min": 1.0,
"refrigeration_temp_max": 4.0,
"freezer_temp_min": -20.0,
"freezer_temp_max": -15.0,
"room_temp_min": 18.0,
"room_temp_max": 25.0,
"temp_deviation_alert_minutes": 15,
"critical_temp_deviation_minutes": 5
},
"production_settings": {
"planning_horizon_days": 7,
"minimum_batch_size": 1.0,
"maximum_batch_size": 100.0,
"production_buffer_percentage": 10.0,
"working_hours_per_day": 12,
"max_overtime_hours": 4,
"capacity_utilization_target": 0.85,
"capacity_warning_threshold": 0.95,
"quality_check_enabled": True,
"minimum_yield_percentage": 85.0,
"quality_score_threshold": 8.0,
"schedule_optimization_enabled": True,
"prep_time_buffer_minutes": 30,
"cleanup_time_buffer_minutes": 15,
"labor_cost_per_hour_eur": 15.0,
"overhead_cost_percentage": 20.0
},
"supplier_settings": {
"default_payment_terms_days": 30,
"default_delivery_days": 3,
"excellent_delivery_rate": 95.0,
"good_delivery_rate": 90.0,
"excellent_quality_rate": 98.0,
"good_quality_rate": 95.0,
"critical_delivery_delay_hours": 24,
"critical_quality_rejection_rate": 10.0,
"high_cost_variance_percentage": 15.0
},
"pos_settings": {
"sync_interval_minutes": 5,
"auto_sync_products": True,
"auto_sync_transactions": True
},
"order_settings": {
"max_discount_percentage": 50.0,
"default_delivery_window_hours": 48,
"dynamic_pricing_enabled": False,
"discount_enabled": True,
"delivery_tracking_enabled": True
}
}
def upgrade():
"""Create tenant_settings table and seed existing tenants"""
# Create tenant_settings table
op.create_table(
'tenant_settings',
sa.Column('id', postgresql.UUID(as_uuid=True), primary_key=True, default=uuid4),
sa.Column('tenant_id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('procurement_settings', postgresql.JSON(), nullable=False),
sa.Column('inventory_settings', postgresql.JSON(), nullable=False),
sa.Column('production_settings', postgresql.JSON(), nullable=False),
sa.Column('supplier_settings', postgresql.JSON(), nullable=False),
sa.Column('pos_settings', postgresql.JSON(), nullable=False),
sa.Column('order_settings', postgresql.JSON(), nullable=False),
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
sa.UniqueConstraint('tenant_id', name='uq_tenant_settings_tenant_id')
)
# Create indexes
op.create_index('ix_tenant_settings_tenant_id', 'tenant_settings', ['tenant_id'])
# Seed existing tenants with default settings
connection = op.get_bind()
# Get all existing tenant IDs
result = connection.execute(sa.text("SELECT id FROM tenants"))
tenant_ids = [row[0] for row in result]
# Insert default settings for each existing tenant
defaults = get_default_settings()
for tenant_id in tenant_ids:
connection.execute(
sa.text("""
INSERT INTO tenant_settings (
id, tenant_id, procurement_settings, inventory_settings,
production_settings, supplier_settings, pos_settings, order_settings
) VALUES (
:id, :tenant_id, :procurement_settings::jsonb, :inventory_settings::jsonb,
:production_settings::jsonb, :supplier_settings::jsonb,
:pos_settings::jsonb, :order_settings::jsonb
)
"""),
{
"id": str(uuid4()),
"tenant_id": tenant_id,
"procurement_settings": str(defaults["procurement_settings"]).replace("'", '"').replace("True", "true").replace("False", "false"),
"inventory_settings": str(defaults["inventory_settings"]).replace("'", '"').replace("True", "true").replace("False", "false"),
"production_settings": str(defaults["production_settings"]).replace("'", '"').replace("True", "true").replace("False", "false"),
"supplier_settings": str(defaults["supplier_settings"]).replace("'", '"').replace("True", "true").replace("False", "false"),
"pos_settings": str(defaults["pos_settings"]).replace("'", '"').replace("True", "true").replace("False", "false"),
"order_settings": str(defaults["order_settings"]).replace("'", '"').replace("True", "true").replace("False", "false")
}
)
def downgrade():
"""Drop tenant_settings table"""
op.drop_index('ix_tenant_settings_tenant_id', table_name='tenant_settings')
op.drop_table('tenant_settings')