- business_roles/param_definitions/param_change_batches/param_values 四表 + 种子(3 内置岗位/16 参数定义/19 版本条目/初始批次 P20260101-00) - 双轨迁移:Alembic bfbda9c3191d + db/migration/V3 导出 + init-dev.sql 同步(schema_sync 校验通过) - 参数值以 Fraction 可解析字符串存储(4/360 禁 0.011111);企业户 2‰→3‰ 双种子支持按到账日回溯 Co-Authored-By: Claude Code <noreply@anthropic.com>
811 lines
30 KiB
Python
811 lines
30 KiB
Python
"""新增岗位角色与计提参数四表
|
||
|
||
Revision ID: bfbda9c3191d
|
||
Revises: a45a8087831b
|
||
Create Date: 2026-10-12 16:01:48.760620
|
||
|
||
"""
|
||
|
||
from collections.abc import Sequence
|
||
from datetime import date
|
||
|
||
from alembic import op
|
||
import sqlalchemy as sa
|
||
from sqlalchemy.dialects import mysql
|
||
|
||
# revision identifiers, used by Alembic.
|
||
revision: str = "bfbda9c3191d"
|
||
down_revision: str | Sequence[str] | None = "a45a8087831b"
|
||
branch_labels: str | Sequence[str] | None = None
|
||
depends_on: str | Sequence[str] | None = None
|
||
|
||
|
||
def upgrade() -> None:
|
||
"""应用迁移。"""
|
||
# ### commands auto generated by Alembic - please adjust! ###
|
||
op.create_table(
|
||
"business_roles",
|
||
sa.Column(
|
||
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
||
),
|
||
sa.Column(
|
||
"role_code",
|
||
sa.String(length=32),
|
||
nullable=False,
|
||
comment="岗位编码:salesperson营销员 sales_manager营销经理 info_officer信息员,创建后不可修改",
|
||
),
|
||
sa.Column("role_name", sa.String(length=64), nullable=False, comment="岗位名称"),
|
||
sa.Column(
|
||
"calc_template",
|
||
sa.String(length=32),
|
||
nullable=False,
|
||
comment="计提公式模板编码(以代码内模板注册表为准)",
|
||
),
|
||
sa.Column("description", sa.String(length=255), nullable=True, comment="岗位说明"),
|
||
sa.Column(
|
||
"sort_order", sa.Integer(), server_default="0", nullable=False, comment="显示排序"
|
||
),
|
||
sa.Column(
|
||
"status",
|
||
mysql.TINYINT(),
|
||
server_default="1",
|
||
nullable=False,
|
||
comment="状态:1启用 0禁用",
|
||
),
|
||
sa.Column(
|
||
"is_builtin",
|
||
mysql.TINYINT(),
|
||
server_default="0",
|
||
nullable=False,
|
||
comment="是否内置岗位:1内置(禁删禁停) 0自定义",
|
||
),
|
||
sa.Column(
|
||
"create_time",
|
||
sa.DateTime(),
|
||
server_default=sa.text("CURRENT_TIMESTAMP"),
|
||
nullable=False,
|
||
comment="创建时间",
|
||
),
|
||
sa.Column(
|
||
"update_time",
|
||
sa.DateTime(),
|
||
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
||
nullable=False,
|
||
comment="更新时间",
|
||
),
|
||
sa.PrimaryKeyConstraint("id", name=op.f("pk_business_roles")),
|
||
sa.UniqueConstraint("role_code", name=op.f("uk_business_roles_role_code")),
|
||
comment="业务岗位角色表(与登录角色roles区分,决定计提公式与参数)",
|
||
mysql_charset="utf8mb4",
|
||
mysql_collate="utf8mb4_general_ci",
|
||
mysql_engine="InnoDB",
|
||
)
|
||
op.create_table(
|
||
"param_definitions",
|
||
sa.Column(
|
||
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
||
),
|
||
sa.Column(
|
||
"param_code",
|
||
sa.String(length=64),
|
||
nullable=False,
|
||
comment="参数编码,如 salesperson.rate.slope",
|
||
),
|
||
sa.Column("param_name", sa.String(length=64), nullable=False, comment="参数名称"),
|
||
sa.Column(
|
||
"param_group",
|
||
sa.String(length=32),
|
||
nullable=False,
|
||
comment="参数分组:业务员个人户/经理分成/企业户/信息员/支付环节/产品阈值/月工资口径",
|
||
),
|
||
sa.Column(
|
||
"value_type",
|
||
sa.String(length=16),
|
||
nullable=False,
|
||
comment="值类型:DECIMAL/INT/BOOL/ENUM/TEXT",
|
||
),
|
||
sa.Column(
|
||
"unit", sa.String(length=16), nullable=True, comment="展示单位:‰/%/元/天,无单位为空"
|
||
),
|
||
sa.Column(
|
||
"dimension_type",
|
||
sa.String(length=32),
|
||
server_default="NONE",
|
||
nullable=False,
|
||
comment="维度类型:NONE/PRODUCT_CATEGORY/BUSINESS_ROLE",
|
||
),
|
||
sa.Column(
|
||
"options_text",
|
||
sa.String(length=255),
|
||
nullable=True,
|
||
comment="枚举选项(竖线分隔),仅 ENUM 使用",
|
||
),
|
||
sa.Column(
|
||
"default_value",
|
||
sa.String(length=64),
|
||
nullable=True,
|
||
comment="系统默认值(Fraction 可解析字符串)",
|
||
),
|
||
sa.Column(
|
||
"value_min", sa.String(length=64), nullable=True, comment="取值下限(含),校验用"
|
||
),
|
||
sa.Column(
|
||
"value_max", sa.String(length=64), nullable=True, comment="取值上限(含),校验用"
|
||
),
|
||
sa.Column(
|
||
"description", sa.String(length=255), nullable=True, comment="口径说明(配置页展示)"
|
||
),
|
||
sa.Column(
|
||
"sort_order", sa.Integer(), server_default="0", nullable=False, comment="显示排序"
|
||
),
|
||
sa.Column(
|
||
"status",
|
||
mysql.TINYINT(),
|
||
server_default="1",
|
||
nullable=False,
|
||
comment="状态:1启用 0停用",
|
||
),
|
||
sa.Column(
|
||
"create_time",
|
||
sa.DateTime(),
|
||
server_default=sa.text("CURRENT_TIMESTAMP"),
|
||
nullable=False,
|
||
comment="创建时间",
|
||
),
|
||
sa.Column(
|
||
"update_time",
|
||
sa.DateTime(),
|
||
server_default=sa.text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
|
||
nullable=False,
|
||
comment="更新时间",
|
||
),
|
||
sa.PrimaryKeyConstraint("id", name=op.f("pk_param_definitions")),
|
||
sa.UniqueConstraint("param_code", name=op.f("uk_param_definitions_param_code")),
|
||
comment="计提参数定义表",
|
||
mysql_charset="utf8mb4",
|
||
mysql_collate="utf8mb4_general_ci",
|
||
mysql_engine="InnoDB",
|
||
)
|
||
op.create_table(
|
||
"param_change_batches",
|
||
sa.Column(
|
||
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
||
),
|
||
sa.Column(
|
||
"version_no", sa.String(length=32), nullable=False, comment="版本号,如 P20261012-01"
|
||
),
|
||
sa.Column("effective_date", sa.Date(), nullable=False, comment="生效日期(业务日期口径)"),
|
||
sa.Column(
|
||
"change_type",
|
||
mysql.TINYINT(),
|
||
nullable=False,
|
||
comment="生效方式:1立即生效 2指定日期生效",
|
||
),
|
||
sa.Column("remark", sa.String(length=255), nullable=True, comment="变更说明"),
|
||
sa.Column(
|
||
"operator_id",
|
||
mysql.BIGINT(unsigned=True),
|
||
nullable=True,
|
||
comment="操作人ID(账号删除后置空)",
|
||
),
|
||
sa.Column(
|
||
"operator_name",
|
||
sa.String(length=64),
|
||
nullable=False,
|
||
comment="操作人用户名(冗余固化)",
|
||
),
|
||
sa.Column(
|
||
"item_count", sa.Integer(), server_default="0", nullable=False, comment="变更参数项数"
|
||
),
|
||
sa.Column(
|
||
"create_time",
|
||
sa.DateTime(),
|
||
server_default=sa.text("CURRENT_TIMESTAMP"),
|
||
nullable=False,
|
||
comment="创建时间",
|
||
),
|
||
sa.ForeignKeyConstraint(
|
||
["operator_id"],
|
||
["users.id"],
|
||
name=op.f("fk_param_change_batches_operator_id"),
|
||
ondelete="SET NULL",
|
||
),
|
||
sa.PrimaryKeyConstraint("id", name=op.f("pk_param_change_batches")),
|
||
sa.UniqueConstraint("version_no", name=op.f("uk_param_change_batches_version_no")),
|
||
comment="参数变更批次表",
|
||
mysql_charset="utf8mb4",
|
||
mysql_collate="utf8mb4_general_ci",
|
||
mysql_engine="InnoDB",
|
||
)
|
||
op.create_table(
|
||
"param_values",
|
||
sa.Column(
|
||
"id", mysql.BIGINT(unsigned=True), autoincrement=True, nullable=False, comment="主键ID"
|
||
),
|
||
sa.Column(
|
||
"param_id",
|
||
mysql.BIGINT(unsigned=True),
|
||
nullable=False,
|
||
comment="参数定义ID(param_definitions.id)",
|
||
),
|
||
sa.Column(
|
||
"dimension_value",
|
||
sa.String(length=32),
|
||
server_default=sa.text("''"),
|
||
nullable=False,
|
||
comment="维度取值:NONE维度为空串;产品类别/岗位编码为具体值",
|
||
),
|
||
sa.Column(
|
||
"effective_date", sa.Date(), nullable=False, comment="生效日期(按业务日期时点化取值)"
|
||
),
|
||
sa.Column(
|
||
"value_text",
|
||
sa.String(length=64),
|
||
nullable=False,
|
||
comment="参数值(Fraction 可解析字符串/枚举码/开关)",
|
||
),
|
||
sa.Column(
|
||
"old_value_text", sa.String(length=64), nullable=True, comment="变更前值(留痕)"
|
||
),
|
||
sa.Column(
|
||
"batch_id",
|
||
mysql.BIGINT(unsigned=True),
|
||
nullable=False,
|
||
comment="变更批次ID(param_change_batches.id)",
|
||
),
|
||
sa.Column(
|
||
"create_time",
|
||
sa.DateTime(),
|
||
server_default=sa.text("CURRENT_TIMESTAMP"),
|
||
nullable=False,
|
||
comment="创建时间",
|
||
),
|
||
sa.ForeignKeyConstraint(
|
||
["batch_id"],
|
||
["param_change_batches.id"],
|
||
name=op.f("fk_param_values_batch_id"),
|
||
ondelete="RESTRICT",
|
||
),
|
||
sa.ForeignKeyConstraint(
|
||
["param_id"],
|
||
["param_definitions.id"],
|
||
name=op.f("fk_param_values_param_id"),
|
||
ondelete="RESTRICT",
|
||
),
|
||
sa.PrimaryKeyConstraint("id", name=op.f("pk_param_values")),
|
||
comment="参数版本条目表(append-only,按生效日期时点化取值)",
|
||
mysql_charset="utf8mb4",
|
||
mysql_collate="utf8mb4_general_ci",
|
||
mysql_engine="InnoDB",
|
||
)
|
||
op.create_index("idx_param_values_batch_id", "param_values", ["batch_id"], unique=False)
|
||
op.create_index(
|
||
"idx_param_values_effective_date", "param_values", ["effective_date"], unique=False
|
||
)
|
||
op.create_index(
|
||
"idx_param_values_param_id_dimension_value_effective_date",
|
||
"param_values",
|
||
["param_id", "dimension_value", "effective_date"],
|
||
unique=False,
|
||
)
|
||
# ### end Alembic commands ###
|
||
|
||
# ---- 种子数据(生产与开发同源;口径依据 2026-10-12 业务裁决对照表)----
|
||
# 显式指定 id:四张表均为本迁移新建、应用时必为空表,id 稳定可控;
|
||
# param_values 按 param_id 引用定义,无法依赖自增(bulk_insert 取不回自增值)。
|
||
op.bulk_insert(
|
||
sa.table(
|
||
"business_roles",
|
||
sa.column("id", sa.BigInteger),
|
||
sa.column("role_code", sa.String),
|
||
sa.column("role_name", sa.String),
|
||
sa.column("calc_template", sa.String),
|
||
sa.column("description", sa.String),
|
||
sa.column("sort_order", sa.Integer),
|
||
sa.column("is_builtin", sa.Integer),
|
||
),
|
||
[
|
||
{
|
||
"id": 1,
|
||
"role_code": "salesperson",
|
||
"role_name": "营销员",
|
||
"calc_template": "TPL_SALESPERSON_DECAY",
|
||
"description": "业务员个人户:回款天数线性递减(10‰ − 天数×4/360‰,361 天起固定 4‰)",
|
||
"sort_order": 1,
|
||
"is_builtin": 1,
|
||
},
|
||
{
|
||
"id": 2,
|
||
"role_code": "sales_manager",
|
||
"role_name": "营销经理",
|
||
"calc_template": "TPL_MANAGER_SHARE",
|
||
"description": "经理绩效:∑本团队业务员绩效×12.5% + ∑全公司业务员绩效×5%",
|
||
"sort_order": 2,
|
||
"is_builtin": 1,
|
||
},
|
||
{
|
||
"id": 3,
|
||
"role_code": "info_officer",
|
||
"role_name": "信息员",
|
||
"calc_template": "TPL_INFO_SALES_FIXED",
|
||
"description": "信息员绩效:本人销售额×1‰",
|
||
"sort_order": 3,
|
||
"is_builtin": 1,
|
||
},
|
||
],
|
||
)
|
||
|
||
op.bulk_insert(
|
||
sa.table(
|
||
"param_change_batches",
|
||
sa.column("id", sa.BigInteger),
|
||
sa.column("version_no", sa.String),
|
||
sa.column("effective_date", sa.Date),
|
||
sa.column("change_type", sa.Integer),
|
||
sa.column("remark", sa.String),
|
||
sa.column("operator_name", sa.String),
|
||
sa.column("item_count", sa.Integer),
|
||
),
|
||
[
|
||
{
|
||
"id": 1,
|
||
"version_no": "P20260101-00",
|
||
"effective_date": date(2026, 1, 1),
|
||
"change_type": 1,
|
||
"remark": "初始版本(系统初始化种子,口径依据 2026-10-12 业务裁决)",
|
||
"operator_name": "system",
|
||
"item_count": 19,
|
||
}
|
||
],
|
||
)
|
||
|
||
op.bulk_insert(
|
||
sa.table(
|
||
"param_definitions",
|
||
sa.column("id", sa.BigInteger),
|
||
sa.column("param_code", sa.String),
|
||
sa.column("param_name", sa.String),
|
||
sa.column("param_group", sa.String),
|
||
sa.column("value_type", sa.String),
|
||
sa.column("unit", sa.String),
|
||
sa.column("dimension_type", sa.String),
|
||
sa.column("options_text", sa.String),
|
||
sa.column("default_value", sa.String),
|
||
sa.column("value_min", sa.String),
|
||
sa.column("value_max", sa.String),
|
||
sa.column("description", sa.String),
|
||
sa.column("sort_order", sa.Integer),
|
||
),
|
||
[
|
||
{
|
||
"id": 1,
|
||
"param_code": "salesperson.rate.start",
|
||
"param_name": "业务员起始计提比例",
|
||
"param_group": "业务员个人户",
|
||
"value_type": "DECIMAL",
|
||
"unit": "‰",
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": None,
|
||
"default_value": "10",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "线性递减段起始值:比例 = 起始 − 回款天数 × 斜率(≤分界天数)",
|
||
"sort_order": 1,
|
||
},
|
||
{
|
||
"id": 2,
|
||
"param_code": "salesperson.rate.slope",
|
||
"param_name": "业务员日递减斜率",
|
||
"param_group": "业务员个人户",
|
||
"value_type": "DECIMAL",
|
||
"unit": "‰/天",
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": None,
|
||
"default_value": "4/360",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "精确分数 4/360‰/天(=1/90),禁止按 0.011111 截断存储",
|
||
"sort_order": 2,
|
||
},
|
||
{
|
||
"id": 3,
|
||
"param_code": "salesperson.rate.floor",
|
||
"param_name": "业务员计提下限",
|
||
"param_group": "业务员个人户",
|
||
"value_type": "DECIMAL",
|
||
"unit": "‰",
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": None,
|
||
"default_value": "4",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "回款天数超过分界天数后固定取此比例(≥361 天 = 4‰)",
|
||
"sort_order": 3,
|
||
},
|
||
{
|
||
"id": 4,
|
||
"param_code": "salesperson.days.boundary",
|
||
"param_name": "线性递减分界天数",
|
||
"param_group": "业务员个人户",
|
||
"value_type": "INT",
|
||
"unit": "天",
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": None,
|
||
"default_value": "360",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "≤分界天数按公式线性递减(360 天=6‰);超过则取下限",
|
||
"sort_order": 4,
|
||
},
|
||
{
|
||
"id": 5,
|
||
"param_code": "manager.share.team",
|
||
"param_name": "经理本团队分成比例",
|
||
"param_group": "经理分成",
|
||
"value_type": "DECIMAL",
|
||
"unit": "%",
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": None,
|
||
"default_value": "12.5",
|
||
"value_min": "0",
|
||
"value_max": "100",
|
||
"description": "∑本人带领业务员绩效 × 此比例(限本团队)",
|
||
"sort_order": 5,
|
||
},
|
||
{
|
||
"id": 6,
|
||
"param_code": "manager.share.company",
|
||
"param_name": "经理全公司分成比例",
|
||
"param_group": "经理分成",
|
||
"value_type": "DECIMAL",
|
||
"unit": "%",
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": None,
|
||
"default_value": "5",
|
||
"value_min": "0",
|
||
"value_max": "100",
|
||
"description": "∑全部业务员绩效 × 此比例(全公司)",
|
||
"sort_order": 6,
|
||
},
|
||
{
|
||
"id": 7,
|
||
"param_code": "enterprise.rate",
|
||
"param_name": "企业户计提比例",
|
||
"param_group": "企业户",
|
||
"value_type": "DECIMAL",
|
||
"unit": "‰",
|
||
"dimension_type": "PRODUCT_CATEGORY",
|
||
"options_text": None,
|
||
"default_value": "3",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "所有企业户适用;2026-08-13 起由 2‰ 提高至 3‰",
|
||
"sort_order": 7,
|
||
},
|
||
{
|
||
"id": 8,
|
||
"param_code": "info.rate",
|
||
"param_name": "信息员计提比例",
|
||
"param_group": "信息员",
|
||
"value_type": "DECIMAL",
|
||
"unit": "‰",
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": None,
|
||
"default_value": "1",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "信息员绩效 = 本人销售额 × 此比例",
|
||
"sort_order": 8,
|
||
},
|
||
{
|
||
"id": 9,
|
||
"param_code": "prepay.rate",
|
||
"param_name": "预付款计提比例",
|
||
"param_group": "支付环节",
|
||
"value_type": "DECIMAL",
|
||
"unit": "‰",
|
||
"dimension_type": "NONE",
|
||
"options_text": None,
|
||
"default_value": "10",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "预付款统一按此比例计提",
|
||
"sort_order": 9,
|
||
},
|
||
{
|
||
"id": 10,
|
||
"param_code": "barter.discount",
|
||
"param_name": "以物抵顶折算比例",
|
||
"param_group": "支付环节",
|
||
"value_type": "DECIMAL",
|
||
"unit": "%",
|
||
"dimension_type": "NONE",
|
||
"options_text": None,
|
||
"default_value": "80",
|
||
"value_min": "0",
|
||
"value_max": "100",
|
||
"description": "以物抵顶回款绩效按应发绩效的此比例折算",
|
||
"sort_order": 10,
|
||
},
|
||
{
|
||
"id": 11,
|
||
"param_code": "payment.start.ratio",
|
||
"param_name": "支付起付比例",
|
||
"param_group": "支付环节",
|
||
"value_type": "DECIMAL",
|
||
"unit": "%",
|
||
"dimension_type": "NONE",
|
||
"options_text": None,
|
||
"default_value": "60",
|
||
"value_min": "0",
|
||
"value_max": "100",
|
||
"description": "单项业务回款额达销售额(不含运费)此比例后起付;达标前累计",
|
||
"sort_order": 11,
|
||
},
|
||
{
|
||
"id": 12,
|
||
"param_code": "payment.weight.mode",
|
||
"param_name": "支付权重应用方式",
|
||
"param_group": "支付环节",
|
||
"value_type": "ENUM",
|
||
"unit": None,
|
||
"dimension_type": "NONE",
|
||
"options_text": "FULL|BALANCE",
|
||
"default_value": "FULL",
|
||
"value_min": None,
|
||
"value_max": None,
|
||
"description": "FULL=按 R×R 乘当月全额应发绩效;BALANCE=乘余额(语义待 M3 前确认)",
|
||
"sort_order": 12,
|
||
},
|
||
{
|
||
"id": 13,
|
||
"param_code": "product.threshold",
|
||
"param_name": "折算毛利润额阈值",
|
||
"param_group": "产品阈值",
|
||
"value_type": "DECIMAL",
|
||
"unit": "%",
|
||
"dimension_type": "PRODUCT_CATEGORY",
|
||
"options_text": None,
|
||
"default_value": "30",
|
||
"value_min": "0",
|
||
"value_max": "100",
|
||
"description": "折算毛利润额 = 销售额 ×(毛利润率 − 阈值);市政水利 30%、轨交 20%",
|
||
"sort_order": 13,
|
||
},
|
||
{
|
||
"id": 14,
|
||
"param_code": "salary.basic.mode",
|
||
"param_name": "月工资基本工资口径",
|
||
"param_group": "月工资口径",
|
||
"value_type": "ENUM",
|
||
"unit": None,
|
||
"dimension_type": "BUSINESS_ROLE",
|
||
"options_text": "INCLUDE|DEDUCT|EXCLUDE",
|
||
"default_value": "INCLUDE",
|
||
"value_min": None,
|
||
"value_max": None,
|
||
"description": "INCLUDE含基本工资 / DEDUCT扣除基本工资 / EXCLUDE不计",
|
||
"sort_order": 14,
|
||
},
|
||
{
|
||
"id": 15,
|
||
"param_code": "probation.annual.threshold",
|
||
"param_name": "试用期年累计结算阈值",
|
||
"param_group": "月工资口径",
|
||
"value_type": "DECIMAL",
|
||
"unit": "元",
|
||
"dimension_type": "NONE",
|
||
"options_text": None,
|
||
"default_value": "18000",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "试用期年累计绩效超过此值部分年底一次性结算",
|
||
"sort_order": 15,
|
||
},
|
||
{
|
||
"id": 16,
|
||
"param_code": "probation.salary.basic",
|
||
"param_name": "试用期基本工资标准",
|
||
"param_group": "月工资口径",
|
||
"value_type": "DECIMAL",
|
||
"unit": "元",
|
||
"dimension_type": "NONE",
|
||
"options_text": None,
|
||
"default_value": "3000",
|
||
"value_min": "0",
|
||
"value_max": None,
|
||
"description": "试用期仅发基本工资、不发放绩效(归属待 M2 复核)",
|
||
"sort_order": 16,
|
||
},
|
||
],
|
||
)
|
||
|
||
op.bulk_insert(
|
||
sa.table(
|
||
"param_values",
|
||
sa.column("id", sa.BigInteger),
|
||
sa.column("param_id", sa.BigInteger),
|
||
sa.column("dimension_value", sa.String),
|
||
sa.column("effective_date", sa.Date),
|
||
sa.column("value_text", sa.String),
|
||
sa.column("batch_id", sa.BigInteger),
|
||
),
|
||
[
|
||
# 业务员个人户(2026-02-17 折算公式执行日起)
|
||
{
|
||
"id": 1,
|
||
"param_id": 1,
|
||
"dimension_value": "salesperson",
|
||
"effective_date": date(2026, 2, 17),
|
||
"value_text": "10",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 2,
|
||
"param_id": 2,
|
||
"dimension_value": "salesperson",
|
||
"effective_date": date(2026, 2, 17),
|
||
"value_text": "4/360",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 3,
|
||
"param_id": 3,
|
||
"dimension_value": "salesperson",
|
||
"effective_date": date(2026, 2, 17),
|
||
"value_text": "4",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 4,
|
||
"param_id": 4,
|
||
"dimension_value": "salesperson",
|
||
"effective_date": date(2026, 2, 17),
|
||
"value_text": "360",
|
||
"batch_id": 1,
|
||
},
|
||
# 经理与信息员(2026-08-13 执行日起)
|
||
{
|
||
"id": 5,
|
||
"param_id": 5,
|
||
"dimension_value": "sales_manager",
|
||
"effective_date": date(2026, 8, 13),
|
||
"value_text": "12.5",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 6,
|
||
"param_id": 6,
|
||
"dimension_value": "sales_manager",
|
||
"effective_date": date(2026, 8, 13),
|
||
"value_text": "5",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 7,
|
||
"param_id": 7,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 2, 17),
|
||
"value_text": "2",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 8,
|
||
"param_id": 7,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 8, 13),
|
||
"value_text": "3",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 9,
|
||
"param_id": 8,
|
||
"dimension_value": "info_officer",
|
||
"effective_date": date(2026, 8, 13),
|
||
"value_text": "1",
|
||
"batch_id": 1,
|
||
},
|
||
# 支付环节与通用口径(需规默认值,无历史冲突)
|
||
{
|
||
"id": 10,
|
||
"param_id": 9,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "10",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 11,
|
||
"param_id": 10,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "80",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 12,
|
||
"param_id": 11,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "60",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 13,
|
||
"param_id": 12,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "FULL",
|
||
"batch_id": 1,
|
||
},
|
||
# 产品阈值(按产品类别维度)
|
||
{
|
||
"id": 14,
|
||
"param_id": 13,
|
||
"dimension_value": "municipal_water",
|
||
"effective_date": date(2026, 2, 17),
|
||
"value_text": "30",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 15,
|
||
"param_id": 13,
|
||
"dimension_value": "rail_transit",
|
||
"effective_date": date(2026, 2, 17),
|
||
"value_text": "20",
|
||
"batch_id": 1,
|
||
},
|
||
# 月工资口径(按岗位维度)
|
||
{
|
||
"id": 16,
|
||
"param_id": 14,
|
||
"dimension_value": "salesperson",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "INCLUDE",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 17,
|
||
"param_id": 14,
|
||
"dimension_value": "sales_manager",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "DEDUCT",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 18,
|
||
"param_id": 15,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "18000",
|
||
"batch_id": 1,
|
||
},
|
||
{
|
||
"id": 19,
|
||
"param_id": 16,
|
||
"dimension_value": "",
|
||
"effective_date": date(2026, 1, 1),
|
||
"value_text": "3000",
|
||
"batch_id": 1,
|
||
},
|
||
],
|
||
)
|
||
|
||
|
||
def downgrade() -> None:
|
||
"""回滚迁移。
|
||
|
||
**注意(autogenerate 不会替你处理)**:生成的 `op.drop_index` 与
|
||
`op.drop_table` 顺序需人工检查 —— 若某索引是外键的唯一支撑索引,
|
||
单独 drop_index 会报 MySQL 1553(needed in a foreign key constraint)。
|
||
**整表回滚应只保留 `op.drop_table`**,DROP TABLE 会连带删除索引与外键。
|
||
"""
|
||
# 整表回滚只保留 drop_table —— DROP TABLE 会连带删除索引与外键。
|
||
# 原 autogenerate 输出的逐个 drop_index 会失败:组合索引最左列 param_id 与
|
||
# idx_param_values_batch_id 分别是两个外键的唯一支撑索引(MySQL errno 1553)。
|
||
# 删除顺序:先删引用方 param_values,再删被引用方。
|
||
op.drop_table("param_values")
|
||
op.drop_table("param_change_batches")
|
||
op.drop_table("param_definitions")
|
||
op.drop_table("business_roles")
|