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..72a00b764 --- /dev/null +++ b/modules/invoiceOut/back/methods/invoiceOut/downloadZip.js @@ -0,0 +1,55 @@ +const JSZip = require('jszip'); +const fs = require('fs-extra'); +const UserError = require('vn-loopback/util/user-error'); + +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(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); + 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..7a9e184ea --- /dev/null +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/downloadZip.spec.js @@ -0,0 +1,53 @@ +const models = require('vn-loopback/server/server').models; +const UserError = require('vn-loopback/util/user-error'); + +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 tx = await models.Order.beginTransaction({}); + + 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 tx = await models.Order.beginTransaction({}); + + let error; + try { + const options = {transaction: tx}; + const zipConfig = { + maxSize: 0 + }; + await models.ZipConfig.create(zipConfig, options); + + await models.InvoiceOut.downloadZip(ctx, invoiceIds, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error).toEqual(new UserError(`Files are too large`)); + }); +}); 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}} - - - - { + 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