diff --git a/back/models/sage-withholding.json b/back/models/sage-withholding.json index 8d93daeae..dddbcfd74 100644 --- a/back/models/sage-withholding.json +++ b/back/models/sage-withholding.json @@ -6,6 +6,9 @@ "table": "sage.TiposRetencion" } }, + "log": { + "showField": "withholding" + }, "properties": { "id": { "type": "Number", diff --git a/db/changes/10340-summer/00-sample.sql b/db/changes/10340-summer/00-sample.sql new file mode 100644 index 000000000..d4858ac72 --- /dev/null +++ b/db/changes/10340-summer/00-sample.sql @@ -0,0 +1,7 @@ +ALTER TABLE `vn`.`sample` ADD COLUMN + (`datepickerEnabled` TINYINT(1) NOT NULL DEFAULT 0); + +ALTER TABLE `vn`.`sample` MODIFY code VARCHAR(25) charset utf8 NOT NULL; + +INSERT INTO `vn`.`sample` (code, description, isVisible, hasCompany, hasPreview, datepickerEnabled) + VALUES ('client-debt-statement', 'Extracto del cliente', 1, 0, 1, 1); \ No newline at end of file diff --git a/db/changes/10340-summer/00-ticket_close.sql b/db/changes/10340-summer/00-ticket_close.sql new file mode 100644 index 000000000..a8086549c --- /dev/null +++ b/db/changes/10340-summer/00-ticket_close.sql @@ -0,0 +1,109 @@ +drop procedure `vn`.`ticket_close`; + +DELIMITER $$ +$$ +create + definer = root@`%` procedure `vn`.`ticket_close`() +BEGIN +/** + * Realiza el cierre de todos los + * tickets de la tabla tmp.ticket_close. + * + * @table tmp.ticket_close(ticketFk) Identificadores de los tickets a cerrar + */ + DECLARE vDone BOOL; + DECLARE vClientFk INT; + DECLARE vCurTicketFk INT; + DECLARE vIsTaxDataChecked BOOL; + DECLARE vCompanyFk INT; + DECLARE vShipped DATE; + DECLARE vNewInvoiceId INT; + DECLARE vHasDailyInvoice BOOL; + DECLARE vWithPackage BOOL; + DECLARE vHasToInvoice BOOL; + + DECLARE cur CURSOR FOR + SELECT ticketFk FROM tmp.ticket_close; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN + RESIGNAL; + END; + + OPEN cur; + + proc: LOOP + SET vDone = FALSE; + + FETCH cur INTO vCurTicketFk; + + IF vDone THEN + LEAVE proc; + END IF; + + -- Fetch ticket data + SELECT + c.id, + c.isTaxDataChecked, + t.companyFk, + t.shipped, + co.hasDailyInvoice, + w.isManaged, + c.hasToInvoice + INTO vClientFk, + vIsTaxDataChecked, + vCompanyFk, + vShipped, + vHasDailyInvoice, + vWithPackage, + vHasToInvoice + FROM ticket t + JOIN `client` c ON c.id = t.clientFk + JOIN province p ON p.id = c.provinceFk + JOIN country co ON co.id = p.countryFk + JOIN warehouse w ON w.id = t.warehouseFk + WHERE t.id = vCurTicketFk; + + INSERT INTO ticketPackaging (ticketFk, packagingFk, quantity) + (SELECT vCurTicketFk, p.id, COUNT(*) + FROM expedition e + JOIN packaging p ON p.itemFk = e.itemFk + WHERE e.ticketFk = vCurTicketFk AND p.isPackageReturnable + AND vWithPackage + GROUP BY p.itemFk); + + -- No retornables o no catalogados + INSERT INTO sale (itemFk, ticketFk, concept, quantity, price, isPriceFixed) + (SELECT e.itemFk, vCurTicketFk, i.name, COUNT(*) AS amount, getSpecialPrice(e.itemFk, vClientFk), 1 + FROM expedition e + JOIN item i ON i.id = e.itemFk + LEFT JOIN packaging p ON p.itemFk = i.id + WHERE e.ticketFk = vCurTicketFk AND IFNULL(p.isPackageReturnable, 0) = 0 + AND getSpecialPrice(e.itemFk, vClientFk) > 0 + GROUP BY e.itemFk); + + CALL vn.zonePromo_Make(); + + IF(vHasDailyInvoice) AND vHasToInvoice THEN + + -- Facturacion rapida + CALL ticketTrackingAdd(vCurTicketFk, 'DELIVERED', NULL); + -- Facturar si está contabilizado + IF vIsTaxDataChecked THEN + CALL invoiceOut_newFromClient( + vClientFk, + (SELECT invoiceSerial(vClientFk, vCompanyFk, 'M')), + vShipped, + vCompanyFk, + NULL, + NULL, + vNewInvoiceId); + END IF; + ELSE + CALL ticketTrackingAdd(vCurTicketFk, (SELECT vn.getAlert3State(vCurTicketFk)), NULL); + END IF; + END LOOP; + + CLOSE cur; +END;;$$ +DELIMITER ; diff --git a/db/changes/10340-summer/00-ticket_closeAll.sql b/db/changes/10340-summer/00-ticket_closeAll.sql new file mode 100644 index 000000000..6441f2c1a --- /dev/null +++ b/db/changes/10340-summer/00-ticket_closeAll.sql @@ -0,0 +1,118 @@ +drop procedure `vn`.`ticket_closeAll`; + +DELIMITER $$ +$$ +create definer = root@`%` procedure `vn`.`ticket_closeAll__`() +BEGIN +/** + * Realiza el cierre de todos los + * tickets de la tabla tmp.ticketClosure. + * + * @param vTicketFk Id del ticket + */ + DECLARE vDone BOOL; + DECLARE vClientFk INT; + DECLARE vCurTicketFk INT; + DECLARE vIsTaxDataChecked BOOL; + DECLARE vCompanyFk INT; + DECLARE vShipped DATE; + DECLARE vNewInvoiceId INT; + DECLARE vHasDailyInvoice BOOL; + DECLARE vWithPackage BOOL; + DECLARE vHasToInvoice BOOL; + + DECLARE cur CURSOR FOR + SELECT ticketFk FROM tmp.ticketClosure; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE; + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN + RESIGNAL; + END; + + DROP TEMPORARY TABLE IF EXISTS tmp.ticketClosure2; + CREATE TEMPORARY TABLE tmp.ticketClosure2 + SELECT ticketFk FROM tmp.ticketClosure; + + INSERT INTO tmp.ticketClosure + SELECT id FROM stowaway s + JOIN tmp.ticketClosure2 tc ON s.shipFk = tc.ticketFk; + OPEN cur; + + proc: LOOP + SET vDone = FALSE; + + FETCH cur INTO vCurTicketFk; + + IF vDone THEN + LEAVE proc; + END IF; + + -- ticketClosure start + SELECT + c.id, + c.isTaxDataChecked, + t.companyFk, + t.shipped, + co.hasDailyInvoice, + w.isManaged, + c.hasToInvoice + INTO vClientFk, + vIsTaxDataChecked, + vCompanyFk, + vShipped, + vHasDailyInvoice, + vWithPackage, + vHasToInvoice + FROM ticket t + JOIN `client` c ON c.id = t.clientFk + JOIN province p ON p.id = c.provinceFk + JOIN country co ON co.id = p.countryFk + JOIN warehouse w ON w.id = t.warehouseFk + WHERE t.id = vCurTicketFk; + + INSERT INTO ticketPackaging (ticketFk, packagingFk, quantity) + (SELECT vCurTicketFk, p.id, COUNT(*) + FROM expedition e + JOIN packaging p ON p.itemFk = e.itemFk + WHERE e.ticketFk = vCurTicketFk AND p.isPackageReturnable + AND vWithPackage + GROUP BY p.itemFk); + + -- No retornables o no catalogados + INSERT INTO sale (itemFk, ticketFk, concept, quantity, price, isPriceFixed) + (SELECT e.itemFk, vCurTicketFk, i.name, COUNT(*) AS amount, getSpecialPrice(e.itemFk, vClientFk), 1 + FROM expedition e + JOIN item i ON i.id = e.itemFk + LEFT JOIN packaging p ON p.itemFk = i.id + WHERE e.ticketFk = vCurTicketFk AND IFNULL(p.isPackageReturnable, 0) = 0 + AND getSpecialPrice(e.itemFk, vClientFk) > 0 + GROUP BY e.itemFk); + + CALL vn.zonePromo_Make(); + + IF(vHasDailyInvoice) AND vHasToInvoice THEN + + -- Facturacion rapida + CALL ticketTrackingAdd(vCurTicketFk, 'DELIVERED', NULL); + -- Facturar si está contabilizado + IF vIsTaxDataChecked THEN + CALL invoiceOut_newFromClient( + vClientFk, + (SELECT invoiceSerial(vClientFk, vCompanyFk, 'M')), + vShipped, + vCompanyFk, + NULL, + NULL, + vNewInvoiceId); + END IF; + ELSE + CALL ticketTrackingAdd(vCurTicketFk, (SELECT vn.getAlert3State(vCurTicketFk)), NULL); + END IF; + END LOOP; + + CLOSE cur; + + DROP TEMPORARY TABLE IF EXISTS tmp.ticketClosure; +END;;$$ +DELIMITER ; + diff --git a/db/changes/10340-summer/00-ticket_closeByTicket.sql b/db/changes/10340-summer/00-ticket_closeByTicket.sql new file mode 100644 index 000000000..a24ea5ad9 --- /dev/null +++ b/db/changes/10340-summer/00-ticket_closeByTicket.sql @@ -0,0 +1,34 @@ +drop procedure `vn`.`ticket_closeByTicket`; + +DELIMITER $$ +$$ +create + definer = root@`%` procedure `vn`.`ticket_closeByTicket`(IN vTicketFk int) +BEGIN + +/** + * Inserta el ticket en la tabla temporal + * para ser cerrado. + * + * @param vTicketFk Id del ticket + */ + + DROP TEMPORARY TABLE IF EXISTS tmp.ticket_close; + CREATE TEMPORARY TABLE tmp.ticket_close ENGINE = MEMORY ( + SELECT + t.id AS ticketFk + FROM expedition e + INNER JOIN ticket t ON t.id = e.ticketFk + LEFT JOIN ticketState ts ON ts.ticketFk = t.id + JOIN alertLevel al ON al.id = ts.alertLevel + WHERE + al.code = 'PACKED' + AND t.id = vTicketFk + AND t.refFk IS NULL + GROUP BY e.ticketFk); + + CALL ticket_close(); + + DROP TEMPORARY TABLE tmp.ticket_close; +END;;$$ +DELIMITER ; diff --git a/db/changes/10340-summer/01-credit_request.sql b/db/changes/10340-summer/01-credit_request.sql new file mode 100644 index 000000000..821623326 --- /dev/null +++ b/db/changes/10340-summer/01-credit_request.sql @@ -0,0 +1,2 @@ +INSERT INTO `vn`.`sample` (`code`, `description`, `isVisible`, `hasCompany`, `hasPreview`) + VALUES ('credit-request', 'Solicitud de crédito', 1, 1, 1); \ No newline at end of file diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index e1aaa2cd6..c44191381 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2339,6 +2339,14 @@ INSERT INTO `vn`.`duaInvoiceIn`(`id`, `duaFk`, `invoiceInFk`) (9, 9, 9), (10, 10, 10); +INSERT INTO `vn`.`invoiceInTax` (`invoiceInFk`, `taxCodeFk`, `taxableBase`, `expenceFk`, `foreignValue`, `taxTypeSageFk`, `transactionTypeSageFk`, `created`) + VALUES + (1, 4, 99.99, '2000000000', null, null, null, CURDATE()), + (2, 4, 999.99, '2000000000', null, null, null, CURDATE()), + (3, 4, 1000.50, '2000000000', null, null, null, CURDATE()), + (4, 4, 0.50, '2000000000', null, null, null, CURDATE()), + (5, 4, 150.50, '2000000000', null, null, null, CURDATE()); + INSERT INTO `vn`.`ticketRecalc`(`ticketFk`) SELECT `id` FROM `vn`.`ticket` t diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index e2ff0f0e4..a561a08cf 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -258,7 +258,9 @@ export default { }, clientLog: { lastModificationPreviousValue: 'vn-client-log vn-table vn-td.before', - lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after' + lastModificationCurrentValue: 'vn-client-log vn-table vn-td.after', + penultimateModificationPreviousValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.before', + penultimateModificationCurrentValue: 'vn-client-log vn-table vn-tr:nth-child(2) vn-td.after' }, clientBalance: { @@ -462,8 +464,8 @@ export default { itemDescriptorPopover: '.vn-popover.shown vn-item-descriptor', itemDescriptorPopoverItemDiaryButton: 'vn-item-descriptor a[href="#!/item/2/diary?warehouseFk=5&lineFk=16"]', popoverDiaryButton: '.vn-popover.shown vn-item-descriptor vn-icon[icon="icon-transaction"]', - firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(4)', - firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(7)', + firstSaleQuantity: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(5)', + firstSaleDiscount: 'vn-ticket-summary [name="sales"] vn-table > div > vn-tbody > vn-tr > vn-td:nth-child(8)', invoiceOutRef: 'vn-ticket-summary > vn-card > vn-horizontal > vn-one:nth-child(1) > vn-label-value:nth-child(7) > section > span', setOk: 'vn-ticket-summary vn-button[label="SET OK"] > button', descriptorTicketId: 'vn-ticket-descriptor > vn-descriptor-content > div > div.body > div.top > div' @@ -566,18 +568,18 @@ export default { moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input', transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text', transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable', - firstSaleId: 'vn-ticket-sale vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(5) > span', + firstSaleId: 'vn-ticket-sale vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(6) > span', firstSaleClaimIcon: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) vn-icon[icon="icon-claims"]', firstSaleDescriptorImage: '.vn-popover.shown vn-item-descriptor img', firstSaleThumbnailImage: 'vn-ticket-sale:nth-child(1) vn-tr:nth-child(1) vn-td:nth-child(3) > img', firstSaleZoomedImage: 'body > div > div > img', firstSaleQuantity: 'vn-ticket-sale [ng-model="sale.quantity"]', - firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(6)', - firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(8) > span', + firstSaleQuantityCell: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td-editable:nth-child(7)', + firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(9) > span', firstSalePriceInput: '.vn-popover.shown input[ng-model="$ctrl.field"]', - firstSaleDiscount: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(9) > span', + firstSaleDiscount: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(10) > span', firstSaleDiscountInput: '.vn-popover.shown [ng-model="$ctrl.field"]', - firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(10)', + firstSaleImport: 'vn-ticket-sale:nth-child(1) vn-td:nth-child(11)', firstSaleReservedIcon: 'vn-ticket-sale vn-tr:nth-child(1) > vn-td:nth-child(2) > vn-icon:nth-child(3)', firstSaleColour: 'vn-ticket-sale vn-tr:nth-child(1) vn-fetched-tags section', firstSaleCheckbox: 'vn-ticket-sale vn-tr:nth-child(1) vn-check[ng-model="sale.checked"]', @@ -585,8 +587,8 @@ export default { secondSaleId: 'vn-ticket-sale:nth-child(2) vn-td-editable:nth-child(4) text > span', secondSaleIdAutocomplete: 'vn-ticket-sale vn-tr:nth-child(2) vn-autocomplete[ng-model="sale.itemFk"]', secondSaleQuantity: 'vn-ticket-sale vn-table vn-tr:nth-child(2) vn-input-number', - secondSaleQuantityCell: 'vn-ticket-sale > div > vn-card > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(6)', - secondSaleConceptCell: 'vn-ticket-sale vn-tbody > :nth-child(2) > :nth-child(7)', + secondSaleQuantityCell: 'vn-ticket-sale > div > vn-card > vn-table > div > vn-tbody > vn-tr:nth-child(2) > vn-td-editable:nth-child(7)', + secondSaleConceptCell: 'vn-ticket-sale vn-tbody > :nth-child(2) > :nth-child(8)', secondSaleConceptInput: 'vn-ticket-sale vn-tbody > :nth-child(2) > vn-td-editable.ng-isolate-scope.selected vn-textfield', totalImport: 'vn-ticket-sale vn-one.taxes > p:nth-child(3) > strong', selectAllSalesCheckbox: 'vn-ticket-sale vn-thead vn-check', @@ -936,7 +938,8 @@ export default { invoiceInDescriptor: { moreMenu: 'vn-invoice-in-descriptor vn-icon-button[icon=more_vert]', moreMenuDeleteInvoiceIn: '.vn-menu [name="deleteInvoice"]', - acceptDeleteButton: '.vn-confirm.shown button[response="accept"]' + moreMenuCloneInvoiceIn: '.vn-menu [name="cloneInvoice"]', + acceptButton: '.vn-confirm.shown button[response="accept"]' }, invoiceInBasicData: { issued: 'vn-invoice-in-basic-data vn-date-picker[ng-model="$ctrl.invoiceIn.issued"]', diff --git a/e2e/paths/02-client/07_edit_web_access.spec.js b/e2e/paths/02-client/07_edit_web_access.spec.js index b1af95638..bcd476f6b 100644 --- a/e2e/paths/02-client/07_edit_web_access.spec.js +++ b/e2e/paths/02-client/07_edit_web_access.spec.js @@ -16,8 +16,15 @@ describe('Client Edit web access path', () => { await browser.close(); }); - it(`should uncheck the Enable web access checkbox and update the name`, async() => { + it('should uncheck the Enable web access checkbox', async() => { await page.waitToClick(selectors.clientWebAccess.enableWebAccessCheckbox); + await page.waitToClick(selectors.clientWebAccess.saveButton); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('Data saved!'); + }); + + it(`should update the name`, async() => { await page.clearInput(selectors.clientWebAccess.userName); await page.write(selectors.clientWebAccess.userName, 'Hulk'); await page.waitToClick(selectors.clientWebAccess.saveButton); @@ -26,9 +33,8 @@ describe('Client Edit web access path', () => { expect(message.text).toContain('Data saved!'); }); - it('should confirm web access is now unchecked', async() => { - await page.accessToSection('client.card.basicData'); - await page.accessToSection('client.card.webAccess'); + it('should reload the section and confirm web access is now unchecked', async() => { + await page.reloadSection('client.card.webAccess'); const result = await page.checkboxState(selectors.clientWebAccess.enableWebAccessCheckbox); expect(result).toBe('unchecked'); @@ -44,13 +50,23 @@ describe('Client Edit web access path', () => { await page.accessToSection('client.card.log'); }); - it(`should confirm the log is showing the updated data for the client`, async() => { + it(`should confirm the last log is showing the updated client name and no modifications on the active checkbox`, async() => { let lastModificationPreviousValue = await page .waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText'); let lastModificationCurrentValue = await page .waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText'); - expect(lastModificationPreviousValue).toEqual('name BruceBanner active true'); + expect(lastModificationPreviousValue).toEqual('name BruceBanner active false'); expect(lastModificationCurrentValue).toEqual('name Hulk active false'); }); + + it(`should confirm the penultimate log is showing the updated avtive field and no modifications on the client name`, async() => { + let penultimateModificationPreviousValue = await page + .waitToGetProperty(selectors.clientLog.penultimateModificationPreviousValue, 'innerText'); + let penultimateModificationCurrentValue = await page + .waitToGetProperty(selectors.clientLog.penultimateModificationCurrentValue, 'innerText'); + + expect(penultimateModificationPreviousValue).toEqual('name BruceBanner active true'); + expect(penultimateModificationCurrentValue).toEqual('name BruceBanner active false'); + }); }); diff --git a/e2e/paths/09-invoice-in/02_descriptor.spec.js b/e2e/paths/09-invoice-in/02_descriptor.spec.js index 2386dada4..02bbce7ac 100644 --- a/e2e/paths/09-invoice-in/02_descriptor.spec.js +++ b/e2e/paths/09-invoice-in/02_descriptor.spec.js @@ -10,16 +10,30 @@ describe('InvoiceIn descriptor path', () => { page = browser.page; await page.loginAndModule('administrative', 'invoiceIn'); await page.accessToSearchResult('10'); + await page.accessToSection('invoiceIn.card.basicData'); }); afterAll(async() => { await browser.close(); }); - it('should delete the invoiceIn using the descriptor more menu', async() => { + it('should clone the invoiceIn using the descriptor more menu', async() => { + await page.waitToClick(selectors.invoiceInDescriptor.moreMenu); + await page.waitToClick(selectors.invoiceInDescriptor.moreMenuCloneInvoiceIn); + await page.waitToClick(selectors.invoiceInDescriptor.acceptButton); + const message = await page.waitForSnackbar(); + + expect(message.text).toContain('InvoiceIn cloned'); + }); + + it('should have been redirected to the created invoiceIn summary', async() => { + await page.waitForState('invoiceIn.card.summary'); + }); + + it('should delete the cloned invoiceIn using the descriptor more menu', async() => { await page.waitToClick(selectors.invoiceInDescriptor.moreMenu); await page.waitToClick(selectors.invoiceInDescriptor.moreMenuDeleteInvoiceIn); - await page.waitToClick(selectors.invoiceInDescriptor.acceptDeleteButton); + await page.waitToClick(selectors.invoiceInDescriptor.acceptButton); const message = await page.waitForSnackbar(); expect(message.text).toContain('InvoiceIn deleted'); diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 75804ba21..d77b0c26d 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -57,7 +57,14 @@ "The postcode doesn't exist. Please enter a correct one": "The postcode doesn't exist. Please enter a correct one", "Can't create stowaway for this ticket": "Can't create stowaway for this ticket", "Swift / BIC can't be empty": "Swift / BIC can't be empty", - "Bought units from buy request": "Bought {{quantity}} units of {{concept}} [{{itemId}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})", + "Deleted sales from ticket": "I have deleted the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", + "Added sale to ticket": "I have added the following line to the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", + "Changed sale discount": "I have changed the following lines discounts from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Created claim": "I have created the claim [{{claimId}}]({{{claimUrl}}}) for the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Changed sale price": "I have changed the price of [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) from {{oldPrice}}€ ➔ *{{newPrice}}€* of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "I have changed the quantity of [{{itemId}} {{concept}}]({{{itemUrl}}}) from {{oldQuantity}} ➔ *{{newQuantity}}* of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale reserved state": "I have changed the following lines reserved state from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Bought units from buy request": "Bought {{quantity}} units of [{{itemId}} {{concept}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})", "MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*", "Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})", @@ -99,5 +106,8 @@ "None": "None", "error densidad = 0": "error densidad = 0", "nickname": "nickname", - "This document already exists on this ticket": "This document already exists on this ticket" + "This document already exists on this ticket": "This document already exists on this ticket", + "State": "State", + "regular": "regular", + "reserved": "reserved" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index ff30a61ff..f301df8cc 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -54,7 +54,7 @@ "This address doesn't exist": "Este consignatario no existe", "You can't create an order for a inactive client": "You can't create an order for a inactive client", "You can't create an order for a client that doesn't has tax data verified": "You can't create an order for a client that doesn't has tax data verified", - "You must delete the claim id %d first": "Antes debes borrar la reclamacion %d", + "You must delete the claim id %d first": "Antes debes borrar la reclamación %d", "You don't have enough privileges": "No tienes suficientes permisos", "Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF", "You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos basicos de una orden con artículos", @@ -122,7 +122,17 @@ "Swift / BIC can't be empty": "Swift / BIC no puede estar vacío", "Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios", "Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios", - "Bought units from buy request": "Se ha comprado {{quantity}} unidades de {{concept}} [{{itemId}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", + "Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", + "Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", + "Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "He cambiado la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "State": "Estado", + "regular": "normal", + "reserved": "reservado", + "Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*", "Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", diff --git a/modules/claim/back/methods/claim/createFromSales.js b/modules/claim/back/methods/claim/createFromSales.js index 2dd1b75c2..f22aabbf3 100644 --- a/modules/claim/back/methods/claim/createFromSales.js +++ b/modules/claim/back/methods/claim/createFromSales.js @@ -3,17 +3,20 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethodCtx('createFromSales', { description: 'Create a claim', - accepts: [{ - arg: 'ticketId', - type: 'Number', - required: true, - description: 'The origin ticket id' - }, { - arg: 'sales', - type: ['Object'], - required: true, - description: 'The claimed sales' - }], + accepts: [ + { + arg: 'ticketId', + type: 'number', + required: true, + description: 'The origin ticket id' + }, + { + arg: 'sales', + type: ['object'], + required: true, + description: 'The claimed sales' + } + ], returns: { type: 'object', root: true @@ -25,6 +28,7 @@ module.exports = Self => { }); Self.createFromSales = async(ctx, ticketId, sales, options) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; let tx; const myOptions = {}; @@ -39,7 +43,20 @@ module.exports = Self => { const userId = ctx.req.accessToken.userId; try { - const ticket = await models.Ticket.findById(ticketId, null, myOptions); + const ticket = await models.Ticket.findById(ticketId, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }, myOptions); + if (ticket.isDeleted) throw new UserError(`You can't create a claim for a removed ticket`); @@ -49,6 +66,8 @@ module.exports = Self => { ticketCreated: ticket.shipped, workerFk: userId }, myOptions); + + let changesMade = ''; const promises = []; for (const sale of sales) { @@ -59,10 +78,25 @@ module.exports = Self => { }, myOptions); promises.push(newClaimBeginning); + changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; } await Promise.all(promises); + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Created claim', { + claimId: newClaim.id, + ticketId: ticketId, + ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, + claimUrl: `${origin}/#!/claim/${newClaim.id}/summary`, + changes: changesMade + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + if (tx) await tx.commit(); return newClaim; diff --git a/modules/claim/back/methods/claim/specs/createFromSales.spec.js b/modules/claim/back/methods/claim/specs/createFromSales.spec.js index f08914025..849ccf8f5 100644 --- a/modules/claim/back/methods/claim/specs/createFromSales.spec.js +++ b/modules/claim/back/methods/claim/specs/createFromSales.spec.js @@ -7,7 +7,13 @@ describe('Claim createFromSales()', () => { instance: 0, quantity: 10 }]; - const ctx = {req: {accessToken: {userId: 1}}}; + const ctx = { + req: { + accessToken: {userId: 1}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; it('should create a new claim', async() => { const tx = await app.models.Claim.beginTransaction({}); diff --git a/modules/client/back/models/sample.json b/modules/client/back/models/sample.json index 725bfb9c7..cfb127ab2 100644 --- a/modules/client/back/models/sample.json +++ b/modules/client/back/models/sample.json @@ -9,23 +9,26 @@ "properties": { "id": { "id": true, - "type": "Number", + "type": "number", "description": "Identifier" }, "code": { - "type": "String" + "type": "string" }, "description": { - "type": "String" + "type": "string" }, "isVisible": { - "type": "Boolean" + "type": "boolean" }, "hasCompany": { - "type": "Boolean" + "type": "boolean" }, "hasPreview": { - "type": "Boolean" + "type": "boolean" + }, + "datepickerEnabled": { + "type": "boolean" } }, "scopes": { diff --git a/modules/client/front/sample/create/index.html b/modules/client/front/sample/create/index.html index e6733a656..2d0f3d29c 100644 --- a/modules/client/front/sample/create/index.html +++ b/modules/client/front/sample/create/index.html @@ -25,38 +25,48 @@