From d3c4a6ba5c68a0458cefa0c4c0867b3ab9530f41 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 12 Jun 2023 15:20:46 +0200 Subject: [PATCH 1/3] refs #5094 feat(ticket_index): docuware multiple deliveryNote --- back/methods/docuware/core.js | 8 +- back/methods/docuware/upload.js | 227 +++++++++--------- loopback/locale/en.json | 3 +- loopback/locale/es.json | 2 +- modules/ticket/front/descriptor-menu/index.js | 8 +- .../front/descriptor-menu/index.spec.js | 4 +- modules/ticket/front/index/index.html | 42 ++-- modules/ticket/front/index/index.js | 31 +-- 8 files changed, 161 insertions(+), 164 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index 2053ddf85..be310e82b 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -20,8 +20,8 @@ module.exports = Self => { const options = await Self.getOptions(); - if (!process.env.NODE_ENV) - return Math.round(); + // if (!process.env.NODE_ENV) + // return Math.round(); const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers); const dialogs = response.data.Dialog; @@ -44,8 +44,8 @@ module.exports = Self => { } }); - if (!process.env.NODE_ENV) - return Math.round(); + // if (!process.env.NODE_ENV) + // return Math.round(); const fileCabinetResponse = await axios.get(`${options.url}/FileCabinets`, options.headers); const fileCabinets = fileCabinetResponse.data.FileCabinet; diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index ea9ee3622..d14e8a668 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -7,30 +7,27 @@ module.exports = Self => { accessType: 'WRITE', accepts: [ { - arg: 'id', - type: 'number', - description: 'The ticket id', - http: {source: 'path'} + arg: 'ticketIds', + type: ['number'], + description: 'The ticket ids', + required: true }, { arg: 'fileCabinet', type: 'string', - description: 'The file cabinet' - }, - { - arg: 'dialog', - type: 'string', - description: 'The dialog' + description: 'The file cabinet', + required: true } ], returns: [], http: { - path: `/:id/upload`, + path: `/upload`, verb: 'POST' } }); - Self.upload = async function(ctx, id, fileCabinet) { + Self.upload = async function(ctx, ticketIds, fileCabinet) { + delete ctx.args.ticketIds; const models = Self.app.models; const action = 'store'; @@ -38,104 +35,116 @@ module.exports = Self => { const fileCabinetId = await Self.getFileCabinet(fileCabinet); const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId); + const uploaded = []; + // get delivery note - const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { - id, - type: 'deliveryNote' - }); - - // get ticket data - const ticket = await models.Ticket.findById(id, { - include: [{ - relation: 'client', - scope: { - fields: ['id', 'socialName', 'fi'] - } - }] - }); - - // 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().socialName, - }, - { - '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': 'Tablet1', - } - ] - }; - - if (process.env.NODE_ENV != 'production') - throw new UserError('Action not allowed on the test environment'); - - // delete old - const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false); - if (docuwareFile) { - const deleteJson = { - 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] - }; - const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`; - await axios.put(deleteUri, deleteJson, options.headers); - } - - const uploadUri = `${options.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': options.headers.headers.Cookie, - ...data.getHeaders() - }, - }; - - return await axios.post(uploadUri, data, uploadOptions) - .catch(() => { - throw new UserError('Failed to upload file'); + for (id of ticketIds) { + // get delivery note + ctx.args.id = id; + const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, { + id, + type: 'deliveryNote' }); + // get ticket data + const ticket = await models.Ticket.findById(id, { + include: [{ + relation: 'client', + scope: { + fields: ['id', 'name', 'fi'] + } + }] + }); + + // 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': 'Tablet1', + } + ] + }; + + // if (process.env.NODE_ENV != 'production') + // throw new UserError('Action not allowed on the test environment'); + + // delete old + const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false); + if (docuwareFile) { + const deleteJson = { + 'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}] + }; + const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`; + await axios.put(deleteUri, deleteJson, options.headers); + } + + const uploadUri = `${options.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': options.headers.headers.Cookie, + ...data.getHeaders() + }, + }; + + try { + await axios.post(uploadUri, data, uploadOptions); + } catch (err) { + const $t = ctx.req.__; + const message = $t('Failed to upload ticket', {id}); + if (uploaded.length) + await models.TicketTracking.setDelivered(ctx, uploaded); + throw new UserError(message); + } + uploaded.push(id); + } + return models.TicketTracking.setDelivered(ctx, ticketIds); }; }; diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 14ffffeb5..b334b8676 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -173,5 +173,6 @@ "This ticket is already a refund": "This ticket is already a refund", "A claim with that sale already exists": "A claim with that sale already exists", "Can't transfer claimed sales": "Can't transfer claimed sales", - "Invalid quantity": "Invalid quantity" + "Invalid quantity": "Invalid quantity", + "Failed to upload delivery note": "Error to upload delivery note {{id}}" } diff --git a/loopback/locale/es.json b/loopback/locale/es.json index d88a4ebc9..bd49db9b3 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -259,7 +259,7 @@ "App name does not exist": "El nombre de aplicación no es válido", "Try again": "Vuelve a intentarlo", "Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9", - "Failed to upload file": "Error al subir archivo", + "Failed to upload delivery note": "Error al subir albarán {{id}}", "The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe", "It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar", "It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo", diff --git a/modules/ticket/front/descriptor-menu/index.js b/modules/ticket/front/descriptor-menu/index.js index 835f395a8..17a34ad59 100644 --- a/modules/ticket/front/descriptor-menu/index.js +++ b/modules/ticket/front/descriptor-menu/index.js @@ -326,14 +326,8 @@ class Controller extends Section { if (!force) return this.$.pdfToTablet.show(); - return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'}) + return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds: [this.id]}) .then(() => { - this.$.balanceCreate.amountPaid = this.ticket.totalWithVat; - this.$.balanceCreate.clientFk = this.ticket.clientFk; - this.$.balanceCreate.description = 'Albaran: '; - this.$.balanceCreate.description += this.ticket.id; - - this.$.balanceCreate.show(); this.vnApp.showSuccess(this.$t('PDF sent!')); }); } diff --git a/modules/ticket/front/descriptor-menu/index.spec.js b/modules/ticket/front/descriptor-menu/index.spec.js index 5d27acff1..95a580fe5 100644 --- a/modules/ticket/front/descriptor-menu/index.spec.js +++ b/modules/ticket/front/descriptor-menu/index.spec.js @@ -304,9 +304,8 @@ describe('Ticket Component vnTicketDescriptorMenu', () => { expect(controller.$.pdfToTablet.show).toHaveBeenCalled(); }); - it('should make a query and show balance create', () => { + it('should make a query', () => { controller.$.balanceCreate = {show: () => {}}; - jest.spyOn(controller.$.balanceCreate, 'show'); jest.spyOn(controller.vnApp, 'showSuccess'); $httpBackend.whenPOST(`Docuwares/${ticket.id}/upload`).respond(true); @@ -314,7 +313,6 @@ describe('Ticket Component vnTicketDescriptorMenu', () => { $httpBackend.flush(); expect(controller.vnApp.showSuccess).toHaveBeenCalled(); - expect(controller.$.balanceCreate.show).toHaveBeenCalled(); }); }); diff --git a/modules/ticket/front/index/index.html b/modules/ticket/front/index/index.html index 1e18ce284..d6e230ebe 100644 --- a/modules/ticket/front/index/index.html +++ b/modules/ticket/front/index/index.html @@ -9,7 +9,7 @@ - @@ -33,7 +33,7 @@ class="clickable vn-tr search-result" ui-sref="ticket.card.summary({id: {{::ticket.id}}})"> - @@ -109,7 +109,7 @@ class="link"> {{::ticket.refFk}} - {{ticket.state}} @@ -132,8 +132,8 @@
- - - - - - Filter by selection - Exclude selection - Remove filter - Remove all filters - Copy value @@ -241,4 +241,4 @@ on-accept="$ctrl.makeInvoice()" question="{{$ctrl.confirmationMessage}}" message="Invoice selected tickets"> - \ No newline at end of file + diff --git a/modules/ticket/front/index/index.js b/modules/ticket/front/index/index.js index 42332ccc8..55229eabb 100644 --- a/modules/ticket/front/index/index.js +++ b/modules/ticket/front/index/index.js @@ -9,28 +9,23 @@ export default class Controller extends Section { this.vnReport = vnReport; } - setDelivered() { + sendDocuware() { const checkedTickets = this.checked; - let ids = []; + let ticketIds = []; for (let ticket of checkedTickets) - ids.push(ticket.id); + ticketIds.push(ticket.id); - this.$http.post('TicketTrackings/setDelivered', ids).then(res => { - let state = res.data; - for (let ticket of checkedTickets) { - ticket.stateFk = state.id; - ticket.state = state.name; - ticket.alertLevel = state.alertLevel; - ticket.alertLevelCode = state.code; - } - this.openDeliveryNotes(ids); - }); - } - - openDeliveryNotes(ids) { - for (let id of ids) - this.vnReport.show(`Tickets/${id}/delivery-note-pdf`); + return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds}) + .then(res => { + let state = res.data; + for (let ticket of checkedTickets) { + ticket.stateFk = state.id; + ticket.state = state.name; + ticket.alertLevel = state.alertLevel; + ticket.alertLevelCode = state.code; + } + }); } openBalanceDialog() { From 92953b9aa821e392a7c8d9969e101d40c99dd1d1 Mon Sep 17 00:00:00 2001 From: alexm Date: Tue, 13 Jun 2023 11:37:35 +0200 Subject: [PATCH 2/3] refs #5094 test(docuware): adapt tests --- back/methods/docuware/core.js | 8 ++++---- back/methods/docuware/specs/upload.spec.js | 5 +++-- back/methods/docuware/upload.js | 9 ++++++--- .../front/descriptor-menu/index.spec.js | 2 +- modules/ticket/front/index/index.spec.js | 20 +++++++++---------- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/back/methods/docuware/core.js b/back/methods/docuware/core.js index be310e82b..2053ddf85 100644 --- a/back/methods/docuware/core.js +++ b/back/methods/docuware/core.js @@ -20,8 +20,8 @@ module.exports = Self => { const options = await Self.getOptions(); - // if (!process.env.NODE_ENV) - // return Math.round(); + if (!process.env.NODE_ENV) + return Math.round(); const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers); const dialogs = response.data.Dialog; @@ -44,8 +44,8 @@ module.exports = Self => { } }); - // if (!process.env.NODE_ENV) - // return Math.round(); + if (!process.env.NODE_ENV) + return Math.round(); const fileCabinetResponse = await axios.get(`${options.url}/FileCabinets`, options.headers); const fileCabinets = fileCabinetResponse.data.FileCabinet; diff --git a/back/methods/docuware/specs/upload.spec.js b/back/methods/docuware/specs/upload.spec.js index 7ac873e95..3b8c55a50 100644 --- a/back/methods/docuware/specs/upload.spec.js +++ b/back/methods/docuware/specs/upload.spec.js @@ -2,8 +2,9 @@ const models = require('vn-loopback/server/server').models; describe('docuware upload()', () => { const userId = 9; - const ticketId = 10; + const ticketIds = [10]; const ctx = { + args: {ticketIds}, req: { getLocale: () => { return 'en'; @@ -27,7 +28,7 @@ describe('docuware upload()', () => { let error; try { - await models.Docuware.upload(ctx, ticketId, fileCabinetName); + await models.Docuware.upload(ctx, ticketIds, fileCabinetName); } catch (e) { error = e.message; } diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index d14e8a668..57e96170f 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -19,7 +19,10 @@ module.exports = Self => { required: true } ], - returns: [], + returns: { + type: 'object', + root: true + }, http: { path: `/upload`, verb: 'POST' @@ -106,8 +109,8 @@ module.exports = Self => { ] }; - // if (process.env.NODE_ENV != 'production') - // throw new UserError('Action not allowed on the test environment'); + if (process.env.NODE_ENV != 'production') + throw new UserError('Action not allowed on the test environment'); // delete old const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false); diff --git a/modules/ticket/front/descriptor-menu/index.spec.js b/modules/ticket/front/descriptor-menu/index.spec.js index 95a580fe5..0aef956db 100644 --- a/modules/ticket/front/descriptor-menu/index.spec.js +++ b/modules/ticket/front/descriptor-menu/index.spec.js @@ -308,7 +308,7 @@ describe('Ticket Component vnTicketDescriptorMenu', () => { controller.$.balanceCreate = {show: () => {}}; jest.spyOn(controller.vnApp, 'showSuccess'); - $httpBackend.whenPOST(`Docuwares/${ticket.id}/upload`).respond(true); + $httpBackend.whenPOST(`Docuwares/upload`).respond(true); controller.uploadDocuware(true); $httpBackend.flush(); diff --git a/modules/ticket/front/index/index.spec.js b/modules/ticket/front/index/index.spec.js index 5046387b0..951c6aa09 100644 --- a/modules/ticket/front/index/index.spec.js +++ b/modules/ticket/front/index/index.spec.js @@ -12,12 +12,14 @@ describe('Component vnTicketIndex', () => { id: 2, clientFk: 1, checked: true, - totalWithVat: 20.5 + totalWithVat: 20.5, + stateFk: 1 }, { id: 3, clientFk: 1, checked: true, - totalWithVat: 30 + totalWithVat: 30, + stateFk: 1 }]; beforeEach(ngModule('ticket')); @@ -86,18 +88,16 @@ describe('Component vnTicketIndex', () => { }); }); - describe('setDelivered()/openDeliveryNotes()', () => { - it('should perform a post to setDelivered and open tabs with the delivery notes', () => { + describe('sendDocuware()', () => { + it('should perform a post to sendDocuware and change tickets state', () => { controller.$.model = {data: tickets, refresh: () => {}}; + const newState = {id: 2}; - $window.open = jest.fn(); - - $httpBackend.expect('POST', 'TicketTrackings/setDelivered').respond('ok'); - controller.setDelivered(); + $httpBackend.expect('POST', 'Docuwares/upload').respond({id: newState.id}); + controller.sendDocuware(); $httpBackend.flush(); - expect($window.open).toHaveBeenCalledWith(`api/Tickets/${tickets[1].id}/delivery-note-pdf`); - expect($window.open).toHaveBeenCalledWith(`api/Tickets/${tickets[2].id}/delivery-note-pdf`); + expect(controller.$.model.data[1].stateFk).toEqual(newState.id); }); }); From dd4b62d0a53ac810f2dd8ef7442e6683988e69e0 Mon Sep 17 00:00:00 2001 From: alexm Date: Tue, 13 Jun 2023 12:42:12 +0200 Subject: [PATCH 3/3] description --- back/methods/docuware/upload.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js index 57e96170f..421c1c4d8 100644 --- a/back/methods/docuware/upload.js +++ b/back/methods/docuware/upload.js @@ -3,7 +3,7 @@ const axios = require('axios'); module.exports = Self => { Self.remoteMethodCtx('upload', { - description: 'Upload an docuware PDF', + description: 'Upload docuware PDFs', accessType: 'WRITE', accepts: [ { @@ -39,8 +39,6 @@ module.exports = Self => { const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId); const uploaded = []; - - // get delivery note for (id of ticketIds) { // get delivery note ctx.args.id = id;