fixes #4074 Descargar ACL del usuario actual #1255
|
@ -4,5 +4,7 @@
|
|||
"files.eol": "\n",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
},
|
||||
"search.useIgnoreFiles": false,
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
}
|
||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -5,13 +5,21 @@ 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/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2312.01] - 2023-04-06
|
||||
|
||||
### Added
|
||||
- (Monitor tickets) Muestra un icono al lado de la zona, si el ticket es frágil y se envía por agencia
|
||||
|
||||
### Changed
|
||||
- (Envíos -> Extra comunitarios) Se agrupan las entradas del mismo travel. Añadidos campos Referencia y Importe.
|
||||
|
||||
### Fixed
|
||||
-
|
||||
|
||||
## [2310.01] - 2023-03-23
|
||||
|
||||
### Added
|
||||
-
|
||||
|
||||
### Changed
|
||||
-
|
||||
- (Trabajadores -> Control de horario) Ahora se puede confirmar/no confirmar el registro horario de cada semana desde esta sección
|
||||
|
||||
### Fixed
|
||||
- (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo"
|
||||
|
|
|
@ -10,6 +10,7 @@ RUN apt-get update \
|
|||
curl \
|
||||
ca-certificates \
|
||||
gnupg2 \
|
||||
graphicsmagick \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& npm install -g npm@8.19.2
|
||||
|
|
|
@ -30,16 +30,23 @@ module.exports = Self => {
|
|||
const recipient = to.replace('@', '');
|
||||
|
||||
if (sender.name != recipient) {
|
||||
await models.Chat.create({
|
||||
const chat = await models.Chat.create({
|
||||
senderFk: sender.id,
|
||||
recipient: to,
|
||||
dated: Date.vnNew(),
|
||||
checkUserStatus: 0,
|
||||
message: message,
|
||||
status: 0,
|
||||
status: 'sending',
|
||||
attempts: 0
|
||||
});
|
||||
|
||||
try {
|
||||
await Self.sendMessage(chat.senderFk, chat.recipient, chat.message);
|
||||
await Self.updateChat(chat, 'sent');
|
||||
} catch (error) {
|
||||
await Self.updateChat(chat, 'error', error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,18 +24,13 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.sendCheckingPresence = async(ctx, recipientId, message, options) => {
|
||||
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
||||
if (!recipientId) return false;
|
||||
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const sender = await models.Account.findById(userId);
|
||||
const recipient = await models.Account.findById(recipientId, null, myOptions);
|
||||
const sender = await models.Account.findById(userId, {fields: ['id']});
|
||||
const recipient = await models.Account.findById(recipientId, null);
|
||||
|
||||
// Prevent sending messages to yourself
|
||||
if (recipientId == userId) return false;
|
||||
|
@ -46,16 +41,23 @@ module.exports = Self => {
|
|||
if (process.env.NODE_ENV == 'test')
|
||||
message = `[Test:Environment to user ${userId}] ` + message;
|
||||
|
||||
await models.Chat.create({
|
||||
const chat = await models.Chat.create({
|
||||
senderFk: sender.id,
|
||||
recipient: `@${recipient.name}`,
|
||||
dated: Date.vnNew(),
|
||||
checkUserStatus: 1,
|
||||
message: message,
|
||||
status: 0,
|
||||
status: 'sending',
|
||||
attempts: 0
|
||||
});
|
||||
|
||||
try {
|
||||
await Self.sendCheckingUserStatus(chat);
|
||||
await Self.updateChat(chat, 'sent');
|
||||
} catch (error) {
|
||||
await Self.updateChat(chat, 'error', error);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -3,7 +3,6 @@ module.exports = Self => {
|
|||
Self.remoteMethodCtx('sendQueued', {
|
||||
description: 'Send a RocketChat message',
|
||||
accessType: 'WRITE',
|
||||
accepts: [],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
|
@ -16,14 +15,17 @@ module.exports = Self => {
|
|||
|
||||
Self.sendQueued = async() => {
|
||||
const models = Self.app.models;
|
||||
const maxAttempts = 3;
|
||||
const sentStatus = 1;
|
||||
const errorStatus = 2;
|
||||
|
||||
const chats = await models.Chat.find({
|
||||
where: {
|
||||
status: {neq: sentStatus},
|
||||
attempts: {lt: maxAttempts}
|
||||
status: {
|
||||
nin: [
|
||||
'sent',
|
||||
'sending'
|
||||
]
|
||||
|
||||
},
|
||||
attempts: {lt: 3}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -31,16 +33,16 @@ module.exports = Self => {
|
|||
if (chat.checkUserStatus) {
|
||||
try {
|
||||
await Self.sendCheckingUserStatus(chat);
|
||||
await updateChat(chat, sentStatus);
|
||||
await Self.updateChat(chat, 'sent');
|
||||
} catch (error) {
|
||||
await updateChat(chat, errorStatus, error);
|
||||
await Self.updateChat(chat, 'error', error);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await Self.sendMessage(chat.senderFk, chat.recipient, chat.message);
|
||||
await updateChat(chat, sentStatus);
|
||||
await Self.updateChat(chat, 'sent');
|
||||
} catch (error) {
|
||||
await updateChat(chat, errorStatus, error);
|
||||
await Self.updateChat(chat, 'error', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,15 +130,17 @@ module.exports = Self => {
|
|||
* @param {object} chat - The chat
|
||||
* @param {string} status - The new status
|
||||
* @param {string} error - The error
|
||||
* @param {object} options - Query options
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
async function updateChat(chat, status, error) {
|
||||
|
||||
Self.updateChat = async(chat, status, error) => {
|
||||
return chat.updateAttributes({
|
||||
status: status,
|
||||
attempts: ++chat.attempts,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current user status on Rocketchat
|
||||
|
|
|
@ -10,7 +10,7 @@ describe('Chat sendCheckingPresence()', () => {
|
|||
|
||||
const chat = {
|
||||
checkUserStatus: 1,
|
||||
status: 0,
|
||||
status: 'pending',
|
||||
attempts: 0
|
||||
};
|
||||
|
||||
|
@ -27,7 +27,7 @@ describe('Chat sendCheckingPresence()', () => {
|
|||
|
||||
const chat = {
|
||||
checkUserStatus: 0,
|
||||
status: 0,
|
||||
status: 'pending',
|
||||
attempts: 0
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('previousLabel', {
|
||||
description: 'Returns the previa label pdf',
|
||||
|
@ -33,17 +31,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.previousLabel = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.previousLabel = (ctx, id) => Self.printReport(ctx, id, 'previa-label');
|
||||
};
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('setSaleQuantity()', () => {
|
||||
beforeAll(async() => {
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 9},
|
||||
http: {
|
||||
req: {
|
||||
headers: {origin: 'http://localhost'}
|
||||
}
|
||||
}
|
||||
};
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
});
|
||||
|
||||
it('should change quantity sale', async() => {
|
||||
const tx = await models.Ticket.beginTransaction({});
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -132,7 +132,7 @@ module.exports = Self => {
|
|||
WHERE u.id = ?`, [userId], options);
|
||||
|
||||
let roles = [];
|
||||
for (role of result)
|
||||
for (const role of result)
|
||||
roles.push(role.name);
|
||||
|
||||
return roles;
|
||||
|
|
|
@ -9,17 +9,29 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "number",
|
||||
"forceId": false
|
||||
"type": "number"
|
||||
},
|
||||
"date": {
|
||||
"created": {
|
||||
"type": "date"
|
||||
},
|
||||
"m3":{
|
||||
"longitude":{
|
||||
"type": "number"
|
||||
},
|
||||
"warehouseFk":{
|
||||
"latitude":{
|
||||
"type": "number"
|
||||
},
|
||||
"dated":{
|
||||
"type": "date"
|
||||
},
|
||||
"ticketFk":{
|
||||
"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/updateFile')(Self);
|
||||
require('../methods/dms/deleteTrashFiles')(Self);
|
||||
require('../methods/dms/saveSign')(Self);
|
||||
|
||||
Self.checkRole = async function(ctx, id) {
|
||||
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 ;
|
|
@ -0,0 +1,6 @@
|
|||
ALTER TABLE vn.invoiceOutSerial
|
||||
ADD `type` ENUM('global', 'quick') DEFAULT NULL NULL;
|
||||
|
||||
UPDATE vn.invoiceOutSerial
|
||||
SET type = 'global'
|
||||
WHERE code IN ('A','V');
|
|
@ -2,13 +2,15 @@ DROP FUNCTION IF EXISTS `vn`.`invoiceOut_getWeight`;
|
|||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceOut_getWeight`(vInvoice VARCHAR(15)) RETURNS decimal(10,2)
|
||||
CREATE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceOut_getWeight`(
|
||||
vInvoiceRef VARCHAR(15)
|
||||
)RETURNS decimal(10,2)
|
||||
READS SQL DATA
|
||||
BEGIN
|
||||
/**
|
||||
* Calcula el peso de una factura emitida
|
||||
*
|
||||
* @param vInvoice Id de la factura
|
||||
* @param vInvoiceRef referencia de la factura
|
||||
* @return vTotalWeight peso de la factura
|
||||
*/
|
||||
DECLARE vTotalWeight DECIMAL(10,2);
|
||||
|
@ -22,7 +24,7 @@ BEGIN
|
|||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN itemCost ic ON ic.itemFk = i.id
|
||||
AND ic.warehouseFk = t.warehouseFk
|
||||
WHERE t.refFk = vInvoice
|
||||
WHERE t.refFk = vInvoiceRef
|
||||
AND i.intrastatFk;
|
||||
|
||||
RETURN vTotalWeight;
|
|
@ -0,0 +1,6 @@
|
|||
UPDATE `vn`.`report`
|
||||
SET `method`='InvoiceOuts/{refFk}/invoice-out-pdf'
|
||||
WHERE name='invoice';
|
||||
|
||||
ALTER TABLE `vn`.`printQueue` MODIFY COLUMN printerFk tinyint(3) unsigned DEFAULT 82 NOT NULL;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
DROP FUNCTION IF EXISTS `vn`.`invoiceOut_getMaxIssued`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`invoiceOut_getMaxIssued`(
|
||||
vSerial VARCHAR(2),
|
||||
vCompanyFk INT,
|
||||
vYear INT
|
||||
) RETURNS DATE
|
||||
READS SQL DATA
|
||||
BEGIN
|
||||
/**
|
||||
* Retorna la fecha a partir de la cual es válido emitir una factura
|
||||
*
|
||||
* @param vSerial Serie de facturación
|
||||
* @param vCompanyFk Empresa factura emitida
|
||||
* @param vYear Año contable
|
||||
* @return vInvoiceOutIssued fecha factura válida
|
||||
*/
|
||||
DECLARE vInvoiceOutIssued DATE;
|
||||
DECLARE vFirstDayOfYear DATE;
|
||||
|
||||
SET vFirstDayOfYear := MAKEDATE(vYear, 1);
|
||||
|
||||
SELECT IFNULL(MAX(io.issued), vFirstDayOfYear) INTO vInvoiceOutIssued
|
||||
FROM invoiceOut io
|
||||
WHERE io.serial = vSerial
|
||||
AND io.companyFk = vCompanyFk
|
||||
AND io.issued BETWEEN vFirstDayOfYear
|
||||
AND util.lastDayOfYear(vFirstDayOfYear);
|
||||
|
||||
RETURN vInvoiceOutIssued;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,258 @@
|
|||
DROP PROCEDURE IF EXISTS `vn`.`invoiceOut_new`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceOut_new`(
|
||||
vSerial VARCHAR(255),
|
||||
vInvoiceDate DATE,
|
||||
vTaxArea VARCHAR(25),
|
||||
OUT vNewInvoiceId INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Creación de facturas emitidas.
|
||||
* requiere previamente tabla ticketToInvoice(id).
|
||||
*
|
||||
* @param vSerial serie a la cual se hace la factura
|
||||
* @param vInvoiceDate fecha de la factura
|
||||
* @param vTaxArea tipo de iva en relacion a la empresa y al cliente
|
||||
* @param vNewInvoiceId id de la factura que se acaba de generar
|
||||
* @return vNewInvoiceId
|
||||
*/
|
||||
DECLARE vIsAnySaleToInvoice BOOL;
|
||||
DECLARE vIsAnyServiceToInvoice BOOL;
|
||||
DECLARE vNewRef VARCHAR(255);
|
||||
DECLARE vWorker INT DEFAULT account.myUser_getId();
|
||||
DECLARE vCompanyFk INT;
|
||||
DECLARE vInterCompanyFk INT;
|
||||
DECLARE vClientFk INT;
|
||||
DECLARE vCplusStandardInvoiceTypeFk INT DEFAULT 1;
|
||||
DECLARE vCplusCorrectingInvoiceTypeFk INT DEFAULT 6;
|
||||
DECLARE vCplusSimplifiedInvoiceTypeFk INT DEFAULT 2;
|
||||
DECLARE vCorrectingSerial VARCHAR(1) DEFAULT 'R';
|
||||
DECLARE vSimplifiedSerial VARCHAR(1) DEFAULT 'S';
|
||||
DECLARE vNewInvoiceInFk INT;
|
||||
DECLARE vIsInterCompany BOOL DEFAULT FALSE;
|
||||
DECLARE vIsCEESerial BOOL DEFAULT FALSE;
|
||||
DECLARE vIsCorrectInvoiceDate BOOL;
|
||||
DECLARE vMaxShipped DATE;
|
||||
|
||||
SET vInvoiceDate = IFNULL(vInvoiceDate, util.CURDATE());
|
||||
|
||||
SELECT t.clientFk,
|
||||
t.companyFk,
|
||||
MAX(DATE(t.shipped)),
|
||||
DATE(vInvoiceDate) >= invoiceOut_getMaxIssued(
|
||||
vSerial,
|
||||
t.companyFk,
|
||||
YEAR(vInvoiceDate))
|
||||
INTO vClientFk,
|
||||
vCompanyFk,
|
||||
vMaxShipped,
|
||||
vIsCorrectInvoiceDate
|
||||
FROM ticketToInvoice tt
|
||||
JOIN ticket t ON t.id = tt.id;
|
||||
|
||||
IF(vMaxShipped > vInvoiceDate) THEN
|
||||
CALL util.throw("Invoice date can't be less than max date");
|
||||
END IF;
|
||||
|
||||
IF NOT vIsCorrectInvoiceDate THEN
|
||||
CALL util.throw('Exists an invoice with a previous date');
|
||||
END IF;
|
||||
|
||||
-- Eliminem de ticketToInvoice els tickets que no han de ser facturats
|
||||
DELETE ti.*
|
||||
FROM ticketToInvoice ti
|
||||
JOIN ticket t ON t.id = ti.id
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
JOIN supplier su ON su.id = t.companyFk
|
||||
JOIN client c ON c.id = t.clientFk
|
||||
LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
|
||||
WHERE (YEAR(t.shipped) < 2001 AND t.isDeleted)
|
||||
OR c.isTaxDataChecked = FALSE
|
||||
OR t.isDeleted
|
||||
OR c.hasToInvoice = FALSE
|
||||
OR itc.id IS NULL;
|
||||
|
||||
SELECT SUM(s.quantity * s.price * (100 - s.discount)/100) <> 0
|
||||
INTO vIsAnySaleToInvoice
|
||||
FROM ticketToInvoice t
|
||||
JOIN sale s ON s.ticketFk = t.id;
|
||||
|
||||
SELECT COUNT(*) > 0 INTO vIsAnyServiceToInvoice
|
||||
FROM ticketToInvoice t
|
||||
JOIN ticketService ts ON ts.ticketFk = t.id;
|
||||
|
||||
IF (vIsAnySaleToInvoice OR vIsAnyServiceToInvoice)
|
||||
AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase())
|
||||
THEN
|
||||
|
||||
-- el trigger añade el siguiente Id_Factura correspondiente a la vSerial
|
||||
INSERT INTO invoiceOut(
|
||||
ref,
|
||||
serial,
|
||||
issued,
|
||||
clientFk,
|
||||
dued,
|
||||
companyFk,
|
||||
cplusInvoiceType477Fk
|
||||
)
|
||||
SELECT
|
||||
1,
|
||||
vSerial,
|
||||
vInvoiceDate,
|
||||
vClientFk,
|
||||
getDueDate(vInvoiceDate, dueDay),
|
||||
vCompanyFk,
|
||||
IF(vSerial = vCorrectingSerial,
|
||||
vCplusCorrectingInvoiceTypeFk,
|
||||
IF(vSerial = vSimplifiedSerial,
|
||||
vCplusSimplifiedInvoiceTypeFk,
|
||||
vCplusStandardInvoiceTypeFk))
|
||||
FROM client
|
||||
WHERE id = vClientFk;
|
||||
|
||||
SET vNewInvoiceId = LAST_INSERT_ID();
|
||||
|
||||
SELECT `ref`
|
||||
INTO vNewRef
|
||||
FROM invoiceOut
|
||||
WHERE id = vNewInvoiceId;
|
||||
|
||||
UPDATE ticket t
|
||||
JOIN ticketToInvoice ti ON ti.id = t.id
|
||||
SET t.refFk = vNewRef;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.updateInter;
|
||||
CREATE TEMPORARY TABLE tmp.updateInter ENGINE = MEMORY
|
||||
SELECT s.id,ti.id ticket_id,vWorker Id_Trabajador
|
||||
FROM ticketToInvoice ti
|
||||
LEFT JOIN ticketState ts ON ti.id = ts.ticket
|
||||
JOIN state s
|
||||
WHERE IFNULL(ts.alertLevel,0) < 3 and s.`code` = getAlert3State(ti.id);
|
||||
|
||||
INSERT INTO ticketTracking(stateFk,ticketFk,workerFk)
|
||||
SELECT * FROM tmp.updateInter;
|
||||
|
||||
INSERT INTO ticketLog (action, userFk, originFk, description)
|
||||
SELECT 'UPDATE', account.myUser_getId(), ti.id, CONCAT('Crea factura ', vNewRef)
|
||||
FROM ticketToInvoice ti;
|
||||
|
||||
CALL invoiceExpenceMake(vNewInvoiceId);
|
||||
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
|
||||
|
||||
UPDATE invoiceOut io
|
||||
JOIN (
|
||||
SELECT SUM(amount) total
|
||||
FROM invoiceOutExpence
|
||||
WHERE invoiceOutFk = vNewInvoiceId
|
||||
) base
|
||||
JOIN (
|
||||
SELECT SUM(vat) total
|
||||
FROM invoiceOutTax
|
||||
WHERE invoiceOutFk = vNewInvoiceId
|
||||
) vat
|
||||
SET io.amount = base.total + vat.total
|
||||
WHERE io.id = vNewInvoiceId;
|
||||
|
||||
DROP TEMPORARY TABLE tmp.updateInter;
|
||||
|
||||
SELECT COUNT(*), id
|
||||
INTO vIsInterCompany, vInterCompanyFk
|
||||
FROM company
|
||||
WHERE clientFk = vClientFk;
|
||||
|
||||
IF (vIsInterCompany) THEN
|
||||
|
||||
INSERT INTO invoiceIn(supplierFk, supplierRef, issued, companyFk)
|
||||
SELECT vCompanyFk, vNewRef, vInvoiceDate, vInterCompanyFk;
|
||||
|
||||
SET vNewInvoiceInFk = LAST_INSERT_ID();
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
|
||||
CREATE TEMPORARY TABLE tmp.ticket
|
||||
(KEY (ticketFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT id ticketFk
|
||||
FROM ticketToInvoice;
|
||||
|
||||
CALL `ticket_getTax`('NATIONAL');
|
||||
|
||||
SET @vTaxableBaseServices := 0.00;
|
||||
SET @vTaxCodeGeneral := NULL;
|
||||
|
||||
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
||||
SELECT vNewInvoiceInFk,
|
||||
@vTaxableBaseServices,
|
||||
sub.expenceFk,
|
||||
sub.taxTypeSageFk,
|
||||
sub.transactionTypeSageFk
|
||||
FROM (
|
||||
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase,
|
||||
i.expenceFk,
|
||||
i.taxTypeSageFk,
|
||||
i.transactionTypeSageFk,
|
||||
@vTaxCodeGeneral := i.taxClassCodeFk
|
||||
FROM tmp.ticketServiceTax tst
|
||||
JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
|
||||
WHERE i.isService
|
||||
HAVING taxableBase
|
||||
) sub;
|
||||
|
||||
INSERT INTO invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
|
||||
SELECT vNewInvoiceInFk,
|
||||
SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral,
|
||||
@vTaxableBaseServices, 0) taxableBase,
|
||||
i.expenceFk,
|
||||
i.taxTypeSageFk ,
|
||||
i.transactionTypeSageFk
|
||||
FROM tmp.ticketTax tt
|
||||
JOIN invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
|
||||
WHERE !i.isService
|
||||
GROUP BY tt.pgcFk
|
||||
HAVING taxableBase
|
||||
ORDER BY tt.priority;
|
||||
|
||||
CALL invoiceInDueDay_calculate(vNewInvoiceInFk);
|
||||
|
||||
SELECT COUNT(*) INTO vIsCEESerial
|
||||
FROM invoiceOutSerial
|
||||
WHERE code = vSerial;
|
||||
|
||||
IF vIsCEESerial THEN
|
||||
|
||||
INSERT INTO invoiceInIntrastat (
|
||||
invoiceInFk,
|
||||
intrastatFk,
|
||||
amount,
|
||||
stems,
|
||||
countryFk,
|
||||
net)
|
||||
SELECT
|
||||
vNewInvoiceInFk,
|
||||
i.intrastatFk,
|
||||
SUM(CAST((s.quantity * s.price * (100 - s.discount) / 100 ) AS DECIMAL(10, 2))),
|
||||
SUM(CAST(IFNULL(i.stems, 1) * s.quantity AS DECIMAL(10, 2))),
|
||||
su.countryFk,
|
||||
CAST(SUM(IFNULL(i.stems, 1)
|
||||
* s.quantity
|
||||
* IF(ic.grams, ic.grams, IFNULL(i.weightByPiece, 0)) / 1000) AS DECIMAL(10, 2))
|
||||
FROM sale s
|
||||
JOIN ticket t ON s.ticketFk = t.id
|
||||
JOIN supplier su ON su.id = t.companyFk
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
LEFT JOIN itemCost ic ON ic.itemFk = i.id AND ic.warehouseFk = t.warehouseFk
|
||||
WHERE t.refFk = vNewRef
|
||||
GROUP BY i.intrastatFk;
|
||||
|
||||
END IF;
|
||||
DROP TEMPORARY TABLE tmp.ticket;
|
||||
DROP TEMPORARY TABLE tmp.ticketAmount;
|
||||
DROP TEMPORARY TABLE tmp.ticketTax;
|
||||
DROP TEMPORARY TABLE tmp.ticketServiceTax;
|
||||
END IF;
|
||||
END IF;
|
||||
DROP TEMPORARY TABLE `ticketToInvoice`;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,141 @@
|
|||
DROP PROCEDURE IF EXISTS `vn`.`ticketPackaging_add`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketPackaging_add`(
|
||||
vClientFk INT,
|
||||
vDated DATE,
|
||||
vCompanyFk INT,
|
||||
vWithoutPeriodGrace BOOLEAN)
|
||||
BEGIN
|
||||
/**
|
||||
* Genera nuevos tickets de embalajes para los clientes no han los han retornado
|
||||
* y actualiza los valores para la tabla ticketPackaging
|
||||
*
|
||||
* @param vClientFk Cliente en caso de NULL todos los clientes
|
||||
* @param vDated Fecha hasta la cual se revisan los embalajes
|
||||
* @param vCompanyFk Empresa de la cual se comprobaran sus clientes
|
||||
* @param vWithoutPeriodGrace si no se aplica el periodo de gracia de un mes
|
||||
*/
|
||||
DECLARE vNewTicket INT;
|
||||
DECLARE vDateStart DATE;
|
||||
DECLARE vDateEnd DATE;
|
||||
DECLARE vGraceDate DATE DEFAULT vDated;
|
||||
DECLARE vWarehouseInventory INT;
|
||||
DECLARE vComponentCost INT;
|
||||
DECLARE vDone INT DEFAULT FALSE;
|
||||
DECLARE vClientId INT;
|
||||
|
||||
DECLARE vCursor CURSOR FOR
|
||||
SELECT DISTINCT clientFk
|
||||
FROM (
|
||||
SELECT clientFk, SUM(quantity) totalQuantity
|
||||
FROM tmp.packagingToInvoice
|
||||
GROUP BY itemFk, clientFk
|
||||
HAVING totalQuantity > 0)sub;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
||||
|
||||
DECLARE EXIT HANDLER FOR SQLEXCEPTION
|
||||
BEGIN
|
||||
ROLLBACK;
|
||||
RESIGNAL;
|
||||
END;
|
||||
|
||||
SELECT id INTO vWarehouseInventory
|
||||
FROM warehouse
|
||||
WHERE `code`= 'inv';
|
||||
|
||||
SELECT id INTO vComponentCost
|
||||
FROM component
|
||||
WHERE `code`= 'purchaseValue';
|
||||
|
||||
SELECT packagingInvoicingDated INTO vDateStart
|
||||
FROM ticketConfig;
|
||||
|
||||
IF vWarehouseInventory IS NULL THEN
|
||||
CALL util.throw('Warehouse inventory not set');
|
||||
END IF;
|
||||
|
||||
IF vComponentCost IS NULL THEN
|
||||
CALL util.throw('Component cost not set');
|
||||
END IF;
|
||||
|
||||
SET vDateEnd = vDated + INTERVAL 1 DAY;
|
||||
|
||||
IF NOT vWithoutPeriodGrace THEN
|
||||
SET vGraceDate = vGraceDate -INTERVAL 1 MONTH;
|
||||
END IF;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.packagingToInvoice;
|
||||
CREATE TEMPORARY TABLE tmp.packagingToInvoice
|
||||
(INDEX (clientFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT p.itemFk,
|
||||
tp.packagingFk,
|
||||
tp.quantity,
|
||||
tp.ticketFk,
|
||||
p.price,
|
||||
t.clientFk
|
||||
FROM ticketPackaging tp
|
||||
JOIN packaging p ON p.id = tp.packagingFk
|
||||
JOIN ticket t ON t.id = tp.ticketFk
|
||||
JOIN client c ON c.id = t.clientFk
|
||||
WHERE c.isActive
|
||||
AND (vClientFk IS NULL OR t.clientFk = vClientFk)
|
||||
AND t.shipped BETWEEN vDateStart AND vDateEnd
|
||||
AND (tp.quantity < 0 OR (tp.quantity > 0 AND t.shipped < vGraceDate))
|
||||
AND tp.quantity
|
||||
AND p.itemFk;
|
||||
|
||||
OPEN vCursor;
|
||||
l: LOOP
|
||||
|
||||
FETCH vCursor INTO vClientId;
|
||||
|
||||
IF vDone THEN
|
||||
LEAVE l;
|
||||
END IF;
|
||||
|
||||
START TRANSACTION;
|
||||
|
||||
CALL ticket_add(
|
||||
vClientId,
|
||||
vDateEnd,
|
||||
vWarehouseInventory,
|
||||
vCompanyFk,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
vDateEnd,
|
||||
account.myUser_getId(),
|
||||
TRUE,
|
||||
vNewTicket);
|
||||
|
||||
INSERT INTO ticketPackaging(ticketFk, packagingFk, quantity, pvp)
|
||||
SELECT vNewTicket, packagingFk, - SUM(quantity) totalQuantity, price
|
||||
FROM tmp.packagingToInvoice
|
||||
WHERE clientFk = vClientId
|
||||
GROUP BY packagingFk
|
||||
HAVING IF(vWithoutPeriodGrace, totalQuantity <> 0, totalQuantity < 0);
|
||||
|
||||
INSERT INTO sale(ticketFk, itemFk, concept, quantity, price)
|
||||
SELECT vNewTicket, pti.itemFk, i.name, SUM(pti.quantity) totalQuantity, pti.price
|
||||
FROM tmp.packagingToInvoice pti
|
||||
JOIN item i ON i.id = pti.itemFk
|
||||
WHERE pti.clientFk = vClientId
|
||||
GROUP BY pti.itemFk
|
||||
HAVING IF(vWithoutPeriodGrace, totalQuantity <> 0, totalQuantity > 0);
|
||||
|
||||
INSERT INTO saleComponent(saleFk, componentFk, value)
|
||||
SELECT id, vComponentCost, price
|
||||
FROM sale
|
||||
WHERE ticketFk = vNewTicket;
|
||||
|
||||
COMMIT;
|
||||
END LOOP;
|
||||
CLOSE vCursor;
|
||||
|
||||
DROP TEMPORARY TABLE tmp.packagingToInvoice;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES ('Operator', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Operator', '*', 'WRITE', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,16 @@
|
|||
ALTER TABLE `vn`.`chat` ADD statusNew enum('pending','sent','error','sending') DEFAULT 'pending' NOT NULL;
|
||||
|
||||
UPDATE `vn`.`chat`
|
||||
SET statusNew = 'pending'
|
||||
WHERE status = 0;
|
||||
|
||||
UPDATE `vn`.`chat`
|
||||
SET statusNew = 'sent'
|
||||
WHERE status = 1;
|
||||
|
||||
UPDATE `vn`.`chat`
|
||||
SET statusNew = 'error'
|
||||
WHERE status = 2;
|
||||
|
||||
ALTER TABLE `vn`.`chat` CHANGE status status__ tinyint(1) DEFAULT NULL NULL;
|
||||
ALTER TABLE `vn`.`chat` CHANGE statusNew status enum('pending','sent','error','sending') CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT 'pending' NOT NULL;
|
|
@ -0,0 +1,14 @@
|
|||
ALTER TABLE `vn`.`itemType` ADD isFragile tinyint(1) NULL;
|
||||
ALTER TABLE `vn`.`itemType` MODIFY COLUMN isFragile tinyint(1) DEFAULT 0 NOT NULL;
|
||||
|
||||
UPDATE `vn`.`itemType`
|
||||
SET isFragile = 1
|
||||
WHERE code IN ('ZKA', 'ZKE');
|
||||
|
||||
UPDATE `vn`.`itemType`
|
||||
SET isFragile = 1
|
||||
WHERE id IN (SELECT it.id
|
||||
FROM itemCategory ic
|
||||
JOIN itemType it ON it.categoryFk = ic.id
|
||||
WHERE ic.code = 'plant');
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
ALTER TABLE `vn`.`operator` ADD sectorFk int(11) NULL;
|
||||
ALTER TABLE `vn`.`operator` ADD labelerFk tinyint(3) unsigned NULL;
|
||||
ALTER TABLE `vn`.`operator` ADD CONSTRAINT operator_FK_5 FOREIGN KEY (labelerFk) REFERENCES `vn`.`printer`(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
UPDATE `vn`.`operator` o
|
||||
JOIN (SELECT id, sectorFk, labelerFk
|
||||
FROM `vn`.`worker`) sub ON sub.id = o.workerFk
|
||||
SET o.sectorFk = sub.sectorFk,
|
||||
o.labelerFk = sub.labelerFk;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`collection_printSticker`(
|
||||
vSelf INT,
|
||||
vLabelCount INT
|
||||
)
|
||||
BEGIN
|
||||
/**
|
||||
* Prints a yellow label from a collection or a ticket
|
||||
*
|
||||
* @param vSelf collection or ticket
|
||||
* @param vLabelCount number of times the collection has been printed
|
||||
*/
|
||||
DECLARE vPrintArgs JSON DEFAULT JSON_OBJECT('collectionOrTicketFk', vSelf);
|
||||
|
||||
IF vLabelCount IS NULL THEN
|
||||
INSERT INTO ticketTrolley
|
||||
SELECT ticketFk, 1
|
||||
FROM ticketCollection
|
||||
WHERE collectionFk = vSelf
|
||||
ON DUPLICATE KEY UPDATE labelCount = labelCount + 1;
|
||||
ELSE
|
||||
SET vPrintArgs = JSON_MERGE_PATCH(vPrintArgs, JSON_OBJECT('labelCount', vLabelCount));
|
||||
END IF;
|
||||
|
||||
CALL report_print(
|
||||
'LabelCollection',
|
||||
(SELECT o.labelerFk FROM operator o WHERE o.workerFk = account.myUser_getId()),
|
||||
account.myUser_getId(),
|
||||
vPrintArgs,
|
||||
'high'
|
||||
);
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`expeditionPallet_printLabel`(vSelf INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Calls the report_print procedure and passes it
|
||||
* the necessary parameters for printing.
|
||||
*
|
||||
* @param vSelf expeditioPallet id.
|
||||
*/
|
||||
DECLARE vPrinterFk INT;
|
||||
DECLARE vUserFk INT DEFAULT account.myUser_getId();
|
||||
|
||||
SELECT o.labelerFk INTO vPrinterFk
|
||||
FROM operator o
|
||||
WHERE o.workerFk = vUserFk;
|
||||
|
||||
CALL vn.report_print(
|
||||
'LabelPalletExpedition',
|
||||
vPrinterFk,
|
||||
account.myUser_getId(),
|
||||
JSON_OBJECT('palletFk', vSelf, 'userFk', vUserFk),
|
||||
'high'
|
||||
);
|
||||
|
||||
UPDATE vn.expeditionPallet
|
||||
SET isPrint = TRUE
|
||||
WHERE id = vSelf;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelving_getAlternatives`(vShelvingFk VARCHAR(10))
|
||||
BEGIN
|
||||
/**
|
||||
* Devuelve un listado de posibles ubicaciones alternativas a ubicar los item de la matricula
|
||||
* del carro que se le ha pasado.
|
||||
*
|
||||
* @param vShelvingFk matricula del carro
|
||||
*/
|
||||
SELECT is2.id,is2.shelvingFk , p.code, is2.itemFk , is2.visible, p.pickingOrder
|
||||
FROM itemShelving is2
|
||||
JOIN shelving sh ON sh.code = is2.shelvingFk
|
||||
JOIN parking p ON p.id = sh.parkingFk
|
||||
JOIN sector s ON s.id = p.sectorFk
|
||||
LEFT JOIN operator o ON o.sectorFk = s.id
|
||||
LEFT JOIN worker w ON w.sectorFk = s.id AND w.id = account.myUser_getId()
|
||||
JOIN warehouse wh ON wh.id = s.warehouseFk
|
||||
JOIN itemShelving is3 ON is3.itemFk = is2.itemFk AND is3.shelvingFk = vShelvingFk COLLATE utf8_unicode_ci
|
||||
WHERE is2.shelvingFk <> vShelvingFk COLLATE utf8_unicode_ci
|
||||
GROUP BY is2.id
|
||||
ORDER BY p.pickingOrder DESC;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`operator_beforeInsert`
|
||||
BEFORE INSERT ON `operator`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
CALL vn.printer_checkSector(NEW.labelerFk, NEW.sectorFk);
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`operator_beforeUpdate`
|
||||
BEFORE UPDATE ON `operator`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NOT (NEW.labelerFk <=> OLD.labelerFk AND NEW.sectorFk <=> OLD.sectorFk) THEN
|
||||
CALL vn.printer_checkSector(NEW.labelerFk, NEW.sectorFk);
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`worker_beforeUpdate`
|
||||
BEFORE UPDATE ON `worker`
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
IF NOT (NEW.labelerFk <=> OLD.labelerFk AND NEW.sectorFk <=> OLD.sectorFk) THEN
|
||||
CALL vn.printer_checkSector(NEW.labelerFk, NEW.sectorFk);
|
||||
|
||||
INSERT IGNORE INTO vn.operator (workerFk)
|
||||
VALUES (NEW.id);
|
||||
|
||||
UPDATE operator
|
||||
SET labelerFk = NEW.labelerFk,
|
||||
sectorFk = NEW.sectorFk
|
||||
WHERE workerFk = NEW.id;
|
||||
END IF;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
||||
SQL SECURITY DEFINER
|
||||
VIEW `vn`.`operatorWorkerCode`
|
||||
AS SELECT `o`.`workerFk` AS `workerFk`,
|
||||
concat(`w`.`firstName`, ' ', `w`.`lastName`) AS `fullName`,
|
||||
`w`.`code` AS `code`,
|
||||
`o`.`numberOfWagons` AS `numberOfWagons`
|
||||
FROM (
|
||||
(
|
||||
`vn`.`worker` `w`
|
||||
JOIN `vn`.`operator` `o` ON(`o`.`workerFk` = `w`.`id`)
|
||||
)
|
||||
JOIN `vn`.`sector` `s` ON(`o`.`sectorFk` = `s`.`id`)
|
||||
)
|
||||
WHERE `o`.`sectorFk` IS NOT NULL
|
||||
AND `s`.`code` IN (
|
||||
'H2',
|
||||
'H2',
|
||||
'PEQUES_H',
|
||||
'ALTILLO COMP',
|
||||
'ALTILLO ARTI'
|
||||
)
|
|
@ -0,0 +1,47 @@
|
|||
DROP PROCEDURE IF EXISTS `vn`.`ticket_getWarnings`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE PROCEDURE `vn`.`ticket_getWarnings`()
|
||||
BEGIN
|
||||
/**
|
||||
* Calcula las adventencias para un conjunto de tickets.
|
||||
* Agrupados por ticket
|
||||
*
|
||||
* @table tmp.sale_getWarnings(ticketFk) Identificadores de los tickets a calcular
|
||||
* @return tmp.ticket_warnings
|
||||
*/
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.sale_warnings;
|
||||
CREATE TEMPORARY TABLE tmp.sale_warnings (
|
||||
ticketFk INT(11),
|
||||
saleFk INT(11),
|
||||
isFragile INTEGER(1) DEFAULT 0,
|
||||
PRIMARY KEY (ticketFk, saleFk)
|
||||
) ENGINE = MEMORY;
|
||||
|
||||
-- Frágil
|
||||
INSERT INTO tmp.sale_warnings(ticketFk, saleFk, isFragile)
|
||||
SELECT tt.ticketFk, s.id, TRUE
|
||||
FROM tmp.sale_getWarnings tt
|
||||
LEFT JOIN sale s ON s.ticketFk = tt.ticketFk
|
||||
LEFT JOIN item i ON i.id = s.itemFk
|
||||
LEFT JOIN itemType it ON it.id = i.typeFk
|
||||
LEFT JOIN agencyMode am ON am.id = tt.agencyModeFk
|
||||
LEFT JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
|
||||
WHERE dm.code IN ('AGENCY')
|
||||
AND it.isFragile;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.ticket_warnings;
|
||||
CREATE TEMPORARY TABLE tmp.ticket_warnings
|
||||
(PRIMARY KEY (ticketFk))
|
||||
ENGINE = MEMORY
|
||||
SELECT
|
||||
sw.ticketFk,
|
||||
MAX(sw.isFragile) AS isFragile
|
||||
FROM tmp.sale_warnings sw
|
||||
GROUP BY sw.ticketFk;
|
||||
|
||||
DROP TEMPORARY TABLE
|
||||
tmp.sale_warnings;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -164,7 +164,7 @@ INSERT INTO `vn`.`warehouse`(`id`, `name`, `code`, `isComparative`, `isInventory
|
|||
(3, 'Warehouse Three', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
||||
(4, 'Warehouse Four', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
||||
(5, 'Warehouse Five', NULL, 1, 1, 1, 1, 0, 0, 2, 1, 1),
|
||||
(13, 'Inventory', NULL, 1, 1, 1, 0, 0, 0, 2, 1, 0),
|
||||
(13, 'Inventory', 'inv', 1, 1, 1, 0, 0, 0, 2, 1, 0),
|
||||
(60, 'Algemesi', NULL, 1, 1, 1, 0, 0, 0, 2, 1, 0);
|
||||
|
||||
|
||||
|
@ -173,10 +173,15 @@ INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPrepare
|
|||
(1, 'First sector', 1, 1, 'FIRST'),
|
||||
(2, 'Second sector', 2, 0, 'SECOND');
|
||||
|
||||
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`)
|
||||
INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`)
|
||||
VALUES ('1106', '1', '1', 'H', '1', '1', '1'),
|
||||
('1107', '1', '1', 'V', '1', '2', '1');
|
||||
|
||||
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAddress`)
|
||||
VALUES
|
||||
(1, 'printer1', 'path1', 0, 1),
|
||||
(2, 'printer2', 'path2', 1, 1);
|
||||
(1, 'printer1', 'path1', 0, 1 , NULL),
|
||||
(2, 'printer2', 'path2', 1, 1 , NULL),
|
||||
(4, 'printer4', 'path4', 0, NULL, '10.1.10.4');
|
||||
|
||||
INSERT INTO `vn`.`worker`(`id`, `code`, `firstName`, `lastName`, `userFk`,`bossFk`, `phone`, `sectorFk`, `labelerFk`)
|
||||
VALUES
|
||||
|
@ -294,7 +299,8 @@ INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt
|
|||
INSERT INTO `vn`.`payDem`(`id`, `payDem`)
|
||||
VALUES
|
||||
(1, 10),
|
||||
(2, 20);
|
||||
(2, 20),
|
||||
(7, 0);
|
||||
|
||||
INSERT INTO `vn`.`autonomy`(`id`, `name`, `countryFk`)
|
||||
VALUES
|
||||
|
@ -495,7 +501,8 @@ INSERT INTO `vn`.`observationType`(`id`,`description`, `code`)
|
|||
(3, 'Delivery', 'delivery'),
|
||||
(4, 'SalesPerson', 'salesPerson'),
|
||||
(5, 'Administrative', 'administrative'),
|
||||
(6, 'Weight', 'weight');
|
||||
(6, 'Weight', 'weight'),
|
||||
(7, 'InvoiceOut', 'invoiceOut');
|
||||
|
||||
INSERT INTO `vn`.`addressObservation`(`id`,`addressFk`,`observationTypeFk`,`description`)
|
||||
VALUES
|
||||
|
@ -571,14 +578,13 @@ INSERT INTO `vn`.`taxArea` (`code`, `claveOperacionFactura`, `CodigoTransaccion`
|
|||
('NATIONAL', 0, 1),
|
||||
('WORLD', 2, 15);
|
||||
|
||||
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`)
|
||||
INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaFk`, `isCEE`, `type`)
|
||||
VALUES
|
||||
('A', 'Global nacional', 1, 'NATIONAL', 0),
|
||||
('T', 'Española rapida', 1, 'NATIONAL', 0),
|
||||
('V', 'Intracomunitaria global', 0, 'CEE', 1),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0),
|
||||
('E', 'Exportación rápida', 0, 'WORLD', 0);
|
||||
;
|
||||
('A', 'Global nacional', 1, 'NATIONAL', 0, 'global'),
|
||||
('T', 'Española rapida', 1, 'NATIONAL', 0, 'quick'),
|
||||
('V', 'Intracomunitaria global', 0, 'CEE', 1, 'global'),
|
||||
('M', 'Múltiple nacional', 1, 'NATIONAL', 0, 'quick'),
|
||||
('E', 'Exportación rápida', 0, 'WORLD', 0, 'quick');
|
||||
|
||||
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
||||
VALUES
|
||||
|
@ -733,7 +739,9 @@ INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `des
|
|||
(9, 23, 5, 'care with the dog'),
|
||||
(10, 23, 4, 'Reclama ticket: 8'),
|
||||
(11, 24, 4, 'Reclama ticket: 7'),
|
||||
(12, 11, 3, 'Delivery after 10am');
|
||||
(12, 11, 3, 'Delivery after 10am'),
|
||||
(13, 1, 7, 'observation of ticket one'),
|
||||
(14, 2, 7, 'observation of ticket two');
|
||||
|
||||
-- FIX for state hours on local, inter_afterInsert
|
||||
-- UPDATE vncontrol.inter SET odbc_date = DATE_ADD(util.VN_CURDATE(), INTERVAL -10 SECOND);
|
||||
|
@ -833,14 +841,14 @@ INSERT INTO `vn`.`temperature`(`code`, `name`, `description`)
|
|||
('warm', 'Warm', 'Warm'),
|
||||
('cool', 'Cool', 'Cool');
|
||||
|
||||
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `life`, `workerFk`, `isPackaging`, `temperatureFk`)
|
||||
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `life`, `workerFk`, `isPackaging`, `temperatureFk`, `isFragile`)
|
||||
VALUES
|
||||
(1, 'CRI', 'Crisantemo', 2, 31, 35, 0, 'cool'),
|
||||
(2, 'ITG', 'Anthurium', 1, 31, 35, 0, 'cool'),
|
||||
(3, 'WPN', 'Paniculata', 2, 31, 35, 0, 'cool'),
|
||||
(4, 'PRT', 'Delivery ports', 3, NULL, 35, 1, 'warm'),
|
||||
(5, 'CON', 'Container', 3, NULL, 35, 1, 'warm'),
|
||||
(6, 'ALS', 'Alstroemeria', 1, 31, 16, 0, 'warm');
|
||||
(1, 'CRI', 'Crisantemo', 2, 31, 35, 0, 'cool', 0),
|
||||
(2, 'ITG', 'Anthurium', 1, 31, 35, 0, 'cool', 1),
|
||||
(3, 'WPN', 'Paniculata', 2, 31, 35, 0, 'cool', 0),
|
||||
(4, 'PRT', 'Delivery ports', 3, NULL, 35, 1, 'warm', 0),
|
||||
(5, 'CON', 'Container', 3, NULL, 35, 1, 'warm', 0),
|
||||
(6, 'ALS', 'Alstroemeria', 1, 31, 16, 0, 'warm', 1);
|
||||
|
||||
INSERT INTO `vn`.`ink`(`id`, `name`, `picture`, `showOrder`, `hex`)
|
||||
VALUES
|
||||
|
@ -2367,11 +2375,11 @@ INSERT INTO `vn`.`device` (`sn`, `model`, `userFk`)
|
|||
VALUES
|
||||
('aaa', 'android', '9');
|
||||
|
||||
INSERT INTO `vn`.`queuePriority`(`id`, `priority`)
|
||||
INSERT INTO `vn`.`queuePriority`(`id`, `priority`, `code`)
|
||||
VALUES
|
||||
(1, 'Alta'),
|
||||
(2, 'Normal'),
|
||||
(3, 'Baja');
|
||||
(1, 'Alta', 'high'),
|
||||
(2, 'Normal', 'normal'),
|
||||
(3, 'Baja', 'low');
|
||||
|
||||
INSERT INTO `vn`.`workerTimeControlParams` (`id`, `dayBreak`, `weekBreak`, `weekScope`, `dayWorkMax`, `dayStayMax`, `weekMaxBreak`, `weekMaxScope`, `askInOut`)
|
||||
VALUES
|
||||
|
@ -2626,8 +2634,8 @@ INSERT INTO `vn`.`supplierAgencyTerm` (`agencyFk`, `supplierFk`, `minimumPackage
|
|||
|
||||
INSERT INTO `vn`.`chat` (`senderFk`, `recipient`, `dated`, `checkUserStatus`, `message`, `status`, `attempts`)
|
||||
VALUES
|
||||
(1101, '@PetterParker', util.VN_CURDATE(), 1, 'First test message', 0, 0),
|
||||
(1101, '@PetterParker', util.VN_CURDATE(), 0, 'Second test message', 0, 0);
|
||||
(1101, '@PetterParker', util.VN_CURDATE(), 1, 'First test message', 0, 'sent'),
|
||||
(1101, '@PetterParker', util.VN_CURDATE(), 0, 'Second test message', 0, 'pending');
|
||||
|
||||
|
||||
INSERT INTO `vn`.`mobileAppVersionControl` (`appName`, `version`, `isVersionCritical`)
|
||||
|
@ -2783,6 +2791,10 @@ INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
|
|||
('lilium', 'dev', 'http://localhost:8080/#/'),
|
||||
('salix', 'dev', 'http://localhost:5000/#!/');
|
||||
|
||||
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
|
||||
VALUES
|
||||
(3, 'invoice', NULL, 'InvoiceOuts/{refFk}/invoice-out-pdf');
|
||||
|
||||
INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
|
||||
VALUES
|
||||
(1, 1);
|
||||
|
@ -2821,4 +2833,11 @@ INSERT INTO `vn`.`deviceProductionUser` (`deviceProductionFk`, `userFk`, `create
|
|||
(1, 1, 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);
|
||||
|
||||
|
||||
|
|
|
@ -19742,6 +19742,102 @@ DELIMITER ;
|
|||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||
/*!50003 DROP FUNCTION IF EXISTS `CURDATE` */;
|
||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`CURDATE`() RETURNS date
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
/**
|
||||
* @return The mock date
|
||||
**/
|
||||
|
||||
RETURN DATE(mockTime());
|
||||
END ;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||
/*!50003 DROP FUNCTION IF EXISTS `mockTime` */;
|
||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`mockTime`() RETURNS datetime
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
/**
|
||||
* Returns the mockTime with predefined timezone or current dateTime
|
||||
* depending of config.mockEnabled
|
||||
*
|
||||
* @return formatted datetime
|
||||
*/
|
||||
DECLARE vMockEnabled BOOL;
|
||||
|
||||
SELECT mockEnabled INTO vMockEnabled FROM config LIMIT 1;
|
||||
|
||||
IF vMockEnabled THEN
|
||||
RETURN mockTimeBase(FALSE);
|
||||
ELSE
|
||||
RETURN NOW();
|
||||
END IF;
|
||||
END ;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||
/*!50003 DROP FUNCTION IF EXISTS `mockTimeBase` */;
|
||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
|
||||
/*!50003 SET character_set_client = utf8mb4 */ ;
|
||||
/*!50003 SET character_set_results = utf8mb4 */ ;
|
||||
/*!50003 SET collation_connection = utf8mb4_unicode_ci */ ;
|
||||
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
|
||||
/*!50003 SET sql_mode = 'IGNORE_SPACE,NO_ENGINE_SUBSTITUTION' */ ;
|
||||
DELIMITER ;;
|
||||
CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`mockTimeBase`(vIsUtc BOOL) RETURNS datetime
|
||||
DETERMINISTIC
|
||||
BEGIN
|
||||
/**
|
||||
* Returns the date formatted to utc if vIsUtc or config.mocTz if not
|
||||
*
|
||||
* @param vIsUtc If date must be returned as UTC format
|
||||
* @return The formatted mock time
|
||||
*/
|
||||
DECLARE vMockUtcTime DATETIME;
|
||||
DECLARE vMockTz VARCHAR(255);
|
||||
|
||||
SELECT mockUtcTime, mockTz
|
||||
INTO vMockUtcTime, vMockTz
|
||||
FROM config
|
||||
LIMIT 1;
|
||||
|
||||
IF vIsUtc OR vMockTz IS NULL THEN
|
||||
RETURN vMockUtcTime;
|
||||
ELSE
|
||||
RETURN CONVERT_TZ(vMockUtcTime, '+00:00', vMockTz);
|
||||
END IF;
|
||||
END ;;
|
||||
DELIMITER ;
|
||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||
/*!50003 SET collation_connection = @saved_col_connection */ ;
|
||||
/*!50003 DROP FUNCTION IF EXISTS `firstDayOfYear` */;
|
||||
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
|
||||
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
|
||||
|
@ -32632,7 +32728,7 @@ CREATE TABLE `printQueue` (
|
|||
CONSTRAINT `printQueue_printerFk` FOREIGN KEY (`printerFk`) REFERENCES `printer` (`id`) ON UPDATE CASCADE,
|
||||
CONSTRAINT `printQueue_priorityFk` FOREIGN KEY (`priorityFk`) REFERENCES `queuePriority` (`id`) ON UPDATE CASCADE,
|
||||
CONSTRAINT `printQueue_report` FOREIGN KEY (`reportFk`) REFERENCES `report` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDBDEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
|
@ -68286,11 +68382,11 @@ BEGIN
|
|||
FROM ticketConfig;
|
||||
|
||||
IF vWarehouseInventory IS NULL THEN
|
||||
CALL util.throw('Warehouse inventory not seted');
|
||||
CALL util.throw('Warehouse inventory not set');
|
||||
END IF;
|
||||
|
||||
IF vComponentCost IS NULL THEN
|
||||
CALL util.throw('Component cost not seted');
|
||||
CALL util.throw('Component cost not set');
|
||||
END IF;
|
||||
|
||||
SET vDateEnd = vDated + INTERVAL 1 DAY;
|
||||
|
@ -81124,3 +81220,4 @@ USE `vn`;
|
|||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2023-02-21 8:14:30
|
||||
|
||||
|
|
|
@ -524,7 +524,6 @@ export default {
|
|||
},
|
||||
itemLog: {
|
||||
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',
|
||||
},
|
||||
ticketSummary: {
|
||||
header: 'vn-ticket-summary > vn-card > h5',
|
||||
|
@ -1040,7 +1039,6 @@ export default {
|
|||
boss: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bossFk"]',
|
||||
role: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.roleFk"]',
|
||||
iban: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.iban"]',
|
||||
switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
|
||||
createButton: 'vn-worker-create vn-submit[label="Create"]',
|
||||
},
|
||||
workerPda: {
|
||||
|
@ -1052,20 +1050,22 @@ export default {
|
|||
invoiceOutIndex: {
|
||||
topbarSearch: 'vn-searchbar',
|
||||
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
|
||||
createInvoice: 'vn-invoice-out-index > div > vn-vertical > vn-button > button vn-icon[icon="add"]',
|
||||
createManualInvoice: 'vn-item[name="manualInvoice"]',
|
||||
createGlobalInvoice: 'vn-item[name="globalInvoice"]',
|
||||
createInvoice: 'vn-invoice-out-index > div > vn-button > button vn-icon[icon="add"]',
|
||||
manualInvoiceForm: '.vn-invoice-out-manual',
|
||||
manualInvoiceTicket: 'vn-autocomplete[ng-model="$ctrl.invoice.ticketFk"]',
|
||||
manualInvoiceClient: 'vn-autocomplete[ng-model="$ctrl.invoice.clientFk"]',
|
||||
manualInvoiceSerial: 'vn-autocomplete[ng-model="$ctrl.invoice.serial"]',
|
||||
manualInvoiceTaxArea: 'vn-autocomplete[ng-model="$ctrl.invoice.taxArea"]',
|
||||
saveInvoice: 'button[response="accept"]',
|
||||
globalInvoiceForm: '.vn-invoice-out-global-invoicing',
|
||||
globalInvoiceClientsRange: 'vn-radio[val="clientsRange"]',
|
||||
globalInvoiceDate: '[ng-model="$ctrl.invoice.invoiceDate"]',
|
||||
globalInvoiceFromClient: '[ng-model="$ctrl.invoice.fromClientId"]',
|
||||
globalInvoiceToClient: '[ng-model="$ctrl.invoice.toClientId"]',
|
||||
saveInvoice: 'button[response="accept"]'
|
||||
},
|
||||
invoiceOutGlobalInvoicing: {
|
||||
oneClient: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-vertical > vn-radio[val="one"]',
|
||||
allClients: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-vertical > vn-radio[val="all"]',
|
||||
clientId: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-autocomplete[ng-model="$ctrl.clientId"]',
|
||||
printer: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-autocomplete[ng-model="$ctrl.printerFk"]',
|
||||
makeInvoice: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-submit',
|
||||
invoiceDate: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-date-picker[ng-model="$ctrl.invoiceDate"]',
|
||||
maxShipped: 'vn-invoice-out-global-invoicing vn-side-menu form > vn-vertical > vn-date-picker[ng-model="$ctrl.maxShipped"]'
|
||||
},
|
||||
invoiceOutDescriptor: {
|
||||
moreMenu: 'vn-invoice-out-descriptor vn-icon-button[icon=more_vert]',
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('Worker time control path', () => {
|
|||
date.setMonth(date.getMonth() + 1);
|
||||
let month = date.toLocaleString('default', {month: 'long'});
|
||||
|
||||
await page.click(selectors.workerTimeControl.nextMonthButton);
|
||||
await page.waitToClick(selectors.workerTimeControl.nextMonthButton);
|
||||
let result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
|
||||
|
||||
expect(result).toContain(month);
|
||||
|
@ -36,7 +36,7 @@ describe('Worker time control path', () => {
|
|||
date.setDate(1);
|
||||
month = date.toLocaleString('default', {month: 'long'});
|
||||
|
||||
await page.click(selectors.workerTimeControl.previousMonthButton);
|
||||
await page.waitToClick(selectors.workerTimeControl.previousMonthButton);
|
||||
result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
|
||||
|
||||
expect(result).toContain(month);
|
||||
|
@ -49,7 +49,7 @@ describe('Worker time control path', () => {
|
|||
|
||||
await page.loginAndModule('salesBoss', 'worker');
|
||||
await page.goto(`http://localhost:5000/#!/worker/${hankPymId}/time-control?timestamp=${timestamp}`);
|
||||
await page.click(selectors.workerTimeControl.secondWeekDay);
|
||||
await page.waitToClick(selectors.workerTimeControl.secondWeekDay);
|
||||
|
||||
result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ describe('Worker create path', () => {
|
|||
await page.write(selectors.workerCreate.street, 'S/ Doomstadt');
|
||||
await page.write(selectors.workerCreate.email, 'doctorDoom@marvel.com');
|
||||
await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332');
|
||||
await page.autocompleteSearch(selectors.workerCreate.switft, 'BBKKESMMMMM');
|
||||
|
||||
// should check for autocompleted worker code and worker user name
|
||||
const workerCode = await page
|
||||
|
|
|
@ -48,17 +48,17 @@ describe('Item log path', () => {
|
|||
await page.accessToSection('item.card.log');
|
||||
});
|
||||
|
||||
it(`should confirm the log is showing 5 entries`, async() => {
|
||||
it(`should confirm the log is showing 4 entries`, async() => {
|
||||
await page.waitForSelector(selectors.itemLog.anyLineCreated);
|
||||
const anyLineCreatedCount = await page.countElement(selectors.itemLog.anyLineCreated);
|
||||
|
||||
expect(anyLineCreatedCount).toEqual(5);
|
||||
expect(anyLineCreatedCount).toEqual(4);
|
||||
});
|
||||
|
||||
it(`should confirm the log is showing the intrastat for the created item`, async() => {
|
||||
xit(`should confirm the log is showing the intrastat for the created item`, async() => {
|
||||
const fifthLineCreatedProperty = await page
|
||||
.waitToGetProperty(selectors.itemLog.fifthLineCreatedProperty, 'innerText');
|
||||
|
||||
expect(fifthLineCreatedProperty).toEqual('Coral y materiales similares');
|
||||
expect(fifthLineCreatedProperty).toEqual('05080000');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -197,6 +197,7 @@ describe('Ticket Edit sale path', () => {
|
|||
});
|
||||
|
||||
it('should check in the history that logs has been added', async() => {
|
||||
pending('https://redmine.verdnatura.es/issues/5455');
|
||||
await page.reload({waitUntil: ['networkidle0', 'domcontentloaded']});
|
||||
await page.waitToClick(selectors.ticketSales.firstSaleHistoryButton);
|
||||
await page.waitForSelector(selectors.ticketSales.firstSaleHistory);
|
||||
|
|
|
@ -9,7 +9,7 @@ describe('Ticket Create notes path', () => {
|
|||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('employee', 'ticket');
|
||||
await page.accessToSearchResult('1');
|
||||
await page.accessToSearchResult('5');
|
||||
await page.accessToSection('ticket.card.observation');
|
||||
});
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ describe('Ticket Future path', () => {
|
|||
it('should search with the origin IPT', async() => {
|
||||
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
|
||||
|
||||
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'Horizontal');
|
||||
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'H');
|
||||
await page.waitToClick(selectors.ticketFuture.submit);
|
||||
|
||||
expect(httpRequest).toContain('ipt=H');
|
||||
|
@ -65,7 +65,7 @@ describe('Ticket Future path', () => {
|
|||
|
||||
await page.clearInput(selectors.ticketFuture.ipt);
|
||||
|
||||
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'Horizontal');
|
||||
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'H');
|
||||
await page.waitToClick(selectors.ticketFuture.submit);
|
||||
|
||||
expect(httpRequest).toContain('futureIpt=H');
|
||||
|
@ -108,7 +108,7 @@ describe('Ticket Future path', () => {
|
|||
|
||||
it('should search in smart-table with an IPT Destination', async() => {
|
||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||
await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'Horizontal');
|
||||
await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'H');
|
||||
|
||||
expect(httpRequest).toContain('futureIpt');
|
||||
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
|
||||
|
|
|
@ -54,7 +54,7 @@ describe('Ticket Advance path', () => {
|
|||
|
||||
it('should search with the origin IPT', async() => {
|
||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'Horizontal');
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'H');
|
||||
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||
|
||||
expect(httpRequest).toContain('futureIpt=H');
|
||||
|
@ -66,7 +66,7 @@ describe('Ticket Advance path', () => {
|
|||
|
||||
it('should search with the destination IPT', async() => {
|
||||
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal');
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'H');
|
||||
await page.waitToClick(selectors.ticketAdvance.submit);
|
||||
|
||||
expect(httpRequest).toContain('ipt=H');
|
||||
|
@ -78,7 +78,7 @@ describe('Ticket Advance path', () => {
|
|||
|
||||
it('should search in smart-table with an IPT Origin', async() => {
|
||||
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'Vertical');
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'V');
|
||||
|
||||
expect(httpRequest).toContain('futureIpt');
|
||||
|
||||
|
@ -89,7 +89,7 @@ describe('Ticket Advance path', () => {
|
|||
|
||||
it('should search in smart-table with an IPT Destination', async() => {
|
||||
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'Vertical');
|
||||
await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'V');
|
||||
|
||||
expect(httpRequest).toContain('ipt');
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ describe('InvoiceOut manual invoice path', () => {
|
|||
|
||||
it('should open the manual invoice form', async() => {
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
|
||||
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
||||
});
|
||||
|
||||
|
@ -45,7 +44,6 @@ describe('InvoiceOut manual invoice path', () => {
|
|||
|
||||
it('should now open the manual invoice form', async() => {
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createManualInvoice);
|
||||
await page.waitForSelector(selectors.invoiceOutIndex.manualInvoiceForm);
|
||||
});
|
||||
|
||||
|
|
|
@ -17,47 +17,27 @@ describe('InvoiceOut global invoice path', () => {
|
|||
await browser.close();
|
||||
});
|
||||
|
||||
let invoicesBefore;
|
||||
let invoicesBeforeOneClient;
|
||||
let invoicesBeforeAllClients;
|
||||
let now = Date.vnNew();
|
||||
|
||||
it('should count the amount of invoices listed before globla invoces are made', async() => {
|
||||
invoicesBefore = await page.countElement(selectors.invoiceOutIndex.searchResult);
|
||||
invoicesBeforeOneClient = await page.countElement(selectors.invoiceOutIndex.searchResult);
|
||||
|
||||
expect(invoicesBefore).toBeGreaterThanOrEqual(4);
|
||||
expect(invoicesBeforeOneClient).toBeGreaterThanOrEqual(4);
|
||||
});
|
||||
|
||||
it('should open the global invoice form', async() => {
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createGlobalInvoice);
|
||||
await page.waitForSelector(selectors.invoiceOutIndex.globalInvoiceForm);
|
||||
await page.accessToSection('invoiceOut.global-invoicing');
|
||||
});
|
||||
|
||||
it('should create a global invoice for charles xavier today', async() => {
|
||||
await page.pickDate(selectors.invoiceOutIndex.globalInvoiceDate);
|
||||
await page.waitToClick(selectors.invoiceOutIndex.globalInvoiceClientsRange);
|
||||
await page.autocompleteSearch(selectors.invoiceOutIndex.globalInvoiceFromClient, 'Petter Parker');
|
||||
await page.autocompleteSearch(selectors.invoiceOutIndex.globalInvoiceToClient, 'Petter Parker');
|
||||
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should count the amount of invoices listed after globla invocing', async() => {
|
||||
await page.waitToClick('[icon="search"]');
|
||||
await page.waitForTimeout(1000); // index search needs time to return results
|
||||
const currentInvoices = await page.countElement(selectors.invoiceOutIndex.searchResult);
|
||||
|
||||
expect(currentInvoices).toBeGreaterThan(invoicesBefore);
|
||||
});
|
||||
|
||||
it('should create a global invoice for all clients today', async() => {
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createInvoice);
|
||||
await page.waitToClick(selectors.invoiceOutIndex.createGlobalInvoice);
|
||||
await page.waitForSelector(selectors.invoiceOutIndex.globalInvoiceForm);
|
||||
await page.pickDate(selectors.invoiceOutIndex.globalInvoiceDate);
|
||||
await page.waitToClick(selectors.invoiceOutIndex.saveInvoice);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
await page.waitToClick(selectors.invoiceOutGlobalInvoicing.oneClient);
|
||||
await page.autocompleteSearch(selectors.invoiceOutGlobalInvoicing.clientId, '1108');
|
||||
await page.pickDate(selectors.invoiceOutGlobalInvoicing.invoiceDate, now);
|
||||
await page.pickDate(selectors.invoiceOutGlobalInvoicing.maxShipped, now);
|
||||
await page.autocompleteSearch(selectors.invoiceOutGlobalInvoicing.printer, '1');
|
||||
await page.waitToClick(selectors.invoiceOutGlobalInvoicing.makeInvoice);
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -89,11 +89,13 @@ describe('Travel basic data path', () => {
|
|||
});
|
||||
|
||||
it('should navigate to the travel logs', async() => {
|
||||
pending('https://redmine.verdnatura.es/issues/5455');
|
||||
await page.accessToSection('travel.card.log');
|
||||
await page.waitForState('travel.card.log');
|
||||
});
|
||||
|
||||
it('should check the 1st log contains details from the changes made', async() => {
|
||||
pending('https://redmine.verdnatura.es/issues/5455');
|
||||
const result = await page.waitToGetProperty(selectors.travelLog.firstLogFirstTD, 'innerText');
|
||||
|
||||
expect(result).toContain('new reference!');
|
||||
|
|
|
@ -37,6 +37,6 @@ describe('Zone descriptor path', () => {
|
|||
await page.accessToSection('ticket.card.log');
|
||||
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) {
|
||||
super($element, $scope);
|
||||
this.weekDays = vnWeekDays.locales;
|
||||
this.defaultDate = Date.vnNew();
|
||||
this.displayControls = true;
|
||||
this.moment = moment;
|
||||
this.defaultDate = Date.vnNew();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,12 +114,14 @@ export default class Calendar extends FormInput {
|
|||
let day = date.getDate();
|
||||
let wday = date.getDay();
|
||||
let month = date.getMonth();
|
||||
let year = date.getFullYear();
|
||||
|
||||
const currentDay = Date.vnNew().getDate();
|
||||
const currentMonth = Date.vnNew().getMonth();
|
||||
const currentYear = Date.vnNew().getFullYear();
|
||||
|
||||
let classes = {
|
||||
today: day === currentDay && month === currentMonth,
|
||||
today: day === currentDay && month === currentMonth && year === currentYear,
|
||||
weekend: wday === 6 || wday === 0,
|
||||
previous: month < this.month,
|
||||
current: month == this.month,
|
||||
|
@ -207,9 +209,8 @@ export default class Calendar extends FormInput {
|
|||
}
|
||||
|
||||
repeatLast() {
|
||||
if (!this.formatDay) return;
|
||||
|
||||
let days = this.element.querySelectorAll('.days > .day');
|
||||
if (this.formatDay) {
|
||||
const days = this.element.querySelectorAll('.days > .day');
|
||||
for (let i = 0; i < days.length; i++) {
|
||||
this.formatDay({
|
||||
$day: this.days[i],
|
||||
|
@ -217,6 +218,16 @@ export default class Calendar extends FormInput {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.formatWeek) {
|
||||
const weeks = this.element.querySelectorAll('.weeks > .day');
|
||||
for (const week of weeks) {
|
||||
this.formatWeek({
|
||||
$element: week
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Calendar.$inject = ['$element', '$scope', 'vnWeekDays', 'moment'];
|
||||
|
||||
|
@ -228,6 +239,7 @@ ngModule.vnComponent('vnCalendar', {
|
|||
hasEvents: '&?',
|
||||
getClass: '&?',
|
||||
formatDay: '&?',
|
||||
formatWeek: '&?',
|
||||
displayControls: '<?',
|
||||
hideYear: '<?',
|
||||
hideContiguous: '<?',
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
},
|
||||
"node_modules/@uirouter/angularjs": {
|
||||
"version": "1.0.30",
|
||||
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
|
||||
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@uirouter/core": "6.0.8"
|
||||
},
|
||||
|
@ -39,28 +38,22 @@
|
|||
},
|
||||
"node_modules/@uirouter/core": {
|
||||
"version": "6.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
|
||||
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/angular": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
|
||||
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw==",
|
||||
"deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/angular-animate": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
|
||||
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA==",
|
||||
"deprecated": "For the actively supported Angular, see https://www.npmjs.com/package/@angular/core. AngularJS support has officially ended. For extended AngularJS support options, see https://goo.gle/angularjs-path-forward."
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/angular-moment": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
|
||||
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"moment": ">=2.8.0 <3.0.0"
|
||||
},
|
||||
|
@ -70,8 +63,7 @@
|
|||
},
|
||||
"node_modules/angular-translate": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
|
||||
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"angular": "^1.8.0"
|
||||
},
|
||||
|
@ -81,29 +73,25 @@
|
|||
},
|
||||
"node_modules/angular-translate-loader-partial": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
|
||||
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"angular-translate": "~2.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/croppie": {
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
|
||||
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
|
@ -114,8 +102,7 @@
|
|||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
|
@ -126,42 +113,36 @@
|
|||
},
|
||||
"node_modules/mg-crud": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
|
||||
"integrity": "sha512-mAR6t0aQHKnT0QHKHpLOi0kNPZfO36iMpIoiLjFHxuio6mIJyuveBJ4VNlNXJRxLh32/FLADEb41/sYo7QUKFw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"angular": "^1.6.1"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/oclazyload": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
|
||||
"integrity": "sha512-HpOSYUgjtt6sTB/C6+FWsExR+9HCnXKsUA96RWkDXfv11C8Cc9X2DlR0WIZwFIiG6FQU0pwB5dhoYyut8bFAOQ=="
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/require-yaml": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
|
||||
"integrity": "sha512-M6eVEgLPRbeOhgSCnOTtdrOOEQzbXRchg24Xa13c39dMuraFKdI9emUo97Rih0YEFzSICmSKg8w4RQp+rd9pOQ==",
|
||||
"license": "BSD",
|
||||
"dependencies": {
|
||||
"js-yaml": ""
|
||||
}
|
||||
},
|
||||
"node_modules/require-yaml/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/require-yaml/node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
|
@ -171,13 +152,11 @@
|
|||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/validator": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
|
||||
"integrity": "sha512-BylxTwhqwjQI5MDJF7amCy/L0ejJO+74DvCsLV52Lq3+3bhVcVMKqNqOiNcQJm2G48u9EAcw4xFERAmFbwXM9Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
|
@ -186,73 +165,51 @@
|
|||
"dependencies": {
|
||||
"@uirouter/angularjs": {
|
||||
"version": "1.0.30",
|
||||
"resolved": "https://registry.npmjs.org/@uirouter/angularjs/-/angularjs-1.0.30.tgz",
|
||||
"integrity": "sha512-qkc3RFZc91S5K0gc/QVAXc9LGDPXjR04vDgG/11j8+yyZEuQojXxKxdLhKIepiPzqLmGRVqzBmBc27gtqaEeZg==",
|
||||
"requires": {
|
||||
"@uirouter/core": "6.0.8"
|
||||
}
|
||||
},
|
||||
"@uirouter/core": {
|
||||
"version": "6.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@uirouter/core/-/core-6.0.8.tgz",
|
||||
"integrity": "sha512-Gc/BAW47i4L54p8dqYCJJZuv2s3tqlXQ0fvl6Zp2xrblELPVfxmjnc0eurx3XwfQdaqm3T6uls6tQKkof/4QMw=="
|
||||
"version": "6.0.8"
|
||||
},
|
||||
"angular": {
|
||||
"version": "1.8.3",
|
||||
"resolved": "https://registry.npmjs.org/angular/-/angular-1.8.3.tgz",
|
||||
"integrity": "sha512-5qjkWIQQVsHj4Sb5TcEs4WZWpFeVFHXwxEBHUhrny41D8UrBAd6T/6nPPAsLngJCReIOqi95W3mxdveveutpZw=="
|
||||
"version": "1.8.3"
|
||||
},
|
||||
"angular-animate": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/angular-animate/-/angular-animate-1.8.2.tgz",
|
||||
"integrity": "sha512-Jbr9+grNMs9Kj57xuBU3Ju3NOPAjS1+g2UAwwDv7su1lt0/PLDy+9zEwDiu8C8xJceoTbmBNKiWGPJGBdCQLlA=="
|
||||
"version": "1.8.2"
|
||||
},
|
||||
"angular-moment": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.3.0.tgz",
|
||||
"integrity": "sha512-KG8rvO9MoaBLwtGnxTeUveSyNtrL+RNgGl1zqWN36+HDCCVGk2DGWOzqKWB6o+eTTbO3Opn4hupWKIElc8XETA==",
|
||||
"requires": {
|
||||
"moment": ">=2.8.0 <3.0.0"
|
||||
}
|
||||
},
|
||||
"angular-translate": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-translate/-/angular-translate-2.19.0.tgz",
|
||||
"integrity": "sha512-Z/Fip5uUT2N85dPQ0sMEe1JdF5AehcDe4tg/9mWXNDVU531emHCg53ZND9Oe0dyNiGX5rWcJKmsL1Fujus1vGQ==",
|
||||
"requires": {
|
||||
"angular": "^1.8.0"
|
||||
}
|
||||
},
|
||||
"angular-translate-loader-partial": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/angular-translate-loader-partial/-/angular-translate-loader-partial-2.19.0.tgz",
|
||||
"integrity": "sha512-NnMw13LMV4bPQmJK7/pZOZAnPxe0M5OtUHchADs5Gye7V7feonuEnrZ8e1CKhBlv9a7IQyWoqcBa4Lnhg8gk5w==",
|
||||
"requires": {
|
||||
"angular-translate": "~2.19.0"
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"croppie": {
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
|
||||
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
|
||||
"version": "2.6.5"
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
"version": "4.0.1"
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
|
@ -260,39 +217,27 @@
|
|||
},
|
||||
"mg-crud": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/mg-crud/-/mg-crud-1.1.2.tgz",
|
||||
"integrity": "sha512-mAR6t0aQHKnT0QHKHpLOi0kNPZfO36iMpIoiLjFHxuio6mIJyuveBJ4VNlNXJRxLh32/FLADEb41/sYo7QUKFw==",
|
||||
"requires": {
|
||||
"angular": "^1.6.1"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.29.4",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||
"version": "2.29.4"
|
||||
},
|
||||
"oclazyload": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/oclazyload/-/oclazyload-0.6.3.tgz",
|
||||
"integrity": "sha512-HpOSYUgjtt6sTB/C6+FWsExR+9HCnXKsUA96RWkDXfv11C8Cc9X2DlR0WIZwFIiG6FQU0pwB5dhoYyut8bFAOQ=="
|
||||
"version": "0.6.3"
|
||||
},
|
||||
"require-yaml": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require-yaml/-/require-yaml-0.0.1.tgz",
|
||||
"integrity": "sha512-M6eVEgLPRbeOhgSCnOTtdrOOEQzbXRchg24Xa13c39dMuraFKdI9emUo97Rih0YEFzSICmSKg8w4RQp+rd9pOQ==",
|
||||
"requires": {
|
||||
"js-yaml": ""
|
||||
},
|
||||
"dependencies": {
|
||||
"argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
|
||||
"requires": {
|
||||
"argparse": "^2.0.1"
|
||||
}
|
||||
|
@ -300,14 +245,10 @@
|
|||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||
"version": "1.0.3"
|
||||
},
|
||||
"validator": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-6.3.0.tgz",
|
||||
"integrity": "sha512-BylxTwhqwjQI5MDJF7amCy/L0ejJO+74DvCsLV52Lq3+3bhVcVMKqNqOiNcQJm2G48u9EAcw4xFERAmFbwXM9Q=="
|
||||
"version": "6.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
||||
module.exports = function(Self) {
|
||||
|
@ -6,344 +5,11 @@ module.exports = function(Self) {
|
|||
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) {
|
||||
const appModels = ctx.Model.app.models;
|
||||
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;
|
||||
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
|
||||
});
|
||||
|
||||
Self.observe('before delete', async function(ctx) {
|
||||
const appModels = ctx.Model.app.models;
|
||||
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;
|
||||
}
|
||||
}
|
||||
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
|
||||
});
|
||||
|
||||
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/getEnumValues')(Self);
|
||||
require('../methods/vn-model/printService')(Self);
|
||||
|
||||
Object.assign(Self, {
|
||||
setup() {
|
||||
|
|
|
@ -152,5 +152,7 @@
|
|||
"It is not possible to modify sales that their articles are from Floramondo": "It is not possible to modify sales that their articles are from Floramondo",
|
||||
"It is not possible to modify cloned sales": "It is not possible to modify cloned sales",
|
||||
"Valid priorities: 1,2,3": "Valid priorities: 1,2,3",
|
||||
"Warehouse inventory not set": "Almacén inventario no está establecido",
|
||||
"Component cost not set": "Componente coste no está estabecido",
|
||||
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 2"
|
||||
}
|
|
@ -251,6 +251,7 @@
|
|||
"Receipt's bank was not found": "No se encontró el banco del recibo",
|
||||
"This receipt was not compensated": "Este recibo no ha sido compensado",
|
||||
"Client's email was not found": "No se encontró el email del cliente",
|
||||
"Negative basis": "Base negativa",
|
||||
"This worker code already exists": "Este codigo de trabajador ya existe",
|
||||
"This personal mail already exists": "Este correo personal ya existe",
|
||||
"This worker already exists": "Este trabajador ya existe",
|
||||
|
@ -264,7 +265,11 @@
|
|||
"It is not possible to modify cloned sales": "No es posible modificar líneas de pedido clonadas",
|
||||
"A supplier with the same name already exists. Change the country.": "Un proveedor con el mismo nombre ya existe. Cambie el país.",
|
||||
"There is no assigned email for this client": "No hay correo asignado para este cliente",
|
||||
"Exists an invoice with a previous date": "Existe una factura con fecha anterior",
|
||||
"Invoice date can't be less than max date": "La fecha de factura no puede ser inferior a la fecha límite",
|
||||
"Warehouse inventory not set": "El almacén inventario no está establecido",
|
||||
"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}}",
|
||||
"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 MySQL = require('loopback-connector-mysql').MySQL;
|
||||
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
|
||||
const Transaction = require('loopback-connector').Transaction;
|
||||
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 {
|
||||
/**
|
||||
* Promisified version of execute().
|
||||
|
@ -219,6 +252,277 @@ class VnMySQL extends MySQL {
|
|||
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;
|
||||
|
|
|
@ -91,7 +91,11 @@ exports.getChanges = (original, changes) => {
|
|||
const isPrivate = firstChar == '$';
|
||||
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];
|
||||
|
||||
if (original[property] != undefined)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const { Report } = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('claimPickupPdf', {
|
||||
description: 'Returns the claim pickup order pdf',
|
||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.claimPickupPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.claimPickupPdf = (ctx, id) => Self.printReport(ctx, id, 'claim-pickup-order');
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
||||
const {mergeFilters, mergeWhere} = require('vn-loopback/util/filter');
|
||||
const { mergeFilters, mergeWhere } = require('vn-loopback/util/filter');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('logs', {
|
||||
|
@ -12,27 +12,27 @@ module.exports = Self => {
|
|||
arg: 'id',
|
||||
type: 'Number',
|
||||
description: 'The claim id',
|
||||
http: {source: 'path'}
|
||||
http: { source: 'path' }
|
||||
},
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
http: {source: 'query'}
|
||||
http: { source: 'query' }
|
||||
},
|
||||
{
|
||||
arg: 'search',
|
||||
type: 'string',
|
||||
http: {source: 'query'}
|
||||
http: { source: 'query' }
|
||||
},
|
||||
{
|
||||
arg: 'userFk',
|
||||
type: 'number',
|
||||
http: {source: 'query'}
|
||||
http: { source: 'query' }
|
||||
},
|
||||
{
|
||||
arg: 'created',
|
||||
type: 'date',
|
||||
http: {source: 'query'}
|
||||
http: { source: 'query' }
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
|
@ -45,7 +45,7 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.logs = async(ctx, id, filter, options) => {
|
||||
Self.logs = async (ctx, id, filter, options) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
const args = ctx.args;
|
||||
const myOptions = {};
|
||||
|
@ -59,22 +59,22 @@ module.exports = Self => {
|
|||
case 'search':
|
||||
return {
|
||||
or: [
|
||||
{changedModel: {like: `%${value}%`}},
|
||||
{oldInstance: {like: `%${value}%`}}
|
||||
{ changedModel: { like: `%${value}%` } },
|
||||
{ oldInstance: { like: `%${value}%` } }
|
||||
]
|
||||
};
|
||||
case 'userFk':
|
||||
return {'cl.userFk': value};
|
||||
return { 'cl.userFk': value };
|
||||
case 'created':
|
||||
value.setHours(0, 0, 0, 0);
|
||||
to = new Date(value);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
|
||||
return {creationDate: {between: [value, to]}};
|
||||
return { creationDate: { between: [value, to] } };
|
||||
}
|
||||
});
|
||||
where = mergeWhere(where, {['cl.originFk']: id});
|
||||
filter = mergeFilters(args.filter, {where});
|
||||
where = mergeWhere(where, { ['cl.originFk']: id });
|
||||
filter = mergeFilters(args.filter, { where });
|
||||
|
||||
const stmts = [];
|
||||
|
||||
|
@ -102,8 +102,8 @@ module.exports = Self => {
|
|||
const logs = [];
|
||||
for (const row of result) {
|
||||
const changes = [];
|
||||
const oldInstance = JSON.parse(row.oldInstance);
|
||||
const newInstance = JSON.parse(row.newInstance);
|
||||
const oldInstance = JSON.parse(row.oldInstance) || {};
|
||||
const newInstance = JSON.parse(row.newInstance) || {};
|
||||
const mergedProperties = [...Object.keys(oldInstance), ...Object.keys(newInstance)];
|
||||
const properties = new Set(mergedProperties);
|
||||
for (const property of properties) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('claim regularizeClaim()', () => {
|
||||
const userId = 18;
|
||||
|
@ -39,6 +40,20 @@ describe('claim regularizeClaim()', () => {
|
|||
return await models.ClaimEnd.create(claimEnds, options);
|
||||
}
|
||||
|
||||
beforeAll(async() => {
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 9},
|
||||
http: {
|
||||
req: {
|
||||
headers: {origin: 'http://localhost'}
|
||||
}
|
||||
}
|
||||
};
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
});
|
||||
|
||||
it('should send a chat message with value "Trash" and then change claim state to resolved', async() => {
|
||||
const tx = await models.Claim.beginTransaction({});
|
||||
|
||||
|
|
|
@ -82,6 +82,11 @@
|
|||
"type": "hasMany",
|
||||
"model": "ClaimDms",
|
||||
"foreignKey": "claimFk"
|
||||
},
|
||||
"lines": {
|
||||
"type": "hasMany",
|
||||
"model": "ClaimBeginning",
|
||||
"foreignKey": "claimFk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,20 @@
|
|||
icon="icon-ticket">
|
||||
</vn-quick-link>
|
||||
</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>
|
||||
</slot-body>
|
||||
</vn-descriptor-content>
|
||||
|
|
|
@ -151,7 +151,7 @@ class Controller extends Section {
|
|||
isClaimEditable() {
|
||||
if (!this.claim) return;
|
||||
|
||||
this.$http.get(`ClaimStates/${this.claim.id}/isEditable`).then(res => {
|
||||
this.$http.get(`ClaimStates/${this.claim.claimStateFk}/isEditable`).then(res => {
|
||||
this.isRewritable = res.data;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ describe('claim', () => {
|
|||
controller = $componentController('vnClaimDetail', {$element, $scope});
|
||||
controller.claim = {
|
||||
ticketFk: 1,
|
||||
id: 2}
|
||||
id: 2,
|
||||
claimStateFk: 2}
|
||||
;
|
||||
controller.salesToClaim = [{saleFk: 1}, {saleFk: 2}];
|
||||
controller.salesClaimed = [{id: 1, sale: {}}];
|
||||
|
|
|
@ -18,3 +18,5 @@ Claim deleted!: Reclamación eliminada!
|
|||
claim: reclamación
|
||||
Photos: Fotos
|
||||
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 => {
|
||||
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();
|
||||
};
|
||||
Self.campaignMetricsEmail = ctx => Self.sendTemplate(ctx, 'campaign-metrics');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('campaignMetricsPdf', {
|
||||
description: 'Returns the campaign metrics pdf',
|
||||
|
@ -50,17 +48,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.campaignMetricsPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.campaignMetricsPdf = (ctx, id) => Self.printReport(ctx, id, 'campaign-metrics');
|
||||
};
|
||||
|
|
|
@ -46,19 +46,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.clientDebtStatementEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.clientDebtStatementEmail = ctx => Self.sendTemplate(ctx, 'client-debt-statement');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('clientDebtStatementHtml', {
|
||||
description: 'Returns the client debt statement email preview',
|
||||
|
@ -45,21 +43,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.clientDebtStatementHtml = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.clientDebtStatementHtml = (ctx, id) => Self.printEmail(ctx, id, 'client-debt-statement');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('clientDebtStatementPdf', {
|
||||
description: 'Returns the client debt statement pdf',
|
||||
|
@ -45,17 +43,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.clientDebtStatementPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.clientDebtStatementPdf = (ctx, id) => Self.printReport(ctx, id, 'client-debt-statement');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('clientWelcomeEmail', {
|
||||
description: 'Sends the client welcome email with an attached PDF',
|
||||
|
@ -41,19 +39,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.clientWelcomeEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.clientWelcomeEmail = ctx => Self.sendTemplate(ctx, 'client-welcome');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('clientWelcomeHtml', {
|
||||
description: 'Returns the client welcome email preview',
|
||||
|
@ -40,19 +38,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.clientWelcomeHtml = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.clientWelcomeHtml = (ctx, id) => Self.printEmail(ctx, id, 'client-welcome');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('clientCreditEmail', {
|
||||
description: 'Sends the credit request email with an attached PDF',
|
||||
|
@ -10,7 +8,7 @@ module.exports = Self => {
|
|||
type: 'number',
|
||||
required: true,
|
||||
description: 'The client id',
|
||||
http: {source: 'path'}
|
||||
http: {source: 'path'},
|
||||
},
|
||||
{
|
||||
arg: 'recipient',
|
||||
|
@ -22,38 +20,25 @@ module.exports = Self => {
|
|||
arg: 'replyTo',
|
||||
type: 'string',
|
||||
description: 'The sender email to reply to',
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
arg: 'recipientId',
|
||||
type: 'number',
|
||||
description: 'The recipient id to send to the recipient preferred language',
|
||||
required: false
|
||||
}
|
||||
description:
|
||||
'The recipient id to send to the recipient preferred language',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
root: true,
|
||||
},
|
||||
http: {
|
||||
path: '/:id/credit-request-email',
|
||||
verb: 'POST'
|
||||
}
|
||||
verb: 'POST',
|
||||
},
|
||||
});
|
||||
|
||||
Self.clientCreditEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.clientCreditEmail = ctx => Self.sendTemplate(ctx, 'credit-request');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('creditRequestHtml', {
|
||||
description: 'Returns the credit request email preview',
|
||||
|
@ -40,21 +38,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.creditRequestHtml = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.creditRequestHtml = (ctx, id) => Self.printEmail(ctx, id, 'credit-request');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('creditRequestPdf', {
|
||||
description: 'Returns the credit request pdf',
|
||||
|
@ -40,17 +38,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.creditRequestPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.creditRequestPdf = (ctx, id) => Self.printReport(ctx, id, 'credit-request');
|
||||
};
|
||||
|
|
|
@ -80,7 +80,7 @@ module.exports = function(Self) {
|
|||
const data = await Self.rawSql(query, [id, date], myOptions);
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('getTransactions', {
|
||||
description: 'Returns last entries',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
}],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getTransactions`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getTransactions = async(filter, options) => {
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const conn = Self.dataSource.connector;
|
||||
const stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
t.id,
|
||||
t.clientFk,
|
||||
t.created,
|
||||
t.amount / 100 amount,
|
||||
t.receiptFk IS NOT NULL AS isConfirmed,
|
||||
tt.message responseMessage,
|
||||
te.message errorMessage
|
||||
FROM hedera.tpvTransaction t
|
||||
JOIN hedera.tpvMerchant m ON m.id = t.merchantFk
|
||||
LEFT JOIN hedera.tpvResponse tt ON tt.id = t.response
|
||||
LEFT JOIN hedera.tpvError te ON te.code = errorCode`);
|
||||
|
||||
stmt.merge(conn.makeSuffix(filter, 't'));
|
||||
|
||||
return Self.rawStmt(stmt, myOptions);
|
||||
};
|
||||
};
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('incotermsAuthorizationEmail', {
|
||||
description: 'Sends the incoterms authorization email with an attached PDF',
|
||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.incotermsAuthorizationEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.incotermsAuthorizationEmail = ctx => Self.sendTemplate(ctx, 'incoterms-authorization');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('incotermsAuthorizationHtml', {
|
||||
description: 'Returns the incoterms authorization email preview',
|
||||
|
@ -46,21 +44,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.incotermsAuthorizationHtml = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.incotermsAuthorizationHtml = (ctx, id) => Self.printEmail(ctx, id, 'incoterms-authorization');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('incotermsAuthorizationPdf', {
|
||||
description: 'Returns the incoterms authorization pdf',
|
||||
|
@ -46,17 +44,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.incotermsAuthorizationPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.incotermsAuthorizationPdf = (ctx, id) => Self.printReport(ctx, id, 'incoterms-authorization');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('letterDebtorNdEmail', {
|
||||
description: 'Sends the second debtor letter email with an attached PDF',
|
||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.letterDebtorNdEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.letterDebtorNdEmail = ctx => Self.sendTemplate(ctx, 'letter-debtor-nd');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('letterDebtorNdHtml', {
|
||||
description: 'Returns the second letter debtor email preview',
|
||||
|
@ -46,21 +44,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.letterDebtorNdHtml = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.letterDebtorNdHtml = (ctx, id) => Self.printEmail(ctx, id, 'letter-debtor-nd');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('letterDebtorPdf', {
|
||||
description: 'Returns the letter debtor pdf',
|
||||
|
@ -46,17 +44,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.letterDebtorPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.letterDebtorPdf = (ctx, id) => Self.printReport(ctx, id, 'letter-debtor');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('letterDebtorStEmail', {
|
||||
description: 'Sends the printer setup email with an attached PDF',
|
||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.letterDebtorStEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.letterDebtorStEmail = ctx => Self.sendTemplate(ctx, 'letter-debtor-st');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('letterDebtorStHtml', {
|
||||
description: 'Returns the letter debtor email preview',
|
||||
|
@ -46,21 +44,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.letterDebtorStHtml = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.letterDebtorStHtml = (ctx, id) => Self.printEmail(ctx, id, 'letter-debtor-st');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('printerSetupEmail', {
|
||||
description: 'Sends the printer setup email with an attached PDF',
|
||||
|
@ -41,19 +39,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.printerSetupEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.printerSetupEmail = ctx => Self.sendTemplate(ctx, 'printer-setup');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('printerSetupHtml', {
|
||||
description: 'Returns the printer setup email preview',
|
||||
|
@ -40,19 +38,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.printerSetupHtml = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.printerSetupHtml = (ctx, id) => Self.printEmail(ctx, id, 'printer-setup');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('sepaCoreEmail', {
|
||||
description: 'Sends the campaign metrics email with an attached PDF',
|
||||
|
@ -47,19 +45,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.sepaCoreEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.sepaCoreEmail = ctx => Self.sendTemplate(ctx, 'sepa-core');
|
||||
};
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Client getTransations', () => {
|
||||
it('should call getTransations() method to receive a list of Web Payments from Bruce Wayne', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const filter = {where: {clientFk: 1101}};
|
||||
const result = await models.Client.getTransactions(filter, options);
|
||||
|
||||
expect(result[1].id).toBeTruthy();
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Client transactions', () => {
|
||||
it('should call transactions() method to receive a list of Web Payments from Bruce Wayne', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {};
|
||||
const filter = {where: {clientFk: 1101}};
|
||||
const result = await models.Client.transactions(ctx, filter, options);
|
||||
|
||||
expect(result[1].id).toBeTruthy();
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should call transactions() method filtering by orderFk', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {args: {orderFk: 6}};
|
||||
const filter = {};
|
||||
const result = await models.Client.transactions(ctx, filter, options);
|
||||
|
||||
const firstRow = result[0];
|
||||
|
||||
expect(result.length).toEqual(1);
|
||||
expect(firstRow.id).toEqual(6);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
it('should call transactions() method filtering by amount', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {args: {amount: 40}};
|
||||
const filter = {};
|
||||
const result = await models.Client.transactions(ctx, filter, options);
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * result.length);
|
||||
const transaction = result[randomIndex];
|
||||
|
||||
expect(transaction.amount).toEqual(40);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
|
||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
||||
const buildFilter = require('vn-loopback/util/filter').buildFilter;
|
||||
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('transactions', {
|
||||
description: 'Returns customer transactions',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'filter',
|
||||
type: 'object',
|
||||
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'orderFk',
|
||||
type: 'number',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'clientFk',
|
||||
type: 'number',
|
||||
http: {source: 'query'}
|
||||
},
|
||||
{
|
||||
arg: 'amount',
|
||||
type: 'number',
|
||||
http: {source: 'query'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/transactions`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.transactions = async(ctx, filter, options) => {
|
||||
const args = ctx.args;
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const where = buildFilter(args, (param, value) => {
|
||||
switch (param) {
|
||||
case 'orderFk':
|
||||
return {'t.id': value};
|
||||
case 'clientFk':
|
||||
return {'t.clientFk': value};
|
||||
case 'amount':
|
||||
return {'t.amount': (value * 100)};
|
||||
}
|
||||
});
|
||||
|
||||
filter = mergeFilters(filter, {where});
|
||||
|
||||
const conn = Self.dataSource.connector;
|
||||
const stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
t.id,
|
||||
t.clientFk,
|
||||
c.name AS customerName,
|
||||
t.created,
|
||||
t.amount / 100 amount,
|
||||
t.receiptFk IS NOT NULL AS isConfirmed,
|
||||
tt.message responseMessage,
|
||||
te.message errorMessage
|
||||
FROM hedera.tpvTransaction t
|
||||
JOIN client c ON c.id = t.clientFk
|
||||
JOIN hedera.tpvMerchant m ON m.id = t.merchantFk
|
||||
LEFT JOIN hedera.tpvResponse tt ON tt.id = t.response
|
||||
LEFT JOIN hedera.tpvError te ON te.code = errorCode`);
|
||||
|
||||
stmt.merge(conn.makeSuffix(filter, 't'));
|
||||
|
||||
return Self.rawStmt(stmt, myOptions);
|
||||
};
|
||||
};
|
|
@ -1,5 +1,3 @@
|
|||
const { Report } = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('balanceCompensationPdf', {
|
||||
description: 'Returns the the debit balances compensation pdf',
|
||||
|
@ -10,7 +8,7 @@ module.exports = Self => {
|
|||
type: 'number',
|
||||
required: true,
|
||||
description: 'The receipt id',
|
||||
http: { source: 'path' }
|
||||
http: {source: 'path'}
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
|
@ -34,17 +32,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.balanceCompensationPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.balanceCompensationPdf = (ctx, id) => Self.printReport(ctx, id, 'balance-compensation');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('receiptPdf', {
|
||||
description: 'Returns the receipt pdf',
|
||||
|
@ -39,17 +37,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.receiptPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.receiptPdf = (ctx, id) => Self.printReport(ctx, id, 'receipt');
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = Self => {
|
|||
require('../methods/client/getCard')(Self);
|
||||
require('../methods/client/getDebt')(Self);
|
||||
require('../methods/client/getMana')(Self);
|
||||
require('../methods/client/getTransactions')(Self);
|
||||
require('../methods/client/transactions')(Self);
|
||||
require('../methods/client/hasCustomerRole')(Self);
|
||||
require('../methods/client/isValidClient')(Self);
|
||||
require('../methods/client/lastActiveTickets')(Self);
|
||||
|
|
|
@ -279,6 +279,18 @@ module.exports = Self => {
|
|||
// Credit changes
|
||||
if (changes.credit !== undefined)
|
||||
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 => {
|
||||
|
|
|
@ -154,6 +154,11 @@
|
|||
"model": "Account",
|
||||
"foreignKey": "id"
|
||||
},
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"foreignKey": "id"
|
||||
},
|
||||
"payMethod": {
|
||||
"type": "belongsTo",
|
||||
"model": "PayMethod",
|
||||
|
|
|
@ -62,6 +62,11 @@
|
|||
"type": "belongsTo",
|
||||
"model": "Bank",
|
||||
"foreignKey": "bankFk"
|
||||
},
|
||||
"supplier": {
|
||||
"type": "belongsTo",
|
||||
"model": "Supplier",
|
||||
"foreignKey": "companyFk"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -70,11 +70,12 @@
|
|||
icon="icon-no036"
|
||||
ng-if="$ctrl.client.isTaxDataChecked == false">
|
||||
</vn-icon>
|
||||
<vn-icon
|
||||
<vn-icon-button
|
||||
vn-tooltip="{{$ctrl.clientUnpaid()}}"
|
||||
icon="icon-clientUnpaid"
|
||||
ui-sref="client.card.unpaid"
|
||||
ng-if="$ctrl.client.unpaid">
|
||||
</vn-icon>
|
||||
</vn-icon-button>
|
||||
</div>
|
||||
<div class="quicklinks">
|
||||
<div ng-transclude="btnOne">
|
||||
|
|
|
@ -46,8 +46,9 @@ class Controller extends Descriptor {
|
|||
}
|
||||
|
||||
clientUnpaid() {
|
||||
return this.$t(`Unpaid Dated`, {dated: this.client.unpaid.dated}) +
|
||||
'<br/>' + this.$t(`Unpaid Amount`, {amount: this.client.unpaid.amount});
|
||||
return this.$t(`Unpaid`) + '<br/>'
|
||||
+ 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>
|
||||
<form
|
||||
name="form"
|
||||
ng-submit="watcher.submit()"
|
||||
ng-submit="$ctrl.onSubmit()"
|
||||
class="vn-w-md">
|
||||
<vn-card class="vn-pa-lg">
|
||||
<vn-vertical>
|
||||
|
|
|
@ -6,9 +6,17 @@ export default class Controller extends Section {
|
|||
if (hasData && !this.clientUnpaid.dated)
|
||||
this.clientUnpaid.dated = Date.vnNew();
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.$.watcher.submit()
|
||||
.then(() => this.card.reload());
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnClientUnpaid', {
|
||||
template: require('./index.html'),
|
||||
controller: Controller
|
||||
controller: Controller,
|
||||
require: {
|
||||
card: '^vnClientCard'
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="clients/getTransactions"
|
||||
link="{clientFk: $ctrl.$params.id}"
|
||||
limit="20"
|
||||
data="transactions"
|
||||
order="created DESC"
|
||||
auto-load="true">
|
||||
<vn-crud-model vn-id="model" url="clients/transactions" link="{clientFk: $ctrl.$params.id}" limit="20"
|
||||
data="transactions" order="created DESC" auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-w-md">
|
||||
<vn-data-viewer model="model" class="vn-w-md">
|
||||
<vn-card>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
|
@ -24,27 +16,19 @@
|
|||
<vn-tbody>
|
||||
<vn-tr ng-repeat="transaction in transactions">
|
||||
<vn-td shrink>
|
||||
<vn-icon
|
||||
vn-tooltip="{{::$ctrl.getFormattedMessage(transaction)}}"
|
||||
<vn-icon vn-tooltip="{{::$ctrl.getFormattedMessage(transaction)}}"
|
||||
ng-show="::((transaction.errorMessage || transaction.responseMessage) && !transaction.isConfirmed)"
|
||||
icon="clear">
|
||||
</vn-icon>
|
||||
<vn-icon
|
||||
vn-tooltip="Confirmed"
|
||||
ng-show="::(transaction.isConfirmed)"
|
||||
icon="check">
|
||||
<vn-icon vn-tooltip="Confirmed" ng-show="::(transaction.isConfirmed)" icon="check">
|
||||
</vn-icon>
|
||||
</vn-td>
|
||||
<vn-td number>{{::transaction.id}}</vn-td>
|
||||
<vn-td>{{::transaction.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
|
||||
<vn-td number>{{::transaction.amount | currency: 'EUR':2}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<vn-icon-button
|
||||
icon="done_all"
|
||||
vn-acl="administrative"
|
||||
vn-acl-action="remove"
|
||||
translate-attr="{title: 'Confirm transaction'}"
|
||||
ng-show="::!transaction.isConfirmed"
|
||||
<vn-icon-button icon="done_all" vn-acl="administrative" vn-acl-action="remove"
|
||||
translate-attr="{title: 'Confirm transaction'}" ng-show="::!transaction.isConfirmed"
|
||||
ng-click="$ctrl.confirm(transaction)">
|
||||
</vn-icon-button>
|
||||
</vn-td>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('entryOrderPdf', {
|
||||
description: 'Returns the entry order pdf',
|
||||
|
@ -38,17 +36,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.entryOrderPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.entryOrderPdf = (ctx, id) => Self.printReport(ctx, id, 'entry-order');
|
||||
};
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('Buy editLatestsBuys()', () => {
|
||||
beforeAll(async() => {
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 9},
|
||||
http: {
|
||||
req: {
|
||||
headers: {origin: 'http://localhost'}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||
active: activeCtx
|
||||
});
|
||||
});
|
||||
|
||||
it('should change the value of a given column for the selected buys', async() => {
|
||||
const tx = await models.Buy.beginTransaction({});
|
||||
const options = {transaction: tx};
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
"base": "Loggable",
|
||||
"log": {
|
||||
"model": "EntryLog",
|
||||
"relation": "entry"
|
||||
"relation": "entry",
|
||||
"grabUser": true
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"name": "Entry",
|
||||
"base": "Loggable",
|
||||
"log": {
|
||||
"model":"EntryLog"
|
||||
"model":"EntryLog",
|
||||
"grabUser": true
|
||||
},
|
||||
"options": {
|
||||
"mysql": {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('invoiceInEmail', {
|
||||
description: 'Sends the invoice in email with an attached PDF',
|
||||
|
@ -35,19 +33,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.invoiceInEmail = async ctx => {
|
||||
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();
|
||||
};
|
||||
Self.invoiceInEmail = ctx => Self.sendTemplate(ctx, 'invoiceIn');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const {Report} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('invoiceInPdf', {
|
||||
description: 'Returns the invoiceIn pdf',
|
||||
|
@ -34,17 +32,5 @@ module.exports = Self => {
|
|||
}
|
||||
});
|
||||
|
||||
Self.invoiceInPdf = async(ctx, id) => {
|
||||
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"`];
|
||||
};
|
||||
Self.invoiceInPdf = (ctx, id) => Self.printReport(ctx, id, 'invoiceIn');
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue