From 6b66fd6b3ca2ddc0b547860b16cdfc5dcf6fa5e0 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 20 May 2024 14:05:15 +0200 Subject: [PATCH 01/25] feat(salix): refs #5926 Decouple docuwareUpload for Tickets --- back/methods/docuware/core.js | 12 +- back/methods/docuware/upload.js | 130 +++--------------- back/models/docuware.json | 3 + db/dump/fixtures.before.sql | 23 +++- .../back/methods/ticket/docuwareUpload.js | 116 ++++++++++++++++ modules/ticket/back/models/ticket-methods.js | 1 + 6 files changed, 168 insertions(+), 117 deletions(-) create mode 100644 modules/ticket/back/methods/ticket/docuwareUpload.js diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index 74d922236..01e4eb4d1 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -23,6 +23,16 @@ module.exports = Self => { }; }; + /** + * Returns the docuware id + * + * @param {object} filter - The filter to use in findOne method + * @return {object} - The doware record + */ + Self.getDocuware = async filter => { + return await Self.app.models.Docuware.findOne(filter); + }; + /** * Returns the dialog id * @@ -35,7 +45,7 @@ module.exports = Self => { if (!process.env.NODE_ENV) return Math.floor(Math.random() + 100); - const docuwareInfo = await Self.app.models.Docuware.findOne({ + const docuwareInfo = await Self.getDocuware({ where: { code, action diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 27be72295..4f212e186 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -1,5 +1,4 @@ const UserError = require('vn-loopback/util/user-error'); -const axios = require('axios'); module.exports = Self => { Self.remoteMethodCtx('upload', { @@ -7,9 +6,9 @@ module.exports = Self => { accessType: 'WRITE', accepts: [ { - arg: 'ticketIds', + arg: 'ids', type: ['number'], - description: 'The ticket ids', + description: 'The ids', required: true }, { @@ -29,8 +28,7 @@ module.exports = Self => { } }); - Self.upload = async function(ctx, ticketIds, fileCabinet, options) { - delete ctx.args.ticketIds; + Self.upload = async function(ctx, ids, fileCabinet, options) { const models = Self.app.models; const action = 'store'; @@ -39,125 +37,29 @@ module.exports = Self => { if (typeof options == 'object') Object.assign(myOptions, options); - const userConfig = await models.UserConfig.findById(ctx.req.accessToken.userId, { + const {tabletFk} = await models.UserConfig.findById(ctx.req.accessToken.userId, { fields: ['tabletFk'] }, myOptions); - if (!userConfig?.tabletFk) + if (!tabletFk) throw new UserError('This user does not have an assigned tablet'); const docuwareOptions = await Self.getOptions(); + const {modelFk} = await Self.getDocuware({code: fileCabinet, action}); + const modelIsValid = await Self.app.models.Module.findOne({where: {code: modelFk}}); + + if (!modelIsValid) + throw new UserError('This fileCabinet does not have an assigned module'); + + const model = modelFk.replace(/^.{1}/g, modelFk[0].toUpperCase()); + const fileCabinetId = await Self.getFileCabinet(fileCabinet); const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId); - const uploaded = []; - for (id of ticketIds) { - // get delivery note - ctx.args.id = id; - const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { - id, - type: 'deliveryNote' - }, myOptions); - // get ticket data - const ticket = await models.Ticket.findById(id, { - include: [{ - relation: 'client', - scope: { - fields: ['id', 'name', 'fi'] - } - }] - }, myOptions); + const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents`; - // upload file - const templateJson = { - 'Fields': [ - { - 'FieldName': 'N__ALBAR_N', - 'ItemElementName': 'string', - 'Item': id, - }, - { - 'FieldName': 'CIF_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().fi, - }, - { - 'FieldName': 'CODIGO_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().id, - }, - { - 'FieldName': 'NOMBRE_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().name + ' - ' + id, - }, - { - 'FieldName': 'FECHA_FACTURA', - 'ItemElementName': 'date', - 'Item': ticket.shipped, - }, - { - 'FieldName': 'TOTAL_FACTURA', - 'ItemElementName': 'Decimal', - 'Item': ticket.totalWithVat, - }, - { - 'FieldName': 'ESTADO', - 'ItemElementName': 'string', - 'Item': 'Pendiente procesar', - }, - { - 'FieldName': 'FIRMA_', - 'ItemElementName': 'string', - 'Item': 'Si', - }, - { - 'FieldName': 'FILTRO_TABLET', - 'ItemElementName': 'string', - 'Item': userConfig.tabletFk, - } - ] - }; + await Self.app.models[model].docuwareUpload({ctx, tabletFk, ids, myOptions, uri, docuwareOptions, dialogId}); - if (process.env.NODE_ENV != 'production') - throw new UserError('Action not allowed on the test environment'); - - // delete old - const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); - if (docuwareFile) { - const deleteJson = { - 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] - }; - const deleteUri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`; - await axios.put(deleteUri, deleteJson, docuwareOptions.headers); - } - - const uploadUri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`; - const FormData = require('form-data'); - const data = new FormData(); - - data.append('document', JSON.stringify(templateJson), 'schema.json'); - data.append('file[]', deliveryNote[0], 'file.pdf'); - const uploadOptions = { - headers: { - 'Content-Type': 'multipart/form-data', - 'X-File-ModifiedDate': Date.vnNew(), - 'Cookie': docuwareOptions.headers.headers.Cookie, - ...data.getHeaders() - }, - }; - - try { - await axios.post(uploadUri, data, uploadOptions); - } catch (err) { - const $t = ctx.req.__; - const message = $t('Failed to upload delivery note', {id}); - if (uploaded.length) - await models.TicketTracking.setDelivered(ctx, uploaded, myOptions); - throw new UserError(message); - } - uploaded.push(id); - } - return models.TicketTracking.setDelivered(ctx, ticketIds, myOptions); + return models.TicketTracking.setDelivered(ctx, ids, myOptions); }; }; diff --git a/back/models/docuware.json b/back/models/docuware.json index b1a6a8bce..4709382cc 100644 --- a/back/models/docuware.json +++ b/back/models/docuware.json @@ -27,6 +27,9 @@ }, "findById": { "type": "string" + }, + "modelFk": { + "type": "string" } }, "relations": { diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 3e6edf07d..c5e66babf 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -3788,7 +3788,26 @@ INSERT INTO vn.workerTeam(id, team, workerFk) VALUES (8, 1, 19); -INSERT INTO vn.workCenter (id, name, payrollCenterFk, counter, warehouseFk, street, geoFk, deliveryManAdjustment) +INSERT INTO vn.workCenter (id, name, payrollCenterFk, counter, warehouseFk, street, geoFk, deliveryManAdjustment) VALUES(100, 'workCenterOne', 1, NULL, 1, 'gotham', NULL, NULL); -UPDATE vn.locker SET workerFk = 1110 WHERE id = 147; \ No newline at end of file +UPDATE vn.locker SET workerFk = 1110 WHERE id = 147; + +ALTER TABLE vn.docuware ADD modelFk VARCHAR(45) DEFAULT NULL NULL; +ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) REFERENCES salix.module(code); +-- Auto-generated SQL script #202405201318 +UPDATE vn.userConfig + SET tabletFk='Tablet1' + WHERE userFk=5; + + +-- Auto-generated SQL script #202405201207 +UPDATE vn.docuware + SET modelFk='ticket' + WHERE id=1; +UPDATE vn.docuware + SET modelFk='ticket' + WHERE id=2; +UPDATE vn.docuware + SET modelFk='worker' + WHERE id=3; diff --git a/modules/ticket/back/methods/ticket/docuwareUpload.js b/modules/ticket/back/methods/ticket/docuwareUpload.js new file mode 100644 index 000000000..fdeeb4519 --- /dev/null +++ b/modules/ticket/back/methods/ticket/docuwareUpload.js @@ -0,0 +1,116 @@ +const axios = require('axios'); +const {models} = require('vn-loopback/server/server'); +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.docuwareUpload = async({ctx, tabletFk, ids: ticketIds, myOptions, uri, docuwareOptions, dialogId}) => { + for (id of ticketIds) { + // get delivery note + ctx.args.id = id; + const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { + id, + type: 'deliveryNote' + }, myOptions); + // get ticket data + const ticket = await models.Ticket.findById(id, { + include: [{ + relation: 'client', + scope: { + fields: ['id', 'name', 'fi'] + } + }] + }, myOptions); + + // upload file + const templateJson = { + 'Fields': [ + { + 'FieldName': 'N__ALBAR_N', + 'ItemElementName': 'string', + 'Item': id, + }, + { + 'FieldName': 'CIF_PROVEEDOR', + 'ItemElementName': 'string', + 'Item': ticket.client().fi, + }, + { + 'FieldName': 'CODIGO_PROVEEDOR', + 'ItemElementName': 'string', + 'Item': ticket.client().id, + }, + { + 'FieldName': 'NOMBRE_PROVEEDOR', + 'ItemElementName': 'string', + 'Item': ticket.client().name + ' - ' + id, + }, + { + 'FieldName': 'FECHA_FACTURA', + 'ItemElementName': 'date', + 'Item': ticket.shipped, + }, + { + 'FieldName': 'TOTAL_FACTURA', + 'ItemElementName': 'Decimal', + 'Item': ticket.totalWithVat, + }, + { + 'FieldName': 'ESTADO', + 'ItemElementName': 'string', + 'Item': 'Pendiente procesar', + }, + { + 'FieldName': 'FIRMA_', + 'ItemElementName': 'string', + 'Item': 'Si', + }, + { + 'FieldName': 'FILTRO_TABLET', + 'ItemElementName': 'string', + 'Item': tabletFk, + } + ] + }; + + if (process.env.NODE_ENV != 'production') + throw new UserError('Action not allowed on the test environment'); + + // delete old + const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); + if (docuwareFile) { + const deleteJson = { + 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] + }; + const deleteUri = `${uri}/${docuwareFile.id}/Fields`; + await axios.put(deleteUri, deleteJson, docuwareOptions.headers); + } + + const uploadUri = `${uri}?StoreDialogId=${dialogId}`; + const FormData = require('form-data'); + const data = new FormData(); + + data.append('document', JSON.stringify(templateJson), 'schema.json'); + data.append('file[]', deliveryNote[0], 'file.pdf'); + const uploadOptions = { + headers: { + 'Content-Type': 'multipart/form-data', + 'X-File-ModifiedDate': Date.vnNew(), + 'Cookie': docuwareOptions.headers.headers.Cookie, + ...data.getHeaders() + }, + }; + + try { + await axios.post(uploadUri, data, uploadOptions); + } catch (err) { + const $t = ctx.req.__; + const message = $t('Failed to upload delivery note', {id}); + if (uploaded.length) + await models.TicketTracking.setDelivered(ctx, uploaded, myOptions); + throw new UserError(message); + } + uploaded.push(id); + } + }; +}; + diff --git a/modules/ticket/back/models/ticket-methods.js b/modules/ticket/back/models/ticket-methods.js index 0ae2ce3b4..7e2b5d27c 100644 --- a/modules/ticket/back/models/ticket-methods.js +++ b/modules/ticket/back/models/ticket-methods.js @@ -45,6 +45,7 @@ module.exports = function(Self) { require('../methods/ticket/invoiceTickets')(Self); require('../methods/ticket/invoiceTicketsAndPdf')(Self); require('../methods/ticket/docuwareDownload')(Self); + require('../methods/ticket/docuwareUpload')(Self); require('../methods/ticket/myLastModified')(Self); require('../methods/ticket/addSaleByCode')(Self); require('../methods/ticket/clone')(Self); -- 2.40.1 From 85f8690634559ac082d0c96e3ab78475dcfae1af Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 20 May 2024 22:39:58 +0200 Subject: [PATCH 02/25] feat(salix): refs #5926 docuwareUpload for Worker --- db/dump/fixtures.before.sql | 4 + .../back/methods/worker/docuwareUpload.js | 117 ++++++++++++++++++ .../worker/back/methods/worker/signedPDA.js | 54 ++++++++ .../back/models/device-production-user.js | 1 + modules/worker/back/models/worker.js | 2 + 5 files changed, 178 insertions(+) create mode 100644 modules/worker/back/methods/worker/docuwareUpload.js create mode 100644 modules/worker/back/methods/worker/signedPDA.js diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index c5e66babf..b83d190c9 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -3793,6 +3793,7 @@ INSERT INTO vn.workCenter (id, name, payrollCenterFk, counter, warehouseFk, stre UPDATE vn.locker SET workerFk = 1110 WHERE id = 147; +ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL; ALTER TABLE vn.docuware ADD modelFk VARCHAR(45) DEFAULT NULL NULL; ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) REFERENCES salix.module(code); -- Auto-generated SQL script #202405201318 @@ -3811,3 +3812,6 @@ UPDATE vn.docuware UPDATE vn.docuware SET modelFk='worker' WHERE id=3; +-- Auto-generated SQL script #202405201951 +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('Worker','isPDASigned','READ','ALLOW','ROLE','hr'); diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js new file mode 100644 index 000000000..530b29ecc --- /dev/null +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -0,0 +1,117 @@ +const axios = require('axios'); +const {models} = require('vn-loopback/server/server'); +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.docuwareUpload = async({ctx, tabletFk, ids: workerId, myOptions, uri, docuwareOptions, dialogId}) => { + if (!Array.isArray(workerId)) workerId = [workerId]; + for (id of ticketIds) { + // get delivery note + ctx.args.id = id; + const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { + id, + type: 'deliveryNote' + }, myOptions); + // get ticket data + const ticket = await models.Ticket.findById(id, { + include: [{ + relation: 'client', + scope: { + fields: ['id', 'name', 'fi'] + } + }] + }, myOptions); + + // upload file + const templateJson = { + 'Fields': [ + { + 'FieldName': 'N__ALBAR_N', + 'ItemElementName': 'string', + 'Item': id, + }, + { + 'FieldName': 'CIF_PROVEEDOR', + 'ItemElementName': 'string', + 'Item': ticket.client().fi, + }, + { + 'FieldName': 'CODIGO_PROVEEDOR', + 'ItemElementName': 'string', + 'Item': ticket.client().id, + }, + { + 'FieldName': 'NOMBRE_PROVEEDOR', + 'ItemElementName': 'string', + 'Item': ticket.client().name + ' - ' + id, + }, + { + 'FieldName': 'FECHA_FACTURA', + 'ItemElementName': 'date', + 'Item': ticket.shipped, + }, + { + 'FieldName': 'TOTAL_FACTURA', + 'ItemElementName': 'Decimal', + 'Item': ticket.totalWithVat, + }, + { + 'FieldName': 'ESTADO', + 'ItemElementName': 'string', + 'Item': 'Pendiente procesar', + }, + { + 'FieldName': 'FIRMA_', + 'ItemElementName': 'string', + 'Item': 'Si', + }, + { + 'FieldName': 'FILTRO_TABLET', + 'ItemElementName': 'string', + 'Item': tabletFk, + } + ] + }; + + if (process.env.NODE_ENV != 'production') + throw new UserError('Action not allowed on the test environment'); + + // delete old + const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); + if (docuwareFile) { + const deleteJson = { + 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] + }; + const deleteUri = `${uri}/${docuwareFile.id}/Fields`; + await axios.put(deleteUri, deleteJson, docuwareOptions.headers); + } + + const uploadUri = `${uri}?StoreDialogId=${dialogId}`; + const FormData = require('form-data'); + const data = new FormData(); + + data.append('document', JSON.stringify(templateJson), 'schema.json'); + data.append('file[]', deliveryNote[0], 'file.pdf'); + const uploadOptions = { + headers: { + 'Content-Type': 'multipart/form-data', + 'X-File-ModifiedDate': Date.vnNew(), + 'Cookie': docuwareOptions.headers.headers.Cookie, + ...data.getHeaders() + }, + }; + + try { + await axios.post(uploadUri, data, uploadOptions); + } catch (err) { + const $t = ctx.req.__; + const message = $t('Failed to upload delivery note', {id}); + if (uploaded.length) + await models.TicketTracking.setDelivered(ctx, uploaded, myOptions); + throw new UserError(message); + } + uploaded.push(id); + } + }; +}; + diff --git a/modules/worker/back/methods/worker/signedPDA.js b/modules/worker/back/methods/worker/signedPDA.js new file mode 100644 index 000000000..5080b84d5 --- /dev/null +++ b/modules/worker/back/methods/worker/signedPDA.js @@ -0,0 +1,54 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethodCtx('isPDASigned', { + description: 'Deallocate the PDA of the worker', + accepts: [{ + arg: 'ctx', + type: 'Object', + http: {source: 'context'} + }, { + arg: 'id', + type: 'number', + required: true, + description: 'The worker id', + http: {source: 'path'} + }, { + arg: 'pda', + type: 'number', + required: true, + description: 'The pda id' + }], + returns: { + type: ['object'], + root: true + }, + http: { + path: '/:id/:pda/isPDASigned', + verb: 'GET' + } + }); + + Self.isPDASigned = async(ctx, options) => { + const models = Self.app.models; + const args = ctx.args; + let tx; + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + const pda = await models.DeviceProduction.findById(args.pda, myOptions); + if (pda.stateFk != 'idle') throw new UserError(`The PDA state is not idle`); + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/worker/back/models/device-production-user.js b/modules/worker/back/models/device-production-user.js index 81af484d3..f228f6d55 100644 --- a/modules/worker/back/models/device-production-user.js +++ b/modules/worker/back/models/device-production-user.js @@ -1,4 +1,5 @@ const UserError = require('vn-loopback/util/user-error'); + module.exports = Self => { Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 0b0e043f2..6f9c1e239 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -8,6 +8,7 @@ module.exports = Self => { require('../methods/worker/createAbsence')(Self); require('../methods/worker/deleteAbsence')(Self); require('../methods/worker/updateAbsence')(Self); + require('../methods/worker/docuwareUpload')(Self); require('../methods/worker/active')(Self); require('../methods/worker/activeWithRole')(Self); require('../methods/worker/activeWithInheritedRole')(Self); @@ -17,6 +18,7 @@ module.exports = Self => { require('../methods/worker/new')(Self); require('../methods/worker/deallocatePDA')(Self); require('../methods/worker/allocatePDA')(Self); + require('../methods/worker/signedPDA')(Self); require('../methods/worker/search')(Self); require('../methods/worker/isAuthorized')(Self); require('../methods/worker/setPassword')(Self); -- 2.40.1 From 2a65313e7fea6f1a635f12eeccd6c71d7f173562 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Tue, 21 May 2024 11:15:58 +0200 Subject: [PATCH 03/25] feat(salix): refs #5926 myt version --- db/dump/fixtures.before.sql | 7 +------ db/versions/11062-tealArborvitae/00-firstScript.sql | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 db/versions/11062-tealArborvitae/00-firstScript.sql diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index b83d190c9..18ccd79d0 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -3793,9 +3793,6 @@ INSERT INTO vn.workCenter (id, name, payrollCenterFk, counter, warehouseFk, stre UPDATE vn.locker SET workerFk = 1110 WHERE id = 147; -ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL; -ALTER TABLE vn.docuware ADD modelFk VARCHAR(45) DEFAULT NULL NULL; -ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) REFERENCES salix.module(code); -- Auto-generated SQL script #202405201318 UPDATE vn.userConfig SET tabletFk='Tablet1' @@ -3812,6 +3809,4 @@ UPDATE vn.docuware UPDATE vn.docuware SET modelFk='worker' WHERE id=3; --- Auto-generated SQL script #202405201951 -INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) - VALUES ('Worker','isPDASigned','READ','ALLOW','ROLE','hr'); + diff --git a/db/versions/11062-tealArborvitae/00-firstScript.sql b/db/versions/11062-tealArborvitae/00-firstScript.sql new file mode 100644 index 000000000..c262cf323 --- /dev/null +++ b/db/versions/11062-tealArborvitae/00-firstScript.sql @@ -0,0 +1,9 @@ +-- Place your SQL code here + +ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL; +ALTER TABLE vn.docuware ADD modelFk VARCHAR(45) DEFAULT NULL NULL; +ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) REFERENCES salix.module(code); + +-- Auto-generated SQL script #202405201951 +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('Worker','isPDASigned','READ','ALLOW','ROLE','hr'); -- 2.40.1 From a3a46d28be9c9caa0a394a7bdc2d1a3fff3a6855 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Tue, 21 May 2024 23:58:25 +0200 Subject: [PATCH 04/25] feat(salix): refs #5926 report(1) --- .../back/methods/ticket/docuwareUpload.js | 4 +- .../back/methods/worker/docuwareUpload.js | 4 +- print/core/report.js | 2 +- .../reports/sign-pda/assets/css/import.js | 12 + .../reports/sign-pda/assets/css/style.css | 49 ++++ .../assets/images/europe.png | Bin .../templates/reports/sign-pda/locale/en.yml | 50 ++++ .../templates/reports/sign-pda/locale/es.yml | 2 + .../templates/reports/sign-pda/locale/fr.yml | 51 ++++ .../templates/reports/sign-pda/locale/pt.yml | 51 ++++ .../templates/reports/sign-pda/sign-pda.html | 252 ++++++++++++++++++ print/templates/reports/sign-pda/sign-pda.js | 110 ++++++++ 12 files changed, 582 insertions(+), 5 deletions(-) create mode 100644 print/templates/reports/sign-pda/assets/css/import.js create mode 100644 print/templates/reports/sign-pda/assets/css/style.css rename print/templates/reports/{delivery-note => sign-pda}/assets/images/europe.png (100%) create mode 100644 print/templates/reports/sign-pda/locale/en.yml create mode 100644 print/templates/reports/sign-pda/locale/es.yml create mode 100644 print/templates/reports/sign-pda/locale/fr.yml create mode 100644 print/templates/reports/sign-pda/locale/pt.yml create mode 100644 print/templates/reports/sign-pda/sign-pda.html create mode 100755 print/templates/reports/sign-pda/sign-pda.js diff --git a/modules/ticket/back/methods/ticket/docuwareUpload.js b/modules/ticket/back/methods/ticket/docuwareUpload.js index fdeeb4519..0b77b27c1 100644 --- a/modules/ticket/back/methods/ticket/docuwareUpload.js +++ b/modules/ticket/back/methods/ticket/docuwareUpload.js @@ -1,7 +1,7 @@ const axios = require('axios'); const {models} = require('vn-loopback/server/server'); const UserError = require('vn-loopback/util/user-error'); - +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.docuwareUpload = async({ctx, tabletFk, ids: ticketIds, myOptions, uri, docuwareOptions, dialogId}) => { for (id of ticketIds) { @@ -72,7 +72,7 @@ module.exports = Self => { ] }; - if (process.env.NODE_ENV != 'production') + if (!isProduction(false)) throw new UserError('Action not allowed on the test environment'); // delete old diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 530b29ecc..37602729b 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -1,7 +1,7 @@ const axios = require('axios'); const {models} = require('vn-loopback/server/server'); const UserError = require('vn-loopback/util/user-error'); - +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.docuwareUpload = async({ctx, tabletFk, ids: workerId, myOptions, uri, docuwareOptions, dialogId}) => { if (!Array.isArray(workerId)) workerId = [workerId]; @@ -73,7 +73,7 @@ module.exports = Self => { ] }; - if (process.env.NODE_ENV != 'production') + if (!isProduction(false)) throw new UserError('Action not allowed on the test environment'); // delete old diff --git a/print/core/report.js b/print/core/report.js index 23cffac2c..fbef20a67 100644 --- a/print/core/report.js +++ b/print/core/report.js @@ -52,7 +52,7 @@ class Report extends Component { options.headerTemplate = '\n'; options.footerTemplate = footer; - const buffer = await page.pdf(options); + const buffer = await page.content(); resolve(buffer); } catch (err) { reject(err); diff --git a/print/templates/reports/sign-pda/assets/css/import.js b/print/templates/reports/sign-pda/assets/css/import.js new file mode 100644 index 000000000..37a98dfdd --- /dev/null +++ b/print/templates/reports/sign-pda/assets/css/import.js @@ -0,0 +1,12 @@ +const Stylesheet = require(`vn-print/core/stylesheet`); + +const path = require('path'); +const vnPrintPath = path.resolve('print'); + +module.exports = new Stylesheet([ + `${vnPrintPath}/common/css/spacing.css`, + `${vnPrintPath}/common/css/misc.css`, + `${vnPrintPath}/common/css/layout.css`, + `${vnPrintPath}/common/css/report.css`, + `${__dirname}/style.css`]) + .mergeStyles(); diff --git a/print/templates/reports/sign-pda/assets/css/style.css b/print/templates/reports/sign-pda/assets/css/style.css new file mode 100644 index 000000000..3e1c64d85 --- /dev/null +++ b/print/templates/reports/sign-pda/assets/css/style.css @@ -0,0 +1,49 @@ +#signature { + padding-right: 10px +} + +#signature img { + -webkit-filter: brightness(0%); + filter: brightness(0%); + margin-bottom: 10px; + max-width: 150px +} + +.description strong { + text-transform: uppercase; +} + +h2 { + font-weight: 100; + color: #555 +} + +.ticket-info { + font-size: 22px +} + +#phytosanitary { + padding-right: 10px +} + +#phytosanitary .flag img { + width: 100% +} + +#phytosanitary .flag .flag-text { + padding-left: 10px; + box-sizing: border-box; +} + +.phytosanitary-info { + margin-top: 10px +} + +.observations { + text-align: justify; + text-justify: inter-word; +} + +.column-oriented { + margin-bottom: 5px; +} \ No newline at end of file diff --git a/print/templates/reports/delivery-note/assets/images/europe.png b/print/templates/reports/sign-pda/assets/images/europe.png similarity index 100% rename from print/templates/reports/delivery-note/assets/images/europe.png rename to print/templates/reports/sign-pda/assets/images/europe.png diff --git a/print/templates/reports/sign-pda/locale/en.yml b/print/templates/reports/sign-pda/locale/en.yml new file mode 100644 index 000000000..a1392e15e --- /dev/null +++ b/print/templates/reports/sign-pda/locale/en.yml @@ -0,0 +1,50 @@ +reportName: sign-pda +deliveryNote: Sign PDA +proforma: Proforma +withoutPrices: Sign PDA +clientId: Client +deliveryAddress: Delivery address +fiscalData: Fiscal data +saleLines: Line items +date: Date +reference: Ref. +quantity: Qty. +concept: Concept +price: PSP/u +discount: Disc. +vat: VAT +amount: Amount +total: Total +subtotal: Subtotal +vatType: VAT Type +digitalSignature: Digital signature +plantPassport: Plant passport +packages: Packages +services: + title: Services + theader: + quantity: Qty. + concept: Concept + price: PSP/u + vat: VAT + amount: Amount + tfoot: + subtotal: Subtotal + warning: Deposit packaging will be invoiced if they have not been returned after 30 days of their delivery. +packagings: + title: Buckets and packaging + theader: + reference: Reference + quantity: Quantity + concept: Concept +taxes: + title: Tax breakdown + theader: + type: Type + taxBase: Tax base + tax: Tax + fee: Fee + tfoot: + subtotal: Subtotal + total: Total +observations: Observations diff --git a/print/templates/reports/sign-pda/locale/es.yml b/print/templates/reports/sign-pda/locale/es.yml new file mode 100644 index 000000000..7f5b656c9 --- /dev/null +++ b/print/templates/reports/sign-pda/locale/es.yml @@ -0,0 +1,2 @@ +reportName: pda +signNote: Recepción PDA diff --git a/print/templates/reports/sign-pda/locale/fr.yml b/print/templates/reports/sign-pda/locale/fr.yml new file mode 100644 index 000000000..603623a47 --- /dev/null +++ b/print/templates/reports/sign-pda/locale/fr.yml @@ -0,0 +1,51 @@ +reportName: bon-de-livraison +deliveryNote: Bon de livraison +proforma: Proforma +withoutPrices: Bon de livraison +clientId: Client +deliveryAddress: Adresse de livraison +fiscalData: Coordonnées +saleLines: Lignes de la commande +date: Date +reference: Ref. +quantity: Quant. +concept: Concept +price: PRIX/u +discount: Remise +vat: TVA +amount: Montant +total: Total +subtotal: Total partiel +vatType: Type de TVA +digitalSignature: Signature numérique +ticket: BL {0} +plantPassport: Passeport phytosanitaire +packages: Paquets +services: + title: Service + theader: + quantity: Quantité + concept: Concept + price: PRIX/u + vat: TVA + amount: Montant + tfoot: + subtotal: Total partiel + warning: Les emballages de consigne seront facturés s'ils n'ont pas été retournés après 30 jours de leur livraison. +packagings: + title: Bacs et emballages + theader: + reference: Référence + quantity: Quantité + concept: Concept +taxes: + title: Répartition taxes + theader: + type: Type + taxBase: Base imposable + tax: Taxe + fee: Quote + tfoot: + subtotal: Total partiel + total: Total +observations: Observations \ No newline at end of file diff --git a/print/templates/reports/sign-pda/locale/pt.yml b/print/templates/reports/sign-pda/locale/pt.yml new file mode 100644 index 000000000..fb49d230b --- /dev/null +++ b/print/templates/reports/sign-pda/locale/pt.yml @@ -0,0 +1,51 @@ +reportName: nota-de-entrega +deliveryNote: Nota de Entrega +proforma: Proforma +withoutPrices: Nota de Entrega +clientId: Cliente +deliveryAddress: Morada de Entrega +fiscalData: Dados Fiscais +saleLines: Linhas da encomenda +date: Data +reference: Ref. +quantity: Qtde. +concept: Conceito +price: PVP/u +discount: Dto. +vat: IVA +amount: Importe +total: Total +subtotal: Sub-total +vatType: Tipo de IVA +digitalSignature: Assinatura digital +ticket: Nota de Entrega {0} +plantPassport: Passaporte vegetal +packages: Pacotes +services: + title: Serviços + theader: + quantity: Quantidade + concept: Conceito + price: PVP/u + vat: IVA + amount: Quantia + tfoot: + subtotal: Subtotal + warning: As embalagens em depósito serão facturadas e cobradas se não são devolvidas 30 dias após a entrega. +packagings: + title: Baldes e Embalagens + theader: + reference: Referência + quantity: Quantidade + concept: Conceito +taxes: + title: Repartição de impostos + theader: + type: Cara + taxBase: Tributável + tax: Taxa + fee: Compartilhado + tfoot: + subtotal: Subtotal + total: Total +observations: Observações \ No newline at end of file diff --git a/print/templates/reports/sign-pda/sign-pda.html b/print/templates/reports/sign-pda/sign-pda.html new file mode 100644 index 000000000..92dd1b126 --- /dev/null +++ b/print/templates/reports/sign-pda/sign-pda.html @@ -0,0 +1,252 @@ + + +
+
+
+
+
+

{{$t(deliverNoteType)}}

+ + + + + + + + + + + + + + + + + + + +
{{$t('clientId')}}{{client.id}}
{{$t(deliverNoteType)}}{{ticket.id}}
{{$t('date')}}{{formatDate(ticket.shipped, '%d-%m-%Y')}}
{{$t('packages')}}{{ticket.packages}}
+
+
+
+
+
{{$t('deliveryAddress')}}
+
+

{{address.nickname}}

+
{{address.street}}
+
{{address.postalCode}}, {{address.city}} ({{address.province}})
+
+
+ +
+
{{$t('fiscalData')}}
+
+
{{client.socialName}}
+
{{client.street}}
+
{{client.fi}}
+
+
+
+
+ +

{{$t('saleLines')}}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{$t('reference')}}{{$t('quantity')}}{{$t('concept')}}{{$t('price')}}{{$t('discount')}}{{$t('vat')}}{{$t('amount')}}
{{sale.itemFk}}{{sale.quantity}}{{sale.concept}}{{sale.price | currency('EUR', $i18n.locale)}}{{(sale.discount / 100) | percentage}}{{sale.vatType}} + {{sale.price * sale.quantity * (1 - sale.discount / 100) | currency('EUR', $i18n.locale)}} +
+ {{sale.tag5}} {{sale.value5}} + {{sale.tag6}} {{sale.value6}} + {{sale.tag7}} {{sale.value7}} +
+ {{$t('subtotal')}} + {{getSubTotal() | currency('EUR', $i18n.locale)}}
+ +
+
+

{{$t('services.title')}}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{$t('services.theader.quantity')}}{{$t('services.theader.concept')}}{{$t('services.theader.price')}}{{$t('services.theader.vat')}}{{$t('services.theader.amount')}}
{{service.quantity}}{{service.description}}{{service.price | currency('EUR', $i18n.locale)}}{{service.taxDescription}}{{service.total | currency('EUR', $i18n.locale)}}
+ {{$t('services.tfoot.subtotal')}} + {{serviceTotal | currency('EUR', $i18n.locale)}}
+ * {{ $t('services.warning') }} +
+
+
+
+

{{$t('packagings.title')}}

+ + + + + + + + + + + + + + + +
{{$t('packagings.theader.reference')}}{{$t('packagings.theader.quantity')}}{{$t('packagings.theader.concept')}}
{{packaging.itemFk}}{{packaging.quantity}}{{packaging.name}}
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{$t('taxes.title')}}
{{$t('taxes.theader.type')}}{{$t('taxes.theader.taxBase')}}{{$t('taxes.theader.tax')}}{{$t('taxes.theader.fee')}}
{{tax.name}}{{tax.Base | currency('EUR', $i18n.locale)}}{{tax.vatPercent | percentage}}{{tax.tax | currency('EUR', $i18n.locale)}}
{{$t('subtotal')}}{{getTotalBase() | currency('EUR', $i18n.locale)}}{{getTotalTax()| currency('EUR', $i18n.locale)}}
{{$t('total')}}{{getTotal() | currency('EUR', $i18n.locale)}}
+
+ +
+
+
+
+
+
+ +
+
{{$t('plantPassport')}}
+
+
+
+
+ A + {{getBotanical()}} +
+
+ B + ES17462130 +
+
+ C + {{ticket.id}} +
+
+ D + ES +
+
+
+
+
+
+
+
+
+
{{$t('digitalSignature')}}
+
+ +
{{formatDate(signature.created, '%d-%m-%Y')}}
+
+
+
+
+
+
+

{{$t('observations')}}

+

{{ticket.description}}

+
+
+
+
+ +
\ No newline at end of file diff --git a/print/templates/reports/sign-pda/sign-pda.js b/print/templates/reports/sign-pda/sign-pda.js new file mode 100755 index 000000000..50d5720ad --- /dev/null +++ b/print/templates/reports/sign-pda/sign-pda.js @@ -0,0 +1,110 @@ +const config = require(`vn-print/core/config`); +const vnReport = require('../../../core/mixins/vn-report.js'); +const md5 = require('md5'); +const fs = require('fs-extra'); + +module.exports = { + name: 'delivery-note', + mixins: [vnReport], + async serverPrefetch() { + this.ticket = await this.findOneFromDef('ticket', [this.id]); + this.checkMainEntity(this.ticket); + this.client = await this.findOneFromDef('client', [this.id]); + this.sales = await this.rawSqlFromDef('sales', [this.id]); + this.address = await this.findOneFromDef(`address`, [this.id]); + this.services = await this.rawSqlFromDef('services', [this.id]); + this.taxes = await this.findOneFromDef('taxes', [this.id]); + this.packagings = await this.rawSqlFromDef('packagings', [this.id]); + this.signature = await this.findOneFromDef('signature', [this.id]); + }, + data() { + return {totalBalance: 0.00}; + }, + computed: { + dmsPath() { + if (!this.signature) return; + + const hash = md5(this.signature.id.toString()).substring(0, 3); + const file = `${config.storage.root}/${hash}/${this.signature.id}.png`; + + if (!fs.existsSync(file)) return null; + + const src = fs.readFileSync(file); + const base64 = Buffer.from(src, 'utf8').toString('base64'); + + return `data:image/png;base64, ${base64}`; + }, + deliverNoteType() { + return this.type ? this.type : 'deliveryNote'; + }, + serviceTotal() { + let total = 0.00; + this.services.forEach(service => { + total += parseFloat(service.price) * service.quantity; + }); + + return total; + }, + showPrices() { + return this.deliverNoteType != 'withoutPrices'; + }, + footerType() { + const translatedType = this.$t(this.deliverNoteType); + return `${translatedType} ${this.id}`; + }, + hasObservations() { + return this.ticket.description !== null; + } + }, + methods: { + getSubTotal() { + let subTotal = 0.00; + this.sales.forEach(sale => { + subTotal += sale.quantity * sale.price * (1 - sale.discount / 100); + }); + + return subTotal; + }, + getTotalBase() { + let totalBase = 0.00; + this.taxes.forEach(tax => { + totalBase += parseFloat(tax.Base); + }); + + return totalBase; + }, + getTotalTax() { + let totalTax = 0.00; + this.taxes.forEach(tax => { + totalTax += parseFloat(tax.tax); + }); + + return totalTax; + }, + getTotal() { + return this.getTotalBase() + this.getTotalTax(); + }, + getBotanical() { + let phytosanitary = []; + this.sales.forEach(sale => { + if (sale.botanical) + phytosanitary.push(sale.botanical); + }); + + return phytosanitary.filter((item, index) => + phytosanitary.indexOf(item) == index + ).join(', '); + } + }, + props: { + id: { + type: Number, + required: true, + description: 'The ticket id' + }, + type: { + type: String, + required: false + } + } +}; -- 2.40.1 From 7042d96aca40035bc97e293fd183160968ebe2bb Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 22 May 2024 08:39:24 +0200 Subject: [PATCH 05/25] feat: refs #5926 pdaPDF report --- .../11062-tealArborvitae/00-firstScript.sql | 2 + .../worker/back/methods/worker/signPdaPdf.js | 35 +++ modules/worker/back/models/worker.js | 1 + print/core/report.js | 2 +- .../reports/sign-pda/assets/css/style.css | 50 +--- .../templates/reports/sign-pda/locale/de.yml | 14 + .../templates/reports/sign-pda/locale/en.yml | 64 +---- .../templates/reports/sign-pda/locale/es.yml | 12 + .../templates/reports/sign-pda/locale/fr.yml | 65 +---- .../templates/reports/sign-pda/locale/pt.yml | 65 +---- .../templates/reports/sign-pda/sign-pda.html | 266 +++--------------- print/templates/reports/sign-pda/sign-pda.js | 91 +----- .../templates/reports/sign-pda/sql/device.sql | 1 + 13 files changed, 159 insertions(+), 509 deletions(-) create mode 100644 modules/worker/back/methods/worker/signPdaPdf.js create mode 100644 print/templates/reports/sign-pda/locale/de.yml create mode 100644 print/templates/reports/sign-pda/sql/device.sql diff --git a/db/versions/11062-tealArborvitae/00-firstScript.sql b/db/versions/11062-tealArborvitae/00-firstScript.sql index c262cf323..f619e8906 100644 --- a/db/versions/11062-tealArborvitae/00-firstScript.sql +++ b/db/versions/11062-tealArborvitae/00-firstScript.sql @@ -7,3 +7,5 @@ ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) -- Auto-generated SQL script #202405201951 INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES ('Worker','isPDASigned','READ','ALLOW','ROLE','hr'); +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('Worker','signPdaPdf','READ','ALLOW','ROLE','hr'); diff --git a/modules/worker/back/methods/worker/signPdaPdf.js b/modules/worker/back/methods/worker/signPdaPdf.js new file mode 100644 index 000000000..e0f075af4 --- /dev/null +++ b/modules/worker/back/methods/worker/signPdaPdf.js @@ -0,0 +1,35 @@ + +module.exports = Self => { + Self.remoteMethodCtx('signPdaPdf', { + description: 'Print pdf to sign PDA', + accepts: [{ + arg: 'id', + type: 'number', + required: true, + description: 'The pda id', + http: {source: 'path'} + }], + returns: [ + { + arg: 'body', + type: 'file', + root: true + }, { + arg: 'Content-Type', + type: 'String', + http: {target: 'header'} + }, { + arg: 'Content-Disposition', + type: 'String', + http: {target: 'header'} + } + ], + http: { + path: '/:id/sign-pda-pdf', + verb: 'GET' + }, + accessScopes: ['DEFAULT', 'read:multimedia'] + }); + + Self.signPdaPdf = (ctx, id) => Self.printReport(ctx, id, 'sign-pda'); +}; diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 6f9c1e239..4e8b22f20 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -19,6 +19,7 @@ module.exports = Self => { require('../methods/worker/deallocatePDA')(Self); require('../methods/worker/allocatePDA')(Self); require('../methods/worker/signedPDA')(Self); + require('../methods/worker/signPdaPdf')(Self); require('../methods/worker/search')(Self); require('../methods/worker/isAuthorized')(Self); require('../methods/worker/setPassword')(Self); diff --git a/print/core/report.js b/print/core/report.js index fbef20a67..23cffac2c 100644 --- a/print/core/report.js +++ b/print/core/report.js @@ -52,7 +52,7 @@ class Report extends Component { options.headerTemplate = '\n'; options.footerTemplate = footer; - const buffer = await page.content(); + const buffer = await page.pdf(options); resolve(buffer); } catch (err) { reject(err); diff --git a/print/templates/reports/sign-pda/assets/css/style.css b/print/templates/reports/sign-pda/assets/css/style.css index 3e1c64d85..4be772dc7 100644 --- a/print/templates/reports/sign-pda/assets/css/style.css +++ b/print/templates/reports/sign-pda/assets/css/style.css @@ -1,13 +1,3 @@ -#signature { - padding-right: 10px -} - -#signature img { - -webkit-filter: brightness(0%); - filter: brightness(0%); - margin-bottom: 10px; - max-width: 150px -} .description strong { text-transform: uppercase; @@ -18,32 +8,18 @@ h2 { color: #555 } -.ticket-info { - font-size: 22px -} - -#phytosanitary { - padding-right: 10px -} - -#phytosanitary .flag img { - width: 100% -} - -#phytosanitary .flag .flag-text { - padding-left: 10px; - box-sizing: border-box; -} - -.phytosanitary-info { - margin-top: 10px -} - -.observations { - text-align: justify; - text-justify: inter-word; -} - .column-oriented { margin-bottom: 5px; -} \ No newline at end of file +} + +.report-info { + font-size: 20px +} +.row-oriented > tbody > tr > th { + padding-left: 30px; + width: 20% +} + +.grid-block { + font-size: 1.2em +} diff --git a/print/templates/reports/sign-pda/locale/de.yml b/print/templates/reports/sign-pda/locale/de.yml new file mode 100644 index 000000000..d43479cc3 --- /dev/null +++ b/print/templates/reports/sign-pda/locale/de.yml @@ -0,0 +1,14 @@ +reportName: pda +signNote: Recepción PDA +date: Fecha +deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +pdaModel: PDA Modelo +pdaSerie: Serie PDA +sim: sim vodafone +pin: pin +puk: puk +label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. +label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. +label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. diff --git a/print/templates/reports/sign-pda/locale/en.yml b/print/templates/reports/sign-pda/locale/en.yml index a1392e15e..d43479cc3 100644 --- a/print/templates/reports/sign-pda/locale/en.yml +++ b/print/templates/reports/sign-pda/locale/en.yml @@ -1,50 +1,14 @@ -reportName: sign-pda -deliveryNote: Sign PDA -proforma: Proforma -withoutPrices: Sign PDA -clientId: Client -deliveryAddress: Delivery address -fiscalData: Fiscal data -saleLines: Line items -date: Date -reference: Ref. -quantity: Qty. -concept: Concept -price: PSP/u -discount: Disc. -vat: VAT -amount: Amount -total: Total -subtotal: Subtotal -vatType: VAT Type -digitalSignature: Digital signature -plantPassport: Plant passport -packages: Packages -services: - title: Services - theader: - quantity: Qty. - concept: Concept - price: PSP/u - vat: VAT - amount: Amount - tfoot: - subtotal: Subtotal - warning: Deposit packaging will be invoiced if they have not been returned after 30 days of their delivery. -packagings: - title: Buckets and packaging - theader: - reference: Reference - quantity: Quantity - concept: Concept -taxes: - title: Tax breakdown - theader: - type: Type - taxBase: Tax base - tax: Tax - fee: Fee - tfoot: - subtotal: Subtotal - total: Total -observations: Observations +reportName: pda +signNote: Recepción PDA +date: Fecha +deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +pdaModel: PDA Modelo +pdaSerie: Serie PDA +sim: sim vodafone +pin: pin +puk: puk +label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. +label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. +label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. diff --git a/print/templates/reports/sign-pda/locale/es.yml b/print/templates/reports/sign-pda/locale/es.yml index 7f5b656c9..d43479cc3 100644 --- a/print/templates/reports/sign-pda/locale/es.yml +++ b/print/templates/reports/sign-pda/locale/es.yml @@ -1,2 +1,14 @@ reportName: pda signNote: Recepción PDA +date: Fecha +deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +pdaModel: PDA Modelo +pdaSerie: Serie PDA +sim: sim vodafone +pin: pin +puk: puk +label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. +label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. +label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. diff --git a/print/templates/reports/sign-pda/locale/fr.yml b/print/templates/reports/sign-pda/locale/fr.yml index 603623a47..d43479cc3 100644 --- a/print/templates/reports/sign-pda/locale/fr.yml +++ b/print/templates/reports/sign-pda/locale/fr.yml @@ -1,51 +1,14 @@ -reportName: bon-de-livraison -deliveryNote: Bon de livraison -proforma: Proforma -withoutPrices: Bon de livraison -clientId: Client -deliveryAddress: Adresse de livraison -fiscalData: Coordonnées -saleLines: Lignes de la commande -date: Date -reference: Ref. -quantity: Quant. -concept: Concept -price: PRIX/u -discount: Remise -vat: TVA -amount: Montant -total: Total -subtotal: Total partiel -vatType: Type de TVA -digitalSignature: Signature numérique -ticket: BL {0} -plantPassport: Passeport phytosanitaire -packages: Paquets -services: - title: Service - theader: - quantity: Quantité - concept: Concept - price: PRIX/u - vat: TVA - amount: Montant - tfoot: - subtotal: Total partiel - warning: Les emballages de consigne seront facturés s'ils n'ont pas été retournés après 30 jours de leur livraison. -packagings: - title: Bacs et emballages - theader: - reference: Référence - quantity: Quantité - concept: Concept -taxes: - title: Répartition taxes - theader: - type: Type - taxBase: Base imposable - tax: Taxe - fee: Quote - tfoot: - subtotal: Total partiel - total: Total -observations: Observations \ No newline at end of file +reportName: pda +signNote: Recepción PDA +date: Fecha +deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +pdaModel: PDA Modelo +pdaSerie: Serie PDA +sim: sim vodafone +pin: pin +puk: puk +label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. +label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. +label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. diff --git a/print/templates/reports/sign-pda/locale/pt.yml b/print/templates/reports/sign-pda/locale/pt.yml index fb49d230b..d43479cc3 100644 --- a/print/templates/reports/sign-pda/locale/pt.yml +++ b/print/templates/reports/sign-pda/locale/pt.yml @@ -1,51 +1,14 @@ -reportName: nota-de-entrega -deliveryNote: Nota de Entrega -proforma: Proforma -withoutPrices: Nota de Entrega -clientId: Cliente -deliveryAddress: Morada de Entrega -fiscalData: Dados Fiscais -saleLines: Linhas da encomenda -date: Data -reference: Ref. -quantity: Qtde. -concept: Conceito -price: PVP/u -discount: Dto. -vat: IVA -amount: Importe -total: Total -subtotal: Sub-total -vatType: Tipo de IVA -digitalSignature: Assinatura digital -ticket: Nota de Entrega {0} -plantPassport: Passaporte vegetal -packages: Pacotes -services: - title: Serviços - theader: - quantity: Quantidade - concept: Conceito - price: PVP/u - vat: IVA - amount: Quantia - tfoot: - subtotal: Subtotal - warning: As embalagens em depósito serão facturadas e cobradas se não são devolvidas 30 dias após a entrega. -packagings: - title: Baldes e Embalagens - theader: - reference: Referência - quantity: Quantidade - concept: Conceito -taxes: - title: Repartição de impostos - theader: - type: Cara - taxBase: Tributável - tax: Taxa - fee: Compartilhado - tfoot: - subtotal: Subtotal - total: Total -observations: Observações \ No newline at end of file +reportName: pda +signNote: Recepción PDA +date: Fecha +deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +pdaModel: PDA Modelo +pdaSerie: Serie PDA +sim: sim vodafone +pin: pin +puk: puk +label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. +label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. +label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. diff --git a/print/templates/reports/sign-pda/sign-pda.html b/print/templates/reports/sign-pda/sign-pda.html index 92dd1b126..1ec7c4964 100644 --- a/print/templates/reports/sign-pda/sign-pda.html +++ b/print/templates/reports/sign-pda/sign-pda.html @@ -1,252 +1,56 @@
-
-
-

{{$t(deliverNoteType)}}

- - - - - - - - - - - - - - - - - - - -
{{$t('clientId')}}{{client.id}}
{{$t(deliverNoteType)}}{{ticket.id}}
{{$t('date')}}{{formatDate(ticket.shipped, '%d-%m-%Y')}}
{{$t('packages')}}{{ticket.packages}}
-
-
-
-
-
{{$t('deliveryAddress')}}
-
-

{{address.nickname}}

-
{{address.street}}
-
{{address.postalCode}}, {{address.city}} ({{address.province}})
-
-
- -
-
{{$t('fiscalData')}}
-
-
{{client.socialName}}
-
{{client.street}}
-
{{client.fi}}
-
-
-
-
- -

{{$t('saleLines')}}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{$t('reference')}}{{$t('quantity')}}{{$t('concept')}}{{$t('price')}}{{$t('discount')}}{{$t('vat')}}{{$t('amount')}}
{{sale.itemFk}}{{sale.quantity}}{{sale.concept}}{{sale.price | currency('EUR', $i18n.locale)}}{{(sale.discount / 100) | percentage}}{{sale.vatType}} - {{sale.price * sale.quantity * (1 - sale.discount / 100) | currency('EUR', $i18n.locale)}} -
- {{sale.tag5}} {{sale.value5}} - {{sale.tag6}} {{sale.value6}} - {{sale.tag7}} {{sale.value7}} -
- {{$t('subtotal')}} - {{getSubTotal() | currency('EUR', $i18n.locale)}}
- -
-
-

{{$t('services.title')}}

- - - - - - - - - - - - +
+ {{$t('date')}}:{{formatDate(new Date(), '%d-%m-%Y')}} +

+ {{$t('deviceRecieved')}}: +

+
{{$t('services.theader.quantity')}}{{$t('services.theader.concept')}}{{$t('services.theader.price')}}{{$t('services.theader.vat')}}{{$t('services.theader.amount')}}
- - - - - - - - - - - - - + + - -
{{service.quantity}}{{service.description}}{{service.price | currency('EUR', $i18n.locale)}}{{service.taxDescription}}{{service.total | currency('EUR', $i18n.locale)}}
- {{$t('services.tfoot.subtotal')}} - {{serviceTotal | currency('EUR', $i18n.locale)}}{{$t('pdaModel')}}{{device.modelFk}}
- * {{ $t('services.warning') }} -
-
-
-
-

{{$t('packagings.title')}}

- - - - - + + - - - - - - + + + + + + + + +
{{$t('packagings.theader.reference')}}{{$t('packagings.theader.quantity')}}{{$t('packagings.theader.concept')}}{{$t('pdaSerie')}}{{device.serialNumber}}
{{packaging.itemFk}}{{packaging.quantity}}{{packaging.name}}
{{$t('sim')}}{{device.sim}}
{{$t('pin')}}{{device.pin}}{{$t('puk')}}{{device.puk}}
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{$t('taxes.title')}}
{{$t('taxes.theader.type')}}{{$t('taxes.theader.taxBase')}}{{$t('taxes.theader.tax')}}{{$t('taxes.theader.fee')}}
{{tax.name}}{{tax.Base | currency('EUR', $i18n.locale)}}{{tax.vatPercent | percentage}}{{tax.tax | currency('EUR', $i18n.locale)}}
{{$t('subtotal')}}{{getTotalBase() | currency('EUR', $i18n.locale)}}{{getTotalTax()| currency('EUR', $i18n.locale)}}
{{$t('total')}}{{getTotal() | currency('EUR', $i18n.locale)}}
-
-
-
-
-
-
-
- -
-
{{$t('plantPassport')}}
-
-
-
-
- A - {{getBotanical()}} -
-
- B - ES17462130 -
-
- C - {{ticket.id}} -
-
- D - ES -
-
-
-
-
-
-
-
-
-
{{$t('digitalSignature')}}
-
- -
{{formatDate(signature.created, '%d-%m-%Y')}}
-
-
-
-
-
-
-

{{$t('observations')}}

-

{{ticket.description}}

+

{{$t('label1')}} +

+

+ {{$t('label2')}} +

+

+ {{$t('label3')}} +

+ +

{{$t('label4')}} +

+

{{$t('label5')}} +

-
\ No newline at end of file + diff --git a/print/templates/reports/sign-pda/sign-pda.js b/print/templates/reports/sign-pda/sign-pda.js index 50d5720ad..844982ad0 100755 --- a/print/templates/reports/sign-pda/sign-pda.js +++ b/print/templates/reports/sign-pda/sign-pda.js @@ -1,100 +1,15 @@ -const config = require(`vn-print/core/config`); const vnReport = require('../../../core/mixins/vn-report.js'); -const md5 = require('md5'); -const fs = require('fs-extra'); module.exports = { - name: 'delivery-note', + name: 'sign-pda', mixins: [vnReport], async serverPrefetch() { - this.ticket = await this.findOneFromDef('ticket', [this.id]); - this.checkMainEntity(this.ticket); - this.client = await this.findOneFromDef('client', [this.id]); - this.sales = await this.rawSqlFromDef('sales', [this.id]); - this.address = await this.findOneFromDef(`address`, [this.id]); - this.services = await this.rawSqlFromDef('services', [this.id]); - this.taxes = await this.findOneFromDef('taxes', [this.id]); - this.packagings = await this.rawSqlFromDef('packagings', [this.id]); - this.signature = await this.findOneFromDef('signature', [this.id]); - }, - data() { - return {totalBalance: 0.00}; + this.device = await this.findOneFromDef('device', [this.id]); }, + data() {}, computed: { - dmsPath() { - if (!this.signature) return; - - const hash = md5(this.signature.id.toString()).substring(0, 3); - const file = `${config.storage.root}/${hash}/${this.signature.id}.png`; - - if (!fs.existsSync(file)) return null; - - const src = fs.readFileSync(file); - const base64 = Buffer.from(src, 'utf8').toString('base64'); - - return `data:image/png;base64, ${base64}`; - }, - deliverNoteType() { - return this.type ? this.type : 'deliveryNote'; - }, - serviceTotal() { - let total = 0.00; - this.services.forEach(service => { - total += parseFloat(service.price) * service.quantity; - }); - - return total; - }, - showPrices() { - return this.deliverNoteType != 'withoutPrices'; - }, - footerType() { - const translatedType = this.$t(this.deliverNoteType); - return `${translatedType} ${this.id}`; - }, - hasObservations() { - return this.ticket.description !== null; - } }, methods: { - getSubTotal() { - let subTotal = 0.00; - this.sales.forEach(sale => { - subTotal += sale.quantity * sale.price * (1 - sale.discount / 100); - }); - - return subTotal; - }, - getTotalBase() { - let totalBase = 0.00; - this.taxes.forEach(tax => { - totalBase += parseFloat(tax.Base); - }); - - return totalBase; - }, - getTotalTax() { - let totalTax = 0.00; - this.taxes.forEach(tax => { - totalTax += parseFloat(tax.tax); - }); - - return totalTax; - }, - getTotal() { - return this.getTotalBase() + this.getTotalTax(); - }, - getBotanical() { - let phytosanitary = []; - this.sales.forEach(sale => { - if (sale.botanical) - phytosanitary.push(sale.botanical); - }); - - return phytosanitary.filter((item, index) => - phytosanitary.indexOf(item) == index - ).join(', '); - } }, props: { id: { diff --git a/print/templates/reports/sign-pda/sql/device.sql b/print/templates/reports/sign-pda/sql/device.sql new file mode 100644 index 000000000..621c9c1fc --- /dev/null +++ b/print/templates/reports/sign-pda/sql/device.sql @@ -0,0 +1 @@ +SELECT * FROM vn.deviceProduction WHERE id = 3 -- 2.40.1 From cf5cae3de09171ba7a51f4b32d0f41c635c5f962 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 22 May 2024 11:57:59 +0200 Subject: [PATCH 06/25] feat(salix): refs #5926 report(2) and minor test fix --- back/methods/docuware/specs/upload.spec.js | 8 +- back/methods/docuware/upload.js | 6 +- db/dump/fixtures.after.sql | 2 + db/dump/fixtures.before.sql | 3 +- .../11062-tealArborvitae/00-firstScript.sql | 6 +- e2e/tests.js | 13 -- modules/ticket/front/descriptor-menu/index.js | 2 +- modules/ticket/front/index/index.js | 6 +- .../back/methods/worker/docuwareUpload.js | 177 +++++++----------- print/templates/reports/sign-pda/sign-pda.js | 1 - 10 files changed, 92 insertions(+), 132 deletions(-) diff --git a/back/methods/docuware/specs/upload.spec.js b/back/methods/docuware/specs/upload.spec.js index 866499b66..7b794cbb9 100644 --- a/back/methods/docuware/specs/upload.spec.js +++ b/back/methods/docuware/specs/upload.spec.js @@ -2,9 +2,9 @@ const models = require('vn-loopback/server/server').models; describe('docuware upload()', () => { const userId = 9; - const ticketIds = [10]; + const ids = [10]; const ctx = { - args: {ticketIds}, + args: {ids}, req: { getLocale: () => { return 'en'; @@ -32,7 +32,7 @@ describe('docuware upload()', () => { const options = {transaction: tx}; const user = await models.UserConfig.findById(userId, null, options); await user.updateAttribute('tabletFk', 'Tablet1', options); - await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options); + await models.Docuware.upload(ctx, ids, fileCabinetName, options); await tx.rollback(); } catch (e) { @@ -50,7 +50,7 @@ describe('docuware upload()', () => { let error; try { const options = {transaction: tx}; - await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options); + await models.Docuware.upload(ctx, ids, fileCabinetName, options); await tx.rollback(); } catch (e) { diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 4f212e186..454f45f83 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -1,4 +1,5 @@ const UserError = require('vn-loopback/util/user-error'); +// const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethodCtx('upload', { @@ -45,7 +46,7 @@ module.exports = Self => { throw new UserError('This user does not have an assigned tablet'); const docuwareOptions = await Self.getOptions(); - const {modelFk} = await Self.getDocuware({code: fileCabinet, action}); + const {modelFk} = await Self.getDocuware({where: {code: fileCabinet, action}}); const modelIsValid = await Self.app.models.Module.findOne({where: {code: modelFk}}); if (!modelIsValid) @@ -58,6 +59,9 @@ module.exports = Self => { const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents`; + // if (!isProduction(false)) + // throw new UserError('Action not allowed on the test environment'); + await Self.app.models[model].docuwareUpload({ctx, tabletFk, ids, myOptions, uri, docuwareOptions, dialogId}); return models.TicketTracking.setDelivered(ctx, ids, myOptions); diff --git a/db/dump/fixtures.after.sql b/db/dump/fixtures.after.sql index fd940d8a6..821b2fa1f 100644 --- a/db/dump/fixtures.after.sql +++ b/db/dump/fixtures.after.sql @@ -315,4 +315,6 @@ INSERT INTO mysql.roles_mapping (`User`, `Host`, `Role`, `Admin_option`) FROM mysql.roles_mapping WHERE `User` LIKE @prefixedLike AND `Host` = @genRoleHost; +INSERT INTO vn.docuware (code,fileCabinetName,`action`,dialogName,findById,dmsTypeFk,modelFk) + VALUES ('hr','RRHH','store','Archivar','N__DOCUMENTO',3,'worker'); FLUSH PRIVILEGES; diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 18ccd79d0..da94c8c2f 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -3796,8 +3796,7 @@ UPDATE vn.locker SET workerFk = 1110 WHERE id = 147; -- Auto-generated SQL script #202405201318 UPDATE vn.userConfig SET tabletFk='Tablet1' - WHERE userFk=5; - + WHERE userFk=9; -- Auto-generated SQL script #202405201207 UPDATE vn.docuware diff --git a/db/versions/11062-tealArborvitae/00-firstScript.sql b/db/versions/11062-tealArborvitae/00-firstScript.sql index f619e8906..a245845d8 100644 --- a/db/versions/11062-tealArborvitae/00-firstScript.sql +++ b/db/versions/11062-tealArborvitae/00-firstScript.sql @@ -1,9 +1,11 @@ -- Place your SQL code here - -ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL; ALTER TABLE vn.docuware ADD modelFk VARCHAR(45) DEFAULT NULL NULL; ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) REFERENCES salix.module(code); +ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL; +-- Auto-generated SQL script #202405221107 + + -- Auto-generated SQL script #202405201951 INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES ('Worker','isPDASigned','READ','ALLOW','ROLE','hr'); diff --git a/e2e/tests.js b/e2e/tests.js index 829056f4c..4d9302921 100644 --- a/e2e/tests.js +++ b/e2e/tests.js @@ -36,20 +36,7 @@ async function test() { const jasmine = new Jasmine(); const specFiles = [ - `./e2e/paths/01*/*[sS]pec.js`, - `./e2e/paths/02*/*[sS]pec.js`, - `./e2e/paths/03*/*[sS]pec.js`, - `./e2e/paths/04*/*[sS]pec.js`, `./e2e/paths/05*/*[sS]pec.js`, - `./e2e/paths/06*/*[sS]pec.js`, - `./e2e/paths/07*/*[sS]pec.js`, - `./e2e/paths/08*/*[sS]pec.js`, - `./e2e/paths/09*/*[sS]pec.js`, - `./e2e/paths/10*/*[sS]pec.js`, - `./e2e/paths/11*/*[sS]pec.js`, - `./e2e/paths/12*/*[sS]pec.js`, - `./e2e/paths/13*/*[sS]pec.js`, - `./e2e/paths/**/*[sS]pec.js` ]; jasmine.loadConfig({ diff --git a/modules/ticket/front/descriptor-menu/index.js b/modules/ticket/front/descriptor-menu/index.js index 32f245454..38221e742 100644 --- a/modules/ticket/front/descriptor-menu/index.js +++ b/modules/ticket/front/descriptor-menu/index.js @@ -315,7 +315,7 @@ class Controller extends Section { if (!force) return this.$.pdfToTablet.show(); - return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds: [this.id]}) + return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ids: [this.id]}) .then(() => { this.vnApp.showSuccess(this.$t('PDF sent!')); }); diff --git a/modules/ticket/front/index/index.js b/modules/ticket/front/index/index.js index 489f677a3..2056e29dc 100644 --- a/modules/ticket/front/index/index.js +++ b/modules/ticket/front/index/index.js @@ -11,12 +11,12 @@ export default class Controller extends Section { sendDocuware() { const checkedTickets = this.checked; - let ticketIds = []; + let ids = []; for (let ticket of checkedTickets) - ticketIds.push(ticket.id); + ids.push(ticket.id); - return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds}) + return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ids}) .then(res => { let state = res.data; for (let ticket of checkedTickets) { diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 37602729b..7be8ccd66 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -3,115 +3,82 @@ const {models} = require('vn-loopback/server/server'); const UserError = require('vn-loopback/util/user-error'); const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { - Self.docuwareUpload = async({ctx, tabletFk, ids: workerId, myOptions, uri, docuwareOptions, dialogId}) => { - if (!Array.isArray(workerId)) workerId = [workerId]; - for (id of ticketIds) { - // get delivery note - ctx.args.id = id; - const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { - id, - type: 'deliveryNote' - }, myOptions); - // get ticket data - const ticket = await models.Ticket.findById(id, { - include: [{ - relation: 'client', - scope: { - fields: ['id', 'name', 'fi'] - } - }] - }, myOptions); + Self.docuwareUpload = async({ctx, tabletFk, ids: id, myOptions, uri, docuwareOptions, dialogId}) => { + // get delivery note + const pdaId = id[0]; + ctx.args.id = pdaId; + delete ctx.args.ids; + // get ticket data + const pda = await models.DeviceProduction.findById(pdaId, myOptions); + const user = await models.UserConfig.find({where: {tabletFk}, fields: ['userFk']}, myOptions); - // upload file - const templateJson = { - 'Fields': [ - { - 'FieldName': 'N__ALBAR_N', - 'ItemElementName': 'string', - 'Item': id, - }, - { - 'FieldName': 'CIF_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().fi, - }, - { - 'FieldName': 'CODIGO_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().id, - }, - { - 'FieldName': 'NOMBRE_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().name + ' - ' + id, - }, - { - 'FieldName': 'FECHA_FACTURA', - 'ItemElementName': 'date', - 'Item': ticket.shipped, - }, - { - 'FieldName': 'TOTAL_FACTURA', - 'ItemElementName': 'Decimal', - 'Item': ticket.totalWithVat, - }, - { - 'FieldName': 'ESTADO', - 'ItemElementName': 'string', - 'Item': 'Pendiente procesar', - }, - { - 'FieldName': 'FIRMA_', - 'ItemElementName': 'string', - 'Item': 'Si', - }, - { - 'FieldName': 'FILTRO_TABLET', - 'ItemElementName': 'string', - 'Item': tabletFk, - } - ] - }; - - if (!isProduction(false)) - throw new UserError('Action not allowed on the test environment'); - - // delete old - const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); - if (docuwareFile) { - const deleteJson = { - 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] - }; - const deleteUri = `${uri}/${docuwareFile.id}/Fields`; - await axios.put(deleteUri, deleteJson, docuwareOptions.headers); - } - - const uploadUri = `${uri}?StoreDialogId=${dialogId}`; - const FormData = require('form-data'); - const data = new FormData(); - - data.append('document', JSON.stringify(templateJson), 'schema.json'); - data.append('file[]', deliveryNote[0], 'file.pdf'); - const uploadOptions = { - headers: { - 'Content-Type': 'multipart/form-data', - 'X-File-ModifiedDate': Date.vnNew(), - 'Cookie': docuwareOptions.headers.headers.Cookie, - ...data.getHeaders() + // upload file + const signPda = await models.Worker.signPdaPdf(ctx, + pdaId + , myOptions); + const templateJson = { + 'Fields': [ + { + 'FieldName': 'N__DOCUMENTO', + 'ItemElementName': 'string', + 'Item': id, }, - }; + { + 'FieldName': 'ESTADO', + 'ItemElementName': 'string', + 'Item': 'Pendiente procesar', + }, + { + 'FieldName': 'FIRMA_', + 'ItemElementName': 'string', + 'Item': 'Si', + }, + { + 'FieldName': 'FILTRO_TABLET', + 'ItemElementName': 'string', + 'Item': tabletFk, + } + ] + }; - try { - await axios.post(uploadUri, data, uploadOptions); - } catch (err) { - const $t = ctx.req.__; - const message = $t('Failed to upload delivery note', {id}); - if (uploaded.length) - await models.TicketTracking.setDelivered(ctx, uploaded, myOptions); - throw new UserError(message); - } - uploaded.push(id); + if (!isProduction(false)) + throw new UserError('Action not allowed on the test environment'); + + // delete old + const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); + if (docuwareFile) { + const deleteJson = { + 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] + }; + const deleteUri = `${uri}/${docuwareFile.id}/Fields`; + await axios.put(deleteUri, deleteJson, docuwareOptions.headers); } + + const uploadUri = `${uri}?StoreDialogId=${dialogId}`; + const FormData = require('form-data'); + const data = new FormData(); + + data.append('document', JSON.stringify(templateJson), 'schema.json'); + data.append('file[]', signPda[0], 'file.pdf'); + const uploadOptions = { + headers: { + 'Content-Type': 'multipart/form-data', + 'X-File-ModifiedDate': Date.vnNew(), + 'Cookie': docuwareOptions.headers.headers.Cookie, + ...data.getHeaders() + }, + }; + + try { + await axios.post(uploadUri, data, uploadOptions); + } catch (err) { + const $t = ctx.req.__; + const message = $t('Failed to upload delivery note', {id}); + if (uploaded.length) + await models.TicketTracking.setDelivered(ctx, uploaded, myOptions); + throw new UserError(message); + } + uploaded.push(id); }; }; diff --git a/print/templates/reports/sign-pda/sign-pda.js b/print/templates/reports/sign-pda/sign-pda.js index 844982ad0..79d7d0148 100755 --- a/print/templates/reports/sign-pda/sign-pda.js +++ b/print/templates/reports/sign-pda/sign-pda.js @@ -6,7 +6,6 @@ module.exports = { async serverPrefetch() { this.device = await this.findOneFromDef('device', [this.id]); }, - data() {}, computed: { }, methods: { -- 2.40.1 From 294fbdcbeb1789f56fc91014b73e86c4232e67ff Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 30 May 2024 12:29:58 +0200 Subject: [PATCH 07/25] feat(salix): refs #5926 #6321 refactor docuwareUpload methods --- back/methods/docuware/core.js | 65 ++++++++++++++++ back/methods/docuware/specs/core.spec.js | 86 ++++++++++++++++++++++ back/methods/docuware/specs/upload.spec.js | 2 +- back/methods/docuware/upload.js | 10 +-- 4 files changed, 157 insertions(+), 6 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index 01e4eb4d1..9c89203bb 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -1,6 +1,51 @@ const axios = require('axios'); module.exports = Self => { + /** + * Returns templateJSON + * + * @param {string} config - The config as template upload + * @return {object} - The template parse + */ + Self.buildTemplateJSON = config => { + const templateJson = { + 'Fields': [] + }; + + templateJson.Fields = Object.keys(config).map(fieldName => ({ + 'FieldName': fieldName, + 'ItemElementName': config[fieldName].type, + 'Item': config[fieldName].value + })); + + return templateJson; + }; + /** + * Returns upload options + * + * @param {string} value - The document value + * @param {Object} configTemplate - The config as template upload + * @param {object} options - The options + * @return {object} - The options with headers + */ + Self.uploadOptions = async(value, configTemplate, options) => { + const FormData = require('form-data'); + const data = new FormData(); + const docuwareOptions = options ?? await Self.getOptions(); + const templateJson = Self.buildTemplateJSON(configTemplate); + data.append('document', JSON.stringify(templateJson), 'schema.json'); + data.append('file[]', value, 'pda.pdf'); + const uploadOptions = { + headers: { + 'Content-Type': 'multipart/form-data', + 'X-File-ModifiedDate': Date.vnNew(), + 'Cookie': docuwareOptions.headers.headers.Cookie, + ...data.getHeaders() + }, + }; + return uploadOptions; + }; + /** * Returns basic headers * @@ -139,6 +184,26 @@ module.exports = Self => { return Self.get(code, filter, parse); }; + /** + * Execute detete old docuware + * + * @param {string} id - The id + * @param {string} fileCabinet - The fieldCabinet + * @param {Object} configTemplate - The config + * @param {string} uri - The uri + * @param {Object} options - The options + */ + Self.deleteOld = async(id, fileCabinet, configTemplate, uri, options) => { + const docuwareOptions = options ?? await Self.getOptions(); + const config = configTemplate ?? {'ESTADO': {type: 'String', value: 'Pendiente eliminar'}}; + const docuwareFile = await Self.checkFile(id, fileCabinet, false); + if (docuwareFile) { + const deleteJson = Self.buildTemplateJSON(config); + + const deleteUri = `${uri}/${docuwareFile.id}/Fields`; + await axios.put(deleteUri, deleteJson, docuwareOptions.headers); + } + }; /** * Returns docuware data filtered * diff --git a/back/methods/docuware/specs/core.spec.js b/back/methods/docuware/specs/core.spec.js index cdf8a3b62..0063c9a9e 100644 --- a/back/methods/docuware/specs/core.spec.js +++ b/back/methods/docuware/specs/core.spec.js @@ -38,6 +38,92 @@ describe('Docuware core', () => { }); }); + describe('buildTemplateJSON()', () => { + it('should return buildTemplateJSON', async() => { + const config = { + 'N__DOCUMENTO': { + type: 'string', + value: '12345' + }, + 'ESTADO': { + type: 'string', + value: 'Pendiente procesar' + }, + 'FIRMA_': { + type: 'string', + value: 'Si' + }, + 'FILTRO_TABLET': { + type: 'string', + value: 'Tablet123' + } + }; + const result = await models.Docuware.buildTemplateJSON(config); + + expect(result).toEqual({ + 'Fields': [ + { + 'FieldName': 'N__DOCUMENTO', + 'ItemElementName': 'string', + 'Item': '12345', + }, + { + 'FieldName': 'ESTADO', + 'ItemElementName': 'string', + 'Item': 'Pendiente procesar', + }, + { + 'FieldName': 'FIRMA_', + 'ItemElementName': 'string', + 'Item': 'Si', + }, + { + 'FieldName': 'FILTRO_TABLET', + 'ItemElementName': 'string', + 'Item': 'Tablet123', + } + ] + }); + }); + }); + + describe('uploadOptions()', () => { + it('should return uploadOptions', async() => { + spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); + + // const dialogs = { + // data: { + // Dialog: [ + // { + // DisplayName: 'find', + // Id: 'getDialogTest' + // } + // ] + // } + // }; + // spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs))); + const result = await models.Docuware.uploadOptions(1, { + 'N__DOCUMENTO': { + type: 'string', + value: '12345' + }}); + + expect(result.headers.Cookie).toEqual(null); + expect(result.headers['Content-Type']).toEqual('multipart/form-data'); + expect(result.headers['content-type']).toMatch(/^multipart\/form-data; boundary=/); + }); + }); + + describe('deleteOld()', () => { + it('should return deleteOld', async() => { + await models.Docuware.deleteOld(1, 'deliveryNote', { + 'N__DOCUMENTO': { + type: 'string', + value: '12345' + }}); + }); + }); + describe('getFileCabinet()', () => { it('should return fileCabinetId', async() => { const code = 'deliveryNote'; diff --git a/back/methods/docuware/specs/upload.spec.js b/back/methods/docuware/specs/upload.spec.js index 7b794cbb9..3c6783e65 100644 --- a/back/methods/docuware/specs/upload.spec.js +++ b/back/methods/docuware/specs/upload.spec.js @@ -1,7 +1,7 @@ const models = require('vn-loopback/server/server').models; describe('docuware upload()', () => { - const userId = 9; + const userId = 18; const ids = [10]; const ctx = { args: {ids}, diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 454f45f83..441052be6 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -59,11 +59,11 @@ module.exports = Self => { const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents`; - // if (!isProduction(false)) - // throw new UserError('Action not allowed on the test environment'); + if (!isProduction(false)) + throw new UserError('Action not allowed on the test environment'); + const upload = {ctx, tabletFk, ids, myOptions, uri, fileCabinet, fileCabinetId, dialogId}; + await Self.app.models[model].docuwareUpload(upload); - await Self.app.models[model].docuwareUpload({ctx, tabletFk, ids, myOptions, uri, docuwareOptions, dialogId}); - - return models.TicketTracking.setDelivered(ctx, ids, myOptions); + // return models.TicketTracking.setDelivered(ctx, ids, myOptions); }; }; -- 2.40.1 From 11381258cc7adef09e4cbc94c53c0f1fa559a217 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 30 May 2024 12:31:28 +0200 Subject: [PATCH 08/25] feat(salix): refs #5926 #6321 ruse core methods --- back/methods/docuware/upload.js | 2 +- db/dump/fixtures.before.sql | 1 - .../back/methods/ticket/docuwareUpload.js | 133 +++++++++--------- .../back/methods/worker/docuwareUpload.js | 81 ++++------- 4 files changed, 96 insertions(+), 121 deletions(-) diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 441052be6..39d56c15b 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -1,5 +1,5 @@ const UserError = require('vn-loopback/util/user-error'); -// const isProduction = require('vn-loopback/server/boot/isProduction'); +const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethodCtx('upload', { diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index da9b7c9c6..9d0d9e8e0 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -3788,7 +3788,6 @@ INSERT INTO vn.workerTeam(id, team, workerFk) VALUES (8, 1, 19); -INSERT INTO vn.workCenter (id, name, payrollCenterFk, counter, warehouseFk, street, geoFk, deliveryManAdjustment) INSERT INTO vn.workCenter (id, name, payrollCenterFk, counter, warehouseFk, street, geoFk, deliveryManAdjustment) VALUES(100, 'workCenterOne', 1, NULL, 1, 'gotham', NULL, NULL); diff --git a/modules/ticket/back/methods/ticket/docuwareUpload.js b/modules/ticket/back/methods/ticket/docuwareUpload.js index 0b77b27c1..1433dcf66 100644 --- a/modules/ticket/back/methods/ticket/docuwareUpload.js +++ b/modules/ticket/back/methods/ticket/docuwareUpload.js @@ -3,7 +3,7 @@ const {models} = require('vn-loopback/server/server'); const UserError = require('vn-loopback/util/user-error'); const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { - Self.docuwareUpload = async({ctx, tabletFk, ids: ticketIds, myOptions, uri, docuwareOptions, dialogId}) => { + Self.docuwareUpload = async({ctx, tabletFk, ids: ticketIds, myOptions, uri, dialogId}) => { for (id of ticketIds) { // get delivery note ctx.args.id = id; @@ -22,83 +22,78 @@ module.exports = Self => { }, myOptions); // upload file - const templateJson = { - 'Fields': [ - { - 'FieldName': 'N__ALBAR_N', - 'ItemElementName': 'string', - 'Item': id, - }, - { - 'FieldName': 'CIF_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().fi, - }, - { - 'FieldName': 'CODIGO_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().id, - }, - { - 'FieldName': 'NOMBRE_PROVEEDOR', - 'ItemElementName': 'string', - 'Item': ticket.client().name + ' - ' + id, - }, - { - 'FieldName': 'FECHA_FACTURA', - 'ItemElementName': 'date', - 'Item': ticket.shipped, - }, - { - 'FieldName': 'TOTAL_FACTURA', - 'ItemElementName': 'Decimal', - 'Item': ticket.totalWithVat, - }, - { - 'FieldName': 'ESTADO', - 'ItemElementName': 'string', - 'Item': 'Pendiente procesar', - }, - { - 'FieldName': 'FIRMA_', - 'ItemElementName': 'string', - 'Item': 'Si', - }, - { - 'FieldName': 'FILTRO_TABLET', - 'ItemElementName': 'string', - 'Item': tabletFk, - } - ] + const configTemplate = { + + 'N__ALBAR_N': { + type: 'string', + value: id, + }, + 'CIF_PROVEEDOR': { + type: 'string', + value: ticket.client().fi, + }, + 'CODIGO_PROVEEDOR': { + type: 'string', + value: ticket.client().id, + }, + 'NOMBRE_PROVEEDOR': { + type: 'string', + value: ticket.client().name + ' - ' + id, + }, + 'FECHA_FACTURA': { + type: 'date', + value: ticket.shipped, + }, + 'TOTAL_FACTURA': { + type: 'Decimal', + value: ticket.totalWithVat, + }, + 'ESTADO': { + type: 'string', + value: 'Pendiente procesar', + }, + 'FIRMA_': { + type: 'string', + value: 'Si', + }, + 'FILTRO_TABLET': { + type: 'string', + value: tabletFk, + } + }; if (!isProduction(false)) throw new UserError('Action not allowed on the test environment'); // delete old - const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); - if (docuwareFile) { - const deleteJson = { - 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] - }; - const deleteUri = `${uri}/${docuwareFile.id}/Fields`; - await axios.put(deleteUri, deleteJson, docuwareOptions.headers); - } + await models.Docuware.deleteOld(id, fileCabinet, uri); + + // const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); + // if (docuwareFile) { + // const deleteJson = { + // 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] + // }; + // const deleteUri = `${uri}/${docuwareFile.id}/Fields`; + // await axios.put(deleteUri, deleteJson, docuwareOptions.headers); + // } const uploadUri = `${uri}?StoreDialogId=${dialogId}`; - const FormData = require('form-data'); - const data = new FormData(); + const uploadOptions = models.Docuware.uploadOptions(deliveryNote[0], configTemplate); - data.append('document', JSON.stringify(templateJson), 'schema.json'); - data.append('file[]', deliveryNote[0], 'file.pdf'); - const uploadOptions = { - headers: { - 'Content-Type': 'multipart/form-data', - 'X-File-ModifiedDate': Date.vnNew(), - 'Cookie': docuwareOptions.headers.headers.Cookie, - ...data.getHeaders() - }, - }; + // const FormData = require('form-data'); + // const data = new FormData(); + + // data.append('document', JSON.stringify(templateJson), 'schema.json'); + // data.append('file[]', deliveryNote[0], 'file.pdf'); + // const uploadOptions = { + // headers: { + // 'Content-Type': 'multipart/form-data', + // 'X-File-ModifiedDate': Date.vnNew(), + // 'Cookie': docuwareOptions.headers.headers.Cookie, + // ...data.getHeaders() + // }, + // }; try { await axios.post(uploadUri, data, uploadOptions); diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 7be8ccd66..24afa8465 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -3,79 +3,60 @@ const {models} = require('vn-loopback/server/server'); const UserError = require('vn-loopback/util/user-error'); const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { - Self.docuwareUpload = async({ctx, tabletFk, ids: id, myOptions, uri, docuwareOptions, dialogId}) => { + Self.docuwareUpload = async({ctx, tabletFk, ids: id, myOptions, uri, fileCabinet, dialogId}) => { // get delivery note const pdaId = id[0]; ctx.args.id = pdaId; delete ctx.args.ids; - // get ticket data - const pda = await models.DeviceProduction.findById(pdaId, myOptions); - const user = await models.UserConfig.find({where: {tabletFk}, fields: ['userFk']}, myOptions); + // // get ticket data + // const pda = await models.DeviceProduction.findById(pdaId, myOptions); + // const user = await models.UserConfig.find({where: {tabletFk}, fields: ['userFk']}, myOptions); // upload file const signPda = await models.Worker.signPdaPdf(ctx, pdaId , myOptions); - const templateJson = { - 'Fields': [ - { - 'FieldName': 'N__DOCUMENTO', - 'ItemElementName': 'string', - 'Item': id, - }, - { - 'FieldName': 'ESTADO', - 'ItemElementName': 'string', - 'Item': 'Pendiente procesar', - }, - { - 'FieldName': 'FIRMA_', - 'ItemElementName': 'string', - 'Item': 'Si', - }, - { - 'FieldName': 'FILTRO_TABLET', - 'ItemElementName': 'string', - 'Item': tabletFk, - } - ] + const configTemplate = { + 'N__DOCUMENTO': { + type: 'String', + value: pdaId + }, + 'ESTADO': { + type: 'String', + value: 'Pendiente procesar' + }, + 'FIRMA_': { + type: 'String', + value: 'Si' + }, + 'FILTRO_TABLET': { + type: 'String', + value: tabletFk + } }; if (!isProduction(false)) throw new UserError('Action not allowed on the test environment'); // delete old - const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); - if (docuwareFile) { - const deleteJson = { - 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] - }; - const deleteUri = `${uri}/${docuwareFile.id}/Fields`; - await axios.put(deleteUri, deleteJson, docuwareOptions.headers); - } + await models.Docuware.deleteOld(id, fileCabinet, uri); + // const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); + // if (docuwareFile) { + // const deleteJson = { + // 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] + // }; + // const deleteUri = `${uri}/${docuwareFile.id}/Fields`; + // await axios.put(deleteUri, deleteJson, docuwareOptions.headers); + // } const uploadUri = `${uri}?StoreDialogId=${dialogId}`; - const FormData = require('form-data'); - const data = new FormData(); - - data.append('document', JSON.stringify(templateJson), 'schema.json'); - data.append('file[]', signPda[0], 'file.pdf'); - const uploadOptions = { - headers: { - 'Content-Type': 'multipart/form-data', - 'X-File-ModifiedDate': Date.vnNew(), - 'Cookie': docuwareOptions.headers.headers.Cookie, - ...data.getHeaders() - }, - }; + const uploadOptions = models.Docuware.uploadOptions(signPda[0], configTemplate); try { await axios.post(uploadUri, data, uploadOptions); } catch (err) { const $t = ctx.req.__; const message = $t('Failed to upload delivery note', {id}); - if (uploaded.length) - await models.TicketTracking.setDelivered(ctx, uploaded, myOptions); throw new UserError(message); } uploaded.push(id); -- 2.40.1 From 2072a3f4040e29a68694ffe44f177d34c161ea3c Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 3 Jun 2024 14:16:19 +0200 Subject: [PATCH 09/25] feat(salix): refs #5926 #5926 Remove unnecessary code --- .../back/methods/ticket/docuwareUpload.js | 25 +-------- .../back/methods/worker/docuwareUpload.js | 14 +---- .../worker/back/methods/worker/signedPDA.js | 54 ------------------- modules/worker/back/models/worker.js | 1 - 4 files changed, 3 insertions(+), 91 deletions(-) delete mode 100644 modules/worker/back/methods/worker/signedPDA.js diff --git a/modules/ticket/back/methods/ticket/docuwareUpload.js b/modules/ticket/back/methods/ticket/docuwareUpload.js index 1433dcf66..d34e71277 100644 --- a/modules/ticket/back/methods/ticket/docuwareUpload.js +++ b/modules/ticket/back/methods/ticket/docuwareUpload.js @@ -11,6 +11,7 @@ module.exports = Self => { id, type: 'deliveryNote' }, myOptions); + // get ticket data const ticket = await models.Ticket.findById(id, { include: [{ @@ -23,7 +24,6 @@ module.exports = Self => { // upload file const configTemplate = { - 'N__ALBAR_N': { type: 'string', value: id, @@ -69,32 +69,9 @@ module.exports = Self => { // delete old await models.Docuware.deleteOld(id, fileCabinet, uri); - // const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); - // if (docuwareFile) { - // const deleteJson = { - // 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] - // }; - // const deleteUri = `${uri}/${docuwareFile.id}/Fields`; - // await axios.put(deleteUri, deleteJson, docuwareOptions.headers); - // } - const uploadUri = `${uri}?StoreDialogId=${dialogId}`; const uploadOptions = models.Docuware.uploadOptions(deliveryNote[0], configTemplate); - // const FormData = require('form-data'); - // const data = new FormData(); - - // data.append('document', JSON.stringify(templateJson), 'schema.json'); - // data.append('file[]', deliveryNote[0], 'file.pdf'); - // const uploadOptions = { - // headers: { - // 'Content-Type': 'multipart/form-data', - // 'X-File-ModifiedDate': Date.vnNew(), - // 'Cookie': docuwareOptions.headers.headers.Cookie, - // ...data.getHeaders() - // }, - // }; - try { await axios.post(uploadUri, data, uploadOptions); } catch (err) { diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 24afa8465..58aa58ce5 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -4,18 +4,16 @@ const UserError = require('vn-loopback/util/user-error'); const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.docuwareUpload = async({ctx, tabletFk, ids: id, myOptions, uri, fileCabinet, dialogId}) => { - // get delivery note const pdaId = id[0]; + ctx.args.id = pdaId; delete ctx.args.ids; - // // get ticket data - // const pda = await models.DeviceProduction.findById(pdaId, myOptions); - // const user = await models.UserConfig.find({where: {tabletFk}, fields: ['userFk']}, myOptions); // upload file const signPda = await models.Worker.signPdaPdf(ctx, pdaId , myOptions); + const configTemplate = { 'N__DOCUMENTO': { type: 'String', @@ -40,14 +38,6 @@ module.exports = Self => { // delete old await models.Docuware.deleteOld(id, fileCabinet, uri); - // const docuwareFile = await models.Docuware.checkFile(id, fileCabinet, false); - // if (docuwareFile) { - // const deleteJson = { - // 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] - // }; - // const deleteUri = `${uri}/${docuwareFile.id}/Fields`; - // await axios.put(deleteUri, deleteJson, docuwareOptions.headers); - // } const uploadUri = `${uri}?StoreDialogId=${dialogId}`; const uploadOptions = models.Docuware.uploadOptions(signPda[0], configTemplate); diff --git a/modules/worker/back/methods/worker/signedPDA.js b/modules/worker/back/methods/worker/signedPDA.js deleted file mode 100644 index 5080b84d5..000000000 --- a/modules/worker/back/methods/worker/signedPDA.js +++ /dev/null @@ -1,54 +0,0 @@ -const UserError = require('vn-loopback/util/user-error'); - -module.exports = Self => { - Self.remoteMethodCtx('isPDASigned', { - description: 'Deallocate the PDA of the worker', - accepts: [{ - arg: 'ctx', - type: 'Object', - http: {source: 'context'} - }, { - arg: 'id', - type: 'number', - required: true, - description: 'The worker id', - http: {source: 'path'} - }, { - arg: 'pda', - type: 'number', - required: true, - description: 'The pda id' - }], - returns: { - type: ['object'], - root: true - }, - http: { - path: '/:id/:pda/isPDASigned', - verb: 'GET' - } - }); - - Self.isPDASigned = async(ctx, options) => { - const models = Self.app.models; - const args = ctx.args; - let tx; - const myOptions = {}; - - if (typeof options == 'object') - Object.assign(myOptions, options); - - if (!myOptions.transaction) { - tx = await Self.beginTransaction({}); - myOptions.transaction = tx; - } - - try { - const pda = await models.DeviceProduction.findById(args.pda, myOptions); - if (pda.stateFk != 'idle') throw new UserError(`The PDA state is not idle`); - } catch (e) { - if (tx) await tx.rollback(); - throw e; - } - }; -}; diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 4e8b22f20..efeac6819 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -18,7 +18,6 @@ module.exports = Self => { require('../methods/worker/new')(Self); require('../methods/worker/deallocatePDA')(Self); require('../methods/worker/allocatePDA')(Self); - require('../methods/worker/signedPDA')(Self); require('../methods/worker/signPdaPdf')(Self); require('../methods/worker/search')(Self); require('../methods/worker/isAuthorized')(Self); -- 2.40.1 From 0ffbe75a015194dd5935be226c31346a04288cda Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 6 Jun 2024 15:36:19 +0200 Subject: [PATCH 10/25] feat(salix): refs #5926 #5926 updates --- back/methods/docuware/core.js | 2 +- back/methods/docuware/upload.js | 13 +++++++------ db/dump/fixtures.before.sql | 14 +++++++++++--- .../11062-tealArborvitae/00-firstScript.sql | 7 ++++--- .../ticket/back/methods/ticket/docuwareUpload.js | 2 +- .../worker/back/methods/worker/docuwareUpload.js | 6 +++--- 6 files changed, 27 insertions(+), 17 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index 9c89203bb..859166267 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -43,7 +43,7 @@ module.exports = Self => { ...data.getHeaders() }, }; - return uploadOptions; + return {data, uploadOptions}; }; /** diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 39d56c15b..019feea99 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -46,7 +46,8 @@ module.exports = Self => { throw new UserError('This user does not have an assigned tablet'); const docuwareOptions = await Self.getOptions(); - const {modelFk} = await Self.getDocuware({where: {code: fileCabinet, action}}); + const docuware = await Self.getDocuware({where: {code: fileCabinet, action}}); + const {modelFk, fileCabinetName} = docuware; const modelIsValid = await Self.app.models.Module.findOne({where: {code: modelFk}}); if (!modelIsValid) @@ -54,13 +55,13 @@ module.exports = Self => { const model = modelFk.replace(/^.{1}/g, modelFk[0].toUpperCase()); - const fileCabinetId = await Self.getFileCabinet(fileCabinet); - const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId); + const fileCabinetId = await Self.getFileCabinet(fileCabinetName); + const dialogId = await Self.getDialog(fileCabinetName, action, fileCabinetId); - const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents`; + const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetName}/Documents`; - if (!isProduction(false)) - throw new UserError('Action not allowed on the test environment'); + // if (!isProduction(false)) + // throw new UserError('Action not allowed on the test environment'); const upload = {ctx, tabletFk, ids, myOptions, uri, fileCabinet, fileCabinetId, dialogId}; await Self.app.models[model].docuwareUpload(upload); diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index efd0874ec..59d739997 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -2748,7 +2748,7 @@ INSERT INTO `bs`.`sale` (`saleFk`, `amount`, `dated`, `typeFk`, `clientFk`) INSERT INTO `vn`.`docuwareConfig` (`id`, `url`) VALUES - (1, 'http://docuware.url/'); + (1, 'http://docuware.url'); INSERT INTO `vn`.`calendarHolidaysName` (`id`, `name`) VALUES @@ -3101,6 +3101,11 @@ INSERT INTO `vn`.`docuwareTablet` (`tablet`,`description`) ('Tablet1','Jarvis tablet'), ('Tablet2','Avengers tablet'); +-- Auto-generated SQL script #202406060955 +INSERT INTO `vn`.`docuwareTablet` (`tablet`,`description`,`fileCabinet`) + VALUES ('Tablet4','Docuware Tablet','RRHH'); + + INSERT INTO `vn`.`sms` (`id`, `senderFk`, `sender`, `destination`, `message`, `statusCode`, `status`, `created`) VALUES (1, 66, '111111111', '0001111111111', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'OK', util.VN_CURDATE()), (2, 66, '222222222', '0002222222222', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', 0, 'PENDING', util.VN_CURDATE()), @@ -3829,8 +3834,8 @@ INSERT INTO `vn`.`ledgerConfig` SET -- Auto-generated SQL script #202405201318 UPDATE vn.userConfig - SET tabletFk='Tablet1' - WHERE userFk=9; + SET tabletFk='Tablet4' + WHERE userFk=5; -- Auto-generated SQL script #202405201207 UPDATE vn.docuware @@ -3884,3 +3889,6 @@ INSERT INTO vn.sectorCollectionSaleGroup SET id = 9, sectorCollectionFk = 3, saleGroupFk = 6; +-- Auto-generated SQL script #202406061004 +INSERT INTO vn.userConfig (userFk,warehouseFk,companyFk,darkMode,tabletFk) + VALUES (37,1,442,1,'Tablet4'); diff --git a/db/versions/11062-tealArborvitae/00-firstScript.sql b/db/versions/11062-tealArborvitae/00-firstScript.sql index a245845d8..39f9f7294 100644 --- a/db/versions/11062-tealArborvitae/00-firstScript.sql +++ b/db/versions/11062-tealArborvitae/00-firstScript.sql @@ -6,8 +6,9 @@ ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL; -- Auto-generated SQL script #202405221107 --- Auto-generated SQL script #202405201951 -INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) - VALUES ('Worker','isPDASigned','READ','ALLOW','ROLE','hr'); INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES ('Worker','signPdaPdf','READ','ALLOW','ROLE','hr'); + +-- Auto-generated SQL script #202406061008 +INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) + VALUES ('Docuware','upload','WRITE','ALLOW','ROLE','hr'); diff --git a/modules/ticket/back/methods/ticket/docuwareUpload.js b/modules/ticket/back/methods/ticket/docuwareUpload.js index d34e71277..86f5efd92 100644 --- a/modules/ticket/back/methods/ticket/docuwareUpload.js +++ b/modules/ticket/back/methods/ticket/docuwareUpload.js @@ -70,7 +70,7 @@ module.exports = Self => { await models.Docuware.deleteOld(id, fileCabinet, uri); const uploadUri = `${uri}?StoreDialogId=${dialogId}`; - const uploadOptions = models.Docuware.uploadOptions(deliveryNote[0], configTemplate); + const {data, uploadOptions} = models.Docuware.uploadOptions(deliveryNote[0], configTemplate); try { await axios.post(uploadUri, data, uploadOptions); diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 58aa58ce5..588fecec4 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -33,14 +33,14 @@ module.exports = Self => { } }; - if (!isProduction(false)) - throw new UserError('Action not allowed on the test environment'); + // if (!isProduction(false)) + // throw new UserError('Action not allowed on the test environment'); // delete old await models.Docuware.deleteOld(id, fileCabinet, uri); const uploadUri = `${uri}?StoreDialogId=${dialogId}`; - const uploadOptions = models.Docuware.uploadOptions(signPda[0], configTemplate); + const {data, uploadOptions} = await models.Docuware.uploadOptions(signPda[0], configTemplate); try { await axios.post(uploadUri, data, uploadOptions); -- 2.40.1 From c919c99aaf42cd22cfc596edfd52d42f7e612931 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 6 Jun 2024 15:55:58 +0200 Subject: [PATCH 11/25] fix: refs #5926 remove validation --- back/methods/docuware/core.js | 9 ++------- back/methods/docuware/upload.js | 8 ++++---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index 859166267..d3e8373a9 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -87,9 +87,6 @@ module.exports = Self => { * @return {number} - The fileCabinet id */ Self.getDialog = async(code, action, fileCabinetId) => { - if (!process.env.NODE_ENV) - return Math.floor(Math.random() + 100); - const docuwareInfo = await Self.getDocuware({ where: { code, @@ -114,8 +111,8 @@ module.exports = Self => { * @return {number} - The fileCabinet id */ Self.getFileCabinet = async code => { - if (!process.env.NODE_ENV) - return Math.floor(Math.random() + 100); + // if (!process.env.NODE_ENV) + // return Math.floor(Math.random() + 100); const options = await Self.getOptions(); const docuwareInfo = await Self.app.models.Docuware.findOne({ @@ -140,8 +137,6 @@ module.exports = Self => { * @return {object} - The data */ Self.get = async(code, filter, parse) => { - if (!process.env.NODE_ENV) return; - const options = await Self.getOptions(); const fileCabinetId = await Self.getFileCabinet(code); const dialogId = await Self.getDialog(code, 'find', fileCabinetId); diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 019feea99..5be0120a7 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -47,7 +47,7 @@ module.exports = Self => { const docuwareOptions = await Self.getOptions(); const docuware = await Self.getDocuware({where: {code: fileCabinet, action}}); - const {modelFk, fileCabinetName} = docuware; + const {modelFk} = docuware; const modelIsValid = await Self.app.models.Module.findOne({where: {code: modelFk}}); if (!modelIsValid) @@ -55,10 +55,10 @@ module.exports = Self => { const model = modelFk.replace(/^.{1}/g, modelFk[0].toUpperCase()); - const fileCabinetId = await Self.getFileCabinet(fileCabinetName); - const dialogId = await Self.getDialog(fileCabinetName, action, fileCabinetId); + const fileCabinetId = await Self.getFileCabinet(fileCabinet); + const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId); - const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetName}/Documents`; + const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents`; // if (!isProduction(false)) // throw new UserError('Action not allowed on the test environment'); -- 2.40.1 From f34ab37afb457499efcaa4be1cf4a490db668c37 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 7 Jun 2024 01:59:39 +0200 Subject: [PATCH 12/25] feat(salix): refs #5926 #5926 update report from TPV --- .../back/methods/worker/docuwareUpload.js | 2 +- .../worker/back/methods/worker/signPdaPdf.js | 25 +++++++---- .../templates/reports/sign-pda/locale/es.yml | 12 +++-- .../templates/reports/sign-pda/sign-pda.html | 44 ++++++++----------- print/templates/reports/sign-pda/sign-pda.js | 8 +++- .../templates/reports/sign-pda/sql/device.sql | 2 +- .../templates/reports/sign-pda/sql/worker.sql | 1 + 7 files changed, 53 insertions(+), 41 deletions(-) create mode 100644 print/templates/reports/sign-pda/sql/worker.sql diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 588fecec4..dc1a7c778 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -11,7 +11,7 @@ module.exports = Self => { // upload file const signPda = await models.Worker.signPdaPdf(ctx, - pdaId + pdaId, workerFk , myOptions); const configTemplate = { diff --git a/modules/worker/back/methods/worker/signPdaPdf.js b/modules/worker/back/methods/worker/signPdaPdf.js index e0f075af4..801b94379 100644 --- a/modules/worker/back/methods/worker/signPdaPdf.js +++ b/modules/worker/back/methods/worker/signPdaPdf.js @@ -2,13 +2,22 @@ module.exports = Self => { Self.remoteMethodCtx('signPdaPdf', { description: 'Print pdf to sign PDA', - accepts: [{ - arg: 'id', - type: 'number', - required: true, - description: 'The pda id', - http: {source: 'path'} - }], + accepts: [ + { + arg: 'id', + type: 'number', + required: true, + description: 'The pda id', + http: {source: 'path'} + }, + { + arg: 'workerFk', + type: 'number', + required: true, + description: 'The worker id', + http: {source: 'path'} + } + ], returns: [ { arg: 'body', @@ -25,7 +34,7 @@ module.exports = Self => { } ], http: { - path: '/:id/sign-pda-pdf', + path: '/:id/:workerFk/sign-pda-pdf', verb: 'GET' }, accessScopes: ['DEFAULT', 'read:multimedia'] diff --git a/print/templates/reports/sign-pda/locale/es.yml b/print/templates/reports/sign-pda/locale/es.yml index d43479cc3..9a9ca991b 100644 --- a/print/templates/reports/sign-pda/locale/es.yml +++ b/print/templates/reports/sign-pda/locale/es.yml @@ -1,14 +1,18 @@ reportName: pda signNote: Recepción PDA date: Fecha -deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional. pdaModel: PDA Modelo pdaSerie: Serie PDA sim: sim vodafone pin: pin puk: puk -label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. -label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. -label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: La empresa prohibe el uso de accesorios no originales. +label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +sign1: Recibe, D/Dña +sign2: DNI +sign: FIRMA diff --git a/print/templates/reports/sign-pda/sign-pda.html b/print/templates/reports/sign-pda/sign-pda.html index 1ec7c4964..5578aeeaa 100644 --- a/print/templates/reports/sign-pda/sign-pda.html +++ b/print/templates/reports/sign-pda/sign-pda.html @@ -8,31 +8,8 @@
{{$t('date')}}:{{formatDate(new Date(), '%d-%m-%Y')}}

- {{$t('deviceRecieved')}}: + {{$t('deviceRecieved',{modelFk: device.modelFk, serialNumber: device.serialNumber})}}:

- - - - - - - - - - - - - - - - - - - - - -
{{$t('pdaModel')}}{{device.modelFk}}
{{$t('pdaSerie')}}{{device.serialNumber}}
{{$t('sim')}}{{device.sim}}
{{$t('pin')}}{{device.pin}}{{$t('puk')}}{{device.puk}}
-

{{$t('label1')}}

@@ -42,9 +19,24 @@ {{$t('label3')}}

-

{{$t('label4')}} +

+ {{$t('label4')}}

-

{{$t('label5')}} +

+ {{$t('label5')}} +

+

+ {{$t('label6')}} +

+ +

+ {{$t('sign1')}} {{worker.firstName}} {{ worker.lastName}} +

+

+ {{$t('sign2')}} {{worker.fi}} +

+

+ {{$t('sign')}}

diff --git a/print/templates/reports/sign-pda/sign-pda.js b/print/templates/reports/sign-pda/sign-pda.js index 79d7d0148..8c2872b99 100755 --- a/print/templates/reports/sign-pda/sign-pda.js +++ b/print/templates/reports/sign-pda/sign-pda.js @@ -5,6 +5,7 @@ module.exports = { mixins: [vnReport], async serverPrefetch() { this.device = await this.findOneFromDef('device', [this.id]); + this.worker = await this.findOneFromDef('worker', [this.workerFk]); }, computed: { }, @@ -14,7 +15,12 @@ module.exports = { id: { type: Number, required: true, - description: 'The ticket id' + description: 'The device id' + }, + workerFk: { + type: Number, + required: true, + description: 'The worker id' }, type: { type: String, diff --git a/print/templates/reports/sign-pda/sql/device.sql b/print/templates/reports/sign-pda/sql/device.sql index 621c9c1fc..42a7553de 100644 --- a/print/templates/reports/sign-pda/sql/device.sql +++ b/print/templates/reports/sign-pda/sql/device.sql @@ -1 +1 @@ -SELECT * FROM vn.deviceProduction WHERE id = 3 +SELECT * FROM vn.deviceProduction WHERE id = ? diff --git a/print/templates/reports/sign-pda/sql/worker.sql b/print/templates/reports/sign-pda/sql/worker.sql new file mode 100644 index 000000000..0f78a23e7 --- /dev/null +++ b/print/templates/reports/sign-pda/sql/worker.sql @@ -0,0 +1 @@ +SELECT * FROM vn.worker WHERE id = ? -- 2.40.1 From ee2b03cabf94bb9498cd7967a3f7ae6d4c8998c3 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 7 Jun 2024 01:59:50 +0200 Subject: [PATCH 13/25] feat(salix): refs #5926 #5926 update docuware.core --- back/methods/docuware/core.js | 20 ++++++++++++++++---- back/methods/docuware/download.js | 2 +- back/methods/docuware/upload.js | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index d3e8373a9..7db5836f0 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -25,16 +25,17 @@ module.exports = Self => { * * @param {string} value - The document value * @param {Object} configTemplate - The config as template upload + * @param {string} fileName - The document fileName with extension * @param {object} options - The options * @return {object} - The options with headers */ - Self.uploadOptions = async(value, configTemplate, options) => { + Self.uploadOptions = async(value, configTemplate, fileName = '', options) => { const FormData = require('form-data'); const data = new FormData(); const docuwareOptions = options ?? await Self.getOptions(); const templateJson = Self.buildTemplateJSON(configTemplate); data.append('document', JSON.stringify(templateJson), 'schema.json'); - data.append('file[]', value, 'pda.pdf'); + data.append('file[]', value, fileName); const uploadOptions = { headers: { 'Content-Type': 'multipart/form-data', @@ -77,6 +78,17 @@ module.exports = Self => { Self.getDocuware = async filter => { return await Self.app.models.Docuware.findOne(filter); }; + /** + * Returns the base url + * + * @param {object} options - The docuware options + * @param {string} fileCabinetId - The fileCabinetId to use in findOne method + * @param {string} model - The model to use in findOne method + * @return {string} - The doware record + */ + Self.baseURL = (options, fileCabinetId, model) => { + return `${options.url}/FileCabinets/${fileCabinetId}/${model}`; + }; /** * Returns the dialog id @@ -97,7 +109,7 @@ module.exports = Self => { const options = await Self.getOptions(); - const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers); + const response = await axios.get(Self.baseURL(options, fileCabinetId, 'dialog'), options.headers); const dialogs = response.data.Dialog; const dialogId = dialogs.find(dialogs => dialogs.DisplayName === docuwareInfo.dialogName).Id; @@ -142,7 +154,7 @@ module.exports = Self => { const dialogId = await Self.getDialog(code, 'find', fileCabinetId); const data = await axios.post( - `${options.url}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`, + `${Self.baseURL(options, fileCabinetId)}/Query/DialogExpression?dialogId=${dialogId}`, filter, options.headers ); diff --git a/back/methods/docuware/download.js b/back/methods/docuware/download.js index eb575236d..83f3ba919 100644 --- a/back/methods/docuware/download.js +++ b/back/methods/docuware/download.js @@ -58,7 +58,7 @@ module.exports = Self => { const fileName = `filename="${id}.pdf"`; const contentType = 'application/pdf'; - const downloadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/FileDownload?targetFileType=Auto&keepAnnotations=false`; + const downloadUri = `${Self.baseURL(options, fileCabinetId)}Documents/${docuwareFile.id}/FileDownload?targetFileType=Auto&keepAnnotations=false`; const stream = await axios.get(downloadUri, options.headers); diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 5be0120a7..cddd8aff1 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -58,7 +58,7 @@ module.exports = Self => { const fileCabinetId = await Self.getFileCabinet(fileCabinet); const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId); - const uri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents`; + const uri = Self.baseURL(docuwareOptions, fileCabinetId, 'Documents'); // if (!isProduction(false)) // throw new UserError('Action not allowed on the test environment'); -- 2.40.1 From e3679c8b9ace3bd85753f6f2ff9de7c360d6ed5b Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 7 Jun 2024 02:00:10 +0200 Subject: [PATCH 14/25] feat(salix): refs #5926 #5926 update docuwareUpload models --- back/methods/docuware/specs/core.spec.js | 2 +- modules/ticket/back/methods/ticket/docuwareUpload.js | 5 +++-- modules/worker/back/methods/worker/docuwareUpload.js | 12 +++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/back/methods/docuware/specs/core.spec.js b/back/methods/docuware/specs/core.spec.js index 0063c9a9e..60d1253ad 100644 --- a/back/methods/docuware/specs/core.spec.js +++ b/back/methods/docuware/specs/core.spec.js @@ -106,7 +106,7 @@ describe('Docuware core', () => { 'N__DOCUMENTO': { type: 'string', value: '12345' - }}); + }}, 'test.pdf'); expect(result.headers.Cookie).toEqual(null); expect(result.headers['Content-Type']).toEqual('multipart/form-data'); diff --git a/modules/ticket/back/methods/ticket/docuwareUpload.js b/modules/ticket/back/methods/ticket/docuwareUpload.js index 86f5efd92..7c722c17d 100644 --- a/modules/ticket/back/methods/ticket/docuwareUpload.js +++ b/modules/ticket/back/methods/ticket/docuwareUpload.js @@ -4,12 +4,13 @@ const UserError = require('vn-loopback/util/user-error'); const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.docuwareUpload = async({ctx, tabletFk, ids: ticketIds, myOptions, uri, dialogId}) => { + const type = 'deliveryNote'; for (id of ticketIds) { // get delivery note ctx.args.id = id; const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { id, - type: 'deliveryNote' + type }, myOptions); // get ticket data @@ -70,7 +71,7 @@ module.exports = Self => { await models.Docuware.deleteOld(id, fileCabinet, uri); const uploadUri = `${uri}?StoreDialogId=${dialogId}`; - const {data, uploadOptions} = models.Docuware.uploadOptions(deliveryNote[0], configTemplate); + const {data, uploadOptions} = models.Docuware.uploadOptions(deliveryNote[0], configTemplate, `${type}.pdf`); try { await axios.post(uploadUri, data, uploadOptions); diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index dc1a7c778..8ae9c89ed 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -10,6 +10,15 @@ module.exports = Self => { delete ctx.args.ids; // upload file + const workerFk = await models.DeviceProductionUser.findOne( + { + fields: ['userFk'], + where: {deviceProductionFk: pdaId} + } + , myOptions); + // const worker = await models.Worker.findById( + // workerFk + // , myOptions); const signPda = await models.Worker.signPdaPdf(ctx, pdaId, workerFk , myOptions); @@ -40,7 +49,8 @@ module.exports = Self => { await models.Docuware.deleteOld(id, fileCabinet, uri); const uploadUri = `${uri}?StoreDialogId=${dialogId}`; - const {data, uploadOptions} = await models.Docuware.uploadOptions(signPda[0], configTemplate); + const fileName = `assign${pdaId}Pda${workerFk}.pdf`; + const {data, uploadOptions} = await models.Docuware.uploadOptions(signPda[0], configTemplate, fileName); try { await axios.post(uploadUri, data, uploadOptions); -- 2.40.1 From 8b35c32583e3d71cf5ee0a39f3c3ec9f22738c83 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 10 Jun 2024 09:54:20 +0200 Subject: [PATCH 15/25] feat(salix): refs #5926 check if device is available --- back/methods/docuware/core.js | 41 +++++++++++++------ back/methods/docuware/specs/core.spec.js | 24 ++++++++++- back/methods/docuware/upload.js | 4 +- .../back/methods/ticket/docuwareUpload.js | 2 +- .../back/methods/worker/docuwareUpload.js | 8 ++-- 5 files changed, 56 insertions(+), 23 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index 7db5836f0..01a3683f7 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -1,21 +1,36 @@ const axios = require('axios'); - +const env = process.env.NODE_ENV; +const {existsSync} = require('fs'); module.exports = Self => { /** * Returns templateJSON * - * @param {string} config - The config as template upload + * @param {object} config - The path for real config file + * @return {boolean} - The template parse + */ + Self.hasDeviceReady = config => { + let isDeviceConfigured = false; + + if (!config) + config = existsSync(`../docuware.${env}.json`) ?? {}; + isDeviceConfigured = !!config?.device; + return isDeviceConfigured; + }; + /** + * Returns templateJSON + * + * @param {string} fields - The config as template upload * @return {object} - The template parse */ - Self.buildTemplateJSON = config => { + Self.buildTemplateJSON = fields => { const templateJson = { 'Fields': [] }; - templateJson.Fields = Object.keys(config).map(fieldName => ({ + templateJson.Fields = Object.keys(fields).map(fieldName => ({ 'FieldName': fieldName, - 'ItemElementName': config[fieldName].type, - 'Item': config[fieldName].value + 'ItemElementName': fields[fieldName].type, + 'Item': fields[fieldName].value })); return templateJson; @@ -24,16 +39,16 @@ module.exports = Self => { * Returns upload options * * @param {string} value - The document value - * @param {Object} configTemplate - The config as template upload + * @param {Object} template - The config as template upload * @param {string} fileName - The document fileName with extension * @param {object} options - The options * @return {object} - The options with headers */ - Self.uploadOptions = async(value, configTemplate, fileName = '', options) => { + Self.uploadOptions = async(value, template, fileName = '', options) => { const FormData = require('form-data'); const data = new FormData(); const docuwareOptions = options ?? await Self.getOptions(); - const templateJson = Self.buildTemplateJSON(configTemplate); + const templateJson = Self.buildTemplateJSON(template); data.append('document', JSON.stringify(templateJson), 'schema.json'); data.append('file[]', value, fileName); const uploadOptions = { @@ -196,16 +211,16 @@ module.exports = Self => { * * @param {string} id - The id * @param {string} fileCabinet - The fieldCabinet - * @param {Object} configTemplate - The config + * @param {Object} template - The config * @param {string} uri - The uri * @param {Object} options - The options */ - Self.deleteOld = async(id, fileCabinet, configTemplate, uri, options) => { + Self.deleteOld = async(id, fileCabinet, template, uri, options) => { const docuwareOptions = options ?? await Self.getOptions(); - const config = configTemplate ?? {'ESTADO': {type: 'String', value: 'Pendiente eliminar'}}; + template = template ?? {'ESTADO': {type: 'String', value: 'Pendiente eliminar'}}; const docuwareFile = await Self.checkFile(id, fileCabinet, false); if (docuwareFile) { - const deleteJson = Self.buildTemplateJSON(config); + const deleteJson = Self.buildTemplateJSON(template); const deleteUri = `${uri}/${docuwareFile.id}/Fields`; await axios.put(deleteUri, deleteJson, docuwareOptions.headers); diff --git a/back/methods/docuware/specs/core.spec.js b/back/methods/docuware/specs/core.spec.js index 60d1253ad..bdc63d830 100644 --- a/back/methods/docuware/specs/core.spec.js +++ b/back/methods/docuware/specs/core.spec.js @@ -1,7 +1,7 @@ const axios = require('axios'); const models = require('vn-loopback/server/server').models; -describe('Docuware core', () => { +fdescribe('Docuware core', () => { beforeAll(() => { process.env.NODE_ENV = 'testing'; }); @@ -10,6 +10,26 @@ describe('Docuware core', () => { delete process.env.NODE_ENV; }); + describe('hasDeviceReady()', () => { + it('should return true', async() => { + const result = await models.Docuware.hasDeviceReady({device: 'Tablet 1'}); + + expect(result).toBeTrue(); + }); + + it('should return false', async() => { + const result = await models.Docuware.hasDeviceReady({device: null}); + + expect(result).toBeFalse(); + }); + + it('should not exists return false ', async() => { + const result = await models.Docuware.hasDeviceReady(); + + expect(result).toBeFalse(); + }); + }); + describe('getOptions()', () => { it('should return url and headers', async() => { const result = await models.Docuware.getOptions(); @@ -102,7 +122,7 @@ describe('Docuware core', () => { // } // }; // spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs))); - const result = await models.Docuware.uploadOptions(1, { + const {uploadOptions: result} = await models.Docuware.uploadOptions(1, { 'N__DOCUMENTO': { type: 'string', value: '12345' diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index cddd8aff1..fdcc6bfa7 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -60,8 +60,8 @@ module.exports = Self => { const uri = Self.baseURL(docuwareOptions, fileCabinetId, 'Documents'); - // if (!isProduction(false)) - // throw new UserError('Action not allowed on the test environment'); + if (!isProduction(false) && !Self.hasDeviceReady()) + throw new UserError('Action not allowed on the test environment'); const upload = {ctx, tabletFk, ids, myOptions, uri, fileCabinet, fileCabinetId, dialogId}; await Self.app.models[model].docuwareUpload(upload); diff --git a/modules/ticket/back/methods/ticket/docuwareUpload.js b/modules/ticket/back/methods/ticket/docuwareUpload.js index 7c722c17d..1229401ed 100644 --- a/modules/ticket/back/methods/ticket/docuwareUpload.js +++ b/modules/ticket/back/methods/ticket/docuwareUpload.js @@ -64,7 +64,7 @@ module.exports = Self => { }; - if (!isProduction(false)) + if (!isProduction(false) && !Self.hasDeviceReady()) throw new UserError('Action not allowed on the test environment'); // delete old diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 8ae9c89ed..0aebb3870 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -16,9 +16,7 @@ module.exports = Self => { where: {deviceProductionFk: pdaId} } , myOptions); - // const worker = await models.Worker.findById( - // workerFk - // , myOptions); + const signPda = await models.Worker.signPdaPdf(ctx, pdaId, workerFk , myOptions); @@ -42,8 +40,8 @@ module.exports = Self => { } }; - // if (!isProduction(false)) - // throw new UserError('Action not allowed on the test environment'); + if (!isProduction(false) && !Self.hasDeviceReady()) + throw new UserError('Action not allowed on the test environment'); // delete old await models.Docuware.deleteOld(id, fileCabinet, uri); -- 2.40.1 From 23553a36949fc0f36d207d01c220700ac9149db9 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 10 Jun 2024 10:12:09 +0200 Subject: [PATCH 16/25] test(salix): refs #5926 docuware.core.spec --- back/methods/docuware/core.js | 11 +++-- back/methods/docuware/specs/core.spec.js | 53 ++++++++++++------------ 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index 01a3683f7..bd18fc45d 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -114,6 +114,9 @@ module.exports = Self => { * @return {number} - The fileCabinet id */ Self.getDialog = async(code, action, fileCabinetId) => { + if (!env && !Self.hasDeviceReady()) + return Math.floor(Math.random() + 100); + const docuwareInfo = await Self.getDocuware({ where: { code, @@ -138,8 +141,8 @@ module.exports = Self => { * @return {number} - The fileCabinet id */ Self.getFileCabinet = async code => { - // if (!process.env.NODE_ENV) - // return Math.floor(Math.random() + 100); + if (!env && !Self.hasDeviceReady()) + return Math.floor(Math.random() + 100); const options = await Self.getOptions(); const docuwareInfo = await Self.app.models.Docuware.findOne({ @@ -164,6 +167,8 @@ module.exports = Self => { * @return {object} - The data */ Self.get = async(code, filter, parse) => { + if (!env && !Self.hasDeviceReady()) return; + const options = await Self.getOptions(); const fileCabinetId = await Self.getFileCabinet(code); const dialogId = await Self.getDialog(code, 'find', fileCabinetId); @@ -185,7 +190,7 @@ module.exports = Self => { * @return {object} - The data */ Self.getById = async(code, id, parse) => { - if (!process.env.NODE_ENV) return; + if (!env && !Self.hasDeviceReady()) return; const docuwareInfo = await Self.app.models.Docuware.findOne({ fields: ['findById'], diff --git a/back/methods/docuware/specs/core.spec.js b/back/methods/docuware/specs/core.spec.js index bdc63d830..2700286d3 100644 --- a/back/methods/docuware/specs/core.spec.js +++ b/back/methods/docuware/specs/core.spec.js @@ -1,9 +1,10 @@ const axios = require('axios'); -const models = require('vn-loopback/server/server').models; +const {Docuware} = require('vn-loopback/server/server').models; fdescribe('Docuware core', () => { beforeAll(() => { process.env.NODE_ENV = 'testing'; + spyOn(Docuware, 'hasDeviceReady').and.returnValue(true); }); afterAll(() => { @@ -12,19 +13,19 @@ fdescribe('Docuware core', () => { describe('hasDeviceReady()', () => { it('should return true', async() => { - const result = await models.Docuware.hasDeviceReady({device: 'Tablet 1'}); + const result = await Docuware.hasDeviceReady({device: 'Tablet 1'}); expect(result).toBeTrue(); }); it('should return false', async() => { - const result = await models.Docuware.hasDeviceReady({device: null}); + const result = await Docuware.hasDeviceReady({device: null}); expect(result).toBeFalse(); }); it('should not exists return false ', async() => { - const result = await models.Docuware.hasDeviceReady(); + const result = await Docuware.hasDeviceReady(); expect(result).toBeFalse(); }); @@ -32,14 +33,14 @@ fdescribe('Docuware core', () => { describe('getOptions()', () => { it('should return url and headers', async() => { - const result = await models.Docuware.getOptions(); + const result = await Docuware.getOptions(); expect(result.url).toBeDefined(); expect(result.headers).toBeDefined(); }); }); - describe('getDialog()', () => { + fdescribe('getDialog()', () => { it('should return dialogId', async() => { const dialogs = { data: { @@ -52,7 +53,7 @@ fdescribe('Docuware core', () => { } }; spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs))); - const result = await models.Docuware.getDialog('deliveryNote', 'find', 'randomFileCabinetId'); + const result = await Docuware.getDialog('deliveryNote', 'find', 'randomFileCabinetId'); expect(result).toEqual('getDialogTest'); }); @@ -78,7 +79,7 @@ fdescribe('Docuware core', () => { value: 'Tablet123' } }; - const result = await models.Docuware.buildTemplateJSON(config); + const result = await Docuware.buildTemplateJSON(config); expect(result).toEqual({ 'Fields': [ @@ -109,7 +110,7 @@ fdescribe('Docuware core', () => { describe('uploadOptions()', () => { it('should return uploadOptions', async() => { - spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); + spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); // const dialogs = { // data: { @@ -122,7 +123,7 @@ fdescribe('Docuware core', () => { // } // }; // spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs))); - const {uploadOptions: result} = await models.Docuware.uploadOptions(1, { + const {uploadOptions: result} = await Docuware.uploadOptions(1, { 'N__DOCUMENTO': { type: 'string', value: '12345' @@ -136,7 +137,7 @@ fdescribe('Docuware core', () => { describe('deleteOld()', () => { it('should return deleteOld', async() => { - await models.Docuware.deleteOld(1, 'deliveryNote', { + await Docuware.deleteOld(1, 'deliveryNote', { 'N__DOCUMENTO': { type: 'string', value: '12345' @@ -144,10 +145,10 @@ fdescribe('Docuware core', () => { }); }); - describe('getFileCabinet()', () => { + fdescribe('getFileCabinet()', () => { it('should return fileCabinetId', async() => { const code = 'deliveryNote'; - const docuwareInfo = await models.Docuware.findOne({ + const docuwareInfo = await Docuware.findOne({ where: { code } @@ -163,30 +164,30 @@ fdescribe('Docuware core', () => { } }; spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs))); - const result = await models.Docuware.getFileCabinet(code); + const result = await Docuware.getFileCabinet(code); expect(result).toEqual('getFileCabinetTest'); }); }); - describe('get()', () => { + fdescribe('get()', () => { it('should return data without parse', async() => { - spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); - spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); + spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); + spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); const data = { data: { id: 1 } }; spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data))); - const result = await models.Docuware.get('deliveryNote'); + const result = await Docuware.get('deliveryNote'); expect(result.id).toEqual(1); }); - it('should return data with parse', async() => { - spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); - spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); + fit('should return data with parse', async() => { + spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); + spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); const data = { data: { Items: [{ @@ -215,7 +216,7 @@ fdescribe('Docuware core', () => { 'secondRequiredField': 'name', }; spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data))); - const [result] = await models.Docuware.get('deliveryNote', null, parse); + const [result] = await Docuware.get('deliveryNote', null, parse); expect(result.id).toEqual(1); expect(result.name).toEqual('myName'); @@ -223,17 +224,17 @@ fdescribe('Docuware core', () => { }); }); - describe('getById()', () => { + fdescribe('getById()', () => { it('should return data', async() => { - spyOn(models.Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); - spyOn(models.Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); + spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); + spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); const data = { data: { id: 1 } }; spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(data))); - const result = await models.Docuware.getById('deliveryNote', 1); + const result = await Docuware.getById('deliveryNote', 1); expect(result.id).toEqual(1); }); -- 2.40.1 From 517e498edb597f4dfbc09c730bdcd33daf5d007a Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 12 Jun 2024 22:29:35 +0200 Subject: [PATCH 17/25] test(salix): refs #5926 #5926 remove fdescribe --- back/methods/docuware/specs/core.spec.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/back/methods/docuware/specs/core.spec.js b/back/methods/docuware/specs/core.spec.js index 2700286d3..d7cb4ea22 100644 --- a/back/methods/docuware/specs/core.spec.js +++ b/back/methods/docuware/specs/core.spec.js @@ -1,7 +1,7 @@ const axios = require('axios'); const {Docuware} = require('vn-loopback/server/server').models; -fdescribe('Docuware core', () => { +describe('Docuware core', () => { beforeAll(() => { process.env.NODE_ENV = 'testing'; spyOn(Docuware, 'hasDeviceReady').and.returnValue(true); @@ -40,7 +40,7 @@ fdescribe('Docuware core', () => { }); }); - fdescribe('getDialog()', () => { + describe('getDialog()', () => { it('should return dialogId', async() => { const dialogs = { data: { @@ -145,7 +145,7 @@ fdescribe('Docuware core', () => { }); }); - fdescribe('getFileCabinet()', () => { + describe('getFileCabinet()', () => { it('should return fileCabinetId', async() => { const code = 'deliveryNote'; const docuwareInfo = await Docuware.findOne({ @@ -170,7 +170,7 @@ fdescribe('Docuware core', () => { }); }); - fdescribe('get()', () => { + describe('get()', () => { it('should return data without parse', async() => { spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); @@ -224,7 +224,7 @@ fdescribe('Docuware core', () => { }); }); - fdescribe('getById()', () => { + describe('getById()', () => { it('should return data', async() => { spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); -- 2.40.1 From 3f1c8433323c63a4078ce961a26d9fe73bd8e858 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 8 Jul 2024 12:00:36 +0200 Subject: [PATCH 18/25] feat(salix): refs #5926 #5926 remove comments and fix sql script --- back/methods/docuware/specs/core.spec.js | 12 ------------ db/dump/fixtures.after.sql | 2 -- db/versions/11062-tealArborvitae/00-firstScript.sql | 5 ++--- e2e/tests.js | 13 +++++++++++++ 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/back/methods/docuware/specs/core.spec.js b/back/methods/docuware/specs/core.spec.js index d7cb4ea22..aeffca5b3 100644 --- a/back/methods/docuware/specs/core.spec.js +++ b/back/methods/docuware/specs/core.spec.js @@ -111,18 +111,6 @@ describe('Docuware core', () => { describe('uploadOptions()', () => { it('should return uploadOptions', async() => { spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); - - // const dialogs = { - // data: { - // Dialog: [ - // { - // DisplayName: 'find', - // Id: 'getDialogTest' - // } - // ] - // } - // }; - // spyOn(axios, 'get').and.returnValue(new Promise(resolve => resolve(dialogs))); const {uploadOptions: result} = await Docuware.uploadOptions(1, { 'N__DOCUMENTO': { type: 'string', diff --git a/db/dump/fixtures.after.sql b/db/dump/fixtures.after.sql index 44a188959..562ea02d8 100644 --- a/db/dump/fixtures.after.sql +++ b/db/dump/fixtures.after.sql @@ -315,6 +315,4 @@ INSERT INTO mysql.roles_mapping (`User`, `Host`, `Role`, `Admin_option`) FROM mysql.roles_mapping WHERE `User` LIKE @prefixedLike AND `Host` = @genRoleHost; -INSERT INTO vn.docuware (code,fileCabinetName,`action`,dialogName,findById,dmsTypeFk,modelFk) - VALUES ('hr','RRHH','store','Archivar','N__DOCUMENTO',3,'worker'); FLUSH PRIVILEGES; diff --git a/db/versions/11062-tealArborvitae/00-firstScript.sql b/db/versions/11062-tealArborvitae/00-firstScript.sql index 39f9f7294..5e74090bb 100644 --- a/db/versions/11062-tealArborvitae/00-firstScript.sql +++ b/db/versions/11062-tealArborvitae/00-firstScript.sql @@ -1,14 +1,13 @@ --- Place your SQL code here ALTER TABLE vn.docuware ADD modelFk VARCHAR(45) DEFAULT NULL NULL; ALTER TABLE vn.docuware ADD CONSTRAINT docuware_module_FK FOREIGN KEY (modelFk) REFERENCES salix.module(code); ALTER TABLE vn.docuwareTablet ADD fileCabinet varchar(100) NULL; --- Auto-generated SQL script #202405221107 +INSERT IGNORE INTO vn.docuware (code,fileCabinetName,`action`,dialogName,findById,dmsTypeFk,modelFk) + VALUES ('hr','RRHH','store','Archivar','N__DOCUMENTO',3,'worker'); INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES ('Worker','signPdaPdf','READ','ALLOW','ROLE','hr'); --- Auto-generated SQL script #202406061008 INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId) VALUES ('Docuware','upload','WRITE','ALLOW','ROLE','hr'); diff --git a/e2e/tests.js b/e2e/tests.js index 4d9302921..829056f4c 100644 --- a/e2e/tests.js +++ b/e2e/tests.js @@ -36,7 +36,20 @@ async function test() { const jasmine = new Jasmine(); const specFiles = [ + `./e2e/paths/01*/*[sS]pec.js`, + `./e2e/paths/02*/*[sS]pec.js`, + `./e2e/paths/03*/*[sS]pec.js`, + `./e2e/paths/04*/*[sS]pec.js`, `./e2e/paths/05*/*[sS]pec.js`, + `./e2e/paths/06*/*[sS]pec.js`, + `./e2e/paths/07*/*[sS]pec.js`, + `./e2e/paths/08*/*[sS]pec.js`, + `./e2e/paths/09*/*[sS]pec.js`, + `./e2e/paths/10*/*[sS]pec.js`, + `./e2e/paths/11*/*[sS]pec.js`, + `./e2e/paths/12*/*[sS]pec.js`, + `./e2e/paths/13*/*[sS]pec.js`, + `./e2e/paths/**/*[sS]pec.js` ]; jasmine.loadConfig({ -- 2.40.1 From 590e0af03801cfddf4321992d4d102be37dc7895 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 8 Jul 2024 12:16:08 +0200 Subject: [PATCH 19/25] test(salix): refs #5926 #5926 fix test --- back/methods/docuware/specs/core.spec.js | 41 ++++++++++++------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/back/methods/docuware/specs/core.spec.js b/back/methods/docuware/specs/core.spec.js index aeffca5b3..a921fdbfe 100644 --- a/back/methods/docuware/specs/core.spec.js +++ b/back/methods/docuware/specs/core.spec.js @@ -1,5 +1,24 @@ const axios = require('axios'); const {Docuware} = require('vn-loopback/server/server').models; +describe('hasDeviceReady()', () => { + it('should return true', async() => { + const result = await Docuware.hasDeviceReady({device: 'Tablet 1'}); + + expect(result).toBeTrue(); + }); + + it('should return false', async() => { + const result = await Docuware.hasDeviceReady({device: null}); + + expect(result).toBeFalse(); + }); + + it('should not exists return false ', async() => { + const result = await Docuware.hasDeviceReady(); + + expect(result).toBeFalse(); + }); +}); describe('Docuware core', () => { beforeAll(() => { @@ -11,26 +30,6 @@ describe('Docuware core', () => { delete process.env.NODE_ENV; }); - describe('hasDeviceReady()', () => { - it('should return true', async() => { - const result = await Docuware.hasDeviceReady({device: 'Tablet 1'}); - - expect(result).toBeTrue(); - }); - - it('should return false', async() => { - const result = await Docuware.hasDeviceReady({device: null}); - - expect(result).toBeFalse(); - }); - - it('should not exists return false ', async() => { - const result = await Docuware.hasDeviceReady(); - - expect(result).toBeFalse(); - }); - }); - describe('getOptions()', () => { it('should return url and headers', async() => { const result = await Docuware.getOptions(); @@ -173,7 +172,7 @@ describe('Docuware core', () => { expect(result.id).toEqual(1); }); - fit('should return data with parse', async() => { + it('should return data with parse', async() => { spyOn(Docuware, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random())))); spyOn(Docuware, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random())))); const data = { -- 2.40.1 From 1dd65d29e9a2f661e6dbb61c7d3481ae69c67618 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 8 Jul 2024 12:20:16 +0200 Subject: [PATCH 20/25] feat(salix): refs #5926 #5926 revert europe.png folder --- .../delivery-note/assets/images/europe.png | Bin 0 -> 55634 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 print/templates/reports/delivery-note/assets/images/europe.png diff --git a/print/templates/reports/delivery-note/assets/images/europe.png b/print/templates/reports/delivery-note/assets/images/europe.png new file mode 100644 index 0000000000000000000000000000000000000000..673be92ae2f0647fd1748e12a36bf073aa145d64 GIT binary patch literal 55634 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 Date: Fri, 12 Jul 2024 23:13:35 +0200 Subject: [PATCH 21/25] feat: refs #5926 #5926 remove arg from path --- modules/worker/back/methods/worker/signPdaPdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/worker/back/methods/worker/signPdaPdf.js b/modules/worker/back/methods/worker/signPdaPdf.js index 801b94379..9d1929d69 100644 --- a/modules/worker/back/methods/worker/signPdaPdf.js +++ b/modules/worker/back/methods/worker/signPdaPdf.js @@ -34,7 +34,7 @@ module.exports = Self => { } ], http: { - path: '/:id/:workerFk/sign-pda-pdf', + path: '/:id/sign-pda-pdf', verb: 'GET' }, accessScopes: ['DEFAULT', 'read:multimedia'] -- 2.40.1 From c248372e50346490fd505356bbaf26a4e79a0084 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 12 Jul 2024 23:13:58 +0200 Subject: [PATCH 22/25] feat: refs #5926 i18n --- print/templates/reports/sign-pda/locale/de.yml | 12 ++++++++---- print/templates/reports/sign-pda/locale/en.yml | 12 ++++++++---- print/templates/reports/sign-pda/locale/fr.yml | 12 ++++++++---- print/templates/reports/sign-pda/locale/pt.yml | 12 ++++++++---- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/print/templates/reports/sign-pda/locale/de.yml b/print/templates/reports/sign-pda/locale/de.yml index d43479cc3..9a9ca991b 100644 --- a/print/templates/reports/sign-pda/locale/de.yml +++ b/print/templates/reports/sign-pda/locale/de.yml @@ -1,14 +1,18 @@ reportName: pda signNote: Recepción PDA date: Fecha -deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional. pdaModel: PDA Modelo pdaSerie: Serie PDA sim: sim vodafone pin: pin puk: puk -label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. -label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. -label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: La empresa prohibe el uso de accesorios no originales. +label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +sign1: Recibe, D/Dña +sign2: DNI +sign: FIRMA diff --git a/print/templates/reports/sign-pda/locale/en.yml b/print/templates/reports/sign-pda/locale/en.yml index d43479cc3..9a9ca991b 100644 --- a/print/templates/reports/sign-pda/locale/en.yml +++ b/print/templates/reports/sign-pda/locale/en.yml @@ -1,14 +1,18 @@ reportName: pda signNote: Recepción PDA date: Fecha -deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional. pdaModel: PDA Modelo pdaSerie: Serie PDA sim: sim vodafone pin: pin puk: puk -label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. -label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. -label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: La empresa prohibe el uso de accesorios no originales. +label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +sign1: Recibe, D/Dña +sign2: DNI +sign: FIRMA diff --git a/print/templates/reports/sign-pda/locale/fr.yml b/print/templates/reports/sign-pda/locale/fr.yml index d43479cc3..9a9ca991b 100644 --- a/print/templates/reports/sign-pda/locale/fr.yml +++ b/print/templates/reports/sign-pda/locale/fr.yml @@ -1,14 +1,18 @@ reportName: pda signNote: Recepción PDA date: Fecha -deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional. pdaModel: PDA Modelo pdaSerie: Serie PDA sim: sim vodafone pin: pin puk: puk -label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. -label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. -label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: La empresa prohibe el uso de accesorios no originales. +label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +sign1: Recibe, D/Dña +sign2: DNI +sign: FIRMA diff --git a/print/templates/reports/sign-pda/locale/pt.yml b/print/templates/reports/sign-pda/locale/pt.yml index d43479cc3..9a9ca991b 100644 --- a/print/templates/reports/sign-pda/locale/pt.yml +++ b/print/templates/reports/sign-pda/locale/pt.yml @@ -1,14 +1,18 @@ reportName: pda signNote: Recepción PDA date: Fecha -deviceRecieved: He recibido de Verdnatura Levante SL, el siguiente material +deviceRecieved: He recibido de Verdnatura Levante SL, el terminal *{modelFk}* con nº de serie {serialNumber} junto con su funda y protector de pantalla para el desarrollo de mi actividad profesional. pdaModel: PDA Modelo pdaSerie: Serie PDA sim: sim vodafone pin: pin puk: puk -label1: El terminal es propiedad de Verdnatura Levante SL y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. +label1: El terminal es propiedad de la empresa y debe ser utilizado exclusivamente para las funciones y aplicaciones determinadas por la empresa. label2: Me comprometo a conservar el terminal con su batería, tarjeta SIM, funda y protector de pantalla, y en caso de finalizar mi relación de servicios con la empresa, realizar la devolución en el plazo máximo de 24 horas. label3: Si el terminal fuese robado, perdido o dañado, me comprometo a abonar el importe íntegro del terminal o su reparación en el centro oficial , así como la funda y el protector de pantalla. -label4: La instalación de App no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. -label5: Mediante este contrato me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +label4: La instalación de aplicaciones no autorizadas por la empresa, puedan provocar o no un daño al terminal o a la empresa por vulneración de datos, virus, etc, no está autorizado, bajo ningún concepto, el uso de accesorios no originales. +label5: La empresa prohibe el uso de accesorios no originales. +label6: Mediante este documento me comprometo a usar la funda, y protector de pantalla en todo momento para minimizar daños en el terminal. +sign1: Recibe, D/Dña +sign2: DNI +sign: FIRMA -- 2.40.1 From ef931f71a08b808cd7c8d8274cdf7a827b9bf9a8 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Fri, 12 Jul 2024 23:14:12 +0200 Subject: [PATCH 23/25] feat: refs #5926 remove comments and others --- back/methods/docuware/download.js | 2 +- modules/worker/back/methods/worker/docuwareUpload.js | 2 +- modules/worker/back/models/device-production-user.js | 1 - print/templates/reports/sign-pda/sign-pda.js | 4 ---- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/back/methods/docuware/download.js b/back/methods/docuware/download.js index 83f3ba919..9bf6b9db9 100644 --- a/back/methods/docuware/download.js +++ b/back/methods/docuware/download.js @@ -4,7 +4,7 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethod('download', { - description: 'Download an docuware PDF', + description: 'Download a docuware PDF', accessType: 'READ', accepts: [ { diff --git a/modules/worker/back/methods/worker/docuwareUpload.js b/modules/worker/back/methods/worker/docuwareUpload.js index 0aebb3870..6cd94d947 100644 --- a/modules/worker/back/methods/worker/docuwareUpload.js +++ b/modules/worker/back/methods/worker/docuwareUpload.js @@ -18,7 +18,7 @@ module.exports = Self => { , myOptions); const signPda = await models.Worker.signPdaPdf(ctx, - pdaId, workerFk + {pdaId, workerFk} , myOptions); const configTemplate = { diff --git a/modules/worker/back/models/device-production-user.js b/modules/worker/back/models/device-production-user.js index d1b7b0187..8eead58e0 100644 --- a/modules/worker/back/models/device-production-user.js +++ b/modules/worker/back/models/device-production-user.js @@ -1,5 +1,4 @@ const UserError = require('vn-loopback/util/user-error'); - module.exports = Self => { Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') diff --git a/print/templates/reports/sign-pda/sign-pda.js b/print/templates/reports/sign-pda/sign-pda.js index 8c2872b99..69c5d3f04 100755 --- a/print/templates/reports/sign-pda/sign-pda.js +++ b/print/templates/reports/sign-pda/sign-pda.js @@ -7,10 +7,6 @@ module.exports = { this.device = await this.findOneFromDef('device', [this.id]); this.worker = await this.findOneFromDef('worker', [this.workerFk]); }, - computed: { - }, - methods: { - }, props: { id: { type: Number, -- 2.40.1 From e410a1f18baa0ed01715b22f30c7419baa6a0e7c Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Tue, 23 Jul 2024 23:12:46 +0200 Subject: [PATCH 24/25] perf(salix): refs #5926 6321 remove unnecessary picture --- .../reports/sign-pda/assets/images/europe.png | Bin 55634 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 print/templates/reports/sign-pda/assets/images/europe.png diff --git a/print/templates/reports/sign-pda/assets/images/europe.png b/print/templates/reports/sign-pda/assets/images/europe.png deleted file mode 100644 index 673be92ae2f0647fd1748e12a36bf073aa145d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55634 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 Date: Wed, 11 Sep 2024 10:30:39 +0200 Subject: [PATCH 25/25] fix: refs #5926 typo --- db/dump/fixtures.before.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 82d29e34f..4ae6be494 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -4007,7 +4007,7 @@ INSERT INTO vn.accountDetailType (id, description, code) (5, 'Referencia Nominas'), (6, 'ABA'); -INSERT INTO vn.userConfig (userFk,warehouseFk,companyFk,darkMode,tabletFk) +INSERT INTO account.userConfig (userFk,warehouseFk,companyFk,darkMode,tabletFk) VALUES (37,1,442,1,'Tablet4'); INSERT IGNORE INTO ormConfig -- 2.40.1