Commit 99fc7982e420b27b2aee3a952c96b35a17ddfcee

Authored by Marcelo Puebla
1 parent 5df5c2a273
Exists in master and in 1 other branch develop

Arreglo en filtros de sector.

src/js/controller-general-unidad.js
1 angular.module('focaModalInforme') 1 angular.module('focaModalInforme')
2 .controller('focaModalInformeGeneralUnidadController', 2 .controller('focaModalInformeGeneralUnidadController',
3 [ 3 [
4 '$filter', 4 '$filter',
5 '$scope', 5 '$scope',
6 '$uibModalInstance', 6 '$uibModalInstance',
7 'focaModalInformeGeneralUnidadService', 7 'focaModalInformeGeneralUnidadService',
8 'i18nService', 8 'i18nService',
9 'focaModalService', 9 'focaModalService',
10 function($filter, $scope, $uibModalInstance, 10 function($filter, $scope, $uibModalInstance,
11 focaModalInformeGeneralUnidadService, i18nService, focaModalService 11 focaModalInformeGeneralUnidadService, i18nService, focaModalService
12 ) { 12 ) {
13 i18nService.setCurrentLang('es'); 13 i18nService.setCurrentLang('es');
14 var fecha = new Date(); 14 var fecha = new Date();
15 $scope.generando = false; 15 $scope.generando = false;
16 $scope.buscar = true; 16 $scope.buscar = true;
17 $scope.informe = {}; 17 $scope.informe = {};
18 $scope.params = { 18 $scope.params = {
19 fechaHasta: new Date(), 19 fechaHasta: new Date(),
20 fechaDesde: new Date(fecha.setMonth(fecha.getMonth() - 1)), 20 fechaDesde: new Date(fecha.setMonth(fecha.getMonth() - 1)),
21 sector: undefined, 21 sector: String,
22 diferenciarProductos: false, 22 diferenciarProductos: false,
23 diferenciarMeses: false 23 diferenciarMeses: false
24 }; 24 };
25 $scope.gridOptions = { 25 $scope.gridOptions = {
26 enableGridMenu: true, 26 enableGridMenu: true,
27 exporterMenuCsv: false, 27 exporterMenuCsv: false,
28 exporterPdfPageSize: 'A4', 28 exporterPdfPageSize: 'A4',
29 exporterPdfFooter: function (currentPage, pageCount) { 29 exporterPdfFooter: function (currentPage, pageCount) {
30 return { 30 return {
31 columns: [ 31 columns: [
32 {text: $filter('date')(new Date(), 'dd/MM/yyyy'), 32 {text: $filter('date')(new Date(), 'dd/MM/yyyy'),
33 margin: [40, 0]}, 33 margin: [40, 0]},
34 {text: currentPage + ' de ' + pageCount, 34 {text: currentPage + ' de ' + pageCount,
35 margin: [28, 0], alignment: 'right'} 35 margin: [28, 0], alignment: 'right'}
36 ] 36 ]
37 }; 37 };
38 }, 38 },
39 columnDefs: [ 39 columnDefs: [
40 { 40 {
41 field: 'vehiculo', 41 field: 'vehiculo',
42 enableSorting: false, 42 enableSorting: false,
43 cellClass: function(grid, row, col) { 43 cellClass: function(grid, row, col) {
44 if (grid.getCellValue(row,col).indexOf('Sector:') !== -1) 44 if (grid.getCellValue(row,col).indexOf('Sector:') !== -1)
45 return 'yellow w-100'; 45 return 'yellow w-100';
46 if (grid.getCellValue(row,col).indexOf('Producto:') !== -1) 46 if (grid.getCellValue(row,col).indexOf('Producto:') !== -1)
47 return 'red w-100'; 47 return 'red w-100';
48 if (grid.getCellValue(row,col).indexOf('Fecha:') !== -1) 48 if (grid.getCellValue(row,col).indexOf('Fecha:') !== -1)
49 return 'green w-100'; 49 return 'green w-100';
50 } 50 }
51 }, 51 },
52 { 52 {
53 field: 'kmRecorridos', 53 field: 'kmRecorridos',
54 enableSorting: false, 54 enableSorting: false,
55 cellClass: function(grid, row, col) { 55 cellClass: function(grid, row, col) {
56 if (!grid.getCellValue(row,col)) 56 if (!grid.getCellValue(row,col))
57 return 'd-none'; 57 return 'd-none';
58 } 58 }
59 }, 59 },
60 { 60 {
61 field: 'litrosRepartidos', 61 field: 'litrosRepartidos',
62 enableSorting: false, 62 enableSorting: false,
63 cellClass: function(grid, row, col) { 63 cellClass: function(grid, row, col) {
64 if (!grid.getCellValue(row,col)) 64 if (!grid.getCellValue(row,col))
65 return 'd-none'; 65 return 'd-none';
66 } 66 }
67 }, 67 },
68 { 68 {
69 field: 'viajes', 69 field: 'viajes',
70 enableSorting: false, 70 enableSorting: false,
71 cellClass: function(grid, row, col) { 71 cellClass: function(grid, row, col) {
72 if (!grid.getCellValue(row,col)) 72 if (!grid.getCellValue(row,col))
73 return 'd-none'; 73 return 'd-none';
74 } 74 }
75 } 75 }
76 ] 76 ]
77 }; 77 };
78 $scope.generarInforme = function() { 78 $scope.generarInforme = function() {
79 $scope.generando = true; 79 $scope.generando = true;
80 focaModalInformeGeneralUnidadService 80 focaModalInformeGeneralUnidadService
81 .getInformeData($scope.params) 81 .getInformeData($scope.params)
82 .then(function(res) { 82 .then(function(res) {
83 var result = []; 83 var result = [];
84 84
85 res.data.forEach(function(sector) { 85 res.data.forEach(function(sector) {
86 if (!$scope.params.sector || 86 if (!$scope.params.sector ||
87 $scope.params.sector.ID === sector.idSector) { 87 $scope.params.sector.ID === sector.idSector) {
88 //AGREGO SECTORES 88 //AGREGO SECTORES
89 result.push({ 89 result.push({
90 vehiculo: 'Sector: ' + 90 vehiculo: 'Sector: ' +
91 $filter('rellenarDigitos')(sector.idSector, 2, '0') + 91 $filter('rellenarDigitos')(sector.idSector, 2, '0') +
92 ' - ' + sector.sector.trim() 92 ' - ' + sector.sector.trim()
93 }); 93 });
94 if (!$scope.params.diferenciarProductos) 94 if (!$scope.params.diferenciarProductos)
95 sector.productos = unirProductos(sector.productos); 95 sector.productos = unirProductos(sector.productos);
96 96
97 sector.productos.forEach(function(producto) { 97 sector.productos.forEach(function(producto) {
98 //AGREGO PRODUCTOS 98 //AGREGO PRODUCTOS
99 if ($scope.params.diferenciarProductos){ 99 if ($scope.params.diferenciarProductos){
100 result.push({ 100 result.push({
101 vehiculo: 'Producto: ' + 101 vehiculo: 'Producto: ' +
102 producto.idProducto + 102 producto.idProducto +
103 ' - ' + producto.producto 103 ' - ' + producto.producto
104 }); 104 });
105 } 105 }
106 if (!$scope.params.diferenciarMeses) { 106 if (!$scope.params.diferenciarMeses) {
107 producto.fechas = unirFechas(producto.fechas); 107 producto.fechas = unirFechas(producto.fechas);
108 } 108 }
109 producto.fechas.forEach(function(fecha) { 109 producto.fechas.forEach(function(fecha) {
110 //AGREGO FECHAS 110 //AGREGO FECHAS
111 if ($scope.params.diferenciarMeses) { 111 if ($scope.params.diferenciarMeses) {
112 result.push({ 112 result.push({
113 vehiculo: 113 vehiculo:
114 'Fecha: ' + fecha.month + ' - ' + fecha.year 114 'Fecha: ' + fecha.month + ' - ' + fecha.year
115 }); 115 });
116 } else { 116 } else {
117 result.push({ 117 result.push({
118 vehiculo: 118 vehiculo:
119 'Fecha: ' + fecha.year 119 'Fecha: ' + fecha.year
120 }); 120 });
121 } 121 }
122 fecha.vehiculos.forEach(function(vehiculo) { 122 fecha.vehiculos.forEach(function(vehiculo) {
123 //AGREGO VEHICULOS 123 //AGREGO VEHICULOS
124 result.push({ 124 result.push({
125 vehiculo: vehiculo.vehiculo, 125 vehiculo: vehiculo.vehiculo,
126 kmRecorridos: 126 kmRecorridos:
127 $filter('number')(vehiculo.data.kms, 2), 127 $filter('number')(vehiculo.data.kms, 2),
128 litrosRepartidos: 128 litrosRepartidos:
129 $filter('number')(vehiculo.data.lts, 2), 129 $filter('number')(vehiculo.data.lts, 2),
130 viajes: 130 viajes:
131 $filter('number')(vehiculo.data.viajes, 0) 131 $filter('number')(vehiculo.data.viajes, 0)
132 }); 132 });
133 }); 133 });
134 }); 134 });
135 }); 135 });
136 } 136 }
137 }); 137 });
138 138
139 $scope.gridOptions.data = result; 139 $scope.gridOptions.data = result;
140 $scope.generando = false; 140 $scope.generando = false;
141 $scope.buscar = false; 141 $scope.buscar = false;
142 }); 142 });
143 }; 143 };
144 144
145 $scope.seleccionarSector = function(key) { 145 $scope.seleccionarSector = function(key) {
146 if (key === 13) { 146 if (key === 13) {
147 var parametrosModal = { 147 var parametrosModal = {
148 titulo: 'Búsqueda de Sector', 148 titulo: 'Búsqueda de Sector',
149 searchText: $scope.params.sector.NOMBRE, 149 searchText: $scope.params.sector.NOMBRE,
150 query: '/sector', 150 query: '/sector',
151 columnas: [ 151 columnas: [
152 { 152 {
153 nombre: 'Código', 153 nombre: 'Código',
154 propiedad: 'ID' 154 propiedad: 'ID'
155 }, 155 },
156 { 156 {
157 nombre: 'Nombre', 157 nombre: 'Nombre',
158 propiedad: 'NOMBRE' 158 propiedad: 'NOMBRE'
159 } 159 }
160 ], 160 ],
161 size: 'md' 161 size: 'md'
162 }; 162 };
163 focaModalService.modal(parametrosModal).then( 163 focaModalService.modal(parametrosModal).then(
164 function(sector) { 164 function(sector) {
165 $scope.params.sector = sector; 165 $scope.params.sector = sector;
166 }, function() {} 166 }, function() {}
167 ); 167 );
168 } 168 }
169 }; 169 };
170 $scope.volver = function() { 170 $scope.volver = function() {
171 $scope.buscar = true; 171 $scope.buscar = true;
172 }; 172 };
173 $scope.cancel = function() { 173 $scope.cancel = function() {
174 $uibModalInstance.dismiss('Cancelar'); 174 $uibModalInstance.dismiss('Cancelar');
175 }; 175 };
176 $scope.clearSector = function() {
177 $scope.params.sector = undefined;
178 };
179 176
180 function unirProductos(productos) { 177 function unirProductos(productos) {
181 var result = [{fechas: []}]; 178 var result = [{fechas: []}];
182 productos.forEach(function(producto) { 179 productos.forEach(function(producto) {
183 producto.fechas.forEach(function(fecha) { 180 producto.fechas.forEach(function(fecha) {
184 var existe = result[0].fechas.filter(function(result) { 181 var existe = result[0].fechas.filter(function(result) {
185 return result.fecha === fecha.fecha; 182 return result.fecha === fecha.fecha;
186 }); 183 });
187 if (existe.length) { 184 if (existe.length) {
188 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos); 185 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos);
189 } else { 186 } else {
190 result[0].fechas.push(fecha); 187 result[0].fechas.push(fecha);
191 } 188 }
192 189
193 }); 190 });
194 }); 191 });
195 result[0].fechas.forEach(function(fecha) { 192 result[0].fechas.forEach(function(fecha) {
196 fecha.vehiculos = unirVehiculos(fecha.vehiculos); 193 fecha.vehiculos = unirVehiculos(fecha.vehiculos);
197 }); 194 });
198 return result; 195 return result;
199 } 196 }
200 197
201 function unirFechas(fechas) { 198 function unirFechas(fechas) {
202 var results = []; 199 var results = [];
203 fechas.forEach(function(fecha) { 200 fechas.forEach(function(fecha) {
204 var existe = results.filter(function(result) { 201 var existe = results.filter(function(result) {
205 return result.year === fecha.year; 202 return result.year === fecha.year;
206 }); 203 });
207 204
208 if (existe.length) { 205 if (existe.length) {
209 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos); 206 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos);
210 } else { 207 } else {
211 results.push({ 208 results.push({
212 year: fecha.year, 209 year: fecha.year,
213 vehiculos: fecha.vehiculos 210 vehiculos: fecha.vehiculos
214 }); 211 });
215 } 212 }
216 }); 213 });
217 214
218 results.forEach(function(result) { 215 results.forEach(function(result) {
219 result.vehiculos = unirVehiculos(result.vehiculos); 216 result.vehiculos = unirVehiculos(result.vehiculos);
220 }); 217 });
221 return results; 218 return results;
222 } 219 }
223 220
224 function unirVehiculos(vehiculos) { 221 function unirVehiculos(vehiculos) {
225 var results = []; 222 var results = [];
226 vehiculos.forEach(function(vehiculo) { 223 vehiculos.forEach(function(vehiculo) {
227 var existe = results.filter(function(result) { 224 var existe = results.filter(function(result) {
228 return result.vehiculo === vehiculo.vehiculo; 225 return result.vehiculo === vehiculo.vehiculo;
229 }); 226 });
230 227
231 if (existe.length) { 228 if (existe.length) {
232 existe[0].data.kms += vehiculo.data.kms; 229 existe[0].data.kms += vehiculo.data.kms;
233 existe[0].data.lts += vehiculo.data.lts; 230 existe[0].data.lts += vehiculo.data.lts;
234 existe[0].data.viajes += vehiculo.data.viajes; 231 existe[0].data.viajes += vehiculo.data.viajes;
235 } else { 232 } else {
236 results.push(vehiculo); 233 results.push(vehiculo);
237 } 234 }
238 }); 235 });
239 return results; 236 return results;
240 } 237 }
241 } 238 }
242 ] 239 ]
243 ); 240 );
244 241
src/js/controller-reparto-optimo.js
1 angular.module('focaModalInforme') 1 angular.module('focaModalInforme')
2 .controller('focaModalInformeRepartoOptimoController', 2 .controller('focaModalInformeRepartoOptimoController',
3 [ 3 [
4 '$filter', 4 '$filter',
5 '$scope', 5 '$scope',
6 '$uibModalInstance', 6 '$uibModalInstance',
7 'focaModalInformeRepartoOptimoService', 7 'focaModalInformeRepartoOptimoService',
8 'i18nService', 8 'i18nService',
9 'focaModalService', 9 'focaModalService',
10 '$uibModal', 10 '$uibModal',
11 function($filter, $scope, $uibModalInstance, focaModalInformeRepartoOptimoService, 11 function($filter, $scope, $uibModalInstance, focaModalInformeRepartoOptimoService,
12 i18nService, focaModalService, $uibModal 12 i18nService, focaModalService, $uibModal
13 ) { 13 ) {
14 var fecha = new Date(); 14 var fecha = new Date();
15 $scope.generando = false; 15 $scope.generando = false;
16 $scope.buscar = true; 16 $scope.buscar = true;
17 $scope.params = { 17 $scope.params = {
18 fechaHasta: new Date(), 18 fechaHasta: new Date(),
19 fechaDesde: new Date(fecha.setMonth(fecha.getMonth() - 1)), 19 fechaDesde: new Date(fecha.setMonth(fecha.getMonth() - 1)),
20 sector: undefined, 20 sector: String,
21 diferenciarProductos: false, 21 diferenciarProductos: false,
22 diferenciarMeses: false 22 diferenciarMeses: false
23 }; 23 };
24 i18nService.setCurrentLang('es'); 24 i18nService.setCurrentLang('es');
25 $scope.gridOptions = { 25 $scope.gridOptions = {
26 enableGridMenu: true, 26 enableGridMenu: true,
27 exporterMenuCsv: false, 27 exporterMenuCsv: false,
28 exporterPdfPageSize: 'A4', 28 exporterPdfPageSize: 'A4',
29 exporterPdfFooter: function (currentPage, pageCount) { 29 exporterPdfFooter: function (currentPage, pageCount) {
30 return { 30 return {
31 columns: [ 31 columns: [
32 {text: $filter('date')(new Date(), 'dd/MM/yyyy'), 32 {text: $filter('date')(new Date(), 'dd/MM/yyyy'),
33 margin: [40, 0]}, 33 margin: [40, 0]},
34 {text: currentPage + ' de ' + pageCount, 34 {text: currentPage + ' de ' + pageCount,
35 margin: [28, 0], alignment: 'right'} 35 margin: [28, 0], alignment: 'right'}
36 ] 36 ]
37 }; 37 };
38 }, 38 },
39 columnDefs: [ 39 columnDefs: [
40 { 40 {
41 field: 'vehiculo', 41 field: 'vehiculo',
42 enableSorting: false, 42 enableSorting: false,
43 cellClass: function(grid, row, col) { 43 cellClass: function(grid, row, col) {
44 if (grid.getCellValue(row,col).indexOf('Sector:') !== -1) 44 if (grid.getCellValue(row,col).indexOf('Sector:') !== -1)
45 return 'yellow w-100'; 45 return 'yellow w-100';
46 if (grid.getCellValue(row,col).indexOf('Producto:') !== -1) 46 if (grid.getCellValue(row,col).indexOf('Producto:') !== -1)
47 return 'red w-100'; 47 return 'red w-100';
48 if (grid.getCellValue(row,col).indexOf('Fecha:') !== -1) 48 if (grid.getCellValue(row,col).indexOf('Fecha:') !== -1)
49 return 'green w-100'; 49 return 'green w-100';
50 } 50 }
51 }, 51 },
52 { 52 {
53 field: 'ltsRepartidos', 53 field: 'ltsRepartidos',
54 enableSorting: false, 54 enableSorting: false,
55 cellClass: function(grid, row, col) { 55 cellClass: function(grid, row, col) {
56 if (!grid.getCellValue(row,col)) 56 if (!grid.getCellValue(row,col))
57 return 'd-none'; 57 return 'd-none';
58 } 58 }
59 }, 59 },
60 { 60 {
61 field: 'ltsOptimos', 61 field: 'ltsOptimos',
62 enableSorting: false, 62 enableSorting: false,
63 cellClass: function(grid, row, col) { 63 cellClass: function(grid, row, col) {
64 if (!grid.getCellValue(row,col)) 64 if (!grid.getCellValue(row,col))
65 return 'd-none'; 65 return 'd-none';
66 } 66 }
67 }, 67 },
68 { 68 {
69 field: 'diferencia', 69 field: 'diferencia',
70 enableSorting: false, 70 enableSorting: false,
71 cellClass: function(grid, row, col) { 71 cellClass: function(grid, row, col) {
72 if (!grid.getCellValue(row,col)) 72 if (!grid.getCellValue(row,col))
73 return 'd-none'; 73 return 'd-none';
74 } 74 }
75 }, 75 },
76 { 76 {
77 field: 'porcentaje', 77 field: 'porcentaje',
78 enableSorting: false, 78 enableSorting: false,
79 cellClass: function(grid, row, col) { 79 cellClass: function(grid, row, col) {
80 if (!grid.getCellValue(row,col)) 80 if (!grid.getCellValue(row,col))
81 return 'd-none'; 81 return 'd-none';
82 } 82 }
83 } 83 }
84 ] 84 ]
85 }; 85 };
86 86
87 $scope.generarInforme = function() { 87 $scope.generarInforme = function() {
88 $scope.generando = true; 88 $scope.generando = true;
89 focaModalInformeRepartoOptimoService 89 focaModalInformeRepartoOptimoService
90 .getInformeData($scope.params) 90 .getInformeData($scope.params)
91 .then(function(res) { 91 .then(function(res) {
92 var result = []; 92 var result = [];
93 93
94 res.data.forEach(function(sector) { 94 res.data.forEach(function(sector) {
95 if (!$scope.params.sector || 95 if (!$scope.params.sector ||
96 $scope.params.sector.ID === sector.idSector) { 96 $scope.params.sector.ID === sector.idSector) {
97 //AGREGO SECTORES 97 //AGREGO SECTORES
98 result.push({ 98 result.push({
99 vehiculo: 'Sector: ' + 99 vehiculo: 'Sector: ' +
100 $filter('rellenarDigitos')(sector.idSector, 2, '0') + 100 $filter('rellenarDigitos')(sector.idSector, 2, '0') +
101 ' - ' + sector.sector.trim() 101 ' - ' + sector.sector.trim()
102 }); 102 });
103 if (!$scope.params.diferenciarProductos) 103 if (!$scope.params.diferenciarProductos)
104 sector.productos = unirProductos(sector.productos); 104 sector.productos = unirProductos(sector.productos);
105 105
106 sector.productos.forEach(function(producto) { 106 sector.productos.forEach(function(producto) {
107 //AGREGO PRODUCTOS 107 //AGREGO PRODUCTOS
108 if ($scope.params.diferenciarProductos) { 108 if ($scope.params.diferenciarProductos) {
109 result.push({ 109 result.push({
110 vehiculo: 'Producto: ' + 110 vehiculo: 'Producto: ' +
111 producto.idProducto + 111 producto.idProducto +
112 ' - ' + producto.producto 112 ' - ' + producto.producto
113 }); 113 });
114 } 114 }
115 if (!$scope.params.diferenciarMeses) { 115 if (!$scope.params.diferenciarMeses) {
116 producto.fechas = unirFechas(producto.fechas); 116 producto.fechas = unirFechas(producto.fechas);
117 } 117 }
118 producto.fechas.forEach(function(fecha) { 118 producto.fechas.forEach(function(fecha) {
119 //AGREGO FECHAS 119 //AGREGO FECHAS
120 if ($scope.params.diferenciarMeses) { 120 if ($scope.params.diferenciarMeses) {
121 result.push({ 121 result.push({
122 vehiculo: 122 vehiculo:
123 'Fecha: ' + fecha.month + ' - ' + fecha.year 123 'Fecha: ' + fecha.month + ' - ' + fecha.year
124 }); 124 });
125 } else { 125 } else {
126 result.push({ 126 result.push({
127 vehiculo: 127 vehiculo:
128 'Fecha: ' + fecha.year 128 'Fecha: ' + fecha.year
129 }); 129 });
130 } 130 }
131 fecha.vehiculos.forEach(function(vehiculo) { 131 fecha.vehiculos.forEach(function(vehiculo) {
132 //AGREGO VEHICULOS 132 //AGREGO VEHICULOS
133 result.push({ 133 result.push({
134 vehiculo: vehiculo.vehiculo, 134 vehiculo: vehiculo.vehiculo,
135 ltsRepartidos: 135 ltsRepartidos:
136 $filter('number') 136 $filter('number')
137 (vehiculo.data.ltsRepartidos, 2), 137 (vehiculo.data.ltsRepartidos, 2),
138 ltsOptimos: 138 ltsOptimos:
139 $filter('number') 139 $filter('number')
140 (vehiculo.data.ltsOptimos, 2), 140 (vehiculo.data.ltsOptimos, 2),
141 diferencia: 141 diferencia:
142 $filter('number') 142 $filter('number')
143 (vehiculo.data.diferencia, 2), 143 (vehiculo.data.diferencia, 2),
144 porcentaje: 144 porcentaje:
145 $filter('number') 145 $filter('number')
146 (vehiculo.data.porcentaje, 2) + '%' 146 (vehiculo.data.porcentaje, 2) + '%'
147 }); 147 });
148 }); 148 });
149 }); 149 });
150 }); 150 });
151 } 151 }
152 }); 152 });
153 153
154 $scope.gridOptions.data = result; 154 $scope.gridOptions.data = result;
155 $scope.generando = false; 155 $scope.generando = false;
156 $scope.buscar = false; 156 $scope.buscar = false;
157 }); 157 });
158 }; 158 };
159 159
160 $scope.seleccionarSector = function(key) { 160 $scope.seleccionarSector = function(key) {
161 if (key === 13) { 161 if (key === 13) {
162 var parametrosModal = { 162 var parametrosModal = {
163 titulo: 'Búsqueda de Sector', 163 titulo: 'Búsqueda de Sector',
164 searchText: $scope.params.sector.NOMBRE, 164 searchText: $scope.params.sector.NOMBRE,
165 query: '/sector', 165 query: '/sector',
166 columnas: [ 166 columnas: [
167 { 167 {
168 nombre: 'Código', 168 nombre: 'Código',
169 propiedad: 'ID' 169 propiedad: 'ID'
170 }, 170 },
171 { 171 {
172 nombre: 'Nombre', 172 nombre: 'Nombre',
173 propiedad: 'NOMBRE' 173 propiedad: 'NOMBRE'
174 } 174 }
175 ], 175 ],
176 size: 'md' 176 size: 'md'
177 }; 177 };
178 focaModalService.modal(parametrosModal).then( 178 focaModalService.modal(parametrosModal).then(
179 function(sector) { 179 function(sector) {
180 $scope.params.sector = sector; 180 $scope.params.sector = sector;
181 }, function() {} 181 }, function() {}
182 ); 182 );
183 } 183 }
184 }; 184 };
185 $scope.clearSector = function() {
186 $scope.params.sector = undefined;
187 };
188 $scope.volver = function() { 185 $scope.volver = function() {
189 $scope.buscar = true; 186 $scope.buscar = true;
190 }; 187 };
191 $scope.cancel = function() { 188 $scope.cancel = function() {
192 $uibModalInstance.dismiss('Cancelar'); 189 $uibModalInstance.dismiss('Cancelar');
193 }; 190 };
194 191
195 function unirProductos(productos) { 192 function unirProductos(productos) {
196 var result = [{fechas: []}]; 193 var result = [{fechas: []}];
197 productos.forEach(function(producto) { 194 productos.forEach(function(producto) {
198 producto.fechas.forEach(function(fecha) { 195 producto.fechas.forEach(function(fecha) {
199 var existe = result[0].fechas.filter(function(result) { 196 var existe = result[0].fechas.filter(function(result) {
200 return result.fecha === fecha.fecha; 197 return result.fecha === fecha.fecha;
201 }); 198 });
202 if (existe.length) { 199 if (existe.length) {
203 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos); 200 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos);
204 } else { 201 } else {
205 result[0].fechas.push(fecha); 202 result[0].fechas.push(fecha);
206 } 203 }
207 204
208 }); 205 });
209 }); 206 });
210 result[0].fechas.forEach(function(fecha) { 207 result[0].fechas.forEach(function(fecha) {
211 fecha.vehiculos = unirVehiculos(fecha.vehiculos); 208 fecha.vehiculos = unirVehiculos(fecha.vehiculos);
212 }); 209 });
213 return result; 210 return result;
214 } 211 }
215 212
216 function unirFechas(fechas) { 213 function unirFechas(fechas) {
217 var results = []; 214 var results = [];
218 fechas.forEach(function(fecha) { 215 fechas.forEach(function(fecha) {
219 var existe = results.filter(function(result) { 216 var existe = results.filter(function(result) {
220 return result.year === fecha.year; 217 return result.year === fecha.year;
221 }); 218 });
222 219
223 if (existe.length) { 220 if (existe.length) {
224 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos); 221 existe[0].vehiculos = existe[0].vehiculos.concat(fecha.vehiculos);
225 } else { 222 } else {
226 results.push({ 223 results.push({
227 year: fecha.year, 224 year: fecha.year,
228 vehiculos: fecha.vehiculos 225 vehiculos: fecha.vehiculos
229 }); 226 });
230 } 227 }
231 }); 228 });
232 229
233 results.forEach(function(result) { 230 results.forEach(function(result) {
234 result.vehiculos = unirVehiculos(result.vehiculos); 231 result.vehiculos = unirVehiculos(result.vehiculos);
235 }); 232 });
236 return results; 233 return results;
237 } 234 }
238 235
239 function unirVehiculos(vehiculos) { 236 function unirVehiculos(vehiculos) {
240 var results = []; 237 var results = [];
241 vehiculos.forEach(function(vehiculo) { 238 vehiculos.forEach(function(vehiculo) {
242 var existe = results.filter(function(result) { 239 var existe = results.filter(function(result) {
243 return result.vehiculo === vehiculo.vehiculo; 240 return result.vehiculo === vehiculo.vehiculo;
244 }); 241 });
245 242
246 if (existe.length) { 243 if (existe.length) {
247 existe[0].data.kms += vehiculo.data.kms; 244 existe[0].data.kms += vehiculo.data.kms;
248 existe[0].data.lts += vehiculo.data.lts; 245 existe[0].data.lts += vehiculo.data.lts;
249 existe[0].data.viajes += vehiculo.data.viajes; 246 existe[0].data.viajes += vehiculo.data.viajes;
250 } else { 247 } else {
251 results.push(vehiculo); 248 results.push(vehiculo);
252 } 249 }
253 }); 250 });
254 return results; 251 return results;
255 } 252 }
256 } 253 }
257 ] 254 ]
258 ); 255 );
259 256
src/views/informe-general-unidad.html
1 <div class="modal-header py-1"> 1 <div class="modal-header py-1">
2 <div class="row w-100"> 2 <div class="row w-100">
3 <div class="col-12"><h5 class="modal-title">Informe general por unidad de reparto</h5></div> 3 <div class="col-12"><h5 class="modal-title">Informe general por unidad de reparto</h5></div>
4 <div class="col-12" ng-hide="buscar"> 4 <div class="col-12" ng-hide="buscar">
5 Filtros: Fecha desde: {{params.fechaDesde | date: 'dd/MM/yyyy'}}, 5 Filtros: Fecha desde: {{params.fechaDesde | date: 'dd/MM/yyyy'}},
6 Fecha hasta: {{params.fechaHasta | date: 'dd/MM/yyyy'}} 6 Fecha hasta: {{params.fechaHasta | date: 'dd/MM/yyyy'}}
7 <span ng-if="params.sector">, Sector: {{params.sector.NOMBRE}}</span> 7 <span ng-if="params.sector">, Sector: {{params.sector.NOMBRE}}</span>
8 <span ng-if="params.diferenciarProductos">, Productos diferenciados</span> 8 <span ng-if="params.diferenciarProductos">, Productos diferenciados</span>
9 <span ng-if="params.diferenciarMeses">, Meses diferenciados</span> 9 <span ng-if="params.diferenciarMeses">, Meses diferenciados</span>
10 </div> 10 </div>
11 </div> 11 </div>
12 </div> 12 </div>
13 <div class="modal-body" id="modal-body"> 13 <div class="modal-body" id="modal-body">
14 14
15 <div class="input-group row" 15 <div class="input-group row"
16 ng-show="buscar"> 16 ng-show="buscar">
17 <small class="col-md-2 col-4 text-left my-1">Fecha Desde</small> 17 <small class="col-md-2 col-4 text-left my-1">Fecha Desde</small>
18 <div class="col-md-4 col-8 input-group mb-3"> 18 <div class="col-md-4 col-8 input-group mb-3">
19 <div class="input-group-prepend"> 19 <div class="input-group-prepend">
20 <button 20 <button
21 class="input-group-text" 21 class="input-group-text"
22 type="button" 22 type="button"
23 ng-click="datepickerOpen = true" 23 ng-click="datepickerOpen = true"
24 > 24 >
25 <i class="fa fa-calendar"></i> 25 <i class="fa fa-calendar"></i>
26 </button> 26 </button>
27 </div> 27 </div>
28 <input 28 <input
29 class="form-control form-control-sm" 29 class="form-control form-control-sm"
30 id="inlineFormInputGroup" 30 id="inlineFormInputGroup"
31 type="text" 31 type="text"
32 ng-model="params.fechaDesde" 32 ng-model="params.fechaDesde"
33 ng-required="true" 33 ng-required="true"
34 uib-datepicker-popup="dd/MM/yyyy" 34 uib-datepicker-popup="dd/MM/yyyy"
35 show-button-bar="false" 35 show-button-bar="false"
36 is-open="datepickerOpen" 36 is-open="datepickerOpen"
37 on-open-focus="false" 37 on-open-focus="false"
38 ng-focus="datepickerOpen = true" 38 ng-focus="datepickerOpen = true"
39 datepicker-options="dateOptions" 39 datepicker-options="dateOptions"
40 /> 40 />
41 </div> 41 </div>
42 <small class="col-md-2 col-4 text-left my-1">Fecha Hasta</small> 42 <small class="col-md-2 col-4 text-left my-1">Fecha Hasta</small>
43 <div class="col-md-4 col-8 input-group mb-3"> 43 <div class="col-md-4 col-8 input-group mb-3">
44 <div class="input-group-prepend"> 44 <div class="input-group-prepend">
45 <button 45 <button
46 class="input-group-text" 46 class="input-group-text"
47 type="button" 47 type="button"
48 ng-click="datepicker2Open = true" 48 ng-click="datepicker2Open = true"
49 > 49 >
50 <i class="fa fa-calendar"></i> 50 <i class="fa fa-calendar"></i>
51 </button> 51 </button>
52 </div> 52 </div>
53 <input 53 <input
54 class="form-control form-control-sm" 54 class="form-control form-control-sm"
55 id="inlineFormInputGroup" 55 id="inlineFormInputGroup"
56 type="text" 56 type="text"
57 ng-model="params.fechaHasta" 57 ng-model="params.fechaHasta"
58 ng-required="true" 58 ng-required="true"
59 uib-datepicker-popup="dd/MM/yyyy" 59 uib-datepicker-popup="dd/MM/yyyy"
60 show-button-bar="false" 60 show-button-bar="false"
61 is-open="datepicker2Open" 61 is-open="datepicker2Open"
62 on-open-focus="false" 62 on-open-focus="false"
63 ng-focus="datepicker2Open = true" 63 ng-focus="datepicker2Open = true"
64 /> 64 />
65 </div> 65 </div>
66 <small class="col-md-2 col-4 text-left my-1">Sector</small> 66 <small class="col-md-2 col-4 text-left my-1">Sector</small>
67 <div class="col-md-4 col-8 input-group mb-3"> 67 <div class="col-md-4 col-8 input-group mb-3">
68 <input 68 <input
69 class="form-control form-control-sm" 69 class="form-control form-control-sm"
70 id="inlineFormInputGroup" 70 id="inlineFormInputGroup"
71 type="text" 71 type="text"
72 ng-model="params.sector.NOMBRE" 72 ng-model="params.sector.NOMBRE"
73 ng-required="true" 73 ng-required="true"
74 ng-keypress="seleccionarSector($event.keyCode)" 74 ng-keypress="seleccionarSector($event.keyCode)"
75 /> 75 />
76 <button 76 <button
77 ng-show="params.sector.NOMBRE.length >= 1" 77 ng-show="params.sector.NOMBRE.length >= 1"
78 type="button" 78 type="button"
79 class="clear-input" 79 class="clear-input"
80 ng-click="clearSector()" 80 ng-click="params.sector.NOMBRE = ''"
81 > 81 >
82 <i class="fa fa-times"></i> 82 <i class="fa fa-times"></i>
83 </button> 83 </button>
84 <div class="input-group-append"> 84 <div class="input-group-append">
85 <div class="input-group-append" ng-hide="ingreso"> 85 <div class="input-group-append" ng-hide="ingreso">
86 <button 86 <button
87 ladda="searchLoading" 87 ladda="searchLoading"
88 data-spinner-color="#FF0000" 88 data-spinner-color="#FF0000"
89 class="btn btn-outline-secondary" 89 class="btn btn-outline-secondary"
90 type="button" 90 type="button"
91 ng-click="seleccionarSector(13)"> 91 ng-click="seleccionarSector(13)">
92 <i class="fa fa-search" aria-hidden="true"></i> 92 <i class="fa fa-search" aria-hidden="true"></i>
93 </button> 93 </button>
94 </div> 94 </div>
95 </div> 95 </div>
96 </div> 96 </div>
97 <small class="col-md-4 col-8 text-left my-1">Diferenciar productos</small> 97 <small class="col-md-4 col-8 text-left my-1">Diferenciar productos</small>
98 <div class="col-md-2 col-4 input-group mb-2"> 98 <div class="col-md-2 col-4 input-group mb-2">
99 <div class="custom-control custom-checkbox ml-auto"> 99 <div class="custom-control custom-checkbox ml-auto">
100 <input 100 <input
101 type="checkbox" 101 type="checkbox"
102 class="custom-control-input" 102 class="custom-control-input"
103 ng-model="params.diferenciarProductos" 103 ng-model="params.diferenciarProductos"
104 id="customCheck1"> 104 id="customCheck1">
105 <label class="custom-control-label" for="customCheck1"></label> 105 <label class="custom-control-label" for="customCheck1"></label>
106 </div> 106 </div>
107 </div> 107 </div>
108 <small class="col-md-4 col-8 text-left my-1">Detallar por mes</small> 108 <small class="col-md-4 col-8 text-left my-1">Detallar por mes</small>
109 <div class="col-md-2 col-4 input-group mb-2"> 109 <div class="col-md-2 col-4 input-group mb-2">
110 <div class="custom-control custom-checkbox ml-auto"> 110 <div class="custom-control custom-checkbox ml-auto">
111 <input 111 <input
112 type="checkbox" 112 type="checkbox"
113 class="custom-control-input" 113 class="custom-control-input"
114 ng-model="params.diferenciarMeses" 114 ng-model="params.diferenciarMeses"
115 id="customCheck2"> 115 id="customCheck2">
116 <label class="custom-control-label" for="customCheck2"></label> 116 <label class="custom-control-label" for="customCheck2"></label>
117 </div> 117 </div>
118 </div> 118 </div>
119 </div> 119 </div>
120 <div 120 <div
121 ng-if="!buscar" 121 ng-if="!buscar"
122 class="row"> 122 class="row">
123 <div class="col-12"> 123 <div class="col-12">
124 <div 124 <div
125 class="gridInforme" 125 class="gridInforme"
126 ui-grid="gridOptions" 126 ui-grid="gridOptions"
127 ui-grid-exporter 127 ui-grid-exporter
128 ui-grid-resize-columns 128 ui-grid-resize-columns
129 ></div> 129 ></div>
130 </div> 130 </div>
131 </div> 131 </div>
132 </div> 132 </div>
133 <div class="modal-footer py-1"> 133 <div class="modal-footer py-1">
134 <button 134 <button
135 ladda="generando" 135 ladda="generando"
136 class="btn btn-sm btn-secondary" 136 class="btn btn-sm btn-secondary"
137 type="button" 137 type="button"
138 ng-click="generarInforme()" 138 ng-click="generarInforme()"
139 ng-show="buscar">Generar</button> 139 ng-show="buscar">Generar</button>
140 <button 140 <button
141 class="btn btn-sm btn-secondary" 141 class="btn btn-sm btn-secondary"
142 type="button" 142 type="button"
143 ng-click="cancel()" 143 ng-click="cancel()"
144 ng-show="buscar">Salir</button> 144 ng-show="buscar">Salir</button>
145 <button 145 <button
146 class="btn btn-sm btn-secondary" 146 class="btn btn-sm btn-secondary"
147 type="button" 147 type="button"
148 ng-click="volver()" 148 ng-click="volver()"
149 ng-hide="buscar">Volver</button> 149 ng-hide="buscar">Volver</button>
150 </div> 150 </div>
151 151
src/views/informe-reparto-optimo.html
1 <div class="modal-header py-1"> 1 <div class="modal-header py-1">
2 <div class="row w-100"> 2 <div class="row w-100">
3 <div class="col-12"><h5 class="modal-title">Informe de reparto óptimo detallado</h5></div> 3 <div class="col-12"><h5 class="modal-title">Informe de reparto óptimo detallado</h5></div>
4 <div class="col-12" ng-hide="buscar"> 4 <div class="col-12" ng-hide="buscar">
5 Filtros: Fecha desde: {{fechaDesde | date: 'dd/MM/yyyy'}}, 5 Filtros: Fecha desde: {{fechaDesde | date: 'dd/MM/yyyy'}},
6 Fecha hasta: {{fechaHasta | date: 'dd/MM/yyyy'}}, 6 Fecha hasta: {{fechaHasta | date: 'dd/MM/yyyy'}},
7 Transportista : {{transportista.NOM}}, 7 Transportista : {{transportista.NOM}},
8 Unidad: {{unidad.codigo}} 8 Unidad: {{unidad.codigo}}
9 </div> 9 </div>
10 </div> 10 </div>
11 </div> 11 </div>
12 <div class="modal-body" id="modal-body"> 12 <div class="modal-body" id="modal-body">
13 <div class="input-group row" 13 <div class="input-group row"
14 ng-show="buscar"> 14 ng-show="buscar">
15 <small class="col-md-2 col-4 text-left my-1">Fecha Desde</small> 15 <small class="col-md-2 col-4 text-left my-1">Fecha Desde</small>
16 <div class="col-md-4 col-8 input-group mb-3"> 16 <div class="col-md-4 col-8 input-group mb-3">
17 <div class="input-group-prepend"> 17 <div class="input-group-prepend">
18 <div class="input-group-text"> 18 <div class="input-group-text">
19 <i class="fa fa-calendar"></i> 19 <i class="fa fa-calendar"></i>
20 </div> 20 </div>
21 </div> 21 </div>
22 <input 22 <input
23 class="form-control form-control-sm" 23 class="form-control form-control-sm"
24 id="inlineFormInputGroup" 24 id="inlineFormInputGroup"
25 type="text" 25 type="text"
26 ng-model="params.fechaDesde" 26 ng-model="params.fechaDesde"
27 ng-required="true" 27 ng-required="true"
28 uib-datepicker-popup="dd/MM/yyyy" 28 uib-datepicker-popup="dd/MM/yyyy"
29 show-button-bar="false" 29 show-button-bar="false"
30 is-open="datepickerOpen" 30 is-open="datepickerOpen"
31 on-open-focus="false" 31 on-open-focus="false"
32 ng-focus="datepickerOpen = true" 32 ng-focus="datepickerOpen = true"
33 datepicker-options="dateOptions" 33 datepicker-options="dateOptions"
34 /> 34 />
35 </div> 35 </div>
36 <small class="col-md-2 col-4 text-left my-1">Fecha Hasta</small> 36 <small class="col-md-2 col-4 text-left my-1">Fecha Hasta</small>
37 <div class="col-md-4 col-8 input-group mb-3"> 37 <div class="col-md-4 col-8 input-group mb-3">
38 <div class="input-group-prepend"> 38 <div class="input-group-prepend">
39 <div class="input-group-text"> 39 <div class="input-group-text">
40 <i class="fa fa-calendar"></i> 40 <i class="fa fa-calendar"></i>
41 </div> 41 </div>
42 </div> 42 </div>
43 <input 43 <input
44 class="form-control form-control-sm" 44 class="form-control form-control-sm"
45 id="inlineFormInputGroup" 45 id="inlineFormInputGroup"
46 type="text" 46 type="text"
47 ng-model="params.fechaHasta" 47 ng-model="params.fechaHasta"
48 ng-required="true" 48 ng-required="true"
49 uib-datepicker-popup="dd/MM/yyyy" 49 uib-datepicker-popup="dd/MM/yyyy"
50 show-button-bar="false" 50 show-button-bar="false"
51 is-open="datepicker2Open" 51 is-open="datepicker2Open"
52 on-open-focus="false" 52 on-open-focus="false"
53 ng-focus="datepicker2Open = true" 53 ng-focus="datepicker2Open = true"
54 /> 54 />
55 </div> 55 </div>
56 <small class="col-md-2 col-4 text-left my-1">Sector</small> 56 <small class="col-md-2 col-4 text-left my-1">Sector</small>
57 <div class="col-md-4 col-8 input-group mb-3"> 57 <div class="col-md-4 col-8 input-group mb-3">
58 <input 58 <input
59 class="form-control form-control-sm" 59 class="form-control form-control-sm"
60 id="inlineFormInputGroup" 60 id="inlineFormInputGroup"
61 type="text" 61 type="text"
62 ng-model="params.sector.NOMBRE" 62 ng-model="params.sector.NOMBRE"
63 ng-required="true" 63 ng-required="true"
64 ng-keypress="seleccionarSector($event.keyCode)" 64 ng-keypress="seleccionarSector($event.keyCode)"
65 /> 65 />
66 <button 66 <button
67 ng-show="params.sector.NOMBRE.length >= 1" 67 ng-show="params.sector.NOMBRE.length >= 1"
68 type="button" 68 type="button"
69 class="clear-input" 69 class="clear-input"
70 ng-click="clearSector()" 70 ng-click="params.sector.NOMBRE = ''"
71 > 71 >
72 <i class="fa fa-times"></i> 72 <i class="fa fa-times"></i>
73 </button> 73 </button>
74 <div class="input-group-append"> 74 <div class="input-group-append">
75 <div class="input-group-append" ng-hide="ingreso"> 75 <div class="input-group-append" ng-hide="ingreso">
76 <button 76 <button
77 ladda="searchLoading" 77 ladda="searchLoading"
78 data-spinner-color="#FF0000" 78 data-spinner-color="#FF0000"
79 class="btn btn-outline-secondary" 79 class="btn btn-outline-secondary"
80 type="button" 80 type="button"
81 ng-click="seleccionarSector(13)"> 81 ng-click="seleccionarSector(13)">
82 <i class="fa fa-search" aria-hidden="true"></i> 82 <i class="fa fa-search" aria-hidden="true"></i>
83 </button> 83 </button>
84 </div> 84 </div>
85 </div> 85 </div>
86 </div> 86 </div>
87 <small class="col-md-4 col-8 text-left my-1">Diferenciar productos</small> 87 <small class="col-md-4 col-8 text-left my-1">Diferenciar productos</small>
88 <div class="col-md-2 col-4 input-group mb-2"> 88 <div class="col-md-2 col-4 input-group mb-2">
89 <div class="custom-control custom-checkbox ml-auto"> 89 <div class="custom-control custom-checkbox ml-auto">
90 <input 90 <input
91 type="checkbox" 91 type="checkbox"
92 class="custom-control-input" 92 class="custom-control-input"
93 ng-model="params.diferenciarProductos" 93 ng-model="params.diferenciarProductos"
94 id="customCheck1"> 94 id="customCheck1">
95 <label class="custom-control-label" for="customCheck1"></label> 95 <label class="custom-control-label" for="customCheck1"></label>
96 </div> 96 </div>
97 </div> 97 </div>
98 <small class="col-md-4 col-8 text-left my-1">Detallar por mes</small> 98 <small class="col-md-4 col-8 text-left my-1">Detallar por mes</small>
99 <div class="col-md-2 col-4 input-group mb-2"> 99 <div class="col-md-2 col-4 input-group mb-2">
100 <div class="custom-control custom-checkbox ml-auto"> 100 <div class="custom-control custom-checkbox ml-auto">
101 <input 101 <input
102 type="checkbox" 102 type="checkbox"
103 class="custom-control-input" 103 class="custom-control-input"
104 ng-model="params.diferenciarMeses" 104 ng-model="params.diferenciarMeses"
105 id="customCheck2"> 105 id="customCheck2">
106 <label class="custom-control-label" for="customCheck2"></label> 106 <label class="custom-control-label" for="customCheck2"></label>
107 </div> 107 </div>
108 </div> 108 </div>
109 </div> 109 </div>
110 <div 110 <div
111 ng-if="!buscar" 111 ng-if="!buscar"
112 class="row"> 112 class="row">
113 <div class="col-12"> 113 <div class="col-12">
114 <div 114 <div
115 class="gridInforme" 115 class="gridInforme"
116 ui-grid="gridOptions" 116 ui-grid="gridOptions"
117 ui-grid-exporter 117 ui-grid-exporter
118 ui-grid-resize-columns 118 ui-grid-resize-columns
119 ></div> 119 ></div>
120 </div> 120 </div>
121 </div> 121 </div>
122 </div> 122 </div>
123 <div class="modal-footer py-1"> 123 <div class="modal-footer py-1">
124 <button 124 <button
125 ladda="generando" 125 ladda="generando"
126 class="btn btn-sm btn-secondary" 126 class="btn btn-sm btn-secondary"
127 type="button" 127 type="button"
128 ng-click="generarInforme()" 128 ng-click="generarInforme()"
129 ng-show="buscar">Generar</button> 129 ng-show="buscar">Generar</button>
130 <button 130 <button
131 class="btn btn-sm btn-secondary" 131 class="btn btn-sm btn-secondary"
132 type="button" 132 type="button"
133 ng-click="cancel()" 133 ng-click="cancel()"
134 ng-show="buscar">Salir</button> 134 ng-show="buscar">Salir</button>
135 <button 135 <button
136 class="btn btn-sm btn-secondary" 136 class="btn btn-sm btn-secondary"
137 type="button" 137 type="button"
138 ng-click="volver()" 138 ng-click="volver()"
139 ng-hide="buscar">Volver</button> 139 ng-hide="buscar">Volver</button>
140 </div> 140 </div>
141 141