diff --git a/db/changes/10180-holyWeek/00-ticketWeekly.sql b/db/changes/10180-holyWeek/00-ticketWeekly.sql new file mode 100644 index 000000000..05d65e124 --- /dev/null +++ b/db/changes/10180-holyWeek/00-ticketWeekly.sql @@ -0,0 +1,13 @@ +ALTER TABLE `vn`.`ticketWeekly` +ADD COLUMN `agencyModeFk` INT(11) NULL DEFAULT NULL AFTER `weekDay`, +ADD INDEX `agencyModeFk_idx` (`agencyModeFk` ASC); + +ALTER TABLE `vn`.`ticketWeekly` +ADD CONSTRAINT `agencyModeFk` + FOREIGN KEY (`agencyModeFk`) + REFERENCES `vn`.`agencyMode` (`id`) + ON DELETE SET NULL + ON UPDATE CASCADE; + +ALTER TABLE `vn`.`ticketWeekly` +CHANGE COLUMN `weekDay` `weekDay` TINYINT(1) NOT NULL COMMENT 'funcion de mysql Lunes = 0, Domingo = 6' ; diff --git a/db/changes/10180-holyWeek/00-ticket_cloneWeekly.sql b/db/changes/10180-holyWeek/00-ticket_cloneWeekly.sql new file mode 100644 index 000000000..544296feb --- /dev/null +++ b/db/changes/10180-holyWeek/00-ticket_cloneWeekly.sql @@ -0,0 +1,130 @@ +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` PROCEDURE `ticket_cloneWeekly`(IN vWeek INT) +BEGIN + DECLARE vIsDone BOOL; + DECLARE vLanding DATE; + DECLARE vShipment DATE; + DECLARE vWarehouse INT; + DECLARE vTicket INT; + DECLARE vWeekDay INT; + DECLARE vClient INT; + DECLARE vEmpresa INT; + DECLARE vAddressFk INT; + DECLARE vAgencyModeFk INT; + DECLARE vNewTicket INT; + DECLARE vYear INT; + + DECLARE rsTicket CURSOR FOR + SELECT tw.ticketFk, weekDay, t.clientFk, t.warehouseFk, t.companyFk, t.addressFk, tw.agencyModeFk + FROM ticketWeekly tw + JOIN ticket t ON tt.ticketFk = t.id; + + DECLARE CONTINUE HANDLER FOR NOT FOUND SET vIsDone = TRUE; + + SET vYear = YEAR(CURDATE()) + IF(vWeek < WEEK(CURDATE()),1, 0); + + OPEN rsTicket; + + myLoop: LOOP + BEGIN + DECLARE vError TEXT; + DECLARE vSalesPersonEmail VARCHAR(150); + DECLARE vMailSent BOOL; + DECLARE vSubject VARCHAR(150); + DECLARE vMessage TEXT; + DECLARE CONTINUE HANDLER FOR SQLEXCEPTION + BEGIN + GET DIAGNOSTICS CONDITION 1 + vError = MESSAGE_TEXT; + + END; + + SET vIsDone = FALSE; + FETCH rsTicket INTO vTicket, vWeekDay, vClient, vWarehouse, vEmpresa, vAddressFk, vAgencyModeFk; + + IF vIsDone THEN + + LEAVE myLoop; + END IF; + SELECT date INTO vShipment + FROM `time` + WHERE `year` = vYear AND `week` = vWeek + AND WEEKDAY(date) = vWeekDay; + + -- busca si el ticket ya ha sido clonado + IF (SELECT COUNT(*) FROM vn.ticket tOrig + JOIN vn.sale saleOrig ON tOrig.id = saleOrig.ticketFk + JOIN vn.saleCloned sc ON sc.saleOriginalFk = saleOrig.id + JOIN vn.sale saleClon ON saleClon.id = sc.saleClonedFk + JOIN vn.ticket tClon ON tClon.id = saleClon.ticketFk + WHERE tOrig.id = vTicket AND DATE(tClon.shipped) = vShipment) > 0 + THEN + ITERATE myLoop; + END IF; + CALL vn.zone_getLanded(vShipment, vAddressFk, vAgencyModeFk, vWarehouse); + + SELECT landed INTO vLanding from tmp.zoneGetLanded LIMIT 1; + + CALL vn.ticketCreateWithoutZone(vClient, vShipment, vWarehouse, vEmpresa, vAddressFk, vAgencyModeFk, NULL, vLanding, account.userGetId(), vNewTicket); + + IF (vLanding IS NULL) THEN + + SELECT e.email INTO vSalesPersonEmail + FROM vn.client c + JOIN vn.worker sp ON sp.id = c.salesPersonFk + JOIN account.emailUser e ON e.userFk = sp.userFk + WHERE c.id = vClient; + + SET vSubject = CONCAT('Turnos - No se ha podido clonar correctamente el ticket ', vTicket, + ' para el dia: ', vShipment); + SET vMessage = CONCAT('No se ha podido clonar el ticket ', vTicket, + ' para el dia: ', vShipment, + ' porque no hay una zona de envĂ­o disponible. Se ha creado el ticket: ', vNewTicket, + ' pero ha que revisar las fechas y la agencia'); + + SELECT COUNT(*) INTO vMailSent + FROM vn.mail + WHERE sender = vSalesPersonEmail + AND subject = vSubject; + + IF NOT vMailSent THEN + INSERT INTO vn.mail (sender,`subject`,body) + VALUES (vSalesPersonEmail, vSubject, vMessage); + END IF; + CALL vn.ticketStateUpdate (vNewTicket, 'FIXING'); + END IF; + + INSERT INTO vn.sale (ticketFk, itemFk, concept, quantity, price, discount, priceFixed, isPriceFixed) + SELECT vNewTicket, saleOrig.itemFk , saleOrig.concept , saleOrig.quantity, saleOrig.price , saleOrig.discount, saleOrig.priceFixed, saleOrig.isPriceFixed + FROM vn.ticket tOrig + JOIN vn.sale saleOrig ON tOrig.id = saleOrig.ticketFk + LEFT JOIN vn.saleCloned sc ON sc.saleOriginalFk = saleOrig.id + LEFT JOIN vn.sale saleClon ON saleClon.id = sc.saleClonedFk + LEFT JOIN vn.ticket tClon ON tClon.id = saleClon.ticketFk AND DATE(tClon.shipped) = vShipment + WHERE tOrig.id = vTicket AND saleClon.id IS NULL; + + INSERT IGNORE INTO vn.saleCloned(saleOriginalFk, saleClonedFk) + SELECT saleOriginal.id, saleClon.id + FROM vn.sale saleOriginal + JOIN vn.sale saleClon ON saleOriginal.itemFk = saleClon.itemFk AND saleOriginal.quantity = saleClon.quantity + WHERE saleOriginal.ticketFk = vTicket AND saleClon.ticketFk = vNewTicket; + + INSERT INTO ticketRequest (description, ordered, shipped, salesPersonCode, buyerCode, quantity, price, + itemFk ,clientFk, response, total, buyed, saleFk) + SELECT tr.description, tr.ordered, tr.shipped, tr.salesPersonCode, tr.buyerCode, tr.quantity, tr.price, + tr.itemFk, tr.clientFk, tr.response, tr.total, tr.buyed, tr.saleFk + FROM sale s JOIN ticketRequest tr ON tr.saleFk = s.id + JOIN sale s2 ON s.concept = s2.concept AND s.quantity = s2.quantity AND m.Id_Article = m2.Id_Article + WHERE s.ticketFk = vTicket AND s2.ticketFk = vNewTicket; + + CALL vn.ticketCalculateClon(vNewTicket, vTicket); + END; + END LOOP; + + CLOSE rsTicket; + +END$$ + +DELIMITER ; + diff --git a/db/changes/10180-holyWeek/01-migrateFromTicketToTicketWeekly.sql b/db/changes/10180-holyWeek/01-migrateFromTicketToTicketWeekly.sql new file mode 100644 index 000000000..939a8e73c --- /dev/null +++ b/db/changes/10180-holyWeek/01-migrateFromTicketToTicketWeekly.sql @@ -0,0 +1,6 @@ +UPDATE vn.ticketWeekly tw + JOIN vn.ticket t ON t.id = tw.ticketFk + JOIN vn.agencyMode am ON am.id = t.agencyModeFk +SET tw.agencyModeFk = t.agencyModeFk +WHERE am.name NOT LIKE '%turno%'; + diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index 4e94c73ff..0b7f48d4e 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -99,7 +99,6 @@ export default { receivedCoreVNLCheckbox: 'vn-client-billing-data vn-check[label="Received core VNL"]', receivedB2BVNLCheckbox: 'vn-client-billing-data vn-check[label="Received B2B VNL"]', swiftBic: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"]', - clearswiftBicButton: 'vn-client-billing-data vn-autocomplete[ng-model="$ctrl.client.bankEntityFk"] .icons > vn-icon[icon=clear]', newBankEntityButton: 'vn-client-billing-data vn-icon-button[vn-tooltip="New bank entity"] > button', newBankEntityName: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.name"]', newBankEntityBIC: '.vn-dialog.shown vn-textfield[ng-model="$ctrl.newBankEntity.bic"]', @@ -378,11 +377,11 @@ export default { topbarSearch: 'vn-searchbar', advancedSearchButton: 'vn-ticket-search-panel button[type=submit]', searchButton: 'vn-searchbar vn-icon[icon="search"]', - searchWeeklyButton: 'vn-searchbar vn-icon[icon="search"]', moreMenu: 'vn-ticket-index vn-icon-menu[icon=more_vert]', sixthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tr:nth-child(6)', weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr', firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]', + firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-tr:nth-child(1) [ng-model="weekly.agencyModeFk"]', acceptDeleteTurn: '.vn-confirm.shown button[response="accept"]' }, createTicketView: { @@ -435,7 +434,6 @@ export default { firstQuantity: 'vn-ticket-package vn-horizontal:nth-child(1) vn-input-number[ng-model="package.quantity"]', firstRemovePackageButton: 'vn-icon-button[vn-tooltip="Remove package"]', addPackageButton: 'vn-icon-button[vn-tooltip="Add package"]', - clearPackageAutocompleteButton: 'vn-autocomplete[label="Package"] .icons > vn-icon[icon=clear]', savePackagesButton: `button[type=submit]` }, ticketSales: { @@ -460,7 +458,6 @@ export default { 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(5)', - firstSaleQuantityClearInput: 'vn-textfield[ng-model="sale.quantity"] div.suffix > i', firstSaleIdAutocomplete: 'vn-ticket-sale vn-table vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(4) > vn-autocomplete', firstSalePrice: 'vn-ticket-sale vn-table vn-tr:nth-child(1) > vn-td:nth-child(7) > span', firstSalePriceInput: '.vn-popover.shown [ng-model="$ctrl.editedPrice"]', @@ -490,7 +487,6 @@ export default { deleteSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="delete"]', transferSaleButton: 'vn-ticket-sale vn-tool-bar > vn-button[icon="call_split"]', moveToTicketInput: '.vn-popover.shown vn-textfield[ng-model="$ctrl.transfer.ticketId"]', - moveToTicketInputClearButton: '.vn-popover.shown i[title="Clear"]', moveToTicketButton: '.vn-popover.shown vn-icon[icon="arrow_forward_ios"]', moveToNewTicketButton: '.vn-popover.shown vn-button[label="New ticket"]', acceptDeleteLineButton: '.vn-confirm.shown button[response=accept]', @@ -553,7 +549,6 @@ export default { createStateView: { state: 'vn-autocomplete[ng-model="$ctrl.stateFk"]', worker: 'vn-autocomplete[ng-model="$ctrl.workerFk"]', - clearStateInputButton: 'vn-autocomplete[ng-model="$ctrl.stateFk"] .icons > vn-icon[icon=clear]', saveStateButton: `button[type=submit]` }, claimsIndex: { diff --git a/e2e/paths/05-ticket/09_weekly.spec.js b/e2e/paths/05-ticket/09_weekly.spec.js index f0b814dd8..33a98d72e 100644 --- a/e2e/paths/05-ticket/09_weekly.spec.js +++ b/e2e/paths/05-ticket/09_weekly.spec.js @@ -106,4 +106,16 @@ describe('Ticket descriptor path', () => { expect(nResults).toEqual(5); }); + + it('should update the agency then remove it afterwards', async() => { + await page.autocompleteSearch(selectors.ticketsIndex.firstWeeklyTicketAgency, 'Silla247'); + let result = await page.waitForLastSnackbar(); + + expect(result).toEqual('Data saved!'); + + await page.clearInput(selectors.ticketsIndex.firstWeeklyTicketAgency); + result = await page.waitForLastSnackbar(); + + expect(result).toEqual('Data saved!'); + }); }); diff --git a/modules/ticket/back/methods/ticket-weekly/filter.js b/modules/ticket/back/methods/ticket-weekly/filter.js index 925dcdfac..77f8ecc57 100644 --- a/modules/ticket/back/methods/ticket-weekly/filter.js +++ b/modules/ticket/back/methods/ticket-weekly/filter.js @@ -51,16 +51,14 @@ module.exports = Self => { stmt = new ParameterizedSQL( `SELECT t.id AS ticketFk, c.id AS clientFk, c.name AS clientName, tw.weekDay, - wh.name AS warehouseName, w.id AS workerFk, u.nickName + wh.name AS warehouseName, u.id AS workerFk, u.nickName, tw.agencyModeFk FROM ticketWeekly tw JOIN ticket t ON t.id = tw.ticketFk JOIN client c ON c.id = t.clientFk - JOIN worker w ON w.id = c.salesPersonFk - JOIN account.user u ON u.id = w.userFk + JOIN account.user u ON u.id = c.salesPersonFk JOIN warehouse wh ON wh.id = t.warehouseFk` ); - stmt.merge(conn.makeSuffix(filter)); let itemsIndex = stmts.push(stmt) - 1; diff --git a/modules/ticket/back/models/ticket-weekly.js b/modules/ticket/back/models/ticket-weekly.js index 733f1483a..8db53b283 100644 --- a/modules/ticket/back/models/ticket-weekly.js +++ b/modules/ticket/back/models/ticket-weekly.js @@ -3,14 +3,6 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { require('../methods/ticket-weekly/filter')(Self); - Self.validatesPresenceOf('ticketFk', { - message: `Ticket id cannot be blank` - }); - - Self.validatesPresenceOf('weekDay', { - message: `Weekday cannot be blank` - }); - Self.rewriteDbError(function(err) { if (err.code === 'ER_DUP_ENTRY') return new UserError(`This ticket is already on weekly tickets`); diff --git a/modules/ticket/back/models/ticket-weekly.json b/modules/ticket/back/models/ticket-weekly.json index c19e79bc1..6d432831f 100644 --- a/modules/ticket/back/models/ticket-weekly.json +++ b/modules/ticket/back/models/ticket-weekly.json @@ -25,6 +25,11 @@ "type": "belongsTo", "model": "Ticket", "foreignKey": "ticketFk" + }, + "agencyMode": { + "type": "belongsTo", + "model": "AgencyMode", + "foreignKey": "agencyModeFk" } } } \ No newline at end of file diff --git a/modules/ticket/front/descriptor/index.js b/modules/ticket/front/descriptor/index.js index 28fdfe3a2..5dc7a3f70 100644 --- a/modules/ticket/front/descriptor/index.js +++ b/modules/ticket/front/descriptor/index.js @@ -163,7 +163,10 @@ class Controller extends Component { } addTurn(day) { - let params = {ticketFk: this.ticket.id, weekDay: day}; + let params = { + ticketFk: this.ticket.id, + weekDay: day, + agencyModeFk: this.ticket.agencyModeFk}; this.$http.patch(`TicketWeeklies`, params).then(() => { this.$.addTurn.hide(); this.vnApp.showSuccess(this.$t('Data saved!')); diff --git a/modules/ticket/front/weekly/index.html b/modules/ticket/front/weekly/index.html index 40ae04106..e97d47eef 100644 --- a/modules/ticket/front/weekly/index.html +++ b/modules/ticket/front/weekly/index.html @@ -25,6 +25,7 @@ Ticket ID Client Weekday + Agency Warehouse Salesperson @@ -45,7 +46,7 @@ {{::weekly.clientName}} - + + + + + {{::weekly.warehouseName}} @@ -89,7 +102,7 @@ diff --git a/modules/ticket/front/weekly/index.js b/modules/ticket/front/weekly/index.js index edc68aadb..a89dbbc2e 100644 --- a/modules/ticket/front/weekly/index.js +++ b/modules/ticket/front/weekly/index.js @@ -17,60 +17,44 @@ export default class Controller extends Section { ]; } - onWeekdayUpdate(ticketFk, weekDay) { - const params = {ticketFk, weekDay}; - this.$http.patch('TicketWeeklies/', params).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Data saved!')); + onUpdate(ticketFk, field, value) { + const params = {ticketFk, [field]: value}; + this.$http.patch('TicketWeeklies', params).then(() => { + this.vnApp.showSuccess(this.$t('Data saved!')); }); } - deleteWeekly(index) { - this.ticketIndex = index; - this.$.deleteWeekly.show(); - event.stopImmediatePropagation(); - } - showClientDescriptor(event, clientFk) { this.$.clientDescriptor.clientFk = clientFk; this.$.clientDescriptor.parent = event.target; this.$.clientDescriptor.show(); - event.preventDefault(); - event.stopImmediatePropagation(); + this.stopEvent(event); } showTicketDescriptor(event, ticketFk) { this.$.ticketDescriptor.ticketFk = ticketFk; this.$.ticketDescriptor.parent = event.target; this.$.ticketDescriptor.show(); - event.preventDefault(); - event.stopImmediatePropagation(); + this.stopEvent(event); } showWorkerDescriptor(event, workerFk) { this.$.workerDescriptor.workerFk = workerFk; this.$.workerDescriptor.parent = event.target; this.$.workerDescriptor.show(); - event.preventDefault(); - event.stopImmediatePropagation(); + this.stopEvent(event); } - onDescriptorLoad() { - this.$.popover.relocate(); + deleteWeekly(event, ticketFk) { + this.$.deleteWeekly.show(ticketFk); + this.stopEvent(event); } - - preventNavigation(event) { - event.preventDefault(); - event.stopImmediatePropagation(); - } - - returnDialog(response) { - const ticket = this.$.model.data[this.ticketIndex]; - if (response === 'accept') { - this.$http.delete(`TicketWeeklies/${ticket.ticketFk}`).then(() => { - this.vnApp.showSuccess(this.$translate.instant('Data saved!')); - this.$.model.remove(this.ticketIndex); - }); - } + onDeleteWeeklyAccept(ticketFk) { + return this.$http.delete(`TicketWeeklies/${ticketFk}`).then(() => { + this.vnApp.showSuccess(this.$t('Data saved!')); + const ticketIndex = this.$.model.data.findIndex(e => e.ticketFk == ticketFk); + this.$.model.remove(ticketIndex); + }); } } diff --git a/modules/ticket/front/weekly/index.spec.js b/modules/ticket/front/weekly/index.spec.js new file mode 100644 index 000000000..a66e5c637 --- /dev/null +++ b/modules/ticket/front/weekly/index.spec.js @@ -0,0 +1,46 @@ +import './index'; +import crudModel from 'core/mocks/crud-model'; + +describe('ticket weekly', () => { + describe('Component vnTicketWeeklyIndex', () => { + let controller; + let $httpBackend; + + beforeEach(ngModule('ticket')); + + beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope) => { + $httpBackend = _$httpBackend_; + const $scope = $rootScope.$new(); + const $element = angular.element(''); + controller = $componentController('vnTicketWeeklyIndex', {$element, $scope}); + })); + + describe('onUpdate()', () => { + it('should make a query a PATCH querye then call vnApp.showSuccess()', () => { + jest.spyOn(controller.vnApp, 'showSuccess'); + + $httpBackend.expectPATCH(`TicketWeeklies`).respond(); + controller.onUpdate('ticketFk', 'field', 'value'); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); + }); + }); + + describe('onDeleteWeeklyAccept()', () => { + it('should make a DELETE query then call showSuccess(), afterwards checks that remove returns value 0', () => { + controller.$.model = crudModel; + controller.$.model.data = [{ticketFk: 'ticketFk'}]; + jest.spyOn(controller.vnApp, 'showSuccess'); + jest.spyOn(controller.$.model, 'remove'); + + $httpBackend.expectDELETE(`TicketWeeklies/ticketFk`).respond(); + controller.onDeleteWeeklyAccept('ticketFk'); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!'); + expect(controller.$.model.remove).toHaveBeenCalledWith(0); + }); + }); + }); +});