diff --git a/back/methods/chat/sendMessage.js b/back/methods/chat/sendMessage.js index ac1d3c956..2ab07af8c 100644 --- a/back/methods/chat/sendMessage.js +++ b/back/methods/chat/sendMessage.js @@ -28,8 +28,10 @@ module.exports = Self => { const models = Self.app.models; const accessToken = ctx.req.accessToken; const sender = await models.Account.findById(accessToken.userId); + const recipient = to.replace('@', ''); - return sendMessage(to, `@${sender.name}: ${message}`); + if (sender.name != recipient) + return sendMessage(to, `@${sender.name}: ${message}`); }; async function sendMessage(name, message) { diff --git a/back/methods/chat/spec/send.spec.js b/back/methods/chat/spec/send.spec.js index 5b01def9b..ebb62a0c8 100644 --- a/back/methods/chat/spec/send.spec.js +++ b/back/methods/chat/spec/send.spec.js @@ -1,11 +1,18 @@ const app = require('vn-loopback/server/server'); describe('chat sendMessage()', () => { - it('should return a response', async() => { + it('should return a "Fake notification sent" as response', async() => { let ctx = {req: {accessToken: {userId: 1}}}; let response = await app.models.Chat.sendMessage(ctx, '@salesPerson', 'I changed something'); expect(response.statusCode).toEqual(200); expect(response.message).toEqual('Fake notification sent'); }); + + it('should not return a response', async() => { + let ctx = {req: {accessToken: {userId: 18}}}; + let response = await app.models.Chat.sendMessage(ctx, '@salesPerson', 'I changed something'); + + expect(response).toBeUndefined(); + }); }); diff --git a/db/changes/10100-AllSaints/00-zone.sql b/db/changes/10100-AllSaints/00-zone.sql index 1fae52f4e..e2542505a 100644 --- a/db/changes/10100-AllSaints/00-zone.sql +++ b/db/changes/10100-AllSaints/00-zone.sql @@ -35,9 +35,13 @@ CREATE TABLE `vn`.`zoneExclusion` ( KEY `zoneFk` (`zoneFk`), CONSTRAINT `zoneExclusion_ibfk_1` FOREIGN KEY (`zoneFk`) REFERENCES `zone` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; - ALTER TABLE `vn`.`zone` - DROP FOREIGN KEY `fk_zone_1`; +DROP FOREIGN KEY `fk_zone_1`; ALTER TABLE `vn`.`zone` - DROP COLUMN `warehouseFk`, - DROP INDEX `fk_zone_1_idx`; +CHANGE COLUMN `warehouseFk` `warehouseFk` SMALLINT(6) UNSIGNED NULL DEFAULT NULL ; +ALTER TABLE `vn`.`zone` +ADD CONSTRAINT `fk_zone_1` + FOREIGN KEY (`warehouseFk`) + REFERENCES `vn`.`warehouse` (`id`) + ON DELETE NO ACTION + ON UPDATE CASCADE; diff --git a/db/changes/10100-AllSaints/04-zone_getOptionsForLandingsql b/db/changes/10100-AllSaints/04-zone_getOptionsForLanding.sql similarity index 100% rename from db/changes/10100-AllSaints/04-zone_getOptionsForLandingsql rename to db/changes/10100-AllSaints/04-zone_getOptionsForLanding.sql diff --git a/db/changes/10110-postCampaign/00-country.sql b/db/changes/10110-postCampaign/00-country.sql new file mode 100644 index 000000000..dcc883ab8 --- /dev/null +++ b/db/changes/10110-postCampaign/00-country.sql @@ -0,0 +1,10 @@ +USE `vn`; + +UPDATE `vn`.`country` SET `ibanLength` = '24' WHERE (`id` = 1); +UPDATE `vn`.`country` SET `ibanLength` = '27' WHERE (`id` = 2); +UPDATE `vn`.`country` SET `ibanLength` = '22' WHERE (`id` = 3); +UPDATE `vn`.`country` SET `ibanLength` = '24' WHERE (`id` = 4); +UPDATE `vn`.`country` SET `ibanLength` = '18' WHERE (`id` = 5); +UPDATE `vn`.`country` SET `ibanLength` = '25' WHERE (`id` = 8); +UPDATE `vn`.`country` SET `ibanLength` = '27' WHERE (`id` = 19); +UPDATE `vn`.`country` SET `ibanLength` = '24' WHERE (`id` = 30); diff --git a/db/changes/10110-postCampaign/00-sample.sql b/db/changes/10110-postCampaign/00-sample.sql new file mode 100644 index 000000000..68e9d1a0f --- /dev/null +++ b/db/changes/10110-postCampaign/00-sample.sql @@ -0,0 +1,8 @@ +USE `vn`; + +UPDATE `vn`.`sample` SET `description` = 'Bienvenida como nuevo cliente' WHERE (`id` = '12'); +UPDATE `vn`.`sample` SET `description` = 'Instalación y configuración de impresora de coronas' WHERE (`id` = '13'); +UPDATE `vn`.`sample` SET `description` = 'Solicitud de domiciliación bancaria' WHERE (`id` = '14'); +UPDATE `vn`.`sample` SET `description` = 'Aviso inicial por saldo deudor' WHERE (`id` = '15'); +UPDATE `vn`.`sample` SET `description` = 'Aviso reiterado por saldo deudor' WHERE (`id` = '16'); +UPDATE `vn`.`sample` SET `isVisible` = '0' WHERE (`id` = '17'); diff --git a/db/changes/10110-postCampaign/00-ticketRequest.sql b/db/changes/10110-postCampaign/00-ticketRequest.sql new file mode 100644 index 000000000..6974df5cd --- /dev/null +++ b/db/changes/10110-postCampaign/00-ticketRequest.sql @@ -0,0 +1,86 @@ +USE `vn`; + +ALTER TABLE `vn`.`ticketRequest` +DROP FOREIGN KEY `fgnAtender`; +ALTER TABLE `vn`.`ticketRequest` +CHANGE COLUMN `atenderFk` `attenderFk` INT(11) NULL DEFAULT NULL ; +ALTER TABLE `vn`.`ticketRequest` +ADD CONSTRAINT `fgnAtender` + FOREIGN KEY (`attenderFk`) + REFERENCES `vn`.`worker` (`id`) + ON UPDATE CASCADE; + +USE `vn2008`; +CREATE + OR REPLACE ALGORITHM = UNDEFINED + DEFINER = `root`@`%` + SQL SECURITY DEFINER +VIEW `vn2008`.`Ordenes` AS + SELECT + `tr`.`id` AS `Id_ORDEN`, + `tr`.`description` AS `ORDEN`, + `tr`.`requesterFk` AS `requesterFk`, + `tr`.`attenderFk` AS `attenderFk`, + `tr`.`quantity` AS `CANTIDAD`, + `tr`.`itemFk` AS `Id_ARTICLE`, + `tr`.`price` AS `PRECIOMAX`, + `tr`.`isOk` AS `isOk`, + `tr`.`saleFk` AS `Id_Movimiento`, + `tr`.`ticketFk` AS `ticketFk`, + `tr`.`response` AS `COMENTARIO`, + `tr`.`created` AS `odbc_date`, + `tr`.`ordered` AS `datORDEN`, + `tr`.`shipped` AS `datTICKET`, + `tr`.`salesPersonCode` AS `CodVENDEDOR`, + `tr`.`buyerCode` AS `CodCOMPRADOR`, + `tr`.`price__` AS `PREU`, + `tr`.`clientFk` AS `Id_CLIENTE`, + `tr`.`ok__` AS `OK`, + `tr`.`total` AS `TOTAL`, + `tr`.`buyed` AS `datCOMPRA`, + `tr`.`ko__` AS `KO` + FROM + `vn`.`ticketRequest` `tr`; + +USE `vn`; + +DROP TRIGGER IF EXISTS `vn`.`ticketRequest_beforeInsert`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` TRIGGER `vn`.`ticketRequest_beforeInsert` BEFORE INSERT ON `ticketRequest` FOR EACH ROW +BEGIN + IF NEW.ticketFk IS NULL THEN + SET NEW.ticketFk = (SELECT s.ticketFk FROM sale s WHERE s.id = NEW.saleFk); + END IF; + + IF NEW.requesterFk IS NULL THEN + SET NEW.requesterFk = (SELECT w.id FROM worker w WHERE w.code = NEW.salesPersonCode); + END IF; + + IF NEW.attenderFk IS NULL THEN + SET NEW.attenderFk = (SELECT w.id FROM worker w WHERE w.code = NEW.buyerCode); + END IF; +END$$ +DELIMITER ; + + +DROP TRIGGER IF EXISTS `vn`.`ticketRequest_beforeUpdate`; + +DELIMITER $$ +USE `vn`$$ +CREATE DEFINER=`root`@`%` TRIGGER `vn`.`ticketRequest_beforeUpdate` BEFORE UPDATE ON `ticketRequest` FOR EACH ROW +BEGIN + IF NEW.saleFk <> OLD.saleFk THEN + SET NEW.ticketFk = (SELECT s.ticketFk FROM sale s WHERE s.id = NEW.saleFk); + END IF; + + IF NEW.salesPersonCode <> OLD.salesPersonCode THEN + SET NEW.requesterFk = (SELECT w.id FROM worker w WHERE w.code = NEW.salesPersonCode); + END IF; + + IF NEW.buyerCode <> OLD.buyerCode THEN + SET NEW.attenderFk = (SELECT w.id FROM worker w WHERE w.code = NEW.buyerCode); + END IF; +END$$ +DELIMITER ; \ No newline at end of file diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 1aba201a8..72c10664e 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -53,13 +53,14 @@ INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossF INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`) VALUES - (1, 'España', 0, 'ES', 1, 22), - (2, 'Italia', 1, 'IT', 1, 25), - (3, 'Alemania', 1, 'DE', 1, 20), - (4, 'Rumania', 1, 'RO', 1, 22), - (5, 'Holanda', 1, 'NL', 1, 16), - (19,'Francia', 1, 'FR', 1, 25), - (30,'Canarias', 1, 'IC', 1, 22); + (1, 'España', 0, 'ES', 1, 24), + (2, 'Italia', 1, 'IT', 1, 27), + (3, 'Alemania', 1, 'DE', 1, 22), + (4, 'Rumania', 1, 'RO', 1, 24), + (5, 'Holanda', 1, 'NL', 1, 18), + (8, 'Portugal', 1, 'PT', 1, 27), + (19,'Francia', 1, 'FR', 1, 27), + (30,'Canarias', 1, 'IC', 1, 24); INSERT INTO `vn`.`warehouse`(`id`, `name`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`) VALUES @@ -195,9 +196,9 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city VALUES (101, 'Bruce Wayne', '84612325V', 'Batman', 'Alfred', '1007 Mountain Drive, Gotham', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), (102, 'Petter Parker', '87945234L', 'Spider man', 'Aunt May', '20 Ingram Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), - (103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 0, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), + (103, 'Clark Kent', '06815934E', 'Super man', 'lois lane', '344 Clinton Street', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 0, 19, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), (104, 'Tony Stark', '06089160W', 'Iron man', 'Pepper Potts', '10880 Malibu Point', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1), - (105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1), + (105, 'Max Eisenhardt', '251628698', 'Magneto', 'Rogue', 'Unknown Whereabouts', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 8, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1), (106, 'DavidCharlesHaller', '53136686Q', 'Legion', 'Charles Xavier', 'Evil hideout', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1), (107, 'Hank Pym', '09854837G', 'Ant man', 'Hawk', 'Anthill', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1), (108, 'Charles Xavier', '22641921P', 'Professor X', 'Beast', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Silla', 46460, 1111111111, 222222222, 333333333, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5,CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, NULL, 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1), @@ -1471,7 +1472,7 @@ INSERT INTO `vn2008`.`workerTeam`(`id`, `team`, `user`) (5, 3, 103), (6, 3, 104); -INSERT INTO `vn`.`ticketRequest`(`id`, `description`, `requesterFk`, `atenderFk`, `quantity`, `itemFk`, `price`, `isOk`, `saleFk`, `ticketFk`, `created`) +INSERT INTO `vn`.`ticketRequest`(`id`, `description`, `requesterFk`, `attenderFk`, `quantity`, `itemFk`, `price`, `isOk`, `saleFk`, `ticketFk`, `created`) VALUES (1, 'Ranged weapon longbow 2m', 18, 35, 5, 1, 9.10, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (2, 'Melee weapon combat first 15cm', 18, 35, 10, 2, 1.07, 0, NULL, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), diff --git a/db/tests/vn/buyUltimateFromInterval.spec.js b/db/tests/vn/buyUltimateFromInterval.spec.js index b416b5cb2..b5e6970f7 100644 --- a/db/tests/vn/buyUltimateFromInterval.spec.js +++ b/db/tests/vn/buyUltimateFromInterval.spec.js @@ -5,25 +5,14 @@ describe('buyUltimateFromInterval()', () => { let today; let future; beforeAll(() => { - let date = new Date(); - let month = `${date.getMonth() + 1}`; - let futureMonth = `${date.getMonth() + 2}`; - let day = date.getDate(); - let year = date.getFullYear(); - let futureYear = year; + let now = new Date(); + now.setHours(0, 0, 0, 0); + today = now; - if (month.toString().length < 2) month = '0' + month; - if (futureMonth.toString().length < 2) futureMonth = '0' + futureMonth; - if (futureMonth.toString() == '13') { - futureMonth = '01'; - futureYear + 1; - } - - - if (day.toString().length < 2) day = `0${day}`; - - today = [year, month, day].join('-'); - future = [futureYear, futureMonth, day].join('-'); + let futureDate = new Date(now); + let futureMonth = now.getMonth() + 1; + futureDate.setMonth(futureMonth); + future = futureDate; }); it(`should create a temporal table with it's data`, async() => { @@ -65,8 +54,8 @@ describe('buyUltimateFromInterval()', () => { expect(buyUltimateFromIntervalTable[0].buyFk).toEqual(3); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); - expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); - expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(today)); + expect(buyUltimateFromIntervalTable[0].landed).toEqual(today); + expect(buyUltimateFromIntervalTable[1].landed).toEqual(today); }); it(`should create a temporal table with it's data in which started value is assigned to ended`, async() => { @@ -101,8 +90,8 @@ describe('buyUltimateFromInterval()', () => { expect(buyUltimateFromIntervalTable[0].buyFk).toEqual(3); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); - expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); - expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(today)); + expect(buyUltimateFromIntervalTable[0].landed).toEqual(today); + expect(buyUltimateFromIntervalTable[1].landed).toEqual(today); }); it(`should create a temporal table with it's data in which ended value is a date in the future`, async() => { @@ -137,7 +126,7 @@ describe('buyUltimateFromInterval()', () => { expect(buyUltimateFromIntervalTable[0].buyFk).toEqual(3); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); - expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); - expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(today)); + expect(buyUltimateFromIntervalTable[0].landed).toEqual(today); + expect(buyUltimateFromIntervalTable[1].landed).toEqual(today); }); }); diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 175b90ef0..171e376d2 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -111,9 +111,7 @@ "This phone already exists": "Este teléfono ya existe", "You cannot move a parent to its own sons": "No puedes mover un elemento padre a uno de sus hijos", "You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado", - "You cannot delete this ticket because is already invoiced, deleted or prepared": "No puedes eliminar este tiquet porque ya está facturado, eliminado o preparado", "You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada", "You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero", - "Has deleted the ticket id": "Ha eliminado el ticket id [#{{id}}]({{{url}}})", - "You cannot remove this ticket because is already invoiced, deleted or prepared": "You cannot remove this ticket because is already invoiced, deleted or prepared" + "Has deleted the ticket id": "Ha eliminado el ticket id [#{{id}}]({{{url}}})" } \ No newline at end of file diff --git a/modules/agency/front/location/index.html b/modules/agency/front/location/index.html index 6882ccc5b..7f33b840c 100644 --- a/modules/agency/front/location/index.html +++ b/modules/agency/front/location/index.html @@ -15,7 +15,7 @@ - { + Self.remoteMethodCtx('calculate', { + description: 'Calculates the price of a sale and its components', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + description: 'The sale id', + type: 'number', + required: true, + http: {source: 'path'} + }], + returns: { + type: 'Number', + root: true + }, + http: { + path: `/:id/calculate`, + verb: 'post' + } + }); + + Self.calculate = async(ctx, id) => { + const models = Self.app.models; + + const sale = await Self.findById(id); + const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); + + if (!isEditable) + throw new UserError(`The sales of this ticket can't be modified`); + + return Self.rawSql('CALL vn.ticketCalculateSale(?)', [id]); + }; +}; diff --git a/modules/ticket/back/methods/sale/specs/calculate.spec.js b/modules/ticket/back/methods/sale/specs/calculate.spec.js new file mode 100644 index 000000000..48b14e972 --- /dev/null +++ b/modules/ticket/back/methods/sale/specs/calculate.spec.js @@ -0,0 +1,24 @@ +const app = require('vn-loopback/server/server'); + +describe('sale calculate()', () => { + const saleId = 7; + + it('should update the sale price', async() => { + const ctx = {req: {accessToken: {userId: 9}}}; + const response = await app.models.Sale.calculate(ctx, saleId); + + expect(response.affectedRows).toBeDefined(); + }); + + it('should throw an error if the ticket is not editable', async() => { + const ctx = {req: {accessToken: {userId: 9}}}; + const immutableSaleId = 1; + await app.models.Sale.calculate(ctx, immutableSaleId) + .catch(response => { + expect(response).toEqual(new Error(`The sales of this ticket can't be modified`)); + error = response; + }); + + expect(error).toBeDefined(); + }); +}); diff --git a/modules/ticket/back/methods/ticket-request/filter.js b/modules/ticket/back/methods/ticket-request/filter.js index 82e1ee8e0..8f1f837d2 100644 --- a/modules/ticket/back/methods/ticket-request/filter.js +++ b/modules/ticket/back/methods/ticket-request/filter.js @@ -76,7 +76,7 @@ module.exports = Self => { case 'ticketFk': return {'t.id': value}; case 'attenderFk': - return {'tr.atenderFk': value}; + return {'tr.attenderFk': value}; case 'isOk': return {'tr.isOk': value}; case 'clientFk': @@ -106,7 +106,7 @@ module.exports = Self => { tr.ticketFk, tr.quantity, tr.price, - tr.atenderFk attenderFk, + tr.attenderFk, tr.description, tr.response, tr.saleFk, @@ -131,7 +131,7 @@ module.exports = Self => { LEFT JOIN sale s ON s.id = tr.saleFk LEFT JOIN worker wk ON wk.id = c.salesPersonFk LEFT JOIN account.user u ON u.id = wk.userFk - LEFT JOIN worker wka ON wka.id = tr.atenderFk + LEFT JOIN worker wka ON wka.id = tr.attenderFk LEFT JOIN account.user ua ON ua.id = wka.userFk`); stmt.merge(conn.makeSuffix(filter)); diff --git a/modules/ticket/back/methods/ticket/setDeleted.js b/modules/ticket/back/methods/ticket/setDeleted.js index b9ac03560..fb72c7dbc 100644 --- a/modules/ticket/back/methods/ticket/setDeleted.js +++ b/modules/ticket/back/methods/ticket/setDeleted.js @@ -27,7 +27,7 @@ module.exports = Self => { const $t = ctx.req.__; // $translate if (!isEditable) - throw new UserError('You cannot delete this ticket because is already invoiced, deleted or prepared'); + throw new UserError(`The sales of this ticket can't be modified`); // Check if has sales with shelving const sales = await models.Sale.find({ diff --git a/modules/ticket/back/models/sale.js b/modules/ticket/back/models/sale.js index b1fe82565..1b352bcff 100644 --- a/modules/ticket/back/models/sale.js +++ b/modules/ticket/back/models/sale.js @@ -5,6 +5,7 @@ module.exports = Self => { require('../methods/sale/updatePrice')(Self); require('../methods/sale/updateQuantity')(Self); require('../methods/sale/updateConcept')(Self); + require('../methods/sale/calculate')(Self); Self.validatesPresenceOf('concept', { message: `Concept cannot be blank` diff --git a/modules/ticket/back/models/ticket-request.json b/modules/ticket/back/models/ticket-request.json index 7f1cb4b02..f216cd531 100644 --- a/modules/ticket/back/models/ticket-request.json +++ b/modules/ticket/back/models/ticket-request.json @@ -33,13 +33,6 @@ "isOk": { "type": "Boolean" }, - "attenderFk": { - "type": "Number", - "required": true, - "mysql": { - "columnName": "atenderFk" - } - }, "response": { "type": "String" } diff --git a/modules/ticket/front/basic-data/index.html b/modules/ticket/front/basic-data/index.html index 4439069e8..3f87cc76d 100644 --- a/modules/ticket/front/basic-data/index.html +++ b/modules/ticket/front/basic-data/index.html @@ -9,6 +9,5 @@ on-step-end="$ctrl.onSubmit()"> -
- -
\ No newline at end of file + + diff --git a/modules/ticket/front/basic-data/step-one/index.html b/modules/ticket/front/basic-data/step-one/index.html index b98267a85..ccdf8a15f 100644 --- a/modules/ticket/front/basic-data/step-one/index.html +++ b/modules/ticket/front/basic-data/step-one/index.html @@ -5,7 +5,7 @@ auto-load="true">
- + - + { describe('onSubmit()', () => { it(`should return an error if the item doesn't have option property in the controller`, () => { - controller.ticket = {}; - + controller._ticket = {id: 1}; controller.onSubmit(); expect(vnApp.showError).toHaveBeenCalledWith('Choose an option'); diff --git a/modules/ticket/front/basic-data/step-two/index.html b/modules/ticket/front/basic-data/step-two/index.html index aa5a23212..ef5b09d4b 100644 --- a/modules/ticket/front/basic-data/step-two/index.html +++ b/modules/ticket/front/basic-data/step-two/index.html @@ -1,42 +1,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ItemDescriptionQuantityPrice (PPU)New price (PPU)Price difference
{{("000000"+sale.itemFk).slice(-6)}} - - - {{::sale.quantity}}{{::sale.price | currency: 'EUR': 2}}{{::sale.component.newPrice | currency: 'EUR': 2}}{{::sale.component.difference | currency: 'EUR': 2}}
{{$ctrl.totalPrice | currency: 'EUR': 2}}{{$ctrl.totalNewPrice | currency: 'EUR': 2}}{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}
-
+ + + + + Item + Description + Quantity + Price (PPU) + New price (PPU) + Price difference + + + + + {{("000000"+sale.itemFk).slice(-6)}} + + + + + {{::sale.quantity}} + {{::sale.price | currency: 'EUR': 2}} + {{::sale.component.newPrice | currency: 'EUR': 2}} + {{::sale.component.difference | currency: 'EUR': 2}} + + + + + + {{$ctrl.totalPrice | currency: 'EUR': 2}} + {{$ctrl.totalNewPrice | currency: 'EUR': 2}} + {{$ctrl.totalPriceDifference | currency: 'EUR': 2}} + + + diff --git a/modules/ticket/front/basic-data/step-two/index.js b/modules/ticket/front/basic-data/step-two/index.js index 1446ac171..a481afefc 100644 --- a/modules/ticket/front/basic-data/step-two/index.js +++ b/modules/ticket/front/basic-data/step-two/index.js @@ -7,6 +7,17 @@ class Controller { $onInit() { this.data.registerChild(this); + } + + get ticket() { + return this._ticket; + } + + set ticket(value) { + this._ticket = value; + + if (!value) return; + this.getTotalPrice(); this.getTotalNewPrice(); this.getTotalDifferenceOfPrice(); diff --git a/modules/ticket/front/descriptor/index.js b/modules/ticket/front/descriptor/index.js index d37d2d42b..9cb320a31 100644 --- a/modules/ticket/front/descriptor/index.js +++ b/modules/ticket/front/descriptor/index.js @@ -40,7 +40,7 @@ class Controller extends Component { showChangeShipped() { if (!this.isEditable) { - this.vnApp.showError(this.$translate.instant('This ticket can\'t be modified')); + this.vnApp.showError(this.$translate.instant(`This ticket can't be modified`)); return; } this.newShipped = this.ticket.shipped; diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js index 6df328d88..fb940a8ef 100644 --- a/modules/ticket/front/sale/index.js +++ b/modules/ticket/front/sale/index.js @@ -32,6 +32,11 @@ class Controller { callback: this.createClaim, show: () => this.isEditable }, + { + name: 'Recalculate price', + callback: this.calculateSalePrice, + show: () => this.hasOneSaleSelected() + }, ]; this._sales = []; this.imagesPath = '//verdnatura.es/vn-image-data/catalog'; @@ -534,6 +539,21 @@ class Controller { this.isEditable = res.data; }); } + + hasOneSaleSelected() { + if (this.totalCheckedLines() === 1) + return true; + return false; + } + + calculateSalePrice() { + const sale = this.checkedLines()[0]; + const query = `Sales/${sale.id}/calculate`; + this.$http.post(query).then(res => { + this.vnApp.showSuccess(this.$translate.instant('Data saved!')); + this.$scope.model.refresh(); + }); + } } Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate']; diff --git a/modules/ticket/front/sale/locale/es.yml b/modules/ticket/front/sale/locale/es.yml index 9deb3f06a..f381eb6e6 100644 --- a/modules/ticket/front/sale/locale/es.yml +++ b/modules/ticket/front/sale/locale/es.yml @@ -29,4 +29,5 @@ SMSAvailability: >- {{notAvailables}} no disponible/s. Disculpe las molestias. Continue anyway?: ¿Continuar de todas formas? This ticket is now empty: El ticket ha quedado vacio -Do you want to delete it?: ¿Quieres borrarlo? \ No newline at end of file +Do you want to delete it?: ¿Quieres borrarlo? +Recalculate price: Recalcular precio \ No newline at end of file diff --git a/modules/ticket/front/sale/specs/index.spec.js b/modules/ticket/front/sale/specs/index.spec.js index cd0e322a5..0606d81a4 100644 --- a/modules/ticket/front/sale/specs/index.spec.js +++ b/modules/ticket/front/sale/specs/index.spec.js @@ -1,5 +1,6 @@ import '../index.js'; import watcher from 'core/mocks/watcher'; +import crudModel from 'core/mocks/crud-model'; describe('Ticket', () => { describe('Component vnTicketSale', () => { @@ -40,6 +41,7 @@ describe('Ticket', () => { $scope.watcher = watcher; $scope.sms = {open: () => {}}; $scope.ticket = ticket; + $scope.model = crudModel; $httpBackend = _$httpBackend_; Object.defineProperties($state.params, { id: { @@ -334,5 +336,27 @@ describe('Ticket', () => { expect(window.open).toHaveBeenCalledWith('/somePath', '_blank'); }); }); + + describe('hasOneSaleSelected()', () => { + it('should return true if just one sale is selected', () => { + controller.sales[0].checked = true; + + expect(controller.hasOneSaleSelected()).toBeTruthy(); + }); + }); + + describe('calculateSalePrice()', () => { + it('should make an HTTP post query ', () => { + controller.sales[0].checked = true; + + $httpBackend.when('POST', `Sales/4/calculate`).respond(200); + $httpBackend.whenGET(`Tickets/1/subtotal`).respond(200, 227.5); + $httpBackend.whenGET(`Tickets/1/getVAT`).respond(200, 10.5); + $httpBackend.whenGET(`Tickets/1/isEditable`).respond(); + + controller.calculateSalePrice(); + $httpBackend.flush(); + }); + }); }); }); diff --git a/modules/travel/back/models/entry.json b/modules/travel/back/models/entry.json index 23c88091a..7d20ffcbd 100644 --- a/modules/travel/back/models/entry.json +++ b/modules/travel/back/models/entry.json @@ -30,8 +30,11 @@ "isConfirmed": { "type": "Boolean" }, - "isRaid": { - "type": "Boolean" + "isVirtual": { + "type": "Boolean", + "mysql": { + "columnName": "isRaid" + } }, "commission": { "type": "Number" diff --git a/modules/worker/back/methods/worker-time-control/specs/addTimeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/addTimeEntry.spec.js new file mode 100644 index 000000000..204b8608c --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/specs/addTimeEntry.spec.js @@ -0,0 +1,45 @@ +// const app = require('vn-loopback/server/server'); + +describe('workerTimeControl addTimeEntry()', () => { + it('should return 1 result filtering by id', async() => { + // let ctx = {req: {accessToken: {userId: 106}}, args: {workerFk: 106}}; + // const firstHour = new Date(); + // firstHour.setHours(7, 0, 0, 0); + // const lastHour = new Date(); + // lastHour.setDate(lastHour.getDate() + 1); + // lastHour.setHours(15, 0, 0, 0); + + // const filter = { + // where: { + // timed: {between: [firstHour, lastHour]} + // } + // }; + // let result = await app.models.WorkerTimeControl.filter(ctx, filter); + + // expect(result.length).toEqual(4); + // }); + + // it('should return a privilege error for a non subordinate worker', async() => { + // let ctx = {req: {accessToken: {userId: 107}}, args: {workerFk: 106}}; + // const firstHour = new Date(); + // firstHour.setHours(7, 0, 0, 0); + // const lastHour = new Date(); + // lastHour.setDate(lastHour.getDate() + 1); + // lastHour.setHours(15, 0, 0, 0); + + // const filter = { + // where: { + // timed: {between: [firstHour, lastHour]} + // } + // }; + + // let error; + // await app.models.WorkerTimeControl.filter(ctx, filter).catch(e => { + // error = e; + // }).finally(() => { + // expect(error.message).toEqual(`You don't have enough privileges`); + // }); + + // expect(error).toBeDefined(); + }); +}); diff --git a/modules/worker/back/methods/worker-time-control/specs/deteleTimeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/deteleTimeEntry.spec.js new file mode 100644 index 000000000..0f3b7ca1b --- /dev/null +++ b/modules/worker/back/methods/worker-time-control/specs/deteleTimeEntry.spec.js @@ -0,0 +1,45 @@ +// const app = require('vn-loopback/server/server'); + +// describe('workerTimeControl filter()', () => { +// it('should return 1 result filtering by id', async() => { +// let ctx = {req: {accessToken: {userId: 106}}, args: {workerFk: 106}}; +// const firstHour = new Date(); +// firstHour.setHours(7, 0, 0, 0); +// const lastHour = new Date(); +// lastHour.setDate(lastHour.getDate() + 1); +// lastHour.setHours(15, 0, 0, 0); + +// const filter = { +// where: { +// timed: {between: [firstHour, lastHour]} +// } +// }; +// let result = await app.models.WorkerTimeControl.filter(ctx, filter); + +// expect(result.length).toEqual(4); +// }); + +// it('should return a privilege error for a non subordinate worker', async() => { +// let ctx = {req: {accessToken: {userId: 107}}, args: {workerFk: 106}}; +// const firstHour = new Date(); +// firstHour.setHours(7, 0, 0, 0); +// const lastHour = new Date(); +// lastHour.setDate(lastHour.getDate() + 1); +// lastHour.setHours(15, 0, 0, 0); + +// const filter = { +// where: { +// timed: {between: [firstHour, lastHour]} +// } +// }; + +// let error; +// await app.models.WorkerTimeControl.filter(ctx, filter).catch(e => { +// error = e; +// }).finally(() => { +// expect(error.message).toEqual(`You don't have enough privileges`); +// }); + +// expect(error).toBeDefined(); +// }); +// }); diff --git a/modules/worker/back/methods/worker/sendMessage.js b/modules/worker/back/methods/worker/sendMessage.js deleted file mode 100644 index f3b4cd911..000000000 --- a/modules/worker/back/methods/worker/sendMessage.js +++ /dev/null @@ -1,170 +0,0 @@ -/* -Author : Enrique Blasco BLanquer -Date: 29 de octubre de 2019 -*/ -let request = require('request-promise-native'); -let UserError = require('vn-loopback/util/user-error'); -module.exports = Self => { - Self.remoteMethod('sendMessage', { - description: 'Send a RocketChat message', - accessType: 'WRITE', - accepts: [{ - arg: 'from', - type: 'String', - required: true, - description: 'user who sends the message' - }, { - arg: 'to', - type: 'String', - required: true, - description: 'user (@) or channel (#) to send the message' - }, { - arg: 'message', - type: 'String', - required: true, - description: 'The message' - }], - returns: { - type: 'boolean', - root: true - }, - http: { - path: `/sendMessage`, - verb: 'POST' - } - }); - - Self.sendMessage = async(from, to, message) => { - const rocketUser = await getRocketUser(); - const userId = rocketUser.data.userId; - const authToken = rocketUser.data.authToken; - if (to.includes('@')) return await sendUserMessage(to.replace('@', ''), userId, authToken, '@' + from + ' te ha mandado un mensaje: ' + message); - else return await sendChannelMessage(to.replace('#', ''), userId, authToken, '@' + from + ' dice: ' + message); - }; - - /** - * Returns a rocketchat token - * @return {Object} userId and authToken - */ - async function getRocketUser() { - const url = 'https://chat.verdnatura.es/api/v1/login'; - const options = { - method: 'POST', - uri: url, - body: { - user: 'VnBot', - password: 'Ub606cux7op.' - }, - headers: { - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } - - /** - * Send a user message - * @param {String} to user to send the message - * @param {String} userId rocket user id - * @param {String} authToken rocket token - * @param {String} message The message - * @return {Object} rocket info - */ - async function sendUserMessage(to, userId, authToken, message) { - const url = 'https://chat.verdnatura.es/api/v1/chat.postMessage'; - const options = { - method: 'POST', - uri: url, - body: { - 'channel': '@' + to, - 'text': message - }, - headers: { - 'X-Auth-Token': authToken, - 'X-User-Id': userId, - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } - - /** - * Send a channel message - * @param {String} to channel to send the message - * @param {String} userId rocket user id - * @param {String} authToken rocket token - * @param {String} message The message - * @return {Object} rocket info - */ - async function sendChannelMessage(to, userId, authToken, message) { - const channelInfo = await getChannelId(to, userId, authToken); - const url = 'https://chat.verdnatura.es/api/v1/chat.sendMessage'; - const channelId = channelInfo.channel._id; - - const options = { - method: 'POST', - uri: url, - body: { - 'message': { - 'rid': channelId, - 'msg': message - } - }, - headers: { - 'X-Auth-Token': authToken, - 'X-User-Id': userId, - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } - - /** - * Get channel id - * @param {String} to channel to get id - * @param {String} userId rocket user id - * @param {String} authToken rocket token - * @return {Object} rocket info - */ - async function getChannelId(to, userId, authToken) { - const url = 'https://chat.verdnatura.es/api/v1/channels.info?roomName=' + to; - const options = { - method: 'GET', - uri: url, - headers: { - 'X-Auth-Token': authToken, - 'X-User-Id': userId, - 'content-type': 'application/json' - }, - json: true - }; - return await request(options) - .then(function(parsedBody) { - return parsedBody; - }) - .catch(function(err) { - throw new UserError(err); - }); - } -}; diff --git a/modules/worker/back/models/worker.js b/modules/worker/back/models/worker.js index 6f120fca3..9abdeb737 100644 --- a/modules/worker/back/models/worker.js +++ b/modules/worker/back/models/worker.js @@ -2,7 +2,6 @@ module.exports = Self => { require('../methods/worker/filter')(Self); require('../methods/worker/mySubordinates')(Self); require('../methods/worker/isSubordinate')(Self); - require('../methods/worker/getWorkerInfo')(Self); require('../methods/worker/getWorkedHours')(Self); - require('../methods/worker/sendMessage')(Self); + require('../methods/worker/getWorkerInfo')(Self); }; diff --git a/modules/worker/front/time-control/index.spec.js b/modules/worker/front/time-control/index.spec.js index af57017ba..8c2c62db9 100644 --- a/modules/worker/front/time-control/index.spec.js +++ b/modules/worker/front/time-control/index.spec.js @@ -24,55 +24,66 @@ describe('Worker', () => { describe('hours() setter', () => { it(`should set hours data at it's corresponding week day`, () => { - let wednesday = new Date(controller.started.getTime()); - wednesday.setDate(wednesday.getDate() + 2); + let today = new Date(); - $httpBackend.whenRoute('GET', 'WorkerTimeControls/filter') - .respond([ - { - id: 1, - timed: controller.started.toJSON(), - userFk: 1 - }, { - id: 2, - timed: wednesday.toJSON(), - userFk: 1 - }, { - id: 3, - timed: wednesday.toJSON(), - userFk: 1 - } - ]); + controller.date = today; - $httpBackend.flush(); + let hours = [ + { + id: 1, + timed: controller.started.toJSON(), + userFk: 1 + }, { + id: 2, + timed: today.toJSON(), + userFk: 1 + }, { + id: 3, + timed: today.toJSON(), + userFk: 1 + } + ]; + + controller.hours = hours; + + let todayInWeek = today.getDay() - 1; expect(controller.weekDays.length).toEqual(7); expect(controller.weekDays[0].hours.length).toEqual(1); - expect(controller.weekDays[2].hours.length).toEqual(2); + expect(controller.weekDays[todayInWeek].hours.length).toEqual(2); }); }); - describe('getWeekdayTotalHours() ', () => { - it(`should return a total worked hours from 07:00 to 15:00`, () => { - const hourOne = new Date(); - hourOne.setHours(7, 0, 0, 0); - const hourTwo = new Date(); - hourTwo.setHours(10, 0, 0, 0); - const hourThree = new Date(); - hourThree.setHours(10, 20, 0, 0); - const hourFour = new Date(); - hourFour.setHours(15, 0, 0, 0); + describe('getWorkedHours() ', () => { + fit(`should set the week days and the worked hours in today`, () => { + let today = new Date(); - const weekday = {hours: [ - {id: 1, timed: hourOne}, - {id: 2, timed: hourTwo}, - {id: 3, timed: hourThree}, - {id: 4, timed: hourFour} - ]}; + $httpBackend.whenRoute('GET', 'WorkerTimeControls/filter').respond({}); + $httpBackend.whenRoute('GET', 'WorkerCalendars/absences').respond({}); + $httpBackend.whenRoute('GET', 'Workers/:id/getWorkedHours') + .respond([ + {dated: today}, + ]); - const result = controller.getWeekdayTotalHours(weekday); + today.setHours(0, 0, 0, 0); - expect(result).toEqual('08:00'); + let weekOffset = today.getDay() - 1; + if (weekOffset < 0) weekOffset = 6; + + let started = new Date(today.getTime()); + started.setDate(started.getDate() - weekOffset); + controller.started = started; + + let ended = new Date(started.getTime()); + ended.setHours(23, 59, 59, 59); + ended.setDate(ended.getDate() + 6); + controller.ended = ended; + + controller.getWorkedHours(controller.started, controller.ended); + + $httpBackend.flush(); + + expect(controller.started).toEqual(started); }); }); diff --git a/print/common/css/email.css b/print/common/css/email.css index 3bb9225c8..6e6350ff5 100644 --- a/print/common/css/email.css +++ b/print/common/css/email.css @@ -23,12 +23,11 @@ body { .grid-block { min-width: 300px; max-width: 600px; - width: 600px; - margin: 0 auto + margin: 0 auto; + color: #333 } h1 { font-weight: 100; - font-size: 1.5em; - color: #333; + font-size: 1.5em } diff --git a/print/config/print.json b/print/config/print.json index a075feed7..ee2ab6f79 100755 --- a/print/config/print.json +++ b/print/config/print.json @@ -8,7 +8,7 @@ "i18n": { "locale": "es", "fallbackLocale": "es", - "silentTranslationWarn": true + "silentTranslationWarn": false }, "pdf": { "format": "A4", diff --git a/print/core/components/email-header/assets/css/style.css b/print/core/components/email-header/assets/css/style.css index 5d2f658ce..4db5e2b2e 100644 --- a/print/core/components/email-header/assets/css/style.css +++ b/print/core/components/email-header/assets/css/style.css @@ -8,7 +8,7 @@ header .logo img { header .topbar { background-color: #95d831; - height: 25px + height: 10px } .topbar:after { diff --git a/print/core/components/report-footer/report-footer.html b/print/core/components/report-footer/report-footer.html index af433676f..a87c36109 100644 --- a/print/core/components/report-footer/report-footer.html +++ b/print/core/components/report-footer/report-footer.html @@ -4,6 +4,6 @@
{{centerText}}
{{$t('numPages')}}
-

{{$t('law.phytosanitary')}}

+

{{$t('law.phytosanitary')}}

diff --git a/print/core/components/report-footer/report-footer.js b/print/core/components/report-footer/report-footer.js index 10f411316..df1dca665 100755 --- a/print/core/components/report-footer/report-footer.js +++ b/print/core/components/report-footer/report-footer.js @@ -1,4 +1,4 @@ module.exports = { name: 'report-footer', - props: ['leftText', 'centerText', 'locale'] + props: ['leftText', 'centerText', 'locale', 'showPhytosanitary'] }; diff --git a/print/core/config.js b/print/core/config.js index d07d4f3fc..8db388401 100644 --- a/print/core/config.js +++ b/print/core/config.js @@ -10,8 +10,11 @@ let configFiles = [ ]; for (let configFile of configFiles) { - if (fs.existsSync(configFile)) - Object.assign(config, require(configFile)); + if (fs.existsSync(configFile)) { + const conf = require(configFile); + for (let prop in conf) + Object.assign(config[prop], conf[prop]); + } } config.env = env; diff --git a/print/templates/email/client-lcr/assets/css/index.js b/print/templates/email/client-lcr/assets/css/index.js deleted file mode 100644 index 321c632dc..000000000 --- a/print/templates/email/client-lcr/assets/css/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const CssReader = require(`${appPath}/lib/cssReader`); - -module.exports = new CssReader([ - `${appPath}/common/css/layout.css`, - `${appPath}/common/css/email.css`, - `${appPath}/common/css/misc.css`]) - .mergeStyles(); diff --git a/print/templates/email/client-lcr/index.html b/print/templates/email/client-lcr/index.html deleted file mode 100644 index 91fa67ab5..000000000 --- a/print/templates/email/client-lcr/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - {{ $t('subject') }} - - -
- - - -
- -
-

{{ $t('title') }}

-
- - -

{{$t('description.dear')}},

-

{{$t('description.instructions')}}

-

{{$t('description.conclusion')}}

-
- - - -
- - \ No newline at end of file diff --git a/print/templates/email/client-lcr/index.js b/print/templates/email/client-lcr/index.js deleted file mode 100755 index bb7ba452d..000000000 --- a/print/templates/email/client-lcr/index.js +++ /dev/null @@ -1,49 +0,0 @@ -const database = require(`${appPath}/lib/database`); -const reportEngine = require(`${appPath}/lib/reportEngine.js`); -const UserException = require(`${appPath}/lib/exceptions/userException`); - -module.exports = { - name: 'client-lcr', - async asyncData(ctx, params) { - const promises = []; - const data = { - isPreview: ctx.method === 'GET', - }; - - if (!params.clientFk) - throw new UserException('No client id specified'); - - promises.push(reportEngine.toPdf('rpt-lcr', ctx)); - promises.push(this.methods.fetchClient(params.clientFk)); - - return Promise.all(promises).then(result => { - const stream = result[0]; - const [[client]] = result[1]; - - Object.assign(data, client); - Object.assign(data, {attachments: [{filename: 'rpt-lcr.pdf', content: stream}]}); - - return data; - }); - }, - created() { - if (this.locale) - this.$i18n.locale = this.locale; - }, - - methods: { - fetchClient(clientFk) { - return database.pool.query(` - SELECT - u.lang locale, - c.email recipient - FROM client c - JOIN account.user u ON u.id = c.id - WHERE c.id = ?`, [clientFk]); - }, - }, - components: { - 'email-header': require('../email-header'), - 'email-footer': require('../email-footer'), - }, -}; diff --git a/print/templates/email/client-lcr/locale.js b/print/templates/email/client-lcr/locale.js deleted file mode 100644 index 85a75d778..000000000 --- a/print/templates/email/client-lcr/locale.js +++ /dev/null @@ -1,64 +0,0 @@ -module.exports = { - messages: { - es: { - subject: 'Autorisation pour débit', - title: 'Autorisation pour débit', - description: { - dear: 'Messieurs', - instructions: `Étant donné les excellentes relations existantes entre nos - deux sociétés et en vue de faciliter les processus de - paiement de nos factures, nous vous suggérons l'utilisation - du système française de compensation LCR. - Ce service consiste à effectuer des recouvrements - automatiques, de manière électronique, de nos effets - - lettres de change et billets à ordre - tirés sur votre société - en Euro, qui présente comme principal avantage pour vous - la substantielle réduction de coûts dans des termes de frais - et commissions bancaires. - Dans le cas où vous accepteriez notre proposition, à - l’échéance de chaque effet, votre compte sera débité - automatiquement par votre Banque. - Ainsi, nous vous demandons de signer et envoyer à votre - Banque l'original de l'autorisation pour débit en annexe, - dûment remplie, et de nous retourner une photocopie de la - dite autorisation. - Ce système étant basé sur la transmission de données de - manière électronique, le maniement de documents - physiques á été éliminé - En vous remercieront pour votre collaboration, nous vous - prions d’agréer, Messieurs, nos salutations distinguées.`, - conclusion: 'Bien cordialement' - }, - }, - fr: { - subject: 'Autorisation pour débit', - title: 'Autorisation pour débit', - description: { - dear: 'Messieurs', - instructions: `Étant donné les excellentes relations existantes entre nos - deux sociétés et en vue de faciliter les processus de - paiement de nos factures, nous vous suggérons l'utilisation - du système française de compensation LCR. - Ce service consiste à effectuer des recouvrements - automatiques, de manière électronique, de nos effets - - lettres de change et billets à ordre - tirés sur votre société - en Euro, qui présente comme principal avantage pour vous - la substantielle réduction de coûts dans des termes de frais - et commissions bancaires. - Dans le cas où vous accepteriez notre proposition, à - l’échéance de chaque effet, votre compte sera débité - automatiquement par votre Banque. - Ainsi, nous vous demandons de signer et envoyer à votre - Banque l'original de l'autorisation pour débit en annexe, - dûment remplie, et de nous retourner une photocopie de la - dite autorisation. - Ce système étant basé sur la transmission de données de - manière électronique, le maniement de documents - physiques á été éliminé - En vous remercieront pour votre collaboration, nous vous - prions d’agréer, Messieurs, nos salutations distinguées.`, - conclusion: 'Bien cordialement' - }, - }, - }, -}; diff --git a/print/templates/email/delivery-note/delivery-note.html b/print/templates/email/delivery-note/delivery-note.html index 9e24d8437..b91f66e69 100644 --- a/print/templates/email/delivery-note/delivery-note.html +++ b/print/templates/email/delivery-note/delivery-note.html @@ -27,9 +27,11 @@

{{ $t('title') }}

-

{{$t('dearClient')}},

-

+

{{$t('dear')}},

+

+

+

diff --git a/print/templates/email/delivery-note/locale/es.yml b/print/templates/email/delivery-note/locale/es.yml index cdc63e8eb..3294b2316 100644 --- a/print/templates/email/delivery-note/locale/es.yml +++ b/print/templates/email/delivery-note/locale/es.yml @@ -1,6 +1,10 @@ subject: Aquí tienes tu albarán title: "¡Este es tu albarán!" -dearClient: Estimado cliente -clientData: A continuación adjuntamos tu albarán. +dear: Estimado cliente +description: Ya está disponible el albarán correspondiente al pedido {0}.
+ Puedes descargarlo haciendo clic en el adjunto de este correo. +poll: Si lo deseas, puedes responder a nuestra encuesta de satisfacción para + ayudarnos a prestar un mejor servicio. ¡Tu opinión es muy importante para nosotros! help: Cualquier duda que te surja, no dudes en consultarla, ¡estamos para - atenderte! \ No newline at end of file + atenderte! +conclusion: ¡Gracias por tu atención! diff --git a/print/templates/email/delivery-note/locale/fr.yml b/print/templates/email/delivery-note/locale/fr.yml new file mode 100644 index 000000000..fdaf6e320 --- /dev/null +++ b/print/templates/email/delivery-note/locale/fr.yml @@ -0,0 +1,9 @@ +subject: Voici votre bon de livraison +title: "Voici votre bon de livraison!" +dear: Cher client, +description: Le bon de livraison correspondant à la commande {0} est maintenant disponible.
+ Vous pouvez le télécharger en cliquant sur la pièce jointe dans cet email. +poll: Si vous le souhaitez, vous pouvez répondre à notre questionaire de satisfaction + pour nous aider à améliorer notre service. Votre avis est très important pour nous! +help: N'hésitez pas nous envoyer toute doute ou question, nous sommes là pour vous aider! +conclusion: Merci pour votre attention! diff --git a/print/templates/email/sepa-core/locale/es.yml b/print/templates/email/sepa-core/locale/es.yml index 33315ee2f..10a1d32fe 100644 --- a/print/templates/email/sepa-core/locale/es.yml +++ b/print/templates/email/sepa-core/locale/es.yml @@ -2,7 +2,17 @@ subject: Solicitud de domiciliación bancaria title: Domiciliación SEPA CORE description: dear: Estimado cliente - instructions: Para poder tramitar tu solicitud de cambio de tu forma de pago a giro - bancario, te adjuntamos los documentos correspondientes a la ley de pago, que - tienes que cumplimentar y enviarnos. - conclusion: Gracias por tu atención. + instructions:

Dadas las excelentes relaciones existentes entre nuestras + dos empresas y para facilitar los procesos de pago de nuestras facturas, + sugerimos el uso del sistema de domiciliación bancaria SEPA CORE.

+

Este servicio consiste en emitir nuestros recibos a su empresa de + forma automatizada y electrónicamente, lo que supone para usted una reducción + sustancial de costos en términos de honorarios y gastos bancarios.

+

En caso de que acepte nuestra propuesta, a la fecha de vencimiento de cada efecto, + se debitará a su cuenta automáticamente a través de su entidad bancaria. + Por tanto, le pedimos que firme y envíe a su banco la autorización original adjunta, + debidamente cumplimentada, y nos devuelva una fotocopia de dicha autorización.

+

Este sistema se basa en la transmisión electrónica de datos; + el manejo de documentos físicos ha sido eliminado.

+

Le agradecemos su cooperación,

+ conclusion: ¡Gracias por su atención! diff --git a/print/templates/email/sepa-core/locale/fr.yml b/print/templates/email/sepa-core/locale/fr.yml new file mode 100644 index 000000000..98bd7593a --- /dev/null +++ b/print/templates/email/sepa-core/locale/fr.yml @@ -0,0 +1,27 @@ +subject: Autorisation pour débit +title: Autorisation pour débit +description: + dear: Messieurs + instructions:

Étant donné les excellentes relations existantes entre nos + deux sociétés et en vue de faciliter les processus de + paiement de nos factures, nous vous suggérons l'utilisation + du système française de compensation LCR.

+

Ce service consiste à effectuer des recouvrements + automatiques, de manière électronique, de nos effets - + lettres de change et billets à ordre - tirés sur votre société + en Euro, qui présente comme principal avantage pour vous + la substantielle réduction de coûts dans des termes de frais + et commissions bancaires.

+

Dans le cas où vous accepteriez notre proposition, à + l’échéance de chaque effet, votre compte sera débité + automatiquement par votre Banque. + Ainsi, nous vous demandons de signer et envoyer à votre + Banque l'original de l'autorisation pour débit en annexe, + dûment remplie, et de nous retourner une photocopie de la + dite autorisation.

+

Ce système étant basé sur la transmission de données de + manière électronique, le maniement de documents + physiques á été éliminé

+

En vous remercieront pour votre collaboration, nous vous + prions d’agréer, Messieurs, nos salutations distinguées.

+ conclusion: Bien cordialement diff --git a/print/templates/email/sepa-core/sepa-core.html b/print/templates/email/sepa-core/sepa-core.html index 4f3900262..445b7efc9 100644 --- a/print/templates/email/sepa-core/sepa-core.html +++ b/print/templates/email/sepa-core/sepa-core.html @@ -28,11 +28,11 @@

{{ $t('title') }}

{{$t('description.dear')}},

-

{{$t('description.instructions')}}

+

{{$t('description.conclusion')}}

- +
tbody > tr > td { - width: 10% !important -} - -.row-oriented.input-table > tbody > tr > th { - width: 90% !important -} \ No newline at end of file diff --git a/print/templates/reports/rpt-lcr/assets/images/signature.png b/print/templates/reports/rpt-lcr/assets/images/signature.png deleted file mode 100644 index c69cc4798..000000000 Binary files a/print/templates/reports/rpt-lcr/assets/images/signature.png and /dev/null differ diff --git a/print/templates/reports/rpt-lcr/index.html b/print/templates/reports/rpt-lcr/index.html deleted file mode 100644 index 236ace8ed..000000000 --- a/print/templates/reports/rpt-lcr/index.html +++ /dev/null @@ -1,189 +0,0 @@ - - - -
- - - -
-

{{$t('title')}}

-
-
{{$t('Creditor')}}
-
- - - - - - - - - - - - - - - - - - - -
{{$t('supplier.name')}}:{{supplierName}}
{{$t('supplier.street')}}:{{supplierStreet}}
{{supplierPostCode}}, {{supplierCity}} ({{supplierProvince}})
{{supplierCountry}}
-
-
-
-
{{$t('Deptor')}}
-
- - - - - - - - - - - - - - - - - - - - - - - -
{{$t('client.name')}}:{{clientName}}
{{$t('client.street')}}:{{clientStreet}}
{{clientPostCode}}, {{clientCity}} ({{clientProvince}})
{{clientCountry}}
{{$t('client.fi')}}: -
- {{fi.charAt(i)}} -
-
-
-
-

{{$t('description')}}

-
-
{{$t('Bank')}}
-
-
- {{$t('client.toCompleteByClient')}} -
- - - - - - - - - - - -
{{$t('bank.name')}}:
{{$t('bank.street')}}:
- - - - - - - - - - - - - - - - - - - - -
{{$t('bank.account')}}:
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
- {{$t('bank.bankCode')}} -
-
-
-
-
- {{$t('bank.agencyCode')}} -
-
-
-
-
- {{$t('bank.accountNumber')}} -
-
-
-
-
- {{$t('bank.ribKey')}} -
-
-
- - - - - - - - - - - -
IBAN:
-
- F - R - -
-
-
-
-

{{$t('authorization')}}

- - -
-
{{$t('client.sign')}}
-
-
-

{{$t('client.signDate')}}:

-
-
-
-
- - - - -
- - \ No newline at end of file diff --git a/print/templates/reports/rpt-lcr/index.js b/print/templates/reports/rpt-lcr/index.js deleted file mode 100755 index d4342764c..000000000 --- a/print/templates/reports/rpt-lcr/index.js +++ /dev/null @@ -1,80 +0,0 @@ -const strftime = require('strftime'); -const database = require(`${appPath}/lib/database`); -const UserException = require(`${appPath}/lib/exceptions/userException`); - -module.exports = { - name: 'rpt-lcr', - async asyncData(ctx, params) { - if (!params.clientFk) - throw new UserException('No client id specified'); - - if (!params.companyFk) - throw new UserException('No company id specified'); - - return this.methods.fetchClient(params.clientFk, params.companyFk) - .then(([[client]]) => { - if (!client) - throw new UserException('No client data found'); - - return client; - }); - }, - created() { - if (this.locale) - this.$i18n.locale = this.locale; - - const embeded = []; - this.files.map(file => { - embeded[file] = `file://${__dirname + file}`; - }); - this.embeded = embeded; - }, - data() { - return { - files: ['/assets/images/signature.png'] - }; - }, - methods: { - fetchClient(clientFk, companyFk) { - return database.pool.query( - `SELECT - c.id clientId, - u.lang locale, - m.code mandateCode, - c.socialName AS clientName, - c.street AS clientStreet, - c.postcode AS clientPostCode, - c.city AS clientCity, - c.fi, - p.name AS clientProvince, - ct.country AS clientCountry, - s.name AS supplierName, - s.street AS supplierStreet, - sc.country AS supplierCountry, - s.postCode AS supplierPostCode, - s.city AS supplierCity, - sp.name AS supplierProvince - FROM client c - JOIN account.user u ON u.id = c.id - JOIN country ct ON ct.id = c.countryFk - LEFT JOIN mandate m ON m.clientFk = c.id - AND m.companyFk = ? AND m.finished IS NULL - LEFT JOIN supplier s ON s.id = m.companyFk - LEFT JOIN country sc ON sc.id = s.countryFk - LEFT JOIN province sp ON sp.id = s.provinceFk - LEFT JOIN province p ON p.id = c.provinceFk - WHERE (m.companyFk = ? OR m.companyFk IS NULL) AND c.id = ? - ORDER BY m.created DESC LIMIT 1`, [companyFk, companyFk, clientFk]); - }, - dated: () => { - return strftime('%d-%m-%Y', new Date()); - }, - toISOString: date => { - return strftime('%d-%m-%Y', date); - }, - }, - components: { - 'report-header': require('../report-header'), - 'report-footer': require('../report-footer'), - }, -}; diff --git a/print/templates/reports/rpt-lcr/locale.js b/print/templates/reports/rpt-lcr/locale.js deleted file mode 100644 index 740b6d8f2..000000000 --- a/print/templates/reports/rpt-lcr/locale.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - messages: { - es: { - title: 'Autorisation pour débit', - Creditor: 'Tireur', - Deptor: 'Tiré', - Bank: 'Banque', - description: `Nous, soussignés, autorisons que tout effet émis par le tireur , susmentionné, et tiré sur notre Société, - soit automatiquement débité dans notre compte selon les suivants détails de domiciliation:`, - authorization: `Cette autorisation maintient sa validité jusqu'à à la réception de - nouvelles instructions.`, - supplier: { - name: 'Nom', - street: 'Adresse' - }, - bank: { - name: 'Nom', - street: 'Adresse', - account: 'RIB', - bankCode: 'Code banque', - agencyCode: 'Code agence', - accountNumber: 'Numero de compte', - ribKey: 'Clé RIB' - }, - client: { - name: 'Nom', - street: 'Adresse', - fi: 'Siren', - sign: 'Signature autorisée du tiré', - signDate: 'Lieu et date', - toCompleteByClient: 'À remplir par le débiteur', - }, - order: 'Ord. domiciliación {0}', - }, - }, -}; diff --git a/print/templates/reports/sepa-core/locale/es.yml b/print/templates/reports/sepa-core/locale/es.yml index d8955bc1f..66fe889b9 100644 --- a/print/templates/reports/sepa-core/locale/es.yml +++ b/print/templates/reports/sepa-core/locale/es.yml @@ -29,11 +29,13 @@ client: swift: Swift BIC accountNumber: Número de cuenta - IBAN accountHolder: "(Titular/es de la cuenta de cargo)" - accountNumberFormat: En España el IBAN consta de 24 posiciones comenzando siempre - por ES + accountNumberFormat: En {0} el IBAN consta de {1} posiciones comenzando siempre por {2} paymentType: Tipo de pago recurrent: Recurrente unique: Único signLocation: Fecha - Localidad sign: Firma del deudor y sello order: Ord. domiciliación {0} +Francia: Francia +España: España +Portugal: Portugal \ No newline at end of file diff --git a/print/templates/reports/sepa-core/locale/fr.yml b/print/templates/reports/sepa-core/locale/fr.yml index d8955bc1f..ec6fd11ab 100644 --- a/print/templates/reports/sepa-core/locale/fr.yml +++ b/print/templates/reports/sepa-core/locale/fr.yml @@ -1,39 +1,38 @@ -title: Orden de domiciliación de adeudo SEPA CORE -description: Mediante la firma de esta orden de domiciliación, el deudor autoriza - (A) al acreedor a enviar instrucciones a la entidad del deudor para adeudar su cuenta - y (B) a la entidad para efectuar los adeudos en su cuenta siguiendo las instrucciones - del acreedor.Como parte de sus derechos, el deudor está legitimado al reembolso - por su entidad en los términos y condiciones del contrato suscrito con la misma. - La solicitud de reembolso deberá efectuarse dentro de las ocho semanas que adeudo - en cuenta. Puede obtener información adicional sobre sus derechos en su entidad - financiera. -documentCopy: Debe llevar a su Entidad Bancaria una copia del documento firmado para - que lo registre y evitar la devolución. -mandatoryFields: TODOS LOS CAMPOS HAN DE SER CUMPLIMENTADOS OBLIGATORIAMENTE. -sendOrder: UNA VEZ FIRMADA ESTA ORDEN DE DOMICILIACIÓN DEBE SER ENVIADA AL ACREEDOR - PARA SU CUSTODIA Y ES RECOMENDABLE FACILITAR UNA COPIA A SU ENTIDAD BANCARIA. +title: Mandat de domiciliation Européene LCR +description: En signant ce formulaire de mandat, vous autorisez VERDNATURA LEVANTE SL + à envoyer des instructions à votre banque pour débiter votre compte, et (B) votre banque + à débiter votre compte conformément aux instructions de VERDNATURA LEVANTE SL. + Vous bénéficiez d’un droit au remboursement par votre banque selon les conditions décrites + dans la convention que vous avez passée avec elle. Toute demande de remboursement doit être + présentée dans les 8 semaines suivant la date de débit de votre compte. + Votre banque peut vous renseigner au sujet de vos droits relatifs à ce mandat. +documentCopy: Veuillez dater, signer et retourner ce document à votre banque. +mandatoryFields: TOUS LES CHAMPS DOIVENT ÊTRE REINSEGNÉS IMPÉRATIVEMENT. +sendOrder: APRÈS SIGNATURA, RENVOYER AU CRÉANCIER ET AU VOTRE ÉTABLISSEMENT FINANCIER. supplier: - toCompleteBySupplier: A cumplimentar por el acreedor - orderReference: Referencia de la orden de domiciliación - identifier: Identificador del acreedor - name: Nombre del acreedor - street: Dirección - location: CP - Población - Provincia - country: País + toCompleteBySupplier: Á compléter pour le créancier + orderReference: Numéro de référence du mandat + identifier: Identifiant créancier + name: Nom du céancier + street: Adresse + location: CP - Commune - Départament + country: Pays client: - toCompleteByClient: A cumplimentar por el deudor - name: Nombre del deudor/es - street: Dirección del deudor - location: CP - Población - Provincia - country: País del deudor + toCompleteByClient: Á compléter pour le débiteur + name: Nom du débiteur(s) + street: Adresse du(des) débiteur(s) + location: CP - Commune - Départament + country: País du(des) débiteur(s) swift: Swift BIC - accountNumber: Número de cuenta - IBAN - accountHolder: "(Titular/es de la cuenta de cargo)" - accountNumberFormat: En España el IBAN consta de 24 posiciones comenzando siempre - por ES - paymentType: Tipo de pago - recurrent: Recurrente - unique: Único - signLocation: Fecha - Localidad - sign: Firma del deudor y sello -order: Ord. domiciliación {0} + accountNumber: Numéro de compte - IBAN + accountHolder: (Débiteur(s) de compte) + accountNumberFormat: En {0} l'IBAN compte {1} postes commençant toujours par {2} + paymentType: Type de paiemen + recurrent: Versement périodique + unique: Paiement unique + signLocation: Date - Commune + sign: Signature du débiteur et tampon +order: Réf. mandat {0} +Francia: France +España: Espagne +Portugal: Portugal \ No newline at end of file diff --git a/print/templates/reports/sepa-core/locale/pt.yml b/print/templates/reports/sepa-core/locale/pt.yml index 2e2d08937..5b71fbdf5 100644 --- a/print/templates/reports/sepa-core/locale/pt.yml +++ b/print/templates/reports/sepa-core/locale/pt.yml @@ -22,18 +22,20 @@ supplier: country: País client: toCompleteByClient: A preencher pelo devedor - name: Nome do devedor (Titular da conta) + name: Nome do devedor street: Dirección del deudor location: Cod. Postal - Município - Distrito country: País do devedor swift: Swift BIC accountNumber: Número de Conta IBAN - accountHolder: "(Titular/es de la cuenta de cargo)" - accountNumberFormat: Em Portugal o IBAN é composto por 25 dígitos e começa sempre - por PT + accountHolder: "(Titular(es) da conta)" + accountNumberFormat: Em {0} o IBAN é composto pelo {1} dígitos e começa sempre pelo {2} paymentType: Tipos de pagamento Pagamento recurrent: Recorrente unique: Pagamento pontual signLocation: Data - Localidade sign: Assinatura e carimbo do devedor order: Referência da ordem {0} +Francia: França +España: Espanha +Portugal: Portugal \ No newline at end of file diff --git a/print/templates/reports/sepa-core/sepa-core.html b/print/templates/reports/sepa-core/sepa-core.html index 16fdc163c..69bf2651b 100644 --- a/print/templates/reports/sepa-core/sepa-core.html +++ b/print/templates/reports/sepa-core/sepa-core.html @@ -32,7 +32,10 @@ {{$t('supplier.identifier')}} - ES89000B97367486 + +
ES89000B97367486
+
B97367486-000
+ {{$t('supplier.name')}} @@ -110,14 +113,19 @@
{{client.countryCode.substr(0, 1)}} {{client.countryCode.substr(1, 1)}} - +
- {{$t('client.accountNumberFormat')}} + {{$t('client.accountNumberFormat', [ + $t(`${client.country}`), + client.ibanLength, + client.countryCode + ])}} +
@@ -145,7 +153,7 @@ {{$t('client.signLocation')}} - {{dated}}, {{supplier.province}} + {{dated}}, {{client.province}} {{$t('client.sign')}}