CRM Engagement Reconciliation & Action Generation Skill
Purpose
Given a multi-source CRM/ERP prompt containing an opportunity, phase milestones, invoices, payments, event, voucher, and contacts, produce a structured JSON with three sections: engagement_reconciliation, invoice_actions, and event_actions.
Input Parsing
Extract these entities from the prompt (they are always present):
as_of_date: date string after "as of"
opportunity: object with opportunity_id, customer_id, customer_name, stage, amount
phase_milestones: array of objects with milestone_id (MS1, MS2, MS3), amount
invoices: array of objects with milestone_id, status (PAID, OPEN, VOID), due_date, amount
payments: array of objects with milestone_id, payment_amount
event: object with event_id, status (SCHEDULED, ACTIVE, COMPLETED, CANCELLED)
voucher: object with code, status, discount_amount, max_uses
contacts: array of objects with contact_name, customer_id, is_primary
event.registrations: array of objects with customer_id, status
1. Engagement Reconciliation
{
"as_of_date": "<as_of_date>",
"opportunity_id": "opportunity.opportunity_id",
"customer_id": "opportunity.customer_id",
"customer_name": "opportunity.customer_name",
"stage": "opportunity.stage",
"won_amount": "opportunity.amount if stage == 'WON' else 0.00",
"phase_total_amount": "sum(phase_milestones.amount)",
"opportunity_matches_phase_total": "opportunity.amount == phase_total_amount",
"total_paid_amount": "sum(payments.payment_amount)",
"outstanding_balance": "phase_total_amount - total_paid_amount",
"primary_contact": {
"contact_name": "contacts.find(c => c.is_primary).contact_name",
"customer_id": "contacts.find(c => c.is_primary).customer_id"
}
}
Milestones Array
Order strictly ascending: MS1, MS2, MS3.
For each milestone, locate its invoice and payment(s):
| Field |
Derivation |
milestone_id |
From phase_milestones |
amount |
From phase_milestones |
invoice_state |
Invoice status if exists; else UNKNOWN |
payment_state |
If invoice is VOID → UNKNOWN. Else if total payments == invoice amount → PAID. Else if 0 < total payments < invoice amount → PARTIAL. Else → UNPAID |
paid_amount |
Sum of payments for this milestone (0 if none) |
due_date |
Invoice due_date if invoice exists and invoice_state == 'OPEN', else null |
recognition_status |
See rules below |
Recognition Status Rules
- If
invoice_state == 'PAID' and payment_state == 'PAID' and milestone_id == 'MS2' → MISSING_REVENUE_JOURNAL
- If
invoice_state == 'PAID' and payment_state == 'PAID' and milestone_id != 'MS2' → RECOGNIZED
- If
invoice_state == 'OPEN' and payment_state == 'UNPAID' → NOT_REQUIRED_UNPAID
- If
invoice_state == 'VOID' → UNKNOWN
- Otherwise →
UNKNOWN
2. Invoice Actions
Primary Accounting Action
- If any milestone has
recognition_status == 'MISSING_REVENUE_JOURNAL' → RECORD_REVENUE_MS2
- Else →
NO_ACCOUNTING_ACTION
Collection Action
- If any milestone has
invoice_state == 'OPEN' and payment_state == 'UNPAID' → MONITOR_UNPAID_NOT_DUE
- Else →
NO_COLLECTION_ACTION
Accounting Action Details
| Condition |
action |
milestone_id |
amount |
debit_account |
credit_account |
owner_queue |
RECORD_REVENUE_MS2 |
RECORD_REVENUE_MS2 |
MS2 |
MS2 amount |
DEFERRED_REVENUE |
IMPLEMENTATION_SERVICES_REVENUE |
ACCOUNTING |
NO_ACCOUNTING_ACTION |
NO_ACCOUNTING_ACTION |
NONE |
0.00 |
NONE |
NONE |
NONE |
Collection Task Details
| Condition |
action |
milestone_id |
amount |
due_date |
owner_queue |
contact_name |
MONITOR_UNPAID_NOT_DUE |
MONITOR_UNPAID_NOT_DUE |
First unpaid milestone (MS1→MS2→MS3) |
That milestone's amount |
That milestone's due_date |
ACCOUNT_MANAGEMENT |
Primary contact name |
NO_COLLECTION_ACTION |
NO_COLLECTION_ACTION |
NONE |
0.00 |
null |
NONE |
Primary contact name |
3. Event Actions
{
"event_id": "event.event_id",
"event_status": "event.status",
"voucher": {
"voucher_code": "voucher.code",
"voucher_status": "voucher.status",
"discount_amount": "voucher.discount_amount",
"max_uses": "voucher.max_uses"
}
}
Invite Action
event_status |
Registration exists for customer_id? |
invite_action |
CANCELLED |
Any |
NO_INVITE_ACTION |
COMPLETED |
Any |
NO_INVITE_ACTION |
UNKNOWN |
Any |
NO_INVITE_ACTION |
SCHEDULED |
Any |
SEND_BRIEFING_INVITE |
ACTIVE |
Yes |
VERIFY_INVITE_SENT |
ACTIVE |
No |
SEND_BRIEFING_INVITE |
(Registration existence: check event.registrations array for an entry whose customer_id matches opportunity.customer_id.)
Invite Task Details
invite_action |
action |
owner_queue |
SEND_BRIEFING_INVITE |
SEND_BRIEFING_INVITE |
ACCOUNT_MANAGEMENT |
VERIFY_INVITE_SENT |
VERIFY_INVITE_SENT |
ACCOUNT_MANAGEMENT |
NO_INVITE_ACTION |
NO_INVITE_ACTION |
NONE |
All invite tasks include: event_id, voucher_code, contact_name (primary contact), customer_id (opportunity.customer_id).
Output Conventions
- All monetary values are USD with exactly two decimal places (e.g.,
25000.00).
null is used for missing dates, not empty strings.
- Milestones array is always ordered ascending: MS1, MS2, MS3.
- Boolean field names ending in
_matches_ use strict equality.
- When enums require
NONE, use the string "NONE", not null.
1---2name: fewshot-attempt-03-143description: CRM Engagement Reconciliation & Action Generation Skill4---5# CRM Engagement Reconciliation & Action Generation Skill67## Purpose8Given a multi-source CRM/ERP prompt containing an opportunity, phase milestones, invoices, payments, event, voucher, and contacts, produce a structured JSON with three sections: `engagement_reconciliation`, `invoice_actions`, and `event_actions`.910## Input Parsing11Extract these entities from the prompt (they are always present):12- `as_of_date`: date string after "as of"13- `opportunity`: object with `opportunity_id`, `customer_id`, `customer_name`, `stage`, `amount`14- `phase_milestones`: array of objects with `milestone_id` (MS1, MS2, MS3), `amount`15- `invoices`: array of objects with `milestone_id`, `status` (PAID, OPEN, VOID), `due_date`, `amount`16- `payments`: array of objects with `milestone_id`, `payment_amount`17- `event`: object with `event_id`, `status` (SCHEDULED, ACTIVE, COMPLETED, CANCELLED)18- `voucher`: object with `code`, `status`, `discount_amount`, `max_uses`19- `contacts`: array of objects with `contact_name`, `customer_id`, `is_primary`20- `event.registrations`: array of objects with `customer_id`, `status`2122## 1. Engagement Reconciliation2324```json25{26 "as_of_date": "<as_of_date>",27 "opportunity_id": "opportunity.opportunity_id",28 "customer_id": "opportunity.customer_id",29 "customer_name": "opportunity.customer_name",30 "stage": "opportunity.stage",31 "won_amount": "opportunity.amount if stage == 'WON' else 0.00",32 "phase_total_amount": "sum(phase_milestones.amount)",33 "opportunity_matches_phase_total": "opportunity.amount == phase_total_amount",34 "total_paid_amount": "sum(payments.payment_amount)",35 "outstanding_balance": "phase_total_amount - total_paid_amount",36 "primary_contact": {37 "contact_name": "contacts.find(c => c.is_primary).contact_name",38 "customer_id": "contacts.find(c => c.is_primary).customer_id"39 }40}41```4243### Milestones Array44Order strictly ascending: MS1, MS2, MS3.4546For each milestone, locate its invoice and payment(s):4748| Field | Derivation |49|-------|-----------|50| `milestone_id` | From phase_milestones |51| `amount` | From phase_milestones |52| `invoice_state` | Invoice `status` if exists; else `UNKNOWN` |53| `payment_state` | If invoice is VOID → `UNKNOWN`. Else if total payments == invoice amount → `PAID`. Else if 0 < total payments < invoice amount → `PARTIAL`. Else → `UNPAID` |54| `paid_amount` | Sum of payments for this milestone (0 if none) |55| `due_date` | Invoice `due_date` if invoice exists and `invoice_state == 'OPEN'`, else `null` |56| `recognition_status` | See rules below |5758#### Recognition Status Rules591. If `invoice_state == 'PAID'` and `payment_state == 'PAID'` and `milestone_id == 'MS2'` → `MISSING_REVENUE_JOURNAL`602. If `invoice_state == 'PAID'` and `payment_state == 'PAID'` and `milestone_id != 'MS2'` → `RECOGNIZED`613. If `invoice_state == 'OPEN'` and `payment_state == 'UNPAID'` → `NOT_REQUIRED_UNPAID`624. If `invoice_state == 'VOID'` → `UNKNOWN`635. Otherwise → `UNKNOWN`6465## 2. Invoice Actions6667### Primary Accounting Action68- If **any** milestone has `recognition_status == 'MISSING_REVENUE_JOURNAL'` → `RECORD_REVENUE_MS2`69- Else → `NO_ACCOUNTING_ACTION`7071### Collection Action72- If **any** milestone has `invoice_state == 'OPEN'` and `payment_state == 'UNPAID'` → `MONITOR_UNPAID_NOT_DUE`73- Else → `NO_COLLECTION_ACTION`7475### Accounting Action Details76| Condition | `action` | `milestone_id` | `amount` | `debit_account` | `credit_account` | `owner_queue` |77|-----------|----------|----------------|----------|-----------------|------------------|---------------|78| `RECORD_REVENUE_MS2` | `RECORD_REVENUE_MS2` | `MS2` | MS2 amount | `DEFERRED_REVENUE` | `IMPLEMENTATION_SERVICES_REVENUE` | `ACCOUNTING` |79| `NO_ACCOUNTING_ACTION` | `NO_ACCOUNTING_ACTION` | `NONE` | `0.00` | `NONE` | `NONE` | `NONE` |8081### Collection Task Details82| Condition | `action` | `milestone_id` | `amount` | `due_date` | `owner_queue` | `contact_name` |83|-----------|----------|----------------|----------|------------|---------------|----------------|84| `MONITOR_UNPAID_NOT_DUE` | `MONITOR_UNPAID_NOT_DUE` | First unpaid milestone (MS1→MS2→MS3) | That milestone's amount | That milestone's `due_date` | `ACCOUNT_MANAGEMENT` | Primary contact name |85| `NO_COLLECTION_ACTION` | `NO_COLLECTION_ACTION` | `NONE` | `0.00` | `null` | `NONE` | Primary contact name |8687## 3. Event Actions8889```json90{91 "event_id": "event.event_id",92 "event_status": "event.status",93 "voucher": {94 "voucher_code": "voucher.code",95 "voucher_status": "voucher.status",96 "discount_amount": "voucher.discount_amount",97 "max_uses": "voucher.max_uses"98 }99}100```101102### Invite Action103| `event_status` | Registration exists for `customer_id`? | `invite_action` |104|----------------|----------------------------------------|-----------------|105| `CANCELLED` | Any | `NO_INVITE_ACTION` |106| `COMPLETED` | Any | `NO_INVITE_ACTION` |107| `UNKNOWN` | Any | `NO_INVITE_ACTION` |108| `SCHEDULED` | Any | `SEND_BRIEFING_INVITE` |109| `ACTIVE` | Yes | `VERIFY_INVITE_SENT` |110| `ACTIVE` | No | `SEND_BRIEFING_INVITE` |111112*(Registration existence: check `event.registrations` array for an entry whose `customer_id` matches `opportunity.customer_id`.)*113114### Invite Task Details115| `invite_action` | `action` | `owner_queue` |116|-----------------|----------|---------------|117| `SEND_BRIEFING_INVITE` | `SEND_BRIEFING_INVITE` | `ACCOUNT_MANAGEMENT` |118| `VERIFY_INVITE_SENT` | `VERIFY_INVITE_SENT` | `ACCOUNT_MANAGEMENT` |119| `NO_INVITE_ACTION` | `NO_INVITE_ACTION` | `NONE` |120121All invite tasks include: `event_id`, `voucher_code`, `contact_name` (primary contact), `customer_id` (opportunity.customer_id).122123## Output Conventions124- All monetary values are USD with exactly two decimal places (e.g., `25000.00`).125- `null` is used for missing dates, not empty strings.126- Milestones array is always ordered ascending: MS1, MS2, MS3.127- Boolean field names ending in `_matches_` use strict equality.128- When enums require `NONE`, use the string `"NONE"`, not `null`.