From c77999871ec457c01421dc5b0d0db964a6df4059 Mon Sep 17 00:00:00 2001 From: vicent Date: Mon, 24 Oct 2022 14:20:09 +0200 Subject: [PATCH 1/3] refs #3951 --- db/changes/10491-august/00-zipConfig.sql | 9 ++++ .../back/methods/invoiceOut/downloadZip.js | 54 +++++++++++++++++++ .../invoiceOut/specs/downloadZip.spec.js | 31 +++++++++++ modules/invoiceOut/back/model-config.json | 3 ++ modules/invoiceOut/back/models/invoice-out.js | 1 + .../invoiceOut/back/models/zip-config.json | 25 +++++++++ modules/invoiceOut/front/index/index.html | 29 ++++++---- modules/invoiceOut/front/index/index.js | 35 ++++++++++-- modules/invoiceOut/front/index/locale/es.yml | 1 + 9 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 db/changes/10491-august/00-zipConfig.sql create mode 100644 modules/invoiceOut/back/methods/invoiceOut/downloadZip.js create mode 100644 modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js create mode 100644 modules/invoiceOut/back/models/zip-config.json diff --git a/db/changes/10491-august/00-zipConfig.sql b/db/changes/10491-august/00-zipConfig.sql new file mode 100644 index 000000000..134ce7770 --- /dev/null +++ b/db/changes/10491-august/00-zipConfig.sql @@ -0,0 +1,9 @@ +CREATE TABLE `vn`.`zipConfig` ( + `id` double(10,2) NOT NULL, + `maxSize` int(11) DEFAULT NULL COMMENT 'in MegaBytes', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('ZipConfig', '*', '*', 'ALLOW', 'ROLE', 'employee'); \ No newline at end of file diff --git a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js new file mode 100644 index 000000000..65f1f8a95 --- /dev/null +++ b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js @@ -0,0 +1,54 @@ +const JSZip = require('jszip'); +const fs = require('fs-extra'); + +module.exports = Self => { + Self.remoteMethodCtx('downloadZip', { + description: 'Download a zip file with multiple invoices pdfs', + accessType: 'READ', + accepts: [ + { + arg: 'ids', + type: ['number'], + description: 'The invoice ids' + } + ], + returns: { + arg: 'base64', + type: 'string', + root: true + }, + http: { + path: '/downloadZip', + verb: 'POST' + } + }); + + Self.downloadZip = async function(ctx, ids, options) { + const models = Self.app.models; + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const zip = new JSZip(); + let totalSize = 0; + const zipConfig = await models.ZipConfig.findOne(); + for (let id of ids) { + if (zipConfig && totalSize > zipConfig.maxSize) return false; + const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions); + const fileName = extractFileName(invoiceOutPdf[2]); + const body = invoiceOutPdf[0]; + const sizeInBytes = (await fs.promises.stat(body.path)).size; + const sizeInMegabytes = sizeInBytes / (1024 * 1024); + totalSize += sizeInMegabytes; + zip.file(fileName, body); + } + const base64 = await zip.generateAsync({type: 'base64'}); + return base64; + }; + + function extractFileName(str) { + const matches = str.match(/"(.*?)"/); + return matches ? matches[1] : str; + } +}; diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js new file mode 100644 index 000000000..c39b41567 --- /dev/null +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js @@ -0,0 +1,31 @@ +const models = require('vn-loopback/server/server').models; + +describe('InvoiceOut downloadZip()', () => { + const userId = 9; + const invoiceIds = [1, 2]; + const ctx = { + req: { + + accessToken: {userId: userId}, + headers: {origin: 'http://localhost:5000'}, + } + }; + + it('should return part of link to dowloand the zip', async() => { + const result = await models.InvoiceOut.downloadZip(ctx, invoiceIds); + + expect(result).toBeDefined(); + }); + + it('should return an error if the size of the files is too large', async() => { + const zipConfig = { + maxSize: 0 + }; + + await models.ZipConfig.create(zipConfig); + + const result = await models.InvoiceOut.downloadZip(ctx, invoiceIds); + + expect(result).toBe(false); + }); +}); diff --git a/modules/invoiceOut/back/model-config.json b/modules/invoiceOut/back/model-config.json index d52f79477..04933c4f0 100644 --- a/modules/invoiceOut/back/model-config.json +++ b/modules/invoiceOut/back/model-config.json @@ -22,5 +22,8 @@ }, "TaxType": { "dataSource": "vn" + }, + "ZipConfig": { + "dataSource": "vn" } } diff --git a/modules/invoiceOut/back/models/invoice-out.js b/modules/invoiceOut/back/models/invoice-out.js index 5af64de2b..9533ad484 100644 --- a/modules/invoiceOut/back/models/invoice-out.js +++ b/modules/invoiceOut/back/models/invoice-out.js @@ -3,6 +3,7 @@ module.exports = Self => { require('../methods/invoiceOut/summary')(Self); require('../methods/invoiceOut/getTickets')(Self); require('../methods/invoiceOut/download')(Self); + require('../methods/invoiceOut/downloadZip')(Self); require('../methods/invoiceOut/delete')(Self); require('../methods/invoiceOut/book')(Self); require('../methods/invoiceOut/createPdf')(Self); diff --git a/modules/invoiceOut/back/models/zip-config.json b/modules/invoiceOut/back/models/zip-config.json new file mode 100644 index 000000000..17fe8a1fa --- /dev/null +++ b/modules/invoiceOut/back/models/zip-config.json @@ -0,0 +1,25 @@ +{ + "name": "ZipConfig", + "base": "VnModel", + "options": { + "mysql": { + "table": "zipConfig" + } + }, + "properties": { + "id": { + "type": "number", + "id": true, + "description": "Identifier" + }, + "maxSize": { + "type": "number" + } + }, + "acls": [{ + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + }] +} \ No newline at end of file diff --git a/modules/invoiceOut/front/index/index.html b/modules/invoiceOut/front/index/index.html index d970bd15a..632dbf90b 100644 --- a/modules/invoiceOut/front/index/index.html +++ b/modules/invoiceOut/front/index/index.html @@ -4,10 +4,24 @@ + + + + + + + + Reference Issued Amount @@ -23,6 +37,12 @@ + + + + {{::invoiceOut.ref | dashIfEmpty}} {{::invoiceOut.issued | date:'dd/MM/yyyy' | dashIfEmpty}} {{::invoiceOut.amount | currency: 'EUR': 2 | dashIfEmpty}} @@ -36,15 +56,6 @@ {{::invoiceOut.created | date:'dd/MM/yyyy' | dashIfEmpty}} {{::invoiceOut.companyCode | dashIfEmpty}} {{::invoiceOut.dued | date:'dd/MM/yyyy' | dashIfEmpty}} - - - - { + if (res.data == false) throw new UserError('Files are too large'); + location.href = 'data:application/zip;base64,' + res.data; + }); + } } } diff --git a/modules/invoiceOut/front/index/locale/es.yml b/modules/invoiceOut/front/index/locale/es.yml index 1460b90d8..74572da49 100644 --- a/modules/invoiceOut/front/index/locale/es.yml +++ b/modules/invoiceOut/front/index/locale/es.yml @@ -6,3 +6,4 @@ Minimum: Minimo Maximum: Máximo Global invoicing: Facturación global Manual invoicing: Facturación manual +Files are too large: Los archivos son demasiado grandes \ No newline at end of file From ec81b1223924d8842c83d6ff86df1b87de577643 Mon Sep 17 00:00:00 2001 From: vicent Date: Wed, 26 Oct 2022 09:33:03 +0200 Subject: [PATCH 2/3] refactor: el UserError se lanza en back --- modules/invoiceOut/back/methods/invoiceOut/downloadZip.js | 3 ++- modules/invoiceOut/front/index/index.js | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js index 65f1f8a95..834d3c031 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js +++ b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js @@ -1,5 +1,6 @@ const JSZip = require('jszip'); const fs = require('fs-extra'); +const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethodCtx('downloadZip', { @@ -34,7 +35,7 @@ module.exports = Self => { let totalSize = 0; const zipConfig = await models.ZipConfig.findOne(); for (let id of ids) { - if (zipConfig && totalSize > zipConfig.maxSize) return false; + if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large'); const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions); const fileName = extractFileName(invoiceOutPdf[2]); const body = invoiceOutPdf[0]; diff --git a/modules/invoiceOut/front/index/index.js b/modules/invoiceOut/front/index/index.js index f1d849710..a46060073 100644 --- a/modules/invoiceOut/front/index/index.js +++ b/modules/invoiceOut/front/index/index.js @@ -1,6 +1,5 @@ import ngModule from '../module'; import Section from 'salix/components/section'; -const UserError = require('vn-loopback/util/user-error'); export default class Controller extends Section { get checked() { @@ -35,7 +34,6 @@ export default class Controller extends Section { }; this.$http.post(`InvoiceOuts/downloadZip`, params) .then(res => { - if (res.data == false) throw new UserError('Files are too large'); location.href = 'data:application/zip;base64,' + res.data; }); } From e0e015c268e206ec3613c08ecb81329888024391 Mon Sep 17 00:00:00 2001 From: vicent Date: Wed, 26 Oct 2022 09:49:58 +0200 Subject: [PATCH 3/3] fix: backTest --- .../back/methods/invoiceOut/downloadZip.js | 2 +- .../invoiceOut/specs/downloadZip.spec.js | 38 +++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js index 834d3c031..72a00b764 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js +++ b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js @@ -33,7 +33,7 @@ module.exports = Self => { const zip = new JSZip(); let totalSize = 0; - const zipConfig = await models.ZipConfig.findOne(); + const zipConfig = await models.ZipConfig.findOne(null, myOptions); for (let id of ids) { if (zipConfig && totalSize > zipConfig.maxSize) throw new UserError('Files are too large'); const invoiceOutPdf = await models.InvoiceOut.download(ctx, id, myOptions); diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js index c39b41567..7a9e184ea 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js @@ -1,4 +1,5 @@ const models = require('vn-loopback/server/server').models; +const UserError = require('vn-loopback/util/user-error'); describe('InvoiceOut downloadZip()', () => { const userId = 9; @@ -12,20 +13,41 @@ describe('InvoiceOut downloadZip()', () => { }; it('should return part of link to dowloand the zip', async() => { - const result = await models.InvoiceOut.downloadZip(ctx, invoiceIds); + const tx = await models.Order.beginTransaction({}); - expect(result).toBeDefined(); + try { + const options = {transaction: tx}; + + const result = await models.InvoiceOut.downloadZip(ctx, invoiceIds, options); + + expect(result).toBeDefined(); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } }); it('should return an error if the size of the files is too large', async() => { - const zipConfig = { - maxSize: 0 - }; + const tx = await models.Order.beginTransaction({}); - await models.ZipConfig.create(zipConfig); + let error; + try { + const options = {transaction: tx}; + const zipConfig = { + maxSize: 0 + }; + await models.ZipConfig.create(zipConfig, options); - const result = await models.InvoiceOut.downloadZip(ctx, invoiceIds); + await models.InvoiceOut.downloadZip(ctx, invoiceIds, options); - expect(result).toBe(false); + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error).toEqual(new UserError(`Files are too large`)); }); });