n8n-nodes-didar-crm 0.0.17 → 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/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
|
@@ -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' },
|
|
@@ -26,8 +26,104 @@ async function caseUpdate(i, returnData) {
|
|
|
26
26
|
const usersMode = this.getNodeParameter('UsersMode', i, 'select');
|
|
27
27
|
const userIdsSelect = this.getNodeParameter('UserIdsSelect', i, []);
|
|
28
28
|
const userIdsManual = this.getNodeParameter('UserIdsManual', i, []);
|
|
29
|
-
const toArray = (v) => Array.isArray(v)
|
|
30
|
-
|
|
29
|
+
const toArray = (v) => Array.isArray(v)
|
|
30
|
+
? v.map((x) => (x !== null && x !== void 0 ? x : '').toString().trim()).filter(Boolean)
|
|
31
|
+
: (typeof v === 'string' && v ? [v.trim()] : []);
|
|
32
|
+
// --- Robust parser for Manual input ---
|
|
33
|
+
const parseManualIds = (input) => {
|
|
34
|
+
const out = [];
|
|
35
|
+
const pushFromString = (raw) => {
|
|
36
|
+
if (!raw)
|
|
37
|
+
return;
|
|
38
|
+
const s = raw.trim();
|
|
39
|
+
if (!s)
|
|
40
|
+
return;
|
|
41
|
+
// Handle "[Array: [...]]" visualization
|
|
42
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
43
|
+
if (arrayLabel) {
|
|
44
|
+
try {
|
|
45
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
46
|
+
recur(arr);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
catch { /* fall through */ }
|
|
50
|
+
}
|
|
51
|
+
// Try JSON array
|
|
52
|
+
if (/^\s*\[/.test(s)) {
|
|
53
|
+
try {
|
|
54
|
+
const arr = JSON.parse(s);
|
|
55
|
+
recur(arr);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
throw new Error('Invalid JSON in "User IDs (Manual)". Expected a JSON array of strings.');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Otherwise treat as CSV/newline/semicolon separated
|
|
63
|
+
s.split(/[\n,;]+/)
|
|
64
|
+
.map((t) => t.trim())
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.forEach((t) => out.push(t));
|
|
67
|
+
};
|
|
68
|
+
const recur = (val) => {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
if (val == null)
|
|
71
|
+
return;
|
|
72
|
+
if (Array.isArray(val)) {
|
|
73
|
+
val.forEach(recur);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
switch (typeof val) {
|
|
77
|
+
case 'string':
|
|
78
|
+
pushFromString(val);
|
|
79
|
+
return;
|
|
80
|
+
case 'number':
|
|
81
|
+
case 'boolean':
|
|
82
|
+
out.push(String(val));
|
|
83
|
+
return;
|
|
84
|
+
case 'object': {
|
|
85
|
+
// Common wrappers
|
|
86
|
+
const obj = val;
|
|
87
|
+
if (obj.values) {
|
|
88
|
+
recur(obj.values);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (obj.Value) {
|
|
92
|
+
recur(obj.Value);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Map/Set support
|
|
96
|
+
if (val instanceof Set) {
|
|
97
|
+
recur(Array.from(val));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (val instanceof Map) {
|
|
101
|
+
recur(Array.from(val.values()));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Fallback: string representation
|
|
105
|
+
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();
|
|
106
|
+
if (s)
|
|
107
|
+
pushFromString(s);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
recur(input);
|
|
115
|
+
// Normalize & dedupe
|
|
116
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
117
|
+
};
|
|
118
|
+
const usersFromSelect = toArray(userIdsSelect);
|
|
119
|
+
const usersFromManual = parseManualIds(userIdsManual);
|
|
120
|
+
// Final pick based on UsersMode
|
|
121
|
+
let userIds = usersMode === 'select' ? usersFromSelect : usersFromManual;
|
|
122
|
+
// Optional: strict GUID validation (enable if needed)
|
|
123
|
+
// const guidRe = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
124
|
+
// userIds = userIds.filter(id => guidRe.test(id));
|
|
125
|
+
userIds = Array.from(new Set(userIds)).filter(Boolean);
|
|
126
|
+
// resultNote
|
|
31
127
|
const resultNoteTop = status === 'Done' ? this.getNodeParameter('ResultNote', i, '') : '';
|
|
32
128
|
// Additional
|
|
33
129
|
const add = this.getNodeParameter('additionalFields', i, {}) || {};
|
|
@@ -105,7 +105,7 @@ exports.caseUpdateProperties = [
|
|
|
105
105
|
typeOptions: { multipleValues: true },
|
|
106
106
|
default: [],
|
|
107
107
|
displayOptions: { show: { ...showForCaseUpdate.show, UsersMode: ['manual'] } },
|
|
108
|
-
description: 'Enter one or more user IDs (GUIDs).',
|
|
108
|
+
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.',
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
displayName: 'Result Note',
|
|
@@ -157,7 +157,8 @@ exports.caseUpdateProperties = [
|
|
|
157
157
|
{ displayName: 'Other Contact IDs', name: 'OtherContactIds', type: 'string', typeOptions: { multipleValues: true }, default: [], description: 'Additional related contact IDs (GUIDs).' },
|
|
158
158
|
{ displayName: 'Custom Fields (JSON)', name: 'Fields', type: 'json', default: '', placeholder: '{ "Field_XXXX_0_YY": "value" }', description: 'JSON object of custom fields.' },
|
|
159
159
|
{ displayName: 'Label IDs', name: 'LabelIds', type: 'string', typeOptions: { multipleValues: true }, default: [], description: 'Label IDs to be set on the case.' },
|
|
160
|
-
{
|
|
160
|
+
{
|
|
161
|
+
displayName: 'Visibility Type', name: 'VisibilityType', type: 'options',
|
|
161
162
|
options: [
|
|
162
163
|
{ name: 'Owner', value: 'Owner' },
|
|
163
164
|
{ name: 'Owner Group', value: 'OwnerGroup' },
|
|
@@ -4,3 +4,5 @@ export { dealGet } from './get.operation';
|
|
|
4
4
|
export { dealCreateProperties } from './create.properties';
|
|
5
5
|
export { dealUpdateProperties } from './update.properties';
|
|
6
6
|
export { dealGetProperties } from './get.properties';
|
|
7
|
+
export { dealSearch } from './search.operation';
|
|
8
|
+
export { dealSearchProperties } from './search.properties';
|
package/dist/nodes/deal/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.dealGetProperties = exports.dealUpdateProperties = exports.dealCreateProperties = exports.dealGet = exports.dealUpdate = exports.dealCreate = void 0;
|
|
3
|
+
exports.dealSearchProperties = exports.dealSearch = exports.dealGetProperties = exports.dealUpdateProperties = exports.dealCreateProperties = exports.dealGet = exports.dealUpdate = exports.dealCreate = void 0;
|
|
4
4
|
var create_operation_1 = require("./create.operation");
|
|
5
5
|
Object.defineProperty(exports, "dealCreate", { enumerable: true, get: function () { return create_operation_1.dealCreate; } });
|
|
6
6
|
var update_operation_1 = require("./update.operation");
|
|
@@ -13,3 +13,7 @@ var update_properties_1 = require("./update.properties");
|
|
|
13
13
|
Object.defineProperty(exports, "dealUpdateProperties", { enumerable: true, get: function () { return update_properties_1.dealUpdateProperties; } });
|
|
14
14
|
var get_properties_1 = require("./get.properties");
|
|
15
15
|
Object.defineProperty(exports, "dealGetProperties", { enumerable: true, get: function () { return get_properties_1.dealGetProperties; } });
|
|
16
|
+
var search_operation_1 = require("./search.operation");
|
|
17
|
+
Object.defineProperty(exports, "dealSearch", { enumerable: true, get: function () { return search_operation_1.dealSearch; } });
|
|
18
|
+
var search_properties_1 = require("./search.properties");
|
|
19
|
+
Object.defineProperty(exports, "dealSearchProperties", { enumerable: true, get: function () { return search_properties_1.dealSearchProperties; } });
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dealSearch = dealSearch;
|
|
4
|
+
const http_1 = require("../../lib/http");
|
|
5
|
+
async function dealSearch(i, returnData) {
|
|
6
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
7
|
+
// Helpers
|
|
8
|
+
const uniq = (arr) => Array.from(new Set(arr.map(s => s.trim()).filter(Boolean)));
|
|
9
|
+
const parseIdList = (input) => {
|
|
10
|
+
const out = [];
|
|
11
|
+
const pushFromString = (raw) => {
|
|
12
|
+
if (!raw)
|
|
13
|
+
return;
|
|
14
|
+
const s = raw.trim();
|
|
15
|
+
if (!s)
|
|
16
|
+
return;
|
|
17
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
18
|
+
if (arrayLabel) {
|
|
19
|
+
try {
|
|
20
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
21
|
+
recur(arr);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
}
|
|
26
|
+
if (/^\s*\[/.test(s)) {
|
|
27
|
+
try {
|
|
28
|
+
const arr = JSON.parse(s);
|
|
29
|
+
recur(arr);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new Error('Invalid JSON array.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
s.split(/[\n,;]+/).map(t => t.trim()).filter(Boolean).forEach(t => out.push(t));
|
|
37
|
+
};
|
|
38
|
+
const recur = (val) => {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
if (val == null)
|
|
41
|
+
return;
|
|
42
|
+
if (Array.isArray(val)) {
|
|
43
|
+
val.forEach(recur);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
switch (typeof val) {
|
|
47
|
+
case 'string':
|
|
48
|
+
pushFromString(val);
|
|
49
|
+
return;
|
|
50
|
+
case 'number':
|
|
51
|
+
case 'boolean':
|
|
52
|
+
out.push(String(val));
|
|
53
|
+
return;
|
|
54
|
+
case 'object': {
|
|
55
|
+
const obj = val;
|
|
56
|
+
if (val instanceof Set) {
|
|
57
|
+
recur(Array.from(val));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (val instanceof Map) {
|
|
61
|
+
recur(Array.from(val.values()));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (obj === null || obj === void 0 ? void 0 : obj.values) {
|
|
65
|
+
recur(obj.values);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (obj === null || obj === void 0 ? void 0 : obj.Value) {
|
|
69
|
+
recur(obj.Value);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
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();
|
|
73
|
+
if (s)
|
|
74
|
+
pushFromString(s);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
default: return;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
recur(input);
|
|
81
|
+
return uniq(out);
|
|
82
|
+
};
|
|
83
|
+
const parseJsonFlexible = (val) => {
|
|
84
|
+
if (val == null || val === '')
|
|
85
|
+
return [];
|
|
86
|
+
if (Array.isArray(val))
|
|
87
|
+
return val;
|
|
88
|
+
if (typeof val === 'string') {
|
|
89
|
+
const s = val.trim();
|
|
90
|
+
if (!s)
|
|
91
|
+
return [];
|
|
92
|
+
const m = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
93
|
+
if (m) {
|
|
94
|
+
try {
|
|
95
|
+
return JSON.parse(m[1]);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new Error('Invalid JSON in Custom Fields.');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(s);
|
|
103
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
throw new Error('Invalid JSON in Custom Fields.');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return [];
|
|
110
|
+
};
|
|
111
|
+
// Optional filters (no required checks)
|
|
112
|
+
const keywords = this.getNodeParameter('Keywords', i, '').trim();
|
|
113
|
+
const status = this.getNodeParameter('Status', i, '');
|
|
114
|
+
// Owner (select/manual) — optional
|
|
115
|
+
const ownerMode = this.getNodeParameter('OwnerMode', i, 'select');
|
|
116
|
+
const ownerSel = this.getNodeParameter('OwnerIdSelect', i, '');
|
|
117
|
+
const ownerMan = this.getNodeParameter('OwnerIdManual', i, '');
|
|
118
|
+
const ownerId = ownerMode === 'select' ? ownerSel : ownerMan;
|
|
119
|
+
// Lists
|
|
120
|
+
const contactIdsInput = this.getNodeParameter('ContactIds', i, []);
|
|
121
|
+
const labelIdsInput = this.getNodeParameter('LabelIds', i, []);
|
|
122
|
+
const contactIds = parseIdList(contactIdsInput);
|
|
123
|
+
const labelIds = parseIdList(labelIdsInput);
|
|
124
|
+
// Pipeline/Stage — optional pair
|
|
125
|
+
const pipeMode = (_a = this.getNodeParameter('PipelineMode', i, 'select')) !== null && _a !== void 0 ? _a : 'select';
|
|
126
|
+
let pipelineId = '';
|
|
127
|
+
let pipelineStageId = '';
|
|
128
|
+
if (pipeMode === 'select') {
|
|
129
|
+
pipelineId = this.getNodeParameter('pipelineId', i, '') || '';
|
|
130
|
+
pipelineStageId = this.getNodeParameter('pipelineStageId', i, '') || '';
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
pipelineId = this.getNodeParameter('PipelineIdManual', i, '') || '';
|
|
134
|
+
pipelineStageId = this.getNodeParameter('PipelineStageIdManual', i, '') || '';
|
|
135
|
+
}
|
|
136
|
+
const bothEmpty = !pipelineId && !pipelineStageId;
|
|
137
|
+
const bothFilled = !!pipelineId && !!pipelineStageId;
|
|
138
|
+
if (!(bothEmpty || bothFilled)) {
|
|
139
|
+
throw new Error('Pipeline and Pipeline Stage must be both provided together or both empty.');
|
|
140
|
+
}
|
|
141
|
+
// Additional
|
|
142
|
+
const add = this.getNodeParameter('additionalFields', i, {}) || {};
|
|
143
|
+
const searchFromTime = (_b = add.SearchFromTime) !== null && _b !== void 0 ? _b : '1000-01-01T00:00:00.000Z';
|
|
144
|
+
const searchToTime = (_c = add.SearchToTime) !== null && _c !== void 0 ? _c : '9999-12-01T00:00:00.000Z';
|
|
145
|
+
const sort = (_d = add.Sort) !== null && _d !== void 0 ? _d : 0;
|
|
146
|
+
const hasProb = Object.prototype.hasOwnProperty.call(add, 'Probability');
|
|
147
|
+
const probability = hasProb ? Number(add.Probability) : undefined;
|
|
148
|
+
const hasNextFrom = Object.prototype.hasOwnProperty.call(add, 'NextActivityFrom');
|
|
149
|
+
const hasNextTo = Object.prototype.hasOwnProperty.call(add, 'NextActivityTo');
|
|
150
|
+
const nextFromVal = (_e = add.NextActivityFrom) !== null && _e !== void 0 ? _e : '';
|
|
151
|
+
const nextToVal = (_f = add.NextActivityTo) !== null && _f !== void 0 ? _f : '';
|
|
152
|
+
const sourceId = (_g = add.SourceId) !== null && _g !== void 0 ? _g : '';
|
|
153
|
+
const lostReasonId = (_h = add.LostReasonId) !== null && _h !== void 0 ? _h : '';
|
|
154
|
+
const customFields = parseJsonFlexible(add.CustomFields);
|
|
155
|
+
const limit = typeof add.Limit === 'number' ? add.Limit : 10;
|
|
156
|
+
const from = 0;
|
|
157
|
+
// Build Criteria with only provided filters
|
|
158
|
+
const criteria = {
|
|
159
|
+
SearchTimeField: 1,
|
|
160
|
+
SearchFromTime: searchFromTime,
|
|
161
|
+
SearchToTime: searchToTime,
|
|
162
|
+
Sort: sort,
|
|
163
|
+
};
|
|
164
|
+
if (keywords)
|
|
165
|
+
criteria['Keywords'] = keywords;
|
|
166
|
+
if (status)
|
|
167
|
+
criteria['Status'] = status;
|
|
168
|
+
if (ownerId)
|
|
169
|
+
criteria['OwnerId'] = ownerId;
|
|
170
|
+
if (contactIds.length)
|
|
171
|
+
criteria['ContactIds'] = contactIds;
|
|
172
|
+
if (labelIds.length)
|
|
173
|
+
criteria['LabelIds'] = labelIds;
|
|
174
|
+
if (hasProb && !Number.isNaN(probability))
|
|
175
|
+
criteria['Probability'] = String(probability);
|
|
176
|
+
if (bothFilled) {
|
|
177
|
+
criteria['PipelineId'] = pipelineId;
|
|
178
|
+
criteria['PipelineStageId'] = pipelineStageId;
|
|
179
|
+
}
|
|
180
|
+
if (hasNextFrom && nextFromVal)
|
|
181
|
+
criteria['NextActivityFrom'] = nextFromVal;
|
|
182
|
+
if (hasNextTo && nextToVal)
|
|
183
|
+
criteria['NextActivityTo'] = nextToVal;
|
|
184
|
+
if (sourceId)
|
|
185
|
+
criteria['SourceId'] = sourceId;
|
|
186
|
+
if (lostReasonId)
|
|
187
|
+
criteria['LostReasonId'] = lostReasonId;
|
|
188
|
+
if (customFields === null || customFields === void 0 ? void 0 : customFields.length)
|
|
189
|
+
criteria['CustomFields'] = customFields;
|
|
190
|
+
const body = { Criteria: criteria, From: from, Limit: limit };
|
|
191
|
+
const resp = await (0, http_1.didarRequest)(this, i, {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
path: '/api/deal/search',
|
|
194
|
+
body,
|
|
195
|
+
});
|
|
196
|
+
const out = ((_j = resp === null || resp === void 0 ? void 0 : resp.Response) !== null && _j !== void 0 ? _j : resp);
|
|
197
|
+
returnData.push({ json: { a: out, b: body } });
|
|
198
|
+
return returnData;
|
|
199
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dealSearchProperties = void 0;
|
|
4
|
+
const showForDealSearch = { show: { resource: ['deal'], operation: ['search'] } };
|
|
5
|
+
exports.dealSearchProperties = [
|
|
6
|
+
// ===== Main fields (ordered) =====
|
|
7
|
+
{
|
|
8
|
+
displayName: 'Keywords',
|
|
9
|
+
name: 'Keywords',
|
|
10
|
+
type: 'string',
|
|
11
|
+
default: '',
|
|
12
|
+
required: false, // ← optional
|
|
13
|
+
displayOptions: showForDealSearch,
|
|
14
|
+
description: 'Free-text search (optional).',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
displayName: 'Status',
|
|
18
|
+
name: 'Status',
|
|
19
|
+
type: 'options',
|
|
20
|
+
options: [
|
|
21
|
+
{ name: 'Not set', value: '' }, // ← allow clearing
|
|
22
|
+
{ name: 'Pending', value: 'Pending' },
|
|
23
|
+
{ name: 'Won', value: 'Won' },
|
|
24
|
+
{ name: 'Lost', value: 'Lost' },
|
|
25
|
+
],
|
|
26
|
+
default: '', // ← not set by default
|
|
27
|
+
displayOptions: showForDealSearch,
|
|
28
|
+
description: 'Deal status filter (optional).',
|
|
29
|
+
},
|
|
30
|
+
// Owner (select/manual) — optional
|
|
31
|
+
{
|
|
32
|
+
displayName: 'Owner Input Mode',
|
|
33
|
+
name: 'OwnerMode',
|
|
34
|
+
type: 'options',
|
|
35
|
+
options: [
|
|
36
|
+
{ name: 'Select from list', value: 'select' },
|
|
37
|
+
{ name: 'Enter ID manually', value: 'manual' },
|
|
38
|
+
],
|
|
39
|
+
default: 'select',
|
|
40
|
+
displayOptions: showForDealSearch,
|
|
41
|
+
description: 'Choose how to set the owner filter (optional).',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Owner',
|
|
45
|
+
name: 'OwnerIdSelect',
|
|
46
|
+
type: 'options',
|
|
47
|
+
typeOptions: { loadOptionsMethod: 'getUsers' },
|
|
48
|
+
default: '',
|
|
49
|
+
displayOptions: { show: { ...showForDealSearch.show, OwnerMode: ['select'] } },
|
|
50
|
+
description: 'Select the owner user (leave empty to ignore).',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
displayName: 'Owner ID',
|
|
54
|
+
name: 'OwnerIdManual',
|
|
55
|
+
type: 'string',
|
|
56
|
+
default: '',
|
|
57
|
+
displayOptions: { show: { ...showForDealSearch.show, OwnerMode: ['manual'] } },
|
|
58
|
+
description: 'Enter owner user ID manually (leave empty to ignore).',
|
|
59
|
+
},
|
|
60
|
+
// ContactIds (multi, robust) — optional
|
|
61
|
+
{
|
|
62
|
+
displayName: 'Contact IDs',
|
|
63
|
+
name: 'ContactIds',
|
|
64
|
+
type: 'string',
|
|
65
|
+
typeOptions: { multipleValues: true },
|
|
66
|
+
default: [],
|
|
67
|
+
displayOptions: showForDealSearch,
|
|
68
|
+
description: 'One or more contact IDs (optional). Accepts multiple, CSV/newlines, JSON array, or expression returning array.',
|
|
69
|
+
},
|
|
70
|
+
// LabelIds (multi, robust) — optional
|
|
71
|
+
{
|
|
72
|
+
displayName: 'Label IDs',
|
|
73
|
+
name: 'LabelIds',
|
|
74
|
+
type: 'string',
|
|
75
|
+
typeOptions: { multipleValues: true },
|
|
76
|
+
default: [],
|
|
77
|
+
displayOptions: showForDealSearch,
|
|
78
|
+
description: 'One or more label IDs (optional). Accepts multiple, CSV/newlines, JSON array, or expression returning array.',
|
|
79
|
+
},
|
|
80
|
+
// ===== Pipeline / Stage (TOP-LEVEL; optional) =====
|
|
81
|
+
{
|
|
82
|
+
displayName: 'Pipeline Input Mode',
|
|
83
|
+
name: 'PipelineMode',
|
|
84
|
+
type: 'options',
|
|
85
|
+
options: [
|
|
86
|
+
{ name: 'Select from list', value: 'select' },
|
|
87
|
+
{ name: 'Enter IDs manually', value: 'manual' },
|
|
88
|
+
],
|
|
89
|
+
default: 'select',
|
|
90
|
+
displayOptions: showForDealSearch,
|
|
91
|
+
description: 'Set pipeline and stage (optional). Both must be provided together or both empty.',
|
|
92
|
+
},
|
|
93
|
+
// Pipeline (select)
|
|
94
|
+
{
|
|
95
|
+
displayName: 'Pipeline',
|
|
96
|
+
name: 'pipelineId',
|
|
97
|
+
type: 'options',
|
|
98
|
+
typeOptions: { loadOptionsMethod: 'getPipelines' },
|
|
99
|
+
default: '',
|
|
100
|
+
displayOptions: { show: { ...showForDealSearch.show, PipelineMode: ['select'] } },
|
|
101
|
+
description: 'Pipeline (optional; required if stage is set).',
|
|
102
|
+
},
|
|
103
|
+
// Pipeline Stage (select) — وابسته به Pipeline
|
|
104
|
+
{
|
|
105
|
+
displayName: 'Pipeline Stage',
|
|
106
|
+
name: 'pipelineStageId',
|
|
107
|
+
type: 'options',
|
|
108
|
+
typeOptions: {
|
|
109
|
+
loadOptionsMethod: 'getStagesForPipeline',
|
|
110
|
+
loadOptionsDependsOn: ['pipelineId'], // ← حتماً باشد
|
|
111
|
+
},
|
|
112
|
+
default: '',
|
|
113
|
+
displayOptions: { show: { ...showForDealSearch.show, PipelineMode: ['select'] } },
|
|
114
|
+
description: 'Stage (optional; required if pipeline is set).',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
displayName: 'Pipeline ID (Manual)',
|
|
118
|
+
name: 'PipelineIdManual',
|
|
119
|
+
type: 'string',
|
|
120
|
+
default: '',
|
|
121
|
+
displayOptions: { show: { ...showForDealSearch.show, PipelineMode: ['manual'] } },
|
|
122
|
+
description: 'Enter pipeline ID (optional; required if stage manual is set).',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
displayName: 'Pipeline Stage ID (Manual)',
|
|
126
|
+
name: 'PipelineStageIdManual',
|
|
127
|
+
type: 'string',
|
|
128
|
+
default: '',
|
|
129
|
+
displayOptions: { show: { ...showForDealSearch.show, PipelineMode: ['manual'] } },
|
|
130
|
+
description: 'Enter pipeline stage ID (optional; required if pipeline manual is set).',
|
|
131
|
+
},
|
|
132
|
+
// ===== Additional fields =====
|
|
133
|
+
{
|
|
134
|
+
displayName: 'Additional Filters',
|
|
135
|
+
name: 'additionalFields',
|
|
136
|
+
type: 'collection',
|
|
137
|
+
placeholder: 'Add filter',
|
|
138
|
+
default: {},
|
|
139
|
+
displayOptions: showForDealSearch,
|
|
140
|
+
options: [
|
|
141
|
+
// Time window (SearchTimeField fixed=1; keep defaults visible)
|
|
142
|
+
{
|
|
143
|
+
displayName: 'Search From Time',
|
|
144
|
+
name: 'SearchFromTime',
|
|
145
|
+
type: 'dateTime',
|
|
146
|
+
default: '1000-01-01T00:00:00.000Z',
|
|
147
|
+
description: 'Lower bound for the selected time field.',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
displayName: 'Search To Time',
|
|
151
|
+
name: 'SearchToTime',
|
|
152
|
+
type: 'dateTime',
|
|
153
|
+
default: '9999-12-01T00:00:00.000Z',
|
|
154
|
+
description: 'Upper bound for the selected time field.',
|
|
155
|
+
},
|
|
156
|
+
// Sort (keep default 0)
|
|
157
|
+
{
|
|
158
|
+
displayName: 'Sort By',
|
|
159
|
+
name: 'Sort',
|
|
160
|
+
type: 'options',
|
|
161
|
+
options: [
|
|
162
|
+
{ name: 'Created Time', value: 0 },
|
|
163
|
+
{ name: 'Last Follow-up Time', value: 1 },
|
|
164
|
+
{ name: 'Next Follow-up Time', value: 2 },
|
|
165
|
+
{ name: 'Has Proforma Invoice', value: 3 },
|
|
166
|
+
{ name: 'Has Invoice', value: 4 },
|
|
167
|
+
{ name: 'No Invoice', value: 5 },
|
|
168
|
+
{ name: 'Has Canceled Invoice', value: 6 },
|
|
169
|
+
{ name: 'Has Proforma With Price', value: 7 },
|
|
170
|
+
{ name: 'Has Proforma Without Price', value: 8 },
|
|
171
|
+
{ name: 'Won Time', value: 9 },
|
|
172
|
+
{ name: 'Next Activity Time', value: 10 },
|
|
173
|
+
],
|
|
174
|
+
default: 0,
|
|
175
|
+
description: 'Sorting mode.',
|
|
176
|
+
},
|
|
177
|
+
// Probability (optional; only sent if provided)
|
|
178
|
+
{
|
|
179
|
+
displayName: 'Probability',
|
|
180
|
+
name: 'Probability',
|
|
181
|
+
type: 'number',
|
|
182
|
+
typeOptions: { minValue: 0, maxValue: 100 },
|
|
183
|
+
default: null,
|
|
184
|
+
description: 'Probability (0–100). Leave empty to ignore.',
|
|
185
|
+
},
|
|
186
|
+
// Next Activity range
|
|
187
|
+
{
|
|
188
|
+
displayName: 'Next Activity From',
|
|
189
|
+
name: 'NextActivityFrom',
|
|
190
|
+
type: 'dateTime',
|
|
191
|
+
default: '', // قبلاً: '1000-01-01T00:00:00.000Z'
|
|
192
|
+
description: 'Lower bound for next activity time (optional).',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
displayName: 'Next Activity To',
|
|
196
|
+
name: 'NextActivityTo',
|
|
197
|
+
type: 'dateTime',
|
|
198
|
+
default: '', // قبلاً: '9999-12-01T00:00:00.000Z'
|
|
199
|
+
description: 'Upper bound for next activity time (optional).',
|
|
200
|
+
},
|
|
201
|
+
// Others
|
|
202
|
+
{ displayName: 'Source ID', name: 'SourceId', type: 'string', default: '' },
|
|
203
|
+
{ displayName: 'Lost Reason ID', name: 'LostReasonId', type: 'string', default: '' },
|
|
204
|
+
// CustomFields as JSON (optional)
|
|
205
|
+
{
|
|
206
|
+
displayName: 'Custom Fields (JSON)',
|
|
207
|
+
name: 'CustomFields',
|
|
208
|
+
type: 'json',
|
|
209
|
+
default: '[]',
|
|
210
|
+
description: 'JSON array of custom field filters (optional).',
|
|
211
|
+
},
|
|
212
|
+
// Pagination (keep default 10; بگو اگر میخواهی 1 باشد)
|
|
213
|
+
{
|
|
214
|
+
displayName: 'Limit',
|
|
215
|
+
name: 'Limit',
|
|
216
|
+
type: 'number',
|
|
217
|
+
default: 10,
|
|
218
|
+
description: 'Number of results to return.',
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
];
|
|
@@ -13,7 +13,95 @@ async function noteCreate(i, returnData) {
|
|
|
13
13
|
const doneDateUi = this.getNodeParameter('DoneDate', i, '');
|
|
14
14
|
const dealId = this.getNodeParameter('DealId', i, ZERO_GUID);
|
|
15
15
|
const caseId = this.getNodeParameter('CaseId', i, ZERO_GUID);
|
|
16
|
-
const contactIds = this.getNodeParameter('ContactIds', i, []);
|
|
16
|
+
// const contactIds = this.getNodeParameter('ContactIds', i, []) as string[];
|
|
17
|
+
// ContactIds
|
|
18
|
+
const rawContactIds = this.getNodeParameter('ContactIds', i, []);
|
|
19
|
+
const parseIdList = (input) => {
|
|
20
|
+
const out = [];
|
|
21
|
+
const pushFromString = (raw) => {
|
|
22
|
+
if (!raw)
|
|
23
|
+
return;
|
|
24
|
+
const s = raw.trim();
|
|
25
|
+
if (!s)
|
|
26
|
+
return;
|
|
27
|
+
// الگوی نمایشی n8n: [Array: [...]]
|
|
28
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
29
|
+
if (arrayLabel) {
|
|
30
|
+
try {
|
|
31
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
32
|
+
recur(arr);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
catch { /* ignore and fall through */ }
|
|
36
|
+
}
|
|
37
|
+
// اگر JSON array باشد
|
|
38
|
+
if (/^\s*\[/.test(s)) {
|
|
39
|
+
try {
|
|
40
|
+
const arr = JSON.parse(s);
|
|
41
|
+
recur(arr);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new Error('Invalid JSON in "Contact IDs". Expected a JSON array of strings.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// در غیر این صورت: CSV / newline / semicolon
|
|
49
|
+
s.split(/[\n,;]+/)
|
|
50
|
+
.map((t) => t.trim())
|
|
51
|
+
.filter(Boolean)
|
|
52
|
+
.forEach((t) => out.push(t));
|
|
53
|
+
};
|
|
54
|
+
const recur = (val) => {
|
|
55
|
+
var _a, _b;
|
|
56
|
+
if (val == null)
|
|
57
|
+
return;
|
|
58
|
+
if (Array.isArray(val)) {
|
|
59
|
+
val.forEach(recur);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
switch (typeof val) {
|
|
63
|
+
case 'string':
|
|
64
|
+
pushFromString(val);
|
|
65
|
+
return;
|
|
66
|
+
case 'number':
|
|
67
|
+
case 'boolean':
|
|
68
|
+
out.push(String(val));
|
|
69
|
+
return;
|
|
70
|
+
case 'object': {
|
|
71
|
+
const obj = val;
|
|
72
|
+
// Map/Set
|
|
73
|
+
if (val instanceof Set) {
|
|
74
|
+
recur(Array.from(val));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (val instanceof Map) {
|
|
78
|
+
recur(Array.from(val.values()));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Wrapperهای رایج
|
|
82
|
+
if (obj === null || obj === void 0 ? void 0 : obj.values) {
|
|
83
|
+
recur(obj.values);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (obj === null || obj === void 0 ? void 0 : obj.Value) {
|
|
87
|
+
recur(obj.Value);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Fallback: تلاش با toString()
|
|
91
|
+
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();
|
|
92
|
+
if (s)
|
|
93
|
+
pushFromString(s);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
default:
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
recur(input);
|
|
101
|
+
// نرمالسازی و حذف تکراریها
|
|
102
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
103
|
+
};
|
|
104
|
+
const contactIdsArr = parseIdList(rawContactIds);
|
|
17
105
|
// 2) اعتبارسنجیهای ضروری
|
|
18
106
|
if (!(resultNote === null || resultNote === void 0 ? void 0 : resultNote.trim())) {
|
|
19
107
|
throw new Error('ResultNote is required for Note creation.');
|
|
@@ -39,7 +127,7 @@ async function noteCreate(i, returnData) {
|
|
|
39
127
|
DueDateType: 'NoTime', // ثابت و نامنمایش
|
|
40
128
|
DoneDateType: 'Notime', // ثابت و نامنمایش
|
|
41
129
|
DealId: dealId || ZERO_GUID,
|
|
42
|
-
ContactIds:
|
|
130
|
+
ContactIds: contactIdsArr,
|
|
43
131
|
CaseId: caseId || ZERO_GUID,
|
|
44
132
|
},
|
|
45
133
|
};
|
|
@@ -14,7 +14,95 @@ async function noteUpdate(i, returnData) {
|
|
|
14
14
|
const doneDateUi = this.getNodeParameter('DoneDate', i, '');
|
|
15
15
|
const dealId = this.getNodeParameter('DealId', i, ZERO_GUID);
|
|
16
16
|
const caseId = this.getNodeParameter('CaseId', i, ZERO_GUID);
|
|
17
|
-
const contactIds = this.getNodeParameter('ContactIds', i, []);
|
|
17
|
+
// const contactIds = this.getNodeParameter('ContactIds', i, []) as string[];
|
|
18
|
+
// ContactIds
|
|
19
|
+
const rawContactIds = this.getNodeParameter('ContactIds', i, []);
|
|
20
|
+
const parseIdList = (input) => {
|
|
21
|
+
const out = [];
|
|
22
|
+
const pushFromString = (raw) => {
|
|
23
|
+
if (!raw)
|
|
24
|
+
return;
|
|
25
|
+
const s = raw.trim();
|
|
26
|
+
if (!s)
|
|
27
|
+
return;
|
|
28
|
+
// الگوی نمایشی n8n: [Array: [...]]
|
|
29
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
30
|
+
if (arrayLabel) {
|
|
31
|
+
try {
|
|
32
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
33
|
+
recur(arr);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
catch { /* ignore and fall through */ }
|
|
37
|
+
}
|
|
38
|
+
// اگر JSON array باشد
|
|
39
|
+
if (/^\s*\[/.test(s)) {
|
|
40
|
+
try {
|
|
41
|
+
const arr = JSON.parse(s);
|
|
42
|
+
recur(arr);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error('Invalid JSON in "Contact IDs". Expected a JSON array of strings.');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// در غیر این صورت: CSV / newline / semicolon
|
|
50
|
+
s.split(/[\n,;]+/)
|
|
51
|
+
.map((t) => t.trim())
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.forEach((t) => out.push(t));
|
|
54
|
+
};
|
|
55
|
+
const recur = (val) => {
|
|
56
|
+
var _a, _b;
|
|
57
|
+
if (val == null)
|
|
58
|
+
return;
|
|
59
|
+
if (Array.isArray(val)) {
|
|
60
|
+
val.forEach(recur);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
switch (typeof val) {
|
|
64
|
+
case 'string':
|
|
65
|
+
pushFromString(val);
|
|
66
|
+
return;
|
|
67
|
+
case 'number':
|
|
68
|
+
case 'boolean':
|
|
69
|
+
out.push(String(val));
|
|
70
|
+
return;
|
|
71
|
+
case 'object': {
|
|
72
|
+
const obj = val;
|
|
73
|
+
// Map/Set
|
|
74
|
+
if (val instanceof Set) {
|
|
75
|
+
recur(Array.from(val));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (val instanceof Map) {
|
|
79
|
+
recur(Array.from(val.values()));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Wrapperهای رایج
|
|
83
|
+
if (obj === null || obj === void 0 ? void 0 : obj.values) {
|
|
84
|
+
recur(obj.values);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (obj === null || obj === void 0 ? void 0 : obj.Value) {
|
|
88
|
+
recur(obj.Value);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Fallback: تلاش با toString()
|
|
92
|
+
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();
|
|
93
|
+
if (s)
|
|
94
|
+
pushFromString(s);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
recur(input);
|
|
102
|
+
// نرمالسازی و حذف تکراریها
|
|
103
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
104
|
+
};
|
|
105
|
+
const contactIdsArr = parseIdList(rawContactIds);
|
|
18
106
|
// 2) Required validations
|
|
19
107
|
if (!(id === null || id === void 0 ? void 0 : id.trim())) {
|
|
20
108
|
throw new Error('Note ID is required for Note update.');
|
|
@@ -44,7 +132,7 @@ async function noteUpdate(i, returnData) {
|
|
|
44
132
|
DueDateType: 'NoTime',
|
|
45
133
|
DoneDateType: 'Notime',
|
|
46
134
|
DealId: dealId || ZERO_GUID,
|
|
47
|
-
ContactIds:
|
|
135
|
+
ContactIds: contactIdsArr,
|
|
48
136
|
CaseId: caseId || ZERO_GUID,
|
|
49
137
|
},
|
|
50
138
|
};
|