n8n-nodes-prestashop8 1.2.0 → 1.2.3

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.
@@ -154,16 +154,23 @@ exports.PrestaShop8Description = {
154
154
  placeholder: '[id_DESC] ou [name_ASC]',
155
155
  description: 'Sort criteria (e.g. [id_DESC], [name_ASC], [date_add_DESC])',
156
156
  },
157
+ {
158
+ displayName: 'Date Format',
159
+ name: 'dateFormat',
160
+ type: 'boolean',
161
+ default: false,
162
+ description: 'Enable date processing in filters (adds date=1 parameter)',
163
+ },
157
164
  ],
158
165
  },
159
- // Display options (always visible for list/search operations)
166
+ // Display options (always visible for list/search/getById operations)
160
167
  {
161
168
  displayName: 'Display',
162
169
  name: 'display',
163
170
  type: 'options',
164
171
  displayOptions: {
165
172
  show: {
166
- operation: ['list', 'search'],
173
+ operation: ['list', 'search', 'getById'],
167
174
  },
168
175
  },
169
176
  options: [
@@ -193,7 +200,7 @@ exports.PrestaShop8Description = {
193
200
  type: 'string',
194
201
  displayOptions: {
195
202
  show: {
196
- operation: ['list', 'search'],
203
+ operation: ['list', 'search', 'getById'],
197
204
  display: ['custom'],
198
205
  },
199
206
  },
@@ -226,9 +233,13 @@ exports.PrestaShop8Description = {
226
233
  name: 'field',
227
234
  type: 'string',
228
235
  default: '',
229
- required: true,
230
236
  placeholder: 'name, reference, price, etc.',
231
237
  description: 'Name of field to filter. Examples: name, reference, price, active, id_manufacturer, etc.',
238
+ displayOptions: {
239
+ hide: {
240
+ operator: ['CUSTOM'],
241
+ },
242
+ },
232
243
  },
233
244
  {
234
245
  displayName: 'Operator',
@@ -237,16 +248,33 @@ exports.PrestaShop8Description = {
237
248
  options: types_1.FILTER_OPERATORS,
238
249
  default: '=',
239
250
  noDataExpression: true,
240
- description: 'Comparison operator for filtering. Available operators: = (exact match), LIKE (contains text), > (greater than), >= (greater or equal), < (less than), <= (less or equal), != (not equal), BEGINS (starts with), ENDS (ends with)',
251
+ description: 'Comparison operator for filtering. Custom allows you to write your own filter expression.',
241
252
  },
242
253
  {
243
254
  displayName: 'Value',
244
255
  name: 'value',
245
256
  type: 'string',
246
257
  default: '',
247
- required: true,
248
- placeholder: 'search value',
249
- description: 'Value to search for. Examples: "Product Name" (for LIKE), "1" (for active), "29.99" (for price), etc.',
258
+ placeholder: 'search value or interval',
259
+ description: 'Value to search for. Examples: "Product Name", "1" (active), "29.99" (price). For Equal/Not Equal: use "10,20" for intervals (becomes [10,20]).',
260
+ displayOptions: {
261
+ hide: {
262
+ operator: ['IS_EMPTY', 'IS_NOT_EMPTY', 'CUSTOM'],
263
+ },
264
+ },
265
+ },
266
+ {
267
+ displayName: 'Custom Filter Expression',
268
+ name: 'customFilter',
269
+ type: 'string',
270
+ default: '',
271
+ placeholder: 'filter[name]=[Product]%',
272
+ description: 'Complete filter expression in PrestaShop format. Example: filter[name]=[Product]%, filter[price]=>[100], etc.',
273
+ displayOptions: {
274
+ show: {
275
+ operator: ['CUSTOM'],
276
+ },
277
+ },
250
278
  },
251
279
  ],
252
280
  },
@@ -28,31 +28,18 @@ const n8n_workflow_1 = require("n8n-workflow");
28
28
  const PrestaShop8_node_description_1 = require("./PrestaShop8.node.description");
29
29
  const types_1 = require("./types");
30
30
  const utils_1 = require("./utils");
31
- // Helper function to build headers based on raw mode (backward compatibility)
32
- function buildHeaders(rawMode) {
33
- const headers = {};
34
- if (rawMode) {
35
- // En mode Raw, demandons explicitement le XML
36
- headers['Output-Format'] = 'XML';
37
- }
38
- else {
39
- headers['Output-Format'] = 'JSON';
40
- }
41
- return headers;
42
- }
31
+ const fieldMappings_1 = require("./fieldMappings");
43
32
  /**
44
- * Build common HTTP request options
33
+ * Build HTTP request options with appropriate headers (consolidated)
45
34
  */
46
35
  function buildHttpOptions(method, url, credentials, rawMode, timeout, body) {
47
36
  const headers = {};
48
- // Only add Content-Type for requests with body (POST, PATCH, PUT)
49
- if (body && (method === 'POST' || method === 'PATCH' || method === 'PUT')) {
37
+ // Add Content-Type for requests with body (POST, PATCH, PUT)
38
+ if (body && ['POST', 'PATCH', 'PUT'].includes(method)) {
50
39
  headers['Content-Type'] = 'application/xml';
51
40
  }
52
- // Add Output-Format for non-raw mode
53
- if (!rawMode) {
54
- headers['Output-Format'] = 'JSON';
55
- }
41
+ // Add Output-Format header based on mode
42
+ headers['Output-Format'] = rawMode ? 'XML' : 'JSON';
56
43
  return {
57
44
  method,
58
45
  url,
@@ -108,35 +95,7 @@ async function executeHttpRequest(helpers, options, credentials, rawMode, operat
108
95
  */
109
96
  function collectRequiredFields(executeFunctions, resource, itemIndex) {
110
97
  const fieldsToCreate = [];
111
- const fieldMappings = {
112
- products: {
113
- productName: 'name-1',
114
- productPrice: 'price',
115
- productCategoryId: 'id_category_default'
116
- },
117
- categories: {
118
- categoryName: 'name-1',
119
- categoryParentId: 'id_parent'
120
- },
121
- customers: {
122
- customerFirstname: 'firstname',
123
- customerLastname: 'lastname',
124
- customerEmail: 'email'
125
- },
126
- addresses: {
127
- addressFirstname: 'firstname',
128
- addressLastname: 'lastname',
129
- addressAddress1: 'address1',
130
- addressCity: 'city',
131
- addressCountryId: 'id_country',
132
- addressCustomerId: 'id_customer'
133
- },
134
- manufacturers: {
135
- manufacturerName: 'name',
136
- manufacturerActive: 'active'
137
- }
138
- };
139
- const mappings = fieldMappings[resource];
98
+ const mappings = (0, fieldMappings_1.getFieldMappingsForResource)(resource);
140
99
  if (mappings) {
141
100
  for (const [inputName, fieldName] of Object.entries(mappings)) {
142
101
  const value = executeFunctions.getNodeParameter(inputName, itemIndex, '');
@@ -311,12 +270,20 @@ class PrestaShop8 {
311
270
  const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
312
271
  const display = this.getNodeParameter('display', i, 'full');
313
272
  const customFields = this.getNodeParameter('customFields', i, '');
314
- const displayValue = display === 'custom' ? customFields : display;
315
- requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, {
273
+ const displayValue = (0, utils_1.processDisplayParameter)(display, resource, customFields);
274
+ const urlParams = {
316
275
  limit: advancedOptions.limit,
317
276
  sort: advancedOptions.sort,
318
- display: displayValue,
319
- }, rawMode);
277
+ };
278
+ // Add date=1 parameter if Date Format option is enabled
279
+ if (advancedOptions.dateFormat) {
280
+ urlParams.date = 1;
281
+ }
282
+ // Only add display parameter if not null (minimal mode returns null)
283
+ if (displayValue !== null) {
284
+ urlParams.display = displayValue;
285
+ }
286
+ requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, urlParams, rawMode);
320
287
  const timeout = this.getNodeParameter('options.timeout', i, 30000);
321
288
  const neverError = this.getNodeParameter('options.response.neverError', i, false);
322
289
  const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
@@ -331,7 +298,7 @@ class PrestaShop8 {
331
298
  username: credentials.apiKey,
332
299
  password: ''
333
300
  },
334
- headers: buildHeaders(rawMode),
301
+ headers: buildHttpOptions('GET', requestUrl, credentials, rawMode, timeout || 30000).headers,
335
302
  timeout: timeout || 30000,
336
303
  transformResponse: [(data) => data],
337
304
  validateStatus: neverError ? () => true : undefined // Accept all status codes if neverError is true
@@ -339,7 +306,7 @@ class PrestaShop8 {
339
306
  requestDebugInfo = captureRequestDebugInfo({
340
307
  method: 'GET',
341
308
  url: requestUrl,
342
- headers: buildHeaders(rawMode),
309
+ headers: buildHttpOptions('GET', requestUrl, credentials, rawMode, timeout || 30000).headers,
343
310
  timeout: timeout
344
311
  }, credentials, rawMode, operation, resource);
345
312
  requestHeaders = requestDebugInfo.headers;
@@ -402,13 +369,18 @@ class PrestaShop8 {
402
369
  }
403
370
  case 'getById': {
404
371
  const id = this.getNodeParameter('id', i);
405
- const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
372
+ const display = this.getNodeParameter('display', i, 'full');
373
+ const customFields = this.getNodeParameter('customFields', i, '');
406
374
  if (!id) {
407
375
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'ID required for this operation');
408
376
  }
409
- requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}/${id}`, {
410
- display: advancedOptions.display === 'custom' ? advancedOptions.customFields : advancedOptions.display,
411
- }, rawMode);
377
+ const displayValue = (0, utils_1.processDisplayParameter)(display, resource, customFields);
378
+ const urlParams = {};
379
+ // Only add display parameter if not null (minimal mode returns null)
380
+ if (displayValue !== null) {
381
+ urlParams.display = displayValue;
382
+ }
383
+ requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}/${id}`, urlParams, rawMode);
412
384
  const timeout = this.getNodeParameter('options.timeout', i, 30000);
413
385
  const neverError = this.getNodeParameter('options.response.neverError', i, false);
414
386
  const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
@@ -537,6 +509,11 @@ class PrestaShop8 {
537
509
  const advancedOptions = this.getNodeParameter('advancedOptions', i, {});
538
510
  const display = this.getNodeParameter('display', i, 'full');
539
511
  const customFields = this.getNodeParameter('customFields', i, '');
512
+ // Debug: Log the filters parameter structure
513
+ const showRequestInfo = this.getNodeParameter('options.request.showRequestInfo', i, false);
514
+ if (showRequestInfo) {
515
+ console.log('DEBUG - filtersParam:', JSON.stringify(filtersParam, null, 2));
516
+ }
540
517
  const filters = filtersParam.filter || [];
541
518
  if (!filters.length) {
542
519
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'At least one filter is required for search');
@@ -544,38 +521,123 @@ class PrestaShop8 {
544
521
  // Build filter parameters for URL
545
522
  const filterParams = {};
546
523
  for (const filter of filters) {
547
- if (filter.value && filter.value.trim()) {
548
- const key = `filter[${filter.field}]`;
549
- // Handle different operators
550
- switch (filter.operator) {
551
- case 'equals':
552
- filterParams[key] = `[${filter.value}]`;
553
- break;
554
- case 'contains':
555
- filterParams[key] = `[${filter.value}]%`;
556
- break;
557
- case 'starts_with':
524
+ if (showRequestInfo) {
525
+ console.log('DEBUG - Processing filter:', JSON.stringify(filter, null, 2));
526
+ }
527
+ // Handle CUSTOM filter operator
528
+ if (filter.operator === 'CUSTOM') {
529
+ if (filter.customFilter && filter.customFilter.trim()) {
530
+ // Add custom filter directly to URL without any interpretation
531
+ // User writes exactly what they want: date=1, filter[name]=test, etc.
532
+ const customFilter = filter.customFilter.trim();
533
+ // Parse the custom filter to extract key=value pairs for URL construction
534
+ const parts = customFilter.split('&');
535
+ for (const part of parts) {
536
+ const [key, value] = part.split('=', 2);
537
+ if (key && value !== undefined) {
538
+ filterParams[key.trim()] = value.trim();
539
+ }
540
+ }
541
+ }
542
+ continue;
543
+ }
544
+ const key = `filter[${filter.field}]`;
545
+ // Handle different operators with correct PrestaShop formats
546
+ switch (filter.operator) {
547
+ case '=':
548
+ if (filter.value && filter.value.trim()) {
549
+ const value = filter.value.trim();
550
+ // Check if value contains comma for interval (e.g., "10,20" → "[10,20]")
551
+ if (value.includes(',') && !value.startsWith('[')) {
552
+ filterParams[key] = `[${value}]`;
553
+ }
554
+ else {
555
+ filterParams[key] = `[${value}]`;
556
+ }
557
+ }
558
+ break;
559
+ case '!=':
560
+ if (filter.value && filter.value.trim()) {
561
+ const value = filter.value.trim();
562
+ // Check if value contains comma for interval (e.g., "10,20" → "![10,20]")
563
+ if (value.includes(',') && !value.startsWith('[')) {
564
+ filterParams[key] = `![${value}]`;
565
+ }
566
+ else {
567
+ filterParams[key] = `![${value}]`;
568
+ }
569
+ }
570
+ break;
571
+ case '>':
572
+ if (filter.value && filter.value.trim()) {
573
+ filterParams[key] = `>[${filter.value}]`;
574
+ }
575
+ break;
576
+ case '>=':
577
+ if (filter.value && filter.value.trim()) {
578
+ filterParams[key] = `>=[${filter.value}]`;
579
+ }
580
+ break;
581
+ case '<':
582
+ if (filter.value && filter.value.trim()) {
583
+ filterParams[key] = `<[${filter.value}]`;
584
+ }
585
+ break;
586
+ case '<=':
587
+ if (filter.value && filter.value.trim()) {
588
+ filterParams[key] = `<=[${filter.value}]`;
589
+ }
590
+ break;
591
+ case 'CONTAINS':
592
+ if (filter.value && filter.value.trim()) {
593
+ filterParams[key] = `%[${filter.value}]%`;
594
+ }
595
+ break;
596
+ case 'BEGINS':
597
+ if (filter.value && filter.value.trim()) {
558
598
  filterParams[key] = `[${filter.value}]%`;
559
- break;
560
- case 'greater_than':
561
- filterParams[key] = `[${filter.value},]`;
562
- break;
563
- case 'less_than':
564
- filterParams[key] = `[,${filter.value}]`;
565
- break;
566
- default:
599
+ }
600
+ break;
601
+ case 'ENDS':
602
+ if (filter.value && filter.value.trim()) {
603
+ filterParams[key] = `%[${filter.value}]`;
604
+ }
605
+ break;
606
+ case 'IS_EMPTY':
607
+ filterParams[key] = `[]`;
608
+ break;
609
+ case 'IS_NOT_EMPTY':
610
+ filterParams[key] = `![]`;
611
+ break;
612
+ default:
613
+ if (filter.value && filter.value.trim()) {
567
614
  filterParams[key] = `[${filter.value}]`;
568
- }
615
+ }
569
616
  }
570
617
  }
571
618
  // Handle display parameter
572
619
  const displayValue = (0, utils_1.processDisplayParameter)(display, resource, customFields);
573
- requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, {
620
+ const urlParams = {
574
621
  ...filterParams,
575
622
  limit: advancedOptions.limit,
576
623
  sort: advancedOptions.sort,
577
- display: displayValue,
578
- }, rawMode);
624
+ };
625
+ // Add date=1 parameter if Date Format option is enabled
626
+ if (advancedOptions.dateFormat) {
627
+ urlParams.date = 1;
628
+ }
629
+ // Only add display parameter if not null (minimal mode returns null)
630
+ if (displayValue !== null) {
631
+ urlParams.display = displayValue;
632
+ }
633
+ if (showRequestInfo) {
634
+ console.log('DEBUG - Final filterParams:', JSON.stringify(filterParams, null, 2));
635
+ console.log('DEBUG - Final urlParams:', JSON.stringify(urlParams, null, 2));
636
+ }
637
+ requestUrl = (0, utils_1.buildUrlWithFilters)(`${credentials.baseUrl}/${resource}`, urlParams, rawMode);
638
+ if (showRequestInfo) {
639
+ console.log('DEBUG - Final URL:', requestUrl);
640
+ }
579
641
  const timeout = this.getNodeParameter('options.timeout', i, 30000);
580
642
  const neverError = this.getNodeParameter('options.response.neverError', i, false);
581
643
  const includeResponseHeaders = this.getNodeParameter('options.response.includeResponseHeaders', i, false);
@@ -590,7 +652,7 @@ class PrestaShop8 {
590
652
  username: credentials.apiKey,
591
653
  password: ''
592
654
  },
593
- headers: buildHeaders(rawMode),
655
+ headers: buildHttpOptions('GET', requestUrl, credentials, rawMode, timeout || 30000).headers,
594
656
  timeout: timeout || 30000,
595
657
  transformResponse: [(data) => data],
596
658
  validateStatus: neverError ? () => true : undefined // Accept all status codes if neverError is true
@@ -598,7 +660,7 @@ class PrestaShop8 {
598
660
  requestDebugInfo = captureRequestDebugInfo({
599
661
  method: 'GET',
600
662
  url: requestUrl,
601
- headers: buildHeaders(rawMode),
663
+ headers: buildHttpOptions('GET', requestUrl, credentials, rawMode, timeout || 30000).headers,
602
664
  timeout: timeout
603
665
  }, credentials, rawMode, operation, resource);
604
666
  requestHeaders = requestDebugInfo.headers;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Centralized field mappings for PrestaShop resources
3
+ * Eliminates duplication between node and utils files
4
+ */
5
+ export interface FieldMapping {
6
+ [inputName: string]: string;
7
+ }
8
+ export interface ResourceFieldMappings {
9
+ [resource: string]: FieldMapping;
10
+ }
11
+ /**
12
+ * Input field name to PrestaShop field name mappings for CREATE operations
13
+ */
14
+ export declare const CREATE_FIELD_MAPPINGS: ResourceFieldMappings;
15
+ /**
16
+ * CamelCase to PrestaShop field name mappings for UPDATE operations
17
+ */
18
+ export declare const CAMELCASE_TO_PRESTASHOP_MAPPINGS: {
19
+ [key: string]: string;
20
+ };
21
+ /**
22
+ * Convert camelCase field name to PrestaShop format
23
+ */
24
+ export declare function convertFromCamelCase(fieldName: string): string;
25
+ /**
26
+ * Get field mappings for a specific resource
27
+ */
28
+ export declare function getFieldMappingsForResource(resource: string): FieldMapping | null;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ /**
3
+ * Centralized field mappings for PrestaShop resources
4
+ * Eliminates duplication between node and utils files
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getFieldMappingsForResource = exports.convertFromCamelCase = exports.CAMELCASE_TO_PRESTASHOP_MAPPINGS = exports.CREATE_FIELD_MAPPINGS = void 0;
8
+ /**
9
+ * Input field name to PrestaShop field name mappings for CREATE operations
10
+ */
11
+ exports.CREATE_FIELD_MAPPINGS = {
12
+ products: {
13
+ productName: 'name-1',
14
+ productPrice: 'price',
15
+ productCategoryId: 'id_category_default'
16
+ },
17
+ categories: {
18
+ categoryName: 'name-1',
19
+ categoryParentId: 'id_parent'
20
+ },
21
+ customers: {
22
+ customerFirstname: 'firstname',
23
+ customerLastname: 'lastname',
24
+ customerEmail: 'email'
25
+ },
26
+ addresses: {
27
+ addressFirstname: 'firstname',
28
+ addressLastname: 'lastname',
29
+ addressAddress1: 'address1',
30
+ addressCity: 'city',
31
+ addressCountryId: 'id_country',
32
+ addressCustomerId: 'id_customer'
33
+ },
34
+ manufacturers: {
35
+ manufacturerName: 'name',
36
+ manufacturerActive: 'active'
37
+ }
38
+ };
39
+ /**
40
+ * CamelCase to PrestaShop field name mappings for UPDATE operations
41
+ */
42
+ exports.CAMELCASE_TO_PRESTASHOP_MAPPINGS = {
43
+ 'manufacturerId': 'id_manufacturer',
44
+ 'categoryId': 'id_category',
45
+ 'supplierId': 'id_supplier',
46
+ 'customerId': 'id_customer',
47
+ 'addressId': 'id_address',
48
+ 'countryId': 'id_country',
49
+ 'stateId': 'id_state',
50
+ 'zoneId': 'id_zone',
51
+ 'currencyId': 'id_currency',
52
+ 'languageId': 'id_language',
53
+ 'groupId': 'id_group',
54
+ };
55
+ /**
56
+ * Convert camelCase field name to PrestaShop format
57
+ */
58
+ function convertFromCamelCase(fieldName) {
59
+ // Check specific mappings first
60
+ if (exports.CAMELCASE_TO_PRESTASHOP_MAPPINGS[fieldName]) {
61
+ return exports.CAMELCASE_TO_PRESTASHOP_MAPPINGS[fieldName];
62
+ }
63
+ // Generic camelCase to snake_case conversion
64
+ return fieldName
65
+ .replace(/([A-Z])/g, '_$1')
66
+ .toLowerCase()
67
+ .replace(/^_/, ''); // Remove leading underscore
68
+ }
69
+ exports.convertFromCamelCase = convertFromCamelCase;
70
+ /**
71
+ * Get field mappings for a specific resource
72
+ */
73
+ function getFieldMappingsForResource(resource) {
74
+ return exports.CREATE_FIELD_MAPPINGS[resource] || null;
75
+ }
76
+ exports.getFieldMappingsForResource = getFieldMappingsForResource;
@@ -6,6 +6,7 @@ export interface IPrestaShopFilter {
6
6
  field: string;
7
7
  operator: string;
8
8
  value: string;
9
+ customFilter?: string;
9
10
  }
10
11
  export interface IPrestaShopRequestOptions {
11
12
  resource: string;
@@ -360,8 +360,10 @@ exports.FILTER_OPERATORS = [
360
360
  { name: '≥ Greater than or equal to', value: '>=' },
361
361
  { name: '< Less than', value: '<' },
362
362
  { name: '≤ Less than or equal to', value: '<=' },
363
- { name: '∋ Contains', value: 'LIKE' },
364
- { name: '∌ Does not contain', value: 'NOT LIKE' },
363
+ { name: '∋ Contains', value: 'CONTAINS' },
365
364
  { name: '↦ Starts with', value: 'BEGINS' },
366
365
  { name: '↤ Ends with', value: 'ENDS' },
366
+ { name: '∅ Is Empty', value: 'IS_EMPTY' },
367
+ { name: '∄ Is not Empty', value: 'IS_NOT_EMPTY' },
368
+ { name: '⚙️ Custom Filter', value: 'CUSTOM' },
367
369
  ];
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.validateDataForResource = exports.validateFieldsForCreate = exports.REQUIRED_FIELDS_BY_RESOURCE = exports.buildUpdateXml = exports.buildCreateXml = exports.buildUrlWithFilters = exports.parseXmlToJson = exports.buildPrestashopXml = exports.simplifyPrestashopResponse = exports.processResponseForMode = exports.processDisplayParameter = exports.processSortParameter = exports.extractPrestashopError = void 0;
27
27
  const xml2js = __importStar(require("xml2js"));
28
28
  const js2xmlparser = __importStar(require("js2xmlparser"));
29
+ const fieldMappings_1 = require("./fieldMappings");
29
30
  /**
30
31
  * Clean error message by removing PHP warnings and debug info
31
32
  */
@@ -67,10 +68,8 @@ function extractPrestashopError(error) {
67
68
  // If it's an axios error with response
68
69
  if (error.response && error.response.data) {
69
70
  const data = error.response.data;
70
- // Debug logging for complex error structures
71
- if (typeof data === 'object' && data !== null) {
72
- console.log('PrestaShop Error Structure:', JSON.stringify(data, null, 2));
73
- }
71
+ // Debug logging for complex error structures (only in debug mode)
72
+ // Note: Removed console.log to prevent production log pollution
74
73
  // Try to parse XML error response
75
74
  if (typeof data === 'string' && data.includes('<error>')) {
76
75
  const errorMatch = data.match(/<error><!\[CDATA\[(.+?)\]\]><\/error>/);
@@ -342,7 +341,7 @@ function convertSimplifiedToPrestaShop(data, resource) {
342
341
  const converted = {};
343
342
  const associations = {};
344
343
  for (const [key, value] of Object.entries(data)) {
345
- const prestashopKey = convertFromCamelCase(key);
344
+ const prestashopKey = (0, fieldMappings_1.convertFromCamelCase)(key);
346
345
  // Detect associations (arrays of IDs)
347
346
  if (Array.isArray(value) && value.every(v => typeof v === 'number' || typeof v === 'string')) {
348
347
  associations[prestashopKey] = {
@@ -359,38 +358,7 @@ function convertSimplifiedToPrestaShop(data, resource) {
359
358
  }
360
359
  return converted;
361
360
  }
362
- /**
363
- * Converts camelCase to snake_case with PrestaShop prefixes
364
- */
365
- function convertFromCamelCase(fieldName) {
366
- // Map certain special fields
367
- const fieldMappings = {
368
- 'manufacturerId': 'id_manufacturer',
369
- 'categoryId': 'id_category',
370
- 'supplierId': 'id_supplier',
371
- 'customerId': 'id_customer',
372
- 'orderId': 'id_order',
373
- 'productId': 'id_product',
374
- 'carrierId': 'id_carrier',
375
- 'currencyId': 'id_currency',
376
- 'languageId': 'id_language',
377
- 'shopId': 'id_shop',
378
- 'taxId': 'id_tax',
379
- 'zoneId': 'id_zone',
380
- 'countryId': 'id_country',
381
- 'stateId': 'id_state',
382
- 'addressId': 'id_address',
383
- 'groupId': 'id_group',
384
- };
385
- if (fieldMappings[fieldName]) {
386
- return fieldMappings[fieldName];
387
- }
388
- // Generic camelCase to snake_case conversion
389
- return fieldName
390
- .replace(/([A-Z])/g, '_$1')
391
- .toLowerCase()
392
- .replace(/^_/, '');
393
- }
361
+ // convertFromCamelCase function moved to fieldMappings.ts for centralization
394
362
  /**
395
363
  * Obtient tous les champs string pour CDATA
396
364
  */
@@ -429,30 +397,21 @@ exports.parseXmlToJson = parseXmlToJson;
429
397
  function buildUrlWithFilters(baseUrl, options, rawMode) {
430
398
  const url = new URL(baseUrl);
431
399
  const params = new URLSearchParams();
432
- // Ajouter les filtres
433
- if (options.filters && Array.isArray(options.filters)) {
434
- for (const filter of options.filters) {
435
- if (filter.field && filter.value) {
436
- const filterKey = `filter[${filter.field}]`;
437
- let filterValue = filter.value;
438
- // Apply operator if it's not '='
439
- if (filter.operator && filter.operator !== '=') {
440
- filterValue = `[${filter.operator}]${filterValue}`;
441
- }
442
- params.append(filterKey, filterValue);
400
+ // Add all parameters from options object
401
+ for (const [key, value] of Object.entries(options)) {
402
+ if (value !== null && value !== undefined && value !== '') {
403
+ if (key === 'sort') {
404
+ // Special handling for sort parameter
405
+ const normalizedSort = processSortParameter(String(value));
406
+ if (normalizedSort)
407
+ params.append('sort', normalizedSort);
408
+ }
409
+ else {
410
+ // Add all other parameters as-is (including custom user parameters)
411
+ params.append(key, String(value));
443
412
  }
444
413
  }
445
414
  }
446
- // Add other parameters
447
- if (options.limit)
448
- params.append('limit', options.limit);
449
- if (options.sort) {
450
- const normalizedSort = processSortParameter(options.sort);
451
- if (normalizedSort)
452
- params.append('sort', normalizedSort);
453
- }
454
- if (options.display !== null && options.display !== undefined)
455
- params.append('display', options.display);
456
415
  // Ajouter output_format seulement si pas en mode Raw
457
416
  if (!rawMode) {
458
417
  params.append('output_format', 'JSON');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-prestashop8",
3
- "version": "1.2.0",
3
+ "version": "1.2.3",
4
4
  "description": "Nœud n8n personnalisé pour PrestaShop 8 avec support CRUD complet et conversion XML/JSON automatique",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",