198 lines
8.2 KiB
Python
198 lines
8.2 KiB
Python
|
|
"""月度结算与调整单模型(M3 ④:月结审批报表补充需求)。
|
|||
|
|
|
|||
|
|
- 月度结算按「确认回款」和计算快照生成;**通过即锁定**(status=3 已审定
|
|||
|
|
即终态,锁定后源数据改动只产生提示与后续调整)
|
|||
|
|
- settlement_items 明细按人固化(可负=负数工资),锁定后无 UPDATE 路径
|
|||
|
|
(重生成 = 草稿/驳回态删旧插新)
|
|||
|
|
- 调整单是已锁定月份的唯一调整通道(补发/扣减,走审批;生效后不回写
|
|||
|
|
已锁定月结——append-only,报表汇总并入)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from datetime import datetime
|
|||
|
|
from decimal import Decimal
|
|||
|
|
|
|||
|
|
from sqlalchemy import JSON, DateTime, ForeignKey, Numeric, String
|
|||
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|||
|
|
|
|||
|
|
from app.models.base import BIGINT_UNSIGNED, TINYINT_TYPE, Base, TimestampMixin, mysql_table_args
|
|||
|
|
|
|||
|
|
# 月结状态(monthly_settlements.status;通过即锁定——两态合一)
|
|||
|
|
SETTLEMENT_STATUS_DRAFT = 1 # 草稿(可重新生成/提交)
|
|||
|
|
SETTLEMENT_STATUS_PENDING = 2 # 待审批(提交后)
|
|||
|
|
SETTLEMENT_STATUS_APPROVED = 3 # 已审定(终态=已锁定)
|
|||
|
|
SETTLEMENT_STATUS_REJECTED = 4 # 已驳回(回草稿可重提)
|
|||
|
|
|
|||
|
|
# 调整单状态(adjustment_orders.status)
|
|||
|
|
ADJUSTMENT_STATUS_PENDING = 1 # 待审批
|
|||
|
|
ADJUSTMENT_STATUS_EFFECTIVE = 2 # 已生效
|
|||
|
|
ADJUSTMENT_STATUS_REJECTED = 3 # 已驳回
|
|||
|
|
ADJUSTMENT_STATUS_WITHDRAWN = 4 # 已撤回
|
|||
|
|
|
|||
|
|
# 调整方向(adjustment_orders.direction)
|
|||
|
|
ADJUSTMENT_DIRECTION_EXTRA = 1 # 补发
|
|||
|
|
ADJUSTMENT_DIRECTION_DEDUCT = 2 # 扣减
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MonthlySettlement(Base, TimestampMixin):
|
|||
|
|
"""月度结算表:按月一行(settlement_month 唯一),快照 + 审批 + 锁定。"""
|
|||
|
|
|
|||
|
|
__tablename__ = "monthly_settlements"
|
|||
|
|
__table_args__ = mysql_table_args("月度结算表(按月一行;通过即锁定,锁定后走调整单)")
|
|||
|
|
|
|||
|
|
id: Mapped[int] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
|
|||
|
|
)
|
|||
|
|
settlement_month: Mapped[str] = mapped_column(
|
|||
|
|
String(7), nullable=False, unique=True, comment="结算月份(YYYY-MM;全表唯一)"
|
|||
|
|
)
|
|||
|
|
status: Mapped[int] = mapped_column(
|
|||
|
|
TINYINT_TYPE,
|
|||
|
|
nullable=False,
|
|||
|
|
server_default="1",
|
|||
|
|
comment="状态:1草稿 2待审批 3已审定(终态=已锁定)4已驳回(回草稿)",
|
|||
|
|
)
|
|||
|
|
total_performance: Mapped[Decimal | None] = mapped_column(
|
|||
|
|
Numeric(14, 2), comment="绩效金额合计(可负)"
|
|||
|
|
)
|
|||
|
|
total_payable: Mapped[Decimal | None] = mapped_column(
|
|||
|
|
Numeric(14, 2), comment="应付绩效合计(可负)"
|
|||
|
|
)
|
|||
|
|
person_count: Mapped[int] = mapped_column(
|
|||
|
|
TINYINT_TYPE, nullable=False, server_default="0", comment="结算人数"
|
|||
|
|
)
|
|||
|
|
snapshot_json: Mapped[dict | None] = mapped_column(
|
|||
|
|
JSON, comment="月度总览快照(生成时刻的确认回款口径说明与汇总参数)"
|
|||
|
|
)
|
|||
|
|
approval_order_no: Mapped[str | None] = mapped_column(
|
|||
|
|
String(32), comment="关联审批单号(冗余固化;开关关闭自动通过时也有单)"
|
|||
|
|
)
|
|||
|
|
submitter_id: Mapped[int | None] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED,
|
|||
|
|
ForeignKey("users.id", ondelete="SET NULL"),
|
|||
|
|
comment="提交人ID(账号删除后置空)",
|
|||
|
|
)
|
|||
|
|
submitter_name: Mapped[str] = mapped_column(
|
|||
|
|
String(64), nullable=False, comment="提交人用户名(冗余固化)"
|
|||
|
|
)
|
|||
|
|
locked_at: Mapped[datetime | None] = mapped_column(
|
|||
|
|
DateTime, comment="锁定时间(status=3 时回填)"
|
|||
|
|
)
|
|||
|
|
locked_by: Mapped[str | None] = mapped_column(String(64), comment="锁定操作人(冗余固化)")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SettlementItem(Base):
|
|||
|
|
"""月结明细表:按人按核算类型固化金额(锁定后只读;重生成=草稿态删旧插新)。"""
|
|||
|
|
|
|||
|
|
__tablename__ = "settlement_items"
|
|||
|
|
__table_args__ = mysql_table_args(
|
|||
|
|
"月结明细表(按人固化;支持负数工资;锁定后只读)",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
id: Mapped[int] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
|
|||
|
|
)
|
|||
|
|
settlement_id: Mapped[int] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED,
|
|||
|
|
ForeignKey("monthly_settlements.id", ondelete="CASCADE"),
|
|||
|
|
nullable=False,
|
|||
|
|
comment="月结ID(monthly_settlements.id;头删明细随删)",
|
|||
|
|
)
|
|||
|
|
personnel_id: Mapped[int] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED,
|
|||
|
|
ForeignKey("personnel.id", ondelete="RESTRICT"),
|
|||
|
|
nullable=False,
|
|||
|
|
comment="结算人员ID(personnel.id)",
|
|||
|
|
)
|
|||
|
|
personnel_name: Mapped[str] = mapped_column(
|
|||
|
|
String(64), nullable=False, comment="结算人员姓名(冗余固化)"
|
|||
|
|
)
|
|||
|
|
role_code: Mapped[str] = mapped_column(
|
|||
|
|
String(32), nullable=False, comment="岗位编码(主岗,聚合锚点)"
|
|||
|
|
)
|
|||
|
|
calc_type: Mapped[int] = mapped_column(
|
|||
|
|
TINYINT_TYPE, nullable=False, comment="核算类型:1单笔回款 2经理团队聚合"
|
|||
|
|
)
|
|||
|
|
performance_sum: Mapped[Decimal] = mapped_column(
|
|||
|
|
Numeric(14, 2),
|
|||
|
|
nullable=False,
|
|||
|
|
comment="绩效金额合计(可负;聚合行排除经理个人户业绩,#12 暂口径)",
|
|||
|
|
)
|
|||
|
|
payable_sum: Mapped[Decimal] = mapped_column(
|
|||
|
|
Numeric(14, 2), nullable=False, comment="应付绩效合计(可负)"
|
|||
|
|
)
|
|||
|
|
basic_amount: Mapped[Decimal | None] = mapped_column(
|
|||
|
|
Numeric(14, 2), comment="月工资标准(personnel.salary_standard 当月快照)"
|
|||
|
|
)
|
|||
|
|
salary_mode: Mapped[str | None] = mapped_column(
|
|||
|
|
String(16), comment="月工资口径快照:INCLUDE/DEDUCT/EXCLUDE"
|
|||
|
|
)
|
|||
|
|
net_amount: Mapped[Decimal | None] = mapped_column(
|
|||
|
|
Numeric(14, 2), comment="净工资 = 绩效±基本工资(按口径;试用期 0)"
|
|||
|
|
)
|
|||
|
|
is_probation: Mapped[int] = mapped_column(
|
|||
|
|
TINYINT_TYPE,
|
|||
|
|
nullable=False,
|
|||
|
|
server_default="0",
|
|||
|
|
comment="试用期:1绩效照算不发放(net=0,超年累计记 note)0否",
|
|||
|
|
)
|
|||
|
|
probation_note: Mapped[str | None] = mapped_column(
|
|||
|
|
String(255), comment="试用期说明(年累计超阈值部分年底一次性结算)"
|
|||
|
|
)
|
|||
|
|
snapshot_json: Mapped[dict | None] = mapped_column(
|
|||
|
|
JSON, comment="个人明细快照(台账行引用列表)"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class AdjustmentOrder(Base, TimestampMixin):
|
|||
|
|
"""调整单:已锁定月份的唯一调整通道(补发/扣减,走审批;不回写月结)。"""
|
|||
|
|
|
|||
|
|
__tablename__ = "adjustment_orders"
|
|||
|
|
__table_args__ = mysql_table_args(
|
|||
|
|
"调整单表(已锁定月份的补发/扣减;走审批;生效后不回写月结快照)",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
id: Mapped[int] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
|
|||
|
|
)
|
|||
|
|
order_no: Mapped[str] = mapped_column(
|
|||
|
|
String(32),
|
|||
|
|
nullable=False,
|
|||
|
|
unique=True,
|
|||
|
|
comment="调整单号,自动生成 TZ{YYYYMMDD}-{4位当日序号}",
|
|||
|
|
)
|
|||
|
|
settlement_month: Mapped[str] = mapped_column(
|
|||
|
|
String(7), nullable=False, comment="调整月份(YYYY-MM;跨月规则待澄清 #9 前不限制)"
|
|||
|
|
)
|
|||
|
|
personnel_id: Mapped[int] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED,
|
|||
|
|
ForeignKey("personnel.id", ondelete="RESTRICT"),
|
|||
|
|
nullable=False,
|
|||
|
|
comment="调整人员ID(personnel.id)",
|
|||
|
|
)
|
|||
|
|
personnel_name: Mapped[str] = mapped_column(
|
|||
|
|
String(64), nullable=False, comment="调整人员姓名(冗余固化)"
|
|||
|
|
)
|
|||
|
|
direction: Mapped[int] = mapped_column(
|
|||
|
|
TINYINT_TYPE, nullable=False, comment="方向:1补发 2扣减"
|
|||
|
|
)
|
|||
|
|
amount: Mapped[Decimal] = mapped_column(
|
|||
|
|
Numeric(14, 2), nullable=False, comment="调整金额(恒正,方向表达语义)"
|
|||
|
|
)
|
|||
|
|
reason: Mapped[str] = mapped_column(
|
|||
|
|
String(255), nullable=False, comment="扣补说明(必填,审批与追溯锚点)"
|
|||
|
|
)
|
|||
|
|
status: Mapped[int] = mapped_column(
|
|||
|
|
TINYINT_TYPE,
|
|||
|
|
nullable=False,
|
|||
|
|
server_default="1",
|
|||
|
|
comment="状态:1待审批 2已生效 3已驳回 4已撤回",
|
|||
|
|
)
|
|||
|
|
submitter_id: Mapped[int | None] = mapped_column(
|
|||
|
|
BIGINT_UNSIGNED,
|
|||
|
|
ForeignKey("users.id", ondelete="SET NULL"),
|
|||
|
|
comment="提交人ID(账号删除后置空)",
|
|||
|
|
)
|
|||
|
|
submitter_name: Mapped[str] = mapped_column(
|
|||
|
|
String(64), nullable=False, comment="提交人用户名(冗余固化)"
|
|||
|
|
)
|