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 41cd84638..ac7900f70 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -1859,7 +1859,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 diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index aed129805..06fabe3e6 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/loopback/locale/en.json b/loopback/locale/en.json index 36da9f661..ad110ec98 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -138,5 +138,8 @@ "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", "Ticket merged": "Ticket [{{id}}]({{{fullPath}}}) ({{{originDated}}}) merged with [{{tfId}}]({{{fullPathFuture}}}) ({{{futureDated}}})", - "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 747007713..ecaae31b9 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -244,5 +244,8 @@ "Ticket merged": "Ticket [{{id}}]({{{fullPath}}}) ({{{originDated}}}) fusionado con [{{tfId}}]({{{fullPathFuture}}}) ({{{futureDated}}})", "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 e9ded147d..afae5dabc 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); + if (!bank) + throw new UserError(`Receipt's bank was not found`); - return email.send(); + const accountingType = await models.AccountingType.findById(bank.accountingTypeFk); + if (!(accountingType && accountingType.code == '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`); + 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