Files
MyPOS/migrations/versions/10ec0a5894e8_phase_1_initial_schema.py

811 lines
43 KiB
Python

"""Phase 1 initial schema
Revision ID: 10ec0a5894e8
Revises:
Create Date: 2026-05-06 15:30:37.356335
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '10ec0a5894e8'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('audit_log',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('actor_id', sa.Integer(), nullable=False),
sa.Column('actor_type', sa.String(length=50), nullable=False),
sa.Column('action', sa.String(length=100), nullable=False),
sa.Column('target_type', sa.String(length=100), nullable=True),
sa.Column('target_id', sa.Integer(), nullable=True),
sa.Column('before_json', sa.JSON(), nullable=True),
sa.Column('after_json', sa.JSON(), nullable=True),
sa.Column('ip_address', sa.String(length=45), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('audit_log', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_audit_log_created_at'), ['created_at'], unique=False)
op.create_table('jwt_blocklist',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('jti', sa.String(length=36), nullable=False),
sa.Column('token_type', sa.String(length=20), nullable=False),
sa.Column('revoked_at', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('jwt_blocklist', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_jwt_blocklist_jti'), ['jti'], unique=True)
op.create_table('plans',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=50), nullable=False),
sa.Column('price_monthly', sa.Numeric(precision=8, scale=2), nullable=False),
sa.Column('max_staff', sa.Integer(), nullable=True),
sa.Column('max_locations', sa.Integer(), nullable=True),
sa.Column('features_json', sa.JSON(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
)
op.create_table('system_users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=255), nullable=False),
sa.Column('password_hash', sa.String(length=255), nullable=False),
sa.Column('name', sa.String(length=100), nullable=False),
sa.Column('role', sa.String(length=50), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('failed_login_attempts', sa.Integer(), nullable=False),
sa.Column('locked_until', sa.DateTime(), nullable=True),
sa.Column('password_reset_token', sa.String(length=255), nullable=True),
sa.Column('password_reset_expires_at', sa.DateTime(), nullable=True),
sa.Column('last_login_at', sa.DateTime(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('system_users', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_system_users_email'), ['email'], unique=True)
op.create_table('tenants',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('slug', sa.String(length=80), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('owner_email', sa.String(length=255), nullable=False),
sa.Column('plan_id', sa.Integer(), nullable=False),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('trial_ends_at', sa.DateTime(), nullable=True),
sa.Column('subscription_expires_at', sa.DateTime(), nullable=True),
sa.Column('is_demo', sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['plan_id'], ['plans.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('tenants', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_tenants_slug'), ['slug'], unique=True)
op.create_table('locations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('address', sa.String(length=255), nullable=True),
sa.Column('phone', sa.String(length=30), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('timezone', sa.String(length=60), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('is_primary', sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('locations', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_locations_tenant_id'), ['tenant_id'], unique=False)
op.create_table('marketing_campaigns',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('channel', sa.String(length=20), nullable=False),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('audience_filter_json', sa.JSON(), nullable=True),
sa.Column('subject', sa.String(length=255), nullable=True),
sa.Column('message_body', sa.Text(), nullable=True),
sa.Column('scheduled_at', sa.DateTime(), nullable=True),
sa.Column('sent_at', sa.DateTime(), nullable=True),
sa.Column('sent_count', sa.Integer(), nullable=False),
sa.Column('open_count', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('marketing_campaigns', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_marketing_campaigns_tenant_id'), ['tenant_id'], unique=False)
op.create_table('products',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('sku', sa.String(length=100), nullable=True),
sa.Column('category', sa.String(length=100), nullable=True),
sa.Column('sale_price', sa.Numeric(precision=8, scale=2), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('products', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_products_tenant_id'), ['tenant_id'], unique=False)
op.create_table('services',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('category', sa.String(length=100), nullable=True),
sa.Column('duration_min', sa.Integer(), nullable=False),
sa.Column('price', sa.Numeric(precision=8, scale=2), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('services', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_services_tenant_id'), ['tenant_id'], unique=False)
op.create_table('tenant_billing_history',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('amount', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('description', sa.String(length=255), nullable=False),
sa.Column('paid_at', sa.DateTime(), nullable=True),
sa.Column('invoice_ref', sa.String(length=100), nullable=True),
sa.Column('recorded_by', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['recorded_by'], ['system_users.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('tenant_billing_history', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_tenant_billing_history_tenant_id'), ['tenant_id'], unique=False)
op.create_table('tenant_setting_overrides',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('setting_key', sa.String(length=100), nullable=False),
sa.Column('setting_value', sa.Text(), nullable=True),
sa.Column('overridden_by', sa.Integer(), nullable=False),
sa.Column('overridden_at', sa.DateTime(), nullable=False),
sa.Column('lifted_at', sa.DateTime(), nullable=True),
sa.Column('note', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['overridden_by'], ['system_users.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('tenant_setting_overrides', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_tenant_setting_overrides_tenant_id'), ['tenant_id'], unique=False)
op.create_table('tenant_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('setting_key', sa.String(length=100), nullable=False),
sa.Column('setting_value', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('tenant_id', 'setting_key', name='uq_tenant_settings_key')
)
with op.batch_alter_table('tenant_settings', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_tenant_settings_tenant_id'), ['tenant_id'], unique=False)
op.create_table('users',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('email', sa.String(length=255), nullable=False),
sa.Column('password_hash', sa.String(length=255), nullable=False),
sa.Column('role', sa.String(length=30), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('failed_login_attempts', sa.Integer(), nullable=False),
sa.Column('locked_until', sa.DateTime(), nullable=True),
sa.Column('password_reset_token', sa.String(length=255), nullable=True),
sa.Column('password_reset_expires_at', sa.DateTime(), nullable=True),
sa.Column('last_login_at', sa.DateTime(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('tenant_id', 'email', name='uq_users_tenant_email')
)
with op.batch_alter_table('users', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_users_email'), ['email'], unique=False)
batch_op.create_index(batch_op.f('ix_users_tenant_id'), ['tenant_id'], unique=False)
op.create_table('daily_reconciliations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('date', sa.Date(), nullable=False),
sa.Column('total_cash', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('total_app_payments', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('total_tips', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('total_gift_card_redemptions', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('expected_cash_in_drawer', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('actual_cash_counted', sa.Numeric(precision=10, scale=2), nullable=True),
sa.Column('variance', sa.Numeric(precision=10, scale=2), nullable=True),
sa.Column('closed_by', sa.Integer(), nullable=True),
sa.Column('closed_at', sa.DateTime(), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['closed_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('tenant_id', 'location_id', 'date', name='uq_reconciliation_location_date')
)
with op.batch_alter_table('daily_reconciliations', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_daily_reconciliations_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_daily_reconciliations_tenant_id'), ['tenant_id'], unique=False)
op.create_table('inventory',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('sku', sa.String(length=100), nullable=True),
sa.Column('category', sa.String(length=100), nullable=True),
sa.Column('qty_on_hand', sa.Integer(), nullable=False),
sa.Column('reorder_level', sa.Integer(), nullable=False),
sa.Column('cost_price', sa.Numeric(precision=8, scale=2), nullable=True),
sa.Column('sale_price', sa.Numeric(precision=8, scale=2), nullable=True),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('inventory', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_inventory_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_inventory_tenant_id'), ['tenant_id'], unique=False)
op.create_table('location_settings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('setting_key', sa.String(length=100), nullable=False),
sa.Column('setting_value', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('location_id', 'setting_key', name='uq_location_settings_key')
)
with op.batch_alter_table('location_settings', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_location_settings_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_location_settings_tenant_id'), ['tenant_id'], unique=False)
op.create_table('promotions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('discount_percent', sa.Integer(), nullable=False),
sa.Column('applies_to', sa.String(length=30), nullable=False),
sa.Column('target_ids_json', sa.JSON(), nullable=True),
sa.Column('starts_at', sa.DateTime(), nullable=False),
sa.Column('ends_at', sa.DateTime(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('created_by', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('promotions', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_promotions_tenant_id'), ['tenant_id'], unique=False)
op.create_table('staff',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('phone', sa.String(length=30), nullable=False),
sa.Column('passcode_hash', sa.String(length=255), nullable=False),
sa.Column('staff_type', sa.String(length=30), nullable=False),
sa.Column('pay_type', sa.String(length=20), nullable=False),
sa.Column('hourly_rate', sa.Numeric(precision=8, scale=2), nullable=True),
sa.Column('salary_amount', sa.Numeric(precision=10, scale=2), nullable=True),
sa.Column('guarantee_amount', sa.Numeric(precision=10, scale=2), nullable=True),
sa.Column('pay_period', sa.String(length=20), nullable=False),
sa.Column('commission_rate', sa.Numeric(precision=5, scale=2), nullable=True),
sa.Column('commission_enabled', sa.Boolean(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('passcode_failed_attempts', sa.Integer(), nullable=False),
sa.Column('passcode_locked_until', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('tenant_id', 'phone', name='uq_staff_tenant_phone')
)
with op.batch_alter_table('staff', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_staff_tenant_id'), ['tenant_id'], unique=False)
op.create_table('customers',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('phone', sa.String(length=30), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column('date_of_birth', sa.Date(), nullable=True),
sa.Column('preferred_staff_id', sa.Integer(), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.Column('loyalty_points', sa.Integer(), nullable=False),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('no_show_count', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['preferred_staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('customers', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_customers_tenant_id'), ['tenant_id'], unique=False)
op.create_table('inventory_log',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('inventory_id', sa.Integer(), nullable=False),
sa.Column('delta', sa.Integer(), nullable=False),
sa.Column('reason', sa.String(length=255), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['inventory_id'], ['inventory.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('inventory_log', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_inventory_log_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_inventory_log_tenant_id'), ['tenant_id'], unique=False)
op.create_table('staff_clockings',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('staff_id', sa.Integer(), nullable=False),
sa.Column('clocked_in_at', sa.DateTime(), nullable=False),
sa.Column('clocked_out_at', sa.DateTime(), nullable=True),
sa.Column('total_minutes', sa.Integer(), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('staff_clockings', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_staff_clockings_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_staff_clockings_staff_id'), ['staff_id'], unique=False)
batch_op.create_index(batch_op.f('ix_staff_clockings_tenant_id'), ['tenant_id'], unique=False)
op.create_table('staff_locations',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('staff_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('staff_id', 'location_id', name='uq_staff_location')
)
with op.batch_alter_table('staff_locations', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_staff_locations_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_staff_locations_staff_id'), ['staff_id'], unique=False)
batch_op.create_index(batch_op.f('ix_staff_locations_tenant_id'), ['tenant_id'], unique=False)
op.create_table('staff_pay_periods',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('staff_id', sa.Integer(), nullable=False),
sa.Column('period_start', sa.Date(), nullable=False),
sa.Column('period_end', sa.Date(), nullable=False),
sa.Column('pay_type', sa.String(length=20), nullable=False),
sa.Column('base_amount', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('commission_amount', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('guarantee_topup', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('total_amount', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('notes', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('staff_pay_periods', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_staff_pay_periods_staff_id'), ['staff_id'], unique=False)
batch_op.create_index(batch_op.f('ix_staff_pay_periods_tenant_id'), ['tenant_id'], unique=False)
op.create_table('staff_schedules',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('staff_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('day_of_week', sa.Integer(), nullable=False),
sa.Column('start_time', sa.Time(), nullable=False),
sa.Column('end_time', sa.Time(), nullable=False),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('staff_schedules', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_staff_schedules_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_staff_schedules_staff_id'), ['staff_id'], unique=False)
batch_op.create_index(batch_op.f('ix_staff_schedules_tenant_id'), ['tenant_id'], unique=False)
op.create_table('waitlist',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('customer_name', sa.String(length=150), nullable=False),
sa.Column('customer_phone', sa.String(length=30), nullable=True),
sa.Column('customer_email', sa.String(length=255), nullable=True),
sa.Column('staff_id', sa.Integer(), nullable=True),
sa.Column('service_id', sa.Integer(), nullable=True),
sa.Column('requested_date', sa.Date(), nullable=True),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('notified_at', sa.DateTime(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['service_id'], ['services.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('waitlist', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_waitlist_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_waitlist_tenant_id'), ['tenant_id'], unique=False)
op.create_table('appointments',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('customer_id', sa.Integer(), nullable=True),
sa.Column('staff_id', sa.Integer(), nullable=True),
sa.Column('service_id', sa.Integer(), nullable=True),
sa.Column('start_time', sa.DateTime(), nullable=False),
sa.Column('end_time', sa.DateTime(), nullable=True),
sa.Column('is_walk_in', sa.Boolean(), nullable=False),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('notes', sa.Text(), nullable=True),
sa.Column('cancellation_reason', sa.Text(), nullable=True),
sa.Column('cancelled_at', sa.DateTime(), nullable=True),
sa.Column('rebook_source', sa.String(length=20), nullable=True),
sa.Column('rebooked_from_transaction_id', sa.Integer(), nullable=True),
sa.Column('created_by', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['customer_id'], ['customers.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['rebooked_from_transaction_id'], ['transactions.id'], name='fk_appt_rebooked_transaction', use_alter=True),
sa.ForeignKeyConstraint(['service_id'], ['services.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('appointments', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_appointments_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_appointments_tenant_id'), ['tenant_id'], unique=False)
op.create_table('checkin_queue',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('customer_id', sa.Integer(), nullable=True),
sa.Column('customer_name', sa.String(length=150), nullable=False),
sa.Column('customer_phone', sa.String(length=30), nullable=False),
sa.Column('service_requested', sa.String(length=150), nullable=True),
sa.Column('checked_in_at', sa.DateTime(), nullable=False),
sa.Column('status', sa.String(length=20), nullable=False),
sa.Column('acknowledged_by', sa.Integer(), nullable=True),
sa.Column('acknowledged_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['acknowledged_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['customer_id'], ['customers.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('checkin_queue', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_checkin_queue_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_checkin_queue_tenant_id'), ['tenant_id'], unique=False)
op.create_table('gift_cards',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('code', sa.String(length=50), nullable=False),
sa.Column('original_value', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('remaining_balance', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('issued_by', sa.Integer(), nullable=True),
sa.Column('issued_to_customer_id', sa.Integer(), nullable=True),
sa.Column('expires_at', sa.DateTime(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['issued_by'], ['users.id'], ),
sa.ForeignKeyConstraint(['issued_to_customer_id'], ['customers.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('tenant_id', 'code', name='uq_gift_card_tenant_code')
)
with op.batch_alter_table('gift_cards', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_gift_cards_tenant_id'), ['tenant_id'], unique=False)
op.create_table('appointment_reminders',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('appointment_id', sa.Integer(), nullable=False),
sa.Column('reminder_type', sa.String(length=10), nullable=False),
sa.Column('scheduled_for', sa.DateTime(), nullable=False),
sa.Column('sent_at', sa.DateTime(), nullable=True),
sa.Column('channel', sa.String(length=20), nullable=False),
sa.Column('status', sa.String(length=20), nullable=False),
sa.ForeignKeyConstraint(['appointment_id'], ['appointments.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('appointment_reminders', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_appointment_reminders_appointment_id'), ['appointment_id'], unique=False)
batch_op.create_index(batch_op.f('ix_appointment_reminders_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_appointment_reminders_tenant_id'), ['tenant_id'], unique=False)
op.create_table('transactions',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('appointment_id', sa.Integer(), nullable=True),
sa.Column('customer_id', sa.Integer(), nullable=True),
sa.Column('staff_id', sa.Integer(), nullable=True),
sa.Column('subtotal', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('discount', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('tip_amount', sa.Numeric(precision=8, scale=2), nullable=False),
sa.Column('gift_card_amount', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('total', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('payment_method', sa.String(length=20), nullable=False),
sa.Column('payment_reference', sa.String(length=255), nullable=True),
sa.Column('gift_card_id', sa.Integer(), nullable=True),
sa.Column('review_request_sent_at', sa.DateTime(), nullable=True),
sa.Column('voided_at', sa.DateTime(), nullable=True),
sa.Column('voided_by', sa.Integer(), nullable=True),
sa.Column('void_reason', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['appointment_id'], ['appointments.id'], name='fk_transaction_appointment', use_alter=True),
sa.ForeignKeyConstraint(['customer_id'], ['customers.id'], ),
sa.ForeignKeyConstraint(['gift_card_id'], ['gift_cards.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.ForeignKeyConstraint(['voided_by'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('transactions', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_transactions_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_transactions_tenant_id'), ['tenant_id'], unique=False)
op.create_table('checkout_reviews',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('transaction_id', sa.Integer(), nullable=False),
sa.Column('customer_id', sa.Integer(), nullable=True),
sa.Column('staff_id', sa.Integer(), nullable=True),
sa.Column('rating', sa.Integer(), nullable=False),
sa.Column('comment', sa.Text(), nullable=True),
sa.Column('is_public_suggested', sa.Boolean(), nullable=False),
sa.Column('google_clicked', sa.Boolean(), nullable=False),
sa.Column('facebook_clicked', sa.Boolean(), nullable=False),
sa.Column('yelp_clicked', sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['customer_id'], ['customers.id'], ),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.ForeignKeyConstraint(['transaction_id'], ['transactions.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('checkout_reviews', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_checkout_reviews_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_checkout_reviews_tenant_id'), ['tenant_id'], unique=False)
op.create_table('commission_log',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('tenant_id', sa.Integer(), nullable=False),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('staff_id', sa.Integer(), nullable=False),
sa.Column('transaction_id', sa.Integer(), nullable=False),
sa.Column('amount', sa.Numeric(precision=10, scale=2), nullable=False),
sa.Column('period', sa.String(length=20), nullable=True),
sa.ForeignKeyConstraint(['location_id'], ['locations.id'], ),
sa.ForeignKeyConstraint(['staff_id'], ['staff.id'], ),
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
sa.ForeignKeyConstraint(['transaction_id'], ['transactions.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('commission_log', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_commission_log_location_id'), ['location_id'], unique=False)
batch_op.create_index(batch_op.f('ix_commission_log_staff_id'), ['staff_id'], unique=False)
batch_op.create_index(batch_op.f('ix_commission_log_tenant_id'), ['tenant_id'], unique=False)
op.create_table('transaction_items',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('transaction_id', sa.Integer(), nullable=False),
sa.Column('service_id', sa.Integer(), nullable=True),
sa.Column('product_id', sa.Integer(), nullable=True),
sa.Column('qty', sa.Integer(), nullable=False),
sa.Column('unit_price', sa.Numeric(precision=8, scale=2), nullable=False),
sa.Column('original_price', sa.Numeric(precision=8, scale=2), nullable=False),
sa.Column('discount_percent', sa.Numeric(precision=5, scale=2), nullable=False),
sa.Column('promotion_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['product_id'], ['products.id'], ),
sa.ForeignKeyConstraint(['promotion_id'], ['promotions.id'], ),
sa.ForeignKeyConstraint(['service_id'], ['services.id'], ),
sa.ForeignKeyConstraint(['transaction_id'], ['transactions.id'], ),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('transaction_items', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_transaction_items_transaction_id'), ['transaction_id'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('transaction_items', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_transaction_items_transaction_id'))
op.drop_table('transaction_items')
with op.batch_alter_table('commission_log', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_commission_log_tenant_id'))
batch_op.drop_index(batch_op.f('ix_commission_log_staff_id'))
batch_op.drop_index(batch_op.f('ix_commission_log_location_id'))
op.drop_table('commission_log')
with op.batch_alter_table('checkout_reviews', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_checkout_reviews_tenant_id'))
batch_op.drop_index(batch_op.f('ix_checkout_reviews_location_id'))
op.drop_table('checkout_reviews')
with op.batch_alter_table('transactions', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_transactions_tenant_id'))
batch_op.drop_index(batch_op.f('ix_transactions_location_id'))
op.drop_table('transactions')
with op.batch_alter_table('appointment_reminders', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_appointment_reminders_tenant_id'))
batch_op.drop_index(batch_op.f('ix_appointment_reminders_location_id'))
batch_op.drop_index(batch_op.f('ix_appointment_reminders_appointment_id'))
op.drop_table('appointment_reminders')
with op.batch_alter_table('gift_cards', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_gift_cards_tenant_id'))
op.drop_table('gift_cards')
with op.batch_alter_table('checkin_queue', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_checkin_queue_tenant_id'))
batch_op.drop_index(batch_op.f('ix_checkin_queue_location_id'))
op.drop_table('checkin_queue')
with op.batch_alter_table('appointments', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_appointments_tenant_id'))
batch_op.drop_index(batch_op.f('ix_appointments_location_id'))
op.drop_table('appointments')
with op.batch_alter_table('waitlist', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_waitlist_tenant_id'))
batch_op.drop_index(batch_op.f('ix_waitlist_location_id'))
op.drop_table('waitlist')
with op.batch_alter_table('staff_schedules', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_staff_schedules_tenant_id'))
batch_op.drop_index(batch_op.f('ix_staff_schedules_staff_id'))
batch_op.drop_index(batch_op.f('ix_staff_schedules_location_id'))
op.drop_table('staff_schedules')
with op.batch_alter_table('staff_pay_periods', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_staff_pay_periods_tenant_id'))
batch_op.drop_index(batch_op.f('ix_staff_pay_periods_staff_id'))
op.drop_table('staff_pay_periods')
with op.batch_alter_table('staff_locations', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_staff_locations_tenant_id'))
batch_op.drop_index(batch_op.f('ix_staff_locations_staff_id'))
batch_op.drop_index(batch_op.f('ix_staff_locations_location_id'))
op.drop_table('staff_locations')
with op.batch_alter_table('staff_clockings', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_staff_clockings_tenant_id'))
batch_op.drop_index(batch_op.f('ix_staff_clockings_staff_id'))
batch_op.drop_index(batch_op.f('ix_staff_clockings_location_id'))
op.drop_table('staff_clockings')
with op.batch_alter_table('inventory_log', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_inventory_log_tenant_id'))
batch_op.drop_index(batch_op.f('ix_inventory_log_location_id'))
op.drop_table('inventory_log')
with op.batch_alter_table('customers', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_customers_tenant_id'))
op.drop_table('customers')
with op.batch_alter_table('staff', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_staff_tenant_id'))
op.drop_table('staff')
with op.batch_alter_table('promotions', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_promotions_tenant_id'))
op.drop_table('promotions')
with op.batch_alter_table('location_settings', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_location_settings_tenant_id'))
batch_op.drop_index(batch_op.f('ix_location_settings_location_id'))
op.drop_table('location_settings')
with op.batch_alter_table('inventory', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_inventory_tenant_id'))
batch_op.drop_index(batch_op.f('ix_inventory_location_id'))
op.drop_table('inventory')
with op.batch_alter_table('daily_reconciliations', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_daily_reconciliations_tenant_id'))
batch_op.drop_index(batch_op.f('ix_daily_reconciliations_location_id'))
op.drop_table('daily_reconciliations')
with op.batch_alter_table('users', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_users_tenant_id'))
batch_op.drop_index(batch_op.f('ix_users_email'))
op.drop_table('users')
with op.batch_alter_table('tenant_settings', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_tenant_settings_tenant_id'))
op.drop_table('tenant_settings')
with op.batch_alter_table('tenant_setting_overrides', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_tenant_setting_overrides_tenant_id'))
op.drop_table('tenant_setting_overrides')
with op.batch_alter_table('tenant_billing_history', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_tenant_billing_history_tenant_id'))
op.drop_table('tenant_billing_history')
with op.batch_alter_table('services', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_services_tenant_id'))
op.drop_table('services')
with op.batch_alter_table('products', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_products_tenant_id'))
op.drop_table('products')
with op.batch_alter_table('marketing_campaigns', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_marketing_campaigns_tenant_id'))
op.drop_table('marketing_campaigns')
with op.batch_alter_table('locations', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_locations_tenant_id'))
op.drop_table('locations')
with op.batch_alter_table('tenants', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_tenants_slug'))
op.drop_table('tenants')
with op.batch_alter_table('system_users', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_system_users_email'))
op.drop_table('system_users')
op.drop_table('plans')
with op.batch_alter_table('jwt_blocklist', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_jwt_blocklist_jti'))
op.drop_table('jwt_blocklist')
with op.batch_alter_table('audit_log', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_audit_log_created_at'))
op.drop_table('audit_log')
# ### end Alembic commands ###