Files
X-Financial/server/tests/test_employee_spreadsheet_import.py

212 lines
6.5 KiB
Python
Raw Normal View History

from __future__ import annotations
from io import BytesIO
import pytest
from openpyxl import Workbook
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool
from app.db.base import Base
from app.models.employee import Employee
from app.services.employee import EmployeeService
from app.services.employee_spreadsheet import EMPLOYEE_HEADERS, EMPLOYEE_SHEET_NAME
def build_session() -> Session:
engine = create_engine(
"sqlite+pysqlite:///:memory:",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
Base.metadata.create_all(bind=engine)
session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False)
return session_factory()
def build_workbook_bytes(rows: list[list[object]]) -> bytes:
workbook = Workbook()
sheet = workbook.active
sheet.title = EMPLOYEE_SHEET_NAME
sheet.append(list(EMPLOYEE_HEADERS))
for row in rows:
sheet.append(row)
buffer = BytesIO()
workbook.save(buffer)
return buffer.getvalue()
def test_import_employees_rejects_invalid_row_without_writing() -> None:
with build_session() as db:
service = EmployeeService(db, tenant_id="default")
first = service.list_employees()[0]
content = build_workbook_bytes(
[
[
first.employeeNo,
"",
first.email,
"",
"",
"",
"",
"",
first.position,
first.grade,
"",
"",
"",
"",
"",
"",
"",
"在职",
"user",
]
]
)
result = service.import_employees(content)
assert result.success is False
assert result.summary.errorCount >= 1
assert any("姓名" in item.message for item in result.errors)
refreshed = service.get_employee(first.id)
assert refreshed is not None
assert refreshed.name == first.name
def test_import_employees_updates_existing_employee() -> None:
with build_session() as db:
service = EmployeeService(db, tenant_id="default")
employee = service.list_employees()[0]
new_name = f"{employee.name}-导入"
content = build_workbook_bytes(
[
[
employee.employeeNo,
new_name,
employee.email,
"",
"",
"13900000001",
"",
"上海",
employee.position,
employee.grade,
"FINANCE-DEPT",
"",
"华东财务组",
"CC-TEST",
"导入户名",
"招商银行上海分行",
"622588000000000002",
"在职",
"user",
]
]
)
result = service.import_employees(content, actor="测试管理员")
assert result.success is True
assert result.summary.updated == 1
updated = service.get_employee(employee.id)
assert updated is not None
assert updated.name == new_name
assert updated.phone == "13900000001"
assert updated.bankAccountName == "导入户名"
assert updated.bankName == "招商银行上海分行"
assert updated.bankAccountNo == "622588000000000002"
persisted = db.execute(select(Employee).where(Employee.id == employee.id)).scalar_one()
assert persisted.tenant_id == "default"
assert persisted.manager_id is None
def test_import_employees_creates_new_employee() -> None:
with build_session() as db:
service = EmployeeService(db, tenant_id="default")
service.list_employees()
content = build_workbook_bytes(
[
[
"E90001",
"导入新员工",
"import.new.user@xfinance.com",
"",
"",
"13811112222",
"2025-01-01",
"上海",
"业务专员",
"P3",
"FINANCE-DEPT",
"E10234",
"华东财务组",
"CC-9001",
"",
"",
"",
"在职",
"user",
]
]
)
result = service.import_employees(content)
assert result.success is True
assert result.summary.created == 1
imported = db.execute(select(Employee).where(Employee.employee_no == "E90001")).scalar_one()
assert imported.name == "导入新员工"
assert imported.email == "import.new.user@xfinance.com"
assert imported.bank_account_name == "导入新员工"
assert imported.bank_name
assert imported.bank_account_no
def test_import_employees_rolls_back_when_membership_sync_fails(monkeypatch) -> None:
with build_session() as db:
service = EmployeeService(db, tenant_id="default")
service.list_employees()
content = build_workbook_bytes(
[
[
"E-ROLLBACK-001",
"导入回滚员工",
"import.rollback@xfinance.com",
"",
"",
"",
"",
"上海",
"业务专员",
"P3",
"FINANCE-DEPT",
"",
"",
"CC-ROLLBACK",
"",
"",
"",
"在职",
"user",
]
]
)
monkeypatch.setattr(
service,
"_ensure_tenant_memberships",
lambda: (_ for _ in ()).throw(RuntimeError("membership sync failed")),
)
with pytest.raises(RuntimeError, match="membership sync failed"):
service.import_employees(content)
assert db.scalar(select(Employee).where(Employee.employee_no == "E-ROLLBACK-001")) is None