Restructure with Turborepo
This commit is contained in:
@@ -0,0 +1,306 @@
|
||||
"""Phase 1 initial schema
|
||||
|
||||
Revision ID: 001_initial
|
||||
Revises: None
|
||||
Create Date: 2026-06-30
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
|
||||
revision: str = "001_initial"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"erp_systems",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("erp_type", sa.String(50), nullable=False, index=True),
|
||||
sa.Column("version", sa.String(50)),
|
||||
sa.Column("base_url", sa.String(500)),
|
||||
sa.Column("auth_config", JSONB),
|
||||
sa.Column("status", sa.String(20), default="pending", index=True),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"pharmacies",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("code", sa.String(50), unique=True, nullable=False, index=True),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("address", sa.String(500)),
|
||||
sa.Column("city", sa.String(100)),
|
||||
sa.Column("province", sa.String(100)),
|
||||
sa.Column("postal_code", sa.String(20)),
|
||||
sa.Column("country", sa.String(2), default="ES"),
|
||||
sa.Column("latitude", sa.Float),
|
||||
sa.Column("longitude", sa.Float),
|
||||
sa.Column("phone", sa.String(50)),
|
||||
sa.Column("email", sa.String(255)),
|
||||
sa.Column("opening_hours", JSONB),
|
||||
sa.Column("status", sa.String(20), default="pending", index=True),
|
||||
sa.Column("erp_type", sa.String(50)),
|
||||
sa.Column("erp_version", sa.String(50)),
|
||||
sa.Column("sync_strategy", sa.String(20), default="incremental"),
|
||||
sa.Column("sync_interval_minutes", sa.Integer, default=30),
|
||||
sa.Column("last_sync_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_sync_status", sa.String(20)),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
)
|
||||
op.create_index("ix_pharmacies_location", "pharmacies", ["latitude", "longitude"])
|
||||
op.create_index("ix_pharmacies_status_erp", "pharmacies", ["status", "erp_type"])
|
||||
|
||||
op.create_table(
|
||||
"connectors",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("erp_system_id", UUID(as_uuid=True), sa.ForeignKey("erp_systems.id"), nullable=False, index=True),
|
||||
sa.Column("pharmacy_id", UUID(as_uuid=True), sa.ForeignKey("pharmacies.id"), nullable=False, index=True),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("connector_type", sa.String(50), nullable=False, index=True),
|
||||
sa.Column("version", sa.String(20), default="1.0.0"),
|
||||
sa.Column("config", JSONB),
|
||||
sa.Column("status", sa.String(20), default="pending", index=True),
|
||||
sa.Column("last_heartbeat_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_error", sa.Text),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
sa.UniqueConstraint("erp_system_id", "pharmacy_id", name="uq_connector_erp_pharmacy"),
|
||||
)
|
||||
|
||||
op.add_column("pharmacies", sa.Column("connector_id", UUID(as_uuid=True), sa.ForeignKey("connectors.id"), nullable=True))
|
||||
op.create_index("ix_pharmacies_connector_id", "pharmacies", ["connector_id"])
|
||||
|
||||
op.create_table(
|
||||
"connector_versions",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("connector_id", UUID(as_uuid=True), sa.ForeignKey("connectors.id"), nullable=False, index=True),
|
||||
sa.Column("connector_type", sa.String(50), nullable=False, index=True),
|
||||
sa.Column("version", sa.String(20), nullable=False),
|
||||
sa.Column("changelog", sa.Text),
|
||||
sa.Column("download_url", sa.String(500)),
|
||||
sa.Column("checksum", sa.String(128)),
|
||||
sa.Column("is_latest", sa.Boolean, default=False, index=True),
|
||||
sa.Column("is_required", sa.Boolean, default=False),
|
||||
sa.Column("released_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
sa.UniqueConstraint("connector_type", "version", name="uq_connector_version"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"connector_configs",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("connector_id", UUID(as_uuid=True), sa.ForeignKey("connectors.id"), nullable=False, index=True),
|
||||
sa.Column("key", sa.String(100), nullable=False),
|
||||
sa.Column("value", sa.Text, nullable=False),
|
||||
sa.Column("encrypted", sa.Boolean, default=False),
|
||||
sa.Column("description", sa.String(500)),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
sa.UniqueConstraint("connector_id", "key", name="uq_connector_config_key"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"medications",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("nregistro", sa.String(50), unique=True, nullable=False, index=True),
|
||||
sa.Column("name", sa.String(500), nullable=False, index=True),
|
||||
sa.Column("active_ingredient", sa.String(500)),
|
||||
sa.Column("dosage", sa.String(100)),
|
||||
sa.Column("form", sa.String(100)),
|
||||
sa.Column("atc_code", sa.String(20), index=True),
|
||||
sa.Column("prescription_required", sa.Boolean, default=False),
|
||||
sa.Column("manufacturer", sa.String(255)),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"medication_catalog",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("pharmacy_id", UUID(as_uuid=True), sa.ForeignKey("pharmacies.id"), nullable=False, index=True),
|
||||
sa.Column("medication_id", UUID(as_uuid=True), sa.ForeignKey("medications.id"), nullable=False, index=True),
|
||||
sa.Column("price", sa.Float),
|
||||
sa.Column("stock", sa.Integer, default=0),
|
||||
sa.Column("is_available", sa.Boolean, default=False, index=True),
|
||||
sa.Column("last_verified_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
sa.UniqueConstraint("pharmacy_id", "medication_id", name="uq_catalog_pharmacy_medication"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"inventory_records",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("pharmacy_id", UUID(as_uuid=True), sa.ForeignKey("pharmacies.id"), nullable=False, index=True),
|
||||
sa.Column("medication_id", UUID(as_uuid=True), sa.ForeignKey("medications.id"), nullable=False, index=True),
|
||||
sa.Column("stock", sa.Integer, default=0),
|
||||
sa.Column("reserved_stock", sa.Integer, default=0),
|
||||
sa.Column("available_stock", sa.Integer, default=0),
|
||||
sa.Column("min_stock", sa.Integer),
|
||||
sa.Column("max_stock", sa.Integer),
|
||||
sa.Column("last_updated_at", sa.DateTime(timezone=True), index=True),
|
||||
sa.Column("source", sa.String(100)),
|
||||
sa.Column("batch_id", sa.String(100)),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
sa.UniqueConstraint("pharmacy_id", "medication_id", name="uq_inventory_pharmacy_medication"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"inventory_history",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("inventory_record_id", UUID(as_uuid=True), sa.ForeignKey("inventory_records.id"), nullable=False, index=True),
|
||||
sa.Column("pharmacy_id", UUID(as_uuid=True), sa.ForeignKey("pharmacies.id"), nullable=False, index=True),
|
||||
sa.Column("medication_id", UUID(as_uuid=True), sa.ForeignKey("medications.id"), nullable=False, index=True),
|
||||
sa.Column("previous_stock", sa.Integer, nullable=False),
|
||||
sa.Column("new_stock", sa.Integer, nullable=False),
|
||||
sa.Column("delta", sa.Integer, nullable=False),
|
||||
sa.Column("reason", sa.String(255)),
|
||||
sa.Column("source", sa.String(100)),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False, index=True),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"technical_users",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("username", sa.String(100), unique=True, nullable=False, index=True),
|
||||
sa.Column("email", sa.String(255)),
|
||||
sa.Column("full_name", sa.String(255)),
|
||||
sa.Column("password_hash", sa.String(255), nullable=False),
|
||||
sa.Column("role", sa.String(20), default="viewer", index=True),
|
||||
sa.Column("is_active", sa.Boolean, default=True, index=True),
|
||||
sa.Column("scopes", JSONB, default=list),
|
||||
sa.Column("last_login_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"api_clients",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("description", sa.Text),
|
||||
sa.Column("owner_id", UUID(as_uuid=True), sa.ForeignKey("technical_users.id"), nullable=True, index=True),
|
||||
sa.Column("is_active", sa.Boolean, default=True, index=True),
|
||||
sa.Column("scopes", JSONB, default=list),
|
||||
sa.Column("rate_limit", sa.Integer),
|
||||
sa.Column("allowed_ips", JSONB, default=list),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"api_keys",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("api_client_id", UUID(as_uuid=True), sa.ForeignKey("api_clients.id"), nullable=False, index=True),
|
||||
sa.Column("key_prefix", sa.String(10), nullable=False, index=True),
|
||||
sa.Column("key_hash", sa.LargeBinary(64), nullable=False),
|
||||
sa.Column("name", sa.String(255), nullable=False),
|
||||
sa.Column("is_active", sa.Boolean, default=True, index=True),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("last_used_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
)
|
||||
op.create_index("ix_api_keys_prefix_active", "api_keys", ["key_prefix", "is_active"])
|
||||
|
||||
op.create_table(
|
||||
"reservations",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("pharmacy_id", UUID(as_uuid=True), sa.ForeignKey("pharmacies.id"), nullable=False, index=True),
|
||||
sa.Column("medication_id", UUID(as_uuid=True), sa.ForeignKey("medications.id"), nullable=False, index=True),
|
||||
sa.Column("user_id", UUID(as_uuid=True), sa.ForeignKey("technical_users.id"), nullable=True, index=True),
|
||||
sa.Column("external_reference", sa.String(255)),
|
||||
sa.Column("quantity", sa.Integer, nullable=False),
|
||||
sa.Column("status", sa.String(20), default="pending", index=True),
|
||||
sa.Column("expires_at", sa.DateTime(timezone=True), index=True),
|
||||
sa.Column("confirmed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("cancelled_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("idempotency_key", sa.String(255), unique=True, index=True),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
)
|
||||
op.create_index("ix_reservations_pharmacy_status", "reservations", ["pharmacy_id", "status"])
|
||||
op.create_index("ix_reservations_expires", "reservations", ["expires_at"])
|
||||
|
||||
op.create_table(
|
||||
"sync_jobs",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("pharmacy_id", UUID(as_uuid=True), sa.ForeignKey("pharmacies.id"), nullable=False, index=True),
|
||||
sa.Column("connector_id", UUID(as_uuid=True), sa.ForeignKey("connectors.id"), nullable=False, index=True),
|
||||
sa.Column("strategy", sa.String(20), nullable=False),
|
||||
sa.Column("status", sa.String(20), default="pending", index=True),
|
||||
sa.Column("started_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
||||
sa.Column("items_total", sa.Integer),
|
||||
sa.Column("items_processed", sa.Integer),
|
||||
sa.Column("items_failed", sa.Integer),
|
||||
sa.Column("error_message", sa.Text),
|
||||
sa.Column("metadata", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"sync_logs",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("sync_job_id", UUID(as_uuid=True), sa.ForeignKey("sync_jobs.id"), nullable=False, index=True),
|
||||
sa.Column("level", sa.String(20), default="INFO", nullable=False),
|
||||
sa.Column("message", sa.Text, nullable=False),
|
||||
sa.Column("details", JSONB),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False, index=True),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"audit_logs",
|
||||
sa.Column("id", UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column("actor_id", UUID(as_uuid=True), index=True),
|
||||
sa.Column("actor_type", sa.String(50)),
|
||||
sa.Column("action", sa.String(100), nullable=False, index=True),
|
||||
sa.Column("resource_type", sa.String(100), nullable=False, index=True),
|
||||
sa.Column("resource_id", sa.String(255), index=True),
|
||||
sa.Column("old_value", JSONB),
|
||||
sa.Column("new_value", JSONB),
|
||||
sa.Column("ip_address", sa.String(45)),
|
||||
sa.Column("user_agent", sa.String(500)),
|
||||
sa.Column("request_id", sa.String(100), index=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False, index=True),
|
||||
)
|
||||
op.create_index("ix_audit_logs_action_resource", "audit_logs", ["action", "resource_type"])
|
||||
op.create_index("ix_audit_logs_actor_created", "audit_logs", ["actor_id", "created_at"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("audit_logs")
|
||||
op.drop_table("sync_logs")
|
||||
op.drop_table("sync_jobs")
|
||||
op.drop_table("reservations")
|
||||
op.drop_table("api_keys")
|
||||
op.drop_table("api_clients")
|
||||
op.drop_table("technical_users")
|
||||
op.drop_table("inventory_history")
|
||||
op.drop_table("inventory_records")
|
||||
op.drop_table("medication_catalog")
|
||||
op.drop_table("medications")
|
||||
op.drop_table("connector_configs")
|
||||
op.drop_table("connector_versions")
|
||||
op.drop_column("pharmacies", "connector_id")
|
||||
op.drop_table("connectors")
|
||||
op.drop_table("pharmacies")
|
||||
op.drop_table("erp_systems")
|
||||
Reference in New Issue
Block a user