n8n-nodes-didar-crm 0.0.23 → 0.0.25
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 +32 -1
- package/dist/credentials/DidarApi.credentials.d.ts +2 -1
- package/dist/credentials/DidarApi.credentials.js +16 -0
- package/dist/nodes/activity/create.operation.js +64 -0
- package/dist/nodes/activity/create.properties.js +8 -0
- package/dist/nodes/activity/search.operation.js +1 -1
- package/dist/nodes/activity/search.properties.js +7 -0
- package/dist/nodes/activity/update.operation.js +64 -0
- package/dist/nodes/activity/update.properties.js +8 -0
- package/dist/nodes/deal/_shared.fields.d.ts +1 -0
- package/dist/nodes/deal/_shared.fields.js +12 -1
- package/dist/nodes/deal/create.operation.js +80 -0
- package/dist/nodes/deal/create.properties.js +1 -0
- package/dist/nodes/deal/update.operation.js +80 -0
- package/dist/nodes/deal/update.properties.js +1 -0
- package/dist/nodes/note/create.operation.js +7 -0
- package/dist/nodes/note/create.properties.js +10 -0
- package/dist/nodes/note/update.operation.js +7 -0
- package/dist/nodes/note/update.properties.js +10 -0
- package/dist/nodes/supplementary/get-base-info.operation.js +1 -0
- package/dist/nodes/supplementary/get-base-info.properties.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -173,7 +173,7 @@ n8n-nodes-didar-crm/
|
|
|
173
173
|
│ ├── note/ # Note operations
|
|
174
174
|
│ ├── case/ # Case operations
|
|
175
175
|
│ ├── product/ # Product operations
|
|
176
|
-
│ └──
|
|
176
|
+
│ └── supplementary/ # Complementary actions (base info, didaryab, popup caller ID)
|
|
177
177
|
│── lib/
|
|
178
178
|
│ ├── http.ts # Request helper
|
|
179
179
|
│ └── loadOptions.ts # Dropdown population
|
|
@@ -181,6 +181,37 @@ n8n-nodes-didar-crm/
|
|
|
181
181
|
|
|
182
182
|
---
|
|
183
183
|
|
|
184
|
+
## ✅ Compatibility
|
|
185
|
+
|
|
186
|
+
- n8n: v1.20.0+ (Community Nodes enabled)
|
|
187
|
+
- Node.js: v18+
|
|
188
|
+
- Didar CRM account with API access
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 🧪 Quick Start Checklist
|
|
193
|
+
|
|
194
|
+
1. Install and build this package.
|
|
195
|
+
2. In n8n, create credentials of type "Didar API".
|
|
196
|
+
- Set `Base URL` (default `https://app.didar.me`).
|
|
197
|
+
- Set `API Key`.
|
|
198
|
+
- Optional: enable `Use Cookie Header` and set `Cookie` if your instance requires it.
|
|
199
|
+
- Use the Test button to verify connectivity.
|
|
200
|
+
3. Add the `Didar CRM` node and pick a resource/operation.
|
|
201
|
+
4. For dropdown fields (pipelines, stages, owners, activity types), you can either pick from list or provide manual IDs.
|
|
202
|
+
5. For lists (IDs, labels, phones, emails, etc.), you can provide JSON arrays or comma/newline separated strings.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 🧯 Troubleshooting
|
|
207
|
+
|
|
208
|
+
- 401/403 errors: re-check `API Key`, `Base URL`, and whether cookie auth is required.
|
|
209
|
+
- Empty dropdowns: ensure the credential test passes and your user has permission to view the entities.
|
|
210
|
+
- Webhook not triggering: the trigger node accepts only POST; verify the sender uses POST and sends valid JSON.
|
|
211
|
+
- Popup Caller ID: requires a separate fixed API key provided by Didar.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
184
215
|
## 🛠 Development
|
|
185
216
|
|
|
186
217
|
1. Clone the repo
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
1
|
+
import { ICredentialType, INodeProperties, ICredentialTestRequest } from 'n8n-workflow';
|
|
2
2
|
export declare class DidarApi implements ICredentialType {
|
|
3
3
|
name: string;
|
|
4
4
|
displayName: string;
|
|
5
5
|
documentationUrl: string;
|
|
6
|
+
test: ICredentialTestRequest;
|
|
6
7
|
properties: INodeProperties[];
|
|
7
8
|
}
|
|
@@ -6,6 +6,22 @@ class DidarApi {
|
|
|
6
6
|
this.name = 'didarApi';
|
|
7
7
|
this.displayName = 'Didar API';
|
|
8
8
|
this.documentationUrl = '';
|
|
9
|
+
// Enable "Test" button to validate API key by calling deal_pipelines
|
|
10
|
+
this.test = {
|
|
11
|
+
request: {
|
|
12
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
13
|
+
url: '/api/pipeline/list/0',
|
|
14
|
+
method: 'POST',
|
|
15
|
+
qs: {
|
|
16
|
+
apikey: '={{$credentials.apiKey}}',
|
|
17
|
+
},
|
|
18
|
+
body: {},
|
|
19
|
+
headers: {
|
|
20
|
+
// Send cookie only when enabled
|
|
21
|
+
Cookie: '={{$credentials.useCookie ? $credentials.cookie : undefined}}',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
9
25
|
this.properties = [
|
|
10
26
|
{
|
|
11
27
|
displayName: 'Base URL',
|
|
@@ -25,6 +25,8 @@ async function activityCreate(i, returnData) {
|
|
|
25
25
|
const ownerId = ownerMode === 'select' ? ownerSel : ownerMan;
|
|
26
26
|
const note = (_a = add.Note) !== null && _a !== void 0 ? _a : '';
|
|
27
27
|
const resultNote = (_b = add.ResultNote) !== null && _b !== void 0 ? _b : '';
|
|
28
|
+
// Tags: only meaningful if ResultNote is provided; accept string | string[] | JSON/CSV
|
|
29
|
+
const rawTags = add.Tags;
|
|
28
30
|
const isDeleted = (_c = add.IsDeleted) !== null && _c !== void 0 ? _c : false;
|
|
29
31
|
const isPinned = (_d = add.IsPinned) !== null && _d !== void 0 ? _d : false;
|
|
30
32
|
const dueDate = (_e = add.DueDate) !== null && _e !== void 0 ? _e : '';
|
|
@@ -143,6 +145,68 @@ async function activityCreate(i, returnData) {
|
|
|
143
145
|
activity['Note'] = note;
|
|
144
146
|
if (resultNote)
|
|
145
147
|
activity['ResultNote'] = resultNote;
|
|
148
|
+
// If ResultNote exists and Tags provided, send as array of { TagId }
|
|
149
|
+
if (resultNote && rawTags != null) {
|
|
150
|
+
const parseIdList = (input) => {
|
|
151
|
+
const out = [];
|
|
152
|
+
const pushFromString = (raw) => {
|
|
153
|
+
if (!raw)
|
|
154
|
+
return;
|
|
155
|
+
const s = raw.trim();
|
|
156
|
+
if (!s)
|
|
157
|
+
return;
|
|
158
|
+
if (/^\s*\[/.test(s)) {
|
|
159
|
+
try {
|
|
160
|
+
const arr = JSON.parse(s);
|
|
161
|
+
recur(arr);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
catch { /* ignore */ }
|
|
165
|
+
}
|
|
166
|
+
s.split(/[\n,;]+/).map((t) => t.trim()).filter(Boolean).forEach((t) => out.push(t));
|
|
167
|
+
};
|
|
168
|
+
const recur = (val) => {
|
|
169
|
+
var _a, _b;
|
|
170
|
+
if (val == null)
|
|
171
|
+
return;
|
|
172
|
+
if (Array.isArray(val)) {
|
|
173
|
+
val.forEach(recur);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
switch (typeof val) {
|
|
177
|
+
case 'string':
|
|
178
|
+
pushFromString(val);
|
|
179
|
+
return;
|
|
180
|
+
case 'number':
|
|
181
|
+
case 'boolean':
|
|
182
|
+
out.push(String(val));
|
|
183
|
+
return;
|
|
184
|
+
case 'object': {
|
|
185
|
+
const obj = val;
|
|
186
|
+
if (val instanceof Set) {
|
|
187
|
+
recur(Array.from(val));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (val instanceof Map) {
|
|
191
|
+
recur(Array.from(val.values()));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
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();
|
|
195
|
+
if (s)
|
|
196
|
+
pushFromString(s);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
default: return;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
recur(input);
|
|
203
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
204
|
+
};
|
|
205
|
+
const tagIds = parseIdList(rawTags);
|
|
206
|
+
if (tagIds.length) {
|
|
207
|
+
activity['Tags'] = tagIds.map((id) => ({ TagId: id }));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
146
210
|
if (isDeleted)
|
|
147
211
|
activity['IsDeleted'] = true;
|
|
148
212
|
if (isPinned)
|
|
@@ -92,6 +92,14 @@ exports.activityCreateProperties = [
|
|
|
92
92
|
options: [
|
|
93
93
|
{ displayName: 'Note', name: 'Note', type: 'string', default: '', description: 'Activity note/description.' },
|
|
94
94
|
{ displayName: 'Result Note', name: 'ResultNote', type: 'string', default: '', description: 'Result/Outcome notes.' },
|
|
95
|
+
{
|
|
96
|
+
displayName: 'Tags',
|
|
97
|
+
name: 'Tags',
|
|
98
|
+
type: 'string',
|
|
99
|
+
typeOptions: { multipleValues: true },
|
|
100
|
+
default: [],
|
|
101
|
+
description: 'List of Tag IDs (GUID). Accepts multiple entries, CSV/comma/semicolon, new lines, or a JSON array (e.g. ["id1","id2"]). Sent only if "Result Note" is provided.',
|
|
102
|
+
},
|
|
95
103
|
{ displayName: 'Is Deleted', name: 'IsDeleted', type: 'boolean', default: false, description: 'Soft-delete flag.' },
|
|
96
104
|
{ displayName: 'Is Pinned', name: 'IsPinned', type: 'boolean', default: false, description: 'Pin this activity.' },
|
|
97
105
|
{ displayName: 'Due Date', name: 'DueDate', type: 'dateTime', default: '', description: 'Planned due date/time (ISO).' },
|
|
@@ -123,7 +123,7 @@ async function activitySearch(i, returnData) {
|
|
|
123
123
|
const tagIdsInput = add.TagIds;
|
|
124
124
|
const tagIds = parseIdList(tagIdsInput);
|
|
125
125
|
const limit = typeof add.Limit === 'number' ? add.Limit : 10;
|
|
126
|
-
const from = 0;
|
|
126
|
+
const from = typeof add.From === 'number' ? add.From : 0;
|
|
127
127
|
// ===== Build payload =====
|
|
128
128
|
const body = {
|
|
129
129
|
FromDate: fromDate,
|
|
@@ -181,6 +181,13 @@ exports.activitySearchProperties = [
|
|
|
181
181
|
description: 'Tag IDs (CSV / JSON array / [Array: [...]] / multi-lines).',
|
|
182
182
|
},
|
|
183
183
|
// Pagination
|
|
184
|
+
{
|
|
185
|
+
displayName: 'From',
|
|
186
|
+
name: 'From',
|
|
187
|
+
type: 'number',
|
|
188
|
+
default: 0,
|
|
189
|
+
description: 'Offset for pagination.',
|
|
190
|
+
},
|
|
184
191
|
{
|
|
185
192
|
displayName: 'Limit',
|
|
186
193
|
name: 'Limit',
|
|
@@ -29,6 +29,8 @@ async function activityUpdate(i, returnData) {
|
|
|
29
29
|
const add = this.getNodeParameter('additionalFields', i, {}) || {};
|
|
30
30
|
const note = (_a = add.Note) !== null && _a !== void 0 ? _a : '';
|
|
31
31
|
const resultNote = (_b = add.ResultNote) !== null && _b !== void 0 ? _b : '';
|
|
32
|
+
// Tags: only meaningful if ResultNote is provided; accept string | string[] | JSON/CSV
|
|
33
|
+
const rawTags = add.Tags;
|
|
32
34
|
const isDeleted = (_c = add.IsDeleted) !== null && _c !== void 0 ? _c : false;
|
|
33
35
|
const isPinned = (_d = add.IsPinned) !== null && _d !== void 0 ? _d : false;
|
|
34
36
|
const dueDate = (_e = add.DueDate) !== null && _e !== void 0 ? _e : '';
|
|
@@ -147,6 +149,68 @@ async function activityUpdate(i, returnData) {
|
|
|
147
149
|
activity['Note'] = note;
|
|
148
150
|
if (resultNote)
|
|
149
151
|
activity['ResultNote'] = resultNote;
|
|
152
|
+
// If ResultNote exists and Tags provided, send as array of { TagId }
|
|
153
|
+
if (resultNote && rawTags != null) {
|
|
154
|
+
const parseIdList = (input) => {
|
|
155
|
+
const out = [];
|
|
156
|
+
const pushFromString = (raw) => {
|
|
157
|
+
if (!raw)
|
|
158
|
+
return;
|
|
159
|
+
const s = raw.trim();
|
|
160
|
+
if (!s)
|
|
161
|
+
return;
|
|
162
|
+
if (/^\s*\[/.test(s)) {
|
|
163
|
+
try {
|
|
164
|
+
const arr = JSON.parse(s);
|
|
165
|
+
recur(arr);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
catch { /* ignore */ }
|
|
169
|
+
}
|
|
170
|
+
s.split(/[\n,;]+/).map((t) => t.trim()).filter(Boolean).forEach((t) => out.push(t));
|
|
171
|
+
};
|
|
172
|
+
const recur = (val) => {
|
|
173
|
+
var _a, _b;
|
|
174
|
+
if (val == null)
|
|
175
|
+
return;
|
|
176
|
+
if (Array.isArray(val)) {
|
|
177
|
+
val.forEach(recur);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
switch (typeof val) {
|
|
181
|
+
case 'string':
|
|
182
|
+
pushFromString(val);
|
|
183
|
+
return;
|
|
184
|
+
case 'number':
|
|
185
|
+
case 'boolean':
|
|
186
|
+
out.push(String(val));
|
|
187
|
+
return;
|
|
188
|
+
case 'object': {
|
|
189
|
+
const obj = val;
|
|
190
|
+
if (val instanceof Set) {
|
|
191
|
+
recur(Array.from(val));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (val instanceof Map) {
|
|
195
|
+
recur(Array.from(val.values()));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
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();
|
|
199
|
+
if (s)
|
|
200
|
+
pushFromString(s);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
default: return;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
recur(input);
|
|
207
|
+
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
208
|
+
};
|
|
209
|
+
const tagIds = parseIdList(rawTags);
|
|
210
|
+
if (tagIds.length) {
|
|
211
|
+
activity['Tags'] = tagIds.map((id) => ({ TagId: id }));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
150
214
|
if (isDeleted)
|
|
151
215
|
activity['IsDeleted'] = true;
|
|
152
216
|
if (isPinned)
|
|
@@ -101,6 +101,14 @@ exports.activityUpdateProperties = [
|
|
|
101
101
|
options: [
|
|
102
102
|
{ displayName: 'Note', name: 'Note', type: 'string', default: '', description: 'Activity note/description.' },
|
|
103
103
|
{ displayName: 'Result Note', name: 'ResultNote', type: 'string', default: '', description: 'Result/Outcome notes.' },
|
|
104
|
+
{
|
|
105
|
+
displayName: 'Tags',
|
|
106
|
+
name: 'Tags',
|
|
107
|
+
type: 'string',
|
|
108
|
+
typeOptions: { multipleValues: true },
|
|
109
|
+
default: [],
|
|
110
|
+
description: 'List of Tag IDs (GUID). Accepts multiple entries, CSV/comma/semicolon, new lines, or a JSON array (e.g. ["id1","id2"]). Sent only if "Result Note" is provided.',
|
|
111
|
+
},
|
|
104
112
|
{ displayName: 'Is Deleted', name: 'IsDeleted', type: 'boolean', default: false, description: 'Soft-delete flag.' },
|
|
105
113
|
{ displayName: 'Is Pinned', name: 'IsPinned', type: 'boolean', default: false, description: 'Pin this activity.' },
|
|
106
114
|
{ displayName: 'Due Date', name: 'DueDate', type: 'dateTime', default: '', description: 'Planned due date/time (ISO).' },
|
|
@@ -6,3 +6,4 @@ export declare const stageFields: (op: "create" | "update") => INodeProperties[]
|
|
|
6
6
|
export declare const personCompanyFields: (op: "create" | "update") => INodeProperties[];
|
|
7
7
|
export declare const statusField: (op: "create" | "update") => INodeProperties;
|
|
8
8
|
export declare const additionalFields: (op: "create" | "update") => INodeProperties;
|
|
9
|
+
export declare const labelIdsField: (op: "create" | "update") => INodeProperties;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.additionalFields = exports.statusField = exports.personCompanyFields = exports.stageFields = exports.pipelineFields = exports.ownerFields = exports.titleField = void 0;
|
|
3
|
+
exports.labelIdsField = exports.additionalFields = exports.statusField = exports.personCompanyFields = exports.stageFields = exports.pipelineFields = exports.ownerFields = exports.titleField = void 0;
|
|
4
4
|
// helper برای تزریق displayOptions به فیلد
|
|
5
5
|
const showFor = (op) => ({
|
|
6
6
|
resource: ['deal'],
|
|
@@ -197,3 +197,14 @@ const additionalFields = (op) => ({
|
|
|
197
197
|
],
|
|
198
198
|
});
|
|
199
199
|
exports.additionalFields = additionalFields;
|
|
200
|
+
// LabelIds (shared, top-level like search pattern)
|
|
201
|
+
const labelIdsField = (op) => ({
|
|
202
|
+
displayName: 'Label IDs',
|
|
203
|
+
name: 'LabelIds',
|
|
204
|
+
type: 'string',
|
|
205
|
+
typeOptions: { multipleValues: true },
|
|
206
|
+
default: [],
|
|
207
|
+
displayOptions: { show: showFor(op) },
|
|
208
|
+
description: 'One or more label IDs (optional). Accepts multiple, CSV/newlines, JSON array, or expression returning array.',
|
|
209
|
+
});
|
|
210
|
+
exports.labelIdsField = labelIdsField;
|
|
@@ -39,6 +39,82 @@ const parseJsonArrayFlexible = (val, fieldLabel = 'DealItems') => {
|
|
|
39
39
|
return [val];
|
|
40
40
|
return [];
|
|
41
41
|
};
|
|
42
|
+
// پارس لیست شناسهها مشابه سرچ
|
|
43
|
+
const parseIdList = (input) => {
|
|
44
|
+
const uniq = (arr) => Array.from(new Set(arr.map(s => s.trim()).filter(Boolean)));
|
|
45
|
+
const out = [];
|
|
46
|
+
const pushFromString = (raw) => {
|
|
47
|
+
if (!raw)
|
|
48
|
+
return;
|
|
49
|
+
const s = raw.trim();
|
|
50
|
+
if (!s)
|
|
51
|
+
return;
|
|
52
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
53
|
+
if (arrayLabel) {
|
|
54
|
+
try {
|
|
55
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
56
|
+
recur(arr);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
catch { }
|
|
60
|
+
}
|
|
61
|
+
if (/^\s*\[/.test(s)) {
|
|
62
|
+
try {
|
|
63
|
+
const arr = JSON.parse(s);
|
|
64
|
+
recur(arr);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new Error('Invalid JSON array.');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
s.split(/[\n,;]+/).map(t => t.trim()).filter(Boolean).forEach(t => out.push(t));
|
|
72
|
+
};
|
|
73
|
+
const recur = (val) => {
|
|
74
|
+
var _a, _b;
|
|
75
|
+
if (val == null)
|
|
76
|
+
return;
|
|
77
|
+
if (Array.isArray(val)) {
|
|
78
|
+
val.forEach(recur);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
switch (typeof val) {
|
|
82
|
+
case 'string':
|
|
83
|
+
pushFromString(val);
|
|
84
|
+
return;
|
|
85
|
+
case 'number':
|
|
86
|
+
case 'boolean':
|
|
87
|
+
out.push(String(val));
|
|
88
|
+
return;
|
|
89
|
+
case 'object': {
|
|
90
|
+
const obj = val;
|
|
91
|
+
if (val instanceof Set) {
|
|
92
|
+
recur(Array.from(val));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (val instanceof Map) {
|
|
96
|
+
recur(Array.from(val.values()));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (obj === null || obj === void 0 ? void 0 : obj.values) {
|
|
100
|
+
recur(obj.values);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (obj === null || obj === void 0 ? void 0 : obj.Value) {
|
|
104
|
+
recur(obj.Value);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
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();
|
|
108
|
+
if (s)
|
|
109
|
+
pushFromString(s);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
default: return;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
recur(input);
|
|
116
|
+
return uniq(out);
|
|
117
|
+
};
|
|
42
118
|
async function dealCreate(i, returnData) {
|
|
43
119
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
44
120
|
// --- Required composed: Owner / Pipeline / Stage ---
|
|
@@ -73,6 +149,9 @@ async function dealCreate(i, returnData) {
|
|
|
73
149
|
const status = this.getNodeParameter('Status', i, '');
|
|
74
150
|
if (!status)
|
|
75
151
|
throw new Error('Status is required.');
|
|
152
|
+
// LabelIds (اختیاری)
|
|
153
|
+
const labelIdsInput = this.getNodeParameter('LabelIds', i, []);
|
|
154
|
+
const labelIds = parseIdList(labelIdsInput);
|
|
76
155
|
// --- Additional Fields (collection) ---
|
|
77
156
|
const add = this.getNodeParameter('additionalFields', i, {}) || {};
|
|
78
157
|
const description = (_a = add.Description) !== null && _a !== void 0 ? _a : '';
|
|
@@ -101,6 +180,7 @@ async function dealCreate(i, returnData) {
|
|
|
101
180
|
}
|
|
102
181
|
const bodyDeal = {
|
|
103
182
|
OwnerId: ownerId,
|
|
183
|
+
...(labelIds.length ? { LabelIds: labelIds } : {}),
|
|
104
184
|
PersonId: personId,
|
|
105
185
|
CompanyId: companyId,
|
|
106
186
|
SourceId: sourceId,
|
|
@@ -9,5 +9,6 @@ exports.dealCreateProperties = [
|
|
|
9
9
|
...(0, _shared_fields_1.stageFields)('create'),
|
|
10
10
|
...(0, _shared_fields_1.personCompanyFields)('create'),
|
|
11
11
|
(0, _shared_fields_1.statusField)('create'),
|
|
12
|
+
(0, _shared_fields_1.labelIdsField)('create'),
|
|
12
13
|
(0, _shared_fields_1.additionalFields)('create'),
|
|
13
14
|
];
|
|
@@ -36,6 +36,82 @@ const parseJsonArrayFlexible = (val, fieldLabel = 'DealItems') => {
|
|
|
36
36
|
return [val];
|
|
37
37
|
return [];
|
|
38
38
|
};
|
|
39
|
+
// پارس لیست شناسهها مشابه سرچ
|
|
40
|
+
const parseIdList = (input) => {
|
|
41
|
+
const uniq = (arr) => Array.from(new Set(arr.map(s => s.trim()).filter(Boolean)));
|
|
42
|
+
const out = [];
|
|
43
|
+
const pushFromString = (raw) => {
|
|
44
|
+
if (!raw)
|
|
45
|
+
return;
|
|
46
|
+
const s = raw.trim();
|
|
47
|
+
if (!s)
|
|
48
|
+
return;
|
|
49
|
+
const arrayLabel = s.match(/^\s*\[Array:\s*(\[[\s\S]*\])\s*\]\s*$/i);
|
|
50
|
+
if (arrayLabel) {
|
|
51
|
+
try {
|
|
52
|
+
const arr = JSON.parse(arrayLabel[1]);
|
|
53
|
+
recur(arr);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
catch { }
|
|
57
|
+
}
|
|
58
|
+
if (/^\s*\[/.test(s)) {
|
|
59
|
+
try {
|
|
60
|
+
const arr = JSON.parse(s);
|
|
61
|
+
recur(arr);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw new Error('Invalid JSON array.');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
s.split(/[\n,;]+/).map(t => t.trim()).filter(Boolean).forEach(t => out.push(t));
|
|
69
|
+
};
|
|
70
|
+
const recur = (val) => {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
if (val == null)
|
|
73
|
+
return;
|
|
74
|
+
if (Array.isArray(val)) {
|
|
75
|
+
val.forEach(recur);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
switch (typeof val) {
|
|
79
|
+
case 'string':
|
|
80
|
+
pushFromString(val);
|
|
81
|
+
return;
|
|
82
|
+
case 'number':
|
|
83
|
+
case 'boolean':
|
|
84
|
+
out.push(String(val));
|
|
85
|
+
return;
|
|
86
|
+
case 'object': {
|
|
87
|
+
const obj = val;
|
|
88
|
+
if (val instanceof Set) {
|
|
89
|
+
recur(Array.from(val));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (val instanceof Map) {
|
|
93
|
+
recur(Array.from(val.values()));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (obj === null || obj === void 0 ? void 0 : obj.values) {
|
|
97
|
+
recur(obj.values);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (obj === null || obj === void 0 ? void 0 : obj.Value) {
|
|
101
|
+
recur(obj.Value);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
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();
|
|
105
|
+
if (s)
|
|
106
|
+
pushFromString(s);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
default: return;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
recur(input);
|
|
113
|
+
return uniq(out);
|
|
114
|
+
};
|
|
39
115
|
async function dealUpdate(i, returnData) {
|
|
40
116
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
41
117
|
// ---- Required Id ----
|
|
@@ -79,6 +155,9 @@ async function dealUpdate(i, returnData) {
|
|
|
79
155
|
const status = this.getNodeParameter('Status', i, '');
|
|
80
156
|
if (!status)
|
|
81
157
|
throw new Error('Status is required.');
|
|
158
|
+
// LabelIds (اختیاری)
|
|
159
|
+
const labelIdsInput = this.getNodeParameter('LabelIds', i, []);
|
|
160
|
+
const labelIds = parseIdList(labelIdsInput);
|
|
82
161
|
// --- Additional Fields (collection) ---
|
|
83
162
|
const add = this.getNodeParameter('additionalFields', i, {}) || {};
|
|
84
163
|
const description = (_a = add.Description) !== null && _a !== void 0 ? _a : '';
|
|
@@ -109,6 +188,7 @@ async function dealUpdate(i, returnData) {
|
|
|
109
188
|
const bodyDeal = {
|
|
110
189
|
Id: dealId,
|
|
111
190
|
OwnerId: ownerId,
|
|
191
|
+
...(labelIds.length ? { LabelIds: labelIds } : {}),
|
|
112
192
|
PersonId: personId,
|
|
113
193
|
CompanyId: companyId,
|
|
114
194
|
SourceId: sourceId,
|
|
@@ -13,6 +13,7 @@ exports.dealUpdateProperties = [
|
|
|
13
13
|
},
|
|
14
14
|
(0, _shared_fields_1.titleField)('update'),
|
|
15
15
|
...(0, _shared_fields_1.ownerFields)('update'),
|
|
16
|
+
(0, _shared_fields_1.labelIdsField)('update'),
|
|
16
17
|
...(0, _shared_fields_1.pipelineFields)('update'),
|
|
17
18
|
...(0, _shared_fields_1.stageFields)('update'),
|
|
18
19
|
...(0, _shared_fields_1.personCompanyFields)('update'),
|
|
@@ -7,6 +7,8 @@ async function noteCreate(i, returnData) {
|
|
|
7
7
|
var _a;
|
|
8
8
|
// 1) ورودیها
|
|
9
9
|
const resultNote = this.getNodeParameter('ResultNote', i);
|
|
10
|
+
// Tags: دریافت ورودی اختیاری
|
|
11
|
+
const rawTags = this.getNodeParameter('Tags', i, []);
|
|
10
12
|
const ownerMode = this.getNodeParameter('OwnerMode', i);
|
|
11
13
|
const ownerIdSel = this.getNodeParameter('OwnerIdSelect', i, '');
|
|
12
14
|
const ownerIdMan = this.getNodeParameter('OwnerIdManual', i, '');
|
|
@@ -102,6 +104,7 @@ async function noteCreate(i, returnData) {
|
|
|
102
104
|
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
103
105
|
};
|
|
104
106
|
const contactIdsArr = parseIdList(rawContactIds);
|
|
107
|
+
const tagIdsArr = parseIdList(rawTags);
|
|
105
108
|
// 2) اعتبارسنجیهای ضروری
|
|
106
109
|
if (!(resultNote === null || resultNote === void 0 ? void 0 : resultNote.trim())) {
|
|
107
110
|
throw new Error('ResultNote is required for Note creation.');
|
|
@@ -131,6 +134,10 @@ async function noteCreate(i, returnData) {
|
|
|
131
134
|
CaseId: caseId || ZERO_GUID,
|
|
132
135
|
},
|
|
133
136
|
};
|
|
137
|
+
// اگر ResultNote مقدار دارد و Tags هم وارد شده، به بدنه اضافه میکنیم
|
|
138
|
+
if ((resultNote === null || resultNote === void 0 ? void 0 : resultNote.trim()) && tagIdsArr.length) {
|
|
139
|
+
body.Activity.Tags = tagIdsArr.map((id) => ({ TagId: id }));
|
|
140
|
+
}
|
|
134
141
|
// 5) ارسال درخواست
|
|
135
142
|
const resp = await (0, http_1.didarRequest)(this, i, {
|
|
136
143
|
method: 'POST',
|
|
@@ -14,6 +14,16 @@ exports.noteCreateProperties = [
|
|
|
14
14
|
displayOptions: showForNoteCreate,
|
|
15
15
|
description: 'The text content of the note. Required.',
|
|
16
16
|
},
|
|
17
|
+
// --- Tags (optional; only sent if ResultNote is present) ---
|
|
18
|
+
{
|
|
19
|
+
displayName: 'Tags',
|
|
20
|
+
name: 'Tags',
|
|
21
|
+
type: 'string',
|
|
22
|
+
typeOptions: { multipleValues: true },
|
|
23
|
+
default: [],
|
|
24
|
+
displayOptions: showForNoteCreate,
|
|
25
|
+
description: 'List of Tag IDs (GUID). Accepts multiple entries, CSV/comma/semicolon, new lines, or a JSON array (e.g. ["id1","id2"]). Sent only if "Result Note" is provided.',
|
|
26
|
+
},
|
|
17
27
|
// --- Owner (Select/Manual) ---
|
|
18
28
|
{
|
|
19
29
|
displayName: 'Owner Input Mode',
|
|
@@ -8,6 +8,8 @@ async function noteUpdate(i, returnData) {
|
|
|
8
8
|
// 1) Parameters
|
|
9
9
|
const id = this.getNodeParameter('Id', i);
|
|
10
10
|
const resultNote = this.getNodeParameter('ResultNote', i);
|
|
11
|
+
// Tags: دریافت ورودی اختیاری
|
|
12
|
+
const rawTags = this.getNodeParameter('Tags', i, []);
|
|
11
13
|
const ownerMode = this.getNodeParameter('OwnerMode', i);
|
|
12
14
|
const ownerIdSel = this.getNodeParameter('OwnerIdSelect', i, '');
|
|
13
15
|
const ownerIdMan = this.getNodeParameter('OwnerIdManual', i, '');
|
|
@@ -103,6 +105,7 @@ async function noteUpdate(i, returnData) {
|
|
|
103
105
|
return Array.from(new Set(out.map((x) => x.trim()).filter(Boolean)));
|
|
104
106
|
};
|
|
105
107
|
const contactIdsArr = parseIdList(rawContactIds);
|
|
108
|
+
const tagIdsArr = parseIdList(rawTags);
|
|
106
109
|
// 2) Required validations
|
|
107
110
|
if (!(id === null || id === void 0 ? void 0 : id.trim())) {
|
|
108
111
|
throw new Error('Note ID is required for Note update.');
|
|
@@ -136,6 +139,10 @@ async function noteUpdate(i, returnData) {
|
|
|
136
139
|
CaseId: caseId || ZERO_GUID,
|
|
137
140
|
},
|
|
138
141
|
};
|
|
142
|
+
// اگر ResultNote مقدار دارد و Tags هم وارد شده، به بدنه اضافه میکنیم
|
|
143
|
+
if ((resultNote === null || resultNote === void 0 ? void 0 : resultNote.trim()) && tagIdsArr.length) {
|
|
144
|
+
body.Activity.Tags = tagIdsArr.map((id) => ({ TagId: id }));
|
|
145
|
+
}
|
|
139
146
|
// 5) Request
|
|
140
147
|
const resp = await (0, http_1.didarRequest)(this, i, {
|
|
141
148
|
method: 'POST',
|
|
@@ -24,6 +24,16 @@ exports.noteUpdateProperties = [
|
|
|
24
24
|
displayOptions: showForNoteUpdate,
|
|
25
25
|
description: 'The text content of the note. Required.',
|
|
26
26
|
},
|
|
27
|
+
// --- Tags (optional; only sent if ResultNote is present) ---
|
|
28
|
+
{
|
|
29
|
+
displayName: 'Tags',
|
|
30
|
+
name: 'Tags',
|
|
31
|
+
type: 'string',
|
|
32
|
+
typeOptions: { multipleValues: true },
|
|
33
|
+
default: [],
|
|
34
|
+
displayOptions: showForNoteUpdate,
|
|
35
|
+
description: 'List of Tag IDs (GUID). Accepts multiple entries, CSV/comma/semicolon, new lines, or a JSON array (e.g. ["id1","id2"]). Sent only if "Result Note" is provided.',
|
|
36
|
+
},
|
|
27
37
|
// --- Owner (Select/Manual) ---
|
|
28
38
|
{
|
|
29
39
|
displayName: 'Owner Input Mode',
|
|
@@ -9,6 +9,7 @@ async function getBaseInfo(i, returnData) {
|
|
|
9
9
|
deal_pipelines: { path: '/api/pipeline/list/0', method: 'POST' },
|
|
10
10
|
case_pipelines: { path: '/api/pipeline/list/1', method: 'POST' },
|
|
11
11
|
activityTypes: { path: '/api/activity/GetActivityType', method: 'POST' },
|
|
12
|
+
user_list: { path: '/api/User/List', method: 'POST' },
|
|
12
13
|
customFields: { path: '/api/customfield/GetCustomfieldList', method: 'POST' },
|
|
13
14
|
productCategories: { path: '/api/product/categories', method: 'POST' },
|
|
14
15
|
};
|
|
@@ -11,6 +11,7 @@ exports.getBaseInfoProperties = [
|
|
|
11
11
|
{ name: 'Deal Pipelines', value: 'deal_pipelines' },
|
|
12
12
|
{ name: 'Case Pipelines', value: 'case_pipelines' },
|
|
13
13
|
{ name: 'Activity Types', value: 'activityTypes' },
|
|
14
|
+
{ name: 'User List', value: 'user_list' },
|
|
14
15
|
{ name: 'Custom Fields List', value: 'customFields' },
|
|
15
16
|
{ name: 'Product Categories', value: 'productCategories' },
|
|
16
17
|
],
|