fix(lint): auto-fix ruff violations and tune lint rules
- Auto-fixed 4,496 lint issues (import sorting, modern syntax, etc.) - Added ignore rules for patterns intentional in this codebase: E402 (late imports), E712 (SQLAlchemy filters), B904 (raise from), SIM108/SIM105/SIM117 (readability preferences) - Added per-file ignores for tests and scripts - Excluded broken scripts/rename_terminology.py (has curly quotes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,9 @@ api_endpoint_rules:
|
|||||||
SCHEMA LOCATION: All response schemas must be defined in models/schema/*.py,
|
SCHEMA LOCATION: All response schemas must be defined in models/schema/*.py,
|
||||||
never inline in endpoint files. This ensures schemas are reusable and discoverable.
|
never inline in endpoint files. This ensures schemas are reusable and discoverable.
|
||||||
pattern:
|
pattern:
|
||||||
file_pattern: "app/api/v1/**/*.py"
|
file_pattern:
|
||||||
|
- "app/api/v1/**/*.py"
|
||||||
|
- "app/modules/*/routes/api/**/*.py"
|
||||||
anti_patterns:
|
anti_patterns:
|
||||||
- "return dict"
|
- "return dict"
|
||||||
- "-> dict"
|
- "-> dict"
|
||||||
@@ -82,7 +84,9 @@ api_endpoint_rules:
|
|||||||
# In app/api/v1/admin/my_feature.py
|
# In app/api/v1/admin/my_feature.py
|
||||||
from models.schema.my_feature import MyRequest
|
from models.schema.my_feature import MyRequest
|
||||||
pattern:
|
pattern:
|
||||||
file_pattern: "app/api/v1/**/*.py"
|
file_pattern:
|
||||||
|
- "app/api/v1/**/*.py"
|
||||||
|
- "app/modules/*/routes/api/**/*.py"
|
||||||
anti_patterns:
|
anti_patterns:
|
||||||
- "from pydantic import"
|
- "from pydantic import"
|
||||||
- "from pydantic.main import"
|
- "from pydantic.main import"
|
||||||
@@ -118,7 +122,9 @@ api_endpoint_rules:
|
|||||||
- db.query() - complex queries are business logic
|
- db.query() - complex queries are business logic
|
||||||
- db.delete() - deleting entities is business logic
|
- db.delete() - deleting entities is business logic
|
||||||
pattern:
|
pattern:
|
||||||
file_pattern: "app/api/v1/**/*.py"
|
file_pattern:
|
||||||
|
- "app/api/v1/**/*.py"
|
||||||
|
- "app/modules/*/routes/api/**/*.py"
|
||||||
anti_patterns:
|
anti_patterns:
|
||||||
- "db.add("
|
- "db.add("
|
||||||
- "db.delete("
|
- "db.delete("
|
||||||
@@ -155,7 +161,9 @@ api_endpoint_rules:
|
|||||||
# Dependency guarantees token_vendor_id is present
|
# Dependency guarantees token_vendor_id is present
|
||||||
return order_service.get_orders(db, current_user.token_vendor_id)
|
return order_service.get_orders(db, current_user.token_vendor_id)
|
||||||
pattern:
|
pattern:
|
||||||
file_pattern: "app/api/v1/**/*.py"
|
file_pattern:
|
||||||
|
- "app/api/v1/**/*.py"
|
||||||
|
- "app/modules/*/routes/api/**/*.py"
|
||||||
anti_patterns:
|
anti_patterns:
|
||||||
- "raise HTTPException"
|
- "raise HTTPException"
|
||||||
- "raise InvalidTokenException"
|
- "raise InvalidTokenException"
|
||||||
@@ -248,7 +256,9 @@ api_endpoint_rules:
|
|||||||
- from models.database.*
|
- from models.database.*
|
||||||
- from app.modules.*.models.*
|
- from app.modules.*.models.*
|
||||||
pattern:
|
pattern:
|
||||||
file_pattern: "app/api/**/*.py"
|
file_pattern:
|
||||||
|
- "app/api/**/*.py"
|
||||||
|
- "app/modules/*/routes/api/**/*.py"
|
||||||
anti_patterns:
|
anti_patterns:
|
||||||
- "from models\\.database\\."
|
- "from models\\.database\\."
|
||||||
- "from app\\.modules\\.[a-z_]+\\.models\\."
|
- "from app\\.modules\\.[a-z_]+\\.models\\."
|
||||||
|
|||||||
@@ -5,51 +5,52 @@ Revises: y3d4e5f6g7h8
|
|||||||
Create Date: 2026-01-11 16:44:59.070110
|
Create Date: 2026-01-11 16:44:59.070110
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '09d84a46530f'
|
revision: str = "09d84a46530f"
|
||||||
down_revision: Union[str, None] = 'y3d4e5f6g7h8'
|
down_revision: str | None = "y3d4e5f6g7h8"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
"""Add celery_task_id column to job tracking tables for Celery integration."""
|
"""Add celery_task_id column to job tracking tables for Celery integration."""
|
||||||
# MarketplaceImportJob
|
# MarketplaceImportJob
|
||||||
op.add_column('marketplace_import_jobs', sa.Column('celery_task_id', sa.String(length=255), nullable=True))
|
op.add_column("marketplace_import_jobs", sa.Column("celery_task_id", sa.String(length=255), nullable=True))
|
||||||
op.create_index(op.f('ix_marketplace_import_jobs_celery_task_id'), 'marketplace_import_jobs', ['celery_task_id'], unique=False)
|
op.create_index(op.f("ix_marketplace_import_jobs_celery_task_id"), "marketplace_import_jobs", ["celery_task_id"], unique=False)
|
||||||
|
|
||||||
# LetzshopHistoricalImportJob
|
# LetzshopHistoricalImportJob
|
||||||
op.add_column('letzshop_historical_import_jobs', sa.Column('celery_task_id', sa.String(length=255), nullable=True))
|
op.add_column("letzshop_historical_import_jobs", sa.Column("celery_task_id", sa.String(length=255), nullable=True))
|
||||||
op.create_index(op.f('ix_letzshop_historical_import_jobs_celery_task_id'), 'letzshop_historical_import_jobs', ['celery_task_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_historical_import_jobs_celery_task_id"), "letzshop_historical_import_jobs", ["celery_task_id"], unique=False)
|
||||||
|
|
||||||
# ArchitectureScan
|
# ArchitectureScan
|
||||||
op.add_column('architecture_scans', sa.Column('celery_task_id', sa.String(length=255), nullable=True))
|
op.add_column("architecture_scans", sa.Column("celery_task_id", sa.String(length=255), nullable=True))
|
||||||
op.create_index(op.f('ix_architecture_scans_celery_task_id'), 'architecture_scans', ['celery_task_id'], unique=False)
|
op.create_index(op.f("ix_architecture_scans_celery_task_id"), "architecture_scans", ["celery_task_id"], unique=False)
|
||||||
|
|
||||||
# TestRun
|
# TestRun
|
||||||
op.add_column('test_runs', sa.Column('celery_task_id', sa.String(length=255), nullable=True))
|
op.add_column("test_runs", sa.Column("celery_task_id", sa.String(length=255), nullable=True))
|
||||||
op.create_index(op.f('ix_test_runs_celery_task_id'), 'test_runs', ['celery_task_id'], unique=False)
|
op.create_index(op.f("ix_test_runs_celery_task_id"), "test_runs", ["celery_task_id"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
"""Remove celery_task_id column from job tracking tables."""
|
"""Remove celery_task_id column from job tracking tables."""
|
||||||
# TestRun
|
# TestRun
|
||||||
op.drop_index(op.f('ix_test_runs_celery_task_id'), table_name='test_runs')
|
op.drop_index(op.f("ix_test_runs_celery_task_id"), table_name="test_runs")
|
||||||
op.drop_column('test_runs', 'celery_task_id')
|
op.drop_column("test_runs", "celery_task_id")
|
||||||
|
|
||||||
# ArchitectureScan
|
# ArchitectureScan
|
||||||
op.drop_index(op.f('ix_architecture_scans_celery_task_id'), table_name='architecture_scans')
|
op.drop_index(op.f("ix_architecture_scans_celery_task_id"), table_name="architecture_scans")
|
||||||
op.drop_column('architecture_scans', 'celery_task_id')
|
op.drop_column("architecture_scans", "celery_task_id")
|
||||||
|
|
||||||
# LetzshopHistoricalImportJob
|
# LetzshopHistoricalImportJob
|
||||||
op.drop_index(op.f('ix_letzshop_historical_import_jobs_celery_task_id'), table_name='letzshop_historical_import_jobs')
|
op.drop_index(op.f("ix_letzshop_historical_import_jobs_celery_task_id"), table_name="letzshop_historical_import_jobs")
|
||||||
op.drop_column('letzshop_historical_import_jobs', 'celery_task_id')
|
op.drop_column("letzshop_historical_import_jobs", "celery_task_id")
|
||||||
|
|
||||||
# MarketplaceImportJob
|
# MarketplaceImportJob
|
||||||
op.drop_index(op.f('ix_marketplace_import_jobs_celery_task_id'), table_name='marketplace_import_jobs')
|
op.drop_index(op.f("ix_marketplace_import_jobs_celery_task_id"), table_name="marketplace_import_jobs")
|
||||||
op.drop_column('marketplace_import_jobs', 'celery_task_id')
|
op.drop_column("marketplace_import_jobs", "celery_task_id")
|
||||||
|
|||||||
@@ -5,64 +5,64 @@ Revises: 7a7ce92593d5
|
|||||||
Create Date: 2025-11-29 12:44:55.427245
|
Create Date: 2025-11-29 12:44:55.427245
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '0bd9ffaaced1'
|
revision: str = "0bd9ffaaced1"
|
||||||
down_revision: Union[str, None] = '7a7ce92593d5'
|
down_revision: str | None = "7a7ce92593d5"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Create application_logs table
|
# Create application_logs table
|
||||||
op.create_table(
|
op.create_table(
|
||||||
'application_logs',
|
"application_logs",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('timestamp', sa.DateTime(), nullable=False),
|
sa.Column("timestamp", sa.DateTime(), nullable=False),
|
||||||
sa.Column('level', sa.String(length=20), nullable=False),
|
sa.Column("level", sa.String(length=20), nullable=False),
|
||||||
sa.Column('logger_name', sa.String(length=200), nullable=False),
|
sa.Column("logger_name", sa.String(length=200), nullable=False),
|
||||||
sa.Column('module', sa.String(length=200), nullable=True),
|
sa.Column("module", sa.String(length=200), nullable=True),
|
||||||
sa.Column('function_name', sa.String(length=100), nullable=True),
|
sa.Column("function_name", sa.String(length=100), nullable=True),
|
||||||
sa.Column('line_number', sa.Integer(), nullable=True),
|
sa.Column("line_number", sa.Integer(), nullable=True),
|
||||||
sa.Column('message', sa.Text(), nullable=False),
|
sa.Column("message", sa.Text(), nullable=False),
|
||||||
sa.Column('exception_type', sa.String(length=200), nullable=True),
|
sa.Column("exception_type", sa.String(length=200), nullable=True),
|
||||||
sa.Column('exception_message', sa.Text(), nullable=True),
|
sa.Column("exception_message", sa.Text(), nullable=True),
|
||||||
sa.Column('stack_trace', sa.Text(), nullable=True),
|
sa.Column("stack_trace", sa.Text(), nullable=True),
|
||||||
sa.Column('request_id', sa.String(length=100), nullable=True),
|
sa.Column("request_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
sa.Column("user_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=True),
|
sa.Column("vendor_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('context', sa.JSON(), nullable=True),
|
sa.Column("context", sa.JSON(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
sa.Column("updated_at", sa.DateTime(), nullable=True),
|
||||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create indexes for better query performance
|
# Create indexes for better query performance
|
||||||
op.create_index(op.f('ix_application_logs_id'), 'application_logs', ['id'], unique=False)
|
op.create_index(op.f("ix_application_logs_id"), "application_logs", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_application_logs_timestamp'), 'application_logs', ['timestamp'], unique=False)
|
op.create_index(op.f("ix_application_logs_timestamp"), "application_logs", ["timestamp"], unique=False)
|
||||||
op.create_index(op.f('ix_application_logs_level'), 'application_logs', ['level'], unique=False)
|
op.create_index(op.f("ix_application_logs_level"), "application_logs", ["level"], unique=False)
|
||||||
op.create_index(op.f('ix_application_logs_logger_name'), 'application_logs', ['logger_name'], unique=False)
|
op.create_index(op.f("ix_application_logs_logger_name"), "application_logs", ["logger_name"], unique=False)
|
||||||
op.create_index(op.f('ix_application_logs_request_id'), 'application_logs', ['request_id'], unique=False)
|
op.create_index(op.f("ix_application_logs_request_id"), "application_logs", ["request_id"], unique=False)
|
||||||
op.create_index(op.f('ix_application_logs_user_id'), 'application_logs', ['user_id'], unique=False)
|
op.create_index(op.f("ix_application_logs_user_id"), "application_logs", ["user_id"], unique=False)
|
||||||
op.create_index(op.f('ix_application_logs_vendor_id'), 'application_logs', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_application_logs_vendor_id"), "application_logs", ["vendor_id"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Drop indexes
|
# Drop indexes
|
||||||
op.drop_index(op.f('ix_application_logs_vendor_id'), table_name='application_logs')
|
op.drop_index(op.f("ix_application_logs_vendor_id"), table_name="application_logs")
|
||||||
op.drop_index(op.f('ix_application_logs_user_id'), table_name='application_logs')
|
op.drop_index(op.f("ix_application_logs_user_id"), table_name="application_logs")
|
||||||
op.drop_index(op.f('ix_application_logs_request_id'), table_name='application_logs')
|
op.drop_index(op.f("ix_application_logs_request_id"), table_name="application_logs")
|
||||||
op.drop_index(op.f('ix_application_logs_logger_name'), table_name='application_logs')
|
op.drop_index(op.f("ix_application_logs_logger_name"), table_name="application_logs")
|
||||||
op.drop_index(op.f('ix_application_logs_level'), table_name='application_logs')
|
op.drop_index(op.f("ix_application_logs_level"), table_name="application_logs")
|
||||||
op.drop_index(op.f('ix_application_logs_timestamp'), table_name='application_logs')
|
op.drop_index(op.f("ix_application_logs_timestamp"), table_name="application_logs")
|
||||||
op.drop_index(op.f('ix_application_logs_id'), table_name='application_logs')
|
op.drop_index(op.f("ix_application_logs_id"), table_name="application_logs")
|
||||||
|
|
||||||
# Drop table
|
# Drop table
|
||||||
op.drop_table('application_logs')
|
op.drop_table("application_logs")
|
||||||
|
|||||||
@@ -5,363 +5,363 @@ Revises: 09d84a46530f
|
|||||||
Create Date: 2026-01-13 19:38:45.423378
|
Create Date: 2026-01-13 19:38:45.423378
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql, sqlite
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
|
||||||
from sqlalchemy.dialects import postgresql
|
|
||||||
from sqlalchemy.dialects import sqlite
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '1b398cf45e85'
|
revision: str = "1b398cf45e85"
|
||||||
down_revision: Union[str, None] = '09d84a46530f'
|
down_revision: str | None = "09d84a46530f"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.create_table('letzshop_vendor_cache',
|
op.create_table("letzshop_vendor_cache",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('letzshop_id', sa.String(length=50), nullable=False),
|
sa.Column("letzshop_id", sa.String(length=50), nullable=False),
|
||||||
sa.Column('slug', sa.String(length=200), nullable=False),
|
sa.Column("slug", sa.String(length=200), nullable=False),
|
||||||
sa.Column('name', sa.String(length=255), nullable=False),
|
sa.Column("name", sa.String(length=255), nullable=False),
|
||||||
sa.Column('company_name', sa.String(length=255), nullable=True),
|
sa.Column("company_name", sa.String(length=255), nullable=True),
|
||||||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
sa.Column("is_active", sa.Boolean(), nullable=True),
|
||||||
sa.Column('description_en', sa.Text(), nullable=True),
|
sa.Column("description_en", sa.Text(), nullable=True),
|
||||||
sa.Column('description_fr', sa.Text(), nullable=True),
|
sa.Column("description_fr", sa.Text(), nullable=True),
|
||||||
sa.Column('description_de', sa.Text(), nullable=True),
|
sa.Column("description_de", sa.Text(), nullable=True),
|
||||||
sa.Column('email', sa.String(length=255), nullable=True),
|
sa.Column("email", sa.String(length=255), nullable=True),
|
||||||
sa.Column('phone', sa.String(length=50), nullable=True),
|
sa.Column("phone", sa.String(length=50), nullable=True),
|
||||||
sa.Column('fax', sa.String(length=50), nullable=True),
|
sa.Column("fax", sa.String(length=50), nullable=True),
|
||||||
sa.Column('website', sa.String(length=500), nullable=True),
|
sa.Column("website", sa.String(length=500), nullable=True),
|
||||||
sa.Column('street', sa.String(length=255), nullable=True),
|
sa.Column("street", sa.String(length=255), nullable=True),
|
||||||
sa.Column('street_number', sa.String(length=50), nullable=True),
|
sa.Column("street_number", sa.String(length=50), nullable=True),
|
||||||
sa.Column('city', sa.String(length=100), nullable=True),
|
sa.Column("city", sa.String(length=100), nullable=True),
|
||||||
sa.Column('zipcode', sa.String(length=20), nullable=True),
|
sa.Column("zipcode", sa.String(length=20), nullable=True),
|
||||||
sa.Column('country_iso', sa.String(length=5), nullable=True),
|
sa.Column("country_iso", sa.String(length=5), nullable=True),
|
||||||
sa.Column('latitude', sa.String(length=20), nullable=True),
|
sa.Column("latitude", sa.String(length=20), nullable=True),
|
||||||
sa.Column('longitude', sa.String(length=20), nullable=True),
|
sa.Column("longitude", sa.String(length=20), nullable=True),
|
||||||
sa.Column('categories', sqlite.JSON(), nullable=True),
|
sa.Column("categories", sqlite.JSON(), nullable=True),
|
||||||
sa.Column('background_image_url', sa.String(length=500), nullable=True),
|
sa.Column("background_image_url", sa.String(length=500), nullable=True),
|
||||||
sa.Column('social_media_links', sqlite.JSON(), nullable=True),
|
sa.Column("social_media_links", sqlite.JSON(), nullable=True),
|
||||||
sa.Column('opening_hours_en', sa.Text(), nullable=True),
|
sa.Column("opening_hours_en", sa.Text(), nullable=True),
|
||||||
sa.Column('opening_hours_fr', sa.Text(), nullable=True),
|
sa.Column("opening_hours_fr", sa.Text(), nullable=True),
|
||||||
sa.Column('opening_hours_de', sa.Text(), nullable=True),
|
sa.Column("opening_hours_de", sa.Text(), nullable=True),
|
||||||
sa.Column('representative_name', sa.String(length=255), nullable=True),
|
sa.Column("representative_name", sa.String(length=255), nullable=True),
|
||||||
sa.Column('representative_title', sa.String(length=100), nullable=True),
|
sa.Column("representative_title", sa.String(length=100), nullable=True),
|
||||||
sa.Column('claimed_by_vendor_id', sa.Integer(), nullable=True),
|
sa.Column("claimed_by_vendor_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('claimed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("claimed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('last_synced_at', sa.DateTime(timezone=True), nullable=False),
|
sa.Column("last_synced_at", sa.DateTime(timezone=True), nullable=False),
|
||||||
sa.Column('raw_data', sqlite.JSON(), nullable=True),
|
sa.Column("raw_data", sqlite.JSON(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['claimed_by_vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["claimed_by_vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index('idx_vendor_cache_active', 'letzshop_vendor_cache', ['is_active'], unique=False)
|
op.create_index("idx_vendor_cache_active", "letzshop_vendor_cache", ["is_active"], unique=False)
|
||||||
op.create_index('idx_vendor_cache_city', 'letzshop_vendor_cache', ['city'], unique=False)
|
op.create_index("idx_vendor_cache_city", "letzshop_vendor_cache", ["city"], unique=False)
|
||||||
op.create_index('idx_vendor_cache_claimed', 'letzshop_vendor_cache', ['claimed_by_vendor_id'], unique=False)
|
op.create_index("idx_vendor_cache_claimed", "letzshop_vendor_cache", ["claimed_by_vendor_id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_vendor_cache_claimed_by_vendor_id'), 'letzshop_vendor_cache', ['claimed_by_vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_vendor_cache_claimed_by_vendor_id"), "letzshop_vendor_cache", ["claimed_by_vendor_id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_vendor_cache_id'), 'letzshop_vendor_cache', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_vendor_cache_id"), "letzshop_vendor_cache", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_vendor_cache_letzshop_id'), 'letzshop_vendor_cache', ['letzshop_id'], unique=True)
|
op.create_index(op.f("ix_letzshop_vendor_cache_letzshop_id"), "letzshop_vendor_cache", ["letzshop_id"], unique=True)
|
||||||
op.create_index(op.f('ix_letzshop_vendor_cache_slug'), 'letzshop_vendor_cache', ['slug'], unique=True)
|
op.create_index(op.f("ix_letzshop_vendor_cache_slug"), "letzshop_vendor_cache", ["slug"], unique=True)
|
||||||
op.drop_constraint('architecture_rules_rule_id_key', 'architecture_rules', type_='unique')
|
op.drop_constraint("architecture_rules_rule_id_key", "architecture_rules", type_="unique")
|
||||||
op.alter_column('capacity_snapshots', 'created_at',
|
op.alter_column("capacity_snapshots", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.alter_column('capacity_snapshots', 'updated_at',
|
op.alter_column("capacity_snapshots", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.create_index(op.f('ix_features_id'), 'features', ['id'], unique=False)
|
op.create_index(op.f("ix_features_id"), "features", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_features_minimum_tier_id'), 'features', ['minimum_tier_id'], unique=False)
|
op.create_index(op.f("ix_features_minimum_tier_id"), "features", ["minimum_tier_id"], unique=False)
|
||||||
op.create_index('idx_inv_tx_order', 'inventory_transactions', ['order_id'], unique=False)
|
op.create_index("idx_inv_tx_order", "inventory_transactions", ["order_id"], unique=False)
|
||||||
op.alter_column('invoices', 'created_at',
|
op.alter_column("invoices", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('invoices', 'updated_at',
|
op.alter_column("invoices", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('letzshop_fulfillment_queue', 'created_at',
|
op.alter_column("letzshop_fulfillment_queue", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('letzshop_fulfillment_queue', 'updated_at',
|
op.alter_column("letzshop_fulfillment_queue", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('letzshop_sync_logs', 'created_at',
|
op.alter_column("letzshop_sync_logs", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('letzshop_sync_logs', 'updated_at',
|
op.alter_column("letzshop_sync_logs", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('media_files', 'created_at',
|
op.alter_column("media_files", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.alter_column('media_files', 'updated_at',
|
op.alter_column("media_files", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
op.alter_column('order_item_exceptions', 'created_at',
|
op.alter_column("order_item_exceptions", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('order_item_exceptions', 'updated_at',
|
op.alter_column("order_item_exceptions", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('order_items', 'created_at',
|
op.alter_column("order_items", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('order_items', 'updated_at',
|
op.alter_column("order_items", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('orders', 'created_at',
|
op.alter_column("orders", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('orders', 'updated_at',
|
op.alter_column("orders", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.drop_index('ix_password_reset_tokens_customer_id', table_name='password_reset_tokens')
|
op.drop_index("ix_password_reset_tokens_customer_id", table_name="password_reset_tokens")
|
||||||
op.create_index(op.f('ix_password_reset_tokens_id'), 'password_reset_tokens', ['id'], unique=False)
|
op.create_index(op.f("ix_password_reset_tokens_id"), "password_reset_tokens", ["id"], unique=False)
|
||||||
op.alter_column('product_media', 'created_at',
|
op.alter_column("product_media", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.alter_column('product_media', 'updated_at',
|
op.alter_column("product_media", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
op.alter_column('products', 'is_digital',
|
op.alter_column("products", "is_digital",
|
||||||
existing_type=sa.BOOLEAN(),
|
existing_type=sa.BOOLEAN(),
|
||||||
nullable=True,
|
nullable=True,
|
||||||
existing_server_default=sa.text('false'))
|
existing_server_default=sa.text("false"))
|
||||||
op.alter_column('products', 'product_type',
|
op.alter_column("products", "product_type",
|
||||||
existing_type=sa.VARCHAR(length=20),
|
existing_type=sa.VARCHAR(length=20),
|
||||||
nullable=True,
|
nullable=True,
|
||||||
existing_server_default=sa.text("'physical'::character varying"))
|
existing_server_default=sa.text("'physical'::character varying"))
|
||||||
op.drop_index('idx_product_is_digital', table_name='products')
|
op.drop_index("idx_product_is_digital", table_name="products")
|
||||||
op.create_index(op.f('ix_products_is_digital'), 'products', ['is_digital'], unique=False)
|
op.create_index(op.f("ix_products_is_digital"), "products", ["is_digital"], unique=False)
|
||||||
op.drop_constraint('uq_vendor_email_settings_vendor_id', 'vendor_email_settings', type_='unique')
|
op.drop_constraint("uq_vendor_email_settings_vendor_id", "vendor_email_settings", type_="unique")
|
||||||
op.drop_index('ix_vendor_email_templates_lookup', table_name='vendor_email_templates')
|
op.drop_index("ix_vendor_email_templates_lookup", table_name="vendor_email_templates")
|
||||||
op.create_index(op.f('ix_vendor_email_templates_id'), 'vendor_email_templates', ['id'], unique=False)
|
op.create_index(op.f("ix_vendor_email_templates_id"), "vendor_email_templates", ["id"], unique=False)
|
||||||
op.alter_column('vendor_invoice_settings', 'created_at',
|
op.alter_column("vendor_invoice_settings", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('vendor_invoice_settings', 'updated_at',
|
op.alter_column("vendor_invoice_settings", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.drop_constraint('vendor_invoice_settings_vendor_id_key', 'vendor_invoice_settings', type_='unique')
|
op.drop_constraint("vendor_invoice_settings_vendor_id_key", "vendor_invoice_settings", type_="unique")
|
||||||
op.alter_column('vendor_letzshop_credentials', 'created_at',
|
op.alter_column("vendor_letzshop_credentials", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('vendor_letzshop_credentials', 'updated_at',
|
op.alter_column("vendor_letzshop_credentials", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.drop_constraint('vendor_letzshop_credentials_vendor_id_key', 'vendor_letzshop_credentials', type_='unique')
|
op.drop_constraint("vendor_letzshop_credentials_vendor_id_key", "vendor_letzshop_credentials", type_="unique")
|
||||||
op.alter_column('vendor_subscriptions', 'created_at',
|
op.alter_column("vendor_subscriptions", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('vendor_subscriptions', 'updated_at',
|
op.alter_column("vendor_subscriptions", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(timezone=True),
|
existing_type=postgresql.TIMESTAMP(timezone=True),
|
||||||
type_=sa.DateTime(),
|
type_=sa.DateTime(),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.drop_constraint('vendor_subscriptions_vendor_id_key', 'vendor_subscriptions', type_='unique')
|
op.drop_constraint("vendor_subscriptions_vendor_id_key", "vendor_subscriptions", type_="unique")
|
||||||
op.drop_constraint('fk_vendor_subscriptions_tier_id', 'vendor_subscriptions', type_='foreignkey')
|
op.drop_constraint("fk_vendor_subscriptions_tier_id", "vendor_subscriptions", type_="foreignkey")
|
||||||
op.create_foreign_key(None, 'vendor_subscriptions', 'subscription_tiers', ['tier_id'], ['id'])
|
op.create_foreign_key(None, "vendor_subscriptions", "subscription_tiers", ["tier_id"], ["id"])
|
||||||
op.alter_column('vendors', 'storefront_locale',
|
op.alter_column("vendors", "storefront_locale",
|
||||||
existing_type=sa.VARCHAR(length=10),
|
existing_type=sa.VARCHAR(length=10),
|
||||||
comment=None,
|
comment=None,
|
||||||
existing_comment='Currency/number formatting locale (NULL = inherit from platform)',
|
existing_comment="Currency/number formatting locale (NULL = inherit from platform)",
|
||||||
existing_nullable=True)
|
existing_nullable=True)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.alter_column('vendors', 'storefront_locale',
|
op.alter_column("vendors", "storefront_locale",
|
||||||
existing_type=sa.VARCHAR(length=10),
|
existing_type=sa.VARCHAR(length=10),
|
||||||
comment='Currency/number formatting locale (NULL = inherit from platform)',
|
comment="Currency/number formatting locale (NULL = inherit from platform)",
|
||||||
existing_nullable=True)
|
existing_nullable=True)
|
||||||
op.drop_constraint(None, 'vendor_subscriptions', type_='foreignkey')
|
op.drop_constraint(None, "vendor_subscriptions", type_="foreignkey")
|
||||||
op.create_foreign_key('fk_vendor_subscriptions_tier_id', 'vendor_subscriptions', 'subscription_tiers', ['tier_id'], ['id'], ondelete='SET NULL')
|
op.create_foreign_key("fk_vendor_subscriptions_tier_id", "vendor_subscriptions", "subscription_tiers", ["tier_id"], ["id"], ondelete="SET NULL")
|
||||||
op.create_unique_constraint('vendor_subscriptions_vendor_id_key', 'vendor_subscriptions', ['vendor_id'])
|
op.create_unique_constraint("vendor_subscriptions_vendor_id_key", "vendor_subscriptions", ["vendor_id"])
|
||||||
op.alter_column('vendor_subscriptions', 'updated_at',
|
op.alter_column("vendor_subscriptions", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('vendor_subscriptions', 'created_at',
|
op.alter_column("vendor_subscriptions", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.create_unique_constraint('vendor_letzshop_credentials_vendor_id_key', 'vendor_letzshop_credentials', ['vendor_id'])
|
op.create_unique_constraint("vendor_letzshop_credentials_vendor_id_key", "vendor_letzshop_credentials", ["vendor_id"])
|
||||||
op.alter_column('vendor_letzshop_credentials', 'updated_at',
|
op.alter_column("vendor_letzshop_credentials", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('vendor_letzshop_credentials', 'created_at',
|
op.alter_column("vendor_letzshop_credentials", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.create_unique_constraint('vendor_invoice_settings_vendor_id_key', 'vendor_invoice_settings', ['vendor_id'])
|
op.create_unique_constraint("vendor_invoice_settings_vendor_id_key", "vendor_invoice_settings", ["vendor_id"])
|
||||||
op.alter_column('vendor_invoice_settings', 'updated_at',
|
op.alter_column("vendor_invoice_settings", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('vendor_invoice_settings', 'created_at',
|
op.alter_column("vendor_invoice_settings", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.drop_index(op.f('ix_vendor_email_templates_id'), table_name='vendor_email_templates')
|
op.drop_index(op.f("ix_vendor_email_templates_id"), table_name="vendor_email_templates")
|
||||||
op.create_index('ix_vendor_email_templates_lookup', 'vendor_email_templates', ['vendor_id', 'template_code', 'language'], unique=False)
|
op.create_index("ix_vendor_email_templates_lookup", "vendor_email_templates", ["vendor_id", "template_code", "language"], unique=False)
|
||||||
op.create_unique_constraint('uq_vendor_email_settings_vendor_id', 'vendor_email_settings', ['vendor_id'])
|
op.create_unique_constraint("uq_vendor_email_settings_vendor_id", "vendor_email_settings", ["vendor_id"])
|
||||||
op.drop_index(op.f('ix_products_is_digital'), table_name='products')
|
op.drop_index(op.f("ix_products_is_digital"), table_name="products")
|
||||||
op.create_index('idx_product_is_digital', 'products', ['is_digital'], unique=False)
|
op.create_index("idx_product_is_digital", "products", ["is_digital"], unique=False)
|
||||||
op.alter_column('products', 'product_type',
|
op.alter_column("products", "product_type",
|
||||||
existing_type=sa.VARCHAR(length=20),
|
existing_type=sa.VARCHAR(length=20),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
existing_server_default=sa.text("'physical'::character varying"))
|
existing_server_default=sa.text("'physical'::character varying"))
|
||||||
op.alter_column('products', 'is_digital',
|
op.alter_column("products", "is_digital",
|
||||||
existing_type=sa.BOOLEAN(),
|
existing_type=sa.BOOLEAN(),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
existing_server_default=sa.text('false'))
|
existing_server_default=sa.text("false"))
|
||||||
op.alter_column('product_media', 'updated_at',
|
op.alter_column("product_media", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=True)
|
nullable=True)
|
||||||
op.alter_column('product_media', 'created_at',
|
op.alter_column("product_media", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=True,
|
nullable=True,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.drop_index(op.f('ix_password_reset_tokens_id'), table_name='password_reset_tokens')
|
op.drop_index(op.f("ix_password_reset_tokens_id"), table_name="password_reset_tokens")
|
||||||
op.create_index('ix_password_reset_tokens_customer_id', 'password_reset_tokens', ['customer_id'], unique=False)
|
op.create_index("ix_password_reset_tokens_customer_id", "password_reset_tokens", ["customer_id"], unique=False)
|
||||||
op.alter_column('orders', 'updated_at',
|
op.alter_column("orders", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('orders', 'created_at',
|
op.alter_column("orders", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('order_items', 'updated_at',
|
op.alter_column("order_items", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('order_items', 'created_at',
|
op.alter_column("order_items", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('order_item_exceptions', 'updated_at',
|
op.alter_column("order_item_exceptions", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('order_item_exceptions', 'created_at',
|
op.alter_column("order_item_exceptions", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('media_files', 'updated_at',
|
op.alter_column("media_files", "updated_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=True)
|
nullable=True)
|
||||||
op.alter_column('media_files', 'created_at',
|
op.alter_column("media_files", "created_at",
|
||||||
existing_type=postgresql.TIMESTAMP(),
|
existing_type=postgresql.TIMESTAMP(),
|
||||||
nullable=True,
|
nullable=True,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.alter_column('letzshop_sync_logs', 'updated_at',
|
op.alter_column("letzshop_sync_logs", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('letzshop_sync_logs', 'created_at',
|
op.alter_column("letzshop_sync_logs", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('letzshop_fulfillment_queue', 'updated_at',
|
op.alter_column("letzshop_fulfillment_queue", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('letzshop_fulfillment_queue', 'created_at',
|
op.alter_column("letzshop_fulfillment_queue", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('invoices', 'updated_at',
|
op.alter_column("invoices", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.alter_column('invoices', 'created_at',
|
op.alter_column("invoices", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('CURRENT_TIMESTAMP'))
|
existing_server_default=sa.text("CURRENT_TIMESTAMP"))
|
||||||
op.drop_index('idx_inv_tx_order', table_name='inventory_transactions')
|
op.drop_index("idx_inv_tx_order", table_name="inventory_transactions")
|
||||||
op.drop_index(op.f('ix_features_minimum_tier_id'), table_name='features')
|
op.drop_index(op.f("ix_features_minimum_tier_id"), table_name="features")
|
||||||
op.drop_index(op.f('ix_features_id'), table_name='features')
|
op.drop_index(op.f("ix_features_id"), table_name="features")
|
||||||
op.alter_column('capacity_snapshots', 'updated_at',
|
op.alter_column("capacity_snapshots", "updated_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.alter_column('capacity_snapshots', 'created_at',
|
op.alter_column("capacity_snapshots", "created_at",
|
||||||
existing_type=sa.DateTime(),
|
existing_type=sa.DateTime(),
|
||||||
type_=postgresql.TIMESTAMP(timezone=True),
|
type_=postgresql.TIMESTAMP(timezone=True),
|
||||||
existing_nullable=False,
|
existing_nullable=False,
|
||||||
existing_server_default=sa.text('now()'))
|
existing_server_default=sa.text("now()"))
|
||||||
op.create_unique_constraint('architecture_rules_rule_id_key', 'architecture_rules', ['rule_id'])
|
op.create_unique_constraint("architecture_rules_rule_id_key", "architecture_rules", ["rule_id"])
|
||||||
op.drop_index(op.f('ix_letzshop_vendor_cache_slug'), table_name='letzshop_vendor_cache')
|
op.drop_index(op.f("ix_letzshop_vendor_cache_slug"), table_name="letzshop_vendor_cache")
|
||||||
op.drop_index(op.f('ix_letzshop_vendor_cache_letzshop_id'), table_name='letzshop_vendor_cache')
|
op.drop_index(op.f("ix_letzshop_vendor_cache_letzshop_id"), table_name="letzshop_vendor_cache")
|
||||||
op.drop_index(op.f('ix_letzshop_vendor_cache_id'), table_name='letzshop_vendor_cache')
|
op.drop_index(op.f("ix_letzshop_vendor_cache_id"), table_name="letzshop_vendor_cache")
|
||||||
op.drop_index(op.f('ix_letzshop_vendor_cache_claimed_by_vendor_id'), table_name='letzshop_vendor_cache')
|
op.drop_index(op.f("ix_letzshop_vendor_cache_claimed_by_vendor_id"), table_name="letzshop_vendor_cache")
|
||||||
op.drop_index('idx_vendor_cache_claimed', table_name='letzshop_vendor_cache')
|
op.drop_index("idx_vendor_cache_claimed", table_name="letzshop_vendor_cache")
|
||||||
op.drop_index('idx_vendor_cache_city', table_name='letzshop_vendor_cache')
|
op.drop_index("idx_vendor_cache_city", table_name="letzshop_vendor_cache")
|
||||||
op.drop_index('idx_vendor_cache_active', table_name='letzshop_vendor_cache')
|
op.drop_index("idx_vendor_cache_active", table_name="letzshop_vendor_cache")
|
||||||
op.drop_table('letzshop_vendor_cache')
|
op.drop_table("letzshop_vendor_cache")
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|||||||
@@ -5,53 +5,55 @@ Revises: cb88bc9b5f86
|
|||||||
Create Date: 2025-12-19 05:40:53.463341
|
Create Date: 2025-12-19 05:40:53.463341
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
|
||||||
# Removed: from sqlalchemy.dialects import sqlite (using sa.JSON for PostgreSQL)
|
# Removed: from sqlalchemy.dialects import sqlite (using sa.JSON for PostgreSQL)
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '204273a59d73'
|
revision: str = "204273a59d73"
|
||||||
down_revision: Union[str, None] = 'cb88bc9b5f86'
|
down_revision: str | None = "cb88bc9b5f86"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
op.create_table('letzshop_historical_import_jobs',
|
op.create_table("letzshop_historical_import_jobs",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
sa.Column("user_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('status', sa.String(length=50), nullable=False),
|
sa.Column("status", sa.String(length=50), nullable=False),
|
||||||
sa.Column('current_phase', sa.String(length=20), nullable=True),
|
sa.Column("current_phase", sa.String(length=20), nullable=True),
|
||||||
sa.Column('current_page', sa.Integer(), nullable=True),
|
sa.Column("current_page", sa.Integer(), nullable=True),
|
||||||
sa.Column('total_pages', sa.Integer(), nullable=True),
|
sa.Column("total_pages", sa.Integer(), nullable=True),
|
||||||
sa.Column('shipments_fetched', sa.Integer(), nullable=True),
|
sa.Column("shipments_fetched", sa.Integer(), nullable=True),
|
||||||
sa.Column('orders_processed', sa.Integer(), nullable=True),
|
sa.Column("orders_processed", sa.Integer(), nullable=True),
|
||||||
sa.Column('orders_imported', sa.Integer(), nullable=True),
|
sa.Column("orders_imported", sa.Integer(), nullable=True),
|
||||||
sa.Column('orders_updated', sa.Integer(), nullable=True),
|
sa.Column("orders_updated", sa.Integer(), nullable=True),
|
||||||
sa.Column('orders_skipped', sa.Integer(), nullable=True),
|
sa.Column("orders_skipped", sa.Integer(), nullable=True),
|
||||||
sa.Column('products_matched', sa.Integer(), nullable=True),
|
sa.Column("products_matched", sa.Integer(), nullable=True),
|
||||||
sa.Column('products_not_found', sa.Integer(), nullable=True),
|
sa.Column("products_not_found", sa.Integer(), nullable=True),
|
||||||
sa.Column('confirmed_stats', sa.JSON(), nullable=True),
|
sa.Column("confirmed_stats", sa.JSON(), nullable=True),
|
||||||
sa.Column('declined_stats', sa.JSON(), nullable=True),
|
sa.Column("declined_stats", sa.JSON(), nullable=True),
|
||||||
sa.Column('error_message', sa.Text(), nullable=True),
|
sa.Column("error_message", sa.Text(), nullable=True),
|
||||||
sa.Column('started_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index('idx_historical_import_vendor', 'letzshop_historical_import_jobs', ['vendor_id', 'status'], unique=False)
|
op.create_index("idx_historical_import_vendor", "letzshop_historical_import_jobs", ["vendor_id", "status"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_historical_import_jobs_id'), 'letzshop_historical_import_jobs', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_historical_import_jobs_id"), "letzshop_historical_import_jobs", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_historical_import_jobs_vendor_id'), 'letzshop_historical_import_jobs', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_historical_import_jobs_vendor_id"), "letzshop_historical_import_jobs", ["vendor_id"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_index(op.f('ix_letzshop_historical_import_jobs_vendor_id'), table_name='letzshop_historical_import_jobs')
|
op.drop_index(op.f("ix_letzshop_historical_import_jobs_vendor_id"), table_name="letzshop_historical_import_jobs")
|
||||||
op.drop_index(op.f('ix_letzshop_historical_import_jobs_id'), table_name='letzshop_historical_import_jobs')
|
op.drop_index(op.f("ix_letzshop_historical_import_jobs_id"), table_name="letzshop_historical_import_jobs")
|
||||||
op.drop_index('idx_historical_import_vendor', table_name='letzshop_historical_import_jobs')
|
op.drop_index("idx_historical_import_vendor", table_name="letzshop_historical_import_jobs")
|
||||||
op.drop_table('letzshop_historical_import_jobs')
|
op.drop_table("letzshop_historical_import_jobs")
|
||||||
|
|||||||
@@ -5,23 +5,23 @@ Revises: 204273a59d73
|
|||||||
Create Date: 2025-12-19 08:46:23.731912
|
Create Date: 2025-12-19 08:46:23.731912
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '2362c2723a93'
|
revision: str = "2362c2723a93"
|
||||||
down_revision: Union[str, None] = '204273a59d73'
|
down_revision: str | None = "204273a59d73"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add order_date column to letzshop_orders table
|
# Add order_date column to letzshop_orders table
|
||||||
op.add_column('letzshop_orders', sa.Column('order_date', sa.DateTime(timezone=True), nullable=True))
|
op.add_column("letzshop_orders", sa.Column("order_date", sa.DateTime(timezone=True), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column('letzshop_orders', 'order_date')
|
op.drop_column("letzshop_orders", "order_date")
|
||||||
|
|||||||
@@ -5,33 +5,33 @@ Revises: 9f3a25ea4991
|
|||||||
Create Date: 2025-12-03 22:26:02.161087
|
Create Date: 2025-12-03 22:26:02.161087
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '28d44d503cac'
|
revision: str = "28d44d503cac"
|
||||||
down_revision: Union[str, None] = '9f3a25ea4991'
|
down_revision: str | None = "9f3a25ea4991"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add nullable contact fields to vendor table
|
# Add nullable contact fields to vendor table
|
||||||
# These allow vendor-specific branding/identity, overriding company defaults
|
# These allow vendor-specific branding/identity, overriding company defaults
|
||||||
op.add_column('vendors', sa.Column('contact_email', sa.String(255), nullable=True))
|
op.add_column("vendors", sa.Column("contact_email", sa.String(255), nullable=True))
|
||||||
op.add_column('vendors', sa.Column('contact_phone', sa.String(50), nullable=True))
|
op.add_column("vendors", sa.Column("contact_phone", sa.String(50), nullable=True))
|
||||||
op.add_column('vendors', sa.Column('website', sa.String(255), nullable=True))
|
op.add_column("vendors", sa.Column("website", sa.String(255), nullable=True))
|
||||||
op.add_column('vendors', sa.Column('business_address', sa.Text(), nullable=True))
|
op.add_column("vendors", sa.Column("business_address", sa.Text(), nullable=True))
|
||||||
op.add_column('vendors', sa.Column('tax_number', sa.String(100), nullable=True))
|
op.add_column("vendors", sa.Column("tax_number", sa.String(100), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Remove contact fields from vendor table
|
# Remove contact fields from vendor table
|
||||||
op.drop_column('vendors', 'tax_number')
|
op.drop_column("vendors", "tax_number")
|
||||||
op.drop_column('vendors', 'business_address')
|
op.drop_column("vendors", "business_address")
|
||||||
op.drop_column('vendors', 'website')
|
op.drop_column("vendors", "website")
|
||||||
op.drop_column('vendors', 'contact_phone')
|
op.drop_column("vendors", "contact_phone")
|
||||||
op.drop_column('vendors', 'contact_email')
|
op.drop_column("vendors", "contact_email")
|
||||||
|
|||||||
@@ -5,18 +5,20 @@ Revises: e1bfb453fbe9
|
|||||||
Create Date: 2025-12-25 18:29:34.167773
|
Create Date: 2025-12-25 18:29:34.167773
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from collections.abc import Sequence
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Sequence, Union
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
|
||||||
# Removed: from sqlalchemy.dialects import sqlite (using sa.JSON for PostgreSQL)
|
# Removed: from sqlalchemy.dialects import sqlite (using sa.JSON for PostgreSQL)
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '2953ed10d22c'
|
revision: str = "2953ed10d22c"
|
||||||
down_revision: Union[str, None] = 'e1bfb453fbe9'
|
down_revision: str | None = "e1bfb453fbe9"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
@@ -25,146 +27,146 @@ def upgrade() -> None:
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
# subscription_tiers - Database-driven tier definitions
|
# subscription_tiers - Database-driven tier definitions
|
||||||
op.create_table('subscription_tiers',
|
op.create_table("subscription_tiers",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('code', sa.String(length=30), nullable=False),
|
sa.Column("code", sa.String(length=30), nullable=False),
|
||||||
sa.Column('name', sa.String(length=100), nullable=False),
|
sa.Column("name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('description', sa.Text(), nullable=True),
|
sa.Column("description", sa.Text(), nullable=True),
|
||||||
sa.Column('price_monthly_cents', sa.Integer(), nullable=False),
|
sa.Column("price_monthly_cents", sa.Integer(), nullable=False),
|
||||||
sa.Column('price_annual_cents', sa.Integer(), nullable=True),
|
sa.Column("price_annual_cents", sa.Integer(), nullable=True),
|
||||||
sa.Column('orders_per_month', sa.Integer(), nullable=True),
|
sa.Column("orders_per_month", sa.Integer(), nullable=True),
|
||||||
sa.Column('products_limit', sa.Integer(), nullable=True),
|
sa.Column("products_limit", sa.Integer(), nullable=True),
|
||||||
sa.Column('team_members', sa.Integer(), nullable=True),
|
sa.Column("team_members", sa.Integer(), nullable=True),
|
||||||
sa.Column('order_history_months', sa.Integer(), nullable=True),
|
sa.Column("order_history_months", sa.Integer(), nullable=True),
|
||||||
sa.Column('features', sa.JSON(), nullable=True),
|
sa.Column("features", sa.JSON(), nullable=True),
|
||||||
sa.Column('stripe_product_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_product_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('stripe_price_monthly_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_price_monthly_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('stripe_price_annual_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_price_annual_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('display_order', sa.Integer(), nullable=True),
|
sa.Column("display_order", sa.Integer(), nullable=True),
|
||||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
sa.Column("is_active", sa.Boolean(), nullable=False),
|
||||||
sa.Column('is_public', sa.Boolean(), nullable=False),
|
sa.Column("is_public", sa.Boolean(), nullable=False),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_subscription_tiers_code'), 'subscription_tiers', ['code'], unique=True)
|
op.create_index(op.f("ix_subscription_tiers_code"), "subscription_tiers", ["code"], unique=True)
|
||||||
op.create_index(op.f('ix_subscription_tiers_id'), 'subscription_tiers', ['id'], unique=False)
|
op.create_index(op.f("ix_subscription_tiers_id"), "subscription_tiers", ["id"], unique=False)
|
||||||
|
|
||||||
# addon_products - Purchasable add-ons (domains, SSL, email)
|
# addon_products - Purchasable add-ons (domains, SSL, email)
|
||||||
op.create_table('addon_products',
|
op.create_table("addon_products",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('code', sa.String(length=50), nullable=False),
|
sa.Column("code", sa.String(length=50), nullable=False),
|
||||||
sa.Column('name', sa.String(length=100), nullable=False),
|
sa.Column("name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('description', sa.Text(), nullable=True),
|
sa.Column("description", sa.Text(), nullable=True),
|
||||||
sa.Column('category', sa.String(length=50), nullable=False),
|
sa.Column("category", sa.String(length=50), nullable=False),
|
||||||
sa.Column('price_cents', sa.Integer(), nullable=False),
|
sa.Column("price_cents", sa.Integer(), nullable=False),
|
||||||
sa.Column('billing_period', sa.String(length=20), nullable=False),
|
sa.Column("billing_period", sa.String(length=20), nullable=False),
|
||||||
sa.Column('quantity_unit', sa.String(length=50), nullable=True),
|
sa.Column("quantity_unit", sa.String(length=50), nullable=True),
|
||||||
sa.Column('quantity_value', sa.Integer(), nullable=True),
|
sa.Column("quantity_value", sa.Integer(), nullable=True),
|
||||||
sa.Column('stripe_product_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_product_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('stripe_price_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_price_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('display_order', sa.Integer(), nullable=True),
|
sa.Column("display_order", sa.Integer(), nullable=True),
|
||||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
sa.Column("is_active", sa.Boolean(), nullable=False),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_addon_products_category'), 'addon_products', ['category'], unique=False)
|
op.create_index(op.f("ix_addon_products_category"), "addon_products", ["category"], unique=False)
|
||||||
op.create_index(op.f('ix_addon_products_code'), 'addon_products', ['code'], unique=True)
|
op.create_index(op.f("ix_addon_products_code"), "addon_products", ["code"], unique=True)
|
||||||
op.create_index(op.f('ix_addon_products_id'), 'addon_products', ['id'], unique=False)
|
op.create_index(op.f("ix_addon_products_id"), "addon_products", ["id"], unique=False)
|
||||||
|
|
||||||
# billing_history - Invoice and payment history
|
# billing_history - Invoice and payment history
|
||||||
op.create_table('billing_history',
|
op.create_table("billing_history",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('stripe_invoice_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_invoice_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('stripe_payment_intent_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_payment_intent_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('invoice_number', sa.String(length=50), nullable=True),
|
sa.Column("invoice_number", sa.String(length=50), nullable=True),
|
||||||
sa.Column('invoice_date', sa.DateTime(timezone=True), nullable=False),
|
sa.Column("invoice_date", sa.DateTime(timezone=True), nullable=False),
|
||||||
sa.Column('due_date', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("due_date", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('subtotal_cents', sa.Integer(), nullable=False),
|
sa.Column("subtotal_cents", sa.Integer(), nullable=False),
|
||||||
sa.Column('tax_cents', sa.Integer(), nullable=False),
|
sa.Column("tax_cents", sa.Integer(), nullable=False),
|
||||||
sa.Column('total_cents', sa.Integer(), nullable=False),
|
sa.Column("total_cents", sa.Integer(), nullable=False),
|
||||||
sa.Column('amount_paid_cents', sa.Integer(), nullable=False),
|
sa.Column("amount_paid_cents", sa.Integer(), nullable=False),
|
||||||
sa.Column('currency', sa.String(length=3), nullable=False),
|
sa.Column("currency", sa.String(length=3), nullable=False),
|
||||||
sa.Column('status', sa.String(length=20), nullable=False),
|
sa.Column("status", sa.String(length=20), nullable=False),
|
||||||
sa.Column('invoice_pdf_url', sa.String(length=500), nullable=True),
|
sa.Column("invoice_pdf_url", sa.String(length=500), nullable=True),
|
||||||
sa.Column('hosted_invoice_url', sa.String(length=500), nullable=True),
|
sa.Column("hosted_invoice_url", sa.String(length=500), nullable=True),
|
||||||
sa.Column('description', sa.Text(), nullable=True),
|
sa.Column("description", sa.Text(), nullable=True),
|
||||||
sa.Column('line_items', sa.JSON(), nullable=True),
|
sa.Column("line_items", sa.JSON(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index('idx_billing_status', 'billing_history', ['vendor_id', 'status'], unique=False)
|
op.create_index("idx_billing_status", "billing_history", ["vendor_id", "status"], unique=False)
|
||||||
op.create_index('idx_billing_vendor_date', 'billing_history', ['vendor_id', 'invoice_date'], unique=False)
|
op.create_index("idx_billing_vendor_date", "billing_history", ["vendor_id", "invoice_date"], unique=False)
|
||||||
op.create_index(op.f('ix_billing_history_id'), 'billing_history', ['id'], unique=False)
|
op.create_index(op.f("ix_billing_history_id"), "billing_history", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_billing_history_status'), 'billing_history', ['status'], unique=False)
|
op.create_index(op.f("ix_billing_history_status"), "billing_history", ["status"], unique=False)
|
||||||
op.create_index(op.f('ix_billing_history_stripe_invoice_id'), 'billing_history', ['stripe_invoice_id'], unique=True)
|
op.create_index(op.f("ix_billing_history_stripe_invoice_id"), "billing_history", ["stripe_invoice_id"], unique=True)
|
||||||
op.create_index(op.f('ix_billing_history_vendor_id'), 'billing_history', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_billing_history_vendor_id"), "billing_history", ["vendor_id"], unique=False)
|
||||||
|
|
||||||
# vendor_addons - Add-ons purchased by vendor
|
# vendor_addons - Add-ons purchased by vendor
|
||||||
op.create_table('vendor_addons',
|
op.create_table("vendor_addons",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('addon_product_id', sa.Integer(), nullable=False),
|
sa.Column("addon_product_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('status', sa.String(length=20), nullable=False),
|
sa.Column("status", sa.String(length=20), nullable=False),
|
||||||
sa.Column('domain_name', sa.String(length=255), nullable=True),
|
sa.Column("domain_name", sa.String(length=255), nullable=True),
|
||||||
sa.Column('quantity', sa.Integer(), nullable=False),
|
sa.Column("quantity", sa.Integer(), nullable=False),
|
||||||
sa.Column('stripe_subscription_item_id', sa.String(length=100), nullable=True),
|
sa.Column("stripe_subscription_item_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('period_start', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("period_start", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('period_end', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("period_end", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('cancelled_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("cancelled_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['addon_product_id'], ['addon_products.id'], ),
|
sa.ForeignKeyConstraint(["addon_product_id"], ["addon_products.id"], ),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index('idx_vendor_addon_product', 'vendor_addons', ['vendor_id', 'addon_product_id'], unique=False)
|
op.create_index("idx_vendor_addon_product", "vendor_addons", ["vendor_id", "addon_product_id"], unique=False)
|
||||||
op.create_index('idx_vendor_addon_status', 'vendor_addons', ['vendor_id', 'status'], unique=False)
|
op.create_index("idx_vendor_addon_status", "vendor_addons", ["vendor_id", "status"], unique=False)
|
||||||
op.create_index(op.f('ix_vendor_addons_addon_product_id'), 'vendor_addons', ['addon_product_id'], unique=False)
|
op.create_index(op.f("ix_vendor_addons_addon_product_id"), "vendor_addons", ["addon_product_id"], unique=False)
|
||||||
op.create_index(op.f('ix_vendor_addons_domain_name'), 'vendor_addons', ['domain_name'], unique=False)
|
op.create_index(op.f("ix_vendor_addons_domain_name"), "vendor_addons", ["domain_name"], unique=False)
|
||||||
op.create_index(op.f('ix_vendor_addons_id'), 'vendor_addons', ['id'], unique=False)
|
op.create_index(op.f("ix_vendor_addons_id"), "vendor_addons", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_vendor_addons_status'), 'vendor_addons', ['status'], unique=False)
|
op.create_index(op.f("ix_vendor_addons_status"), "vendor_addons", ["status"], unique=False)
|
||||||
op.create_index(op.f('ix_vendor_addons_vendor_id'), 'vendor_addons', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_vendor_addons_vendor_id"), "vendor_addons", ["vendor_id"], unique=False)
|
||||||
|
|
||||||
# stripe_webhook_events - Webhook idempotency tracking
|
# stripe_webhook_events - Webhook idempotency tracking
|
||||||
op.create_table('stripe_webhook_events',
|
op.create_table("stripe_webhook_events",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('event_id', sa.String(length=100), nullable=False),
|
sa.Column("event_id", sa.String(length=100), nullable=False),
|
||||||
sa.Column('event_type', sa.String(length=100), nullable=False),
|
sa.Column("event_type", sa.String(length=100), nullable=False),
|
||||||
sa.Column('status', sa.String(length=20), nullable=False),
|
sa.Column("status", sa.String(length=20), nullable=False),
|
||||||
sa.Column('processed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("processed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('error_message', sa.Text(), nullable=True),
|
sa.Column("error_message", sa.Text(), nullable=True),
|
||||||
sa.Column('payload_encrypted', sa.Text(), nullable=True),
|
sa.Column("payload_encrypted", sa.Text(), nullable=True),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=True),
|
sa.Column("vendor_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('subscription_id', sa.Integer(), nullable=True),
|
sa.Column("subscription_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['subscription_id'], ['vendor_subscriptions.id'], ),
|
sa.ForeignKeyConstraint(["subscription_id"], ["vendor_subscriptions.id"], ),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index('idx_webhook_event_type_status', 'stripe_webhook_events', ['event_type', 'status'], unique=False)
|
op.create_index("idx_webhook_event_type_status", "stripe_webhook_events", ["event_type", "status"], unique=False)
|
||||||
op.create_index(op.f('ix_stripe_webhook_events_event_id'), 'stripe_webhook_events', ['event_id'], unique=True)
|
op.create_index(op.f("ix_stripe_webhook_events_event_id"), "stripe_webhook_events", ["event_id"], unique=True)
|
||||||
op.create_index(op.f('ix_stripe_webhook_events_event_type'), 'stripe_webhook_events', ['event_type'], unique=False)
|
op.create_index(op.f("ix_stripe_webhook_events_event_type"), "stripe_webhook_events", ["event_type"], unique=False)
|
||||||
op.create_index(op.f('ix_stripe_webhook_events_id'), 'stripe_webhook_events', ['id'], unique=False)
|
op.create_index(op.f("ix_stripe_webhook_events_id"), "stripe_webhook_events", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_stripe_webhook_events_status'), 'stripe_webhook_events', ['status'], unique=False)
|
op.create_index(op.f("ix_stripe_webhook_events_status"), "stripe_webhook_events", ["status"], unique=False)
|
||||||
op.create_index(op.f('ix_stripe_webhook_events_subscription_id'), 'stripe_webhook_events', ['subscription_id'], unique=False)
|
op.create_index(op.f("ix_stripe_webhook_events_subscription_id"), "stripe_webhook_events", ["subscription_id"], unique=False)
|
||||||
op.create_index(op.f('ix_stripe_webhook_events_vendor_id'), 'stripe_webhook_events', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_stripe_webhook_events_vendor_id"), "stripe_webhook_events", ["vendor_id"], unique=False)
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Add new columns to vendor_subscriptions
|
# Add new columns to vendor_subscriptions
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
op.add_column('vendor_subscriptions', sa.Column('stripe_price_id', sa.String(length=100), nullable=True))
|
op.add_column("vendor_subscriptions", sa.Column("stripe_price_id", sa.String(length=100), nullable=True))
|
||||||
op.add_column('vendor_subscriptions', sa.Column('stripe_payment_method_id', sa.String(length=100), nullable=True))
|
op.add_column("vendor_subscriptions", sa.Column("stripe_payment_method_id", sa.String(length=100), nullable=True))
|
||||||
op.add_column('vendor_subscriptions', sa.Column('proration_behavior', sa.String(length=50), nullable=True))
|
op.add_column("vendor_subscriptions", sa.Column("proration_behavior", sa.String(length=50), nullable=True))
|
||||||
op.add_column('vendor_subscriptions', sa.Column('scheduled_tier_change', sa.String(length=30), nullable=True))
|
op.add_column("vendor_subscriptions", sa.Column("scheduled_tier_change", sa.String(length=30), nullable=True))
|
||||||
op.add_column('vendor_subscriptions', sa.Column('scheduled_change_at', sa.DateTime(timezone=True), nullable=True))
|
op.add_column("vendor_subscriptions", sa.Column("scheduled_change_at", sa.DateTime(timezone=True), nullable=True))
|
||||||
op.add_column('vendor_subscriptions', sa.Column('payment_retry_count', sa.Integer(), server_default='0', nullable=False))
|
op.add_column("vendor_subscriptions", sa.Column("payment_retry_count", sa.Integer(), server_default="0", nullable=False))
|
||||||
op.add_column('vendor_subscriptions', sa.Column('last_payment_error', sa.Text(), nullable=True))
|
op.add_column("vendor_subscriptions", sa.Column("last_payment_error", sa.Text(), nullable=True))
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Seed subscription tiers
|
# Seed subscription tiers
|
||||||
@@ -172,106 +174,106 @@ def upgrade() -> None:
|
|||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
|
|
||||||
subscription_tiers = sa.table(
|
subscription_tiers = sa.table(
|
||||||
'subscription_tiers',
|
"subscription_tiers",
|
||||||
sa.column('code', sa.String),
|
sa.column("code", sa.String),
|
||||||
sa.column('name', sa.String),
|
sa.column("name", sa.String),
|
||||||
sa.column('description', sa.Text),
|
sa.column("description", sa.Text),
|
||||||
sa.column('price_monthly_cents', sa.Integer),
|
sa.column("price_monthly_cents", sa.Integer),
|
||||||
sa.column('price_annual_cents', sa.Integer),
|
sa.column("price_annual_cents", sa.Integer),
|
||||||
sa.column('orders_per_month', sa.Integer),
|
sa.column("orders_per_month", sa.Integer),
|
||||||
sa.column('products_limit', sa.Integer),
|
sa.column("products_limit", sa.Integer),
|
||||||
sa.column('team_members', sa.Integer),
|
sa.column("team_members", sa.Integer),
|
||||||
sa.column('order_history_months', sa.Integer),
|
sa.column("order_history_months", sa.Integer),
|
||||||
sa.column('features', sa.JSON),
|
sa.column("features", sa.JSON),
|
||||||
sa.column('display_order', sa.Integer),
|
sa.column("display_order", sa.Integer),
|
||||||
sa.column('is_active', sa.Boolean),
|
sa.column("is_active", sa.Boolean),
|
||||||
sa.column('is_public', sa.Boolean),
|
sa.column("is_public", sa.Boolean),
|
||||||
sa.column('created_at', sa.DateTime),
|
sa.column("created_at", sa.DateTime),
|
||||||
sa.column('updated_at', sa.DateTime),
|
sa.column("updated_at", sa.DateTime),
|
||||||
)
|
)
|
||||||
|
|
||||||
op.bulk_insert(subscription_tiers, [
|
op.bulk_insert(subscription_tiers, [
|
||||||
{
|
{
|
||||||
'code': 'essential',
|
"code": "essential",
|
||||||
'name': 'Essential',
|
"name": "Essential",
|
||||||
'description': 'Perfect for solo vendors getting started with Letzshop',
|
"description": "Perfect for solo vendors getting started with Letzshop",
|
||||||
'price_monthly_cents': 4900,
|
"price_monthly_cents": 4900,
|
||||||
'price_annual_cents': 49000,
|
"price_annual_cents": 49000,
|
||||||
'orders_per_month': 100,
|
"orders_per_month": 100,
|
||||||
'products_limit': 200,
|
"products_limit": 200,
|
||||||
'team_members': 1,
|
"team_members": 1,
|
||||||
'order_history_months': 6,
|
"order_history_months": 6,
|
||||||
'features': ['letzshop_sync', 'inventory_basic', 'invoice_lu', 'customer_view'],
|
"features": ["letzshop_sync", "inventory_basic", "invoice_lu", "customer_view"],
|
||||||
'display_order': 1,
|
"display_order": 1,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'is_public': True,
|
"is_public": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'code': 'professional',
|
"code": "professional",
|
||||||
'name': 'Professional',
|
"name": "Professional",
|
||||||
'description': 'For active multi-channel vendors shipping EU-wide',
|
"description": "For active multi-channel vendors shipping EU-wide",
|
||||||
'price_monthly_cents': 9900,
|
"price_monthly_cents": 9900,
|
||||||
'price_annual_cents': 99000,
|
"price_annual_cents": 99000,
|
||||||
'orders_per_month': 500,
|
"orders_per_month": 500,
|
||||||
'products_limit': None,
|
"products_limit": None,
|
||||||
'team_members': 3,
|
"team_members": 3,
|
||||||
'order_history_months': 24,
|
"order_history_months": 24,
|
||||||
'features': [
|
"features": [
|
||||||
'letzshop_sync', 'inventory_locations', 'inventory_purchase_orders',
|
"letzshop_sync", "inventory_locations", "inventory_purchase_orders",
|
||||||
'invoice_lu', 'invoice_eu_vat', 'customer_view', 'customer_export'
|
"invoice_lu", "invoice_eu_vat", "customer_view", "customer_export"
|
||||||
],
|
],
|
||||||
'display_order': 2,
|
"display_order": 2,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'is_public': True,
|
"is_public": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'code': 'business',
|
"code": "business",
|
||||||
'name': 'Business',
|
"name": "Business",
|
||||||
'description': 'For high-volume vendors with teams and data-driven operations',
|
"description": "For high-volume vendors with teams and data-driven operations",
|
||||||
'price_monthly_cents': 19900,
|
"price_monthly_cents": 19900,
|
||||||
'price_annual_cents': 199000,
|
"price_annual_cents": 199000,
|
||||||
'orders_per_month': 2000,
|
"orders_per_month": 2000,
|
||||||
'products_limit': None,
|
"products_limit": None,
|
||||||
'team_members': 10,
|
"team_members": 10,
|
||||||
'order_history_months': None,
|
"order_history_months": None,
|
||||||
'features': [
|
"features": [
|
||||||
'letzshop_sync', 'inventory_locations', 'inventory_purchase_orders',
|
"letzshop_sync", "inventory_locations", "inventory_purchase_orders",
|
||||||
'invoice_lu', 'invoice_eu_vat', 'invoice_bulk', 'customer_view',
|
"invoice_lu", "invoice_eu_vat", "invoice_bulk", "customer_view",
|
||||||
'customer_export', 'analytics_dashboard', 'accounting_export',
|
"customer_export", "analytics_dashboard", "accounting_export",
|
||||||
'api_access', 'automation_rules', 'team_roles'
|
"api_access", "automation_rules", "team_roles"
|
||||||
],
|
],
|
||||||
'display_order': 3,
|
"display_order": 3,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'is_public': True,
|
"is_public": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'code': 'enterprise',
|
"code": "enterprise",
|
||||||
'name': 'Enterprise',
|
"name": "Enterprise",
|
||||||
'description': 'Custom solutions for large operations and agencies',
|
"description": "Custom solutions for large operations and agencies",
|
||||||
'price_monthly_cents': 39900,
|
"price_monthly_cents": 39900,
|
||||||
'price_annual_cents': None,
|
"price_annual_cents": None,
|
||||||
'orders_per_month': None,
|
"orders_per_month": None,
|
||||||
'products_limit': None,
|
"products_limit": None,
|
||||||
'team_members': None,
|
"team_members": None,
|
||||||
'order_history_months': None,
|
"order_history_months": None,
|
||||||
'features': [
|
"features": [
|
||||||
'letzshop_sync', 'inventory_locations', 'inventory_purchase_orders',
|
"letzshop_sync", "inventory_locations", "inventory_purchase_orders",
|
||||||
'invoice_lu', 'invoice_eu_vat', 'invoice_bulk', 'customer_view',
|
"invoice_lu", "invoice_eu_vat", "invoice_bulk", "customer_view",
|
||||||
'customer_export', 'analytics_dashboard', 'accounting_export',
|
"customer_export", "analytics_dashboard", "accounting_export",
|
||||||
'api_access', 'automation_rules', 'team_roles', 'white_label',
|
"api_access", "automation_rules", "team_roles", "white_label",
|
||||||
'multi_vendor', 'custom_integrations', 'sla_guarantee', 'dedicated_support'
|
"multi_vendor", "custom_integrations", "sla_guarantee", "dedicated_support"
|
||||||
],
|
],
|
||||||
'display_order': 4,
|
"display_order": 4,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'is_public': False,
|
"is_public": False,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -279,141 +281,141 @@ def upgrade() -> None:
|
|||||||
# Seed add-on products
|
# Seed add-on products
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
addon_products = sa.table(
|
addon_products = sa.table(
|
||||||
'addon_products',
|
"addon_products",
|
||||||
sa.column('code', sa.String),
|
sa.column("code", sa.String),
|
||||||
sa.column('name', sa.String),
|
sa.column("name", sa.String),
|
||||||
sa.column('description', sa.Text),
|
sa.column("description", sa.Text),
|
||||||
sa.column('category', sa.String),
|
sa.column("category", sa.String),
|
||||||
sa.column('price_cents', sa.Integer),
|
sa.column("price_cents", sa.Integer),
|
||||||
sa.column('billing_period', sa.String),
|
sa.column("billing_period", sa.String),
|
||||||
sa.column('quantity_unit', sa.String),
|
sa.column("quantity_unit", sa.String),
|
||||||
sa.column('quantity_value', sa.Integer),
|
sa.column("quantity_value", sa.Integer),
|
||||||
sa.column('display_order', sa.Integer),
|
sa.column("display_order", sa.Integer),
|
||||||
sa.column('is_active', sa.Boolean),
|
sa.column("is_active", sa.Boolean),
|
||||||
sa.column('created_at', sa.DateTime),
|
sa.column("created_at", sa.DateTime),
|
||||||
sa.column('updated_at', sa.DateTime),
|
sa.column("updated_at", sa.DateTime),
|
||||||
)
|
)
|
||||||
|
|
||||||
op.bulk_insert(addon_products, [
|
op.bulk_insert(addon_products, [
|
||||||
{
|
{
|
||||||
'code': 'domain',
|
"code": "domain",
|
||||||
'name': 'Custom Domain',
|
"name": "Custom Domain",
|
||||||
'description': 'Connect your own domain with SSL certificate included',
|
"description": "Connect your own domain with SSL certificate included",
|
||||||
'category': 'domain',
|
"category": "domain",
|
||||||
'price_cents': 1500,
|
"price_cents": 1500,
|
||||||
'billing_period': 'annual',
|
"billing_period": "annual",
|
||||||
'quantity_unit': None,
|
"quantity_unit": None,
|
||||||
'quantity_value': None,
|
"quantity_value": None,
|
||||||
'display_order': 1,
|
"display_order": 1,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'code': 'email_5',
|
"code": "email_5",
|
||||||
'name': '5 Email Addresses',
|
"name": "5 Email Addresses",
|
||||||
'description': 'Professional email addresses on your domain',
|
"description": "Professional email addresses on your domain",
|
||||||
'category': 'email',
|
"category": "email",
|
||||||
'price_cents': 500,
|
"price_cents": 500,
|
||||||
'billing_period': 'monthly',
|
"billing_period": "monthly",
|
||||||
'quantity_unit': 'emails',
|
"quantity_unit": "emails",
|
||||||
'quantity_value': 5,
|
"quantity_value": 5,
|
||||||
'display_order': 2,
|
"display_order": 2,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'code': 'email_10',
|
"code": "email_10",
|
||||||
'name': '10 Email Addresses',
|
"name": "10 Email Addresses",
|
||||||
'description': 'Professional email addresses on your domain',
|
"description": "Professional email addresses on your domain",
|
||||||
'category': 'email',
|
"category": "email",
|
||||||
'price_cents': 900,
|
"price_cents": 900,
|
||||||
'billing_period': 'monthly',
|
"billing_period": "monthly",
|
||||||
'quantity_unit': 'emails',
|
"quantity_unit": "emails",
|
||||||
'quantity_value': 10,
|
"quantity_value": 10,
|
||||||
'display_order': 3,
|
"display_order": 3,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'code': 'email_25',
|
"code": "email_25",
|
||||||
'name': '25 Email Addresses',
|
"name": "25 Email Addresses",
|
||||||
'description': 'Professional email addresses on your domain',
|
"description": "Professional email addresses on your domain",
|
||||||
'category': 'email',
|
"category": "email",
|
||||||
'price_cents': 1900,
|
"price_cents": 1900,
|
||||||
'billing_period': 'monthly',
|
"billing_period": "monthly",
|
||||||
'quantity_unit': 'emails',
|
"quantity_unit": "emails",
|
||||||
'quantity_value': 25,
|
"quantity_value": 25,
|
||||||
'display_order': 4,
|
"display_order": 4,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'code': 'storage_10gb',
|
"code": "storage_10gb",
|
||||||
'name': 'Additional Storage (10GB)',
|
"name": "Additional Storage (10GB)",
|
||||||
'description': 'Extra storage for product images and files',
|
"description": "Extra storage for product images and files",
|
||||||
'category': 'storage',
|
"category": "storage",
|
||||||
'price_cents': 500,
|
"price_cents": 500,
|
||||||
'billing_period': 'monthly',
|
"billing_period": "monthly",
|
||||||
'quantity_unit': 'GB',
|
"quantity_unit": "GB",
|
||||||
'quantity_value': 10,
|
"quantity_value": 10,
|
||||||
'display_order': 5,
|
"display_order": 5,
|
||||||
'is_active': True,
|
"is_active": True,
|
||||||
'created_at': now,
|
"created_at": now,
|
||||||
'updated_at': now,
|
"updated_at": now,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Remove new columns from vendor_subscriptions
|
# Remove new columns from vendor_subscriptions
|
||||||
op.drop_column('vendor_subscriptions', 'last_payment_error')
|
op.drop_column("vendor_subscriptions", "last_payment_error")
|
||||||
op.drop_column('vendor_subscriptions', 'payment_retry_count')
|
op.drop_column("vendor_subscriptions", "payment_retry_count")
|
||||||
op.drop_column('vendor_subscriptions', 'scheduled_change_at')
|
op.drop_column("vendor_subscriptions", "scheduled_change_at")
|
||||||
op.drop_column('vendor_subscriptions', 'scheduled_tier_change')
|
op.drop_column("vendor_subscriptions", "scheduled_tier_change")
|
||||||
op.drop_column('vendor_subscriptions', 'proration_behavior')
|
op.drop_column("vendor_subscriptions", "proration_behavior")
|
||||||
op.drop_column('vendor_subscriptions', 'stripe_payment_method_id')
|
op.drop_column("vendor_subscriptions", "stripe_payment_method_id")
|
||||||
op.drop_column('vendor_subscriptions', 'stripe_price_id')
|
op.drop_column("vendor_subscriptions", "stripe_price_id")
|
||||||
|
|
||||||
# Drop stripe_webhook_events
|
# Drop stripe_webhook_events
|
||||||
op.drop_index(op.f('ix_stripe_webhook_events_vendor_id'), table_name='stripe_webhook_events')
|
op.drop_index(op.f("ix_stripe_webhook_events_vendor_id"), table_name="stripe_webhook_events")
|
||||||
op.drop_index(op.f('ix_stripe_webhook_events_subscription_id'), table_name='stripe_webhook_events')
|
op.drop_index(op.f("ix_stripe_webhook_events_subscription_id"), table_name="stripe_webhook_events")
|
||||||
op.drop_index(op.f('ix_stripe_webhook_events_status'), table_name='stripe_webhook_events')
|
op.drop_index(op.f("ix_stripe_webhook_events_status"), table_name="stripe_webhook_events")
|
||||||
op.drop_index(op.f('ix_stripe_webhook_events_id'), table_name='stripe_webhook_events')
|
op.drop_index(op.f("ix_stripe_webhook_events_id"), table_name="stripe_webhook_events")
|
||||||
op.drop_index(op.f('ix_stripe_webhook_events_event_type'), table_name='stripe_webhook_events')
|
op.drop_index(op.f("ix_stripe_webhook_events_event_type"), table_name="stripe_webhook_events")
|
||||||
op.drop_index(op.f('ix_stripe_webhook_events_event_id'), table_name='stripe_webhook_events')
|
op.drop_index(op.f("ix_stripe_webhook_events_event_id"), table_name="stripe_webhook_events")
|
||||||
op.drop_index('idx_webhook_event_type_status', table_name='stripe_webhook_events')
|
op.drop_index("idx_webhook_event_type_status", table_name="stripe_webhook_events")
|
||||||
op.drop_table('stripe_webhook_events')
|
op.drop_table("stripe_webhook_events")
|
||||||
|
|
||||||
# Drop vendor_addons
|
# Drop vendor_addons
|
||||||
op.drop_index(op.f('ix_vendor_addons_vendor_id'), table_name='vendor_addons')
|
op.drop_index(op.f("ix_vendor_addons_vendor_id"), table_name="vendor_addons")
|
||||||
op.drop_index(op.f('ix_vendor_addons_status'), table_name='vendor_addons')
|
op.drop_index(op.f("ix_vendor_addons_status"), table_name="vendor_addons")
|
||||||
op.drop_index(op.f('ix_vendor_addons_id'), table_name='vendor_addons')
|
op.drop_index(op.f("ix_vendor_addons_id"), table_name="vendor_addons")
|
||||||
op.drop_index(op.f('ix_vendor_addons_domain_name'), table_name='vendor_addons')
|
op.drop_index(op.f("ix_vendor_addons_domain_name"), table_name="vendor_addons")
|
||||||
op.drop_index(op.f('ix_vendor_addons_addon_product_id'), table_name='vendor_addons')
|
op.drop_index(op.f("ix_vendor_addons_addon_product_id"), table_name="vendor_addons")
|
||||||
op.drop_index('idx_vendor_addon_status', table_name='vendor_addons')
|
op.drop_index("idx_vendor_addon_status", table_name="vendor_addons")
|
||||||
op.drop_index('idx_vendor_addon_product', table_name='vendor_addons')
|
op.drop_index("idx_vendor_addon_product", table_name="vendor_addons")
|
||||||
op.drop_table('vendor_addons')
|
op.drop_table("vendor_addons")
|
||||||
|
|
||||||
# Drop billing_history
|
# Drop billing_history
|
||||||
op.drop_index(op.f('ix_billing_history_vendor_id'), table_name='billing_history')
|
op.drop_index(op.f("ix_billing_history_vendor_id"), table_name="billing_history")
|
||||||
op.drop_index(op.f('ix_billing_history_stripe_invoice_id'), table_name='billing_history')
|
op.drop_index(op.f("ix_billing_history_stripe_invoice_id"), table_name="billing_history")
|
||||||
op.drop_index(op.f('ix_billing_history_status'), table_name='billing_history')
|
op.drop_index(op.f("ix_billing_history_status"), table_name="billing_history")
|
||||||
op.drop_index(op.f('ix_billing_history_id'), table_name='billing_history')
|
op.drop_index(op.f("ix_billing_history_id"), table_name="billing_history")
|
||||||
op.drop_index('idx_billing_vendor_date', table_name='billing_history')
|
op.drop_index("idx_billing_vendor_date", table_name="billing_history")
|
||||||
op.drop_index('idx_billing_status', table_name='billing_history')
|
op.drop_index("idx_billing_status", table_name="billing_history")
|
||||||
op.drop_table('billing_history')
|
op.drop_table("billing_history")
|
||||||
|
|
||||||
# Drop addon_products
|
# Drop addon_products
|
||||||
op.drop_index(op.f('ix_addon_products_id'), table_name='addon_products')
|
op.drop_index(op.f("ix_addon_products_id"), table_name="addon_products")
|
||||||
op.drop_index(op.f('ix_addon_products_code'), table_name='addon_products')
|
op.drop_index(op.f("ix_addon_products_code"), table_name="addon_products")
|
||||||
op.drop_index(op.f('ix_addon_products_category'), table_name='addon_products')
|
op.drop_index(op.f("ix_addon_products_category"), table_name="addon_products")
|
||||||
op.drop_table('addon_products')
|
op.drop_table("addon_products")
|
||||||
|
|
||||||
# Drop subscription_tiers
|
# Drop subscription_tiers
|
||||||
op.drop_index(op.f('ix_subscription_tiers_id'), table_name='subscription_tiers')
|
op.drop_index(op.f("ix_subscription_tiers_id"), table_name="subscription_tiers")
|
||||||
op.drop_index(op.f('ix_subscription_tiers_code'), table_name='subscription_tiers')
|
op.drop_index(op.f("ix_subscription_tiers_code"), table_name="subscription_tiers")
|
||||||
op.drop_table('subscription_tiers')
|
op.drop_table("subscription_tiers")
|
||||||
|
|||||||
@@ -9,36 +9,36 @@ Adds:
|
|||||||
- vendors.letzshop_vendor_slug - Letzshop shop URL slug
|
- vendors.letzshop_vendor_slug - Letzshop shop URL slug
|
||||||
- vendor_subscriptions.card_collected_at - Track when card was collected for trial
|
- vendor_subscriptions.card_collected_at - Track when card was collected for trial
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '404b3e2d2865'
|
revision: str = "404b3e2d2865"
|
||||||
down_revision: Union[str, None] = 'l0a1b2c3d4e5'
|
down_revision: str | None = "l0a1b2c3d4e5"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add Letzshop vendor identity fields to vendors table
|
# Add Letzshop vendor identity fields to vendors table
|
||||||
op.add_column('vendors', sa.Column('letzshop_vendor_id', sa.String(length=100), nullable=True))
|
op.add_column("vendors", sa.Column("letzshop_vendor_id", sa.String(length=100), nullable=True))
|
||||||
op.add_column('vendors', sa.Column('letzshop_vendor_slug', sa.String(length=200), nullable=True))
|
op.add_column("vendors", sa.Column("letzshop_vendor_slug", sa.String(length=200), nullable=True))
|
||||||
op.create_index(op.f('ix_vendors_letzshop_vendor_id'), 'vendors', ['letzshop_vendor_id'], unique=True)
|
op.create_index(op.f("ix_vendors_letzshop_vendor_id"), "vendors", ["letzshop_vendor_id"], unique=True)
|
||||||
op.create_index(op.f('ix_vendors_letzshop_vendor_slug'), 'vendors', ['letzshop_vendor_slug'], unique=False)
|
op.create_index(op.f("ix_vendors_letzshop_vendor_slug"), "vendors", ["letzshop_vendor_slug"], unique=False)
|
||||||
|
|
||||||
# Add card collection tracking to vendor_subscriptions
|
# Add card collection tracking to vendor_subscriptions
|
||||||
op.add_column('vendor_subscriptions', sa.Column('card_collected_at', sa.DateTime(timezone=True), nullable=True))
|
op.add_column("vendor_subscriptions", sa.Column("card_collected_at", sa.DateTime(timezone=True), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Remove card collection tracking from vendor_subscriptions
|
# Remove card collection tracking from vendor_subscriptions
|
||||||
op.drop_column('vendor_subscriptions', 'card_collected_at')
|
op.drop_column("vendor_subscriptions", "card_collected_at")
|
||||||
|
|
||||||
# Remove Letzshop vendor identity fields from vendors
|
# Remove Letzshop vendor identity fields from vendors
|
||||||
op.drop_index(op.f('ix_vendors_letzshop_vendor_slug'), table_name='vendors')
|
op.drop_index(op.f("ix_vendors_letzshop_vendor_slug"), table_name="vendors")
|
||||||
op.drop_index(op.f('ix_vendors_letzshop_vendor_id'), table_name='vendors')
|
op.drop_index(op.f("ix_vendors_letzshop_vendor_id"), table_name="vendors")
|
||||||
op.drop_column('vendors', 'letzshop_vendor_slug')
|
op.drop_column("vendors", "letzshop_vendor_slug")
|
||||||
op.drop_column('vendors', 'letzshop_vendor_id')
|
op.drop_column("vendors", "letzshop_vendor_id")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Create Date: 2025-10-27 22:28:33.137564
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@@ -14,9 +14,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "4951b2e50581"
|
revision: str = "4951b2e50581"
|
||||||
down_revision: Union[str, None] = None
|
down_revision: str | None = None
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -5,27 +5,27 @@ Revises: d2e3f4a5b6c7
|
|||||||
Create Date: 2025-12-20 18:07:51.144136
|
Create Date: 2025-12-20 18:07:51.144136
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '55b92e155566'
|
revision: str = "55b92e155566"
|
||||||
down_revision: Union[str, None] = 'd2e3f4a5b6c7'
|
down_revision: str | None = "d2e3f4a5b6c7"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add new tracking fields to orders table
|
# Add new tracking fields to orders table
|
||||||
op.add_column('orders', sa.Column('tracking_url', sa.String(length=500), nullable=True))
|
op.add_column("orders", sa.Column("tracking_url", sa.String(length=500), nullable=True))
|
||||||
op.add_column('orders', sa.Column('shipment_number', sa.String(length=100), nullable=True))
|
op.add_column("orders", sa.Column("shipment_number", sa.String(length=100), nullable=True))
|
||||||
op.add_column('orders', sa.Column('shipping_carrier', sa.String(length=50), nullable=True))
|
op.add_column("orders", sa.Column("shipping_carrier", sa.String(length=50), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column('orders', 'shipping_carrier')
|
op.drop_column("orders", "shipping_carrier")
|
||||||
op.drop_column('orders', 'shipment_number')
|
op.drop_column("orders", "shipment_number")
|
||||||
op.drop_column('orders', 'tracking_url')
|
op.drop_column("orders", "tracking_url")
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ Revises: d0325d7c0f25
|
|||||||
Create Date: 2025-12-01 20:30:06.158027
|
Create Date: 2025-12-01 20:30:06.158027
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '5818330181a5'
|
revision: str = "5818330181a5"
|
||||||
down_revision: Union[str, None] = 'd0325d7c0f25'
|
down_revision: str | None = "d0325d7c0f25"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
@@ -29,8 +29,8 @@ def upgrade() -> None:
|
|||||||
This allows one company owner to manage multiple vendor brands.
|
This allows one company owner to manage multiple vendor brands.
|
||||||
"""
|
"""
|
||||||
# Use batch operations for SQLite compatibility
|
# Use batch operations for SQLite compatibility
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
batch_op.alter_column('owner_user_id',
|
batch_op.alter_column("owner_user_id",
|
||||||
existing_type=sa.INTEGER(),
|
existing_type=sa.INTEGER(),
|
||||||
nullable=True)
|
nullable=True)
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ def downgrade() -> None:
|
|||||||
WARNING: This will fail if there are vendors without owner_user_id!
|
WARNING: This will fail if there are vendors without owner_user_id!
|
||||||
"""
|
"""
|
||||||
# Use batch operations for SQLite compatibility
|
# Use batch operations for SQLite compatibility
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
batch_op.alter_column('owner_user_id',
|
batch_op.alter_column("owner_user_id",
|
||||||
existing_type=sa.INTEGER(),
|
existing_type=sa.INTEGER(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Create Date: 2025-11-22 15:16:13.213613
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@@ -14,9 +14,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "72aa309d4007"
|
revision: str = "72aa309d4007"
|
||||||
down_revision: Union[str, None] = "fef1d20ce8b4"
|
down_revision: str | None = "fef1d20ce8b4"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -6,18 +6,17 @@ Create Date: 2025-11-28 09:21:16.545203
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy.dialects import postgresql
|
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "7a7ce92593d5"
|
revision: str = "7a7ce92593d5"
|
||||||
down_revision: Union[str, None] = "a2064e1dfcd4"
|
down_revision: str | None = "a2064e1dfcd4"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -5,99 +5,99 @@ Revises: b4c5d6e7f8a9
|
|||||||
Create Date: 2025-12-12 22:48:09.501172
|
Create Date: 2025-12-12 22:48:09.501172
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '82ea1b4a3ccb'
|
revision: str = "82ea1b4a3ccb"
|
||||||
down_revision: Union[str, None] = 'b4c5d6e7f8a9'
|
down_revision: str | None = "b4c5d6e7f8a9"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Create test_collections table
|
# Create test_collections table
|
||||||
op.create_table('test_collections',
|
op.create_table("test_collections",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('total_tests', sa.Integer(), nullable=True),
|
sa.Column("total_tests", sa.Integer(), nullable=True),
|
||||||
sa.Column('total_files', sa.Integer(), nullable=True),
|
sa.Column("total_files", sa.Integer(), nullable=True),
|
||||||
sa.Column('total_classes', sa.Integer(), nullable=True),
|
sa.Column("total_classes", sa.Integer(), nullable=True),
|
||||||
sa.Column('unit_tests', sa.Integer(), nullable=True),
|
sa.Column("unit_tests", sa.Integer(), nullable=True),
|
||||||
sa.Column('integration_tests', sa.Integer(), nullable=True),
|
sa.Column("integration_tests", sa.Integer(), nullable=True),
|
||||||
sa.Column('performance_tests', sa.Integer(), nullable=True),
|
sa.Column("performance_tests", sa.Integer(), nullable=True),
|
||||||
sa.Column('system_tests', sa.Integer(), nullable=True),
|
sa.Column("system_tests", sa.Integer(), nullable=True),
|
||||||
sa.Column('test_files', sa.JSON(), nullable=True),
|
sa.Column("test_files", sa.JSON(), nullable=True),
|
||||||
sa.Column('collected_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("collected_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_test_collections_id'), 'test_collections', ['id'], unique=False)
|
op.create_index(op.f("ix_test_collections_id"), "test_collections", ["id"], unique=False)
|
||||||
|
|
||||||
# Create test_runs table
|
# Create test_runs table
|
||||||
op.create_table('test_runs',
|
op.create_table("test_runs",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('timestamp', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("timestamp", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('total_tests', sa.Integer(), nullable=True),
|
sa.Column("total_tests", sa.Integer(), nullable=True),
|
||||||
sa.Column('passed', sa.Integer(), nullable=True),
|
sa.Column("passed", sa.Integer(), nullable=True),
|
||||||
sa.Column('failed', sa.Integer(), nullable=True),
|
sa.Column("failed", sa.Integer(), nullable=True),
|
||||||
sa.Column('errors', sa.Integer(), nullable=True),
|
sa.Column("errors", sa.Integer(), nullable=True),
|
||||||
sa.Column('skipped', sa.Integer(), nullable=True),
|
sa.Column("skipped", sa.Integer(), nullable=True),
|
||||||
sa.Column('xfailed', sa.Integer(), nullable=True),
|
sa.Column("xfailed", sa.Integer(), nullable=True),
|
||||||
sa.Column('xpassed', sa.Integer(), nullable=True),
|
sa.Column("xpassed", sa.Integer(), nullable=True),
|
||||||
sa.Column('coverage_percent', sa.Float(), nullable=True),
|
sa.Column("coverage_percent", sa.Float(), nullable=True),
|
||||||
sa.Column('duration_seconds', sa.Float(), nullable=True),
|
sa.Column("duration_seconds", sa.Float(), nullable=True),
|
||||||
sa.Column('triggered_by', sa.String(length=100), nullable=True),
|
sa.Column("triggered_by", sa.String(length=100), nullable=True),
|
||||||
sa.Column('git_commit_hash', sa.String(length=40), nullable=True),
|
sa.Column("git_commit_hash", sa.String(length=40), nullable=True),
|
||||||
sa.Column('git_branch', sa.String(length=100), nullable=True),
|
sa.Column("git_branch", sa.String(length=100), nullable=True),
|
||||||
sa.Column('test_path', sa.String(length=500), nullable=True),
|
sa.Column("test_path", sa.String(length=500), nullable=True),
|
||||||
sa.Column('pytest_args', sa.String(length=500), nullable=True),
|
sa.Column("pytest_args", sa.String(length=500), nullable=True),
|
||||||
sa.Column('status', sa.String(length=20), nullable=True),
|
sa.Column("status", sa.String(length=20), nullable=True),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_test_runs_id'), 'test_runs', ['id'], unique=False)
|
op.create_index(op.f("ix_test_runs_id"), "test_runs", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_test_runs_status'), 'test_runs', ['status'], unique=False)
|
op.create_index(op.f("ix_test_runs_status"), "test_runs", ["status"], unique=False)
|
||||||
op.create_index(op.f('ix_test_runs_timestamp'), 'test_runs', ['timestamp'], unique=False)
|
op.create_index(op.f("ix_test_runs_timestamp"), "test_runs", ["timestamp"], unique=False)
|
||||||
|
|
||||||
# Create test_results table
|
# Create test_results table
|
||||||
op.create_table('test_results',
|
op.create_table("test_results",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('run_id', sa.Integer(), nullable=False),
|
sa.Column("run_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('node_id', sa.String(length=500), nullable=False),
|
sa.Column("node_id", sa.String(length=500), nullable=False),
|
||||||
sa.Column('test_name', sa.String(length=200), nullable=False),
|
sa.Column("test_name", sa.String(length=200), nullable=False),
|
||||||
sa.Column('test_file', sa.String(length=300), nullable=False),
|
sa.Column("test_file", sa.String(length=300), nullable=False),
|
||||||
sa.Column('test_class', sa.String(length=200), nullable=True),
|
sa.Column("test_class", sa.String(length=200), nullable=True),
|
||||||
sa.Column('outcome', sa.String(length=20), nullable=False),
|
sa.Column("outcome", sa.String(length=20), nullable=False),
|
||||||
sa.Column('duration_seconds', sa.Float(), nullable=True),
|
sa.Column("duration_seconds", sa.Float(), nullable=True),
|
||||||
sa.Column('error_message', sa.Text(), nullable=True),
|
sa.Column("error_message", sa.Text(), nullable=True),
|
||||||
sa.Column('traceback', sa.Text(), nullable=True),
|
sa.Column("traceback", sa.Text(), nullable=True),
|
||||||
sa.Column('markers', sa.JSON(), nullable=True),
|
sa.Column("markers", sa.JSON(), nullable=True),
|
||||||
sa.Column('parameters', sa.JSON(), nullable=True),
|
sa.Column("parameters", sa.JSON(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['run_id'], ['test_runs.id'], ),
|
sa.ForeignKeyConstraint(["run_id"], ["test_runs.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_test_results_id'), 'test_results', ['id'], unique=False)
|
op.create_index(op.f("ix_test_results_id"), "test_results", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_test_results_node_id'), 'test_results', ['node_id'], unique=False)
|
op.create_index(op.f("ix_test_results_node_id"), "test_results", ["node_id"], unique=False)
|
||||||
op.create_index(op.f('ix_test_results_outcome'), 'test_results', ['outcome'], unique=False)
|
op.create_index(op.f("ix_test_results_outcome"), "test_results", ["outcome"], unique=False)
|
||||||
op.create_index(op.f('ix_test_results_run_id'), 'test_results', ['run_id'], unique=False)
|
op.create_index(op.f("ix_test_results_run_id"), "test_results", ["run_id"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Drop test_results table first (has foreign key to test_runs)
|
# Drop test_results table first (has foreign key to test_runs)
|
||||||
op.drop_index(op.f('ix_test_results_run_id'), table_name='test_results')
|
op.drop_index(op.f("ix_test_results_run_id"), table_name="test_results")
|
||||||
op.drop_index(op.f('ix_test_results_outcome'), table_name='test_results')
|
op.drop_index(op.f("ix_test_results_outcome"), table_name="test_results")
|
||||||
op.drop_index(op.f('ix_test_results_node_id'), table_name='test_results')
|
op.drop_index(op.f("ix_test_results_node_id"), table_name="test_results")
|
||||||
op.drop_index(op.f('ix_test_results_id'), table_name='test_results')
|
op.drop_index(op.f("ix_test_results_id"), table_name="test_results")
|
||||||
op.drop_table('test_results')
|
op.drop_table("test_results")
|
||||||
|
|
||||||
# Drop test_runs table
|
# Drop test_runs table
|
||||||
op.drop_index(op.f('ix_test_runs_timestamp'), table_name='test_runs')
|
op.drop_index(op.f("ix_test_runs_timestamp"), table_name="test_runs")
|
||||||
op.drop_index(op.f('ix_test_runs_status'), table_name='test_runs')
|
op.drop_index(op.f("ix_test_runs_status"), table_name="test_runs")
|
||||||
op.drop_index(op.f('ix_test_runs_id'), table_name='test_runs')
|
op.drop_index(op.f("ix_test_runs_id"), table_name="test_runs")
|
||||||
op.drop_table('test_runs')
|
op.drop_table("test_runs")
|
||||||
|
|
||||||
# Drop test_collections table
|
# Drop test_collections table
|
||||||
op.drop_index(op.f('ix_test_collections_id'), table_name='test_collections')
|
op.drop_index(op.f("ix_test_collections_id"), table_name="test_collections")
|
||||||
op.drop_table('test_collections')
|
op.drop_table("test_collections")
|
||||||
|
|||||||
@@ -5,40 +5,41 @@ Revises: 987b4ecfa503
|
|||||||
Create Date: 2025-12-13 13:13:46.969503
|
Create Date: 2025-12-13 13:13:46.969503
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '91d02647efae'
|
revision: str = "91d02647efae"
|
||||||
down_revision: Union[str, None] = '987b4ecfa503'
|
down_revision: str | None = "987b4ecfa503"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Create marketplace_import_errors table to store detailed import error information
|
# Create marketplace_import_errors table to store detailed import error information
|
||||||
op.create_table('marketplace_import_errors',
|
op.create_table("marketplace_import_errors",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('import_job_id', sa.Integer(), nullable=False),
|
sa.Column("import_job_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('row_number', sa.Integer(), nullable=False),
|
sa.Column("row_number", sa.Integer(), nullable=False),
|
||||||
sa.Column('identifier', sa.String(), nullable=True),
|
sa.Column("identifier", sa.String(), nullable=True),
|
||||||
sa.Column('error_type', sa.String(length=50), nullable=False),
|
sa.Column("error_type", sa.String(length=50), nullable=False),
|
||||||
sa.Column('error_message', sa.Text(), nullable=False),
|
sa.Column("error_message", sa.Text(), nullable=False),
|
||||||
sa.Column('row_data', sa.JSON(), nullable=True),
|
sa.Column("row_data", sa.JSON(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['import_job_id'], ['marketplace_import_jobs.id'], ondelete='CASCADE'),
|
sa.ForeignKeyConstraint(["import_job_id"], ["marketplace_import_jobs.id"], ondelete="CASCADE"),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index('idx_import_error_job_id', 'marketplace_import_errors', ['import_job_id'], unique=False)
|
op.create_index("idx_import_error_job_id", "marketplace_import_errors", ["import_job_id"], unique=False)
|
||||||
op.create_index('idx_import_error_type', 'marketplace_import_errors', ['error_type'], unique=False)
|
op.create_index("idx_import_error_type", "marketplace_import_errors", ["error_type"], unique=False)
|
||||||
op.create_index(op.f('ix_marketplace_import_errors_id'), 'marketplace_import_errors', ['id'], unique=False)
|
op.create_index(op.f("ix_marketplace_import_errors_id"), "marketplace_import_errors", ["id"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_index(op.f('ix_marketplace_import_errors_id'), table_name='marketplace_import_errors')
|
op.drop_index(op.f("ix_marketplace_import_errors_id"), table_name="marketplace_import_errors")
|
||||||
op.drop_index('idx_import_error_type', table_name='marketplace_import_errors')
|
op.drop_index("idx_import_error_type", table_name="marketplace_import_errors")
|
||||||
op.drop_index('idx_import_error_job_id', table_name='marketplace_import_errors')
|
op.drop_index("idx_import_error_job_id", table_name="marketplace_import_errors")
|
||||||
op.drop_table('marketplace_import_errors')
|
op.drop_table("marketplace_import_errors")
|
||||||
|
|||||||
@@ -11,169 +11,169 @@ This migration adds:
|
|||||||
- letzshop_sync_logs: Audit trail for sync operations
|
- letzshop_sync_logs: Audit trail for sync operations
|
||||||
- Adds channel fields to orders table for multi-marketplace support
|
- Adds channel fields to orders table for multi-marketplace support
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '987b4ecfa503'
|
revision: str = "987b4ecfa503"
|
||||||
down_revision: Union[str, None] = '82ea1b4a3ccb'
|
down_revision: str | None = "82ea1b4a3ccb"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add channel fields to orders table
|
# Add channel fields to orders table
|
||||||
op.add_column('orders', sa.Column('channel', sa.String(length=50), nullable=True, server_default='direct'))
|
op.add_column("orders", sa.Column("channel", sa.String(length=50), nullable=True, server_default="direct"))
|
||||||
op.add_column('orders', sa.Column('external_order_id', sa.String(length=100), nullable=True))
|
op.add_column("orders", sa.Column("external_order_id", sa.String(length=100), nullable=True))
|
||||||
op.add_column('orders', sa.Column('external_channel_data', sa.JSON(), nullable=True))
|
op.add_column("orders", sa.Column("external_channel_data", sa.JSON(), nullable=True))
|
||||||
op.create_index(op.f('ix_orders_channel'), 'orders', ['channel'], unique=False)
|
op.create_index(op.f("ix_orders_channel"), "orders", ["channel"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_external_order_id'), 'orders', ['external_order_id'], unique=False)
|
op.create_index(op.f("ix_orders_external_order_id"), "orders", ["external_order_id"], unique=False)
|
||||||
|
|
||||||
# Create vendor_letzshop_credentials table
|
# Create vendor_letzshop_credentials table
|
||||||
op.create_table('vendor_letzshop_credentials',
|
op.create_table("vendor_letzshop_credentials",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('api_key_encrypted', sa.Text(), nullable=False),
|
sa.Column("api_key_encrypted", sa.Text(), nullable=False),
|
||||||
sa.Column('api_endpoint', sa.String(length=255), server_default='https://letzshop.lu/graphql', nullable=True),
|
sa.Column("api_endpoint", sa.String(length=255), server_default="https://letzshop.lu/graphql", nullable=True),
|
||||||
sa.Column('auto_sync_enabled', sa.Boolean(), server_default='0', nullable=True),
|
sa.Column("auto_sync_enabled", sa.Boolean(), server_default="0", nullable=True),
|
||||||
sa.Column('sync_interval_minutes', sa.Integer(), server_default='15', nullable=True),
|
sa.Column("sync_interval_minutes", sa.Integer(), server_default="15", nullable=True),
|
||||||
sa.Column('last_sync_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("last_sync_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('last_sync_status', sa.String(length=50), nullable=True),
|
sa.Column("last_sync_status", sa.String(length=50), nullable=True),
|
||||||
sa.Column('last_sync_error', sa.Text(), nullable=True),
|
sa.Column("last_sync_error", sa.Text(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint("id"),
|
||||||
sa.UniqueConstraint('vendor_id')
|
sa.UniqueConstraint("vendor_id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_vendor_letzshop_credentials_id'), 'vendor_letzshop_credentials', ['id'], unique=False)
|
op.create_index(op.f("ix_vendor_letzshop_credentials_id"), "vendor_letzshop_credentials", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_vendor_letzshop_credentials_vendor_id'), 'vendor_letzshop_credentials', ['vendor_id'], unique=True)
|
op.create_index(op.f("ix_vendor_letzshop_credentials_vendor_id"), "vendor_letzshop_credentials", ["vendor_id"], unique=True)
|
||||||
|
|
||||||
# Create letzshop_orders table
|
# Create letzshop_orders table
|
||||||
op.create_table('letzshop_orders',
|
op.create_table("letzshop_orders",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('letzshop_order_id', sa.String(length=100), nullable=False),
|
sa.Column("letzshop_order_id", sa.String(length=100), nullable=False),
|
||||||
sa.Column('letzshop_shipment_id', sa.String(length=100), nullable=True),
|
sa.Column("letzshop_shipment_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('letzshop_order_number', sa.String(length=100), nullable=True),
|
sa.Column("letzshop_order_number", sa.String(length=100), nullable=True),
|
||||||
sa.Column('local_order_id', sa.Integer(), nullable=True),
|
sa.Column("local_order_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('letzshop_state', sa.String(length=50), nullable=True),
|
sa.Column("letzshop_state", sa.String(length=50), nullable=True),
|
||||||
sa.Column('customer_email', sa.String(length=255), nullable=True),
|
sa.Column("customer_email", sa.String(length=255), nullable=True),
|
||||||
sa.Column('customer_name', sa.String(length=255), nullable=True),
|
sa.Column("customer_name", sa.String(length=255), nullable=True),
|
||||||
sa.Column('total_amount', sa.String(length=50), nullable=True),
|
sa.Column("total_amount", sa.String(length=50), nullable=True),
|
||||||
sa.Column('currency', sa.String(length=10), server_default='EUR', nullable=True),
|
sa.Column("currency", sa.String(length=10), server_default="EUR", nullable=True),
|
||||||
sa.Column('raw_order_data', sa.JSON(), nullable=True),
|
sa.Column("raw_order_data", sa.JSON(), nullable=True),
|
||||||
sa.Column('inventory_units', sa.JSON(), nullable=True),
|
sa.Column("inventory_units", sa.JSON(), nullable=True),
|
||||||
sa.Column('sync_status', sa.String(length=50), server_default='pending', nullable=True),
|
sa.Column("sync_status", sa.String(length=50), server_default="pending", nullable=True),
|
||||||
sa.Column('last_synced_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("last_synced_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('sync_error', sa.Text(), nullable=True),
|
sa.Column("sync_error", sa.Text(), nullable=True),
|
||||||
sa.Column('confirmed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("confirmed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('rejected_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("rejected_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('tracking_set_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("tracking_set_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('tracking_number', sa.String(length=100), nullable=True),
|
sa.Column("tracking_number", sa.String(length=100), nullable=True),
|
||||||
sa.Column('tracking_carrier', sa.String(length=100), nullable=True),
|
sa.Column("tracking_carrier", sa.String(length=100), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['local_order_id'], ['orders.id'], ),
|
sa.ForeignKeyConstraint(["local_order_id"], ["orders.id"], ),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_letzshop_orders_id'), 'letzshop_orders', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_id"), "letzshop_orders", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_orders_letzshop_order_id'), 'letzshop_orders', ['letzshop_order_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_letzshop_order_id"), "letzshop_orders", ["letzshop_order_id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_orders_letzshop_shipment_id'), 'letzshop_orders', ['letzshop_shipment_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_letzshop_shipment_id"), "letzshop_orders", ["letzshop_shipment_id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_orders_vendor_id'), 'letzshop_orders', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_vendor_id"), "letzshop_orders", ["vendor_id"], unique=False)
|
||||||
op.create_index('idx_letzshop_order_vendor', 'letzshop_orders', ['vendor_id', 'letzshop_order_id'], unique=False)
|
op.create_index("idx_letzshop_order_vendor", "letzshop_orders", ["vendor_id", "letzshop_order_id"], unique=False)
|
||||||
op.create_index('idx_letzshop_order_state', 'letzshop_orders', ['vendor_id', 'letzshop_state'], unique=False)
|
op.create_index("idx_letzshop_order_state", "letzshop_orders", ["vendor_id", "letzshop_state"], unique=False)
|
||||||
op.create_index('idx_letzshop_order_sync', 'letzshop_orders', ['vendor_id', 'sync_status'], unique=False)
|
op.create_index("idx_letzshop_order_sync", "letzshop_orders", ["vendor_id", "sync_status"], unique=False)
|
||||||
|
|
||||||
# Create letzshop_fulfillment_queue table
|
# Create letzshop_fulfillment_queue table
|
||||||
op.create_table('letzshop_fulfillment_queue',
|
op.create_table("letzshop_fulfillment_queue",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('letzshop_order_id', sa.Integer(), nullable=False),
|
sa.Column("letzshop_order_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('operation', sa.String(length=50), nullable=False),
|
sa.Column("operation", sa.String(length=50), nullable=False),
|
||||||
sa.Column('payload', sa.JSON(), nullable=False),
|
sa.Column("payload", sa.JSON(), nullable=False),
|
||||||
sa.Column('status', sa.String(length=50), server_default='pending', nullable=True),
|
sa.Column("status", sa.String(length=50), server_default="pending", nullable=True),
|
||||||
sa.Column('attempts', sa.Integer(), server_default='0', nullable=True),
|
sa.Column("attempts", sa.Integer(), server_default="0", nullable=True),
|
||||||
sa.Column('max_attempts', sa.Integer(), server_default='3', nullable=True),
|
sa.Column("max_attempts", sa.Integer(), server_default="3", nullable=True),
|
||||||
sa.Column('last_attempt_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("last_attempt_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('next_retry_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("next_retry_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('error_message', sa.Text(), nullable=True),
|
sa.Column("error_message", sa.Text(), nullable=True),
|
||||||
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('response_data', sa.JSON(), nullable=True),
|
sa.Column("response_data", sa.JSON(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['letzshop_order_id'], ['letzshop_orders.id'], ),
|
sa.ForeignKeyConstraint(["letzshop_order_id"], ["letzshop_orders.id"], ),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_letzshop_fulfillment_queue_id'), 'letzshop_fulfillment_queue', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_fulfillment_queue_id"), "letzshop_fulfillment_queue", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_fulfillment_queue_vendor_id'), 'letzshop_fulfillment_queue', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_fulfillment_queue_vendor_id"), "letzshop_fulfillment_queue", ["vendor_id"], unique=False)
|
||||||
op.create_index('idx_fulfillment_queue_status', 'letzshop_fulfillment_queue', ['status', 'vendor_id'], unique=False)
|
op.create_index("idx_fulfillment_queue_status", "letzshop_fulfillment_queue", ["status", "vendor_id"], unique=False)
|
||||||
op.create_index('idx_fulfillment_queue_retry', 'letzshop_fulfillment_queue', ['status', 'next_retry_at'], unique=False)
|
op.create_index("idx_fulfillment_queue_retry", "letzshop_fulfillment_queue", ["status", "next_retry_at"], unique=False)
|
||||||
|
|
||||||
# Create letzshop_sync_logs table
|
# Create letzshop_sync_logs table
|
||||||
op.create_table('letzshop_sync_logs',
|
op.create_table("letzshop_sync_logs",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('operation_type', sa.String(length=50), nullable=False),
|
sa.Column("operation_type", sa.String(length=50), nullable=False),
|
||||||
sa.Column('direction', sa.String(length=10), nullable=False),
|
sa.Column("direction", sa.String(length=10), nullable=False),
|
||||||
sa.Column('status', sa.String(length=50), nullable=False),
|
sa.Column("status", sa.String(length=50), nullable=False),
|
||||||
sa.Column('records_processed', sa.Integer(), server_default='0', nullable=True),
|
sa.Column("records_processed", sa.Integer(), server_default="0", nullable=True),
|
||||||
sa.Column('records_succeeded', sa.Integer(), server_default='0', nullable=True),
|
sa.Column("records_succeeded", sa.Integer(), server_default="0", nullable=True),
|
||||||
sa.Column('records_failed', sa.Integer(), server_default='0', nullable=True),
|
sa.Column("records_failed", sa.Integer(), server_default="0", nullable=True),
|
||||||
sa.Column('error_details', sa.JSON(), nullable=True),
|
sa.Column("error_details", sa.JSON(), nullable=True),
|
||||||
sa.Column('started_at', sa.DateTime(timezone=True), nullable=False),
|
sa.Column("started_at", sa.DateTime(timezone=True), nullable=False),
|
||||||
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('duration_seconds', sa.Integer(), nullable=True),
|
sa.Column("duration_seconds", sa.Integer(), nullable=True),
|
||||||
sa.Column('triggered_by', sa.String(length=100), nullable=True),
|
sa.Column("triggered_by", sa.String(length=100), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_letzshop_sync_logs_id'), 'letzshop_sync_logs', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_sync_logs_id"), "letzshop_sync_logs", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_sync_logs_vendor_id'), 'letzshop_sync_logs', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_sync_logs_vendor_id"), "letzshop_sync_logs", ["vendor_id"], unique=False)
|
||||||
op.create_index('idx_sync_log_vendor_type', 'letzshop_sync_logs', ['vendor_id', 'operation_type'], unique=False)
|
op.create_index("idx_sync_log_vendor_type", "letzshop_sync_logs", ["vendor_id", "operation_type"], unique=False)
|
||||||
op.create_index('idx_sync_log_vendor_date', 'letzshop_sync_logs', ['vendor_id', 'started_at'], unique=False)
|
op.create_index("idx_sync_log_vendor_date", "letzshop_sync_logs", ["vendor_id", "started_at"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Drop letzshop_sync_logs table
|
# Drop letzshop_sync_logs table
|
||||||
op.drop_index('idx_sync_log_vendor_date', table_name='letzshop_sync_logs')
|
op.drop_index("idx_sync_log_vendor_date", table_name="letzshop_sync_logs")
|
||||||
op.drop_index('idx_sync_log_vendor_type', table_name='letzshop_sync_logs')
|
op.drop_index("idx_sync_log_vendor_type", table_name="letzshop_sync_logs")
|
||||||
op.drop_index(op.f('ix_letzshop_sync_logs_vendor_id'), table_name='letzshop_sync_logs')
|
op.drop_index(op.f("ix_letzshop_sync_logs_vendor_id"), table_name="letzshop_sync_logs")
|
||||||
op.drop_index(op.f('ix_letzshop_sync_logs_id'), table_name='letzshop_sync_logs')
|
op.drop_index(op.f("ix_letzshop_sync_logs_id"), table_name="letzshop_sync_logs")
|
||||||
op.drop_table('letzshop_sync_logs')
|
op.drop_table("letzshop_sync_logs")
|
||||||
|
|
||||||
# Drop letzshop_fulfillment_queue table
|
# Drop letzshop_fulfillment_queue table
|
||||||
op.drop_index('idx_fulfillment_queue_retry', table_name='letzshop_fulfillment_queue')
|
op.drop_index("idx_fulfillment_queue_retry", table_name="letzshop_fulfillment_queue")
|
||||||
op.drop_index('idx_fulfillment_queue_status', table_name='letzshop_fulfillment_queue')
|
op.drop_index("idx_fulfillment_queue_status", table_name="letzshop_fulfillment_queue")
|
||||||
op.drop_index(op.f('ix_letzshop_fulfillment_queue_vendor_id'), table_name='letzshop_fulfillment_queue')
|
op.drop_index(op.f("ix_letzshop_fulfillment_queue_vendor_id"), table_name="letzshop_fulfillment_queue")
|
||||||
op.drop_index(op.f('ix_letzshop_fulfillment_queue_id'), table_name='letzshop_fulfillment_queue')
|
op.drop_index(op.f("ix_letzshop_fulfillment_queue_id"), table_name="letzshop_fulfillment_queue")
|
||||||
op.drop_table('letzshop_fulfillment_queue')
|
op.drop_table("letzshop_fulfillment_queue")
|
||||||
|
|
||||||
# Drop letzshop_orders table
|
# Drop letzshop_orders table
|
||||||
op.drop_index('idx_letzshop_order_sync', table_name='letzshop_orders')
|
op.drop_index("idx_letzshop_order_sync", table_name="letzshop_orders")
|
||||||
op.drop_index('idx_letzshop_order_state', table_name='letzshop_orders')
|
op.drop_index("idx_letzshop_order_state", table_name="letzshop_orders")
|
||||||
op.drop_index('idx_letzshop_order_vendor', table_name='letzshop_orders')
|
op.drop_index("idx_letzshop_order_vendor", table_name="letzshop_orders")
|
||||||
op.drop_index(op.f('ix_letzshop_orders_vendor_id'), table_name='letzshop_orders')
|
op.drop_index(op.f("ix_letzshop_orders_vendor_id"), table_name="letzshop_orders")
|
||||||
op.drop_index(op.f('ix_letzshop_orders_letzshop_shipment_id'), table_name='letzshop_orders')
|
op.drop_index(op.f("ix_letzshop_orders_letzshop_shipment_id"), table_name="letzshop_orders")
|
||||||
op.drop_index(op.f('ix_letzshop_orders_letzshop_order_id'), table_name='letzshop_orders')
|
op.drop_index(op.f("ix_letzshop_orders_letzshop_order_id"), table_name="letzshop_orders")
|
||||||
op.drop_index(op.f('ix_letzshop_orders_id'), table_name='letzshop_orders')
|
op.drop_index(op.f("ix_letzshop_orders_id"), table_name="letzshop_orders")
|
||||||
op.drop_table('letzshop_orders')
|
op.drop_table("letzshop_orders")
|
||||||
|
|
||||||
# Drop vendor_letzshop_credentials table
|
# Drop vendor_letzshop_credentials table
|
||||||
op.drop_index(op.f('ix_vendor_letzshop_credentials_vendor_id'), table_name='vendor_letzshop_credentials')
|
op.drop_index(op.f("ix_vendor_letzshop_credentials_vendor_id"), table_name="vendor_letzshop_credentials")
|
||||||
op.drop_index(op.f('ix_vendor_letzshop_credentials_id'), table_name='vendor_letzshop_credentials')
|
op.drop_index(op.f("ix_vendor_letzshop_credentials_id"), table_name="vendor_letzshop_credentials")
|
||||||
op.drop_table('vendor_letzshop_credentials')
|
op.drop_table("vendor_letzshop_credentials")
|
||||||
|
|
||||||
# Drop channel fields from orders table
|
# Drop channel fields from orders table
|
||||||
op.drop_index(op.f('ix_orders_external_order_id'), table_name='orders')
|
op.drop_index(op.f("ix_orders_external_order_id"), table_name="orders")
|
||||||
op.drop_index(op.f('ix_orders_channel'), table_name='orders')
|
op.drop_index(op.f("ix_orders_channel"), table_name="orders")
|
||||||
op.drop_column('orders', 'external_channel_data')
|
op.drop_column("orders", "external_channel_data")
|
||||||
op.drop_column('orders', 'external_order_id')
|
op.drop_column("orders", "external_order_id")
|
||||||
op.drop_column('orders', 'channel')
|
op.drop_column("orders", "channel")
|
||||||
|
|||||||
@@ -13,17 +13,17 @@ Architecture Change:
|
|||||||
The vendor ownership is now determined via the company relationship:
|
The vendor ownership is now determined via the company relationship:
|
||||||
- vendor.company.owner_user_id contains the owner
|
- vendor.company.owner_user_id contains the owner
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = '9f3a25ea4991'
|
revision: str = "9f3a25ea4991"
|
||||||
down_revision: Union[str, None] = '5818330181a5'
|
down_revision: str | None = "5818330181a5"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
@@ -35,9 +35,9 @@ def upgrade() -> None:
|
|||||||
Note: SQLite batch mode recreates the table without the column,
|
Note: SQLite batch mode recreates the table without the column,
|
||||||
so we don't need to explicitly drop constraints.
|
so we don't need to explicitly drop constraints.
|
||||||
"""
|
"""
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
# Drop the column - batch mode handles constraints automatically
|
# Drop the column - batch mode handles constraints automatically
|
||||||
batch_op.drop_column('owner_user_id')
|
batch_op.drop_column("owner_user_id")
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
@@ -48,13 +48,13 @@ def downgrade() -> None:
|
|||||||
You will need to manually populate owner_user_id from company.owner_user_id
|
You will need to manually populate owner_user_id from company.owner_user_id
|
||||||
if reverting this migration.
|
if reverting this migration.
|
||||||
"""
|
"""
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
batch_op.add_column(
|
batch_op.add_column(
|
||||||
sa.Column('owner_user_id', sa.Integer(), nullable=True)
|
sa.Column("owner_user_id", sa.Integer(), nullable=True)
|
||||||
)
|
)
|
||||||
batch_op.create_foreign_key(
|
batch_op.create_foreign_key(
|
||||||
'vendors_owner_user_id_fkey',
|
"vendors_owner_user_id_fkey",
|
||||||
'users',
|
"users",
|
||||||
['owner_user_id'],
|
["owner_user_id"],
|
||||||
['id']
|
["id"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Create Date: 2025-11-23 19:52:40.509538
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@@ -14,9 +14,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "a2064e1dfcd4"
|
revision: str = "a2064e1dfcd4"
|
||||||
down_revision: Union[str, None] = "f68d8da5315a"
|
down_revision: str | None = "f68d8da5315a"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ The override pattern: NULL value means "inherit from marketplace_product".
|
|||||||
Setting a value creates a vendor-specific override.
|
Setting a value creates a vendor-specific override.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@@ -23,9 +23,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "a3b4c5d6e7f8"
|
revision: str = "a3b4c5d6e7f8"
|
||||||
down_revision: Union[str, None] = "f2b3c4d5e6f7"
|
down_revision: str | None = "f2b3c4d5e6f7"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -5,27 +5,27 @@ Revises: fcfdc02d5138
|
|||||||
Create Date: 2025-12-17 20:55:41.477848
|
Create Date: 2025-12-17 20:55:41.477848
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'a9a86cef6cca'
|
revision: str = "a9a86cef6cca"
|
||||||
down_revision: Union[str, None] = 'fcfdc02d5138'
|
down_revision: str | None = "fcfdc02d5138"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add new columns to letzshop_orders for customer locale and country
|
# Add new columns to letzshop_orders for customer locale and country
|
||||||
op.add_column('letzshop_orders', sa.Column('customer_locale', sa.String(length=10), nullable=True))
|
op.add_column("letzshop_orders", sa.Column("customer_locale", sa.String(length=10), nullable=True))
|
||||||
op.add_column('letzshop_orders', sa.Column('shipping_country_iso', sa.String(length=5), nullable=True))
|
op.add_column("letzshop_orders", sa.Column("shipping_country_iso", sa.String(length=5), nullable=True))
|
||||||
op.add_column('letzshop_orders', sa.Column('billing_country_iso', sa.String(length=5), nullable=True))
|
op.add_column("letzshop_orders", sa.Column("billing_country_iso", sa.String(length=5), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column('letzshop_orders', 'billing_country_iso')
|
op.drop_column("letzshop_orders", "billing_country_iso")
|
||||||
op.drop_column('letzshop_orders', 'shipping_country_iso')
|
op.drop_column("letzshop_orders", "shipping_country_iso")
|
||||||
op.drop_column('letzshop_orders', 'customer_locale')
|
op.drop_column("letzshop_orders", "customer_locale")
|
||||||
|
|||||||
@@ -5,26 +5,26 @@ Revises: 91d02647efae
|
|||||||
Create Date: 2025-12-13 13:35:46.524893
|
Create Date: 2025-12-13 13:35:46.524893
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'b412e0b49c2e'
|
revision: str = "b412e0b49c2e"
|
||||||
down_revision: Union[str, None] = '91d02647efae'
|
down_revision: str | None = "91d02647efae"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add language column with default value for existing rows
|
# Add language column with default value for existing rows
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'marketplace_import_jobs',
|
"marketplace_import_jobs",
|
||||||
sa.Column('language', sa.String(length=5), nullable=False, server_default='en')
|
sa.Column("language", sa.String(length=5), nullable=False, server_default="en")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column('marketplace_import_jobs', 'language')
|
op.drop_column("marketplace_import_jobs", "language")
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ after migrating the data to the new structure.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
@@ -24,9 +24,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "b4c5d6e7f8a9"
|
revision: str = "b4c5d6e7f8a9"
|
||||||
down_revision: Union[str, None] = "a3b4c5d6e7f8"
|
down_revision: str | None = "a3b4c5d6e7f8"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def parse_price(price_str: str) -> float | None:
|
def parse_price(price_str: str) -> float | None:
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ Revises: m1b2c3d4e5f6
|
|||||||
Create Date: 2025-12-28 20:00:24.263518
|
Create Date: 2025-12-28 20:00:24.263518
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'ba2c0ce78396'
|
revision: str = "ba2c0ce78396"
|
||||||
down_revision: Union[str, None] = 'm1b2c3d4e5f6'
|
down_revision: str | None = "m1b2c3d4e5f6"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
@@ -25,8 +25,8 @@ def upgrade() -> None:
|
|||||||
alongside the copyright notice (e.g., Privacy Policy, Terms of Service).
|
alongside the copyright notice (e.g., Privacy Policy, Terms of Service).
|
||||||
"""
|
"""
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'content_pages',
|
"content_pages",
|
||||||
sa.Column('show_in_legal', sa.Boolean(), nullable=True, default=False)
|
sa.Column("show_in_legal", sa.Boolean(), nullable=True, default=False)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set default value for existing rows (PostgreSQL uses true/false for boolean)
|
# Set default value for existing rows (PostgreSQL uses true/false for boolean)
|
||||||
@@ -38,4 +38,4 @@ def upgrade() -> None:
|
|||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
"""Remove show_in_legal column from content_pages table."""
|
"""Remove show_in_legal column from content_pages table."""
|
||||||
op.drop_column('content_pages', 'show_in_legal')
|
op.drop_column("content_pages", "show_in_legal")
|
||||||
|
|||||||
@@ -5,31 +5,31 @@ Revises: 55b92e155566
|
|||||||
Create Date: 2025-12-20 18:49:53.432904
|
Create Date: 2025-12-20 18:49:53.432904
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'c00d2985701f'
|
revision: str = "c00d2985701f"
|
||||||
down_revision: Union[str, None] = '55b92e155566'
|
down_revision: str | None = "55b92e155566"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add carrier settings and test mode to vendor_letzshop_credentials
|
# Add carrier settings and test mode to vendor_letzshop_credentials
|
||||||
op.add_column('vendor_letzshop_credentials', sa.Column('test_mode_enabled', sa.Boolean(), nullable=True, server_default='0'))
|
op.add_column("vendor_letzshop_credentials", sa.Column("test_mode_enabled", sa.Boolean(), nullable=True, server_default="0"))
|
||||||
op.add_column('vendor_letzshop_credentials', sa.Column('default_carrier', sa.String(length=50), nullable=True))
|
op.add_column("vendor_letzshop_credentials", sa.Column("default_carrier", sa.String(length=50), nullable=True))
|
||||||
op.add_column('vendor_letzshop_credentials', sa.Column('carrier_greco_label_url', sa.String(length=500), nullable=True, server_default='https://dispatchweb.fr/Tracky/Home/'))
|
op.add_column("vendor_letzshop_credentials", sa.Column("carrier_greco_label_url", sa.String(length=500), nullable=True, server_default="https://dispatchweb.fr/Tracky/Home/"))
|
||||||
op.add_column('vendor_letzshop_credentials', sa.Column('carrier_colissimo_label_url', sa.String(length=500), nullable=True))
|
op.add_column("vendor_letzshop_credentials", sa.Column("carrier_colissimo_label_url", sa.String(length=500), nullable=True))
|
||||||
op.add_column('vendor_letzshop_credentials', sa.Column('carrier_xpresslogistics_label_url', sa.String(length=500), nullable=True))
|
op.add_column("vendor_letzshop_credentials", sa.Column("carrier_xpresslogistics_label_url", sa.String(length=500), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column('vendor_letzshop_credentials', 'carrier_xpresslogistics_label_url')
|
op.drop_column("vendor_letzshop_credentials", "carrier_xpresslogistics_label_url")
|
||||||
op.drop_column('vendor_letzshop_credentials', 'carrier_colissimo_label_url')
|
op.drop_column("vendor_letzshop_credentials", "carrier_colissimo_label_url")
|
||||||
op.drop_column('vendor_letzshop_credentials', 'carrier_greco_label_url')
|
op.drop_column("vendor_letzshop_credentials", "carrier_greco_label_url")
|
||||||
op.drop_column('vendor_letzshop_credentials', 'default_carrier')
|
op.drop_column("vendor_letzshop_credentials", "default_carrier")
|
||||||
op.drop_column('vendor_letzshop_credentials', 'test_mode_enabled')
|
op.drop_column("vendor_letzshop_credentials", "test_mode_enabled")
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ Design principles:
|
|||||||
- Customer/address data snapshotted at order time
|
- Customer/address data snapshotted at order time
|
||||||
- Products must exist in catalog (enforced by FK)
|
- Products must exist in catalog (enforced by FK)
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'c1d2e3f4a5b6'
|
revision: str = "c1d2e3f4a5b6"
|
||||||
down_revision: Union[str, None] = '2362c2723a93'
|
down_revision: str | None = "2362c2723a93"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def table_exists(table_name: str) -> bool:
|
def table_exists(table_name: str) -> bool:
|
||||||
@@ -48,7 +48,7 @@ def index_exists(index_name: str, table_name: str) -> bool:
|
|||||||
inspector = inspect(bind)
|
inspector = inspect(bind)
|
||||||
try:
|
try:
|
||||||
indexes = inspector.get_indexes(table_name)
|
indexes = inspector.get_indexes(table_name)
|
||||||
return any(idx['name'] == index_name for idx in indexes)
|
return any(idx["name"] == index_name for idx in indexes)
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -71,382 +71,382 @@ def upgrade() -> None:
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
# Drop letzshop_fulfillment_queue (references letzshop_orders)
|
# Drop letzshop_fulfillment_queue (references letzshop_orders)
|
||||||
if table_exists('letzshop_fulfillment_queue'):
|
if table_exists("letzshop_fulfillment_queue"):
|
||||||
safe_drop_index('idx_fulfillment_queue_retry', 'letzshop_fulfillment_queue')
|
safe_drop_index("idx_fulfillment_queue_retry", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('idx_fulfillment_queue_status', 'letzshop_fulfillment_queue')
|
safe_drop_index("idx_fulfillment_queue_status", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('ix_letzshop_fulfillment_queue_vendor_id', 'letzshop_fulfillment_queue')
|
safe_drop_index("ix_letzshop_fulfillment_queue_vendor_id", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('ix_letzshop_fulfillment_queue_id', 'letzshop_fulfillment_queue')
|
safe_drop_index("ix_letzshop_fulfillment_queue_id", "letzshop_fulfillment_queue")
|
||||||
op.drop_table('letzshop_fulfillment_queue')
|
op.drop_table("letzshop_fulfillment_queue")
|
||||||
|
|
||||||
# Drop letzshop_orders table (replaced by unified orders)
|
# Drop letzshop_orders table (replaced by unified orders)
|
||||||
if table_exists('letzshop_orders'):
|
if table_exists("letzshop_orders"):
|
||||||
safe_drop_index('idx_letzshop_order_sync', 'letzshop_orders')
|
safe_drop_index("idx_letzshop_order_sync", "letzshop_orders")
|
||||||
safe_drop_index('idx_letzshop_order_state', 'letzshop_orders')
|
safe_drop_index("idx_letzshop_order_state", "letzshop_orders")
|
||||||
safe_drop_index('idx_letzshop_order_vendor', 'letzshop_orders')
|
safe_drop_index("idx_letzshop_order_vendor", "letzshop_orders")
|
||||||
safe_drop_index('ix_letzshop_orders_vendor_id', 'letzshop_orders')
|
safe_drop_index("ix_letzshop_orders_vendor_id", "letzshop_orders")
|
||||||
safe_drop_index('ix_letzshop_orders_letzshop_shipment_id', 'letzshop_orders')
|
safe_drop_index("ix_letzshop_orders_letzshop_shipment_id", "letzshop_orders")
|
||||||
safe_drop_index('ix_letzshop_orders_letzshop_order_id', 'letzshop_orders')
|
safe_drop_index("ix_letzshop_orders_letzshop_order_id", "letzshop_orders")
|
||||||
safe_drop_index('ix_letzshop_orders_id', 'letzshop_orders')
|
safe_drop_index("ix_letzshop_orders_id", "letzshop_orders")
|
||||||
op.drop_table('letzshop_orders')
|
op.drop_table("letzshop_orders")
|
||||||
|
|
||||||
# Drop order_items (references orders)
|
# Drop order_items (references orders)
|
||||||
if table_exists('order_items'):
|
if table_exists("order_items"):
|
||||||
safe_drop_index('ix_order_items_id', 'order_items')
|
safe_drop_index("ix_order_items_id", "order_items")
|
||||||
safe_drop_index('ix_order_items_order_id', 'order_items')
|
safe_drop_index("ix_order_items_order_id", "order_items")
|
||||||
op.drop_table('order_items')
|
op.drop_table("order_items")
|
||||||
|
|
||||||
# Drop old orders table
|
# Drop old orders table
|
||||||
if table_exists('orders'):
|
if table_exists("orders"):
|
||||||
safe_drop_index('ix_orders_external_order_id', 'orders')
|
safe_drop_index("ix_orders_external_order_id", "orders")
|
||||||
safe_drop_index('ix_orders_channel', 'orders')
|
safe_drop_index("ix_orders_channel", "orders")
|
||||||
safe_drop_index('ix_orders_vendor_id', 'orders')
|
safe_drop_index("ix_orders_vendor_id", "orders")
|
||||||
safe_drop_index('ix_orders_status', 'orders')
|
safe_drop_index("ix_orders_status", "orders")
|
||||||
safe_drop_index('ix_orders_order_number', 'orders')
|
safe_drop_index("ix_orders_order_number", "orders")
|
||||||
safe_drop_index('ix_orders_id', 'orders')
|
safe_drop_index("ix_orders_id", "orders")
|
||||||
safe_drop_index('ix_orders_customer_id', 'orders')
|
safe_drop_index("ix_orders_customer_id", "orders")
|
||||||
op.drop_table('orders')
|
op.drop_table("orders")
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Step 2: Create new unified orders table
|
# Step 2: Create new unified orders table
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
op.create_table('orders',
|
op.create_table("orders",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('customer_id', sa.Integer(), nullable=False),
|
sa.Column("customer_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('order_number', sa.String(length=100), nullable=False),
|
sa.Column("order_number", sa.String(length=100), nullable=False),
|
||||||
|
|
||||||
# Channel/Source
|
# Channel/Source
|
||||||
sa.Column('channel', sa.String(length=50), nullable=False, server_default='direct'),
|
sa.Column("channel", sa.String(length=50), nullable=False, server_default="direct"),
|
||||||
|
|
||||||
# External references (for marketplace orders)
|
# External references (for marketplace orders)
|
||||||
sa.Column('external_order_id', sa.String(length=100), nullable=True),
|
sa.Column("external_order_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('external_shipment_id', sa.String(length=100), nullable=True),
|
sa.Column("external_shipment_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('external_order_number', sa.String(length=100), nullable=True),
|
sa.Column("external_order_number", sa.String(length=100), nullable=True),
|
||||||
sa.Column('external_data', sa.JSON(), nullable=True),
|
sa.Column("external_data", sa.JSON(), nullable=True),
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
sa.Column('status', sa.String(length=50), nullable=False, server_default='pending'),
|
sa.Column("status", sa.String(length=50), nullable=False, server_default="pending"),
|
||||||
|
|
||||||
# Financials
|
# Financials
|
||||||
sa.Column('subtotal', sa.Float(), nullable=True),
|
sa.Column("subtotal", sa.Float(), nullable=True),
|
||||||
sa.Column('tax_amount', sa.Float(), nullable=True),
|
sa.Column("tax_amount", sa.Float(), nullable=True),
|
||||||
sa.Column('shipping_amount', sa.Float(), nullable=True),
|
sa.Column("shipping_amount", sa.Float(), nullable=True),
|
||||||
sa.Column('discount_amount', sa.Float(), nullable=True),
|
sa.Column("discount_amount", sa.Float(), nullable=True),
|
||||||
sa.Column('total_amount', sa.Float(), nullable=False),
|
sa.Column("total_amount", sa.Float(), nullable=False),
|
||||||
sa.Column('currency', sa.String(length=10), server_default='EUR', nullable=True),
|
sa.Column("currency", sa.String(length=10), server_default="EUR", nullable=True),
|
||||||
|
|
||||||
# Customer snapshot
|
# Customer snapshot
|
||||||
sa.Column('customer_first_name', sa.String(length=100), nullable=False),
|
sa.Column("customer_first_name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('customer_last_name', sa.String(length=100), nullable=False),
|
sa.Column("customer_last_name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('customer_email', sa.String(length=255), nullable=False),
|
sa.Column("customer_email", sa.String(length=255), nullable=False),
|
||||||
sa.Column('customer_phone', sa.String(length=50), nullable=True),
|
sa.Column("customer_phone", sa.String(length=50), nullable=True),
|
||||||
sa.Column('customer_locale', sa.String(length=10), nullable=True),
|
sa.Column("customer_locale", sa.String(length=10), nullable=True),
|
||||||
|
|
||||||
# Shipping address snapshot
|
# Shipping address snapshot
|
||||||
sa.Column('ship_first_name', sa.String(length=100), nullable=False),
|
sa.Column("ship_first_name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('ship_last_name', sa.String(length=100), nullable=False),
|
sa.Column("ship_last_name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('ship_company', sa.String(length=200), nullable=True),
|
sa.Column("ship_company", sa.String(length=200), nullable=True),
|
||||||
sa.Column('ship_address_line_1', sa.String(length=255), nullable=False),
|
sa.Column("ship_address_line_1", sa.String(length=255), nullable=False),
|
||||||
sa.Column('ship_address_line_2', sa.String(length=255), nullable=True),
|
sa.Column("ship_address_line_2", sa.String(length=255), nullable=True),
|
||||||
sa.Column('ship_city', sa.String(length=100), nullable=False),
|
sa.Column("ship_city", sa.String(length=100), nullable=False),
|
||||||
sa.Column('ship_postal_code', sa.String(length=20), nullable=False),
|
sa.Column("ship_postal_code", sa.String(length=20), nullable=False),
|
||||||
sa.Column('ship_country_iso', sa.String(length=5), nullable=False),
|
sa.Column("ship_country_iso", sa.String(length=5), nullable=False),
|
||||||
|
|
||||||
# Billing address snapshot
|
# Billing address snapshot
|
||||||
sa.Column('bill_first_name', sa.String(length=100), nullable=False),
|
sa.Column("bill_first_name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('bill_last_name', sa.String(length=100), nullable=False),
|
sa.Column("bill_last_name", sa.String(length=100), nullable=False),
|
||||||
sa.Column('bill_company', sa.String(length=200), nullable=True),
|
sa.Column("bill_company", sa.String(length=200), nullable=True),
|
||||||
sa.Column('bill_address_line_1', sa.String(length=255), nullable=False),
|
sa.Column("bill_address_line_1", sa.String(length=255), nullable=False),
|
||||||
sa.Column('bill_address_line_2', sa.String(length=255), nullable=True),
|
sa.Column("bill_address_line_2", sa.String(length=255), nullable=True),
|
||||||
sa.Column('bill_city', sa.String(length=100), nullable=False),
|
sa.Column("bill_city", sa.String(length=100), nullable=False),
|
||||||
sa.Column('bill_postal_code', sa.String(length=20), nullable=False),
|
sa.Column("bill_postal_code", sa.String(length=20), nullable=False),
|
||||||
sa.Column('bill_country_iso', sa.String(length=5), nullable=False),
|
sa.Column("bill_country_iso", sa.String(length=5), nullable=False),
|
||||||
|
|
||||||
# Tracking
|
# Tracking
|
||||||
sa.Column('shipping_method', sa.String(length=100), nullable=True),
|
sa.Column("shipping_method", sa.String(length=100), nullable=True),
|
||||||
sa.Column('tracking_number', sa.String(length=100), nullable=True),
|
sa.Column("tracking_number", sa.String(length=100), nullable=True),
|
||||||
sa.Column('tracking_provider', sa.String(length=100), nullable=True),
|
sa.Column("tracking_provider", sa.String(length=100), nullable=True),
|
||||||
|
|
||||||
# Notes
|
# Notes
|
||||||
sa.Column('customer_notes', sa.Text(), nullable=True),
|
sa.Column("customer_notes", sa.Text(), nullable=True),
|
||||||
sa.Column('internal_notes', sa.Text(), nullable=True),
|
sa.Column("internal_notes", sa.Text(), nullable=True),
|
||||||
|
|
||||||
# Timestamps
|
# Timestamps
|
||||||
sa.Column('order_date', sa.DateTime(timezone=True), nullable=False),
|
sa.Column("order_date", sa.DateTime(timezone=True), nullable=False),
|
||||||
sa.Column('confirmed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("confirmed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('shipped_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("shipped_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('delivered_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("delivered_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('cancelled_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("cancelled_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
|
|
||||||
# Foreign keys
|
# Foreign keys
|
||||||
sa.ForeignKeyConstraint(['customer_id'], ['customers.id']),
|
sa.ForeignKeyConstraint(["customer_id"], ["customers.id"]),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id']),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Indexes for orders
|
# Indexes for orders
|
||||||
op.create_index(op.f('ix_orders_id'), 'orders', ['id'], unique=False)
|
op.create_index(op.f("ix_orders_id"), "orders", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_vendor_id'), 'orders', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_orders_vendor_id"), "orders", ["vendor_id"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_customer_id'), 'orders', ['customer_id'], unique=False)
|
op.create_index(op.f("ix_orders_customer_id"), "orders", ["customer_id"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_order_number'), 'orders', ['order_number'], unique=True)
|
op.create_index(op.f("ix_orders_order_number"), "orders", ["order_number"], unique=True)
|
||||||
op.create_index(op.f('ix_orders_channel'), 'orders', ['channel'], unique=False)
|
op.create_index(op.f("ix_orders_channel"), "orders", ["channel"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_status'), 'orders', ['status'], unique=False)
|
op.create_index(op.f("ix_orders_status"), "orders", ["status"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_external_order_id'), 'orders', ['external_order_id'], unique=False)
|
op.create_index(op.f("ix_orders_external_order_id"), "orders", ["external_order_id"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_external_shipment_id'), 'orders', ['external_shipment_id'], unique=False)
|
op.create_index(op.f("ix_orders_external_shipment_id"), "orders", ["external_shipment_id"], unique=False)
|
||||||
op.create_index('idx_order_vendor_status', 'orders', ['vendor_id', 'status'], unique=False)
|
op.create_index("idx_order_vendor_status", "orders", ["vendor_id", "status"], unique=False)
|
||||||
op.create_index('idx_order_vendor_channel', 'orders', ['vendor_id', 'channel'], unique=False)
|
op.create_index("idx_order_vendor_channel", "orders", ["vendor_id", "channel"], unique=False)
|
||||||
op.create_index('idx_order_vendor_date', 'orders', ['vendor_id', 'order_date'], unique=False)
|
op.create_index("idx_order_vendor_date", "orders", ["vendor_id", "order_date"], unique=False)
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Step 3: Create new order_items table
|
# Step 3: Create new order_items table
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
op.create_table('order_items',
|
op.create_table("order_items",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('order_id', sa.Integer(), nullable=False),
|
sa.Column("order_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
sa.Column("product_id", sa.Integer(), nullable=False),
|
||||||
|
|
||||||
# Product snapshot
|
# Product snapshot
|
||||||
sa.Column('product_name', sa.String(length=255), nullable=False),
|
sa.Column("product_name", sa.String(length=255), nullable=False),
|
||||||
sa.Column('product_sku', sa.String(length=100), nullable=True),
|
sa.Column("product_sku", sa.String(length=100), nullable=True),
|
||||||
sa.Column('gtin', sa.String(length=50), nullable=True),
|
sa.Column("gtin", sa.String(length=50), nullable=True),
|
||||||
sa.Column('gtin_type', sa.String(length=20), nullable=True),
|
sa.Column("gtin_type", sa.String(length=20), nullable=True),
|
||||||
|
|
||||||
# Pricing
|
# Pricing
|
||||||
sa.Column('quantity', sa.Integer(), nullable=False),
|
sa.Column("quantity", sa.Integer(), nullable=False),
|
||||||
sa.Column('unit_price', sa.Float(), nullable=False),
|
sa.Column("unit_price", sa.Float(), nullable=False),
|
||||||
sa.Column('total_price', sa.Float(), nullable=False),
|
sa.Column("total_price", sa.Float(), nullable=False),
|
||||||
|
|
||||||
# External references (for marketplace items)
|
# External references (for marketplace items)
|
||||||
sa.Column('external_item_id', sa.String(length=100), nullable=True),
|
sa.Column("external_item_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('external_variant_id', sa.String(length=100), nullable=True),
|
sa.Column("external_variant_id", sa.String(length=100), nullable=True),
|
||||||
|
|
||||||
# Item state (for marketplace confirmation flow)
|
# Item state (for marketplace confirmation flow)
|
||||||
sa.Column('item_state', sa.String(length=50), nullable=True),
|
sa.Column("item_state", sa.String(length=50), nullable=True),
|
||||||
|
|
||||||
# Inventory tracking
|
# Inventory tracking
|
||||||
sa.Column('inventory_reserved', sa.Boolean(), server_default='0', nullable=True),
|
sa.Column("inventory_reserved", sa.Boolean(), server_default="0", nullable=True),
|
||||||
sa.Column('inventory_fulfilled', sa.Boolean(), server_default='0', nullable=True),
|
sa.Column("inventory_fulfilled", sa.Boolean(), server_default="0", nullable=True),
|
||||||
|
|
||||||
# Timestamps
|
# Timestamps
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
|
|
||||||
# Foreign keys
|
# Foreign keys
|
||||||
sa.ForeignKeyConstraint(['order_id'], ['orders.id']),
|
sa.ForeignKeyConstraint(["order_id"], ["orders.id"]),
|
||||||
sa.ForeignKeyConstraint(['product_id'], ['products.id']),
|
sa.ForeignKeyConstraint(["product_id"], ["products.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Indexes for order_items
|
# Indexes for order_items
|
||||||
op.create_index(op.f('ix_order_items_id'), 'order_items', ['id'], unique=False)
|
op.create_index(op.f("ix_order_items_id"), "order_items", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_order_items_order_id'), 'order_items', ['order_id'], unique=False)
|
op.create_index(op.f("ix_order_items_order_id"), "order_items", ["order_id"], unique=False)
|
||||||
op.create_index(op.f('ix_order_items_product_id'), 'order_items', ['product_id'], unique=False)
|
op.create_index(op.f("ix_order_items_product_id"), "order_items", ["product_id"], unique=False)
|
||||||
op.create_index(op.f('ix_order_items_gtin'), 'order_items', ['gtin'], unique=False)
|
op.create_index(op.f("ix_order_items_gtin"), "order_items", ["gtin"], unique=False)
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Step 4: Create updated letzshop_fulfillment_queue (references orders)
|
# Step 4: Create updated letzshop_fulfillment_queue (references orders)
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
op.create_table('letzshop_fulfillment_queue',
|
op.create_table("letzshop_fulfillment_queue",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('order_id', sa.Integer(), nullable=False),
|
sa.Column("order_id", sa.Integer(), nullable=False),
|
||||||
|
|
||||||
# Operation type
|
# Operation type
|
||||||
sa.Column('operation', sa.String(length=50), nullable=False),
|
sa.Column("operation", sa.String(length=50), nullable=False),
|
||||||
|
|
||||||
# Operation payload
|
# Operation payload
|
||||||
sa.Column('payload', sa.JSON(), nullable=False),
|
sa.Column("payload", sa.JSON(), nullable=False),
|
||||||
|
|
||||||
# Status and retry
|
# Status and retry
|
||||||
sa.Column('status', sa.String(length=50), server_default='pending', nullable=True),
|
sa.Column("status", sa.String(length=50), server_default="pending", nullable=True),
|
||||||
sa.Column('attempts', sa.Integer(), server_default='0', nullable=True),
|
sa.Column("attempts", sa.Integer(), server_default="0", nullable=True),
|
||||||
sa.Column('max_attempts', sa.Integer(), server_default='3', nullable=True),
|
sa.Column("max_attempts", sa.Integer(), server_default="3", nullable=True),
|
||||||
sa.Column('last_attempt_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("last_attempt_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('next_retry_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("next_retry_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('error_message', sa.Text(), nullable=True),
|
sa.Column("error_message", sa.Text(), nullable=True),
|
||||||
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
|
|
||||||
# Response from Letzshop
|
# Response from Letzshop
|
||||||
sa.Column('response_data', sa.JSON(), nullable=True),
|
sa.Column("response_data", sa.JSON(), nullable=True),
|
||||||
|
|
||||||
# Timestamps
|
# Timestamps
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
|
|
||||||
# Foreign keys
|
# Foreign keys
|
||||||
sa.ForeignKeyConstraint(['order_id'], ['orders.id']),
|
sa.ForeignKeyConstraint(["order_id"], ["orders.id"]),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id']),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Indexes for letzshop_fulfillment_queue
|
# Indexes for letzshop_fulfillment_queue
|
||||||
op.create_index(op.f('ix_letzshop_fulfillment_queue_id'), 'letzshop_fulfillment_queue', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_fulfillment_queue_id"), "letzshop_fulfillment_queue", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_fulfillment_queue_vendor_id'), 'letzshop_fulfillment_queue', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_fulfillment_queue_vendor_id"), "letzshop_fulfillment_queue", ["vendor_id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_fulfillment_queue_order_id'), 'letzshop_fulfillment_queue', ['order_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_fulfillment_queue_order_id"), "letzshop_fulfillment_queue", ["order_id"], unique=False)
|
||||||
op.create_index('idx_fulfillment_queue_status', 'letzshop_fulfillment_queue', ['status', 'vendor_id'], unique=False)
|
op.create_index("idx_fulfillment_queue_status", "letzshop_fulfillment_queue", ["status", "vendor_id"], unique=False)
|
||||||
op.create_index('idx_fulfillment_queue_retry', 'letzshop_fulfillment_queue', ['status', 'next_retry_at'], unique=False)
|
op.create_index("idx_fulfillment_queue_retry", "letzshop_fulfillment_queue", ["status", "next_retry_at"], unique=False)
|
||||||
op.create_index('idx_fulfillment_queue_order', 'letzshop_fulfillment_queue', ['order_id'], unique=False)
|
op.create_index("idx_fulfillment_queue_order", "letzshop_fulfillment_queue", ["order_id"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Drop new letzshop_fulfillment_queue
|
# Drop new letzshop_fulfillment_queue
|
||||||
safe_drop_index('idx_fulfillment_queue_order', 'letzshop_fulfillment_queue')
|
safe_drop_index("idx_fulfillment_queue_order", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('idx_fulfillment_queue_retry', 'letzshop_fulfillment_queue')
|
safe_drop_index("idx_fulfillment_queue_retry", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('idx_fulfillment_queue_status', 'letzshop_fulfillment_queue')
|
safe_drop_index("idx_fulfillment_queue_status", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('ix_letzshop_fulfillment_queue_order_id', 'letzshop_fulfillment_queue')
|
safe_drop_index("ix_letzshop_fulfillment_queue_order_id", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('ix_letzshop_fulfillment_queue_vendor_id', 'letzshop_fulfillment_queue')
|
safe_drop_index("ix_letzshop_fulfillment_queue_vendor_id", "letzshop_fulfillment_queue")
|
||||||
safe_drop_index('ix_letzshop_fulfillment_queue_id', 'letzshop_fulfillment_queue')
|
safe_drop_index("ix_letzshop_fulfillment_queue_id", "letzshop_fulfillment_queue")
|
||||||
safe_drop_table('letzshop_fulfillment_queue')
|
safe_drop_table("letzshop_fulfillment_queue")
|
||||||
|
|
||||||
# Drop new order_items
|
# Drop new order_items
|
||||||
safe_drop_index('ix_order_items_gtin', 'order_items')
|
safe_drop_index("ix_order_items_gtin", "order_items")
|
||||||
safe_drop_index('ix_order_items_product_id', 'order_items')
|
safe_drop_index("ix_order_items_product_id", "order_items")
|
||||||
safe_drop_index('ix_order_items_order_id', 'order_items')
|
safe_drop_index("ix_order_items_order_id", "order_items")
|
||||||
safe_drop_index('ix_order_items_id', 'order_items')
|
safe_drop_index("ix_order_items_id", "order_items")
|
||||||
safe_drop_table('order_items')
|
safe_drop_table("order_items")
|
||||||
|
|
||||||
# Drop new orders
|
# Drop new orders
|
||||||
safe_drop_index('idx_order_vendor_date', 'orders')
|
safe_drop_index("idx_order_vendor_date", "orders")
|
||||||
safe_drop_index('idx_order_vendor_channel', 'orders')
|
safe_drop_index("idx_order_vendor_channel", "orders")
|
||||||
safe_drop_index('idx_order_vendor_status', 'orders')
|
safe_drop_index("idx_order_vendor_status", "orders")
|
||||||
safe_drop_index('ix_orders_external_shipment_id', 'orders')
|
safe_drop_index("ix_orders_external_shipment_id", "orders")
|
||||||
safe_drop_index('ix_orders_external_order_id', 'orders')
|
safe_drop_index("ix_orders_external_order_id", "orders")
|
||||||
safe_drop_index('ix_orders_status', 'orders')
|
safe_drop_index("ix_orders_status", "orders")
|
||||||
safe_drop_index('ix_orders_channel', 'orders')
|
safe_drop_index("ix_orders_channel", "orders")
|
||||||
safe_drop_index('ix_orders_order_number', 'orders')
|
safe_drop_index("ix_orders_order_number", "orders")
|
||||||
safe_drop_index('ix_orders_customer_id', 'orders')
|
safe_drop_index("ix_orders_customer_id", "orders")
|
||||||
safe_drop_index('ix_orders_vendor_id', 'orders')
|
safe_drop_index("ix_orders_vendor_id", "orders")
|
||||||
safe_drop_index('ix_orders_id', 'orders')
|
safe_drop_index("ix_orders_id", "orders")
|
||||||
safe_drop_table('orders')
|
safe_drop_table("orders")
|
||||||
|
|
||||||
# Recreate old orders table
|
# Recreate old orders table
|
||||||
op.create_table('orders',
|
op.create_table("orders",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('customer_id', sa.Integer(), nullable=False),
|
sa.Column("customer_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('order_number', sa.String(), nullable=False),
|
sa.Column("order_number", sa.String(), nullable=False),
|
||||||
sa.Column('channel', sa.String(length=50), nullable=True, server_default='direct'),
|
sa.Column("channel", sa.String(length=50), nullable=True, server_default="direct"),
|
||||||
sa.Column('external_order_id', sa.String(length=100), nullable=True),
|
sa.Column("external_order_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('external_channel_data', sa.JSON(), nullable=True),
|
sa.Column("external_channel_data", sa.JSON(), nullable=True),
|
||||||
sa.Column('status', sa.String(), nullable=False),
|
sa.Column("status", sa.String(), nullable=False),
|
||||||
sa.Column('subtotal', sa.Float(), nullable=False),
|
sa.Column("subtotal", sa.Float(), nullable=False),
|
||||||
sa.Column('tax_amount', sa.Float(), nullable=True),
|
sa.Column("tax_amount", sa.Float(), nullable=True),
|
||||||
sa.Column('shipping_amount', sa.Float(), nullable=True),
|
sa.Column("shipping_amount", sa.Float(), nullable=True),
|
||||||
sa.Column('discount_amount', sa.Float(), nullable=True),
|
sa.Column("discount_amount", sa.Float(), nullable=True),
|
||||||
sa.Column('total_amount', sa.Float(), nullable=False),
|
sa.Column("total_amount", sa.Float(), nullable=False),
|
||||||
sa.Column('currency', sa.String(), nullable=True),
|
sa.Column("currency", sa.String(), nullable=True),
|
||||||
sa.Column('shipping_address_id', sa.Integer(), nullable=False),
|
sa.Column("shipping_address_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('billing_address_id', sa.Integer(), nullable=False),
|
sa.Column("billing_address_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('shipping_method', sa.String(), nullable=True),
|
sa.Column("shipping_method", sa.String(), nullable=True),
|
||||||
sa.Column('tracking_number', sa.String(), nullable=True),
|
sa.Column("tracking_number", sa.String(), nullable=True),
|
||||||
sa.Column('customer_notes', sa.Text(), nullable=True),
|
sa.Column("customer_notes", sa.Text(), nullable=True),
|
||||||
sa.Column('internal_notes', sa.Text(), nullable=True),
|
sa.Column("internal_notes", sa.Text(), nullable=True),
|
||||||
sa.Column('paid_at', sa.DateTime(), nullable=True),
|
sa.Column("paid_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('shipped_at', sa.DateTime(), nullable=True),
|
sa.Column("shipped_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('delivered_at', sa.DateTime(), nullable=True),
|
sa.Column("delivered_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('cancelled_at', sa.DateTime(), nullable=True),
|
sa.Column("cancelled_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['billing_address_id'], ['customer_addresses.id']),
|
sa.ForeignKeyConstraint(["billing_address_id"], ["customer_addresses.id"]),
|
||||||
sa.ForeignKeyConstraint(['customer_id'], ['customers.id']),
|
sa.ForeignKeyConstraint(["customer_id"], ["customers.id"]),
|
||||||
sa.ForeignKeyConstraint(['shipping_address_id'], ['customer_addresses.id']),
|
sa.ForeignKeyConstraint(["shipping_address_id"], ["customer_addresses.id"]),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id']),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_orders_customer_id'), 'orders', ['customer_id'], unique=False)
|
op.create_index(op.f("ix_orders_customer_id"), "orders", ["customer_id"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_id'), 'orders', ['id'], unique=False)
|
op.create_index(op.f("ix_orders_id"), "orders", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_order_number'), 'orders', ['order_number'], unique=True)
|
op.create_index(op.f("ix_orders_order_number"), "orders", ["order_number"], unique=True)
|
||||||
op.create_index(op.f('ix_orders_status'), 'orders', ['status'], unique=False)
|
op.create_index(op.f("ix_orders_status"), "orders", ["status"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_vendor_id'), 'orders', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_orders_vendor_id"), "orders", ["vendor_id"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_channel'), 'orders', ['channel'], unique=False)
|
op.create_index(op.f("ix_orders_channel"), "orders", ["channel"], unique=False)
|
||||||
op.create_index(op.f('ix_orders_external_order_id'), 'orders', ['external_order_id'], unique=False)
|
op.create_index(op.f("ix_orders_external_order_id"), "orders", ["external_order_id"], unique=False)
|
||||||
|
|
||||||
# Recreate old order_items table
|
# Recreate old order_items table
|
||||||
op.create_table('order_items',
|
op.create_table("order_items",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('order_id', sa.Integer(), nullable=False),
|
sa.Column("order_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
sa.Column("product_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('product_name', sa.String(), nullable=False),
|
sa.Column("product_name", sa.String(), nullable=False),
|
||||||
sa.Column('product_sku', sa.String(), nullable=True),
|
sa.Column("product_sku", sa.String(), nullable=True),
|
||||||
sa.Column('quantity', sa.Integer(), nullable=False),
|
sa.Column("quantity", sa.Integer(), nullable=False),
|
||||||
sa.Column('unit_price', sa.Float(), nullable=False),
|
sa.Column("unit_price", sa.Float(), nullable=False),
|
||||||
sa.Column('total_price', sa.Float(), nullable=False),
|
sa.Column("total_price", sa.Float(), nullable=False),
|
||||||
sa.Column('inventory_reserved', sa.Boolean(), nullable=True),
|
sa.Column("inventory_reserved", sa.Boolean(), nullable=True),
|
||||||
sa.Column('inventory_fulfilled', sa.Boolean(), nullable=True),
|
sa.Column("inventory_fulfilled", sa.Boolean(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['order_id'], ['orders.id']),
|
sa.ForeignKeyConstraint(["order_id"], ["orders.id"]),
|
||||||
sa.ForeignKeyConstraint(['product_id'], ['products.id']),
|
sa.ForeignKeyConstraint(["product_id"], ["products.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_order_items_id'), 'order_items', ['id'], unique=False)
|
op.create_index(op.f("ix_order_items_id"), "order_items", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_order_items_order_id'), 'order_items', ['order_id'], unique=False)
|
op.create_index(op.f("ix_order_items_order_id"), "order_items", ["order_id"], unique=False)
|
||||||
|
|
||||||
# Recreate old letzshop_orders table
|
# Recreate old letzshop_orders table
|
||||||
op.create_table('letzshop_orders',
|
op.create_table("letzshop_orders",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('letzshop_order_id', sa.String(length=100), nullable=False),
|
sa.Column("letzshop_order_id", sa.String(length=100), nullable=False),
|
||||||
sa.Column('letzshop_shipment_id', sa.String(length=100), nullable=True),
|
sa.Column("letzshop_shipment_id", sa.String(length=100), nullable=True),
|
||||||
sa.Column('letzshop_order_number', sa.String(length=100), nullable=True),
|
sa.Column("letzshop_order_number", sa.String(length=100), nullable=True),
|
||||||
sa.Column('local_order_id', sa.Integer(), nullable=True),
|
sa.Column("local_order_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('letzshop_state', sa.String(length=50), nullable=True),
|
sa.Column("letzshop_state", sa.String(length=50), nullable=True),
|
||||||
sa.Column('customer_email', sa.String(length=255), nullable=True),
|
sa.Column("customer_email", sa.String(length=255), nullable=True),
|
||||||
sa.Column('customer_name', sa.String(length=255), nullable=True),
|
sa.Column("customer_name", sa.String(length=255), nullable=True),
|
||||||
sa.Column('total_amount', sa.String(length=50), nullable=True),
|
sa.Column("total_amount", sa.String(length=50), nullable=True),
|
||||||
sa.Column('currency', sa.String(length=10), server_default='EUR', nullable=True),
|
sa.Column("currency", sa.String(length=10), server_default="EUR", nullable=True),
|
||||||
sa.Column('customer_locale', sa.String(length=10), nullable=True),
|
sa.Column("customer_locale", sa.String(length=10), nullable=True),
|
||||||
sa.Column('shipping_country_iso', sa.String(length=5), nullable=True),
|
sa.Column("shipping_country_iso", sa.String(length=5), nullable=True),
|
||||||
sa.Column('billing_country_iso', sa.String(length=5), nullable=True),
|
sa.Column("billing_country_iso", sa.String(length=5), nullable=True),
|
||||||
sa.Column('order_date', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("order_date", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('raw_order_data', sa.JSON(), nullable=True),
|
sa.Column("raw_order_data", sa.JSON(), nullable=True),
|
||||||
sa.Column('inventory_units', sa.JSON(), nullable=True),
|
sa.Column("inventory_units", sa.JSON(), nullable=True),
|
||||||
sa.Column('sync_status', sa.String(length=50), server_default='pending', nullable=True),
|
sa.Column("sync_status", sa.String(length=50), server_default="pending", nullable=True),
|
||||||
sa.Column('last_synced_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("last_synced_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('sync_error', sa.Text(), nullable=True),
|
sa.Column("sync_error", sa.Text(), nullable=True),
|
||||||
sa.Column('confirmed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("confirmed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('rejected_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("rejected_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('tracking_set_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("tracking_set_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('tracking_number', sa.String(length=100), nullable=True),
|
sa.Column("tracking_number", sa.String(length=100), nullable=True),
|
||||||
sa.Column('tracking_carrier', sa.String(length=100), nullable=True),
|
sa.Column("tracking_carrier", sa.String(length=100), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['local_order_id'], ['orders.id']),
|
sa.ForeignKeyConstraint(["local_order_id"], ["orders.id"]),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id']),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_letzshop_orders_id'), 'letzshop_orders', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_id"), "letzshop_orders", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_orders_letzshop_order_id'), 'letzshop_orders', ['letzshop_order_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_letzshop_order_id"), "letzshop_orders", ["letzshop_order_id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_orders_letzshop_shipment_id'), 'letzshop_orders', ['letzshop_shipment_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_letzshop_shipment_id"), "letzshop_orders", ["letzshop_shipment_id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_orders_vendor_id'), 'letzshop_orders', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_orders_vendor_id"), "letzshop_orders", ["vendor_id"], unique=False)
|
||||||
op.create_index('idx_letzshop_order_vendor', 'letzshop_orders', ['vendor_id', 'letzshop_order_id'], unique=False)
|
op.create_index("idx_letzshop_order_vendor", "letzshop_orders", ["vendor_id", "letzshop_order_id"], unique=False)
|
||||||
op.create_index('idx_letzshop_order_state', 'letzshop_orders', ['vendor_id', 'letzshop_state'], unique=False)
|
op.create_index("idx_letzshop_order_state", "letzshop_orders", ["vendor_id", "letzshop_state"], unique=False)
|
||||||
op.create_index('idx_letzshop_order_sync', 'letzshop_orders', ['vendor_id', 'sync_status'], unique=False)
|
op.create_index("idx_letzshop_order_sync", "letzshop_orders", ["vendor_id", "sync_status"], unique=False)
|
||||||
|
|
||||||
# Recreate old letzshop_fulfillment_queue table
|
# Recreate old letzshop_fulfillment_queue table
|
||||||
op.create_table('letzshop_fulfillment_queue',
|
op.create_table("letzshop_fulfillment_queue",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('letzshop_order_id', sa.Integer(), nullable=False),
|
sa.Column("letzshop_order_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('operation', sa.String(length=50), nullable=False),
|
sa.Column("operation", sa.String(length=50), nullable=False),
|
||||||
sa.Column('payload', sa.JSON(), nullable=False),
|
sa.Column("payload", sa.JSON(), nullable=False),
|
||||||
sa.Column('status', sa.String(length=50), server_default='pending', nullable=True),
|
sa.Column("status", sa.String(length=50), server_default="pending", nullable=True),
|
||||||
sa.Column('attempts', sa.Integer(), server_default='0', nullable=True),
|
sa.Column("attempts", sa.Integer(), server_default="0", nullable=True),
|
||||||
sa.Column('max_attempts', sa.Integer(), server_default='3', nullable=True),
|
sa.Column("max_attempts", sa.Integer(), server_default="3", nullable=True),
|
||||||
sa.Column('last_attempt_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("last_attempt_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('next_retry_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("next_retry_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('error_message', sa.Text(), nullable=True),
|
sa.Column("error_message", sa.Text(), nullable=True),
|
||||||
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('response_data', sa.JSON(), nullable=True),
|
sa.Column("response_data", sa.JSON(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("(CURRENT_TIMESTAMP)"), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['letzshop_order_id'], ['letzshop_orders.id']),
|
sa.ForeignKeyConstraint(["letzshop_order_id"], ["letzshop_orders.id"]),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id']),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_letzshop_fulfillment_queue_id'), 'letzshop_fulfillment_queue', ['id'], unique=False)
|
op.create_index(op.f("ix_letzshop_fulfillment_queue_id"), "letzshop_fulfillment_queue", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_letzshop_fulfillment_queue_vendor_id'), 'letzshop_fulfillment_queue', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_letzshop_fulfillment_queue_vendor_id"), "letzshop_fulfillment_queue", ["vendor_id"], unique=False)
|
||||||
op.create_index('idx_fulfillment_queue_status', 'letzshop_fulfillment_queue', ['status', 'vendor_id'], unique=False)
|
op.create_index("idx_fulfillment_queue_status", "letzshop_fulfillment_queue", ["status", "vendor_id"], unique=False)
|
||||||
op.create_index('idx_fulfillment_queue_retry', 'letzshop_fulfillment_queue', ['status', 'next_retry_at'], unique=False)
|
op.create_index("idx_fulfillment_queue_retry", "letzshop_fulfillment_queue", ["status", "next_retry_at"], unique=False)
|
||||||
|
|||||||
@@ -9,56 +9,56 @@ Adds:
|
|||||||
- cost_cents to products (for profit calculation)
|
- cost_cents to products (for profit calculation)
|
||||||
- Letzshop feed settings to vendors (tax_rate, boost_sort, delivery_method, preorder_days)
|
- Letzshop feed settings to vendors (tax_rate, boost_sort, delivery_method, preorder_days)
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'c9e22eadf533'
|
revision: str = "c9e22eadf533"
|
||||||
down_revision: Union[str, None] = 'e1f2a3b4c5d6'
|
down_revision: str | None = "e1f2a3b4c5d6"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# === MARKETPLACE PRODUCTS: Add tax_rate_percent ===
|
# === MARKETPLACE PRODUCTS: Add tax_rate_percent ===
|
||||||
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
|
with op.batch_alter_table("marketplace_products", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('tax_rate_percent', sa.Integer(), nullable=False, server_default='17'))
|
batch_op.add_column(sa.Column("tax_rate_percent", sa.Integer(), nullable=False, server_default="17"))
|
||||||
|
|
||||||
# === PRODUCTS: Add tax_rate_percent and cost_cents, rename supplier_cost_cents ===
|
# === PRODUCTS: Add tax_rate_percent and cost_cents, rename supplier_cost_cents ===
|
||||||
with op.batch_alter_table('products', schema=None) as batch_op:
|
with op.batch_alter_table("products", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('tax_rate_percent', sa.Integer(), nullable=False, server_default='17'))
|
batch_op.add_column(sa.Column("tax_rate_percent", sa.Integer(), nullable=False, server_default="17"))
|
||||||
batch_op.add_column(sa.Column('cost_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("cost_cents", sa.Integer(), nullable=True))
|
||||||
# Drop old supplier_cost_cents column (data migrated to cost_cents if needed)
|
# Drop old supplier_cost_cents column (data migrated to cost_cents if needed)
|
||||||
try:
|
try:
|
||||||
batch_op.drop_column('supplier_cost_cents')
|
batch_op.drop_column("supplier_cost_cents")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # Column may not exist
|
pass # Column may not exist
|
||||||
|
|
||||||
# === VENDORS: Add Letzshop feed settings ===
|
# === VENDORS: Add Letzshop feed settings ===
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('letzshop_default_tax_rate', sa.Integer(), nullable=False, server_default='17'))
|
batch_op.add_column(sa.Column("letzshop_default_tax_rate", sa.Integer(), nullable=False, server_default="17"))
|
||||||
batch_op.add_column(sa.Column('letzshop_boost_sort', sa.String(length=10), nullable=True, server_default='5.0'))
|
batch_op.add_column(sa.Column("letzshop_boost_sort", sa.String(length=10), nullable=True, server_default="5.0"))
|
||||||
batch_op.add_column(sa.Column('letzshop_delivery_method', sa.String(length=100), nullable=True, server_default='package_delivery'))
|
batch_op.add_column(sa.Column("letzshop_delivery_method", sa.String(length=100), nullable=True, server_default="package_delivery"))
|
||||||
batch_op.add_column(sa.Column('letzshop_preorder_days', sa.Integer(), nullable=True, server_default='1'))
|
batch_op.add_column(sa.Column("letzshop_preorder_days", sa.Integer(), nullable=True, server_default="1"))
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# === VENDORS: Remove Letzshop feed settings ===
|
# === VENDORS: Remove Letzshop feed settings ===
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
batch_op.drop_column('letzshop_preorder_days')
|
batch_op.drop_column("letzshop_preorder_days")
|
||||||
batch_op.drop_column('letzshop_delivery_method')
|
batch_op.drop_column("letzshop_delivery_method")
|
||||||
batch_op.drop_column('letzshop_boost_sort')
|
batch_op.drop_column("letzshop_boost_sort")
|
||||||
batch_op.drop_column('letzshop_default_tax_rate')
|
batch_op.drop_column("letzshop_default_tax_rate")
|
||||||
|
|
||||||
# === PRODUCTS: Remove tax_rate_percent and cost_cents ===
|
# === PRODUCTS: Remove tax_rate_percent and cost_cents ===
|
||||||
with op.batch_alter_table('products', schema=None) as batch_op:
|
with op.batch_alter_table("products", schema=None) as batch_op:
|
||||||
batch_op.drop_column('cost_cents')
|
batch_op.drop_column("cost_cents")
|
||||||
batch_op.drop_column('tax_rate_percent')
|
batch_op.drop_column("tax_rate_percent")
|
||||||
batch_op.add_column(sa.Column('supplier_cost_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("supplier_cost_cents", sa.Integer(), nullable=True))
|
||||||
|
|
||||||
# === MARKETPLACE PRODUCTS: Remove tax_rate_percent ===
|
# === MARKETPLACE PRODUCTS: Remove tax_rate_percent ===
|
||||||
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
|
with op.batch_alter_table("marketplace_products", schema=None) as batch_op:
|
||||||
batch_op.drop_column('tax_rate_percent')
|
batch_op.drop_column("tax_rate_percent")
|
||||||
|
|||||||
@@ -5,33 +5,33 @@ Revises: a9a86cef6cca
|
|||||||
Create Date: 2025-12-18 20:54:55.185857
|
Create Date: 2025-12-18 20:54:55.185857
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'cb88bc9b5f86'
|
revision: str = "cb88bc9b5f86"
|
||||||
down_revision: Union[str, None] = 'a9a86cef6cca'
|
down_revision: str | None = "a9a86cef6cca"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add GTIN (EAN/UPC barcode) columns to products table for order EAN matching
|
# Add GTIN (EAN/UPC barcode) columns to products table for order EAN matching
|
||||||
# gtin: The barcode number (e.g., "0889698273022")
|
# gtin: The barcode number (e.g., "0889698273022")
|
||||||
# gtin_type: The format type from Letzshop (e.g., "gtin13", "gtin14", "isbn13")
|
# gtin_type: The format type from Letzshop (e.g., "gtin13", "gtin14", "isbn13")
|
||||||
op.add_column('products', sa.Column('gtin', sa.String(length=50), nullable=True))
|
op.add_column("products", sa.Column("gtin", sa.String(length=50), nullable=True))
|
||||||
op.add_column('products', sa.Column('gtin_type', sa.String(length=20), nullable=True))
|
op.add_column("products", sa.Column("gtin_type", sa.String(length=20), nullable=True))
|
||||||
|
|
||||||
# Add index for EAN lookups during order matching
|
# Add index for EAN lookups during order matching
|
||||||
op.create_index('idx_product_gtin', 'products', ['gtin'], unique=False)
|
op.create_index("idx_product_gtin", "products", ["gtin"], unique=False)
|
||||||
op.create_index('idx_product_vendor_gtin', 'products', ['vendor_id', 'gtin'], unique=False)
|
op.create_index("idx_product_vendor_gtin", "products", ["vendor_id", "gtin"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_index('idx_product_vendor_gtin', table_name='products')
|
op.drop_index("idx_product_vendor_gtin", table_name="products")
|
||||||
op.drop_index('idx_product_gtin', table_name='products')
|
op.drop_index("idx_product_gtin", table_name="products")
|
||||||
op.drop_column('products', 'gtin_type')
|
op.drop_column("products", "gtin_type")
|
||||||
op.drop_column('products', 'gtin')
|
op.drop_column("products", "gtin")
|
||||||
|
|||||||
@@ -5,73 +5,73 @@ Revises: 0bd9ffaaced1
|
|||||||
Create Date: 2025-11-30 14:58:17.165142
|
Create Date: 2025-11-30 14:58:17.165142
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'd0325d7c0f25'
|
revision: str = "d0325d7c0f25"
|
||||||
down_revision: Union[str, None] = '0bd9ffaaced1'
|
down_revision: str | None = "0bd9ffaaced1"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Create companies table
|
# Create companies table
|
||||||
op.create_table(
|
op.create_table(
|
||||||
'companies',
|
"companies",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('name', sa.String(), nullable=False),
|
sa.Column("name", sa.String(), nullable=False),
|
||||||
sa.Column('description', sa.Text(), nullable=True),
|
sa.Column("description", sa.Text(), nullable=True),
|
||||||
sa.Column('owner_user_id', sa.Integer(), nullable=False),
|
sa.Column("owner_user_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('contact_email', sa.String(), nullable=False),
|
sa.Column("contact_email", sa.String(), nullable=False),
|
||||||
sa.Column('contact_phone', sa.String(), nullable=True),
|
sa.Column("contact_phone", sa.String(), nullable=True),
|
||||||
sa.Column('website', sa.String(), nullable=True),
|
sa.Column("website", sa.String(), nullable=True),
|
||||||
sa.Column('business_address', sa.Text(), nullable=True),
|
sa.Column("business_address", sa.Text(), nullable=True),
|
||||||
sa.Column('tax_number', sa.String(), nullable=True),
|
sa.Column("tax_number", sa.String(), nullable=True),
|
||||||
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
|
sa.Column("is_active", sa.Boolean(), nullable=False, server_default="true"),
|
||||||
sa.Column('is_verified', sa.Boolean(), nullable=False, server_default='false'),
|
sa.Column("is_verified", sa.Boolean(), nullable=False, server_default="false"),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False, server_default=sa.func.now()),
|
sa.Column("created_at", sa.DateTime(), nullable=False, server_default=sa.func.now()),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.func.now(), onupdate=sa.func.now()),
|
sa.Column("updated_at", sa.DateTime(), nullable=False, server_default=sa.func.now(), onupdate=sa.func.now()),
|
||||||
sa.ForeignKeyConstraint(['owner_user_id'], ['users.id'], ),
|
sa.ForeignKeyConstraint(["owner_user_id"], ["users.id"], ),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_companies_id'), 'companies', ['id'], unique=False)
|
op.create_index(op.f("ix_companies_id"), "companies", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_companies_name'), 'companies', ['name'], unique=False)
|
op.create_index(op.f("ix_companies_name"), "companies", ["name"], unique=False)
|
||||||
|
|
||||||
# Use batch mode for SQLite to modify vendors table
|
# Use batch mode for SQLite to modify vendors table
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
# Add company_id column
|
# Add company_id column
|
||||||
batch_op.add_column(sa.Column('company_id', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("company_id", sa.Integer(), nullable=True))
|
||||||
batch_op.create_index(batch_op.f('ix_vendors_company_id'), ['company_id'], unique=False)
|
batch_op.create_index(batch_op.f("ix_vendors_company_id"), ["company_id"], unique=False)
|
||||||
batch_op.create_foreign_key('fk_vendors_company_id', 'companies', ['company_id'], ['id'])
|
batch_op.create_foreign_key("fk_vendors_company_id", "companies", ["company_id"], ["id"])
|
||||||
|
|
||||||
# Remove old contact fields
|
# Remove old contact fields
|
||||||
batch_op.drop_column('contact_email')
|
batch_op.drop_column("contact_email")
|
||||||
batch_op.drop_column('contact_phone')
|
batch_op.drop_column("contact_phone")
|
||||||
batch_op.drop_column('website')
|
batch_op.drop_column("website")
|
||||||
batch_op.drop_column('business_address')
|
batch_op.drop_column("business_address")
|
||||||
batch_op.drop_column('tax_number')
|
batch_op.drop_column("tax_number")
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Use batch mode for SQLite to modify vendors table
|
# Use batch mode for SQLite to modify vendors table
|
||||||
with op.batch_alter_table('vendors', schema=None) as batch_op:
|
with op.batch_alter_table("vendors", schema=None) as batch_op:
|
||||||
# Re-add contact fields to vendors
|
# Re-add contact fields to vendors
|
||||||
batch_op.add_column(sa.Column('tax_number', sa.String(), nullable=True))
|
batch_op.add_column(sa.Column("tax_number", sa.String(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('business_address', sa.Text(), nullable=True))
|
batch_op.add_column(sa.Column("business_address", sa.Text(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('website', sa.String(), nullable=True))
|
batch_op.add_column(sa.Column("website", sa.String(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('contact_phone', sa.String(), nullable=True))
|
batch_op.add_column(sa.Column("contact_phone", sa.String(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('contact_email', sa.String(), nullable=True))
|
batch_op.add_column(sa.Column("contact_email", sa.String(), nullable=True))
|
||||||
|
|
||||||
# Remove company_id from vendors
|
# Remove company_id from vendors
|
||||||
batch_op.drop_constraint('fk_vendors_company_id', type_='foreignkey')
|
batch_op.drop_constraint("fk_vendors_company_id", type_="foreignkey")
|
||||||
batch_op.drop_index(batch_op.f('ix_vendors_company_id'))
|
batch_op.drop_index(batch_op.f("ix_vendors_company_id"))
|
||||||
batch_op.drop_column('company_id')
|
batch_op.drop_column("company_id")
|
||||||
|
|
||||||
# Drop companies table
|
# Drop companies table
|
||||||
op.drop_index(op.f('ix_companies_name'), table_name='companies')
|
op.drop_index(op.f("ix_companies_name"), table_name="companies")
|
||||||
op.drop_index(op.f('ix_companies_id'), table_name='companies')
|
op.drop_index(op.f("ix_companies_id"), table_name="companies")
|
||||||
op.drop_table('companies')
|
op.drop_table("companies")
|
||||||
|
|||||||
@@ -12,25 +12,25 @@ The exception system allows marketplace orders to be imported even when
|
|||||||
products are not found by GTIN. Items are linked to a placeholder product
|
products are not found by GTIN. Items are linked to a placeholder product
|
||||||
and exceptions are tracked for QC resolution.
|
and exceptions are tracked for QC resolution.
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'd2e3f4a5b6c7'
|
revision: str = "d2e3f4a5b6c7"
|
||||||
down_revision: Union[str, None] = 'c1d2e3f4a5b6'
|
down_revision: str | None = "c1d2e3f4a5b6"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def column_exists(table_name: str, column_name: str) -> bool:
|
def column_exists(table_name: str, column_name: str) -> bool:
|
||||||
"""Check if a column exists in a table."""
|
"""Check if a column exists in a table."""
|
||||||
bind = op.get_bind()
|
bind = op.get_bind()
|
||||||
inspector = inspect(bind)
|
inspector = inspect(bind)
|
||||||
columns = [col['name'] for col in inspector.get_columns(table_name)]
|
columns = [col["name"] for col in inspector.get_columns(table_name)]
|
||||||
return column_name in columns
|
return column_name in columns
|
||||||
|
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ def index_exists(index_name: str, table_name: str) -> bool:
|
|||||||
inspector = inspect(bind)
|
inspector = inspect(bind)
|
||||||
try:
|
try:
|
||||||
indexes = inspector.get_indexes(table_name)
|
indexes = inspector.get_indexes(table_name)
|
||||||
return any(idx['name'] == index_name for idx in indexes)
|
return any(idx["name"] == index_name for idx in indexes)
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -56,124 +56,124 @@ def upgrade() -> None:
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Step 1: Add needs_product_match column to order_items
|
# Step 1: Add needs_product_match column to order_items
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
if not column_exists('order_items', 'needs_product_match'):
|
if not column_exists("order_items", "needs_product_match"):
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'order_items',
|
"order_items",
|
||||||
sa.Column(
|
sa.Column(
|
||||||
'needs_product_match',
|
"needs_product_match",
|
||||||
sa.Boolean(),
|
sa.Boolean(),
|
||||||
server_default='0',
|
server_default="0",
|
||||||
nullable=False
|
nullable=False
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if not index_exists('ix_order_items_needs_product_match', 'order_items'):
|
if not index_exists("ix_order_items_needs_product_match", "order_items"):
|
||||||
op.create_index(
|
op.create_index(
|
||||||
'ix_order_items_needs_product_match',
|
"ix_order_items_needs_product_match",
|
||||||
'order_items',
|
"order_items",
|
||||||
['needs_product_match']
|
["needs_product_match"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Step 2: Create order_item_exceptions table
|
# Step 2: Create order_item_exceptions table
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
if not table_exists('order_item_exceptions'):
|
if not table_exists("order_item_exceptions"):
|
||||||
op.create_table(
|
op.create_table(
|
||||||
'order_item_exceptions',
|
"order_item_exceptions",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('order_item_id', sa.Integer(), nullable=False),
|
sa.Column("order_item_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
sa.Column('original_gtin', sa.String(length=50), nullable=True),
|
sa.Column("original_gtin", sa.String(length=50), nullable=True),
|
||||||
sa.Column('original_product_name', sa.String(length=500), nullable=True),
|
sa.Column("original_product_name", sa.String(length=500), nullable=True),
|
||||||
sa.Column('original_sku', sa.String(length=100), nullable=True),
|
sa.Column("original_sku", sa.String(length=100), nullable=True),
|
||||||
sa.Column(
|
sa.Column(
|
||||||
'exception_type',
|
"exception_type",
|
||||||
sa.String(length=50),
|
sa.String(length=50),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default='product_not_found'
|
server_default="product_not_found"
|
||||||
),
|
),
|
||||||
sa.Column(
|
sa.Column(
|
||||||
'status',
|
"status",
|
||||||
sa.String(length=50),
|
sa.String(length=50),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default='pending'
|
server_default="pending"
|
||||||
),
|
),
|
||||||
sa.Column('resolved_product_id', sa.Integer(), nullable=True),
|
sa.Column("resolved_product_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('resolved_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("resolved_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('resolved_by', sa.Integer(), nullable=True),
|
sa.Column("resolved_by", sa.Integer(), nullable=True),
|
||||||
sa.Column('resolution_notes', sa.Text(), nullable=True),
|
sa.Column("resolution_notes", sa.Text(), nullable=True),
|
||||||
sa.Column(
|
sa.Column(
|
||||||
'created_at',
|
"created_at",
|
||||||
sa.DateTime(timezone=True),
|
sa.DateTime(timezone=True),
|
||||||
server_default=sa.text('(CURRENT_TIMESTAMP)'),
|
server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
||||||
nullable=False
|
nullable=False
|
||||||
),
|
),
|
||||||
sa.Column(
|
sa.Column(
|
||||||
'updated_at',
|
"updated_at",
|
||||||
sa.DateTime(timezone=True),
|
sa.DateTime(timezone=True),
|
||||||
server_default=sa.text('(CURRENT_TIMESTAMP)'),
|
server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
||||||
nullable=False
|
nullable=False
|
||||||
),
|
),
|
||||||
sa.ForeignKeyConstraint(
|
sa.ForeignKeyConstraint(
|
||||||
['order_item_id'],
|
["order_item_id"],
|
||||||
['order_items.id'],
|
["order_items.id"],
|
||||||
ondelete='CASCADE'
|
ondelete="CASCADE"
|
||||||
),
|
),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id']),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
|
||||||
sa.ForeignKeyConstraint(['resolved_product_id'], ['products.id']),
|
sa.ForeignKeyConstraint(["resolved_product_id"], ["products.id"]),
|
||||||
sa.ForeignKeyConstraint(['resolved_by'], ['users.id']),
|
sa.ForeignKeyConstraint(["resolved_by"], ["users.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create indexes
|
# Create indexes
|
||||||
op.create_index(
|
op.create_index(
|
||||||
'ix_order_item_exceptions_id',
|
"ix_order_item_exceptions_id",
|
||||||
'order_item_exceptions',
|
"order_item_exceptions",
|
||||||
['id']
|
["id"]
|
||||||
)
|
)
|
||||||
op.create_index(
|
op.create_index(
|
||||||
'ix_order_item_exceptions_vendor_id',
|
"ix_order_item_exceptions_vendor_id",
|
||||||
'order_item_exceptions',
|
"order_item_exceptions",
|
||||||
['vendor_id']
|
["vendor_id"]
|
||||||
)
|
)
|
||||||
op.create_index(
|
op.create_index(
|
||||||
'ix_order_item_exceptions_status',
|
"ix_order_item_exceptions_status",
|
||||||
'order_item_exceptions',
|
"order_item_exceptions",
|
||||||
['status']
|
["status"]
|
||||||
)
|
)
|
||||||
op.create_index(
|
op.create_index(
|
||||||
'idx_exception_vendor_status',
|
"idx_exception_vendor_status",
|
||||||
'order_item_exceptions',
|
"order_item_exceptions",
|
||||||
['vendor_id', 'status']
|
["vendor_id", "status"]
|
||||||
)
|
)
|
||||||
op.create_index(
|
op.create_index(
|
||||||
'idx_exception_gtin',
|
"idx_exception_gtin",
|
||||||
'order_item_exceptions',
|
"order_item_exceptions",
|
||||||
['vendor_id', 'original_gtin']
|
["vendor_id", "original_gtin"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Unique constraint on order_item_id (one exception per item)
|
# Unique constraint on order_item_id (one exception per item)
|
||||||
op.create_index(
|
op.create_index(
|
||||||
'uq_order_item_exception',
|
"uq_order_item_exception",
|
||||||
'order_item_exceptions',
|
"order_item_exceptions",
|
||||||
['order_item_id'],
|
["order_item_id"],
|
||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Drop order_item_exceptions table
|
# Drop order_item_exceptions table
|
||||||
if table_exists('order_item_exceptions'):
|
if table_exists("order_item_exceptions"):
|
||||||
op.drop_index('uq_order_item_exception', table_name='order_item_exceptions')
|
op.drop_index("uq_order_item_exception", table_name="order_item_exceptions")
|
||||||
op.drop_index('idx_exception_gtin', table_name='order_item_exceptions')
|
op.drop_index("idx_exception_gtin", table_name="order_item_exceptions")
|
||||||
op.drop_index('idx_exception_vendor_status', table_name='order_item_exceptions')
|
op.drop_index("idx_exception_vendor_status", table_name="order_item_exceptions")
|
||||||
op.drop_index('ix_order_item_exceptions_status', table_name='order_item_exceptions')
|
op.drop_index("ix_order_item_exceptions_status", table_name="order_item_exceptions")
|
||||||
op.drop_index('ix_order_item_exceptions_vendor_id', table_name='order_item_exceptions')
|
op.drop_index("ix_order_item_exceptions_vendor_id", table_name="order_item_exceptions")
|
||||||
op.drop_index('ix_order_item_exceptions_id', table_name='order_item_exceptions')
|
op.drop_index("ix_order_item_exceptions_id", table_name="order_item_exceptions")
|
||||||
op.drop_table('order_item_exceptions')
|
op.drop_table("order_item_exceptions")
|
||||||
|
|
||||||
# Remove needs_product_match column from order_items
|
# Remove needs_product_match column from order_items
|
||||||
if column_exists('order_items', 'needs_product_match'):
|
if column_exists("order_items", "needs_product_match"):
|
||||||
if index_exists('ix_order_items_needs_product_match', 'order_items'):
|
if index_exists("ix_order_items_needs_product_match", "order_items"):
|
||||||
op.drop_index('ix_order_items_needs_product_match', table_name='order_items')
|
op.drop_index("ix_order_items_needs_product_match", table_name="order_items")
|
||||||
op.drop_column('order_items', 'needs_product_match')
|
op.drop_column("order_items", "needs_product_match")
|
||||||
|
|||||||
@@ -5,87 +5,87 @@ Revises: 404b3e2d2865
|
|||||||
Create Date: 2025-12-27 20:48:00.661523
|
Create Date: 2025-12-27 20:48:00.661523
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'd7a4a3f06394'
|
revision: str = "d7a4a3f06394"
|
||||||
down_revision: Union[str, None] = '404b3e2d2865'
|
down_revision: str | None = "404b3e2d2865"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Create email_templates table
|
# Create email_templates table
|
||||||
op.create_table('email_templates',
|
op.create_table("email_templates",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('code', sa.String(length=100), nullable=False),
|
sa.Column("code", sa.String(length=100), nullable=False),
|
||||||
sa.Column('language', sa.String(length=5), nullable=False),
|
sa.Column("language", sa.String(length=5), nullable=False),
|
||||||
sa.Column('name', sa.String(length=255), nullable=False),
|
sa.Column("name", sa.String(length=255), nullable=False),
|
||||||
sa.Column('description', sa.Text(), nullable=True),
|
sa.Column("description", sa.Text(), nullable=True),
|
||||||
sa.Column('category', sa.String(length=50), nullable=False),
|
sa.Column("category", sa.String(length=50), nullable=False),
|
||||||
sa.Column('subject', sa.String(length=500), nullable=False),
|
sa.Column("subject", sa.String(length=500), nullable=False),
|
||||||
sa.Column('body_html', sa.Text(), nullable=False),
|
sa.Column("body_html", sa.Text(), nullable=False),
|
||||||
sa.Column('body_text', sa.Text(), nullable=True),
|
sa.Column("body_text", sa.Text(), nullable=True),
|
||||||
sa.Column('variables', sa.Text(), nullable=True),
|
sa.Column("variables", sa.Text(), nullable=True),
|
||||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
sa.Column("is_active", sa.Boolean(), nullable=False),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint("id"),
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_email_templates_category'), 'email_templates', ['category'], unique=False)
|
op.create_index(op.f("ix_email_templates_category"), "email_templates", ["category"], unique=False)
|
||||||
op.create_index(op.f('ix_email_templates_code'), 'email_templates', ['code'], unique=False)
|
op.create_index(op.f("ix_email_templates_code"), "email_templates", ["code"], unique=False)
|
||||||
op.create_index(op.f('ix_email_templates_id'), 'email_templates', ['id'], unique=False)
|
op.create_index(op.f("ix_email_templates_id"), "email_templates", ["id"], unique=False)
|
||||||
|
|
||||||
# Create email_logs table
|
# Create email_logs table
|
||||||
op.create_table('email_logs',
|
op.create_table("email_logs",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('template_code', sa.String(length=100), nullable=True),
|
sa.Column("template_code", sa.String(length=100), nullable=True),
|
||||||
sa.Column('template_id', sa.Integer(), nullable=True),
|
sa.Column("template_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('recipient_email', sa.String(length=255), nullable=False),
|
sa.Column("recipient_email", sa.String(length=255), nullable=False),
|
||||||
sa.Column('recipient_name', sa.String(length=255), nullable=True),
|
sa.Column("recipient_name", sa.String(length=255), nullable=True),
|
||||||
sa.Column('subject', sa.String(length=500), nullable=False),
|
sa.Column("subject", sa.String(length=500), nullable=False),
|
||||||
sa.Column('body_html', sa.Text(), nullable=True),
|
sa.Column("body_html", sa.Text(), nullable=True),
|
||||||
sa.Column('body_text', sa.Text(), nullable=True),
|
sa.Column("body_text", sa.Text(), nullable=True),
|
||||||
sa.Column('from_email', sa.String(length=255), nullable=False),
|
sa.Column("from_email", sa.String(length=255), nullable=False),
|
||||||
sa.Column('from_name', sa.String(length=255), nullable=True),
|
sa.Column("from_name", sa.String(length=255), nullable=True),
|
||||||
sa.Column('reply_to', sa.String(length=255), nullable=True),
|
sa.Column("reply_to", sa.String(length=255), nullable=True),
|
||||||
sa.Column('status', sa.String(length=20), nullable=False),
|
sa.Column("status", sa.String(length=20), nullable=False),
|
||||||
sa.Column('sent_at', sa.DateTime(), nullable=True),
|
sa.Column("sent_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('delivered_at', sa.DateTime(), nullable=True),
|
sa.Column("delivered_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('opened_at', sa.DateTime(), nullable=True),
|
sa.Column("opened_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('clicked_at', sa.DateTime(), nullable=True),
|
sa.Column("clicked_at", sa.DateTime(), nullable=True),
|
||||||
sa.Column('error_message', sa.Text(), nullable=True),
|
sa.Column("error_message", sa.Text(), nullable=True),
|
||||||
sa.Column('retry_count', sa.Integer(), nullable=False),
|
sa.Column("retry_count", sa.Integer(), nullable=False),
|
||||||
sa.Column('provider', sa.String(length=50), nullable=True),
|
sa.Column("provider", sa.String(length=50), nullable=True),
|
||||||
sa.Column('provider_message_id', sa.String(length=255), nullable=True),
|
sa.Column("provider_message_id", sa.String(length=255), nullable=True),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=True),
|
sa.Column("vendor_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
sa.Column("user_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('related_type', sa.String(length=50), nullable=True),
|
sa.Column("related_type", sa.String(length=50), nullable=True),
|
||||||
sa.Column('related_id', sa.Integer(), nullable=True),
|
sa.Column("related_id", sa.Integer(), nullable=True),
|
||||||
sa.Column('extra_data', sa.Text(), nullable=True),
|
sa.Column("extra_data", sa.Text(), nullable=True),
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
sa.ForeignKeyConstraint(['template_id'], ['email_templates.id']),
|
sa.ForeignKeyConstraint(["template_id"], ["email_templates.id"]),
|
||||||
sa.ForeignKeyConstraint(['user_id'], ['users.id']),
|
sa.ForeignKeyConstraint(["user_id"], ["users.id"]),
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id']),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
|
||||||
sa.PrimaryKeyConstraint('id')
|
sa.PrimaryKeyConstraint("id")
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_email_logs_id'), 'email_logs', ['id'], unique=False)
|
op.create_index(op.f("ix_email_logs_id"), "email_logs", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_email_logs_provider_message_id'), 'email_logs', ['provider_message_id'], unique=False)
|
op.create_index(op.f("ix_email_logs_provider_message_id"), "email_logs", ["provider_message_id"], unique=False)
|
||||||
op.create_index(op.f('ix_email_logs_recipient_email'), 'email_logs', ['recipient_email'], unique=False)
|
op.create_index(op.f("ix_email_logs_recipient_email"), "email_logs", ["recipient_email"], unique=False)
|
||||||
op.create_index(op.f('ix_email_logs_status'), 'email_logs', ['status'], unique=False)
|
op.create_index(op.f("ix_email_logs_status"), "email_logs", ["status"], unique=False)
|
||||||
op.create_index(op.f('ix_email_logs_template_code'), 'email_logs', ['template_code'], unique=False)
|
op.create_index(op.f("ix_email_logs_template_code"), "email_logs", ["template_code"], unique=False)
|
||||||
op.create_index(op.f('ix_email_logs_user_id'), 'email_logs', ['user_id'], unique=False)
|
op.create_index(op.f("ix_email_logs_user_id"), "email_logs", ["user_id"], unique=False)
|
||||||
op.create_index(op.f('ix_email_logs_vendor_id'), 'email_logs', ['vendor_id'], unique=False)
|
op.create_index(op.f("ix_email_logs_vendor_id"), "email_logs", ["vendor_id"], unique=False)
|
||||||
|
|
||||||
# application_logs - alter columns
|
# application_logs - alter columns
|
||||||
op.alter_column('application_logs', 'created_at', existing_type=sa.DATETIME(), nullable=False)
|
op.alter_column("application_logs", "created_at", existing_type=sa.DATETIME(), nullable=False)
|
||||||
op.alter_column('application_logs', 'updated_at', existing_type=sa.DATETIME(), nullable=False)
|
op.alter_column("application_logs", "updated_at", existing_type=sa.DATETIME(), nullable=False)
|
||||||
|
|
||||||
# capacity_snapshots indexes (PostgreSQL IF EXISTS/IF NOT EXISTS)
|
# capacity_snapshots indexes (PostgreSQL IF EXISTS/IF NOT EXISTS)
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_capacity_snapshots_date"))
|
op.execute(text("DROP INDEX IF EXISTS ix_capacity_snapshots_date"))
|
||||||
@@ -93,17 +93,17 @@ def upgrade() -> None:
|
|||||||
op.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS ix_capacity_snapshots_snapshot_date ON capacity_snapshots (snapshot_date)"))
|
op.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS ix_capacity_snapshots_snapshot_date ON capacity_snapshots (snapshot_date)"))
|
||||||
|
|
||||||
# cart_items - alter columns
|
# cart_items - alter columns
|
||||||
op.alter_column('cart_items', 'created_at', existing_type=sa.DATETIME(), nullable=False)
|
op.alter_column("cart_items", "created_at", existing_type=sa.DATETIME(), nullable=False)
|
||||||
op.alter_column('cart_items', 'updated_at', existing_type=sa.DATETIME(), nullable=False)
|
op.alter_column("cart_items", "updated_at", existing_type=sa.DATETIME(), nullable=False)
|
||||||
|
|
||||||
# customer_addresses index rename
|
# customer_addresses index rename
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_customers_addresses_id"))
|
op.execute(text("DROP INDEX IF EXISTS ix_customers_addresses_id"))
|
||||||
op.execute(text("CREATE INDEX IF NOT EXISTS ix_customer_addresses_id ON customer_addresses (id)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS ix_customer_addresses_id ON customer_addresses (id)"))
|
||||||
|
|
||||||
# inventory - alter columns and constraints
|
# inventory - alter columns and constraints
|
||||||
op.alter_column('inventory', 'warehouse', existing_type=sa.VARCHAR(), nullable=False)
|
op.alter_column("inventory", "warehouse", existing_type=sa.VARCHAR(), nullable=False)
|
||||||
op.alter_column('inventory', 'bin_location', existing_type=sa.VARCHAR(), nullable=False)
|
op.alter_column("inventory", "bin_location", existing_type=sa.VARCHAR(), nullable=False)
|
||||||
op.alter_column('inventory', 'location', existing_type=sa.VARCHAR(), nullable=True)
|
op.alter_column("inventory", "location", existing_type=sa.VARCHAR(), nullable=True)
|
||||||
op.execute(text("DROP INDEX IF EXISTS idx_inventory_product_location"))
|
op.execute(text("DROP INDEX IF EXISTS idx_inventory_product_location"))
|
||||||
op.execute(text("ALTER TABLE inventory DROP CONSTRAINT IF EXISTS uq_inventory_product_location"))
|
op.execute(text("ALTER TABLE inventory DROP CONSTRAINT IF EXISTS uq_inventory_product_location"))
|
||||||
op.execute(text("""
|
op.execute(text("""
|
||||||
@@ -120,8 +120,8 @@ def upgrade() -> None:
|
|||||||
op.execute(text("CREATE INDEX IF NOT EXISTS ix_marketplace_product_translations_id ON marketplace_product_translations (id)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS ix_marketplace_product_translations_id ON marketplace_product_translations (id)"))
|
||||||
|
|
||||||
# marketplace_products - alter columns
|
# marketplace_products - alter columns
|
||||||
op.alter_column('marketplace_products', 'is_digital', existing_type=sa.BOOLEAN(), nullable=True)
|
op.alter_column("marketplace_products", "is_digital", existing_type=sa.BOOLEAN(), nullable=True)
|
||||||
op.alter_column('marketplace_products', 'is_active', existing_type=sa.BOOLEAN(), nullable=True)
|
op.alter_column("marketplace_products", "is_active", existing_type=sa.BOOLEAN(), nullable=True)
|
||||||
|
|
||||||
# marketplace_products indexes
|
# marketplace_products indexes
|
||||||
op.execute(text("DROP INDEX IF EXISTS idx_mp_is_active"))
|
op.execute(text("DROP INDEX IF EXISTS idx_mp_is_active"))
|
||||||
@@ -146,7 +146,7 @@ def upgrade() -> None:
|
|||||||
"""))
|
"""))
|
||||||
|
|
||||||
# order_items - alter column
|
# order_items - alter column
|
||||||
op.alter_column('order_items', 'needs_product_match', existing_type=sa.BOOLEAN(), nullable=True)
|
op.alter_column("order_items", "needs_product_match", existing_type=sa.BOOLEAN(), nullable=True)
|
||||||
|
|
||||||
# order_items indexes
|
# order_items indexes
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_order_items_gtin"))
|
op.execute(text("DROP INDEX IF EXISTS ix_order_items_gtin"))
|
||||||
@@ -185,7 +185,7 @@ def upgrade() -> None:
|
|||||||
op.execute(text("CREATE INDEX IF NOT EXISTS ix_vendor_domains_id ON vendor_domains (id)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS ix_vendor_domains_id ON vendor_domains (id)"))
|
||||||
|
|
||||||
# vendor_subscriptions - alter column and FK
|
# vendor_subscriptions - alter column and FK
|
||||||
op.alter_column('vendor_subscriptions', 'payment_retry_count', existing_type=sa.INTEGER(), nullable=False)
|
op.alter_column("vendor_subscriptions", "payment_retry_count", existing_type=sa.INTEGER(), nullable=False)
|
||||||
op.execute(text("""
|
op.execute(text("""
|
||||||
DO $$
|
DO $$
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -207,12 +207,12 @@ def upgrade() -> None:
|
|||||||
op.execute(text("CREATE INDEX IF NOT EXISTS ix_vendor_users_invitation_token ON vendor_users (invitation_token)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS ix_vendor_users_invitation_token ON vendor_users (invitation_token)"))
|
||||||
|
|
||||||
# vendors - alter column
|
# vendors - alter column
|
||||||
op.alter_column('vendors', 'company_id', existing_type=sa.INTEGER(), nullable=False)
|
op.alter_column("vendors", "company_id", existing_type=sa.INTEGER(), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# vendors
|
# vendors
|
||||||
op.alter_column('vendors', 'company_id', existing_type=sa.INTEGER(), nullable=True)
|
op.alter_column("vendors", "company_id", existing_type=sa.INTEGER(), nullable=True)
|
||||||
|
|
||||||
# vendor_users indexes
|
# vendor_users indexes
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_vendor_users_invitation_token"))
|
op.execute(text("DROP INDEX IF EXISTS ix_vendor_users_invitation_token"))
|
||||||
@@ -226,7 +226,7 @@ def downgrade() -> None:
|
|||||||
|
|
||||||
# vendor_subscriptions
|
# vendor_subscriptions
|
||||||
op.execute(text("ALTER TABLE vendor_subscriptions DROP CONSTRAINT IF EXISTS fk_vendor_subscriptions_tier_id"))
|
op.execute(text("ALTER TABLE vendor_subscriptions DROP CONSTRAINT IF EXISTS fk_vendor_subscriptions_tier_id"))
|
||||||
op.alter_column('vendor_subscriptions', 'payment_retry_count', existing_type=sa.INTEGER(), nullable=True)
|
op.alter_column("vendor_subscriptions", "payment_retry_count", existing_type=sa.INTEGER(), nullable=True)
|
||||||
|
|
||||||
# vendor_domains indexes
|
# vendor_domains indexes
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_vendor_domains_id"))
|
op.execute(text("DROP INDEX IF EXISTS ix_vendor_domains_id"))
|
||||||
@@ -260,7 +260,7 @@ def downgrade() -> None:
|
|||||||
# order_items
|
# order_items
|
||||||
op.execute(text("CREATE INDEX IF NOT EXISTS ix_order_items_product_id ON order_items (product_id)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS ix_order_items_product_id ON order_items (product_id)"))
|
||||||
op.execute(text("CREATE INDEX IF NOT EXISTS ix_order_items_gtin ON order_items (gtin)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS ix_order_items_gtin ON order_items (gtin)"))
|
||||||
op.alter_column('order_items', 'needs_product_match', existing_type=sa.BOOLEAN(), nullable=False)
|
op.alter_column("order_items", "needs_product_match", existing_type=sa.BOOLEAN(), nullable=False)
|
||||||
|
|
||||||
# order_item_exceptions
|
# order_item_exceptions
|
||||||
op.execute(text("ALTER TABLE order_item_exceptions DROP CONSTRAINT IF EXISTS uq_order_item_exceptions_order_item_id"))
|
op.execute(text("ALTER TABLE order_item_exceptions DROP CONSTRAINT IF EXISTS uq_order_item_exceptions_order_item_id"))
|
||||||
@@ -278,8 +278,8 @@ def downgrade() -> None:
|
|||||||
op.execute(text("CREATE INDEX IF NOT EXISTS idx_mp_is_active ON marketplace_products (is_active)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS idx_mp_is_active ON marketplace_products (is_active)"))
|
||||||
|
|
||||||
# marketplace_products columns
|
# marketplace_products columns
|
||||||
op.alter_column('marketplace_products', 'is_active', existing_type=sa.BOOLEAN(), nullable=False)
|
op.alter_column("marketplace_products", "is_active", existing_type=sa.BOOLEAN(), nullable=False)
|
||||||
op.alter_column('marketplace_products', 'is_digital', existing_type=sa.BOOLEAN(), nullable=False)
|
op.alter_column("marketplace_products", "is_digital", existing_type=sa.BOOLEAN(), nullable=False)
|
||||||
|
|
||||||
# marketplace imports
|
# marketplace imports
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_marketplace_product_translations_id"))
|
op.execute(text("DROP INDEX IF EXISTS ix_marketplace_product_translations_id"))
|
||||||
@@ -296,17 +296,17 @@ def downgrade() -> None:
|
|||||||
END $$;
|
END $$;
|
||||||
"""))
|
"""))
|
||||||
op.execute(text("CREATE INDEX IF NOT EXISTS idx_inventory_product_location ON inventory (product_id, location)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS idx_inventory_product_location ON inventory (product_id, location)"))
|
||||||
op.alter_column('inventory', 'location', existing_type=sa.VARCHAR(), nullable=False)
|
op.alter_column("inventory", "location", existing_type=sa.VARCHAR(), nullable=False)
|
||||||
op.alter_column('inventory', 'bin_location', existing_type=sa.VARCHAR(), nullable=True)
|
op.alter_column("inventory", "bin_location", existing_type=sa.VARCHAR(), nullable=True)
|
||||||
op.alter_column('inventory', 'warehouse', existing_type=sa.VARCHAR(), nullable=True)
|
op.alter_column("inventory", "warehouse", existing_type=sa.VARCHAR(), nullable=True)
|
||||||
|
|
||||||
# customer_addresses
|
# customer_addresses
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_customer_addresses_id"))
|
op.execute(text("DROP INDEX IF EXISTS ix_customer_addresses_id"))
|
||||||
op.execute(text("CREATE INDEX IF NOT EXISTS ix_customers_addresses_id ON customer_addresses (id)"))
|
op.execute(text("CREATE INDEX IF NOT EXISTS ix_customers_addresses_id ON customer_addresses (id)"))
|
||||||
|
|
||||||
# cart_items
|
# cart_items
|
||||||
op.alter_column('cart_items', 'updated_at', existing_type=sa.DATETIME(), nullable=True)
|
op.alter_column("cart_items", "updated_at", existing_type=sa.DATETIME(), nullable=True)
|
||||||
op.alter_column('cart_items', 'created_at', existing_type=sa.DATETIME(), nullable=True)
|
op.alter_column("cart_items", "created_at", existing_type=sa.DATETIME(), nullable=True)
|
||||||
|
|
||||||
# capacity_snapshots
|
# capacity_snapshots
|
||||||
op.execute(text("DROP INDEX IF EXISTS ix_capacity_snapshots_snapshot_date"))
|
op.execute(text("DROP INDEX IF EXISTS ix_capacity_snapshots_snapshot_date"))
|
||||||
@@ -314,19 +314,19 @@ def downgrade() -> None:
|
|||||||
op.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS ix_capacity_snapshots_date ON capacity_snapshots (snapshot_date)"))
|
op.execute(text("CREATE UNIQUE INDEX IF NOT EXISTS ix_capacity_snapshots_date ON capacity_snapshots (snapshot_date)"))
|
||||||
|
|
||||||
# application_logs
|
# application_logs
|
||||||
op.alter_column('application_logs', 'updated_at', existing_type=sa.DATETIME(), nullable=True)
|
op.alter_column("application_logs", "updated_at", existing_type=sa.DATETIME(), nullable=True)
|
||||||
op.alter_column('application_logs', 'created_at', existing_type=sa.DATETIME(), nullable=True)
|
op.alter_column("application_logs", "created_at", existing_type=sa.DATETIME(), nullable=True)
|
||||||
|
|
||||||
# Drop email tables
|
# Drop email tables
|
||||||
op.drop_index(op.f('ix_email_logs_vendor_id'), table_name='email_logs')
|
op.drop_index(op.f("ix_email_logs_vendor_id"), table_name="email_logs")
|
||||||
op.drop_index(op.f('ix_email_logs_user_id'), table_name='email_logs')
|
op.drop_index(op.f("ix_email_logs_user_id"), table_name="email_logs")
|
||||||
op.drop_index(op.f('ix_email_logs_template_code'), table_name='email_logs')
|
op.drop_index(op.f("ix_email_logs_template_code"), table_name="email_logs")
|
||||||
op.drop_index(op.f('ix_email_logs_status'), table_name='email_logs')
|
op.drop_index(op.f("ix_email_logs_status"), table_name="email_logs")
|
||||||
op.drop_index(op.f('ix_email_logs_recipient_email'), table_name='email_logs')
|
op.drop_index(op.f("ix_email_logs_recipient_email"), table_name="email_logs")
|
||||||
op.drop_index(op.f('ix_email_logs_provider_message_id'), table_name='email_logs')
|
op.drop_index(op.f("ix_email_logs_provider_message_id"), table_name="email_logs")
|
||||||
op.drop_index(op.f('ix_email_logs_id'), table_name='email_logs')
|
op.drop_index(op.f("ix_email_logs_id"), table_name="email_logs")
|
||||||
op.drop_table('email_logs')
|
op.drop_table("email_logs")
|
||||||
op.drop_index(op.f('ix_email_templates_id'), table_name='email_templates')
|
op.drop_index(op.f("ix_email_templates_id"), table_name="email_templates")
|
||||||
op.drop_index(op.f('ix_email_templates_code'), table_name='email_templates')
|
op.drop_index(op.f("ix_email_templates_code"), table_name="email_templates")
|
||||||
op.drop_index(op.f('ix_email_templates_category'), table_name='email_templates')
|
op.drop_index(op.f("ix_email_templates_category"), table_name="email_templates")
|
||||||
op.drop_table('email_templates')
|
op.drop_table("email_templates")
|
||||||
|
|||||||
@@ -17,16 +17,17 @@ It also renames 'product_type' to 'product_type_raw' to preserve the original
|
|||||||
Google Shopping feed value while using 'product_type' for the new enum.
|
Google Shopping feed value while using 'product_type' for the new enum.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "e1a2b3c4d5e6"
|
revision: str = "e1a2b3c4d5e6"
|
||||||
down_revision: Union[str, None] = "28d44d503cac"
|
down_revision: str | None = "28d44d503cac"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ Revises: j8e9f0a1b2c3
|
|||||||
Create Date: 2025-12-25 12:21:24.006548
|
Create Date: 2025-12-25 12:21:24.006548
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'e1bfb453fbe9'
|
revision: str = "e1bfb453fbe9"
|
||||||
down_revision: Union[str, None] = 'j8e9f0a1b2c3'
|
down_revision: str | None = "j8e9f0a1b2c3"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def get_column_names(conn, table_name: str) -> set:
|
def get_column_names(conn, table_name: str) -> set:
|
||||||
@@ -43,11 +43,11 @@ def upgrade() -> None:
|
|||||||
# Check if columns already exist (idempotent)
|
# Check if columns already exist (idempotent)
|
||||||
columns = get_column_names(conn, "inventory")
|
columns = get_column_names(conn, "inventory")
|
||||||
|
|
||||||
if 'warehouse' not in columns:
|
if "warehouse" not in columns:
|
||||||
op.add_column('inventory', sa.Column('warehouse', sa.String(), nullable=False, server_default='strassen'))
|
op.add_column("inventory", sa.Column("warehouse", sa.String(), nullable=False, server_default="strassen"))
|
||||||
|
|
||||||
if 'bin_location' not in columns:
|
if "bin_location" not in columns:
|
||||||
op.add_column('inventory', sa.Column('bin_location', sa.String(), nullable=False, server_default=''))
|
op.add_column("inventory", sa.Column("bin_location", sa.String(), nullable=False, server_default=""))
|
||||||
|
|
||||||
# Migrate existing data: copy location to bin_location, set default warehouse
|
# Migrate existing data: copy location to bin_location, set default warehouse
|
||||||
conn.execute(text("""
|
conn.execute(text("""
|
||||||
@@ -60,12 +60,12 @@ def upgrade() -> None:
|
|||||||
# Create indexes if they don't exist
|
# Create indexes if they don't exist
|
||||||
existing_indexes = get_index_names(conn, "inventory")
|
existing_indexes = get_index_names(conn, "inventory")
|
||||||
|
|
||||||
if 'idx_inventory_warehouse_bin' not in existing_indexes:
|
if "idx_inventory_warehouse_bin" not in existing_indexes:
|
||||||
op.create_index('idx_inventory_warehouse_bin', 'inventory', ['warehouse', 'bin_location'], unique=False)
|
op.create_index("idx_inventory_warehouse_bin", "inventory", ["warehouse", "bin_location"], unique=False)
|
||||||
if 'ix_inventory_bin_location' not in existing_indexes:
|
if "ix_inventory_bin_location" not in existing_indexes:
|
||||||
op.create_index(op.f('ix_inventory_bin_location'), 'inventory', ['bin_location'], unique=False)
|
op.create_index(op.f("ix_inventory_bin_location"), "inventory", ["bin_location"], unique=False)
|
||||||
if 'ix_inventory_warehouse' not in existing_indexes:
|
if "ix_inventory_warehouse" not in existing_indexes:
|
||||||
op.create_index(op.f('ix_inventory_warehouse'), 'inventory', ['warehouse'], unique=False)
|
op.create_index(op.f("ix_inventory_warehouse"), "inventory", ["warehouse"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
@@ -74,17 +74,17 @@ def downgrade() -> None:
|
|||||||
# Check which indexes exist before dropping
|
# Check which indexes exist before dropping
|
||||||
existing_indexes = get_index_names(conn, "inventory")
|
existing_indexes = get_index_names(conn, "inventory")
|
||||||
|
|
||||||
if 'ix_inventory_warehouse' in existing_indexes:
|
if "ix_inventory_warehouse" in existing_indexes:
|
||||||
op.drop_index(op.f('ix_inventory_warehouse'), table_name='inventory')
|
op.drop_index(op.f("ix_inventory_warehouse"), table_name="inventory")
|
||||||
if 'ix_inventory_bin_location' in existing_indexes:
|
if "ix_inventory_bin_location" in existing_indexes:
|
||||||
op.drop_index(op.f('ix_inventory_bin_location'), table_name='inventory')
|
op.drop_index(op.f("ix_inventory_bin_location"), table_name="inventory")
|
||||||
if 'idx_inventory_warehouse_bin' in existing_indexes:
|
if "idx_inventory_warehouse_bin" in existing_indexes:
|
||||||
op.drop_index('idx_inventory_warehouse_bin', table_name='inventory')
|
op.drop_index("idx_inventory_warehouse_bin", table_name="inventory")
|
||||||
|
|
||||||
# Check if columns exist before dropping
|
# Check if columns exist before dropping
|
||||||
columns = get_column_names(conn, "inventory")
|
columns = get_column_names(conn, "inventory")
|
||||||
|
|
||||||
if 'bin_location' in columns:
|
if "bin_location" in columns:
|
||||||
op.drop_column('inventory', 'bin_location')
|
op.drop_column("inventory", "bin_location")
|
||||||
if 'warehouse' in columns:
|
if "warehouse" in columns:
|
||||||
op.drop_column('inventory', 'warehouse')
|
op.drop_column("inventory", "warehouse")
|
||||||
|
|||||||
@@ -20,17 +20,17 @@ Affected tables:
|
|||||||
See docs/architecture/money-handling.md for full documentation.
|
See docs/architecture/money-handling.md for full documentation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'e1f2a3b4c5d6'
|
revision: str = "e1f2a3b4c5d6"
|
||||||
down_revision: Union[str, None] = 'c00d2985701f'
|
down_revision: str | None = "c00d2985701f"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
@@ -38,186 +38,186 @@ def upgrade() -> None:
|
|||||||
# Strategy: Add new _cents columns, migrate data, drop old columns
|
# Strategy: Add new _cents columns, migrate data, drop old columns
|
||||||
|
|
||||||
# === PRODUCTS TABLE ===
|
# === PRODUCTS TABLE ===
|
||||||
with op.batch_alter_table('products', schema=None) as batch_op:
|
with op.batch_alter_table("products", schema=None) as batch_op:
|
||||||
# Add new cents columns
|
# Add new cents columns
|
||||||
batch_op.add_column(sa.Column('price_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("price_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('sale_price_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("sale_price_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('supplier_cost_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("supplier_cost_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('margin_percent_x100', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("margin_percent_x100", sa.Integer(), nullable=True))
|
||||||
|
|
||||||
# Migrate data for products
|
# Migrate data for products
|
||||||
op.execute('UPDATE products SET price_cents = ROUND(COALESCE(price, 0) * 100)')
|
op.execute("UPDATE products SET price_cents = ROUND(COALESCE(price, 0) * 100)")
|
||||||
op.execute('UPDATE products SET sale_price_cents = ROUND(sale_price * 100) WHERE sale_price IS NOT NULL')
|
op.execute("UPDATE products SET sale_price_cents = ROUND(sale_price * 100) WHERE sale_price IS NOT NULL")
|
||||||
op.execute('UPDATE products SET supplier_cost_cents = ROUND(supplier_cost * 100) WHERE supplier_cost IS NOT NULL')
|
op.execute("UPDATE products SET supplier_cost_cents = ROUND(supplier_cost * 100) WHERE supplier_cost IS NOT NULL")
|
||||||
op.execute('UPDATE products SET margin_percent_x100 = ROUND(margin_percent * 100) WHERE margin_percent IS NOT NULL')
|
op.execute("UPDATE products SET margin_percent_x100 = ROUND(margin_percent * 100) WHERE margin_percent IS NOT NULL")
|
||||||
|
|
||||||
# Drop old columns
|
# Drop old columns
|
||||||
with op.batch_alter_table('products', schema=None) as batch_op:
|
with op.batch_alter_table("products", schema=None) as batch_op:
|
||||||
batch_op.drop_column('price')
|
batch_op.drop_column("price")
|
||||||
batch_op.drop_column('sale_price')
|
batch_op.drop_column("sale_price")
|
||||||
batch_op.drop_column('supplier_cost')
|
batch_op.drop_column("supplier_cost")
|
||||||
batch_op.drop_column('margin_percent')
|
batch_op.drop_column("margin_percent")
|
||||||
|
|
||||||
# === ORDERS TABLE ===
|
# === ORDERS TABLE ===
|
||||||
with op.batch_alter_table('orders', schema=None) as batch_op:
|
with op.batch_alter_table("orders", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('subtotal_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("subtotal_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('tax_amount_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("tax_amount_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('shipping_amount_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("shipping_amount_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('discount_amount_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("discount_amount_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('total_amount_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("total_amount_cents", sa.Integer(), nullable=True))
|
||||||
|
|
||||||
# Migrate data for orders
|
# Migrate data for orders
|
||||||
op.execute('UPDATE orders SET subtotal_cents = ROUND(COALESCE(subtotal, 0) * 100)')
|
op.execute("UPDATE orders SET subtotal_cents = ROUND(COALESCE(subtotal, 0) * 100)")
|
||||||
op.execute('UPDATE orders SET tax_amount_cents = ROUND(COALESCE(tax_amount, 0) * 100)')
|
op.execute("UPDATE orders SET tax_amount_cents = ROUND(COALESCE(tax_amount, 0) * 100)")
|
||||||
op.execute('UPDATE orders SET shipping_amount_cents = ROUND(COALESCE(shipping_amount, 0) * 100)')
|
op.execute("UPDATE orders SET shipping_amount_cents = ROUND(COALESCE(shipping_amount, 0) * 100)")
|
||||||
op.execute('UPDATE orders SET discount_amount_cents = ROUND(COALESCE(discount_amount, 0) * 100)')
|
op.execute("UPDATE orders SET discount_amount_cents = ROUND(COALESCE(discount_amount, 0) * 100)")
|
||||||
op.execute('UPDATE orders SET total_amount_cents = ROUND(COALESCE(total_amount, 0) * 100)')
|
op.execute("UPDATE orders SET total_amount_cents = ROUND(COALESCE(total_amount, 0) * 100)")
|
||||||
|
|
||||||
# Make total_amount_cents NOT NULL after migration
|
# Make total_amount_cents NOT NULL after migration
|
||||||
with op.batch_alter_table('orders', schema=None) as batch_op:
|
with op.batch_alter_table("orders", schema=None) as batch_op:
|
||||||
batch_op.drop_column('subtotal')
|
batch_op.drop_column("subtotal")
|
||||||
batch_op.drop_column('tax_amount')
|
batch_op.drop_column("tax_amount")
|
||||||
batch_op.drop_column('shipping_amount')
|
batch_op.drop_column("shipping_amount")
|
||||||
batch_op.drop_column('discount_amount')
|
batch_op.drop_column("discount_amount")
|
||||||
batch_op.drop_column('total_amount')
|
batch_op.drop_column("total_amount")
|
||||||
# Alter total_amount_cents to be NOT NULL
|
# Alter total_amount_cents to be NOT NULL
|
||||||
batch_op.alter_column('total_amount_cents',
|
batch_op.alter_column("total_amount_cents",
|
||||||
existing_type=sa.Integer(),
|
existing_type=sa.Integer(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|
||||||
# === ORDER_ITEMS TABLE ===
|
# === ORDER_ITEMS TABLE ===
|
||||||
with op.batch_alter_table('order_items', schema=None) as batch_op:
|
with op.batch_alter_table("order_items", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('unit_price_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("unit_price_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('total_price_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("total_price_cents", sa.Integer(), nullable=True))
|
||||||
|
|
||||||
# Migrate data for order_items
|
# Migrate data for order_items
|
||||||
op.execute('UPDATE order_items SET unit_price_cents = ROUND(COALESCE(unit_price, 0) * 100)')
|
op.execute("UPDATE order_items SET unit_price_cents = ROUND(COALESCE(unit_price, 0) * 100)")
|
||||||
op.execute('UPDATE order_items SET total_price_cents = ROUND(COALESCE(total_price, 0) * 100)')
|
op.execute("UPDATE order_items SET total_price_cents = ROUND(COALESCE(total_price, 0) * 100)")
|
||||||
|
|
||||||
with op.batch_alter_table('order_items', schema=None) as batch_op:
|
with op.batch_alter_table("order_items", schema=None) as batch_op:
|
||||||
batch_op.drop_column('unit_price')
|
batch_op.drop_column("unit_price")
|
||||||
batch_op.drop_column('total_price')
|
batch_op.drop_column("total_price")
|
||||||
batch_op.alter_column('unit_price_cents',
|
batch_op.alter_column("unit_price_cents",
|
||||||
existing_type=sa.Integer(),
|
existing_type=sa.Integer(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
batch_op.alter_column('total_price_cents',
|
batch_op.alter_column("total_price_cents",
|
||||||
existing_type=sa.Integer(),
|
existing_type=sa.Integer(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|
||||||
# === CART_ITEMS TABLE ===
|
# === CART_ITEMS TABLE ===
|
||||||
with op.batch_alter_table('cart_items', schema=None) as batch_op:
|
with op.batch_alter_table("cart_items", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('price_at_add_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("price_at_add_cents", sa.Integer(), nullable=True))
|
||||||
|
|
||||||
# Migrate data for cart_items
|
# Migrate data for cart_items
|
||||||
op.execute('UPDATE cart_items SET price_at_add_cents = ROUND(COALESCE(price_at_add, 0) * 100)')
|
op.execute("UPDATE cart_items SET price_at_add_cents = ROUND(COALESCE(price_at_add, 0) * 100)")
|
||||||
|
|
||||||
with op.batch_alter_table('cart_items', schema=None) as batch_op:
|
with op.batch_alter_table("cart_items", schema=None) as batch_op:
|
||||||
batch_op.drop_column('price_at_add')
|
batch_op.drop_column("price_at_add")
|
||||||
batch_op.alter_column('price_at_add_cents',
|
batch_op.alter_column("price_at_add_cents",
|
||||||
existing_type=sa.Integer(),
|
existing_type=sa.Integer(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|
||||||
# === MARKETPLACE_PRODUCTS TABLE ===
|
# === MARKETPLACE_PRODUCTS TABLE ===
|
||||||
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
|
with op.batch_alter_table("marketplace_products", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('price_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("price_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('sale_price_cents', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("sale_price_cents", sa.Integer(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('weight_grams', sa.Integer(), nullable=True))
|
batch_op.add_column(sa.Column("weight_grams", sa.Integer(), nullable=True))
|
||||||
|
|
||||||
# Migrate data for marketplace_products
|
# Migrate data for marketplace_products
|
||||||
op.execute('UPDATE marketplace_products SET price_cents = ROUND(price_numeric * 100) WHERE price_numeric IS NOT NULL')
|
op.execute("UPDATE marketplace_products SET price_cents = ROUND(price_numeric * 100) WHERE price_numeric IS NOT NULL")
|
||||||
op.execute('UPDATE marketplace_products SET sale_price_cents = ROUND(sale_price_numeric * 100) WHERE sale_price_numeric IS NOT NULL')
|
op.execute("UPDATE marketplace_products SET sale_price_cents = ROUND(sale_price_numeric * 100) WHERE sale_price_numeric IS NOT NULL")
|
||||||
op.execute('UPDATE marketplace_products SET weight_grams = ROUND(weight * 1000) WHERE weight IS NOT NULL')
|
op.execute("UPDATE marketplace_products SET weight_grams = ROUND(weight * 1000) WHERE weight IS NOT NULL")
|
||||||
|
|
||||||
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
|
with op.batch_alter_table("marketplace_products", schema=None) as batch_op:
|
||||||
batch_op.drop_column('price_numeric')
|
batch_op.drop_column("price_numeric")
|
||||||
batch_op.drop_column('sale_price_numeric')
|
batch_op.drop_column("sale_price_numeric")
|
||||||
batch_op.drop_column('weight')
|
batch_op.drop_column("weight")
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# === MARKETPLACE_PRODUCTS TABLE ===
|
# === MARKETPLACE_PRODUCTS TABLE ===
|
||||||
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
|
with op.batch_alter_table("marketplace_products", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('price_numeric', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("price_numeric", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('sale_price_numeric', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("sale_price_numeric", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('weight', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("weight", sa.Float(), nullable=True))
|
||||||
|
|
||||||
op.execute('UPDATE marketplace_products SET price_numeric = price_cents / 100.0 WHERE price_cents IS NOT NULL')
|
op.execute("UPDATE marketplace_products SET price_numeric = price_cents / 100.0 WHERE price_cents IS NOT NULL")
|
||||||
op.execute('UPDATE marketplace_products SET sale_price_numeric = sale_price_cents / 100.0 WHERE sale_price_cents IS NOT NULL')
|
op.execute("UPDATE marketplace_products SET sale_price_numeric = sale_price_cents / 100.0 WHERE sale_price_cents IS NOT NULL")
|
||||||
op.execute('UPDATE marketplace_products SET weight = weight_grams / 1000.0 WHERE weight_grams IS NOT NULL')
|
op.execute("UPDATE marketplace_products SET weight = weight_grams / 1000.0 WHERE weight_grams IS NOT NULL")
|
||||||
|
|
||||||
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
|
with op.batch_alter_table("marketplace_products", schema=None) as batch_op:
|
||||||
batch_op.drop_column('price_cents')
|
batch_op.drop_column("price_cents")
|
||||||
batch_op.drop_column('sale_price_cents')
|
batch_op.drop_column("sale_price_cents")
|
||||||
batch_op.drop_column('weight_grams')
|
batch_op.drop_column("weight_grams")
|
||||||
|
|
||||||
# === CART_ITEMS TABLE ===
|
# === CART_ITEMS TABLE ===
|
||||||
with op.batch_alter_table('cart_items', schema=None) as batch_op:
|
with op.batch_alter_table("cart_items", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('price_at_add', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("price_at_add", sa.Float(), nullable=True))
|
||||||
|
|
||||||
op.execute('UPDATE cart_items SET price_at_add = price_at_add_cents / 100.0')
|
op.execute("UPDATE cart_items SET price_at_add = price_at_add_cents / 100.0")
|
||||||
|
|
||||||
with op.batch_alter_table('cart_items', schema=None) as batch_op:
|
with op.batch_alter_table("cart_items", schema=None) as batch_op:
|
||||||
batch_op.drop_column('price_at_add_cents')
|
batch_op.drop_column("price_at_add_cents")
|
||||||
batch_op.alter_column('price_at_add',
|
batch_op.alter_column("price_at_add",
|
||||||
existing_type=sa.Float(),
|
existing_type=sa.Float(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|
||||||
# === ORDER_ITEMS TABLE ===
|
# === ORDER_ITEMS TABLE ===
|
||||||
with op.batch_alter_table('order_items', schema=None) as batch_op:
|
with op.batch_alter_table("order_items", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('unit_price', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("unit_price", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('total_price', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("total_price", sa.Float(), nullable=True))
|
||||||
|
|
||||||
op.execute('UPDATE order_items SET unit_price = unit_price_cents / 100.0')
|
op.execute("UPDATE order_items SET unit_price = unit_price_cents / 100.0")
|
||||||
op.execute('UPDATE order_items SET total_price = total_price_cents / 100.0')
|
op.execute("UPDATE order_items SET total_price = total_price_cents / 100.0")
|
||||||
|
|
||||||
with op.batch_alter_table('order_items', schema=None) as batch_op:
|
with op.batch_alter_table("order_items", schema=None) as batch_op:
|
||||||
batch_op.drop_column('unit_price_cents')
|
batch_op.drop_column("unit_price_cents")
|
||||||
batch_op.drop_column('total_price_cents')
|
batch_op.drop_column("total_price_cents")
|
||||||
batch_op.alter_column('unit_price',
|
batch_op.alter_column("unit_price",
|
||||||
existing_type=sa.Float(),
|
existing_type=sa.Float(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
batch_op.alter_column('total_price',
|
batch_op.alter_column("total_price",
|
||||||
existing_type=sa.Float(),
|
existing_type=sa.Float(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|
||||||
# === ORDERS TABLE ===
|
# === ORDERS TABLE ===
|
||||||
with op.batch_alter_table('orders', schema=None) as batch_op:
|
with op.batch_alter_table("orders", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('subtotal', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("subtotal", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('tax_amount', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("tax_amount", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('shipping_amount', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("shipping_amount", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('discount_amount', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("discount_amount", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('total_amount', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("total_amount", sa.Float(), nullable=True))
|
||||||
|
|
||||||
op.execute('UPDATE orders SET subtotal = subtotal_cents / 100.0')
|
op.execute("UPDATE orders SET subtotal = subtotal_cents / 100.0")
|
||||||
op.execute('UPDATE orders SET tax_amount = tax_amount_cents / 100.0')
|
op.execute("UPDATE orders SET tax_amount = tax_amount_cents / 100.0")
|
||||||
op.execute('UPDATE orders SET shipping_amount = shipping_amount_cents / 100.0')
|
op.execute("UPDATE orders SET shipping_amount = shipping_amount_cents / 100.0")
|
||||||
op.execute('UPDATE orders SET discount_amount = discount_amount_cents / 100.0')
|
op.execute("UPDATE orders SET discount_amount = discount_amount_cents / 100.0")
|
||||||
op.execute('UPDATE orders SET total_amount = total_amount_cents / 100.0')
|
op.execute("UPDATE orders SET total_amount = total_amount_cents / 100.0")
|
||||||
|
|
||||||
with op.batch_alter_table('orders', schema=None) as batch_op:
|
with op.batch_alter_table("orders", schema=None) as batch_op:
|
||||||
batch_op.drop_column('subtotal_cents')
|
batch_op.drop_column("subtotal_cents")
|
||||||
batch_op.drop_column('tax_amount_cents')
|
batch_op.drop_column("tax_amount_cents")
|
||||||
batch_op.drop_column('shipping_amount_cents')
|
batch_op.drop_column("shipping_amount_cents")
|
||||||
batch_op.drop_column('discount_amount_cents')
|
batch_op.drop_column("discount_amount_cents")
|
||||||
batch_op.drop_column('total_amount_cents')
|
batch_op.drop_column("total_amount_cents")
|
||||||
batch_op.alter_column('total_amount',
|
batch_op.alter_column("total_amount",
|
||||||
existing_type=sa.Float(),
|
existing_type=sa.Float(),
|
||||||
nullable=False)
|
nullable=False)
|
||||||
|
|
||||||
# === PRODUCTS TABLE ===
|
# === PRODUCTS TABLE ===
|
||||||
with op.batch_alter_table('products', schema=None) as batch_op:
|
with op.batch_alter_table("products", schema=None) as batch_op:
|
||||||
batch_op.add_column(sa.Column('price', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("price", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('sale_price', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("sale_price", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('supplier_cost', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("supplier_cost", sa.Float(), nullable=True))
|
||||||
batch_op.add_column(sa.Column('margin_percent', sa.Float(), nullable=True))
|
batch_op.add_column(sa.Column("margin_percent", sa.Float(), nullable=True))
|
||||||
|
|
||||||
op.execute('UPDATE products SET price = price_cents / 100.0 WHERE price_cents IS NOT NULL')
|
op.execute("UPDATE products SET price = price_cents / 100.0 WHERE price_cents IS NOT NULL")
|
||||||
op.execute('UPDATE products SET sale_price = sale_price_cents / 100.0 WHERE sale_price_cents IS NOT NULL')
|
op.execute("UPDATE products SET sale_price = sale_price_cents / 100.0 WHERE sale_price_cents IS NOT NULL")
|
||||||
op.execute('UPDATE products SET supplier_cost = supplier_cost_cents / 100.0 WHERE supplier_cost_cents IS NOT NULL')
|
op.execute("UPDATE products SET supplier_cost = supplier_cost_cents / 100.0 WHERE supplier_cost_cents IS NOT NULL")
|
||||||
op.execute('UPDATE products SET margin_percent = margin_percent_x100 / 100.0 WHERE margin_percent_x100 IS NOT NULL')
|
op.execute("UPDATE products SET margin_percent = margin_percent_x100 / 100.0 WHERE margin_percent_x100 IS NOT NULL")
|
||||||
|
|
||||||
with op.batch_alter_table('products', schema=None) as batch_op:
|
with op.batch_alter_table("products", schema=None) as batch_op:
|
||||||
batch_op.drop_column('price_cents')
|
batch_op.drop_column("price_cents")
|
||||||
batch_op.drop_column('sale_price_cents')
|
batch_op.drop_column("sale_price_cents")
|
||||||
batch_op.drop_column('supplier_cost_cents')
|
batch_op.drop_column("supplier_cost_cents")
|
||||||
batch_op.drop_column('margin_percent_x100')
|
batch_op.drop_column("margin_percent_x100")
|
||||||
|
|||||||
@@ -16,18 +16,18 @@ Supports three communication channels:
|
|||||||
- Admin <-> Customer
|
- Admin <-> Customer
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "e3f4a5b6c7d8"
|
revision: str = "e3f4a5b6c7d8"
|
||||||
down_revision: Union[str, None] = "c9e22eadf533"
|
down_revision: str | None = "c9e22eadf533"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def table_exists(table_name: str) -> bool:
|
def table_exists(table_name: str) -> bool:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ language fallback capabilities. Fields in product_translations can be
|
|||||||
NULL to inherit from marketplace_product_translations.
|
NULL to inherit from marketplace_product_translations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@@ -21,9 +21,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "f2b3c4d5e6f7"
|
revision: str = "f2b3c4d5e6f7"
|
||||||
down_revision: Union[str, None] = "e1a2b3c4d5e6"
|
down_revision: str | None = "e1a2b3c4d5e6"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -8,17 +8,17 @@ This migration adds validator_type column to architecture scans and violations
|
|||||||
to support multiple validator types (architecture, security, performance).
|
to support multiple validator types (architecture, security, performance).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "f4a5b6c7d8e9"
|
revision: str = "f4a5b6c7d8e9"
|
||||||
down_revision: Union[str, None] = "e3f4a5b6c7d8"
|
down_revision: str | None = "e3f4a5b6c7d8"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Create Date: 2025-11-22 23:51:40.694983
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@@ -14,9 +14,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "f68d8da5315a"
|
revision: str = "f68d8da5315a"
|
||||||
down_revision: Union[str, None] = "72aa309d4007"
|
down_revision: str | None = "72aa309d4007"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Create Date: 2025-11-13 16:51:25.010057
|
|||||||
SQLite-compatible version
|
SQLite-compatible version
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
@@ -15,9 +15,9 @@ from alembic import op
|
|||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "fa7d4d10e358"
|
revision: str = "fa7d4d10e358"
|
||||||
down_revision: Union[str, None] = "4951b2e50581"
|
down_revision: str | None = "4951b2e50581"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
|
|||||||
@@ -11,17 +11,17 @@ This migration adds language preference fields to support multi-language UI:
|
|||||||
|
|
||||||
Supported languages: en (English), fr (French), de (German), lb (Luxembourgish)
|
Supported languages: en (English), fr (French), de (German), lb (Luxembourgish)
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'fcfdc02d5138'
|
revision: str = "fcfdc02d5138"
|
||||||
down_revision: Union[str, None] = 'b412e0b49c2e'
|
down_revision: str | None = "b412e0b49c2e"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
@@ -30,25 +30,25 @@ def upgrade() -> None:
|
|||||||
# ========================================================================
|
# ========================================================================
|
||||||
# default_language: Default language for vendor content (products, etc.)
|
# default_language: Default language for vendor content (products, etc.)
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'vendors',
|
"vendors",
|
||||||
sa.Column('default_language', sa.String(5), nullable=False, server_default='fr')
|
sa.Column("default_language", sa.String(5), nullable=False, server_default="fr")
|
||||||
)
|
)
|
||||||
# dashboard_language: Language for vendor team dashboard UI
|
# dashboard_language: Language for vendor team dashboard UI
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'vendors',
|
"vendors",
|
||||||
sa.Column('dashboard_language', sa.String(5), nullable=False, server_default='fr')
|
sa.Column("dashboard_language", sa.String(5), nullable=False, server_default="fr")
|
||||||
)
|
)
|
||||||
# storefront_language: Default language for customer-facing shop
|
# storefront_language: Default language for customer-facing shop
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'vendors',
|
"vendors",
|
||||||
sa.Column('storefront_language', sa.String(5), nullable=False, server_default='fr')
|
sa.Column("storefront_language", sa.String(5), nullable=False, server_default="fr")
|
||||||
)
|
)
|
||||||
# storefront_languages: JSON array of enabled languages for storefront
|
# storefront_languages: JSON array of enabled languages for storefront
|
||||||
# Allows vendors to enable/disable specific languages
|
# Allows vendors to enable/disable specific languages
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'vendors',
|
"vendors",
|
||||||
sa.Column(
|
sa.Column(
|
||||||
'storefront_languages',
|
"storefront_languages",
|
||||||
sa.JSON,
|
sa.JSON,
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default='["fr", "de", "en"]'
|
server_default='["fr", "de", "en"]'
|
||||||
@@ -60,8 +60,8 @@ def upgrade() -> None:
|
|||||||
# ========================================================================
|
# ========================================================================
|
||||||
# preferred_language: User's preferred UI language (NULL = use context default)
|
# preferred_language: User's preferred UI language (NULL = use context default)
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'users',
|
"users",
|
||||||
sa.Column('preferred_language', sa.String(5), nullable=True)
|
sa.Column("preferred_language", sa.String(5), nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
@@ -69,16 +69,16 @@ def upgrade() -> None:
|
|||||||
# ========================================================================
|
# ========================================================================
|
||||||
# preferred_language: Customer's preferred language (NULL = use storefront default)
|
# preferred_language: Customer's preferred language (NULL = use storefront default)
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'customers',
|
"customers",
|
||||||
sa.Column('preferred_language', sa.String(5), nullable=True)
|
sa.Column("preferred_language", sa.String(5), nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
# Remove columns in reverse order
|
# Remove columns in reverse order
|
||||||
op.drop_column('customers', 'preferred_language')
|
op.drop_column("customers", "preferred_language")
|
||||||
op.drop_column('users', 'preferred_language')
|
op.drop_column("users", "preferred_language")
|
||||||
op.drop_column('vendors', 'storefront_languages')
|
op.drop_column("vendors", "storefront_languages")
|
||||||
op.drop_column('vendors', 'storefront_language')
|
op.drop_column("vendors", "storefront_language")
|
||||||
op.drop_column('vendors', 'dashboard_language')
|
op.drop_column("vendors", "dashboard_language")
|
||||||
op.drop_column('vendors', 'default_language')
|
op.drop_column("vendors", "default_language")
|
||||||
|
|||||||
@@ -6,17 +6,15 @@ Create Date: 2025-11-22 13:41:18.069674
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "fef1d20ce8b4"
|
revision: str = "fef1d20ce8b4"
|
||||||
down_revision: Union[str, None] = "fa7d4d10e358"
|
down_revision: str | None = "fa7d4d10e358"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Create Date: 2024-12-21
|
|||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
|
|||||||
@@ -9,16 +9,17 @@ This migration adds:
|
|||||||
- invoices: Invoice records with seller/buyer snapshots
|
- invoices: Invoice records with seller/buyer snapshots
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "h6c7d8e9f0a1"
|
revision: str = "h6c7d8e9f0a1"
|
||||||
down_revision: Union[str, None] = "g5b6c7d8e9f0"
|
down_revision: str | None = "g5b6c7d8e9f0"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -8,16 +8,17 @@ This migration adds:
|
|||||||
- vendor_subscriptions: Per-vendor subscription tracking with tier limits
|
- vendor_subscriptions: Per-vendor subscription tracking with tier limits
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "i7d8e9f0a1b2"
|
revision: str = "i7d8e9f0a1b2"
|
||||||
down_revision: Union[str, None] = "h6c7d8e9f0a1"
|
down_revision: str | None = "h6c7d8e9f0a1"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -15,16 +15,17 @@ After this migration:
|
|||||||
- The marketplace_product_id FK is kept for "view original source" feature
|
- The marketplace_product_id FK is kept for "view original source" feature
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
from sqlalchemy import text
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "j8e9f0a1b2c3"
|
revision: str = "j8e9f0a1b2c3"
|
||||||
down_revision: Union[str, None] = "i7d8e9f0a1b2"
|
down_revision: str | None = "i7d8e9f0a1b2"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
@@ -259,4 +260,3 @@ def downgrade() -> None:
|
|||||||
1. It would lose any vendor customizations made after migration
|
1. It would lose any vendor customizations made after migration
|
||||||
2. The model code may still work with populated fields
|
2. The model code may still work with populated fields
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ Adds tier_id column to vendor_subscriptions table with FK to subscription_tiers.
|
|||||||
Backfills tier_id based on existing tier (code) values.
|
Backfills tier_id based on existing tier (code) values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "k9f0a1b2c3d4"
|
revision = "k9f0a1b2c3d4"
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ Create Date: 2025-12-26
|
|||||||
Adds table for tracking daily platform capacity metrics for growth forecasting.
|
Adds table for tracking daily platform capacity metrics for growth forecasting.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "l0a1b2c3d4e5"
|
revision = "l0a1b2c3d4e5"
|
||||||
|
|||||||
@@ -5,67 +5,67 @@ Revises: d7a4a3f06394
|
|||||||
Create Date: 2025-12-27 22:00:00.000000
|
Create Date: 2025-12-27 22:00:00.000000
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'm1b2c3d4e5f6'
|
revision: str = "m1b2c3d4e5f6"
|
||||||
down_revision: Union[str, None] = 'd7a4a3f06394'
|
down_revision: str | None = "d7a4a3f06394"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
op.create_table('vendor_onboarding',
|
op.create_table("vendor_onboarding",
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
sa.Column('vendor_id', sa.Integer(), nullable=False),
|
sa.Column("vendor_id", sa.Integer(), nullable=False),
|
||||||
# Overall status
|
# Overall status
|
||||||
sa.Column('status', sa.String(length=20), nullable=False, server_default='not_started'),
|
sa.Column("status", sa.String(length=20), nullable=False, server_default="not_started"),
|
||||||
sa.Column('current_step', sa.String(length=30), nullable=False, server_default='company_profile'),
|
sa.Column("current_step", sa.String(length=30), nullable=False, server_default="company_profile"),
|
||||||
# Step 1: Company Profile
|
# Step 1: Company Profile
|
||||||
sa.Column('step_company_profile_completed', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
sa.Column("step_company_profile_completed", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||||
sa.Column('step_company_profile_completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("step_company_profile_completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('step_company_profile_data', sa.JSON(), nullable=True),
|
sa.Column("step_company_profile_data", sa.JSON(), nullable=True),
|
||||||
# Step 2: Letzshop API Configuration
|
# Step 2: Letzshop API Configuration
|
||||||
sa.Column('step_letzshop_api_completed', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
sa.Column("step_letzshop_api_completed", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||||
sa.Column('step_letzshop_api_completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("step_letzshop_api_completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('step_letzshop_api_connection_verified', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
sa.Column("step_letzshop_api_connection_verified", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||||
# Step 3: Product Import
|
# Step 3: Product Import
|
||||||
sa.Column('step_product_import_completed', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
sa.Column("step_product_import_completed", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||||
sa.Column('step_product_import_completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("step_product_import_completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('step_product_import_csv_url_set', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
sa.Column("step_product_import_csv_url_set", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||||
# Step 4: Order Sync
|
# Step 4: Order Sync
|
||||||
sa.Column('step_order_sync_completed', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
sa.Column("step_order_sync_completed", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||||
sa.Column('step_order_sync_completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("step_order_sync_completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('step_order_sync_job_id', sa.Integer(), nullable=True),
|
sa.Column("step_order_sync_job_id", sa.Integer(), nullable=True),
|
||||||
# Completion tracking
|
# Completion tracking
|
||||||
sa.Column('started_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
# Admin override
|
# Admin override
|
||||||
sa.Column('skipped_by_admin', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
sa.Column("skipped_by_admin", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
||||||
sa.Column('skipped_at', sa.DateTime(timezone=True), nullable=True),
|
sa.Column("skipped_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
sa.Column('skipped_reason', sa.Text(), nullable=True),
|
sa.Column("skipped_reason", sa.Text(), nullable=True),
|
||||||
sa.Column('skipped_by_user_id', sa.Integer(), nullable=True),
|
sa.Column("skipped_by_user_id", sa.Integer(), nullable=True),
|
||||||
# Timestamps
|
# Timestamps
|
||||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||||
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
||||||
# Constraints
|
# Constraints
|
||||||
sa.ForeignKeyConstraint(['vendor_id'], ['vendors.id'], ondelete='CASCADE'),
|
sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"], ondelete="CASCADE"),
|
||||||
sa.ForeignKeyConstraint(['skipped_by_user_id'], ['users.id']),
|
sa.ForeignKeyConstraint(["skipped_by_user_id"], ["users.id"]),
|
||||||
sa.PrimaryKeyConstraint('id'),
|
sa.PrimaryKeyConstraint("id"),
|
||||||
)
|
)
|
||||||
op.create_index(op.f('ix_vendor_onboarding_id'), 'vendor_onboarding', ['id'], unique=False)
|
op.create_index(op.f("ix_vendor_onboarding_id"), "vendor_onboarding", ["id"], unique=False)
|
||||||
op.create_index(op.f('ix_vendor_onboarding_vendor_id'), 'vendor_onboarding', ['vendor_id'], unique=True)
|
op.create_index(op.f("ix_vendor_onboarding_vendor_id"), "vendor_onboarding", ["vendor_id"], unique=True)
|
||||||
op.create_index(op.f('ix_vendor_onboarding_status'), 'vendor_onboarding', ['status'], unique=False)
|
op.create_index(op.f("ix_vendor_onboarding_status"), "vendor_onboarding", ["status"], unique=False)
|
||||||
op.create_index('idx_onboarding_vendor_status', 'vendor_onboarding', ['vendor_id', 'status'], unique=False)
|
op.create_index("idx_onboarding_vendor_status", "vendor_onboarding", ["vendor_id", "status"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_index('idx_onboarding_vendor_status', table_name='vendor_onboarding')
|
op.drop_index("idx_onboarding_vendor_status", table_name="vendor_onboarding")
|
||||||
op.drop_index(op.f('ix_vendor_onboarding_status'), table_name='vendor_onboarding')
|
op.drop_index(op.f("ix_vendor_onboarding_status"), table_name="vendor_onboarding")
|
||||||
op.drop_index(op.f('ix_vendor_onboarding_vendor_id'), table_name='vendor_onboarding')
|
op.drop_index(op.f("ix_vendor_onboarding_vendor_id"), table_name="vendor_onboarding")
|
||||||
op.drop_index(op.f('ix_vendor_onboarding_id'), table_name='vendor_onboarding')
|
op.drop_index(op.f("ix_vendor_onboarding_id"), table_name="vendor_onboarding")
|
||||||
op.drop_table('vendor_onboarding')
|
op.drop_table("vendor_onboarding")
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ Alters:
|
|||||||
Revision ID: billing_001
|
Revision ID: billing_001
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# Revision identifiers
|
# Revision identifiers
|
||||||
revision = "billing_001"
|
revision = "billing_001"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,17 +15,18 @@ Phase 2 changes:
|
|||||||
- NEW COLUMN on loyalty_cards: last_activity_at
|
- NEW COLUMN on loyalty_cards: last_activity_at
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from alembic import op
|
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "loyalty_003_phase2"
|
revision: str = "loyalty_003_phase2"
|
||||||
down_revision: Union[str, None] = "0fb5d6d6ff97"
|
down_revision: str | None = "0fb5d6d6ff97"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -7,16 +7,17 @@ Create Date: 2025-12-31 10:00:00.000000
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "n2c3d4e5f6a7"
|
revision: str = "n2c3d4e5f6a7"
|
||||||
down_revision: Union[str, None] = "ba2c0ce78396"
|
down_revision: str | None = "ba2c0ce78396"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -245,7 +246,7 @@ def upgrade() -> None:
|
|||||||
tier_ids[row[1]] = row[0]
|
tier_ids[row[1]] = row[0]
|
||||||
|
|
||||||
# Insert features
|
# Insert features
|
||||||
now = sa.func.now()
|
sa.func.now()
|
||||||
for category, code, name, description, ui_location, ui_icon, ui_route, display_order in FEATURES:
|
for category, code, name, description, ui_location, ui_icon, ui_route, display_order in FEATURES:
|
||||||
minimum_tier_code = MINIMUM_TIER.get(code)
|
minimum_tier_code = MINIMUM_TIER.get(code)
|
||||||
minimum_tier_id = tier_ids.get(minimum_tier_code) if minimum_tier_code else None
|
minimum_tier_id = tier_ids.get(minimum_tier_code) if minimum_tier_code else None
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ Adds an audit trail for inventory movements:
|
|||||||
- Store quantity snapshots for historical analysis
|
- Store quantity snapshots for historical analysis
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "o3c4d5e6f7a8"
|
revision = "o3c4d5e6f7a8"
|
||||||
down_revision = "n2c3d4e5f6a7"
|
down_revision = "n2c3d4e5f6a7"
|
||||||
|
|||||||
@@ -6,24 +6,24 @@ Revises: o3c4d5e6f7a8
|
|||||||
Create Date: 2026-01-01 12:00:00.000000
|
Create Date: 2026-01-01 12:00:00.000000
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'p4d5e6f7a8b9'
|
revision: str = "p4d5e6f7a8b9"
|
||||||
down_revision: Union[str, None] = 'o3c4d5e6f7a8'
|
down_revision: str | None = "o3c4d5e6f7a8"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add shipped_quantity column to order_items
|
# Add shipped_quantity column to order_items
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'order_items',
|
"order_items",
|
||||||
sa.Column('shipped_quantity', sa.Integer(), nullable=False, server_default='0')
|
sa.Column("shipped_quantity", sa.Integer(), nullable=False, server_default="0")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set shipped_quantity = quantity for already fulfilled items
|
# Set shipped_quantity = quantity for already fulfilled items
|
||||||
@@ -36,4 +36,4 @@ def upgrade() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column('order_items', 'shipped_quantity')
|
op.drop_column("order_items", "shipped_quantity")
|
||||||
|
|||||||
@@ -10,42 +10,42 @@ Revises: p4d5e6f7a8b9
|
|||||||
Create Date: 2026-01-02 10:00:00.000000
|
Create Date: 2026-01-02 10:00:00.000000
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = 'q5e6f7a8b9c0'
|
revision: str = "q5e6f7a8b9c0"
|
||||||
down_revision: Union[str, None] = 'p4d5e6f7a8b9'
|
down_revision: str | None = "p4d5e6f7a8b9"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# Add VAT regime (domestic, oss, reverse_charge, origin, exempt)
|
# Add VAT regime (domestic, oss, reverse_charge, origin, exempt)
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'orders',
|
"orders",
|
||||||
sa.Column('vat_regime', sa.String(20), nullable=True)
|
sa.Column("vat_regime", sa.String(20), nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add VAT rate as percentage (e.g., 17.00 for 17%)
|
# Add VAT rate as percentage (e.g., 17.00 for 17%)
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'orders',
|
"orders",
|
||||||
sa.Column('vat_rate', sa.Numeric(5, 2), nullable=True)
|
sa.Column("vat_rate", sa.Numeric(5, 2), nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add human-readable VAT label (e.g., "Luxembourg VAT 17%")
|
# Add human-readable VAT label (e.g., "Luxembourg VAT 17%")
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'orders',
|
"orders",
|
||||||
sa.Column('vat_rate_label', sa.String(100), nullable=True)
|
sa.Column("vat_rate_label", sa.String(100), nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add destination country for cross-border sales (ISO code)
|
# Add destination country for cross-border sales (ISO code)
|
||||||
op.add_column(
|
op.add_column(
|
||||||
'orders',
|
"orders",
|
||||||
sa.Column('vat_destination_country', sa.String(2), nullable=True)
|
sa.Column("vat_destination_country", sa.String(2), nullable=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Populate VAT fields for existing orders based on shipping country
|
# Populate VAT fields for existing orders based on shipping country
|
||||||
@@ -66,7 +66,7 @@ def upgrade() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
op.drop_column('orders', 'vat_destination_country')
|
op.drop_column("orders", "vat_destination_country")
|
||||||
op.drop_column('orders', 'vat_rate_label')
|
op.drop_column("orders", "vat_rate_label")
|
||||||
op.drop_column('orders', 'vat_rate')
|
op.drop_column("orders", "vat_rate")
|
||||||
op.drop_column('orders', 'vat_regime')
|
op.drop_column("orders", "vat_regime")
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ This migration is idempotent - it checks for existing columns before
|
|||||||
making changes.
|
making changes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "r6f7a8b9c0d1"
|
revision = "r6f7a8b9c0d1"
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ NULL means the vendor inherits from platform defaults.
|
|||||||
Examples: 'fr-LU', 'de-DE', 'en-GB'
|
Examples: 'fr-LU', 'de-DE', 'en-GB'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "s7a8b9c0d1e2"
|
revision = "s7a8b9c0d1e2"
|
||||||
|
|||||||
@@ -18,16 +18,17 @@ Major terminology migration:
|
|||||||
- letzshop_vendor_cache -> letzshop_store_cache
|
- letzshop_vendor_cache -> letzshop_store_cache
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
from sqlalchemy import text
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "t001_terminology"
|
revision: str = "t001_terminology"
|
||||||
down_revision: Union[str, None] = "loyalty_003_phase2"
|
down_revision: str | None = "loyalty_003_phase2"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def _col_exists(table: str, col: str) -> bool:
|
def _col_exists(table: str, col: str) -> bool:
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ Completes the Company/Vendor -> Merchant/Store terminology migration by
|
|||||||
renaming 4 constraints and 12 indexes that still used "vendor" in their names.
|
renaming 4 constraints and 12 indexes that still used "vendor" in their names.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "t002_constraints"
|
revision: str = "t002_constraints"
|
||||||
down_revision: Union[str, None] = "t001_terminology"
|
down_revision: str | None = "t001_terminology"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
# (old_name, new_name, table) — table is needed for RENAME CONSTRAINT
|
# (old_name, new_name, table) — table is needed for RENAME CONSTRAINT
|
||||||
CONSTRAINTS = [
|
CONSTRAINTS = [
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ Create Date: 2026-01-03
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "t8b9c0d1e2f3"
|
revision = "t8b9c0d1e2f3"
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ Changes:
|
|||||||
- Create vendor_email_templates table for vendor-specific template overrides
|
- Create vendor_email_templates table for vendor-specific template overrides
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "u9c0d1e2f3g4"
|
revision = "u9c0d1e2f3g4"
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ Changes:
|
|||||||
- Premium providers (SendGrid, Mailgun, SES) are tier-gated (Business+)
|
- Premium providers (SendGrid, Mailgun, SES) are tier-gated (Business+)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "v0a1b2c3d4e5"
|
revision = "v0a1b2c3d4e5"
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ Revises: v0a1b2c3d4e5
|
|||||||
Create Date: 2026-01-06 10:00:00.000000
|
Create Date: 2026-01-06 10:00:00.000000
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "w1b2c3d4e5f6"
|
revision: str = "w1b2c3d4e5f6"
|
||||||
down_revision: Union[str, None] = "v0a1b2c3d4e5"
|
down_revision: str | None = "v0a1b2c3d4e5"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ Create Date: 2026-01-06 23:15:00.000000
|
|||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "x2c3d4e5f6g7"
|
revision: str = "x2c3d4e5f6g7"
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ Create Date: 2026-01-07 10:00:00.000000
|
|||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "y3d4e5f6g7h8"
|
revision: str = "y3d4e5f6g7h8"
|
||||||
|
|||||||
@@ -15,16 +15,17 @@ This migration adds multi-platform support:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "z4e5f6a7b8c9"
|
revision: str = "z4e5f6a7b8c9"
|
||||||
down_revision: Union[str, None] = "1b398cf45e85"
|
down_revision: str | None = "1b398cf45e85"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
# Platform marketing page slugs (is_platform_page=True)
|
# Platform marketing page slugs (is_platform_page=True)
|
||||||
PLATFORM_PAGE_SLUGS = [
|
PLATFORM_PAGE_SLUGS = [
|
||||||
@@ -303,7 +304,7 @@ def upgrade() -> None:
|
|||||||
("cms", "cms_scheduling", "Page Scheduling", "Schedule page publish/unpublish", "settings", "clock", None, tier_ids.get("enterprise"), 6),
|
("cms", "cms_scheduling", "Page Scheduling", "Schedule page publish/unpublish", "settings", "clock", None, tier_ids.get("enterprise"), 6),
|
||||||
]
|
]
|
||||||
|
|
||||||
for category, code, name, description, ui_location, ui_icon, ui_route, minimum_tier_id, display_order in cms_features:
|
for category, code, name, description, ui_location, ui_icon, _ui_route, minimum_tier_id, display_order in cms_features:
|
||||||
min_tier_val = minimum_tier_id if minimum_tier_id else "NULL"
|
min_tier_val = minimum_tier_id if minimum_tier_id else "NULL"
|
||||||
conn.execute(
|
conn.execute(
|
||||||
sa.text(f"""
|
sa.text(f"""
|
||||||
|
|||||||
@@ -10,16 +10,17 @@ This migration adds the Loyalty+ platform:
|
|||||||
3. Creates vendor default pages (about, rewards-catalog, terms, privacy)
|
3. Creates vendor default pages (about, rewards-catalog, terms, privacy)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "z5f6g7h8i9j0"
|
revision: str = "z5f6g7h8i9j0"
|
||||||
down_revision: Union[str, None] = "z4e5f6a7b8c9"
|
down_revision: str | None = "z4e5f6a7b8c9"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -17,16 +17,17 @@ All other platforms are accessed via:
|
|||||||
- Production: {code}.lu or custom domain
|
- Production: {code}.lu or custom domain
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = "z6g7h8i9j0k1"
|
revision: str = "z6g7h8i9j0k1"
|
||||||
down_revision: Union[str, None] = "z5f6g7h8i9j0"
|
down_revision: str | None = "z5f6g7h8i9j0"
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
branch_labels: str | Sequence[str] | None = None
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ This migration:
|
|||||||
2. Alters columns to be NOT NULL
|
2. Alters columns to be NOT NULL
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "z7h8i9j0k1l2"
|
revision = "z7h8i9j0k1l2"
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ The sections column stores hero, features, pricing, and cta configurations
|
|||||||
with TranslatableText pattern for i18n.
|
with TranslatableText pattern for i18n.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "z8i9j0k1l2m3"
|
revision = "z8i9j0k1l2m3"
|
||||||
|
|||||||
@@ -13,11 +13,10 @@ Platform admins are assigned to specific platforms via admin_platforms.
|
|||||||
Existing admins are migrated to super admins for backward compatibility.
|
Existing admins are migrated to super admins for backward compatibility.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import UTC, datetime
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "z9j0k1l2m3n4"
|
revision = "z9j0k1l2m3n4"
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ Adds configurable admin sidebar menus:
|
|||||||
- Mandatory items enforced at application level (companies, vendors, users, settings)
|
- Mandatory items enforced at application level (companies, vendors, users, settings)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "za0k1l2m3n4o5"
|
revision = "za0k1l2m3n4o5"
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ Also updates unique constraints to include frontend_type and adds
|
|||||||
a check constraint ensuring user_id scope is only used for admin frontend.
|
a check constraint ensuring user_id scope is only used for admin frontend.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "zb1l2m3n4o5p6"
|
revision = "zb1l2m3n4o5p6"
|
||||||
@@ -25,7 +25,7 @@ depends_on = None
|
|||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
# 1. Create the enum type for frontend_type
|
# 1. Create the enum type for frontend_type
|
||||||
frontend_type_enum = sa.Enum('admin', 'vendor', name='frontendtype')
|
frontend_type_enum = sa.Enum("admin", "vendor", name="frontendtype")
|
||||||
frontend_type_enum.create(op.get_bind(), checkfirst=True)
|
frontend_type_enum.create(op.get_bind(), checkfirst=True)
|
||||||
|
|
||||||
# 2. Add frontend_type column with default value
|
# 2. Add frontend_type column with default value
|
||||||
@@ -33,7 +33,7 @@ def upgrade() -> None:
|
|||||||
"admin_menu_configs",
|
"admin_menu_configs",
|
||||||
sa.Column(
|
sa.Column(
|
||||||
"frontend_type",
|
"frontend_type",
|
||||||
sa.Enum('admin', 'vendor', name='frontendtype'),
|
sa.Enum("admin", "vendor", name="frontendtype"),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
server_default="admin",
|
server_default="admin",
|
||||||
comment="Which frontend this config applies to (admin or vendor)",
|
comment="Which frontend this config applies to (admin or vendor)",
|
||||||
@@ -114,4 +114,4 @@ def downgrade() -> None:
|
|||||||
op.drop_column("admin_menu_configs", "frontend_type")
|
op.drop_column("admin_menu_configs", "frontend_type")
|
||||||
|
|
||||||
# Drop the enum type
|
# Drop the enum type
|
||||||
sa.Enum('admin', 'vendor', name='frontendtype').drop(op.get_bind(), checkfirst=True)
|
sa.Enum("admin", "vendor", name="frontendtype").drop(op.get_bind(), checkfirst=True)
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ This replaces the simpler Platform.settings["enabled_modules"] JSON approach
|
|||||||
for better auditability and query capabilities.
|
for better auditability and query capabilities.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "zc2m3n4o5p6q7"
|
revision = "zc2m3n4o5p6q7"
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ This migration ensures that CMS and Customers modules are enabled for all platfo
|
|||||||
since they are now core modules that cannot be disabled.
|
since they are now core modules that cannot be disabled.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = "zd3n4o5p6q7r8"
|
revision = "zd3n4o5p6q7r8"
|
||||||
@@ -30,7 +31,7 @@ def upgrade() -> None:
|
|||||||
sa.text("SELECT id FROM platforms")
|
sa.text("SELECT id FROM platforms")
|
||||||
).fetchall()
|
).fetchall()
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(UTC)
|
||||||
core_modules = ["cms", "customers"]
|
core_modules = ["cms", "customers"]
|
||||||
|
|
||||||
for (platform_id,) in platforms:
|
for (platform_id,) in platforms:
|
||||||
@@ -80,4 +81,3 @@ def downgrade() -> None:
|
|||||||
break functionality. It just removes the explicit enabling done by upgrade.
|
break functionality. It just removes the explicit enabling done by upgrade.
|
||||||
"""
|
"""
|
||||||
# No-op: We don't want to disable core modules
|
# No-op: We don't want to disable core modules
|
||||||
pass
|
|
||||||
|
|||||||
@@ -44,22 +44,22 @@ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
|
from app.modules.enums import FrontendType
|
||||||
from app.modules.tenancy.exceptions import (
|
from app.modules.tenancy.exceptions import (
|
||||||
AdminRequiredException,
|
AdminRequiredException,
|
||||||
InsufficientPermissionsException,
|
InsufficientPermissionsException,
|
||||||
InsufficientStorePermissionsException,
|
InsufficientStorePermissionsException,
|
||||||
InvalidTokenException,
|
InvalidTokenException,
|
||||||
UnauthorizedStoreAccessException,
|
|
||||||
StoreNotFoundException,
|
StoreNotFoundException,
|
||||||
StoreOwnerOnlyException,
|
StoreOwnerOnlyException,
|
||||||
|
UnauthorizedStoreAccessException,
|
||||||
)
|
)
|
||||||
|
from app.modules.tenancy.models import Store
|
||||||
|
from app.modules.tenancy.models import User as UserModel
|
||||||
from app.modules.tenancy.services.store_service import store_service
|
from app.modules.tenancy.services.store_service import store_service
|
||||||
from middleware.auth import AuthManager
|
from middleware.auth import AuthManager
|
||||||
from middleware.rate_limiter import RateLimiter
|
from middleware.rate_limiter import RateLimiter
|
||||||
from app.modules.tenancy.models import User as UserModel
|
|
||||||
from app.modules.tenancy.models import Store
|
|
||||||
from models.schema.auth import UserContext
|
from models.schema.auth import UserContext
|
||||||
from app.modules.enums import FrontendType
|
|
||||||
|
|
||||||
# Initialize dependencies
|
# Initialize dependencies
|
||||||
security = HTTPBearer(auto_error=False) # auto_error=False prevents automatic 403
|
security = HTTPBearer(auto_error=False) # auto_error=False prevents automatic 403
|
||||||
@@ -485,7 +485,6 @@ def require_module_access(module_code: str, frontend_type: FrontendType):
|
|||||||
if user_context.is_super_admin:
|
if user_context.is_super_admin:
|
||||||
# Super admins bypass module checks
|
# Super admins bypass module checks
|
||||||
return user_context
|
return user_context
|
||||||
else:
|
|
||||||
platform = getattr(request.state, "admin_platform", None)
|
platform = getattr(request.state, "admin_platform", None)
|
||||||
if platform:
|
if platform:
|
||||||
platform_id = platform.id
|
platform_id = platform.id
|
||||||
@@ -572,10 +571,10 @@ def require_menu_access(menu_item_id: str, frontend_type: "FrontendType"):
|
|||||||
Returns:
|
Returns:
|
||||||
Dependency function that validates menu access and returns User
|
Dependency function that validates menu access and returns User
|
||||||
"""
|
"""
|
||||||
from app.modules.registry import get_menu_item_module
|
|
||||||
from app.modules.service import module_service
|
|
||||||
from app.modules.core.services.menu_service import menu_service
|
from app.modules.core.services.menu_service import menu_service
|
||||||
from app.modules.enums import FrontendType as FT
|
from app.modules.enums import FrontendType as FT
|
||||||
|
from app.modules.registry import get_menu_item_module
|
||||||
|
from app.modules.service import module_service
|
||||||
|
|
||||||
def _check_menu_access(
|
def _check_menu_access(
|
||||||
request: Request,
|
request: Request,
|
||||||
@@ -941,52 +940,82 @@ def get_current_merchant_optional(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def require_merchant_owner(merchant_id: int):
|
def get_merchant_for_current_user(
|
||||||
"""
|
|
||||||
Dependency factory to require ownership of a specific merchant.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
@router.get("/merchants/{merchant_id}/subscriptions")
|
|
||||||
def list_subscriptions(
|
|
||||||
merchant_id: int,
|
|
||||||
user: UserContext = Depends(require_merchant_owner(merchant_id))
|
|
||||||
):
|
|
||||||
...
|
|
||||||
"""
|
|
||||||
|
|
||||||
def _check_merchant_ownership(
|
|
||||||
request: Request,
|
request: Request,
|
||||||
credentials: HTTPAuthorizationCredentials | None = Depends(security),
|
current_user: UserContext = Depends(get_current_merchant_api),
|
||||||
merchant_token: str | None = Cookie(None),
|
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
) -> UserContext:
|
):
|
||||||
user_context = get_current_merchant_from_cookie_or_header(
|
"""
|
||||||
request, credentials, merchant_token, db
|
Get the active merchant owned by the current API user.
|
||||||
)
|
|
||||||
|
|
||||||
# Verify user owns this specific merchant
|
Used by merchant API endpoints (header-only auth) that need the Merchant object.
|
||||||
|
Stores the merchant on request.state.merchant for endpoint use.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Merchant ORM object
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
MerchantNotFoundException: If user owns no active merchants
|
||||||
|
"""
|
||||||
|
from app.modules.tenancy.exceptions import MerchantNotFoundException
|
||||||
from app.modules.tenancy.models import Merchant
|
from app.modules.tenancy.models import Merchant
|
||||||
|
|
||||||
merchant = (
|
merchant = (
|
||||||
db.query(Merchant)
|
db.query(Merchant)
|
||||||
.filter(
|
.filter(
|
||||||
Merchant.id == merchant_id,
|
Merchant.owner_user_id == current_user.id,
|
||||||
Merchant.owner_user_id == user_context.id,
|
|
||||||
Merchant.is_active == True, # noqa: E712
|
Merchant.is_active == True, # noqa: E712
|
||||||
)
|
)
|
||||||
|
.order_by(Merchant.id)
|
||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
|
||||||
if not merchant:
|
if not merchant:
|
||||||
raise InsufficientPermissionsException(
|
raise MerchantNotFoundException(
|
||||||
f"You do not own merchant {merchant_id}"
|
str(current_user.id), identifier_type="owner_user_id"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Store merchant in request state for endpoint use
|
|
||||||
request.state.merchant = merchant
|
request.state.merchant = merchant
|
||||||
|
return merchant
|
||||||
|
|
||||||
return user_context
|
|
||||||
|
|
||||||
return _check_merchant_ownership
|
def get_merchant_for_current_user_page(
|
||||||
|
request: Request,
|
||||||
|
current_user: UserContext = Depends(get_current_merchant_from_cookie_or_header),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get the active merchant owned by the current page user.
|
||||||
|
|
||||||
|
Used by merchant page routes (cookie+header auth) that need the Merchant object.
|
||||||
|
Stores the merchant on request.state.merchant for endpoint use.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Merchant ORM object
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
MerchantNotFoundException: If user owns no active merchants
|
||||||
|
"""
|
||||||
|
from app.modules.tenancy.exceptions import MerchantNotFoundException
|
||||||
|
from app.modules.tenancy.models import Merchant
|
||||||
|
|
||||||
|
merchant = (
|
||||||
|
db.query(Merchant)
|
||||||
|
.filter(
|
||||||
|
Merchant.owner_user_id == current_user.id,
|
||||||
|
Merchant.is_active == True, # noqa: E712
|
||||||
|
)
|
||||||
|
.order_by(Merchant.id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
|
||||||
|
if not merchant:
|
||||||
|
raise MerchantNotFoundException(
|
||||||
|
str(current_user.id), identifier_type="owner_user_id"
|
||||||
|
)
|
||||||
|
|
||||||
|
request.state.merchant = merchant
|
||||||
|
return merchant
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ This module provides:
|
|||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.v1 import admin, merchant, platform, storefront, store, webhooks
|
from app.api.v1 import admin, merchant, platform, store, storefront, webhooks
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
API Version 1 - All endpoints
|
API Version 1 - All endpoints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from . import admin, merchant, storefront, store
|
from . import admin, merchant, store, storefront
|
||||||
|
|
||||||
__all__ = ["admin", "merchant", "store", "storefront"]
|
__all__ = ["admin", "merchant", "store", "storefront"]
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ IMPORTANT:
|
|||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
|
||||||
# Create admin router
|
# Create admin router
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ IMPORTANT:
|
|||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
|
||||||
# Create merchant router
|
# Create merchant router
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ from sqlalchemy.orm import Session
|
|||||||
|
|
||||||
from app.core.database import get_db
|
from app.core.database import get_db
|
||||||
from app.core.environment import should_use_secure_cookies
|
from app.core.environment import should_use_secure_cookies
|
||||||
from app.modules.marketplace.services.platform_signup_service import platform_signup_service
|
from app.modules.marketplace.services.platform_signup_service import (
|
||||||
|
platform_signup_service,
|
||||||
|
)
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ def get_migration_status():
|
|||||||
try:
|
try:
|
||||||
from alembic.config import Config
|
from alembic.config import Config
|
||||||
|
|
||||||
alembic_cfg = Config("alembic.ini")
|
Config("alembic.ini")
|
||||||
|
|
||||||
# This would need more implementation to actually check status
|
# This would need more implementation to actually check status
|
||||||
# For now, just return a placeholder
|
# For now, just return a placeholder
|
||||||
|
|||||||
@@ -102,7 +102,9 @@ def get_log_level_from_db():
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from app.core.database import SessionLocal
|
from app.core.database import SessionLocal
|
||||||
from app.modules.core.services.admin_settings_service import admin_settings_service
|
from app.modules.core.services.admin_settings_service import (
|
||||||
|
admin_settings_service,
|
||||||
|
)
|
||||||
|
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
if not db:
|
if not db:
|
||||||
@@ -127,7 +129,9 @@ def get_rotation_settings_from_db():
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from app.core.database import SessionLocal
|
from app.core.database import SessionLocal
|
||||||
from app.modules.core.services.admin_settings_service import admin_settings_service
|
from app.modules.core.services.admin_settings_service import (
|
||||||
|
admin_settings_service,
|
||||||
|
)
|
||||||
|
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
if not db:
|
if not db:
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import logging
|
|||||||
import time
|
import time
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ class HealthCheckResult:
|
|||||||
message: str = ""
|
message: str = ""
|
||||||
latency_ms: float = 0.0
|
latency_ms: float = 0.0
|
||||||
details: dict[str, Any] = field(default_factory=dict)
|
details: dict[str, Any] = field(default_factory=dict)
|
||||||
checked_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
checked_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -70,7 +70,7 @@ class AggregatedHealth:
|
|||||||
|
|
||||||
status: HealthStatus
|
status: HealthStatus
|
||||||
checks: list[HealthCheckResult]
|
checks: list[HealthCheckResult]
|
||||||
timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> dict[str, Any]:
|
||||||
"""Convert to dictionary for JSON response."""
|
"""Convert to dictionary for JSON response."""
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Processes webhook events from Stripe:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
import stripe
|
import stripe
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -19,10 +19,10 @@ from app.modules.billing.models import (
|
|||||||
AddOnProduct,
|
AddOnProduct,
|
||||||
BillingHistory,
|
BillingHistory,
|
||||||
MerchantSubscription,
|
MerchantSubscription,
|
||||||
|
StoreAddOn,
|
||||||
StripeWebhookEvent,
|
StripeWebhookEvent,
|
||||||
SubscriptionStatus,
|
SubscriptionStatus,
|
||||||
SubscriptionTier,
|
SubscriptionTier,
|
||||||
StoreAddOn,
|
|
||||||
)
|
)
|
||||||
from app.modules.tenancy.models import Store, StorePlatform
|
from app.modules.tenancy.models import Store, StorePlatform
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ class StripeWebhookHandler:
|
|||||||
if existing.status == "processed":
|
if existing.status == "processed":
|
||||||
logger.info(f"Skipping duplicate event {event_id}")
|
logger.info(f"Skipping duplicate event {event_id}")
|
||||||
return {"status": "skipped", "reason": "duplicate"}
|
return {"status": "skipped", "reason": "duplicate"}
|
||||||
elif existing.status == "failed":
|
if existing.status == "failed":
|
||||||
logger.info(f"Retrying previously failed event {event_id}")
|
logger.info(f"Retrying previously failed event {event_id}")
|
||||||
else:
|
else:
|
||||||
# Record the event
|
# Record the event
|
||||||
@@ -86,14 +86,14 @@ class StripeWebhookHandler:
|
|||||||
if not handler:
|
if not handler:
|
||||||
logger.debug(f"No handler for event type {event_type}")
|
logger.debug(f"No handler for event type {event_type}")
|
||||||
existing.status = "processed"
|
existing.status = "processed"
|
||||||
existing.processed_at = datetime.now(timezone.utc)
|
existing.processed_at = datetime.now(UTC)
|
||||||
db.commit()
|
db.commit()
|
||||||
return {"status": "ignored", "reason": f"no handler for {event_type}"}
|
return {"status": "ignored", "reason": f"no handler for {event_type}"}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = handler(db, event)
|
result = handler(db, event)
|
||||||
existing.status = "processed"
|
existing.status = "processed"
|
||||||
existing.processed_at = datetime.now(timezone.utc)
|
existing.processed_at = datetime.now(UTC)
|
||||||
db.commit()
|
db.commit()
|
||||||
logger.info(f"Successfully processed event {event_id} ({event_type})")
|
logger.info(f"Successfully processed event {event_id} ({event_type})")
|
||||||
return {"status": "processed", "result": result}
|
return {"status": "processed", "result": result}
|
||||||
@@ -181,15 +181,15 @@ class StripeWebhookHandler:
|
|||||||
if session.subscription:
|
if session.subscription:
|
||||||
stripe_sub = stripe.Subscription.retrieve(session.subscription)
|
stripe_sub = stripe.Subscription.retrieve(session.subscription)
|
||||||
subscription.period_start = datetime.fromtimestamp(
|
subscription.period_start = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_start, tz=timezone.utc
|
stripe_sub.current_period_start, tz=UTC
|
||||||
)
|
)
|
||||||
subscription.period_end = datetime.fromtimestamp(
|
subscription.period_end = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_end, tz=timezone.utc
|
stripe_sub.current_period_end, tz=UTC
|
||||||
)
|
)
|
||||||
|
|
||||||
if stripe_sub.trial_end:
|
if stripe_sub.trial_end:
|
||||||
subscription.trial_ends_at = datetime.fromtimestamp(
|
subscription.trial_ends_at = datetime.fromtimestamp(
|
||||||
stripe_sub.trial_end, tz=timezone.utc
|
stripe_sub.trial_end, tz=UTC
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"Subscription checkout completed for merchant {merchant_id}")
|
logger.info(f"Subscription checkout completed for merchant {merchant_id}")
|
||||||
@@ -264,10 +264,10 @@ class StripeWebhookHandler:
|
|||||||
try:
|
try:
|
||||||
stripe_sub = stripe.Subscription.retrieve(session.subscription)
|
stripe_sub = stripe.Subscription.retrieve(session.subscription)
|
||||||
period_start = datetime.fromtimestamp(
|
period_start = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_start, tz=timezone.utc
|
stripe_sub.current_period_start, tz=UTC
|
||||||
)
|
)
|
||||||
period_end = datetime.fromtimestamp(
|
period_end = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_end, tz=timezone.utc
|
stripe_sub.current_period_end, tz=UTC
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not retrieve subscription period: {e}")
|
logger.warning(f"Could not retrieve subscription period: {e}")
|
||||||
@@ -320,10 +320,10 @@ class StripeWebhookHandler:
|
|||||||
subscription.stripe_subscription_id = stripe_sub.id
|
subscription.stripe_subscription_id = stripe_sub.id
|
||||||
subscription.status = self._map_stripe_status(stripe_sub.status)
|
subscription.status = self._map_stripe_status(stripe_sub.status)
|
||||||
subscription.period_start = datetime.fromtimestamp(
|
subscription.period_start = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_start, tz=timezone.utc
|
stripe_sub.current_period_start, tz=UTC
|
||||||
)
|
)
|
||||||
subscription.period_end = datetime.fromtimestamp(
|
subscription.period_end = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_end, tz=timezone.utc
|
stripe_sub.current_period_end, tz=UTC
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"Subscription created for merchant {subscription.merchant_id}")
|
logger.info(f"Subscription created for merchant {subscription.merchant_id}")
|
||||||
@@ -348,15 +348,15 @@ class StripeWebhookHandler:
|
|||||||
# Update status and period
|
# Update status and period
|
||||||
subscription.status = self._map_stripe_status(stripe_sub.status)
|
subscription.status = self._map_stripe_status(stripe_sub.status)
|
||||||
subscription.period_start = datetime.fromtimestamp(
|
subscription.period_start = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_start, tz=timezone.utc
|
stripe_sub.current_period_start, tz=UTC
|
||||||
)
|
)
|
||||||
subscription.period_end = datetime.fromtimestamp(
|
subscription.period_end = datetime.fromtimestamp(
|
||||||
stripe_sub.current_period_end, tz=timezone.utc
|
stripe_sub.current_period_end, tz=UTC
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle cancellation
|
# Handle cancellation
|
||||||
if stripe_sub.cancel_at_period_end:
|
if stripe_sub.cancel_at_period_end:
|
||||||
subscription.cancelled_at = datetime.now(timezone.utc)
|
subscription.cancelled_at = datetime.now(UTC)
|
||||||
subscription.cancellation_reason = stripe_sub.metadata.get(
|
subscription.cancellation_reason = stripe_sub.metadata.get(
|
||||||
"cancellation_reason", "user_request"
|
"cancellation_reason", "user_request"
|
||||||
)
|
)
|
||||||
@@ -407,7 +407,7 @@ class StripeWebhookHandler:
|
|||||||
|
|
||||||
# Cancel the subscription
|
# Cancel the subscription
|
||||||
subscription.status = SubscriptionStatus.CANCELLED.value
|
subscription.status = SubscriptionStatus.CANCELLED.value
|
||||||
subscription.cancelled_at = datetime.now(timezone.utc)
|
subscription.cancelled_at = datetime.now(UTC)
|
||||||
|
|
||||||
# Find all stores for this merchant, then cancel their add-ons
|
# Find all stores for this merchant, then cancel their add-ons
|
||||||
store_ids = [
|
store_ids = [
|
||||||
@@ -429,7 +429,7 @@ class StripeWebhookHandler:
|
|||||||
addon_count = 0
|
addon_count = 0
|
||||||
for addon in cancelled_addons:
|
for addon in cancelled_addons:
|
||||||
addon.status = "cancelled"
|
addon.status = "cancelled"
|
||||||
addon.cancelled_at = datetime.now(timezone.utc)
|
addon.cancelled_at = datetime.now(UTC)
|
||||||
addon_count += 1
|
addon_count += 1
|
||||||
|
|
||||||
if addon_count > 0:
|
if addon_count > 0:
|
||||||
@@ -463,7 +463,7 @@ class StripeWebhookHandler:
|
|||||||
stripe_invoice_id=invoice.id,
|
stripe_invoice_id=invoice.id,
|
||||||
stripe_payment_intent_id=invoice.payment_intent,
|
stripe_payment_intent_id=invoice.payment_intent,
|
||||||
invoice_number=invoice.number,
|
invoice_number=invoice.number,
|
||||||
invoice_date=datetime.fromtimestamp(invoice.created, tz=timezone.utc),
|
invoice_date=datetime.fromtimestamp(invoice.created, tz=UTC),
|
||||||
subtotal_cents=invoice.subtotal,
|
subtotal_cents=invoice.subtotal,
|
||||||
tax_cents=invoice.tax or 0,
|
tax_cents=invoice.tax or 0,
|
||||||
total_cents=invoice.total,
|
total_cents=invoice.total,
|
||||||
@@ -550,8 +550,8 @@ class StripeWebhookHandler:
|
|||||||
merchant_id=subscription.merchant_id,
|
merchant_id=subscription.merchant_id,
|
||||||
stripe_invoice_id=invoice.id,
|
stripe_invoice_id=invoice.id,
|
||||||
invoice_number=invoice.number,
|
invoice_number=invoice.number,
|
||||||
invoice_date=datetime.fromtimestamp(invoice.created, tz=timezone.utc),
|
invoice_date=datetime.fromtimestamp(invoice.created, tz=UTC),
|
||||||
due_date=datetime.fromtimestamp(invoice.due_date, tz=timezone.utc)
|
due_date=datetime.fromtimestamp(invoice.due_date, tz=UTC)
|
||||||
if invoice.due_date
|
if invoice.due_date
|
||||||
else None,
|
else None,
|
||||||
subtotal_cents=invoice.subtotal,
|
subtotal_cents=invoice.subtotal,
|
||||||
|
|||||||
@@ -54,31 +54,31 @@ Usage:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from app.modules.base import ModuleDefinition, ScheduledTask
|
from app.modules.base import ModuleDefinition, ScheduledTask
|
||||||
from app.modules.task_base import ModuleTask, DatabaseTask
|
from app.modules.events import (
|
||||||
from app.modules.tasks import (
|
ModuleEvent,
|
||||||
discover_module_tasks,
|
ModuleEventBus,
|
||||||
build_beat_schedule,
|
ModuleEventData,
|
||||||
parse_schedule,
|
module_event_bus,
|
||||||
get_module_task_routes,
|
|
||||||
)
|
)
|
||||||
from app.modules.registry import (
|
from app.modules.registry import (
|
||||||
MODULES,
|
|
||||||
CORE_MODULES,
|
CORE_MODULES,
|
||||||
OPTIONAL_MODULES,
|
|
||||||
INTERNAL_MODULES,
|
INTERNAL_MODULES,
|
||||||
|
MODULES,
|
||||||
|
OPTIONAL_MODULES,
|
||||||
get_core_module_codes,
|
get_core_module_codes,
|
||||||
get_optional_module_codes,
|
|
||||||
get_internal_module_codes,
|
get_internal_module_codes,
|
||||||
get_module_tier,
|
get_module_tier,
|
||||||
|
get_optional_module_codes,
|
||||||
is_core_module,
|
is_core_module,
|
||||||
is_internal_module,
|
is_internal_module,
|
||||||
)
|
)
|
||||||
from app.modules.service import ModuleService, module_service
|
from app.modules.service import ModuleService, module_service
|
||||||
from app.modules.events import (
|
from app.modules.task_base import DatabaseTask, ModuleTask
|
||||||
ModuleEvent,
|
from app.modules.tasks import (
|
||||||
ModuleEventData,
|
build_beat_schedule,
|
||||||
ModuleEventBus,
|
discover_module_tasks,
|
||||||
module_event_bus,
|
get_module_task_routes,
|
||||||
|
parse_schedule,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ def __getattr__(name: str):
|
|||||||
from app.modules.analytics.definition import analytics_module
|
from app.modules.analytics.definition import analytics_module
|
||||||
|
|
||||||
return analytics_module
|
return analytics_module
|
||||||
elif name == "get_analytics_module_with_routers":
|
if name == "get_analytics_module_with_routers":
|
||||||
from app.modules.analytics.definition import get_analytics_module_with_routers
|
from app.modules.analytics.definition import get_analytics_module_with_routers
|
||||||
|
|
||||||
return get_analytics_module_with_routers
|
return get_analytics_module_with_routers
|
||||||
|
|||||||
@@ -6,7 +6,12 @@ Defines the analytics module including its features, menu items,
|
|||||||
route configurations, and self-contained module settings.
|
route configurations, and self-contained module settings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from app.modules.base import MenuItemDefinition, MenuSectionDefinition, ModuleDefinition, PermissionDefinition
|
from app.modules.base import (
|
||||||
|
MenuItemDefinition,
|
||||||
|
MenuSectionDefinition,
|
||||||
|
ModuleDefinition,
|
||||||
|
PermissionDefinition,
|
||||||
|
)
|
||||||
from app.modules.enums import FrontendType
|
from app.modules.enums import FrontendType
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +31,9 @@ def _get_store_page_router():
|
|||||||
|
|
||||||
def _get_feature_provider():
|
def _get_feature_provider():
|
||||||
"""Lazy import of feature provider to avoid circular imports."""
|
"""Lazy import of feature provider to avoid circular imports."""
|
||||||
from app.modules.analytics.services.analytics_features import analytics_feature_provider
|
from app.modules.analytics.services.analytics_features import (
|
||||||
|
analytics_feature_provider,
|
||||||
|
)
|
||||||
|
|
||||||
return analytics_feature_provider
|
return analytics_feature_provider
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def __getattr__(name: str):
|
|||||||
if name == "store_api_router":
|
if name == "store_api_router":
|
||||||
from app.modules.analytics.routes.api import store_router
|
from app.modules.analytics.routes.api import store_router
|
||||||
return store_router
|
return store_router
|
||||||
elif name == "store_page_router":
|
if name == "store_page_router":
|
||||||
from app.modules.analytics.routes.pages import store_router
|
from app.modules.analytics.routes.pages import store_router
|
||||||
return store_router
|
return store_router
|
||||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ from fastapi import APIRouter, Depends, Query
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.api.deps import get_current_store_api, get_db, require_module_access
|
from app.api.deps import get_current_store_api, get_db, require_module_access
|
||||||
from app.modules.billing.dependencies.feature_gate import RequireFeature
|
|
||||||
from app.modules.analytics.services import stats_service
|
|
||||||
from app.modules.analytics.schemas import (
|
from app.modules.analytics.schemas import (
|
||||||
StoreAnalyticsCatalog,
|
StoreAnalyticsCatalog,
|
||||||
StoreAnalyticsImports,
|
StoreAnalyticsImports,
|
||||||
StoreAnalyticsInventory,
|
StoreAnalyticsInventory,
|
||||||
StoreAnalyticsResponse,
|
StoreAnalyticsResponse,
|
||||||
)
|
)
|
||||||
|
from app.modules.analytics.services import stats_service
|
||||||
|
from app.modules.billing.dependencies.feature_gate import RequireFeature
|
||||||
from app.modules.enums import FrontendType
|
from app.modules.enums import FrontendType
|
||||||
from app.modules.tenancy.models import User
|
from app.modules.tenancy.models import User
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ from sqlalchemy.orm import Session
|
|||||||
|
|
||||||
from app.api.deps import get_db, require_menu_access
|
from app.api.deps import get_db, require_menu_access
|
||||||
from app.modules.core.utils.page_context import get_admin_context
|
from app.modules.core.utils.page_context import get_admin_context
|
||||||
from app.templates_config import templates
|
|
||||||
from app.modules.enums import FrontendType
|
from app.modules.enums import FrontendType
|
||||||
from app.modules.tenancy.models import User
|
from app.modules.tenancy.models import User
|
||||||
|
from app.templates_config import templates
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ from fastapi.responses import HTMLResponse
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.api.deps import get_current_store_from_cookie_or_header, get_db
|
from app.api.deps import get_current_store_from_cookie_or_header, get_db
|
||||||
from app.modules.core.services.platform_settings_service import platform_settings_service # noqa: MOD-004 - shared platform service
|
from app.modules.core.services.platform_settings_service import (
|
||||||
|
platform_settings_service, # noqa: MOD-004 - shared platform service
|
||||||
|
)
|
||||||
|
from app.modules.tenancy.models import Store, User
|
||||||
from app.templates_config import templates
|
from app.templates_config import templates
|
||||||
from app.modules.tenancy.models import User
|
|
||||||
from app.modules.tenancy.models import Store
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -6,29 +6,29 @@ This is the canonical location for analytics schemas.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from app.modules.analytics.schemas.stats import (
|
from app.modules.analytics.schemas.stats import (
|
||||||
StatsResponse,
|
|
||||||
MarketplaceStatsResponse,
|
|
||||||
ImportStatsResponse,
|
|
||||||
UserStatsResponse,
|
|
||||||
StoreStatsResponse,
|
|
||||||
ProductStatsResponse,
|
|
||||||
PlatformStatsResponse,
|
|
||||||
OrderStatsBasicResponse,
|
|
||||||
AdminDashboardResponse,
|
AdminDashboardResponse,
|
||||||
StoreProductStats,
|
|
||||||
StoreOrderStats,
|
|
||||||
StoreCustomerStats,
|
|
||||||
StoreRevenueStats,
|
|
||||||
StoreInfo,
|
|
||||||
StoreDashboardStatsResponse,
|
|
||||||
StoreAnalyticsImports,
|
|
||||||
StoreAnalyticsCatalog,
|
|
||||||
StoreAnalyticsInventory,
|
|
||||||
StoreAnalyticsResponse,
|
|
||||||
ValidatorStats,
|
|
||||||
CodeQualityDashboardStatsResponse,
|
CodeQualityDashboardStatsResponse,
|
||||||
CustomerStatsResponse,
|
CustomerStatsResponse,
|
||||||
|
ImportStatsResponse,
|
||||||
|
MarketplaceStatsResponse,
|
||||||
|
OrderStatsBasicResponse,
|
||||||
OrderStatsResponse,
|
OrderStatsResponse,
|
||||||
|
PlatformStatsResponse,
|
||||||
|
ProductStatsResponse,
|
||||||
|
StatsResponse,
|
||||||
|
StoreAnalyticsCatalog,
|
||||||
|
StoreAnalyticsImports,
|
||||||
|
StoreAnalyticsInventory,
|
||||||
|
StoreAnalyticsResponse,
|
||||||
|
StoreCustomerStats,
|
||||||
|
StoreDashboardStatsResponse,
|
||||||
|
StoreInfo,
|
||||||
|
StoreOrderStats,
|
||||||
|
StoreProductStats,
|
||||||
|
StoreRevenueStats,
|
||||||
|
StoreStatsResponse,
|
||||||
|
UserStatsResponse,
|
||||||
|
ValidatorStats,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ from app.modules.core.schemas.dashboard import (
|
|||||||
PlatformStatsResponse,
|
PlatformStatsResponse,
|
||||||
ProductStatsResponse,
|
ProductStatsResponse,
|
||||||
StatsResponse,
|
StatsResponse,
|
||||||
UserStatsResponse,
|
|
||||||
StoreCustomerStats,
|
StoreCustomerStats,
|
||||||
StoreDashboardStatsResponse,
|
StoreDashboardStatsResponse,
|
||||||
StoreInfo,
|
StoreInfo,
|
||||||
@@ -31,9 +30,9 @@ from app.modules.core.schemas.dashboard import (
|
|||||||
StoreProductStats,
|
StoreProductStats,
|
||||||
StoreRevenueStats,
|
StoreRevenueStats,
|
||||||
StoreStatsResponse,
|
StoreStatsResponse,
|
||||||
|
UserStatsResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Store Analytics (Analytics-specific, not in core)
|
# Store Analytics (Analytics-specific, not in core)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ This is the canonical location for analytics services.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from app.modules.analytics.services.stats_service import (
|
from app.modules.analytics.services.stats_service import (
|
||||||
stats_service,
|
|
||||||
StatsService,
|
StatsService,
|
||||||
|
stats_service,
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
@@ -12,11 +12,8 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from sqlalchemy import func
|
|
||||||
|
|
||||||
from app.modules.contracts.features import (
|
from app.modules.contracts.features import (
|
||||||
FeatureDeclaration,
|
FeatureDeclaration,
|
||||||
FeatureProviderProtocol,
|
|
||||||
FeatureScope,
|
FeatureScope,
|
||||||
FeatureType,
|
FeatureType,
|
||||||
FeatureUsage,
|
FeatureUsage,
|
||||||
|
|||||||
@@ -18,14 +18,16 @@ from typing import Any
|
|||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.modules.tenancy.exceptions import AdminOperationException, StoreNotFoundException
|
from app.modules.catalog.models import Product
|
||||||
from app.modules.customers.models.customer import Customer
|
from app.modules.customers.models.customer import Customer
|
||||||
from app.modules.inventory.models import Inventory
|
from app.modules.inventory.models import Inventory
|
||||||
from app.modules.marketplace.models import MarketplaceImportJob, MarketplaceProduct
|
from app.modules.marketplace.models import MarketplaceImportJob, MarketplaceProduct
|
||||||
from app.modules.orders.models import Order
|
from app.modules.orders.models import Order
|
||||||
from app.modules.catalog.models import Product
|
from app.modules.tenancy.exceptions import (
|
||||||
from app.modules.tenancy.models import User
|
AdminOperationException,
|
||||||
from app.modules.tenancy.models import Store
|
StoreNotFoundException,
|
||||||
|
)
|
||||||
|
from app.modules.tenancy.models import Store, User
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ Self-Contained Module Structure:
|
|||||||
└── locales/ # Translation files
|
└── locales/ # Translation files
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Any, Callable
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
@@ -52,7 +53,6 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
from app.modules.enums import FrontendType
|
from app.modules.enums import FrontendType
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Menu Item Definitions
|
# Menu Item Definitions
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -805,9 +805,8 @@ class ModuleDefinition:
|
|||||||
"""
|
"""
|
||||||
if self.is_core:
|
if self.is_core:
|
||||||
return "core"
|
return "core"
|
||||||
elif self.is_internal:
|
if self.is_internal:
|
||||||
return "internal"
|
return "internal"
|
||||||
else:
|
|
||||||
return "optional"
|
return "optional"
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def __getattr__(name: str):
|
|||||||
if name == "billing_module":
|
if name == "billing_module":
|
||||||
from app.modules.billing.definition import billing_module
|
from app.modules.billing.definition import billing_module
|
||||||
return billing_module
|
return billing_module
|
||||||
elif name == "get_billing_module_with_routers":
|
if name == "get_billing_module_with_routers":
|
||||||
from app.modules.billing.definition import get_billing_module_with_routers
|
from app.modules.billing.definition import get_billing_module_with_routers
|
||||||
return get_billing_module_with_routers
|
return get_billing_module_with_routers
|
||||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ route configurations, and scheduled tasks.
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.modules.base import MenuItemDefinition, MenuSectionDefinition, ModuleDefinition, PermissionDefinition, ScheduledTask
|
from app.modules.base import (
|
||||||
|
MenuItemDefinition,
|
||||||
|
MenuSectionDefinition,
|
||||||
|
ModuleDefinition,
|
||||||
|
PermissionDefinition,
|
||||||
|
ScheduledTask,
|
||||||
|
)
|
||||||
from app.modules.enums import FrontendType
|
from app.modules.enums import FrontendType
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user