fix: 加强对账归属查询一致性
This commit is contained in:
parent
2de4b9009a
commit
9674554b98
@ -1,7 +1,7 @@
|
|||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from sqlalchemy import extract, func, select
|
from sqlalchemy import and_, extract, func, select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
@ -101,7 +101,13 @@ def _reported_good_quantities(
|
|||||||
func.sum(ProductionReportAllocation.good_qty).label("good_qty"),
|
func.sum(ProductionReportAllocation.good_qty).label("good_qty"),
|
||||||
)
|
)
|
||||||
.select_from(ProductionReportAllocation)
|
.select_from(ProductionReportAllocation)
|
||||||
.join(ProductionReportItem, ProductionReportItem.id == ProductionReportAllocation.report_item_id)
|
.join(
|
||||||
|
ProductionReportItem,
|
||||||
|
and_(
|
||||||
|
ProductionReportItem.id == ProductionReportAllocation.report_item_id,
|
||||||
|
ProductionReportItem.report_id == ProductionReportAllocation.report_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
.join(ProductionReport, ProductionReport.id == ProductionReportAllocation.report_id)
|
.join(ProductionReport, ProductionReport.id == ProductionReportAllocation.report_id)
|
||||||
.where(
|
.where(
|
||||||
ProductionReport.status == ReportStatus.approved,
|
ProductionReport.status == ReportStatus.approved,
|
||||||
|
|||||||
@ -1,6 +1,113 @@
|
|||||||
|
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
|
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():
|
def test_fold_reported_good_quantity_rows_uses_allocation_month_for_latest_process_only():
|
||||||
rows = [
|
rows = [
|
||||||
("嘉恒", "产品A", "1序", 7, 999),
|
("嘉恒", "产品A", "1序", 7, 999),
|
||||||
@ -13,3 +120,104 @@ def test_fold_reported_good_quantity_rows_uses_allocation_month_for_latest_proce
|
|||||||
|
|
||||||
assert totals[("嘉恒", "产品A", 7)] == 350
|
assert totals[("嘉恒", "产品A", 7)] == 350
|
||||||
assert totals[("嘉恒", "产品A", 8)] == 300
|
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()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user