PageSourceSearch

https://app.pymo.uy/js/controllers/items-batch-upload.js?v=4.0.8

js pymo.uy collected 2026-10-04 22:29:17 UTC 9,608 bytes, 270 lines download raw bytes

1materialAdmin
2
3  //=================================================
4  // ITEMS BATCH UPLOAD
5  //=================================================
6  .controller('itemsBatchUploadCtrl', function ($scope, $location, growlService, Item, Utils, Session) {
7
8    var _this = this;
9
10    $scope.primaryButtonHandler = (addonName) => Utils.requestAddon(addonName);
11    const user = Session.get();
12    $scope.isSalesReportEnabled = user?.company?.addons?.includes("stock");
13
14    // Inicializar formulario
15    _this.form = {};
16    _this.uploading = false;
17    _this.uploadResult = null;
18    _this.errorCsvContent = null;
19
20    // Función para procesar el archivo CSV
21    this.submit = function (csvForm) {
22      if (csvForm.$invalid) {
23        growlService.growlError('Por favor seleccione un archivo CSV válido.');
24        return;
25      }
26
27      if (!_this.form.csvFile) {
28        growlService.growlError('Por favor seleccione un archivo CSV.');
29        return;
30      }
31
32      _this.uploading = true;
33      _this.uploadResult = null;
34
35      // Llamar al servicio de carga masiva
36      Item.batchUpload(_this.form.csvFile).then(function (response) {
37        _this.uploading = false;
38        _this.uploadResult = response;
39
40        // Si hay errores, generar el CSV con errores marcados
41        if (response.error_rows && response.error_rows.length > 0) {
42          _this.generateErrorCsv(response);
43        }
44
45        // Mostrar notificaciones
46        if (response.success_count > 0) {
47          growlService.growl(
48            response.success_count + ' items procesados correctamente.',
49            'success'
50          );
51        }
52
53        if (response.error_count > 0) {
54          growlService.growlError(
55            'Se encontraron ' + response.error_count + ' errores durante el procesamiento.'
56          );
57        }
58
59        // Si todo fue exitoso, mostrar opción de volver
60        if (response.error_count === 0 && response.success_count > 0) {
61          setTimeout(function () {
62            swal({
63              title: "¡Procesamiento Completado!",
64              text: response.success_count + " items fueron procesados exitosamente.\n\n¿Desea volver al listado de items?",
65              type: "success",
66              showCancelButton: true,
67              confirmButtonClass: "btn-primary",
68              confirmButtonText: "Ir a Items",
69              cancelButtonText: "Quedarse aquí",
70              closeOnConfirm: true
71            }, function (goToItems) {
72              if (goToItems) {
73                $location.path('items');
74                $scope.$apply();
75              }
76            });
77          }, 500);
78        }
79
80      }).catch(function (error) {
81        _this.uploading = false;
82
83        var errorMessage = 'Error al procesar el archivo CSV';
84        if (error.data && error.data.error) {
85          errorMessage += ': ' + error.data.error;
86        } else if (error.data && error.data.details) {
87          errorMessage += ': ' + error.data.details;
88        }
89
90        growlService.growlError(errorMessage);
91
92        // Mostrar error detallado si está disponible
93        if (error.data && (error.data.error || error.data.details)) {
94          swal("Error de Procesamiento", errorMessage, "error");
95        }
96      });
97    };
98
99    // Función para limpiar el formulario y resultados
100    this.clearResults = function () {
101      _this.uploadResult = null;
102      _this.form.csvFile = null;
103      _this.errorCsvContent = null;
104    };
105
106    // Función para generar CSV con errores marcados
107    this.generateErrorCsv = function (response) {
108      if (!response.error_rows || response.error_rows.length === 0) {
109        return;
110      }
111
112      var headers = response.headers || ['ID', 'Tipo', 'Categoria', 'SKU', 'Nombre', 'Precio', 'Stock', 'Valor minimo', 'Moneda', 'Detalle', 'Impuesto'];
113      var csvContent = headers.join(',') + ',ERRORES\n';
114
115      response.error_rows.forEach(function (errorRow) {
116        console.log('error_rows', errorRow);
117        var row = errorRow.row_data;
118        var line = '';
119
120        headers.forEach(function (header, index) {
121          if (index > 0) line += ',';
122
123          var value = row[header] || '';
124
125          // Escapar comillas y envolver en comillas si contiene comas
126          value = String(value).replace(/"/g, '""');
127          if (value.includes(',') || value.includes('"') || value.includes('\n')) {
128            value = '"' + value + '"';
129          }
130
131          line += value;
132        });
133
134        // Agregar columna de descripción del error
135        var errorMsg = '';
136        if (errorRow.error_type === 'missing_required_fields') {
137          errorMsg = 'Faltan campos: ' + errorRow.missing_fields.join(', ');
138        } else if (errorRow.error_type === 'invalid_currency') {
139          errorMsg = 'Moneda inválida';
140        } else if (errorRow.error_type === 'invalid_category') {
141          errorMsg = 'Categoría inválida';
142        } else if (errorRow.error_message) {
143          errorMsg = errorRow.error_message;
144        }
145
146        line += ',"' + errorMsg + '"';
147        csvContent += line + '\n';
148      });
149
150      _this.errorCsvContent = csvContent;
151    };
152
153    // Función para descargar CSV con errores
154    this.downloadErrorCsv = function () {
155      if (!_this.errorCsvContent) {
156        growlService.growlError('No hay CSV de errores disponible');
157        return;
158      }
159
160      var blob = new Blob([_this.errorCsvContent], { type: 'text/csv;charset=utf-8;' });
161      var link = document.createElement('a');
162
163      if (link.download !== undefined) {
164        var url = URL.createObjectURL(blob);
165        link.setAttribute('href', url);
166        link.setAttribute('download', 'items_con_errores.csv');
167        link.style.visibility = 'hidden';
168        document.body.appendChild(link);
169        link.click();
170        document.body.removeChild(link);
171        URL.revokeObjectURL(url);
172
173        growlService.growl('CSV con errores descargado exitosamente', 'success');
174      } else {
175        growlService.growlError('Su navegador no soporta la descarga automática');
176      }
177    };
178
179    // Función para navegar de vuelta
180    this.goBack = function () {
181      $location.path('items');
182    };
183
184    // Variable para controlar el estado de descarga
185    _this.downloadingCSV = false;
186
187    // Función para descargar CSV con todos los items de la BD
188    this.downloadExampleCSV = function () {
189      _this.downloadingCSV = true;
190
191      // Obtener todos los items de la base de datos
192      Item.findAll().then(function (response) {
193        var csvContent = "ID,Tipo,Categoria,SKU,Nombre,Precio,Stock,Valor minimo,Moneda,Detalle,Impuesto\n";
194
195        // Formatear cada item en el CSV
196        if (response.items && response.items.length > 0) {
197          response.items.forEach(function (item) {
198            var id = item.id || '';
199            var type = item.is_product ? 'Producto' : 'Servicio';
200            var categoria = item.category && item.category.name ? item.category.name : '';
201            var codigo = item.code || '';
202            var nombre = item.type || '';
203            var precio = item.price || '0.00';
204            var stock = item.available || '0';
205            var valorMinimo = item.critic_amount || '10';
206            var moneda = item.currency && item.currency.code ? item.currency.code : 'UYU';
207            var detalle = item.details || '';
208            var impuesto = item.tax?.name || '';
209
210            // Escapar comillas dobles en los campos
211            categoria = categoria.replace(/"/g, '""');
212            codigo = codigo.replace(/"/g, '""');
213            nombre = nombre.replace(/"/g, '""');
214            detalle = detalle.replace(/"/g, '""');
215
216            // Agregar la línea al CSV, envolviendo en comillas los campos que puedan contener comas
217            csvContent += id + ',' +
218              '"' + type + '",' +
219              '"' + categoria + '",' +
220              '"' + codigo + '",' +
221              '"' + nombre + '",' +
222              precio + ',' +
223              stock + ',' +
224              valorMinimo + ',' +
225              moneda + ',' +
226              '"' + detalle + '",' +
227              '"' + impuesto + '"\n';
228          });
229        } else {
230          // Si no hay items, agregar una línea de ejemplo
231          csvContent += "";
232        }
233
234        // Crear blob y URL para descarga
235        var blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
236        var link = document.createElement('a');
237
238        if (link.download !== undefined) {
239          // Navegadores modernos
240          var url = URL.createObjectURL(blob);
241          link.setAttribute('href', url);
242          link.setAttribute('download', 'items_completo.csv');
243          link.style.visibility = 'hidden';
244          document.body.appendChild(link);
245          link.click();
246          document.body.removeChild(link);
247          URL.revokeObjectURL(url);
248
249          _this.downloadingCSV = false;
250          var itemCount = response.items ? response.items.length : 0;
251          growlService.growl('CSV con ' + itemCount + ' items descargado exitosamente', 'success');
252        } else {
253          // Fallback para navegadores más antiguos
254          _this.downloadingCSV = false;
255          growlService.growlError('Su navegador no soporta la descarga automática. Por favor use el enlace directo.');
256        }
257
258      }).catch(function (error) {
259        _this.downloadingCSV = false;
260
261        var errorMessage = 'Error al obtener los items de la base de datos';
262        if (error.data && error.data.error) {
263          errorMessage += ': ' + error.data.error;
264        }
265
266        growlService.growlError(errorMessage);
267      });
268    };
269
270  });

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.