2026-10-12 21:49:05 -04:00
|
|
|
|
"""营销人员模型:人员档案、产品辐射区域与市场拓展计划。
|
|
|
|
|
|
|
|
|
|
|
|
- 岗位(business_role_id)决定计提公式模板,与工资标准同属「同步不覆盖」的
|
|
|
|
|
|
人工维护字段(对照表 #26 落地口径,表注释标注供 M4 同步模块遵守)
|
|
|
|
|
|
- manager_id 自引用表达「经理带团队」:NULL=经理本人或未分配;M3 经理
|
|
|
|
|
|
12.5% 聚合、M4 部门汇总均按该关系展开,无需独立团队表
|
|
|
|
|
|
- 一期营销人员不登录系统,不与 users 关联;若后续开放自助查工资再加列
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from datetime import date
|
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
|
|
|
|
from sqlalchemy import Date, ForeignKey, Numeric, String
|
|
|
|
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
|
|
|
|
|
|
|
|
from app.models.base import (
|
|
|
|
|
|
BIGINT_UNSIGNED,
|
|
|
|
|
|
TINYINT_TYPE,
|
|
|
|
|
|
Base,
|
|
|
|
|
|
TimestampMixin,
|
|
|
|
|
|
TPlusSyncMixin,
|
|
|
|
|
|
mysql_table_args,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-10-14 21:49:53 -04:00
|
|
|
|
# personnel.status 的在职值(对照 BUSINESS_STATUS_* 集中在 models/business.py
|
|
|
|
|
|
# 的先例:状态语义单点导出,业务/客户两侧的归属校验共用,防魔数各自漂移)
|
|
|
|
|
|
PERSONNEL_STATUS_ACTIVE = 1
|
|
|
|
|
|
|
2026-10-12 21:49:05 -04:00
|
|
|
|
|
|
|
|
|
|
class Personnel(Base, TimestampMixin, TPlusSyncMixin):
|
|
|
|
|
|
"""营销人员表(需求 2.1.1)。
|
|
|
|
|
|
|
|
|
|
|
|
表名 personnel 为集合名词(单复数同形),本身即复数语义。
|
|
|
|
|
|
试用期人员绩效照算不发放(对照表 #17),is_probation 供 M3 区分。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "personnel"
|
|
|
|
|
|
__table_args__ = mysql_table_args("营销人员表(T+同步不覆盖岗位与工资标准)")
|
|
|
|
|
|
|
|
|
|
|
|
id: Mapped[int] = mapped_column(
|
|
|
|
|
|
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
|
|
|
|
|
|
)
|
|
|
|
|
|
personnel_name: Mapped[str] = mapped_column(String(64), nullable=False, comment="姓名")
|
|
|
|
|
|
business_role_id: Mapped[int] = mapped_column(
|
|
|
|
|
|
BIGINT_UNSIGNED,
|
|
|
|
|
|
ForeignKey("business_roles.id", ondelete="RESTRICT"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
comment="岗位角色ID(决定计提公式模板;T+同步不覆盖)",
|
|
|
|
|
|
)
|
|
|
|
|
|
manager_id: Mapped[int | None] = mapped_column(
|
|
|
|
|
|
BIGINT_UNSIGNED,
|
|
|
|
|
|
ForeignKey("personnel.id", ondelete="RESTRICT"),
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
comment="直属营销经理ID(NULL=经理本人或未分配;团队成员关系锚点)",
|
|
|
|
|
|
)
|
|
|
|
|
|
phone: Mapped[str | None] = mapped_column(String(20), comment="联系电话")
|
|
|
|
|
|
hire_date: Mapped[date | None] = mapped_column(Date, comment="入职日期")
|
|
|
|
|
|
is_probation: Mapped[int] = mapped_column(
|
|
|
|
|
|
TINYINT_TYPE,
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
server_default="0",
|
|
|
|
|
|
comment="是否试用期:1是 0否(试用期绩效照算不发放)",
|
|
|
|
|
|
)
|
|
|
|
|
|
salary_standard: Mapped[Decimal | None] = mapped_column(
|
|
|
|
|
|
Numeric(12, 2), comment="月工资标准(元;T+同步不覆盖)"
|
|
|
|
|
|
)
|
|
|
|
|
|
status: Mapped[int] = mapped_column(
|
|
|
|
|
|
TINYINT_TYPE,
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
server_default="1",
|
|
|
|
|
|
comment="状态:1在职 0离职(离职人员不可再被业务/客户归属引用)",
|
|
|
|
|
|
)
|
|
|
|
|
|
remark: Mapped[str | None] = mapped_column(String(255), comment="备注")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PersonnelRegion(Base, TimestampMixin):
|
|
|
|
|
|
"""产品辐射区域表(需求 2.1.1):人员负责的产品辐射地理范围,1:N 子表。
|
|
|
|
|
|
|
|
|
|
|
|
随人员编辑整表替换维护,无独立管理诉求,故不加 T+ 预留列。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "personnel_regions"
|
|
|
|
|
|
__table_args__ = mysql_table_args("产品辐射区域表")
|
|
|
|
|
|
|
|
|
|
|
|
id: Mapped[int] = mapped_column(
|
|
|
|
|
|
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
|
|
|
|
|
|
)
|
|
|
|
|
|
personnel_id: Mapped[int] = mapped_column(
|
|
|
|
|
|
BIGINT_UNSIGNED,
|
|
|
|
|
|
ForeignKey("personnel.id", ondelete="RESTRICT"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
comment="人员ID(personnel.id)",
|
|
|
|
|
|
)
|
|
|
|
|
|
region_name: Mapped[str] = mapped_column(
|
|
|
|
|
|
String(128), nullable=False, comment="区域名称(产品辐射地理范围)"
|
|
|
|
|
|
)
|
|
|
|
|
|
remark: Mapped[str | None] = mapped_column(String(255), comment="备注")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PersonnelPlan(Base, TimestampMixin):
|
|
|
|
|
|
"""市场拓展计划表(需求 2.1.1):计划期间 + 计划内容 + 指标说明的自由条目。
|
|
|
|
|
|
|
|
|
|
|
|
结构化指标粒度待 T+ 字段映射确认,先以自由条目容器落地;条目最终由
|
|
|
|
|
|
T+ 同步确定,故预留 source_no 单列(无索引:查询按 personnel_id 走)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "personnel_plans"
|
|
|
|
|
|
__table_args__ = mysql_table_args("市场拓展计划表")
|
|
|
|
|
|
|
|
|
|
|
|
id: Mapped[int] = mapped_column(
|
|
|
|
|
|
BIGINT_UNSIGNED, primary_key=True, autoincrement=True, comment="主键ID"
|
|
|
|
|
|
)
|
|
|
|
|
|
personnel_id: Mapped[int] = mapped_column(
|
|
|
|
|
|
BIGINT_UNSIGNED,
|
|
|
|
|
|
ForeignKey("personnel.id", ondelete="RESTRICT"),
|
|
|
|
|
|
nullable=False,
|
|
|
|
|
|
index=True,
|
|
|
|
|
|
comment="人员ID(personnel.id)",
|
|
|
|
|
|
)
|
|
|
|
|
|
plan_period: Mapped[str] = mapped_column(
|
|
|
|
|
|
String(32), nullable=False, comment="计划期间(如 2026Q1、2026-05)"
|
|
|
|
|
|
)
|
|
|
|
|
|
plan_content: Mapped[str] = mapped_column(String(500), nullable=False, comment="计划内容")
|
|
|
|
|
|
target_desc: Mapped[str | None] = mapped_column(String(255), comment="指标说明(文字描述)")
|
|
|
|
|
|
source_no: Mapped[str | None] = mapped_column(String(64), comment="T+ 原始条目号(预留)")
|