Sales CRM 集約: 見積管理 / Aggregate: Quote Management
バウンデッドコンテキスト: File Management (ファイル管理) — 拡張
→ 知識ベース:
knowledge/07-sales-management/sales-process.md(見積プロセス)
集約ルート (Aggregate Roots)
1. QuoteTemplate (見積書テンプレート)
QuoteTemplate (集約ルート)
├── name: string (テンプレート名)
├── documentTitle: string (帳票名 — 例: 見積書、御見積書)
├── companyName: string (自社名 — CompanySettings から取得可)
├── logoUrl: string (会社ロゴ URL)
├── stampUrl: string (印影 URL)
├── taxMode: TaxMode (外税 / 内税)
├── defaultExpiry: number (有効期限デフォルト日数)
├── remarks: string (デフォルト備考)
├── isDefault: boolean (デフォルトテンプレートフラグ)
├── createdBy: → User (作成者)
├── updatedBy: → User (最終更新者)
└── version: number (楽観ロック用)
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
| name | string | ✓ | テンプレート名 (最大 100 文字) |
| documentTitle | string | ✓ | 帳票タイトル (例: 見積書) |
| companyName | string | 自社名 (未指定時 CompanySettings.company_name を使用) | |
| logoUrl | string | 会社ロゴ画像 URL | |
| stampUrl | string | 印影画像 URL | |
| taxMode | TaxMode | ✓ | exclusive (外税) / inclusive (内税) |
| defaultExpiry | number | 有効期限デフォルト日数 (デフォルト: 30) | |
| remarks | string | デフォルト備考テキスト (最大 2000 文字) | |
| isDefault | boolean | デフォルトテンプレートフラグ (デフォルト: false) | |
| createdBy | → User | 作成者 (読取専用) | |
| updatedBy | → User | 最終更新者 (読取専用) | |
| version | number | 楽観ロック用バージョン番号 |
不変条件 (Invariants):
nameは空文字不可documentTitleは空文字不可taxModeはexclusive/inclusiveのいずれかisDefault = trueのテンプレートはシステム全体で最大 1 件logoUrl,stampUrlはhttp:/https:プロトコルのみ- 楽観ロック: バージョン不整合で 409 Conflict
- admin のみ作成・更新・削除可能
2. QuoteDocument (見積書)
QuoteDocument (集約ルート)
├── quoteNumber: string (見積番号 — 自動採番)
├── template: → QuoteTemplate (使用テンプレート)
├── deal: → Deal (関連案件)
├── account: → Account (宛先取引先)
├── contact: → Contact (宛先担当者、任意)
├── subject: string (件名)
├── issueDate: Date (発行日)
├── expiryDate: Date (有効期限)
├── message: string (メッセージ)
├── lineItems[] (明細行)
│ ├── itemName: string (品目名、最大 50 文字)
│ ├── quantity: number (数量、正の数)
│ ├── unitPrice: number (単価)
│ ├── unit: string (単位 — 式、個、セット等)
│ ├── description: string (明細詳細、最大 200 文字)
│ └── lineTotal: number (行合計 = quantity × unitPrice)
├── subtotal: number (小計)
├── taxAmount: number (消費税額)
├── totalAmount: number (合計)
├── remarks: string (備考)
├── status: QuoteStatus (下書き / 発行済み)
├── file: → File (生成された PDF ファイル)
├── salesRep: → User (営業担当)
├── createdBy: → User (作成者)
├── updatedBy: → User (最終更新者)
└── version: number (楽観ロック用)
| プロパティ | 型 | 必須 | 説明 |
|---|---|---|---|
| quoteNumber | string | ✓ | 見積番号 (自動採番、一意) |
| template | → QuoteTemplate | ✓ | 使用テンプレート |
| deal | → Deal | ✓ | 関連案件 |
| account | → Account | ✓ | 宛先取引先 (Deal.account から自動設定) |
| contact | → Contact | 宛先担当者 (任意) | |
| subject | string | ✓ | 件名 (最大 255 文字) |
| issueDate | Date | ✓ | 発行日 |
| expiryDate | Date | 有効期限 (issueDate + template.defaultExpiry) | |
| message | string | メッセージ (最大 2000 文字) | |
| lineItems | QuoteLineItem[] | ✓ | 明細行 (最低 1 行) |
| subtotal | number | 小計 (計算値: lineItems の lineTotal 合計) | |
| taxAmount | number | 消費税額 (計算値) | |
| totalAmount | number | 合計 (計算値: subtotal + taxAmount or subtotal) | |
| remarks | string | 備考 (最大 2000 文字) | |
| status | QuoteStatus | ✓ | draft / issued (デフォルト: draft) |
| file | → File | 生成 PDF の File 参照 (fileType=quote) | |
| salesRep | → User | 営業担当 | |
| createdBy | → User | 作成者 (読取専用) | |
| updatedBy | → User | 最終更新者 (読取専用) | |
| version | number | 楽観ロック用 |
不変条件 (Invariants):
quoteNumberはシステム全体で一意dealは必須。見積書は必ず案件に紐付くlineItemsは最低 1 行lineItem.quantityは正の数lineItem.unitPriceは 0 以上lineItem.itemNameは空文字不可、最大 50 文字subtotal= Σ(lineItem.lineTotal) (計算値)- 外税:
taxAmount=subtotal× 税率、totalAmount=subtotal+taxAmount - 内税:
taxAmount=subtotal-subtotal/ (1 + 税率)、totalAmount=subtotal expiryDate≥issueDatestatus=issuedの見積書は PDF を生成し File (fileType=quote) として保存- 楽観ロック: バージョン不整合で 409 Conflict
ライフサイクル (State Machine):
設計判断:
issued後の編集は不可。修正が必要な場合は新規見積書を作成。 TransLead の実装に準拠。
トランザクション境界
| 操作 | 同一トランザクション | 結果整合性 (Eventual) |
|---|---|---|
| QuoteTemplate 作成 | QuoteTemplate のみ | — |
| QuoteDocument 作成 | QuoteDocument のみ | — |
| QuoteDocument 発行 | QuoteDocument.status → issued + File 作成 | Deal との関連更新 |
関連図
┌──────────────────┐ ┌──────────────┐
│ QuoteTemplate │ │ Deal │
│ (テンプレート) │ │ (案件) │
└────────┬─────────┘ └──────┬───────┘
│ 参照 │ 1
│ │
▼ N│
┌──────────────────┐ │
│ QuoteDocument │◀───────────┘
│ (見積書) │
│ ├── lineItems[] │
│ └── file → File │──────▶ File (fileType=quote)
└──────────────────┘
- QuoteTemplate : QuoteDocument = 1 : N
- Deal : QuoteDocument = 1 : N
- QuoteDocument : File = 1 : 1 (発行時)