feat: 对账小账本按归属日期统计
This commit is contained in:
parent
f73c3a1166
commit
2de4b9009a
@ -6,7 +6,16 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from app.database import get_db
|
||||
from app.deps import require_roles
|
||||
from app.models import Personnel, Product, ProductionReport, ProductionReportItem, ReconciliationLedgerEntry, ReportStatus, Role
|
||||
from app.models import (
|
||||
Personnel,
|
||||
Product,
|
||||
ProductionReport,
|
||||
ProductionReportAllocation,
|
||||
ProductionReportItem,
|
||||
ReconciliationLedgerEntry,
|
||||
ReportStatus,
|
||||
Role,
|
||||
)
|
||||
from app.schemas import (
|
||||
ReconciliationEntryUpdate,
|
||||
ReconciliationLedgerOut,
|
||||
@ -58,6 +67,20 @@ def _month_range(year: int, month: int) -> tuple[date, date]:
|
||||
return start, end
|
||||
|
||||
|
||||
def _fold_reported_good_quantity_rows(
|
||||
rows,
|
||||
latest_processes: dict[tuple[str, str], set[str]],
|
||||
) -> dict[tuple[str, str, int], float]:
|
||||
totals: dict[tuple[str, str, int], float] = {}
|
||||
for point_name, product_name, process_name, month, good_qty in rows:
|
||||
product_key = (point_name, product_name)
|
||||
if str(process_name or "").strip() not in latest_processes.get(product_key, set()):
|
||||
continue
|
||||
key = (point_name, product_name, int(month))
|
||||
totals[key] = round2(totals.get(key, 0) + as_float(good_qty))
|
||||
return totals
|
||||
|
||||
|
||||
def _reported_good_quantities(
|
||||
db: Session,
|
||||
*,
|
||||
@ -74,14 +97,16 @@ def _reported_good_quantities(
|
||||
ProductionReportItem.attendance_point_name,
|
||||
ProductionReportItem.product_name,
|
||||
ProductionReportItem.process_name,
|
||||
extract("month", ProductionReport.report_date).label("month"),
|
||||
func.sum(ProductionReportItem.good_qty).label("good_qty"),
|
||||
extract("month", ProductionReportAllocation.allocation_date).label("month"),
|
||||
func.sum(ProductionReportAllocation.good_qty).label("good_qty"),
|
||||
)
|
||||
.join(ProductionReport, ProductionReport.id == ProductionReportItem.report_id)
|
||||
.select_from(ProductionReportAllocation)
|
||||
.join(ProductionReportItem, ProductionReportItem.id == ProductionReportAllocation.report_item_id)
|
||||
.join(ProductionReport, ProductionReport.id == ProductionReportAllocation.report_id)
|
||||
.where(
|
||||
ProductionReport.status == ReportStatus.approved,
|
||||
ProductionReport.is_voided.is_(False),
|
||||
extract("year", ProductionReport.report_date) == year,
|
||||
extract("year", ProductionReportAllocation.allocation_date) == year,
|
||||
ProductionReportItem.attendance_point_name.in_(point_names),
|
||||
ProductionReportItem.product_name.in_(product_names),
|
||||
)
|
||||
@ -89,17 +114,10 @@ def _reported_good_quantities(
|
||||
ProductionReportItem.attendance_point_name,
|
||||
ProductionReportItem.product_name,
|
||||
ProductionReportItem.process_name,
|
||||
extract("month", ProductionReport.report_date),
|
||||
extract("month", ProductionReportAllocation.allocation_date),
|
||||
)
|
||||
).all()
|
||||
totals: dict[tuple[str, str, int], float] = {}
|
||||
for point_name, product_name, process_name, month, good_qty in rows:
|
||||
product_key = (point_name, product_name)
|
||||
if str(process_name or "").strip() not in latest_processes.get(product_key, set()):
|
||||
continue
|
||||
key = (point_name, product_name, int(month))
|
||||
totals[key] = round2(totals.get(key, 0) + as_float(good_qty))
|
||||
return totals
|
||||
return _fold_reported_good_quantity_rows(rows, latest_processes)
|
||||
|
||||
|
||||
def _ledger_quantities(db: Session, *, year: int) -> dict[tuple[str, str, int], dict[str, float]]:
|
||||
@ -125,7 +143,9 @@ def list_reconciliation_years(
|
||||
current_year = today().year
|
||||
years = {current_year}
|
||||
report_years = db.execute(
|
||||
select(extract("year", ProductionReport.report_date))
|
||||
select(extract("year", ProductionReportAllocation.allocation_date))
|
||||
.select_from(ProductionReportAllocation)
|
||||
.join(ProductionReport, ProductionReport.id == ProductionReportAllocation.report_id)
|
||||
.where(
|
||||
ProductionReport.status == ReportStatus.approved,
|
||||
ProductionReport.is_voided.is_(False),
|
||||
|
||||
15
tests/test_reconciliation_allocations.py
Normal file
15
tests/test_reconciliation_allocations.py
Normal file
@ -0,0 +1,15 @@
|
||||
from app.routers import reconciliation
|
||||
|
||||
|
||||
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
|
||||
Loading…
Reference in New Issue
Block a user