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.database import get_db
|
||||||
from app.deps import require_roles
|
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 (
|
from app.schemas import (
|
||||||
ReconciliationEntryUpdate,
|
ReconciliationEntryUpdate,
|
||||||
ReconciliationLedgerOut,
|
ReconciliationLedgerOut,
|
||||||
@ -58,6 +67,20 @@ def _month_range(year: int, month: int) -> tuple[date, date]:
|
|||||||
return start, end
|
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(
|
def _reported_good_quantities(
|
||||||
db: Session,
|
db: Session,
|
||||||
*,
|
*,
|
||||||
@ -74,14 +97,16 @@ def _reported_good_quantities(
|
|||||||
ProductionReportItem.attendance_point_name,
|
ProductionReportItem.attendance_point_name,
|
||||||
ProductionReportItem.product_name,
|
ProductionReportItem.product_name,
|
||||||
ProductionReportItem.process_name,
|
ProductionReportItem.process_name,
|
||||||
extract("month", ProductionReport.report_date).label("month"),
|
extract("month", ProductionReportAllocation.allocation_date).label("month"),
|
||||||
func.sum(ProductionReportItem.good_qty).label("good_qty"),
|
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(
|
.where(
|
||||||
ProductionReport.status == ReportStatus.approved,
|
ProductionReport.status == ReportStatus.approved,
|
||||||
ProductionReport.is_voided.is_(False),
|
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.attendance_point_name.in_(point_names),
|
||||||
ProductionReportItem.product_name.in_(product_names),
|
ProductionReportItem.product_name.in_(product_names),
|
||||||
)
|
)
|
||||||
@ -89,17 +114,10 @@ def _reported_good_quantities(
|
|||||||
ProductionReportItem.attendance_point_name,
|
ProductionReportItem.attendance_point_name,
|
||||||
ProductionReportItem.product_name,
|
ProductionReportItem.product_name,
|
||||||
ProductionReportItem.process_name,
|
ProductionReportItem.process_name,
|
||||||
extract("month", ProductionReport.report_date),
|
extract("month", ProductionReportAllocation.allocation_date),
|
||||||
)
|
)
|
||||||
).all()
|
).all()
|
||||||
totals: dict[tuple[str, str, int], float] = {}
|
return _fold_reported_good_quantity_rows(rows, latest_processes)
|
||||||
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 _ledger_quantities(db: Session, *, year: int) -> dict[tuple[str, str, int], dict[str, float]]:
|
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
|
current_year = today().year
|
||||||
years = {current_year}
|
years = {current_year}
|
||||||
report_years = db.execute(
|
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(
|
.where(
|
||||||
ProductionReport.status == ReportStatus.approved,
|
ProductionReport.status == ReportStatus.approved,
|
||||||
ProductionReport.is_voided.is_(False),
|
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