feat(lifecycle): split unallocated sales/design cost into shared pool

This commit is contained in:
2026-05-04 09:41:20 +09:00
parent 4827d953d1
commit 7882d0cd05

View File

@@ -1148,7 +1148,7 @@ def build_project_lifecycle_cost(
rows.sort(key=lambda item: (role_order.index(item["project_type"]), item["project_code"])) rows.sort(key=lambda item: (role_order.index(item["project_type"]), item["project_code"]))
total_income = sum(float(item.get("income_supply") or 0) for item in rows_with_allocation) total_income = sum(float(item.get("income_supply") or 0) for item in rows_with_allocation)
total_expense = sum(float(item.get("adjusted_expense_supply") or 0) for item in rows_with_allocation) total_expense = sum(float(item.get("expense_supply") or 0) for item in rows_with_allocation)
project_codes = [item.get("project_code") for item in rows_with_allocation if item.get("project_code")] project_codes = [item.get("project_code") for item in rows_with_allocation if item.get("project_code")]
breakdown_components = { breakdown_components = {
"시공비": {"direct": 0.0, "shared": 0.0, "total": 0.0}, "시공비": {"direct": 0.0, "shared": 0.0, "total": 0.0},
@@ -1202,13 +1202,19 @@ def build_project_lifecycle_cost(
numerator = int(project_info.get("allocation_numerator") or 1) numerator = int(project_info.get("allocation_numerator") or 1)
denominator = int(project_info.get("allocation_denominator") or 1) denominator = int(project_info.get("allocation_denominator") or 1)
allocation_ratio = float(project_info.get("allocation_ratio") or 1.0) allocation_ratio = float(project_info.get("allocation_ratio") or 1.0)
expense_supply = float(row["expense_supply"] or 0) * allocation_ratio base_expense_supply = float(row["expense_supply"] or 0)
# Until common-cost allocation is introduced, lifecycle-linked costs # 영업/설계는 배분 비율이 직접분이며, 미배분 잔액은 공통배분분으로 분리한다.
# are treated as direct costs for the target project. if project_type in {"영업", "설계"}:
source_component = "direct" direct_expense_supply = base_expense_supply * allocation_ratio
breakdown_components[bucket][source_component] += expense_supply shared_expense_supply = max(base_expense_supply - direct_expense_supply, 0.0)
breakdown_components[bucket]["total"] += expense_supply else:
direct_expense_supply = base_expense_supply
shared_expense_supply = 0.0
breakdown_components[bucket]["direct"] += direct_expense_supply
breakdown_components[bucket]["shared"] += shared_expense_supply
breakdown_components[bucket]["total"] += base_expense_supply
project_entry = breakdown_project_maps[bucket].setdefault( project_entry = breakdown_project_maps[bucket].setdefault(
project_code, project_code,
@@ -1226,8 +1232,9 @@ def build_project_lifecycle_cost(
"expense_supply": 0.0, "expense_supply": 0.0,
}, },
) )
project_entry[f"{source_component}_expense_supply"] += expense_supply project_entry["direct_expense_supply"] += direct_expense_supply
project_entry["expense_supply"] += expense_supply project_entry["shared_expense_supply"] += shared_expense_supply
project_entry["expense_supply"] += base_expense_supply
account_entry = breakdown_account_maps[bucket].setdefault( account_entry = breakdown_account_maps[bucket].setdefault(
account_code or "미지정", account_code or "미지정",
@@ -1239,8 +1246,9 @@ def build_project_lifecycle_cost(
"expense_supply": 0.0, "expense_supply": 0.0,
}, },
) )
account_entry[f"{source_component}_expense_supply"] += expense_supply account_entry["direct_expense_supply"] += direct_expense_supply
account_entry["expense_supply"] += expense_supply account_entry["shared_expense_supply"] += shared_expense_supply
account_entry["expense_supply"] += base_expense_supply
breakdown = [ breakdown = [
{ {