Extract ourdigital-estimate-engine; presales-seo now calls it

New skill 96-ourdigital-estimate-engine: method-aware quoting engine
(effort / coaching / procurement) with universal rate_card + per-service
catalog. Real catalogs: seo (effort), education (coaching); stubs:
digital_ads, digital_branding. Validated to reproduce real quotes —
SEO basic ₩10.5M / treatment ₩25.0M, SHR chain ₩29.5M, L'Escape basic
₩10.5M, GA4/GTM coaching ₩1,570,000, procurement +15%.

Refactor 95-ourdigital-presales-seo: remove rate_card.yaml, sow_templates.yaml,
estimate.py (migrated to engine); add findings_to_scope.py; Stage 5 now maps
findings→scope.json and calls the engine CLI. build_deck/kg_query unchanged;
end-to-end validated on SHR (29.5M) + deck renders engine estimate.json.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 01:54:11 +09:00
parent 34c3a1df4f
commit c9bdbb57f7
20 changed files with 822 additions and 349 deletions

View File

@@ -0,0 +1,18 @@
"""Procurement method: cost = Σ(unit_cost × qty × (1 + markup)) for non-labor items."""
def build(scope, rate, catalog):
markup = rate.get("procurement_markup", 0.15)
items, total = [], 0.0
for it in scope.get("items", []):
qty = it.get("qty", 1)
amt = it["unit_cost"] * qty * (1 + markup)
items.append({"label": it["label"], "unit_cost": it["unit_cost"], "qty": qty,
"markup": markup, "amount": amt, "currency": it.get("currency", "KRW")})
total += amt
return {
"kind": "procurement", "service": catalog.get("service", "procurement"),
"label": "조달 항목 (Buying & Supplying)", "markup": markup,
"items": items, "subtotal_sum": total, "proposal": int(round(total)),
"stub": bool(catalog.get("_stub", False)),
}