@synity/bitrix-skills 1.3.1 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cli.js +7 -1
- package/package.json +8 -1
- package/src/features/bx-crm/assets/SKILL.md +73 -36
- package/src/features/bx-crm/assets/commerce.md +56 -27
- package/src/features/bx-crm/assets/convert.md +70 -0
- package/src/features/bx-crm/assets/document.md +103 -0
- package/src/features/bx-crm/assets/flows.md +144 -0
- package/src/features/bx-crm/assets/onboard.md +91 -73
- package/src/features/bx-crm/assets/report.md +64 -33
- package/src/features/bx-crm/assets/research.md +62 -24
- package/src/features/bx-crm/assets/vn-norms.md +50 -0
- package/src/features/task-sync/assets/skill/SKILL.md +173 -0
|
@@ -1,32 +1,12 @@
|
|
|
1
1
|
# bx:crm — Onboard (Write SOP)
|
|
2
2
|
|
|
3
|
-
Covers: contact, company (VN), deal, lead creation.
|
|
3
|
+
Covers: contact, company (VN), deal, lead creation, and safe updates.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Phone Normalization (CRITICAL)
|
|
8
|
-
|
|
9
|
-
`upsertContact` auto-normalizes — **pass raw phone, never pre-format**.
|
|
10
|
-
|
|
11
|
-
| Input | Stored as |
|
|
12
|
-
|-------|-----------|
|
|
13
|
-
| `0977680550` | `+84977680550` |
|
|
14
|
-
| `0977 68 0550` | `+84977680550` |
|
|
15
|
-
| `84977680550` | `+84977680550` |
|
|
16
|
-
| `+84977680550` | `+84977680550` (idempotent) |
|
|
17
|
-
| `+6591234567` | `+6591234567` (non-VN: keep) |
|
|
5
|
+
## Phone + VN Normalization
|
|
18
6
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## HONORIFIC
|
|
7
|
+
See [vn-norms.md](./vn-norms.md) for phone `+84`, HONORIFIC, address, and MST rules.
|
|
22
8
|
|
|
23
|
-
|
|
24
|
-
|-------|-------|
|
|
25
|
-
| Anh / Mr | `Anh` |
|
|
26
|
-
| Chị / Ms / Mrs | `Chị` |
|
|
27
|
-
| Ông (senior male) | `Ông` |
|
|
28
|
-
| Bà (senior female) | `Bà` |
|
|
29
|
-
| Unknown | omit field |
|
|
9
|
+
`upsertContact` auto-normalizes phone numbers. Pass raw phone, never pre-format.
|
|
30
10
|
|
|
31
11
|
---
|
|
32
12
|
|
|
@@ -35,93 +15,131 @@ Covers: contact, company (VN), deal, lead creation.
|
|
|
35
15
|
```js
|
|
36
16
|
upsertContact({
|
|
37
17
|
name: "Nguyen Van A",
|
|
38
|
-
phone: "0977680550",
|
|
18
|
+
phone: "0977680550",
|
|
39
19
|
email: "a@example.com",
|
|
40
20
|
HONORIFIC: "Anh",
|
|
41
|
-
match: { phone }
|
|
21
|
+
match: { phone }
|
|
42
22
|
})
|
|
43
23
|
```
|
|
44
24
|
|
|
45
|
-
Minimum: `name` + (`phone` OR `email`) + `HONORIFIC`
|
|
46
|
-
|
|
47
|
-
---
|
|
25
|
+
Minimum: `name` + (`phone` OR `email`) + `HONORIFIC` if determinable. See [vn-norms.md](./vn-norms.md) for the HONORIFIC table.
|
|
48
26
|
|
|
49
27
|
## Company — VN Rule (CRITICAL)
|
|
50
28
|
|
|
51
|
-
**
|
|
29
|
+
**Never call `crm.company.add` directly for VN companies with MST.**
|
|
52
30
|
|
|
53
31
|
`upsertCompanyByTaxCode` auto-does:
|
|
54
|
-
1. VietQR lookup → legal name + address from GDT tax registry
|
|
55
|
-
2. Sets `RQ_INN` = `RQ_VAT_ID` = taxCode on requisite
|
|
56
|
-
3. Creates address entity
|
|
32
|
+
1. VietQR lookup → legal name + address from GDT tax registry.
|
|
33
|
+
2. Sets `RQ_INN` = `RQ_VAT_ID` = taxCode on requisite.
|
|
34
|
+
3. Creates address entity for document templates.
|
|
57
35
|
|
|
58
36
|
```js
|
|
59
37
|
upsertCompanyByTaxCode({
|
|
60
|
-
taxCode: "0315427733",
|
|
61
|
-
title: "Pegasus Realty",
|
|
38
|
+
taxCode: "0315427733",
|
|
39
|
+
title: "Pegasus Realty",
|
|
62
40
|
phone: "...",
|
|
63
|
-
email: "..."
|
|
41
|
+
email: "..."
|
|
64
42
|
})
|
|
65
43
|
```
|
|
66
44
|
|
|
67
|
-
**Verify
|
|
68
|
-
- `RQ_INN` populated
|
|
69
|
-
- `
|
|
70
|
-
-
|
|
71
|
-
- Address entity exists (ENTITY_TYPE_ID=8)
|
|
72
|
-
|
|
73
|
-
---
|
|
45
|
+
**Verify company:**
|
|
46
|
+
- `RQ_INN` and `RQ_VAT_ID` populated.
|
|
47
|
+
- `RQ_COMPANY_FULL_NAME` populated from registry.
|
|
48
|
+
- Address entity exists (`ENTITY_TYPE_ID=8`).
|
|
74
49
|
|
|
75
50
|
## Deal — Required Fields
|
|
76
51
|
|
|
77
|
-
Use `createDealWithParties` for atomic contact + company + deal
|
|
52
|
+
Use `createDealWithParties` for atomic contact + company + deal.
|
|
78
53
|
|
|
79
54
|
| Field | How to get | Example |
|
|
80
|
-
|
|
81
|
-
| `STAGE_ID` | `codemode.entityIds()`
|
|
82
|
-
| `CURRENCY_ID` |
|
|
83
|
-
| `OPPORTUNITY` |
|
|
84
|
-
| `SOURCE_ID` |
|
|
85
|
-
|
|
86
|
-
**SOURCE_ID mapping:**
|
|
87
|
-
|
|
88
|
-
| Context | Value |
|
|
89
|
-
|---------|-------|
|
|
90
|
-
| Event / offline meeting | `OTHER` |
|
|
91
|
-
| Website / form | `WEB` |
|
|
92
|
-
| Referral / partner | `PARTNER` |
|
|
93
|
-
| Cold call / outreach | `CALL` |
|
|
94
|
-
| Social / ad | `ADVERTISING` |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `STAGE_ID` | `codemode.entityIds()` | never hardcode |
|
|
57
|
+
| `CURRENCY_ID` | user input | `USD`, `VND` |
|
|
58
|
+
| `OPPORTUNITY` | user input number | `588` |
|
|
59
|
+
| `SOURCE_ID` | mapping below | `OTHER` |
|
|
60
|
+
|
|
61
|
+
**SOURCE_ID mapping:** Event/offline=`OTHER`, website/form=`WEB`, partner=`PARTNER`, call=`CALL`, social/ad=`ADVERTISING`.
|
|
95
62
|
|
|
96
63
|
**COMMENTS — BANT template:**
|
|
64
|
+
```text
|
|
65
|
+
Budget: <amount/range/unknown>
|
|
66
|
+
Authority: <decision maker>
|
|
67
|
+
Need: <problem or goal>
|
|
68
|
+
Timeline: <start date>
|
|
69
|
+
Notes: <context>
|
|
97
70
|
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
71
|
+
|
|
72
|
+
## Lead — Create + Products
|
|
73
|
+
|
|
74
|
+
Use lead when the prospect is unqualified or missing deal-level budget/timeline.
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
createLeadWithParties({
|
|
78
|
+
title: "Nguyen Van A - CRM training",
|
|
79
|
+
name: "Nguyen Van A",
|
|
80
|
+
phone: "0977680550",
|
|
81
|
+
email: "a@example.com",
|
|
82
|
+
companyTitle: "ABC Training",
|
|
83
|
+
sourceId: "WEB",
|
|
84
|
+
comments: "Need: CRM training\nTimeline: Q2"
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
setLeadProducts({
|
|
88
|
+
leadId,
|
|
89
|
+
products: [{ name: "Workshop", price: 5000000, quantity: 1, currency: "VND" }]
|
|
90
|
+
})
|
|
103
91
|
```
|
|
104
92
|
|
|
105
|
-
|
|
93
|
+
For qualification, convert via [convert.md](./convert.md).
|
|
94
|
+
|
|
95
|
+
## Update Existing Entity
|
|
96
|
+
|
|
97
|
+
1. Search before update: `codemode.search({ keywords, entities: ["contact","company","deal","lead"], intent: "read" })`.
|
|
98
|
+
2. Read current entity by ID and validate target ownership.
|
|
99
|
+
3. Discover allowed fields with `crm.{entity}.fields` when field names are uncertain.
|
|
100
|
+
4. Show before/after field diff and confirm every update, even for a single match.
|
|
101
|
+
5. Send only changed fields.
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
// MCP gap fallback when no typed update helper exists.
|
|
105
|
+
codemode.request({
|
|
106
|
+
method: "POST",
|
|
107
|
+
path: "/crm.deal.update/",
|
|
108
|
+
body: { id: dealId, fields: { OPPORTUNITY: 9000000, ASSIGNED_BY_ID: 12 } }
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Skip this search rule for `upsertContact` and `upsertCompanyByTaxCode`; they dedup internally.
|
|
113
|
+
|
|
114
|
+
## Custom Fields & Routing
|
|
115
|
+
|
|
116
|
+
- `UF_CRM_*`: discover with `codemode.entityIds()` or `crm.{entity}.fields`.
|
|
117
|
+
- `categoryId`: route deal to the correct pipeline.
|
|
118
|
+
- `assignedById`: responsible user. Resolve user IDs before writing.
|
|
119
|
+
- Do not echo full custom-field PII unless user explicitly asks.
|
|
120
|
+
|
|
121
|
+
## Idempotency
|
|
122
|
+
|
|
123
|
+
Omit `idempotencyKey`. **Verified 2026-05-15:** D1 table `idempotency_keys` missing; passing the key throws `SQLITE_ERROR` before write. Re-verify when MCP version changes. Escalation: MCP team.
|
|
106
124
|
|
|
107
125
|
---
|
|
108
126
|
|
|
109
127
|
## Verify Checklist (mandatory after every write op)
|
|
110
128
|
|
|
111
|
-
- [ ] Contact:
|
|
112
|
-
- [ ] Company: `RQ_VAT_ID` populated, address entity exists
|
|
113
|
-
- [ ] Deal: `STAGE_ID` set, `OPPORTUNITY > 0`, payer linked
|
|
114
|
-
- [ ] Lead: `SOURCE_ID` set, phone/email present
|
|
129
|
+
- [ ] Contact: VN phone stored with `+84`; HONORIFIC matches [vn-norms.md](./vn-norms.md).
|
|
130
|
+
- [ ] Company: `RQ_VAT_ID` populated, address entity exists, MST format valid.
|
|
131
|
+
- [ ] Deal: `STAGE_ID` set, `OPPORTUNITY > 0`, payer linked.
|
|
132
|
+
- [ ] Lead: `SOURCE_ID` set, phone/email present, products set when user requested products.
|
|
133
|
+
- [ ] Update: only intended fields changed.
|
|
115
134
|
|
|
116
135
|
---
|
|
117
136
|
|
|
118
137
|
## Known Pitfalls
|
|
119
138
|
|
|
120
139
|
| Mistake | Symptom | Fix |
|
|
121
|
-
|
|
122
|
-
| Used `bx
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| Used `bx:task` for CRM | Creates project task, not CRM record | Use `/bx:crm` |
|
|
123
142
|
| `crm.contact.add` directly | No dedup, phone not normalized | `upsertContact` |
|
|
124
|
-
| `crm.company.add` directly | Requisite missing
|
|
143
|
+
| `crm.company.add` directly | Requisite missing tax fields + address | `upsertCompanyByTaxCode` |
|
|
125
144
|
| Passed `idempotencyKey` | D1 table error at runtime | Omit the field |
|
|
126
|
-
| Pre-formatted phone `+84977...` | Helper double-normalizes (idempotent, fine) | Pass raw anyway |
|
|
127
145
|
| Skipped verify step | Bugs discovered sessions later | Always verify |
|
|
@@ -4,10 +4,16 @@ Covers: dealForecast, stuckInStage, overdueActivities, arReport, teamReport.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## Timezone
|
|
8
|
+
|
|
9
|
+
MCP may return UTC ISO timestamps. Convert display times to Asia/Saigon (UTC+7). Date-only summaries use the Asia/Saigon date.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
7
13
|
## Helper Selection
|
|
8
14
|
|
|
9
15
|
| Report | Helper | Typical Frequency |
|
|
10
|
-
|
|
16
|
+
|---|---|---|
|
|
11
17
|
| Pipeline forecast month/quarter | `dealForecast` | Weekly |
|
|
12
18
|
| Deals stuck too long in stage | `stuckInStage` | Weekly |
|
|
13
19
|
| Overdue activities | `overdueActivities` | Daily |
|
|
@@ -19,61 +25,71 @@ Covers: dealForecast, stuckInStage, overdueActivities, arReport, teamReport.
|
|
|
19
25
|
|
|
20
26
|
## Output Template — dealForecast
|
|
21
27
|
|
|
22
|
-
```
|
|
23
|
-
| Month
|
|
24
|
-
|
|
25
|
-
| T5/26
|
|
28
|
+
```text
|
|
29
|
+
| Month | Predicted | Won | At Risk | # Deals |
|
|
30
|
+
|---|---:|---:|---:|---:|
|
|
31
|
+
| T5/26 | $X | $Y | $Z | N |
|
|
26
32
|
```
|
|
27
33
|
|
|
28
|
-
Red flags: deals in same stage > 30 days with no activity
|
|
34
|
+
Red flags: deals in same stage > 30 days with no activity.
|
|
29
35
|
|
|
30
36
|
---
|
|
31
37
|
|
|
32
38
|
## Output Template — stuckInStage
|
|
33
39
|
|
|
34
|
-
Group by pipeline stage
|
|
40
|
+
Group by pipeline stage. Sort by days stuck DESC within each group.
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
```
|
|
42
|
+
```text
|
|
38
43
|
Stage: [Stage Name]
|
|
39
|
-
|
|
40
|
-
2. ...
|
|
44
|
+
1. [dealId] "[title]" — [owner] — [N] days — last activity: [date]
|
|
41
45
|
```
|
|
42
46
|
|
|
43
47
|
---
|
|
44
48
|
|
|
45
49
|
## Output Template — overdueActivities
|
|
46
50
|
|
|
47
|
-
```
|
|
51
|
+
```text
|
|
48
52
|
| Deal/Contact | Activity | Assigned | Due | Days Overdue |
|
|
49
|
-
|
|
50
|
-
| ...
|
|
53
|
+
|---|---|---|---|---:|
|
|
54
|
+
| ... | ... | ... | ... | N |
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
Sort by days overdue DESC. Group by assignee if
|
|
57
|
+
Sort by days overdue DESC. Group by assignee if more than 10 items.
|
|
54
58
|
|
|
55
59
|
---
|
|
56
60
|
|
|
57
|
-
## Output Template — arReport
|
|
61
|
+
## Output Template — arReport
|
|
58
62
|
|
|
63
|
+
```text
|
|
64
|
+
| Bucket | Amount | % of Total | # Invoices |
|
|
65
|
+
|---|---:|---:|---:|
|
|
66
|
+
| Current | $X | X% | N |
|
|
67
|
+
| 1-30d | $X | X% | N |
|
|
68
|
+
| 31-60d | $X | X% | N |
|
|
69
|
+
| 61-90d | $X | X% | N |
|
|
70
|
+
| 90d+ | $X | X% | N |
|
|
59
71
|
```
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
|
|
73
|
+
For 90d+ bucket: list each debtor below the table.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Output Template — teamReport
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
| Rep | # Deals | Total Value | Avg Response | Overdue |
|
|
81
|
+
|---|---:|---:|---:|---:|
|
|
82
|
+
| ... | N | $X | Nh | N |
|
|
67
83
|
```
|
|
68
84
|
|
|
69
|
-
|
|
85
|
+
Sort by Total Value DESC. Flag response time and overdue counts using the thresholds below.
|
|
70
86
|
|
|
71
87
|
---
|
|
72
88
|
|
|
73
89
|
## Interpretation Thresholds
|
|
74
90
|
|
|
75
|
-
| Metric | Yellow
|
|
76
|
-
|
|
91
|
+
| Metric | Yellow | Red | Action |
|
|
92
|
+
|---|---|---|---|
|
|
77
93
|
| stuckInStage | > 14 days | > 30 days | Assign follow-up activity |
|
|
78
94
|
| AR 90d+ bucket | > 10% total AR | > 20% total AR | Escalate to account manager |
|
|
79
95
|
| teamReport response_time | > 12h | > 24h | Flag for coaching |
|
|
@@ -84,15 +100,30 @@ For 90d+ bucket: list each debtor individually below the table.
|
|
|
84
100
|
## Common Report Calls
|
|
85
101
|
|
|
86
102
|
```js
|
|
87
|
-
// Weekly pipeline review
|
|
88
103
|
dealForecast({ period: "month", includeAtRisk: true })
|
|
104
|
+
stuckInStage({ minDays: 14, pipelineId: null })
|
|
105
|
+
overdueActivities({ assignedTo: null })
|
|
106
|
+
arReport({ groupByBucket: true, listDebtors90Plus: true })
|
|
107
|
+
teamReport({ period: "month", includeResponseTime: true })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
89
111
|
|
|
90
|
-
|
|
91
|
-
stuckInStage({ minDays: 14, pipelineId: null }) // null = all pipelines
|
|
112
|
+
## Example Dialog — dealForecast
|
|
92
113
|
|
|
93
|
-
|
|
94
|
-
overdueActivities({ assignedTo: null }) // null = all reps
|
|
114
|
+
User: "review pipeline tuần này"
|
|
95
115
|
|
|
96
|
-
|
|
97
|
-
|
|
116
|
+
LLM behavior: call `dealForecast({ period: "month", includeAtRisk: true })`, convert dates to Asia/Saigon, then summarize risks.
|
|
117
|
+
|
|
118
|
+
Sample output:
|
|
119
|
+
```text
|
|
120
|
+
Pipeline T5/26: predicted 1.2B VND, won 420M, at risk 180M across 4 deals.
|
|
121
|
+
Red flag: 3 deals stuck >30 days with no recent activity.
|
|
122
|
+
Next action: assign follow-up activities today for all red-flag deals.
|
|
98
123
|
```
|
|
124
|
+
|
|
125
|
+
## Verify Checklist
|
|
126
|
+
|
|
127
|
+
- [ ] Date/time displayed in Asia/Saigon.
|
|
128
|
+
- [ ] Totals reconcile with helper output.
|
|
129
|
+
- [ ] Red/yellow flags use the threshold table above.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# bx:crm — Research (Analysis Protocol)
|
|
2
2
|
|
|
3
|
-
Covers: customer360, contactSignals, dealSignals, customerJourney, chatHistory.
|
|
3
|
+
Covers: customer360, contactSignals, dealSignals, customerJourney, chatHistory, nextAction, behaviorWindow.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
## Helper Selection
|
|
8
8
|
|
|
9
9
|
| Need | Helper | Returns |
|
|
10
|
-
|
|
10
|
+
|---|---|---|
|
|
11
11
|
| Full customer overview | `customer360` | profile + deals + comms + invoices |
|
|
12
12
|
| Quick risk signals | `contactSignals` | 8 sections raw context |
|
|
13
13
|
| Deal health check | `dealSignals` | pipeline + activities + AR |
|
|
@@ -20,31 +20,47 @@ Covers: customer360, contactSignals, dealSignals, customerJourney, chatHistory.
|
|
|
20
20
|
|
|
21
21
|
## Output Protocol — customer360
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
4. **Pending actions** — overdue activities, unpaid invoices
|
|
29
|
-
5. **Recommendation** — next action in 1 concrete sentence
|
|
23
|
+
1. **Profile** — name, company, lifecycle stage, assigned rep.
|
|
24
|
+
2. **Deals** — active deals (stage, value, days in stage).
|
|
25
|
+
3. **Last contact** — channel + date + 1-line summary.
|
|
26
|
+
4. **Pending actions** — overdue activities, unpaid invoices.
|
|
27
|
+
5. **Recommendation** — next action in 1 concrete sentence.
|
|
30
28
|
|
|
31
29
|
---
|
|
32
30
|
|
|
33
31
|
## Output Protocol — contactSignals / dealSignals
|
|
34
32
|
|
|
35
|
-
1. **Risk level** — `HIGH` / `MEDIUM` / `LOW` + 1-line reason
|
|
36
|
-
2. **Top 3 signals** —
|
|
37
|
-
3. **Next action** — specific, with suggested deadline
|
|
38
|
-
4. **Reference** — contactId / dealId for traceability
|
|
33
|
+
1. **Risk level** — `HIGH` / `MEDIUM` / `LOW` + 1-line reason.
|
|
34
|
+
2. **Top 3 signals** — most critical first.
|
|
35
|
+
3. **Next action** — specific, with suggested deadline.
|
|
36
|
+
4. **Reference** — contactId / dealId for traceability.
|
|
39
37
|
|
|
40
38
|
---
|
|
41
39
|
|
|
42
40
|
## Output Protocol — customerJourney
|
|
43
41
|
|
|
44
|
-
1. **Timeline** —
|
|
45
|
-
2. **Turning points** — when
|
|
46
|
-
3. **Sentiment trend** — Positive / Neutral / Negative
|
|
47
|
-
4. **Gap analysis** — longest silence period
|
|
42
|
+
1. **Timeline** — stage changes + key communications.
|
|
43
|
+
2. **Turning points** — when deal got stuck or advanced.
|
|
44
|
+
3. **Sentiment trend** — Positive / Neutral / Negative.
|
|
45
|
+
4. **Gap analysis** — longest silence period in days.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Output Protocol — nextAction
|
|
50
|
+
|
|
51
|
+
1. **Action** — one imperative sentence.
|
|
52
|
+
2. **Reasoning** — 1-2 bullets explaining why now.
|
|
53
|
+
3. **Deadline** — exact date/time when possible.
|
|
54
|
+
4. **Reference** — source contactId/dealId.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Output Protocol — behaviorWindow
|
|
59
|
+
|
|
60
|
+
1. **Window** — date range and interaction count.
|
|
61
|
+
2. **Trend** — Increasing / Stable / Declining.
|
|
62
|
+
3. **Last 3 events** — date + channel + 1-line summary.
|
|
63
|
+
4. **Recommendation** — next action tied to the trend.
|
|
48
64
|
|
|
49
65
|
---
|
|
50
66
|
|
|
@@ -52,20 +68,42 @@ Always present in this order:
|
|
|
52
68
|
|
|
53
69
|
```js
|
|
54
70
|
chatHistory({
|
|
55
|
-
entityType: "contact",
|
|
71
|
+
entityType: "contact",
|
|
56
72
|
entityId: 123,
|
|
57
|
-
channels: ["chat", "zalo", "email"],
|
|
73
|
+
channels: ["chat", "zalo", "email"],
|
|
58
74
|
limit: 50
|
|
59
75
|
})
|
|
60
76
|
```
|
|
61
77
|
|
|
62
|
-
Present as: `[date] [channel]: <1-line summary
|
|
78
|
+
Present as: `[date] [channel]: <1-line summary>`.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Example Dialog — customer360
|
|
83
|
+
|
|
84
|
+
User: "phân tích contact #123 trước cuộc họp 2pm"
|
|
85
|
+
|
|
86
|
+
LLM behavior: call `customer360({ contactId: 123 })`, then follow the customer360 output protocol.
|
|
87
|
+
|
|
88
|
+
Sample output:
|
|
89
|
+
```text
|
|
90
|
+
Profile: Nguyen Van A, ABC Training, assigned to Linh.
|
|
91
|
+
Deals: 2 active; main deal 80M VND stuck 18 days.
|
|
92
|
+
Last contact: Zalo 2026-05-14, asked for contract terms.
|
|
93
|
+
Pending actions: proposal follow-up overdue 1 day.
|
|
94
|
+
Recommendation: Call before 14:00 and confirm decision timeline.
|
|
95
|
+
```
|
|
63
96
|
|
|
64
97
|
---
|
|
65
98
|
|
|
66
99
|
## Pre-Research Checklist
|
|
67
100
|
|
|
68
|
-
|
|
69
|
-
- [ ]
|
|
70
|
-
- [ ]
|
|
71
|
-
|
|
101
|
+
- [ ] Have contactId or dealId ready.
|
|
102
|
+
- [ ] Know the goal: overview, risk check, history, next action, or behavior.
|
|
103
|
+
- [ ] Use `codemode.search({ intent: "read" })` if unsure which helper fits.
|
|
104
|
+
|
|
105
|
+
## Verify Checklist
|
|
106
|
+
|
|
107
|
+
- [ ] Output cites source contactId/dealId.
|
|
108
|
+
- [ ] Recommendations are based on returned signals, not invented context.
|
|
109
|
+
- [ ] PII is summarized only as needed for the user's stated purpose.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# bx:crm — VN Normalization Reference
|
|
2
|
+
|
|
3
|
+
Vietnam-specific normalization for phone, honorific, address, and MST.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Phone Normalization
|
|
8
|
+
|
|
9
|
+
`upsertContact` auto-normalizes. Pass raw user input.
|
|
10
|
+
|
|
11
|
+
| Input | Stored as |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `0977680550` | `+84977680550` |
|
|
14
|
+
| `0977 68 0550` | `+84977680550` |
|
|
15
|
+
| `84977680550` | `+84977680550` |
|
|
16
|
+
| `+84977680550` | `+84977680550` |
|
|
17
|
+
| `+6591234567` | `+6591234567` |
|
|
18
|
+
|
|
19
|
+
Rules: strip spaces/dashes; VN leading `0` becomes `+84`; existing country code stays; non-VN numbers stay unchanged.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## HONORIFIC
|
|
24
|
+
|
|
25
|
+
| Input | Value |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Anh / Mr | `Anh` |
|
|
28
|
+
| Chị / Ms / Mrs | `Chị` |
|
|
29
|
+
| Em | `Em` |
|
|
30
|
+
| Cô | `Cô` |
|
|
31
|
+
| Chú | `Chú` |
|
|
32
|
+
| Bác | `Bác` |
|
|
33
|
+
| Ông (senior male) | `Ông` |
|
|
34
|
+
| Bà (senior female) | `Bà` |
|
|
35
|
+
| Unknown | omit field |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Address + MST
|
|
40
|
+
|
|
41
|
+
VN legal address should preserve: street, ward, district, province/city, country. Do not invent missing parts. Prefer GDT/VietQR registry data from `upsertCompanyByTaxCode`.
|
|
42
|
+
|
|
43
|
+
- Company MST: usually 10 digits.
|
|
44
|
+
- Branch MST: 13 digits, often `10-digit-parent-3-digit-branch`.
|
|
45
|
+
- Store in Bitrix requisites as `RQ_INN` and `RQ_VAT_ID`.
|
|
46
|
+
|
|
47
|
+
## Verify Checklist
|
|
48
|
+
|
|
49
|
+
- [ ] Raw phone was passed to helper, not preformatted.
|
|
50
|
+
- [ ] Missing address/MST parts were not invented.
|