n8n-nodes-didar-crm 0.0.23 → 0.0.24

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.
@@ -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
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-didar-crm",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "description": "Didar CRM nodes for n8n (Trigger + Deal.create)",
5
5
  "author": "You",
6
6
  "license": "MIT",