Initial local state

This commit is contained in:
2026-07-15 07:17:25 +05:00
commit 68221821a5
78 changed files with 10318 additions and 0 deletions
+184
View File
@@ -0,0 +1,184 @@
# - *- coding: utf- 8 - *-
import asyncio
from logging.config import fileConfig
from typing import Any, List, Optional
from alembic import context
from alembic.operations import ops
from sqlalchemy import pool, text
from sqlalchemy.engine import Connection
from sqlalchemy.schema import DefaultClause
from sqlalchemy.ext.asyncio import async_engine_from_config
from tgbot.database.core import Base
import tgbot.database # noqa: F401
config = context.config
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = Base.metadata
INDEX_DIFF_NOISE = {"ix_storage_payments_id", "ix_storage_settings_id"}
# Отбрасывание SQLite-шума из автогенерации
def _filter_autogenerated_ops(container: Any) -> None:
filtered_ops = []
for operation in container.ops:
if isinstance(operation, ops.AlterColumnOp):
continue
if _is_index_noise(operation):
continue
if isinstance(operation, ops.ModifyTableOps):
_filter_autogenerated_ops(operation)
if operation.ops:
filtered_ops.append(operation)
continue
filtered_ops.append(operation)
container.ops = filtered_ops
# Подготовка новых колонок к безопасному SQLite ADD COLUMN
def _prepare_sqlite_add_columns(container: Any) -> None:
for operation in container.ops:
if isinstance(operation, ops.AddColumnOp):
_apply_server_default(operation.column)
continue
if isinstance(operation, ops.ModifyTableOps):
_prepare_sqlite_add_columns(operation)
# Перенос default модели в server_default миграции
def _apply_server_default(column: Any) -> None:
if column.nullable or column.server_default is not None or column.default is None:
return
default_value = _get_simple_default(column.default.arg)
if default_value is None:
return
column.server_default = DefaultClause(text(default_value))
# Получение SQL-литерала для простых default-значений
def _get_simple_default(value: Any) -> Optional[str]:
if callable(value):
return None
if isinstance(value, bool):
return "1" if value else "0"
if isinstance(value, (int, float)):
return str(value)
if isinstance(value, str):
return repr(value)
return None
# Игнорирование индексов, которые SQLite отражает иначе
def _is_index_noise(operation: Any) -> bool:
if not isinstance(operation, (ops.CreateIndexOp, ops.DropIndexOp)):
return False
return getattr(operation, "index_name", "") in INDEX_DIFF_NOISE
# Ограничение сравнения SQLite только реальными добавлениями/удалениями
def _include_object(object_: Any, name: str, type_: str, reflected: bool, compare_to: Any) -> bool:
if type_ == "column" and compare_to is not None:
return False
if type_ == "index" and name in INDEX_DIFF_NOISE:
return False
return True
# Очистка автосозданной миграции перед записью
def _process_revision_directives(_context: Any, _revision: Any, directives: List[Any]) -> None:
if not directives:
return
script = directives[0]
_prepare_sqlite_add_columns(script.upgrade_ops)
_filter_autogenerated_ops(script.upgrade_ops)
_filter_autogenerated_ops(script.downgrade_ops)
if not script.upgrade_ops.ops and not script.downgrade_ops.ops:
config.attributes["autogenerate_empty"] = True
directives.clear()
# Запуск миграций без подключения
def run_migrations_offline() -> None:
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
render_as_batch=True,
compare_type=False,
include_object=_include_object,
process_revision_directives=_process_revision_directives,
)
with context.begin_transaction():
context.run_migrations()
# Запуск миграций на соединении
def do_run_migrations(connection: Connection) -> None:
context.configure(
connection=connection,
target_metadata=target_metadata,
render_as_batch=True,
compare_type=False,
include_object=_include_object,
process_revision_directives=_process_revision_directives,
)
with context.begin_transaction():
context.run_migrations()
# Запуск миграций через async-engine
async def run_async_migrations() -> None:
connectable = async_engine_from_config(
config.get_section(config.config_ini_section, {}),
poolclass=pool.NullPool,
)
connection = connectable.connect()
try:
await connection.start()
await connection.run_sync(do_run_migrations)
finally:
await connection.close()
await connectable.dispose()
if context.is_offline_mode():
run_migrations_offline()
else:
external_connection = config.attributes.get("connection")
if external_connection is not None:
do_run_migrations(external_connection)
else:
asyncio.run(run_async_migrations())
+24
View File
@@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
def upgrade() -> None:
${upgrades if upgrades else "pass"}
def downgrade() -> None:
${downgrades if downgrades else "pass"}
@@ -0,0 +1,515 @@
"""Начальная схема Телеграм-магазина
Revision ID: 0001_shop_schema
Revises:
Create Date: 2026-05-29 00:00:00
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "0001_shop_schema"
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
# Проверка наличия таблицы
def _table_exists(table_name: str) -> bool:
bind = op.get_bind()
result = bind.execute(
sa.text(
"""
SELECT name
FROM sqlite_master
WHERE type = 'table'
AND name = :table_name
"""
),
{"table_name": table_name},
)
return result.scalar_one_or_none() is not None
# Проверка наличия колонки
def _column_exists(table_name: str, column_name: str) -> bool:
bind = op.get_bind()
result = bind.execute(sa.text(f"PRAGMA table_info({table_name})"))
return column_name in [row.name for row in result]
# Добавление колонки, если ее нет
def _add_column(table_name: str, column_name: str, column_sql: str) -> None:
if not _column_exists(table_name, column_name):
op.execute(sa.text(f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_sql}"))
# Создание индекса, если его нет
def _create_index(index_name: str, table_name: str, columns: str, *, unique: bool = False) -> None:
unique_sql = "UNIQUE " if unique else ""
op.execute(sa.text(f"CREATE {unique_sql}INDEX IF NOT EXISTS {index_name} ON {table_name} ({columns})"))
# Нормализация текстовых bool-значений
def _normalize_boolean_text(table_name: str, column_name: str, default_value: str) -> None:
bind = op.get_bind()
bind.execute(
sa.text(
f"""
UPDATE {table_name}
SET {column_name} = CASE LOWER(CAST(COALESCE({column_name}, :default_value) AS TEXT))
WHEN '1' THEN 'True'
WHEN 'true' THEN 'True'
WHEN 'yes' THEN 'True'
WHEN 'on' THEN 'True'
WHEN 'да' THEN 'True'
WHEN '0' THEN 'False'
WHEN 'false' THEN 'False'
WHEN 'no' THEN 'False'
WHEN 'off' THEN 'False'
WHEN 'нет' THEN 'False'
ELSE :default_value
END
"""
),
{"default_value": default_value},
)
# Сбор SQL для переноса текстовой колонки
def _copy_expr(table_name: str, column_name: str, default_value: str) -> str:
if _column_exists(table_name, column_name):
return f"COALESCE({column_name}, '{default_value}')"
return f"'{default_value}'"
# Сбор SQL для переноса числовой колонки
def _copy_int_expr(table_name: str, column_name: str, default_value: int = 0) -> str:
if _column_exists(table_name, column_name):
return f"COALESCE({column_name}, {default_value})"
return str(default_value)
# Создание или правка таблицы пользователей
def _ensure_users() -> None:
bind = op.get_bind()
if not _table_exists("storage_users"):
op.create_table(
"storage_users",
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("user_login", sa.String(length=255), nullable=False, server_default=""),
sa.Column("user_name", sa.String(length=255), nullable=False, server_default=""),
sa.Column("user_surname", sa.String(length=255), nullable=False, server_default=""),
sa.Column("user_fullname", sa.String(length=511), nullable=False, server_default=""),
sa.Column("user_balance", sa.Float(), nullable=False, server_default="0"),
sa.Column("user_refill", sa.Float(), nullable=False, server_default="0"),
sa.Column("user_give", sa.Float(), nullable=False, server_default="0"),
sa.Column("user_unix", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("increment"),
)
else:
_add_column("storage_users", "user_login", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_users", "user_name", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_users", "user_surname", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_users", "user_fullname", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_users", "user_balance", "REAL NOT NULL DEFAULT 0")
_add_column("storage_users", "user_refill", "REAL NOT NULL DEFAULT 0")
_add_column("storage_users", "user_give", "REAL NOT NULL DEFAULT 0")
_add_column("storage_users", "user_unix", "INTEGER NOT NULL DEFAULT 0")
bind.execute(sa.text("UPDATE storage_users SET user_login = COALESCE(user_login, '')"))
bind.execute(sa.text("UPDATE storage_users SET user_name = COALESCE(user_name, '')"))
bind.execute(sa.text("UPDATE storage_users SET user_surname = COALESCE(user_surname, '')"))
bind.execute(
sa.text(
"""
UPDATE storage_users
SET user_fullname = CASE
WHEN user_fullname IS NULL OR user_fullname = '' THEN TRIM(user_name || ' ' || user_surname)
ELSE user_fullname
END
"""
)
)
bind.execute(sa.text("UPDATE storage_users SET user_balance = COALESCE(user_balance, 0)"))
bind.execute(sa.text("UPDATE storage_users SET user_refill = COALESCE(user_refill, 0)"))
bind.execute(sa.text("UPDATE storage_users SET user_give = COALESCE(user_give, 0)"))
bind.execute(sa.text("UPDATE storage_users SET user_unix = COALESCE(user_unix, 0)"))
bind.execute(sa.text("DELETE FROM storage_users WHERE user_id IS NULL"))
bind.execute(
sa.text(
"""
DELETE
FROM storage_users
WHERE user_id IS NOT NULL
AND rowid NOT IN (
SELECT MAX(rowid)
FROM storage_users
WHERE user_id IS NOT NULL
GROUP BY user_id
)
"""
)
)
_create_index("ix_storage_users_user_id", "storage_users", "user_id", unique=True)
# Создание или правка таблицы настроек
def _ensure_settings() -> None:
bind = op.get_bind()
if not _table_exists("storage_settings"):
op.create_table(
"storage_settings",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("status_work", sa.String(length=16), nullable=False, server_default="True"),
sa.Column("status_refill", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("status_buy", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("notification_refill", sa.String(length=16), nullable=False, server_default="True"),
sa.Column("notification_buy", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("misc_faq", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_support", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_bot", sa.String(length=255), nullable=False, server_default="None"),
sa.Column("misc_hosting_text", sa.String(length=64), nullable=False, server_default="telegraph"),
sa.Column("misc_token_telegraph", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_discord_webhook_url", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_discord_webhook_name", sa.String(length=255), nullable=False, server_default="None"),
sa.Column("misc_hide_category", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("misc_hide_position", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("misc_profit_day", sa.Integer(), nullable=False, server_default="0"),
sa.Column("misc_profit_week", sa.Integer(), nullable=False, server_default="0"),
sa.Column("misc_profit_month", sa.Integer(), nullable=False, server_default="0"),
sa.PrimaryKeyConstraint("id"),
)
else:
bind.execute(sa.text("DROP TABLE IF EXISTS storage_settings_new"))
op.create_table(
"storage_settings_new",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("status_work", sa.String(length=16), nullable=False, server_default="True"),
sa.Column("status_refill", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("status_buy", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("notification_refill", sa.String(length=16), nullable=False, server_default="True"),
sa.Column("notification_buy", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("misc_faq", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_support", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_bot", sa.String(length=255), nullable=False, server_default="None"),
sa.Column("misc_hosting_text", sa.String(length=64), nullable=False, server_default="telegraph"),
sa.Column("misc_token_telegraph", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_discord_webhook_url", sa.String(), nullable=False, server_default="None"),
sa.Column("misc_discord_webhook_name", sa.String(length=255), nullable=False, server_default="None"),
sa.Column("misc_hide_category", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("misc_hide_position", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("misc_profit_day", sa.Integer(), nullable=False, server_default="0"),
sa.Column("misc_profit_week", sa.Integer(), nullable=False, server_default="0"),
sa.Column("misc_profit_month", sa.Integer(), nullable=False, server_default="0"),
sa.PrimaryKeyConstraint("id"),
)
bind.execute(
sa.text(
f"""
INSERT INTO storage_settings_new (
id,
status_work,
status_refill,
status_buy,
notification_refill,
notification_buy,
misc_faq,
misc_support,
misc_bot,
misc_hosting_text,
misc_token_telegraph,
misc_discord_webhook_url,
misc_discord_webhook_name,
misc_hide_category,
misc_hide_position,
misc_profit_day,
misc_profit_week,
misc_profit_month
)
SELECT
1,
{_copy_expr("storage_settings", "status_work", "True")},
{_copy_expr("storage_settings", "status_refill", "False")},
{_copy_expr("storage_settings", "status_buy", "False")},
{_copy_expr("storage_settings", "notification_refill", "True")},
{_copy_expr("storage_settings", "notification_buy", "False")},
{_copy_expr("storage_settings", "misc_faq", "None")},
{_copy_expr("storage_settings", "misc_support", "None")},
{_copy_expr("storage_settings", "misc_bot", "None")},
{_copy_expr("storage_settings", "misc_hosting_text", "telegraph")},
{_copy_expr("storage_settings", "misc_token_telegraph", "None")},
{_copy_expr("storage_settings", "misc_discord_webhook_url", "None")},
{_copy_expr("storage_settings", "misc_discord_webhook_name", "None")},
{_copy_expr("storage_settings", "misc_hide_category", "False")},
{_copy_expr("storage_settings", "misc_hide_position", "False")},
{_copy_int_expr("storage_settings", "misc_profit_day")},
{_copy_int_expr("storage_settings", "misc_profit_week")},
{_copy_int_expr("storage_settings", "misc_profit_month")}
FROM storage_settings
LIMIT 1
"""
)
)
bind.execute(sa.text("DROP TABLE storage_settings"))
bind.execute(sa.text("ALTER TABLE storage_settings_new RENAME TO storage_settings"))
bind.execute(
sa.text(
"""
INSERT INTO storage_settings (id)
SELECT 1
WHERE NOT EXISTS (SELECT 1 FROM storage_settings WHERE id = 1)
"""
)
)
for column, default in (
("status_work", "True"),
("status_refill", "False"),
("status_buy", "False"),
("notification_refill", "True"),
("notification_buy", "False"),
("misc_hide_category", "False"),
("misc_hide_position", "False"),
):
_normalize_boolean_text("storage_settings", column, default)
_create_index("ix_storage_settings_id", "storage_settings", "id", unique=True)
# Создание или правка таблицы платежей
def _ensure_payments() -> None:
bind = op.get_bind()
if not _table_exists("storage_payments"):
op.create_table(
"storage_payments",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("cryptobot_token", sa.String(), nullable=False, server_default="None"),
sa.Column("yoomoney_token", sa.String(), nullable=False, server_default="None"),
sa.Column("status_cryptobot", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("status_yoomoney", sa.String(length=16), nullable=False, server_default="False"),
sa.PrimaryKeyConstraint("id"),
)
else:
bind.execute(sa.text("DROP TABLE IF EXISTS storage_payments_new"))
op.create_table(
"storage_payments_new",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("cryptobot_token", sa.String(), nullable=False, server_default="None"),
sa.Column("yoomoney_token", sa.String(), nullable=False, server_default="None"),
sa.Column("status_cryptobot", sa.String(length=16), nullable=False, server_default="False"),
sa.Column("status_yoomoney", sa.String(length=16), nullable=False, server_default="False"),
sa.PrimaryKeyConstraint("id"),
)
bind.execute(
sa.text(
f"""
INSERT INTO storage_payments_new (
id,
cryptobot_token,
yoomoney_token,
status_cryptobot,
status_yoomoney
)
SELECT
1,
{_copy_expr("storage_payments", "cryptobot_token", "None")},
{_copy_expr("storage_payments", "yoomoney_token", "None")},
{_copy_expr("storage_payments", "status_cryptobot", "False")},
{_copy_expr("storage_payments", "status_yoomoney", "False")}
FROM storage_payments
LIMIT 1
"""
)
)
bind.execute(sa.text("DROP TABLE storage_payments"))
bind.execute(sa.text("ALTER TABLE storage_payments_new RENAME TO storage_payments"))
bind.execute(
sa.text(
"""
INSERT INTO storage_payments (id)
SELECT 1
WHERE NOT EXISTS (SELECT 1 FROM storage_payments WHERE id = 1)
"""
)
)
_normalize_boolean_text("storage_payments", "status_cryptobot", "False")
_normalize_boolean_text("storage_payments", "status_yoomoney", "False")
_create_index("ix_storage_payments_id", "storage_payments", "id", unique=True)
# Создание или правка таблицы категорий
def _ensure_category() -> None:
if not _table_exists("storage_category"):
op.create_table(
"storage_category",
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("category_id", sa.BigInteger(), nullable=False),
sa.Column("category_name", sa.String(length=255), nullable=False),
sa.Column("category_unix", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("increment"),
)
else:
_add_column("storage_category", "category_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_category", "category_name", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_category", "category_unix", "INTEGER NOT NULL DEFAULT 0")
_create_index("ix_storage_category_category_id", "storage_category", "category_id")
# Создание или правка таблицы позиций
def _ensure_position() -> None:
if not _table_exists("storage_position"):
op.create_table(
"storage_position",
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("category_id", sa.BigInteger(), nullable=False),
sa.Column("position_id", sa.BigInteger(), nullable=False),
sa.Column("position_name", sa.String(length=255), nullable=False),
sa.Column("position_price", sa.Float(), nullable=False, server_default="0"),
sa.Column("position_desc", sa.Text(), nullable=False, server_default="None"),
sa.Column("position_photo", sa.Text(), nullable=False, server_default="None"),
sa.Column("position_unix", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("increment"),
)
else:
_add_column("storage_position", "category_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_position", "position_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_position", "position_name", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_position", "position_price", "REAL NOT NULL DEFAULT 0")
_add_column("storage_position", "position_desc", "TEXT NOT NULL DEFAULT 'None'")
_add_column("storage_position", "position_photo", "TEXT NOT NULL DEFAULT 'None'")
_add_column("storage_position", "position_unix", "INTEGER NOT NULL DEFAULT 0")
_create_index("ix_storage_position_category_id", "storage_position", "category_id")
_create_index("ix_storage_position_position_id", "storage_position", "position_id")
# Создание или правка таблицы товаров
def _ensure_item() -> None:
if not _table_exists("storage_item"):
op.create_table(
"storage_item",
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("category_id", sa.BigInteger(), nullable=False),
sa.Column("position_id", sa.BigInteger(), nullable=False),
sa.Column("item_id", sa.BigInteger(), nullable=False),
sa.Column("item_unix", sa.Integer(), nullable=False),
sa.Column("item_data", sa.Text(), nullable=False),
sa.PrimaryKeyConstraint("increment"),
)
else:
_add_column("storage_item", "user_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_item", "category_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_item", "position_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_item", "item_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_item", "item_unix", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_item", "item_data", "TEXT NOT NULL DEFAULT ''")
_create_index("ix_storage_item_user_id", "storage_item", "user_id")
_create_index("ix_storage_item_category_id", "storage_item", "category_id")
_create_index("ix_storage_item_position_id", "storage_item", "position_id")
_create_index("ix_storage_item_item_id", "storage_item", "item_id")
# Создание или правка таблицы покупок
def _ensure_purchases() -> None:
if not _table_exists("storage_purchases"):
op.create_table(
"storage_purchases",
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("user_balance_before", sa.Float(), nullable=False, server_default="0"),
sa.Column("user_balance_after", sa.Float(), nullable=False, server_default="0"),
sa.Column("purchase_receipt", sa.BigInteger(), nullable=False),
sa.Column("purchase_data", sa.Text(), nullable=False),
sa.Column("purchase_count", sa.Integer(), nullable=False, server_default="1"),
sa.Column("purchase_price", sa.Float(), nullable=False, server_default="0"),
sa.Column("purchase_price_one", sa.Float(), nullable=False, server_default="0"),
sa.Column("purchase_position_id", sa.BigInteger(), nullable=False),
sa.Column("purchase_position_name", sa.String(length=255), nullable=False),
sa.Column("purchase_category_id", sa.BigInteger(), nullable=False),
sa.Column("purchase_category_name", sa.String(length=255), nullable=False),
sa.Column("purchase_unix", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("increment"),
)
else:
_add_column("storage_purchases", "user_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_purchases", "user_balance_before", "REAL NOT NULL DEFAULT 0")
_add_column("storage_purchases", "user_balance_after", "REAL NOT NULL DEFAULT 0")
_add_column("storage_purchases", "purchase_receipt", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_purchases", "purchase_data", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_purchases", "purchase_count", "INTEGER NOT NULL DEFAULT 1")
_add_column("storage_purchases", "purchase_price", "REAL NOT NULL DEFAULT 0")
_add_column("storage_purchases", "purchase_price_one", "REAL NOT NULL DEFAULT 0")
_add_column("storage_purchases", "purchase_position_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_purchases", "purchase_position_name", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_purchases", "purchase_category_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_purchases", "purchase_category_name", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_purchases", "purchase_unix", "INTEGER NOT NULL DEFAULT 0")
_create_index("ix_storage_purchases_user_id", "storage_purchases", "user_id")
_create_index("ix_storage_purchases_purchase_receipt", "storage_purchases", "purchase_receipt")
_create_index("ix_storage_purchases_purchase_position_id", "storage_purchases", "purchase_position_id")
_create_index("ix_storage_purchases_purchase_category_id", "storage_purchases", "purchase_category_id")
# Создание или правка таблицы пополнений
def _ensure_refill() -> None:
if not _table_exists("storage_refill"):
op.create_table(
"storage_refill",
sa.Column("increment", sa.Integer(), autoincrement=True, nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("refill_receipt", sa.BigInteger(), nullable=False),
sa.Column("refill_comment", sa.String(length=255), nullable=False, server_default=""),
sa.Column("refill_amount", sa.Float(), nullable=False, server_default="0"),
sa.Column("refill_method", sa.String(length=64), nullable=False),
sa.Column("refill_unix", sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint("increment"),
)
else:
_add_column("storage_refill", "user_id", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_refill", "refill_receipt", "INTEGER NOT NULL DEFAULT 0")
_add_column("storage_refill", "refill_comment", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_refill", "refill_amount", "REAL NOT NULL DEFAULT 0")
_add_column("storage_refill", "refill_method", "TEXT NOT NULL DEFAULT ''")
_add_column("storage_refill", "refill_unix", "INTEGER NOT NULL DEFAULT 0")
_create_index("ix_storage_refill_user_id", "storage_refill", "user_id")
_create_index("ix_storage_refill_refill_receipt", "storage_refill", "refill_receipt")
# Применение начальной схемы магазина
def upgrade() -> None:
_ensure_users()
_ensure_settings()
_ensure_payments()
_ensure_category()
_ensure_position()
_ensure_item()
_ensure_purchases()
_ensure_refill()
# Откат начальной схемы магазина
def downgrade() -> None:
op.drop_table("storage_refill")
op.drop_table("storage_purchases")
op.drop_table("storage_item")
op.drop_table("storage_position")
op.drop_table("storage_category")
op.drop_table("storage_payments")
op.drop_table("storage_settings")
op.drop_table("storage_users")
@@ -0,0 +1,32 @@
"""add column 'status_stars' into storage_payments
Revision ID: 492769059249
Revises: 0001_shop_schema
Create Date: 2026-05-30 19:15:00.214301+03:00
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = '492769059249'
down_revision: Union[str, None] = '0001_shop_schema'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
batch_op.add_column(sa.Column('status_stars', sa.String(length=16), server_default=sa.text("'False'"), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
batch_op.drop_column('status_stars')
# ### end Alembic commands ###
@@ -0,0 +1,32 @@
"""add column 'stars_course' into storage_payment
Revision ID: 700275128e27
Revises: 492769059249
Create Date: 2026-05-30 19:28:42.490458+03:00
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = '700275128e27'
down_revision: Union[str, None] = '492769059249'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
batch_op.add_column(sa.Column('stars_course', sa.Float(), server_default=sa.text('(1.5)'), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('storage_payments', schema=None) as batch_op:
batch_op.drop_column('stars_course')
# ### end Alembic commands ###
@@ -0,0 +1,32 @@
"""add column 'misc_method_prod' into 'storage_settings'
Revision ID: 36380377b0e6
Revises: 700275128e27
Create Date: 2026-06-01 19:34:35.858117+03:00
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = '36380377b0e6'
down_revision: Union[str, None] = '700275128e27'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('storage_settings', schema=None) as batch_op:
batch_op.add_column(sa.Column('misc_method_prod', sa.String(length=16), server_default=sa.text("'skip'"), nullable=False))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('storage_settings', schema=None) as batch_op:
batch_op.drop_column('misc_method_prod')
# ### end Alembic commands ###