PayrollManagementSystem/backend/alembic/versions/24c5d6e7f8a9_requirement_completion.py
liuwenxin 2c003fc4a7 style(迁移): 修正迁移脚本的 Ruff 检查问题
将等价字典构造改为字面量并标记未使用字段,保持迁移 SQL 语义。
2026-11-03 17:18:44 +08:00

153 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""V24 需求补齐:试用期台账、产品来源及分产品系数参数。"""
import sqlalchemy as sa
from alembic import op
revision = "24c5d6e7f8a9"
down_revision = "23b4c5d6e7f8"
branch_labels = None
depends_on = None
PROBATION_DDL = "CREATE TABLE probation_deferrals (\n id BIGINT UNSIGNED NOT NULL COMMENT '主键ID' AUTO_INCREMENT, \n ledger_id BIGINT UNSIGNED NOT NULL COMMENT '原核算台账版本ID', \n personnel_id BIGINT UNSIGNED NOT NULL COMMENT '归属人员ID', \n year VARCHAR(4) NOT NULL COMMENT '原计提年度', \n month VARCHAR(7) NOT NULL COMMENT '本次计提或应付变化月份', \n accrued NUMERIC(14, 2) NOT NULL COMMENT '本次试用期计提额,可负', \n eligible NUMERIC(14, 2) NOT NULL COMMENT '满足起付及权重的本次应付差额,可负', \n create_time DATETIME NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP, \n CONSTRAINT pk_probation_deferrals PRIMARY KEY (id), \n CONSTRAINT uk_probation_deferrals_ledger_id UNIQUE (ledger_id), \n CONSTRAINT fk_probation_deferrals_ledger_id FOREIGN KEY(ledger_id) REFERENCES performance_ledgers (id) ON DELETE RESTRICT\n)ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='试用期缓发台账(每个正式台账版本一条)' COLLATE utf8mb4_general_ci;\n\nCREATE INDEX idx_probation_deferrals_personnel_id_year ON probation_deferrals (personnel_id, year);\n\nCREATE TABLE probation_payments (\n id BIGINT UNSIGNED NOT NULL COMMENT '主键ID' AUTO_INCREMENT, \n settlement_id BIGINT UNSIGNED COMMENT '月结ID(当月调整单结清时为空)', \n adjustment_order_id BIGINT UNSIGNED COMMENT '已生成工资后的结清调整单ID', \n personnel_id BIGINT UNSIGNED NOT NULL COMMENT '归属人员ID', \n year VARCHAR(4) NOT NULL COMMENT '原累计年度', \n month VARCHAR(7) NOT NULL COMMENT '本次实际支付月份', \n amount NUMERIC(14, 2) NOT NULL COMMENT '本次补发或扣回金额', \n snapshot_json JSON NOT NULL COMMENT '计提、起付权重、门槛及历史补发依据', \n create_time DATETIME NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP, \n CONSTRAINT pk_probation_payments PRIMARY KEY (id), \n CONSTRAINT uk_probation_payments_settlement_person_year UNIQUE (settlement_id, personnel_id, year), \n CONSTRAINT uk_probation_payments_adjustment_order_id UNIQUE (adjustment_order_id), \n CONSTRAINT fk_probation_payments_settlement_id FOREIGN KEY(settlement_id) REFERENCES monthly_settlements (id) ON DELETE CASCADE, \n CONSTRAINT fk_probation_payments_adjustment_order_id FOREIGN KEY(adjustment_order_id) REFERENCES adjustment_orders (id) ON DELETE RESTRICT\n)ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='试用期年终及后续结清记录(工资生成事务内固化)' COLLATE utf8mb4_general_ci;\n\nCREATE INDEX idx_probation_payments_personnel_id_year ON probation_payments (personnel_id, year);"
def upgrade():
op.add_column(
"personnel",
sa.Column(
"probation_start_date", sa.Date(), nullable=True, comment="试用期开始日(含当日)"
),
)
op.add_column(
"personnel",
sa.Column(
"regularization_date",
sa.Date(),
nullable=True,
comment="转正生效日(当日不再属于试用期)",
),
)
op.add_column(
"personnel",
sa.Column(
"resignation_date", sa.Date(), nullable=True, comment="离职生效日(当日起不计基本工资)"
),
)
op.add_column(
"personnel",
sa.Column(
"salary_history_json",
sa.JSON(),
nullable=True,
comment="工资标准、岗位及任职资料生效版本",
),
)
op.add_column(
"products",
sa.Column(
"source_trade_json",
sa.JSON(),
nullable=True,
comment="最近有效发货数量、含税成交价及源单据快照",
),
)
op.add_column(
"products",
sa.Column(
"quantity_price_manual",
sa.SmallInteger(),
server_default="0",
nullable=False,
comment="数量单价人工保护:1人工维护 0随源更新",
),
)
for name, title in (
("normal_payable_sum", "当月正常实付绩效"),
("deferred_sum", "当月试用期缓发变化"),
("probation_payment_sum", "本月年度补发或扣回"),
):
op.add_column(
"settlement_items",
sa.Column(name, sa.Numeric(14, 2), server_default="0", nullable=False, comment=title),
)
for statement in PROBATION_DDL.split(";"):
if statement.strip():
op.execute(sa.text(statement))
# 仅建立新参数定义,不写任何历史生效值;未覆盖项继续回溯原通用参数。
fields = (
("first", "个人户首日比例", "DECIMAL", "‰"),
("start", "个人户线性阶段起始比例", "DECIMAL", "‰"),
("slope", "个人户线性阶段日斜率", "DECIMAL", "‰/天"),
("floor", "个人户超期比例", "DECIMAL", "‰"),
("boundary", "个人户线性阶段截止天数", "INT", "天"),
)
definitions = sa.table(
"param_definitions",
*[
sa.column(name, sa.String())
for name in (
"param_code",
"param_name",
"param_group",
"value_type",
"unit",
"dimension_type",
"value_min",
"value_max",
"description",
)
],
sa.column("sort_order", sa.Integer()),
sa.column("status", sa.Integer()),
)
op.bulk_insert(
definitions,
[
{
"param_code": f"coefficient.personal.{field}",
"param_name": title,
"param_group": "产品系数表",
"value_type": kind,
"unit": unit,
"dimension_type": "PRODUCT_CATEGORY",
"value_min": "1" if kind == "INT" else "0",
"value_max": "3660" if kind == "INT" else None,
"description": "按产品类别维护;未配置时沿用同日期的通用业务员参数。",
"sort_order": 52 + index,
"status": 1,
}
for index, (field, title, kind, unit) in enumerate(fields)
],
)
def downgrade():
bind = op.get_bind()
for table, condition in (("personnel", "salary_history_json IS NOT NULL"), ("products", "source_trade_json IS NOT NULL OR quantity_price_manual<>0")):
if bind.execute(sa.text(f"SELECT COUNT(*) FROM {table} WHERE {condition}")).scalar():
raise RuntimeError("存在人员或产品新增资料,请回退应用并保留业务结构")
for table in ("probation_payments", "probation_deferrals"):
if bind.execute(sa.text(f"SELECT COUNT(*) FROM {table}")).scalar():
raise RuntimeError("存在试用期结算记录,请回退应用并保留业务数据")
if bind.execute(
sa.text(
"SELECT COUNT(*) FROM param_values v JOIN param_definitions d ON d.id=v.param_id WHERE d.param_code LIKE 'coefficient.personal.%'"
)
).scalar():
raise RuntimeError("存在已维护的产品系数,请保留新结构")
bind.execute(
sa.text("DELETE FROM param_definitions WHERE param_code LIKE 'coefficient.personal.%'")
)
op.drop_table("probation_payments")
op.drop_table("probation_deferrals")
for name in ("normal_payable_sum", "deferred_sum", "probation_payment_sum"):
op.drop_column("settlement_items", name)
op.drop_column("products", "quantity_price_manual")
op.drop_column("products", "source_trade_json")
op.drop_column("personnel", "regularization_date")
op.drop_column("personnel", "salary_history_json")
op.drop_column("personnel", "resignation_date")
op.drop_column("personnel", "probation_start_date")