|
|
|
@@ -0,0 +1,810 @@
|
|
|
|
|
"""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 ###
|