集約: 原価分析 / Aggregate: Cost Analysis
バウンデッドコンテキスト: bc-cost
CostReport(原価レポート)
CostReport (集約ルート / 読取専用ビュー) ├── materialCost ← GoodsReceipt[] から集計 ├── labourCost ← WorkReport[] から集計 ├── billingAmount ← DeliveryInspection から取得 └── wipAmount ← 仕掛計算
| プロパティ | 型 | 説明 |
|---|---|---|
| orderNumber | string | 受注番号 |
| materialCost | Money | 材料費 (外注費 + 購入費) — 費目区分 (`ExpenseCategory`) ごとに内訳を保持 |
| labourCost | Money | 労務費 (作業時間 × 単価) — 工程区分 (`InternalProcessCategory`) ごとに内訳を保持 |
| totalCost | Money | 実際原価 (materialCost + labourCost) |
| billingAmount | Money | 検収金額 (売上) |
| wipAmount | Money | 仕掛金額 |
| profitMargin | number | 粗利率 |
| riskLevel | RiskLevel | リスク判定 (green / yellow / red) |
不変条件 (Invariants)
- `totalCost` = `materialCost` + `labourCost`
- `profitMargin` = (`billingAmount` - `totalCost`) / `billingAmount`
- `riskLevel`:
- `wipAmount` = `totalCost` - `billingAmount` (billingAmount が 0 の場合は totalCost)
- `wipRisk` = `totalCost` - `Round(billingAmount × 0.7, 0)` (検収額の 70% を整数丸めで回収見込みとし、残り 30% を未回収リスクとして留保。未完了受注のみ対象)
- CostReport は **読取専用の集約** (CQRS の Read Model)
- 他の集約のイベントから非同期に集計される
- 直接書き込みは行わない
- 受注単位で 1 レポート (受注番号がキー)
- 費目区分 (ExpenseCategory) と社内工程区分 (InternalProcessCategory) ごとに動的に列を生成。ゼロ合計の列は自動非表示
AnnualCostReport(得意先別年間原価)
| プロパティ | 型 | 説明 |
|---|---|---|
| customerId | string | 得意先コード |
| fiscalYear | number | 会計年度 |
| orderAmount | Money | 受注金額合計 (受注日ベースで期間フィルタ) |
| purchaseCost | Money | 仕入原価 (受入日ベースで期間フィルタ: `Σ(receivedQty × receivedUnitPrice)`) |
| manufacturingCost | Money | 製造原価 (作業日ベースで期間フィルタ: `Σ(workReport.amount)`) |
| grossProfit | Money | 粗利 (`orderAmount - purchaseCost - manufacturingCost`) |
| grossProfitRate | number | 粗利率 (`grossProfit / orderAmount × 100`, 0除算ガード付き) |
| CostReport フィールド | ソース集約 | 集計方法 |
| materialCost | GoodsReceipt | Σ(数量 × 発注単価) per 受注番号。費目区分 (`ExpenseCategory`) ごとに内訳 |
| labourCost | WorkReport | Σ(作業時間 × 時間単価) per 受注番号。工程区分 (`InternalProcessCategory`) ごとに内訳 |
| billingAmount | DeliveryInspection | 検収金額 per 受注番号 |
| wipAmount | (計算) | totalCost - billingAmount |