721 lines
54 KiB
Python
721 lines
54 KiB
Python
|
|
"""Initial schema for suppliers service
|
||
|
|
|
||
|
|
Revision ID: 0001
|
||
|
|
Revises:
|
||
|
|
Create Date: 2025-09-30 18:00:00.0000
|
||
|
|
|
||
|
|
"""
|
||
|
|
from alembic import op
|
||
|
|
import sqlalchemy as sa
|
||
|
|
from sqlalchemy.dialects import postgresql
|
||
|
|
|
||
|
|
# revision identifiers, used by Alembic.
|
||
|
|
revision = '00001'
|
||
|
|
down_revision = None
|
||
|
|
branch_labels = None
|
||
|
|
depends_on = None
|
||
|
|
|
||
|
|
|
||
|
|
def upgrade() -> None:
|
||
|
|
# Create suppliers table
|
||
|
|
op.create_table('suppliers',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('name', sa.String(255), nullable=False),
|
||
|
|
sa.Column('supplier_code', sa.String(50), nullable=True),
|
||
|
|
sa.Column('tax_id', sa.String(50), nullable=True),
|
||
|
|
sa.Column('registration_number', sa.String(100), nullable=True),
|
||
|
|
sa.Column('supplier_type', sa.Enum('ingredients', 'packaging', 'equipment', 'services', 'utilities', 'multi', name='suppliertype'), nullable=False),
|
||
|
|
sa.Column('status', sa.Enum('active', 'inactive', 'pending_approval', 'suspended', 'blacklisted', name='supplierstatus'), nullable=False),
|
||
|
|
sa.Column('contact_person', sa.String(200), nullable=True),
|
||
|
|
sa.Column('email', sa.String(254), nullable=True),
|
||
|
|
sa.Column('phone', sa.String(30), nullable=True),
|
||
|
|
sa.Column('mobile', sa.String(30), nullable=True),
|
||
|
|
sa.Column('website', sa.String(255), nullable=True),
|
||
|
|
sa.Column('address_line1', sa.String(255), nullable=True),
|
||
|
|
sa.Column('address_line2', sa.String(255), nullable=True),
|
||
|
|
sa.Column('city', sa.String(100), nullable=True),
|
||
|
|
sa.Column('state_province', sa.String(100), nullable=True),
|
||
|
|
sa.Column('postal_code', sa.String(20), nullable=True),
|
||
|
|
sa.Column('country', sa.String(100), nullable=True),
|
||
|
|
sa.Column('payment_terms', sa.Enum('cod', 'net_15', 'net_30', 'net_45', 'net_60', 'prepaid', 'credit_terms', name='paymentterms'), nullable=False),
|
||
|
|
sa.Column('credit_limit', sa.Numeric(12, 2), nullable=True),
|
||
|
|
sa.Column('currency', sa.String(3), nullable=False),
|
||
|
|
sa.Column('standard_lead_time', sa.Integer, nullable=False),
|
||
|
|
sa.Column('minimum_order_amount', sa.Numeric(10, 2), nullable=True),
|
||
|
|
sa.Column('delivery_area', sa.String(255), nullable=True),
|
||
|
|
sa.Column('quality_rating', sa.Float, nullable=True),
|
||
|
|
sa.Column('delivery_rating', sa.Float, nullable=True),
|
||
|
|
sa.Column('total_orders', sa.Integer, nullable=False),
|
||
|
|
sa.Column('total_amount', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('approved_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('approved_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('rejection_reason', sa.Text, nullable=True),
|
||
|
|
sa.Column('notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('certifications', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('business_hours', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('specializations', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('updated_by', sa.UUID(), nullable=False),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_suppliers_tenant_id'), 'suppliers', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_suppliers_name'), 'suppliers', ['name'], unique=False)
|
||
|
|
op.create_index(op.f('ix_suppliers_supplier_code'), 'suppliers', ['supplier_code'], unique=False)
|
||
|
|
op.create_index(op.f('ix_suppliers_email'), 'suppliers', ['email'], unique=False)
|
||
|
|
op.create_index(op.f('ix_suppliers_status'), 'suppliers', ['status'], unique=False)
|
||
|
|
op.create_index('ix_suppliers_tenant_name', 'suppliers', ['tenant_id', 'name'], unique=False)
|
||
|
|
op.create_index('ix_suppliers_tenant_status', 'suppliers', ['tenant_id', 'status'], unique=False)
|
||
|
|
op.create_index('ix_suppliers_tenant_type', 'suppliers', ['tenant_id', 'supplier_type'], unique=False)
|
||
|
|
op.create_index('ix_suppliers_quality_rating', 'suppliers', ['quality_rating'], unique=False)
|
||
|
|
|
||
|
|
# Create supplier_price_lists table
|
||
|
|
op.create_table('supplier_price_lists',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('inventory_product_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('product_code', sa.String(100), nullable=True),
|
||
|
|
sa.Column('unit_price', sa.Numeric(10, 4), nullable=False),
|
||
|
|
sa.Column('unit_of_measure', sa.String(20), nullable=False),
|
||
|
|
sa.Column('minimum_order_quantity', sa.Integer, nullable=True),
|
||
|
|
sa.Column('price_per_unit', sa.Numeric(10, 4), nullable=False),
|
||
|
|
sa.Column('tier_pricing', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('effective_date', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('expiry_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('is_active', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('brand', sa.String(100), nullable=True),
|
||
|
|
sa.Column('packaging_size', sa.String(50), nullable=True),
|
||
|
|
sa.Column('origin_country', sa.String(100), nullable=True),
|
||
|
|
sa.Column('shelf_life_days', sa.Integer, nullable=True),
|
||
|
|
sa.Column('storage_requirements', sa.Text, nullable=True),
|
||
|
|
sa.Column('quality_specs', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('allergens', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('updated_by', sa.UUID(), nullable=False),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_supplier_price_lists_tenant_id'), 'supplier_price_lists', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_price_lists_supplier_id'), 'supplier_price_lists', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_price_lists_inventory_product_id'), 'supplier_price_lists', ['inventory_product_id'], unique=False)
|
||
|
|
op.create_index('ix_price_lists_tenant_supplier', 'supplier_price_lists', ['tenant_id', 'supplier_id'], unique=False)
|
||
|
|
op.create_index('ix_price_lists_inventory_product', 'supplier_price_lists', ['inventory_product_id'], unique=False)
|
||
|
|
op.create_index('ix_price_lists_active', 'supplier_price_lists', ['is_active'], unique=False)
|
||
|
|
op.create_index('ix_price_lists_effective_date', 'supplier_price_lists', ['effective_date'], unique=False)
|
||
|
|
|
||
|
|
# Create purchase_orders table
|
||
|
|
op.create_table('purchase_orders',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('po_number', sa.String(50), nullable=False),
|
||
|
|
sa.Column('reference_number', sa.String(100), nullable=True),
|
||
|
|
sa.Column('status', sa.Enum('draft', 'pending_approval', 'approved', 'sent_to_supplier', 'confirmed', 'partially_received', 'completed', 'cancelled', 'disputed', name='purchaseorderstatus'), nullable=False),
|
||
|
|
sa.Column('priority', sa.String(20), nullable=False),
|
||
|
|
sa.Column('order_date', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('required_delivery_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('estimated_delivery_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('subtotal', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('tax_amount', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('shipping_cost', sa.Numeric(10, 2), nullable=False),
|
||
|
|
sa.Column('discount_amount', sa.Numeric(10, 2), nullable=False),
|
||
|
|
sa.Column('total_amount', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('currency', sa.String(3), nullable=False),
|
||
|
|
sa.Column('delivery_address', sa.Text, nullable=True),
|
||
|
|
sa.Column('delivery_instructions', sa.Text, nullable=True),
|
||
|
|
sa.Column('delivery_contact', sa.String(200), nullable=True),
|
||
|
|
sa.Column('delivery_phone', sa.String(30), nullable=True),
|
||
|
|
sa.Column('requires_approval', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('approved_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('approved_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('rejection_reason', sa.Text, nullable=True),
|
||
|
|
sa.Column('sent_to_supplier_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('supplier_confirmation_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('supplier_reference', sa.String(100), nullable=True),
|
||
|
|
sa.Column('notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('internal_notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('terms_and_conditions', sa.Text, nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('updated_by', sa.UUID(), nullable=False),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_purchase_orders_tenant_id'), 'purchase_orders', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_purchase_orders_supplier_id'), 'purchase_orders', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_purchase_orders_po_number'), 'purchase_orders', ['po_number'], unique=False)
|
||
|
|
op.create_index(op.f('ix_purchase_orders_status'), 'purchase_orders', ['status'], unique=False)
|
||
|
|
op.create_index('ix_purchase_orders_tenant_supplier', 'purchase_orders', ['tenant_id', 'supplier_id'], unique=False)
|
||
|
|
op.create_index('ix_purchase_orders_tenant_status', 'purchase_orders', ['tenant_id', 'status'], unique=False)
|
||
|
|
op.create_index('ix_purchase_orders_order_date', 'purchase_orders', ['order_date'], unique=False)
|
||
|
|
op.create_index('ix_purchase_orders_delivery_date', 'purchase_orders', ['required_delivery_date'], unique=False)
|
||
|
|
|
||
|
|
# Create purchase_order_items table
|
||
|
|
op.create_table('purchase_order_items',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('purchase_order_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('price_list_item_id', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('inventory_product_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('product_code', sa.String(10), nullable=True),
|
||
|
|
sa.Column('ordered_quantity', sa.Integer, nullable=False),
|
||
|
|
sa.Column('unit_of_measure', sa.String(20), nullable=False),
|
||
|
|
sa.Column('unit_price', sa.Numeric(10, 4), nullable=False),
|
||
|
|
sa.Column('line_total', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('received_quantity', sa.Integer, nullable=False),
|
||
|
|
sa.Column('remaining_quantity', sa.Integer, nullable=False),
|
||
|
|
sa.Column('quality_requirements', sa.Text, nullable=True),
|
||
|
|
sa.Column('item_notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.ForeignKeyConstraint(['price_list_item_id'], ['supplier_price_lists.id'], ),
|
||
|
|
sa.ForeignKeyConstraint(['purchase_order_id'], ['purchase_orders.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_purchase_order_items_tenant_id'), 'purchase_order_items', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_purchase_order_items_purchase_order_id'), 'purchase_order_items', ['purchase_order_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_purchase_order_items_price_list_item_id'), 'purchase_order_items', ['price_list_item_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_purchase_order_items_inventory_product_id'), 'purchase_order_items', ['inventory_product_id'], unique=False)
|
||
|
|
op.create_index('ix_po_items_tenant_po', 'purchase_order_items', ['tenant_id', 'purchase_order_id'], unique=False)
|
||
|
|
op.create_index('ix_po_items_inventory_product', 'purchase_order_items', ['inventory_product_id'], unique=False)
|
||
|
|
|
||
|
|
# Create deliveries table
|
||
|
|
op.create_table('deliveries',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('purchase_order_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('delivery_number', sa.String(50), nullable=False),
|
||
|
|
sa.Column('supplier_delivery_note', sa.String(10), nullable=True),
|
||
|
|
sa.Column('status', sa.Enum('scheduled', 'in_transit', 'out_for_delivery', 'delivered', 'partially_delivered', 'failed_delivery', 'returned', name='deliverystatus'), nullable=False),
|
||
|
|
sa.Column('scheduled_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('estimated_arrival', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('actual_arrival', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('delivery_address', sa.Text, nullable=True),
|
||
|
|
sa.Column('delivery_contact', sa.String(20), nullable=True),
|
||
|
|
sa.Column('delivery_phone', sa.String(30), nullable=True),
|
||
|
|
sa.Column('carrier_name', sa.String(200), nullable=True),
|
||
|
|
sa.Column('tracking_number', sa.String(100), nullable=True),
|
||
|
|
sa.Column('inspection_passed', sa.Boolean, nullable=True),
|
||
|
|
sa.Column('inspection_notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('quality_issues', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('received_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('received_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('photos', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=False),
|
||
|
|
sa.ForeignKeyConstraint(['purchase_order_id'], ['purchase_orders.id'], ),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_deliveries_tenant_id'), 'deliveries', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_deliveries_purchase_order_id'), 'deliveries', ['purchase_order_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_deliveries_supplier_id'), 'deliveries', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_deliveries_delivery_number'), 'deliveries', ['delivery_number'], unique=False)
|
||
|
|
op.create_index(op.f('ix_deliveries_status'), 'deliveries', ['status'], unique=False)
|
||
|
|
op.create_index('ix_deliveries_tenant_status', 'deliveries', ['tenant_id', 'status'], unique=False)
|
||
|
|
op.create_index('ix_deliveries_scheduled_date', 'deliveries', ['scheduled_date'], unique=False)
|
||
|
|
|
||
|
|
# Create delivery_items table
|
||
|
|
op.create_table('delivery_items',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('delivery_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('purchase_order_item_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('inventory_product_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('ordered_quantity', sa.Integer, nullable=False),
|
||
|
|
sa.Column('delivered_quantity', sa.Integer, nullable=False),
|
||
|
|
sa.Column('accepted_quantity', sa.Integer, nullable=False),
|
||
|
|
sa.Column('rejected_quantity', sa.Integer, nullable=False),
|
||
|
|
sa.Column('batch_lot_number', sa.String(100), nullable=True),
|
||
|
|
sa.Column('expiry_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('quality_grade', sa.String(20), nullable=True),
|
||
|
|
sa.Column('quality_issues', sa.Text, nullable=True),
|
||
|
|
sa.Column('rejection_reason', sa.Text, nullable=True),
|
||
|
|
sa.Column('item_notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.ForeignKeyConstraint(['delivery_id'], ['deliveries.id'], ),
|
||
|
|
sa.ForeignKeyConstraint(['purchase_order_item_id'], ['purchase_order_items.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_delivery_items_tenant_id'), 'delivery_items', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_delivery_items_delivery_id'), 'delivery_items', ['delivery_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_delivery_items_purchase_order_item_id'), 'delivery_items', ['purchase_order_item_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_delivery_items_inventory_product_id'), 'delivery_items', ['inventory_product_id'], unique=False)
|
||
|
|
op.create_index('ix_delivery_items_tenant_delivery', 'delivery_items', ['tenant_id', 'delivery_id'], unique=False)
|
||
|
|
op.create_index('ix_delivery_items_inventory_product', 'delivery_items', ['inventory_product_id'], unique=False)
|
||
|
|
|
||
|
|
# Create supplier_quality_reviews table
|
||
|
|
op.create_table('supplier_quality_reviews',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('purchase_order_id', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('delivery_id', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('review_date', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('review_type', sa.String(50), nullable=False),
|
||
|
|
sa.Column('quality_rating', sa.Enum('excellent', 'good', 'average', 'poor', 'very_poor', name='qualityrating'), nullable=False),
|
||
|
|
sa.Column('delivery_rating', sa.Enum('excellent', 'good', 'average', 'poor', 'very_poor', name='deliveryrating'), nullable=False),
|
||
|
|
sa.Column('communication_rating', sa.Integer, nullable=False),
|
||
|
|
sa.Column('overall_rating', sa.Float, nullable=False),
|
||
|
|
sa.Column('quality_comments', sa.Text, nullable=True),
|
||
|
|
sa.Column('delivery_comments', sa.Text, nullable=True),
|
||
|
|
sa.Column('communication_comments', sa.Text, nullable=True),
|
||
|
|
sa.Column('improvement_suggestions', sa.Text, nullable=True),
|
||
|
|
sa.Column('quality_issues', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('corrective_actions', sa.Text, nullable=True),
|
||
|
|
sa.Column('follow_up_required', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('follow_up_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('is_final', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('approved_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('reviewed_by', sa.UUID(), nullable=False),
|
||
|
|
sa.ForeignKeyConstraint(['delivery_id'], ['deliveries.id'], ),
|
||
|
|
sa.ForeignKeyConstraint(['purchase_order_id'], ['purchase_orders.id'], ),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_supplier_quality_reviews_tenant_id'), 'supplier_quality_reviews', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_quality_reviews_supplier_id'), 'supplier_quality_reviews', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_quality_reviews_purchase_order_id'), 'supplier_quality_reviews', ['purchase_order_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_quality_reviews_delivery_id'), 'supplier_quality_reviews', ['delivery_id'], unique=False)
|
||
|
|
op.create_index('ix_quality_reviews_tenant_supplier', 'supplier_quality_reviews', ['tenant_id', 'supplier_id'], unique=False)
|
||
|
|
op.create_index('ix_quality_reviews_date', 'supplier_quality_reviews', ['review_date'], unique=False)
|
||
|
|
op.create_index('ix_quality_reviews_overall_rating', 'supplier_quality_reviews', ['overall_rating'], unique=False)
|
||
|
|
|
||
|
|
# Create supplier_invoices table
|
||
|
|
op.create_table('supplier_invoices',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('purchase_order_id', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('invoice_number', sa.String(50), nullable=False),
|
||
|
|
sa.Column('supplier_invoice_number', sa.String(10), nullable=False),
|
||
|
|
sa.Column('status', sa.Enum('pending', 'approved', 'paid', 'overdue', 'disputed', 'cancelled', name='invoicestatus'), nullable=False),
|
||
|
|
sa.Column('invoice_date', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('due_date', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('received_date', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('subtotal', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('tax_amount', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('shipping_cost', sa.Numeric(10, 2), nullable=False),
|
||
|
|
sa.Column('discount_amount', sa.Numeric(10, 2), nullable=False),
|
||
|
|
sa.Column('total_amount', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('currency', sa.String(3), nullable=False),
|
||
|
|
sa.Column('paid_amount', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('payment_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('payment_reference', sa.String(10), nullable=True),
|
||
|
|
sa.Column('approved_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('approved_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('rejection_reason', sa.Text, nullable=True),
|
||
|
|
sa.Column('notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('invoice_document_url', sa.String(500), nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=False),
|
||
|
|
sa.ForeignKeyConstraint(['purchase_order_id'], ['purchase_orders.id'], ),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_supplier_invoices_tenant_id'), 'supplier_invoices', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_invoices_supplier_id'), 'supplier_invoices', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_invoices_purchase_order_id'), 'supplier_invoices', ['purchase_order_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_invoices_invoice_number'), 'supplier_invoices', ['invoice_number'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_invoices_status'), 'supplier_invoices', ['status'], unique=False)
|
||
|
|
op.create_index('ix_invoices_tenant_supplier', 'supplier_invoices', ['tenant_id', 'supplier_id'], unique=False)
|
||
|
|
op.create_index('ix_invoices_tenant_status', 'supplier_invoices', ['tenant_id', 'status'], unique=False)
|
||
|
|
op.create_index('ix_invoices_due_date', 'supplier_invoices', ['due_date'], unique=False)
|
||
|
|
op.create_index('ix_invoices_invoice_number', 'supplier_invoices', ['invoice_number'], unique=False)
|
||
|
|
|
||
|
|
# Create supplier_performance_metrics table
|
||
|
|
op.create_table('supplier_performance_metrics',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('metric_type', sa.Enum('delivery_performance', 'quality_score', 'price_competitiveness', 'communication_rating', 'order_accuracy', 'response_time', 'compliance_score', 'financial_stability', name='performancemetrictype'), nullable=False),
|
||
|
|
sa.Column('period', sa.Enum('daily', 'weekly', 'monthly', 'quarterly', 'yearly', name='performanceperiod'), nullable=False),
|
||
|
|
sa.Column('period_start', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('period_end', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('metric_value', sa.Float, nullable=False),
|
||
|
|
sa.Column('target_value', sa.Float, nullable=True),
|
||
|
|
sa.Column('previous_value', sa.Float, nullable=True),
|
||
|
|
sa.Column('total_orders', sa.Integer, nullable=False),
|
||
|
|
sa.Column('total_deliveries', sa.Integer, nullable=False),
|
||
|
|
sa.Column('on_time_deliveries', sa.Integer, nullable=False),
|
||
|
|
sa.Column('late_deliveries', sa.Integer, nullable=False),
|
||
|
|
sa.Column('quality_issues', sa.Integer, nullable=False),
|
||
|
|
sa.Column('total_amount', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('metrics_data', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('trend_direction', sa.String(20), nullable=True),
|
||
|
|
sa.Column('trend_percentage', sa.Float, nullable=True),
|
||
|
|
sa.Column('notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('external_factors', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('calculated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('calculated_by', sa.UUID(), nullable=True),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_supplier_performance_metrics_tenant_id'), 'supplier_performance_metrics', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_performance_metrics_supplier_id'), 'supplier_performance_metrics', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_performance_metrics_metric_type'), 'supplier_performance_metrics', ['metric_type'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_performance_metrics_period'), 'supplier_performance_metrics', ['period'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_performance_metrics_period_start'), 'supplier_performance_metrics', ['period_start'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_performance_metrics_period_end'), 'supplier_performance_metrics', ['period_end'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_performance_metrics_metric_value'), 'supplier_performance_metrics', ['metric_value'], unique=False)
|
||
|
|
op.create_index('ix_performance_metrics_tenant_supplier', 'supplier_performance_metrics', ['tenant_id', 'supplier_id'], unique=False)
|
||
|
|
op.create_index('ix_performance_metrics_type_period', 'supplier_performance_metrics', ['metric_type', 'period'], unique=False)
|
||
|
|
op.create_index('ix_performance_metrics_period_dates', 'supplier_performance_metrics', ['period_start', 'period_end'], unique=False)
|
||
|
|
op.create_index('ix_performance_metrics_value', 'supplier_performance_metrics', ['metric_value'], unique=False)
|
||
|
|
|
||
|
|
# Create supplier_alerts table
|
||
|
|
op.create_table('supplier_alerts',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('alert_type', sa.Enum('poor_quality', 'late_delivery', 'price_increase', 'low_performance', 'contract_expiry', 'compliance_issue', 'financial_risk', 'communication_issue', 'capacity_constraint', 'certification_expiry', name='alerttype'), nullable=False),
|
||
|
|
sa.Column('severity', sa.Enum('critical', 'high', 'medium', 'low', 'info', name='alertseverity'), nullable=False),
|
||
|
|
sa.Column('status', sa.Enum('active', 'acknowledged', 'in_progress', 'resolved', 'dismissed', name='alertstatus'), nullable=False),
|
||
|
|
sa.Column('title', sa.String(255), nullable=False),
|
||
|
|
sa.Column('message', sa.Text, nullable=False),
|
||
|
|
sa.Column('description', sa.Text, nullable=True),
|
||
|
|
sa.Column('trigger_value', sa.Float, nullable=True),
|
||
|
|
sa.Column('threshold_value', sa.Float, nullable=True),
|
||
|
|
sa.Column('metric_type', sa.Enum('delivery_performance', 'quality_score', 'price_competitiveness', 'communication_rating', 'order_accuracy', 'response_time', 'compliance_score', 'financial_stability', name='performancemetrictype'), nullable=True),
|
||
|
|
sa.Column('purchase_order_id', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('delivery_id', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('performance_metric_id', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('triggered_at', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('acknowledged_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('acknowledged_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('resolved_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('resolved_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('recommended_actions', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('actions_taken', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('resolution_notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('auto_resolve', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('auto_resolve_condition', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('escalated', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('escalated_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('escalated_to', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('notification_sent', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('notification_sent_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('notification_recipients', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('priority_score', sa.Integer, nullable=False),
|
||
|
|
sa.Column('business_impact', sa.String(50), nullable=True),
|
||
|
|
sa.Column('tags', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=True),
|
||
|
|
sa.ForeignKeyConstraint(['performance_metric_id'], ['supplier_performance_metrics.id'], ),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_tenant_id'), 'supplier_alerts', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_supplier_id'), 'supplier_alerts', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_alert_type'), 'supplier_alerts', ['alert_type'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_severity'), 'supplier_alerts', ['severity'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_status'), 'supplier_alerts', ['status'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_metric_type'), 'supplier_alerts', ['metric_type'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_purchase_order_id'), 'supplier_alerts', ['purchase_order_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_delivery_id'), 'supplier_alerts', ['delivery_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_alerts_performance_metric_id'), 'supplier_alerts', ['performance_metric_id'], unique=False)
|
||
|
|
op.create_index('ix_supplier_alerts_tenant_supplier', 'supplier_alerts', ['tenant_id', 'supplier_id'], unique=False)
|
||
|
|
op.create_index('ix_supplier_alerts_type_severity', 'supplier_alerts', ['alert_type', 'severity'], unique=False)
|
||
|
|
op.create_index('ix_supplier_alerts_status_triggered', 'supplier_alerts', ['status', 'triggered_at'], unique=False)
|
||
|
|
op.create_index('ix_supplier_alerts_priority', 'supplier_alerts', ['priority_score'], unique=False)
|
||
|
|
|
||
|
|
# Create supplier_scorecards table
|
||
|
|
op.create_table('supplier_scorecards',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('supplier_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('scorecard_name', sa.String(255), nullable=False),
|
||
|
|
sa.Column('period', sa.Enum('daily', 'weekly', 'monthly', 'quarterly', 'yearly', name='performanceperiod'), nullable=False),
|
||
|
|
sa.Column('period_start', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('period_end', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('overall_score', sa.Float, nullable=False),
|
||
|
|
sa.Column('quality_score', sa.Float, nullable=False),
|
||
|
|
sa.Column('delivery_score', sa.Float, nullable=False),
|
||
|
|
sa.Column('cost_score', sa.Float, nullable=False),
|
||
|
|
sa.Column('service_score', sa.Float, nullable=False),
|
||
|
|
sa.Column('overall_rank', sa.Integer, nullable=True),
|
||
|
|
sa.Column('category_rank', sa.Integer, nullable=True),
|
||
|
|
sa.Column('total_suppliers_evaluated', sa.Integer, nullable=True),
|
||
|
|
sa.Column('on_time_delivery_rate', sa.Float, nullable=False),
|
||
|
|
sa.Column('quality_rejection_rate', sa.Float, nullable=False),
|
||
|
|
sa.Column('order_accuracy_rate', sa.Float, nullable=False),
|
||
|
|
sa.Column('response_time_hours', sa.Float, nullable=False),
|
||
|
|
sa.Column('cost_variance_percentage', sa.Float, nullable=False),
|
||
|
|
sa.Column('total_orders_processed', sa.Integer, nullable=False),
|
||
|
|
sa.Column('total_amount_processed', sa.Numeric(12, 2), nullable=False),
|
||
|
|
sa.Column('average_order_value', sa.Numeric(10, 2), nullable=False),
|
||
|
|
sa.Column('cost_savings_achieved', sa.Numeric(10, 2), nullable=False),
|
||
|
|
sa.Column('score_trend', sa.String(20), nullable=True),
|
||
|
|
sa.Column('score_change_percentage', sa.Float, nullable=True),
|
||
|
|
sa.Column('strengths', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('improvement_areas', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('recommended_actions', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('is_final', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('approved_by', sa.UUID(), nullable=True),
|
||
|
|
sa.Column('approved_at', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('attachments', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('generated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('generated_by', sa.UUID(), nullable=False),
|
||
|
|
sa.ForeignKeyConstraint(['supplier_id'], ['suppliers.id'], ),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_tenant_id'), 'supplier_scorecards', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_supplier_id'), 'supplier_scorecards', ['supplier_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_scorecard_name'), 'supplier_scorecards', ['scorecard_name'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_period'), 'supplier_scorecards', ['period'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_period_start'), 'supplier_scorecards', ['period_start'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_period_end'), 'supplier_scorecards', ['period_end'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_overall_score'), 'supplier_scorecards', ['overall_score'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_scorecards_is_final'), 'supplier_scorecards', ['is_final'], unique=False)
|
||
|
|
op.create_index('ix_scorecards_tenant_supplier', 'supplier_scorecards', ['tenant_id', 'supplier_id'], unique=False)
|
||
|
|
op.create_index('ix_scorecards_period_dates', 'supplier_scorecards', ['period_start', 'period_end'], unique=False)
|
||
|
|
op.create_index('ix_scorecards_overall_score', 'supplier_scorecards', ['overall_score'], unique=False)
|
||
|
|
op.create_index('ix_scorecards_period', 'supplier_scorecards', ['period'], unique=False)
|
||
|
|
op.create_index('ix_scorecards_final', 'supplier_scorecards', ['is_final'], unique=False)
|
||
|
|
|
||
|
|
# Create supplier_benchmarks table
|
||
|
|
op.create_table('supplier_benchmarks',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('benchmark_name', sa.String(255), nullable=False),
|
||
|
|
sa.Column('benchmark_type', sa.String(50), nullable=False),
|
||
|
|
sa.Column('supplier_category', sa.String(100), nullable=True),
|
||
|
|
sa.Column('metric_type', sa.Enum('delivery_performance', 'quality_score', 'price_competitiveness', 'communication_rating', 'order_accuracy', 'response_time', 'compliance_score', 'financial_stability', name='performancemetrictype'), nullable=False),
|
||
|
|
sa.Column('excellent_threshold', sa.Float, nullable=False),
|
||
|
|
sa.Column('good_threshold', sa.Float, nullable=False),
|
||
|
|
sa.Column('acceptable_threshold', sa.Float, nullable=False),
|
||
|
|
sa.Column('poor_threshold', sa.Float, nullable=False),
|
||
|
|
sa.Column('data_source', sa.String(255), nullable=True),
|
||
|
|
sa.Column('sample_size', sa.Integer, nullable=True),
|
||
|
|
sa.Column('confidence_level', sa.Float, nullable=True),
|
||
|
|
sa.Column('effective_date', sa.DateTime(timezone=True), nullable=False),
|
||
|
|
sa.Column('expiry_date', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('is_active', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('description', sa.Text, nullable=True),
|
||
|
|
sa.Column('methodology', sa.Text, nullable=True),
|
||
|
|
sa.Column('notes', sa.Text, nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=False),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_supplier_benchmarks_tenant_id'), 'supplier_benchmarks', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_benchmarks_benchmark_name'), 'supplier_benchmarks', ['benchmark_name'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_benchmarks_benchmark_type'), 'supplier_benchmarks', ['benchmark_type'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_benchmarks_supplier_category'), 'supplier_benchmarks', ['supplier_category'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_benchmarks_metric_type'), 'supplier_benchmarks', ['metric_type'], unique=False)
|
||
|
|
op.create_index(op.f('ix_supplier_benchmarks_is_active'), 'supplier_benchmarks', ['is_active'], unique=False)
|
||
|
|
op.create_index('ix_benchmarks_tenant_type', 'supplier_benchmarks', ['tenant_id', 'benchmark_type'], unique=False)
|
||
|
|
op.create_index('ix_benchmarks_metric_type', 'supplier_benchmarks', ['metric_type'], unique=False)
|
||
|
|
op.create_index('ix_benchmarks_category', 'supplier_benchmarks', ['supplier_category'], unique=False)
|
||
|
|
op.create_index('ix_benchmarks_active', 'supplier_benchmarks', ['is_active'], unique=False)
|
||
|
|
|
||
|
|
# Create alert_rules table
|
||
|
|
op.create_table('alert_rules',
|
||
|
|
sa.Column('id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('rule_name', sa.String(255), nullable=False),
|
||
|
|
sa.Column('rule_description', sa.Text, nullable=True),
|
||
|
|
sa.Column('is_active', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('alert_type', sa.Enum('poor_quality', 'late_delivery', 'price_increase', 'low_performance', 'contract_expiry', 'compliance_issue', 'financial_risk', 'communication_issue', 'capacity_constraint', 'certification_expiry', name='alerttype'), nullable=False),
|
||
|
|
sa.Column('severity', sa.Enum('critical', 'high', 'medium', 'low', 'info', name='alertseverity'), nullable=False),
|
||
|
|
sa.Column('metric_type', sa.Enum('delivery_performance', 'quality_score', 'price_competitiveness', 'communication_rating', 'order_accuracy', 'response_time', 'compliance_score', 'financial_stability', name='performancemetrictype'), nullable=True),
|
||
|
|
sa.Column('trigger_condition', sa.String(50), nullable=False),
|
||
|
|
sa.Column('threshold_value', sa.Float, nullable=False),
|
||
|
|
sa.Column('consecutive_violations', sa.Integer, nullable=False),
|
||
|
|
sa.Column('supplier_categories', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('supplier_ids', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('exclude_suppliers', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('evaluation_period', sa.Enum('daily', 'weekly', 'monthly', 'quarterly', 'yearly', name='performanceperiod'), nullable=False),
|
||
|
|
sa.Column('time_window_hours', sa.Integer, nullable=True),
|
||
|
|
sa.Column('business_hours_only', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('auto_resolve', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('auto_resolve_threshold', sa.Float, nullable=True),
|
||
|
|
sa.Column('auto_resolve_duration_hours', sa.Integer, nullable=True),
|
||
|
|
sa.Column('notification_enabled', sa.Boolean, nullable=False),
|
||
|
|
sa.Column('notification_recipients', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('escalation_minutes', sa.Integer, nullable=True),
|
||
|
|
sa.Column('escalation_recipients', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('recommended_actions', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('auto_actions', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('priority', sa.Integer, nullable=False),
|
||
|
|
sa.Column('tags', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||
|
|
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('timezone(\'utc\', now())'), nullable=True),
|
||
|
|
sa.Column('created_by', sa.UUID(), nullable=False),
|
||
|
|
sa.Column('last_triggered', sa.DateTime(timezone=True), nullable=True),
|
||
|
|
sa.Column('trigger_count', sa.Integer, nullable=False),
|
||
|
|
sa.PrimaryKeyConstraint('id')
|
||
|
|
)
|
||
|
|
op.create_index(op.f('ix_alert_rules_tenant_id'), 'alert_rules', ['tenant_id'], unique=False)
|
||
|
|
op.create_index(op.f('ix_alert_rules_rule_name'), 'alert_rules', ['rule_name'], unique=False)
|
||
|
|
op.create_index(op.f('ix_alert_rules_is_active'), 'alert_rules', ['is_active'], unique=False)
|
||
|
|
op.create_index(op.f('ix_alert_rules_alert_type'), 'alert_rules', ['alert_type'], unique=False)
|
||
|
|
op.create_index(op.f('ix_alert_rules_severity'), 'alert_rules', ['severity'], unique=False)
|
||
|
|
op.create_index(op.f('ix_alert_rules_metric_type'), 'alert_rules', ['metric_type'], unique=False)
|
||
|
|
op.create_index(op.f('ix_alert_rules_priority'), 'alert_rules', ['priority'], unique=False)
|
||
|
|
op.create_index('ix_alert_rules_tenant_active', 'alert_rules', ['tenant_id', 'is_active'], unique=False)
|
||
|
|
op.create_index('ix_alert_rules_type_severity', 'alert_rules', ['alert_type', 'severity'], unique=False)
|
||
|
|
op.create_index('ix_alert_rules_metric_type', 'alert_rules', ['metric_type'], unique=False)
|
||
|
|
op.create_index('ix_alert_rules_priority', 'alert_rules', ['priority'], unique=False)
|
||
|
|
|
||
|
|
|
||
|
|
def downgrade() -> None:
|
||
|
|
op.drop_index(op.f('ix_alert_rules_priority'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_metric_type'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_type_severity'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_tenant_active'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_priority'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_metric_type'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_severity'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_alert_type'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_is_active'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_rule_name'), table_name='alert_rules')
|
||
|
|
op.drop_index(op.f('ix_alert_rules_tenant_id'), table_name='alert_rules')
|
||
|
|
op.drop_table('alert_rules')
|
||
|
|
|
||
|
|
op.drop_index('ix_benchmarks_active', table_name='supplier_benchmarks')
|
||
|
|
op.drop_index('ix_benchmarks_category', table_name='supplier_benchmarks')
|
||
|
|
op.drop_index('ix_benchmarks_metric_type', table_name='supplier_benchmarks')
|
||
|
|
op.drop_index('ix_benchmarks_tenant_type', table_name='supplier_benchmarks')
|
||
|
|
op.drop_index(op.f('ix_supplier_benchmarks_is_active'), table_name='supplier_benchmarks')
|
||
|
|
op.drop_index(op.f('ix_supplier_benchmarks_metric_type'), table_name='supplier_benchmarks')
|
||
|
|
op.drop_index(op.f('ix_supplier_benchmarks_supplier_category'), table_name='supplier_benchmarks')
|
||
|
|
op.drop_index(op.f('ix_supplier_benchmarks_benchmark_type'), table_name='supplier_benchmarks')
|
||
|
|
op.drop_index(op.f('ix_supplier_benchmarks_benchmark_name'), table_name='supplier_benchmarks')
|
||
|
|
op.drop_index(op.f('ix_supplier_benchmarks_tenant_id'), table_name='supplier_benchmarks')
|
||
|
|
op.drop_table('supplier_benchmarks')
|
||
|
|
|
||
|
|
op.drop_index('ix_scorecards_final', table_name='supplier_scorecards')
|
||
|
|
op.drop_index('ix_scorecards_period', table_name='supplier_scorecards')
|
||
|
|
op.drop_index('ix_scorecards_overall_score', table_name='supplier_scorecards')
|
||
|
|
op.drop_index('ix_scorecards_period_dates', table_name='supplier_scorecards')
|
||
|
|
op.drop_index('ix_scorecards_tenant_supplier', table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_is_final'), table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_overall_score'), table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_period_end'), table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_period_start'), table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_period'), table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_scorecard_name'), table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_supplier_id'), table_name='supplier_scorecards')
|
||
|
|
op.drop_index(op.f('ix_supplier_scorecards_tenant_id'), table_name='supplier_scorecards')
|
||
|
|
op.drop_table('supplier_scorecards')
|
||
|
|
|
||
|
|
op.drop_index('ix_supplier_alerts_priority', table_name='supplier_alerts')
|
||
|
|
op.drop_index('ix_supplier_alerts_status_triggered', table_name='supplier_alerts')
|
||
|
|
op.drop_index('ix_supplier_alerts_type_severity', table_name='supplier_alerts')
|
||
|
|
op.drop_index('ix_supplier_alerts_tenant_supplier', table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_performance_metric_id'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_delivery_id'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_purchase_order_id'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_metric_type'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_status'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_severity'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_alert_type'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_supplier_id'), table_name='supplier_alerts')
|
||
|
|
op.drop_index(op.f('ix_supplier_alerts_tenant_id'), table_name='supplier_alerts')
|
||
|
|
op.drop_table('supplier_alerts')
|
||
|
|
|
||
|
|
op.drop_index('ix_performance_metrics_value', table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index('ix_performance_metrics_period_dates', table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index('ix_performance_metrics_type_period', table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index('ix_performance_metrics_tenant_supplier', table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index(op.f('ix_supplier_performance_metrics_metric_value'), table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index(op.f('ix_supplier_performance_metrics_period_end'), table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index(op.f('ix_supplier_performance_metrics_period_start'), table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index(op.f('ix_supplier_performance_metrics_period'), table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index(op.f('ix_supplier_performance_metrics_metric_type'), table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index(op.f('ix_supplier_performance_metrics_supplier_id'), table_name='supplier_performance_metrics')
|
||
|
|
op.drop_index(op.f('ix_supplier_performance_metrics_tenant_id'), table_name='supplier_performance_metrics')
|
||
|
|
op.drop_table('supplier_performance_metrics')
|
||
|
|
|
||
|
|
op.drop_index('ix_invoices_invoice_number', table_name='supplier_invoices')
|
||
|
|
op.drop_index('ix_invoices_due_date', table_name='supplier_invoices')
|
||
|
|
op.drop_index('ix_invoices_tenant_status', table_name='supplier_invoices')
|
||
|
|
op.drop_index('ix_invoices_tenant_supplier', table_name='supplier_invoices')
|
||
|
|
op.drop_index(op.f('ix_supplier_invoices_status'), table_name='supplier_invoices')
|
||
|
|
op.drop_index(op.f('ix_supplier_invoices_invoice_number'), table_name='supplier_invoices')
|
||
|
|
op.drop_index(op.f('ix_supplier_invoices_purchase_order_id'), table_name='supplier_invoices')
|
||
|
|
op.drop_index(op.f('ix_supplier_invoices_supplier_id'), table_name='supplier_invoices')
|
||
|
|
op.drop_index(op.f('ix_supplier_invoices_tenant_id'), table_name='supplier_invoices')
|
||
|
|
op.drop_table('supplier_invoices')
|
||
|
|
|
||
|
|
op.drop_index('ix_quality_reviews_overall_rating', table_name='supplier_quality_reviews')
|
||
|
|
op.drop_index('ix_quality_reviews_date', table_name='supplier_quality_reviews')
|
||
|
|
op.drop_index('ix_quality_reviews_tenant_supplier', table_name='supplier_quality_reviews')
|
||
|
|
op.drop_index(op.f('ix_supplier_quality_reviews_delivery_id'), table_name='supplier_quality_reviews')
|
||
|
|
op.drop_index(op.f('ix_supplier_quality_reviews_purchase_order_id'), table_name='supplier_quality_reviews')
|
||
|
|
op.drop_index(op.f('ix_supplier_quality_reviews_supplier_id'), table_name='supplier_quality_reviews')
|
||
|
|
op.drop_index(op.f('ix_supplier_quality_reviews_tenant_id'), table_name='supplier_quality_reviews')
|
||
|
|
op.drop_table('supplier_quality_reviews')
|
||
|
|
|
||
|
|
op.drop_index('ix_delivery_items_inventory_product', table_name='delivery_items')
|
||
|
|
op.drop_index('ix_delivery_items_tenant_delivery', table_name='delivery_items')
|
||
|
|
op.drop_index(op.f('ix_delivery_items_inventory_product_id'), table_name='delivery_items')
|
||
|
|
op.drop_index(op.f('ix_delivery_items_purchase_order_item_id'), table_name='delivery_items')
|
||
|
|
op.drop_index(op.f('ix_delivery_items_delivery_id'), table_name='delivery_items')
|
||
|
|
op.drop_index(op.f('ix_delivery_items_tenant_id'), table_name='delivery_items')
|
||
|
|
op.drop_table('delivery_items')
|
||
|
|
|
||
|
|
op.drop_index('ix_deliveries_scheduled_date', table_name='deliveries')
|
||
|
|
op.drop_index('ix_deliveries_tenant_status', table_name='deliveries')
|
||
|
|
op.drop_index(op.f('ix_deliveries_status'), table_name='deliveries')
|
||
|
|
op.drop_index(op.f('ix_deliveries_delivery_number'), table_name='deliveries')
|
||
|
|
op.drop_index(op.f('ix_deliveries_supplier_id'), table_name='deliveries')
|
||
|
|
op.drop_index(op.f('ix_deliveries_purchase_order_id'), table_name='deliveries')
|
||
|
|
op.drop_index(op.f('ix_deliveries_tenant_id'), table_name='deliveries')
|
||
|
|
op.drop_table('deliveries')
|
||
|
|
|
||
|
|
op.drop_index('ix_po_items_inventory_product', table_name='purchase_order_items')
|
||
|
|
op.drop_index('ix_po_items_tenant_po', table_name='purchase_order_items')
|
||
|
|
op.drop_index(op.f('ix_purchase_order_items_inventory_product_id'), table_name='purchase_order_items')
|
||
|
|
op.drop_index(op.f('ix_purchase_order_items_price_list_item_id'), table_name='purchase_order_items')
|
||
|
|
op.drop_index(op.f('ix_purchase_order_items_purchase_order_id'), table_name='purchase_order_items')
|
||
|
|
op.drop_index(op.f('ix_purchase_order_items_tenant_id'), table_name='purchase_order_items')
|
||
|
|
op.drop_table('purchase_order_items')
|
||
|
|
|
||
|
|
op.drop_index('ix_purchase_orders_delivery_date', table_name='purchase_orders')
|
||
|
|
op.drop_index('ix_purchase_orders_order_date', table_name='purchase_orders')
|
||
|
|
op.drop_index('ix_purchase_orders_tenant_status', table_name='purchase_orders')
|
||
|
|
op.drop_index('ix_purchase_orders_tenant_supplier', table_name='purchase_orders')
|
||
|
|
op.drop_index(op.f('ix_purchase_orders_status'), table_name='purchase_orders')
|
||
|
|
op.drop_index(op.f('ix_purchase_orders_po_number'), table_name='purchase_orders')
|
||
|
|
op.drop_index(op.f('ix_purchase_orders_supplier_id'), table_name='purchase_orders')
|
||
|
|
op.drop_index(op.f('ix_purchase_orders_tenant_id'), table_name='purchase_orders')
|
||
|
|
op.drop_table('purchase_orders')
|
||
|
|
|
||
|
|
op.drop_index('ix_price_lists_effective_date', table_name='supplier_price_lists')
|
||
|
|
op.drop_index('ix_price_lists_active', table_name='supplier_price_lists')
|
||
|
|
op.drop_index('ix_price_lists_inventory_product', table_name='supplier_price_lists')
|
||
|
|
op.drop_index('ix_price_lists_tenant_supplier', table_name='supplier_price_lists')
|
||
|
|
op.drop_index(op.f('ix_supplier_price_lists_inventory_product_id'), table_name='supplier_price_lists')
|
||
|
|
op.drop_index(op.f('ix_supplier_price_lists_supplier_id'), table_name='supplier_price_lists')
|
||
|
|
op.drop_index(op.f('ix_supplier_price_lists_tenant_id'), table_name='supplier_price_lists')
|
||
|
|
op.drop_table('supplier_price_lists')
|
||
|
|
|
||
|
|
op.drop_index('ix_suppliers_quality_rating', table_name='suppliers')
|
||
|
|
op.drop_index('ix_suppliers_tenant_type', table_name='suppliers')
|
||
|
|
op.drop_index('ix_suppliers_tenant_status', table_name='suppliers')
|
||
|
|
op.drop_index('ix_suppliers_tenant_name', table_name='suppliers')
|
||
|
|
op.drop_index(op.f('ix_suppliers_status'), table_name='suppliers')
|
||
|
|
op.drop_index(op.f('ix_suppliers_email'), table_name='suppliers')
|
||
|
|
op.drop_index(op.f('ix_suppliers_supplier_code'), table_name='suppliers')
|
||
|
|
op.drop_index(op.f('ix_suppliers_name'), table_name='suppliers')
|
||
|
|
op.drop_index(op.f('ix_suppliers_tenant_id'), table_name='suppliers')
|
||
|
|
op.drop_table('suppliers')
|