Skip to content

Commit 50bc926

Browse files
committed
feat: Add LESMEE database migration with hybrid key strategy
- Add 11 new Alembic migrations for LESMEE schema - Create enums.py with 12 ENUM types - Restructure models.py with domain-based organization - Add complete LESMEE models file (lesmee_models_complete.py) - Implement hybrid key strategy (INT for internal, UUID for public) - Add 16 new tables: users, staff, customers, products, orders, workflow - Include proper indexing and relationships
1 parent 964a016 commit 50bc926

15 files changed

+1502
-64
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""Create performance indexes for LESMEE identity tables
2+
3+
Revision ID: 03422fadcc73
4+
Revises: a4b98987c493
5+
Create Date: 2025-11-21 20:06:44.761814
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '03422fadcc73'
15+
down_revision = 'a4b98987c493'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# Core identity indexes
22+
op.execute("CREATE INDEX idx_staff_user_id ON staff(user_id);")
23+
op.execute("CREATE INDEX idx_users_email ON users(email);")
24+
op.execute("CREATE INDEX idx_users_user_type ON users(user_type);")
25+
op.execute("CREATE INDEX idx_staff_role ON staff(role);")
26+
op.execute("CREATE INDEX idx_staff_availability ON staff(is_available);")
27+
op.execute("CREATE INDEX idx_customers_is_vip ON customers(is_vip);")
28+
29+
30+
def downgrade():
31+
# Drop performance indexes
32+
indexes_to_drop = [
33+
'idx_customers_is_vip',
34+
'idx_staff_availability',
35+
'idx_staff_role',
36+
'idx_users_user_type',
37+
'idx_users_email',
38+
'idx_staff_user_id'
39+
]
40+
41+
for index_name in indexes_to_drop:
42+
op.execute(f"DROP INDEX IF EXISTS {index_name}")
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""Create LESMEE Settings and Audit Logs tables
2+
3+
Revision ID: 1888a26586e5
4+
Revises: afa0d7780e75
5+
Create Date: 2025-11-21 20:15:15.383638
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '1888a26586e5'
15+
down_revision = 'afa0d7780e75'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# Settings table
22+
op.execute("""
23+
CREATE TABLE settings (
24+
key VARCHAR(100) PRIMARY KEY,
25+
value TEXT,
26+
description TEXT,
27+
updated_at TIMESTAMPTZ DEFAULT NOW()
28+
);
29+
""")
30+
31+
# Audit logs table
32+
op.execute("""
33+
CREATE TABLE audit_logs (
34+
id SERIAL PRIMARY KEY,
35+
user_id INT REFERENCES users(id),
36+
action_type VARCHAR(50) NOT NULL,
37+
table_name VARCHAR(100),
38+
record_id VARCHAR(50),
39+
old_values JSONB,
40+
new_values JSONB,
41+
ip_address VARCHAR(45),
42+
created_at TIMESTAMPTZ DEFAULT NOW()
43+
);
44+
""")
45+
46+
# Create indexes for audit logs
47+
op.execute("CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at DESC);")
48+
op.execute("CREATE INDEX idx_audit_logs_user_action ON audit_logs(user_id, action_type);")
49+
op.execute("CREATE INDEX idx_audit_logs_table_name ON audit_logs(table_name);")
50+
51+
# Additional performance indexes
52+
op.execute("CREATE INDEX idx_orders_status_deadline ON orders(status, deadline_at);")
53+
op.execute("CREATE INDEX idx_orders_customer_product ON orders(customer_id, product_id);")
54+
op.execute("CREATE INDEX idx_orders_created_at ON orders(ordered_at DESC);")
55+
op.execute("CREATE INDEX idx_order_items_status ON order_items(status);")
56+
op.execute("CREATE INDEX idx_order_items_staff ON order_items(assigned_staff_id);")
57+
op.execute("CREATE INDEX idx_work_assignments_type_status ON work_assignments(work_type, status);")
58+
op.execute("CREATE INDEX idx_work_assignments_assigned_at ON work_assignments(assigned_at DESC);")
59+
op.execute("CREATE INDEX idx_work_assignments_completion ON work_assignments(completed_at);")
60+
op.execute("CREATE INDEX idx_commissions_staff_type ON commissions(staff_id, commission_type);")
61+
op.execute("CREATE INDEX idx_commissions_paid_status ON commissions(is_paid);")
62+
op.execute("CREATE INDEX idx_staff_department ON staff(department);")
63+
op.execute("CREATE INDEX idx_customers_vip_status ON customers(is_vip);")
64+
65+
66+
def downgrade():
67+
# Drop additional performance indexes
68+
op.execute("DROP INDEX IF EXISTS idx_customers_vip_status")
69+
op.execute("DROP INDEX IF EXISTS idx_staff_department")
70+
op.execute("DROP INDEX IF EXISTS idx_commissions_paid_status")
71+
op.execute("DROP INDEX IF EXISTS idx_commissions_staff_type")
72+
op.execute("DROP INDEX IF EXISTS idx_work_assignments_completion")
73+
op.execute("DROP INDEX IF EXISTS idx_work_assignments_assigned_at")
74+
op.execute("DROP INDEX IF EXISTS idx_work_assignments_type_status")
75+
op.execute("DROP INDEX IF EXISTS idx_order_items_staff")
76+
op.execute("DROP INDEX IF EXISTS idx_order_items_status")
77+
op.execute("DROP INDEX IF EXISTS idx_orders_created_at")
78+
op.execute("DROP INDEX IF EXISTS idx_orders_customer_product")
79+
op.execute("DROP INDEX IF EXISTS idx_orders_status_deadline")
80+
81+
# Drop audit log indexes
82+
op.execute("DROP INDEX IF EXISTS idx_audit_logs_table_name")
83+
op.execute("DROP INDEX IF EXISTS idx_audit_logs_user_action")
84+
op.execute("DROP INDEX IF EXISTS idx_audit_logs_created_at")
85+
86+
# Drop tables
87+
op.execute("DROP TABLE IF EXISTS audit_logs")
88+
op.execute("DROP TABLE IF EXISTS settings")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Add UUID extension for LESMEE schema
2+
3+
Revision ID: 61e21c4d3142
4+
Revises: b2374a5f43e5
5+
Create Date: 2025-11-21 20:03:26.490669
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '61e21c4d3142'
15+
down_revision = 'b2374a5f43e5'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# Create UUID extension for UUID generation functions
22+
op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
23+
24+
25+
def downgrade():
26+
# Note: Extensions can't be dropped if they're in use
27+
# This will be handled in final rollback
28+
pass
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Create LESMEE Users table with INT primary key
2+
3+
Revision ID: 7941988f444a
4+
Revises: e39fd2ea2f5f
5+
Create Date: 2025-11-21 20:06:06.762394
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '7941988f444a'
15+
down_revision = 'e39fd2ea2f5f'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
op.execute("""
22+
CREATE TABLE users (
23+
id SERIAL PRIMARY KEY,
24+
email VARCHAR(255) UNIQUE NOT NULL,
25+
password_hash VARCHAR(255) NOT NULL,
26+
full_name VARCHAR(255),
27+
phone VARCHAR(50),
28+
user_type user_role_type NOT NULL DEFAULT 'customer',
29+
is_active BOOLEAN DEFAULT TRUE,
30+
last_login_at TIMESTAMPTZ,
31+
created_at TIMESTAMPTZ DEFAULT NOW(),
32+
updated_at TIMESTAMPTZ DEFAULT NOW()
33+
);
34+
""")
35+
36+
# Create trigger for updated_at
37+
op.execute("""
38+
CREATE TRIGGER update_users_modtime
39+
BEFORE UPDATE ON users
40+
FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
41+
""")
42+
43+
44+
def downgrade():
45+
op.execute("DROP TRIGGER IF EXISTS update_users_modtime ON users")
46+
op.execute("DROP TABLE IF EXISTS users")
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""Create LESMEE Products and Product Options tables
2+
3+
Revision ID: 7d93e3488432
4+
Revises: 03422fadcc73
5+
Create Date: 2025-11-21 20:11:01.140758
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '7d93e3488432'
15+
down_revision = '03422fadcc73'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# Products table
22+
op.execute("""
23+
CREATE TABLE products (
24+
id SERIAL PRIMARY KEY,
25+
name VARCHAR(255) NOT NULL,
26+
slug VARCHAR(255) UNIQUE,
27+
base_price NUMERIC(19, 4) NOT NULL DEFAULT 0,
28+
category VARCHAR(100),
29+
description TEXT,
30+
is_active BOOLEAN DEFAULT TRUE,
31+
commission_config JSONB DEFAULT '{}',
32+
metadata JSONB DEFAULT '{}',
33+
created_at TIMESTAMPTZ DEFAULT NOW(),
34+
updated_at TIMESTAMPTZ DEFAULT NOW()
35+
);
36+
""")
37+
38+
# Product options table
39+
op.execute("""
40+
CREATE TABLE product_options (
41+
id SERIAL PRIMARY KEY,
42+
product_id INT NOT NULL REFERENCES products(id),
43+
option_name VARCHAR(255) NOT NULL,
44+
is_required BOOLEAN DEFAULT FALSE,
45+
price_adjustment NUMERIC(19, 4) DEFAULT 0,
46+
option_values JSONB,
47+
created_at TIMESTAMPTZ DEFAULT NOW()
48+
);
49+
""")
50+
51+
# Create trigger
52+
op.execute("CREATE TRIGGER update_products_modtime BEFORE UPDATE ON products FOR EACH ROW EXECUTE PROCEDURE update_modified_column();")
53+
54+
# Create indexes
55+
op.execute("CREATE INDEX idx_products_category ON products(category);")
56+
op.execute("CREATE INDEX idx_products_is_active ON products(is_active);")
57+
op.execute("CREATE INDEX idx_product_options_product_id ON product_options(product_id);")
58+
59+
60+
def downgrade():
61+
op.execute("DROP INDEX IF EXISTS idx_product_options_product_id")
62+
op.execute("DROP INDEX IF EXISTS idx_products_is_active")
63+
op.execute("DROP INDEX IF EXISTS idx_products_category")
64+
op.execute("DROP TRIGGER IF EXISTS update_products_modtime ON products")
65+
op.execute("DROP TABLE IF EXISTS product_options")
66+
op.execute("DROP TABLE IF EXISTS products")
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""Create LESMEE Staff and Staff_Finances tables
2+
3+
Revision ID: 94d2d958190e
4+
Revises: 7941988f444a
5+
Create Date: 2025-11-21 20:06:21.436135
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = '94d2d958190e'
15+
down_revision = '7941988f444a'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
# Staff table
22+
op.execute("""
23+
CREATE TABLE staff (
24+
id SERIAL PRIMARY KEY,
25+
user_id INT UNIQUE NOT NULL REFERENCES users(id),
26+
employee_code VARCHAR(50) UNIQUE,
27+
role staff_role_type NOT NULL,
28+
department VARCHAR(100),
29+
skill_level INT DEFAULT 1,
30+
is_available BOOLEAN DEFAULT TRUE,
31+
created_at TIMESTAMPTZ DEFAULT NOW(),
32+
updated_at TIMESTAMPTZ DEFAULT NOW()
33+
);
34+
""")
35+
36+
# Staff finances table
37+
op.execute("""
38+
CREATE TABLE staff_finances (
39+
staff_id INT PRIMARY KEY REFERENCES staff(id),
40+
base_salary NUMERIC(19, 4) DEFAULT 0,
41+
bank_name VARCHAR(255),
42+
bank_account VARCHAR(50),
43+
tax_code VARCHAR(50),
44+
updated_at TIMESTAMPTZ DEFAULT NOW()
45+
);
46+
""")
47+
48+
# Create trigger for staff table
49+
op.execute("CREATE TRIGGER update_staff_modtime BEFORE UPDATE ON staff FOR EACH ROW EXECUTE PROCEDURE update_modified_column();")
50+
51+
52+
def downgrade():
53+
op.execute("DROP TRIGGER IF EXISTS update_staff_modtime ON staff")
54+
op.execute("DROP TABLE IF EXISTS staff_finances")
55+
op.execute("DROP TABLE IF EXISTS staff")
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Create LESMEE Invoices table for financial management
2+
3+
Revision ID: a1fd466d2512
4+
Revises: cf2ea22e8259
5+
Create Date: 2025-11-21 20:11:37.396093
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel.sql.sqltypes
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = 'a1fd466d2512'
15+
down_revision = 'cf2ea22e8259'
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade():
21+
op.execute("""
22+
CREATE TABLE invoices (
23+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
24+
order_id UUID NOT NULL REFERENCES orders(id),
25+
invoice_number VARCHAR(50) UNIQUE,
26+
amount NUMERIC(19, 4) NOT NULL,
27+
status invoice_status_type DEFAULT 'draft',
28+
issue_date TIMESTAMPTZ DEFAULT NOW(),
29+
due_date TIMESTAMPTZ,
30+
paid_date TIMESTAMPTZ,
31+
commission_snapshot JSONB DEFAULT '{}',
32+
created_at TIMESTAMPTZ DEFAULT NOW(),
33+
updated_at TIMESTAMPTZ DEFAULT NOW()
34+
);
35+
""")
36+
37+
# Create trigger and indexes
38+
op.execute("CREATE TRIGGER update_invoices_modtime BEFORE UPDATE ON invoices FOR EACH ROW EXECUTE PROCEDURE update_modified_column();")
39+
op.execute("CREATE INDEX idx_invoices_order_id ON invoices(order_id);")
40+
op.execute("CREATE INDEX idx_invoices_status ON invoices(status);")
41+
42+
43+
def downgrade():
44+
op.execute("DROP INDEX IF EXISTS idx_invoices_status")
45+
op.execute("DROP INDEX IF EXISTS idx_invoices_order_id")
46+
op.execute("DROP TRIGGER IF EXISTS update_invoices_modtime ON invoices")
47+
op.execute("DROP TABLE IF EXISTS invoices")

0 commit comments

Comments
 (0)