From a846ea5070c7ea7bd5d7809a8a5743ebe6ff5aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=84=A6=E9=BE=99=E8=A8=80?= Date: Thu, 18 Jun 2026 14:23:11 +0800 Subject: [PATCH] feat: add monthly payroll center tables --- financial_system/database/orm.py | 166 ++++++++++++++++++ financial_system/database/sql/init_mysql.sql | 119 +++++++++++++ financial_system/database/sql/schema.sql | 119 +++++++++++++ ...pgrade_20260618_monthly_payroll_center.sql | 124 +++++++++++++ 4 files changed, 528 insertions(+) create mode 100644 financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql diff --git a/financial_system/database/orm.py b/financial_system/database/orm.py index 6620878..d74cd21 100644 --- a/financial_system/database/orm.py +++ b/financial_system/database/orm.py @@ -441,6 +441,172 @@ class OvertimeRecordORM(Base): created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, comment="创建时间") +class AttendanceSyncJobORM(Base): + """钉钉或 Excel 考勤同步批次,用于实时看板和月度核算追踪。""" + + __tablename__ = "attendance_sync_jobs" + __table_args__ = { + "comment": "考勤同步任务表,记录钉钉实时同步或Excel导入的批次状态", + "mysql_charset": "utf8mb4", + "mysql_collate": "utf8mb4_unicode_ci", + } + + id: Mapped[str] = mapped_column(String(32), primary_key=True, comment="同步任务ID,UUID十六进制字符串") + source_type: Mapped[str] = mapped_column(String(32), nullable=False, index=True, comment="数据来源:dingtalk钉钉、excel表格") + sync_mode: Mapped[str] = mapped_column(String(32), nullable=False, default="today", index=True, comment="同步模式:today今日、month本月至今、range自定义范围") + attendance_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True, comment="实时看板对应考勤日期") + salary_month: Mapped[str] = mapped_column(String(7), nullable=False, default="", index=True, comment="工资月份,格式YYYY-MM") + start_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="同步开始日期") + end_date: Mapped[date | None] = mapped_column(Date, nullable=True, comment="同步结束日期") + status: Mapped[str] = mapped_column(String(32), nullable=False, default="created", index=True, comment="任务状态:created、running、completed、failed") + employee_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="本次同步涉及员工数量") + record_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="本次同步获得或解析的考勤记录数量") + error_message: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="任务失败时的错误信息") + created_by: Mapped[str] = mapped_column(String(64), nullable=False, default="", comment="创建人用户名快照") + created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, comment="创建时间") + updated_at: Mapped[datetime] = mapped_column( + DateTime, + nullable=False, + default=datetime.utcnow, + onupdate=datetime.utcnow, + comment="更新时间", + ) + + +class SalaryPreviewORM(Base): + """本月进行中的薪资预估结果,不作为最终工资发放依据。""" + + __tablename__ = "salary_preview" + __table_args__ = ( + UniqueConstraint("sync_job_id", "employee_id", name="uq_salary_preview_sync_employee"), + { + "comment": "实时薪资预估表,保存当前同步批次下的员工级薪资预估", + "mysql_charset": "utf8mb4", + "mysql_collate": "utf8mb4_unicode_ci", + }, + ) + + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="薪资预估主键ID") + sync_job_id: Mapped[str] = mapped_column( + ForeignKey("attendance_sync_jobs.id", ondelete="CASCADE"), + nullable=False, + index=True, + comment="关联考勤同步任务ID", + ) + employee_id: Mapped[int | None] = mapped_column( + ForeignKey("employees.id", ondelete="SET NULL"), + nullable=True, + index=True, + comment="关联员工ID,未匹配员工时为空", + ) + employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照") + employee_name: Mapped[str] = mapped_column(String(128), nullable=False, index=True, comment="员工姓名快照") + department: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="部门名称快照") + position: Mapped[str] = mapped_column(String(128), nullable=False, default="", comment="岗位名称快照") + salary_month: Mapped[str] = mapped_column(String(7), nullable=False, default="", index=True, comment="工资月份,格式YYYY-MM") + attendance_days: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="本月当前出勤天数") + actual_work_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="本月当前实际工时") + punch_overtime_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="打卡推算加班工时") + leave_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="请假或调休工时") + remaining_overtime_hours: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="剩余加班工时") + late_deduction: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前迟到扣款") + missing_card_deduction: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前缺卡扣款") + commission_amount: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前已录入提成") + overtime_pay: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="当前加班费") + estimated_gross_salary: Mapped[float | None] = mapped_column(Float, nullable=True, comment="当前预估应发工资") + estimated_net_salary: Mapped[float | None] = mapped_column(Float, nullable=True, comment="当前预估实发工资") + status: Mapped[str] = mapped_column(String(32), nullable=False, default="estimated", index=True, comment="预估状态:estimated已预估、warning有异常") + note: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="预估备注或异常说明") + created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, comment="创建时间") + updated_at: Mapped[datetime] = mapped_column( + DateTime, + nullable=False, + default=datetime.utcnow, + onupdate=datetime.utcnow, + comment="更新时间", + ) + + sync_job: Mapped[AttendanceSyncJobORM] = relationship() + employee: Mapped[EmployeeORM | None] = relationship() + + +class MonthlyPayrollRunORM(Base): + """月度正式核算批次,支持复核、锁定和导出。""" + + __tablename__ = "monthly_payroll_runs" + __table_args__ = { + "comment": "月度工资核算批次表,记录每月工资计算、复核和锁定状态", + "mysql_charset": "utf8mb4", + "mysql_collate": "utf8mb4_unicode_ci", + } + + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="月度核算批次主键ID") + salary_month: Mapped[str] = mapped_column(String(7), nullable=False, index=True, comment="工资月份,格式YYYY-MM") + source_type: Mapped[str] = mapped_column(String(32), nullable=False, default="excel", comment="数据来源:excel或dingtalk") + source_job_id: Mapped[str] = mapped_column(String(32), nullable=False, default="", index=True, comment="来源任务ID,可关联工资任务或同步任务") + status: Mapped[str] = mapped_column(String(32), nullable=False, default="draft", index=True, comment="核算状态:draft、calculated、reviewed、locked、failed") + employee_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="参与核算员工数量") + gross_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="应发工资合计") + net_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="实发工资合计") + deduction_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="扣款合计") + overtime_total: Mapped[float] = mapped_column(Float, nullable=False, default=0, comment="剩余加班工时合计") + locked_by: Mapped[str] = mapped_column(String(64), nullable=False, default="", comment="锁定人用户名快照") + locked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, comment="锁定时间") + created_by: Mapped[str] = mapped_column(String(64), nullable=False, default="", comment="创建人用户名快照") + created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, comment="创建时间") + updated_at: Mapped[datetime] = mapped_column( + DateTime, + nullable=False, + default=datetime.utcnow, + onupdate=datetime.utcnow, + comment="更新时间", + ) + + +class PayrollExceptionORM(Base): + """核算异常记录,例如缺员工、缺薪资档案、缺卡、请假无法识别。""" + + __tablename__ = "payroll_exceptions" + __table_args__ = { + "comment": "工资核算异常表,保存月度核算或实时同步过程中发现的待处理问题", + "mysql_charset": "utf8mb4", + "mysql_collate": "utf8mb4_unicode_ci", + } + + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="异常记录主键ID") + run_id: Mapped[int | None] = mapped_column( + ForeignKey("monthly_payroll_runs.id", ondelete="CASCADE"), + nullable=True, + index=True, + comment="关联月度核算批次ID", + ) + sync_job_id: Mapped[str | None] = mapped_column( + ForeignKey("attendance_sync_jobs.id", ondelete="CASCADE"), + nullable=True, + index=True, + comment="关联考勤同步任务ID", + ) + employee_id: Mapped[int | None] = mapped_column( + ForeignKey("employees.id", ondelete="SET NULL"), + nullable=True, + index=True, + comment="关联员工ID,未匹配员工时为空", + ) + employee_no: Mapped[str] = mapped_column(String(64), nullable=False, default="", index=True, comment="员工编号快照") + employee_name: Mapped[str] = mapped_column(String(128), nullable=False, default="", index=True, comment="员工姓名快照") + salary_month: Mapped[str] = mapped_column(String(7), nullable=False, default="", index=True, comment="工资月份,格式YYYY-MM") + exception_type: Mapped[str] = mapped_column(String(64), nullable=False, index=True, comment="异常类型编码") + severity: Mapped[str] = mapped_column(String(32), nullable=False, default="warning", index=True, comment="严重级别:info、warning、error") + message: Mapped[str] = mapped_column(Text, nullable=False, default="", comment="异常说明") + status: Mapped[str] = mapped_column(String(32), nullable=False, default="open", index=True, comment="处理状态:open待处理、resolved已处理、ignored已忽略") + created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow, comment="创建时间") + resolved_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, comment="处理完成时间") + + run: Mapped[MonthlyPayrollRunORM | None] = relationship() + sync_job: Mapped[AttendanceSyncJobORM | None] = relationship() + employee: Mapped[EmployeeORM | None] = relationship() + + class CommissionRecordORM(Base): """月度提成或奖金记录,支持手工维护和 Excel 导入。""" diff --git a/financial_system/database/sql/init_mysql.sql b/financial_system/database/sql/init_mysql.sql index 71f507e..e4aded5 100644 --- a/financial_system/database/sql/init_mysql.sql +++ b/financial_system/database/sql/init_mysql.sql @@ -474,6 +474,125 @@ CREATE TABLE IF NOT EXISTS `overtime_record` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='加班记录表,保存每日加班工时、加班类型、单价和金额'; +CREATE TABLE IF NOT EXISTS `attendance_sync_jobs` ( + `id` VARCHAR(32) NOT NULL COMMENT '同步任务ID,UUID十六进制字符串', + `source_type` VARCHAR(32) NOT NULL COMMENT '数据来源:dingtalk钉钉、excel表格', + `sync_mode` VARCHAR(32) NOT NULL DEFAULT 'today' COMMENT '同步模式:today今日、month本月至今、range自定义范围', + `attendance_date` DATE NULL COMMENT '实时看板对应考勤日期', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `start_date` DATE NULL COMMENT '同步开始日期', + `end_date` DATE NULL COMMENT '同步结束日期', + `status` VARCHAR(32) NOT NULL DEFAULT 'created' COMMENT '任务状态:created、running、completed、failed', + `employee_count` INT NOT NULL DEFAULT 0 COMMENT '本次同步涉及员工数量', + `record_count` INT NOT NULL DEFAULT 0 COMMENT '本次同步获得或解析的考勤记录数量', + `error_message` TEXT NOT NULL COMMENT '任务失败时的错误信息', + `created_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人用户名快照', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `ix_attendance_sync_jobs_source_type` (`source_type`), + KEY `ix_attendance_sync_jobs_sync_mode` (`sync_mode`), + KEY `ix_attendance_sync_jobs_attendance_date` (`attendance_date`), + KEY `ix_attendance_sync_jobs_salary_month` (`salary_month`), + KEY `ix_attendance_sync_jobs_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='考勤同步任务表,记录钉钉实时同步或Excel导入的批次状态'; + +CREATE TABLE IF NOT EXISTS `salary_preview` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '薪资预估主键ID', + `sync_job_id` VARCHAR(32) NOT NULL COMMENT '关联考勤同步任务ID', + `employee_id` INT NULL COMMENT '关联员工ID,未匹配员工时为空', + `employee_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '员工编号快照', + `employee_name` VARCHAR(128) NOT NULL COMMENT '员工姓名快照', + `department` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '部门名称快照', + `position` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '岗位名称快照', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `attendance_days` FLOAT NOT NULL DEFAULT 0 COMMENT '本月当前出勤天数', + `actual_work_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '本月当前实际工时', + `punch_overtime_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '打卡推算加班工时', + `leave_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '请假或调休工时', + `remaining_overtime_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '剩余加班工时', + `late_deduction` FLOAT NOT NULL DEFAULT 0 COMMENT '当前迟到扣款', + `missing_card_deduction` FLOAT NOT NULL DEFAULT 0 COMMENT '当前缺卡扣款', + `commission_amount` FLOAT NOT NULL DEFAULT 0 COMMENT '当前已录入提成', + `overtime_pay` FLOAT NOT NULL DEFAULT 0 COMMENT '当前加班费', + `estimated_gross_salary` FLOAT NULL COMMENT '当前预估应发工资', + `estimated_net_salary` FLOAT NULL COMMENT '当前预估实发工资', + `status` VARCHAR(32) NOT NULL DEFAULT 'estimated' COMMENT '预估状态:estimated已预估、warning有异常', + `note` TEXT NOT NULL COMMENT '预估备注或异常说明', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uq_salary_preview_sync_employee` (`sync_job_id`, `employee_id`), + KEY `ix_salary_preview_sync_job_id` (`sync_job_id`), + KEY `ix_salary_preview_employee_id` (`employee_id`), + KEY `ix_salary_preview_employee_no` (`employee_no`), + KEY `ix_salary_preview_employee_name` (`employee_name`), + KEY `ix_salary_preview_salary_month` (`salary_month`), + KEY `ix_salary_preview_status` (`status`), + CONSTRAINT `fk_salary_preview_sync_job_id` + FOREIGN KEY (`sync_job_id`) REFERENCES `attendance_sync_jobs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_salary_preview_employee_id` + FOREIGN KEY (`employee_id`) REFERENCES `employees` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='实时薪资预估表,保存当前同步批次下的员工级薪资预估'; + +CREATE TABLE IF NOT EXISTS `monthly_payroll_runs` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '月度核算批次主键ID', + `salary_month` VARCHAR(7) NOT NULL COMMENT '工资月份,格式YYYY-MM', + `source_type` VARCHAR(32) NOT NULL DEFAULT 'excel' COMMENT '数据来源:excel或dingtalk', + `source_job_id` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '来源任务ID,可关联工资任务或同步任务', + `status` VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT '核算状态:draft、calculated、reviewed、locked、failed', + `employee_count` INT NOT NULL DEFAULT 0 COMMENT '参与核算员工数量', + `gross_total` FLOAT NOT NULL DEFAULT 0 COMMENT '应发工资合计', + `net_total` FLOAT NOT NULL DEFAULT 0 COMMENT '实发工资合计', + `deduction_total` FLOAT NOT NULL DEFAULT 0 COMMENT '扣款合计', + `overtime_total` FLOAT NOT NULL DEFAULT 0 COMMENT '剩余加班工时合计', + `locked_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '锁定人用户名快照', + `locked_at` DATETIME NULL COMMENT '锁定时间', + `created_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人用户名快照', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `ix_monthly_payroll_runs_salary_month` (`salary_month`), + KEY `ix_monthly_payroll_runs_source_job_id` (`source_job_id`), + KEY `ix_monthly_payroll_runs_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='月度工资核算批次表,记录每月工资计算、复核和锁定状态'; + +CREATE TABLE IF NOT EXISTS `payroll_exceptions` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '异常记录主键ID', + `run_id` INT NULL COMMENT '关联月度核算批次ID', + `sync_job_id` VARCHAR(32) NULL COMMENT '关联考勤同步任务ID', + `employee_id` INT NULL COMMENT '关联员工ID,未匹配员工时为空', + `employee_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '员工编号快照', + `employee_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '员工姓名快照', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `exception_type` VARCHAR(64) NOT NULL COMMENT '异常类型编码', + `severity` VARCHAR(32) NOT NULL DEFAULT 'warning' COMMENT '严重级别:info、warning、error', + `message` TEXT NOT NULL COMMENT '异常说明', + `status` VARCHAR(32) NOT NULL DEFAULT 'open' COMMENT '处理状态:open待处理、resolved已处理、ignored已忽略', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `resolved_at` DATETIME NULL COMMENT '处理完成时间', + PRIMARY KEY (`id`), + KEY `ix_payroll_exceptions_run_id` (`run_id`), + KEY `ix_payroll_exceptions_sync_job_id` (`sync_job_id`), + KEY `ix_payroll_exceptions_employee_id` (`employee_id`), + KEY `ix_payroll_exceptions_employee_no` (`employee_no`), + KEY `ix_payroll_exceptions_employee_name` (`employee_name`), + KEY `ix_payroll_exceptions_salary_month` (`salary_month`), + KEY `ix_payroll_exceptions_exception_type` (`exception_type`), + KEY `ix_payroll_exceptions_severity` (`severity`), + KEY `ix_payroll_exceptions_status` (`status`), + CONSTRAINT `fk_payroll_exceptions_run_id` + FOREIGN KEY (`run_id`) REFERENCES `monthly_payroll_runs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_payroll_exceptions_sync_job_id` + FOREIGN KEY (`sync_job_id`) REFERENCES `attendance_sync_jobs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_payroll_exceptions_employee_id` + FOREIGN KEY (`employee_id`) REFERENCES `employees` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='工资核算异常表,保存月度核算或实时同步过程中发现的待处理问题'; + CREATE TABLE IF NOT EXISTS `commission_record` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '提成记录主键ID', `employee_id` INT NOT NULL COMMENT '关联员工ID', diff --git a/financial_system/database/sql/schema.sql b/financial_system/database/sql/schema.sql index a5b3540..762e101 100644 --- a/financial_system/database/sql/schema.sql +++ b/financial_system/database/sql/schema.sql @@ -384,6 +384,125 @@ CREATE TABLE IF NOT EXISTS `overtime_record` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='加班记录表,保存每日加班工时、加班类型、单价和金额'; +CREATE TABLE IF NOT EXISTS `attendance_sync_jobs` ( + `id` VARCHAR(32) NOT NULL COMMENT '同步任务ID,UUID十六进制字符串', + `source_type` VARCHAR(32) NOT NULL COMMENT '数据来源:dingtalk钉钉、excel表格', + `sync_mode` VARCHAR(32) NOT NULL DEFAULT 'today' COMMENT '同步模式:today今日、month本月至今、range自定义范围', + `attendance_date` DATE NULL COMMENT '实时看板对应考勤日期', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `start_date` DATE NULL COMMENT '同步开始日期', + `end_date` DATE NULL COMMENT '同步结束日期', + `status` VARCHAR(32) NOT NULL DEFAULT 'created' COMMENT '任务状态:created、running、completed、failed', + `employee_count` INT NOT NULL DEFAULT 0 COMMENT '本次同步涉及员工数量', + `record_count` INT NOT NULL DEFAULT 0 COMMENT '本次同步获得或解析的考勤记录数量', + `error_message` TEXT NOT NULL COMMENT '任务失败时的错误信息', + `created_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人用户名快照', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `ix_attendance_sync_jobs_source_type` (`source_type`), + KEY `ix_attendance_sync_jobs_sync_mode` (`sync_mode`), + KEY `ix_attendance_sync_jobs_attendance_date` (`attendance_date`), + KEY `ix_attendance_sync_jobs_salary_month` (`salary_month`), + KEY `ix_attendance_sync_jobs_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='考勤同步任务表,记录钉钉实时同步或Excel导入的批次状态'; + +CREATE TABLE IF NOT EXISTS `salary_preview` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '薪资预估主键ID', + `sync_job_id` VARCHAR(32) NOT NULL COMMENT '关联考勤同步任务ID', + `employee_id` INT NULL COMMENT '关联员工ID,未匹配员工时为空', + `employee_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '员工编号快照', + `employee_name` VARCHAR(128) NOT NULL COMMENT '员工姓名快照', + `department` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '部门名称快照', + `position` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '岗位名称快照', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `attendance_days` FLOAT NOT NULL DEFAULT 0 COMMENT '本月当前出勤天数', + `actual_work_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '本月当前实际工时', + `punch_overtime_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '打卡推算加班工时', + `leave_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '请假或调休工时', + `remaining_overtime_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '剩余加班工时', + `late_deduction` FLOAT NOT NULL DEFAULT 0 COMMENT '当前迟到扣款', + `missing_card_deduction` FLOAT NOT NULL DEFAULT 0 COMMENT '当前缺卡扣款', + `commission_amount` FLOAT NOT NULL DEFAULT 0 COMMENT '当前已录入提成', + `overtime_pay` FLOAT NOT NULL DEFAULT 0 COMMENT '当前加班费', + `estimated_gross_salary` FLOAT NULL COMMENT '当前预估应发工资', + `estimated_net_salary` FLOAT NULL COMMENT '当前预估实发工资', + `status` VARCHAR(32) NOT NULL DEFAULT 'estimated' COMMENT '预估状态:estimated已预估、warning有异常', + `note` TEXT NOT NULL COMMENT '预估备注或异常说明', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uq_salary_preview_sync_employee` (`sync_job_id`, `employee_id`), + KEY `ix_salary_preview_sync_job_id` (`sync_job_id`), + KEY `ix_salary_preview_employee_id` (`employee_id`), + KEY `ix_salary_preview_employee_no` (`employee_no`), + KEY `ix_salary_preview_employee_name` (`employee_name`), + KEY `ix_salary_preview_salary_month` (`salary_month`), + KEY `ix_salary_preview_status` (`status`), + CONSTRAINT `fk_salary_preview_sync_job_id` + FOREIGN KEY (`sync_job_id`) REFERENCES `attendance_sync_jobs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_salary_preview_employee_id` + FOREIGN KEY (`employee_id`) REFERENCES `employees` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='实时薪资预估表,保存当前同步批次下的员工级薪资预估'; + +CREATE TABLE IF NOT EXISTS `monthly_payroll_runs` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '月度核算批次主键ID', + `salary_month` VARCHAR(7) NOT NULL COMMENT '工资月份,格式YYYY-MM', + `source_type` VARCHAR(32) NOT NULL DEFAULT 'excel' COMMENT '数据来源:excel或dingtalk', + `source_job_id` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '来源任务ID,可关联工资任务或同步任务', + `status` VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT '核算状态:draft、calculated、reviewed、locked、failed', + `employee_count` INT NOT NULL DEFAULT 0 COMMENT '参与核算员工数量', + `gross_total` FLOAT NOT NULL DEFAULT 0 COMMENT '应发工资合计', + `net_total` FLOAT NOT NULL DEFAULT 0 COMMENT '实发工资合计', + `deduction_total` FLOAT NOT NULL DEFAULT 0 COMMENT '扣款合计', + `overtime_total` FLOAT NOT NULL DEFAULT 0 COMMENT '剩余加班工时合计', + `locked_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '锁定人用户名快照', + `locked_at` DATETIME NULL COMMENT '锁定时间', + `created_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人用户名快照', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `ix_monthly_payroll_runs_salary_month` (`salary_month`), + KEY `ix_monthly_payroll_runs_source_job_id` (`source_job_id`), + KEY `ix_monthly_payroll_runs_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='月度工资核算批次表,记录每月工资计算、复核和锁定状态'; + +CREATE TABLE IF NOT EXISTS `payroll_exceptions` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '异常记录主键ID', + `run_id` INT NULL COMMENT '关联月度核算批次ID', + `sync_job_id` VARCHAR(32) NULL COMMENT '关联考勤同步任务ID', + `employee_id` INT NULL COMMENT '关联员工ID,未匹配员工时为空', + `employee_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '员工编号快照', + `employee_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '员工姓名快照', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `exception_type` VARCHAR(64) NOT NULL COMMENT '异常类型编码', + `severity` VARCHAR(32) NOT NULL DEFAULT 'warning' COMMENT '严重级别:info、warning、error', + `message` TEXT NOT NULL COMMENT '异常说明', + `status` VARCHAR(32) NOT NULL DEFAULT 'open' COMMENT '处理状态:open待处理、resolved已处理、ignored已忽略', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `resolved_at` DATETIME NULL COMMENT '处理完成时间', + PRIMARY KEY (`id`), + KEY `ix_payroll_exceptions_run_id` (`run_id`), + KEY `ix_payroll_exceptions_sync_job_id` (`sync_job_id`), + KEY `ix_payroll_exceptions_employee_id` (`employee_id`), + KEY `ix_payroll_exceptions_employee_no` (`employee_no`), + KEY `ix_payroll_exceptions_employee_name` (`employee_name`), + KEY `ix_payroll_exceptions_salary_month` (`salary_month`), + KEY `ix_payroll_exceptions_exception_type` (`exception_type`), + KEY `ix_payroll_exceptions_severity` (`severity`), + KEY `ix_payroll_exceptions_status` (`status`), + CONSTRAINT `fk_payroll_exceptions_run_id` + FOREIGN KEY (`run_id`) REFERENCES `monthly_payroll_runs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_payroll_exceptions_sync_job_id` + FOREIGN KEY (`sync_job_id`) REFERENCES `attendance_sync_jobs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_payroll_exceptions_employee_id` + FOREIGN KEY (`employee_id`) REFERENCES `employees` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='工资核算异常表,保存月度核算或实时同步过程中发现的待处理问题'; + CREATE TABLE IF NOT EXISTS `commission_record` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '提成记录主键ID', `employee_id` INT NOT NULL COMMENT '关联员工ID', diff --git a/financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql b/financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql new file mode 100644 index 0000000..af0a58d --- /dev/null +++ b/financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql @@ -0,0 +1,124 @@ +-- 2026-06-18 月度核算中心与实时考勤看板升级脚本。 +-- Execute with: +-- mysql -h localhost -P 3306 -u root -p12345678 financial_system < financial_system/database/sql/upgrade_20260618_monthly_payroll_center.sql + +USE `financial_system`; + +CREATE TABLE IF NOT EXISTS `attendance_sync_jobs` ( + `id` VARCHAR(32) NOT NULL COMMENT '同步任务ID,UUID十六进制字符串', + `source_type` VARCHAR(32) NOT NULL COMMENT '数据来源:dingtalk钉钉、excel表格', + `sync_mode` VARCHAR(32) NOT NULL DEFAULT 'today' COMMENT '同步模式:today今日、month本月至今、range自定义范围', + `attendance_date` DATE NULL COMMENT '实时看板对应考勤日期', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `start_date` DATE NULL COMMENT '同步开始日期', + `end_date` DATE NULL COMMENT '同步结束日期', + `status` VARCHAR(32) NOT NULL DEFAULT 'created' COMMENT '任务状态:created、running、completed、failed', + `employee_count` INT NOT NULL DEFAULT 0 COMMENT '本次同步涉及员工数量', + `record_count` INT NOT NULL DEFAULT 0 COMMENT '本次同步获得或解析的考勤记录数量', + `error_message` TEXT NOT NULL COMMENT '任务失败时的错误信息', + `created_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人用户名快照', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `ix_attendance_sync_jobs_source_type` (`source_type`), + KEY `ix_attendance_sync_jobs_sync_mode` (`sync_mode`), + KEY `ix_attendance_sync_jobs_attendance_date` (`attendance_date`), + KEY `ix_attendance_sync_jobs_salary_month` (`salary_month`), + KEY `ix_attendance_sync_jobs_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='考勤同步任务表,记录钉钉实时同步或Excel导入的批次状态'; + +CREATE TABLE IF NOT EXISTS `salary_preview` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '薪资预估主键ID', + `sync_job_id` VARCHAR(32) NOT NULL COMMENT '关联考勤同步任务ID', + `employee_id` INT NULL COMMENT '关联员工ID,未匹配员工时为空', + `employee_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '员工编号快照', + `employee_name` VARCHAR(128) NOT NULL COMMENT '员工姓名快照', + `department` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '部门名称快照', + `position` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '岗位名称快照', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `attendance_days` FLOAT NOT NULL DEFAULT 0 COMMENT '本月当前出勤天数', + `actual_work_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '本月当前实际工时', + `punch_overtime_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '打卡推算加班工时', + `leave_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '请假或调休工时', + `remaining_overtime_hours` FLOAT NOT NULL DEFAULT 0 COMMENT '剩余加班工时', + `late_deduction` FLOAT NOT NULL DEFAULT 0 COMMENT '当前迟到扣款', + `missing_card_deduction` FLOAT NOT NULL DEFAULT 0 COMMENT '当前缺卡扣款', + `commission_amount` FLOAT NOT NULL DEFAULT 0 COMMENT '当前已录入提成', + `overtime_pay` FLOAT NOT NULL DEFAULT 0 COMMENT '当前加班费', + `estimated_gross_salary` FLOAT NULL COMMENT '当前预估应发工资', + `estimated_net_salary` FLOAT NULL COMMENT '当前预估实发工资', + `status` VARCHAR(32) NOT NULL DEFAULT 'estimated' COMMENT '预估状态:estimated已预估、warning有异常', + `note` TEXT NOT NULL COMMENT '预估备注或异常说明', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uq_salary_preview_sync_employee` (`sync_job_id`, `employee_id`), + KEY `ix_salary_preview_sync_job_id` (`sync_job_id`), + KEY `ix_salary_preview_employee_id` (`employee_id`), + KEY `ix_salary_preview_employee_no` (`employee_no`), + KEY `ix_salary_preview_employee_name` (`employee_name`), + KEY `ix_salary_preview_salary_month` (`salary_month`), + KEY `ix_salary_preview_status` (`status`), + CONSTRAINT `fk_salary_preview_sync_job_id` + FOREIGN KEY (`sync_job_id`) REFERENCES `attendance_sync_jobs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_salary_preview_employee_id` + FOREIGN KEY (`employee_id`) REFERENCES `employees` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='实时薪资预估表,保存当前同步批次下的员工级薪资预估'; + +CREATE TABLE IF NOT EXISTS `monthly_payroll_runs` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '月度核算批次主键ID', + `salary_month` VARCHAR(7) NOT NULL COMMENT '工资月份,格式YYYY-MM', + `source_type` VARCHAR(32) NOT NULL DEFAULT 'excel' COMMENT '数据来源:excel或dingtalk', + `source_job_id` VARCHAR(32) NOT NULL DEFAULT '' COMMENT '来源任务ID,可关联工资任务或同步任务', + `status` VARCHAR(32) NOT NULL DEFAULT 'draft' COMMENT '核算状态:draft、calculated、reviewed、locked、failed', + `employee_count` INT NOT NULL DEFAULT 0 COMMENT '参与核算员工数量', + `gross_total` FLOAT NOT NULL DEFAULT 0 COMMENT '应发工资合计', + `net_total` FLOAT NOT NULL DEFAULT 0 COMMENT '实发工资合计', + `deduction_total` FLOAT NOT NULL DEFAULT 0 COMMENT '扣款合计', + `overtime_total` FLOAT NOT NULL DEFAULT 0 COMMENT '剩余加班工时合计', + `locked_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '锁定人用户名快照', + `locked_at` DATETIME NULL COMMENT '锁定时间', + `created_by` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '创建人用户名快照', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `ix_monthly_payroll_runs_salary_month` (`salary_month`), + KEY `ix_monthly_payroll_runs_source_job_id` (`source_job_id`), + KEY `ix_monthly_payroll_runs_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='月度工资核算批次表,记录每月工资计算、复核和锁定状态'; + +CREATE TABLE IF NOT EXISTS `payroll_exceptions` ( + `id` INT NOT NULL AUTO_INCREMENT COMMENT '异常记录主键ID', + `run_id` INT NULL COMMENT '关联月度核算批次ID', + `sync_job_id` VARCHAR(32) NULL COMMENT '关联考勤同步任务ID', + `employee_id` INT NULL COMMENT '关联员工ID,未匹配员工时为空', + `employee_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '员工编号快照', + `employee_name` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '员工姓名快照', + `salary_month` VARCHAR(7) NOT NULL DEFAULT '' COMMENT '工资月份,格式YYYY-MM', + `exception_type` VARCHAR(64) NOT NULL COMMENT '异常类型编码', + `severity` VARCHAR(32) NOT NULL DEFAULT 'warning' COMMENT '严重级别:info、warning、error', + `message` TEXT NOT NULL COMMENT '异常说明', + `status` VARCHAR(32) NOT NULL DEFAULT 'open' COMMENT '处理状态:open待处理、resolved已处理、ignored已忽略', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `resolved_at` DATETIME NULL COMMENT '处理完成时间', + PRIMARY KEY (`id`), + KEY `ix_payroll_exceptions_run_id` (`run_id`), + KEY `ix_payroll_exceptions_sync_job_id` (`sync_job_id`), + KEY `ix_payroll_exceptions_employee_id` (`employee_id`), + KEY `ix_payroll_exceptions_employee_no` (`employee_no`), + KEY `ix_payroll_exceptions_employee_name` (`employee_name`), + KEY `ix_payroll_exceptions_salary_month` (`salary_month`), + KEY `ix_payroll_exceptions_exception_type` (`exception_type`), + KEY `ix_payroll_exceptions_severity` (`severity`), + KEY `ix_payroll_exceptions_status` (`status`), + CONSTRAINT `fk_payroll_exceptions_run_id` + FOREIGN KEY (`run_id`) REFERENCES `monthly_payroll_runs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_payroll_exceptions_sync_job_id` + FOREIGN KEY (`sync_job_id`) REFERENCES `attendance_sync_jobs` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_payroll_exceptions_employee_id` + FOREIGN KEY (`employee_id`) REFERENCES `employees` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + COMMENT='工资核算异常表,保存月度核算或实时同步过程中发现的待处理问题';