"""Record whether the 年假基数 cap was applied, per block. The annual-leave calculator gains a second cap on 2026-08-26: a prorated result may never exceed the employee's own 年假基数. It is a different rule from the 15-day combined cap already tracked by ``cap_applied_*``, and it produces a different sentence when HR has to explain a settlement to the employee who received it — "your result was capped at your own basis" versus "statutory + welfare exceeded 15 so welfare was reduced". One shared flag would have put the wrong explanation on screen, so the two get their own columns. NOT NULL DEFAULT FALSE, and false is the literal truth for every existing row: the 基数 cap did not exist when they were written, and history rows replay from persisted columns rather than recomputing, so none of them change meaning. Revision ID: 550 Revises: 549 """ from __future__ import annotations import sqlalchemy as sa from alembic import op revision = "550" down_revision = "549" branch_labels = None depends_on = None TABLE = "hr_tools_annual_leave_calculations" COLUMNS = ("basis_cap_applied_full_year", "basis_cap_applied_settlement") def _column_exists(bind, column: str) -> bool: return bind.execute( sa.text( "SELECT 1 FROM information_schema.columns " "WHERE table_schema = DATABASE() AND table_name = :t " "AND column_name = :c" ), {"t": TABLE, "c": column}, ).scalar() is not None def upgrade() -> None: bind = op.get_bind() for column in COLUMNS: if not _column_exists(bind, column): op.add_column( TABLE, sa.Column( column, sa.Boolean(), nullable=False, server_default=sa.text("0"), ), ) def downgrade() -> None: bind = op.get_bind() for column in reversed(COLUMNS): if _column_exists(bind, column): op.drop_column(TABLE, column)