From 40ea9805cf34df06d362d4d834356c7ac6c54d36 Mon Sep 17 00:00:00 2001 From: alexandre Date: Mon, 21 Nov 2022 10:49:19 +0100 Subject: [PATCH 1/4] fixes #4547 check compensation and tests --- .../10503-november/00-isCompensationACL.sql | 4 +++ db/dump/fixtures.sql | 5 +-- e2e/helpers/selectors.js | 3 +- .../02-client/23_send_compensation.spec.js | 27 +++++++++++++++ .../receipt/balanceCompensationEmail.js | 34 +++++++++++++------ modules/client/back/methods/receipt/filter.js | 14 +++++--- modules/client/front/balance/index/index.html | 10 +++--- modules/client/front/balance/index/index.js | 16 ++++----- .../client/front/balance/index/index.spec.js | 14 ++++++++ .../client/front/balance/index/locale/en.yml | 4 ++- 10 files changed, 99 insertions(+), 32 deletions(-) create mode 100644 db/changes/10503-november/00-isCompensationACL.sql create mode 100644 e2e/paths/02-client/23_send_compensation.spec.js diff --git a/db/changes/10503-november/00-isCompensationACL.sql b/db/changes/10503-november/00-isCompensationACL.sql new file mode 100644 index 000000000..ac01758b0 --- /dev/null +++ b/db/changes/10503-november/00-isCompensationACL.sql @@ -0,0 +1,4 @@ +INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('Receipt', 'balanceCompensationEmail', 'WRITE', 'ALLOW', 'ROLE', 'employee'), + ('Receipt', 'balanceCompensationPdf', 'READ', 'ALLOW', 'ROLE', 'employee'); diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 49cf639ef..e7649ab0f 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1853,7 +1853,8 @@ INSERT INTO `vn`.`receipt`(`id`, `invoiceFk`, `amountPaid`, `payed`, `workerFk`, (1, 'Cobro web', 100.50, util.VN_CURDATE(), 9, 1, 1101, util.VN_CURDATE(), 442, 1), (2, 'Cobro web', 200.50, DATE_ADD(util.VN_CURDATE(), INTERVAL -5 DAY), 9, 1, 1101, DATE_ADD(util.VN_CURDATE(), INTERVAL -5 DAY), 442, 1), (3, 'Cobro en efectivo', 300.00, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), 9, 1, 1102, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), 442, 0), - (4, 'Cobro en efectivo', 400.00, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), 9, 1, 1103, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), 442, 0); + (4, 'Cobro en efectivo', 400.00, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), 9, 1, 1103, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), 442, 0), + (5, 'Compensación', 400.00, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), 9, 3, 1103, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), 442, 0); INSERT INTO `vn`.`workerTeam`(`id`, `team`, `workerFk`) VALUES @@ -2701,7 +2702,7 @@ INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`) INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`) VALUES (1, 9); - + INSERT INTO `vn`.`productionConfig` (`isPreviousPreparationRequired`, `ticketPrintedMax`, `ticketTrolleyMax`, `rookieDays`, `notBuyingMonths`, `id`, `isZoneClosedByExpeditionActivated`, `maxNotReadyCollections`, `minTicketsToCloseZone`, `movingTicketDelRoute`, `defaultZone`, `defautlAgencyMode`, `hasUniqueCollectionTime`, `maxCollectionWithoutUser`, `pendingCollectionsOrder`, `pendingCollectionsAge`) VALUES (0, 8, 80, 0, 0, 1, 0, 15, 25, -1, 697, 1328, 0, 1, 8, 6); diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 1391731de..b6f8cb3a2 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -324,7 +324,8 @@ export default { anyBalanceLine: 'vn-client-balance-index vn-tbody > vn-tr', firstLineBalance: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(8)', firstLineReference: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable', - firstLineReferenceInput: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable > div > field > vn-textfield' + firstLineReferenceInput: 'vn-client-balance-index vn-tbody > vn-tr:nth-child(1) > vn-td-editable > div > field > vn-textfield', + compensationButton: 'vn-client-balance-index vn-icon-button[vn-dialog="send_compensation"]' }, webPayment: { confirmFirstPaymentButton: 'vn-client-web-payment vn-tr:nth-child(1) vn-icon-button[icon="done_all"]', diff --git a/e2e/paths/02-client/23_send_compensation.spec.js b/e2e/paths/02-client/23_send_compensation.spec.js new file mode 100644 index 000000000..6ec8936a8 --- /dev/null +++ b/e2e/paths/02-client/23_send_compensation.spec.js @@ -0,0 +1,27 @@ +import selectors from '../../helpers/selectors'; +import getBrowser from '../../helpers/puppeteer'; + +describe('Client Send balance compensation', () => { + let browser; + let page; + beforeAll(async() => { + browser = await getBrowser(); + page = browser.page; + await page.loginAndModule('employee', 'client'); + await page.accessToSearchResult('Clark Kent'); + await page.accessToSection('client.card.balance.index'); + }); + + afterAll(async() => { + await browser.close(); + }); + + it(`should click on send compensation button`, async() => { + await page.autocompleteSearch(selectors.clientBalance.company, 'VNL'); + await page.waitToClick(selectors.clientBalance.compensationButton); + await page.waitToClick(selectors.clientBalance.saveButton); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Notification sent!'); + }); +}); diff --git a/modules/client/back/methods/receipt/balanceCompensationEmail.js b/modules/client/back/methods/receipt/balanceCompensationEmail.js index e9ded147d..f02eeaf74 100644 --- a/modules/client/back/methods/receipt/balanceCompensationEmail.js +++ b/modules/client/back/methods/receipt/balanceCompensationEmail.js @@ -1,4 +1,5 @@ const {Email} = require('vn-print'); +const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethodCtx('balanceCompensationEmail', { @@ -10,7 +11,7 @@ module.exports = Self => { type: 'Number', required: true, description: 'The receipt id', - http: { source: 'path' } + http: {source: 'path'} } ], returns: { @@ -23,18 +24,29 @@ module.exports = Self => { } }); - Self.balanceCompensationEmail = async (ctx, id) => { - + Self.balanceCompensationEmail = async(ctx, id) => { const models = Self.app.models; - const receipt = await models.Receipt.findById(id, {fields: ['clientFk']}); - const client = await models.Client.findById(receipt.clientFk, {fields:['email']}); + const receipt = await models.Receipt.findById(id, {fields: ['clientFk', 'bankFk']}); - const email = new Email('balance-compensation', { - lang: ctx.req.getLocale(), - recipient: client.email+',administracion@verdnatura.es', - id - }); + const bank = await models.Bank.findById(receipt.bankFk, null); + if (!bank) + throw new UserError('Receipt\'s bank was not found'); - return email.send(); + const accountingType = await models.AccountingType.findById(bank.accountingTypeFk, null); + if (!(accountingType && accountingType.code == 'compensation')) + throw new UserError('Receipt\'s type was not compensation'); + + const client = await models.Client.findById(receipt.clientFk, {fields: ['email']}); + + if (!client.email) + throw new UserError('Client\'s email was not found'); + else { + const email = new Email('balance-compensation', { + lang: ctx.req.getLocale(), + recipient: client.email + ',administracion@verdnatura.es', + id + }); + return email.send(); + } }; }; diff --git a/modules/client/back/methods/receipt/filter.js b/modules/client/back/methods/receipt/filter.js index e29ccf8f2..9549fb001 100644 --- a/modules/client/back/methods/receipt/filter.js +++ b/modules/client/back/methods/receipt/filter.js @@ -42,7 +42,7 @@ module.exports = Self => { const stmt = new ParameterizedSQL( `SELECT * FROM ( - SELECT + SELECT r.id, r.isConciliate, r.payed, @@ -56,13 +56,16 @@ module.exports = Self => { u.name userName, r.clientFk, FALSE hasPdf, - FALSE isInvoice + FALSE isInvoice, + CASE WHEN at2.code LIKE 'compensation' THEN True ELSE False END as isCompensation FROM vn.receipt r LEFT JOIN vn.worker w ON w.id = r.workerFk LEFT JOIN account.user u ON u.id = w.userFk JOIN vn.company c ON c.id = r.companyFk + JOIN vn.accounting a ON a.id = r.bankFk + JOIN vn.accountingType at2 ON at2.id = a.accountingTypeFk WHERE r.clientFk = ? AND r.companyFk = ? - UNION ALL + UNION ALL SELECT i.id, TRUE, @@ -77,9 +80,12 @@ module.exports = Self => { NULL, i.clientFk, i.hasPdf, - TRUE isInvoice + TRUE isInvoice, + CASE WHEN at2.code LIKE 'compensation' THEN True ELSE False END as isCompensation FROM vn.invoiceOut i JOIN vn.company c ON c.id = i.companyFk + JOIN vn.accounting a ON a.id = i.bankFk + JOIN vn.accountingType at2 ON at2.id = a.accountingTypeFk WHERE i.clientFk = ? AND i.companyFk = ? ORDER BY payed DESC, created DESC ) t ORDER BY payed DESC, created DESC`, diff --git a/modules/client/front/balance/index/index.html b/modules/client/front/balance/index/index.html index 1e0716112..faf772c2d 100644 --- a/modules/client/front/balance/index/index.html +++ b/modules/client/front/balance/index/index.html @@ -121,7 +121,7 @@ - + @@ -155,9 +155,9 @@ company-fk="$ctrl.companyId" client-fk="$ctrl.$params.id"> - - - \ No newline at end of file + diff --git a/modules/client/front/balance/index/index.js b/modules/client/front/balance/index/index.js index b2529924f..47cefab7d 100644 --- a/modules/client/front/balance/index/index.js +++ b/modules/client/front/balance/index/index.js @@ -55,41 +55,41 @@ class Controller extends Section { } })).then(() => this.getBalances()); } - + getCurrentBalance() { const clientRisks = this.$.riskModel.data; const selectedCompany = this.companyId; const currentBalance = clientRisks.find(balance => { return balance.companyFk === selectedCompany; }); - + return currentBalance && currentBalance.amount; } - + getBalances() { const balances = this.$.model.data; balances.forEach((balance, index) => { if (index === 0) - balance.balance = this.getCurrentBalance(); + balance.balance = this.getCurrentBalance(); if (index > 0) { let previousBalance = balances[index - 1]; balance.balance = previousBalance.balance - (previousBalance.debit - previousBalance.credit); } }); } - + showInvoiceOutDescriptor(event, balance) { if (!balance.isInvoice) return; if (event.defaultPrevented) return; - + this.$.invoiceOutDescriptor.show(event.target, balance.id); } - + changeDescription(balance) { const params = {description: balance.description}; const endpoint = `Receipts/${balance.id}`; this.$http.patch(endpoint, params) - .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))); + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))); } sendEmail(balance) { diff --git a/modules/client/front/balance/index/index.spec.js b/modules/client/front/balance/index/index.spec.js index e130ac655..ea28cb55d 100644 --- a/modules/client/front/balance/index/index.spec.js +++ b/modules/client/front/balance/index/index.spec.js @@ -151,5 +151,19 @@ describe('Client', () => { $httpBackend.flush(); }); }); + + describe('sendEmail()', () => { + it('should send an email', () => { + jest.spyOn(controller.vnEmail, 'send'); + + const $data = {id: 1103}; + + controller.sendEmail($data); + + const expectedPath = `Receipts/${$data.id}/balance-compensation-email`; + + expect(controller.vnEmail.send).toHaveBeenCalledWith(expectedPath); + }); + }); }); }); diff --git a/modules/client/front/balance/index/locale/en.yml b/modules/client/front/balance/index/locale/en.yml index 53cb1313e..0b37c1a1d 100644 --- a/modules/client/front/balance/index/locale/en.yml +++ b/modules/client/front/balance/index/locale/en.yml @@ -1 +1,3 @@ -BILL: N/INV {{ref}} \ No newline at end of file +BILL: N/INV {{ref}} +Notify compensation: Do you want to report compensation to the client by mail? +Send compensation: Send compensation From c51354b13f535b00bbf888e227e117567d637abd Mon Sep 17 00:00:00 2001 From: alexandre Date: Tue, 22 Nov 2022 09:29:25 +0100 Subject: [PATCH 2/4] refs #3949 textfield, edit and create dialog added --- modules/invoiceIn/front/basic-data/index.html | 226 +++++++++++++++++- modules/invoiceIn/front/basic-data/index.js | 170 ++++++++++++- .../invoiceIn/front/basic-data/locale/en.yml | 1 + .../invoiceIn/front/basic-data/locale/es.yml | 15 ++ 4 files changed, 403 insertions(+), 9 deletions(-) create mode 100644 modules/invoiceIn/front/basic-data/locale/en.yml create mode 100644 modules/invoiceIn/front/basic-data/locale/es.yml diff --git a/modules/invoiceIn/front/basic-data/index.html b/modules/invoiceIn/front/basic-data/index.html index be89e502c..4b206372d 100644 --- a/modules/invoiceIn/front/basic-data/index.html +++ b/modules/invoiceIn/front/basic-data/index.html @@ -5,6 +5,24 @@ form="form" save="patch"> + + + + + +
@@ -31,14 +49,14 @@ @@ -57,17 +75,47 @@ {{id}} - {{name}} + + + + + + + + + + + + @@ -104,4 +152,166 @@ ng-click="watcher.loadOriginalData()"> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/invoiceIn/front/basic-data/index.js b/modules/invoiceIn/front/basic-data/index.js index 6a39f35cd..40a01ac18 100644 --- a/modules/invoiceIn/front/basic-data/index.js +++ b/modules/invoiceIn/front/basic-data/index.js @@ -1,9 +1,177 @@ import ngModule from '../module'; import Section from 'salix/components/section'; +import UserError from 'core/lib/user-error'; + +class Controller extends Section { + constructor($element, $, vnFile) { + super($element, $, vnFile); + this.vnFile = vnFile; + this.getAllowedContentTypes(); + this._fileExists = false; + } + + get fileExists() { + return this._fileExists; + } + + getAllowedContentTypes() { + this.$http.get('DmsContainers/allowedContentTypes').then(res => { + const contentTypes = res.data.join(', '); + this.allowedContentTypes = contentTypes; + }); + } + + async checkFileExists(dmsId) { + if (!dmsId) return; + let filter = { + fields: ['id'] + }; + await this.$http.get(`Dms/${dmsId}`, {filter}) + .then(() => this._fileExists = false) + .catch(() => this._fileExists = true); + } + + openEditDialog(dmsId) { + this.getFile(dmsId).then(() => this.$.dmsEditDialog.show()); + } + + openCreateDialog() { + this.dms = { + reference: null, + warehouseId: null, + companyId: null, + dmsTypeId: null, + description: null, + hasFile: true, + hasFileAttached: true, + files: null + }; + this.$.dmsCreateDialog.show(); + } + + async getFile(dmsId) { + const path = `Dms/${dmsId}`; + await this.$http.get(path).then(res => { + const dms = res.data && res.data; + this.dms = { + dmsId: dms.id, + reference: dms.reference, + warehouseId: dms.warehouseFk, + companyId: dms.companyFk, + dmsTypeId: dms.dmsTypeFk, + description: dms.description, + hasFile: dms.hasFile, + hasFileAttached: false, + files: [] + }; + }); + } + + get contentTypesInfo() { + return this.$t('ContentTypesInfo', { + allowedContentTypes: this.allowedContentTypes + }); + } + + downloadFile(dmsId) { + this.vnFile.download(`api/dms/${dmsId}/downloadFile`); + } + + onFileChange(files) { + let hasFileAttached = false; + if (files.length > 0) + hasFileAttached = true; + + this.$.$applyAsync(() => { + this.dms.hasFileAttached = hasFileAttached; + }); + } + + onEdit() { + if (!this.dms.companyId) + throw new UserError(`The company can't be empty`); + if (!this.dms.warehouseId) + throw new UserError(`The warehouse can't be empty`); + if (!this.dms.dmsTypeId) + throw new UserError(`The DMS Type can't be empty`); + if (!this.dms.description) + throw new UserError(`The description can't be empty`); + if (!this.dms.files) + throw new UserError(`The files can't be empty`); + + const query = `dms/${this.dms.dmsId}/updateFile`; + const options = { + method: 'POST', + url: query, + params: this.dms, + headers: { + 'Content-Type': undefined + }, + transformRequest: files => { + const formData = new FormData(); + + for (let i = 0; i < files.length; i++) + formData.append(files[i].name, files[i]); + + return formData; + }, + data: this.dms.files + }; + + this.$http(options).then(res => { + if (res) { + this.vnApp.showSuccess(this.$t('Data saved!')); + this.$.watcher.updateOriginalData(); + } + }); + } + + onCreate() { + if (!this.dms.companyId) + throw new UserError(`The company can't be empty`); + if (!this.dms.warehouseId) + throw new UserError(`The warehouse can't be empty`); + if (!this.dms.dmsTypeId) + throw new UserError(`The DMS Type can't be empty`); + if (!this.dms.description) + throw new UserError(`The description can't be empty`); + if (!this.dms.files) + throw new UserError(`The files can't be empty`); + + const query = `Dms/uploadFile`; + const options = { + method: 'POST', + url: query, + params: this.dms, + headers: { + 'Content-Type': undefined + }, + transformRequest: files => { + const formData = new FormData(); + + for (let i = 0; i < files.length; i++) + formData.append(files[i].name, files[i]); + + return formData; + }, + data: this.dms.files + }; + + this.$http(options).then(res => { + if (res) { + this.vnApp.showSuccess(this.$t('Data saved!')); + this.invoiceIn.dmsFk = res.data[0].id; + this.$.watcher.updateOriginalData(); + } + }); + } +} + +Controller.$inject = ['$element', '$scope', 'vnFile']; ngModule.vnComponent('vnInvoiceInBasicData', { template: require('./index.html'), - controller: Section, + controller: Controller, bindings: { invoiceIn: '<' } diff --git a/modules/invoiceIn/front/basic-data/locale/en.yml b/modules/invoiceIn/front/basic-data/locale/en.yml new file mode 100644 index 000000000..19f4dc8c2 --- /dev/null +++ b/modules/invoiceIn/front/basic-data/locale/en.yml @@ -0,0 +1 @@ +ContentTypesInfo: Allowed file types {{allowedContentTypes}} diff --git a/modules/invoiceIn/front/basic-data/locale/es.yml b/modules/invoiceIn/front/basic-data/locale/es.yml new file mode 100644 index 000000000..e2e494fa5 --- /dev/null +++ b/modules/invoiceIn/front/basic-data/locale/es.yml @@ -0,0 +1,15 @@ +Upload file: Subir fichero +Edit file: Editar fichero +Upload: Subir +Document: Documento +ContentTypesInfo: "Tipos de archivo permitidos: {{allowedContentTypes}}" +Generate identifier for original file: Generar identificador para archivo original +File management: Gestión documental +Hard copy: Copia +This file will be deleted: Este fichero va a ser borrado +Are you sure?: Estas seguro? +File deleted: Fichero eliminado +Remove file: Eliminar fichero +Download file: Descargar fichero +Edit document: Editar documento +Create document: Crear documento From 34fb80beec8468d1d43e3673d11598003cde26a2 Mon Sep 17 00:00:00 2001 From: alexandre Date: Tue, 22 Nov 2022 14:24:19 +0100 Subject: [PATCH 3/4] fix translations --- loopback/locale/en.json | 5 ++++- loopback/locale/es.json | 5 ++++- .../back/methods/receipt/balanceCompensationEmail.js | 10 +++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 887a657a3..2f889ceec 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -137,5 +137,8 @@ "Claim pickup order sent": "Claim pickup order sent [({{claimId}})]({{{claimUrl}}}) to client *{{clientName}}*", "You don't have grant privilege": "You don't have grant privilege", "You don't own the role and you can't assign it to another user": "You don't own the role and you can't assign it to another user", - "Sale(s) blocked, please contact production": "Sale(s) blocked, please contact production" + "Sale(s) blocked, please contact production": "Sale(s) blocked, please contact production", + "Receipt's bank was not found": "Receipt's bank was not found", + "This receipt was not compensated": "This receipt was not compensated", + "Client's email was not found": "Client's email was not found" } diff --git a/loopback/locale/es.json b/loopback/locale/es.json index b85edb5fe..cb1e2d252 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -243,5 +243,8 @@ "You don't own the role and you can't assign it to another user": "No eres el propietario del rol y no puedes asignarlo a otro usuario", "Already has this status": "Ya tiene este estado", "There aren't records for this week": "No existen registros para esta semana", - "Empty data source": "Origen de datos vacio" + "Empty data source": "Origen de datos vacio", + "Receipt's bank was not found": "No se encontró el banco del recibo", + "This receipt was not compensated": "Este recibo no ha sido compensado", + "Client's email was not found": "No se encontró el email del cliente" } diff --git a/modules/client/back/methods/receipt/balanceCompensationEmail.js b/modules/client/back/methods/receipt/balanceCompensationEmail.js index f02eeaf74..afae5dabc 100644 --- a/modules/client/back/methods/receipt/balanceCompensationEmail.js +++ b/modules/client/back/methods/receipt/balanceCompensationEmail.js @@ -28,18 +28,18 @@ module.exports = Self => { const models = Self.app.models; const receipt = await models.Receipt.findById(id, {fields: ['clientFk', 'bankFk']}); - const bank = await models.Bank.findById(receipt.bankFk, null); + const bank = await models.Bank.findById(receipt.bankFk); if (!bank) - throw new UserError('Receipt\'s bank was not found'); + throw new UserError(`Receipt's bank was not found`); - const accountingType = await models.AccountingType.findById(bank.accountingTypeFk, null); + const accountingType = await models.AccountingType.findById(bank.accountingTypeFk); if (!(accountingType && accountingType.code == 'compensation')) - throw new UserError('Receipt\'s type was not compensation'); + throw new UserError(`This receipt was not compensated`); const client = await models.Client.findById(receipt.clientFk, {fields: ['email']}); if (!client.email) - throw new UserError('Client\'s email was not found'); + throw new UserError(`Client's email was not found`); else { const email = new Email('balance-compensation', { lang: ctx.req.getLocale(), From 164b797ab88d9e91cb9b62cd850fa86776c8f6ad Mon Sep 17 00:00:00 2001 From: alexandre Date: Wed, 23 Nov 2022 11:09:32 +0100 Subject: [PATCH 4/4] front and e2e tests done --- e2e/helpers/selectors.js | 11 ++ e2e/paths/09-invoice-in/03_basic_data.spec.js | 140 ++++++++++++++++++ modules/invoiceIn/front/basic-data/index.html | 6 +- modules/invoiceIn/front/basic-data/index.js | 78 +++++----- .../invoiceIn/front/basic-data/index.spec.js | 102 +++++++++++++ 5 files changed, 296 insertions(+), 41 deletions(-) create mode 100644 modules/invoiceIn/front/basic-data/index.spec.js diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 1a87b5bf0..aed129805 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -1038,6 +1038,17 @@ export default { booked: 'vn-invoice-in-basic-data vn-date-picker[ng-model="$ctrl.invoiceIn.booked"]', currency: 'vn-invoice-in-basic-data vn-autocomplete[ng-model="$ctrl.invoiceIn.currencyFk"]', company: 'vn-invoice-in-basic-data vn-autocomplete[ng-model="$ctrl.invoiceIn.companyFk"]', + dms: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"]', + download: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.prepend > prepend > vn-icon-button', + edit: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.append > append > vn-icon-button[icon="edit"]', + create: 'vn-invoice-in-basic-data vn-textfield[ng-model="$ctrl.invoiceIn.dmsFk"] > div.container > div.append > append > vn-icon-button[icon="add_circle"]', + reference: 'vn-textfield[ng-model="$ctrl.dms.reference"]', + companyId: 'vn-autocomplete[ng-model="$ctrl.dms.companyId"]', + warehouseId: 'vn-autocomplete[ng-model="$ctrl.dms.warehouseId"]', + dmsTypeId: 'vn-autocomplete[ng-model="$ctrl.dms.dmsTypeId"]', + description: 'vn-textarea[ng-model="$ctrl.dms.description"]', + inputFile: 'vn-input-file[ng-model="$ctrl.dms.files"]', + confirm: 'button[response="accept"]', save: 'vn-invoice-in-basic-data button[type=submit]' }, invoiceInTax: { diff --git a/e2e/paths/09-invoice-in/03_basic_data.spec.js b/e2e/paths/09-invoice-in/03_basic_data.spec.js index 0c3f914e4..0a28ed191 100644 --- a/e2e/paths/09-invoice-in/03_basic_data.spec.js +++ b/e2e/paths/09-invoice-in/03_basic_data.spec.js @@ -4,6 +4,7 @@ import getBrowser from '../../helpers/puppeteer'; describe('InvoiceIn basic data path', () => { let browser; let page; + let newDms; beforeAll(async() => { browser = await getBrowser(); @@ -24,6 +25,8 @@ describe('InvoiceIn basic data path', () => { await page.autocompleteSearch(selectors.invoiceInBasicData.supplier, 'Verdnatura'); await page.clearInput(selectors.invoiceInBasicData.supplierRef); await page.write(selectors.invoiceInBasicData.supplierRef, '9999'); + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.write(selectors.invoiceInBasicData.dms, '2'); await page.pickDate(selectors.invoiceInBasicData.bookEntried, now); await page.pickDate(selectors.invoiceInBasicData.booked, now); await page.autocompleteSearch(selectors.invoiceInBasicData.currency, 'USD'); @@ -61,4 +64,141 @@ describe('InvoiceIn basic data path', () => { expect(result).toEqual('ORN'); }); + + it(`should confirm the invoiceIn dms was edited`, async() => { + const result = await page + .waitToGetProperty(selectors.invoiceInBasicData.dms, 'value'); + + expect(result).toEqual('2'); + }); + + it(`should create a new invoiceIn dms and save the changes`, async() => { + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.waitToClick(selectors.invoiceInBasicData.create); + + await page.clearInput(selectors.invoiceInBasicData.reference); + await page.write(selectors.invoiceInBasicData.reference, 'New Dms'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + let message = await page.waitForSnackbar(); + + expect(message.text).toContain('The company can\'t be empty'); + + await page.clearInput(selectors.invoiceInBasicData.companyId); + await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'VNL'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The warehouse can\'t be empty'); + + await page.clearInput(selectors.invoiceInBasicData.warehouseId); + await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Warehouse One'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The DMS Type can\'t be empty'); + + await page.clearInput(selectors.invoiceInBasicData.dmsTypeId); + await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Ticket'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The description can\'t be empty'); + + await page.waitToClick(selectors.invoiceInBasicData.description); + await page.write(selectors.invoiceInBasicData.description, 'Dms without edition.'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('The files can\'t be empty'); + + let currentDir = process.cwd(); + let filePath = `${currentDir}/e2e/assets/thermograph.jpeg`; + + const [fileChooser] = await Promise.all([ + page.waitForFileChooser(), + page.waitToClick(selectors.invoiceInBasicData.inputFile) + ]); + await fileChooser.accept([filePath]); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + + newDms = await page + .waitToGetProperty(selectors.invoiceInBasicData.dms, 'value'); + }); + + it(`should confirm the invoiceIn was edited with the new dms`, async() => { + await page.reloadSection('invoiceIn.card.basicData'); + const result = await page + .waitToGetProperty(selectors.invoiceInBasicData.dms, 'value'); + + expect(result).toEqual(newDms); + }); + + it(`should edit the invoiceIn`, async() => { + await page.waitToClick(selectors.invoiceInBasicData.edit); + + await page.clearInput(selectors.invoiceInBasicData.reference); + await page.write(selectors.invoiceInBasicData.reference, 'Dms Edited'); + await page.clearInput(selectors.invoiceInBasicData.companyId); + await page.autocompleteSearch(selectors.invoiceInBasicData.companyId, 'CCs'); + await page.clearInput(selectors.invoiceInBasicData.warehouseId); + await page.autocompleteSearch(selectors.invoiceInBasicData.warehouseId, 'Algemesi'); + await page.clearInput(selectors.invoiceInBasicData.dmsTypeId); + await page.autocompleteSearch(selectors.invoiceInBasicData.dmsTypeId, 'Basura'); + await page.waitToClick(selectors.invoiceInBasicData.description); + await page.write(selectors.invoiceInBasicData.description, ' Nevermind, now is edited.'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + let message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + }); + + it(`should confirm the new dms has been edited`, async() => { + await page.reloadSection('invoiceIn.card.basicData'); + await page.waitToClick(selectors.invoiceInBasicData.edit); + + const reference = await page + .waitToGetProperty(selectors.invoiceInBasicData.reference, 'value'); + const companyId = await page + .waitToGetProperty(selectors.invoiceInBasicData.companyId, 'value'); + const warehouseId = await page + .waitToGetProperty(selectors.invoiceInBasicData.warehouseId, 'value'); + const dmsTypeId = await page + .waitToGetProperty(selectors.invoiceInBasicData.dmsTypeId, 'value'); + const description = await page + .waitToGetProperty(selectors.invoiceInBasicData.description, 'value'); + + expect(reference).toEqual('Dms Edited'); + expect(companyId).toEqual('CCs'); + expect(warehouseId).toEqual('Algemesi'); + expect(dmsTypeId).toEqual('Basura'); + expect(description).toEqual('Dms without edition. Nevermind, now is edited.'); + + await page.waitToClick(selectors.invoiceInBasicData.confirm); + }); + + it(`should disable edit and download if dms doesn't exists, and set back the original dms`, async() => { + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.write(selectors.invoiceInBasicData.dms, '9999'); + + await page.waitForSelector(`${selectors.invoiceInBasicData.download}.disabled`); + await page.waitForSelector(`${selectors.invoiceInBasicData.edit}.disabled`); + + await page.clearInput(selectors.invoiceInBasicData.dms); + await page.write(selectors.invoiceInBasicData.dms, '1'); + + await page.waitToClick(selectors.invoiceInBasicData.save); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + }); }); diff --git a/modules/invoiceIn/front/basic-data/index.html b/modules/invoiceIn/front/basic-data/index.html index 4b206372d..a22abbb33 100644 --- a/modules/invoiceIn/front/basic-data/index.html +++ b/modules/invoiceIn/front/basic-data/index.html @@ -82,7 +82,7 @@ rule> - Save - { - const contentTypes = res.data.join(', '); - this.allowedContentTypes = contentTypes; + get contentTypesInfo() { + return this.$t('ContentTypesInfo', { + allowedContentTypes: this.allowedContentTypes }); } + get editDownloadDisabled() { + return this._editDownloadDisabled; + } + async checkFileExists(dmsId) { if (!dmsId) return; let filter = { fields: ['id'] }; await this.$http.get(`Dms/${dmsId}`, {filter}) - .then(() => this._fileExists = false) - .catch(() => this._fileExists = true); - } - - openEditDialog(dmsId) { - this.getFile(dmsId).then(() => this.$.dmsEditDialog.show()); - } - - openCreateDialog() { - this.dms = { - reference: null, - warehouseId: null, - companyId: null, - dmsTypeId: null, - description: null, - hasFile: true, - hasFileAttached: true, - files: null - }; - this.$.dmsCreateDialog.show(); + .then(() => this._editDownloadDisabled = false) + .catch(() => this._editDownloadDisabled = true); } async getFile(dmsId) { @@ -67,12 +53,33 @@ class Controller extends Section { }); } - get contentTypesInfo() { - return this.$t('ContentTypesInfo', { - allowedContentTypes: this.allowedContentTypes + getAllowedContentTypes() { + this.$http.get('DmsContainers/allowedContentTypes').then(res => { + if (res.data.length > 0) { + const contentTypes = res.data.join(', '); + this.allowedContentTypes = contentTypes; + } }); } + openEditDialog(dmsId) { + this.getFile(dmsId).then(() => this.$.dmsEditDialog.show()); + } + + openCreateDialog() { + this.dms = { + reference: null, + warehouseId: null, + companyId: null, + dmsTypeId: null, + description: null, + hasFile: true, + hasFileAttached: true, + files: null + }; + this.$.dmsCreateDialog.show(); + } + downloadFile(dmsId) { this.vnFile.download(`api/dms/${dmsId}/downloadFile`); } @@ -96,8 +103,6 @@ class Controller extends Section { throw new UserError(`The DMS Type can't be empty`); if (!this.dms.description) throw new UserError(`The description can't be empty`); - if (!this.dms.files) - throw new UserError(`The files can't be empty`); const query = `dms/${this.dms.dmsId}/updateFile`; const options = { @@ -121,7 +126,7 @@ class Controller extends Section { this.$http(options).then(res => { if (res) { this.vnApp.showSuccess(this.$t('Data saved!')); - this.$.watcher.updateOriginalData(); + if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id; } }); } @@ -160,8 +165,7 @@ class Controller extends Section { this.$http(options).then(res => { if (res) { this.vnApp.showSuccess(this.$t('Data saved!')); - this.invoiceIn.dmsFk = res.data[0].id; - this.$.watcher.updateOriginalData(); + if (res.data.length > 0) this.invoiceIn.dmsFk = res.data[0].id; } }); } diff --git a/modules/invoiceIn/front/basic-data/index.spec.js b/modules/invoiceIn/front/basic-data/index.spec.js new file mode 100644 index 000000000..09aa08293 --- /dev/null +++ b/modules/invoiceIn/front/basic-data/index.spec.js @@ -0,0 +1,102 @@ +import './index.js'; +import watcher from 'core/mocks/watcher'; + +describe('InvoiceIn', () => { + describe('Component vnInvoiceInBasicData', () => { + let controller; + let $scope; + let $httpBackend; + let $httpParamSerializer; + + beforeEach(ngModule('invoiceIn')); + + beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => { + $scope = $rootScope.$new(); + $httpBackend = _$httpBackend_; + $httpParamSerializer = _$httpParamSerializer_; + const $element = angular.element(''); + controller = $componentController('vnInvoiceInBasicData', {$element, $scope}); + controller.$.watcher = watcher; + $httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond({}); + })); + + describe('onFileChange()', () => { + it('should set dms hasFileAttached property to true if has any files', () => { + const files = [{id: 1, name: 'MyFile'}]; + controller.onFileChange(files); + + $scope.$apply(); + + expect(controller.dms.hasFileAttached).toBeTruthy(); + }); + }); + + describe('checkFileExists()', () => { + it(`should return false if a file exists`, () => { + const fileIdExists = 1; + controller.checkFileExists(fileIdExists); + + expect(controller.editDownloadDisabled).toBe(false); + }); + }); + + describe('onEdit()', () => { + it(`should perform a POST query to edit the dms properties`, () => { + jest.spyOn(controller.vnApp, 'showSuccess'); + + const dms = { + dmsId: 1, + reference: 'Ref1', + warehouseId: 1, + companyId: 442, + dmsTypeId: 20, + description: 'This is a description', + files: [] + }; + + controller.dms = dms; + const serializedParams = $httpParamSerializer(controller.dms); + const query = `dms/${controller.dms.dmsId}/updateFile?${serializedParams}`; + + $httpBackend.expectPOST(query).respond({}); + controller.onEdit(); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalled(); + }); + }); + + describe('onCreate()', () => { + it(`should perform a POST query to create a new dms`, () => { + jest.spyOn(controller.vnApp, 'showSuccess'); + + const dms = { + reference: 'Ref1', + warehouseId: 1, + companyId: 442, + dmsTypeId: 20, + description: 'This is a description', + files: [{ + lastModified: 1668673957761, + lastModifiedDate: new Date(), + name: 'file-example.png', + size: 19653, + type: 'image/png', + webkitRelativePath: '' + }] + }; + + controller.dms = dms; + const serializedParams = $httpParamSerializer(controller.dms); + const query = `Dms/uploadFile?${serializedParams}`; + + $httpBackend.expectPOST(query).respond({}); + controller.onCreate(); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalled(); + }); + }); + }); +}); +