集約: 見積・受注 / Aggregate: Quotation & Order
バウンデッドコンテキスト: bc-quotation
Quotation(見積)
Quotation (集約ルート) ├── QuotationLine[] (明細) └── ApprovalRecord (承認記録)
| プロパティ | 型 | 説明 |
|---|---|---|
| quotationNumber | string | 見積番号 — ナチュラルキー |
| revisionSeq | number | 改訂番号 |
| quotationStatus | QuotationStatus | 作成中 → 承認待ち → 承認済み → 受注済み |
| customerId | string | 得意先コード |
| lines | QuotationLine[] | 明細行 |
| totalAmount | Money | 見積合計金額 |
不変条件 (Invariants)
- `totalAmount` = Σ `lines[].amount` (明細合計と見積合計は常に一致)
- `revisionSeq` は単調増加 (改訂は追記のみ、遡り禁止)
- 承認済み見積のみ受注に変換可能 (`quotationStatus === 'approved'`)
- 改訂時は前版の全明細をコピーし、新版として保存
計算フィールド
| フィールド | 計算式 | トリガー |
|---|---|---|
| 明細金額 | `quantity × unitPrice` | 数量 or 単価変更時に即時再計算 |
| 諸経費行の金額 | `Σ(他の全行金額) × overheadRate%` | 他行の金額変更時。率は得意先マスタの諸経費率を優先、未設定時はシステム既定値 |
| 見積合計 | `Σ(全行の明細金額)` | 明細変更時 |
| 原価合計 | `Σ(内部原価) + Σ(外部原価)` | 明細変更時 |
| 粗利 | `見積合計 - 原価合計` | 明細変更時 |
| 粗利率 | `(粗利 / 見積合計) × 100` | 明細変更時 |
| 限界利益 | `見積合計 - Σ(外部原価のみ)` | 明細変更時 |
| 限界利益率 | `(限界利益 / 見積合計) × 100` | 明細変更時 |
入力検証
| 検証 | 条件 |
|---|---|
| 得意先必須 | コードと名称の両方が入力されていること |
| 見積名称必須 | 空でないこと |
| 明細の品名必須 | データが入力された行の品名 (`itemName`) が空でないこと |
自動入力
- **部品コード入力時**: 部品マスタから品名・単位・単価を取得。さらに得意先別単価マスタに該当あれば単価を上書き
- **得意先変更時**: 受渡条件・支払条件・有効期限を得意先マスタから再取得 (未設定時はシステム既定値にフォールバック)
- **新規作成時**: 作成者=ログインユーザー、日付=当日
- **保存時**: 明細の単価を得意先別単価マスタに自動登録 (次回以降の見積で参照)
コマンド
| コマンド | ガード条件 | アクション |
|---|---|---|
| 登録 (Save) | 入力検証通過 | 見積ヘッダ+明細保存。新規時は採番管理で見積番号を自動発行。得意先別単価を自動登録 |
| 承認申請 | 保存済み (見積番号あり) | Excel添付メール送信。ステータス→申請中 |
| 承認 | 見積承認権限あり AND 見積合計 <= 承認者の最大承認金額 AND 入力検証通過 | ステータス→承認済み、承認者コード+承認日を記録 |
| 否認 | 更新モード | ステータス→否認 |
| 改訂 | 更新モード | 改訂番号 = 最大+1、ステータス→作成中にリセット、承認フィールドクリア |
| 参照作成 | 既存見積あり | 全データコピー、見積番号クリア、ステータスリセット (新規として扱う) |
ライフサイクル (State Machine)
**否認 (rejected)**: 否認された見積は終端ステータス。再提出する場合は改訂 (新しい revisionSeq) として新版を作成する。否認→承認待ちへの直接遷移は禁止。
SalesOrder(受注)
SalesOrder (集約ルート) ├── CustomerPO (注文書) ├── → PurchaseOrder[] (外注手配 — 別集約への参照) └── → ProductionInstruction[] (製作指示 — 別集約への参照)
| プロパティ | 型 | 説明 |
|---|---|---|
| orderNumber | string | 受注番号 — ナチュラルキー |
| parentOrderNumber | string? | 親オーダNo (分割受注時) |
| customerId | string | 得意先コード |
| dueDate | Date | 納期 |
| scheduledDate | Date? | 完了予定日 |
| completedDate | Date? | 完了日 |
| customerPO | CustomerPO? | 顧客注文書 |
不変条件 (Invariants)
- `orderNumber` はシステム全体で一意 (採番管理テーブルで自動採番)
- 受注作成時に `quotationNumber` への参照が必須 (見積なし受注は禁止)
- `completedDate` は全ての手配・製作指示が完了するまで設定不可
- 受注キャンセルは未着手の手配・製作指示が存在する場合のみ可能
- **工事完了ロック**: `completedDate` が設定済みの受注は、手配入力・手配確定・製作指示のすべての編集操作を拒否。関連する全画面の保存・確定・承認・発注・着手・完了・取消コマンドを無効化
- **売上金額の自動更新**: 売上登録時に `salesAmount = Σ(売上明細金額)`、`salesDate = MAX(売上日)` を自動更新。受注金額が 0 の場合は売上合計額で初期化
ライフサイクル (State Machine)
「工事完了-未売上」→「工事完了-売上済」の遷移は売上計上によって自動遷移する。この区分により売上計上漏れを検出できる。
新システムでは「検収待ち」「手直し」等の中間ステータスを追加し、品質管理プロセスをより詳細に追跡することも可能。