This commit is contained in:
Mateusz Gruszczyński
2026-03-13 11:03:13 +01:00
commit 35571df778
132 changed files with 11197 additions and 0 deletions

1
migrations/README.txt Normal file
View File

@@ -0,0 +1 @@
Initial project migration placeholder. Use `flask --app run.py db init`, `flask --app run.py db migrate`, `flask --app run.py db upgrade` to regenerate against the current models.

35
migrations/alembic.ini Normal file
View File

@@ -0,0 +1,35 @@
[alembic]
script_location = migrations
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

86
migrations/env.py Normal file
View File

@@ -0,0 +1,86 @@
from __future__ import with_statement
from logging.config import fileConfig
from flask import current_app
from alembic import context
config = context.config
fileConfig(config.config_file_name)
logger = fileConfig
def get_engine():
try:
return current_app.extensions['migrate'].db.get_engine()
except (TypeError, AttributeError):
return current_app.extensions['migrate'].db.engine
def get_engine_url():
try:
return get_engine().url.render_as_string(hide_password=False).replace('%', '%%')
except AttributeError:
return str(get_engine().url).replace('%', '%%')
config.set_main_option('sqlalchemy.url', get_engine_url())
target_db = current_app.extensions['migrate'].db
def get_metadata():
if hasattr(target_db, 'metadatas'):
return target_db.metadatas[None]
return target_db.metadata
def run_migrations_offline():
url = config.get_main_option('sqlalchemy.url')
conf_args = dict(current_app.extensions['migrate'].configure_args)
conf_args.setdefault('compare_type', True)
conf_args.setdefault('render_as_batch', True)
context.configure(
url=url,
target_metadata=get_metadata(),
literal_binds=True,
**conf_args,
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
conf_args = dict(current_app.extensions['migrate'].configure_args)
if conf_args.get('process_revision_directives') is None:
def process_revision_directives(context_, revision, directives):
if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
print('No changes in schema detected.')
conf_args['process_revision_directives'] = process_revision_directives
connectable = get_engine()
with connectable.connect() as connection:
conf_args.setdefault('compare_type', True)
conf_args.setdefault('render_as_batch', True)
context.configure(
connection=connection,
target_metadata=get_metadata(),
**conf_args,
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

24
migrations/script.py.mako Normal file
View File

@@ -0,0 +1,24 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

View File

@@ -0,0 +1,9 @@
-- SQLite migration for split payment support
-- Run once on an existing database.
ALTER TABLE product ADD COLUMN split_payment_default INTEGER NOT NULL DEFAULT 0;
ALTER TABLE invoice ADD COLUMN split_payment INTEGER NOT NULL DEFAULT 0;
-- Optional backfill examples:
-- UPDATE product SET split_payment_default = 1 WHERE name IN ('Usługa A', 'Usługa B');
-- UPDATE invoice SET split_payment = 1 WHERE gross_amount >= 15000;

View File

@@ -0,0 +1,9 @@
ALTER TABLE company ADD COLUMN bank_account VARCHAR(64) DEFAULT '';
ALTER TABLE invoice ADD COLUMN seller_bank_account VARCHAR(64) DEFAULT '';
-- Optional backfill from current company data for already issued invoices:
-- UPDATE invoice
-- SET seller_bank_account = (
-- SELECT company.bank_account FROM company WHERE company.id = invoice.company_id
-- )
-- WHERE COALESCE(seller_bank_account, '') = '';

View File

@@ -0,0 +1,50 @@
"""add contractor regon and address to invoice
Revision ID: add_invoice_contractor_fields
Revises:
Create Date: 2026-03-10 12:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
revision = "add_invoice_contractor_fields"
down_revision = None
branch_labels = None
depends_on = None
def _has_column(table_name: str, column_name: str) -> bool:
bind = op.get_bind()
inspector = inspect(bind)
return column_name in {col["name"] for col in inspector.get_columns(table_name)}
def _has_index(table_name: str, index_name: str) -> bool:
bind = op.get_bind()
inspector = inspect(bind)
return index_name in {idx["name"] for idx in inspector.get_indexes(table_name)}
def upgrade():
with op.batch_alter_table("invoice", schema=None) as batch_op:
if not _has_column("invoice", "contractor_regon"):
batch_op.add_column(sa.Column("contractor_regon", sa.String(length=32), nullable=True))
if not _has_column("invoice", "contractor_address"):
batch_op.add_column(sa.Column("contractor_address", sa.String(length=512), nullable=True))
index_name = batch_op.f("ix_invoice_contractor_regon")
if _has_column("invoice", "contractor_regon") and not _has_index("invoice", index_name):
batch_op.create_index(index_name, ["contractor_regon"], unique=False)
def downgrade():
with op.batch_alter_table("invoice", schema=None) as batch_op:
index_name = batch_op.f("ix_invoice_contractor_regon")
if _has_index("invoice", index_name):
batch_op.drop_index(index_name)
if _has_column("invoice", "contractor_address"):
batch_op.drop_column("contractor_address")
if _has_column("invoice", "contractor_regon"):
batch_op.drop_column("contractor_regon")

View File

@@ -0,0 +1,42 @@
"""add split payment and mail security
Revision ID: 002_add_split_payment_and_mail_security
Revises: add_invoice_contractor_fields
Create Date: 2026-03-12
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
revision = '002_add_split_payment_and_mail_security'
down_revision = 'add_invoice_contractor_fields'
branch_labels = None
depends_on = None
def _has_column(table_name: str, column_name: str) -> bool:
bind = op.get_bind()
inspector = inspect(bind)
return column_name in {col["name"] for col in inspector.get_columns(table_name)}
def upgrade():
with op.batch_alter_table('product') as batch_op:
if not _has_column('product', 'split_payment_default'):
batch_op.add_column(sa.Column('split_payment_default', sa.Boolean(), nullable=False, server_default=sa.false()))
with op.batch_alter_table('invoice') as batch_op:
if not _has_column('invoice', 'split_payment'):
batch_op.add_column(sa.Column('split_payment', sa.Boolean(), nullable=False, server_default=sa.false()))
def downgrade():
with op.batch_alter_table('invoice') as batch_op:
if _has_column('invoice', 'split_payment'):
batch_op.drop_column('split_payment')
with op.batch_alter_table('product') as batch_op:
if _has_column('product', 'split_payment_default'):
batch_op.drop_column('split_payment_default')

View File

@@ -0,0 +1,44 @@
"""add bank account to company and invoice
Revision ID: 003_add_bank_account_to_company_and_invoice
Revises: 002_add_split_payment_and_mail_security
Create Date: 2026-03-12
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
revision = '003_add_bank_account_to_company_and_invoice'
down_revision = '002_add_split_payment_and_mail_security'
branch_labels = None
depends_on = None
def _has_column(table_name: str, column_name: str) -> bool:
bind = op.get_bind()
inspector = inspect(bind)
return column_name in {col['name'] for col in inspector.get_columns(table_name)}
def upgrade():
with op.batch_alter_table('company') as batch_op:
if not _has_column('company', 'bank_account'):
batch_op.add_column(sa.Column('bank_account', sa.String(length=64), nullable=True, server_default=''))
with op.batch_alter_table('invoice') as batch_op:
if not _has_column('invoice', 'seller_bank_account'):
batch_op.add_column(sa.Column('seller_bank_account', sa.String(length=64), nullable=True, server_default=''))
op.execute("UPDATE invoice SET seller_bank_account = COALESCE(seller_bank_account, '')")
op.execute("UPDATE company SET bank_account = COALESCE(bank_account, '')")
def downgrade():
with op.batch_alter_table('invoice') as batch_op:
if _has_column('invoice', 'seller_bank_account'):
batch_op.drop_column('seller_bank_account')
with op.batch_alter_table('company') as batch_op:
if _has_column('company', 'bank_account'):
batch_op.drop_column('bank_account')