"""人员工资与任职资料按生效日期留存,生成历史工资不读后来变更的标准。""" from datetime import date from types import SimpleNamespace from sqlalchemy import select from app.core.exceptions import BadRequestError from app.models import MonthlySettlement FIELDS = ( "salary_standard", "business_role_id", "hire_date", "resignation_date", "probation_start_date", "regularization_date", "is_probation", ) def capture(person, effective): return { "status": str(person.status), "effective_date": str(effective), **{ key: str(getattr(person, key)) if getattr(person, key) is not None else None for key in FIELDS }, } def begin_update(session, person, effective): if session.scalar( select(MonthlySettlement.id).where( MonthlySettlement.settlement_month >= effective.strftime("%Y-%m") ) ): raise BadRequestError( "生效月份或后续月份已生成工资,请使用未来生效日期;历史差额走当月调整" ) history = list(person.salary_history_json or []) if not history: baseline = person.hire_date or person.create_time.date() history.append(capture(person, baseline)) return history def save_version(person, effective, history=None): entries = list(history or person.salary_history_json or []) entries.append(capture(person, effective)) person.salary_history_json = entries def resolve(person, day): entries = [ entry for entry in (person.salary_history_json or []) if entry["effective_date"] <= str(day) ] values = {key: getattr(person, key) for key in FIELDS} values["status"] = person.status if person.salary_history_json and not entries: values["salary_standard"] = None elif entries: selected = max(enumerate(entries), key=lambda pair: (pair[1]["effective_date"], pair[0]))[1] values["status"] = int(selected.get("status", "1")) for key in FIELDS: raw = selected.get(key) if key.endswith("date"): values[key] = date.fromisoformat(raw) if raw else None elif key in ("business_role_id", "is_probation"): values[key] = int(raw) if raw else 0 elif key == "salary_standard": from decimal import Decimal values[key] = Decimal(raw) if raw is not None else None return SimpleNamespace(**values) def employed(person, profile, day): if profile.hire_date and profile.hire_date > day: return False if profile.resignation_date: return day < profile.resignation_date return profile.status == 1