This commit is contained in:
Carlos Jimenez Ruiz 2019-11-14 14:22:22 +01:00
commit 8629b72d55
61 changed files with 598 additions and 875 deletions

View File

@ -28,7 +28,9 @@ module.exports = Self => {
const models = Self.app.models; const models = Self.app.models;
const accessToken = ctx.req.accessToken; const accessToken = ctx.req.accessToken;
const sender = await models.Account.findById(accessToken.userId); const sender = await models.Account.findById(accessToken.userId);
const recipient = to.replace('@', '');
if (sender.name != recipient)
return sendMessage(to, `@${sender.name}: ${message}`); return sendMessage(to, `@${sender.name}: ${message}`);
}; };

View File

@ -1,11 +1,18 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('chat sendMessage()', () => { 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 ctx = {req: {accessToken: {userId: 1}}};
let response = await app.models.Chat.sendMessage(ctx, '@salesPerson', 'I changed something'); let response = await app.models.Chat.sendMessage(ctx, '@salesPerson', 'I changed something');
expect(response.statusCode).toEqual(200); expect(response.statusCode).toEqual(200);
expect(response.message).toEqual('Fake notification sent'); 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();
});
}); });

View File

@ -35,9 +35,13 @@ CREATE TABLE `vn`.`zoneExclusion` (
KEY `zoneFk` (`zoneFk`), KEY `zoneFk` (`zoneFk`),
CONSTRAINT `zoneExclusion_ibfk_1` FOREIGN KEY (`zoneFk`) REFERENCES `zone` (`id`) ON DELETE CASCADE ON UPDATE CASCADE CONSTRAINT `zoneExclusion_ibfk_1` FOREIGN KEY (`zoneFk`) REFERENCES `zone` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
ALTER TABLE `vn`.`zone` ALTER TABLE `vn`.`zone`
DROP FOREIGN KEY `fk_zone_1`; DROP FOREIGN KEY `fk_zone_1`;
ALTER TABLE `vn`.`zone` ALTER TABLE `vn`.`zone`
DROP COLUMN `warehouseFk`, CHANGE COLUMN `warehouseFk` `warehouseFk` SMALLINT(6) UNSIGNED NULL DEFAULT NULL ;
DROP INDEX `fk_zone_1_idx`; ALTER TABLE `vn`.`zone`
ADD CONSTRAINT `fk_zone_1`
FOREIGN KEY (`warehouseFk`)
REFERENCES `vn`.`warehouse` (`id`)
ON DELETE NO ACTION
ON UPDATE CASCADE;

View File

@ -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);

View File

@ -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');

View File

@ -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 ;

View File

@ -53,13 +53,14 @@ INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossF
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`) INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`)
VALUES VALUES
(1, 'España', 0, 'ES', 1, 22), (1, 'España', 0, 'ES', 1, 24),
(2, 'Italia', 1, 'IT', 1, 25), (2, 'Italia', 1, 'IT', 1, 27),
(3, 'Alemania', 1, 'DE', 1, 20), (3, 'Alemania', 1, 'DE', 1, 22),
(4, 'Rumania', 1, 'RO', 1, 22), (4, 'Rumania', 1, 'RO', 1, 24),
(5, 'Holanda', 1, 'NL', 1, 16), (5, 'Holanda', 1, 'NL', 1, 18),
(19,'Francia', 1, 'FR', 1, 25), (8, 'Portugal', 1, 'PT', 1, 27),
(30,'Canarias', 1, 'IC', 1, 22); (19,'Francia', 1, 'FR', 1, 27),
(30,'Canarias', 1, 'IC', 1, 24);
INSERT INTO `vn`.`warehouse`(`id`, `name`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`) INSERT INTO `vn`.`warehouse`(`id`, `name`, `isComparative`, `isInventory`, `hasAvailable`, `isManaged`, `hasStowaway`, `hasDms`)
VALUES VALUES
@ -195,9 +196,9 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
VALUES 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), (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), (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), (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), (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), (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), (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), (5, 3, 103),
(6, 3, 104); (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 VALUES
(1, 'Ranged weapon longbow 2m', 18, 35, 5, 1, 9.10, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)), (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)), (2, 'Melee weapon combat first 15cm', 18, 35, 10, 2, 1.07, 0, NULL, 1, DATE_ADD(CURDATE(), INTERVAL -15 DAY)),

View File

@ -5,25 +5,14 @@ describe('buyUltimateFromInterval()', () => {
let today; let today;
let future; let future;
beforeAll(() => { beforeAll(() => {
let date = new Date(); let now = new Date();
let month = `${date.getMonth() + 1}`; now.setHours(0, 0, 0, 0);
let futureMonth = `${date.getMonth() + 2}`; today = now;
let day = date.getDate();
let year = date.getFullYear();
let futureYear = year;
if (month.toString().length < 2) month = '0' + month; let futureDate = new Date(now);
if (futureMonth.toString().length < 2) futureMonth = '0' + futureMonth; let futureMonth = now.getMonth() + 1;
if (futureMonth.toString() == '13') { futureDate.setMonth(futureMonth);
futureMonth = '01'; future = futureDate;
futureYear + 1;
}
if (day.toString().length < 2) day = `0${day}`;
today = [year, month, day].join('-');
future = [futureYear, futureMonth, day].join('-');
}); });
it(`should create a temporal table with it's data`, async() => { 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[0].buyFk).toEqual(3);
expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5);
expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); expect(buyUltimateFromIntervalTable[0].landed).toEqual(today);
expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(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() => { 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[0].buyFk).toEqual(3);
expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5);
expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); expect(buyUltimateFromIntervalTable[0].landed).toEqual(today);
expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(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() => { 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[0].buyFk).toEqual(3);
expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5); expect(buyUltimateFromIntervalTable[1].buyFk).toEqual(5);
expect(buyUltimateFromIntervalTable[0].landed).toEqual(new Date(today)); expect(buyUltimateFromIntervalTable[0].landed).toEqual(today);
expect(buyUltimateFromIntervalTable[1].landed).toEqual(new Date(today)); expect(buyUltimateFromIntervalTable[1].landed).toEqual(today);
}); });
}); });

View File

@ -111,9 +111,7 @@
"This phone already exists": "Este teléfono ya existe", "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 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 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 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", "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}}})", "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"
} }

View File

@ -15,7 +15,7 @@
<vn-treeview vn-id="treeview" root-label="Locations" <vn-treeview vn-id="treeview" root-label="Locations"
fetch-func="$ctrl.onFetch($item)" fetch-func="$ctrl.onFetch($item)"
sort-func="$ctrl.onSort($a, $b)"> sort-func="$ctrl.onSort($a, $b)">
<vn-check <vn-check acl-role="deliveryBoss"
ng-model="item.selected" ng-model="item.selected"
on-change="$ctrl.onSelection(value, item)" on-change="$ctrl.onSelection(value, item)"
triple-state="true" triple-state="true"

View File

@ -0,0 +1,34 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
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]);
};
};

View File

@ -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();
});
});

View File

@ -76,7 +76,7 @@ module.exports = Self => {
case 'ticketFk': case 'ticketFk':
return {'t.id': value}; return {'t.id': value};
case 'attenderFk': case 'attenderFk':
return {'tr.atenderFk': value}; return {'tr.attenderFk': value};
case 'isOk': case 'isOk':
return {'tr.isOk': value}; return {'tr.isOk': value};
case 'clientFk': case 'clientFk':
@ -106,7 +106,7 @@ module.exports = Self => {
tr.ticketFk, tr.ticketFk,
tr.quantity, tr.quantity,
tr.price, tr.price,
tr.atenderFk attenderFk, tr.attenderFk,
tr.description, tr.description,
tr.response, tr.response,
tr.saleFk, tr.saleFk,
@ -131,7 +131,7 @@ module.exports = Self => {
LEFT JOIN sale s ON s.id = tr.saleFk LEFT JOIN sale s ON s.id = tr.saleFk
LEFT JOIN worker wk ON wk.id = c.salesPersonFk LEFT JOIN worker wk ON wk.id = c.salesPersonFk
LEFT JOIN account.user u ON u.id = wk.userFk 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`); LEFT JOIN account.user ua ON ua.id = wka.userFk`);
stmt.merge(conn.makeSuffix(filter)); stmt.merge(conn.makeSuffix(filter));

View File

@ -27,7 +27,7 @@ module.exports = Self => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
if (!isEditable) 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 // Check if has sales with shelving
const sales = await models.Sale.find({ const sales = await models.Sale.find({

View File

@ -5,6 +5,7 @@ module.exports = Self => {
require('../methods/sale/updatePrice')(Self); require('../methods/sale/updatePrice')(Self);
require('../methods/sale/updateQuantity')(Self); require('../methods/sale/updateQuantity')(Self);
require('../methods/sale/updateConcept')(Self); require('../methods/sale/updateConcept')(Self);
require('../methods/sale/calculate')(Self);
Self.validatesPresenceOf('concept', { Self.validatesPresenceOf('concept', {
message: `Concept cannot be blank` message: `Concept cannot be blank`

View File

@ -33,13 +33,6 @@
"isOk": { "isOk": {
"type": "Boolean" "type": "Boolean"
}, },
"attenderFk": {
"type": "Number",
"required": true,
"mysql": {
"columnName": "atenderFk"
}
},
"response": { "response": {
"type": "String" "type": "String"
} }

View File

@ -9,6 +9,5 @@
on-step-end="$ctrl.onSubmit()"> on-step-end="$ctrl.onSubmit()">
</vn-step-control> </vn-step-control>
</vn-button-bar> </vn-button-bar>
<div compact>
<ui-view></ui-view> <ui-view></ui-view>
</div>

View File

@ -5,7 +5,7 @@
auto-load="true"> auto-load="true">
</vn-crud-model> </vn-crud-model>
<form name="form"> <form name="form">
<vn-card class="vn-pa-lg"> <vn-card class="vn-w-md vn-pa-lg">
<vn-horizontal> <vn-horizontal>
<vn-autocomplete vn-one <vn-autocomplete vn-one
vn-id="client" vn-id="client"

View File

@ -1,5 +1,5 @@
<form name="form"> <form name="form">
<vn-card class="vn-pa-lg"> <vn-card class="vn-w-md vn-pa-lg">
<vn-horizontal> <vn-horizontal>
<vn-autocomplete vn-one <vn-autocomplete vn-one
url="TicketUpdateActions" url="TicketUpdateActions"

View File

@ -13,8 +13,14 @@ class Controller {
this.data.registerChild(this); this.data.registerChild(this);
} }
$onChanges() { get ticket() {
this.ticket.option = 1; return this._ticket;
}
set ticket(value) {
this._ticket = value;
if (value) this.ticket.option = 1;
} }
onStepChange(state) { onStepChange(state) {

View File

@ -21,8 +21,7 @@ describe('ticket', () => {
describe('onSubmit()', () => { describe('onSubmit()', () => {
it(`should return an error if the item doesn't have option property in the controller`, () => { it(`should return an error if the item doesn't have option property in the controller`, () => {
controller.ticket = {}; controller._ticket = {id: 1};
controller.onSubmit(); controller.onSubmit();
expect(vnApp.showError).toHaveBeenCalledWith('Choose an option'); expect(vnApp.showError).toHaveBeenCalledWith('Choose an option');

View File

@ -1,42 +1,40 @@
<form name="form"> <form name="form">
<vn-card class="vn-pa-lg"> <vn-card class="vn-w-lg vn-pa-lg">
<vn-horizontal> <vn-table>
<table class="vn-table"> <vn-thead>
<thead> <vn-tr>
<tr> <vn-th number>Item</vn-th>
<th number translate>Item</th> <vn-th style="text-align:center">Description</vn-th>
<th translate style="text-align:center">Description</th> <vn-th number>Quantity</vn-th>
<th number translate>Quantity</th> <vn-th number>Price (PPU)</vn-th>
<th number translate>Price (PPU)</th> <vn-th number>New price (PPU)</vn-th>
<th number translate>New price (PPU)</th> <vn-th number>Price difference</vn-th>
<th number translate>Price difference</th> </vn-tr>
</tr> </vn-thead>
</thead> <vn-tbody>
<tbody> <vn-tr ng-repeat="sale in $ctrl.ticket.sale.items track by sale.id">
<tr ng-repeat="sale in $ctrl.ticket.sale.items track by sale.id"> <vn-td number>{{("000000"+sale.itemFk).slice(-6)}}</vn-td>
<td number>{{("000000"+sale.itemFk).slice(-6)}}</td> <vn-td expand>
<td expand>
<vn-fetched-tags <vn-fetched-tags
max-length="6" max-length="6"
item="::sale.item" item="::sale.item"
name="::sale.concept"> name="::sale.concept">
</vn-fetched-tags> </vn-fetched-tags>
</td> </vn-td>
<td number>{{::sale.quantity}}</td> <vn-td number>{{::sale.quantity}}</vn-td>
<td number>{{::sale.price | currency: 'EUR': 2}}</td> <vn-td number>{{::sale.price | currency: 'EUR': 2}}</vn-td>
<td number>{{::sale.component.newPrice | currency: 'EUR': 2}}</td> <vn-td number>{{::sale.component.newPrice | currency: 'EUR': 2}}</vn-td>
<td number>{{::sale.component.difference | currency: 'EUR': 2}}</td> <vn-td number>{{::sale.component.difference | currency: 'EUR': 2}}</vn-td>
</tr> </vn-tr>
</tbody> </vn-tbody>
<tfoot> <vn-tfoot>
<tr> <vn-tr>
<td colspan="3"></td> <vn-td colspan="3"></vn-td>
<td number><strong>{{$ctrl.totalPrice | currency: 'EUR': 2}}</strong></td> <vn-td number><strong>{{$ctrl.totalPrice | currency: 'EUR': 2}}</strong></vn-td>
<td number><strong>{{$ctrl.totalNewPrice | currency: 'EUR': 2}}</strong></td> <vn-td number><strong>{{$ctrl.totalNewPrice | currency: 'EUR': 2}}</strong></vn-td>
<td number><strong>{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}</strong></td> <vn-td number><strong>{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}</strong></vn-td>
</tr> </vn-tr>
</tfoot> </vn-tfoot>
</table> </vn-table>
</vn-horizontal>
</vn-card> </vn-card>
</form> </form>

View File

@ -7,6 +7,17 @@ class Controller {
$onInit() { $onInit() {
this.data.registerChild(this); this.data.registerChild(this);
}
get ticket() {
return this._ticket;
}
set ticket(value) {
this._ticket = value;
if (!value) return;
this.getTotalPrice(); this.getTotalPrice();
this.getTotalNewPrice(); this.getTotalNewPrice();
this.getTotalDifferenceOfPrice(); this.getTotalDifferenceOfPrice();

View File

@ -40,7 +40,7 @@ class Controller extends Component {
showChangeShipped() { showChangeShipped() {
if (!this.isEditable) { 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; return;
} }
this.newShipped = this.ticket.shipped; this.newShipped = this.ticket.shipped;

View File

@ -32,6 +32,11 @@ class Controller {
callback: this.createClaim, callback: this.createClaim,
show: () => this.isEditable show: () => this.isEditable
}, },
{
name: 'Recalculate price',
callback: this.calculateSalePrice,
show: () => this.hasOneSaleSelected()
},
]; ];
this._sales = []; this._sales = [];
this.imagesPath = '//verdnatura.es/vn-image-data/catalog'; this.imagesPath = '//verdnatura.es/vn-image-data/catalog';
@ -534,6 +539,21 @@ class Controller {
this.isEditable = res.data; 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']; Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];

View File

@ -30,3 +30,4 @@ SMSAvailability: >-
Continue anyway?: ¿Continuar de todas formas? Continue anyway?: ¿Continuar de todas formas?
This ticket is now empty: El ticket ha quedado vacio This ticket is now empty: El ticket ha quedado vacio
Do you want to delete it?: ¿Quieres borrarlo? Do you want to delete it?: ¿Quieres borrarlo?
Recalculate price: Recalcular precio

View File

@ -1,5 +1,6 @@
import '../index.js'; import '../index.js';
import watcher from 'core/mocks/watcher'; import watcher from 'core/mocks/watcher';
import crudModel from 'core/mocks/crud-model';
describe('Ticket', () => { describe('Ticket', () => {
describe('Component vnTicketSale', () => { describe('Component vnTicketSale', () => {
@ -40,6 +41,7 @@ describe('Ticket', () => {
$scope.watcher = watcher; $scope.watcher = watcher;
$scope.sms = {open: () => {}}; $scope.sms = {open: () => {}};
$scope.ticket = ticket; $scope.ticket = ticket;
$scope.model = crudModel;
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
Object.defineProperties($state.params, { Object.defineProperties($state.params, {
id: { id: {
@ -334,5 +336,27 @@ describe('Ticket', () => {
expect(window.open).toHaveBeenCalledWith('/somePath', '_blank'); 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();
});
});
}); });
}); });

View File

@ -30,8 +30,11 @@
"isConfirmed": { "isConfirmed": {
"type": "Boolean" "type": "Boolean"
}, },
"isRaid": { "isVirtual": {
"type": "Boolean" "type": "Boolean",
"mysql": {
"columnName": "isRaid"
}
}, },
"commission": { "commission": {
"type": "Number" "type": "Number"

View File

@ -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();
});
});

View File

@ -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();
// });
// });

View File

@ -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);
});
}
};

View File

@ -2,7 +2,6 @@ module.exports = Self => {
require('../methods/worker/filter')(Self); require('../methods/worker/filter')(Self);
require('../methods/worker/mySubordinates')(Self); require('../methods/worker/mySubordinates')(Self);
require('../methods/worker/isSubordinate')(Self); require('../methods/worker/isSubordinate')(Self);
require('../methods/worker/getWorkerInfo')(Self);
require('../methods/worker/getWorkedHours')(Self); require('../methods/worker/getWorkedHours')(Self);
require('../methods/worker/sendMessage')(Self); require('../methods/worker/getWorkerInfo')(Self);
}; };

View File

@ -24,55 +24,66 @@ describe('Worker', () => {
describe('hours() setter', () => { describe('hours() setter', () => {
it(`should set hours data at it's corresponding week day`, () => { it(`should set hours data at it's corresponding week day`, () => {
let wednesday = new Date(controller.started.getTime()); let today = new Date();
wednesday.setDate(wednesday.getDate() + 2);
$httpBackend.whenRoute('GET', 'WorkerTimeControls/filter') controller.date = today;
.respond([
let hours = [
{ {
id: 1, id: 1,
timed: controller.started.toJSON(), timed: controller.started.toJSON(),
userFk: 1 userFk: 1
}, { }, {
id: 2, id: 2,
timed: wednesday.toJSON(), timed: today.toJSON(),
userFk: 1 userFk: 1
}, { }, {
id: 3, id: 3,
timed: wednesday.toJSON(), timed: today.toJSON(),
userFk: 1 userFk: 1
} }
]); ];
$httpBackend.flush(); controller.hours = hours;
let todayInWeek = today.getDay() - 1;
expect(controller.weekDays.length).toEqual(7); expect(controller.weekDays.length).toEqual(7);
expect(controller.weekDays[0].hours.length).toEqual(1); 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() ', () => { describe('getWorkedHours() ', () => {
it(`should return a total worked hours from 07:00 to 15:00`, () => { fit(`should set the week days and the worked hours in today`, () => {
const hourOne = new Date(); let today = 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);
const weekday = {hours: [ $httpBackend.whenRoute('GET', 'WorkerTimeControls/filter').respond({});
{id: 1, timed: hourOne}, $httpBackend.whenRoute('GET', 'WorkerCalendars/absences').respond({});
{id: 2, timed: hourTwo}, $httpBackend.whenRoute('GET', 'Workers/:id/getWorkedHours')
{id: 3, timed: hourThree}, .respond([
{id: 4, timed: hourFour} {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);
}); });
}); });

View File

@ -23,12 +23,11 @@ body {
.grid-block { .grid-block {
min-width: 300px; min-width: 300px;
max-width: 600px; max-width: 600px;
width: 600px; margin: 0 auto;
margin: 0 auto color: #333
} }
h1 { h1 {
font-weight: 100; font-weight: 100;
font-size: 1.5em; font-size: 1.5em
color: #333;
} }

View File

@ -8,7 +8,7 @@
"i18n": { "i18n": {
"locale": "es", "locale": "es",
"fallbackLocale": "es", "fallbackLocale": "es",
"silentTranslationWarn": true "silentTranslationWarn": false
}, },
"pdf": { "pdf": {
"format": "A4", "format": "A4",

View File

@ -8,7 +8,7 @@ header .logo img {
header .topbar { header .topbar {
background-color: #95d831; background-color: #95d831;
height: 25px height: 10px
} }
.topbar:after { .topbar:after {

View File

@ -4,6 +4,6 @@
<section v-if="centerText" class="uppercase">{{centerText}}</section> <section v-if="centerText" class="uppercase">{{centerText}}</section>
<section class="number">{{$t('numPages')}}</section> <section class="number">{{$t('numPages')}}</section>
</section> </section>
<p class="phytosanitary">{{$t('law.phytosanitary')}}</p> <p class="phytosanitary" v-if="showPhytosanitary">{{$t('law.phytosanitary')}}</p>
<p class="privacy" v-html="$t('law.privacy')"></p> <p class="privacy" v-html="$t('law.privacy')"></p>
</footer> </footer>

View File

@ -1,4 +1,4 @@
module.exports = { module.exports = {
name: 'report-footer', name: 'report-footer',
props: ['leftText', 'centerText', 'locale'] props: ['leftText', 'centerText', 'locale', 'showPhytosanitary']
}; };

View File

@ -10,8 +10,11 @@ let configFiles = [
]; ];
for (let configFile of configFiles) { for (let configFile of configFiles) {
if (fs.existsSync(configFile)) if (fs.existsSync(configFile)) {
Object.assign(config, require(configFile)); const conf = require(configFile);
for (let prop in conf)
Object.assign(config[prop], conf[prop]);
}
} }
config.env = env; config.env = env;

View File

@ -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();

View File

@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<title>{{ $t('subject') }}</title>
</head>
<body>
<section class="container">
<!-- Header component -->
<email-header></email-header>
<!-- End header component -->
<section class="main">
<!-- Title block -->
<div class="title">
<h1>{{ $t('title') }}</h1>
</div>
<!-- Title block end -->
<p>{{$t('description.dear')}},</p>
<p>{{$t('description.instructions')}}</p>
<p>{{$t('description.conclusion')}}</p>
</section>
<!-- Footer component -->
<email-footer :locale="locale"></email-footer>
<!-- End footer component -->
</section>
</body>
</html>

View File

@ -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'),
},
};

View File

@ -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 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 dagré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 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 dagréer, Messieurs, nos salutations distinguées.`,
conclusion: 'Bien cordialement'
},
},
},
};

View File

@ -27,9 +27,11 @@
<div class="grid-row"> <div class="grid-row">
<div class="grid-block white vn-pa-lg"> <div class="grid-block white vn-pa-lg">
<h1>{{ $t('title') }}</h1> <h1>{{ $t('title') }}</h1>
<p>{{$t('dearClient')}},</p> <p>{{$t('dear')}},</p>
<p v-html="$t('clientData')"></p> <p v-html="$t('description', [ticketId])"></p>
<p v-html="$t('poll')"></p>
<p v-html="$t('help')"></p> <p v-html="$t('help')"></p>
<p v-html="$t('conclusion')"></p>
</div> </div>
</div> </div>
<!-- Footer block --> <!-- Footer block -->

View File

@ -1,6 +1,10 @@
subject: Aquí tienes tu albarán subject: Aquí tienes tu albarán
title: "¡Este es tu albarán!" title: "¡Este es tu albarán!"
dearClient: Estimado cliente dear: Estimado cliente
clientData: A continuación adjuntamos tu albarán. description: Ya está disponible el albarán correspondiente al pedido {0}. <br/>
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, <strong>¡estamos para help: Cualquier duda que te surja, no dudes en consultarla, <strong>¡estamos para
atenderte!</strong> atenderte!</strong>
conclusion: ¡Gracias por tu atención!

View File

@ -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.<br/>
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, <strong>nous sommes là pour vous aider!</strong>
conclusion: Merci pour votre attention!

View File

@ -2,7 +2,17 @@ subject: Solicitud de domiciliación bancaria
title: Domiciliación SEPA CORE title: Domiciliación SEPA CORE
description: description:
dear: Estimado cliente dear: Estimado cliente
instructions: Para poder tramitar tu solicitud de cambio de tu forma de pago a giro instructions: <p>Dadas las excelentes relaciones existentes entre nuestras
bancario, te adjuntamos los documentos correspondientes a la ley de pago, que dos empresas y para facilitar los procesos de pago de nuestras facturas,
tienes que cumplimentar y enviarnos. sugerimos el uso del sistema de domiciliación bancaria SEPA CORE.</p>
conclusion: Gracias por tu atención. <p>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.</p>
<p>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.</p>
<p>Este sistema se basa en la transmisión electrónica de datos;
el manejo de documentos físicos ha sido eliminado.</p>
<p>Le agradecemos su cooperación,</p>
conclusion: ¡Gracias por su atención!

View File

@ -0,0 +1,27 @@
subject: Autorisation pour débit
title: Autorisation pour débit
description:
dear: Messieurs
instructions: <p>É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.</p>
<p>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.</p>
<p>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.</p>
<p>Ce système étant basé sur la transmission de données de
manière électronique, le maniement de documents
physiques á été éliminé</p>
<p>En vous remercieront pour votre collaboration, nous vous
prions dagréer, Messieurs, nos salutations distinguées.</p>
conclusion: Bien cordialement

View File

@ -28,11 +28,11 @@
<div class="grid-block white vn-pa-lg"> <div class="grid-block white vn-pa-lg">
<h1>{{ $t('title') }}</h1> <h1>{{ $t('title') }}</h1>
<p>{{$t('description.dear')}},</p> <p>{{$t('description.dear')}},</p>
<p>{{$t('description.instructions')}}</p> <div v-html="$t('description.instructions')"></div>
<p>{{$t('description.conclusion')}}</p> <p>{{$t('description.conclusion')}}</p>
</div> </div>
</div> </div>
<!-- Block --> <!-- Attachments block -->
<div class="grid-row" v-if="isPreview"> <div class="grid-row" v-if="isPreview">
<div class="grid-block white vn-pa-lg"> <div class="grid-block white vn-pa-lg">
<attachment v-for="attachment in attachments" <attachment v-for="attachment in attachments"

View File

@ -238,6 +238,7 @@
<div class="grid-row"> <div class="grid-row">
<div class="grid-block"> <div class="grid-block">
<report-footer id="pageFooter" <report-footer id="pageFooter"
v-bind:show-phytosanitary="true"
v-bind:left-text="$t('ticket', [ticket.id])" v-bind:left-text="$t('ticket', [ticket.id])"
v-bind:center-text="client.socialName" v-bind:center-text="client.socialName"
v-bind:is-preview="isPreview" v-bind:is-preview="isPreview"

View File

@ -1,8 +0,0 @@
const CssReader = require(`${appPath}/lib/cssReader`);
module.exports = new CssReader([
`${appPath}/common/css/layout.css`,
`${appPath}/common/css/report.css`,
`${appPath}/common/css/misc.css`,
`${__dirname}/style.css`])
.mergeStyles();

View File

@ -1,31 +0,0 @@
.payment-type {
width: auto
}
.payment-type th:nth-child(2), .payment-type th:nth-child(5) {
padding: 10px !important
}
.payment-type th:nth-child(3){
padding: 0 50px !important
}
.table-margin {
margin-top: 20px
}
.grey-background {
background-color: #DDD
}
.emptyField {
width: 100%;
}
.row-oriented.input-table > tbody > tr > td {
width: 10% !important
}
.row-oriented.input-table > tbody > tr > th {
width: 90% !important
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

View File

@ -1,189 +0,0 @@
<!DOCTYPE html>
<html lang="es">
<body>
<section class="container">
<!-- Header component -->
<report-header :locale="locale"></report-header>
<!-- End header component -->
<section class="main">
<h1 class="title centered">{{$t('title')}}</h1>
<section class="panel">
<section class="header">{{$t('Creditor')}}</section>
<section class="body">
<table class="row-oriented">
<tbody>
<tr>
<td >{{$t('supplier.name')}}:</td>
<th>{{supplierName}}</th>
</tr>
<tr>
<td>{{$t('supplier.street')}}:</td>
<th>{{supplierStreet}}</th>
</tr>
<tr>
<td></td>
<th>{{supplierPostCode}}, {{supplierCity}} ({{supplierProvince}})</th>
</tr>
<tr>
<td></td>
<th>{{supplierCountry}}</th>
</tr>
</tbody>
</table>
</section>
</section>
<section class="panel">
<section class="header">{{$t('Deptor')}}</section>
<section class="body">
<table class="row-oriented">
<tbody>
<tr>
<td>{{$t('client.name')}}:</td>
<th>{{clientName}}</th>
</tr>
<tr>
<td>{{$t('client.street')}}:</td>
<th>{{clientStreet}}</th>
</tr>
<tr>
<td></td>
<th>{{clientPostCode}}, {{clientCity}} ({{clientProvince}})</th>
</tr>
<tr>
<td></td>
<th>{{clientCountry}}</th>
</tr>
<tr>
<td>{{$t('client.fi')}}:</td>
<th>
<section class="field square">
<span v-for="i in 12">{{fi.charAt(i)}}</span>
</section>
</th>
</tr>
</tbody>
</table>
</section>
</section>
<p class="font">{{$t('description')}}</p>
<section class="panel">
<section class="header">{{$t('Bank')}}</section>
<section class="body">
<section class="vertical-text">
{{$t('client.toCompleteByClient')}}
</section>
<table class="row-oriented input-table">
<tbody>
<tr>
<td>{{$t('bank.name')}}:</td>
<th><span class="emptyField"></span></th>
</tr>
<tr>
<td>{{$t('bank.street')}}:</td>
<th><span class="emptyField"></span></th>
</tr>
</tbody>
</table>
<!-- RIB -->
<table class="table-margin">
<tbody>
<tr>
<td>{{$t('bank.account')}}:</td>
</tr>
<tr>
<td style="padding-right: 1em">
<section class="field square">
<span v-for="i in 5"></span>
</section>
</td>
<td style="padding-right: 1em">
<section class="field square">
<span v-for="i in 5"></span>
</section>
</td>
<td style="padding-right: 1em">
<section class="field square">
<span v-for="i in 11"></span>
</section>
</td>
<td>
<section class="field square" >
<span v-for="i in 2"></span>
</section>
</td>
</tr>
<tr>
<td style="padding-right: 1em">
<div class="line">
<div class="vertical-aligned">
<span>{{$t('bank.bankCode')}}</span>
</div>
</div>
</td>
<td style="padding-right: 1em">
<div class="line">
<div class="vertical-aligned">
<span>{{$t('bank.agencyCode')}}</span>
</div>
</div>
</td>
<td style="padding-right: 1em">
<div class="line">
<div class="vertical-aligned">
<span>{{$t('bank.accountNumber')}}</span>
</div>
</div>
</td>
<td>
<div class="line">
<div class="vertical-aligned">
<span>{{$t('bank.ribKey')}}</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<!-- IBAN -->
<table class="table-margin">
<tbody>
<tr>
<td>IBAN:</td>
</tr>
<tr>
<td>
<section class="field square">
<span class="grey-background">F</span>
<span class="grey-background">R</span>
<span v-for="i in 23"></span>
</section>
</td>
</tr>
</tbody>
</table>
</section>
</section>
<p>{{$t('authorization')}}</p>
<!-- signature -->
<section class="signature panel">
<section class="header">{{$t('client.sign')}}</section>
<section class="body centered">
<section>
<p>{{$t('client.signDate')}}:</p>
</section>
</section>
</section>
</section>
<!-- Footer component -->
<report-footer id="pageFooter"
:left-text="$t('order', [mandateCode])"
:center-text="clientName"
:locale="locale">
</report-footer>
<!-- End footer component -->
</section>
</body>
</html>

View File

@ -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'),
},
};

View File

@ -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}',
},
},
};

View File

@ -29,11 +29,13 @@ client:
swift: Swift BIC swift: Swift BIC
accountNumber: Número de cuenta - IBAN accountNumber: Número de cuenta - IBAN
accountHolder: "(Titular/es de la cuenta de cargo)" accountHolder: "(Titular/es de la cuenta de cargo)"
accountNumberFormat: En España el IBAN consta de 24 posiciones comenzando siempre accountNumberFormat: En {0} el IBAN consta de {1} posiciones comenzando siempre por {2}
por ES
paymentType: Tipo de pago paymentType: Tipo de pago
recurrent: Recurrente recurrent: Recurrente
unique: Único unique: Único
signLocation: Fecha - Localidad signLocation: Fecha - Localidad
sign: Firma del deudor y sello sign: Firma del deudor y sello
order: Ord. domiciliación {0} order: Ord. domiciliación {0}
Francia: Francia
España: España
Portugal: Portugal

View File

@ -1,39 +1,38 @@
title: Orden de domiciliación de adeudo SEPA CORE title: Mandat de domiciliation Européene LCR
description: Mediante la firma de esta orden de domiciliación, el deudor autoriza description: En signant ce formulaire de mandat, vous autorisez VERDNATURA LEVANTE SL
(A) al acreedor a enviar instrucciones a la entidad del deudor para adeudar su cuenta à envoyer des instructions à votre banque pour débiter votre compte, et (B) votre banque
y (B) a la entidad para efectuar los adeudos en su cuenta siguiendo las instrucciones à débiter votre compte conformément aux instructions de VERDNATURA LEVANTE SL.
del acreedor.Como parte de sus derechos, el deudor está legitimado al reembolso Vous bénéficiez dun droit au remboursement par votre banque selon les conditions décrites
por su entidad en los términos y condiciones del contrato suscrito con la misma. dans la convention que vous avez passée avec elle. Toute demande de remboursement doit être
La solicitud de reembolso deberá efectuarse dentro de las ocho semanas que adeudo présentée dans les 8 semaines suivant la date de débit de votre compte.
en cuenta. Puede obtener información adicional sobre sus derechos en su entidad Votre banque peut vous renseigner au sujet de vos droits relatifs à ce mandat.
financiera. documentCopy: Veuillez dater, signer et retourner ce document à votre banque.
documentCopy: Debe llevar a su Entidad Bancaria una copia del documento firmado para mandatoryFields: TOUS LES CHAMPS DOIVENT ÊTRE REINSEGNÉS IMPÉRATIVEMENT.
que lo registre y evitar la devolución. sendOrder: APRÈS SIGNATURA, RENVOYER AU CRÉANCIER ET AU VOTRE ÉTABLISSEMENT FINANCIER.
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.
supplier: supplier:
toCompleteBySupplier: A cumplimentar por el acreedor toCompleteBySupplier: Á compléter pour le créancier
orderReference: Referencia de la orden de domiciliación orderReference: Numéro de référence du mandat
identifier: Identificador del acreedor identifier: Identifiant créancier
name: Nombre del acreedor name: Nom du céancier
street: Dirección street: Adresse
location: CP - Población - Provincia location: CP - Commune - Départament
country: País country: Pays
client: client:
toCompleteByClient: A cumplimentar por el deudor toCompleteByClient: Á compléter pour le débiteur
name: Nombre del deudor/es name: Nom du débiteur(s)
street: Dirección del deudor street: Adresse du(des) débiteur(s)
location: CP - Población - Provincia location: CP - Commune - Départament
country: País del deudor country: País du(des) débiteur(s)
swift: Swift BIC swift: Swift BIC
accountNumber: Número de cuenta - IBAN accountNumber: Numéro de compte - IBAN
accountHolder: "(Titular/es de la cuenta de cargo)" accountHolder: (Débiteur(s) de compte)
accountNumberFormat: En España el IBAN consta de 24 posiciones comenzando siempre accountNumberFormat: En {0} l'IBAN compte {1} postes commençant toujours par {2}
por ES paymentType: Type de paiemen
paymentType: Tipo de pago recurrent: Versement périodique
recurrent: Recurrente unique: Paiement unique
unique: Único signLocation: Date - Commune
signLocation: Fecha - Localidad sign: Signature du débiteur et tampon
sign: Firma del deudor y sello order: Réf. mandat {0}
order: Ord. domiciliación {0} Francia: France
España: Espagne
Portugal: Portugal

View File

@ -22,18 +22,20 @@ supplier:
country: País country: País
client: client:
toCompleteByClient: A preencher pelo devedor toCompleteByClient: A preencher pelo devedor
name: Nome do devedor (Titular da conta) name: Nome do devedor
street: Dirección del deudor street: Dirección del deudor
location: Cod. Postal - Município - Distrito location: Cod. Postal - Município - Distrito
country: País do devedor country: País do devedor
swift: Swift BIC swift: Swift BIC
accountNumber: Número de Conta IBAN accountNumber: Número de Conta IBAN
accountHolder: "(Titular/es de la cuenta de cargo)" accountHolder: "(Titular(es) da conta)"
accountNumberFormat: Em Portugal o IBAN é composto por 25 dígitos e começa sempre accountNumberFormat: Em {0} o IBAN é composto pelo {1} dígitos e começa sempre pelo {2}
por PT
paymentType: Tipos de pagamento Pagamento paymentType: Tipos de pagamento Pagamento
recurrent: Recorrente recurrent: Recorrente
unique: Pagamento pontual unique: Pagamento pontual
signLocation: Data - Localidade signLocation: Data - Localidade
sign: Assinatura e carimbo do devedor sign: Assinatura e carimbo do devedor
order: Referência da ordem {0} order: Referência da ordem {0}
Francia: França
España: Espanha
Portugal: Portugal

View File

@ -32,7 +32,10 @@
</tr> </tr>
<tr> <tr>
<td>{{$t('supplier.identifier')}}</td> <td>{{$t('supplier.identifier')}}</td>
<th>ES89000B97367486</th> <th>
<div>ES89000B97367486</div>
<div>B97367486-000</div>
</th>
</tr> </tr>
<tr> <tr>
<td>{{$t('supplier.name')}}</td> <td>{{$t('supplier.name')}}</td>
@ -110,14 +113,19 @@
<div class="field square"> <div class="field square">
<span>{{client.countryCode.substr(0, 1)}}</span> <span>{{client.countryCode.substr(0, 1)}}</span>
<span>{{client.countryCode.substr(1, 1)}}</span> <span>{{client.countryCode.substr(1, 1)}}</span>
<span v-for="i in client.ibanLength"></span> <span v-for="i in (client.ibanLength - 2)"></span>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="description" colspan="2"> <td class="description" colspan="2">
<div class="line"> <div class="line">
<span>{{$t('client.accountNumberFormat')}}</span> <span>{{$t('client.accountNumberFormat', [
$t(`${client.country}`),
client.ibanLength,
client.countryCode
])}}
</span>
</div> </div>
</td> </td>
</tr> </tr>
@ -145,7 +153,7 @@
</tr> </tr>
<tr> <tr>
<td>{{$t('client.signLocation')}}</td> <td>{{$t('client.signLocation')}}</td>
<th>{{dated}}, {{supplier.province}}</th> <th>{{dated}}, {{client.province}}</th>
</tr> </tr>
<tr> <tr>
<td>{{$t('client.sign')}}</td> <td>{{$t('client.sign')}}</td>