Merge
|
@ -0,0 +1,96 @@
|
|||
const request = require('request-promise-native');
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('sendMessage', {
|
||||
description: 'Send a RocketChat message',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
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: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/sendMessage`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.sendMessage = async(ctx, to, message) => {
|
||||
const models = Self.app.models;
|
||||
const accessToken = ctx.req.accessToken;
|
||||
const sender = await models.Account.findById(accessToken.userId);
|
||||
const recipient = to.replace('@', '');
|
||||
|
||||
if (sender.name != recipient)
|
||||
return sendMessage(to, `@${sender.name}: ${message}`);
|
||||
};
|
||||
|
||||
async function sendMessage(name, message) {
|
||||
const models = Self.app.models;
|
||||
const chatConfig = await models.ChatConfig.findOne();
|
||||
|
||||
if (!Self.token)
|
||||
Self.token = await login();
|
||||
|
||||
const uri = `${chatConfig.uri}/chat.postMessage`;
|
||||
return send(uri, {
|
||||
'channel': name,
|
||||
'text': message
|
||||
}).catch(async error => {
|
||||
if (error.statusCode === 401 && !Self.loginAttempted) {
|
||||
Self.token = await login();
|
||||
Self.loginAttempted = true;
|
||||
|
||||
return sendMessage(name, message);
|
||||
}
|
||||
|
||||
throw new Error(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rocketchat token
|
||||
* @return {Object} userId and authToken
|
||||
*/
|
||||
async function login() {
|
||||
const models = Self.app.models;
|
||||
const chatConfig = await models.ChatConfig.findOne();
|
||||
const uri = `${chatConfig.uri}/login`;
|
||||
return send(uri, {
|
||||
user: chatConfig.user,
|
||||
password: chatConfig.password
|
||||
}).then(res => res.data);
|
||||
}
|
||||
|
||||
function send(uri, body) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({statusCode: 200, message: 'Fake notification sent'});
|
||||
});
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
uri: uri,
|
||||
body: body,
|
||||
headers: {'content-type': 'application/json'},
|
||||
json: true
|
||||
};
|
||||
|
||||
if (Self.token) {
|
||||
options.headers['X-Auth-Token'] = Self.token.authToken;
|
||||
options.headers['X-User-Id'] = Self.token.userId;
|
||||
}
|
||||
|
||||
return request(options);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('chat sendMessage()', () => {
|
||||
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();
|
||||
});
|
||||
});
|
|
@ -14,6 +14,12 @@
|
|||
"Container": {
|
||||
"dataSource": "storage"
|
||||
},
|
||||
"Chat": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ChatConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Delivery": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "ChatConfig",
|
||||
"description": "Chat API config",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "chatConfig"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"uri": {
|
||||
"type": "String"
|
||||
},
|
||||
"user": {
|
||||
"type": "String"
|
||||
},
|
||||
"password": {
|
||||
"type": "String"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/chat/sendMessage')(Self);
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "Chat",
|
||||
"base": "VnModel",
|
||||
"acls": [{
|
||||
"property": "validations",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}]
|
||||
}
|
||||
|
|
@ -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,11 @@
|
|||
USE `vn`;
|
||||
|
||||
CREATE TABLE `vn`.`chatConfig` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uri` VARCHAR(255) NOT NULL,
|
||||
`user` VARCHAR(50) NOT NULL,
|
||||
`password` VARCHAR(50) NOT NULL,
|
||||
PRIMARY KEY (`id`));
|
||||
|
||||
|
||||
INSERT INTO `vn`.`chatConfig` (`uri`, `user`, `password`) VALUES ('https://chat.verdnatura.es/api/v1', 'VnBot', 'Ub606cux7op.');
|
|
@ -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 ;
|
|
@ -1,11 +1,11 @@
|
|||
ALTER TABLE `vn`.`itemTaxCountry` AUTO_INCREMENT = 1;
|
||||
ALTER TABLE `vn2008`.`Consignatarios` AUTO_INCREMENT = 1;
|
||||
ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1;
|
||||
ALTER TABLE `vn`.`zoneGeo` AUTO_INCREMENT = 1;
|
||||
|
||||
INSERT INTO `vn`.`ticketConfig` (`id`, `scopeDays`)
|
||||
VALUES
|
||||
('1', '6');
|
||||
|
||||
|
||||
INSERT INTO `account`.`mailConfig` (`id`, `domain`)
|
||||
VALUES
|
||||
('1', 'verdnatura.es');
|
||||
|
@ -14,16 +14,20 @@ INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `password`,`role`,`active`
|
|||
SELECT id, name, CONCAT(name, 'Nick'),MD5('nightmare'), id, 1, CONCAT(name, '@mydomain.com'), 'es'
|
||||
FROM `account`.`role`;
|
||||
|
||||
INSERT INTO `vn2008`.`Trabajadores`(`Id_Trabajador`,`CodigoTrabajador`, `Nombre`, `Apellidos`, `user_id`, `boss`)
|
||||
INSERT INTO `vn`.`worker`(`id`,`code`, `firstName`, `lastName`, `userFk`, `bossFk`)
|
||||
SELECT id,UPPER(LPAD(role, 3, '0')), name, name, id, 9
|
||||
FROM `vn`.`user`;
|
||||
|
||||
UPDATE `vn2008`.`Trabajadores` SET boss = NULL WHERE Id_Trabajador = 20;
|
||||
UPDATE `vn2008`.`Trabajadores` SET boss = 20
|
||||
WHERE Id_Trabajador = 1 OR Id_Trabajador = 9;
|
||||
UPDATE `vn`.`worker` SET bossFk = NULL WHERE id = 20;
|
||||
UPDATE `vn`.`worker` SET bossFk = 20
|
||||
WHERE id = 1 OR id = 9;
|
||||
|
||||
DELETE FROM `vn`.`worker` WHERE name ='customer';
|
||||
DELETE FROM `vn`.`worker` WHERE firstName ='customer';
|
||||
|
||||
INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType`, `maxAmount`, `employeeFk`, `testUrl`)
|
||||
VALUES
|
||||
(1, 978, 1, 0, 2000, 9, 0);
|
||||
|
||||
INSERT INTO `account`.`user`(`id`,`name`,`password`,`role`,`active`,`email`,`lang`)
|
||||
VALUES
|
||||
(101, 'BruceWayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es'),
|
||||
|
@ -39,23 +43,24 @@ INSERT INTO `account`.`user`(`id`,`name`,`password`,`role`,`active`,`email`,`lan
|
|||
(111, 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'es'),
|
||||
(112, 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'es');
|
||||
|
||||
INSERT INTO `vn2008`.`Trabajadores`(`CodigoTrabajador`, `Id_Trabajador`, `Nombre`, `Apellidos`, `user_id`,`boss`)
|
||||
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`)
|
||||
VALUES
|
||||
('LGN', 106, 'David Charles', 'Haller', 106, 19),
|
||||
('ANT', 107, 'Hank' , 'Pym' , 107, 19),
|
||||
('DCX', 110, 'Charles' , 'Xavier', 108, 19),
|
||||
('HLK', 109, 'Bruce' , 'Banner', 109, 19),
|
||||
('JJJ', 108, 'Jessica' , 'Jones' , 110, 19);
|
||||
(106, 'LGN', 'David Charles', 'Haller', 106, 19),
|
||||
(107, 'ANT', 'Hank' , 'Pym' , 107, 19),
|
||||
(108, 'DCX', 'Charles' , 'Xavier', 108, 19),
|
||||
(109, 'HLK', 'Bruce' , 'Banner', 109, 19),
|
||||
(110, 'JJJ', 'Jessica' , 'Jones' , 110, 19);
|
||||
|
||||
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
|
||||
|
@ -191,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),
|
||||
|
@ -1456,7 +1461,7 @@ INSERT INTO `vn`.`receipt`(`id`, `invoiceFk`, `amountPaid`, `amountUnpaid`, `pay
|
|||
(1, 'Cobro web', 100.50, 0.00, CURDATE(), 9, 1, 101, CURDATE(), 442, 1),
|
||||
(2, 'Cobro web', 200.50, 0.00, DATE_ADD(CURDATE(), INTERVAL -5 DAY), 9, 1, 101, DATE_ADD(CURDATE(), INTERVAL -5 DAY), 442, 1),
|
||||
(3, 'Cobro en efectivo', 300.00, 100.00, DATE_ADD(CURDATE(), INTERVAL -10 DAY), 9, 1, 102, DATE_ADD(CURDATE(), INTERVAL -10 DAY), 442, 0),
|
||||
(4, 'Cobro en efectivo', -400.00, -50.00, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 9, 1, 103, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 442, 0);
|
||||
(4, 'Cobro en efectivo', 400.00, -50.00, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 9, 1, 103, DATE_ADD(CURDATE(), INTERVAL -15 DAY), 442, 0);
|
||||
|
||||
INSERT INTO `vn2008`.`workerTeam`(`id`, `team`, `user`)
|
||||
VALUES
|
||||
|
@ -1467,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)),
|
||||
|
|
|
@ -47,16 +47,17 @@ TABLES=(
|
|||
claimResult
|
||||
ticketUpdateAction
|
||||
state
|
||||
sample
|
||||
)
|
||||
dump_tables ${TABLES[@]}
|
||||
|
||||
TABLES=(
|
||||
vn2008
|
||||
time
|
||||
accion_dits
|
||||
businessReasonEnd
|
||||
container
|
||||
department
|
||||
escritos
|
||||
Grupos
|
||||
iva_group_codigo
|
||||
tarifa_componentes
|
||||
|
@ -80,7 +81,6 @@ dump_tables ${TABLES[@]}
|
|||
TABLES=(
|
||||
hedera
|
||||
imageCollection
|
||||
tpvConfig
|
||||
tpvError
|
||||
tpvResponse
|
||||
)
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -55,5 +55,6 @@
|
|||
"You can't delete a confirmed order": "You can't delete a confirmed order",
|
||||
"Value has an invalid format": "Value has an invalid format",
|
||||
"The postcode doesn't exists. Ensure you put the correct format": "The postcode doesn't exists. Ensure you put the correct format",
|
||||
"Can't create stowaway for this ticket": "Can't create stowaway for this ticket"
|
||||
"Can't create stowaway for this ticket": "Can't create stowaway for this ticket",
|
||||
"Has deleted the ticket id": "Has deleted the ticket id [#{{id}}]({{{url}}})"
|
||||
}
|
|
@ -109,8 +109,11 @@
|
|||
"The postcode doesn't exists. Ensure you put the correct format": "El código postal no existe. Asegúrate de ponerlo con el formato correcto",
|
||||
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
|
||||
"This phone already exists": "Este teléfono ya existe",
|
||||
"You cannot move a parent to any of its sons": "You cannot move a parent to any of its sons",
|
||||
"You cannot move a parent to its own sons": "You cannot move a parent to its own sons",
|
||||
"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",
|
||||
"AMOUNT_NOT_MATCH_GROUPING": "AMOUNT_NOT_MATCH_GROUPING"
|
||||
"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"
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
module.exports = function(app) {
|
||||
require('../../../print/server.js')(app);
|
||||
require('../../../print/boot.js')(app);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Serializes an object to a query params
|
||||
*
|
||||
* @param {Object} obj The params object
|
||||
* @return {String} Serialized params
|
||||
*/
|
||||
exports.httpParamSerializer = function(obj) {
|
||||
let query = '';
|
||||
for (let param in obj) {
|
||||
if (query != '')
|
||||
query += '&';
|
||||
query += `${param}=${obj[param]}`;
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
|
@ -10,19 +10,19 @@
|
|||
on-search="$ctrl.onSearch($params)">
|
||||
</vn-auto-search>
|
||||
<div class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-treeview
|
||||
vn-id="treeview"
|
||||
root-label="Locations"
|
||||
fetch-func="$ctrl.onFetch($item)"
|
||||
sort-func="$ctrl.onSort($a, $b)">
|
||||
<vn-check
|
||||
ng-model="item.selected"
|
||||
on-change="$ctrl.onSelection(value, item)"
|
||||
triple-state="true"
|
||||
ng-click="$event.preventDefault()"
|
||||
label="{{::item.name}}">
|
||||
</vn-check>
|
||||
</vn-treeview>
|
||||
<vn-card class="vn-pa-lg vn-mt-md">
|
||||
<vn-treeview
|
||||
vn-id="treeview"
|
||||
root-label="Locations"
|
||||
fetch-func="$ctrl.onFetch($item)"
|
||||
sort-func="$ctrl.onSort($a, $b)">
|
||||
<vn-check acl-role="deliveryBoss"
|
||||
ng-model="item.selected"
|
||||
on-change="$ctrl.onSelection(value, item)"
|
||||
triple-state="true"
|
||||
ng-click="$event.preventDefault()"
|
||||
label="{{::item.name}}">
|
||||
</vn-check>
|
||||
</vn-treeview>
|
||||
</vn-card>
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,7 @@ class Controller extends ModuleCard {
|
|||
}, {
|
||||
relation: 'client',
|
||||
scope: {
|
||||
fields: ['salesPersonFk', 'name'],
|
||||
fields: ['salesPersonFk', 'name', 'email'],
|
||||
include: {
|
||||
relation: 'salesPerson',
|
||||
scope: {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
class Controller {
|
||||
constructor($scope, $state, $http, $translate, vnApp, aclService) {
|
||||
constructor($scope, $state, $http, $translate, vnApp, aclService, $httpParamSerializer) {
|
||||
this.$scope = $scope;
|
||||
this.$state = $state;
|
||||
this.$http = $http;
|
||||
this.$translate = $translate;
|
||||
this.vnApp = vnApp;
|
||||
this.aclService = aclService;
|
||||
this.$httpParamSerializer = $httpParamSerializer;
|
||||
this.moreOptions = [
|
||||
{callback: this.showPickupOrder, name: 'Show Pickup order'},
|
||||
{callback: this.confirmPickupOrder, name: 'Send Pickup order'},
|
||||
|
@ -60,7 +61,12 @@ class Controller {
|
|||
}
|
||||
|
||||
showPickupOrder() {
|
||||
let url = `report/rpt-claim-pickup-order?claimFk=${this.claim.id}`;
|
||||
const params = {
|
||||
clientId: this.claim.clientFk,
|
||||
claimId: this.claim.id
|
||||
};
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
let url = `api/report/claim-pickup-order?${serializedParams}`;
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
|
@ -70,7 +76,14 @@ class Controller {
|
|||
|
||||
sendPickupOrder(response) {
|
||||
if (response === 'accept') {
|
||||
this.$http.post(`email/claim-pickup-order`, {claimFk: this.claim.id}).then(
|
||||
const params = {
|
||||
recipient: this.claim.client.email,
|
||||
clientId: this.claim.clientFk,
|
||||
claimId: this.claim.id
|
||||
};
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
const url = `email/claim-pickup-order?${serializedParams}`;
|
||||
this.$http.get(url).then(
|
||||
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
|
||||
);
|
||||
}
|
||||
|
@ -90,7 +103,7 @@ class Controller {
|
|||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope', '$state', '$http', '$translate', 'vnApp', 'aclService'];
|
||||
Controller.$inject = ['$scope', '$state', '$http', '$translate', 'vnApp', 'aclService', '$httpParamSerializer'];
|
||||
|
||||
ngModule.component('vnClaimDescriptor', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Item Component vnClaimDescriptor', () => {
|
||||
let $httpParamSerializer;
|
||||
let $httpBackend;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('claim'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
|
||||
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, _$httpParamSerializer_) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpParamSerializer = _$httpParamSerializer_;
|
||||
controller = $componentController('vnClaimDescriptor');
|
||||
controller.claim = {id: 2};
|
||||
controller.claim = {id: 2, clientFk: 101, client: {email: 'client@email'}};
|
||||
}));
|
||||
|
||||
describe('showPickupOrder()', () => {
|
||||
it('should open a new window showing a pickup order PDF document', () => {
|
||||
let expectedPath = 'report/rpt-claim-pickup-order?claimFk=2';
|
||||
const params = {
|
||||
clientId: controller.claim.clientFk,
|
||||
claimId: controller.claim.id
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
let expectedPath = `api/report/claim-pickup-order?${serializedParams}`;
|
||||
spyOn(window, 'open');
|
||||
controller.showPickupOrder();
|
||||
|
||||
|
@ -38,8 +45,15 @@ describe('Item Component vnClaimDescriptor', () => {
|
|||
it('should make a query and call vnApp.showMessage() if the response is accept', () => {
|
||||
spyOn(controller.vnApp, 'showMessage');
|
||||
|
||||
$httpBackend.when('POST', `email/claim-pickup-order`, {claimFk: 2}).respond();
|
||||
$httpBackend.expect('POST', `email/claim-pickup-order`, {claimFk: 2}).respond();
|
||||
const params = {
|
||||
recipient: 'client@email',
|
||||
clientId: controller.claim.clientFk,
|
||||
claimId: controller.claim.id
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
|
||||
$httpBackend.when('GET', `email/claim-pickup-order?${serializedParams}`).respond();
|
||||
$httpBackend.expect('GET', `email/claim-pickup-order?${serializedParams}`).respond();
|
||||
controller.sendPickupOrder('accept');
|
||||
$httpBackend.flush();
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('Client activeWorkersWithRole', () => {
|
|||
|
||||
let isBuyer = await app.models.Account.hasRole(result[0].id, 'buyer');
|
||||
|
||||
expect(result.length).toEqual(11);
|
||||
expect(result.length).toEqual(12);
|
||||
expect(isBuyer).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,6 +2,8 @@ let request = require('request-promise-native');
|
|||
let UserError = require('vn-loopback/util/user-error');
|
||||
let getFinalState = require('vn-loopback/util/hook').getFinalState;
|
||||
let isMultiple = require('vn-loopback/util/hook').isMultiple;
|
||||
const httpParamSerializer = require('vn-loopback/util/http').httpParamSerializer;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
module.exports = Self => {
|
||||
// Methods
|
||||
|
@ -239,15 +241,18 @@ module.exports = Self => {
|
|||
});
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
uri: 'http://127.0.0.1:3000/api/email/payment-update',
|
||||
body: {
|
||||
clientFk: instance.id
|
||||
},
|
||||
json: true
|
||||
// Send email to client
|
||||
|
||||
if (!instance.email) return;
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const headers = loopBackContext.active.http.req.headers;
|
||||
const params = {
|
||||
clientId: instance.id,
|
||||
recipient: instance.email
|
||||
};
|
||||
await request(options);
|
||||
const serializedParams = httpParamSerializer(params);
|
||||
const query = `${headers.origin}/api/email/payment-update?${serializedParams}`;
|
||||
await request.get(query);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -43,6 +43,11 @@
|
|||
"model": "Client",
|
||||
"foreignKey": "clientFk"
|
||||
},
|
||||
"ticket": {
|
||||
"type": "belongsTo",
|
||||
"model": "Ticket",
|
||||
"foreignKey": "ticketFk"
|
||||
},
|
||||
"greugeType": {
|
||||
"type": "belongsTo",
|
||||
"model": "GreugeType",
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
<mg-ajax path="ClientSamples" options="vnPost"></mg-ajax>
|
||||
<vn-crud-model auto-load="true"
|
||||
url="Companies"
|
||||
data="companiesData"
|
||||
order="code">
|
||||
</vn-crud-model>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.clientSample"
|
||||
|
@ -8,9 +13,13 @@
|
|||
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
vn-id="sampleType"
|
||||
<vn-textfield vn-one
|
||||
label="Recipient"
|
||||
ng-model="$ctrl.clientSample.recipient">
|
||||
</vn-textfield>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one vn-id="sampleType"
|
||||
ng-model="$ctrl.clientSample.typeFk"
|
||||
model="ClientSample.typeFk"
|
||||
fields="['code','hasCompany']"
|
||||
|
@ -19,11 +28,10 @@
|
|||
value-field="id"
|
||||
label="Sample">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
vn-one
|
||||
ng-model="$ctrl.clientSample.companyFk"
|
||||
<vn-autocomplete vn-one
|
||||
ng-model="$ctrl.companyId"
|
||||
model="ClientSample.companyFk"
|
||||
url="Companies"
|
||||
data="companiesData"
|
||||
show-field="code"
|
||||
value-field="id"
|
||||
label="Company"
|
||||
|
@ -41,5 +49,9 @@
|
|||
<vn-dialog
|
||||
vn-id="show-preview"
|
||||
on-open="$ctrl.onPreviewOpen()">
|
||||
<tpl-body></tpl-body>
|
||||
<tpl-body class="client-sample-dialog">
|
||||
<div class="loading">
|
||||
<vn-spinner enable="true"></vn-spinner>
|
||||
</div>
|
||||
</tpl-body>
|
||||
</vn-dialog>
|
||||
|
|
|
@ -1,33 +1,84 @@
|
|||
import ngModule from '../../module';
|
||||
import Component from 'core/lib/component';
|
||||
import './style.scss';
|
||||
|
||||
class Controller {
|
||||
constructor($scope, $state, $http, vnApp, $translate) {
|
||||
this.$scope = $scope;
|
||||
this.$state = $state;
|
||||
this.$stateParams = $state.params;
|
||||
this.$http = $http;
|
||||
class Controller extends Component {
|
||||
constructor($element, $, vnApp, $httpParamSerializer, vnConfig) {
|
||||
super($element, $);
|
||||
this.vnApp = vnApp;
|
||||
this.$translate = $translate;
|
||||
this.$httpParamSerializer = $httpParamSerializer;
|
||||
this.vnConfig = vnConfig;
|
||||
this.clientSample = {
|
||||
clientFk: this.$stateParams.id
|
||||
clientFk: this.$params.id,
|
||||
companyFk: vnConfig.companyFk
|
||||
};
|
||||
}
|
||||
|
||||
jsonToQuery(json) {
|
||||
let query = '';
|
||||
for (let param in json) {
|
||||
if (query != '')
|
||||
query += '&';
|
||||
query += `${param}=${json[param]}`;
|
||||
}
|
||||
get client() {
|
||||
return this._client;
|
||||
}
|
||||
|
||||
return query;
|
||||
set client(value) {
|
||||
this._client = value;
|
||||
|
||||
if (value)
|
||||
this.clientSample.recipient = value.email;
|
||||
}
|
||||
|
||||
get companyId() {
|
||||
if (!this.clientSample.companyFk)
|
||||
this.clientSample.companyFk = this.vnConfig.companyFk;
|
||||
return this.clientSample.companyFk;
|
||||
}
|
||||
|
||||
set companyId(value) {
|
||||
this.clientSample.companyFk = value;
|
||||
}
|
||||
|
||||
showPreview() {
|
||||
let sampleType = this.$scope.sampleType.selection;
|
||||
let params = {clientFk: this.$stateParams.id};
|
||||
let sampleType = this.$.sampleType.selection;
|
||||
|
||||
if (!sampleType)
|
||||
return this.vnApp.showError(this.$translate.instant('Choose a sample'));
|
||||
|
||||
if (sampleType.hasCompany && !this.clientSample.companyFk)
|
||||
return this.vnApp.showError(this.$translate.instant('Choose a company'));
|
||||
|
||||
const params = {
|
||||
clientId: this.$params.id,
|
||||
recipient: this.clientSample.recipient,
|
||||
isPreview: true
|
||||
};
|
||||
|
||||
if (sampleType.hasCompany)
|
||||
params.companyId = this.clientSample.companyFk;
|
||||
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
const query = `email/${sampleType.code}?${serializedParams}`;
|
||||
this.$http.get(query).then(res => {
|
||||
this.$.showPreview.show();
|
||||
let dialog = document.body.querySelector('div.vn-dialog');
|
||||
let body = dialog.querySelector('tpl-body');
|
||||
let scroll = dialog.querySelector('div:first-child');
|
||||
|
||||
body.innerHTML = res.data;
|
||||
scroll.scrollTop = 0;
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.$.watcher.check();
|
||||
this.$.watcher.realSubmit().then(() =>
|
||||
this.sendSample()
|
||||
);
|
||||
}
|
||||
|
||||
sendSample() {
|
||||
let sampleType = this.$.sampleType.selection;
|
||||
let params = {
|
||||
clientId: this.$params.id,
|
||||
recipient: this.clientSample.recipient
|
||||
};
|
||||
|
||||
if (!sampleType)
|
||||
return this.vnApp.showError(this.$translate.instant('Choose a sample'));
|
||||
|
@ -36,50 +87,22 @@ class Controller {
|
|||
return this.vnApp.showError(this.$translate.instant('Choose a company'));
|
||||
|
||||
if (sampleType.hasCompany)
|
||||
params.companyFk = this.clientSample.companyFk;
|
||||
params.companyId = this.clientSample.companyFk;
|
||||
|
||||
let query = `email/${sampleType.code}?${this.jsonToQuery(params)}`;
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
const query = `email/${sampleType.code}?${serializedParams}`;
|
||||
this.$http.get(query).then(res => {
|
||||
if (res.data) {
|
||||
let dialog = this.$scope.showPreview.element;
|
||||
let body = dialog.querySelector('tpl-body');
|
||||
let scroll = dialog.querySelector('div:first-child');
|
||||
|
||||
body.innerHTML = res.data;
|
||||
this.$scope.showPreview.show();
|
||||
|
||||
scroll.scrollTop = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.$scope.watcher.check();
|
||||
this.$scope.watcher.realSubmit().then(() =>
|
||||
this.sendSample()
|
||||
);
|
||||
}
|
||||
|
||||
sendSample() {
|
||||
let sampleType = this.$scope.sampleType.selection;
|
||||
let params = {clientFk: this.$stateParams.id};
|
||||
|
||||
if (sampleType.hasCompany)
|
||||
params.companyFk = this.clientSample.companyFk;
|
||||
|
||||
|
||||
let query = `email/${sampleType.code}?${this.jsonToQuery(params)}`;
|
||||
this.$http.post(query).then(res => {
|
||||
if (res) {
|
||||
this.vnApp.showSuccess(this.$translate.instant('Notification sent!'));
|
||||
this.$state.go('client.card.sample.index');
|
||||
}
|
||||
this.vnApp.showSuccess(this.$translate.instant('Notification sent!'));
|
||||
this.$state.go('client.card.sample.index');
|
||||
});
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];
|
||||
Controller.$inject = ['$element', '$scope', 'vnApp', '$httpParamSerializer', 'vnConfig'];
|
||||
|
||||
ngModule.component('vnClientSampleCreate', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
client: '<'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,14 +2,16 @@ import './index';
|
|||
|
||||
describe('Client', () => {
|
||||
describe('Component vnClientSampleCreate', () => {
|
||||
let $httpParamSerializer;
|
||||
let $scope;
|
||||
let $element;
|
||||
let $httpBackend;
|
||||
let $state;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('client'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, _$state_) => {
|
||||
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, _$state_, _$httpParamSerializer_) => {
|
||||
$scope = $rootScope.$new();
|
||||
$scope.sampleType = {};
|
||||
$scope.watcher = {
|
||||
|
@ -35,30 +37,24 @@ describe('Client', () => {
|
|||
$state = _$state_;
|
||||
$state.params.id = 101;
|
||||
$httpBackend = _$httpBackend_;
|
||||
controller = $componentController('vnClientSampleCreate', {$scope, $state});
|
||||
$httpParamSerializer = _$httpParamSerializer_;
|
||||
$element = angular.element('<vn-client-sample-create></vn-client-sample-create>');
|
||||
controller = $componentController('vnClientSampleCreate', {$element, $scope});
|
||||
}));
|
||||
|
||||
describe('jsonToQuery()', () => {
|
||||
it(`should convert a JSON object with clientFk property to query params`, () => {
|
||||
let myObject = {clientFk: 101};
|
||||
let result = controller.jsonToQuery(myObject);
|
||||
|
||||
expect(result).toEqual('clientFk=101');
|
||||
});
|
||||
|
||||
it(`should convert a JSON object with clientFk and companyFk properties to query params`, () => {
|
||||
let myObject = {clientFk: 101, companyFk: 442};
|
||||
let result = controller.jsonToQuery(myObject);
|
||||
|
||||
expect(result).toEqual('clientFk=101&companyFk=442');
|
||||
});
|
||||
});
|
||||
|
||||
describe('showPreview()', () => {
|
||||
it(`should perform a query (GET) and open a sample preview`, () => {
|
||||
spyOn(controller.$scope.showPreview, 'show');
|
||||
spyOn(controller.$.showPreview, 'show');
|
||||
const element = document.createElement('div');
|
||||
document.body.querySelector = () => {
|
||||
return {
|
||||
querySelector: () => {
|
||||
return element;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
controller.$scope.sampleType.selection = {
|
||||
controller.$.sampleType.selection = {
|
||||
hasCompany: false,
|
||||
code: 'MyReport'
|
||||
};
|
||||
|
@ -69,18 +65,32 @@ describe('Client', () => {
|
|||
|
||||
let event = {preventDefault: () => {}};
|
||||
|
||||
$httpBackend.when('GET', `email/MyReport?clientFk=101`).respond(true);
|
||||
$httpBackend.expect('GET', `email/MyReport?clientFk=101`);
|
||||
const params = {
|
||||
clientId: 101,
|
||||
isPreview: true
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
|
||||
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
|
||||
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
|
||||
controller.showPreview(event);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.$scope.showPreview.show).toHaveBeenCalledWith();
|
||||
expect(controller.$.showPreview.show).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it(`should perform a query (GET) with companyFk param and open a sample preview`, () => {
|
||||
spyOn(controller.$scope.showPreview, 'show');
|
||||
spyOn(controller.$.showPreview, 'show');
|
||||
const element = document.createElement('div');
|
||||
document.body.querySelector = () => {
|
||||
return {
|
||||
querySelector: () => {
|
||||
return element;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
controller.$scope.sampleType.selection = {
|
||||
controller.$.sampleType.selection = {
|
||||
hasCompany: true,
|
||||
code: 'MyReport'
|
||||
};
|
||||
|
@ -92,12 +102,19 @@ describe('Client', () => {
|
|||
|
||||
let event = {preventDefault: () => {}};
|
||||
|
||||
$httpBackend.when('GET', `email/MyReport?clientFk=101&companyFk=442`).respond(true);
|
||||
$httpBackend.expect('GET', `email/MyReport?clientFk=101&companyFk=442`);
|
||||
const params = {
|
||||
clientId: 101,
|
||||
companyId: 442,
|
||||
isPreview: true
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
|
||||
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
|
||||
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
|
||||
controller.showPreview(event);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.$scope.showPreview.show).toHaveBeenCalledWith();
|
||||
expect(controller.$.showPreview.show).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -114,7 +131,7 @@ describe('Client', () => {
|
|||
it(`should perform a query (GET) and call go() method`, () => {
|
||||
spyOn(controller.$state, 'go');
|
||||
|
||||
controller.$scope.sampleType.selection = {
|
||||
controller.$.sampleType.selection = {
|
||||
hasCompany: false,
|
||||
code: 'MyReport'
|
||||
};
|
||||
|
@ -123,8 +140,13 @@ describe('Client', () => {
|
|||
clientFk: 101
|
||||
};
|
||||
|
||||
$httpBackend.when('POST', `email/MyReport?clientFk=101`).respond(true);
|
||||
$httpBackend.expect('POST', `email/MyReport?clientFk=101`);
|
||||
const params = {
|
||||
clientId: 101
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
|
||||
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
|
||||
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
|
||||
controller.sendSample();
|
||||
$httpBackend.flush();
|
||||
|
||||
|
@ -134,7 +156,7 @@ describe('Client', () => {
|
|||
it(`should perform a query (GET) with companyFk param and call go() method`, () => {
|
||||
spyOn(controller.$state, 'go');
|
||||
|
||||
controller.$scope.sampleType.selection = {
|
||||
controller.$.sampleType.selection = {
|
||||
hasCompany: true,
|
||||
code: 'MyReport'
|
||||
};
|
||||
|
@ -144,8 +166,14 @@ describe('Client', () => {
|
|||
companyFk: 442
|
||||
};
|
||||
|
||||
$httpBackend.when('POST', `email/MyReport?clientFk=101&companyFk=442`).respond(true);
|
||||
$httpBackend.expect('POST', `email/MyReport?clientFk=101&companyFk=442`);
|
||||
const params = {
|
||||
clientId: 101,
|
||||
companyId: 442
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
|
||||
$httpBackend.when('GET', `email/MyReport?${serializedParams}`).respond(true);
|
||||
$httpBackend.expect('GET', `email/MyReport?${serializedParams}`);
|
||||
controller.sendSample();
|
||||
$httpBackend.flush();
|
||||
|
||||
|
|
|
@ -1,36 +1,34 @@
|
|||
vn-client-sample-create {
|
||||
vn-dialog {
|
||||
& > div {
|
||||
padding: 0 !important
|
||||
div.vn-dialog {
|
||||
tpl-body.client-sample-dialog {
|
||||
width: 800px;
|
||||
|
||||
.container, .container h1 {
|
||||
font-family: "Roboto","Helvetica","Arial",sans-serif;
|
||||
font-size: 1em !important;
|
||||
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
margin: auto
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em 0
|
||||
}
|
||||
|
||||
footer p {
|
||||
font-size: 10px !important;
|
||||
line-height: 10px
|
||||
}
|
||||
}
|
||||
|
||||
tpl-body {
|
||||
min-width: 800px;
|
||||
|
||||
.title h1 {
|
||||
font-size: 2em !important;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.container, .container h1 {
|
||||
font-family: "Roboto","Helvetica","Arial",sans-serif;
|
||||
font-size: 1em !important;
|
||||
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
margin: auto
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em 0
|
||||
}
|
||||
|
||||
footer p {
|
||||
font-size: 10px !important;
|
||||
line-height: 10px
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.title h1 {
|
||||
font-size: 2em !important;
|
||||
margin: 0
|
||||
}
|
||||
.loading {
|
||||
text-align: center
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
"ItemTypeTag": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ItemShelvingSale": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Origin": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "ItemShelvingSale",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "itemShelvingSale"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"quantity": {
|
||||
"type": "Number"
|
||||
},
|
||||
"created": {
|
||||
"type": "Date"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"sale": {
|
||||
"type": "belongsTo",
|
||||
"model": "Sale",
|
||||
"foreignKey": "saleFk"
|
||||
},
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,6 +36,24 @@ class Controller extends ModuleCard {
|
|||
scope: {
|
||||
fields: ['id', 'name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
fields: ['userFk'],
|
||||
include: {
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['id'],
|
||||
include: {
|
||||
relation: 'emailUser',
|
||||
scope: {
|
||||
fields: ['email']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import ngModule from '../module';
|
||||
|
||||
class Controller {
|
||||
constructor($, $http, vnApp, $translate, aclService) {
|
||||
constructor($, $http, vnApp, $translate, aclService, $httpParamSerializer) {
|
||||
this.$http = $http;
|
||||
this.vnApp = vnApp;
|
||||
this.$translate = $translate;
|
||||
this.$ = $;
|
||||
this.aclService = aclService;
|
||||
this.$httpParamSerializer = $httpParamSerializer;
|
||||
this.moreOptions = [
|
||||
{callback: this.showRouteReport, name: 'Show route report'},
|
||||
{callback: this.sendRouteReport, name: 'Send route report'},
|
||||
|
@ -36,13 +37,26 @@ class Controller {
|
|||
}
|
||||
|
||||
showRouteReport() {
|
||||
let url = `report/rpt-route?routeFk=${this.route.id}`;
|
||||
const user = this.route.worker.user;
|
||||
const params = {
|
||||
clientId: user.id,
|
||||
routeId: this.route.id
|
||||
};
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
let url = `api/report/driver-route?${serializedParams}`;
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
sendRouteReport() {
|
||||
let url = `email/driver-route?routeFk=${this.route.id}`;
|
||||
this.$http.post(url).then(() => {
|
||||
const user = this.route.worker.user;
|
||||
const params = {
|
||||
recipient: user.emailUser.email,
|
||||
clientId: user.id,
|
||||
routeId: this.route.id
|
||||
};
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
const url = `email/driver-route?${serializedParams}`;
|
||||
this.$http.get(url).then(() => {
|
||||
this.vnApp.showSuccess(this.$translate.instant('Report sent'));
|
||||
});
|
||||
}
|
||||
|
@ -62,7 +76,7 @@ class Controller {
|
|||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate', 'aclService'];
|
||||
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate', 'aclService', '$httpParamSerializer'];
|
||||
|
||||
ngModule.component('vnRouteDescriptor', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('setDeleted', {
|
||||
Self.remoteMethodCtx('setDeleted', {
|
||||
description: 'Sets true the isDeleted value of a ticket',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
|
@ -21,16 +21,82 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.setDeleted = async id => {
|
||||
try {
|
||||
let claimOfATicket = await Self.app.models.Claim.findOne({where: {ticketFk: id}});
|
||||
if (claimOfATicket)
|
||||
throw new UserError('You must delete the claim id %d first', 'DELETE_CLAIM_FIRST', claimOfATicket.id);
|
||||
Self.setDeleted = async(ctx, id) => {
|
||||
const models = Self.app.models;
|
||||
const isEditable = await Self.isEditable(ctx, id);
|
||||
const $t = ctx.req.__; // $translate
|
||||
|
||||
let currentTicket = await Self.app.models.Ticket.findById(id);
|
||||
return await currentTicket.updateAttributes({isDeleted: true});
|
||||
} catch (e) {
|
||||
throw e;
|
||||
if (!isEditable)
|
||||
throw new UserError('You cannot delete this ticket because is already invoiced, deleted or prepared');
|
||||
|
||||
// Check if has sales with shelving
|
||||
const sales = await models.Sale.find({
|
||||
include: {relation: 'itemShelving'},
|
||||
where: {ticketFk: id}
|
||||
});
|
||||
const hasItemShelvingSales = sales.some(sale => {
|
||||
return sale.itemShelving();
|
||||
});
|
||||
if (hasItemShelvingSales)
|
||||
throw new UserError(`You cannot delete a ticket that part of it is being prepared`);
|
||||
|
||||
// Check for existing claim
|
||||
const claimOfATicket = await models.Claim.findOne({where: {ticketFk: id}});
|
||||
if (claimOfATicket)
|
||||
throw new UserError('You must delete the claim id %d first', 'DELETE_CLAIM_FIRST', claimOfATicket.id);
|
||||
|
||||
// Check for existing purchase requests
|
||||
const hasPurchaseRequests = await models.TicketRequest.count({
|
||||
ticketFk: id,
|
||||
isOk: true
|
||||
});
|
||||
|
||||
if (hasPurchaseRequests)
|
||||
throw new UserError('You must delete all the buy requests first');
|
||||
|
||||
// Remove ticket greuges
|
||||
const ticketGreuges = await models.Greuge.find({where: {ticketFk: id}});
|
||||
const ownGreuges = ticketGreuges.every(greuge => {
|
||||
return greuge.ticketFk = id;
|
||||
});
|
||||
if (ownGreuges) {
|
||||
for (const greuge of ticketGreuges) {
|
||||
const instance = await models.Greuge.findById(greuge.id);
|
||||
|
||||
await instance.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
const ticket = await models.Ticket.findById(id, {
|
||||
include: {
|
||||
relation: 'client',
|
||||
scope: {
|
||||
fields: ['id', 'salesPersonFk'],
|
||||
include: {
|
||||
relation: 'salesPerson',
|
||||
scope: {
|
||||
fields: ['id', 'userFk'],
|
||||
include: {
|
||||
relation: 'user'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Send notification to salesPerson
|
||||
const salesPerson = ticket.client().salesPerson();
|
||||
if (salesPerson) {
|
||||
const salesPersonUser = salesPerson.user().name;
|
||||
const origin = ctx.req.headers.origin;
|
||||
const message = $t(`Has deleted the ticket id`, {
|
||||
id: id,
|
||||
url: `${origin}/#!/ticket/${id}/summary`
|
||||
});
|
||||
await models.Chat.sendMessage(ctx, `@${salesPersonUser}`, message);
|
||||
}
|
||||
|
||||
return ticket.updateAttribute('isDeleted', true);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -2,12 +2,23 @@ const app = require('vn-loopback/server/server');
|
|||
|
||||
describe('ticket deleted()', () => {
|
||||
let ticket;
|
||||
let ctx;
|
||||
|
||||
beforeAll(async done => {
|
||||
let originalTicket = await app.models.Ticket.findOne({where: {id: 16}});
|
||||
originalTicket.id = null;
|
||||
ticket = await app.models.Ticket.create(originalTicket);
|
||||
|
||||
ctx = {
|
||||
req: {
|
||||
accessToken: {userId: 106},
|
||||
headers: {
|
||||
origin: 'http://localhost:5000'
|
||||
},
|
||||
__: () => {}
|
||||
}
|
||||
};
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -22,7 +33,7 @@ describe('ticket deleted()', () => {
|
|||
});
|
||||
|
||||
it('should set a ticket to deleted', async() => {
|
||||
await app.models.Ticket.setDeleted(ticket.id);
|
||||
await app.models.Ticket.setDeleted(ctx, ticket.id);
|
||||
|
||||
let deletedTicket = await app.models.Ticket.findOne({where: {id: ticket.id}, fields: ['isDeleted']});
|
||||
|
||||
|
@ -34,7 +45,7 @@ describe('ticket deleted()', () => {
|
|||
let error;
|
||||
|
||||
try {
|
||||
await app.models.Ticket.setDeleted(ticketId);
|
||||
await app.models.Ticket.setDeleted(ctx, ticketId);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
|
|
@ -72,6 +72,11 @@
|
|||
"type": "hasOne",
|
||||
"model": "SaleTracking",
|
||||
"foreignKey": "saleFk"
|
||||
},
|
||||
"itemShelving": {
|
||||
"type": "hasOne",
|
||||
"model": "ItemShelvingSale",
|
||||
"foreignKey": "saleFk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,6 @@
|
|||
"isOk": {
|
||||
"type": "Boolean"
|
||||
},
|
||||
"attenderFk": {
|
||||
"type": "Number",
|
||||
"required": true,
|
||||
"mysql": {
|
||||
"columnName": "atenderFk"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"type": "String"
|
||||
}
|
||||
|
|
|
@ -20,7 +20,15 @@ class Controller extends ModuleCard {
|
|||
}, {
|
||||
relation: 'client',
|
||||
scope: {
|
||||
fields: ['salesPersonFk', 'name', 'isActive', 'isFreezed', 'isTaxDataChecked', 'credit'],
|
||||
fields: [
|
||||
'salesPersonFk',
|
||||
'name',
|
||||
'isActive',
|
||||
'isFreezed',
|
||||
'isTaxDataChecked',
|
||||
'credit',
|
||||
'email'
|
||||
],
|
||||
include: {
|
||||
relation: 'salesPerson',
|
||||
scope: {
|
||||
|
|
|
@ -197,7 +197,7 @@
|
|||
|
||||
<vn-confirm
|
||||
vn-id="confirm-delivery-note"
|
||||
on-response="$ctrl.sendDeliveryNote($response)"
|
||||
on-accept="$ctrl.sendDeliveryNote()"
|
||||
question="Send Delivery Note"
|
||||
message="Are you sure you want to send it?">
|
||||
</vn-confirm>
|
|
@ -2,9 +2,10 @@ import ngModule from '../module';
|
|||
import Component from 'core/lib/component';
|
||||
|
||||
class Controller extends Component {
|
||||
constructor($element, $, aclService) {
|
||||
constructor($element, $, aclService, $httpParamSerializer) {
|
||||
super($element, $);
|
||||
this.aclService = aclService;
|
||||
this.$httpParamSerializer = $httpParamSerializer;
|
||||
this.moreOptions = [
|
||||
{name: 'Add turn', callback: this.showAddTurnDialog},
|
||||
{name: 'Show Delivery Note', callback: this.showDeliveryNote},
|
||||
|
@ -198,10 +199,27 @@ class Controller extends Component {
|
|||
}
|
||||
|
||||
showDeliveryNote() {
|
||||
let url = `report/rpt-delivery-note?ticketFk=${this.ticket.id}`;
|
||||
const params = {
|
||||
clientId: this.ticket.client.id,
|
||||
ticketId: this.ticket.id
|
||||
};
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
let url = `api/report/delivery-note?${serializedParams}`;
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
sendDeliveryNote() {
|
||||
const params = {
|
||||
recipient: this.ticket.client.email,
|
||||
clientId: this.ticket.client.id,
|
||||
ticketId: this.ticket.id
|
||||
};
|
||||
const serializedParams = this.$httpParamSerializer(params);
|
||||
this.$http.get(`email/delivery-note?${serializedParams}`).then(
|
||||
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
|
||||
);
|
||||
}
|
||||
|
||||
showSMSDialog() {
|
||||
const address = this.ticket.address;
|
||||
this.newSMS = {
|
||||
|
@ -272,17 +290,9 @@ class Controller extends Component {
|
|||
confirmDeliveryNote() {
|
||||
this.$.confirmDeliveryNote.show();
|
||||
}
|
||||
|
||||
sendDeliveryNote(response) {
|
||||
if (response === 'accept') {
|
||||
this.$http.post(`email/delivery-note`, {ticketFk: this.ticket.id}).then(
|
||||
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controller.$inject = ['$element', '$scope', 'aclService'];
|
||||
Controller.$inject = ['$element', '$scope', 'aclService', '$httpParamSerializer'];
|
||||
|
||||
ngModule.component('vnTicketDescriptor', {
|
||||
template: require('./index.html'),
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import './index.js';
|
||||
|
||||
describe('Ticket Component vnTicketDescriptor', () => {
|
||||
let $httpParamSerializer;
|
||||
let $httpBackend;
|
||||
let controller;
|
||||
let $state;
|
||||
|
||||
beforeEach(ngModule('ticket'));
|
||||
|
||||
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, $compile, _$state_) => {
|
||||
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $rootScope, $compile, _$state_, _$httpParamSerializer_) => {
|
||||
let $element = $compile(`<vn-autocomplete></vn-autocomplete>`)($rootScope);
|
||||
$state = _$state_;
|
||||
$state.getCurrentPath = () => {
|
||||
|
@ -17,8 +18,9 @@ describe('Ticket Component vnTicketDescriptor', () => {
|
|||
];
|
||||
};
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpParamSerializer = _$httpParamSerializer_;
|
||||
controller = $componentController('vnTicketDescriptor', {$element});
|
||||
controller._ticket = {id: 2, invoiceOut: {id: 1}};
|
||||
controller._ticket = {id: 2, invoiceOut: {id: 1}, client: {id: 101, email: 'client@email'}};
|
||||
controller.cardReload = ()=> {
|
||||
return true;
|
||||
};
|
||||
|
@ -82,7 +84,12 @@ describe('Ticket Component vnTicketDescriptor', () => {
|
|||
|
||||
describe('showDeliveryNote()', () => {
|
||||
it('should open a new window showing a delivery note PDF document', () => {
|
||||
let expectedPath = 'report/rpt-delivery-note?ticketFk=2';
|
||||
const params = {
|
||||
clientId: controller.ticket.client.id,
|
||||
ticketId: controller.ticket.id
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
let expectedPath = `api/report/delivery-note?${serializedParams}`;
|
||||
spyOn(window, 'open');
|
||||
controller.showDeliveryNote();
|
||||
|
||||
|
@ -90,6 +97,26 @@ describe('Ticket Component vnTicketDescriptor', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('sendDeliveryNote()', () => {
|
||||
it('should make a query and call vnApp.showMessage()', () => {
|
||||
spyOn(controller.vnApp, 'showMessage');
|
||||
|
||||
const params = {
|
||||
recipient: 'client@email',
|
||||
clientId: controller.ticket.client.id,
|
||||
ticketId: controller.ticket.id
|
||||
};
|
||||
const serializedParams = $httpParamSerializer(params);
|
||||
|
||||
$httpBackend.when('GET', `email/delivery-note?${serializedParams}`).respond();
|
||||
$httpBackend.expect('GET', `email/delivery-note?${serializedParams}`).respond();
|
||||
controller.sendDeliveryNote();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Notification sent!');
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeInvoice()', () => {
|
||||
it('should make a query and call $state.reload() method if the response is accept', () => {
|
||||
spyOn(controller.$state, 'reload');
|
||||
|
|
|
@ -31,8 +31,7 @@ import './picture';
|
|||
import './request/index';
|
||||
import './request/create';
|
||||
import './log';
|
||||
import './weekly/index';
|
||||
import './weekly/create';
|
||||
import './weekly';
|
||||
import './dms/index';
|
||||
import './dms/create';
|
||||
import './dms/edit';
|
||||
|
|
|
@ -197,11 +197,6 @@
|
|||
"state": "ticket.weekly.index",
|
||||
"component": "vn-ticket-weekly-index",
|
||||
"description": "Weekly tickets"
|
||||
}, {
|
||||
"url": "/create",
|
||||
"state": "ticket.weekly.create",
|
||||
"component": "vn-ticket-weekly-create",
|
||||
"description": "Add weekly ticket"
|
||||
}, {
|
||||
"url": "/request",
|
||||
"state": "ticket.card.request",
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
<mg-ajax path="ticketWeeklies" options="vnPost"></mg-ajax>
|
||||
<vn-watcher
|
||||
vn-id="watcher"
|
||||
data="$ctrl.ticketWeekly"
|
||||
form="form"
|
||||
save="post">
|
||||
</vn-watcher>
|
||||
<form name="form" vn-http-submit="$ctrl.onSubmit()" class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-one vn-id="ticket"
|
||||
url="tickets"
|
||||
ng-model="$ctrl.ticketWeekly.ticketFk"
|
||||
fields="['id', 'nickname', 'clientFk', 'warehouseFk']"
|
||||
search-function="{nickname: $search}"
|
||||
show-field="id"
|
||||
value-field="id"
|
||||
label="Ticket"
|
||||
on-change="$ctrl.onChangeTicket(ticket.selection)">
|
||||
<tpl-item>#{{id}} - {{nickname}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one label="Weekday"
|
||||
ng-model="$ctrl.ticketWeekly.weekDay"
|
||||
data="$ctrl.weekdays"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
translate-fields="['name']"
|
||||
order="id">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-autocomplete vn-id="client" vn-one disabled="true"
|
||||
url="clients"
|
||||
fields="['id', 'name', 'salesPersonFk']"
|
||||
ng-model="$ctrl.ticketWeekly.clientFk"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Client"
|
||||
selection="$ctrl.clientSelection">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one disabled="true"
|
||||
ng-model="$ctrl.ticketWeekly.warehouseFk"
|
||||
url="warehouses"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Warehouse">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete vn-one disabled="true"
|
||||
ng-model="$ctrl.ticketWeekly.salesPersonFk"
|
||||
url="clients/activeWorkersWithRole"
|
||||
search-function="{firstName: $search}"
|
||||
show-field="firstName"
|
||||
value-field="id"
|
||||
where="{role: 'employee'}"
|
||||
label="Salesperson">
|
||||
<tpl-item>{{firstName}} {{lastName}}</tpl-item>
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
</vn-card>
|
||||
<vn-button-bar>
|
||||
<vn-submit label="Create"></vn-submit>
|
||||
<vn-button ui-sref="ticket.weekly.index" label="Cancel"></vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
<!-- New postcode dialog -->
|
||||
<vn-client-postcode
|
||||
vn-id="postcode"
|
||||
on-response="$ctrl.onResponse($response)">
|
||||
</vn-client-postcode>
|
|
@ -1,49 +0,0 @@
|
|||
import ngModule from '../../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($scope, $state, $http, $translate, vnApp) {
|
||||
this.$ = $scope;
|
||||
this.$state = $state;
|
||||
this.$http = $http;
|
||||
this.$translate = $translate;
|
||||
this.vnApp = vnApp;
|
||||
this.ticketWeekly = {};
|
||||
this.weekdays = [
|
||||
{id: 0, name: 'Monday'},
|
||||
{id: 1, name: 'Tuesday'},
|
||||
{id: 2, name: 'Wednesday'},
|
||||
{id: 3, name: 'Thursday'},
|
||||
{id: 4, name: 'Friday'},
|
||||
{id: 5, name: 'Saturday'},
|
||||
{id: 6, name: 'Sunday'}
|
||||
];
|
||||
}
|
||||
|
||||
onChangeTicket(ticket) {
|
||||
this.ticketWeekly.clientFk = ticket.clientFk;
|
||||
this.ticketWeekly.warehouseFk = ticket.warehouseFk;
|
||||
}
|
||||
|
||||
get clientSelection() {
|
||||
return this._clientSelection;
|
||||
}
|
||||
|
||||
set clientSelection(value) {
|
||||
this._clientSelection = value;
|
||||
|
||||
if (value)
|
||||
this.ticketWeekly.salesPersonFk = value.salesPersonFk;
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
return this.$.watcher.submit().then(
|
||||
json => this.$state.go('ticket.weekly.index')
|
||||
);
|
||||
}
|
||||
}
|
||||
Controller.$inject = ['$scope', '$state', '$http', '$translate', 'vnApp'];
|
||||
|
||||
ngModule.component('vnTicketWeeklyCreate', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
});
|
|
@ -1,58 +0,0 @@
|
|||
import './index';
|
||||
|
||||
describe('Ticket', () => {
|
||||
describe('Component vnTicketWeeklyCreate', () => {
|
||||
let $componentController;
|
||||
let $scope;
|
||||
let $state;
|
||||
let controller;
|
||||
|
||||
beforeEach(ngModule('ticket'));
|
||||
|
||||
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_) => {
|
||||
$componentController = _$componentController_;
|
||||
$scope = $rootScope.$new();
|
||||
$state = _$state_;
|
||||
$scope.watcher = {
|
||||
submit: () => {
|
||||
return {
|
||||
then: callback => {
|
||||
callback({data: {id: '1234'}});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
controller = $componentController('vnTicketWeeklyCreate', {$scope, $state});
|
||||
}));
|
||||
|
||||
describe('onChangeTicket() setter', () => {
|
||||
it(`should define clientFk and warehouseFk properties on ticketWeekly object`, () => {
|
||||
controller.onChangeTicket({clientFk: 101, warehouseFk: 1});
|
||||
|
||||
expect(controller.ticketWeekly.clientFk).toEqual(101);
|
||||
expect(controller.ticketWeekly.warehouseFk).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clientSelection() setter', () => {
|
||||
it(`should define salesPersonFk property on ticketWeekly object`, () => {
|
||||
controller.clientSelection = {clientFk: 101, salesPersonFk: 106};
|
||||
|
||||
expect(controller.ticketWeekly.salesPersonFk).toEqual(106);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSubmit()', () => {
|
||||
it(`should call submit() on the watcher then expect a callback`, () => {
|
||||
spyOn(controller.$state, 'go');
|
||||
controller.ticketWeekly = {
|
||||
ticketFk: 11,
|
||||
weekDay: 0
|
||||
};
|
||||
controller.onSubmit();
|
||||
|
||||
expect(controller.$state.go).toHaveBeenCalledWith('ticket.weekly.index');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
Weekday: Día de la semana
|
||||
Add weekly ticket: Añadir ticket programado
|
|
@ -94,9 +94,3 @@
|
|||
question="This ticket will be removed from weekly tickets! Continue anyway?"
|
||||
message="You are going to delete this weekly ticket">
|
||||
</vn-confirm>
|
||||
<a ui-sref="ticket.weekly.create"
|
||||
vn-tooltip="Add weekly ticket"
|
||||
vn-bind="+"
|
||||
fixed-bottom-right>
|
||||
<vn-float-button icon="add"></vn-float-button>
|
||||
</a>
|
|
@ -1,4 +1,4 @@
|
|||
import ngModule from '../../module';
|
||||
import ngModule from '../module';
|
||||
|
||||
export default class Controller {
|
||||
constructor($scope, vnApp, $translate, $http) {
|
|
@ -30,8 +30,11 @@
|
|||
"isConfirmed": {
|
||||
"type": "Boolean"
|
||||
},
|
||||
"isRaid": {
|
||||
"type": "Boolean"
|
||||
"isVirtual": {
|
||||
"type": "Boolean",
|
||||
"mysql": {
|
||||
"columnName": "isRaid"
|
||||
}
|
||||
},
|
||||
"commission": {
|
||||
"type": "Number"
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
|
@ -3,5 +3,4 @@ module.exports = Self => {
|
|||
require('../methods/worker/mySubordinates')(Self);
|
||||
require('../methods/worker/isSubordinate')(Self);
|
||||
require('../methods/worker/getWorkerInfo')(Self);
|
||||
require('../methods/worker/sendMessage')(Self);
|
||||
};
|
||||
|
|
|
@ -13500,7 +13500,7 @@
|
|||
"dependencies": {
|
||||
"jsesc": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
|
||||
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
|
||||
"dev": true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
const express = require('express');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const templatesPath = path.resolve(__dirname, './templates');
|
||||
const componentsPath = path.resolve(__dirname, './core/components');
|
||||
|
||||
module.exports = app => {
|
||||
global.appPath = __dirname;
|
||||
|
||||
process.env.OPENSSL_CONF = '/etc/ssl/';
|
||||
|
||||
// Extended locale intl polyfill
|
||||
const IntlPolyfill = require('intl');
|
||||
Intl.NumberFormat = IntlPolyfill.NumberFormat;
|
||||
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
|
||||
|
||||
// Init database instance
|
||||
require('./core/database').init();
|
||||
// Init SMTP Instance
|
||||
require('./core/smtp').init();
|
||||
//
|
||||
require('./core/mixins');
|
||||
require('./core/filters');
|
||||
require('./core/directives');
|
||||
// Init router
|
||||
require('./core/router')(app);
|
||||
|
||||
/**
|
||||
* Serve component static files
|
||||
*/
|
||||
const componentsDir = fs.readdirSync(componentsPath);
|
||||
componentsDir.forEach(componentName => {
|
||||
const componentDir = path.join(componentsPath, '/', componentName);
|
||||
const assetsDir = `${componentDir}/assets`;
|
||||
|
||||
app.use(`/api/${componentName}/assets`, express.static(assetsDir));
|
||||
});
|
||||
|
||||
/**
|
||||
* Serve static files
|
||||
*/
|
||||
const templatesDir = fs.readdirSync(templatesPath);
|
||||
templatesDir.forEach(directory => {
|
||||
const templateTypeDir = path.join(templatesPath, '/', directory);
|
||||
const templates = fs.readdirSync(templateTypeDir);
|
||||
|
||||
templates.forEach(templateName => {
|
||||
const templateDir = path.join(templatesPath, '/', directory, '/', templateName);
|
||||
const assetsDir = `${templateDir}/assets`;
|
||||
|
||||
app.use(`/api/${templateName}/assets`, express.static(assetsDir));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1,40 +1,33 @@
|
|||
/**
|
||||
* Email only stylesheet
|
||||
*
|
||||
*/
|
||||
body {
|
||||
background-color: #EEE
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
min-width: 320px;
|
||||
margin: 0 auto;
|
||||
color: #555
|
||||
}
|
||||
|
||||
.main {
|
||||
-webkit-text-size-adjust: none;
|
||||
-ms-text-size-adjust: none;
|
||||
background-color: #FFF;
|
||||
padding: 20px
|
||||
font-weight: 400;
|
||||
color: #555;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.main a {
|
||||
.grid {
|
||||
background-color: #FFF
|
||||
|
||||
}
|
||||
|
||||
.grid a {
|
||||
color: #8dba25
|
||||
}
|
||||
|
||||
.main h1 {
|
||||
color: #999
|
||||
.grid-block {
|
||||
min-width: 300px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
color: #333
|
||||
}
|
||||
|
||||
.main h3 {
|
||||
font-size: 16px
|
||||
}
|
||||
|
||||
.title {
|
||||
background-color: #95d831;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
padding: 35px 0
|
||||
}
|
||||
|
||||
.title h1 {
|
||||
font-size: 32px;
|
||||
color: #333;
|
||||
margin: 0
|
||||
h1 {
|
||||
font-weight: 100;
|
||||
font-size: 1.5em
|
||||
}
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
.container {
|
||||
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
|
||||
font-size: 16px
|
||||
/**
|
||||
* CSS layout elements
|
||||
*
|
||||
*/
|
||||
|
||||
.grid {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 16px !important;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.grid-row {
|
||||
background-color: transparent
|
||||
}
|
||||
|
||||
.grid-block {
|
||||
box-sizing: border-box;
|
||||
min-height: 40px
|
||||
}
|
||||
|
||||
.grid-block.empty {
|
||||
height: 40px
|
||||
}
|
||||
|
||||
.grid-block.white {
|
||||
background-color: #FFF
|
||||
}
|
||||
|
||||
.columns {
|
||||
overflow: hidden
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.columns .size100 {
|
||||
|
@ -18,6 +42,7 @@
|
|||
}
|
||||
|
||||
.columns .size50 {
|
||||
box-sizing: border-box;
|
||||
width: 50%;
|
||||
float: left
|
||||
}
|
||||
|
@ -173,7 +198,7 @@ table {
|
|||
}
|
||||
|
||||
.panel .row-oriented td, .panel .row-oriented th {
|
||||
padding: 10px 0
|
||||
padding: 8px 10px
|
||||
}
|
||||
|
||||
.row-oriented > tbody > tr > td {
|
||||
|
@ -199,8 +224,8 @@ table {
|
|||
margin-left: -1px;
|
||||
margin-right: 1px;
|
||||
margin-top: 10px;
|
||||
padding: 5px 0;
|
||||
color: #999;
|
||||
padding: 5px 0
|
||||
}
|
||||
|
||||
.line .vertical-aligned {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* CSS misc classes
|
||||
*
|
||||
*/
|
||||
.uppercase {
|
||||
text-transform: uppercase
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
/**
|
||||
* Report only stylesheet
|
||||
*
|
||||
*/
|
||||
body {
|
||||
zoom: 0.55
|
||||
zoom: 0.53
|
||||
}
|
||||
|
||||
.title {
|
||||
|
|
|
@ -0,0 +1,349 @@
|
|||
/**
|
||||
* CSS spacing classes
|
||||
*
|
||||
* vn-[p|m][t|r|b|l|a|x|y]-[none|auto|xs|sm|md|lg|xl]
|
||||
* T D S
|
||||
*
|
||||
* T - type
|
||||
* - values: p (padding), m (margin)
|
||||
*
|
||||
* D - direction
|
||||
* - values:
|
||||
* t (top), r (right), b (bottom), l (left),
|
||||
* a (all), x (both left & right), y (both top & bottom)
|
||||
*
|
||||
* S - size
|
||||
* - values:
|
||||
* none,
|
||||
* auto (ONLY for specific margins: vn-ml-*, vn-mr-*, vn-mx-*),
|
||||
* xs (extra small),
|
||||
* sm (small),
|
||||
* md (medium),
|
||||
* lg (large),
|
||||
* xl (extra large)
|
||||
*/
|
||||
|
||||
/* ++++++++++++++++++++++++++++++++++++++++++++++++ Padding */
|
||||
|
||||
.vn-pa-none {
|
||||
padding: 0;
|
||||
}
|
||||
.vn-pl-none {
|
||||
padding-left: 0;
|
||||
}
|
||||
.vn-pr-none {
|
||||
padding-right: 0;
|
||||
}
|
||||
.vn-pt-none {
|
||||
padding-top: 0;
|
||||
}
|
||||
.vn-pb-none {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.vn-py-none {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.vn-px-none {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.vn-pa-xs {
|
||||
padding: 4px;
|
||||
}
|
||||
.vn-pl-xs {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.vn-pr-xs {
|
||||
padding-right: 4px;
|
||||
}
|
||||
.vn-pt-xs {
|
||||
padding-top: 4px;
|
||||
}
|
||||
.vn-pb-xs {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.vn-py-xs {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.vn-px-xs {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
/* Small */
|
||||
|
||||
.vn-pa-sm {
|
||||
padding: 8px;
|
||||
}
|
||||
.vn-pl-sm {
|
||||
padding-left: 8px;
|
||||
}
|
||||
.vn-pr-sm {
|
||||
padding-right: 8px;
|
||||
}
|
||||
.vn-pt-sm {
|
||||
padding-top: 8px;
|
||||
}
|
||||
.vn-pb-sm {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.vn-py-sm {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.vn-px-sm {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
/* Medium */
|
||||
|
||||
.vn-pa-md {
|
||||
padding: 16px;
|
||||
}
|
||||
.vn-pl-md {
|
||||
padding-left: 16px;
|
||||
}
|
||||
.vn-pr-md {
|
||||
padding-right: 16px;
|
||||
}
|
||||
.vn-pt-md {
|
||||
padding-top: 16px;
|
||||
}
|
||||
.vn-pb-md {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.vn-py-md {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.vn-px-md {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
/* Large */
|
||||
|
||||
.vn-pa-lg {
|
||||
padding: 32px;
|
||||
}
|
||||
.vn-pl-lg {
|
||||
padding-left: 32px;
|
||||
}
|
||||
.vn-pr-lg {
|
||||
padding-right: 32px;
|
||||
}
|
||||
.vn-pt-lg {
|
||||
padding-top: 32px;
|
||||
}
|
||||
.vn-pb-lg {
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
.vn-py-lg {
|
||||
padding-top: 32px;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
.vn-px-lg {
|
||||
padding-left: 32px;
|
||||
padding-right: 32px;
|
||||
}
|
||||
|
||||
/* Extra large */
|
||||
|
||||
.vn-pa-xl {
|
||||
padding: 100px;
|
||||
}
|
||||
.vn-pl-xl {
|
||||
padding-left: 100px;
|
||||
}
|
||||
.vn-pr-xl {
|
||||
padding-right: 100px;
|
||||
}
|
||||
.vn-pt-xl {
|
||||
padding-top: 100px;
|
||||
}
|
||||
.vn-pb-xl {
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
.vn-py-xl {
|
||||
padding-top: 100px;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
.vn-px-xl {
|
||||
padding-left: 100px;
|
||||
padding-right: 100px;
|
||||
}
|
||||
|
||||
/* ++++++++++++++++++++++++++++++++++++++++++++++++ Margin */
|
||||
|
||||
/* None */
|
||||
|
||||
.vn-ma-none {
|
||||
padding: 0;
|
||||
}
|
||||
.vn-ml-none {
|
||||
padding-left: 0;
|
||||
}
|
||||
.vn-mr-none {
|
||||
padding-right: 0;
|
||||
}
|
||||
.vn-mt-none {
|
||||
padding-top: 0;
|
||||
}
|
||||
.vn-mb-none {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.vn-my-none {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.vn-mx-none {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
/* Auto */
|
||||
|
||||
.vn-ml-none {
|
||||
padding-left: auto;
|
||||
}
|
||||
.vn-mr-none {
|
||||
padding-right: auto;
|
||||
}
|
||||
.vn-mx-none {
|
||||
padding-left: auto;
|
||||
padding-right: auto;
|
||||
}
|
||||
|
||||
/* Extra small */
|
||||
|
||||
.vn-ma-xs {
|
||||
margin: 4px;
|
||||
}
|
||||
.vn-mt-xs {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.vn-ml-xs {
|
||||
margin-left: 4px;
|
||||
}
|
||||
.vn-mr-xs {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.vn-mb-xs {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.vn-my-xs {
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.vn-mx-xs {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* Small */
|
||||
|
||||
.vn-ma-sm {
|
||||
margin: 8px;
|
||||
}
|
||||
.vn-mt-sm {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.vn-ml-sm {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.vn-mr-sm {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.vn-mb-sm {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.vn-my-sm {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.vn-mx-sm {
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* Medium */
|
||||
|
||||
.vn-ma-md {
|
||||
margin: 16px;
|
||||
}
|
||||
.vn-mt-md {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.vn-ml-md {
|
||||
margin-left: 16px;
|
||||
}
|
||||
.vn-mr-md {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.vn-mb-md {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.vn-my-md {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.vn-mx-md {
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
/* Large */
|
||||
|
||||
.vn-ma-lg {
|
||||
margin: 32px;
|
||||
}
|
||||
.vn-mt-lg {
|
||||
margin-top: 32px;
|
||||
}
|
||||
.vn-ml-lg {
|
||||
margin-left: 32px;
|
||||
}
|
||||
.vn-mr-lg {
|
||||
margin-right: 32px;
|
||||
}
|
||||
.vn-mb-lg {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.vn-my-lg {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.vn-mx-lg {
|
||||
margin-left: 32px;
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
/* Extra large */
|
||||
|
||||
.vn-ma-xl {
|
||||
margin: 100px;
|
||||
}
|
||||
.vn-mt-xl {
|
||||
margin-top: 100px;
|
||||
}
|
||||
.vn-ml-xl {
|
||||
margin-left: 100px;
|
||||
}
|
||||
.vn-mr-xl {
|
||||
margin-right: 100px;
|
||||
}
|
||||
.vn-mb-xl {
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
.vn-my-xl {
|
||||
margin-top: 100px;
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
.vn-mx-xl {
|
||||
margin-left: 100px;
|
||||
margin-right: 100px;
|
||||
}
|
|
@ -1,10 +1,15 @@
|
|||
{
|
||||
"app": {
|
||||
"host": "http://localhost:5000",
|
||||
"port": 3000,
|
||||
"defaultLanguage": "es",
|
||||
"senderMail": "nocontestar@verdnatura.es",
|
||||
"senderName": "Verdnatura"
|
||||
},
|
||||
"i18n": {
|
||||
"locale": "es",
|
||||
"fallbackLocale": "es",
|
||||
"silentTranslationWarn": false
|
||||
},
|
||||
"pdf": {
|
||||
"format": "A4",
|
||||
"border": "1.5cm",
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
[
|
||||
{"type": "email", "name": "client-welcome"},
|
||||
{"type": "email", "name": "printer-setup"},
|
||||
{"type": "email", "name": "payment-update"},
|
||||
{"type": "email", "name": "letter-debtor-st"},
|
||||
{"type": "email", "name": "letter-debtor-nd"},
|
||||
{"type": "email", "name": "claim-pickup-order"},
|
||||
{"type": "email", "name": "sepa-core"},
|
||||
{"type": "email", "name": "client-lcr"},
|
||||
{"type": "email", "name": "driver-route"},
|
||||
{"type": "email", "name": "delivery-note"},
|
||||
{"type": "report", "name": "rpt-delivery-note"},
|
||||
{"type": "report", "name": "rpt-claim-pickup-order"},
|
||||
{"type": "report", "name": "rpt-letter-debtor"},
|
||||
{"type": "report", "name": "rpt-sepa-core"},
|
||||
{"type": "report", "name": "rpt-receipt"},
|
||||
{"type": "report", "name": "rpt-zone"},
|
||||
{"type": "report", "name": "rpt-route"},
|
||||
{"type": "report", "name": "rpt-lcr"},
|
||||
{"type": "report", "name": "rpt-item-label"},
|
||||
{"type": "static", "name": "email-header"},
|
||||
{"type": "static", "name": "email-footer"},
|
||||
{"type": "static", "name": "report-header"},
|
||||
{"type": "static", "name": "report-footer"}
|
||||
]
|
|
@ -0,0 +1,104 @@
|
|||
const Vue = require('vue');
|
||||
const VueI18n = require('vue-i18n');
|
||||
const renderer = require('vue-server-renderer').createRenderer();
|
||||
Vue.use(VueI18n);
|
||||
|
||||
const fs = require('fs');
|
||||
const yaml = require('js-yaml');
|
||||
const juice = require('juice');
|
||||
const path = require('path');
|
||||
const config = require('./config');
|
||||
|
||||
class Component {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
get path() {
|
||||
return `./components/${this.name}`;
|
||||
}
|
||||
|
||||
get template() {
|
||||
const templatePath = `${this.path}/${this.name}.html`;
|
||||
const fullPath = path.resolve(__dirname, templatePath);
|
||||
|
||||
return fs.readFileSync(fullPath, 'utf8');
|
||||
}
|
||||
|
||||
get locale() {
|
||||
if (!this._locale)
|
||||
this.getLocale();
|
||||
|
||||
return this._locale;
|
||||
}
|
||||
|
||||
getLocale() {
|
||||
const mergedLocale = {messages: {}};
|
||||
const localePath = path.resolve(__dirname, `${this.path}/locale`);
|
||||
|
||||
if (!fs.existsSync(localePath))
|
||||
return mergedLocale;
|
||||
|
||||
const localeDir = fs.readdirSync(localePath);
|
||||
localeDir.forEach(locale => {
|
||||
const fullPath = path.join(localePath, '/', locale);
|
||||
const yamlLocale = fs.readFileSync(fullPath, 'utf8');
|
||||
const jsonLocale = yaml.safeLoad(yamlLocale);
|
||||
const localeName = locale.replace('.yml', '');
|
||||
|
||||
mergedLocale.messages[localeName] = jsonLocale;
|
||||
});
|
||||
|
||||
this._locale = mergedLocale;
|
||||
}
|
||||
|
||||
get stylesheet() {
|
||||
let mergedStyles = '';
|
||||
const stylePath = path.resolve(__dirname, `${this.path}/assets/css`);
|
||||
|
||||
if (!fs.existsSync(stylePath))
|
||||
return mergedStyles;
|
||||
|
||||
return require(`${stylePath}/import`);
|
||||
}
|
||||
|
||||
get attachments() {
|
||||
const attachmentsPath = `${this.path}/attachments.json`;
|
||||
const fullPath = path.resolve(__dirname, attachmentsPath);
|
||||
|
||||
if (!fs.existsSync(fullPath))
|
||||
return [];
|
||||
|
||||
return require(fullPath);
|
||||
}
|
||||
|
||||
build() {
|
||||
const fullPath = path.resolve(__dirname, this.path);
|
||||
if (!fs.existsSync(fullPath))
|
||||
throw new Error(`Sample "${this.name}" not found`);
|
||||
|
||||
const component = require(`${this.path}/${this.name}`);
|
||||
component.i18n = this.locale;
|
||||
component.attachments = this.attachments;
|
||||
component.template = juice.inlineContent(this.template, this.stylesheet, {
|
||||
inlinePseudoElements: true
|
||||
});
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
async render() {
|
||||
const component = this.build();
|
||||
const i18n = new VueI18n(config.i18n);
|
||||
const app = new Vue({
|
||||
i18n: i18n,
|
||||
render: h => h(component, {
|
||||
props: this.args
|
||||
})
|
||||
});
|
||||
|
||||
return renderer.renderToString(app);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Component;
|
|
@ -0,0 +1,9 @@
|
|||
const Stylesheet = require(`${appPath}/core/stylesheet`);
|
||||
|
||||
module.exports = new Stylesheet([
|
||||
`${appPath}/common/css/spacing.css`,
|
||||
`${appPath}/common/css/misc.css`,
|
||||
`${appPath}/common/css/layout.css`,
|
||||
`${appPath}/common/css/email.css`,
|
||||
`${__dirname}/style.css`])
|
||||
.mergeStyles();
|
|
@ -0,0 +1,22 @@
|
|||
div {
|
||||
display: inline-block;
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
a {
|
||||
background-color: #F5F5F5;
|
||||
border: 1px solid #CCC;
|
||||
display: flex;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
min-width: 150px;
|
||||
text-decoration: none;
|
||||
border-radius: 3px;
|
||||
color: #8dba25
|
||||
}
|
||||
|
||||
a > div.icon {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
color: #555
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M2 12.5C2 9.46 4.46 7 7.5 7H18c2.21 0 4 1.79 4 4s-1.79 4-4 4H9.5C8.12 15 7 13.88 7 12.5S8.12 10 9.5 10H17v2H9.41c-.55 0-.55 1 0 1H18c1.1 0 2-.9 2-2s-.9-2-2-2H7.5C5.57 9 4 10.57 4 12.5S5.57 16 7.5 16H17v2H7.5C4.46 18 2 15.54 2 12.5z"/><path fill="none" d="M0 0h24v24H0V0z"/></svg>
|
After Width: | Height: | Size: 371 B |
|
@ -0,0 +1,6 @@
|
|||
<div class="vn-mx-xs" v-if="attachment.component">
|
||||
<a target="_blank" class="vn-py-sm vn-px-md" v-bind:href="path">
|
||||
<div class="text">{{attachment.filename}}</div>
|
||||
<div class="icon vn-pl-md">▼</div>
|
||||
</a>
|
||||
</div>
|
|
@ -0,0 +1,37 @@
|
|||
module.exports = {
|
||||
name: 'attachment',
|
||||
computed: {
|
||||
path() {
|
||||
const filename = this.attachment.filename;
|
||||
const component = this.attachment.component;
|
||||
if (this.attachment.cid)
|
||||
return `/api/${component}/assets/files/${filename}`;
|
||||
else
|
||||
return `/api/report/${component}?${this.getHttpParams()}`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getHttpParams() {
|
||||
const props = this.args;
|
||||
let query = '';
|
||||
for (let param in props) {
|
||||
if (query != '')
|
||||
query += '&';
|
||||
query += `${param}=${props[param]}`;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
},
|
||||
props: {
|
||||
attachment: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
args: {
|
||||
type: Object,
|
||||
required: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
const Stylesheet = require(`${appPath}/core/stylesheet`);
|
||||
|
||||
module.exports = new Stylesheet([
|
||||
`${appPath}/common/css/spacing.css`,
|
||||
`${appPath}/common/css/misc.css`,
|
||||
`${appPath}/common/css/layout.css`,
|
||||
`${appPath}/common/css/email.css`,
|
||||
`${__dirname}/style.css`])
|
||||
.mergeStyles();
|
|
@ -1,21 +1,12 @@
|
|||
@media (max-width: 400px) {
|
||||
.buttons a {
|
||||
display: block;
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
font-size: 14px !important;
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.buttons a {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
width: 50%
|
||||
}
|
||||
|
||||
.buttons .btn {
|
||||
|
@ -23,18 +14,20 @@
|
|||
text-align: center
|
||||
}
|
||||
|
||||
.buttons .btn .text {
|
||||
display: inline-block;
|
||||
padding: 22px 0
|
||||
}
|
||||
|
||||
.buttons .btn .icon {
|
||||
background-color: #95d831;
|
||||
box-sizing: border-box;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 16.5px 0;
|
||||
float: right;
|
||||
width: 70px
|
||||
color: #333;
|
||||
float: left
|
||||
}
|
||||
|
||||
|
||||
.buttons .btn .text {
|
||||
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.networks {
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
@ -0,0 +1,22 @@
|
|||
[
|
||||
{
|
||||
"filename": "facebook.png",
|
||||
"path": "/assets/images/facebook.png",
|
||||
"cid": "facebook.png"
|
||||
},
|
||||
{
|
||||
"filename": "twitter.png",
|
||||
"path": "/assets/images/twitter.png",
|
||||
"cid": "twitter.png"
|
||||
},
|
||||
{
|
||||
"filename": "instagram.png",
|
||||
"path": "/assets/images/instagram.png",
|
||||
"cid": "instagram.png"
|
||||
},
|
||||
{
|
||||
"filename": "linkedin.png",
|
||||
"path": "/assets/images/linkedin.png",
|
||||
"cid": "linkedin.png"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,47 @@
|
|||
<footer>
|
||||
<!-- Action button block -->
|
||||
<div class="buttons">
|
||||
<div class="columns">
|
||||
<div class="size50">
|
||||
<a href="https://www.verdnatura.es" target="_blank">
|
||||
<div class="btn">
|
||||
<!-- <span class="icon vn-pa-sm"><img v-bind:src="getEmailSrc('action.png')"/></span> -->
|
||||
<span class="text vn-pa-sm">{{ $t('buttons.webAcccess')}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="size50">
|
||||
<a href="https://goo.gl/forms/j8WSL151ZW6QtlT72" target="_blank">
|
||||
<div class="btn">
|
||||
<!-- <span class="icon vn-pa-sm"><img v-bind:src="getEmailSrc('info.png')"/></span> -->
|
||||
<span class="text vn-pa-sm">{{ $t('buttons.info')}}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Networks block -->
|
||||
<div class="networks">
|
||||
<a href="https://www.facebook.com/Verdnatura" target="_blank">
|
||||
<img v-bind:src="getEmailSrc('facebook.png')" alt="Facebook"/>
|
||||
</a>
|
||||
<a href="https://www.twitter.com/Verdnatura" target="_blank">
|
||||
<img v-bind:src="getEmailSrc('twitter.png')" alt="Twitter"/>
|
||||
</a>
|
||||
<a href="https://www.instagram.com/Verdnatura" target="_blank">
|
||||
<img v-bind:src="getEmailSrc('instagram.png')" alt="Instagram"/>
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/company/verdnatura" target="_blank">
|
||||
<img v-bind:src="getEmailSrc('linkedin.png')" alt="Linkedin"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Privacy block -->
|
||||
<div class="privacy">
|
||||
<p>{{$t('privacy.fiscalAddress')}}</p>
|
||||
<p>{{$t('privacy.disclaimer')}}</p>
|
||||
<p>{{$t('privacy.law')}}</p>
|
||||
</div>
|
||||
<!-- Privacy block end -->
|
||||
</footer>
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
name: 'email-footer',
|
||||
props: ['isPreview', 'locale']
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
buttons:
|
||||
webAcccess: Visita nuestra Web
|
||||
info: Ayúdanos a mejorar
|
||||
privacy:
|
||||
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla
|
||||
· www.verdnatura.es · clientes@verdnatura.es
|
||||
disclaimer: '- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado
|
||||
exclusivamente por la persona destinataria del mismo. Si has recibido este mensaje
|
||||
por error, te rogamos lo comuniques al remitente y borres dicho mensaje y cualquier
|
||||
documento adjunto que pudiera contener. Verdnatura Levante SL no renuncia a la
|
||||
confidencialidad ni a ningún privilegio por causa de transmisión errónea o mal
|
||||
funcionamiento. Igualmente no se hace responsable de los cambios, alteraciones,
|
||||
errores u omisiones que pudieran hacerse al mensaje una vez enviado.'
|
||||
law: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección de
|
||||
Datos de Carácter Personal, te comunicamos que los datos personales que facilites
|
||||
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en
|
||||
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
|
||||
comunicándolo por escrito al domicilio social de la entidad. La finalidad del
|
||||
fichero es la gestión administrativa, contabilidad, y facturación.
|
|
@ -0,0 +1,19 @@
|
|||
buttons:
|
||||
webAcccess: Visitez notre site web
|
||||
info: Aidez-nous à améliorer
|
||||
privacy:
|
||||
fiscalAddress: VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla
|
||||
· www.verdnatura.es · clientes@verdnatura.es
|
||||
disclaimer: '- AVIS - Ce message est privé et confidentiel et doit être utilisé.exclusivamente
|
||||
por la persona destinataria del mismo. Si has recibido este mensajepor error,
|
||||
te rogamos lo comuniques al remitente y borres dicho mensaje y cualquier documentoadjunto
|
||||
que pudiera contener. Verdnatura Levante SL no renuncia a la confidencialidad
|
||||
ni aningún privilegio por causa de transmisión errónea o mal funcionamiento. Igualmente
|
||||
no se haceresponsable de los cambios, alteraciones, errores u omisiones que pudieran
|
||||
hacerse al mensaje una vez enviado.'
|
||||
law: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección de
|
||||
Datos de Carácter Personal, te comunicamos que los datos personales que facilites
|
||||
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L.,pudiendo en
|
||||
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
|
||||
comunicándolo porescrito al domicilio social de la entidad. La finalidad del fichero
|
||||
es la gestión administrativa, contabilidad, y facturación.
|
|
@ -0,0 +1,9 @@
|
|||
const Stylesheet = require(`${appPath}/core/stylesheet`);
|
||||
|
||||
module.exports = new Stylesheet([
|
||||
`${appPath}/common/css/spacing.css`,
|
||||
`${appPath}/common/css/misc.css`,
|
||||
`${appPath}/common/css/layout.css`,
|
||||
`${appPath}/common/css/email.css`,
|
||||
`${__dirname}/style.css`])
|
||||
.mergeStyles();
|
|
@ -0,0 +1,19 @@
|
|||
header .logo {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
header .logo img {
|
||||
width: 50%
|
||||
}
|
||||
|
||||
header .topbar {
|
||||
background-color: #95d831;
|
||||
height: 10px
|
||||
}
|
||||
|
||||
.topbar:after {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
content: ' ';
|
||||
clear: both;
|
||||
}
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
@ -0,0 +1,131 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="500"
|
||||
height="68.596313"
|
||||
viewBox="0 0 499.99999 68.596313"
|
||||
enable-background="new 0 0 226.229 31.038"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="verdnatura-white.svg"><metadata
|
||||
id="metadata61"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs59" /><sodipodi:namedview
|
||||
pagecolor="#333333"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1013"
|
||||
id="namedview57"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.5909426"
|
||||
inkscape:cx="268.25598"
|
||||
inkscape:cy="112.75218"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="30"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" /><g
|
||||
id="Background"
|
||||
transform="translate(2.2478643e-6,43.261169)" /><g
|
||||
id="Guides"
|
||||
transform="translate(2.2478643e-6,43.261169)" /><g
|
||||
id="g883"
|
||||
transform="matrix(2.2101465,0,0,2.2101465,0,-594.44542)"><g
|
||||
transform="translate(0,268.962)"
|
||||
style="fill:#ffffff"
|
||||
id="g9"><path
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path11"
|
||||
d="M 10.417,30.321 0,0 h 8.233 l 4.26,15.582 0.349,1.276 c 0.521,1.866 0.918,3.431 1.191,4.693 0.15,-0.618 0.335,-1.345 0.555,-2.182 0.219,-0.837 0.528,-1.935 0.925,-3.293 L 19.981,0 h 8.19 l -10.5,30.321 z" /></g><g
|
||||
transform="translate(0,268.962)"
|
||||
id="g13"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path15"
|
||||
d="m 139.809,19.787 c -0.665,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.283,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.204,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.653,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.925,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.307,-1.159 3.021,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.646,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.076,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.673,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.991,0 3.602,0.241 4.833,0.722 1.231,0.481 2.095,1.209 2.59,2.185 0.339,0.701 0.483,1.536 0.432,2.504 -0.052,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z" /></g><g
|
||||
transform="translate(0,268.962)"
|
||||
id="g17"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path19"
|
||||
d="m 185.7,30.321 6.27,-22.393 h 7.049 l -1.097,3.918 c 1.213,-1.537 2.502,-2.659 3.867,-3.366 1.365,-0.707 2.951,-1.074 4.758,-1.101 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.912,-0.093 -0.303,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -2.104,0.168 -2.932,0.504 -0.829,0.336 -1.561,0.854 -2.197,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.359,4.232 l -2.104,7.516 H 185.7 Z" /></g><g
|
||||
transform="translate(0,268.962)"
|
||||
id="g21"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path23"
|
||||
d="m 217.631,19.787 c -0.664,0.357 -1.748,0.686 -3.25,0.988 -0.727,0.137 -1.282,0.254 -1.667,0.35 -0.95,0.247 -1.661,0.563 -2.134,0.947 -0.472,0.384 -0.799,0.899 -0.979,1.544 -0.223,0.796 -0.155,1.438 0.205,1.925 0.359,0.488 0.945,0.731 1.757,0.731 1.252,0 2.375,-0.36 3.369,-1.081 0.994,-0.721 1.654,-1.665 1.98,-2.831 z m 5.106,10.534 h -7.458 c 0.017,-0.356 0.048,-0.726 0.094,-1.11 l 0.159,-1.192 c -1.318,1.026 -2.627,1.786 -3.927,2.279 -1.299,0.493 -2.643,0.739 -4.031,0.739 -2.158,0 -3.7,-0.593 -4.625,-1.779 -0.926,-1.187 -1.106,-2.788 -0.542,-4.804 0.519,-1.851 1.431,-3.356 2.737,-4.515 1.306,-1.159 3.02,-1.972 5.142,-2.438 1.169,-0.247 2.641,-0.515 4.413,-0.803 2.647,-0.412 4.082,-1.016 4.304,-1.812 l 0.151,-0.539 c 0.182,-0.65 0.077,-1.145 -0.317,-1.483 -0.393,-0.339 -1.071,-0.508 -2.033,-0.508 -1.045,0 -1.934,0.214 -2.666,0.643 -0.731,0.428 -1.289,1.058 -1.672,1.887 h -6.748 c 1.065,-2.53 2.64,-4.413 4.723,-5.65 2.083,-1.237 4.724,-1.856 7.923,-1.856 1.99,0 3.601,0.241 4.833,0.722 1.232,0.481 2.095,1.209 2.591,2.185 0.339,0.701 0.483,1.536 0.431,2.504 -0.051,0.969 -0.377,2.525 -0.978,4.669 l -2.375,8.483 c -0.284,1.014 -0.416,1.812 -0.396,2.395 0.02,0.583 0.188,0.962 0.503,1.141 z" /></g><g
|
||||
transform="translate(0,268.962)"
|
||||
id="g25"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path27"
|
||||
d="m 188.386,7.928 -6.269,22.393 h -7.174 l 0.864,-3.085 c -1.227,1.246 -2.476,2.163 -3.746,2.751 -1.27,0.588 -2.625,0.882 -4.067,0.882 -2.471,0 -4.154,-0.634 -5.048,-1.901 -0.895,-1.268 -0.993,-3.149 -0.294,-5.644 l 4.31,-15.396 h 7.338 l -3.508,12.53 c -0.516,1.842 -0.641,3.109 -0.375,3.803 0.266,0.694 0.967,1.041 2.105,1.041 1.275,0 2.323,-0.422 3.142,-1.267 0.819,-0.845 1.497,-2.223 2.031,-4.133 l 3.353,-11.974 z" /></g><g
|
||||
transform="translate(0,268.962)"
|
||||
id="g29"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="clip-rule:evenodd;fill:#8ed300;fill-opacity:1;fill-rule:evenodd"
|
||||
id="path31"
|
||||
d="m 149.937,12.356 1.239,-4.428 h 2.995 l 1.771,-6.326 h 7.338 l -1.771,6.326 h 3.753 l -1.24,4.428 h -3.753 l -2.716,9.702 c -0.416,1.483 -0.498,2.465 -0.247,2.946 0.25,0.48 0.905,0.721 1.964,0.721 l 0.549,-0.011 0.39,-0.031 -1.31,4.678 c -0.811,0.148 -1.596,0.263 -2.354,0.344 -0.758,0.081 -1.48,0.122 -2.167,0.122 -2.543,0 -4.108,-0.621 -4.695,-1.863 -0.587,-1.242 -0.313,-3.887 0.82,-7.936 l 2.428,-8.672 z" /></g><g
|
||||
transform="translate(0,268.962)"
|
||||
style="fill:#ffffff"
|
||||
id="g33"><path
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path35"
|
||||
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z" /><g
|
||||
style="fill:#ffffff"
|
||||
id="g37"><path
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path39"
|
||||
d="m 73.875,18.896 c -0.561,2.004 -0.616,3.537 -0.167,4.601 0.449,1.064 1.375,1.595 2.774,1.595 1.399,0 2.605,-0.524 3.62,-1.574 1.015,-1.05 1.806,-2.59 2.375,-4.622 0.526,-1.879 0.556,-3.334 0.09,-4.363 -0.466,-1.029 -1.393,-1.543 -2.778,-1.543 -1.304,0 -2.487,0.528 -3.551,1.585 -1.064,1.057 -1.852,2.496 -2.363,4.321 z M 96.513,0 88.024,30.321 h -7.337 l 0.824,-2.944 c -1.166,1.22 -2.369,2.121 -3.61,2.703 -1.241,0.582 -2.583,0.874 -4.025,0.874 -2.802,0 -4.772,-1.081 -5.912,-3.243 -1.139,-2.162 -1.218,-4.993 -0.238,-8.493 0.988,-3.528 2.668,-6.404 5.042,-8.627 2.374,-2.224 4.927,-3.336 7.661,-3.336 1.47,0 2.695,0.296 3.676,0.887 0.981,0.591 1.681,1.465 2.099,2.62 L 89.217,0 Z" /></g></g><g
|
||||
transform="translate(0,268.962)"
|
||||
style="fill:#ffffff"
|
||||
id="g41"><path
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path43"
|
||||
d="M 46.488,30.321 52.757,7.928 h 7.049 l -1.098,3.918 C 59.921,10.309 61.21,9.187 62.576,8.48 63.942,7.773 68.591,7.406 70.398,7.379 l -2.03,7.25 c -0.304,-0.042 -0.608,-0.072 -0.911,-0.093 -0.304,-0.02 -0.592,-0.03 -0.867,-0.03 -1.126,0 -5.167,0.168 -5.997,0.504 -0.829,0.336 -1.561,0.854 -2.196,1.555 -0.406,0.467 -0.789,1.136 -1.149,2.007 -0.361,0.872 -0.814,2.282 -1.36,4.232 l -2.104,7.516 h -7.296 z" /></g><g
|
||||
transform="translate(0,268.962)"
|
||||
style="fill:#ffffff"
|
||||
id="g45"><path
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path47"
|
||||
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z" /><g
|
||||
style="fill:#ffffff"
|
||||
id="g49"><path
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path51"
|
||||
d="m 32.673,16.742 8.351,-0.021 c 0.375,-1.436 0.308,-2.558 -0.201,-3.365 -0.509,-0.807 -1.402,-1.211 -2.68,-1.211 -1.209,0 -2.285,0.397 -3.229,1.19 -0.944,0.793 -1.69,1.93 -2.241,3.407 z m 6.144,6.536 h 7.043 c -1.347,2.456 -3.172,4.356 -5.477,5.7 -2.305,1.345 -4.885,2.017 -7.74,2.017 -3.473,0 -5.923,-1.054 -7.351,-3.161 -1.427,-2.107 -1.632,-4.98 -0.613,-8.618 1.038,-3.707 2.875,-6.641 5.512,-8.803 2.637,-2.163 5.678,-3.244 9.123,-3.244 3.555,0 6.04,1.099 7.456,3.298 1.417,2.198 1.582,5.234 0.498,9.109 l -0.239,0.814 -0.167,0.484 H 31.721 c -0.441,1.575 -0.438,2.777 0.01,3.606 0.448,0.829 1.332,1.244 2.65,1.244 0.975,0 1.836,-0.206 2.583,-0.617 0.747,-0.411 1.366,-1.021 1.853,-1.829 z" /></g></g><g
|
||||
transform="translate(0,268.962)"
|
||||
id="g53"><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#8ed300;fill-opacity:1"
|
||||
id="path55"
|
||||
d="m 112.881,30.643 -6.404,-18.639 -6.455,18.639 h -7.254 l 9.565,-30.321 h 8.19 l 4.434,15.582 0.35,1.276 c 0.521,1.866 0.917,3.431 1.191,4.693 l 0.555,-2.182 c 0.219,-0.837 0.528,-1.935 0.925,-3.293 l 4.468,-16.076 h 8.19 l -10.501,30.321 z" /></g></g></svg>
|
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,7 @@
|
|||
[
|
||||
{
|
||||
"filename": "logo-black.png",
|
||||
"path": "/assets/images/logo-black.png",
|
||||
"cid": "logo-black.png"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,8 @@
|
|||
<header>
|
||||
<div class="logo">
|
||||
<a href="https://www.verdnatura.es" target="_blank">
|
||||
<img v-bind:src="getEmailSrc('logo-black.png')" alt="VerdNatura"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="topbar"></div>
|
||||
</header>
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
name: 'email-header',
|
||||
props: ['locale']
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
const CssReader = require(`${appPath}/lib/cssReader`);
|
||||
const Stylesheet = require(`${appPath}/core/stylesheet`);
|
||||
|
||||
module.exports = new CssReader([
|
||||
module.exports = new Stylesheet([
|
||||
`${appPath}/common/css/layout.css`,
|
||||
`${appPath}/common/css/report.css`,
|
||||
`${appPath}/common/css/misc.css`,
|
|
@ -0,0 +1,10 @@
|
|||
numPages: Página {{page}} de {{pages}}
|
||||
law:
|
||||
phytosanitary: 'VERDNATURA LEVANTE SL - Pasaporte Fitosanitario R.P. Generalitat
|
||||
Valenciana - Nº Comerciante: ES17462130'
|
||||
privacy: En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección
|
||||
de Datos de Carácter Personal, le comunicamos que los datos personales que facilite
|
||||
se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en
|
||||
todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición,
|
||||
comunicándolo por escrito al domicilio social de la entidad. La finalidad del
|
||||
fichero es la gestión administrativa, contabilidad, y facturación.
|