diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 13ac2bcb3..e48a20ec6 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -664,12 +664,12 @@ INSERT INTO `vn`.`itemCategory`(`id`, `name`, `display`, `color`, `icon`, `code`
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `life`,`workerFk`, `isPackaging`)
VALUES
- (1, 'CRI', 'Crisantemo', 2, 31, 5, 0),
- (2, 'ITG', 'Anthurium', 1, 31, 5, 0),
- (3, 'WPN', 'Paniculata', 2, 31, 5, 0),
- (4, 'PRT', 'Delivery ports', 3, NULL, 5, 1),
- (5, 'CON', 'Container', 3, NULL, 5, 1),
- (6, 'ALS', 'Alstroemeria', 1, 31, 5, 0);
+ (1, 'CRI', 'Crisantemo', 2, 31, 35, 0),
+ (2, 'ITG', 'Anthurium', 1, 31, 35, 0),
+ (3, 'WPN', 'Paniculata', 2, 31, 35, 0),
+ (4, 'PRT', 'Delivery ports', 3, NULL, 35, 1),
+ (5, 'CON', 'Container', 3, NULL, 35, 1),
+ (6, 'ALS', 'Alstroemeria', 1, 31, 35, 0);
INSERT INTO `vn`.`ink`(`id`, `name`, `picture`, `showOrder`)
VALUES
diff --git a/front/core/styles/font-family.scss b/front/core/styles/font-family.scss
index 035d96bc9..24d9bbe43 100644
--- a/front/core/styles/font-family.scss
+++ b/front/core/styles/font-family.scss
@@ -14,7 +14,7 @@
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
- src: url('./icons/Material-Design-Icons.woff2') format('woff2');
+ src: url('./icons/MaterialIcons-Regular.woff2') format('woff2');
}
.material-icons {
diff --git a/front/core/styles/icons/Material-Design-Icons.woff2 b/front/core/styles/icons/Material-Design-Icons.woff2
deleted file mode 100644
index 20f1f6746..000000000
Binary files a/front/core/styles/icons/Material-Design-Icons.woff2 and /dev/null differ
diff --git a/front/core/styles/icons/MaterialIcons-Regular.woff2 b/front/core/styles/icons/MaterialIcons-Regular.woff2
new file mode 100644
index 000000000..9fa211252
Binary files /dev/null and b/front/core/styles/icons/MaterialIcons-Regular.woff2 differ
diff --git a/modules/client/back/methods/client/consumption.js b/modules/client/back/methods/client/consumption.js
new file mode 100644
index 000000000..cb5b39c29
--- /dev/null
+++ b/modules/client/back/methods/client/consumption.js
@@ -0,0 +1,122 @@
+
+const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
+const buildFilter = require('vn-loopback/util/filter').buildFilter;
+const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
+
+module.exports = Self => {
+ Self.remoteMethodCtx('consumption', {
+ description: 'Find all instances of the model matched by filter from the data source.',
+ accessType: 'READ',
+ accepts: [
+ {
+ arg: 'filter',
+ type: 'Object',
+ description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string'
+ }, {
+ arg: 'search',
+ type: 'String',
+ description: `If it's and integer searchs by id, otherwise it searchs by name`
+ }, {
+ arg: 'itemFk',
+ type: 'Integer',
+ description: 'Item id'
+ }, {
+ arg: 'categoryFk',
+ type: 'Integer',
+ description: 'Category id'
+ }, {
+ arg: 'typeFk',
+ type: 'Integer',
+ description: 'Item type id',
+ }, {
+ arg: 'buyerFk',
+ type: 'Integer',
+ description: 'Buyer id'
+ }, {
+ arg: 'from',
+ type: 'Date',
+ description: `The from date filter`
+ }, {
+ arg: 'to',
+ type: 'Date',
+ description: `The to date filter`
+ }, {
+ arg: 'grouped',
+ type: 'Boolean',
+ description: 'Group by item'
+ }
+ ],
+ returns: {
+ type: ['Object'],
+ root: true
+ },
+ http: {
+ path: `/consumption`,
+ verb: 'GET'
+ }
+ });
+
+ Self.consumption = async(ctx, filter) => {
+ const conn = Self.dataSource.connector;
+ const args = ctx.args;
+ const where = buildFilter(ctx.args, (param, value) => {
+ switch (param) {
+ case 'search':
+ return /^\d+$/.test(value)
+ ? {'i.id': value}
+ : {'i.name': {like: `%${value}%`}};
+ case 'itemId':
+ return {'i.id': value};
+ case 'description':
+ return {'i.description': {like: `%${value}%`}};
+ case 'categoryId':
+ return {'it.categoryFk': value};
+ case 'typeId':
+ return {'it.id': value};
+ case 'buyerId':
+ return {'it.workerFk': value};
+ }
+ });
+ filter = mergeFilters(filter, {where});
+
+ let stmt = new ParameterizedSQL('SELECT');
+ if (args.grouped)
+ stmt.merge(`SUM(s.quantity) AS quantity,`);
+ else
+ stmt.merge(`s.quantity,`);
+
+ stmt.merge(`s.itemFk,
+ s.concept,
+ s.ticketFk,
+ t.shipped,
+ i.name AS itemName,
+ i.size AS itemSize,
+ i.typeFk AS itemTypeFk,
+ i.subName,
+ i.tag5,
+ i.value5,
+ i.tag6,
+ i.value6,
+ i.tag7,
+ i.value7,
+ i.tag8,
+ i.value8,
+ i.tag9,
+ i.value9,
+ i.tag10,
+ i.value10
+ FROM sale s
+ JOIN ticket t ON t.id = s.ticketFk
+ JOIN item i ON i.id = s.itemFk
+ JOIN itemType it ON it.id = i.typeFk`, [args.grouped]);
+
+ stmt.merge(conn.makeWhere(filter.where));
+
+ if (args.grouped)
+ stmt.merge(`GROUP BY s.itemFk`);
+
+ stmt.merge(conn.makePagination(filter));
+
+ return conn.executeStmt(stmt);
+ };
+};
diff --git a/modules/client/back/methods/client/specs/consumption.spec.js b/modules/client/back/methods/client/specs/consumption.spec.js
new file mode 100644
index 000000000..e7a42a8da
--- /dev/null
+++ b/modules/client/back/methods/client/specs/consumption.spec.js
@@ -0,0 +1,40 @@
+const app = require('vn-loopback/server/server');
+
+describe('client consumption() filter', () => {
+ it('should return a list of buyed items by ticket', async() => {
+ const ctx = {req: {accessToken: {userId: 9}}, args: {}};
+ const filter = {
+ where: {
+ clientFk: 101
+ },
+ order: 'itemTypeFk, itemName, itemSize'
+ };
+ const result = await app.models.Client.consumption(ctx, filter);
+
+ expect(result.length).toEqual(10);
+ });
+
+ it('should return a list of tickets grouped by item', async() => {
+ const ctx = {req: {accessToken: {userId: 9}},
+ args: {
+ grouped: true
+ }
+ };
+ const filter = {
+ where: {
+ clientFk: 101
+ },
+ order: 'itemTypeFk, itemName, itemSize'
+ };
+ const result = await app.models.Client.consumption(ctx, filter);
+
+ const firstRow = result[0];
+ const secondRow = result[1];
+ const thirdRow = result[2];
+
+ expect(result.length).toEqual(3);
+ expect(firstRow.quantity).toEqual(10);
+ expect(secondRow.quantity).toEqual(15);
+ expect(thirdRow.quantity).toEqual(20);
+ });
+});
diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js
index 8ceff0c2a..056b49d01 100644
--- a/modules/client/back/models/client.js
+++ b/modules/client/back/models/client.js
@@ -2,7 +2,6 @@ let request = require('request-promise-native');
let UserError = require('vn-loopback/util/user-error');
let getFinalState = require('vn-loopback/util/hook').getFinalState;
let isMultiple = require('vn-loopback/util/hook').isMultiple;
-const httpParamSerializer = require('vn-loopback/util/http').httpParamSerializer;
const LoopBackContext = require('loopback-context');
module.exports = Self => {
@@ -27,6 +26,7 @@ module.exports = Self => {
require('../methods/client/sendSms')(Self);
require('../methods/client/createAddress')(Self);
require('../methods/client/updateAddress')(Self);
+ require('../methods/client/consumption')(Self);
// Validations
diff --git a/modules/client/front/consumption-search-panel/index.html b/modules/client/front/consumption-search-panel/index.html
new file mode 100644
index 000000000..e957c891b
--- /dev/null
+++ b/modules/client/front/consumption-search-panel/index.html
@@ -0,0 +1,68 @@
+
diff --git a/modules/client/front/consumption-search-panel/index.js b/modules/client/front/consumption-search-panel/index.js
new file mode 100644
index 000000000..9b9354a93
--- /dev/null
+++ b/modules/client/front/consumption-search-panel/index.js
@@ -0,0 +1,7 @@
+import ngModule from '../module';
+import SearchPanel from 'core/components/searchbar/search-panel';
+
+ngModule.component('vnConsumptionSearchPanel', {
+ template: require('./index.html'),
+ controller: SearchPanel
+});
diff --git a/modules/client/front/consumption-search-panel/locale/es.yml b/modules/client/front/consumption-search-panel/locale/es.yml
new file mode 100644
index 000000000..68de42b23
--- /dev/null
+++ b/modules/client/front/consumption-search-panel/locale/es.yml
@@ -0,0 +1,3 @@
+Item id: Id artículo
+From: Desde
+To: Hasta
\ No newline at end of file
diff --git a/modules/client/front/consumption/index.html b/modules/client/front/consumption/index.html
new file mode 100644
index 000000000..8ea65ecae
--- /dev/null
+++ b/modules/client/front/consumption/index.html
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Item
+ Ticket
+ Fecha
+ Description
+ Quantity
+
+
+
+
+
+
+ {{::sale.itemFk}}
+
+
+
+
+ {{::sale.ticketFk}}
+
+
+ {{::sale.shipped | date: 'dd/MM/yyyy'}}
+
+
+
+
+ {{::sale.quantity | dashIfEmpty}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/client/front/consumption/index.js b/modules/client/front/consumption/index.js
new file mode 100644
index 000000000..4b075abb9
--- /dev/null
+++ b/modules/client/front/consumption/index.js
@@ -0,0 +1,69 @@
+import ngModule from '../module';
+import Section from 'salix/components/section';
+
+class Controller extends Section {
+ constructor($element, $, vnReport, vnEmail) {
+ super($element, $);
+ this.vnReport = vnReport;
+ this.vnEmail = vnEmail;
+
+ this.filter = {
+ where: {
+ isPackaging: false
+ }
+ };
+
+ const minDate = new Date();
+ minDate.setHours(0, 0, 0, 0);
+ minDate.setMonth(minDate.getMonth() - 2);
+
+ const maxDate = new Date();
+ maxDate.setHours(23, 59, 59, 59);
+
+ this.filterParams = {
+ from: minDate,
+ to: maxDate
+ };
+ }
+
+ get reportParams() {
+ const userParams = this.$.model.userParams;
+ return Object.assign({
+ authorization: this.vnToken.token,
+ recipientId: this.client.id
+ }, userParams);
+ }
+
+ showTicketDescriptor(event, sale) {
+ if (!sale.isTicket) return;
+
+ this.$.ticketDescriptor.show(event.target, sale.origin);
+ }
+
+ showReport() {
+ this.vnReport.show('campaign-metrics', this.reportParams);
+ }
+
+ sendEmail() {
+ this.vnEmail.send('campaign-metrics', this.reportParams);
+ }
+
+ changeGrouped(value) {
+ const model = this.$.model;
+
+ model.addFilter({}, {grouped: value});
+ }
+}
+
+Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
+
+ngModule.component('vnClientConsumption', {
+ template: require('./index.html'),
+ controller: Controller,
+ bindings: {
+ client: '<'
+ },
+ require: {
+ card: '^vnClientCard'
+ }
+});
diff --git a/modules/client/front/consumption/index.spec.js b/modules/client/front/consumption/index.spec.js
new file mode 100644
index 000000000..d76bc1e76
--- /dev/null
+++ b/modules/client/front/consumption/index.spec.js
@@ -0,0 +1,72 @@
+import './index.js';
+import crudModel from 'core/mocks/crud-model';
+
+describe('Client', () => {
+ describe('Component vnClientConsumption', () => {
+ let $scope;
+ let controller;
+ let $httpParamSerializer;
+ let $httpBackend;
+
+ beforeEach(ngModule('client'));
+
+ beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpParamSerializer_, _$httpBackend_) => {
+ $scope = $rootScope.$new();
+ $httpParamSerializer = _$httpParamSerializer_;
+ $httpBackend = _$httpBackend_;
+ const $element = angular.element(' {
+ it('should call the window.open function', () => {
+ jest.spyOn(window, 'open').mockReturnThis();
+
+ const now = new Date();
+ controller.$.model.userParams = {
+ from: now,
+ to: now
+ };
+
+ controller.showReport();
+
+ const expectedParams = {
+ recipientId: 101,
+ from: now,
+ to: now
+ };
+ const serializedParams = $httpParamSerializer(expectedParams);
+ const path = `api/report/campaign-metrics?${serializedParams}`;
+
+ expect(window.open).toHaveBeenCalledWith(path);
+ });
+ });
+
+ describe('sendEmail()', () => {
+ it('should make a GET query sending the report', () => {
+ const now = new Date();
+ controller.$.model.userParams = {
+ from: now,
+ to: now
+ };
+ const expectedParams = {
+ recipientId: 101,
+ from: now,
+ to: now
+ };
+
+ const serializedParams = $httpParamSerializer(expectedParams);
+ const path = `email/campaign-metrics?${serializedParams}`;
+
+ $httpBackend.expect('GET', path).respond({});
+ controller.sendEmail();
+ $httpBackend.flush();
+ });
+ });
+ });
+});
+
diff --git a/modules/client/front/consumption/locale/es.yml b/modules/client/front/consumption/locale/es.yml
new file mode 100644
index 000000000..adf0f060c
--- /dev/null
+++ b/modules/client/front/consumption/locale/es.yml
@@ -0,0 +1,6 @@
+Group by item: Agrupar por artículo
+Open as PDF: Abrir como PDF
+Send to email: Enviar por email
+Search by item id or name: Buscar por id de artículo o nombre
+The consumption report will be sent: Se enviará el informe de consumo
+Please, confirm: Por favor, confirma
diff --git a/modules/client/front/descriptor/index.html b/modules/client/front/descriptor/index.html
index 19fcba128..aac5eb343 100644
--- a/modules/client/front/descriptor/index.html
+++ b/modules/client/front/descriptor/index.html
@@ -13,11 +13,6 @@
translate>
Send SMS
-
- View consumer report
-
@@ -94,28 +89,4 @@
-
-
-
-
-
-
-
-
-
-
- Accept
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/modules/client/front/descriptor/index.js b/modules/client/front/descriptor/index.js
index 7d3f628b4..89248ebed 100644
--- a/modules/client/front/descriptor/index.js
+++ b/modules/client/front/descriptor/index.js
@@ -39,14 +39,6 @@ class Controller extends Descriptor {
};
this.$.sms.open();
}
-
- onConsumerReportAccept() {
- this.vnReport.show('campaign-metrics', {
- recipientId: this.id,
- from: this.from,
- to: this.to,
- });
- }
}
ngModule.vnComponent('vnClientDescriptor', {
diff --git a/modules/client/front/index.js b/modules/client/front/index.js
index 324046206..7df6f7b10 100644
--- a/modules/client/front/index.js
+++ b/modules/client/front/index.js
@@ -40,3 +40,5 @@ import './postcode';
import './dms/index';
import './dms/create';
import './dms/edit';
+import './consumption';
+import './consumption-search-panel';
diff --git a/modules/client/front/locale/es.yml b/modules/client/front/locale/es.yml
index a20acf6cb..e332a0229 100644
--- a/modules/client/front/locale/es.yml
+++ b/modules/client/front/locale/es.yml
@@ -56,4 +56,5 @@ Requested credits: Créditos solicitados
Contacts: Contactos
Samples: Plantillas
Send sample: Enviar plantilla
-Log: Historial
\ No newline at end of file
+Log: Historial
+Consumption: Consumo
\ No newline at end of file
diff --git a/modules/client/front/routes.json b/modules/client/front/routes.json
index a7920f403..2a5f63bb2 100644
--- a/modules/client/front/routes.json
+++ b/modules/client/front/routes.json
@@ -18,16 +18,17 @@
{"state": "client.card.greuge.index", "icon": "work"},
{"state": "client.card.balance.index", "icon": "icon-invoices"},
{"state": "client.card.recovery.index", "icon": "icon-recovery"},
+ {"state": "client.card.webAccess", "icon": "cloud"},
{"state": "client.card.log", "icon": "history"},
{
"description": "Others",
"icon": "more",
"childs": [
- {"state": "client.card.webAccess", "icon": "cloud"},
+ {"state": "client.card.sample.index", "icon": "mail"},
+ {"state": "client.card.consumption", "icon": "show_chart"},
{"state": "client.card.mandate", "icon": "pan_tool"},
{"state": "client.card.creditInsurance.index", "icon": "icon-solunion"},
{"state": "client.card.contact", "icon": "contact_phone"},
- {"state": "client.card.sample.index", "icon": "mail"},
{"state": "client.card.webPayment", "icon": "icon-onlinepayment"},
{"state": "client.card.dms.index", "icon": "cloud_upload"}
]
@@ -350,6 +351,15 @@
"params": {
"client": "$ctrl.client"
}
+ },
+ {
+ "url": "/consumption",
+ "state": "client.card.consumption",
+ "component": "vn-client-consumption",
+ "description": "Consumption",
+ "params": {
+ "client": "$ctrl.client"
+ }
}
]
}
diff --git a/print/core/components/email-footer/assets/css/style.css b/print/core/components/email-footer/assets/css/style.css
index 4bc22fdfd..29620a64e 100644
--- a/print/core/components/email-footer/assets/css/style.css
+++ b/print/core/components/email-footer/assets/css/style.css
@@ -46,6 +46,7 @@
}
.privacy {
+ text-align: center;
padding: 20px 0;
font-size: 10px;
font-weight: 100
diff --git a/print/core/components/email-header/assets/css/style.css b/print/core/components/email-header/assets/css/style.css
index 4db5e2b2e..e6451ca5a 100644
--- a/print/core/components/email-header/assets/css/style.css
+++ b/print/core/components/email-header/assets/css/style.css
@@ -1,5 +1,10 @@
+header {
+ text-align: center
+}
+
header .logo {
- margin-bottom: 15px;
+ margin-top: 25px;
+ margin-bottom: 25px
}
header .logo img {
diff --git a/print/core/filters/date.js b/print/core/filters/date.js
index 0988eda75..5d1bc0de5 100644
--- a/print/core/filters/date.js
+++ b/print/core/filters/date.js
@@ -2,5 +2,6 @@ const Vue = require('vue');
const strftime = require('strftime');
Vue.filter('date', function(value, specifiers = '%d-%m-%Y') {
+ if (!(value instanceof Date)) value = new Date(value);
return strftime(specifiers, value);
});
diff --git a/print/templates/email/campaign-metrics/attachments.json b/print/templates/email/campaign-metrics/attachments.json
index 3f6a93bb5..d836d6040 100644
--- a/print/templates/email/campaign-metrics/attachments.json
+++ b/print/templates/email/campaign-metrics/attachments.json
@@ -1,6 +1,6 @@
[
{
- "filename": "campaing-metrics",
+ "filename": "campaign-metrics.pdf",
"component": "campaign-metrics"
}
]
\ No newline at end of file
diff --git a/print/templates/email/campaign-metrics/campaign-metrics.html b/print/templates/email/campaign-metrics/campaign-metrics.html
index 4ba95adb9..9d7014f34 100644
--- a/print/templates/email/campaign-metrics/campaign-metrics.html
+++ b/print/templates/email/campaign-metrics/campaign-metrics.html
@@ -25,7 +25,7 @@
{{ $t('title') }}
{{$t('dear')}},
-
{{$t('description')}}
+
diff --git a/print/templates/email/campaign-metrics/campaign-metrics.js b/print/templates/email/campaign-metrics/campaign-metrics.js
index 51d2ebb44..0ace0fc25 100755
--- a/print/templates/email/campaign-metrics/campaign-metrics.js
+++ b/print/templates/email/campaign-metrics/campaign-metrics.js
@@ -4,7 +4,17 @@ const emailFooter = new Component('email-footer');
module.exports = {
name: 'campaign-metrics',
-
+ created() {
+ this.filters = this.$options.filters;
+ },
+ computed: {
+ minDate: function() {
+ return this.filters.date(this.from, '%d-%m-%Y');
+ },
+ maxDate: function() {
+ return this.filters.date(this.to, '%d-%m-%Y');
+ }
+ },
components: {
'email-header': emailHeader.build(),
'email-footer': emailFooter.build()
diff --git a/print/templates/email/campaign-metrics/locale/es.yml b/print/templates/email/campaign-metrics/locale/es.yml
index e662ca614..d1c1182a2 100644
--- a/print/templates/email/campaign-metrics/locale/es.yml
+++ b/print/templates/email/campaign-metrics/locale/es.yml
@@ -1,7 +1,8 @@
-subject: Informe consumo campaña
-title: Informe consumo campaña
+subject: Informe de consumo
+title: Informe de consumo
dear: Estimado cliente
-description: Con motivo de esta próxima campaña, me complace
+description: Tal y como nos ha solicitado nos complace
relacionarle a continuación el consumo que nos consta en su cuenta para las
- mismas fechas del año pasado. Espero le sea de utilidad para preparar su pedido.
+ fechas comprendidas entre {0} y {1} .
+ Espero le sea de utilidad para preparar su pedido.
Al mismo tiempo aprovecho la ocasión para saludarle cordialmente.
diff --git a/print/templates/reports/campaign-metrics/campaign-metrics.js b/print/templates/reports/campaign-metrics/campaign-metrics.js
index ef1d735de..420c1ffb1 100755
--- a/print/templates/reports/campaign-metrics/campaign-metrics.js
+++ b/print/templates/reports/campaign-metrics/campaign-metrics.js
@@ -6,9 +6,6 @@ const reportFooter = new Component('report-footer');
module.exports = {
name: 'campaign-metrics',
async serverPrefetch() {
- this.to = new Date(this.to);
- this.from = new Date(this.from);
-
this.client = await this.fetchClient(this.recipientId);
this.sales = await this.fetchSales(this.recipientId, this.from, this.to);
@@ -54,7 +51,7 @@ module.exports = {
t.clientFk = ? AND it.isPackaging = FALSE
AND DATE(t.shipped) BETWEEN ? AND ?
GROUP BY s.itemFk
- ORDER BY i.typeFk , i.name , i.size`, [clientId, from, to]);
+ ORDER BY i.typeFk , i.name`, [clientId, from, to]);
},
},
components: {
@@ -66,12 +63,10 @@ module.exports = {
required: true
},
from: {
- required: true,
- type: Date
+ required: true
},
to: {
- required: true,
- type: Date
+ required: true
}
}
};
diff --git a/print/templates/reports/campaign-metrics/locale/es.yml b/print/templates/reports/campaign-metrics/locale/es.yml
index df0c91971..8a4cc4637 100644
--- a/print/templates/reports/campaign-metrics/locale/es.yml
+++ b/print/templates/reports/campaign-metrics/locale/es.yml
@@ -1,4 +1,4 @@
-title: Consumo de campaña
+title: Consumo
Client: Cliente
clientData: Datos del cliente
dated: Fecha