224 lines
6.2 KiB
Python
224 lines
6.2 KiB
Python
from datetime import date, datetime
|
|
|
|
from sqlalchemy import BigInteger, create_engine
|
|
from sqlalchemy.ext.compiler import compiles
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from app.database import Base
|
|
from app.models import (
|
|
ProductionReport,
|
|
ProductionReportAllocation,
|
|
ProductionReportItem,
|
|
ReportStatus,
|
|
)
|
|
from app.routers import reconciliation
|
|
|
|
|
|
@compiles(BigInteger, "sqlite")
|
|
def _compile_big_integer_for_sqlite(type_, compiler, **kw):
|
|
return "INTEGER"
|
|
|
|
|
|
def _sqlite_db():
|
|
engine = create_engine("sqlite+pysqlite:///:memory:", future=True)
|
|
Base.metadata.create_all(engine)
|
|
SessionLocal = sessionmaker(bind=engine, future=True)
|
|
return SessionLocal()
|
|
|
|
|
|
def _report(
|
|
*,
|
|
id: int,
|
|
status: ReportStatus = ReportStatus.approved,
|
|
is_voided: bool = False,
|
|
):
|
|
return ProductionReport(
|
|
id=id,
|
|
session_id=id,
|
|
attendance_point_name="嘉恒",
|
|
employee_phone="13800000000",
|
|
report_date=date(2026, 7, 31),
|
|
start_at=datetime(2026, 7, 31, 20, 0),
|
|
end_at=datetime(2026, 8, 1, 2, 0),
|
|
duration_minutes=360,
|
|
break_minutes=0,
|
|
effective_minutes=360,
|
|
total_good_qty=650,
|
|
total_output_qty=650,
|
|
actual_beat=1,
|
|
standard_beat=1,
|
|
expected_workload=0,
|
|
pace_rate=0,
|
|
workload_rate=0,
|
|
status=status,
|
|
is_voided=is_voided,
|
|
submitted_at=datetime(2026, 8, 1, 2, 1),
|
|
)
|
|
|
|
|
|
def _item(
|
|
*,
|
|
id: int,
|
|
report_id: int,
|
|
process_name: str = "2序",
|
|
):
|
|
return ProductionReportItem(
|
|
id=id,
|
|
report_id=report_id,
|
|
attendance_point_name="嘉恒",
|
|
device_no="A-01",
|
|
project_no="P1",
|
|
product_name="产品A",
|
|
process_name=process_name,
|
|
operator_count=1,
|
|
process_unit_price_yuan=1,
|
|
standard_beat=1,
|
|
standard_workload=0,
|
|
good_qty=650,
|
|
defect_qty=0,
|
|
scrap_qty=0,
|
|
allocated_minutes=360,
|
|
)
|
|
|
|
|
|
def _allocation(
|
|
*,
|
|
id: int,
|
|
report_id: int,
|
|
report_item_id: int,
|
|
allocation_date: date,
|
|
good_qty: float,
|
|
):
|
|
return ProductionReportAllocation(
|
|
id=id,
|
|
report_id=report_id,
|
|
report_item_id=report_item_id,
|
|
attendance_point_name="嘉恒",
|
|
employee_phone="13800000000",
|
|
allocation_date=allocation_date,
|
|
day_minutes=0,
|
|
overtime_minutes=0,
|
|
night_minutes=0,
|
|
effective_minutes=0,
|
|
good_qty=good_qty,
|
|
defect_qty=0,
|
|
scrap_qty=0,
|
|
changeover_count=0,
|
|
reference_wage=0,
|
|
)
|
|
|
|
|
|
def test_fold_reported_good_quantity_rows_uses_allocation_month_for_latest_process_only():
|
|
rows = [
|
|
("嘉恒", "产品A", "1序", 7, 999),
|
|
("嘉恒", "产品A", "2序", 7, 350),
|
|
("嘉恒", "产品A", "2序", 8, 300),
|
|
]
|
|
latest_processes = {("嘉恒", "产品A"): {"2序"}}
|
|
|
|
totals = reconciliation._fold_reported_good_quantity_rows(rows, latest_processes)
|
|
|
|
assert totals[("嘉恒", "产品A", 7)] == 350
|
|
assert totals[("嘉恒", "产品A", 8)] == 300
|
|
|
|
|
|
def test_reported_good_quantities_groups_by_allocation_month_for_approved_unvoided_latest_process():
|
|
db = _sqlite_db()
|
|
try:
|
|
approved = _report(id=1)
|
|
pending = _report(id=2, status=ReportStatus.pending)
|
|
voided = _report(id=3, is_voided=True)
|
|
latest_item = _item(id=1, report_id=approved.id, process_name="2序")
|
|
earlier_item = _item(id=2, report_id=approved.id, process_name="1序")
|
|
pending_item = _item(id=3, report_id=pending.id, process_name="2序")
|
|
voided_item = _item(id=4, report_id=voided.id, process_name="2序")
|
|
db.add_all([
|
|
approved,
|
|
pending,
|
|
voided,
|
|
latest_item,
|
|
earlier_item,
|
|
pending_item,
|
|
voided_item,
|
|
_allocation(
|
|
id=1,
|
|
report_id=approved.id,
|
|
report_item_id=latest_item.id,
|
|
allocation_date=date(2026, 7, 31),
|
|
good_qty=350,
|
|
),
|
|
_allocation(
|
|
id=2,
|
|
report_id=approved.id,
|
|
report_item_id=latest_item.id,
|
|
allocation_date=date(2026, 8, 1),
|
|
good_qty=300,
|
|
),
|
|
_allocation(
|
|
id=3,
|
|
report_id=approved.id,
|
|
report_item_id=earlier_item.id,
|
|
allocation_date=date(2026, 7, 31),
|
|
good_qty=999,
|
|
),
|
|
_allocation(
|
|
id=4,
|
|
report_id=pending.id,
|
|
report_item_id=pending_item.id,
|
|
allocation_date=date(2026, 7, 31),
|
|
good_qty=111,
|
|
),
|
|
_allocation(
|
|
id=5,
|
|
report_id=voided.id,
|
|
report_item_id=voided_item.id,
|
|
allocation_date=date(2026, 8, 1),
|
|
good_qty=222,
|
|
),
|
|
])
|
|
db.commit()
|
|
|
|
totals = reconciliation._reported_good_quantities(
|
|
db,
|
|
year=2026,
|
|
product_keys=[("嘉恒", "产品A")],
|
|
latest_processes={("嘉恒", "产品A"): {"2序"}},
|
|
)
|
|
|
|
assert totals[("嘉恒", "产品A", 7)] == 350
|
|
assert totals[("嘉恒", "产品A", 8)] == 300
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def test_reported_good_quantities_ignores_mismatched_allocation_report_item_rows():
|
|
db = _sqlite_db()
|
|
try:
|
|
approved = _report(id=1)
|
|
other_report = _report(id=2, status=ReportStatus.pending)
|
|
other_item = _item(id=2, report_id=other_report.id, process_name="2序")
|
|
db.add_all([
|
|
approved,
|
|
other_report,
|
|
other_item,
|
|
_allocation(
|
|
id=1,
|
|
report_id=approved.id,
|
|
report_item_id=other_item.id,
|
|
allocation_date=date(2026, 7, 31),
|
|
good_qty=123,
|
|
),
|
|
])
|
|
db.commit()
|
|
|
|
totals = reconciliation._reported_good_quantities(
|
|
db,
|
|
year=2026,
|
|
product_keys=[("嘉恒", "产品A")],
|
|
latest_processes={("嘉恒", "产品A"): {"2序"}},
|
|
)
|
|
|
|
assert totals == {}
|
|
finally:
|
|
db.close()
|