Sales CRM 業務ルール集 / Business Rules & Policies
各ドメインの不変条件・ポリシー・バリデーションルールを集約する。 コード実装時はこのドキュメントのルールを検証ロジックとして反映すること。
→ 対応ドメイン:
docs/ddd/pms/05-business-rules.md(PMS 側の業務ルール)
1. 取引先 (Account)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| A-001 | 取引先名は必須 | 不変条件 | name は空文字不可 |
| A-002 | 担当グループは必須 | ポリシー | assignedGroup は必須 (フォーム) |
| A-003 | 営業担当は必須 | ポリシー | salesRep は必須 (フォーム) |
| A-004 | URL 形式検証 | 検証 | website は https?:// で始まる |
| A-005 | 電話番号形式 | 検証 | phone は数字・ハイフン・括弧・スペースのみ |
| A-006 | 郵便番号形式 | 検証 | postalCode は XXX-XXXX 形式 |
| A-007 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
| A-008 | 計算フィールド読取専用 | 不変条件 | dealCount, activityCount 等はユーザー変更不可 |
2. 担当者 (Contact)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| C-001 | 担当者名は必須 | 不変条件 | name は空文字不可 |
| C-002 | 取引先は必須 | 不変条件 | account は必須。担当者は必ず 1 つの取引先に所属 |
| C-003 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
3. 案件 (Deal)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| D-001 | 案件名は必須 | 不変条件 | name は空文字不可 |
| D-002 | 案件タイプは必須 | 不変条件 | dealType は negotiation / inquiry / lead のいずれか |
| D-003 | ステータスは必須 | 不変条件 | status は open / won / lost のいずれか (デフォルト: open) |
| D-004 | 取引先は必須 | 不変条件 | account は必須。案件は必ず取引先に紐付く |
| D-005 | 金額は非負 | 検証 | amount ≥ 0 (nonnegative) |
| D-006 | 確度は 0-100 | 検証 | probability は 0 以上 100 以下 |
| D-007 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
案件ステータス遷移
open → won (成約)
open → lost (失注)
won → open (現在は制約なし — 将来的に PMS 連携後は禁止予定)
lost → open (現在は制約なし)
注意: 現在の実装ではステータス遷移に制約がない。 PMS 連携 (CreateOrderRequest) が発生した
won案件をopenに戻すと 整合性の問題が生じる可能性がある。
4. 活動 (Activity)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| ACT-001 | 活動タイプは必須 | 不変条件 | activityType は ACTIVITY_TYPE_VALUES の値のみ |
| ACT-002 | 件名は必須 | 不変条件 | subject は空文字不可 |
| ACT-003 | 取引先は必須 | 不変条件 | account は必須 |
| ACT-004 | 活動日は必須 | 不変条件 | activityDate は必須 |
| ACT-005 | 所要時間は正の数 | 検証 | duration > 0 (指定時) |
| ACT-006 | 件名は 255 文字以内 | 検証 | subject.length ≤ 255 |
| ACT-007 | 詳細は 2000 文字以内 | 検証 | description.length ≤ 2000 |
| ACT-008 | データ必須 | 不変条件 | data が null/undefined の場合は 400 エラー |
| ACT-009 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
5. 日報 (Daily Report)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| DR-001 | 担当者は必須 | 不変条件 | user は必須。未指定時は req.user から自動設定 |
| DR-002 | 報告日は必須 | 不変条件 | reportDate は必須 |
| DR-003 | 同日重複禁止 | 不変条件 | user + reportDate の組み合わせはユニーク |
| DR-004 | RBAC 読取制限 | ポリシー | sales_rep は自分の日報のみ閲覧可 |
| DR-005 | RBAC 更新制限 | ポリシー | sales_rep は自分の日報のみ編集可 |
| DR-006 | 削除は admin のみ | ポリシー | admin ロールのみ日報削除可能 |
6. ファイル (File)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| F-001 | ファイル名は必須 | 不変条件 | fileName は空文字不可 |
| F-002 | ファイルタイプは必須 | 不変条件 | fileType は FILE_TYPE_VALUES の値のみ |
| F-003 | URL は link 時必須 | 不変条件 | fileSource=link の場合 url は必須 |
| F-004 | javascript: 禁止 | セキュリティ | url に javascript: プロトコル禁止 (XSS 防止) |
| F-005 | http(s) のみ | セキュリティ | url は http: / https: プロトコルのみ許可 |
| F-006 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
7. ユーザー (User)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| U-001 | ユーザー名は一意 | 不変条件 | username はシステム全体で一意 |
| U-002 | ユーザー名パターン | 不変条件 | username は /^[a-z0-9_]+$/ のみ |
| U-003 | ログイン試行制限 | セキュリティ | 3 回失敗で 15 分間ロック |
| U-004 | トークン有効期限 | セキュリティ | 2 時間 (7200 秒) |
| U-005 | 初回パスワード変更 | ポリシー | 新規ユーザーは must_change_password = true |
| U-006 | admin のみユーザー作成 | ポリシー | create は admin ロールのみ |
| U-007 | admin のみユーザー削除 | ポリシー | delete は admin ロールのみ |
8. 企業設定 (Company Settings)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| CS-001 | シングルトン | 不変条件 | レコードは 1 件のみ。2 件目作成禁止 |
| CS-002 | 企業名は必須 | 不変条件 | company_name は必須 (最大 100 文字) |
| CS-003 | 郵便番号形式 | 検証 | postal_code は XXX-XXXX |
| CS-004 | 営業時間形式 | 検証 | HH:MM 形式 (00:00-23:59) |
| CS-005 | 営業時間整合性 | 検証 | start < end |
| CS-006 | 会計年度月 | 検証 | fiscal_year_start_month は 1-12 の整数 |
| CS-007 | admin のみ変更可 | ポリシー | create/update/delete は admin のみ |
| CS-008 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
9. カスタムフィールド (Custom Field Config)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| CF-001 | フィールド名パターン | 不変条件 | field_name は /^[a-z_][a-z0-9_]*$/ |
| CF-002 | 予約語禁止 | 不変条件 | field_name に予約語は使用不可 |
| CF-003 | entity_type + field_name 一意 | 不変条件 | 同一エンティティ内でフィールド名は重複不可 |
| CF-004 | dropdown はオプション必須 | 検証 | field_type=dropdown の場合、最低 1 つのオプション |
| CF-005 | number の min ≤ max | 検証 | min_value > max_value は禁止 |
| CF-006 | admin のみ操作可 | ポリシー | 全操作は admin ロールのみ |
| CF-007 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
10. RBAC データスコーピング
→ 参照:
03-aggregates/iam.md§RBAC データアクセスマトリクス
| ID | ルール | 種別 | 説明 | 対象 |
|---|---|---|---|---|
| RBAC-001 | sales_rep 読取制限 | ポリシー | sales_rep は salesRep = user.id のレコードのみ閲覧可 | accounts, contacts, deals, activities, files |
| RBAC-002 | sales_rep 更新制限 | ポリシー | sales_rep は自分が担当するレコードのみ更新可 | accounts, contacts, deals, activities, files |
| RBAC-003 | executive 読取専用 | ポリシー | executive は全レコード閲覧可だが作成・更新・削除不可 | accounts, contacts, deals, activities, files |
| RBAC-004 | manager 全データアクセス | ポリシー | manager は全レコードの閲覧・作成・更新可、削除は不可 | accounts, contacts, deals, activities, files |
| RBAC-005 | 削除は admin のみ | ポリシー | 営業データコレクションの削除は admin のみ | accounts, contacts, deals, activities, files |
11. 案件コメント (Comment)
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| CMT-001 | 案件参照は必須 | 不変条件 | deal は必須。コメントは必ず案件に紐付く |
| CMT-002 | 本文は必須 | 不変条件 | body は空文字不可、最大 2000 文字 |
| CMT-003 | 投稿者自動設定 | ポリシー | author は req.user から自動設定 |
| CMT-004 | 削除権限 | ポリシー | 削除は投稿者本人または admin のみ |
| CMT-005 | commentCount 同期 | 不変条件 | コメント作成/削除時に Deal.commentCount を更新 |
12. レポート・分析 (Reporting)
→ 参照:
03-aggregates/reporting.md
| ID | ルール | 種別 | 説明 | 対象 |
|---|---|---|---|---|
| RPT-001 | 加重金額計算 | 計算 | weightedAmount = amount x probability / 100 | PipelineReport |
| RPT-002 | 成約率計算 | 計算 | winRate = won / (won + lost) x 100。open のみの場合は 0 | PipelineReport |
| RPT-003 | 期間フィルタ必須 | ポリシー | レポートリクエストには startDate と endDate が必須 | PipelineReport, ActivitySummary |
| RPT-004 | RBAC スコーピング | ポリシー | sales_rep は自分の担当データのみ集計対象 | PipelineReport, ActivitySummary |
| RPT-005 | 読取専用 | 不変条件 | レポートデータは永続化しない (都度集計) | PipelineReport, ActivitySummary |
13. 見積書 (Quote)
→ 参照:
03-aggregates/quote-management.md
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| QT-001 | テンプレート名は必須 | 不変条件 | QuoteTemplate.name は空文字不可 |
| QT-002 | 帳票名は必須 | 不変条件 | QuoteTemplate.documentTitle は空文字不可 |
| QT-003 | 税区分は必須 | 不変条件 | QuoteTemplate.taxMode は exclusive / inclusive のいずれか |
| QT-004 | デフォルトテンプレートは 1 件 | 不変条件 | isDefault=true のテンプレートはシステム全体で最大 1 件 |
| QT-005 | テンプレートは admin のみ | ポリシー | QuoteTemplate の作成・更新・削除は admin のみ |
| QT-006 | 見積番号は一意 | 不変条件 | QuoteDocument.quoteNumber はシステム全体で一意 |
| QT-007 | 案件は必須 | 不変条件 | QuoteDocument.deal は必須 |
| QT-008 | 明細行は最低 1 行 | 不変条件 | QuoteDocument.lineItems.length ≥ 1 |
| QT-009 | 品目名は必須 | 検証 | lineItem.itemName は空文字不可、最大 50 文字 |
| QT-010 | 数量は正の数 | 検証 | lineItem.quantity > 0 |
| QT-011 | 単価は非負 | 検証 | lineItem.unitPrice ≥ 0 |
| QT-012 | 有効期限は発行日以降 | 検証 | QuoteDocument.expiryDate ≥ issueDate |
| QT-013 | 外税計算 | 計算 | exclusive: taxAmount = subtotal × 税率、totalAmount = subtotal + taxAmount |
| QT-014 | 内税計算 | 計算 | inclusive: taxAmount = subtotal - subtotal / (1 + 税率)、totalAmount = subtotal |
| QT-015 | 発行後は編集不可 | 不変条件 | status=issued の見積書は更新不可 |
| QT-016 | 発行時 PDF 生成 | ポリシー | status → issued で PDF を生成し File (fileType=quote) として保存 |
| QT-017 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
14. 目標管理 (Goal Tracking)
→ 参照:
03-aggregates/goal-tracking.md
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| GL-001 | 目標名は必須 | 不変条件 | Goal.name は空文字不可 |
| GL-002 | エンティティタイプは必須 | 不変条件 | Goal.entityType は deals / activities のいずれか |
| GL-003 | 集計方法は必須 | 不変条件 | Goal.aggregationMethod は count / sum / average のいずれか |
| GL-004 | sum/average 時は対象フィールド必須 | 不変条件 | aggregationMethod が sum / average の場合、targetField は必須 |
| GL-005 | 対象フィールドは数値型 | 検証 | targetField は対象エンティティの数値フィールドのみ |
| GL-006 | 期間は必須 | 不変条件 | Goal.period は monthly / quarterly / yearly のいずれか |
| GL-007 | 目標は admin のみ | ポリシー | Goal の作成・更新・削除は admin のみ |
| GL-008 | 目標値は非負 | 検証 | GoalValue.targetValue ≥ 0 |
| GL-009 | 期間の整合性 | 検証 | GoalValue.periodStart < periodEnd |
| GL-010 | 目標値はユニーク | 不変条件 | 同一 goal + user + periodStart の組み合わせは重複不可 |
| GL-011 | 達成率計算 | 計算 | achievementRate = actualValue / targetValue × 100 |
| GL-012 | RBAC スコーピング | ポリシー | sales_rep は自分の担当データのみ集計対象 |
| GL-013 | 楽観ロック | 不変条件 | version 不整合で 409 Conflict |
15. 重複検出 (Duplicate Detection)
→ 参照:
03-aggregates/sales-pipeline.md§DuplicateDetection
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| DUP-001 | 自己参照禁止 | 不変条件 | DuplicateCandidate.sourceId ≠ targetId |
| DUP-002 | ペアはユニーク | 不変条件 | sourceId + targetId の組み合わせは順序問わず一意 |
| DUP-003 | スコアは 0-100 | 検証 | similarityScore は 0 以上 100 以下 |
| DUP-004 | 統合時にメタデータ必須 | 不変条件 | status=merged の場合、mergedAt と mergedBy は必須 |
| DUP-005 | 統合は admin/manager のみ | ポリシー | 統合操作は admin または manager ロールのみ |
| DUP-006 | 統合は関連データ移行を含む | ポリシー | 統合時に target の関連 Deal/Contact/Activity/File を source に移行 |
| DUP-007 | バッチ検出は 1 日 1 回 | ポリシー | 自動検出バッチジョブは 1 日 1 回実行 |
16. カレンダー連携 (Calendar Sync)
→ 参照:
03-aggregates/activity-tracking.md§CalendarSync
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| CAL-001 | ユーザー+プロバイダはユニーク | 不変条件 | user + provider の組み合わせは一意 |
| CAL-002 | 有効時はトークン必須 | 不変条件 | isEnabled=true の場合、authToken は必須 |
| CAL-003 | トークンは暗号化保存 | セキュリティ | authToken は暗号化して保存 (PII) |
| CAL-004 | 自身の設定のみ管理 | ポリシー | 各ユーザーが自身の CalendarSync のみ作成・更新・削除可能 |
| CAL-005 | アウトバウンド同期のみ | ポリシー | Activity → Calendar 方向のみ (初期実装) |
17. メール取り込み (Email Capture)
→ 参照:
03-aggregates/activity-tracking.md§EmailCapture
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| EM-001 | メールアドレス形式 | 検証 | emailAddress は有効なメール形式 |
| EM-002 | ポート範囲 | 検証 | port は 1-65535 の範囲 |
| EM-003 | 認証情報は暗号化保存 | セキュリティ | authCredentials は暗号化して保存 (PII) |
| EM-004 | 自身の設定のみ管理 | ポリシー | 各ユーザーが自身の EmailCapture のみ管理可能 |
| EM-005 | Contact マッチング | ポリシー | 送信者メールで Contact を検索し、見つかった場合のみ Activity 作成 |
| EM-006 | 活動タイプ自動設定 | ポリシー | 取り込みメールの活動タイプは autoActivityType に設定 (デフォルト: email_received) |
18. 日報 楽観ロック (Daily Report — 追加ルール)
→ 参照:
03-aggregates/activity-tracking.md§DailyReport
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| DR-007 | 楽観ロック | 不変条件 | version フィールドで更新競合を検出。version 不整合で 409 Conflict |
19. カスタムフィールド 影響件数 (Custom Field — 追加ルール)
→ 参照:
03-aggregates/custom-fields.md§AffectedRecordCount
| ID | ルール | 種別 | 説明 |
|---|---|---|---|
| CF-008 | 削除前に影響件数を表示 | ポリシー | カスタムフィールド削除確認時に、そのフィールドを使用中のレコード件数をカウントして表示 |
| CF-009 | 影響件数は都度計算 | 計算 | affectedRecordCount は永続化せず都度クエリで計算 |
Appendix. 共通バリデーション定数
バリデーション定数の実装値はインスタンスドキュメント (
instances/translead/configuration.md) を参照。