n8n-nodes-didar-crm 0.0.16 → 0.0.18
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/README.md +97 -53
- package/dist/lib/loadOptions.d.ts +4 -1
- package/dist/lib/loadOptions.js +10 -1
- package/dist/nodes/DidarCrm.node.js +6 -0
- package/dist/nodes/activity/create.operation.js +94 -6
- package/dist/nodes/activity/update.operation.js +93 -6
- package/dist/nodes/case/create.operation.js +98 -2
- package/dist/nodes/case/create.properties.js +3 -2
- package/dist/nodes/case/update.operation.js +98 -2
- package/dist/nodes/case/update.properties.js +3 -2
- package/dist/nodes/deal/index.d.ts +2 -0
- package/dist/nodes/deal/index.js +5 -1
- package/dist/nodes/deal/search.operation.d.ts +2 -0
- package/dist/nodes/deal/search.operation.js +199 -0
- package/dist/nodes/deal/search.properties.d.ts +2 -0
- package/dist/nodes/deal/search.properties.js +222 -0
- package/dist/nodes/note/create.operation.js +90 -2
- package/dist/nodes/note/update.operation.js +90 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,35 +1,60 @@
|
|
|
1
1
|
# n8n-nodes-didar-crm
|
|
2
2
|
|
|
3
|
-
Custom **n8n nodes** for integrating with **Didar CRM**.
|
|
3
|
+
Custom **n8n nodes** for integrating with **Didar CRM**.
|
|
4
4
|
This package provides actions and triggers to automate workflows with your Didar CRM account.
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## ✨ Features
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
### Trigger
|
|
11
|
+
- **Didar CRM Trigger** → receive webhook events for entity changes, updates, deletions.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
### Deal
|
|
14
|
+
- Create a deal
|
|
15
|
+
- Update a deal (by Id)
|
|
16
|
+
- Get deal details (by Id)
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
### Person (Contact)
|
|
19
|
+
- Create a person
|
|
20
|
+
- Update a person (by Id)
|
|
21
|
+
- Get person details (by Id)
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
### Company
|
|
24
|
+
- Create a company
|
|
25
|
+
- Update a company (by Id)
|
|
26
|
+
- Get company details (by Id)
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
### Activity
|
|
29
|
+
- Create an activity
|
|
30
|
+
- Update an activity (by Id)
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
### Note
|
|
33
|
+
- Create a note
|
|
34
|
+
- Update a note (by Id)
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
### Case (Card)
|
|
37
|
+
- Create a case
|
|
38
|
+
- Update a case (by Id)
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
### Product
|
|
41
|
+
- Create a product
|
|
42
|
+
- Update a product (by Id)
|
|
43
|
+
- Get products by list of codes
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🧩 Common Features
|
|
48
|
+
|
|
49
|
+
- Dropdown selectors for Pipelines, Owners (Users), Activity Types, Product Categories, etc.
|
|
50
|
+
- Manual ID input option for each dropdown field
|
|
51
|
+
- Additional fields for advanced properties:
|
|
52
|
+
- Visibility Type
|
|
53
|
+
- Custom Fields (JSON)
|
|
54
|
+
- Multi-value lists (phones, emails, websites, addresses, bank accounts, labels, users)
|
|
55
|
+
- Correct handling of defaults (zero GUIDs, booleans, etc.)
|
|
56
|
+
- Webhook trigger enforces **POST** requests only
|
|
57
|
+
- Consistent **Owner selection mechanism** (select from list or manual ID) across all resources
|
|
33
58
|
|
|
34
59
|
---
|
|
35
60
|
|
|
@@ -39,41 +64,61 @@ This package provides actions and triggers to automate workflows with your Didar
|
|
|
39
64
|
npm install n8n-nodes-didar-crm
|
|
40
65
|
```
|
|
41
66
|
|
|
42
|
-
|
|
67
|
+
Place the module inside your n8n custom directory (usually `~/.n8n/custom/`) and restart n8n.
|
|
43
68
|
|
|
44
69
|
---
|
|
45
70
|
|
|
46
71
|
## ⚡ Usage
|
|
47
72
|
|
|
48
73
|
### 1. Trigger node
|
|
49
|
-
|
|
50
74
|
Add the **Didar CRM Trigger** node to your workflow.
|
|
75
|
+
- Exposes a webhook URL in n8n
|
|
76
|
+
- Accepts **POST** requests only
|
|
77
|
+
- Returns structured payload:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"data": { ... },
|
|
82
|
+
"changes": [ ... ],
|
|
83
|
+
"meta": { ... },
|
|
84
|
+
"raw": { ... },
|
|
85
|
+
"receivedAt": "timestamp"
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. Resource operations
|
|
90
|
+
|
|
91
|
+
#### Deal
|
|
92
|
+
- **Create** → required fields: `Title`, `OwnerId`, `PipelineId`, `PipelineStageId`
|
|
93
|
+
- **Update** → same as create + `Id`
|
|
94
|
+
- **Get** → requires `Id`
|
|
51
95
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
96
|
+
#### Person (Contact)
|
|
97
|
+
- **Create** → required fields: `LastName`, `OwnerId`
|
|
98
|
+
- **Update** → same as create + `Id`
|
|
99
|
+
- **Get** → requires `Id`
|
|
55
100
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"meta": { ... },
|
|
61
|
-
"raw": { ... },
|
|
62
|
-
"receivedAt": "timestamp"
|
|
63
|
-
}
|
|
64
|
-
```
|
|
101
|
+
#### Company
|
|
102
|
+
- **Create** → required fields: `Name`, `OwnerId`
|
|
103
|
+
- **Update** → same as create + `Id`
|
|
104
|
+
- **Get** → requires `Id`
|
|
65
105
|
|
|
66
|
-
|
|
106
|
+
#### Activity
|
|
107
|
+
- **Create Activity** → required fields: `Title`, `OwnerId`, `ActivityTypeId`
|
|
108
|
+
- **Update Activity** → same as create + `Id`
|
|
67
109
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
110
|
+
#### Note
|
|
111
|
+
- **Create Note** → required fields: `ResultNote`, `OwnerId`
|
|
112
|
+
- **Update Note** → same as create + `Id`
|
|
71
113
|
|
|
72
|
-
|
|
114
|
+
#### Case (Card)
|
|
115
|
+
- **Create Case** → required fields: `Title`, `OwnerId`, `PipelineStageId`, `Status`
|
|
116
|
+
- **Update Case** → same as create + `Id`
|
|
73
117
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
118
|
+
#### Product
|
|
119
|
+
- **Create Product** → required fields: `Title`
|
|
120
|
+
- **Update Product** → same as create + `Id`
|
|
121
|
+
- **Get Products by Codes** → required field: `Code[]` (one or more codes)
|
|
77
122
|
|
|
78
123
|
---
|
|
79
124
|
|
|
@@ -81,8 +126,8 @@ Add the **Didar CRM Trigger** node to your workflow.
|
|
|
81
126
|
|
|
82
127
|
A new credential type **Didar API** is provided.
|
|
83
128
|
|
|
84
|
-
|
|
85
|
-
|
|
129
|
+
- Set your **API Key** and (if required) authentication cookies.
|
|
130
|
+
- Shared across all nodes.
|
|
86
131
|
|
|
87
132
|
---
|
|
88
133
|
|
|
@@ -94,19 +139,18 @@ n8n-nodes-didar-crm/
|
|
|
94
139
|
│── README.md
|
|
95
140
|
│── LICENSE
|
|
96
141
|
│── nodes/
|
|
97
|
-
│ ├── DidarCrm.node.ts # Main entry for
|
|
142
|
+
│ ├── DidarCrm.node.ts # Main entry for resources & operations
|
|
98
143
|
│ ├── DidarCrmTrigger.node.ts # Trigger node
|
|
99
|
-
│
|
|
100
|
-
│
|
|
101
|
-
│
|
|
102
|
-
│
|
|
103
|
-
│
|
|
104
|
-
│
|
|
105
|
-
│
|
|
106
|
-
│ └── get.operation.ts
|
|
144
|
+
│ ├── deal/ # Deal operations
|
|
145
|
+
│ ├── person/ # Person operations
|
|
146
|
+
│ ├── company/ # Company operations
|
|
147
|
+
│ ├── activity/ # Activity operations
|
|
148
|
+
│ ├── note/ # Note operations
|
|
149
|
+
│ ├── case/ # Case operations
|
|
150
|
+
│ └── product/ # Product operations
|
|
107
151
|
│── lib/
|
|
108
|
-
|
|
109
|
-
|
|
152
|
+
│ ├── http.ts # Request helper
|
|
153
|
+
│ └── loadOptions.ts # Dropdown population
|
|
110
154
|
```
|
|
111
155
|
|
|
112
156
|
---
|
|
@@ -124,7 +168,7 @@ n8n-nodes-didar-crm/
|
|
|
124
168
|
```bash
|
|
125
169
|
npm run build
|
|
126
170
|
```
|
|
127
|
-
4. Link into your n8n custom directory and restart
|
|
171
|
+
4. Link into your n8n custom directory and restart:
|
|
128
172
|
|
|
129
173
|
```bash
|
|
130
174
|
npm link
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
|
|
2
2
|
export declare function getPipelines(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
3
|
-
export declare function getStagesForPipeline(this: ILoadOptionsFunctions): Promise<
|
|
3
|
+
export declare function getStagesForPipeline(this: ILoadOptionsFunctions): Promise<{
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}[]>;
|
|
4
7
|
export declare function getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
5
8
|
export declare function getActivityTypes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
6
9
|
export declare function getProductCategories(this: ILoadOptionsFunctions): Promise<any>;
|
package/dist/lib/loadOptions.js
CHANGED
|
@@ -29,7 +29,15 @@ async function getPipelines() {
|
|
|
29
29
|
}
|
|
30
30
|
async function getStagesForPipeline() {
|
|
31
31
|
var _a, _b;
|
|
32
|
-
|
|
32
|
+
// اول سعی کن از نام استاندارد search/ایجاد جدید بخوانی:
|
|
33
|
+
let pipelineId = this.getCurrentNodeParameter('pipelineId') || '';
|
|
34
|
+
// فالبک برای نودهای قدیمی/جداگانه که شاید اسم دیگری داشته باشند:
|
|
35
|
+
if (!pipelineId) {
|
|
36
|
+
try {
|
|
37
|
+
pipelineId = this.getCurrentNodeParameter('PipelineIdSelect') || '';
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
}
|
|
33
41
|
if (!pipelineId)
|
|
34
42
|
return [];
|
|
35
43
|
const creds = await this.getCredentials('didarApi');
|
|
@@ -44,6 +52,7 @@ async function getStagesForPipeline() {
|
|
|
44
52
|
url,
|
|
45
53
|
json: true,
|
|
46
54
|
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: {}, // بعضی سرورها POST بدون body را دوست ندارند
|
|
47
56
|
};
|
|
48
57
|
if (useCookie && cookie)
|
|
49
58
|
options.headers.Cookie = cookie;
|
|
@@ -98,6 +98,7 @@ class DidarCrm {
|
|
|
98
98
|
{ name: 'Create', value: 'create', action: 'Create a deal' },
|
|
99
99
|
{ name: 'Update', value: 'update', action: 'Update a deal by Id' },
|
|
100
100
|
{ name: 'Get', value: 'get', action: 'Get a deal by Id' },
|
|
101
|
+
{ name: 'Search', value: 'search', action: 'Search deals' },
|
|
101
102
|
],
|
|
102
103
|
default: 'create',
|
|
103
104
|
description: 'Select the action to perform on the selected resource.',
|
|
@@ -181,6 +182,7 @@ class DidarCrm {
|
|
|
181
182
|
...deal_1.dealCreateProperties,
|
|
182
183
|
...deal_1.dealUpdateProperties,
|
|
183
184
|
...deal_1.dealGetProperties,
|
|
185
|
+
...deal_1.dealSearchProperties,
|
|
184
186
|
// Person properties (imported)
|
|
185
187
|
...person_1.personCreateProperties,
|
|
186
188
|
...person_1.personUpdateProperties,
|
|
@@ -224,6 +226,10 @@ class DidarCrm {
|
|
|
224
226
|
await DealOps.dealGet.call(this, i, returnData);
|
|
225
227
|
continue;
|
|
226
228
|
}
|
|
229
|
+
if (operation === 'search') {
|
|
230
|
+
await DealOps.dealSearch.call(this, i, returnData);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
227
233
|
}
|
|
228
234
|
if (resource === 'person') {
|
|
229
235
|
if (operation === 'create') {
|
|
@@ -35,12 +35,100 @@ async function activityCreate(i, returnData) {
|
|
|
35
35
|
const dealId = (_h = add.DealId) !== null && _h !== void 0 ? _h : '00000000-0000-0000-0000-000000000000';
|
|
36
36
|
const caseId = (_j = add.CaseId) !== null && _j !== void 0 ? _j : '00000000-0000-0000-0000-000000000000';
|
|
37
37
|
// ContactIds: string[]
|
|
38
|
-
let contactIds;
|
|
39
|
-
const cids = add.ContactIds;
|
|
40
|
-
if (Array.isArray(cids))
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
// let contactIds: string[] | undefined;
|
|
39
|
+
// const cids = add.ContactIds as string[] | string | undefined;
|
|
40
|
+
// if (Array.isArray(cids)) contactIds = cids.filter(Boolean);
|
|
41
|
+
// else if (typeof cids === 'string' && cids) contactIds = [cids];
|
|
42
|
+
// ---- Robust ContactIds parsing: string | string[] | JSON array | CSV/newlines | [Array: [...]] ----
|
|
43
|
+
const rawContactIds = add.ContactIds;
|
|
44
|
+
const parseIdList = (input) => {
|
|
45
|
+
const out = [];
|
|
46
|
+
const pushFromString = (raw) => {
|
|
47
|
+
if (!raw)
|
|
48
|
+
return;
|
|
49
|
+
const s = raw.trim();
|
|
50
|
+
if (!s)
|
|
51
|
+
return;
|
|
52
|
+
// Handle n8n visualization: [Array: [...]]
|
|
53
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
54
|
+
if (arrayLabel) {
|
|
55
|
+
try {
|
|
56
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
57
|
+
recur(arr);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
catch { /* fall through */ }
|
|
61
|
+
}
|
|
62
|
+
// JSON array
|
|
63
|
+
if (/^\s*\[/.test(s)) {
|
|
64
|
+
try {
|
|
65
|
+
const arr = JSON.parse(s);
|
|
66
|
+
recur(arr);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
throw new Error('Invalid JSON in "Contact IDs". Expected a JSON array of strings.');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Otherwise CSV/newlines/semicolon
|
|
74
|
+
s.split(/[\n,;]+/)
|
|
75
|
+
.map((t) => t.trim())
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.forEach((t) => out.push(t));
|
|
78
|
+
};
|
|
79
|
+
const recur = (val) => {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
if (val == null)
|
|
82
|
+
return;
|
|
83
|
+
if (Array.isArray(val)) {
|
|
84
|
+
val.forEach(recur);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
switch (typeof val) {
|
|
88
|
+
case 'string':
|
|
89
|
+
pushFromString(val);
|
|
90
|
+
return;
|
|
91
|
+
case 'number':
|
|
92
|
+
case 'boolean':
|
|
93
|
+
out.push(String(val));
|
|
94
|
+
return;
|
|
95
|
+
case 'object': {
|
|
96
|
+
const obj = val;
|
|
97
|
+
// Map/Set support
|
|
98
|
+
if (val instanceof Set) {
|
|
99
|
+
recur(Array.from(val));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (val instanceof Map) {
|
|
103
|
+
recur(Array.from(val.values()));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Common wrappers
|
|
107
|
+
if (obj === null || obj === void 0 ? void 0 : obj.values) {
|
|
108
|
+
recur(obj.values);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (obj === null || obj === void 0 ? void 0 : obj.Value) {
|
|
112
|
+
recur(obj.Value);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Fallback: try toString
|
|
116
|
+
const s = ((_b = (_a = obj === null || obj === void 0 ? void 0 : obj.toString) === null || _a === void 0 ? void 0 : _a.call(obj)) !== null && _b !== void 0 ? _b : '').toString();
|
|
117
|
+
if (s)
|
|
118
|
+
pushFromString(s);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
default:
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
recur(input);
|
|
126
|
+
// Normalize and dedupe
|
|
127
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
128
|
+
};
|
|
129
|
+
const contactIdsParsed = parseIdList(rawContactIds);
|
|
130
|
+
const contactIds = contactIdsParsed;
|
|
131
|
+
// Creator
|
|
44
132
|
const creatorId = (_k = add.CreatorId) !== null && _k !== void 0 ? _k : '00000000-0000-0000-0000-000000000000';
|
|
45
133
|
const recurrenceType = (_l = add.RecurrenceType) !== null && _l !== void 0 ? _l : '';
|
|
46
134
|
const recurrenceDataNum = Number((_m = add.RecurrenceData) !== null && _m !== void 0 ? _m : 0);
|
|
@@ -37,12 +37,99 @@ async function activityUpdate(i, returnData) {
|
|
|
37
37
|
const dealId = (_h = add.DealId) !== null && _h !== void 0 ? _h : '00000000-0000-0000-0000-000000000000';
|
|
38
38
|
const caseId = (_j = add.CaseId) !== null && _j !== void 0 ? _j : '00000000-0000-0000-0000-000000000000';
|
|
39
39
|
// ContactIds: string[]
|
|
40
|
-
let contactIds;
|
|
41
|
-
const cids = add.ContactIds;
|
|
42
|
-
if (Array.isArray(cids))
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
// let contactIds: string[] | undefined;
|
|
41
|
+
// const cids = add.ContactIds as string[] | string | undefined;
|
|
42
|
+
// if (Array.isArray(cids)) contactIds = cids.filter(Boolean);
|
|
43
|
+
// else if (typeof cids === 'string' && cids) contactIds = [cids];
|
|
44
|
+
// ---- Robust ContactIds parsing: string | string[] | JSON array | CSV/newlines | [Array: [...]] ----
|
|
45
|
+
const rawContactIds = add.ContactIds;
|
|
46
|
+
const parseIdList = (input) => {
|
|
47
|
+
const out = [];
|
|
48
|
+
const pushFromString = (raw) => {
|
|
49
|
+
if (!raw)
|
|
50
|
+
return;
|
|
51
|
+
const s = raw.trim();
|
|
52
|
+
if (!s)
|
|
53
|
+
return;
|
|
54
|
+
// Handle n8n visualization: [Array: [...]]
|
|
55
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
56
|
+
if (arrayLabel) {
|
|
57
|
+
try {
|
|
58
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
59
|
+
recur(arr);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
catch { /* fall through */ }
|
|
63
|
+
}
|
|
64
|
+
// JSON array
|
|
65
|
+
if (/^\s*\[/.test(s)) {
|
|
66
|
+
try {
|
|
67
|
+
const arr = JSON.parse(s);
|
|
68
|
+
recur(arr);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error('Invalid JSON in "Contact IDs". Expected a JSON array of strings.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Otherwise CSV/newlines/semicolon
|
|
76
|
+
s.split(/[\n,;]+/)
|
|
77
|
+
.map((t) => t.trim())
|
|
78
|
+
.filter(Boolean)
|
|
79
|
+
.forEach((t) => out.push(t));
|
|
80
|
+
};
|
|
81
|
+
const recur = (val) => {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
if (val == null)
|
|
84
|
+
return;
|
|
85
|
+
if (Array.isArray(val)) {
|
|
86
|
+
val.forEach(recur);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
switch (typeof val) {
|
|
90
|
+
case 'string':
|
|
91
|
+
pushFromString(val);
|
|
92
|
+
return;
|
|
93
|
+
case 'number':
|
|
94
|
+
case 'boolean':
|
|
95
|
+
out.push(String(val));
|
|
96
|
+
return;
|
|
97
|
+
case 'object': {
|
|
98
|
+
const obj = val;
|
|
99
|
+
// Map/Set support
|
|
100
|
+
if (val instanceof Set) {
|
|
101
|
+
recur(Array.from(val));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (val instanceof Map) {
|
|
105
|
+
recur(Array.from(val.values()));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Common wrappers
|
|
109
|
+
if (obj === null || obj === void 0 ? void 0 : obj.values) {
|
|
110
|
+
recur(obj.values);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (obj === null || obj === void 0 ? void 0 : obj.Value) {
|
|
114
|
+
recur(obj.Value);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Fallback: try toString
|
|
118
|
+
const s = ((_b = (_a = obj === null || obj === void 0 ? void 0 : obj.toString) === null || _a === void 0 ? void 0 : _a.call(obj)) !== null && _b !== void 0 ? _b : '').toString();
|
|
119
|
+
if (s)
|
|
120
|
+
pushFromString(s);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
recur(input);
|
|
128
|
+
// Normalize and dedupe
|
|
129
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
130
|
+
};
|
|
131
|
+
const contactIdsParsed = parseIdList(rawContactIds);
|
|
132
|
+
const contactIds = contactIdsParsed;
|
|
46
133
|
const creatorId = (_k = add.CreatorId) !== null && _k !== void 0 ? _k : '00000000-0000-0000-0000-000000000000';
|
|
47
134
|
const recurrenceType = (_l = add.RecurrenceType) !== null && _l !== void 0 ? _l : '';
|
|
48
135
|
const recurrenceDataNum = Number((_m = add.RecurrenceData) !== null && _m !== void 0 ? _m : 0);
|
|
@@ -23,8 +23,104 @@ async function caseCreate(i, returnData) {
|
|
|
23
23
|
const usersMode = this.getNodeParameter('UsersMode', i, 'select');
|
|
24
24
|
const userIdsSelect = this.getNodeParameter('UserIdsSelect', i, []);
|
|
25
25
|
const userIdsManual = this.getNodeParameter('UserIdsManual', i, []);
|
|
26
|
-
const toArray = (v) => Array.isArray(v)
|
|
27
|
-
|
|
26
|
+
const toArray = (v) => Array.isArray(v)
|
|
27
|
+
? v.map((x) => (x !== null && x !== void 0 ? x : '').toString().trim()).filter(Boolean)
|
|
28
|
+
: (typeof v === 'string' && v ? [v.trim()] : []);
|
|
29
|
+
// --- Robust parser for Manual input ---
|
|
30
|
+
const parseManualIds = (input) => {
|
|
31
|
+
const out = [];
|
|
32
|
+
const pushFromString = (raw) => {
|
|
33
|
+
if (!raw)
|
|
34
|
+
return;
|
|
35
|
+
const s = raw.trim();
|
|
36
|
+
if (!s)
|
|
37
|
+
return;
|
|
38
|
+
// Handle "[Array: [...]]" visualization
|
|
39
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
40
|
+
if (arrayLabel) {
|
|
41
|
+
try {
|
|
42
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
43
|
+
recur(arr);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
catch { /* fall through */ }
|
|
47
|
+
}
|
|
48
|
+
// Try JSON array
|
|
49
|
+
if (/^\s*\[/.test(s)) {
|
|
50
|
+
try {
|
|
51
|
+
const arr = JSON.parse(s);
|
|
52
|
+
recur(arr);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
throw new Error('Invalid JSON in "User IDs (Manual)". Expected a JSON array of strings.');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Otherwise treat as CSV/newline/semicolon separated
|
|
60
|
+
s.split(/[\n,;]+/)
|
|
61
|
+
.map((t) => t.trim())
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
.forEach((t) => out.push(t));
|
|
64
|
+
};
|
|
65
|
+
const recur = (val) => {
|
|
66
|
+
var _a, _b;
|
|
67
|
+
if (val == null)
|
|
68
|
+
return;
|
|
69
|
+
if (Array.isArray(val)) {
|
|
70
|
+
val.forEach(recur);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
switch (typeof val) {
|
|
74
|
+
case 'string':
|
|
75
|
+
pushFromString(val);
|
|
76
|
+
return;
|
|
77
|
+
case 'number':
|
|
78
|
+
case 'boolean':
|
|
79
|
+
out.push(String(val));
|
|
80
|
+
return;
|
|
81
|
+
case 'object': {
|
|
82
|
+
// Common wrappers
|
|
83
|
+
const obj = val;
|
|
84
|
+
if (obj.values) {
|
|
85
|
+
recur(obj.values);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (obj.Value) {
|
|
89
|
+
recur(obj.Value);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Map/Set support
|
|
93
|
+
if (val instanceof Set) {
|
|
94
|
+
recur(Array.from(val));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (val instanceof Map) {
|
|
98
|
+
recur(Array.from(val.values()));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Fallback: string representation
|
|
102
|
+
const s = ((_b = (_a = obj === null || obj === void 0 ? void 0 : obj.toString) === null || _a === void 0 ? void 0 : _a.call(obj)) !== null && _b !== void 0 ? _b : '').toString();
|
|
103
|
+
if (s)
|
|
104
|
+
pushFromString(s);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
default:
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
recur(input);
|
|
112
|
+
// Normalize & dedupe
|
|
113
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
114
|
+
};
|
|
115
|
+
const usersFromSelect = toArray(userIdsSelect);
|
|
116
|
+
const usersFromManual = parseManualIds(userIdsManual);
|
|
117
|
+
// Final pick based on UsersMode
|
|
118
|
+
let userIds = usersMode === 'select' ? usersFromSelect : usersFromManual;
|
|
119
|
+
// Optional: strict GUID validation (enable if needed)
|
|
120
|
+
// const guidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
121
|
+
// userIds = userIds.filter(id => guidRe.test(id));
|
|
122
|
+
userIds = Array.from(new Set(userIds)).filter(Boolean);
|
|
123
|
+
// resultNote
|
|
28
124
|
const resultNoteTop = status === 'Done' ? this.getNodeParameter('ResultNote', i, '') : '';
|
|
29
125
|
// Additional
|
|
30
126
|
const add = this.getNodeParameter('additionalFields', i, {}) || {};
|
|
@@ -95,7 +95,7 @@ exports.caseCreateProperties = [
|
|
|
95
95
|
typeOptions: { multipleValues: true },
|
|
96
96
|
default: [],
|
|
97
97
|
displayOptions: { show: { ...showForCaseCreate.show, UsersMode: ['manual'] } },
|
|
98
|
-
description: 'Enter one or more user IDs (GUIDs).',
|
|
98
|
+
description: 'Enter one or more user IDs (GUIDs or GUIDs,GUIDs or [GUIDs,GUIDs]). Accepts multiple fields, CSV/newlines, JSON array, or an expression returning an array.',
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
101
|
displayName: 'Result Note',
|
|
@@ -150,7 +150,8 @@ exports.caseCreateProperties = [
|
|
|
150
150
|
{ displayName: 'Custom Fields (JSON)', name: 'Fields', type: 'json', default: '', placeholder: '{ "Field_XXXX_0_YY": "value" }', description: 'JSON object of custom fields.' },
|
|
151
151
|
// string[]
|
|
152
152
|
{ displayName: 'Label IDs', name: 'LabelIds', type: 'string', typeOptions: { multipleValues: true }, default: [], description: 'Label IDs to be set on the case.' },
|
|
153
|
-
{
|
|
153
|
+
{
|
|
154
|
+
displayName: 'Visibility Type', name: 'VisibilityType', type: 'options',
|
|
154
155
|
options: [
|
|
155
156
|
{ name: 'Owner', value: 'Owner' },
|
|
156
157
|
{ name: 'Owner Group', value: 'OwnerGroup' },
|