feat: 对账小账本按归属日期统计

This commit is contained in:
souplearn 2026-07-25 05:08:43 +08:00
parent f73c3a1166
commit 2de4b9009a
2 changed files with 50 additions and 15 deletions

View File

@ -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),

View 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