From 8c71d76988d717ca1685d42bf1a0a94fc4ae19ef Mon Sep 17 00:00:00 2001 From: joan Date: Tue, 26 Jan 2021 07:21:05 +0100 Subject: [PATCH 01/14] 2752 - Import json file --- front/core/components/field/index.js | 10 +- front/core/components/input-file/index.js | 5 +- modules/entry/back/methods/entry/import.js | 48 ++++++ modules/entry/back/models/entry.js | 1 + modules/entry/front/buy/import/index.html | 162 ++++++++++++++++++ modules/entry/front/buy/import/index.js | 76 ++++++++ modules/entry/front/buy/import/style.scss | 7 + modules/entry/front/buy/index.html | 1 - modules/entry/front/buy/index/index.html | 11 ++ modules/entry/front/buy/{ => index}/index.js | 4 +- .../entry/front/buy/{ => index}/locale/es.yml | 0 modules/entry/front/index.js | 4 +- modules/entry/front/routes.json | 24 ++- 13 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 modules/entry/back/methods/entry/import.js create mode 100644 modules/entry/front/buy/import/index.html create mode 100644 modules/entry/front/buy/import/index.js create mode 100644 modules/entry/front/buy/import/style.scss delete mode 100644 modules/entry/front/buy/index.html create mode 100644 modules/entry/front/buy/index/index.html rename modules/entry/front/buy/{ => index}/index.js (66%) rename modules/entry/front/buy/{ => index}/locale/es.yml (100%) diff --git a/front/core/components/field/index.js b/front/core/components/field/index.js index d18973bbe..40ea01e47 100644 --- a/front/core/components/field/index.js +++ b/front/core/components/field/index.js @@ -20,7 +20,8 @@ export default class Field extends FormInput { super.$onInit(); if (this.info) this.classList.add('has-icons'); - this.input.addEventListener('change', () => this.onChange()); + this.input.addEventListener('change', event => + this.onChange(event)); } set field(value) { @@ -186,10 +187,13 @@ export default class Field extends FormInput { this.refreshHint(); } - onChange() { + onChange($event) { // Changes doesn't reflect until appling async this.$.$applyAsync(() => { - this.emit('change', {value: this.field}); + this.emit('change', { + value: this.field, + $event: $event + }); }); } } diff --git a/front/core/components/input-file/index.js b/front/core/components/input-file/index.js index 8bdb1a4fe..4d057f3cc 100644 --- a/front/core/components/input-file/index.js +++ b/front/core/components/input-file/index.js @@ -71,10 +71,11 @@ export default class InputFile extends Field { this.input.click(); } - onChange() { + onChange($event) { this.emit('change', { value: this.field, - $files: this.files + $files: this.files, + $event: $event }); } } diff --git a/modules/entry/back/methods/entry/import.js b/modules/entry/back/methods/entry/import.js new file mode 100644 index 000000000..efcd1001d --- /dev/null +++ b/modules/entry/back/methods/entry/import.js @@ -0,0 +1,48 @@ +module.exports = Self => { + Self.remoteMethodCtx('import', { + description: 'Imports the buys from a JSON file', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'The entry id', + http: {source: 'path'} + }, + { + arg: 'buys', + type: ['Object'], + description: 'The buys', + }], + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/:id/import`, + verb: 'POST' + } + }); + + Self.import = async(ctx, id) => { + const args = ctx.args; + const models = Self.app.models; + + const entry = await models.Entry.findById(id); + console.log(entry); + + const buys = []; + for (let buy of args.buys) { + buys.push({ + entryFk: entry.id, + itemFk: buy.itemFk, + stickers: 1, + packing: buy.packing, + grouping: buy.grouping, + packageFk: 1 + }); + } + + await models.Buy.create(buys); + }; +}; diff --git a/modules/entry/back/models/entry.js b/modules/entry/back/models/entry.js index 94dbe787d..b5fafaaea 100644 --- a/modules/entry/back/models/entry.js +++ b/modules/entry/back/models/entry.js @@ -2,4 +2,5 @@ module.exports = Self => { require('../methods/entry/filter')(Self); require('../methods/entry/getEntry')(Self); require('../methods/entry/getBuys')(Self); + require('../methods/entry/import')(Self); }; diff --git a/modules/entry/front/buy/import/index.html b/modules/entry/front/buy/import/index.html new file mode 100644 index 000000000..f3fbdc50c --- /dev/null +++ b/modules/entry/front/buy/import/index.html @@ -0,0 +1,162 @@ + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Item IDDescriptionSizeQuantityStickersPackingGroupingBuying value
+ + + {{::id}} - {{::name}} + + + {{::buy.description | dashIfEmpty}}{{::buy.size | dashIfEmpty}}{{::line.quantity}}{{::line.stickers | dashIfEmpty}} + + {{::buy.packing | dashIfEmpty}} + + + + {{::buy.grouping | dashIfEmpty}} + + + {{::buy.buyingValue | currency: 'EUR':2}}
+
+
+ + + + + + +
+
diff --git a/modules/entry/front/buy/import/index.js b/modules/entry/front/buy/import/index.js new file mode 100644 index 000000000..685af5ba8 --- /dev/null +++ b/modules/entry/front/buy/import/index.js @@ -0,0 +1,76 @@ +import ngModule from '../../module'; +import Section from 'salix/components/section'; +import './style.scss'; + +class Controller extends Section { + constructor($element, $) { + super($element, $); + this.import = { + file: '', + invoice: null, + buys: [] + }; + } + + onFileChange($event) { + const input = $event.target; + const file = input.files[0]; + + const reader = new FileReader(); + reader.onload = event => + this.fillData(event.target.result); + reader.readAsText(file, 'UTF-8'); + } + + fillData(raw) { + this.import.file = raw; + + const data = JSON.parse(raw); + const [invoice] = data.invoices; + + this.$.$applyAsync(() => { + this.import.invoice = invoice.id_invoice; + this.import.description = invoice.tx_awb; + + const boxes = invoice.boxes; + const buys = []; + for (let box of boxes) { + for (let product of box.products) { + const packing = product.nu_stems_bunch * product.nu_bunches; + buys.push({ + description: product.nm_product, + size: product.nu_length, + packing: packing, + grouping: product.nu_stems_bunch, + buyingValue: parseFloat(product.mny_rate_stem) + }); + } + } + this.import.buys = buys; + }); + } + + onSubmit() { + const params = {buys: this.import.buys}; + const query = `Entries/${this.entry.id}/import`; + return this.$http.post(query, params).then(() => { + this.vnApp.showSuccess(this.$t('Data saved!')); + }); + } + + itemSearchFunc($search) { + return /^\d+$/.test($search) + ? {id: $search} + : {name: {like: '%' + $search + '%'}}; + } +} + +Controller.$inject = ['$element', '$scope']; + +ngModule.vnComponent('vnEntryBuyImport', { + template: require('./index.html'), + controller: Controller, + bindings: { + worker: '<' + } +}); diff --git a/modules/entry/front/buy/import/style.scss b/modules/entry/front/buy/import/style.scss new file mode 100644 index 000000000..73f136fc1 --- /dev/null +++ b/modules/entry/front/buy/import/style.scss @@ -0,0 +1,7 @@ +vn-ticket-request { + .vn-textfield { + margin: 0!important; + max-width: 100px; + } +} + diff --git a/modules/entry/front/buy/index.html b/modules/entry/front/buy/index.html deleted file mode 100644 index 8b1378917..000000000 --- a/modules/entry/front/buy/index.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/modules/entry/front/buy/index/index.html b/modules/entry/front/buy/index/index.html new file mode 100644 index 000000000..54b9786d8 --- /dev/null +++ b/modules/entry/front/buy/index/index.html @@ -0,0 +1,11 @@ +
+ + + + + + +
\ No newline at end of file diff --git a/modules/entry/front/buy/index.js b/modules/entry/front/buy/index/index.js similarity index 66% rename from modules/entry/front/buy/index.js rename to modules/entry/front/buy/index/index.js index 00a8421fb..6cb27e022 100644 --- a/modules/entry/front/buy/index.js +++ b/modules/entry/front/buy/index/index.js @@ -1,7 +1,7 @@ -import ngModule from '../module'; +import ngModule from '../../module'; import Section from 'salix/components/section'; -ngModule.vnComponent('vnEntryBuy', { +ngModule.vnComponent('vnEntryBuyIndex', { template: require('./index.html'), controller: Section, bindings: { diff --git a/modules/entry/front/buy/locale/es.yml b/modules/entry/front/buy/index/locale/es.yml similarity index 100% rename from modules/entry/front/buy/locale/es.yml rename to modules/entry/front/buy/index/locale/es.yml diff --git a/modules/entry/front/index.js b/modules/entry/front/index.js index 14aecc8db..88700b166 100644 --- a/modules/entry/front/index.js +++ b/modules/entry/front/index.js @@ -13,4 +13,6 @@ import './card'; import './note'; import './summary'; import './log'; -import './buy'; +import './buy/index'; +import './buy/import'; + diff --git a/modules/entry/front/routes.json b/modules/entry/front/routes.json index 010e738b3..a598558cc 100644 --- a/modules/entry/front/routes.json +++ b/modules/entry/front/routes.json @@ -11,7 +11,7 @@ ], "card": [ {"state": "entry.card.basicData", "icon": "settings"}, - {"state": "entry.card.buy", "icon": "icon-lines"}, + {"state": "entry.card.buy.index", "icon": "icon-lines"}, {"state": "entry.card.observation", "icon": "insert_drive_file"}, {"state": "entry.card.log", "icon": "history"} ] @@ -75,14 +75,30 @@ "state": "entry.card.log", "component": "vn-entry-log", "description": "Log" - }, { - "url" : "/buy", + }, + { + "url": "/buy", "state": "entry.card.buy", - "component": "vn-entry-buy", + "abstract": true, + "component": "ui-view" + }, + { + "url" : "/index", + "state": "entry.card.buy.index", + "component": "vn-entry-buy-index", "description": "Buy", "params": { "entry": "$ctrl.entry" } + }, + { + "url" : "/import", + "state": "entry.card.buy.import", + "component": "vn-entry-buy-import", + "description": "Import buys", + "params": { + "entry": "$ctrl.entry" + } } ] } \ No newline at end of file From 38311f526d528ec0858ce0d7cf4728391573dd36 Mon Sep 17 00:00:00 2001 From: joan Date: Wed, 3 Feb 2021 09:03:03 +0100 Subject: [PATCH 02/14] Calculate volume --- modules/entry/back/methods/entry/import.js | 3 ++- .../entry/back/methods/entry/importPreview.js | 25 +++++++++++++++++++ modules/entry/front/buy/import/index.html | 24 +++--------------- modules/entry/front/buy/import/index.js | 4 ++- 4 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 modules/entry/back/methods/entry/importPreview.js diff --git a/modules/entry/back/methods/entry/import.js b/modules/entry/back/methods/entry/import.js index efcd1001d..4b33fe964 100644 --- a/modules/entry/back/methods/entry/import.js +++ b/modules/entry/back/methods/entry/import.js @@ -29,7 +29,6 @@ module.exports = Self => { const models = Self.app.models; const entry = await models.Entry.findById(id); - console.log(entry); const buys = []; for (let buy of args.buys) { @@ -37,8 +36,10 @@ module.exports = Self => { entryFk: entry.id, itemFk: buy.itemFk, stickers: 1, + quantity: 1, packing: buy.packing, grouping: buy.grouping, + buyingValue: buy.buyingValue, packageFk: 1 }); } diff --git a/modules/entry/back/methods/entry/importPreview.js b/modules/entry/back/methods/entry/importPreview.js new file mode 100644 index 000000000..074f27ca7 --- /dev/null +++ b/modules/entry/back/methods/entry/importPreview.js @@ -0,0 +1,25 @@ +module.exports = Self => { + Self.remoteMethod('importPreview', { + description: '', + accessType: 'READ', + accepts: { + arg: 'id', + type: 'number', + required: true, + description: 'The entry id', + http: {source: 'path'} + }, + returns: { + type: ['Object'], + root: true + }, + http: { + path: `/:id/importPreview`, + verb: 'GET' + } + }); + + Self.importPreview = async id => { + return buys; + }; +}; diff --git a/modules/entry/front/buy/import/index.html b/modules/entry/front/buy/import/index.html index f3fbdc50c..231b086fd 100644 --- a/modules/entry/front/buy/import/index.html +++ b/modules/entry/front/buy/import/index.html @@ -87,15 +87,11 @@ Item ID Description Size - Quantity - Stickers Packing Grouping Buying value - - + Package + Volume @@ -118,8 +114,6 @@ {{::buy.description | dashIfEmpty}} {{::buy.size | dashIfEmpty}} - {{::line.quantity}} - {{::line.stickers | dashIfEmpty}} {{::buy.packing | dashIfEmpty}} @@ -131,18 +125,8 @@ {{::buy.buyingValue | currency: 'EUR':2}} - - + {{::buy.packageFk | dashIfEmpty}} + {{::buy.volume | number}} diff --git a/modules/entry/front/buy/import/index.js b/modules/entry/front/buy/import/index.js index 685af5ba8..08e515380 100644 --- a/modules/entry/front/buy/import/index.js +++ b/modules/entry/front/buy/import/index.js @@ -35,6 +35,7 @@ class Controller extends Section { const boxes = invoice.boxes; const buys = []; for (let box of boxes) { + const boxVolume = box.nu_length * box.nu_width * box.nu_height; for (let product of box.products) { const packing = product.nu_stems_bunch * product.nu_bunches; buys.push({ @@ -42,7 +43,8 @@ class Controller extends Section { size: product.nu_length, packing: packing, grouping: product.nu_stems_bunch, - buyingValue: parseFloat(product.mny_rate_stem) + buyingValue: parseFloat(product.mny_rate_stem), + volume: boxVolume }); } } From c092bf58b497821f75129e680ededaaeac1dbff5 Mon Sep 17 00:00:00 2001 From: joan Date: Tue, 9 Feb 2021 07:59:55 +0100 Subject: [PATCH 03/14] Import buys --- db/dump/fixtures.sql | 11 +++++- modules/entry/back/methods/entry/import.js | 35 ++++++++++++++++++- .../entry/back/methods/entry/importPreview.js | 19 ++++++++-- modules/entry/back/models/entry.js | 1 + modules/entry/back/models/entry.json | 2 +- modules/entry/front/buy/import/index.html | 17 ++++++--- modules/entry/front/buy/import/index.js | 10 ++++-- 7 files changed, 84 insertions(+), 11 deletions(-) diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 8742c161f..1a35cafa3 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2174,4 +2174,13 @@ INSERT INTO `hedera`.`image`(`collectionFk`, `name`) INSERT INTO `hedera`.`imageCollectionSize`(`id`, `collectionFk`,`width`, `height`) VALUES - (1, 4, 160, 160); \ No newline at end of file + (1, 4, 160, 160); + +INSERT INTO `vn`.`rateConfig`(`rate0`, `rate1`, `rate2`, `rate3`) + VALUES + (36, 31, 25, 21); + +INSERT INTO `vn`.`rate`(`dated`, `warehouseFk`, `rate0`, `rate1`, `rate2`, `rate3`) + VALUES + (DATE_ADD(CURDATE(), INTERVAL -1 YEAR), 1, 10, 15, 20, 25), + (CURDATE(), 1, 12, 17, 22, 27); diff --git a/modules/entry/back/methods/entry/import.js b/modules/entry/back/methods/entry/import.js index 4b33fe964..137d67441 100644 --- a/modules/entry/back/methods/entry/import.js +++ b/modules/entry/back/methods/entry/import.js @@ -1,3 +1,5 @@ + +const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; module.exports = Self => { Self.remoteMethodCtx('import', { description: 'Imports the buys from a JSON file', @@ -9,6 +11,16 @@ module.exports = Self => { description: 'The entry id', http: {source: 'path'} }, + { + arg: 'ref', + type: 'string', + description: 'The buyed boxes ids', + }, + { + arg: 'observation', + type: 'string', + description: 'The observation', + }, { arg: 'buys', type: ['Object'], @@ -25,10 +37,14 @@ module.exports = Self => { }); Self.import = async(ctx, id) => { + const conn = Self.dataSource.connector; const args = ctx.args; const models = Self.app.models; const entry = await models.Entry.findById(id); + await entry.updateAttributes({ + observation: args.notes + }); const buys = []; for (let buy of args.buys) { @@ -44,6 +60,23 @@ module.exports = Self => { }); } - await models.Buy.create(buys); + const createdBuys = await models.Buy.create(buys); + const buyIds = createdBuys.map(buy => buy.id); + + /* let stmts = []; + let stmt; + + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.buyRecalc'); + stmt = new ParameterizedSQL( + `CREATE TEMPORARY TABLE tmp.buyRecalc + (INDEX (id)) + ENGINE = MEMORY + SELECT ? AS id`, [buyIds]); + + stmts.push(stmt); + stmts.push('CALL buy_recalcPrices()'); + + const sql = ParameterizedSQL.join(stmts, ';'); + await conn.executeStmt(sql); */ }; }; diff --git a/modules/entry/back/methods/entry/importPreview.js b/modules/entry/back/methods/entry/importPreview.js index 074f27ca7..38fbcd3c7 100644 --- a/modules/entry/back/methods/entry/importPreview.js +++ b/modules/entry/back/methods/entry/importPreview.js @@ -2,13 +2,18 @@ module.exports = Self => { Self.remoteMethod('importPreview', { description: '', accessType: 'READ', - accepts: { + accepts: [{ arg: 'id', type: 'number', required: true, description: 'The entry id', http: {source: 'path'} }, + { + arg: 'buys', + type: ['Object'], + description: 'The buys', + }], returns: { type: ['Object'], root: true @@ -19,7 +24,17 @@ module.exports = Self => { } }); - Self.importPreview = async id => { + Self.importPreview = async(id, buys) => { + const models = Self.app.models; + for (let buy of buys) { + const packaging = await models.Packaging.findOne({ + fields: ['id'], + where: {volume: {gte: buy.volume}}, + order: 'volume ASC' + }); + buy.packageFk = packaging.id; + } + return buys; }; }; diff --git a/modules/entry/back/models/entry.js b/modules/entry/back/models/entry.js index b5fafaaea..55e226a4f 100644 --- a/modules/entry/back/models/entry.js +++ b/modules/entry/back/models/entry.js @@ -3,4 +3,5 @@ module.exports = Self => { require('../methods/entry/getEntry')(Self); require('../methods/entry/getBuys')(Self); require('../methods/entry/import')(Self); + require('../methods/entry/importPreview')(Self); }; diff --git a/modules/entry/back/models/entry.json b/modules/entry/back/models/entry.json index 40d6d29dd..78d3c5e4f 100644 --- a/modules/entry/back/models/entry.json +++ b/modules/entry/back/models/entry.json @@ -28,7 +28,7 @@ "type": "boolean" }, "notes": { - "type": "String" + "type": "string" }, "isConfirmed": { "type": "boolean" diff --git a/modules/entry/front/buy/import/index.html b/modules/entry/front/buy/import/index.html index 231b086fd..903c61edc 100644 --- a/modules/entry/front/buy/import/index.html +++ b/modules/entry/front/buy/import/index.html @@ -59,8 +59,8 @@ + label="Observation" + ng-model="$ctrl.import.observation"> @@ -90,7 +90,7 @@ Packing Grouping Buying value - Package + Box Volume @@ -125,7 +125,16 @@ {{::buy.buyingValue | currency: 'EUR':2}} - {{::buy.packageFk | dashIfEmpty}} + + + + {{::buy.volume | number}} diff --git a/modules/entry/front/buy/import/index.js b/modules/entry/front/buy/import/index.js index 08e515380..15cdba704 100644 --- a/modules/entry/front/buy/import/index.js +++ b/modules/entry/front/buy/import/index.js @@ -30,7 +30,7 @@ class Controller extends Section { this.$.$applyAsync(() => { this.import.invoice = invoice.id_invoice; - this.import.description = invoice.tx_awb; + this.import.observation = invoice.tx_awb; const boxes = invoice.boxes; const buys = []; @@ -48,7 +48,13 @@ class Controller extends Section { }); } } - this.import.buys = buys; + + const params = {buys}; + const query = `Entries/${this.entry.id}/importPreview`; + this.$http.get(query, {params}).then(res => { + this.import.buys = res.data; + }); + // this.import.buys = buys; }); } From 5cbd8d632d115037130b22df304a3ec54d071096 Mon Sep 17 00:00:00 2001 From: joan Date: Tue, 9 Mar 2021 10:01:40 +0100 Subject: [PATCH 04/14] Changes --- db/dump/fixtures.sql | 1 + modules/entry/back/methods/entry/import.js | 6 +- modules/entry/front/buy/import/index.html | 52 +------- modules/entry/front/buy/import/index.js | 29 +++-- modules/entry/front/buy/import/index.spec.js | 128 +++++++++++++++++++ modules/entry/front/buy/import/locale/es.yml | 2 + 6 files changed, 156 insertions(+), 62 deletions(-) create mode 100644 modules/entry/front/buy/import/index.spec.js create mode 100644 modules/entry/front/buy/import/locale/es.yml diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index c5486459c..0f25a94f3 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2179,6 +2179,7 @@ INSERT INTO `vn`.`rate`(`dated`, `warehouseFk`, `rate0`, `rate1`, `rate2`, `rate VALUES (DATE_ADD(CURDATE(), INTERVAL -1 YEAR), 1, 10, 15, 20, 25), (CURDATE(), 1, 12, 17, 22, 27); + INSERT INTO `vn`.`awb` (id, code, package, weight, created, amount, transitoryFk, taxFk) VALUES (1, '07546501420', 67, 671, CURDATE(), 1761, 1, 1), diff --git a/modules/entry/back/methods/entry/import.js b/modules/entry/back/methods/entry/import.js index 137d67441..cbb2e9ace 100644 --- a/modules/entry/back/methods/entry/import.js +++ b/modules/entry/back/methods/entry/import.js @@ -43,7 +43,7 @@ module.exports = Self => { const entry = await models.Entry.findById(id); await entry.updateAttributes({ - observation: args.notes + observation: args.observation }); const buys = []; @@ -63,7 +63,7 @@ module.exports = Self => { const createdBuys = await models.Buy.create(buys); const buyIds = createdBuys.map(buy => buy.id); - /* let stmts = []; + let stmts = []; let stmt; stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.buyRecalc'); @@ -77,6 +77,6 @@ module.exports = Self => { stmts.push('CALL buy_recalcPrices()'); const sql = ParameterizedSQL.join(stmts, ';'); - await conn.executeStmt(sql); */ + await conn.executeStmt(sql); }; }; diff --git a/modules/entry/front/buy/import/index.html b/modules/entry/front/buy/import/index.html index 903c61edc..ad812244a 100644 --- a/modules/entry/front/buy/import/index.html +++ b/modules/entry/front/buy/import/index.html @@ -7,53 +7,13 @@ name="form" ng-submit="$ctrl.onSubmit()" class="vn-ma-md"> -
+
- - - + ng-model="$ctrl.import.ref"> @@ -84,7 +44,7 @@ - + @@ -96,7 +56,7 @@ -
Item IDItem Description Size Packing
+ + label="Import"> { - this.import.invoice = invoice.id_invoice; this.import.observation = invoice.tx_awb; const boxes = invoice.boxes; @@ -49,21 +46,27 @@ class Controller extends Section { } } - const params = {buys}; - const query = `Entries/${this.entry.id}/importPreview`; - this.$http.get(query, {params}).then(res => { - this.import.buys = res.data; - }); - // this.import.buys = buys; + const boxesId = boxes.map(box => box.id_box); + this.import.ref = boxesId.join(', '); + + this.fetchBuys(buys); + }); + } + + fetchBuys(buys) { + const params = {buys}; + const query = `Entries/${this.entry.id}/importPreview`; + this.$http.get(query, {params}).then(res => { + this.import.buys = res.data; }); } onSubmit() { - const params = {buys: this.import.buys}; + const params = this.import; const query = `Entries/${this.entry.id}/import`; - return this.$http.post(query, params).then(() => { - this.vnApp.showSuccess(this.$t('Data saved!')); - }); + return this.$http.post(query, params) + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) + .then(() => this.$state.go('entry.card.buy.index')); } itemSearchFunc($search) { diff --git a/modules/entry/front/buy/import/index.spec.js b/modules/entry/front/buy/import/index.spec.js new file mode 100644 index 000000000..408fdab83 --- /dev/null +++ b/modules/entry/front/buy/import/index.spec.js @@ -0,0 +1,128 @@ +import './index.js'; + +describe('Entry', () => { + describe('Component vnEntryBuyImport', () => { + let controller; + let $httpParamSerializer; + let $httpBackend; + + beforeEach(ngModule('entry')); + + beforeEach(angular.mock.inject(($componentController, $compile, $rootScope, _$httpParamSerializer_, _$httpBackend_) => { + $httpBackend = _$httpBackend_; + $httpParamSerializer = _$httpParamSerializer_; + let $element = $compile(' { + it(`should call to the fillData() method`, () => { + controller.fetchBuys = jest.fn(); + + const rawData = `{ + "invoices": [ + { + "tx_awb": "123456", + "boxes": [ + { + "id_box": 1, + "nu_length": 1, + "nu_width": 15, + "nu_height": 80, + "products": [ + { + "nm_product": "Bow", + "nu_length": 1, + "nu_stems_bunch": 1, + "nu_bunches": 1, + "mny_rate_stem": 5.77 + } + + ] + }, + { + "id_box": 2, + "nu_length": 25, + "nu_width": 1, + "nu_height": 45, + "products": [ + { + "nm_product": "Arrow", + "nu_length": 25, + "nu_stems_bunch": 1, + "nu_bunches": 1, + "mny_rate_stem": 2.16 + } + ] + } + ] + } + ]}`; + const expectedBuys = [ + {'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200}, + {'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125} + ]; + controller.fillData(rawData); + controller.$.$apply(); + + const importData = controller.import; + + expect(importData.observation).toEqual('123456'); + expect(importData.ref).toEqual('1, 2'); + + expect(controller.fetchBuys).toHaveBeenCalledWith(expectedBuys); + }); + }); + + describe('fetchBuys()', () => { + it(`should perform a query to fetch the buys data`, () => { + const buys = [ + {'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200}, + {'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125} + ]; + + const serializedParams = $httpParamSerializer({buys}); + const query = `Entries/1/importPreview?${serializedParams}`; + $httpBackend.expectGET(query).respond(200, buys); + controller.fetchBuys(buys); + $httpBackend.flush(); + + const importData = controller.import; + + expect(importData.buys.length).toEqual(2); + }); + }); + + describe('onSubmit()', () => { + it(`should perform a query to update columns`, () => { + jest.spyOn(controller.vnApp, 'showSuccess'); + jest.spyOn(controller.$state, 'go'); + + controller.import = { + observation: '123456', + ref: '1, 2', + buys: [ + {'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200}, + {'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125} + ] + }; + const params = controller.import; + + const query = `Entries/1/import`; + $httpBackend.expectPOST(query, params).respond(200, params.buys); + controller.onSubmit(); + $httpBackend.flush(); + + const importData = controller.import; + + expect(importData.buys.length).toEqual(2); + + expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); + expect(controller.$state.go).toHaveBeenCalledWith('foo.card.summary', {id: 1}, undefined); + }); + }); + }); +}); diff --git a/modules/entry/front/buy/import/locale/es.yml b/modules/entry/front/buy/import/locale/es.yml new file mode 100644 index 000000000..7962a2870 --- /dev/null +++ b/modules/entry/front/buy/import/locale/es.yml @@ -0,0 +1,2 @@ +#Ordenar alfabeticamente +reference: Referencia \ No newline at end of file From 31205f455697379f02e3909fa3480efc82615dae Mon Sep 17 00:00:00 2001 From: joan Date: Tue, 9 Mar 2021 13:39:22 +0100 Subject: [PATCH 05/14] Added front unit test --- modules/entry/front/buy/import/index.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/entry/front/buy/import/index.spec.js b/modules/entry/front/buy/import/index.spec.js index 408fdab83..7a7cca12b 100644 --- a/modules/entry/front/buy/import/index.spec.js +++ b/modules/entry/front/buy/import/index.spec.js @@ -99,7 +99,7 @@ describe('Entry', () => { describe('onSubmit()', () => { it(`should perform a query to update columns`, () => { jest.spyOn(controller.vnApp, 'showSuccess'); - jest.spyOn(controller.$state, 'go'); + controller.$state.go = jest.fn(); controller.import = { observation: '123456', @@ -121,7 +121,7 @@ describe('Entry', () => { expect(importData.buys.length).toEqual(2); expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); - expect(controller.$state.go).toHaveBeenCalledWith('foo.card.summary', {id: 1}, undefined); + expect(controller.$state.go).toHaveBeenCalledWith('entry.card.buy.index'); }); }); }); From 2a2e276c6bbeb5883bd0692d11ca11ef27d4dacd Mon Sep 17 00:00:00 2001 From: joan Date: Wed, 10 Mar 2021 09:01:32 +0100 Subject: [PATCH 06/14] Added back unit test plus fixes --- front/core/components/field/index.js | 3 + front/core/components/input-file/index.html | 2 +- front/core/components/input-file/index.js | 10 +++ modules/entry/back/methods/entry/import.js | 77 ++++++++++-------- .../back/methods/entry/specs/import.spec.js | 78 +++++++++++++++++++ modules/entry/front/buy/import/index.html | 16 ++-- modules/entry/front/buy/import/locale/es.yml | 2 - modules/entry/front/buy/import/style.scss | 10 +-- modules/entry/front/buy/locale/es.yml | 4 + 9 files changed, 153 insertions(+), 49 deletions(-) create mode 100644 modules/entry/back/methods/entry/specs/import.spec.js delete mode 100644 modules/entry/front/buy/import/locale/es.yml create mode 100644 modules/entry/front/buy/locale/es.yml diff --git a/front/core/components/field/index.js b/front/core/components/field/index.js index 40ea01e47..e10ef6383 100644 --- a/front/core/components/field/index.js +++ b/front/core/components/field/index.js @@ -83,6 +83,9 @@ export default class Field extends FormInput { this._required = value; let required = this.element.querySelector('.required'); display(required, this._required); + + this.$.$applyAsync(() => + this.input.setAttribute('required', value)); } get required() { diff --git a/front/core/components/input-file/index.html b/front/core/components/input-file/index.html index 5ec7e1da4..bdfdcdcb4 100644 --- a/front/core/components/input-file/index.html +++ b/front/core/components/input-file/index.html @@ -13,7 +13,7 @@ diff --git a/front/core/components/input-file/index.js b/front/core/components/input-file/index.js index 4d057f3cc..962f38b73 100644 --- a/front/core/components/input-file/index.js +++ b/front/core/components/input-file/index.js @@ -78,6 +78,16 @@ export default class InputFile extends Field { $event: $event }); } + + get accept() { + return this._accept; + } + + set accept(value) { + this._accept = value; + this.$.$applyAsync(() => + this.input.setAttribute('accept', value)); + } } ngModule.vnComponent('vnInputFile', { diff --git a/modules/entry/back/methods/entry/import.js b/modules/entry/back/methods/entry/import.js index cbb2e9ace..21d4ef0e1 100644 --- a/modules/entry/back/methods/entry/import.js +++ b/modules/entry/back/methods/entry/import.js @@ -36,47 +36,60 @@ module.exports = Self => { } }); - Self.import = async(ctx, id) => { + Self.import = async(ctx, id, options) => { const conn = Self.dataSource.connector; const args = ctx.args; const models = Self.app.models; - const entry = await models.Entry.findById(id); - await entry.updateAttributes({ - observation: args.observation - }); - - const buys = []; - for (let buy of args.buys) { - buys.push({ - entryFk: entry.id, - itemFk: buy.itemFk, - stickers: 1, - quantity: 1, - packing: buy.packing, - grouping: buy.grouping, - buyingValue: buy.buyingValue, - packageFk: 1 - }); + let tx; + if (!options.transaction) { + tx = await Self.beginTransaction({}); + options.transaction = tx; } - const createdBuys = await models.Buy.create(buys); - const buyIds = createdBuys.map(buy => buy.id); + try { + const entry = await models.Entry.findById(id, null, options); + await entry.updateAttributes({ + observation: args.observation, + ref: args.ref + }, options); - let stmts = []; - let stmt; + const buys = []; + for (let buy of args.buys) { + buys.push({ + entryFk: entry.id, + itemFk: buy.itemFk, + stickers: 1, + quantity: 1, + packing: buy.packing, + grouping: buy.grouping, + buyingValue: buy.buyingValue, + packageFk: 1 + }); + } - stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.buyRecalc'); - stmt = new ParameterizedSQL( - `CREATE TEMPORARY TABLE tmp.buyRecalc - (INDEX (id)) - ENGINE = MEMORY - SELECT ? AS id`, [buyIds]); + const createdBuys = await models.Buy.create(buys, options); + const buyIds = createdBuys.map(buy => buy.id); - stmts.push(stmt); - stmts.push('CALL buy_recalcPrices()'); + let stmts = []; + let stmt; - const sql = ParameterizedSQL.join(stmts, ';'); - await conn.executeStmt(sql); + stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.buyRecalc'); + stmt = new ParameterizedSQL( + `CREATE TEMPORARY TABLE tmp.buyRecalc + (INDEX (id)) + ENGINE = MEMORY + SELECT ? AS id`, [buyIds]); + + stmts.push(stmt); + stmts.push('CALL buy_recalcPrices()'); + + const sql = ParameterizedSQL.join(stmts, ';'); + await conn.executeStmt(sql, options); + if (tx) await tx.commit(); + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } }; }; diff --git a/modules/entry/back/methods/entry/specs/import.spec.js b/modules/entry/back/methods/entry/specs/import.spec.js new file mode 100644 index 000000000..882795065 --- /dev/null +++ b/modules/entry/back/methods/entry/specs/import.spec.js @@ -0,0 +1,78 @@ +const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); + +describe('entry import()', () => { + let newEntry; + const buyerId = 35; + const companyId = 442; + const travelId = 1; + const supplierId = 1; + const activeCtx = { + accessToken: {userId: buyerId}, + }; + + beforeAll(async done => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + + done(); + }); + + it('should import the buy rows', async() => { + const ctx = { + req: activeCtx, + args: { + observation: '123456', + ref: '1, 2', + buys: [ + { + itemFk: 1, + buyingValue: 5.77, + description: 'Bow', + grouping: 1, + packing: 1, + size: 1, + volume: 1200 + }, + { + itemFk: 4, + buyingValue: 2.16, + description: 'Arrow', + grouping: 1, + packing: 1, + size: 25, + volume: 1125 + } + ] + } + }; + const tx = await app.models.Entry.beginTransaction({}); + const options = {transaction: tx}; + + newEntry = await app.models.Entry.create({ + dated: new Date(), + supplierFk: supplierId, + travelFk: travelId, + companyFk: companyId, + observation: 'The entry', + ref: 'Entry ref' + }, options); + + await app.models.Entry.import(ctx, newEntry.id, options); + + const updatedEntry = await app.models.Entry.findById(newEntry.id, null, options); + const entryBuys = await app.models.Buy.find({ + where: {entryFk: newEntry.id} + }, options); + + expect(updatedEntry.observation).toEqual('123456'); + expect(updatedEntry.ref).toEqual('1, 2'); + expect(entryBuys.length).toEqual(2); + + // Restores + await newEntry.destroy(options); + + await tx.rollback(); + }); +}); diff --git a/modules/entry/front/buy/import/index.html b/modules/entry/front/buy/import/index.html index ad812244a..00fa709b2 100644 --- a/modules/entry/front/buy/import/index.html +++ b/modules/entry/front/buy/import/index.html @@ -10,7 +10,7 @@
- @@ -29,22 +29,22 @@ label="File" ng-model="$ctrl.import.file" on-change="$ctrl.onFileChange($event)" - required="true" - multiple="true"> + accept="application/json" + required="true"> - + - + @@ -56,7 +56,7 @@ - @@ -108,7 +108,8 @@ + ui-sref="entry.card.buy.index"> + diff --git a/modules/entry/front/buy/import/index.js b/modules/entry/front/buy/import/index.js index fbf3343f8..9dc3418f0 100644 --- a/modules/entry/front/buy/import/index.js +++ b/modules/entry/front/buy/import/index.js @@ -62,11 +62,23 @@ class Controller extends Section { } onSubmit() { - const params = this.import; - const query = `Entries/${this.entry.id}/importBuys`; - return this.$http.post(query, params) - .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) - .then(() => this.$state.go('entry.card.buy.index')); + try { + const params = this.import; + const hasAnyEmptyRow = params.buys.some(buy => { + return buy.itemFk == null; + }); + + if (hasAnyEmptyRow) + throw new Error(`Some of the imported buys doesn't have an item`); + + const query = `Entries/${this.entry.id}/importBuys`; + return this.$http.post(query, params) + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) + .then(() => this.$state.go('entry.card.buy.index')); + } catch (e) { + this.vnApp.showError(this.$t(e.message)); + return false; + } } itemSearchFunc($search) { diff --git a/modules/entry/front/buy/locale/es.yml b/modules/entry/front/buy/locale/es.yml index 8d035e9a7..b5a82948b 100644 --- a/modules/entry/front/buy/locale/es.yml +++ b/modules/entry/front/buy/locale/es.yml @@ -1,4 +1,5 @@ reference: Referencia Observation: Observación -Box: Embalage -Import buys: Importar compras \ No newline at end of file +Box: Embalaje +Import buys: Importar compras +Some of the imported buys doesn't have an item: Algunas de las compras importadas no tienen un artículo \ No newline at end of file From 4c7d95f001b86425722a7898fbeffbfb8d9148a5 Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 12 Mar 2021 08:45:41 +0100 Subject: [PATCH 13/14] Requested changes + e2e updated --- e2e/paths/12-entry/03_latestBuys.spec.js | 2 +- .../methods/entry/specs/importBuys.spec.js | 16 ++++++++------ .../entry/specs/importBuysPreview.spec.js | 3 ++- modules/entry/front/buy/import/index.spec.js | 21 +++++++++++++++++-- modules/entry/front/latest-buys/index.html | 2 +- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/e2e/paths/12-entry/03_latestBuys.spec.js b/e2e/paths/12-entry/03_latestBuys.spec.js index 8e9de8158..f2d64e3b4 100644 --- a/e2e/paths/12-entry/03_latestBuys.spec.js +++ b/e2e/paths/12-entry/03_latestBuys.spec.js @@ -41,6 +41,6 @@ describe('Entry lastest buys path', () => { it('should navigate to the entry.buy section by clicking one of the buys', async() => { await page.waitToClick(selectors.entryLatestBuys.firstBuy); - await page.waitForState('entry.card.buy'); + await page.waitForState('entry.card.buy.index'); }); }); diff --git a/modules/entry/back/methods/entry/specs/importBuys.spec.js b/modules/entry/back/methods/entry/specs/importBuys.spec.js index 06fb46d3a..d0793a2f6 100644 --- a/modules/entry/back/methods/entry/specs/importBuys.spec.js +++ b/modules/entry/back/methods/entry/specs/importBuys.spec.js @@ -20,11 +20,13 @@ describe('entry import()', () => { }); it('should import the buy rows', async() => { + const expectedRef = '1, 2'; + const expectedObservation = '123456'; const ctx = { req: activeCtx, args: { - observation: '123456', - ref: '1, 2', + observation: expectedObservation, + ref: expectedRef, buys: [ { itemFk: 1, @@ -33,7 +35,8 @@ describe('entry import()', () => { grouping: 1, packing: 1, size: 1, - volume: 1200 + volume: 1200, + packageFk: '94' }, { itemFk: 4, @@ -42,7 +45,8 @@ describe('entry import()', () => { grouping: 1, packing: 1, size: 25, - volume: 1125 + volume: 1125, + packageFk: '94' } ] } @@ -66,8 +70,8 @@ describe('entry import()', () => { where: {entryFk: newEntry.id} }, options); - expect(updatedEntry.observation).toEqual('123456'); - expect(updatedEntry.ref).toEqual('1, 2'); + expect(updatedEntry.observation).toEqual(expectedObservation); + expect(updatedEntry.ref).toEqual(expectedRef); expect(entryBuys.length).toEqual(2); // Restores diff --git a/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js index db7776781..d286993ad 100644 --- a/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js +++ b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js @@ -12,6 +12,7 @@ describe('entry importBuysPreview()', () => { }); it('should return the buys with the calculated packageFk', async() => { + const expectedPackageFk = '3'; const buys = [ { itemFk: 1, @@ -35,6 +36,6 @@ describe('entry importBuysPreview()', () => { const randomIndex = Math.floor(Math.random() * result.length); const buy = result[randomIndex]; - expect(buy.packageFk).toEqual('3'); + expect(buy.packageFk).toEqual(expectedPackageFk); }); }); diff --git a/modules/entry/front/buy/import/index.spec.js b/modules/entry/front/buy/import/index.spec.js index e7428e8be..126c7375f 100644 --- a/modules/entry/front/buy/import/index.spec.js +++ b/modules/entry/front/buy/import/index.spec.js @@ -97,6 +97,23 @@ describe('Entry', () => { }); describe('onSubmit()', () => { + it(`should throw an error when some of the rows doesn't have an item`, () => { + jest.spyOn(controller.vnApp, 'showError'); + + controller.import = { + observation: '123456', + ref: '1, 2', + buys: [ + {'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200}, + {'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125} + ] + }; + + controller.onSubmit(); + + expect(controller.vnApp.showError).toHaveBeenCalledWith(`Some of the imported buys doesn't have an item`); + }); + it(`should perform a query to update columns`, () => { jest.spyOn(controller.vnApp, 'showSuccess'); controller.$state.go = jest.fn(); @@ -105,8 +122,8 @@ describe('Entry', () => { observation: '123456', ref: '1, 2', buys: [ - {'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200}, - {'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125} + {'itemFk': 10, 'buyingValue': 5.77, 'description': 'Bow', 'grouping': 1, 'packing': 1, 'size': 1, 'volume': 1200}, + {'itemFk': 11, 'buyingValue': 2.16, 'description': 'Arrow', 'grouping': 1, 'packing': 1, 'size': 25, 'volume': 1125} ] }; const params = controller.import; diff --git a/modules/entry/front/latest-buys/index.html b/modules/entry/front/latest-buys/index.html index 34f6464fc..3b6143e76 100644 --- a/modules/entry/front/latest-buys/index.html +++ b/modules/entry/front/latest-buys/index.html @@ -61,7 +61,7 @@ + ui-sref="entry.card.buy.index({id: {{::buy.entryFk}}})"> Date: Fri, 12 Mar 2021 11:56:02 +0100 Subject: [PATCH 14/14] e2e path for entry.buy.card.index import --- e2e/assets/07_import_buys.json | 127 ++++++++++++++++++ .../ecc/3.jpeg => e2e/assets/thermograph.jpeg | Bin e2e/helpers/selectors.js | 11 ++ e2e/paths/10-travel/05_thermograph.spec.js | 2 +- e2e/paths/12-entry/07_import_buys.spec.js | 62 +++++++++ 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 e2e/assets/07_import_buys.json rename storage/dms/ecc/3.jpeg => e2e/assets/thermograph.jpeg (100%) create mode 100644 e2e/paths/12-entry/07_import_buys.spec.js diff --git a/e2e/assets/07_import_buys.json b/e2e/assets/07_import_buys.json new file mode 100644 index 000000000..5f0f74342 --- /dev/null +++ b/e2e/assets/07_import_buys.json @@ -0,0 +1,127 @@ +{ + "invoices": [ + { + "tx_company": "TESSAROSES S.A.", + "id_invoice": "20062926", + "id_purchaseorder": "20106319", + "tx_customer_ref": "", + "id_customer": "56116", + "id_customer_floricode": "", + "nm_bill": "VERDNATURA LEVANTE SL", + "nm_ship": "VERDNATURA LEVANTE SL", + "nm_cargo": "OYAMBARILLO", + "dt_purchaseorder": "06/19/2020", + "dt_fly": "06/20/2020", + "dt_invoice": "06/19/2020", + "nm_incoterm": "FOB UIO", + "tx_awb": "729-6340 2846", + "tx_hawb": "LA0061832844", + "tx_oe": "05520204000335992", + "nu_totalstemsPO": "850", + "mny_flower": "272.5000", + "mny_freight": "0.0000", + "mny_total": "272.5000", + "nu_boxes": "4", + "nu_fulls": "1.75", + "dt_posted": "2020-06-19T13:31:41", + "boxes": [ + { + "id_box": "200573095", + "nm_box": "HB", + "tp_box": "HB", + "tx_label": "", + "nu_length": "96", + "nu_width": "32", + "nu_height": "30.5", + "products": [ + { + "id_floricode": "27887", + "id_migros_variety": "", + "nm_product": "FREEDOM 60CM 25ST", + "nm_species": "ROSES", + "nm_variety": "FREEDOM", + "nu_length": "60", + "nu_stems_bunch": "25", + "nu_bunches": "10", + "mny_rate_stem": "0.3500", + "mny_freight_unit": "0.0000", + "barcodes": "202727621,202725344,202725345,202725571,202725730,202725731,202725732,202725925,202726131,202726685" + } + ] + }, + { + "id_box": "200573106", + "nm_box": "HB", + "tp_box": "HB", + "tx_label": "", + "nu_length": "96", + "nu_width": "32", + "nu_height": "30.5", + "products": [ + { + "id_floricode": "27887", + "id_migros_variety": "", + "nm_product": "FREEDOM 70CM 25ST", + "nm_species": "ROSES", + "nm_variety": "FREEDOM", + "nu_length": "70", + "nu_stems_bunch": "25", + "nu_bunches": "8", + "mny_rate_stem": "0.4000", + "mny_freight_unit": "0.0000", + "barcodes": "202727077,202727078,202727079,202727080,202727650,202727654,202727656,202727657" + } + ] + }, + { + "id_box": "200573117", + "nm_box": "HB", + "tp_box": "HB", + "tx_label": "", + "nu_length": "96", + "nu_width": "32", + "nu_height": "30.5", + "products": [ + { + "id_floricode": "28409", + "id_migros_variety": "", + "nm_product": "TIBET 40CM 25ST", + "nm_species": "ROSES", + "nm_variety": "TIBET", + "nu_length": "40", + "nu_stems_bunch": "25", + "nu_bunches": "12", + "mny_rate_stem": "0.2500", + "mny_freight_unit": "0.0000", + "barcodes": "202723350,202723351,202723352,202723353,202723354,202723355,202723356,202723357,202726690,202726745,202726813,202726814" + } + ] + }, + { + "id_box": "200573506", + "nm_box": "QB 2", + "tp_box": "QB", + "tx_label": "", + "nu_length": "80", + "nu_width": "30", + "nu_height": "17.5", + "products": [ + { + "id_floricode": "27887", + "id_migros_variety": "", + "nm_product": "FREEDOM 50CM 25ST", + "nm_species": "ROSES", + "nm_variety": "FREEDOM", + "nu_length": "50", + "nu_stems_bunch": "25", + "nu_bunches": "4", + "mny_rate_stem": "0.3000", + "mny_freight_unit": "0.0000", + "barcodes": "202727837,202727839,202727842,202726682" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/storage/dms/ecc/3.jpeg b/e2e/assets/thermograph.jpeg similarity index 100% rename from storage/dms/ecc/3.jpeg rename to e2e/assets/thermograph.jpeg diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 75ae95293..d06e9c75d 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -1015,6 +1015,17 @@ export default { travelsQuicklink: 'vn-entry-descriptor vn-quick-link[icon="local_airport"] > a', entriesQuicklink: 'vn-entry-descriptor vn-quick-link[icon="icon-entry"] > a' }, + entryBuys: { + importButton: 'vn-entry-buy-index vn-icon[icon="publish"]', + ref: 'vn-entry-buy-import vn-textfield[ng-model="$ctrl.import.ref"]', + observation: 'vn-entry-buy-import vn-textarea[ng-model="$ctrl.import.observation"]', + file: 'vn-entry-buy-import vn-input-file[ng-model="$ctrl.import.file"]', + firstImportedItem: 'vn-entry-buy-import tbody:nth-child(2) vn-autocomplete[ng-model="buy.itemFk"]', + secondImportedItem: 'vn-entry-buy-import tbody:nth-child(3) vn-autocomplete[ng-model="buy.itemFk"]', + thirdImportedItem: 'vn-entry-buy-import tbody:nth-child(4) vn-autocomplete[ng-model="buy.itemFk"]', + fourthImportedItem: 'vn-entry-buy-import tbody:nth-child(5) vn-autocomplete[ng-model="buy.itemFk"]', + importBuysButton: 'vn-entry-buy-import button[type="submit"]' + }, entryLatestBuys: { firstBuy: 'vn-entry-latest-buys vn-tbody > a:nth-child(1)', allBuysCheckBox: 'vn-entry-latest-buys vn-thead vn-check', diff --git a/e2e/paths/10-travel/05_thermograph.spec.js b/e2e/paths/10-travel/05_thermograph.spec.js index 44fc783f0..97077554f 100644 --- a/e2e/paths/10-travel/05_thermograph.spec.js +++ b/e2e/paths/10-travel/05_thermograph.spec.js @@ -38,7 +38,7 @@ describe('Travel thermograph path', () => { it('should select the file to upload', async() => { let currentDir = process.cwd(); - let filePath = `${currentDir}/storage/dms/ecc/3.jpeg`; + let filePath = `${currentDir}/e2e/assets/thermograph.jpeg`; const [fileChooser] = await Promise.all([ page.waitForFileChooser(), diff --git a/e2e/paths/12-entry/07_import_buys.spec.js b/e2e/paths/12-entry/07_import_buys.spec.js new file mode 100644 index 000000000..02db0ded5 --- /dev/null +++ b/e2e/paths/12-entry/07_import_buys.spec.js @@ -0,0 +1,62 @@ +import selectors from '../../helpers/selectors.js'; +import getBrowser from '../../helpers/puppeteer'; + +describe('Entry import buys path', () => { + let browser; + let page; + + beforeAll(async() => { + browser = await getBrowser(); + page = browser.page; + await page.loginAndModule('buyer', 'entry'); + await page.accessToSearchResult('1'); + }); + + afterAll(async() => { + await browser.close(); + }); + + it('should count the summary buys and find there only one at this point', async() => { + const buysCount = await page.countElement(selectors.entrySummary.anyBuyLine); + + expect(buysCount).toEqual(1); + }); + + it('should navigate to the buy section and then click the import button opening the import form', async() => { + await page.accessToSection('entry.card.buy.index'); + await page.waitToClick(selectors.entryBuys.importButton); + await page.waitForState('entry.card.buy.import'); + }); + + it('should fill the form, import the designated JSON file and select items for each import and confirm import', async() => { + await page.write(selectors.entryBuys.ref, 'a reference'); + await page.write(selectors.entryBuys.observation, 'an observation'); + + let currentDir = process.cwd(); + let filePath = `${currentDir}/e2e/assets/07_import_buys.json`; + + const [fileChooser] = await Promise.all([ + page.waitForFileChooser(), + page.waitToClick(selectors.entryBuys.file) + ]); + await fileChooser.accept([filePath]); + + await page.autocompleteSearch(selectors.entryBuys.firstImportedItem, 'Ranged Reinforced weapon pistol 9mm'); + await page.autocompleteSearch(selectors.entryBuys.secondImportedItem, 'Melee Reinforced weapon heavy shield 1x0.5m'); + await page.autocompleteSearch(selectors.entryBuys.thirdImportedItem, 'Container medical box 1m'); + await page.autocompleteSearch(selectors.entryBuys.fourthImportedItem, 'Container ammo box 1m'); + + await page.waitToClick(selectors.entryBuys.importBuysButton); + + const message = await page.waitForSnackbar(); + const state = await page.getState(); + + expect(message.text).toContain('Data saved!'); + expect(state).toBe('entry.card.buy.index'); + }); + + it('should navigate to the entry summary and count the buys to find 4 buys have been added', async() => { + await page.waitToClick('vn-icon[icon="preview"]'); + await page.waitForNumberOfElements(selectors.entrySummary.anyBuyLine, 5); + }); +});
ItemItem Description Size Packing
+ + label="Import buys"> tbody td:nth-child(1) { + width: 250px } -} - +} \ No newline at end of file diff --git a/modules/entry/front/buy/locale/es.yml b/modules/entry/front/buy/locale/es.yml new file mode 100644 index 000000000..8d035e9a7 --- /dev/null +++ b/modules/entry/front/buy/locale/es.yml @@ -0,0 +1,4 @@ +reference: Referencia +Observation: Observación +Box: Embalage +Import buys: Importar compras \ No newline at end of file From a4d2c531a341189916629ac4428aef662a3d7348 Mon Sep 17 00:00:00 2001 From: joan Date: Wed, 10 Mar 2021 10:29:58 +0100 Subject: [PATCH 07/14] Changes --- .../entry/{import.js => importBuys.js} | 13 +++++-- ...{importPreview.js => importBuysPreview.js} | 8 ++-- .../{import.spec.js => importBuys.spec.js} | 2 +- .../entry/specs/importBuysPreview.spec.js | 39 +++++++++++++++++++ modules/entry/back/models/entry.js | 4 +- modules/entry/front/buy/import/index.js | 4 +- 6 files changed, 57 insertions(+), 13 deletions(-) rename modules/entry/back/methods/entry/{import.js => importBuys.js} (89%) rename modules/entry/back/methods/entry/{importPreview.js => importBuysPreview.js} (80%) rename modules/entry/back/methods/entry/specs/{import.spec.js => importBuys.spec.js} (96%) create mode 100644 modules/entry/back/methods/entry/specs/importBuysPreview.spec.js diff --git a/modules/entry/back/methods/entry/import.js b/modules/entry/back/methods/entry/importBuys.js similarity index 89% rename from modules/entry/back/methods/entry/import.js rename to modules/entry/back/methods/entry/importBuys.js index 21d4ef0e1..57877f80c 100644 --- a/modules/entry/back/methods/entry/import.js +++ b/modules/entry/back/methods/entry/importBuys.js @@ -1,8 +1,8 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; module.exports = Self => { - Self.remoteMethodCtx('import', { - description: 'Imports the buys from a JSON file', + Self.remoteMethodCtx('importBuys', { + description: 'Imports the buys from a list', accessType: 'WRITE', accepts: [{ arg: 'id', @@ -11,6 +11,11 @@ module.exports = Self => { description: 'The entry id', http: {source: 'path'} }, + { + arg: 'options', + type: 'object', + description: 'Callback options', + }, { arg: 'ref', type: 'string', @@ -31,12 +36,12 @@ module.exports = Self => { root: true }, http: { - path: `/:id/import`, + path: `/:id/importBuys`, verb: 'POST' } }); - Self.import = async(ctx, id, options) => { + Self.importBuys = async(ctx, id, options = {}) => { const conn = Self.dataSource.connector; const args = ctx.args; const models = Self.app.models; diff --git a/modules/entry/back/methods/entry/importPreview.js b/modules/entry/back/methods/entry/importBuysPreview.js similarity index 80% rename from modules/entry/back/methods/entry/importPreview.js rename to modules/entry/back/methods/entry/importBuysPreview.js index 38fbcd3c7..9d6662327 100644 --- a/modules/entry/back/methods/entry/importPreview.js +++ b/modules/entry/back/methods/entry/importBuysPreview.js @@ -1,6 +1,6 @@ module.exports = Self => { - Self.remoteMethod('importPreview', { - description: '', + Self.remoteMethod('importBuysPreview', { + description: 'Calculates the preview buys for an entry import', accessType: 'READ', accepts: [{ arg: 'id', @@ -19,12 +19,12 @@ module.exports = Self => { root: true }, http: { - path: `/:id/importPreview`, + path: `/:id/importBuysPreview`, verb: 'GET' } }); - Self.importPreview = async(id, buys) => { + Self.importBuysPreview = async(id, buys) => { const models = Self.app.models; for (let buy of buys) { const packaging = await models.Packaging.findOne({ diff --git a/modules/entry/back/methods/entry/specs/import.spec.js b/modules/entry/back/methods/entry/specs/importBuys.spec.js similarity index 96% rename from modules/entry/back/methods/entry/specs/import.spec.js rename to modules/entry/back/methods/entry/specs/importBuys.spec.js index 882795065..89d2a3f35 100644 --- a/modules/entry/back/methods/entry/specs/import.spec.js +++ b/modules/entry/back/methods/entry/specs/importBuys.spec.js @@ -59,7 +59,7 @@ describe('entry import()', () => { ref: 'Entry ref' }, options); - await app.models.Entry.import(ctx, newEntry.id, options); + await app.models.Entry.importBuys(ctx, newEntry.id, options); const updatedEntry = await app.models.Entry.findById(newEntry.id, null, options); const entryBuys = await app.models.Buy.find({ diff --git a/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js new file mode 100644 index 000000000..481023fb1 --- /dev/null +++ b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js @@ -0,0 +1,39 @@ +const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); + +describe('entry importBuysPreview()', () => { + beforeAll(async done => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + + done(); + }); + + it('should import the buy rows', async() => { + const buys = [ + { + itemFk: 1, + buyingValue: 5.77, + description: 'Bow', + grouping: 1, + size: 1, + volume: 1200 + }, + { + itemFk: 4, + buyingValue: 2.16, + description: 'Arrow', + grouping: 1, + size: 25, + volume: 1125 + } + ]; + + const result = await app.models.Entry.importBuysPreview(1, buys); + const randomIndex = Math.floor(Math.random() * result.length); + const buy = result[randomIndex]; + + expect(buy.packageFk).toEqual('3'); + }); +}); diff --git a/modules/entry/back/models/entry.js b/modules/entry/back/models/entry.js index 55e226a4f..f1a22fddd 100644 --- a/modules/entry/back/models/entry.js +++ b/modules/entry/back/models/entry.js @@ -2,6 +2,6 @@ module.exports = Self => { require('../methods/entry/filter')(Self); require('../methods/entry/getEntry')(Self); require('../methods/entry/getBuys')(Self); - require('../methods/entry/import')(Self); - require('../methods/entry/importPreview')(Self); + require('../methods/entry/importBuys')(Self); + require('../methods/entry/importBuysPreview')(Self); }; diff --git a/modules/entry/front/buy/import/index.js b/modules/entry/front/buy/import/index.js index 2235162fe..fbf3343f8 100644 --- a/modules/entry/front/buy/import/index.js +++ b/modules/entry/front/buy/import/index.js @@ -55,7 +55,7 @@ class Controller extends Section { fetchBuys(buys) { const params = {buys}; - const query = `Entries/${this.entry.id}/importPreview`; + const query = `Entries/${this.entry.id}/importBuysPreview`; this.$http.get(query, {params}).then(res => { this.import.buys = res.data; }); @@ -63,7 +63,7 @@ class Controller extends Section { onSubmit() { const params = this.import; - const query = `Entries/${this.entry.id}/import`; + const query = `Entries/${this.entry.id}/importBuys`; return this.$http.post(query, params) .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))) .then(() => this.$state.go('entry.card.buy.index')); From c144d9ed4700657b766ba8477223ee5134363d12 Mon Sep 17 00:00:00 2001 From: joan Date: Wed, 10 Mar 2021 10:53:22 +0100 Subject: [PATCH 08/14] Unit test changed query path --- modules/entry/front/buy/import/index.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/entry/front/buy/import/index.spec.js b/modules/entry/front/buy/import/index.spec.js index 7a7cca12b..e7428e8be 100644 --- a/modules/entry/front/buy/import/index.spec.js +++ b/modules/entry/front/buy/import/index.spec.js @@ -85,7 +85,7 @@ describe('Entry', () => { ]; const serializedParams = $httpParamSerializer({buys}); - const query = `Entries/1/importPreview?${serializedParams}`; + const query = `Entries/1/importBuysPreview?${serializedParams}`; $httpBackend.expectGET(query).respond(200, buys); controller.fetchBuys(buys); $httpBackend.flush(); @@ -111,7 +111,7 @@ describe('Entry', () => { }; const params = controller.import; - const query = `Entries/1/import`; + const query = `Entries/1/importBuys`; $httpBackend.expectPOST(query, params).respond(200, params.buys); controller.onSubmit(); $httpBackend.flush(); From a7c2e08f8fbb61b4bae2f71e58f9732e011b6a56 Mon Sep 17 00:00:00 2001 From: joan Date: Wed, 10 Mar 2021 11:04:56 +0100 Subject: [PATCH 09/14] ACl fixes --- modules/entry/front/buy/index/index.html | 5 ++++- modules/entry/front/routes.json | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/modules/entry/front/buy/index/index.html b/modules/entry/front/buy/index/index.html index 54b9786d8..fc6438394 100644 --- a/modules/entry/front/buy/index/index.html +++ b/modules/entry/front/buy/index/index.html @@ -1,6 +1,9 @@
- + Date: Wed, 10 Mar 2021 11:11:04 +0100 Subject: [PATCH 10/14] Acl fix --- modules/entry/front/buy/index/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/entry/front/buy/index/index.html b/modules/entry/front/buy/index/index.html index fc6438394..0ff11c8a6 100644 --- a/modules/entry/front/buy/index/index.html +++ b/modules/entry/front/buy/index/index.html @@ -2,7 +2,7 @@ Date: Wed, 10 Mar 2021 11:14:50 +0100 Subject: [PATCH 11/14] Updated it description --- .../entry/back/methods/entry/specs/importBuysPreview.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js index 481023fb1..4eb4ec916 100644 --- a/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js +++ b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js @@ -10,7 +10,7 @@ describe('entry importBuysPreview()', () => { done(); }); - it('should import the buy rows', async() => { + it('should return the buys with the calculated packageFk', async() => { const buys = [ { itemFk: 1, From be37334f497d0b98cd5216046db8f884e946d410 Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 12 Mar 2021 08:17:56 +0100 Subject: [PATCH 12/14] Requested changes --- front/core/components/input-file/index.html | 2 +- .../entry/back/methods/entry/importBuys.js | 2 +- .../methods/entry/specs/importBuys.spec.js | 2 -- .../entry/specs/importBuysPreview.spec.js | 3 ++- modules/entry/front/buy/import/index.html | 11 +++++----- modules/entry/front/buy/import/index.js | 22 ++++++++++++++----- modules/entry/front/buy/locale/es.yml | 5 +++-- 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/front/core/components/input-file/index.html b/front/core/components/input-file/index.html index bdfdcdcb4..5ec7e1da4 100644 --- a/front/core/components/input-file/index.html +++ b/front/core/components/input-file/index.html @@ -13,7 +13,7 @@
diff --git a/modules/entry/back/methods/entry/importBuys.js b/modules/entry/back/methods/entry/importBuys.js index 57877f80c..10871f4ad 100644 --- a/modules/entry/back/methods/entry/importBuys.js +++ b/modules/entry/back/methods/entry/importBuys.js @@ -69,7 +69,7 @@ module.exports = Self => { packing: buy.packing, grouping: buy.grouping, buyingValue: buy.buyingValue, - packageFk: 1 + packageFk: buy.packageFk }); } diff --git a/modules/entry/back/methods/entry/specs/importBuys.spec.js b/modules/entry/back/methods/entry/specs/importBuys.spec.js index 89d2a3f35..06fb46d3a 100644 --- a/modules/entry/back/methods/entry/specs/importBuys.spec.js +++ b/modules/entry/back/methods/entry/specs/importBuys.spec.js @@ -71,8 +71,6 @@ describe('entry import()', () => { expect(entryBuys.length).toEqual(2); // Restores - await newEntry.destroy(options); - await tx.rollback(); }); }); diff --git a/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js index 4eb4ec916..db7776781 100644 --- a/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js +++ b/modules/entry/back/methods/entry/specs/importBuysPreview.spec.js @@ -2,6 +2,7 @@ const app = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); describe('entry importBuysPreview()', () => { + const entryId = 1; beforeAll(async done => { spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ active: activeCtx @@ -30,7 +31,7 @@ describe('entry importBuysPreview()', () => { } ]; - const result = await app.models.Entry.importBuysPreview(1, buys); + const result = await app.models.Entry.importBuysPreview(entryId, buys); const randomIndex = Math.floor(Math.random() * result.length); const buy = result[randomIndex]; diff --git a/modules/entry/front/buy/import/index.html b/modules/entry/front/buy/import/index.html index 00fa709b2..74b6c708a 100644 --- a/modules/entry/front/buy/import/index.html +++ b/modules/entry/front/buy/import/index.html @@ -75,13 +75,13 @@
{{::buy.description | dashIfEmpty}} {{::buy.size | dashIfEmpty}} - - {{::buy.packing | dashIfEmpty}} + + {{::buy.packing | dashIfEmpty}} - - {{::buy.grouping | dashIfEmpty}} + + {{::buy.grouping | dashIfEmpty}} {{::buy.buyingValue | currency: 'EUR':2}}