补齐证书齐备、证件折算、移动用证和合同绑定,并带上报销批量发放、付款备注以及个人开发流程升级,便于继续讨论已安排计划的调整规则。 Co-authored-by: Cursor <cursoragent@cursor.com>
148 lines
4.5 KiB
Python
148 lines
4.5 KiB
Python
"""备用金批量发放:同一备注和附件写入每张待发放单,实际金额按单提交。"""
|
|
from datetime import datetime
|
|
|
|
from fastapi import HTTPException
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from app.core.bx_auth import BxUserContext
|
|
from app.models.bx_models import BxAdvanceApply, BxAttachment
|
|
from app.services.bx.advance import AdvanceService
|
|
|
|
TENANT = "shanxia2.0"
|
|
NOW = datetime(2026, 11, 4, 15, 0, 0)
|
|
|
|
|
|
def _session():
|
|
engine = create_engine("sqlite://")
|
|
BxAdvanceApply.__table__.create(engine)
|
|
BxAttachment.__table__.create(engine)
|
|
return sessionmaker(bind=engine)()
|
|
|
|
|
|
def _user() -> BxUserContext:
|
|
return BxUserContext(
|
|
id="finance-1",
|
|
name="财务",
|
|
dept_id="dept",
|
|
role="super_admin",
|
|
tenant_no=TENANT,
|
|
)
|
|
|
|
|
|
def _apply(*, apply_id: str, amount: float, status: str = "approved", issue_status: str = None) -> BxAdvanceApply:
|
|
return BxAdvanceApply(
|
|
id=apply_id,
|
|
apply_code=apply_id,
|
|
applicant_id="applicant",
|
|
applicant_name="汪璇",
|
|
reason="备用金",
|
|
amount=amount,
|
|
status=status,
|
|
issue_status=issue_status,
|
|
tenant_no=TENANT,
|
|
create_dept_id="dept",
|
|
create_time=NOW,
|
|
update_time=NOW,
|
|
del_flag=0,
|
|
)
|
|
|
|
|
|
def _attachment() -> BxAttachment:
|
|
return BxAttachment(
|
|
id="att-1",
|
|
biz_type="advance_issue",
|
|
biz_id="0",
|
|
file_name="发放截图.png",
|
|
file_path="bx/advance_issue/shot.png",
|
|
file_type="image/png",
|
|
file_size=12,
|
|
tenant_no=TENANT,
|
|
create_dept_id="dept",
|
|
create_time=NOW,
|
|
del_flag=0,
|
|
)
|
|
|
|
|
|
def test_batch_issue_writes_shared_remark_and_attachment_with_per_bill_amount():
|
|
db = _session()
|
|
db.add_all([_apply(apply_id="BYJ-A", amount=100), _apply(apply_id="BYJ-B", amount=80.5)])
|
|
db.add(_attachment())
|
|
db.commit()
|
|
|
|
rows = AdvanceService.batch_issue(
|
|
db,
|
|
[{"id": "BYJ-A", "actual_amount": 100}, {"id": "BYJ-B", "actual_amount": 70}],
|
|
_user(),
|
|
issue_remark="同一批次发放",
|
|
attachment_ids=["att-1"],
|
|
)
|
|
db.commit()
|
|
|
|
assert [row.id for row in rows] == ["BYJ-A", "BYJ-B"]
|
|
saved = {row.id: row for row in db.query(BxAdvanceApply).all()}
|
|
assert saved["BYJ-A"].status == "issued"
|
|
assert saved["BYJ-A"].issue_status == "issued"
|
|
assert float(saved["BYJ-A"].actual_amount) == 100
|
|
assert saved["BYJ-A"].issue_remark == "同一批次发放"
|
|
assert float(saved["BYJ-B"].actual_amount) == 70
|
|
assert saved["BYJ-B"].issue_remark == "同一批次发放"
|
|
|
|
copies = (
|
|
db.query(BxAttachment)
|
|
.filter(BxAttachment.del_flag == 0, BxAttachment.biz_type == "advance_issue")
|
|
.order_by(BxAttachment.biz_id)
|
|
.all()
|
|
)
|
|
assert [(row.biz_id, row.file_path) for row in copies] == [
|
|
("BYJ-A", "bx/advance_issue/shot.png"),
|
|
("BYJ-B", "bx/advance_issue/shot.png"),
|
|
]
|
|
assert db.query(BxAttachment).filter(BxAttachment.id == "att-1").one().del_flag == 1
|
|
|
|
|
|
def test_batch_issue_requires_attachment_and_rejects_issued_bill():
|
|
db = _session()
|
|
db.add_all(
|
|
[
|
|
_apply(apply_id="BYJ-A", amount=100),
|
|
_apply(apply_id="BYJ-B", amount=80, status="issued", issue_status="issued"),
|
|
]
|
|
)
|
|
db.commit()
|
|
|
|
try:
|
|
AdvanceService.batch_issue(
|
|
db,
|
|
[{"id": "BYJ-A", "actual_amount": 100}, {"id": "BYJ-B", "actual_amount": 80}],
|
|
_user(),
|
|
issue_remark="不应写入",
|
|
attachment_ids=["att-1"],
|
|
)
|
|
except HTTPException as exc:
|
|
assert exc.status_code == 400
|
|
db.rollback()
|
|
else:
|
|
raise AssertionError("expected batch issue to reject an issued bill")
|
|
|
|
saved = {row.id: row for row in db.query(BxAdvanceApply).all()}
|
|
assert saved["BYJ-A"].status == "approved"
|
|
assert saved["BYJ-A"].issue_remark is None
|
|
|
|
try:
|
|
AdvanceService.batch_issue(
|
|
db,
|
|
[{"id": "BYJ-A", "actual_amount": 100}],
|
|
_user(),
|
|
issue_remark="没有附件",
|
|
attachment_ids=[],
|
|
)
|
|
except HTTPException as exc:
|
|
assert exc.status_code == 400
|
|
assert "附件" in str(exc.detail)
|
|
db.rollback()
|
|
else:
|
|
raise AssertionError("expected batch issue to require an attachment")
|
|
|
|
assert db.query(BxAdvanceApply).filter(BxAdvanceApply.id == "BYJ-A").one().status == "approved"
|