forked from jiaoly/financial_system
更新UI相关部分
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from financial_system.domain.models import EmployeeAttendance, PayrollResult
|
||||
from financial_system.domain.payroll_exceptions import detect_payroll_exceptions
|
||||
|
||||
|
||||
def test_detect_payroll_exceptions_flags_core_payroll_risks() -> None:
|
||||
result = PayrollResult(
|
||||
employee=EmployeeAttendance(name="张三", employee_no="ZA0001", department="生产中心"),
|
||||
salary_month="2026-06",
|
||||
salary_mode="monthly",
|
||||
attendance_days=0,
|
||||
absence_days=22,
|
||||
actual_work_hours=0,
|
||||
punch_overtime_hours=1,
|
||||
leave_hours=3,
|
||||
remaining_overtime_hours=-2,
|
||||
workday_overtime_hours=0,
|
||||
weekend_overtime_hours=0,
|
||||
holiday_overtime_hours=0,
|
||||
minor_late_count=4,
|
||||
major_late_count=1,
|
||||
penalized_late_count=2,
|
||||
late_deduction=60,
|
||||
missing_card_count=1,
|
||||
missing_card_deduction=20,
|
||||
total_deduction=80,
|
||||
base_salary=None,
|
||||
base_pay=None,
|
||||
commission_amount=0,
|
||||
overtime_rate=0,
|
||||
weekend_overtime_rate=0,
|
||||
holiday_overtime_rate=0,
|
||||
overtime_pay=0,
|
||||
gross_salary=None,
|
||||
net_salary=None,
|
||||
note="未配置薪资档案",
|
||||
)
|
||||
|
||||
exception_types = {item.exception_type for item in detect_payroll_exceptions([result])}
|
||||
|
||||
assert "missing_salary_profile" in exception_types
|
||||
assert "missing_card" in exception_types
|
||||
assert "late_penalty" in exception_types
|
||||
assert "negative_remaining_overtime" in exception_types
|
||||
assert "empty_attendance" in exception_types
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_detect_payroll_exceptions_flags_core_payroll_risks()
|
||||
@@ -0,0 +1,80 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from financial_system.services.realtime_attendance_service import TodayAttendanceItemData, build_realtime_summary
|
||||
|
||||
|
||||
def test_build_realtime_summary_counts_today_status() -> None:
|
||||
items = [
|
||||
TodayAttendanceItemData(
|
||||
employee_id=1,
|
||||
employee_no="ZA0001",
|
||||
employee_name="张三",
|
||||
department="生产中心",
|
||||
position="技工",
|
||||
first_punch="08:58",
|
||||
last_punch="18:30",
|
||||
attendance_status="present",
|
||||
late_minutes=0,
|
||||
missing_card_count=0,
|
||||
leave_hours=0,
|
||||
today_overtime_hours=1,
|
||||
month_remaining_overtime_hours=4,
|
||||
estimated_net_salary=6800,
|
||||
note="",
|
||||
),
|
||||
TodayAttendanceItemData(
|
||||
employee_id=2,
|
||||
employee_no="ZA0002",
|
||||
employee_name="李四",
|
||||
department="行政人事部",
|
||||
position="人事专员",
|
||||
first_punch="09:08",
|
||||
last_punch="",
|
||||
attendance_status="missing",
|
||||
late_minutes=8,
|
||||
missing_card_count=1,
|
||||
leave_hours=2,
|
||||
today_overtime_hours=0,
|
||||
month_remaining_overtime_hours=-2,
|
||||
estimated_net_salary=None,
|
||||
note="缺少下班卡",
|
||||
),
|
||||
TodayAttendanceItemData(
|
||||
employee_id=3,
|
||||
employee_no="ZA0003",
|
||||
employee_name="王五",
|
||||
department="财务部",
|
||||
position="财务",
|
||||
first_punch="",
|
||||
last_punch="",
|
||||
attendance_status="unchecked",
|
||||
late_minutes=0,
|
||||
missing_card_count=0,
|
||||
leave_hours=0,
|
||||
today_overtime_hours=0,
|
||||
month_remaining_overtime_hours=0,
|
||||
estimated_net_salary=5000,
|
||||
note="",
|
||||
),
|
||||
]
|
||||
|
||||
summary = build_realtime_summary(date(2026, 6, 22), items)
|
||||
|
||||
assert summary.employee_total == 3
|
||||
assert summary.checked_in_count == 2
|
||||
assert summary.unchecked_count == 1
|
||||
assert summary.late_count == 1
|
||||
assert summary.leave_count == 1
|
||||
assert summary.missing_card_count == 1
|
||||
assert summary.estimated_overtime_count == 1
|
||||
assert summary.estimated_net_total == 11800
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_build_realtime_summary_counts_today_status()
|
||||
@@ -0,0 +1,47 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from tempfile import TemporaryDirectory
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from financial_system.core.settings import get_settings
|
||||
|
||||
|
||||
def test_database_url_environment_variable_overrides_config_file() -> None:
|
||||
with TemporaryDirectory() as temp_dir:
|
||||
config_path = Path(temp_dir) / "app_settings.json"
|
||||
config_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"database": {
|
||||
"url": "mysql+pymysql://json-user:json-pass@json-host:3306/financial_system?charset=utf8mb4"
|
||||
}
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
docker_database_url = (
|
||||
"mysql+pymysql://docker-user:docker-pass@mysql.example.com:3306/"
|
||||
"financial_system?charset=utf8mb4"
|
||||
)
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"APP_CONFIG_FILE": str(config_path),
|
||||
"DATABASE_URL": docker_database_url,
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
settings = get_settings()
|
||||
|
||||
assert settings.database_url == docker_database_url
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_database_url_environment_variable_overrides_config_file()
|
||||
Reference in New Issue
Block a user