Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5395-travel.extra-community
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
6d04933fe0
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [2310.01] - 2023-03-23
|
## [2312.01] - 2023-04-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
-
|
-
|
||||||
|
@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Changed
|
### Changed
|
||||||
-
|
-
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
-
|
||||||
|
|
||||||
|
## [2310.01] - 2023-03-23
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Trabajadores -> Control de horario) Ahora se puede confirmar/no confirmar el registro horario de cada semana desde esta sección
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo"
|
- (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo"
|
||||||
- (General) Al pasar el ratón por encima del icono de "Borrar" en un campo, se hacía más grande afectando a la interfaz
|
- (General) Al pasar el ratón por encima del icono de "Borrar" en un campo, se hacía más grande afectando a la interfaz
|
||||||
|
|
|
@ -10,6 +10,7 @@ RUN apt-get update \
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
gnupg2 \
|
gnupg2 \
|
||||||
|
graphicsmagick \
|
||||||
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
|
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
|
||||||
&& apt-get install -y --no-install-recommends nodejs \
|
&& apt-get install -y --no-install-recommends nodejs \
|
||||||
&& npm install -g npm@8.19.2
|
&& npm install -g npm@8.19.2
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('previousLabel', {
|
Self.remoteMethodCtx('previousLabel', {
|
||||||
description: 'Returns the previa label pdf',
|
description: 'Returns the previa label pdf',
|
||||||
|
@ -33,17 +31,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.previousLabel = async(ctx, id) => {
|
Self.previousLabel = (ctx, id) => Self.printReport(ctx, id, 'previa-label');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('previa-label', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="previa-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,215 +0,0 @@
|
||||||
const md5 = require('md5');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
|
||||||
Self.remoteMethodCtx('saveSign', {
|
|
||||||
description: 'Save sign',
|
|
||||||
accessType: 'WRITE',
|
|
||||||
accepts:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
arg: 'signContent',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
description: 'The sign content'
|
|
||||||
}, {
|
|
||||||
arg: 'tickets',
|
|
||||||
type: ['number'],
|
|
||||||
required: true,
|
|
||||||
description: 'The tickets'
|
|
||||||
}, {
|
|
||||||
arg: 'signedTime',
|
|
||||||
type: 'date',
|
|
||||||
description: 'The signed time'
|
|
||||||
}, {
|
|
||||||
arg: 'addressFk',
|
|
||||||
type: 'number',
|
|
||||||
required: true,
|
|
||||||
description: 'The address fk'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: 'Object',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
path: `/saveSign`,
|
|
||||||
verb: 'POST'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function createGestDoc(ticketId, userFk) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
if (!await gestDocExists(ticketId)) {
|
|
||||||
const result = await models.Ticket.findOne({
|
|
||||||
where: {
|
|
||||||
id: ticketId
|
|
||||||
},
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
relation: 'warehouse',
|
|
||||||
scope: {
|
|
||||||
fields: ['id']
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
relation: 'client',
|
|
||||||
scope: {
|
|
||||||
fields: ['name']
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
relation: 'route',
|
|
||||||
scope: {
|
|
||||||
fields: ['id']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
const warehouseFk = result.warehouseFk;
|
|
||||||
const companyFk = result.companyFk;
|
|
||||||
const client = result.client.name;
|
|
||||||
const route = result.route.id;
|
|
||||||
|
|
||||||
const resultDmsType = await models.DmsType.findOne({
|
|
||||||
where: {
|
|
||||||
code: 'Ticket'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const resultDms = await models.Dms.create({
|
|
||||||
dmsTypeFk: resultDmsType.id,
|
|
||||||
reference: ticketId,
|
|
||||||
description: `Ticket ${ticketId} Cliente ${client} Ruta ${route}`,
|
|
||||||
companyFk: companyFk,
|
|
||||||
warehouseFk: warehouseFk,
|
|
||||||
workerFk: userFk
|
|
||||||
});
|
|
||||||
|
|
||||||
return resultDms.insertId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function gestDocExists(ticket) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
const result = await models.TicketDms.findOne({
|
|
||||||
where: {
|
|
||||||
ticketFk: ticket
|
|
||||||
},
|
|
||||||
fields: ['dmsFk']
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const isSigned = await models.Ticket.findOne({
|
|
||||||
where: {
|
|
||||||
id: ticket
|
|
||||||
},
|
|
||||||
fields: ['isSigned']
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isSigned)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
await models.Dms.destroyById(ticket);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function dmsRecover(ticket, signContent) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
await models.DmsRecover.create({
|
|
||||||
ticketFk: ticket,
|
|
||||||
sign: signContent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ticketGestdoc(ticket, dmsFk) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
models.TicketDms.replaceOrCreate({
|
|
||||||
ticketFk: ticket,
|
|
||||||
dmsFk: dmsFk
|
|
||||||
});
|
|
||||||
|
|
||||||
const queryVnTicketSetState = `CALL vn.ticket_setState(?, ?)`;
|
|
||||||
|
|
||||||
await Self.rawSql(queryVnTicketSetState, [ticket, 'DELIVERED']);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateGestdoc(file, ticket) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
models.Dms.updateOne({
|
|
||||||
where: {
|
|
||||||
id: ticket
|
|
||||||
},
|
|
||||||
file: file,
|
|
||||||
contentType: 'image/png'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Self.saveSign = async(ctx, signContent, tickets, signedTime) => {
|
|
||||||
const models = Self.app.models;
|
|
||||||
let tx = await Self.beginTransaction({});
|
|
||||||
try {
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
const dmsDir = `storage/dms`;
|
|
||||||
|
|
||||||
let image = null;
|
|
||||||
|
|
||||||
for (let i = 0; i < tickets.length; i++) {
|
|
||||||
const alertLevel = await models.TicketState.findOne({
|
|
||||||
where: {
|
|
||||||
ticketFk: tickets[i]
|
|
||||||
},
|
|
||||||
fields: ['alertLevel']
|
|
||||||
});
|
|
||||||
|
|
||||||
signedTime ? signedTime != undefined : signedTime = Date.vnNew();
|
|
||||||
|
|
||||||
if (alertLevel >= 2) {
|
|
||||||
let dir;
|
|
||||||
let id = null;
|
|
||||||
let fileName = null;
|
|
||||||
|
|
||||||
if (!await gestDocExists(tickets[i])) {
|
|
||||||
id = await createGestDoc(tickets[i], userId);
|
|
||||||
|
|
||||||
const hashDir = md5(id).substring(0, 3);
|
|
||||||
dir = `${dmsDir}/${hashDir}`;
|
|
||||||
|
|
||||||
if (!fs.existsSync(dir))
|
|
||||||
fs.mkdirSync(dir);
|
|
||||||
|
|
||||||
fileName = `${id}.png`;
|
|
||||||
image = `${dir}/${fileName}`;
|
|
||||||
} else
|
|
||||||
|
|
||||||
if (image != null) {
|
|
||||||
if (!fs.existsSync(dir))
|
|
||||||
dmsRecover(tickets[i], signContent);
|
|
||||||
else {
|
|
||||||
fs.writeFile(image, signContent, 'base64', async function(err) {
|
|
||||||
if (err) {
|
|
||||||
await tx.rollback();
|
|
||||||
return err.message;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
dmsRecover(tickets[i], signContent);
|
|
||||||
|
|
||||||
if (id != null && fileName.length > 0) {
|
|
||||||
ticketGestdoc(tickets[i], id);
|
|
||||||
updateGestdoc(id, fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
|
||||||
|
|
||||||
return 'OK';
|
|
||||||
} catch (err) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw err.message;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -9,17 +9,29 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
"id": true,
|
"id": true,
|
||||||
"type": "number",
|
"type": "number"
|
||||||
"forceId": false
|
|
||||||
},
|
},
|
||||||
"date": {
|
"created": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
},
|
},
|
||||||
"m3":{
|
"longitude":{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"warehouseFk":{
|
"latitude":{
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"dated":{
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"ticketFk":{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"relations": {
|
||||||
|
"ticket": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Ticket",
|
||||||
|
"foreignKey": "ticketFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ module.exports = Self => {
|
||||||
require('../methods/dms/removeFile')(Self);
|
require('../methods/dms/removeFile')(Self);
|
||||||
require('../methods/dms/updateFile')(Self);
|
require('../methods/dms/updateFile')(Self);
|
||||||
require('../methods/dms/deleteTrashFiles')(Self);
|
require('../methods/dms/deleteTrashFiles')(Self);
|
||||||
require('../methods/dms/saveSign')(Self);
|
|
||||||
|
|
||||||
Self.checkRole = async function(ctx, id) {
|
Self.checkRole = async function(ctx, id) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
DROP TABLE `vn`.`dmsRecover`;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`delivery` DROP FOREIGN KEY delivery_FK;
|
||||||
|
ALTER TABLE `vn`.`delivery` DROP COLUMN addressFk;
|
||||||
|
ALTER TABLE `vn`.`delivery` ADD ticketFk INT NOT NULL;
|
||||||
|
ALTER TABLE `vn`.`delivery` ADD CONSTRAINT delivery_ticketFk_FK FOREIGN KEY (`ticketFk`) REFERENCES `vn`.`ticket`(`id`);
|
||||||
|
|
||||||
|
DELETE FROM `salix`.`ACL` WHERE `property` = 'saveSign';
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
|
||||||
|
VALUES
|
||||||
|
('Ticket','saveSign','WRITE','ALLOW','employee');
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS vn.route_getTickets;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`route_getTickets`(vRouteFk INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Pasado un RouteFk devuelve la información
|
||||||
|
* de sus tickets.
|
||||||
|
*
|
||||||
|
* @param vRouteFk
|
||||||
|
*
|
||||||
|
* @select Información de los tickets
|
||||||
|
*/
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
t.id Id,
|
||||||
|
t.clientFk Client,
|
||||||
|
a.id Address,
|
||||||
|
t.packages Packages,
|
||||||
|
a.street AddressName,
|
||||||
|
a.postalCode PostalCode,
|
||||||
|
a.city City,
|
||||||
|
sub2.itemPackingTypeFk PackingType,
|
||||||
|
c.phone ClientPhone,
|
||||||
|
c.mobile ClientMobile,
|
||||||
|
a.phone AddressPhone,
|
||||||
|
a.mobile AddressMobile,
|
||||||
|
d.longitude Longitude,
|
||||||
|
d.latitude Latitude,
|
||||||
|
wm.mediaValue SalePersonPhone,
|
||||||
|
tob.Note Note,
|
||||||
|
t.isSigned Signed
|
||||||
|
FROM ticket t
|
||||||
|
JOIN client c ON t.clientFk = c.id
|
||||||
|
JOIN address a ON t.addressFk = a.id
|
||||||
|
LEFT JOIN delivery d ON t.id = d.ticketFk
|
||||||
|
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
|
||||||
|
LEFT JOIN
|
||||||
|
(SELECT tob.description Note, t.id
|
||||||
|
FROM ticketObservation tob
|
||||||
|
JOIN ticket t ON tob.ticketFk = t.id
|
||||||
|
JOIN observationType ot ON ot.id = tob.observationTypeFk
|
||||||
|
WHERE t.routeFk = vRouteFk
|
||||||
|
AND ot.code = 'delivery'
|
||||||
|
)tob ON tob.id = t.id
|
||||||
|
LEFT JOIN
|
||||||
|
(SELECT sub.ticketFk,
|
||||||
|
CONCAT('(', GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk ORDER BY sub.items DESC SEPARATOR ','), ') ') itemPackingTypeFk
|
||||||
|
FROM (SELECT s.ticketFk , i.itemPackingTypeFk, COUNT(*) items
|
||||||
|
FROM ticket t
|
||||||
|
JOIN sale s ON s.ticketFk = t.id
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
WHERE t.routeFk = vRouteFk
|
||||||
|
GROUP BY t.id,i.itemPackingTypeFk)sub
|
||||||
|
GROUP BY sub.ticketFk
|
||||||
|
) sub2 ON sub2.ticketFk = t.id
|
||||||
|
WHERE t.routeFk = vRouteFk
|
||||||
|
GROUP BY t.id
|
||||||
|
ORDER BY t.priority;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -2825,4 +2825,11 @@ INSERT INTO `vn`.`deviceProductionUser` (`deviceProductionFk`, `userFk`, `create
|
||||||
(1, 1, util.VN_NOW()),
|
(1, 1, util.VN_NOW()),
|
||||||
(3, 3, util.VN_NOW());
|
(3, 3, util.VN_NOW());
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`workerTimeControlMail` (`id`, `workerFk`, `year`, `week`, `state`, `updated`, `sendedCounter`, `reason`)
|
||||||
|
VALUES
|
||||||
|
(1, 9, 2000, 49, 'REVISE', util.VN_NOW(), 1, 'test2'),
|
||||||
|
(2, 9, 2000, 50, 'SENDED', util.VN_NOW(), 1, NULL),
|
||||||
|
(3, 9, 2000, 51, 'CONFIRMED', util.VN_NOW(), 1, NULL),
|
||||||
|
(4, 9, 2001, 1, 'SENDED', util.VN_NOW(), 1, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -81220,3 +81220,4 @@ USE `vn`;
|
||||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
-- Dump completed on 2023-02-21 8:14:30
|
-- Dump completed on 2023-02-21 8:14:30
|
||||||
|
|
||||||
|
|
|
@ -524,7 +524,7 @@ export default {
|
||||||
},
|
},
|
||||||
itemLog: {
|
itemLog: {
|
||||||
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
|
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
|
||||||
fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(2) td.after',
|
fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(4) td.after',
|
||||||
},
|
},
|
||||||
ticketSummary: {
|
ticketSummary: {
|
||||||
header: 'vn-ticket-summary > vn-card > h5',
|
header: 'vn-ticket-summary > vn-card > h5',
|
||||||
|
|
|
@ -59,6 +59,6 @@ describe('Item log path', () => {
|
||||||
const fifthLineCreatedProperty = await page
|
const fifthLineCreatedProperty = await page
|
||||||
.waitToGetProperty(selectors.itemLog.fifthLineCreatedProperty, 'innerText');
|
.waitToGetProperty(selectors.itemLog.fifthLineCreatedProperty, 'innerText');
|
||||||
|
|
||||||
expect(fifthLineCreatedProperty).toEqual('Coral y materiales similares');
|
expect(fifthLineCreatedProperty).toEqual('05080000');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,6 +37,6 @@ describe('Zone descriptor path', () => {
|
||||||
await page.accessToSection('ticket.card.log');
|
await page.accessToSection('ticket.card.log');
|
||||||
const lastChanges = await page.waitToGetProperty(selectors.ticketLog.changes, 'innerText');
|
const lastChanges = await page.waitToGetProperty(selectors.ticketLog.changes, 'innerText');
|
||||||
|
|
||||||
expect(lastChanges).toContain('Arreglar');
|
expect(lastChanges).toContain('1');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,9 +15,9 @@ export default class Calendar extends FormInput {
|
||||||
constructor($element, $scope, vnWeekDays, moment) {
|
constructor($element, $scope, vnWeekDays, moment) {
|
||||||
super($element, $scope);
|
super($element, $scope);
|
||||||
this.weekDays = vnWeekDays.locales;
|
this.weekDays = vnWeekDays.locales;
|
||||||
this.defaultDate = Date.vnNew();
|
|
||||||
this.displayControls = true;
|
this.displayControls = true;
|
||||||
this.moment = moment;
|
this.moment = moment;
|
||||||
|
this.defaultDate = Date.vnNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -207,14 +207,23 @@ export default class Calendar extends FormInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
repeatLast() {
|
repeatLast() {
|
||||||
if (!this.formatDay) return;
|
if (this.formatDay) {
|
||||||
|
const days = this.element.querySelectorAll('.days > .day');
|
||||||
|
for (let i = 0; i < days.length; i++) {
|
||||||
|
this.formatDay({
|
||||||
|
$day: this.days[i],
|
||||||
|
$element: days[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let days = this.element.querySelectorAll('.days > .day');
|
if (this.formatWeek) {
|
||||||
for (let i = 0; i < days.length; i++) {
|
const weeks = this.element.querySelectorAll('.weeks > .day');
|
||||||
this.formatDay({
|
for (const week of weeks) {
|
||||||
$day: this.days[i],
|
this.formatWeek({
|
||||||
$element: days[i]
|
$element: week
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,6 +237,7 @@ ngModule.vnComponent('vnCalendar', {
|
||||||
hasEvents: '&?',
|
hasEvents: '&?',
|
||||||
getClass: '&?',
|
getClass: '&?',
|
||||||
formatDay: '&?',
|
formatDay: '&?',
|
||||||
|
formatWeek: '&?',
|
||||||
displayControls: '<?',
|
displayControls: '<?',
|
||||||
hideYear: '<?',
|
hideYear: '<?',
|
||||||
hideContiguous: '<?',
|
hideContiguous: '<?',
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
const {Report, Email} = require('vn-print');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.printReport = async function(ctx, id, reportName) {
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const params = {lang: ctx.req.getLocale()};
|
||||||
|
|
||||||
|
delete args.ctx;
|
||||||
|
for (const param in args)
|
||||||
|
params[param] = args[param];
|
||||||
|
|
||||||
|
const report = new Report(reportName, params);
|
||||||
|
const stream = await report.toPdfStream();
|
||||||
|
|
||||||
|
let fileName = `${reportName}`;
|
||||||
|
if (id) fileName += `-${id}`;
|
||||||
|
|
||||||
|
return [stream, 'application/pdf', `filename="${fileName}.pdf"`];
|
||||||
|
};
|
||||||
|
|
||||||
|
Self.printEmail = async function(ctx, id, templateName) {
|
||||||
|
const {accessToken} = ctx.req;
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const params = {lang: ctx.req.getLocale()};
|
||||||
|
|
||||||
|
delete args.ctx;
|
||||||
|
for (const param in args)
|
||||||
|
params[param] = args[param];
|
||||||
|
|
||||||
|
params.isPreview = true;
|
||||||
|
params.access_token = accessToken.id;
|
||||||
|
|
||||||
|
const report = new Email(templateName, params);
|
||||||
|
const html = await report.render();
|
||||||
|
|
||||||
|
let fileName = `${templateName}`;
|
||||||
|
if (id) fileName += `-${id}`;
|
||||||
|
|
||||||
|
return [html, 'text/html', `filename=${fileName}.pdf"`];
|
||||||
|
};
|
||||||
|
|
||||||
|
Self.sendTemplate = async function(ctx, templateName) {
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const params = {
|
||||||
|
recipient: args.recipient,
|
||||||
|
lang: ctx.req.getLocale()
|
||||||
|
};
|
||||||
|
|
||||||
|
delete args.ctx;
|
||||||
|
for (const param in args)
|
||||||
|
params[param] = args[param];
|
||||||
|
|
||||||
|
const email = new Email(templateName, params);
|
||||||
|
|
||||||
|
return email.send();
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,3 @@
|
||||||
const pick = require('object.pick');
|
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
|
@ -6,344 +5,11 @@ module.exports = function(Self) {
|
||||||
Self.super_.setup.call(this);
|
Self.super_.setup.call(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.observe('after save', async function(ctx) {
|
|
||||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
||||||
await logInModel(ctx, loopBackContext);
|
|
||||||
});
|
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
const appModels = ctx.Model.app.models;
|
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
|
||||||
const definition = ctx.Model.definition;
|
|
||||||
const options = {};
|
|
||||||
|
|
||||||
// Check for transactions
|
|
||||||
if (ctx.options && ctx.options.transaction)
|
|
||||||
options.transaction = ctx.options.transaction;
|
|
||||||
|
|
||||||
let oldInstance;
|
|
||||||
let newInstance;
|
|
||||||
|
|
||||||
if (ctx.data) {
|
|
||||||
const changes = pick(ctx.currentInstance, Object.keys(ctx.data));
|
|
||||||
newInstance = ctx.data;
|
|
||||||
oldInstance = changes;
|
|
||||||
|
|
||||||
if (ctx.where && !ctx.currentInstance) {
|
|
||||||
const fields = Object.keys(ctx.data);
|
|
||||||
const modelName = definition.name;
|
|
||||||
|
|
||||||
ctx.oldInstances = await appModels[modelName].find({
|
|
||||||
where: ctx.where,
|
|
||||||
fields: fields
|
|
||||||
}, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get changes from created instance
|
|
||||||
if (ctx.isNewInstance)
|
|
||||||
newInstance = ctx.instance.__data;
|
|
||||||
|
|
||||||
ctx.hookState.oldInstance = oldInstance;
|
|
||||||
ctx.hookState.newInstance = newInstance;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.observe('before delete', async function(ctx) {
|
Self.observe('before delete', async function(ctx) {
|
||||||
const appModels = ctx.Model.app.models;
|
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
|
||||||
const definition = ctx.Model.definition;
|
|
||||||
const relations = ctx.Model.relations;
|
|
||||||
|
|
||||||
let options = {};
|
|
||||||
if (ctx.options && ctx.options.transaction)
|
|
||||||
options.transaction = ctx.options.transaction;
|
|
||||||
|
|
||||||
if (ctx.where) {
|
|
||||||
let affectedModel = definition.name;
|
|
||||||
let deletedInstances = await appModels[affectedModel].find({
|
|
||||||
where: ctx.where
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
let relation = definition.settings.log.relation;
|
|
||||||
|
|
||||||
if (relation) {
|
|
||||||
let primaryKey = relations[relation].keyFrom;
|
|
||||||
|
|
||||||
let arrangedDeletedInstances = [];
|
|
||||||
for (let i = 0; i < deletedInstances.length; i++) {
|
|
||||||
if (primaryKey)
|
|
||||||
deletedInstances[i].originFk = deletedInstances[i][primaryKey];
|
|
||||||
let arrangedInstance = await fkToValue(deletedInstances[i], ctx);
|
|
||||||
arrangedDeletedInstances[i] = arrangedInstance;
|
|
||||||
}
|
|
||||||
ctx.hookState.oldInstance = arrangedDeletedInstances;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.observe('after delete', async function(ctx) {
|
|
||||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
||||||
if (ctx.hookState.oldInstance)
|
|
||||||
logDeletedInstances(ctx, loopBackContext);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function logDeletedInstances(ctx, loopBackContext) {
|
|
||||||
const appModels = ctx.Model.app.models;
|
|
||||||
const definition = ctx.Model.definition;
|
|
||||||
let options = {};
|
|
||||||
if (ctx.options && ctx.options.transaction)
|
|
||||||
options.transaction = ctx.options.transaction;
|
|
||||||
|
|
||||||
ctx.hookState.oldInstance.forEach(async instance => {
|
|
||||||
let userFk;
|
|
||||||
if (loopBackContext)
|
|
||||||
userFk = loopBackContext.active.accessToken.userId;
|
|
||||||
|
|
||||||
let changedModelValue = definition.settings.log.changedModelValue;
|
|
||||||
let logRecord = {
|
|
||||||
originFk: instance.originFk,
|
|
||||||
userFk: userFk,
|
|
||||||
action: 'delete',
|
|
||||||
changedModel: definition.name,
|
|
||||||
changedModelId: instance.id,
|
|
||||||
changedModelValue: instance[changedModelValue],
|
|
||||||
oldInstance: instance,
|
|
||||||
newInstance: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
delete instance.originFk;
|
|
||||||
|
|
||||||
let logModel = definition.settings.log.model;
|
|
||||||
await appModels[logModel].create(logRecord, options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get log values from a foreign key
|
|
||||||
async function fkToValue(instance, ctx) {
|
|
||||||
const appModels = ctx.Model.app.models;
|
|
||||||
const relations = ctx.Model.relations;
|
|
||||||
let options = {};
|
|
||||||
|
|
||||||
// Check for transactions
|
|
||||||
if (ctx.options && ctx.options.transaction)
|
|
||||||
options.transaction = ctx.options.transaction;
|
|
||||||
|
|
||||||
const instanceCopy = JSON.parse(JSON.stringify(instance));
|
|
||||||
const result = {};
|
|
||||||
for (const key in instanceCopy) {
|
|
||||||
let value = instanceCopy[key];
|
|
||||||
|
|
||||||
if (value instanceof Object)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (value === undefined) continue;
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
for (let relationName in relations) {
|
|
||||||
const relation = relations[relationName];
|
|
||||||
if (relation.keyFrom == key && key != 'id') {
|
|
||||||
const model = relation.modelTo;
|
|
||||||
const modelName = relation.modelTo.modelName;
|
|
||||||
const properties = model && model.definition.properties;
|
|
||||||
const settings = model && model.definition.settings;
|
|
||||||
|
|
||||||
const recordSet = await appModels[modelName].findById(value, null, options);
|
|
||||||
|
|
||||||
const hasShowField = settings.log && settings.log.showField;
|
|
||||||
let showField = hasShowField && recordSet
|
|
||||||
&& recordSet[settings.log.showField];
|
|
||||||
|
|
||||||
if (!showField) {
|
|
||||||
const showFieldNames = [
|
|
||||||
'name',
|
|
||||||
'description',
|
|
||||||
'code',
|
|
||||||
'nickname'
|
|
||||||
];
|
|
||||||
for (field of showFieldNames) {
|
|
||||||
const propField = properties && properties[field];
|
|
||||||
const recordField = recordSet && recordSet[field];
|
|
||||||
|
|
||||||
if (propField && recordField) {
|
|
||||||
showField = field;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showField && recordSet && recordSet[showField]) {
|
|
||||||
value = recordSet[showField];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = recordSet && recordSet.id || value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result[key] = value;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function logInModel(ctx, loopBackContext) {
|
|
||||||
const appModels = ctx.Model.app.models;
|
|
||||||
const definition = ctx.Model.definition;
|
|
||||||
const defSettings = ctx.Model.definition.settings;
|
|
||||||
const relations = ctx.Model.relations;
|
|
||||||
|
|
||||||
const options = {};
|
|
||||||
if (ctx.options && ctx.options.transaction)
|
|
||||||
options.transaction = ctx.options.transaction;
|
|
||||||
|
|
||||||
let primaryKey;
|
|
||||||
for (let property in definition.properties) {
|
|
||||||
if (definition.properties[property].id) {
|
|
||||||
primaryKey = property;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!primaryKey) throw new Error('Primary key not found');
|
|
||||||
let originId;
|
|
||||||
|
|
||||||
// RELATIONS LOG
|
|
||||||
let changedModelId;
|
|
||||||
|
|
||||||
if (ctx.instance && !defSettings.log.relation) {
|
|
||||||
originId = ctx.instance.id;
|
|
||||||
changedModelId = ctx.instance.id;
|
|
||||||
} else if (defSettings.log.relation) {
|
|
||||||
primaryKey = relations[defSettings.log.relation].keyFrom;
|
|
||||||
|
|
||||||
if (ctx.where && ctx.where[primaryKey])
|
|
||||||
originId = ctx.where[primaryKey];
|
|
||||||
else if (ctx.instance) {
|
|
||||||
originId = ctx.instance[primaryKey];
|
|
||||||
changedModelId = ctx.instance.id;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
originId = ctx.currentInstance.id;
|
|
||||||
changedModelId = ctx.currentInstance.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the changedModelValue to save and the instances changed in case its an updateAll
|
|
||||||
let showField = defSettings.log.showField;
|
|
||||||
let where;
|
|
||||||
if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) {
|
|
||||||
changedModelId = [];
|
|
||||||
where = [];
|
|
||||||
let changedInstances = await appModels[definition.name].find({
|
|
||||||
where: ctx.where,
|
|
||||||
fields: ['id', showField, primaryKey]
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
changedInstances.forEach(element => {
|
|
||||||
where.push(element[showField]);
|
|
||||||
changedModelId.push(element.id);
|
|
||||||
originId = element[primaryKey];
|
|
||||||
});
|
|
||||||
} else if (ctx.hookState.oldInstance)
|
|
||||||
where = ctx.instance[showField];
|
|
||||||
|
|
||||||
// Set oldInstance, newInstance, userFk and action
|
|
||||||
let oldInstance = {};
|
|
||||||
if (ctx.hookState.oldInstance)
|
|
||||||
Object.assign(oldInstance, ctx.hookState.oldInstance);
|
|
||||||
|
|
||||||
let newInstance = {};
|
|
||||||
if (ctx.hookState.newInstance)
|
|
||||||
Object.assign(newInstance, ctx.hookState.newInstance);
|
|
||||||
let userFk;
|
|
||||||
if (loopBackContext)
|
|
||||||
userFk = loopBackContext.active.accessToken.userId;
|
|
||||||
|
|
||||||
let action = setActionType(ctx);
|
|
||||||
|
|
||||||
removeUnloggable(definition, oldInstance);
|
|
||||||
removeUnloggable(definition, newInstance);
|
|
||||||
|
|
||||||
oldInstance = await fkToValue(oldInstance, ctx);
|
|
||||||
newInstance = await fkToValue(newInstance, ctx);
|
|
||||||
|
|
||||||
// Prevent log with no new changes
|
|
||||||
const hasNewChanges = Object.keys(newInstance).length;
|
|
||||||
if (!hasNewChanges) return;
|
|
||||||
|
|
||||||
let logRecord = {
|
|
||||||
originFk: originId,
|
|
||||||
userFk: userFk,
|
|
||||||
action: action,
|
|
||||||
changedModel: definition.name,
|
|
||||||
changedModelId: changedModelId, // Model property with an different data type will throw a NaN error
|
|
||||||
changedModelValue: where,
|
|
||||||
oldInstance: oldInstance,
|
|
||||||
newInstance: newInstance
|
|
||||||
};
|
|
||||||
|
|
||||||
let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx);
|
|
||||||
let logModel = defSettings.log.model;
|
|
||||||
|
|
||||||
await appModels[logModel].create(logsToSave, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes unwanted properties
|
|
||||||
* @param {*} definition Model definition
|
|
||||||
* @param {*} properties Modified object properties
|
|
||||||
*/
|
|
||||||
function removeUnloggable(definition, properties) {
|
|
||||||
const objectCopy = Object.assign({}, properties);
|
|
||||||
const propList = Object.keys(objectCopy);
|
|
||||||
const propDefs = new Map();
|
|
||||||
|
|
||||||
for (let property in definition.properties) {
|
|
||||||
const propertyDef = definition.properties[property];
|
|
||||||
|
|
||||||
propDefs.set(property, propertyDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let property of propList) {
|
|
||||||
const propertyDef = propDefs.get(property);
|
|
||||||
const firstChar = property.substring(0, 1);
|
|
||||||
const isPrivate = firstChar == '$';
|
|
||||||
|
|
||||||
if (isPrivate || !propertyDef)
|
|
||||||
delete properties[property];
|
|
||||||
|
|
||||||
if (!propertyDef) continue;
|
|
||||||
|
|
||||||
if (propertyDef.log === false || isPrivate)
|
|
||||||
delete properties[property];
|
|
||||||
else if (propertyDef.logValue === false)
|
|
||||||
properties[property] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this function retuns all the instances changed in case this is an updateAll
|
|
||||||
function setLogsToSave(changedInstances, changedInstancesIds, logRecord, ctx) {
|
|
||||||
let promises = [];
|
|
||||||
if (changedInstances && typeof changedInstances == 'object') {
|
|
||||||
for (let i = 0; i < changedInstances.length; i++) {
|
|
||||||
logRecord.changedModelId = changedInstancesIds[i];
|
|
||||||
logRecord.changedModelValue = changedInstances[i];
|
|
||||||
if (ctx.oldInstances)
|
|
||||||
logRecord.oldInstance = ctx.oldInstances[i];
|
|
||||||
promises.push(JSON.parse(JSON.stringify(logRecord)));
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
return logRecord;
|
|
||||||
|
|
||||||
return promises;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setActionType(ctx) {
|
|
||||||
let oldInstance = ctx.hookState.oldInstance;
|
|
||||||
let newInstance = ctx.hookState.newInstance;
|
|
||||||
|
|
||||||
if (oldInstance && newInstance)
|
|
||||||
return 'update';
|
|
||||||
else if (!oldInstance && newInstance)
|
|
||||||
return 'insert';
|
|
||||||
|
|
||||||
return 'delete';
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,7 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
require('../methods/vn-model/getSetValues')(Self);
|
require('../methods/vn-model/getSetValues')(Self);
|
||||||
require('../methods/vn-model/getEnumValues')(Self);
|
require('../methods/vn-model/getEnumValues')(Self);
|
||||||
|
require('../methods/vn-model/printService')(Self);
|
||||||
|
|
||||||
Object.assign(Self, {
|
Object.assign(Self, {
|
||||||
setup() {
|
setup() {
|
||||||
|
|
|
@ -270,5 +270,6 @@
|
||||||
"Warehouse inventory not set": "El almacén inventario no está establecido",
|
"Warehouse inventory not set": "El almacén inventario no está establecido",
|
||||||
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
|
"This locker has already been assigned": "Esta taquilla ya ha sido asignada",
|
||||||
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº {{id}}",
|
"Tickets with associated refunds": "No se pueden borrar tickets con abonos asociados. Este ticket está asociado al abono Nº {{id}}",
|
||||||
"Not exist this branch": "La rama no existe"
|
"Not exist this branch": "La rama no existe",
|
||||||
|
"This ticket cannot be signed because it has not been boxed": "Este ticket no puede firmarse porque no ha sido encajado"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,41 @@ const mysql = require('mysql');
|
||||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||||
const MySQL = require('loopback-connector-mysql').MySQL;
|
const MySQL = require('loopback-connector-mysql').MySQL;
|
||||||
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
|
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
|
||||||
|
const Transaction = require('loopback-connector').Transaction;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const limitSet = new Set([
|
||||||
|
'save',
|
||||||
|
'updateOrCreate',
|
||||||
|
'replaceOrCreate',
|
||||||
|
'replaceById',
|
||||||
|
'update'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const opOpts = {
|
||||||
|
update: [
|
||||||
|
'update',
|
||||||
|
'replaceById',
|
||||||
|
// |insert
|
||||||
|
'save',
|
||||||
|
'updateOrCreate',
|
||||||
|
'replaceOrCreate'
|
||||||
|
],
|
||||||
|
delete: [
|
||||||
|
'destroy',
|
||||||
|
'destroyAll'
|
||||||
|
],
|
||||||
|
insert: [
|
||||||
|
'create'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const opMap = new Map();
|
||||||
|
for (const op in opOpts) {
|
||||||
|
for (const met of opOpts[op])
|
||||||
|
opMap.set(met, op);
|
||||||
|
}
|
||||||
|
|
||||||
class VnMySQL extends MySQL {
|
class VnMySQL extends MySQL {
|
||||||
/**
|
/**
|
||||||
* Promisified version of execute().
|
* Promisified version of execute().
|
||||||
|
@ -219,6 +252,277 @@ class VnMySQL extends MySQL {
|
||||||
this.makePagination(filter)
|
this.makePagination(filter)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create(model, data, opts, cb) {
|
||||||
|
const ctx = {data};
|
||||||
|
this.invokeMethod('create',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAll(model, data, opts, cb) {
|
||||||
|
const ctx = {data};
|
||||||
|
this.invokeMethod('createAll',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(model, data, opts, cb) {
|
||||||
|
const ctx = {data};
|
||||||
|
this.invokeMethod('save',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOrCreate(model, data, opts, cb) {
|
||||||
|
const ctx = {data};
|
||||||
|
this.invokeMethod('updateOrCreate',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceOrCreate(model, data, opts, cb) {
|
||||||
|
const ctx = {data};
|
||||||
|
this.invokeMethod('replaceOrCreate',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyAll(model, where, opts, cb) {
|
||||||
|
const ctx = {where};
|
||||||
|
this.invokeMethod('destroyAll',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(model, where, data, opts, cb) {
|
||||||
|
const ctx = {where, data};
|
||||||
|
this.invokeMethod('update',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceById(model, id, data, opts, cb) {
|
||||||
|
const ctx = {id, data};
|
||||||
|
this.invokeMethod('replaceById',
|
||||||
|
arguments, model, ctx, opts, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoggable(model) {
|
||||||
|
const Model = this.getModelDefinition(model).model;
|
||||||
|
const settings = Model.definition.settings;
|
||||||
|
return settings.base && settings.base === 'Loggable';
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeMethod(method, args, model, ctx, opts, cb) {
|
||||||
|
if (!this.isLoggable(model))
|
||||||
|
return super[method].apply(this, args);
|
||||||
|
|
||||||
|
this.invokeMethodP(method, [...args], model, ctx, opts)
|
||||||
|
.then(res => cb(...res), cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
async invokeMethodP(method, args, model, ctx, opts) {
|
||||||
|
const Model = this.getModelDefinition(model).model;
|
||||||
|
const settings = Model.definition.settings;
|
||||||
|
let tx;
|
||||||
|
if (!opts.transaction) {
|
||||||
|
tx = await Transaction.begin(this, {});
|
||||||
|
opts = Object.assign({transaction: tx, httpCtx: opts.httpCtx}, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch old values (update|delete) or login
|
||||||
|
let where, id, data, idName, limit, op, oldInstances, newInstances;
|
||||||
|
const hasGrabUser = settings.log && settings.log.grabUser;
|
||||||
|
if(hasGrabUser){
|
||||||
|
const userId = opts.httpCtx && opts.httpCtx.active.accessToken.userId;
|
||||||
|
const user = await Model.app.models.Account.findById(userId, {fields: ['name']}, opts);
|
||||||
|
await this.executeP(`CALL account.myUser_loginWithName(?)`, [user.name], opts);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
where = ctx.where;
|
||||||
|
id = ctx.id;
|
||||||
|
data = ctx.data;
|
||||||
|
idName = this.idName(model);
|
||||||
|
|
||||||
|
limit = limitSet.has(method);
|
||||||
|
|
||||||
|
op = opMap.get(method);
|
||||||
|
|
||||||
|
if (!where) {
|
||||||
|
if (id) where = {[idName]: id};
|
||||||
|
else where = {[idName]: data[idName]};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch old values
|
||||||
|
switch (op) {
|
||||||
|
case 'update':
|
||||||
|
case 'delete':
|
||||||
|
// Single entity operation
|
||||||
|
const stmt = this.buildSelectStmt(op, data, idName, model, where, limit);
|
||||||
|
stmt.merge(`FOR UPDATE`);
|
||||||
|
oldInstances = await this.executeStmt(stmt, opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await new Promise(resolve => {
|
||||||
|
const fnArgs = args.slice(0, -2);
|
||||||
|
fnArgs.push(opts, (...args) => resolve(args));
|
||||||
|
super[method].apply(this, fnArgs);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(hasGrabUser)
|
||||||
|
await this.executeP(`CALL account.myUser_logout()`, null, opts);
|
||||||
|
else {
|
||||||
|
// Fetch new values
|
||||||
|
const ids = [];
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case 'insert':
|
||||||
|
case 'update': {
|
||||||
|
switch (method) {
|
||||||
|
case 'createAll':
|
||||||
|
for (const row of res[1])
|
||||||
|
ids.push(row[idName]);
|
||||||
|
break;
|
||||||
|
case 'create':
|
||||||
|
ids.push(res[1]);
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
if (data[idName] != null)
|
||||||
|
ids.push(data[idName]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newWhere = ids.length ? {[idName]: ids} : where;
|
||||||
|
|
||||||
|
const stmt = this.buildSelectStmt(op, data, idName, model, newWhere, limit);
|
||||||
|
newInstances = await this.executeStmt(stmt, opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.createLogRecord(oldInstances, newInstances, model, opts);
|
||||||
|
}
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
return res;
|
||||||
|
} catch (err) {
|
||||||
|
if (tx) tx.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildSelectStmt(op, data, idName, model, where, limit) {
|
||||||
|
const Model = this.getModelDefinition(model).model;
|
||||||
|
const properties = Object.keys(Model.definition.properties);
|
||||||
|
|
||||||
|
const fields = data ? Object.keys(data) : [];
|
||||||
|
if (op == 'delete')
|
||||||
|
properties.forEach(property => fields.push(property));
|
||||||
|
else {
|
||||||
|
const log = Model.definition.settings.log;
|
||||||
|
fields.push(idName);
|
||||||
|
if (log.relation) fields.push(Model.relations[log.relation].keyFrom);
|
||||||
|
if (log.showField) fields.push(log.showField);
|
||||||
|
else {
|
||||||
|
const showFieldNames = ['name', 'description', 'code', 'nickname'];
|
||||||
|
for (const field of showFieldNames) {
|
||||||
|
if (properties.includes(field)) {
|
||||||
|
log.showField = field;
|
||||||
|
fields.push(field);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const stmt = new ParameterizedSQL(
|
||||||
|
'SELECT ' +
|
||||||
|
this.buildColumnNames(model, {fields}) +
|
||||||
|
' FROM ' +
|
||||||
|
this.tableEscaped(model)
|
||||||
|
);
|
||||||
|
stmt.merge(this.buildWhere(model, where));
|
||||||
|
if (limit) stmt.merge(`LIMIT 1`);
|
||||||
|
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createLogRecord(oldInstances, newInstances, model, opts) {
|
||||||
|
function setActionType() {
|
||||||
|
if (oldInstances && newInstances)
|
||||||
|
return 'update';
|
||||||
|
else if (!oldInstances && newInstances)
|
||||||
|
return 'insert';
|
||||||
|
return 'delete';
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = setActionType();
|
||||||
|
if (!newInstances && action != 'delete') return;
|
||||||
|
|
||||||
|
const Model = this.getModelDefinition(model).model;
|
||||||
|
const models = Model.app.models;
|
||||||
|
const definition = Model.definition;
|
||||||
|
const log = definition.settings.log;
|
||||||
|
|
||||||
|
const primaryKey = this.idName(model);
|
||||||
|
const originRelation = log.relation;
|
||||||
|
const originFkField = originRelation
|
||||||
|
? Model.relations[originRelation].keyFrom
|
||||||
|
: primaryKey;
|
||||||
|
|
||||||
|
// Prevent adding logs when deleting a principal entity (Client, Zone...)
|
||||||
|
if (action == 'delete' && !originRelation) return;
|
||||||
|
|
||||||
|
function map(instances) {
|
||||||
|
const map = new Map();
|
||||||
|
if (!instances) return;
|
||||||
|
for (const instance of instances)
|
||||||
|
map.set(instance[primaryKey], instance);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changedModel = definition.name;
|
||||||
|
const userFk = opts.httpCtx && opts.httpCtx.active.accessToken.userId;
|
||||||
|
const oldMap = map(oldInstances);
|
||||||
|
const newMap = map(newInstances);
|
||||||
|
const ids = (oldMap || newMap).keys();
|
||||||
|
|
||||||
|
const logEntries = [];
|
||||||
|
|
||||||
|
function insertValuesLogEntry(logEntry, instance) {
|
||||||
|
logEntry.originFk = instance[originFkField];
|
||||||
|
logEntry.changedModelId = instance[primaryKey];
|
||||||
|
if (log.showField) logEntry.changedModelValue = instance[log.showField];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of ids) {
|
||||||
|
const oldI = oldMap && oldMap.get(id);
|
||||||
|
const newI = newMap && newMap.get(id);
|
||||||
|
|
||||||
|
const logEntry = {
|
||||||
|
action,
|
||||||
|
userFk,
|
||||||
|
changedModel,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (newI) {
|
||||||
|
insertValuesLogEntry(logEntry, newI);
|
||||||
|
// Delete unchanged properties
|
||||||
|
if (oldI) {
|
||||||
|
Object.keys(oldI).forEach(prop => {
|
||||||
|
const hasChanges = oldI[prop] instanceof Date ?
|
||||||
|
oldI[prop]?.getTime() != newI[prop]?.getTime() :
|
||||||
|
oldI[prop] != newI[prop];
|
||||||
|
|
||||||
|
if (!hasChanges) {
|
||||||
|
delete oldI[prop];
|
||||||
|
delete newI[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
insertValuesLogEntry(logEntry, oldI);
|
||||||
|
|
||||||
|
logEntry.oldInstance = oldI;
|
||||||
|
logEntry.newInstance = newI;
|
||||||
|
logEntries.push(logEntry);
|
||||||
|
}
|
||||||
|
await models[log.model].create(logEntries, opts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.VnMySQL = VnMySQL;
|
exports.VnMySQL = VnMySQL;
|
||||||
|
|
|
@ -91,7 +91,11 @@ exports.getChanges = (original, changes) => {
|
||||||
const isPrivate = firstChar == '$';
|
const isPrivate = firstChar == '$';
|
||||||
if (isPrivate) return;
|
if (isPrivate) return;
|
||||||
|
|
||||||
if (changes[property] != original[property]) {
|
const hasChanges = original[property] instanceof Date ?
|
||||||
|
changes[property]?.getTime() != original[property]?.getTime() :
|
||||||
|
changes[property] != original[property];
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
newChanges[property] = changes[property];
|
newChanges[property] = changes[property];
|
||||||
|
|
||||||
if (original[property] != undefined)
|
if (original[property] != undefined)
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const { Report } = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('claimPickupPdf', {
|
Self.remoteMethodCtx('claimPickupPdf', {
|
||||||
description: 'Returns the claim pickup order pdf',
|
description: 'Returns the claim pickup order pdf',
|
||||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.claimPickupPdf = async(ctx, id) => {
|
Self.claimPickupPdf = (ctx, id) => Self.printReport(ctx, id, 'claim-pickup-order');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('claim-pickup-order', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -86,7 +86,20 @@
|
||||||
icon="icon-ticket">
|
icon="icon-ticket">
|
||||||
</vn-quick-link>
|
</vn-quick-link>
|
||||||
</div>
|
</div>
|
||||||
<div ng-transclude="btnThree"></div>
|
<div ng-transclude="btnThree">
|
||||||
|
<vn-quick-link
|
||||||
|
tooltip="Sale tracking"
|
||||||
|
state="['ticket.card.saleTracking', {id: $ctrl.claim.ticketFk}]"
|
||||||
|
icon="assignment">
|
||||||
|
</vn-quick-link>
|
||||||
|
</div>
|
||||||
|
<div ng-transclude="btnFour">
|
||||||
|
<vn-quick-link
|
||||||
|
tooltip="Ticket tracking"
|
||||||
|
state="['ticket.card.tracking.index', {id: $ctrl.claim.ticketFk}]"
|
||||||
|
icon="icon-eye">
|
||||||
|
</vn-quick-link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</slot-body>
|
</slot-body>
|
||||||
</vn-descriptor-content>
|
</vn-descriptor-content>
|
||||||
|
|
|
@ -18,3 +18,5 @@ Claim deleted!: Reclamación eliminada!
|
||||||
claim: reclamación
|
claim: reclamación
|
||||||
Photos: Fotos
|
Photos: Fotos
|
||||||
Go to the claim: Ir a la reclamación
|
Go to the claim: Ir a la reclamación
|
||||||
|
Sale tracking: Líneas preparadas
|
||||||
|
Ticket tracking: Estados del ticket
|
||||||
|
|
|
@ -51,19 +51,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.campaignMetricsEmail = async ctx => {
|
Self.campaignMetricsEmail = ctx => Self.sendTemplate(ctx, 'campaign-metrics');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('campaign-metrics', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('campaignMetricsPdf', {
|
Self.remoteMethodCtx('campaignMetricsPdf', {
|
||||||
description: 'Returns the campaign metrics pdf',
|
description: 'Returns the campaign metrics pdf',
|
||||||
|
@ -50,17 +48,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.campaignMetricsPdf = async(ctx, id) => {
|
Self.campaignMetricsPdf = (ctx, id) => Self.printReport(ctx, id, 'campaign-metrics');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('campaign-metrics', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,19 +46,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.clientDebtStatementEmail = async ctx => {
|
Self.clientDebtStatementEmail = ctx => Self.sendTemplate(ctx, 'client-debt-statement');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('client-debt-statement', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('clientDebtStatementHtml', {
|
Self.remoteMethodCtx('clientDebtStatementHtml', {
|
||||||
description: 'Returns the client debt statement email preview',
|
description: 'Returns the client debt statement email preview',
|
||||||
|
@ -45,21 +43,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.clientDebtStatementHtml = async(ctx, id) => {
|
Self.clientDebtStatementHtml = (ctx, id) => Self.printEmail(ctx, id, 'client-debt-statement');
|
||||||
const {accessToken} = ctx.req;
|
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
params.isPreview = true;
|
|
||||||
params.access_token = accessToken.id;
|
|
||||||
|
|
||||||
const report = new Email('client-debt-statement', params);
|
|
||||||
const html = await report.render();
|
|
||||||
|
|
||||||
return [html, 'text/html', `filename="mail-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('clientDebtStatementPdf', {
|
Self.remoteMethodCtx('clientDebtStatementPdf', {
|
||||||
description: 'Returns the client debt statement pdf',
|
description: 'Returns the client debt statement pdf',
|
||||||
|
@ -45,17 +43,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.clientDebtStatementPdf = async(ctx, id) => {
|
Self.clientDebtStatementPdf = (ctx, id) => Self.printReport(ctx, id, 'client-debt-statement');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('client-debt-statement', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('clientWelcomeEmail', {
|
Self.remoteMethodCtx('clientWelcomeEmail', {
|
||||||
description: 'Sends the client welcome email with an attached PDF',
|
description: 'Sends the client welcome email with an attached PDF',
|
||||||
|
@ -41,19 +39,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.clientWelcomeEmail = async ctx => {
|
Self.clientWelcomeEmail = ctx => Self.sendTemplate(ctx, 'client-welcome');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('client-welcome', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('clientWelcomeHtml', {
|
Self.remoteMethodCtx('clientWelcomeHtml', {
|
||||||
description: 'Returns the client welcome email preview',
|
description: 'Returns the client welcome email preview',
|
||||||
|
@ -40,19 +38,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.clientWelcomeHtml = async(ctx, id) => {
|
Self.clientWelcomeHtml = (ctx, id) => Self.printEmail(ctx, id, 'client-welcome');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
params.isPreview = true;
|
|
||||||
|
|
||||||
const report = new Email('client-welcome', params);
|
|
||||||
const html = await report.render();
|
|
||||||
|
|
||||||
return [html, 'text/html', `filename="mail-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('clientCreditEmail', {
|
Self.remoteMethodCtx('clientCreditEmail', {
|
||||||
description: 'Sends the credit request email with an attached PDF',
|
description: 'Sends the credit request email with an attached PDF',
|
||||||
|
@ -10,7 +8,7 @@ module.exports = Self => {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true,
|
required: true,
|
||||||
description: 'The client id',
|
description: 'The client id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'recipient',
|
arg: 'recipient',
|
||||||
|
@ -22,38 +20,25 @@ module.exports = Self => {
|
||||||
arg: 'replyTo',
|
arg: 'replyTo',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The sender email to reply to',
|
description: 'The sender email to reply to',
|
||||||
required: false
|
required: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'recipientId',
|
arg: 'recipientId',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'The recipient id to send to the recipient preferred language',
|
description:
|
||||||
required: false
|
'The recipient id to send to the recipient preferred language',
|
||||||
}
|
required: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: ['object'],
|
type: ['object'],
|
||||||
root: true
|
root: true,
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
path: '/:id/credit-request-email',
|
path: '/:id/credit-request-email',
|
||||||
verb: 'POST'
|
verb: 'POST',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.clientCreditEmail = async ctx => {
|
Self.clientCreditEmail = ctx => Self.sendTemplate(ctx, 'credit-request');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('credit-request', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('creditRequestHtml', {
|
Self.remoteMethodCtx('creditRequestHtml', {
|
||||||
description: 'Returns the credit request email preview',
|
description: 'Returns the credit request email preview',
|
||||||
|
@ -40,21 +38,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.creditRequestHtml = async(ctx, id) => {
|
Self.creditRequestHtml = (ctx, id) => Self.printEmail(ctx, id, 'credit-request');
|
||||||
const {accessToken} = ctx.req;
|
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
params.isPreview = true;
|
|
||||||
params.access_token = accessToken.id;
|
|
||||||
|
|
||||||
const report = new Email('credit-request', params);
|
|
||||||
const html = await report.render();
|
|
||||||
|
|
||||||
return [html, 'text/html', `filename="mail-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('creditRequestPdf', {
|
Self.remoteMethodCtx('creditRequestPdf', {
|
||||||
description: 'Returns the credit request pdf',
|
description: 'Returns the credit request pdf',
|
||||||
|
@ -40,17 +38,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.creditRequestPdf = async(ctx, id) => {
|
Self.creditRequestPdf = (ctx, id) => Self.printReport(ctx, id, 'credit-request');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('credit-request', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,7 +80,7 @@ module.exports = function(Self) {
|
||||||
const data = await Self.rawSql(query, [id, date], myOptions);
|
const data = await Self.rawSql(query, [id, date], myOptions);
|
||||||
|
|
||||||
client.debt = data[0].debt;
|
client.debt = data[0].debt;
|
||||||
client.unpaid = await Self.app.models.ClientUnpaid.findOne({id}, myOptions);
|
client.unpaid = await Self.app.models.ClientUnpaid.findById(id, null, myOptions);
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('incotermsAuthorizationEmail', {
|
Self.remoteMethodCtx('incotermsAuthorizationEmail', {
|
||||||
description: 'Sends the incoterms authorization email with an attached PDF',
|
description: 'Sends the incoterms authorization email with an attached PDF',
|
||||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.incotermsAuthorizationEmail = async ctx => {
|
Self.incotermsAuthorizationEmail = ctx => Self.sendTemplate(ctx, 'incoterms-authorization');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('incoterms-authorization', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('incotermsAuthorizationHtml', {
|
Self.remoteMethodCtx('incotermsAuthorizationHtml', {
|
||||||
description: 'Returns the incoterms authorization email preview',
|
description: 'Returns the incoterms authorization email preview',
|
||||||
|
@ -46,21 +44,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.incotermsAuthorizationHtml = async(ctx, id) => {
|
Self.incotermsAuthorizationHtml = (ctx, id) => Self.printEmail(ctx, id, 'incoterms-authorization');
|
||||||
const {accessToken} = ctx.req;
|
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
params.isPreview = true;
|
|
||||||
params.access_token = accessToken.id;
|
|
||||||
|
|
||||||
const report = new Email('incoterms-authorization', params);
|
|
||||||
const html = await report.render();
|
|
||||||
|
|
||||||
return [html, 'text/html', `filename="mail-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('incotermsAuthorizationPdf', {
|
Self.remoteMethodCtx('incotermsAuthorizationPdf', {
|
||||||
description: 'Returns the incoterms authorization pdf',
|
description: 'Returns the incoterms authorization pdf',
|
||||||
|
@ -46,17 +44,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.incotermsAuthorizationPdf = async(ctx, id) => {
|
Self.incotermsAuthorizationPdf = (ctx, id) => Self.printReport(ctx, id, 'incoterms-authorization');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('incoterms-authorization', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('letterDebtorNdEmail', {
|
Self.remoteMethodCtx('letterDebtorNdEmail', {
|
||||||
description: 'Sends the second debtor letter email with an attached PDF',
|
description: 'Sends the second debtor letter email with an attached PDF',
|
||||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.letterDebtorNdEmail = async ctx => {
|
Self.letterDebtorNdEmail = ctx => Self.sendTemplate(ctx, 'letter-debtor-nd');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('letter-debtor-nd', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('letterDebtorNdHtml', {
|
Self.remoteMethodCtx('letterDebtorNdHtml', {
|
||||||
description: 'Returns the second letter debtor email preview',
|
description: 'Returns the second letter debtor email preview',
|
||||||
|
@ -46,21 +44,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.letterDebtorNdHtml = async(ctx, id) => {
|
Self.letterDebtorNdHtml = (ctx, id) => Self.printEmail(ctx, id, 'letter-debtor-nd');
|
||||||
const {accessToken} = ctx.req;
|
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
params.isPreview = true;
|
|
||||||
params.access_token = accessToken.id;
|
|
||||||
|
|
||||||
const report = new Email('letter-debtor-nd', params);
|
|
||||||
const html = await report.render();
|
|
||||||
|
|
||||||
return [html, 'text/html', `filename="mail-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('letterDebtorPdf', {
|
Self.remoteMethodCtx('letterDebtorPdf', {
|
||||||
description: 'Returns the letter debtor pdf',
|
description: 'Returns the letter debtor pdf',
|
||||||
|
@ -46,17 +44,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.letterDebtorPdf = async(ctx, id) => {
|
Self.letterDebtorPdf = (ctx, id) => Self.printReport(ctx, id, 'letter-debtor');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('letter-debtor', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('letterDebtorStEmail', {
|
Self.remoteMethodCtx('letterDebtorStEmail', {
|
||||||
description: 'Sends the printer setup email with an attached PDF',
|
description: 'Sends the printer setup email with an attached PDF',
|
||||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.letterDebtorStEmail = async ctx => {
|
Self.letterDebtorStEmail = ctx => Self.sendTemplate(ctx, 'letter-debtor-st');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('letter-debtor-st', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('letterDebtorStHtml', {
|
Self.remoteMethodCtx('letterDebtorStHtml', {
|
||||||
description: 'Returns the letter debtor email preview',
|
description: 'Returns the letter debtor email preview',
|
||||||
|
@ -46,21 +44,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.letterDebtorStHtml = async(ctx, id) => {
|
Self.letterDebtorStHtml = (ctx, id) => Self.printEmail(ctx, id, 'letter-debtor-st');
|
||||||
const {accessToken} = ctx.req;
|
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
params.isPreview = true;
|
|
||||||
params.access_token = accessToken.id;
|
|
||||||
|
|
||||||
const report = new Email('letter-debtor-st', params);
|
|
||||||
const html = await report.render();
|
|
||||||
|
|
||||||
return [html, 'text/html', `filename="mail-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('printerSetupEmail', {
|
Self.remoteMethodCtx('printerSetupEmail', {
|
||||||
description: 'Sends the printer setup email with an attached PDF',
|
description: 'Sends the printer setup email with an attached PDF',
|
||||||
|
@ -41,19 +39,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.printerSetupEmail = async ctx => {
|
Self.printerSetupEmail = ctx => Self.sendTemplate(ctx, 'printer-setup');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('printer-setup', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('printerSetupHtml', {
|
Self.remoteMethodCtx('printerSetupHtml', {
|
||||||
description: 'Returns the printer setup email preview',
|
description: 'Returns the printer setup email preview',
|
||||||
|
@ -40,19 +38,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.printerSetupHtml = async(ctx, id) => {
|
Self.printerSetupHtml = (ctx, id) => Self.printEmail(ctx, id, 'printer-setup');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
params.isPreview = true;
|
|
||||||
|
|
||||||
const report = new Email('printer-setup', params);
|
|
||||||
const html = await report.render();
|
|
||||||
|
|
||||||
return [html, 'text/html', `filename="mail-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('sepaCoreEmail', {
|
Self.remoteMethodCtx('sepaCoreEmail', {
|
||||||
description: 'Sends the campaign metrics email with an attached PDF',
|
description: 'Sends the campaign metrics email with an attached PDF',
|
||||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.sepaCoreEmail = async ctx => {
|
Self.sepaCoreEmail = ctx => Self.sendTemplate(ctx, 'sepa-core');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('sepa-core', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const { Report } = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('balanceCompensationPdf', {
|
Self.remoteMethodCtx('balanceCompensationPdf', {
|
||||||
description: 'Returns the the debit balances compensation pdf',
|
description: 'Returns the the debit balances compensation pdf',
|
||||||
|
@ -10,7 +8,7 @@ module.exports = Self => {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
required: true,
|
required: true,
|
||||||
description: 'The receipt id',
|
description: 'The receipt id',
|
||||||
http: { source: 'path' }
|
http: {source: 'path'}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: [
|
returns: [
|
||||||
|
@ -34,17 +32,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.balanceCompensationPdf = async(ctx, id) => {
|
Self.balanceCompensationPdf = (ctx, id) => Self.printReport(ctx, id, 'balance-compensation');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('balance-compensation', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('receiptPdf', {
|
Self.remoteMethodCtx('receiptPdf', {
|
||||||
description: 'Returns the receipt pdf',
|
description: 'Returns the receipt pdf',
|
||||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.receiptPdf = async(ctx, id) => {
|
Self.receiptPdf = (ctx, id) => Self.printReport(ctx, id, 'receipt');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('receipt', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -279,6 +279,18 @@ module.exports = Self => {
|
||||||
// Credit changes
|
// Credit changes
|
||||||
if (changes.credit !== undefined)
|
if (changes.credit !== undefined)
|
||||||
await Self.changeCredit(ctx, finalState, changes);
|
await Self.changeCredit(ctx, finalState, changes);
|
||||||
|
|
||||||
|
const oldInstance = {};
|
||||||
|
if (!ctx.isNewInstance) {
|
||||||
|
const newProps = Object.keys(changes);
|
||||||
|
Object.keys(orgData.__data).forEach(prop => {
|
||||||
|
if (newProps.includes(prop))
|
||||||
|
oldInstance[prop] = orgData[prop];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.hookState.oldInstance = oldInstance;
|
||||||
|
ctx.hookState.newInstance = changes;
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.observe('after save', async ctx => {
|
Self.observe('after save', async ctx => {
|
||||||
|
|
|
@ -62,6 +62,11 @@
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Bank",
|
"model": "Bank",
|
||||||
"foreignKey": "bankFk"
|
"foreignKey": "bankFk"
|
||||||
|
},
|
||||||
|
"supplier": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Supplier",
|
||||||
|
"foreignKey": "companyFk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -70,11 +70,12 @@
|
||||||
icon="icon-no036"
|
icon="icon-no036"
|
||||||
ng-if="$ctrl.client.isTaxDataChecked == false">
|
ng-if="$ctrl.client.isTaxDataChecked == false">
|
||||||
</vn-icon>
|
</vn-icon>
|
||||||
<vn-icon
|
<vn-icon-button
|
||||||
vn-tooltip="{{$ctrl.clientUnpaid()}}"
|
vn-tooltip="{{$ctrl.clientUnpaid()}}"
|
||||||
icon="icon-clientUnpaid"
|
icon="icon-clientUnpaid"
|
||||||
|
ui-sref="client.card.unpaid"
|
||||||
ng-if="$ctrl.client.unpaid">
|
ng-if="$ctrl.client.unpaid">
|
||||||
</vn-icon>
|
</vn-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="quicklinks">
|
<div class="quicklinks">
|
||||||
<div ng-transclude="btnOne">
|
<div ng-transclude="btnOne">
|
||||||
|
|
|
@ -46,8 +46,9 @@ class Controller extends Descriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
clientUnpaid() {
|
clientUnpaid() {
|
||||||
return this.$t(`Unpaid Dated`, {dated: this.client.unpaid.dated}) +
|
return this.$t(`Unpaid`) + '<br/>'
|
||||||
'<br/>' + this.$t(`Unpaid Amount`, {amount: this.client.unpaid.amount});
|
+ this.$t(`Unpaid Dated`, {dated: this.client.unpaid.dated}) + '<br/>'
|
||||||
|
+ this.$t(`Unpaid Amount`, {amount: this.client.unpaid.amount});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</vn-watcher>
|
</vn-watcher>
|
||||||
<form
|
<form
|
||||||
name="form"
|
name="form"
|
||||||
ng-submit="watcher.submit()"
|
ng-submit="$ctrl.onSubmit()"
|
||||||
class="vn-w-md">
|
class="vn-w-md">
|
||||||
<vn-card class="vn-pa-lg">
|
<vn-card class="vn-pa-lg">
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
|
|
|
@ -6,9 +6,17 @@ export default class Controller extends Section {
|
||||||
if (hasData && !this.clientUnpaid.dated)
|
if (hasData && !this.clientUnpaid.dated)
|
||||||
this.clientUnpaid.dated = Date.vnNew();
|
this.clientUnpaid.dated = Date.vnNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
this.$.watcher.submit()
|
||||||
|
.then(() => this.card.reload());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.vnComponent('vnClientUnpaid', {
|
ngModule.vnComponent('vnClientUnpaid', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
controller: Controller
|
controller: Controller,
|
||||||
|
require: {
|
||||||
|
card: '^vnClientCard'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('entryOrderPdf', {
|
Self.remoteMethodCtx('entryOrderPdf', {
|
||||||
description: 'Returns the entry order pdf',
|
description: 'Returns the entry order pdf',
|
||||||
|
@ -38,17 +36,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.entryOrderPdf = async(ctx, id) => {
|
Self.entryOrderPdf = (ctx, id) => Self.printReport(ctx, id, 'entry-order');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('entry-order', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('invoiceInEmail', {
|
Self.remoteMethodCtx('invoiceInEmail', {
|
||||||
description: 'Sends the invoice in email with an attached PDF',
|
description: 'Sends the invoice in email with an attached PDF',
|
||||||
|
@ -35,19 +33,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.invoiceInEmail = async ctx => {
|
Self.invoiceInEmail = ctx => Self.sendTemplate(ctx, 'invoiceIn');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('invoiceIn', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('invoiceInPdf', {
|
Self.remoteMethodCtx('invoiceInPdf', {
|
||||||
description: 'Returns the invoiceIn pdf',
|
description: 'Returns the invoiceIn pdf',
|
||||||
|
@ -34,17 +32,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.invoiceInPdf = async(ctx, id) => {
|
Self.invoiceInPdf = (ctx, id) => Self.printReport(ctx, id, 'invoiceIn');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
delete args.ctx;
|
|
||||||
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('invoiceIn', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('exportationPdf', {
|
Self.remoteMethodCtx('exportationPdf', {
|
||||||
description: 'Returns the exportation pdf',
|
description: 'Returns the exportation pdf',
|
||||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.exportationPdf = async(ctx, reference) => {
|
Self.exportationPdf = (ctx, reference) => Self.printReport(ctx, reference, 'exportation');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('exportation', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${reference}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
const axios = require('axios');
|
||||||
|
const uuid = require('uuid');
|
||||||
|
const fs = require('fs/promises');
|
||||||
|
const { createWriteStream } = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const gm = require('gm');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('download', {
|
||||||
|
description: 'Processes the image download queue',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
http: {
|
||||||
|
path: `/download`,
|
||||||
|
verb: 'POST',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.download = async () => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const tempContainer = await models.TempContainer.container(
|
||||||
|
'salix-image'
|
||||||
|
);
|
||||||
|
const tempPath = path.join(
|
||||||
|
tempContainer.client.root,
|
||||||
|
tempContainer.name
|
||||||
|
);
|
||||||
|
const maxAttempts = 3;
|
||||||
|
const collectionName = 'catalog';
|
||||||
|
|
||||||
|
const tx = await Self.beginTransaction({});
|
||||||
|
|
||||||
|
let tempFilePath;
|
||||||
|
let queueRow;
|
||||||
|
try {
|
||||||
|
const myOptions = { transaction: tx };
|
||||||
|
|
||||||
|
queueRow = await Self.findOne(
|
||||||
|
{
|
||||||
|
fields: ['id', 'itemFk', 'url', 'attempts'],
|
||||||
|
where: {
|
||||||
|
url: { neq: null },
|
||||||
|
attempts: {
|
||||||
|
lt: maxAttempts,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
order: 'priority, attempts, updated',
|
||||||
|
},
|
||||||
|
myOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!queueRow) return;
|
||||||
|
|
||||||
|
const collection = await models.ImageCollection.findOne(
|
||||||
|
{
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'maxWidth',
|
||||||
|
'maxHeight',
|
||||||
|
'model',
|
||||||
|
'property',
|
||||||
|
],
|
||||||
|
where: { name: collectionName },
|
||||||
|
include: {
|
||||||
|
relation: 'sizes',
|
||||||
|
scope: {
|
||||||
|
fields: ['width', 'height', 'crop'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
myOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileName = `${uuid.v4()}.png`;
|
||||||
|
tempFilePath = path.join(tempPath, fileName);
|
||||||
|
|
||||||
|
// Insert image row
|
||||||
|
await models.Image.create(
|
||||||
|
{
|
||||||
|
name: fileName,
|
||||||
|
collectionFk: collectionName,
|
||||||
|
updated: Date.vnNow(),
|
||||||
|
},
|
||||||
|
myOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update item
|
||||||
|
const model = models[collection.model];
|
||||||
|
if (!model) throw new Error('No matching model found');
|
||||||
|
|
||||||
|
const item = await model.findById(queueRow.itemFk, null, myOptions);
|
||||||
|
if (item) {
|
||||||
|
await item.updateAttribute(
|
||||||
|
collection.property,
|
||||||
|
fileName,
|
||||||
|
myOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download remote image
|
||||||
|
const response = await axios.get(queueRow.url, {
|
||||||
|
responseType: 'stream',
|
||||||
|
});
|
||||||
|
|
||||||
|
const writeStream = createWriteStream(tempFilePath);
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
writeStream.on('open', () => response.data.pipe(writeStream));
|
||||||
|
writeStream.on('finish', () => resolve());
|
||||||
|
writeStream.on('error', error => reject(error));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Resize
|
||||||
|
const container = await models.ImageContainer.container(
|
||||||
|
collectionName
|
||||||
|
);
|
||||||
|
const rootPath = container.client.root;
|
||||||
|
const collectionDir = path.join(rootPath, collectionName);
|
||||||
|
|
||||||
|
// To max size
|
||||||
|
const { maxWidth, maxHeight } = collection;
|
||||||
|
const fullSizePath = path.join(collectionDir, 'full');
|
||||||
|
const toFullSizePath = `${fullSizePath}/${fileName}`;
|
||||||
|
|
||||||
|
await fs.mkdir(fullSizePath, { recursive: true });
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
gm(tempFilePath)
|
||||||
|
.resize(maxWidth, maxHeight, '>')
|
||||||
|
.setFormat('png')
|
||||||
|
.write(toFullSizePath, function (err) {
|
||||||
|
if (err) reject(err);
|
||||||
|
if (!err) resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// To collection sizes
|
||||||
|
for (const size of collection.sizes()) {
|
||||||
|
const { width, height } = size;
|
||||||
|
|
||||||
|
const sizePath = path.join(collectionDir, `${width}x${height}`);
|
||||||
|
const toSizePath = `${sizePath}/${fileName}`;
|
||||||
|
|
||||||
|
await fs.mkdir(sizePath, { recursive: true });
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const gmInstance = gm(tempFilePath);
|
||||||
|
|
||||||
|
if (size.crop) {
|
||||||
|
gmInstance
|
||||||
|
.resize(width, height, '^')
|
||||||
|
.gravity('Center')
|
||||||
|
.crop(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!size.crop) gmInstance.resize(width, height, '>');
|
||||||
|
|
||||||
|
gmInstance
|
||||||
|
.setFormat('png')
|
||||||
|
.write(toSizePath, function (err) {
|
||||||
|
if (err) reject(err);
|
||||||
|
if (!err) resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.unlink(tempFilePath);
|
||||||
|
} catch (error) { }
|
||||||
|
|
||||||
|
await queueRow.destroy(myOptions);
|
||||||
|
|
||||||
|
// Restart queue
|
||||||
|
Self.download();
|
||||||
|
|
||||||
|
await tx.commit();
|
||||||
|
} catch (error) {
|
||||||
|
await tx.rollback();
|
||||||
|
|
||||||
|
if (queueRow.attempts < maxAttempts) {
|
||||||
|
await queueRow.updateAttributes({
|
||||||
|
error: error,
|
||||||
|
attempts: queueRow.attempts + 1,
|
||||||
|
updated: Date.vnNew(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.unlink(tempFilePath);
|
||||||
|
} catch (error) { }
|
||||||
|
|
||||||
|
Self.download();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -62,7 +62,7 @@ module.exports = Self => {
|
||||||
writeStream.on('open', () => response.pipe(writeStream));
|
writeStream.on('open', () => response.pipe(writeStream));
|
||||||
writeStream.on('error', async error =>
|
writeStream.on('error', async error =>
|
||||||
await errorHandler(image.itemFk, error, filePath));
|
await errorHandler(image.itemFk, error, filePath));
|
||||||
writeStream.on('finish', writeStream.end());
|
writeStream.on('finish', () => writeStream.end());
|
||||||
|
|
||||||
writeStream.on('close', async function() {
|
writeStream.on('close', async function() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('labelPdf', {
|
Self.remoteMethodCtx('labelPdf', {
|
||||||
description: 'Returns the item label pdf',
|
description: 'Returns the item label pdf',
|
||||||
|
@ -56,17 +54,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.labelPdf = async(ctx, id) => {
|
Self.labelPdf = (ctx, id) => Self.printReport(ctx, id, 'item-label');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('item-label', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="item-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/item-image-queue/downloadImages')(Self);
|
require('../methods/item-image-queue/download')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('driverRouteEmail', {
|
Self.remoteMethodCtx('driverRouteEmail', {
|
||||||
description: 'Sends the driver route email with an attached PDF',
|
description: 'Sends the driver route email with an attached PDF',
|
||||||
|
@ -41,19 +39,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.driverRouteEmail = async ctx => {
|
Self.driverRouteEmail = ctx => Self.sendTemplate(ctx, 'driver-route');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('driver-route', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('driverRoutePdf', {
|
Self.remoteMethodCtx('driverRoutePdf', {
|
||||||
description: 'Returns the driver route pdf',
|
description: 'Returns the driver route pdf',
|
||||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.driverRoutePdf = async(ctx, id) => {
|
Self.driverRoutePdf = (ctx, id) => Self.printReport(ctx, id, 'driver-route');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('driver-route', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('campaignMetricsPdf', {
|
Self.remoteMethodCtx('campaignMetricsPdf', {
|
||||||
description: 'Returns the campaign metrics pdf',
|
description: 'Returns the campaign metrics pdf',
|
||||||
|
@ -49,17 +47,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.campaignMetricsPdf = async(ctx, id) => {
|
Self.campaignMetricsPdf = (ctx, id) => Self.printReport(ctx, id, 'supplier-campaign-metrics');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('supplier-campaign-metrics', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('collectionLabel', {
|
Self.remoteMethodCtx('collectionLabel', {
|
||||||
description: 'Returns the collection label',
|
description: 'Returns the collection label',
|
||||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.collectionLabel = async(ctx, id) => {
|
Self.collectionLabel = (ctx, id) => Self.printReport(ctx, id, 'collection-label');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('collection-label', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -165,18 +165,29 @@ module.exports = Self => {
|
||||||
'shipped',
|
'shipped',
|
||||||
'landed',
|
'landed',
|
||||||
'isDeleted',
|
'isDeleted',
|
||||||
'routeFk'
|
'routeFk',
|
||||||
|
'nickname'
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
relation: 'client',
|
relation: 'client',
|
||||||
scope: {
|
scope: {
|
||||||
fields: 'salesPersonFk'
|
fields: 'salesPersonFk'
|
||||||
}
|
},
|
||||||
}]
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'address',
|
||||||
|
scope: {
|
||||||
|
fields: 'nickname'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
args.routeFk = null;
|
args.routeFk = null;
|
||||||
|
if (args.isWithoutNegatives === false) delete args.isWithoutNegatives;
|
||||||
const updatedTicket = Object.assign({}, args);
|
const updatedTicket = Object.assign({}, args);
|
||||||
delete updatedTicket.ctx;
|
delete updatedTicket.ctx;
|
||||||
delete updatedTicket.option;
|
delete updatedTicket.option;
|
||||||
|
@ -224,37 +235,41 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const changes = loggable.getChanges(originalTicket, updatedTicket);
|
const changes = loggable.getChanges(originalTicket, updatedTicket);
|
||||||
const oldProperties = await loggable.translateValues(Self, changes.old);
|
const hasChanges = Object.keys(changes.old).length > 0 || Object.keys(changes.new).length > 0;
|
||||||
const newProperties = await loggable.translateValues(Self, changes.new);
|
|
||||||
|
|
||||||
await models.TicketLog.create({
|
if (hasChanges) {
|
||||||
originFk: args.id,
|
const oldProperties = await loggable.translateValues(Self, changes.old);
|
||||||
userFk: userId,
|
const newProperties = await loggable.translateValues(Self, changes.new);
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Ticket',
|
|
||||||
changedModelId: args.id,
|
|
||||||
oldInstance: oldProperties,
|
|
||||||
newInstance: newProperties
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
const salesPersonId = originalTicket.client().salesPersonFk;
|
await models.TicketLog.create({
|
||||||
if (salesPersonId) {
|
originFk: args.id,
|
||||||
const origin = ctx.req.headers.origin;
|
userFk: userId,
|
||||||
|
action: 'update',
|
||||||
|
changedModel: 'Ticket',
|
||||||
|
changedModelId: args.id,
|
||||||
|
oldInstance: oldProperties,
|
||||||
|
newInstance: newProperties
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
let changesMade = '';
|
const salesPersonId = originalTicket.client().salesPersonFk;
|
||||||
for (let change in newProperties) {
|
if (salesPersonId) {
|
||||||
let value = newProperties[change];
|
const origin = ctx.req.headers.origin;
|
||||||
let oldValue = oldProperties[change];
|
|
||||||
|
|
||||||
changesMade += `\r\n~${$t(change)}: ${oldValue}~ ➔ *${$t(change)}: ${value}*`;
|
let changesMade = '';
|
||||||
|
for (let change in newProperties) {
|
||||||
|
let value = newProperties[change];
|
||||||
|
let oldValue = oldProperties[change];
|
||||||
|
|
||||||
|
changesMade += `\r\n~${$t(change)}: ${oldValue}~ ➔ *${$t(change)}: ${value}*`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = $t('Changed this data from the ticket', {
|
||||||
|
ticketId: args.id,
|
||||||
|
ticketUrl: `${origin}/#!/ticket/${args.id}/sale`,
|
||||||
|
changes: changesMade
|
||||||
|
});
|
||||||
|
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = $t('Changed this data from the ticket', {
|
|
||||||
ticketId: args.id,
|
|
||||||
ticketUrl: `${origin}/#!/ticket/${args.id}/sale`,
|
|
||||||
changes: changesMade
|
|
||||||
});
|
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.id = args.id;
|
res.id = args.id;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('deliveryNoteEmail', {
|
Self.remoteMethodCtx('deliveryNoteEmail', {
|
||||||
description: 'Sends the delivery note email with an attached PDF',
|
description: 'Sends the delivery note email with an attached PDF',
|
||||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.deliveryNoteEmail = async ctx => {
|
Self.deliveryNoteEmail = ctx => Self.sendTemplate(ctx, 'delivery-note');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('delivery-note', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('deliveryNotePdf', {
|
Self.remoteMethodCtx('deliveryNotePdf', {
|
||||||
description: 'Returns the delivery note pdf',
|
description: 'Returns the delivery note pdf',
|
||||||
|
@ -46,17 +44,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.deliveryNotePdf = async(ctx, id) => {
|
Self.deliveryNotePdf = (ctx, id) => Self.printReport(ctx, id, 'delivery-note');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('delivery-note', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('expeditionPalletLabel', {
|
Self.remoteMethodCtx('expeditionPalletLabel', {
|
||||||
description: 'Returns the expedition pallet label',
|
description: 'Returns the expedition pallet label',
|
||||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.expeditionPalletLabel = async(ctx, id) => {
|
Self.expeditionPalletLabel = (ctx, id) => Self.printReport(ctx, id, 'expedition-pallet-label');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('expedition-pallet-label', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="doc-${id}.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('saveSign', {
|
||||||
|
description: 'Save sign',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
arg: 'tickets',
|
||||||
|
type: ['number'],
|
||||||
|
required: true,
|
||||||
|
description: 'The tickets'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'location',
|
||||||
|
type: 'object',
|
||||||
|
description: 'The employee location the moment the sign is saved'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'signedTime',
|
||||||
|
type: 'date',
|
||||||
|
description: 'The signed time'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/saveSign`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.saveSign = async(ctx, options) => {
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setLocation(ticketId) {
|
||||||
|
await models.Delivery.create({
|
||||||
|
ticketFk: ticketId,
|
||||||
|
longitude: args.location.Longitude,
|
||||||
|
latitude: args.location.Latitude,
|
||||||
|
dated: args.signedTime || new Date()
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function gestDocExists(ticketId) {
|
||||||
|
const ticketDms = await models.TicketDms.findOne({
|
||||||
|
where: {ticketFk: ticketId},
|
||||||
|
fields: ['dmsFk']
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (!ticketDms) return false;
|
||||||
|
|
||||||
|
const ticket = await models.Ticket.findById(ticketId, {fields: ['isSigned']}, myOptions);
|
||||||
|
if (ticket.isSigned == true)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
await models.Dms.destroyAll({where: {reference: ticketId}}, myOptions);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createGestDoc(id) {
|
||||||
|
const ticket = await models.Ticket.findById(id,
|
||||||
|
{include: [
|
||||||
|
{
|
||||||
|
relation: 'warehouse',
|
||||||
|
scope: {
|
||||||
|
fields: ['id']
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'route',
|
||||||
|
scope: {
|
||||||
|
fields: ['id']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, myOptions);
|
||||||
|
const dmsType = await models.DmsType.findOne({where: {code: 'Ticket'}, fields: ['id']}, myOptions);
|
||||||
|
const ctxUploadFile = Object.assign({}, ctx);
|
||||||
|
ctxUploadFile.args = {
|
||||||
|
warehouseId: ticket.warehouseFk,
|
||||||
|
companyId: ticket.companyFk,
|
||||||
|
dmsTypeId: dmsType.id,
|
||||||
|
reference: id,
|
||||||
|
description: `Ticket ${id} Cliente ${ticket.client().name} Ruta ${ticket.route().id}`,
|
||||||
|
hasFile: true
|
||||||
|
};
|
||||||
|
await models.Ticket.uploadFile(ctxUploadFile, id, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < args.tickets.length; i++) {
|
||||||
|
const ticketState = await models.TicketState.findOne(
|
||||||
|
{where: {ticketFk: args.tickets[i]},
|
||||||
|
fields: ['alertLevel']
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const packedAlertLevel = await models.AlertLevel.findOne({where: {code: 'PACKED'},
|
||||||
|
fields: ['id']
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (ticketState.alertLevel < packedAlertLevel.id)
|
||||||
|
throw new UserError('This ticket cannot be signed because it has not been boxed');
|
||||||
|
else if (!await gestDocExists(args.tickets[i])) {
|
||||||
|
if (args.location) setLocation(args.tickets[i]);
|
||||||
|
await createGestDoc(args.tickets[i]);
|
||||||
|
await Self.rawSql(`CALL vn.ticket_setState(?, ?)`, [args.tickets[i], 'DELIVERED'], myOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -2,13 +2,13 @@ const models = require('vn-loopback/server/server').models;
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('ticket merge()', () => {
|
describe('ticket merge()', () => {
|
||||||
const tickets = [{
|
const tickets = {
|
||||||
originId: 13,
|
originId: 13,
|
||||||
destinationId: 12,
|
destinationId: 12,
|
||||||
originShipped: Date.vnNew(),
|
originShipped: Date.vnNew(),
|
||||||
destinationShipped: Date.vnNew(),
|
destinationShipped: Date.vnNew(),
|
||||||
workerFk: 1
|
workerFk: 1
|
||||||
}];
|
};
|
||||||
|
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: 9},
|
accessToken: {userId: 9},
|
||||||
|
@ -37,14 +37,14 @@ describe('ticket merge()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const chatNotificationBeforeMerge = await models.Chat.find();
|
const chatNotificationBeforeMerge = await models.Chat.find();
|
||||||
|
|
||||||
await models.Ticket.merge(ctx, tickets, options);
|
await models.Ticket.merge(ctx, [tickets], options);
|
||||||
|
|
||||||
const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets[0].originId}}, options);
|
const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets.originId}}, options);
|
||||||
const deletedTicket = await models.Ticket.findOne({where: {id: tickets[0].originId}}, options);
|
const deletedTicket = await models.Ticket.findOne({where: {id: tickets.originId}}, options);
|
||||||
const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets[0].destinationId}}, options);
|
const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets.destinationId}}, options);
|
||||||
const chatNotificationAfterMerge = await models.Chat.find();
|
const chatNotificationAfterMerge = await models.Chat.find();
|
||||||
|
|
||||||
expect(createdTicketLog.length).toEqual(1);
|
expect(createdTicketLog.length).toEqual(2);
|
||||||
expect(deletedTicket.isDeleted).toEqual(true);
|
expect(deletedTicket.isDeleted).toEqual(true);
|
||||||
expect(salesTicketFuture.length).toEqual(2);
|
expect(salesTicketFuture.length).toEqual(2);
|
||||||
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);
|
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Ticket saveSign()', () => {
|
||||||
|
const FormData = require('form-data');
|
||||||
|
const data = new FormData();
|
||||||
|
let ctx = {req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {
|
||||||
|
...data.getHeaders()
|
||||||
|
}
|
||||||
|
|
||||||
|
}};
|
||||||
|
|
||||||
|
it(`should throw error if the ticket's alert level is lower than 2`, async() => {
|
||||||
|
const tx = await models.TicketDms.beginTransaction({});
|
||||||
|
const ticketWithOkState = 12;
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
ctx.args = {tickets: [ticketWithOkState]};
|
||||||
|
|
||||||
|
await models.Ticket.saveSign(ctx, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -105,8 +105,8 @@ module.exports = Self => {
|
||||||
originFk: id,
|
originFk: id,
|
||||||
userFk: userId,
|
userFk: userId,
|
||||||
action: 'update',
|
action: 'update',
|
||||||
changedModel: 'Ticket',
|
changedModel: 'Sale',
|
||||||
changedModelId: id,
|
changedModelId: sale.id,
|
||||||
oldInstance: {
|
oldInstance: {
|
||||||
item: originalSaleData.itemFk,
|
item: originalSaleData.itemFk,
|
||||||
quantity: originalSaleData.quantity,
|
quantity: originalSaleData.quantity,
|
||||||
|
@ -126,8 +126,8 @@ module.exports = Self => {
|
||||||
originFk: ticketId,
|
originFk: ticketId,
|
||||||
userFk: userId,
|
userFk: userId,
|
||||||
action: 'update',
|
action: 'update',
|
||||||
changedModel: 'Ticket',
|
changedModel: 'Sale',
|
||||||
changedModelId: ticketId,
|
changedModelId: sale.id,
|
||||||
oldInstance: {
|
oldInstance: {
|
||||||
item: originalSaleData.itemFk,
|
item: originalSaleData.itemFk,
|
||||||
quantity: originalSaleData.quantity,
|
quantity: originalSaleData.quantity,
|
||||||
|
|
|
@ -39,4 +39,5 @@ module.exports = function(Self) {
|
||||||
require('../methods/ticket/isRoleAdvanced')(Self);
|
require('../methods/ticket/isRoleAdvanced')(Self);
|
||||||
require('../methods/ticket/collectionLabel')(Self);
|
require('../methods/ticket/collectionLabel')(Self);
|
||||||
require('../methods/ticket/expeditionPalletLabel')(Self);
|
require('../methods/ticket/expeditionPalletLabel')(Self);
|
||||||
|
require('../methods/ticket/saveSign')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
"isDeleted": {
|
"isDeleted": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"isSigned": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"priority": {
|
"priority": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Email} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('extraCommunityEmail', {
|
Self.remoteMethodCtx('extraCommunityEmail', {
|
||||||
description: 'Sends the extra community email with an attached PDF',
|
description: 'Sends the extra community email with an attached PDF',
|
||||||
|
@ -74,19 +72,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.extraCommunityEmail = async ctx => {
|
Self.extraCommunityEmail = ctx => Self.sendTemplate(ctx, 'extra-community');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {
|
|
||||||
recipient: args.recipient,
|
|
||||||
lang: ctx.req.getLocale()
|
|
||||||
};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const email = new Email('extra-community', params);
|
|
||||||
|
|
||||||
return email.send();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
const {Report} = require('vn-print');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('extraCommunityPdf', {
|
Self.remoteMethodCtx('extraCommunityPdf', {
|
||||||
description: 'Returns the extra community pdf',
|
description: 'Returns the extra community pdf',
|
||||||
|
@ -11,6 +9,16 @@ module.exports = Self => {
|
||||||
description: 'The recipient id',
|
description: 'The recipient id',
|
||||||
required: false
|
required: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
arg: 'filter',
|
||||||
|
type: 'object',
|
||||||
|
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'search',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Searchs the travel by id'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
arg: 'landedTo',
|
arg: 'landedTo',
|
||||||
type: 'date'
|
type: 'date'
|
||||||
|
@ -73,17 +81,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.extraCommunityPdf = async ctx => {
|
Self.extraCommunityPdf = ctx => Self.printReport(ctx, null, 'extra-community');
|
||||||
const args = Object.assign({}, ctx.args);
|
|
||||||
const params = {lang: ctx.req.getLocale()};
|
|
||||||
|
|
||||||
delete args.ctx;
|
|
||||||
for (const param in args)
|
|
||||||
params[param] = args[param];
|
|
||||||
|
|
||||||
const report = new Report('extra-community', params);
|
|
||||||
const stream = await report.toPdfStream();
|
|
||||||
|
|
||||||
return [stream, 'application/pdf', `filename="extra-community.pdf"`];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<vn-crud-model
|
<vn-crud-model
|
||||||
vn-id="model"
|
vn-id="model"
|
||||||
url="Travels/extraCommunityFilter"
|
url="Travels/extraCommunityFilter"
|
||||||
filter="::$ctrl.filter"
|
user-params="::$ctrl.defaultFilter"
|
||||||
data="travels"
|
data="travels"
|
||||||
order="landed ASC, shipped ASC, travelFk, loadPriority, agencyModeFk, supplierName, evaNotes"
|
order="landed ASC, shipped ASC, travelFk, loadPriority, agencyModeFk, supplierName, evaNotes"
|
||||||
limit="20"
|
limit="20"
|
||||||
|
|
|
@ -141,8 +141,11 @@ class Controller extends Section {
|
||||||
|
|
||||||
get reportParams() {
|
get reportParams() {
|
||||||
const userParams = this.$.model.userParams;
|
const userParams = this.$.model.userParams;
|
||||||
|
const currentFilter = this.$.model.currentFilter;
|
||||||
|
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
authorization: this.vnToken.token
|
authorization: this.vnToken.token,
|
||||||
|
filter: currentFilter
|
||||||
}, userParams);
|
}, userParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('getMailStates', {
|
||||||
|
description: 'Get the states of a month about time control mail',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The worker id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'month',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The number of the month'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'year',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The number of the year'
|
||||||
|
}],
|
||||||
|
returns: [{
|
||||||
|
type: ['object'],
|
||||||
|
root: true
|
||||||
|
}],
|
||||||
|
http: {
|
||||||
|
path: `/:id/getMailStates`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getMailStates = async(ctx, workerId, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const args = ctx.args;
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const times = await models.Time.find({
|
||||||
|
fields: ['week'],
|
||||||
|
where: {
|
||||||
|
month: args.month,
|
||||||
|
year: args.year
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const weeks = times.map(time => time.week);
|
||||||
|
const weekNumbersSet = new Set(weeks);
|
||||||
|
const weekNumbers = Array.from(weekNumbersSet);
|
||||||
|
|
||||||
|
const workerTimeControlMails = await models.WorkerTimeControlMail.find({
|
||||||
|
where: {
|
||||||
|
workerFk: workerId,
|
||||||
|
year: args.year,
|
||||||
|
week: {inq: weekNumbers}
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
return workerTimeControlMails;
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('workerTimeControl getMailStates()', () => {
|
||||||
|
const workerId = 9;
|
||||||
|
const ctx = {args: {
|
||||||
|
month: 12,
|
||||||
|
year: 2000
|
||||||
|
}};
|
||||||
|
|
||||||
|
it('should get the states of a month about time control mail', async() => {
|
||||||
|
const tx = await models.WorkerTimeControl.beginTransaction({});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const response = await models.WorkerTimeControl.getMailStates(ctx, workerId, options);
|
||||||
|
|
||||||
|
expect(response[0].state).toEqual('REVISE');
|
||||||
|
expect(response[1].state).toEqual('SENDED');
|
||||||
|
expect(response[2].state).toEqual('CONFIRMED');
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -47,6 +47,10 @@ module.exports = Self => {
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const isHimself = userId == args.workerId;
|
||||||
|
if (!isHimself)
|
||||||
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
|
||||||
const workerTimeControlMail = await models.WorkerTimeControlMail.findOne({
|
const workerTimeControlMail = await models.WorkerTimeControlMail.findOne({
|
||||||
where: {
|
where: {
|
||||||
workerFk: args.workerId,
|
workerFk: args.workerId,
|
||||||
|
@ -60,8 +64,6 @@ module.exports = Self => {
|
||||||
const oldState = workerTimeControlMail.state;
|
const oldState = workerTimeControlMail.state;
|
||||||
const oldReason = workerTimeControlMail.reason;
|
const oldReason = workerTimeControlMail.reason;
|
||||||
|
|
||||||
if (oldState == args.state) throw new UserError('Already has this status');
|
|
||||||
|
|
||||||
await workerTimeControlMail.updateAttributes({
|
await workerTimeControlMail.updateAttributes({
|
||||||
state: args.state,
|
state: args.state,
|
||||||
reason: args.reason || null
|
reason: args.reason || null
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
"year": {
|
"year": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"month": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"week": {
|
"week": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ module.exports = Self => {
|
||||||
require('../methods/worker-time-control/sendMail')(Self);
|
require('../methods/worker-time-control/sendMail')(Self);
|
||||||
require('../methods/worker-time-control/updateWorkerTimeControlMail')(Self);
|
require('../methods/worker-time-control/updateWorkerTimeControlMail')(Self);
|
||||||
require('../methods/worker-time-control/weeklyHourRecordEmail')(Self);
|
require('../methods/worker-time-control/weeklyHourRecordEmail')(Self);
|
||||||
|
require('../methods/worker-time-control/getMailStates')(Self);
|
||||||
|
|
||||||
Self.rewriteDbError(function(err) {
|
Self.rewriteDbError(function(err) {
|
||||||
if (err.code === 'ER_DUP_ENTRY')
|
if (err.code === 'ER_DUP_ENTRY')
|
||||||
|
|
|
@ -13,14 +13,20 @@ export default class Controller extends Section {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get ibanCountry() {
|
||||||
|
if (!this.worker || !this.worker.iban) return false;
|
||||||
|
|
||||||
|
let countryCode = this.worker.iban.substr(0, 2);
|
||||||
|
|
||||||
|
return countryCode;
|
||||||
|
}
|
||||||
|
|
||||||
autofillBic() {
|
autofillBic() {
|
||||||
if (!this.worker || !this.worker.iban) return;
|
if (!this.worker || !this.worker.iban) return;
|
||||||
|
|
||||||
let bankEntityId = parseInt(this.worker.iban.substr(4, 4));
|
let bankEntityId = parseInt(this.worker.iban.substr(4, 4));
|
||||||
let filter = {where: {id: bankEntityId}};
|
let filter = {where: {id: bankEntityId}};
|
||||||
|
|
||||||
if (this.ibanCountry != 'ES') return;
|
|
||||||
|
|
||||||
this.$http.get(`BankEntities`, {filter}).then(response => {
|
this.$http.get(`BankEntities`, {filter}).then(response => {
|
||||||
const hasData = response.data && response.data[0];
|
const hasData = response.data && response.data[0];
|
||||||
|
|
||||||
|
|
|
@ -78,15 +78,31 @@
|
||||||
</vn-table>
|
</vn-table>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
|
|
||||||
<vn-button-bar class="vn-pa-xs vn-w-lg">
|
<vn-button-bar ng-show="$ctrl.state" class="vn-w-lg">
|
||||||
<vn-button
|
<vn-button
|
||||||
label="Satisfied"
|
label="Satisfied"
|
||||||
|
disabled="$ctrl.state == 'CONFIRMED'"
|
||||||
|
ng-if="$ctrl.isHimSelf"
|
||||||
ng-click="$ctrl.isSatisfied()">
|
ng-click="$ctrl.isSatisfied()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-button
|
<vn-button
|
||||||
label="Not satisfied"
|
label="Not satisfied"
|
||||||
|
disabled="$ctrl.state == 'REVISE'"
|
||||||
|
ng-if="$ctrl.isHimSelf"
|
||||||
ng-click="reason.show()">
|
ng-click="reason.show()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
|
<vn-button
|
||||||
|
label="Reason"
|
||||||
|
ng-if="$ctrl.reason && ($ctrl.isHimSelf || $ctrl.isHr)"
|
||||||
|
ng-click="reason.show()">
|
||||||
|
</vn-button>
|
||||||
|
<vn-button
|
||||||
|
label="Resend"
|
||||||
|
ng-click="sendEmailConfirmation.show()"
|
||||||
|
class="right"
|
||||||
|
vn-tooltip="Resend email of this week to the user"
|
||||||
|
ng-show="::$ctrl.isHr">
|
||||||
|
</vn-button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
|
|
||||||
<vn-side-menu side="right">
|
<vn-side-menu side="right">
|
||||||
|
@ -106,6 +122,8 @@
|
||||||
vn-id="calendar"
|
vn-id="calendar"
|
||||||
class="vn-pt-md"
|
class="vn-pt-md"
|
||||||
ng-model="$ctrl.date"
|
ng-model="$ctrl.date"
|
||||||
|
format-week="$ctrl.formatWeek($element)"
|
||||||
|
on-move="$ctrl.getMailStates($date)"
|
||||||
has-events="$ctrl.hasEvents($day)">
|
has-events="$ctrl.hasEvents($day)">
|
||||||
</vn-calendar>
|
</vn-calendar>
|
||||||
</div>
|
</div>
|
||||||
|
@ -166,15 +184,31 @@
|
||||||
vn-id="reason"
|
vn-id="reason"
|
||||||
on-accept="$ctrl.isUnsatisfied()">
|
on-accept="$ctrl.isUnsatisfied()">
|
||||||
<tpl-body>
|
<tpl-body>
|
||||||
<vn-textarea
|
<div class="reasonDialog">
|
||||||
label="Reason"
|
<vn-textarea
|
||||||
ng-model="$ctrl.reason"
|
label="Reason"
|
||||||
required="true"
|
ng-model="$ctrl.reason"
|
||||||
rows="3">
|
disabled="!$ctrl.isHimSelf"
|
||||||
</vn-textarea>
|
rows="5"
|
||||||
|
required="true">
|
||||||
|
</vn-textarea>
|
||||||
|
</div>
|
||||||
</tpl-body>
|
</tpl-body>
|
||||||
<tpl-buttons>
|
<tpl-buttons ng-if="$ctrl.isHimSelf">
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
<button response="accept" translate>Save</button>
|
<button response="accept" translate>Save</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
|
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="sendEmailConfirmation"
|
||||||
|
on-accept="$ctrl.resendEmail()"
|
||||||
|
message="Send time control email">
|
||||||
|
<tpl-body style="min-width: 500px;">
|
||||||
|
<span translate>Are you sure you want to send it?</span>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Confirm</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
import UserError from 'core/lib/user-error';
|
||||||
|
|
||||||
class Controller extends Section {
|
class Controller extends Section {
|
||||||
constructor($element, $, vnWeekDays) {
|
constructor($element, $, vnWeekDays) {
|
||||||
|
@ -24,12 +25,31 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.date = initialDate;
|
this.date = initialDate;
|
||||||
|
|
||||||
|
this.getMailStates(this.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isHr() {
|
||||||
|
return this.aclService.hasAny(['hr']);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isHimSelf() {
|
||||||
|
const userId = window.localStorage.currentUserWorkerId;
|
||||||
|
return userId == this.$params.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
get worker() {
|
get worker() {
|
||||||
return this._worker;
|
return this._worker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get weekNumber() {
|
||||||
|
return this.getWeekNumber(this.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
set weekNumber(value) {
|
||||||
|
this._weekNumber = value;
|
||||||
|
}
|
||||||
|
|
||||||
set worker(value) {
|
set worker(value) {
|
||||||
this._worker = value;
|
this._worker = value;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +88,27 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fetchHours();
|
this.fetchHours();
|
||||||
|
this.getWeekData();
|
||||||
|
}
|
||||||
|
|
||||||
|
getWeekData() {
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
workerFk: this.$params.id,
|
||||||
|
year: this._date.getFullYear(),
|
||||||
|
week: this.getWeekNumber(this._date)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.$http.get('WorkerTimeControlMails', {filter})
|
||||||
|
.then(res => {
|
||||||
|
const workerTimeControlMail = res.data;
|
||||||
|
if (!workerTimeControlMail.length) {
|
||||||
|
this.state = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.state = workerTimeControlMail[0].state;
|
||||||
|
this.reason = workerTimeControlMail[0].reason;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -294,42 +335,56 @@ class Controller extends Section {
|
||||||
this.$.editEntry.show($event);
|
this.$.editEntry.show($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
getWeekNumber(currentDate) {
|
getWeekNumber(date) {
|
||||||
const startDate = new Date(currentDate.getFullYear(), 0, 1);
|
const tempDate = new Date(date);
|
||||||
let days = Math.floor((currentDate - startDate) /
|
let dayOfWeek = tempDate.getDay();
|
||||||
(24 * 60 * 60 * 1000));
|
dayOfWeek = (dayOfWeek === 0) ? 7 : dayOfWeek;
|
||||||
return Math.ceil(days / 7);
|
const firstDayOfWeek = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() - (dayOfWeek - 1));
|
||||||
|
const firstDayOfYear = new Date(tempDate.getFullYear(), 0, 1);
|
||||||
|
const differenceInMilliseconds = firstDayOfWeek.getTime() - firstDayOfYear.getTime();
|
||||||
|
const weekNumber = Math.floor(differenceInMilliseconds / (1000 * 60 * 60 * 24 * 7)) + 1;
|
||||||
|
return weekNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSatisfied() {
|
isSatisfied() {
|
||||||
const weekNumber = this.getWeekNumber(this.date);
|
|
||||||
const params = {
|
const params = {
|
||||||
workerId: this.worker.id,
|
workerId: this.worker.id,
|
||||||
year: this.date.getFullYear(),
|
year: this.date.getFullYear(),
|
||||||
week: weekNumber,
|
week: this.weekNumber,
|
||||||
state: 'CONFIRMED'
|
state: 'CONFIRMED'
|
||||||
};
|
};
|
||||||
const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
|
const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
|
||||||
this.$http.post(query, params).then(() => {
|
this.$http.post(query, params).then(() => {
|
||||||
|
this.getMailStates(this.date);
|
||||||
|
this.getWeekData();
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isUnsatisfied() {
|
isUnsatisfied() {
|
||||||
const weekNumber = this.getWeekNumber(this.date);
|
if (!this.reason) throw new UserError(`You must indicate a reason`);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
workerId: this.worker.id,
|
workerId: this.worker.id,
|
||||||
year: this.date.getFullYear(),
|
year: this.date.getFullYear(),
|
||||||
week: weekNumber,
|
week: this.weekNumber,
|
||||||
state: 'REVISE',
|
state: 'REVISE',
|
||||||
reason: this.reason
|
reason: this.reason
|
||||||
};
|
};
|
||||||
const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
|
const query = `WorkerTimeControls/updateWorkerTimeControlMail`;
|
||||||
this.$http.post(query, params).then(() => {
|
this.$http.post(query, params).then(() => {
|
||||||
|
this.getMailStates(this.date);
|
||||||
|
this.getWeekData();
|
||||||
this.vnApp.showSuccess(this.$t('Data saved!'));
|
this.vnApp.showSuccess(this.$t('Data saved!'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeState(state, reason) {
|
||||||
|
this.state = state;
|
||||||
|
this.reason = reason;
|
||||||
|
this.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
try {
|
try {
|
||||||
const entry = this.selectedRow;
|
const entry = this.selectedRow;
|
||||||
|
@ -345,6 +400,77 @@ class Controller extends Section {
|
||||||
this.vnApp.showError(this.$t(e.message));
|
this.vnApp.showError(this.$t(e.message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resendEmail() {
|
||||||
|
const timestamp = this.date.getTime() / 1000;
|
||||||
|
const url = `${window.location.origin}/#!/worker/${this.worker.id}/time-control?timestamp=${timestamp}`;
|
||||||
|
const params = {
|
||||||
|
recipient: this.worker.user.emailUser.email,
|
||||||
|
week: this.weekNumber,
|
||||||
|
year: this.date.getFullYear(),
|
||||||
|
url: url,
|
||||||
|
};
|
||||||
|
this.$http.post(`WorkerTimeControls/weekly-hour-hecord-email`, params)
|
||||||
|
.then(() => {
|
||||||
|
this.vnApp.showSuccess(this.$t('Email sended'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTime(timeString) {
|
||||||
|
const [hours, minutes, seconds] = timeString.split(':');
|
||||||
|
return [parseInt(hours), parseInt(minutes), parseInt(seconds)];
|
||||||
|
}
|
||||||
|
|
||||||
|
getMailStates(date) {
|
||||||
|
const params = {
|
||||||
|
month: date.getMonth() + 1,
|
||||||
|
year: date.getFullYear()
|
||||||
|
};
|
||||||
|
const query = `WorkerTimeControls/${this.$params.id}/getMailStates`;
|
||||||
|
this.$http.get(query, {params})
|
||||||
|
.then(res => {
|
||||||
|
this.workerTimeControlMails = res.data;
|
||||||
|
this.repaint();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
formatWeek($element) {
|
||||||
|
const weekNumberHTML = $element.firstElementChild;
|
||||||
|
const weekNumberValue = weekNumberHTML.innerHTML;
|
||||||
|
|
||||||
|
if (!this.workerTimeControlMails) return;
|
||||||
|
const workerTimeControlMail = this.workerTimeControlMails.find(
|
||||||
|
workerTimeControlMail => workerTimeControlMail.week == weekNumberValue
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!workerTimeControlMail) return;
|
||||||
|
const state = workerTimeControlMail.state;
|
||||||
|
|
||||||
|
if (state == 'CONFIRMED') {
|
||||||
|
weekNumberHTML.classList.remove('revise');
|
||||||
|
weekNumberHTML.classList.remove('sended');
|
||||||
|
|
||||||
|
weekNumberHTML.classList.add('confirmed');
|
||||||
|
weekNumberHTML.setAttribute('title', 'Conforme');
|
||||||
|
}
|
||||||
|
if (state == 'REVISE') {
|
||||||
|
weekNumberHTML.classList.remove('confirmed');
|
||||||
|
weekNumberHTML.classList.remove('sended');
|
||||||
|
|
||||||
|
weekNumberHTML.classList.add('revise');
|
||||||
|
weekNumberHTML.setAttribute('title', 'No conforme');
|
||||||
|
}
|
||||||
|
if (state == 'SENDED') {
|
||||||
|
weekNumberHTML.classList.add('sended');
|
||||||
|
weekNumberHTML.setAttribute('title', 'Pendiente');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repaint() {
|
||||||
|
let calendars = this.element.querySelectorAll('vn-calendar');
|
||||||
|
for (let calendar of calendars)
|
||||||
|
calendar.$ctrl.repaint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', 'vnWeekDays'];
|
Controller.$inject = ['$element', '$scope', 'vnWeekDays'];
|
||||||
|
|
|
@ -5,12 +5,14 @@ describe('Component vnWorkerTimeControl', () => {
|
||||||
let $scope;
|
let $scope;
|
||||||
let $element;
|
let $element;
|
||||||
let controller;
|
let controller;
|
||||||
|
let $httpParamSerializer;
|
||||||
|
|
||||||
beforeEach(ngModule('worker'));
|
beforeEach(ngModule('worker'));
|
||||||
|
|
||||||
beforeEach(inject(($componentController, $rootScope, $stateParams, _$httpBackend_) => {
|
beforeEach(inject(($componentController, $rootScope, $stateParams, _$httpBackend_, _$httpParamSerializer_) => {
|
||||||
$stateParams.id = 1;
|
$stateParams.id = 1;
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
|
$httpParamSerializer = _$httpParamSerializer_;
|
||||||
$scope = $rootScope.$new();
|
$scope = $rootScope.$new();
|
||||||
$element = angular.element('<vn-worker-time-control></vn-worker-time-control>');
|
$element = angular.element('<vn-worker-time-control></vn-worker-time-control>');
|
||||||
controller = $componentController('vnWorkerTimeControl', {$element, $scope});
|
controller = $componentController('vnWorkerTimeControl', {$element, $scope});
|
||||||
|
@ -82,6 +84,9 @@ describe('Component vnWorkerTimeControl', () => {
|
||||||
$httpBackend.whenRoute('GET', 'Workers/:id/getWorkedHours')
|
$httpBackend.whenRoute('GET', 'Workers/:id/getWorkedHours')
|
||||||
.respond(response);
|
.respond(response);
|
||||||
|
|
||||||
|
$httpBackend.whenRoute('GET', 'WorkerTimeControlMails')
|
||||||
|
.respond([]);
|
||||||
|
|
||||||
today.setHours(0, 0, 0, 0);
|
today.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
let weekOffset = today.getDay() - 1;
|
let weekOffset = today.getDay() - 1;
|
||||||
|
@ -97,7 +102,6 @@ describe('Component vnWorkerTimeControl', () => {
|
||||||
controller.ended = ended;
|
controller.ended = ended;
|
||||||
|
|
||||||
controller.getWorkedHours(controller.started, controller.ended);
|
controller.getWorkedHours(controller.started, controller.ended);
|
||||||
|
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.weekDays.length).toEqual(7);
|
expect(controller.weekDays.length).toEqual(7);
|
||||||
|
@ -152,5 +156,120 @@ describe('Component vnWorkerTimeControl', () => {
|
||||||
expect(controller.date.toDateString()).toEqual(date.toDateString());
|
expect(controller.date.toDateString()).toEqual(date.toDateString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getWeekData() ', () => {
|
||||||
|
it(`should make a query an then update the state and reason`, () => {
|
||||||
|
const today = Date.vnNew();
|
||||||
|
const response = [
|
||||||
|
{
|
||||||
|
state: 'SENDED',
|
||||||
|
reason: null
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
controller._date = today;
|
||||||
|
|
||||||
|
$httpBackend.whenRoute('GET', 'WorkerTimeControlMails')
|
||||||
|
.respond(response);
|
||||||
|
|
||||||
|
controller.getWeekData();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.state).toBe('SENDED');
|
||||||
|
expect(controller.reason).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isSatisfied() ', () => {
|
||||||
|
it(`should make a query an then call three methods`, () => {
|
||||||
|
const today = Date.vnNew();
|
||||||
|
jest.spyOn(controller, 'getWeekData').mockReturnThis();
|
||||||
|
jest.spyOn(controller, 'getMailStates').mockReturnThis();
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
|
controller.$.model = {applyFilter: jest.fn().mockReturnValue(Promise.resolve())};
|
||||||
|
controller.worker = {id: 1};
|
||||||
|
controller.date = today;
|
||||||
|
controller.weekNumber = 1;
|
||||||
|
|
||||||
|
$httpBackend.expect('POST', 'WorkerTimeControls/updateWorkerTimeControlMail').respond();
|
||||||
|
controller.isSatisfied();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.getMailStates).toHaveBeenCalledWith(controller.date);
|
||||||
|
expect(controller.getWeekData).toHaveBeenCalled();
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isUnsatisfied() ', () => {
|
||||||
|
it(`should throw an error is reason is empty`, () => {
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
controller.isUnsatisfied();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`You must indicate a reason`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should make a query an then call three methods`, () => {
|
||||||
|
const today = Date.vnNew();
|
||||||
|
jest.spyOn(controller, 'getWeekData').mockReturnThis();
|
||||||
|
jest.spyOn(controller, 'getMailStates').mockReturnThis();
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
|
controller.$.model = {applyFilter: jest.fn().mockReturnValue(Promise.resolve())};
|
||||||
|
controller.worker = {id: 1};
|
||||||
|
controller.date = today;
|
||||||
|
controller.weekNumber = 1;
|
||||||
|
controller.reason = 'reason';
|
||||||
|
|
||||||
|
$httpBackend.expect('POST', 'WorkerTimeControls/updateWorkerTimeControlMail').respond();
|
||||||
|
controller.isSatisfied();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.getMailStates).toHaveBeenCalledWith(controller.date);
|
||||||
|
expect(controller.getWeekData).toHaveBeenCalled();
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resendEmail() ', () => {
|
||||||
|
it(`should make a query an then call showSuccess method`, () => {
|
||||||
|
const today = Date.vnNew();
|
||||||
|
jest.spyOn(controller, 'getWeekData').mockReturnThis();
|
||||||
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
|
controller.$.model = {applyFilter: jest.fn().mockReturnValue(Promise.resolve())};
|
||||||
|
controller.worker = {id: 1};
|
||||||
|
controller.worker = {user: {emailUser: {email: 'employee@verdnatura.es'}}};
|
||||||
|
controller.date = today;
|
||||||
|
controller.weekNumber = 1;
|
||||||
|
|
||||||
|
$httpBackend.expect('POST', 'WorkerTimeControls/weekly-hour-hecord-email').respond();
|
||||||
|
controller.resendEmail();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getMailStates() ', () => {
|
||||||
|
it(`should make a query an then call showSuccess method`, () => {
|
||||||
|
const today = Date.vnNew();
|
||||||
|
jest.spyOn(controller, 'repaint').mockReturnThis();
|
||||||
|
|
||||||
|
controller.$params = {id: 1};
|
||||||
|
|
||||||
|
$httpBackend.expect('GET', `WorkerTimeControls/1/getMailStates?month=1&year=2001`).respond();
|
||||||
|
controller.getMailStates(today);
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.repaint).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,3 +14,9 @@ The entry type can't be empty: El tipo de fichada no puede quedar vacía
|
||||||
Satisfied: Conforme
|
Satisfied: Conforme
|
||||||
Not satisfied: No conforme
|
Not satisfied: No conforme
|
||||||
Reason: Motivo
|
Reason: Motivo
|
||||||
|
Resend: Reenviar
|
||||||
|
Email sended: Email enviado
|
||||||
|
You must indicate a reason: Debes indicar un motivo
|
||||||
|
Send time control email: Enviar email control horario
|
||||||
|
Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
|
||||||
|
Resend email of this week to the user: Reenviar email de esta semana al usuario
|
||||||
|
|
|
@ -24,8 +24,29 @@ vn-worker-time-control {
|
||||||
.totalBox {
|
.totalBox {
|
||||||
max-width: none
|
max-width: none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasonDialog{
|
||||||
|
min-width: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-time-entry {
|
.edit-time-entry {
|
||||||
width: 200px
|
width: 200px
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirmed {
|
||||||
|
color: #97B92F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.revise {
|
||||||
|
color: #f61e1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sended {
|
||||||
|
color: #d19b25;
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
"inflation": {
|
"inflation": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"m3Max": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"itemMaxSize": {
|
"itemMaxSize": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,21 @@
|
||||||
rule>
|
rule>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
vn-one
|
vn-one
|
||||||
label="Maximum m³"
|
label="Max m³"
|
||||||
ng-model="$ctrl.zone.itemMaxSize"
|
ng-model="$ctrl.zone.itemMaxSize"
|
||||||
min="0"
|
min="0"
|
||||||
step="0.01"
|
vn-acl="deliveryBoss"
|
||||||
vn-acl="deliveryBoss"
|
rule>
|
||||||
rule>
|
</vn-input-number>
|
||||||
</vn-input-number>
|
<vn-input-number
|
||||||
|
vn-one
|
||||||
|
label="Maximum m³"
|
||||||
|
ng-model="$ctrl.zone.m3Max"
|
||||||
|
min="0"
|
||||||
|
vn-acl="deliveryBoss"
|
||||||
|
rule>
|
||||||
|
</vn-input-number>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
|
|
|
@ -13,6 +13,7 @@ Indefinitely: Indefinido
|
||||||
Inflation: Inflación
|
Inflation: Inflación
|
||||||
Locations: Localizaciones
|
Locations: Localizaciones
|
||||||
Maximum m³: M³ máximo
|
Maximum m³: M³ máximo
|
||||||
|
Max m³: Medida máxima
|
||||||
New zone: Nueva zona
|
New zone: Nueva zona
|
||||||
One day: Un día
|
One day: Un día
|
||||||
Pick up: Recogida
|
Pick up: Recogida
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^5.0.0",
|
||||||
"ftps": "^1.2.0",
|
"ftps": "^1.2.0",
|
||||||
|
"gm": "^1.25.0",
|
||||||
"got": "^10.7.0",
|
"got": "^10.7.0",
|
||||||
"helmet": "^3.21.2",
|
"helmet": "^3.21.2",
|
||||||
"i18n": "^0.8.4",
|
"i18n": "^0.8.4",
|
||||||
|
@ -3625,6 +3626,16 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/array-parallel": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w=="
|
||||||
|
},
|
||||||
|
"node_modules/array-series": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg=="
|
||||||
|
},
|
||||||
"node_modules/array-slice": {
|
"node_modules/array-slice": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
@ -9284,6 +9295,67 @@
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gm": {
|
||||||
|
"version": "1.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gm/-/gm-1.25.0.tgz",
|
||||||
|
"integrity": "sha512-4kKdWXTtgQ4biIo7hZA396HT062nDVVHPjQcurNZ3o/voYN+o5FUC5kOwuORbpExp3XbTJ3SU7iRipiIhQtovw==",
|
||||||
|
"dependencies": {
|
||||||
|
"array-parallel": "~0.1.3",
|
||||||
|
"array-series": "~0.1.5",
|
||||||
|
"cross-spawn": "^4.0.0",
|
||||||
|
"debug": "^3.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gm/node_modules/cross-spawn": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==",
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^4.0.1",
|
||||||
|
"which": "^1.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gm/node_modules/debug": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gm/node_modules/lru-cache": {
|
||||||
|
"version": "4.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
||||||
|
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
||||||
|
"dependencies": {
|
||||||
|
"pseudomap": "^1.0.2",
|
||||||
|
"yallist": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gm/node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
},
|
||||||
|
"node_modules/gm/node_modules/which": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"which": "bin/which"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/gm/node_modules/yallist": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
|
||||||
|
},
|
||||||
"node_modules/google-auth-library": {
|
"node_modules/google-auth-library": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
@ -28673,6 +28745,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"array-parallel": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w=="
|
||||||
|
},
|
||||||
|
"array-series": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz",
|
||||||
|
"integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg=="
|
||||||
|
},
|
||||||
"array-slice": {
|
"array-slice": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
@ -32611,6 +32693,63 @@
|
||||||
"sparkles": "^1.0.0"
|
"sparkles": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gm": {
|
||||||
|
"version": "1.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gm/-/gm-1.25.0.tgz",
|
||||||
|
"integrity": "sha512-4kKdWXTtgQ4biIo7hZA396HT062nDVVHPjQcurNZ3o/voYN+o5FUC5kOwuORbpExp3XbTJ3SU7iRipiIhQtovw==",
|
||||||
|
"requires": {
|
||||||
|
"array-parallel": "~0.1.3",
|
||||||
|
"array-series": "~0.1.5",
|
||||||
|
"cross-spawn": "^4.0.0",
|
||||||
|
"debug": "^3.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-yAXz/pA1tD8Gtg2S98Ekf/sewp3Lcp3YoFKJ4Hkp5h5yLWnKVTDU0kwjKJ8NDCYcfTLfyGkzTikst+jWypT1iA==",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "^4.0.1",
|
||||||
|
"which": "^1.2.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "3.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||||
|
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "4.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
||||||
|
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
||||||
|
"requires": {
|
||||||
|
"pseudomap": "^1.0.2",
|
||||||
|
"yallist": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
},
|
||||||
|
"which": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
|
||||||
|
"requires": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"google-auth-library": {
|
"google-auth-library": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-back",
|
"name": "salix-back",
|
||||||
"version": "23.10.01",
|
"version": "23.12.01",
|
||||||
"author": "Verdnatura Levante SL",
|
"author": "Verdnatura Levante SL",
|
||||||
"description": "Salix backend",
|
"description": "Salix backend",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^5.0.0",
|
"fs-extra": "^5.0.0",
|
||||||
"ftps": "^1.2.0",
|
"ftps": "^1.2.0",
|
||||||
|
"gm": "^1.25.0",
|
||||||
"got": "^10.7.0",
|
"got": "^10.7.0",
|
||||||
"helmet": "^3.21.2",
|
"helmet": "^3.21.2",
|
||||||
"i18n": "^0.8.4",
|
"i18n": "^0.8.4",
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
</p>
|
</p>
|
||||||
<h4 style="text-align: center; margin-top: 10%">{{$t('Agree') | uppercase}}</h4>
|
<h4 style="text-align: center; margin-top: 10%">{{$t('Agree') | uppercase}}</h4>
|
||||||
<p style="margin-top: 8%; text-align: justify">
|
<p style="margin-top: 8%; text-align: justify">
|
||||||
{{$t('Date')}} {{client.payed | date('%d-%m-%Y')}} {{$t('Compensate')}} {{client.amountPaid}} €
|
{{$t('Date')}} {{formatDate(receipt.payed, '%d-%m-%Y')}} {{$t('Compensate')}} {{receipt.amountPaid}} €
|
||||||
{{$t('From client')}} {{client.name}} {{$t('Against the balance of')}}: {{client.invoiceFk}}.
|
{{$t('From client')}} {{client.name}} {{$t('Against the balance of')}}: {{receipt.description}}.
|
||||||
</p>
|
</p>
|
||||||
<p style="margin-top: 8%">
|
<p style="margin-top: 8%">
|
||||||
{{$t('Reception')}} <span style="color: blue">administracion@verdnatura.es</span>
|
{{$t('Reception')}} <span style="color: blue">administracion@verdnatura.es</span>
|
||||||
|
|
|
@ -1,12 +1,31 @@
|
||||||
const vnReport = require('../../../core/mixins/vn-report.js');
|
const vnReport = require('../../../core/mixins/vn-report.js');
|
||||||
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'balance-compensation',
|
name: 'balance-compensation',
|
||||||
mixins: [vnReport],
|
mixins: [vnReport],
|
||||||
async serverPrefetch() {
|
async serverPrefetch() {
|
||||||
this.client = await this.findOneFromDef('client', [this.id]);
|
this.receipt = await app.models.Receipt.findOne({
|
||||||
this.checkMainEntity(this.client);
|
fields: ['amountPaid', 'payed', 'clientFk', 'companyFk', 'description'],
|
||||||
this.company = await this.findOneFromDef('company', [this.id]);
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['name', 'street', 'fi', 'city'],
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
relation: 'supplier',
|
||||||
|
scope: {
|
||||||
|
fields: ['name', 'street', 'nif', 'city'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
where: {id: this.id}
|
||||||
|
});
|
||||||
|
this.client = this.receipt.client();
|
||||||
|
this.company = this.receipt.supplier();
|
||||||
|
|
||||||
|
this.checkMainEntity(this.receipt);
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
id: {
|
id: {
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
SELECT
|
|
||||||
c.name,
|
|
||||||
c.socialName,
|
|
||||||
c.street,
|
|
||||||
c.fi,
|
|
||||||
c.city,
|
|
||||||
r.invoiceFk,
|
|
||||||
r.amountPaid,
|
|
||||||
r.payed
|
|
||||||
FROM client c
|
|
||||||
JOIN receipt r ON r.clientFk = c.id
|
|
||||||
WHERE r.id = ?
|
|
|
@ -1,8 +0,0 @@
|
||||||
SELECT
|
|
||||||
s.name,
|
|
||||||
s.nif,
|
|
||||||
s.street,
|
|
||||||
s.city
|
|
||||||
FROM supplier s
|
|
||||||
JOIN receipt r ON r.companyFk = s.id
|
|
||||||
WHERE r.id = ?;
|
|
|
@ -51,7 +51,7 @@
|
||||||
<td>{{entry.supplierName}}</td>
|
<td>{{entry.supplierName}}</td>
|
||||||
<td>{{entry.reference}}</td>
|
<td>{{entry.reference}}</td>
|
||||||
<td class="number">{{entry.volumeKg | number($i18n.locale)}}</td>
|
<td class="number">{{entry.volumeKg | number($i18n.locale)}}</td>
|
||||||
<td class="number">{{entry.loadedKg | number($i18n.locale)}}</td>
|
<td class="number">{{entry.loadedkg | number($i18n.locale)}}</td>
|
||||||
<td class="number">{{entry.stickers}}</td>
|
<td class="number">{{entry.stickers}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="!travel.entries">
|
<tr v-if="!travel.entries">
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue