Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into dev
This commit is contained in:
commit
8629b72d55
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
|
@ -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');
|
|
@ -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 ;
|
|
@ -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)),
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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}}})"
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
<vn-treeview vn-id="treeview" root-label="Locations"
|
||||
fetch-func="$ctrl.onFetch($item)"
|
||||
sort-func="$ctrl.onSort($a, $b)">
|
||||
<vn-check
|
||||
<vn-check acl-role="deliveryBoss"
|
||||
ng-model="item.selected"
|
||||
on-change="$ctrl.onSelection(value, item)"
|
||||
triple-state="true"
|
||||
|
|
|
@ -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]);
|
||||
};
|
||||
};
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -33,13 +33,6 @@
|
|||
"isOk": {
|
||||
"type": "Boolean"
|
||||
},
|
||||
"attenderFk": {
|
||||
"type": "Number",
|
||||
"required": true,
|
||||
"mysql": {
|
||||
"columnName": "atenderFk"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"type": "String"
|
||||
}
|
||||
|
|
|
@ -9,6 +9,5 @@
|
|||
on-step-end="$ctrl.onSubmit()">
|
||||
</vn-step-control>
|
||||
</vn-button-bar>
|
||||
<div compact>
|
||||
<ui-view></ui-view>
|
||||
</div>
|
||||
|
||||
<ui-view></ui-view>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<form name="form">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-card class="vn-w-md vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
vn-id="client"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<form name="form">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-card class="vn-w-md vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one
|
||||
url="TicketUpdateActions"
|
||||
|
|
|
@ -13,8 +13,14 @@ class Controller {
|
|||
this.data.registerChild(this);
|
||||
}
|
||||
|
||||
$onChanges() {
|
||||
this.ticket.option = 1;
|
||||
get ticket() {
|
||||
return this._ticket;
|
||||
}
|
||||
|
||||
set ticket(value) {
|
||||
this._ticket = value;
|
||||
|
||||
if (value) this.ticket.option = 1;
|
||||
}
|
||||
|
||||
onStepChange(state) {
|
||||
|
|
|
@ -21,8 +21,7 @@ describe('ticket', () => {
|
|||
|
||||
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');
|
||||
|
|
|
@ -1,42 +1,40 @@
|
|||
<form name="form">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<table class="vn-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th number translate>Item</th>
|
||||
<th translate style="text-align:center">Description</th>
|
||||
<th number translate>Quantity</th>
|
||||
<th number translate>Price (PPU)</th>
|
||||
<th number translate>New price (PPU)</th>
|
||||
<th number translate>Price difference</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="sale in $ctrl.ticket.sale.items track by sale.id">
|
||||
<td number>{{("000000"+sale.itemFk).slice(-6)}}</td>
|
||||
<td expand>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="::sale.item"
|
||||
name="::sale.concept">
|
||||
</vn-fetched-tags>
|
||||
</td>
|
||||
<td number>{{::sale.quantity}}</td>
|
||||
<td number>{{::sale.price | currency: 'EUR': 2}}</td>
|
||||
<td number>{{::sale.component.newPrice | currency: 'EUR': 2}}</td>
|
||||
<td number>{{::sale.component.difference | currency: 'EUR': 2}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3"></td>
|
||||
<td number><strong>{{$ctrl.totalPrice | currency: 'EUR': 2}}</strong></td>
|
||||
<td number><strong>{{$ctrl.totalNewPrice | currency: 'EUR': 2}}</strong></td>
|
||||
<td number><strong>{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}</strong></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</vn-horizontal>
|
||||
<vn-card class="vn-w-lg vn-pa-lg">
|
||||
<vn-table>
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th number>Item</vn-th>
|
||||
<vn-th style="text-align:center">Description</vn-th>
|
||||
<vn-th number>Quantity</vn-th>
|
||||
<vn-th number>Price (PPU)</vn-th>
|
||||
<vn-th number>New price (PPU)</vn-th>
|
||||
<vn-th number>Price difference</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="sale in $ctrl.ticket.sale.items track by sale.id">
|
||||
<vn-td number>{{("000000"+sale.itemFk).slice(-6)}}</vn-td>
|
||||
<vn-td expand>
|
||||
<vn-fetched-tags
|
||||
max-length="6"
|
||||
item="::sale.item"
|
||||
name="::sale.concept">
|
||||
</vn-fetched-tags>
|
||||
</vn-td>
|
||||
<vn-td number>{{::sale.quantity}}</vn-td>
|
||||
<vn-td number>{{::sale.price | currency: 'EUR': 2}}</vn-td>
|
||||
<vn-td number>{{::sale.component.newPrice | currency: 'EUR': 2}}</vn-td>
|
||||
<vn-td number>{{::sale.component.difference | currency: 'EUR': 2}}</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
<vn-tfoot>
|
||||
<vn-tr>
|
||||
<vn-td colspan="3"></vn-td>
|
||||
<vn-td number><strong>{{$ctrl.totalPrice | currency: 'EUR': 2}}</strong></vn-td>
|
||||
<vn-td number><strong>{{$ctrl.totalNewPrice | currency: 'EUR': 2}}</strong></vn-td>
|
||||
<vn-td number><strong>{{$ctrl.totalPriceDifference | currency: 'EUR': 2}}</strong></vn-td>
|
||||
</vn-tr>
|
||||
</vn-tfoot>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</form>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'];
|
||||
|
|
|
@ -30,3 +30,4 @@ SMSAvailability: >-
|
|||
Continue anyway?: ¿Continuar de todas formas?
|
||||
This ticket is now empty: El ticket ha quedado vacio
|
||||
Do you want to delete it?: ¿Quieres borrarlo?
|
||||
Recalculate price: Recalcular precio
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -30,8 +30,11 @@
|
|||
"isConfirmed": {
|
||||
"type": "Boolean"
|
||||
},
|
||||
"isRaid": {
|
||||
"type": "Boolean"
|
||||
"isVirtual": {
|
||||
"type": "Boolean",
|
||||
"mysql": {
|
||||
"columnName": "isRaid"
|
||||
}
|
||||
},
|
||||
"commission": {
|
||||
"type": "Number"
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
// });
|
||||
// });
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"i18n": {
|
||||
"locale": "es",
|
||||
"fallbackLocale": "es",
|
||||
"silentTranslationWarn": true
|
||||
"silentTranslationWarn": false
|
||||
},
|
||||
"pdf": {
|
||||
"format": "A4",
|
||||
|
|
|
@ -8,7 +8,7 @@ header .logo img {
|
|||
|
||||
header .topbar {
|
||||
background-color: #95d831;
|
||||
height: 25px
|
||||
height: 10px
|
||||
}
|
||||
|
||||
.topbar:after {
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
<section v-if="centerText" class="uppercase">{{centerText}}</section>
|
||||
<section class="number">{{$t('numPages')}}</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>
|
||||
</footer>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
name: 'report-footer',
|
||||
props: ['leftText', 'centerText', 'locale']
|
||||
props: ['leftText', 'centerText', 'locale', 'showPhytosanitary']
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
|
@ -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>
|
|
@ -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'),
|
||||
},
|
||||
};
|
|
@ -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'
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -27,9 +27,11 @@
|
|||
<div class="grid-row">
|
||||
<div class="grid-block white vn-pa-lg">
|
||||
<h1>{{ $t('title') }}</h1>
|
||||
<p>{{$t('dearClient')}},</p>
|
||||
<p v-html="$t('clientData')"></p>
|
||||
<p>{{$t('dear')}},</p>
|
||||
<p v-html="$t('description', [ticketId])"></p>
|
||||
<p v-html="$t('poll')"></p>
|
||||
<p v-html="$t('help')"></p>
|
||||
<p v-html="$t('conclusion')"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer block -->
|
||||
|
|
|
@ -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}. <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
|
||||
atenderte!</strong>
|
||||
conclusion: ¡Gracias por tu atención!
|
||||
|
|
|
@ -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!
|
|
@ -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: <p>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.</p>
|
||||
<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!
|
||||
|
|
|
@ -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 d’agréer, Messieurs, nos salutations distinguées.</p>
|
||||
conclusion: Bien cordialement
|
|
@ -28,11 +28,11 @@
|
|||
<div class="grid-block white vn-pa-lg">
|
||||
<h1>{{ $t('title') }}</h1>
|
||||
<p>{{$t('description.dear')}},</p>
|
||||
<p>{{$t('description.instructions')}}</p>
|
||||
<div v-html="$t('description.instructions')"></div>
|
||||
<p>{{$t('description.conclusion')}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Block -->
|
||||
<!-- Attachments block -->
|
||||
<div class="grid-row" v-if="isPreview">
|
||||
<div class="grid-block white vn-pa-lg">
|
||||
<attachment v-for="attachment in attachments"
|
||||
|
|
|
@ -238,6 +238,7 @@
|
|||
<div class="grid-row">
|
||||
<div class="grid-block">
|
||||
<report-footer id="pageFooter"
|
||||
v-bind:show-phytosanitary="true"
|
||||
v-bind:left-text="$t('ticket', [ticket.id])"
|
||||
v-bind:center-text="client.socialName"
|
||||
v-bind:is-preview="isPreview"
|
||||
|
|
|
@ -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();
|
|
@ -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 |
|
@ -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>
|
|
@ -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'),
|
||||
},
|
||||
};
|
|
@ -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}',
|
||||
},
|
||||
},
|
||||
};
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -32,7 +32,10 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>{{$t('supplier.identifier')}}</td>
|
||||
<th>ES89000B97367486</th>
|
||||
<th>
|
||||
<div>ES89000B97367486</div>
|
||||
<div>B97367486-000</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{$t('supplier.name')}}</td>
|
||||
|
@ -110,14 +113,19 @@
|
|||
<div class="field square">
|
||||
<span>{{client.countryCode.substr(0, 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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="description" colspan="2">
|
||||
<div class="line">
|
||||
<span>{{$t('client.accountNumberFormat')}}</span>
|
||||
<span>{{$t('client.accountNumberFormat', [
|
||||
$t(`${client.country}`),
|
||||
client.ibanLength,
|
||||
client.countryCode
|
||||
])}}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -145,7 +153,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>{{$t('client.signLocation')}}</td>
|
||||
<th>{{dated}}, {{supplier.province}}</th>
|
||||
<th>{{dated}}, {{client.province}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{$t('client.sign')}}</td>
|
||||
|
|
Loading…
Reference in New Issue