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 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
- * **Trigger**
10
+ ### Trigger
11
+ - **Didar CRM Trigger** → receive webhook events for entity changes, updates, deletions.
11
12
 
12
- * Receive webhook events from Didar CRM (entity changes, updates, deletions, etc.)
13
+ ### Deal
14
+ - Create a deal
15
+ - Update a deal (by Id)
16
+ - Get deal details (by Id)
13
17
 
14
- * **Deal**
18
+ ### Person (Contact)
19
+ - Create a person
20
+ - Update a person (by Id)
21
+ - Get person details (by Id)
15
22
 
16
- * Create a deal
17
- * Update a deal (by Id)
18
- * Get deal details (by Id)
23
+ ### Company
24
+ - Create a company
25
+ - Update a company (by Id)
26
+ - Get company details (by Id)
19
27
 
20
- * **Person (Contact)**
28
+ ### Activity
29
+ - Create an activity
30
+ - Update an activity (by Id)
21
31
 
22
- * Create a person
23
- * Update a person (by Id)
24
- * Get person details (by Id)
32
+ ### Note
33
+ - Create a note
34
+ - Update a note (by Id)
25
35
 
26
- * **Common**
36
+ ### Case (Card)
37
+ - Create a case
38
+ - Update a case (by Id)
27
39
 
28
- * Dropdown selectors for Pipelines, Pipeline Stages, Owners (Users)
29
- * Manual ID input option for each dropdown field
30
- * Support for additional fields like Source, Lost Reason, Visibility Type, Custom Fields, etc.
31
- * Proper handling of default values (zero-guid where needed)
32
- * Webhook trigger enforces POST requests
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
- Then, place the module inside your n8n custom directory (usually `~/.n8n/custom/`) and restart n8n.
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
- * It exposes a webhook URL in n8n.
53
- * Only **POST** requests are accepted.
54
- * Returns payload structured into:
96
+ #### Person (Contact)
97
+ - **Create** required fields: `LastName`, `OwnerId`
98
+ - **Update** same as create + `Id`
99
+ - **Get** → requires `Id`
55
100
 
56
- ```json
57
- {
58
- "data": { ... },
59
- "changes": [ ... ],
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
- ### 2. Deal operations
106
+ #### Activity
107
+ - **Create Activity** → required fields: `Title`, `OwnerId`, `ActivityTypeId`
108
+ - **Update Activity** → same as create + `Id`
67
109
 
68
- * **Create Deal** → required fields: `Title`, `OwnerId`, `PipelineId`, `PipelineStageId`, `PersonId`, `CompanyId`, `Status`
69
- * **Update Deal** → same as Create, plus required `Id`
70
- * **Get Deal** → requires `Id`
110
+ #### Note
111
+ - **Create Note** → required fields: `ResultNote`, `OwnerId`
112
+ - **Update Note** → same as create + `Id`
71
113
 
72
- ### 3. Person (Contact) operations
114
+ #### Case (Card)
115
+ - **Create Case** → required fields: `Title`, `OwnerId`, `PipelineStageId`, `Status`
116
+ - **Update Case** → same as create + `Id`
73
117
 
74
- * **Create Person** → required fields: `LastName`, `OwnerId`
75
- * **Update Person** → same as Create, plus required `Id`
76
- * **Get Person** → requires `Id`
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
- * You must set your **API Key** and (if required) authentication cookies.
85
- * Used across all operations.
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 resource & operation switch
142
+ │ ├── DidarCrm.node.ts # Main entry for resources & operations
98
143
  │ ├── DidarCrmTrigger.node.ts # Trigger node
99
- └── deal/ # Deal operations
100
- ├── create.operation.ts
101
- ├── update.operation.ts
102
- └── get.operation.ts
103
- └── person/ # Person operations
104
- ├── create.operation.ts
105
- ├── update.operation.ts
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
- ├── http.ts # Request helper
109
- └── loadOptions.ts # Dropdown population (pipelines, stages, users)
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 n8n:
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<INodePropertyOptions[]>;
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>;
@@ -29,7 +29,15 @@ async function getPipelines() {
29
29
  }
30
30
  async function getStagesForPipeline() {
31
31
  var _a, _b;
32
- const pipelineId = String(this.getNodeParameter('PipelineIdSelect', '') || '');
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
- contactIds = cids.filter(Boolean);
42
- else if (typeof cids === 'string' && cids)
43
- contactIds = [cids];
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
- contactIds = cids.filter(Boolean);
44
- else if (typeof cids === 'string' && cids)
45
- contactIds = [cids];
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) ? v : (typeof v === 'string' && v ? [v] : []);
27
- const userIds = usersMode === 'select' ? toArray(userIdsSelect) : toArray(userIdsManual);
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
- { displayName: 'Visibility Type', name: 'VisibilityType', type: 'options',
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' },