From e1ac64a8d96bce68d7a6c7691441de5aa50634aa Mon Sep 17 00:00:00 2001 From: bernat Date: Wed, 16 Dec 2020 08:05:41 +0100 Subject: [PATCH 1/4] supplier consumption --- .../back/methods/supplier/consumption.js | 165 ++++++++++++++++++ .../supplier/specs/consumption.spec.js | 36 ++++ modules/supplier/back/models/supplier.js | 1 + .../front/consumption-search-panel/index.html | 68 ++++++++ .../front/consumption-search-panel/index.js | 15 ++ .../consumption-search-panel/locale/es.yml | 7 + modules/supplier/front/consumption/index.html | 82 +++++++++ modules/supplier/front/consumption/index.js | 65 +++++++ .../supplier/front/consumption/index.spec.js | 72 ++++++++ .../supplier/front/consumption/locale/es.yml | 2 + modules/supplier/front/index.js | 2 + modules/supplier/front/routes.json | 12 +- .../assets/css/import.js | 9 + .../assets/css/style.css | 11 ++ .../supplier-campaign-metrics/locale/es.yml | 13 ++ .../supplier-campaign-metrics/sql/buys.sql | 33 ++++ .../supplier-campaign-metrics/sql/entries.sql | 8 + .../sql/supplier.sql | 12 ++ .../supplier-campaign-metrics.html | 122 +++++++++++++ .../supplier-campaign-metrics.js | 61 +++++++ 20 files changed, 795 insertions(+), 1 deletion(-) create mode 100644 modules/supplier/back/methods/supplier/consumption.js create mode 100644 modules/supplier/back/methods/supplier/specs/consumption.spec.js create mode 100644 modules/supplier/front/consumption-search-panel/index.html create mode 100644 modules/supplier/front/consumption-search-panel/index.js create mode 100644 modules/supplier/front/consumption-search-panel/locale/es.yml create mode 100644 modules/supplier/front/consumption/index.html create mode 100644 modules/supplier/front/consumption/index.js create mode 100644 modules/supplier/front/consumption/index.spec.js create mode 100644 modules/supplier/front/consumption/locale/es.yml create mode 100644 print/templates/reports/supplier-campaign-metrics/assets/css/import.js create mode 100644 print/templates/reports/supplier-campaign-metrics/assets/css/style.css create mode 100644 print/templates/reports/supplier-campaign-metrics/locale/es.yml create mode 100644 print/templates/reports/supplier-campaign-metrics/sql/buys.sql create mode 100644 print/templates/reports/supplier-campaign-metrics/sql/entries.sql create mode 100644 print/templates/reports/supplier-campaign-metrics/sql/supplier.sql create mode 100644 print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html create mode 100755 print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js diff --git a/modules/supplier/back/methods/supplier/consumption.js b/modules/supplier/back/methods/supplier/consumption.js new file mode 100644 index 000000000..1d7cd0411 --- /dev/null +++ b/modules/supplier/back/methods/supplier/consumption.js @@ -0,0 +1,165 @@ + +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: 'itemId', + type: 'Number', + description: 'Item id' + }, { + arg: 'categoryId', + type: 'Number', + description: 'Category id' + }, { + arg: 'typeId', + type: 'Number', + description: 'Item type id', + }, { + arg: 'buyerId', + type: 'Number', + description: 'Buyer id' + }, { + arg: 'from', + type: 'Date', + description: `The from date filter` + }, { + arg: 'to', + type: 'Date', + description: `The to date filter` + } + ], + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/consumption`, + verb: 'GET' + } + }); + + Self.consumption = async(ctx, filter) => { + const conn = Self.dataSource.connector; + 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}; + case 'from': + return {'e.shipped': {gte: value}}; + case 'to': + return {'e.shipped': {lte: value}}; + } + }); + + filter = mergeFilters(filter, {where}); + let stmts = []; + let stmt; + + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.entry'); + + stmt = new ParameterizedSQL( + `CREATE TEMPORARY TABLE tmp.entry + (INDEX (id)) + ENGINE = MEMORY + SELECT + e.id, + e.ref, + e.supplierFk, + t.shipped + FROM vn.entry e + JOIN vn.travel t ON t.id = e.travelFk`); + + stmt.merge(conn.makeGroupBy('e.id')); + stmts.push(stmt); + + const entriesIndex = stmts.push('SELECT * FROM tmp.entry') - 1; + stmt = new ParameterizedSQL( + `SELECT + b.id AS buyId, + b.itemFk, + b.entryFk, + b.quantity, + CAST(b.buyingValue AS DECIMAL(10,2)) AS price, + CAST(SUM(b.buyingValue*b.quantity)AS DECIMAL(10,2)) AS total, + i.id, + i.description, + i.name AS itemName, + i.subName, + i.size AS itemSize, + i.typeFk AS itemTypeFk, + i.tag5, + i.value5, + i.tag6, + i.value6, + i.tag7, + i.value7, + i.tag8, + i.value8, + i.tag9, + i.value9, + i.tag10, + i.value10, + it.id, + it.workerFk, + it.categoryFk, + it.code AS itemTypeCode + FROM buy b + JOIN tmp.entry e ON e.id = b.entryFk + JOIN item i ON i.id = b.itemFk + JOIN itemType it ON it.id = i.typeFk` + ); + stmt.merge(conn.makeWhere(filter.where)); + stmt.merge(conn.makeGroupBy('b.id')); + const buysIndex = stmts.push(stmt) - 1; + stmts.push(`DROP TEMPORARY TABLE tmp.entry`); + const sql = ParameterizedSQL.join(stmts, ';'); + + stmt.merge(conn.makePagination(filter)); + + const result = await conn.executeStmt(sql); + + const entries = result[entriesIndex]; + const buys = result[buysIndex]; + const entriesMap = new Map(); + + for (let entry of entries) + entriesMap.set(entry.id, entry); + + for (let buy of buys) { + const entry = entriesMap.get(buy.entryFk); + + if (entry) { + if (!entry.buys) entry.buys = []; + + entry.buys.push(buy); + } + } + return entries; + }; +}; diff --git a/modules/supplier/back/methods/supplier/specs/consumption.spec.js b/modules/supplier/back/methods/supplier/specs/consumption.spec.js new file mode 100644 index 000000000..2436f485e --- /dev/null +++ b/modules/supplier/back/methods/supplier/specs/consumption.spec.js @@ -0,0 +1,36 @@ +const app = require('vn-loopback/server/server'); + +describe('supplier consumption() filter', () => { + it('should return a list of entries from the supplier 2', async() => { + const ctx = {req: {accessToken: {userId: 9}}, args: {}}; + const filter = { + where: { + supplierFk: 2 + }, + order: 'itemTypeFk, itemName, itemSize' + }; + const result = await app.models.Supplier.consumption(ctx, filter); + + expect(result.length).toEqual(8); + }); + + it('should return a list of entries from the item id 1 and supplier 1', async() => { + const ctx = {req: {accessToken: {userId: 9}}, + args: { + itemId: 1 + } + }; + const filter = { + where: { + supplierFk: 1 + }, + order: 'itemTypeFk, itemName, itemSize' + }; + const result = await app.models.Supplier.consumption(ctx, filter); + + const expectedItemId = 1; + const firstRowBuys = result[0].buys[0]; + + expect(firstRowBuys.itemFk).toEqual(expectedItemId); + }); +}); diff --git a/modules/supplier/back/models/supplier.js b/modules/supplier/back/models/supplier.js index 37c94c266..d214b5fad 100644 --- a/modules/supplier/back/models/supplier.js +++ b/modules/supplier/back/models/supplier.js @@ -5,6 +5,7 @@ module.exports = Self => { require('../methods/supplier/filter')(Self); require('../methods/supplier/getSummary')(Self); require('../methods/supplier/updateFiscalData')(Self); + require('../methods/supplier/consumption')(Self); Self.validatesPresenceOf('name', { message: 'The social name cannot be empty' diff --git a/modules/supplier/front/consumption-search-panel/index.html b/modules/supplier/front/consumption-search-panel/index.html new file mode 100644 index 000000000..e957c891b --- /dev/null +++ b/modules/supplier/front/consumption-search-panel/index.html @@ -0,0 +1,68 @@ +
+
+ + + + + + + + + {{nickname}} + + + + + +
{{name}}
+
+ {{category.name}} +
+
+
+ + +
+ + + + + + + + + +
+
diff --git a/modules/supplier/front/consumption-search-panel/index.js b/modules/supplier/front/consumption-search-panel/index.js new file mode 100644 index 000000000..8e636661a --- /dev/null +++ b/modules/supplier/front/consumption-search-panel/index.js @@ -0,0 +1,15 @@ +import ngModule from '../module'; +import SearchPanel from 'core/components/searchbar/search-panel'; + +class Controller extends SearchPanel { + constructor($, $element) { + super($, $element); + + this.filter = this.$.filter; + } +} + +ngModule.vnComponent('vnSupplierConsumptionSearchPanel', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/supplier/front/consumption-search-panel/locale/es.yml b/modules/supplier/front/consumption-search-panel/locale/es.yml new file mode 100644 index 000000000..f136283f8 --- /dev/null +++ b/modules/supplier/front/consumption-search-panel/locale/es.yml @@ -0,0 +1,7 @@ +Item id: Id artículo +From: Desde +To: Hasta +Campaign: Campaña +allSaints: Día de todos los Santos +valentinesDay: Día de San Valentín +mothersDay: Día de la madre \ No newline at end of file diff --git a/modules/supplier/front/consumption/index.html b/modules/supplier/front/consumption/index.html new file mode 100644 index 000000000..4587f60f2 --- /dev/null +++ b/modules/supplier/front/consumption/index.html @@ -0,0 +1,82 @@ + + + + + + + + +
+ + + + + + +
+ + + + Entry + {{::entry.id}} + Date + {{::entry.shipped | date: 'dd/MM/yyyy'}} + Reference + {{::entry.ref}} + + + + + + + {{::buy.itemName}} + + + + + + + {{::buy.quantity | dashIfEmpty}} + {{::buy.price | dashIfEmpty}} + {{::buy.total | dashIfEmpty}} + + + + + + + + + + +
+
+ + diff --git a/modules/supplier/front/consumption/index.js b/modules/supplier/front/consumption/index.js new file mode 100644 index 000000000..9e1357a9f --- /dev/null +++ b/modules/supplier/front/consumption/index.js @@ -0,0 +1,65 @@ +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.setDefaultFilter(); + } + + setDefaultFilter() { + 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.supplier.id + }, userParams); + } + + showReport() { + this.vnReport.show('supplier-campaign-metrics', this.reportParams); + } + + sendEmail() { + this.vnEmail.send('supplier-campaign-metrics', this.reportParams); + } + + getTotal(entry) { + if (entry.buys) { + let total = 0; + for (let buy of entry.buys) + total += buy.total; + + return total; + } + } +} + +Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail']; + +ngModule.vnComponent('vnSupplierConsumption', { + template: require('./index.html'), + controller: Controller, + bindings: { + supplier: '<' + }, + require: { + card: '^vnSupplierCard' + } +}); diff --git a/modules/supplier/front/consumption/index.spec.js b/modules/supplier/front/consumption/index.spec.js new file mode 100644 index 000000000..35eb6935c --- /dev/null +++ b/modules/supplier/front/consumption/index.spec.js @@ -0,0 +1,72 @@ +import './index.js'; +import crudModel from 'core/mocks/crud-model'; + +describe('Supplier', () => { + describe('Component vnSupplierConsumption', () => { + let $scope; + let controller; + let $httpParamSerializer; + let $httpBackend; + + beforeEach(ngModule('supplier')); + + beforeEach(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: 2, + from: now, + to: now + }; + const serializedParams = $httpParamSerializer(expectedParams); + const path = `api/report/supplier-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: 2, + from: now, + to: now + }; + + const serializedParams = $httpParamSerializer(expectedParams); + const path = `email/supplier-campaign-metrics?${serializedParams}`; + + $httpBackend.expect('GET', path).respond({}); + controller.sendEmail(); + $httpBackend.flush(); + }); + }); + }); +}); + diff --git a/modules/supplier/front/consumption/locale/es.yml b/modules/supplier/front/consumption/locale/es.yml new file mode 100644 index 000000000..dd052696c --- /dev/null +++ b/modules/supplier/front/consumption/locale/es.yml @@ -0,0 +1,2 @@ + +Total entry: Total entrada diff --git a/modules/supplier/front/index.js b/modules/supplier/front/index.js index 8b4ac9ca2..613e02e4a 100644 --- a/modules/supplier/front/index.js +++ b/modules/supplier/front/index.js @@ -10,3 +10,5 @@ import './basic-data'; import './fiscal-data'; import './contact'; import './log'; +import './consumption'; +import './consumption-search-panel'; diff --git a/modules/supplier/front/routes.json b/modules/supplier/front/routes.json index 10727fecf..7501d329c 100644 --- a/modules/supplier/front/routes.json +++ b/modules/supplier/front/routes.json @@ -12,7 +12,8 @@ {"state": "supplier.card.basicData", "icon": "settings"}, {"state": "supplier.card.fiscalData", "icon": "account_balance"}, {"state": "supplier.card.contact", "icon": "contact_phone"}, - {"state": "supplier.card.log", "icon": "history"} + {"state": "supplier.card.log", "icon": "history"}, + {"state": "supplier.card.consumption", "icon": "show_chart"} ] }, "routes": [ @@ -75,6 +76,15 @@ "params": { "supplier": "$ctrl.supplier" } + }, + { + "url": "/consumption?q", + "state": "supplier.card.consumption", + "component": "vn-supplier-consumption", + "description": "Consumption", + "params": { + "supplier": "$ctrl.supplier" + } } ] } \ No newline at end of file diff --git a/print/templates/reports/supplier-campaign-metrics/assets/css/import.js b/print/templates/reports/supplier-campaign-metrics/assets/css/import.js new file mode 100644 index 000000000..fd8796c2b --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/assets/css/import.js @@ -0,0 +1,9 @@ +const Stylesheet = require(`${appPath}/core/stylesheet`); + +module.exports = new Stylesheet([ + `${appPath}/common/css/spacing.css`, + `${appPath}/common/css/misc.css`, + `${appPath}/common/css/layout.css`, + `${appPath}/common/css/report.css`, + `${__dirname}/style.css`]) + .mergeStyles(); diff --git a/print/templates/reports/supplier-campaign-metrics/assets/css/style.css b/print/templates/reports/supplier-campaign-metrics/assets/css/style.css new file mode 100644 index 000000000..6e730869e --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/assets/css/style.css @@ -0,0 +1,11 @@ +.column-oriented { + margin-top: 50px !important; +} + +.bottom-line > tr { + border-bottom: 1px solid #ccc; +} + +.bottom-line tr:nth-last-child() { + border-bottom: none; +} diff --git a/print/templates/reports/supplier-campaign-metrics/locale/es.yml b/print/templates/reports/supplier-campaign-metrics/locale/es.yml new file mode 100644 index 000000000..31c1e17dd --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/locale/es.yml @@ -0,0 +1,13 @@ +title: Consumo +Supplier: Proveedor +supplierData: Datos del proveedor +dated: Fecha +From: Desde +To: Hasta +supplier: Proveedor {0} +reference: Referencia +Quantity: Cantidad +entry: Entrada +itemName: Artículo +price: Precio +total: Total \ No newline at end of file diff --git a/print/templates/reports/supplier-campaign-metrics/sql/buys.sql b/print/templates/reports/supplier-campaign-metrics/sql/buys.sql new file mode 100644 index 000000000..898568c0a --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/sql/buys.sql @@ -0,0 +1,33 @@ +SELECT + b.id AS buyId, + b.itemFk, + b.entryFk, + CAST(b.buyingValue AS DECIMAL(10,2)) AS price, + b.quantity, + i.id, + i.description, + i.name AS itemName, + i.subName, + i.size AS itemSize, + i.typeFk AS itemTypeFk, + i.tag5, + i.value5, + i.tag6, + i.value6, + i.tag7, + i.value7, + i.tag8, + i.value8, + i.tag9, + i.value9, + i.tag10, + i.value10, + it.id, + it.workerFk, + it.categoryFk, + it.code AS itemTypeCode + FROM buy b + JOIN item i ON i.id = b.itemFk + JOIN itemType it ON it.id = i.typeFk + WHERE b.entryFk IN(:entriesId) AND b.quantity > 0 + ORDER BY i.typeFk , i.name \ No newline at end of file diff --git a/print/templates/reports/supplier-campaign-metrics/sql/entries.sql b/print/templates/reports/supplier-campaign-metrics/sql/entries.sql new file mode 100644 index 000000000..946c4ab65 --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/sql/entries.sql @@ -0,0 +1,8 @@ + SELECT + e.id, + e.ref, + e.supplierFk, + t.shipped + FROM vn.entry e + JOIN vn.travel t ON t.id = e.travelFk + WHERE e.supplierFk = ? AND DATE(t.shipped) BETWEEN ? AND ? diff --git a/print/templates/reports/supplier-campaign-metrics/sql/supplier.sql b/print/templates/reports/supplier-campaign-metrics/sql/supplier.sql new file mode 100644 index 000000000..0c2fa12ed --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/sql/supplier.sql @@ -0,0 +1,12 @@ +SELECT + s.street, + s.city, + s.postcode, + s.id, + s.name AS supplierName, + p.name AS province, + co.country + FROM supplier s + JOIN province p ON s.provinceFk = p.id + JOIN country co ON s.countryFk = co.id + WHERE s.id = ? diff --git a/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html new file mode 100644 index 000000000..f5286df1e --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html @@ -0,0 +1,122 @@ + + + + + + + + + +
+ + + +
+
+
+
+

{{$t('title')}}

+
+ + + + + + + + + + + + + + + +
{{$t('Supplier')}}{{supplier.id}}
{{$t('From')}}{{from | date('%d-%m-%Y')}}
{{$t('To')}}{{to | date('%d-%m-%Y')}}
+
+
+
+
+
{{$t('supplierData')}}
+
+

{{supplier.supplierName}}

+
+ {{supplier.street}} +
+
+ {{supplier.postcode}}, {{supplier.city}} ({{supplier.province}}) +
+
+ {{supplier.country}} +
+
+
+
+
+
+ + + + + + + + + + + +
{{$t('entry')}}{{entry.id}}{{$t('dated')}}{{entry.shipped | date('%d-%m-%Y')}}{{$t('reference')}}{{entry.ref}}
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
{{$t('itemName')}}{{$t('Quantity')}}{{$t('price')}}{{$t('total')}}
{{buy.itemName}}{{buy.quantity}}{{buy.price | currency('EUR', $i18n.locale)}}{{buy.quantity * buy.price | currency('EUR', $i18n.locale)}}
+
+ {{buy.tag5}} + {{buy.value5}} +
+
+
+ {{buy.tag6}} + {{buy.value6}} +
+
+
+ {{buy.tag7}} + {{buy.value7}} +
+
+
+
+
+
+ + + +
+ + \ No newline at end of file diff --git a/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js new file mode 100755 index 000000000..8a0a378a2 --- /dev/null +++ b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.js @@ -0,0 +1,61 @@ +const Component = require(`${appPath}/core/component`); +const reportHeader = new Component('report-header'); +const reportFooter = new Component('report-footer'); + +module.exports = { + name: 'supplier-campaign-metrics', + async serverPrefetch() { + this.supplier = await this.fetchSupplier(this.recipientId); + let entries = await this.fetchEntries(this.recipientId, this.from, this.to); + + const entriesId = []; + + for (let entry of entries) + entriesId.push(entry.id); + + const buys = await this.fetchBuys(entriesId); + + const entriesMap = new Map(); + for (let entry of entries) + entriesMap.set(entry.id, entry); + + for (let buy of buys) { + const entry = entriesMap.get(buy.entryFk); + if (entry) { + if (!entry.buys) entry.buys = []; + + entry.buys.push(buy); + } + } + + this.entries = entries; + if (!this.supplier) + throw new Error('Something went wrong'); + }, + methods: { + fetchSupplier(supplierId) { + return this.findOneFromDef('supplier', [supplierId]); + }, + fetchEntries(supplierId, from, to) { + return this.rawSqlFromDef('entries', [supplierId, from, to]); + }, + fetchBuys(entriesId) { + return this.rawSqlFromDef('buys', {entriesId}); + } + }, + components: { + 'report-header': reportHeader.build(), + 'report-footer': reportFooter.build() + }, + props: { + recipientId: { + required: true + }, + from: { + required: true + }, + to: { + required: true + } + } +}; From a13b554a26d9ebbe5ab784a1d38567790b228327 Mon Sep 17 00:00:00 2001 From: bernat Date: Wed, 16 Dec 2020 14:42:57 +0100 Subject: [PATCH 2/4] supplier consumption email and fix report --- .../assets/css/import.js | 8 ++ .../attachments.json | 6 ++ .../supplier-campaign-metrics/locale/es.yml | 8 ++ .../supplier-campaign-metrics.html | 46 +++++++++++ .../supplier-campaign-metrics.js | 33 ++++++++ .../assets/css/style.css | 11 ++- .../supplier-campaign-metrics.html | 81 ++++++++----------- 7 files changed, 143 insertions(+), 50 deletions(-) create mode 100644 print/templates/email/supplier-campaign-metrics/assets/css/import.js create mode 100644 print/templates/email/supplier-campaign-metrics/attachments.json create mode 100644 print/templates/email/supplier-campaign-metrics/locale/es.yml create mode 100644 print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.html create mode 100755 print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js diff --git a/print/templates/email/supplier-campaign-metrics/assets/css/import.js b/print/templates/email/supplier-campaign-metrics/assets/css/import.js new file mode 100644 index 000000000..b44d6bd37 --- /dev/null +++ b/print/templates/email/supplier-campaign-metrics/assets/css/import.js @@ -0,0 +1,8 @@ +const Stylesheet = require(`${appPath}/core/stylesheet`); + +module.exports = new Stylesheet([ + `${appPath}/common/css/spacing.css`, + `${appPath}/common/css/misc.css`, + `${appPath}/common/css/layout.css`, + `${appPath}/common/css/email.css`]) + .mergeStyles(); diff --git a/print/templates/email/supplier-campaign-metrics/attachments.json b/print/templates/email/supplier-campaign-metrics/attachments.json new file mode 100644 index 000000000..4eacb54db --- /dev/null +++ b/print/templates/email/supplier-campaign-metrics/attachments.json @@ -0,0 +1,6 @@ +[ + { + "filename": "supplier-campaign-metrics.pdf", + "component": "supplier-campaign-metrics" + } +] \ No newline at end of file diff --git a/print/templates/email/supplier-campaign-metrics/locale/es.yml b/print/templates/email/supplier-campaign-metrics/locale/es.yml new file mode 100644 index 000000000..d1c1182a2 --- /dev/null +++ b/print/templates/email/supplier-campaign-metrics/locale/es.yml @@ -0,0 +1,8 @@ +subject: Informe de consumo +title: Informe de consumo +dear: Estimado cliente +description: Tal y como nos ha solicitado nos complace + relacionarle a continuación el consumo que nos consta en su cuenta para las + 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/email/supplier-campaign-metrics/supplier-campaign-metrics.html b/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.html new file mode 100644 index 000000000..9d7014f34 --- /dev/null +++ b/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.html @@ -0,0 +1,46 @@ + + + + + + {{ $t('subject') }} + + + + + + + + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+

{{ $t('title') }}

+

{{$t('dear')}},

+

+
+
+ +
+
+ +
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js b/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js new file mode 100755 index 000000000..20113d8ea --- /dev/null +++ b/print/templates/email/supplier-campaign-metrics/supplier-campaign-metrics.js @@ -0,0 +1,33 @@ +const Component = require(`${appPath}/core/component`); +const emailHeader = new Component('email-header'); +const emailFooter = new Component('email-footer'); + +module.exports = { + name: 'supplier-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() + }, + props: { + recipientId: { + required: true + }, + from: { + required: true + }, + to: { + required: true + } + } +}; diff --git a/print/templates/reports/supplier-campaign-metrics/assets/css/style.css b/print/templates/reports/supplier-campaign-metrics/assets/css/style.css index 6e730869e..32caeb43c 100644 --- a/print/templates/reports/supplier-campaign-metrics/assets/css/style.css +++ b/print/templates/reports/supplier-campaign-metrics/assets/css/style.css @@ -1,5 +1,5 @@ .column-oriented { - margin-top: 50px !important; + margin-top: 0px; } .bottom-line > tr { @@ -9,3 +9,12 @@ .bottom-line tr:nth-last-child() { border-bottom: none; } + +h2 { + font-weight: 100; + color: #555; +} + +.description strong { + text-transform: uppercase; +} \ No newline at end of file diff --git a/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html index f5286df1e..1303f2266 100644 --- a/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html +++ b/print/templates/reports/supplier-campaign-metrics/supplier-campaign-metrics.html @@ -50,61 +50,44 @@ -
+
+

+ {{$t('entry')}} {{entry.id}} + {{$t('dated')}} {{entry.shipped | date('%d-%m-%Y')}} + {{$t('reference')}} {{entry.ref}} +

- - - - - - + + + + + + + + + + + + + + +
{{$t('entry')}}{{entry.id}}{{$t('dated')}}{{entry.shipped | date('%d-%m-%Y')}}{{$t('reference')}}{{entry.ref}}{{$t('itemName')}}{{$t('Quantity')}}{{$t('price')}}{{$t('total')}}
{{buy.itemName}}{{buy.quantity}}{{buy.price | currency('EUR', $i18n.locale)}}{{buy.quantity * buy.price | currency('EUR', $i18n.locale)}}
+ + {{buy.tag5}} {{buy.value5}} + + + {{buy.tag6}} {{buy.value6}} + + + {{buy.tag7}} {{buy.value7}} + +
-
- - - - - - - - - - - - - - - - - - - - - - - - -
{{$t('itemName')}}{{$t('Quantity')}}{{$t('price')}}{{$t('total')}}
{{buy.itemName}}{{buy.quantity}}{{buy.price | currency('EUR', $i18n.locale)}}{{buy.quantity * buy.price | currency('EUR', $i18n.locale)}}
-
- {{buy.tag5}} - {{buy.value5}} -
-
-
- {{buy.tag6}} - {{buy.value6}} -
-
-
- {{buy.tag7}} - {{buy.value7}} -
-
-
+
From b9344832d6dd505e194eff563b03507a7c3c1087 Mon Sep 17 00:00:00 2001 From: bernat Date: Fri, 18 Dec 2020 10:08:51 +0100 Subject: [PATCH 3/4] cr changes --- .../back/methods/supplier/consumption.js | 19 +++--- modules/supplier/front/consumption/index.html | 12 ++-- modules/supplier/front/routes.json | 1 + .../supplier-campaign-metrics/sql/buys.sql | 64 +++++++++---------- .../supplier-campaign-metrics/sql/entries.sql | 16 ++--- 5 files changed, 59 insertions(+), 53 deletions(-) diff --git a/modules/supplier/back/methods/supplier/consumption.js b/modules/supplier/back/methods/supplier/consumption.js index 1d7cd0411..e8e22625f 100644 --- a/modules/supplier/back/methods/supplier/consumption.js +++ b/modules/supplier/back/methods/supplier/consumption.js @@ -71,9 +71,9 @@ module.exports = Self => { case 'buyerId': return {'it.workerFk': value}; case 'from': - return {'e.shipped': {gte: value}}; + return {'t.shipped': {gte: value}}; case 'to': - return {'e.shipped': {lte: value}}; + return {'t.shipped': {lte: value}}; } }); @@ -93,9 +93,13 @@ module.exports = Self => { e.supplierFk, t.shipped FROM vn.entry e - JOIN vn.travel t ON t.id = e.travelFk`); - + JOIN vn.travel t ON t.id = e.travelFk + JOIN buy b ON b.id = b.entryFk + JOIN item i ON i.id = b.itemFk + JOIN itemType it ON it.id = i.typeFk`); + stmt.merge(conn.makeWhere(filter.where)); stmt.merge(conn.makeGroupBy('e.id')); + stmt.merge(conn.makeLimit(filter)); stmts.push(stmt); const entriesIndex = stmts.push('SELECT * FROM tmp.entry') - 1; @@ -129,19 +133,18 @@ module.exports = Self => { it.workerFk, it.categoryFk, it.code AS itemTypeCode - FROM buy b + FROM buy b JOIN tmp.entry e ON e.id = b.entryFk JOIN item i ON i.id = b.itemFk JOIN itemType it ON it.id = i.typeFk` ); - stmt.merge(conn.makeWhere(filter.where)); + stmt.merge('WHERE b.quantity > 0'); stmt.merge(conn.makeGroupBy('b.id')); + stmt.merge(conn.makeOrderBy(filter.order)); const buysIndex = stmts.push(stmt) - 1; stmts.push(`DROP TEMPORARY TABLE tmp.entry`); const sql = ParameterizedSQL.join(stmts, ';'); - stmt.merge(conn.makePagination(filter)); - const result = await conn.executeStmt(sql); const entries = result[entriesIndex]; diff --git a/modules/supplier/front/consumption/index.html b/modules/supplier/front/consumption/index.html index 4587f60f2..a0df60ea9 100644 --- a/modules/supplier/front/consumption/index.html +++ b/modules/supplier/front/consumption/index.html @@ -36,12 +36,12 @@ ng-if="entry.buys"> - Entry - {{::entry.id}} + Entry + {{::entry.id}} Date {{::entry.shipped | date: 'dd/MM/yyyy'}} Reference - {{::entry.ref}} + {{::entry.ref}} @@ -62,6 +62,8 @@ {{::buy.total | dashIfEmpty}} + + - - + + diff --git a/modules/supplier/front/routes.json b/modules/supplier/front/routes.json index 7501d329c..fc03ffe70 100644 --- a/modules/supplier/front/routes.json +++ b/modules/supplier/front/routes.json @@ -82,6 +82,7 @@ "state": "supplier.card.consumption", "component": "vn-supplier-consumption", "description": "Consumption", + "acl": ["administrative"], "params": { "supplier": "$ctrl.supplier" } diff --git a/print/templates/reports/supplier-campaign-metrics/sql/buys.sql b/print/templates/reports/supplier-campaign-metrics/sql/buys.sql index 898568c0a..a094ac205 100644 --- a/print/templates/reports/supplier-campaign-metrics/sql/buys.sql +++ b/print/templates/reports/supplier-campaign-metrics/sql/buys.sql @@ -1,33 +1,33 @@ SELECT - b.id AS buyId, - b.itemFk, - b.entryFk, - CAST(b.buyingValue AS DECIMAL(10,2)) AS price, - b.quantity, - i.id, - i.description, - i.name AS itemName, - i.subName, - i.size AS itemSize, - i.typeFk AS itemTypeFk, - i.tag5, - i.value5, - i.tag6, - i.value6, - i.tag7, - i.value7, - i.tag8, - i.value8, - i.tag9, - i.value9, - i.tag10, - i.value10, - it.id, - it.workerFk, - it.categoryFk, - it.code AS itemTypeCode - FROM buy b - JOIN item i ON i.id = b.itemFk - JOIN itemType it ON it.id = i.typeFk - WHERE b.entryFk IN(:entriesId) AND b.quantity > 0 - ORDER BY i.typeFk , i.name \ No newline at end of file + b.id AS buyId, + b.itemFk, + b.entryFk, + CAST(b.buyingValue AS DECIMAL(10,2)) AS price, + b.quantity, + i.id, + i.description, + i.name AS itemName, + i.subName, + i.size AS itemSize, + i.typeFk AS itemTypeFk, + i.tag5, + i.value5, + i.tag6, + i.value6, + i.tag7, + i.value7, + i.tag8, + i.value8, + i.tag9, + i.value9, + i.tag10, + i.value10, + it.id, + it.workerFk, + it.categoryFk, + it.code AS itemTypeCode + FROM buy b + JOIN item i ON i.id = b.itemFk + JOIN itemType it ON it.id = i.typeFk + WHERE b.entryFk IN(:entriesId) AND b.quantity > 0 + ORDER BY i.typeFk , i.name \ No newline at end of file diff --git a/print/templates/reports/supplier-campaign-metrics/sql/entries.sql b/print/templates/reports/supplier-campaign-metrics/sql/entries.sql index 946c4ab65..aa458dda0 100644 --- a/print/templates/reports/supplier-campaign-metrics/sql/entries.sql +++ b/print/templates/reports/supplier-campaign-metrics/sql/entries.sql @@ -1,8 +1,8 @@ - SELECT - e.id, - e.ref, - e.supplierFk, - t.shipped - FROM vn.entry e - JOIN vn.travel t ON t.id = e.travelFk - WHERE e.supplierFk = ? AND DATE(t.shipped) BETWEEN ? AND ? +SELECT + e.id, + e.ref, + e.supplierFk, + t.shipped + FROM vn.entry e + JOIN vn.travel t ON t.id = e.travelFk + WHERE e.supplierFk = ? AND DATE(t.shipped) BETWEEN ? AND ? From 1a641d1d4b35a513d660705967534001715f48c5 Mon Sep 17 00:00:00 2001 From: bernat Date: Fri, 18 Dec 2020 22:31:46 +0100 Subject: [PATCH 4/4] cr changes --- .../back/methods/supplier/specs/consumption.spec.js | 2 +- .../supplier/front/consumption-search-panel/index.js | 10 +--------- modules/supplier/front/routes.json | 1 - 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/modules/supplier/back/methods/supplier/specs/consumption.spec.js b/modules/supplier/back/methods/supplier/specs/consumption.spec.js index 2436f485e..0e8f3afcc 100644 --- a/modules/supplier/back/methods/supplier/specs/consumption.spec.js +++ b/modules/supplier/back/methods/supplier/specs/consumption.spec.js @@ -11,7 +11,7 @@ describe('supplier consumption() filter', () => { }; const result = await app.models.Supplier.consumption(ctx, filter); - expect(result.length).toEqual(8); + expect(result.length).toEqual(6); }); it('should return a list of entries from the item id 1 and supplier 1', async() => { diff --git a/modules/supplier/front/consumption-search-panel/index.js b/modules/supplier/front/consumption-search-panel/index.js index 8e636661a..f6c63c55c 100644 --- a/modules/supplier/front/consumption-search-panel/index.js +++ b/modules/supplier/front/consumption-search-panel/index.js @@ -1,15 +1,7 @@ import ngModule from '../module'; import SearchPanel from 'core/components/searchbar/search-panel'; -class Controller extends SearchPanel { - constructor($, $element) { - super($, $element); - - this.filter = this.$.filter; - } -} - ngModule.vnComponent('vnSupplierConsumptionSearchPanel', { template: require('./index.html'), - controller: Controller + controller: SearchPanel }); diff --git a/modules/supplier/front/routes.json b/modules/supplier/front/routes.json index 684895c66..54d203c8c 100644 --- a/modules/supplier/front/routes.json +++ b/modules/supplier/front/routes.json @@ -83,7 +83,6 @@ "state": "supplier.card.consumption", "component": "vn-supplier-consumption", "description": "Consumption", - "acl": ["administrative"], "params": { "supplier": "$ctrl.supplier" }