admin / Apex
publicBid Management and Orchestration Tool with AI Capability
Apex / Synapse-Apexv2 / backend / app / services / costing.py
6795 B · main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | """Cost engine (Module 3) and pricing maths (Module 4). All money maths use Decimal. These functions are pure and unit-tested. """ from __future__ import annotations from decimal import ROUND_HALF_UP, Decimal TWOPLACES = Decimal("0.01") def q(value: Decimal) -> Decimal: return value.quantize(TWOPLACES, rounding=ROUND_HALF_UP) def compute_effort_lines(rules: list[dict], inputs: dict) -> list[dict]: """Resolve effort rules against endpoint inputs → resource hour lines. rules: [{name, trigger, endpoint_type, per_qty, resource_type_id, resource_name, hourly_cost_rate, hours_recurring, hours_oneoff}] inputs: {endpoints: {servers, workstations, network_devices, users, sites}, term_months, scope: {...}} """ endpoints = inputs.get("endpoints", {}) lines: list[dict] = [] for r in rules: trigger = r["trigger"] per_qty = Decimal(str(r.get("per_qty") or 1)) if trigger == "fixed": multiplier = Decimal("1") elif trigger in ("per_endpoint", "per_site"): etype = r.get("endpoint_type") or ("sites" if trigger == "per_site" else "") count = Decimal(str(endpoints.get(etype, 0) or 0)) if per_qty == 0: continue multiplier = count / per_qty else: continue rec = Decimal(str(r.get("hours_recurring") or 0)) * multiplier one = Decimal(str(r.get("hours_oneoff") or 0)) * multiplier if rec == 0 and one == 0: continue rate = Decimal(str(r.get("hourly_cost_rate") or 0)) if rec > 0: lines.append({ "group": "labour_recurring", "description": f"{r.get('resource_name', 'Resource')} — {r['name']}", "resource_type_id": r.get("resource_type_id"), "qty": 1, "hours": q(rec), "rate_snapshot": q(rate), "monthly": q(rec * rate), "oneoff": Decimal("0.00"), }) if one > 0: lines.append({ "group": "labour_oneoff", "description": f"{r.get('resource_name', 'Resource')} — {r['name']} (one-off)", "resource_type_id": r.get("resource_type_id"), "qty": 1, "hours": q(one), "rate_snapshot": q(rate), "monthly": Decimal("0.00"), "oneoff": q(one * rate), }) return lines def compute_licence_lines(licences: list[dict], inputs: dict) -> list[dict]: endpoints = inputs.get("endpoints", {}) total_endpoints = Decimal(str( (endpoints.get("servers", 0) or 0) + (endpoints.get("workstations", 0) or 0) + (endpoints.get("network_devices", 0) or 0) )) users = Decimal(str(endpoints.get("users", 0) or 0)) sites = Decimal(str(endpoints.get("sites", 0) or 0)) lines: list[dict] = [] for lic in licences: unit = lic["unit"] unit_cost = Decimal(str(lic["unit_cost"])) qty = { "per_endpoint": total_endpoints, "per_user": users, "per_site": sites, "flat": Decimal("1"), }.get(unit, Decimal("0")) amount = qty * unit_cost if amount == 0: continue billing = lic.get("billing", "monthly") monthly = amount if billing == "monthly" else (amount / 12 if billing == "annual" else Decimal("0")) oneoff = amount if billing == "one_off" else Decimal("0") lines.append({ "group": "licensing", "description": f"{lic['name']} ({unit})", "resource_type_id": None, "qty": q(qty), "hours": Decimal("0.00"), "rate_snapshot": q(unit_cost), "monthly": q(monthly), "oneoff": q(oneoff), }) return lines def compute_non_people_lines(inputs: dict) -> list[dict]: """User-entered non-people resource costs (hardware, travel, connectivity, etc.).""" lines: list[dict] = [] for item in inputs.get("non_people", []) or []: monthly = Decimal(str(item.get("monthly", 0) or 0)) oneoff = Decimal(str(item.get("oneoff", 0) or 0)) if monthly == 0 and oneoff == 0: continue lines.append({ "group": "non_people", "description": item.get("description", "Non-people cost"), "resource_type_id": None, "qty": Decimal("1"), "hours": Decimal("0.00"), "rate_snapshot": Decimal("0.00"), "monthly": q(monthly), "oneoff": q(oneoff), }) return lines def sheet_totals(lines: list[dict], term_months: int) -> dict: monthly = sum((Decimal(str(l["monthly"])) for l in lines), Decimal("0")) oneoff = sum((Decimal(str(l["oneoff"])) for l in lines), Decimal("0")) total_contract = monthly * Decimal(str(term_months)) + oneoff return { "monthly": q(monthly), "oneoff": q(oneoff), "term_months": term_months, "total_contract": q(total_contract), } def price_scenario(base_cost: Decimal, risk_pct: Decimal, mgmt_pct: Decimal, margin_pct: Decimal, term_months: int) -> dict: """Waterfall: base → +risk → +mgmt → total cost → ÷(1-margin) → sell price. margin_pct is margin ON PRICE (not markup). All inputs Decimal percentages. """ base = Decimal(str(base_cost)) risk_amt = base * risk_pct / Decimal("100") mgmt_amt = base * mgmt_pct / Decimal("100") total_cost = base + risk_amt + mgmt_amt margin_frac = margin_pct / Decimal("100") if margin_frac >= Decimal("1"): raise ValueError("margin must be < 100%") sell_price = total_cost / (Decimal("1") - margin_frac) profit = sell_price - total_cost monthly_price = sell_price / Decimal(str(term_months)) if term_months else Decimal("0") return { "base_cost": q(base), "risk_amount": q(risk_amt), "mgmt_amount": q(mgmt_amt), "total_cost": q(total_cost), "margin_pct": q(margin_pct), "sell_price": q(sell_price), "profit": q(profit), "monthly_price": q(monthly_price), "term_months": term_months, "waterfall": [ {"label": "Base delivery cost", "value": q(base)}, {"label": f"Risk contingency ({q(risk_pct)}%)", "value": q(risk_amt)}, {"label": f"Management contingency ({q(mgmt_pct)}%)", "value": q(mgmt_amt)}, {"label": "Total cost", "value": q(total_cost)}, {"label": f"Margin ({q(margin_pct)}% of price)", "value": q(profit)}, {"label": "Sell price (total contract)", "value": q(sell_price)}, ], } |