fixes #4564 que reports maneje si el origen de datos no devuelve registros #1266
|
@ -2,10 +2,11 @@ coverage
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
storage
|
storage
|
||||||
|
.idea
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
.eslintcache
|
.eslintcache
|
||||||
datasources.*.json
|
datasources.*.json
|
||||||
print.*.json
|
print.*.json
|
||||||
db.json
|
db.json
|
||||||
junit.xml
|
junit.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Coloque su configuración en este archivo para sobrescribir la configuración predeterminada y de usuario.
|
// Coloque su configuración en este archivo para sobrescribir la configuración predeterminada y de usuario.
|
||||||
{
|
{
|
||||||
// Carácter predeterminado de final de línea.
|
// Carácter predeterminado de final de línea.
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
## [2302.01] - 2023-01-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- [General](Inicio) Permite recuperar la contraseña
|
||||||
|
- [Ticket](Opciones) Subir albarán a Docuware
|
||||||
|
- [Ticket](Opciones) Enviar correo con PDF de Docuware
|
||||||
|
- [Artículo](Datos Básicos) Añadido campo Unidades/Caja
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- [Reclamaciones](Descriptor) Cambiado el campo Agencia por Zona
|
||||||
|
- [Tickets](Líneas preparadas) Actualizada sección para que sea más visual
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- [General] Al utilizar el traductor de Google se descuadraban los iconos
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- [Tickets](Control clientes) Eliminada sección
|
39
Dockerfile
39
Dockerfile
|
@ -1,32 +1,43 @@
|
||||||
FROM debian:stretch-slim
|
FROM debian:bullseye-slim
|
||||||
ENV TZ Europe/Madrid
|
ENV TZ Europe/Madrid
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# NodeJs
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
gnupg2 \
|
gnupg2 \
|
||||||
libfontconfig lftp \
|
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
|
||||||
&& apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
|
&& apt-get install -y --no-install-recommends nodejs \
|
||||||
libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
|
&& npm install -g npm@8.19.2
|
||||||
libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
|
|
||||||
libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
|
# Puppeteer
|
||||||
libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
|
|
||||||
&& curl -sL https://deb.nodesource.com/setup_12.x | bash - \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
nodejs \
|
libfontconfig lftp xvfb gconf-service libasound2 libatk1.0-0 libc6 \
|
||||||
&& apt-get purge -y --auto-remove \
|
libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 \
|
||||||
gnupg2 \
|
libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 \
|
||||||
|
libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
|
||||||
|
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \
|
||||||
|
libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
|
||||||
|
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& npm -g install pm2
|
&& npm -g install pm2
|
||||||
|
|
||||||
|
# Salix
|
||||||
|
|
||||||
WORKDIR /salix
|
WORKDIR /salix
|
||||||
|
|
||||||
|
COPY print/package.json print/package-lock.json print/
|
||||||
|
RUN npm --prefix ./print install --omit=dev ./print
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
COPY loopback/package.json loopback/
|
COPY loopback/package.json loopback/
|
||||||
COPY print/package.json print/
|
RUN npm install --omit=dev
|
||||||
RUN npm install --only=prod
|
|
||||||
RUN npm --prefix ./print install --only=prod ./print
|
|
||||||
|
|
||||||
COPY loopback loopback
|
COPY loopback loopback
|
||||||
COPY back back
|
COPY back back
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#!/usr/bin/env groovy
|
#!/usr/bin/env groovy
|
||||||
|
|
||||||
pipeline {
|
pipeline {
|
||||||
agent any
|
agent any
|
||||||
options {
|
options {
|
||||||
|
|
|
@ -29,6 +29,8 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.privileges = async function(ctx, id, roleFk, hasGrant, options) {
|
Self.privileges = async function(ctx, id, roleFk, hasGrant, options) {
|
||||||
|
if (!(hasGrant != null || roleFk)) return;
|
||||||
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
|
||||||
|
@ -37,22 +39,40 @@ module.exports = Self => {
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const user = await models.Account.findById(userId, null, myOptions);
|
const user = await models.Account.findById(userId, {fields: ['hasGrant']}, myOptions);
|
||||||
|
|
||||||
|
const userToUpdate = await models.Account.findById(id, {
|
||||||
|
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password'],
|
||||||
|
include: {
|
||||||
|
relation: 'role',
|
||||||
|
scope: {
|
||||||
|
fields: ['name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
if (!user.hasGrant)
|
if (!user.hasGrant)
|
||||||
throw new UserError(`You don't have enough privileges`);
|
throw new UserError(`You don't have grant privilege`);
|
||||||
|
|
||||||
|
const hasRoleFromUser = await models.Account.hasRole(userId, userToUpdate.role().name, myOptions);
|
||||||
|
|
||||||
|
if (!hasRoleFromUser)
|
||||||
|
throw new UserError(`You don't own the role and you can't assign it to another user`);
|
||||||
|
|
||||||
const userToUpdate = await models.Account.findById(id);
|
|
||||||
if (hasGrant != null)
|
if (hasGrant != null)
|
||||||
return await userToUpdate.updateAttribute('hasGrant', hasGrant, myOptions);
|
userToUpdate.hasGrant = hasGrant;
|
||||||
if (!roleFk) return;
|
|
||||||
|
|
||||||
const role = await models.Role.findById(roleFk, null, myOptions);
|
if (roleFk) {
|
||||||
const hasRole = await models.Account.hasRole(userId, role.name, myOptions);
|
const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions);
|
||||||
|
const hasRole = await models.Account.hasRole(userId, role.name, myOptions);
|
||||||
|
|
||||||
if (!hasRole)
|
if (!hasRole)
|
||||||
throw new UserError(`You don't have enough privileges`);
|
throw new UserError(`You don't own the role and you can't assign it to another user`);
|
||||||
|
|
||||||
await userToUpdate.updateAttribute('roleFk', roleFk, myOptions);
|
userToUpdate.roleFk = roleFk;
|
||||||
|
}
|
||||||
|
|
||||||
|
await userToUpdate.save(userToUpdate);
|
||||||
|
await models.UserAccount.sync(userToUpdate.name);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('recoverPassword', {
|
||||||
|
description: 'Send email to the user',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'email',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The email of user',
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: `/recoverPassword`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.recoverPassword = async function(email) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await models.user.resetPassword({email, emailTemplate: 'recover-password'});
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code === 'EMAIL_NOT_FOUND')
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -4,7 +4,9 @@ describe('account privileges()', () => {
|
||||||
const employeeId = 1;
|
const employeeId = 1;
|
||||||
const developerId = 9;
|
const developerId = 9;
|
||||||
const sysadminId = 66;
|
const sysadminId = 66;
|
||||||
const bruceWayneId = 1101;
|
const itBossId = 104;
|
||||||
|
const rootId = 100;
|
||||||
|
const clarkKent = 1103;
|
||||||
|
|
||||||
it('should throw an error when user not has privileges', async() => {
|
it('should throw an error when user not has privileges', async() => {
|
||||||
const ctx = {req: {accessToken: {userId: developerId}}};
|
const ctx = {req: {accessToken: {userId: developerId}}};
|
||||||
|
@ -22,7 +24,7 @@ describe('account privileges()', () => {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toContain(`You don't have enough privileges`);
|
expect(error.message).toContain(`You don't have grant privilege`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error when user has privileges but not has the role', async() => {
|
it('should throw an error when user has privileges but not has the role', async() => {
|
||||||
|
@ -33,12 +35,7 @@ describe('account privileges()', () => {
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
const root = await models.Role.findOne({
|
await models.Account.privileges(ctx, employeeId, rootId, null, options);
|
||||||
where: {
|
|
||||||
name: 'root'
|
|
||||||
}
|
|
||||||
}, options);
|
|
||||||
await models.Account.privileges(ctx, employeeId, root.id, null, options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -46,7 +43,26 @@ describe('account privileges()', () => {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toContain(`You don't have enough privileges`);
|
expect(error.message).toContain(`You don't own the role and you can't assign it to another user`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when user has privileges but not has the role from user', async() => {
|
||||||
|
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||||
|
const tx = await models.Account.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
await models.Account.privileges(ctx, itBossId, developerId, null, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toContain(`You don't own the role and you can't assign it to another user`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should change role', async() => {
|
it('should change role', async() => {
|
||||||
|
@ -63,8 +79,8 @@ describe('account privileges()', () => {
|
||||||
let error;
|
let error;
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
await models.Account.privileges(ctx, bruceWayneId, agency.id, null, options);
|
await models.Account.privileges(ctx, clarkKent, agency.id, null, options);
|
||||||
result = await models.Account.findById(bruceWayneId, null, options);
|
result = await models.Account.findById(clarkKent, null, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -84,8 +100,8 @@ describe('account privileges()', () => {
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await models.Account.privileges(ctx, bruceWayneId, null, true, options);
|
await models.Account.privileges(ctx, clarkKent, null, true, options);
|
||||||
result = await models.Account.findById(bruceWayneId, null, options);
|
result = await models.Account.findById(clarkKent, null, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('account changePassword()', () => {
|
describe('account setPassword()', () => {
|
||||||
it('should throw an error when password does not meet requirements', async() => {
|
it('should throw an error when password does not meet requirements', async() => {
|
||||||
let req = app.models.Account.setPassword(1, 'insecurePass');
|
let req = app.models.Account.setPassword(1, 'insecurePass');
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,12 @@ module.exports = Self => {
|
||||||
filter = mergeFilters(filter, {where});
|
filter = mergeFilters(filter, {where});
|
||||||
|
|
||||||
const stmt = new ParameterizedSQL(
|
const stmt = new ParameterizedSQL(
|
||||||
`SELECT * FROM campaign`);
|
`SELECT * FROM (`);
|
||||||
|
stmt.merge('SELECT * FROM campaign');
|
||||||
stmt.merge(conn.makeWhere(filter.where));
|
stmt.merge(conn.makeWhere(filter.where));
|
||||||
|
stmt.merge('ORDER BY dated ASC');
|
||||||
|
stmt.merge('LIMIT 10000000000000000000');
|
||||||
|
stmt.merge(') sub');
|
||||||
stmt.merge('GROUP BY code');
|
stmt.merge('GROUP BY code');
|
||||||
stmt.merge(conn.makePagination(filter));
|
stmt.merge(conn.makePagination(filter));
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ module.exports = Self => {
|
||||||
let message = $t(`There's a new urgent ticket:`);
|
let message = $t(`There's a new urgent ticket:`);
|
||||||
const ostUri = 'https://cau.verdnatura.es/scp/tickets.php?id=';
|
const ostUri = 'https://cau.verdnatura.es/scp/tickets.php?id=';
|
||||||
tickets.forEach(ticket => {
|
tickets.forEach(ticket => {
|
||||||
message += `\r\n[ID: *${ticket.number}* - ${ticket.subject} (@${ticket.username})](${ostUri + ticket.id})`;
|
message += `\r\n[ID: ${ticket.number} - ${ticket.subject} @${ticket.username}](${ostUri + ticket.id})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
const department = await models.Department.findOne({
|
const department = await models.Department.findOne({
|
||||||
|
@ -42,7 +42,5 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (channelName)
|
if (channelName)
|
||||||
return Self.send(ctx, `#${channelName}`, `@all ➔ ${message}`);
|
return Self.send(ctx, `#${channelName}`, `@all ➔ ${message}`);
|
||||||
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,9 @@ module.exports = Self => {
|
||||||
if (!recipient)
|
if (!recipient)
|
||||||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV == 'test')
|
||||||
|
message = `[Test:Environment to user ${userId}] ` + message;
|
||||||
|
|
||||||
await models.Chat.create({
|
await models.Chat.create({
|
||||||
senderFk: sender.id,
|
senderFk: sender.id,
|
||||||
recipient: `@${recipient.name}`,
|
recipient: `@${recipient.name}`,
|
||||||
|
|
|
@ -27,7 +27,7 @@ describe('Chat notifyIssue()', () => {
|
||||||
subject: 'Issue title'}
|
subject: 'Issue title'}
|
||||||
]);
|
]);
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
const expectedMessage = `@all ➔ There's a new urgent ticket:\r\n[ID: *00001* - Issue title (@batman)](https://cau.verdnatura.es/scp/tickets.php?id=1)`;
|
const expectedMessage = `@all ➔ There's a new urgent ticket:\r\n[ID: 00001 - Issue title @batman](https://cau.verdnatura.es/scp/tickets.php?id=1)`;
|
||||||
|
|
||||||
const department = await app.models.Department.findById(departmentId);
|
const department = await app.models.Department.findById(departmentId);
|
||||||
let orgChatName = department.chatName;
|
let orgChatName = department.chatName;
|
||||||
|
|
|
@ -26,11 +26,30 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.setSaleQuantity = async(saleId, quantity) => {
|
Self.setSaleQuantity = async(saleId, quantity) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
const sale = await models.Sale.findById(saleId);
|
if (typeof options == 'object')
|
||||||
return await sale.updateAttributes({
|
Object.assign(myOptions, options);
|
||||||
originalQuantity: sale.quantity,
|
|
||||||
quantity: quantity
|
if (!myOptions.transaction) {
|
||||||
});
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sale = await models.Sale.findById(saleId, null, myOptions);
|
||||||
|
const saleUpdated = await sale.updateAttributes({
|
||||||
|
originalQuantity: sale.quantity,
|
||||||
|
quantity: quantity
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return saleUpdated;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,15 +2,26 @@ const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('setSaleQuantity()', () => {
|
describe('setSaleQuantity()', () => {
|
||||||
it('should change quantity sale', async() => {
|
it('should change quantity sale', async() => {
|
||||||
const saleId = 30;
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
const newQuantity = 10;
|
|
||||||
|
|
||||||
const originalSale = await models.Sale.findById(saleId);
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
await models.Collection.setSaleQuantity(saleId, newQuantity);
|
const saleId = 30;
|
||||||
const updateSale = await models.Sale.findById(saleId);
|
const newQuantity = 10;
|
||||||
|
|
||||||
expect(updateSale.originalQuantity).toEqual(originalSale.quantity);
|
const originalSale = await models.Sale.findById(saleId, null, options);
|
||||||
expect(updateSale.quantity).toEqual(newQuantity);
|
|
||||||
|
await models.Collection.setSaleQuantity(saleId, newQuantity, options);
|
||||||
|
const updateSale = await models.Sale.findById(saleId, null, options);
|
||||||
|
|
||||||
|
expect(updateSale.originalQuantity).toEqual(originalSale.quantity);
|
||||||
|
expect(updateSale.quantity).toEqual(newQuantity);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,20 +47,22 @@ module.exports = Self => {
|
||||||
for (let dms of dmsToDelete) {
|
for (let dms of dmsToDelete) {
|
||||||
const pathHash = DmsContainer.getHash(dms.id);
|
const pathHash = DmsContainer.getHash(dms.id);
|
||||||
const dmsContainer = await DmsContainer.container(pathHash);
|
const dmsContainer = await DmsContainer.container(pathHash);
|
||||||
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
|
|
||||||
try {
|
try {
|
||||||
|
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
|
||||||
await fs.unlink(dstFile);
|
await fs.unlink(dstFile);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
continue;
|
if (err.code != 'ENOENT' && dms.file)
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await dms.destroy(myOptions);
|
||||||
|
|
||||||
const dstFolder = path.join(dmsContainer.client.root, pathHash);
|
const dstFolder = path.join(dmsContainer.client.root, pathHash);
|
||||||
try {
|
try {
|
||||||
await fs.rmdir(dstFolder);
|
await fs.rmdir(dstFolder);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await dms.destroy(myOptions);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
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 = new Date();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
const got = require('got');
|
const axios = require('axios');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('checkFile', {
|
Self.remoteMethodCtx('checkFile', {
|
||||||
|
@ -8,7 +8,7 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'The id',
|
description: 'The id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -18,14 +18,14 @@ module.exports = Self => {
|
||||||
description: 'The fileCabinet name'
|
description: 'The fileCabinet name'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'dialog',
|
arg: 'signed',
|
||||||
type: 'string',
|
type: 'boolean',
|
||||||
required: true,
|
required: true,
|
||||||
description: 'The dialog name'
|
description: 'If pdf is necessary to be signed'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'boolean',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -34,58 +34,51 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.checkFile = async function(ctx, id, fileCabinet, dialog) {
|
Self.checkFile = async function(ctx, id, fileCabinet, signed) {
|
||||||
const myUserId = ctx.req.accessToken.userId;
|
|
||||||
if (!myUserId)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const docuwareConfig = await models.DocuwareConfig.findOne();
|
const action = 'find';
|
||||||
|
|
||||||
const docuwareInfo = await models.Docuware.findOne({
|
const docuwareInfo = await models.Docuware.findOne({
|
||||||
where: {
|
where: {
|
||||||
code: fileCabinet,
|
code: fileCabinet,
|
||||||
dialogName: dialog
|
action: action
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const docuwareUrl = docuwareConfig.url;
|
|
||||||
const cookie = docuwareConfig.token;
|
|
||||||
const fileCabinetName = docuwareInfo.fileCabinetName;
|
|
||||||
const find = docuwareInfo.find;
|
|
||||||
const options = {
|
|
||||||
'headers': {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Cookie': cookie
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const searchFilter = {
|
const searchFilter = {
|
||||||
condition: [
|
condition: [
|
||||||
{
|
{
|
||||||
DBName: find,
|
DBName: docuwareInfo.findById,
|
||||||
|
|
||||||
Value: [id]
|
Value: [id]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
sortOrder: [
|
||||||
|
{
|
||||||
|
Field: 'FILENAME',
|
||||||
|
Direction: 'Desc'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// get fileCabinetId
|
const options = await Self.getOptions();
|
||||||
const fileCabinetResponse = await got.get(`${docuwareUrl}/FileCabinets`, options);
|
|
||||||
const fileCabinetJson = JSON.parse(fileCabinetResponse.body).FileCabinet;
|
|
||||||
const fileCabinetId = fileCabinetJson.find(dialogs => dialogs.Name === fileCabinetName).Id;
|
|
||||||
|
|
||||||
// get dialog
|
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
||||||
const dialogResponse = await got.get(`${docuwareUrl}/FileCabinets/${fileCabinetId}/dialogs`, options);
|
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
|
||||||
const dialogJson = JSON.parse(dialogResponse.body).Dialog;
|
|
||||||
const dialogId = dialogJson.find(dialogs => dialogs.DisplayName === 'find').Id;
|
|
||||||
|
|
||||||
// get docuwareID
|
const response = await axios.post(
|
||||||
Object.assign(options, {'body': JSON.stringify(searchFilter)});
|
`${options.url}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`,
|
||||||
const response = await got.post(
|
searchFilter,
|
||||||
`${docuwareUrl}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`, options);
|
options.headers
|
||||||
JSON.parse(response.body).Items[0].Id;
|
);
|
||||||
|
const [documents] = response.data.Items;
|
||||||
|
if (!documents) return false;
|
||||||
|
|
||||||
return true;
|
const state = documents.Fields.find(field => field.FieldName == 'ESTADO');
|
||||||
|
if (signed && state.Item != 'Firmado') return false;
|
||||||
|
|
||||||
|
return {id: documents.Id};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
/**
|
||||||
|
* Returns the dialog id
|
||||||
|
*
|
||||||
|
* @param {string} code - The fileCabinet name
|
||||||
|
* @param {string} action - The fileCabinet name
|
||||||
|
* @param {string} fileCabinetId - Optional The fileCabinet name
|
||||||
|
* @return {number} - The fileCabinet id
|
||||||
|
*/
|
||||||
|
Self.getDialog = async(code, action, fileCabinetId) => {
|
||||||
|
const docuwareInfo = await Self.app.models.Docuware.findOne({
|
||||||
|
where: {
|
||||||
|
code: code,
|
||||||
|
action: action
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!fileCabinetId) fileCabinetId = await Self.getFileCabinet(code);
|
||||||
|
|
||||||
|
const options = await Self.getOptions();
|
||||||
|
|
||||||
|
if (!process.env.NODE_ENV)
|
||||||
|
return Math.round();
|
||||||
|
|
||||||
|
const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers);
|
||||||
|
const dialogs = response.data.Dialog;
|
||||||
|
const dialogId = dialogs.find(dialogs => dialogs.DisplayName === docuwareInfo.dialogName).Id;
|
||||||
|
|
||||||
|
return dialogId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fileCabinetId
|
||||||
|
*
|
||||||
|
* @param {string} code - The fileCabinet code
|
||||||
|
* @return {number} - The fileCabinet id
|
||||||
|
*/
|
||||||
|
Self.getFileCabinet = async code => {
|
||||||
|
const options = await Self.getOptions();
|
||||||
|
const docuwareInfo = await Self.app.models.Docuware.findOne({
|
||||||
|
where: {
|
||||||
|
code: code
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!process.env.NODE_ENV)
|
||||||
|
return Math.round();
|
||||||
|
|
||||||
|
const fileCabinetResponse = await axios.get(`${options.url}/FileCabinets`, options.headers);
|
||||||
|
const fileCabinets = fileCabinetResponse.data.FileCabinet;
|
||||||
|
const fileCabinetId = fileCabinets.find(fileCabinet => fileCabinet.Name === docuwareInfo.fileCabinetName).Id;
|
||||||
|
|
||||||
|
return fileCabinetId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns basic headers
|
||||||
|
*
|
||||||
|
* @param {string} cookie - The docuware cookie
|
||||||
|
* @return {object} - The headers
|
||||||
|
*/
|
||||||
|
Self.getOptions = async() => {
|
||||||
|
const docuwareConfig = await Self.app.models.DocuwareConfig.findOne();
|
||||||
|
const headers = {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Cookie': docuwareConfig.cookie
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: docuwareConfig.url,
|
||||||
|
headers
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,72 @@
|
||||||
|
const {Email} = require('vn-print');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('deliveryNoteEmail', {
|
||||||
|
description: 'Sends the delivery note email with an docuware attached PDF',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
description: 'The ticket id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'recipient',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The recipient email',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'recipientId',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The client id',
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: [
|
||||||
|
{
|
||||||
|
arg: 'body',
|
||||||
|
type: 'file',
|
||||||
|
root: true
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Type',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}, {
|
||||||
|
arg: 'Content-Disposition',
|
||||||
|
type: 'String',
|
||||||
|
http: {target: 'header'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
http: {
|
||||||
|
path: '/:id/delivery-note-email',
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.deliveryNoteEmail = async(ctx, id) => {
|
||||||
|
const args = Object.assign({}, ctx.args);
|
||||||
|
const params = {
|
||||||
|
recipient: args.recipient,
|
||||||
|
lang: ctx.req.getLocale()
|
||||||
|
};
|
||||||
|
|
||||||
|
delete args.ctx;
|
||||||
|
for (const param in args)
|
||||||
|
params[param] = args[param];
|
||||||
|
|
||||||
|
const email = new Email('delivery-note', params);
|
||||||
|
|
||||||
|
const docuwareFile = await Self.app.models.Docuware.download(ctx, id, 'deliveryNote');
|
||||||
|
|
||||||
|
return email.send({
|
||||||
|
overrideAttachments: true,
|
||||||
|
attachments: [{
|
||||||
|
filename: `${id}.pdf`,
|
||||||
|
content: docuwareFile[0]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
/* eslint max-len: ["error", { "code": 180 }]*/
|
/* eslint max-len: ["error", { "code": 180 }]*/
|
||||||
const got = require('got');
|
const axios = require('axios');
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
@ -10,19 +10,13 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
arg: 'id',
|
arg: 'id',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: 'The id',
|
description: 'The ticket id',
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'fileCabinet',
|
arg: 'fileCabinet',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The id',
|
description: 'The file cabinet',
|
||||||
http: {source: 'path'}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'dialog',
|
|
||||||
type: 'string',
|
|
||||||
description: 'The id',
|
|
||||||
http: {source: 'path'}
|
http: {source: 'path'}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -42,79 +36,26 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
http: {
|
http: {
|
||||||
path: `/:id/download/:fileCabinet/:dialog`,
|
path: `/:id/download/:fileCabinet`,
|
||||||
verb: 'GET'
|
verb: 'GET'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.download = async function(ctx, id, fileCabinet, dialog) {
|
Self.download = async function(ctx, id, fileCabinet) {
|
||||||
const myUserId = ctx.req.accessToken.userId;
|
|
||||||
if (!myUserId)
|
|
||||||
throw new UserError(`You don't have enough privileges`);
|
|
||||||
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const docuwareConfig = await models.DocuwareConfig.findOne();
|
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, true);
|
||||||
const docuwareInfo = await models.Docuware.findOne({
|
if (!docuwareFile) throw new UserError('The DOCUWARE PDF document does not exists');
|
||||||
where: {
|
|
||||||
code: fileCabinet,
|
|
||||||
dialogName: dialog
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const docuwareUrl = docuwareConfig.url;
|
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
||||||
const cookie = docuwareConfig.token;
|
const options = await Self.getOptions();
|
||||||
const fileCabinetName = docuwareInfo.fileCabinetName;
|
options.headers.responseType = 'stream';
|
||||||
const find = docuwareInfo.find;
|
|
||||||
const options = {
|
|
||||||
'headers': {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Cookie': cookie
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const searchFilter = {
|
|
||||||
condition: [
|
|
||||||
{
|
|
||||||
DBName: find,
|
|
||||||
Value: [id]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
const fileName = `filename="${id}.pdf"`;
|
||||||
// get fileCabinetId
|
const contentType = 'application/pdf';
|
||||||
const fileCabinetResponse = await got.get(`${docuwareUrl}/FileCabinets`, options);
|
const downloadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/FileDownload?targetFileType=Auto&keepAnnotations=false`;
|
||||||
const fileCabinetJson = JSON.parse(fileCabinetResponse.body).FileCabinet;
|
|
||||||
const fileCabinetId = fileCabinetJson.find(dialogs => dialogs.Name === fileCabinetName).Id;
|
|
||||||
|
|
||||||
// get dialog
|
const stream = await axios.get(downloadUri, options.headers);
|
||||||
const dialogResponse = await got.get(`${docuwareUrl}/FileCabinets/${fileCabinetId}/dialogs`, options);
|
|
||||||
const dialogJson = JSON.parse(dialogResponse.body).Dialog;
|
|
||||||
const dialogId = dialogJson.find(dialogs => dialogs.DisplayName === 'find').Id;
|
|
||||||
|
|
||||||
// get docuwareID
|
return [stream.data, contentType, fileName];
|
||||||
Object.assign(options, {'body': JSON.stringify(searchFilter)});
|
|
||||||
const response = await got.post(`${docuwareUrl}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`, options);
|
|
||||||
const docuwareId = JSON.parse(response.body).Items[0].Id;
|
|
||||||
|
|
||||||
// download & save file
|
|
||||||
const fileName = `filename="${id}.pdf"`;
|
|
||||||
const contentType = 'application/pdf';
|
|
||||||
const downloadUri = `${docuwareUrl}/FileCabinets/${fileCabinetId}/Documents/${docuwareId}/FileDownload?targetFileType=Auto&keepAnnotations=false`;
|
|
||||||
const downloadOptions = {
|
|
||||||
'headers': {
|
|
||||||
'Cookie': cookie
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const stream = got.stream(downloadUri, downloadOptions);
|
|
||||||
|
|
||||||
return [stream, contentType, fileName];
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code === 'ENOENT')
|
|
||||||
throw new UserError('The DOCUWARE PDF document does not exists');
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
const got = require('got');
|
const axios = require('axios');
|
||||||
|
|
||||||
describe('docuware download()', () => {
|
describe('docuware download()', () => {
|
||||||
const ticketId = 1;
|
const ticketId = 1;
|
||||||
|
@ -12,53 +12,71 @@ describe('docuware download()', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fileCabinetName = 'deliveryClient';
|
const docuwareModel = models.Docuware;
|
||||||
const dialogDisplayName = 'find';
|
const fileCabinetName = 'deliveryNote';
|
||||||
const dialogName = 'findTicket';
|
|
||||||
|
|
||||||
const gotGetResponse = {
|
beforeAll(() => {
|
||||||
body: JSON.stringify(
|
spyOn(docuwareModel, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
{
|
spyOn(docuwareModel, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
FileCabinet: [
|
|
||||||
{Id: 12, Name: fileCabinetName}
|
|
||||||
],
|
|
||||||
Dialog: [
|
|
||||||
{Id: 34, DisplayName: dialogDisplayName}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
it('should return exist file in docuware', async() => {
|
|
||||||
const gotPostResponse = {
|
|
||||||
body: JSON.stringify(
|
|
||||||
{
|
|
||||||
Items: [
|
|
||||||
{Id: 56}
|
|
||||||
],
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
spyOn(got, 'get').and.returnValue(new Promise(resolve => resolve(gotGetResponse)));
|
|
||||||
spyOn(got, 'post').and.returnValue(new Promise(resolve => resolve(gotPostResponse)));
|
|
||||||
|
|
||||||
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, dialogName);
|
|
||||||
|
|
||||||
expect(result).toEqual(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return not exist file in docuware', async() => {
|
it('should return false if there are no documents', async() => {
|
||||||
const gotPostResponse = {
|
const response = {
|
||||||
body: JSON.stringify(
|
data: {
|
||||||
{
|
Items: []
|
||||||
Items: [],
|
}
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
|
||||||
|
|
||||||
spyOn(got, 'get').and.returnValue(new Promise(resolve => resolve(gotGetResponse)));
|
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
|
||||||
spyOn(got, 'post').and.returnValue(new Promise(resolve => resolve(gotPostResponse)));
|
|
||||||
|
|
||||||
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, dialogName);
|
|
||||||
|
|
||||||
expect(result).toEqual(false);
|
expect(result).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return false if the document is unsigned', async() => {
|
||||||
|
const response = {
|
||||||
|
data: {
|
||||||
|
Items: [
|
||||||
|
{
|
||||||
|
Id: 1,
|
||||||
|
Fields: [
|
||||||
|
{
|
||||||
|
FieldName: 'ESTADO',
|
||||||
|
Item: 'Unsigned'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
|
||||||
|
|
||||||
|
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
|
||||||
|
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the document data', async() => {
|
||||||
|
const docuwareId = 1;
|
||||||
|
const response = {
|
||||||
|
data: {
|
||||||
|
Items: [
|
||||||
|
{
|
||||||
|
Id: docuwareId,
|
||||||
|
Fields: [
|
||||||
|
{
|
||||||
|
FieldName: 'ESTADO',
|
||||||
|
Item: 'Firmado'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
|
||||||
|
|
||||||
|
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
|
||||||
|
|
||||||
|
expect(result.id).toEqual(docuwareId);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
const got = require('got');
|
const axios = require('axios');
|
||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
|
|
||||||
describe('docuware download()', () => {
|
describe('docuware download()', () => {
|
||||||
|
@ -13,36 +13,33 @@ describe('docuware download()', () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should return the downloaded file name', async() => {
|
const docuwareModel = models.Docuware;
|
||||||
const fileCabinetName = 'deliveryClient';
|
const fileCabinetName = 'deliveryNote';
|
||||||
const dialogDisplayName = 'find';
|
|
||||||
const dialogName = 'findTicket';
|
|
||||||
const gotGetResponse = {
|
|
||||||
body: JSON.stringify(
|
|
||||||
{
|
|
||||||
FileCabinet: [
|
|
||||||
{Id: 12, Name: fileCabinetName}
|
|
||||||
],
|
|
||||||
Dialog: [
|
|
||||||
{Id: 34, DisplayName: dialogDisplayName}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
const gotPostResponse = {
|
beforeAll(() => {
|
||||||
body: JSON.stringify(
|
spyOn(docuwareModel, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
{
|
spyOn(docuwareModel, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
|
||||||
Items: [
|
});
|
||||||
{Id: 56}
|
|
||||||
],
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
spyOn(got, 'get').and.returnValue(new Promise(resolve => resolve(gotGetResponse)));
|
it('should return error if file not exist', async() => {
|
||||||
spyOn(got, 'post').and.returnValue(new Promise(resolve => resolve(gotPostResponse)));
|
spyOn(docuwareModel, 'checkFile').and.returnValue(false);
|
||||||
spyOn(got, 'stream').and.returnValue(new stream.PassThrough({objectMode: true}));
|
spyOn(axios, 'get').and.returnValue(new stream.PassThrough({objectMode: true}));
|
||||||
|
|
||||||
const result = await models.Docuware.download(ctx, ticketId, fileCabinetName, dialogName);
|
let error;
|
||||||
|
try {
|
||||||
|
await models.Docuware.download(ctx, ticketId, fileCabinetName);
|
||||||
|
} catch (e) {
|
||||||
|
error = e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual('The DOCUWARE PDF document does not exists');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the downloaded file if exist file ', async() => {
|
||||||
|
spyOn(docuwareModel, 'checkFile').and.returnValue({});
|
||||||
|
spyOn(axios, 'get').and.returnValue(new stream.PassThrough({objectMode: true}));
|
||||||
|
|
||||||
|
const result = await models.Docuware.download(ctx, ticketId, fileCabinetName);
|
||||||
|
|
||||||
expect(result[1]).toEqual('application/pdf');
|
expect(result[1]).toEqual('application/pdf');
|
||||||
expect(result[2]).toEqual(`filename="${ticketId}.pdf"`);
|
expect(result[2]).toEqual(`filename="${ticketId}.pdf"`);
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('docuware upload()', () => {
|
||||||
|
const userId = 9;
|
||||||
|
const ticketId = 10;
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
getLocale: () => {
|
||||||
|
return 'en';
|
||||||
|
},
|
||||||
|
accessToken: {userId: userId},
|
||||||
|
headers: {origin: 'http://localhost:5000'},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const docuwareModel = models.Docuware;
|
||||||
|
const ticketModel = models.Ticket;
|
||||||
|
const fileCabinetName = 'deliveryNote';
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
spyOn(docuwareModel, 'getFileCabinet').and.returnValue(new Promise(resolve => resolve(Math.random())));
|
||||||
|
spyOn(docuwareModel, 'getDialog').and.returnValue(new Promise(resolve => resolve(Math.random())));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should try upload file', async() => {
|
||||||
|
spyOn(ticketModel, 'deliveryNotePdf').and.returnValue(new Promise(resolve => resolve({})));
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
await models.Docuware.upload(ctx, ticketId, fileCabinetName);
|
||||||
|
} catch (e) {
|
||||||
|
error = e.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual('Action not allowed on the test environment');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,141 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('upload', {
|
||||||
|
description: 'Upload an docuware PDF',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
description: 'The ticket id',
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'fileCabinet',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The file cabinet'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'dialog',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The dialog'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: [],
|
||||||
|
http: {
|
||||||
|
path: `/:id/upload`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.upload = async function(ctx, id, fileCabinet) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const action = 'store';
|
||||||
|
|
||||||
|
const options = await Self.getOptions();
|
||||||
|
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
||||||
|
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
|
||||||
|
|
||||||
|
// get delivery note
|
||||||
|
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
|
||||||
|
id,
|
||||||
|
type: 'deliveryNote'
|
||||||
|
});
|
||||||
|
|
||||||
|
// get ticket data
|
||||||
|
const ticket = await models.Ticket.findById(id, {
|
||||||
|
include: [{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'socialName', 'fi']
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// upload file
|
||||||
|
const templateJson = {
|
||||||
|
'Fields': [
|
||||||
|
{
|
||||||
|
'FieldName': 'N__ALBAR_N',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'CIF_PROVEEDOR',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': ticket.client().fi,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'CODIGO_PROVEEDOR',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': ticket.client().id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'NOMBRE_PROVEEDOR',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': ticket.client().socialName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'FECHA_FACTURA',
|
||||||
|
'ItemElementName': 'date',
|
||||||
|
'Item': ticket.shipped,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'TOTAL_FACTURA',
|
||||||
|
'ItemElementName': 'Decimal',
|
||||||
|
'Item': ticket.totalWithVat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'ESTADO',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': 'Pendiente procesar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'FIRMA_',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': 'Si',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'FILTRO_TABLET',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': 'Tablet1',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV != 'production')
|
||||||
|
throw new UserError('Action not allowed on the test environment');
|
||||||
|
|
||||||
|
// delete old
|
||||||
|
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
|
||||||
|
if (docuwareFile) {
|
||||||
|
const deleteJson = {
|
||||||
|
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
|
||||||
|
};
|
||||||
|
const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
|
||||||
|
await axios.put(deleteUri, deleteJson, options.headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
|
||||||
|
const FormData = require('form-data');
|
||||||
|
const data = new FormData();
|
||||||
|
|
||||||
|
data.append('document', JSON.stringify(templateJson), 'schema.json');
|
||||||
|
data.append('file[]', deliveryNote[0], 'file.pdf');
|
||||||
|
const uploadOptions = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
'X-File-ModifiedDate': new Date(),
|
||||||
|
'Cookie': options.headers.headers.Cookie,
|
||||||
|
...data.getHeaders()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return await axios.post(uploadUri, data, uploadOptions)
|
||||||
|
.catch(() => {
|
||||||
|
throw new UserError('Failed to upload file');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,49 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('clean', {
|
||||||
|
description: 'clean notifications from queue',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/clean`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.clean = async options => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const status = ['sent', 'error'];
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const config = await models.NotificationConfig.findOne({}, myOptions);
|
||||||
|
|
||||||
|
if (!config.cleanDays) return;
|
||||||
|
|
||||||
|
const cleanDate = new Date();
|
||||||
|
cleanDate.setDate(cleanDate.getDate() - config.cleanDays);
|
||||||
|
|
||||||
|
await models.NotificationQueue.destroyAll({
|
||||||
|
where: {status: {inq: status}},
|
||||||
|
created: {lt: cleanDate}
|
||||||
|
}, myOptions);
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,83 @@
|
||||||
|
const {Email} = require('vn-print');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('send', {
|
||||||
|
description: 'Send notifications from queue',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/send`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.send = async options => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const findStatus = 'pending';
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const notificationQueue = await models.NotificationQueue.find({
|
||||||
|
where: {status: findStatus},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'notification',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'subscription',
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: 'user',
|
||||||
|
scope: {
|
||||||
|
fields: ['name', 'lang'],
|
||||||
|
include: {
|
||||||
|
relation: 'emailUser'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const statusSent = 'sent';
|
||||||
|
const statusError = 'error';
|
||||||
|
|
||||||
|
for (const queue of notificationQueue) {
|
||||||
|
const queueName = queue.notification().name;
|
||||||
|
const queueParams = JSON.parse(queue.params);
|
||||||
|
|
||||||
|
for (const notificationUser of queue.notification().subscription()) {
|
||||||
|
try {
|
||||||
|
const sendParams = {
|
||||||
|
recipient: notificationUser.user().emailUser().email,
|
||||||
|
lang: notificationUser.user().lang
|
||||||
|
};
|
||||||
|
|
||||||
|
if (notificationUser.userFk == queue.authorFk) {
|
||||||
|
await queue.updateAttribute('status', statusSent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newParams = Object.assign({}, queueParams, sendParams);
|
||||||
|
const email = new Email(queueName, newParams);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV != 'test')
|
||||||
|
await email.send();
|
||||||
|
|
||||||
|
await queue.updateAttribute('status', statusSent);
|
||||||
|
} catch (error) {
|
||||||
|
await queue.updateAttribute('status', statusError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Notification Clean()', () => {
|
||||||
|
it('should delete old rows with error', async() => {
|
||||||
|
const userId = 9;
|
||||||
|
const status = 'error';
|
||||||
|
const tx = await models.NotificationQueue.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const notification = await models.Notification.findOne({}, options);
|
||||||
|
const notificationConfig = await models.NotificationConfig.findOne({});
|
||||||
|
|
||||||
|
const cleanDate = new Date();
|
||||||
|
cleanDate.setDate(cleanDate.getDate() - (notificationConfig.cleanDays + 1));
|
||||||
|
|
||||||
|
let before;
|
||||||
|
let after;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const notificationDelete = await models.NotificationQueue.create({
|
||||||
|
notificationFk: notification.name,
|
||||||
|
authorFk: userId,
|
||||||
|
status: status,
|
||||||
|
created: cleanDate
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
before = await models.NotificationQueue.findById(notificationDelete.id, null, options);
|
||||||
|
|
||||||
|
await models.Notification.clean(options);
|
||||||
|
|
||||||
|
after = await models.NotificationQueue.findById(notificationDelete.id, null, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(before.notificationFk).toEqual(notification.name);
|
||||||
|
expect(after).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Notification Send()', () => {
|
||||||
|
it('should send notification', async() => {
|
||||||
|
const statusPending = 'pending';
|
||||||
|
const tx = await models.NotificationQueue.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const filter = {
|
||||||
|
where: {
|
||||||
|
status: statusPending
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let before;
|
||||||
|
let after;
|
||||||
|
|
||||||
|
try {
|
||||||
|
before = await models.NotificationQueue.find(filter, options);
|
||||||
|
|
||||||
|
await models.Notification.send(options);
|
||||||
|
|
||||||
|
after = await models.NotificationQueue.find(filter, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(before.length).toEqual(3);
|
||||||
|
expect(after.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,12 +1,13 @@
|
||||||
const jsdom = require('jsdom');
|
const jsdom = require('jsdom');
|
||||||
const mysql = require('mysql');
|
const mysql = require('mysql');
|
||||||
|
const FormData = require('form-data');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('closeTicket', {
|
Self.remoteMethodCtx('closeTicket', {
|
||||||
description: 'Close tickets without response from the user',
|
description: 'Close tickets without response from the user',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {
|
returns: {
|
||||||
type: 'Object',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -24,39 +25,39 @@ module.exports = Self => {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const con = mysql.createConnection({
|
const con = mysql.createConnection({
|
||||||
host: `${config.hostDb}`,
|
host: config.hostDb,
|
||||||
user: `${config.userDb}`,
|
user: config.userDb,
|
||||||
password: `${config.passwordDb}`,
|
password: config.passwordDb,
|
||||||
port: `${config.portDb}`
|
port: config.portDb
|
||||||
});
|
});
|
||||||
|
|
||||||
const sql = `SELECT ot.ticket_id, ot.number
|
const sql = `SELECT ot.ticket_id, ot.number
|
||||||
FROM osticket.ost_ticket ot
|
FROM osticket.ost_ticket ot
|
||||||
JOIN osticket.ost_ticket_status ots ON ots.id = ot.status_id
|
JOIN osticket.ost_ticket_status ots ON ots.id = ot.status_id
|
||||||
JOIN osticket.ost_thread ot2 ON ot2.object_id = ot.ticket_id AND ot2.object_type = 'T'
|
JOIN osticket.ost_thread ot2 ON ot2.object_id = ot.ticket_id AND ot2.object_type = 'T'
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT ote.thread_id, MAX(ote.created) created, MAX(ote.updated) updated
|
SELECT ote.thread_id, MAX(ote.created) created, MAX(ote.updated) updated
|
||||||
FROM osticket.ost_thread_entry ote
|
FROM osticket.ost_thread_entry ote
|
||||||
WHERE ote.staff_id != 0 AND ote.type = 'R'
|
WHERE ote.staff_id AND ote.type = 'R'
|
||||||
GROUP BY ote.thread_id
|
GROUP BY ote.thread_id
|
||||||
) sub ON sub.thread_id = ot2.id
|
) sub ON sub.thread_id = ot2.id
|
||||||
WHERE ot.isanswered = 1
|
WHERE ot.isanswered
|
||||||
AND ots.state = '${config.oldStatus}'
|
AND ots.state = ?
|
||||||
AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ${config.day} DAY)`;
|
AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ? DAY)`;
|
||||||
|
|
||||||
let ticketsId = [];
|
const ticketsId = [];
|
||||||
con.connect(err => {
|
con.connect(err => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
con.query(sql, (err, results) => {
|
con.query(sql, [config.oldStatus, config.day],
|
||||||
if (err) throw err;
|
(err, results) => {
|
||||||
for (const result of results)
|
if (err) throw err;
|
||||||
ticketsId.push(result.ticket_id);
|
for (const result of results)
|
||||||
});
|
ticketsId.push(result.ticket_id);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
await getRequestToken();
|
||||||
|
|
||||||
await requestToken();
|
async function getRequestToken() {
|
||||||
|
|
||||||
async function requestToken() {
|
|
||||||
const response = await fetch(ostUri);
|
const response = await fetch(ostUri);
|
||||||
|
|
||||||
const result = response.headers.get('set-cookie');
|
const result = response.headers.get('set-cookie');
|
||||||
|
@ -95,24 +96,56 @@ module.exports = Self => {
|
||||||
|
|
||||||
async function close(token, secondCookie) {
|
async function close(token, secondCookie) {
|
||||||
for (const ticketId of ticketsId) {
|
for (const ticketId of ticketsId) {
|
||||||
const ostUri = `${config.host}/ajax.php/tickets/${ticketId}/status`;
|
try {
|
||||||
const data = {
|
const lock = await getLockCode(token, secondCookie, ticketId);
|
||||||
status_id: config.newStatusId,
|
if (!lock.code) {
|
||||||
comments: config.comment,
|
let error = `Can't get lock code`;
|
||||||
undefined: config.action
|
if (lock.msg) error += `: ${lock.msg}`;
|
||||||
};
|
throw new Error(error);
|
||||||
const params = {
|
|
||||||
method: 'POST',
|
|
||||||
body: new URLSearchParams(data),
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
||||||
'X-CSRFToken': token,
|
|
||||||
'Cookie': secondCookie
|
|
||||||
}
|
}
|
||||||
};
|
let form = new FormData();
|
||||||
|
form.append('__CSRFToken__', token);
|
||||||
|
form.append('id', ticketId);
|
||||||
|
form.append('a', config.responseType);
|
||||||
|
form.append('lockCode', lock.code);
|
||||||
|
form.append('from_email_id', config.fromEmailId);
|
||||||
|
form.append('reply-to', config.replyTo);
|
||||||
|
form.append('cannedResp', 0);
|
||||||
|
form.append('response', config.comment);
|
||||||
|
form.append('signature', 'none');
|
||||||
|
form.append('reply_status_id', config.newStatusId);
|
||||||
|
|
||||||
return fetch(ostUri, params);
|
const ostUri = `${config.host}/tickets.php?id=${ticketId}`;
|
||||||
|
const params = {
|
||||||
|
method: 'POST',
|
||||||
|
body: form,
|
||||||
|
headers: {
|
||||||
|
'Cookie': secondCookie
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await fetch(ostUri, params);
|
||||||
|
} catch (e) {
|
||||||
|
const err = new Error(`${ticketId} Ticket close failed: ${e.message}`);
|
||||||
|
err.stack += e.stack;
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getLockCode(token, secondCookie, ticketId) {
|
||||||
|
const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`;
|
||||||
|
const params = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': token,
|
||||||
|
'Cookie': secondCookie
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const response = await fetch(ostUri, params);
|
||||||
|
const body = await response.text();
|
||||||
|
const json = JSON.parse(body);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,11 +19,11 @@ describe('getStarredModules()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should return the starred modules for a given user`, async() => {
|
it(`should return the starred modules for a given user`, async() => {
|
||||||
const newStarred = await app.models.StarredModule.create({workerFk: 9, moduleFk: 'Clients', position: 1});
|
const newStarred = await app.models.StarredModule.create({workerFk: 9, moduleFk: 'customer', position: 1});
|
||||||
const starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
const starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
||||||
|
|
||||||
expect(starredModules.length).toEqual(1);
|
expect(starredModules.length).toEqual(1);
|
||||||
expect(starredModules[0].moduleFk).toEqual('Clients');
|
expect(starredModules[0].moduleFk).toEqual('customer');
|
||||||
|
|
||||||
// restores
|
// restores
|
||||||
await app.models.StarredModule.destroyById(newStarred.id);
|
await app.models.StarredModule.destroyById(newStarred.id);
|
||||||
|
|
|
@ -26,29 +26,29 @@ describe('setPosition()', () => {
|
||||||
const filter = {
|
const filter = {
|
||||||
where: {
|
where: {
|
||||||
workerFk: ctx.req.accessToken.userId,
|
workerFk: ctx.req.accessToken.userId,
|
||||||
moduleFk: 'Orders'
|
moduleFk: 'order'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||||
|
|
||||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(orders.position).toEqual(1);
|
expect(orders.position).toEqual(1);
|
||||||
expect(clients.position).toEqual(2);
|
expect(clients.position).toEqual(2);
|
||||||
|
|
||||||
await app.models.StarredModule.setPosition(ctx, 'Clients', 'left', options);
|
await app.models.StarredModule.setPosition(ctx, 'customer', 'left', options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
clients = await app.models.StarredModule.findOne(filter, options);
|
clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Orders';
|
filter.where.moduleFk = 'order';
|
||||||
orders = await app.models.StarredModule.findOne(filter, options);
|
orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(clients.position).toEqual(1);
|
expect(clients.position).toEqual(1);
|
||||||
|
@ -67,29 +67,29 @@ describe('setPosition()', () => {
|
||||||
const filter = {
|
const filter = {
|
||||||
where: {
|
where: {
|
||||||
workerFk: ctx.req.accessToken.userId,
|
workerFk: ctx.req.accessToken.userId,
|
||||||
moduleFk: 'Orders'
|
moduleFk: 'order'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||||
|
|
||||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(orders.position).toEqual(1);
|
expect(orders.position).toEqual(1);
|
||||||
expect(clients.position).toEqual(2);
|
expect(clients.position).toEqual(2);
|
||||||
|
|
||||||
await app.models.StarredModule.setPosition(ctx, 'Orders', 'right', options);
|
await app.models.StarredModule.setPosition(ctx, 'order', 'right', options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Orders';
|
filter.where.moduleFk = 'order';
|
||||||
orders = await app.models.StarredModule.findOne(filter, options);
|
orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
clients = await app.models.StarredModule.findOne(filter, options);
|
clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(orders.position).toEqual(2);
|
expect(orders.position).toEqual(2);
|
||||||
|
@ -108,35 +108,35 @@ describe('setPosition()', () => {
|
||||||
const filter = {
|
const filter = {
|
||||||
where: {
|
where: {
|
||||||
workerFk: ctx.req.accessToken.userId,
|
workerFk: ctx.req.accessToken.userId,
|
||||||
moduleFk: 'Items'
|
moduleFk: 'item'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Items', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'item', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Claims', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Zones', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'zone', options);
|
||||||
|
|
||||||
const items = await app.models.StarredModule.findOne(filter, options);
|
const items = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Claims';
|
filter.where.moduleFk = 'claim';
|
||||||
const claims = await app.models.StarredModule.findOne(filter, options);
|
const claims = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Orders';
|
filter.where.moduleFk = 'order';
|
||||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Zones';
|
filter.where.moduleFk = 'zone';
|
||||||
const zones = await app.models.StarredModule.findOne(filter, options);
|
const zones = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(items.position).toEqual(1);
|
expect(items.position).toEqual(1);
|
||||||
|
@ -145,12 +145,12 @@ describe('setPosition()', () => {
|
||||||
expect(orders.position).toEqual(4);
|
expect(orders.position).toEqual(4);
|
||||||
expect(zones.position).toEqual(5);
|
expect(zones.position).toEqual(5);
|
||||||
|
|
||||||
await app.models.StarredModule.setPosition(ctx, 'Clients', 'right', options);
|
await app.models.StarredModule.setPosition(ctx, 'customer', 'right', options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Orders';
|
filter.where.moduleFk = 'order';
|
||||||
orders = await app.models.StarredModule.findOne(filter, options);
|
orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
clients = await app.models.StarredModule.findOne(filter, options);
|
clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(orders.position).toEqual(3);
|
expect(orders.position).toEqual(3);
|
||||||
|
@ -169,31 +169,31 @@ describe('setPosition()', () => {
|
||||||
const filter = {
|
const filter = {
|
||||||
where: {
|
where: {
|
||||||
workerFk: ctx.req.accessToken.userId,
|
workerFk: ctx.req.accessToken.userId,
|
||||||
moduleFk: 'Items'
|
moduleFk: 'item'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Items', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'item', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Clients', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Claims', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Orders', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Zones', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'zone', options);
|
||||||
|
|
||||||
const items = await app.models.StarredModule.findOne(filter, options);
|
const items = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Claims';
|
filter.where.moduleFk = 'claim';
|
||||||
const claims = await app.models.StarredModule.findOne(filter, options);
|
const claims = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Orders';
|
filter.where.moduleFk = 'order';
|
||||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Zones';
|
filter.where.moduleFk = 'zone';
|
||||||
const zones = await app.models.StarredModule.findOne(filter, options);
|
const zones = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(items.position).toEqual(1);
|
expect(items.position).toEqual(1);
|
||||||
|
@ -202,13 +202,13 @@ describe('setPosition()', () => {
|
||||||
expect(orders.position).toEqual(4);
|
expect(orders.position).toEqual(4);
|
||||||
expect(zones.position).toEqual(5);
|
expect(zones.position).toEqual(5);
|
||||||
|
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Claims', options);
|
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||||
await app.models.StarredModule.setPosition(ctx, 'Clients', 'right', options);
|
await app.models.StarredModule.setPosition(ctx, 'customer', 'right', options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Clients';
|
filter.where.moduleFk = 'customer';
|
||||||
clients = await app.models.StarredModule.findOne(filter, options);
|
clients = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
filter.where.moduleFk = 'Orders';
|
filter.where.moduleFk = 'order';
|
||||||
orders = await app.models.StarredModule.findOne(filter, options);
|
orders = await app.models.StarredModule.findOne(filter, options);
|
||||||
|
|
||||||
expect(orders.position).toEqual(2);
|
expect(orders.position).toEqual(2);
|
||||||
|
|
|
@ -21,15 +21,15 @@ describe('toggleStarredModule()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a new starred module and then remove it by calling the method again with same args', async() => {
|
it('should create a new starred module and then remove it by calling the method again with same args', async() => {
|
||||||
const starredModule = await app.models.StarredModule.toggleStarredModule(ctx, 'Orders');
|
const starredModule = await app.models.StarredModule.toggleStarredModule(ctx, 'order');
|
||||||
let starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
let starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
||||||
|
|
||||||
expect(starredModules.length).toEqual(1);
|
expect(starredModules.length).toEqual(1);
|
||||||
expect(starredModule.moduleFk).toEqual('Orders');
|
expect(starredModule.moduleFk).toEqual('order');
|
||||||
expect(starredModule.workerFk).toEqual(activeCtx.accessToken.userId);
|
expect(starredModule.workerFk).toEqual(activeCtx.accessToken.userId);
|
||||||
expect(starredModule.position).toEqual(starredModules.length);
|
expect(starredModule.position).toEqual(starredModules.length);
|
||||||
|
|
||||||
await app.models.StarredModule.toggleStarredModule(ctx, 'Orders');
|
await app.models.StarredModule.toggleStarredModule(ctx, 'order');
|
||||||
starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
||||||
|
|
||||||
expect(starredModules.length).toEqual(0);
|
expect(starredModules.length).toEqual(0);
|
||||||
|
|
|
@ -77,6 +77,21 @@
|
||||||
"Module": {
|
"Module": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"Notification": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"NotificationAcl": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"NotificationConfig": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"NotificationQueue": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"NotificationSubscription": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"Province": {
|
"Province": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
@ -101,6 +116,9 @@
|
||||||
"Town": {
|
"Town": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"Url": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"UserConfig": {
|
"UserConfig": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||||
const md5 = require('md5');
|
const md5 = require('md5');
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
const {Email} = require('vn-print');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/account/login')(Self);
|
require('../methods/account/login')(Self);
|
||||||
|
@ -6,6 +9,7 @@ module.exports = Self => {
|
||||||
require('../methods/account/acl')(Self);
|
require('../methods/account/acl')(Self);
|
||||||
require('../methods/account/change-password')(Self);
|
require('../methods/account/change-password')(Self);
|
||||||
require('../methods/account/set-password')(Self);
|
require('../methods/account/set-password')(Self);
|
||||||
|
require('../methods/account/recover-password')(Self);
|
||||||
require('../methods/account/validate-token')(Self);
|
require('../methods/account/validate-token')(Self);
|
||||||
require('../methods/account/privileges')(Self);
|
require('../methods/account/privileges')(Self);
|
||||||
|
|
||||||
|
@ -27,17 +31,62 @@ module.exports = Self => {
|
||||||
ctx.data.password = md5(ctx.data.password);
|
ctx.data.password = md5(ctx.data.password);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
||||||
|
if (!ctx.args || !ctx.args.data.email) return;
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
const httpCtx = {req: loopBackContext.active};
|
||||||
|
const httpRequest = httpCtx.req.http.req;
|
||||||
|
const headers = httpRequest.headers;
|
||||||
|
const origin = headers.origin;
|
||||||
|
const url = origin.split(':');
|
||||||
|
|
||||||
|
const userId = ctx.instance.id;
|
||||||
|
const user = await models.user.findById(userId);
|
||||||
|
|
||||||
|
class Mailer {
|
||||||
|
async send(verifyOptions, cb) {
|
||||||
|
const params = {
|
||||||
|
url: verifyOptions.verifyHref,
|
||||||
|
recipient: verifyOptions.to,
|
||||||
|
lang: ctx.req.getLocale()
|
||||||
|
};
|
||||||
|
|
||||||
|
const email = new Email('email-verify', params);
|
||||||
|
email.send();
|
||||||
|
|
||||||
|
cb(null, verifyOptions.to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
type: 'email',
|
||||||
|
to: instance.email,
|
||||||
|
from: {},
|
||||||
|
redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`,
|
||||||
|
template: false,
|
||||||
|
mailer: new Mailer,
|
||||||
|
host: url[1].split('/')[2],
|
||||||
|
port: url[2],
|
||||||
|
protocol: url[0],
|
||||||
|
user: Self
|
||||||
|
};
|
||||||
|
|
||||||
|
await user.verify(options);
|
||||||
|
});
|
||||||
|
|
||||||
Self.remoteMethod('getCurrentUserData', {
|
Self.remoteMethod('getCurrentUserData', {
|
||||||
description: 'Gets the current user data',
|
description: 'Gets the current user data',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'ctx',
|
arg: 'ctx',
|
||||||
type: 'Object',
|
type: 'object',
|
||||||
http: {source: 'context'}
|
http: {source: 'context'}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'Object',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
@ -58,7 +107,7 @@ module.exports = Self => {
|
||||||
*
|
*
|
||||||
* @param {Integer} userId The user id
|
* @param {Integer} userId The user id
|
||||||
* @param {String} name The role name
|
* @param {String} name The role name
|
||||||
* @param {Object} options Options
|
* @param {object} options Options
|
||||||
* @return {Boolean} %true if user has the role, %false otherwise
|
* @return {Boolean} %true if user has the role, %false otherwise
|
||||||
*/
|
*/
|
||||||
Self.hasRole = async function(userId, name, options) {
|
Self.hasRole = async function(userId, name, options) {
|
||||||
|
@ -70,8 +119,8 @@ module.exports = Self => {
|
||||||
* Get all user roles.
|
* Get all user roles.
|
||||||
*
|
*
|
||||||
* @param {Integer} userId The user id
|
* @param {Integer} userId The user id
|
||||||
* @param {Object} options Options
|
* @param {object} options Options
|
||||||
* @return {Object} User role list
|
* @return {object} User role list
|
||||||
*/
|
*/
|
||||||
Self.getRoles = async(userId, options) => {
|
Self.getRoles = async(userId, options) => {
|
||||||
let result = await Self.rawSql(
|
let result = await Self.rawSql(
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
"email": {
|
"email": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"emailVerified": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"created": {
|
"created": {
|
||||||
"type": "date"
|
"type": "date"
|
||||||
},
|
},
|
||||||
|
@ -88,20 +91,34 @@
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$everyone",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"property": "recoverPassword",
|
||||||
|
"accessType": "EXECUTE",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"property": "logout",
|
"property": "logout",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$authenticated",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"property": "validateToken",
|
"property": "validateToken",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$authenticated",
|
"principalId": "$authenticated",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"property": "privileges",
|
||||||
|
"accessType": "*",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$authenticated",
|
||||||
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,23 @@ module.exports = Self => {
|
||||||
require('../methods/chat/sendCheckingPresence')(Self);
|
require('../methods/chat/sendCheckingPresence')(Self);
|
||||||
require('../methods/chat/notifyIssues')(Self);
|
require('../methods/chat/notifyIssues')(Self);
|
||||||
require('../methods/chat/sendQueued')(Self);
|
require('../methods/chat/sendQueued')(Self);
|
||||||
|
|
||||||
|
Self.observe('before save', async function(ctx) {
|
||||||
|
if (!ctx.isNewInstance) return;
|
||||||
|
|
||||||
|
let {message} = ctx.instance;
|
||||||
|
if (!message) return;
|
||||||
|
|
||||||
|
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
|
||||||
|
if (!parts) return;
|
||||||
|
|
||||||
|
const replacedParts = parts.map(part => {
|
||||||
|
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const [index, part] of parts.entries())
|
||||||
|
message = message.replace(part, replacedParts[index]);
|
||||||
|
|
||||||
|
ctx.instance.message = message;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ module.exports = Self => {
|
||||||
require('../methods/dms/removeFile')(Self);
|
require('../methods/dms/removeFile')(Self);
|
||||||
require('../methods/dms/updateFile')(Self);
|
require('../methods/dms/updateFile')(Self);
|
||||||
require('../methods/dms/deleteTrashFiles')(Self);
|
require('../methods/dms/deleteTrashFiles')(Self);
|
||||||
|
require('../methods/dms/saveSign')(Self);
|
||||||
|
|
||||||
Self.checkRole = async function(ctx, id) {
|
Self.checkRole = async function(ctx, id) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"token": {
|
"cookie": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -29,4 +29,4 @@
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
require('../methods/docuware/download')(Self);
|
require('../methods/docuware/download')(Self);
|
||||||
|
require('../methods/docuware/upload')(Self);
|
||||||
require('../methods/docuware/checkFile')(Self);
|
require('../methods/docuware/checkFile')(Self);
|
||||||
|
require('../methods/docuware/deliveryNoteEmail')(Self);
|
||||||
|
require('../methods/docuware/core')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,20 +19,14 @@
|
||||||
"fileCabinetName": {
|
"fileCabinetName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"dialogName": {
|
"dialogName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"find": {
|
"findById": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"acls": [
|
}
|
||||||
{
|
|
||||||
"property": "*",
|
|
||||||
"accessType": "*",
|
|
||||||
"principalType": "ROLE",
|
|
||||||
"principalId": "$everyone",
|
|
||||||
"permission": "ALLOW"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/notification/send')(Self);
|
||||||
|
require('../methods/notification/clean')(Self);
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "Notification",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "util.notification"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"subscription": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"model": "NotificationSubscription",
|
||||||
|
"foreignKey": "notificationFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "NotificationAcl",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "util.notificationAcl"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"notification": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Notification",
|
||||||
|
"foreignKey": "notificationFk"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Role",
|
||||||
|
"foreignKey": "roleFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "NotificationConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "util.notificationConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"cleanDays": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"name": "NotificationQueue",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "util.notificationQueue"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"params": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"created": {
|
||||||
|
"type": "date"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"notification": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Notification",
|
||||||
|
"foreignKey": "notificationFk",
|
||||||
|
"primaryKey": "name"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Account",
|
||||||
|
"foreignKey": "authorFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "NotificationSubscription",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "util.notificationSubscription"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"notificationFk": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"notification": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Notification",
|
||||||
|
"foreignKey": "notificationFk"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Account",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,9 +27,6 @@
|
||||||
"newStatusId": {
|
"newStatusId": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"action": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"day": {
|
"day": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
@ -47,6 +44,15 @@
|
||||||
},
|
},
|
||||||
"portDb": {
|
"portDb": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"responseType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fromEmailId": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"replyTo": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('loopback model Account', () => {
|
describe('loopback model Account', () => {
|
||||||
it('should return true if the user has the given role', async() => {
|
it('should return true if the user has the given role', async() => {
|
||||||
let result = await app.models.Account.hasRole(1, 'employee');
|
let result = await models.Account.hasRole(1, 'employee');
|
||||||
|
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if the user doesnt have the given role', async() => {
|
it('should return false if the user doesnt have the given role', async() => {
|
||||||
let result = await app.models.Account.hasRole(1, 'administrator');
|
let result = await models.Account.hasRole(1, 'administrator');
|
||||||
|
|
||||||
expect(result).toBeFalsy();
|
expect(result).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('account recoverPassword()', () => {
|
||||||
|
const userId = 1107;
|
||||||
|
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: userId},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send email with token', async() => {
|
||||||
|
const userId = 1107;
|
||||||
|
const user = await models.Account.findById(userId);
|
||||||
|
|
||||||
|
await models.Account.recoverPassword(user.email);
|
||||||
|
|
||||||
|
const result = await models.AccessToken.findOne({where: {userId: userId}});
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "Url",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "salix.url"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"appName": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"id": 1
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true,
|
||||||
|
"id": 2
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
const {Email} = require('vn-print');
|
||||||
|
|
||||||
|
module.exports = function(Self) {
|
||||||
|
Self.on('resetPasswordRequest', async function(info) {
|
||||||
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
const httpCtx = {req: loopBackContext.active};
|
||||||
|
const httpRequest = httpCtx.req.http.req;
|
||||||
|
const headers = httpRequest.headers;
|
||||||
|
const origin = headers.origin;
|
||||||
|
|
||||||
|
const user = await Self.app.models.Account.findById(info.user.id);
|
||||||
|
const params = {
|
||||||
|
recipient: info.email,
|
||||||
|
lang: user.lang,
|
||||||
|
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
|
||||||
|
};
|
||||||
|
|
||||||
|
const options = Object.assign({}, info.options);
|
||||||
|
for (const param in options)
|
||||||
|
params[param] = options[param];
|
||||||
|
|
||||||
|
const email = new Email(options.emailTemplate, params);
|
||||||
|
|
||||||
|
return email.send();
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,3 +0,0 @@
|
||||||
ALTER TABLE `vn`.`accountingType` ADD daysInFuture INT NULL;
|
|
||||||
ALTER TABLE `vn`.`accountingType` MODIFY COLUMN daysInFuture int(11) DEFAULT 0 NULL;
|
|
||||||
UPDATE `vn`.`accountingType` SET daysInFuture=1 WHERE id=8;
|
|
|
@ -1,4 +0,0 @@
|
||||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
|
||||||
VALUES
|
|
||||||
('ItemType', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
|
||||||
('ItemType', '*', 'WRITE', 'ALLOW', 'ROLE', 'buyer');
|
|
|
@ -1,14 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS `vn`.`mdbBranch` (
|
|
||||||
`name` VARCHAR(255),
|
|
||||||
PRIMARY KEY(`name`)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `vn`.`mdbVersion` (
|
|
||||||
`app` VARCHAR(255) NOT NULL,
|
|
||||||
`branchFk` VARCHAR(255) NOT NULL,
|
|
||||||
`version` INT,
|
|
||||||
CONSTRAINT `mdbVersion_branchFk` FOREIGN KEY (`branchFk`) REFERENCES `vn`.`mdbBranch` (`name`) ON DELETE CASCADE ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT IGNORE INTO `salix`.`ACL` (`id`, `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
|
||||||
VALUES(318, 'MdbVersion', '*', '*', 'ALLOW', 'ROLE', 'developer');
|
|
|
@ -1,13 +0,0 @@
|
||||||
CREATE TABLE `vn`.`chat` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`senderFk` int(10) unsigned DEFAULT NULL,
|
|
||||||
`recipient` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
|
|
||||||
`dated` date DEFAULT NULL,
|
|
||||||
`checkUserStatus` tinyint(1) DEFAULT NULL,
|
|
||||||
`message` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
|
|
||||||
`status` tinyint(1) DEFAULT NULL,
|
|
||||||
`attempts` int(1) DEFAULT NULL,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `chat_FK` (`senderFk`),
|
|
||||||
CONSTRAINT `chat_FK` FOREIGN KEY (`senderFk`) REFERENCES `account`.`user` (`id`) ON UPDATE CASCADE
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
|
|
@ -1,8 +0,0 @@
|
||||||
ALTER TABLE `vn`.`creditInsurance` ADD creditClassificationFk int(11) NULL;
|
|
||||||
|
|
||||||
UPDATE `vn`.`creditInsurance` AS `destiny`
|
|
||||||
SET `destiny`.`creditClassificationFk`= (SELECT creditClassification FROM `vn`.`creditInsurance` AS `origin` WHERE `origin`.id = `destiny`.id);
|
|
||||||
|
|
||||||
ALTER TABLE `vn`.`creditInsurance`
|
|
||||||
ADD CONSTRAINT `creditInsurance_creditClassificationFk` FOREIGN KEY (`creditClassificationFk`)
|
|
||||||
REFERENCES `vn`.`creditClassification` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
@ -1,3 +0,0 @@
|
||||||
INSERT INTO `salix`.`defaultViewConfig` (tableCode, columns)
|
|
||||||
VALUES ('clientsDetail', '{"id":true,"phone":true,"city":true,"socialName":true,"salesPersonFk":true,"email":true,"name":false,"fi":false,"credit":false,"creditInsurance":false,"mobile":false,"street":false,"countryFk":false,"provinceFk":false,"postcode":false,"created":false,"businessTypeFk":false,"payMethodFk":false,"sageTaxTypeFk":false,"sageTransactionTypeFk":false,"isActive":false,"isVies":false,"isTaxDataChecked":false,"isEqualizated":false,"isFreezed":false,"hasToInvoice":false,"hasToInvoiceByAddress":false,"isToBeMailed":false,"hasLcr":false,"hasCoreVnl":false,"hasSepaVnl":false}');
|
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
DROP PROCEDURE IF EXISTS `vn`.`ticket_doRefund`;
|
|
||||||
|
|
||||||
DELIMITER $$
|
|
||||||
$$
|
|
||||||
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_doRefund`(OUT vNewTicket INT)
|
|
||||||
BEGIN
|
|
||||||
/**
|
|
||||||
* Crea un ticket de abono a partir de tmp.sale y/o tmp.ticketService
|
|
||||||
*
|
|
||||||
* @return vNewTicket
|
|
||||||
*/
|
|
||||||
DECLARE vDone BIT DEFAULT 0;
|
|
||||||
DECLARE vClientFk MEDIUMINT;
|
|
||||||
DECLARE vWarehouse TINYINT;
|
|
||||||
DECLARE vCompany MEDIUMINT;
|
|
||||||
DECLARE vAddress MEDIUMINT;
|
|
||||||
DECLARE vRefundAgencyMode INT;
|
|
||||||
DECLARE vItemFk INT;
|
|
||||||
DECLARE vQuantity DECIMAL (10,2);
|
|
||||||
DECLARE vConcept VARCHAR(50);
|
|
||||||
DECLARE vPrice DECIMAL (10,2);
|
|
||||||
DECLARE vDiscount TINYINT;
|
|
||||||
DECLARE vSaleNew INT;
|
|
||||||
DECLARE vSaleMain INT;
|
|
||||||
DECLARE vZoneFk INT;
|
|
||||||
DECLARE vDescription VARCHAR(50);
|
|
||||||
DECLARE vTaxClassFk INT;
|
|
||||||
DECLARE vTicketServiceTypeFk INT;
|
|
||||||
DECLARE vOriginTicket INT;
|
|
||||||
|
|
||||||
DECLARE cSales CURSOR FOR
|
|
||||||
SELECT s.id, s.itemFk, - s.quantity, s.concept, s.price, s.discount
|
|
||||||
FROM tmp.sale s;
|
|
||||||
|
|
||||||
DECLARE cTicketServices CURSOR FOR
|
|
||||||
SELECT ts.description, - ts.quantity, ts.price, ts.taxClassFk, ts.ticketServiceTypeFk
|
|
||||||
FROM tmp.ticketService ts;
|
|
||||||
|
|
||||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
|
|
||||||
|
|
||||||
SELECT sub.ticketFk INTO vOriginTicket
|
|
||||||
FROM (
|
|
||||||
SELECT s.ticketFk
|
|
||||||
FROM tmp.sale s
|
|
||||||
UNION ALL
|
|
||||||
SELECT ts.ticketFk
|
|
||||||
FROM tmp.ticketService ts
|
|
||||||
) sub
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
SELECT id INTO vRefundAgencyMode
|
|
||||||
FROM agencyMode WHERE `name` = 'ABONO';
|
|
||||||
|
|
||||||
SELECT clientFk, warehouseFk, companyFk, addressFk
|
|
||||||
INTO vClientFk, vWarehouse, vCompany, vAddress
|
|
||||||
FROM ticket
|
|
||||||
WHERE id = vOriginTicket;
|
|
||||||
|
|
||||||
SELECT id INTO vZoneFk
|
|
||||||
FROM zone WHERE agencyModeFk = vRefundAgencyMode
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
INSERT INTO vn.ticket (
|
|
||||||
clientFk,
|
|
||||||
shipped,
|
|
||||||
addressFk,
|
|
||||||
agencyModeFk,
|
|
||||||
nickname,
|
|
||||||
warehouseFk,
|
|
||||||
companyFk,
|
|
||||||
landed,
|
|
||||||
zoneFk
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
vClientFk,
|
|
||||||
CURDATE(),
|
|
||||||
vAddress,
|
|
||||||
vRefundAgencyMode,
|
|
||||||
a.nickname,
|
|
||||||
vWarehouse,
|
|
||||||
vCompany,
|
|
||||||
CURDATE(),
|
|
||||||
vZoneFk
|
|
||||||
FROM address a
|
|
||||||
WHERE a.id = vAddress;
|
|
||||||
|
|
||||||
SET vNewTicket = LAST_INSERT_ID();
|
|
||||||
|
|
||||||
SET vDone := FALSE;
|
|
||||||
OPEN cSales;
|
|
||||||
FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
|
|
||||||
|
|
||||||
WHILE NOT vDone DO
|
|
||||||
|
|
||||||
INSERT INTO vn.sale(ticketFk, itemFk, quantity, concept, price, discount)
|
|
||||||
VALUES( vNewTicket, vItemFk, vQuantity, vConcept, vPrice, vDiscount );
|
|
||||||
|
|
||||||
SET vSaleNew = LAST_INSERT_ID();
|
|
||||||
|
|
||||||
INSERT INTO vn.saleComponent(saleFk,componentFk,`value`)
|
|
||||||
SELECT vSaleNew,componentFk,`value`
|
|
||||||
FROM vn.saleComponent
|
|
||||||
WHERE saleFk = vSaleMain;
|
|
||||||
|
|
||||||
FETCH cSales INTO vSaleMain, vItemFk, vQuantity, vConcept, vPrice, vDiscount;
|
|
||||||
|
|
||||||
END WHILE;
|
|
||||||
CLOSE cSales;
|
|
||||||
|
|
||||||
SET vDone := FALSE;
|
|
||||||
OPEN cTicketServices;
|
|
||||||
FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk;
|
|
||||||
|
|
||||||
WHILE NOT vDone DO
|
|
||||||
|
|
||||||
INSERT INTO vn.ticketService(description, quantity, price, taxClassFk, ticketFk, ticketServiceTypeFk)
|
|
||||||
VALUES(vDescription, vQuantity, vPrice, vTaxClassFk, vNewTicket, vTicketServiceTypeFk);
|
|
||||||
|
|
||||||
FETCH cTicketServices INTO vDescription, vQuantity, vPrice, vTaxClassFk, vTicketServiceTypeFk;
|
|
||||||
|
|
||||||
END WHILE;
|
|
||||||
CLOSE cTicketServices;
|
|
||||||
|
|
||||||
INSERT INTO vn.ticketRefund(refundTicketFk, originalTicketFk)
|
|
||||||
VALUES(vNewTicket, vOriginTicket);
|
|
||||||
END$$
|
|
||||||
DELIMITER ;
|
|
|
@ -1,11 +0,0 @@
|
||||||
DELIMITER $$
|
|
||||||
$$
|
|
||||||
CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`creditInsurance_beforeInsert`
|
|
||||||
BEFORE INSERT ON `creditInsurance`
|
|
||||||
FOR EACH ROW
|
|
||||||
BEGIN
|
|
||||||
IF NEW.creditClassificationFk THEN
|
|
||||||
SET NEW.creditClassification = NEW.creditClassificationFk;
|
|
||||||
END IF;
|
|
||||||
END$$
|
|
||||||
DELIMITER ;
|
|
|
@ -1 +0,0 @@
|
||||||
RENAME TABLE `edi`.`fileConfig` to `edi`.`tableConfig`;
|
|
|
@ -1,22 +0,0 @@
|
||||||
CREATE TABLE `edi`.`fileConfig`
|
|
||||||
(
|
|
||||||
name varchar(25) NOT NULL,
|
|
||||||
checksum text NULL,
|
|
||||||
keyValue tinyint(1) default true NOT NULL,
|
|
||||||
constraint fileConfig_pk
|
|
||||||
primary key (name)
|
|
||||||
);
|
|
||||||
|
|
||||||
create unique index fileConfig_name_uindex
|
|
||||||
on `edi`.`fileConfig` (name);
|
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue)
|
|
||||||
VALUES ('FEC010104', null, 0);
|
|
||||||
|
|
||||||
INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue)
|
|
||||||
VALUES ('VBN020101', null, 1);
|
|
||||||
|
|
||||||
INSERT INTO `edi`.`fileConfig` (name, checksum, keyValue)
|
|
||||||
VALUES ('florecompc2', null, 1);
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
ALTER TABLE `vn`.`chat` MODIFY COLUMN message TEXT CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL;
|
|
||||||
ALTER TABLE `vn`.`chat` MODIFY COLUMN dated DATETIME DEFAULT NULL NULL;
|
|
||||||
ALTER TABLE `vn`.`chat` ADD error TEXT NULL;
|
|
|
@ -1,3 +0,0 @@
|
||||||
ALTER TABLE `vn`.`creditInsurance`
|
|
||||||
ADD CONSTRAINT `creditInsurance_creditClassificationFk` FOREIGN KEY (`creditClassificationFk`)
|
|
||||||
REFERENCES `vn`.`creditClassification` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
@ -1,21 +0,0 @@
|
||||||
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
|
|
||||||
VALUES
|
|
||||||
('InvoiceOut','refund','WRITE','ALLOW','ROLE','invoicing'),
|
|
||||||
('InvoiceOut','refund','WRITE','ALLOW','ROLE','salesAssistant'),
|
|
||||||
('InvoiceOut','refund','WRITE','ALLOW','ROLE','claimManager'),
|
|
||||||
('Ticket','refund','WRITE','ALLOW','ROLE','invoicing'),
|
|
||||||
('Ticket','refund','WRITE','ALLOW','ROLE','salesAssistant'),
|
|
||||||
('Ticket','refund','WRITE','ALLOW','ROLE','claimManager'),
|
|
||||||
('Sale','refund','WRITE','ALLOW','ROLE','salesAssistant'),
|
|
||||||
('Sale','refund','WRITE','ALLOW','ROLE','claimManager'),
|
|
||||||
('TicketRefund','*','WRITE','ALLOW','ROLE','invoicing'),
|
|
||||||
('ClaimObservation','*','WRITE','ALLOW','ROLE','salesPerson'),
|
|
||||||
('ClaimObservation','*','READ','ALLOW','ROLE','salesPerson'),
|
|
||||||
('Client','setPassword','WRITE','ALLOW','ROLE','salesPerson'),
|
|
||||||
('Client','updateUser','WRITE','ALLOW','ROLE','salesPerson');
|
|
||||||
|
|
||||||
DELETE FROM `salix`.`ACL` WHERE id=313;
|
|
||||||
|
|
||||||
UPDATE `salix`.`ACL`
|
|
||||||
SET principalId='invoicing'
|
|
||||||
WHERE id=297;
|
|
|
@ -1,4 +0,0 @@
|
||||||
INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
|
||||||
VALUES
|
|
||||||
('ZoneExclusionGeo', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
|
||||||
('ZoneExclusionGeo', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
|
|
|
@ -1,2 +0,0 @@
|
||||||
ALTER TABLE `vn2008`.`albaran_gestdoc` DROP FOREIGN KEY fk_albaran_gestdoc_gestdoc1;
|
|
||||||
ALTER TABLE `vn2008`.`albaran_gestdoc` ADD CONSTRAINT albaran_gestdoc_FK FOREIGN KEY (gestdoc_id) REFERENCES `vn`.`dms`(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
@ -1,3 +0,0 @@
|
||||||
alter table `vn`.`client`
|
|
||||||
add hasIncoterms tinyint(1) default 0 not null comment 'Received incoterms authorization from client';
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
DROP FUNCTION `account`.`userGetId`;
|
|
||||||
DROP FUNCTION `account`.`myUserGetName`;
|
|
||||||
DROP FUNCTION `account`.`myUserGetId`;
|
|
||||||
DROP FUNCTION `account`.`myUserHasRole`;
|
|
||||||
DROP FUNCTION `account`.`myUserHasRoleId`;
|
|
||||||
DROP FUNCTION `account`.`userGetName`;
|
|
||||||
DROP FUNCTION `account`.`userHasRole`;
|
|
||||||
DROP FUNCTION `account`.`userHasRoleId`;
|
|
||||||
DROP PROCEDURE `account`.`myUserLogout`;
|
|
||||||
DROP PROCEDURE `account`.`userLogin`;
|
|
||||||
DROP PROCEDURE `account`.`userLoginWithKey`;
|
|
||||||
DROP PROCEDURE `account`.`userLoginWithName`;
|
|
||||||
DROP PROCEDURE `account`.`userSetPassword`;
|
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE `vn`.`item` MODIFY COLUMN description TEXT CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL;
|
|
|
@ -1,5 +0,0 @@
|
||||||
ALTER TABLE `vn`.`itemType` CHANGE `transaction` transaction__ tinyint(4) DEFAULT 0 NOT NULL;
|
|
||||||
ALTER TABLE `vn`.`itemType` CHANGE location location__ varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL;
|
|
||||||
ALTER TABLE `vn`.`itemType` CHANGE hasComponents hasComponents__ tinyint(1) DEFAULT 1 NOT NULL;
|
|
||||||
ALTER TABLE `vn`.`itemType` CHANGE warehouseFk warehouseFk__ smallint(6) unsigned DEFAULT 60 NOT NULL;
|
|
||||||
ALTER TABLE `vn`.`itemType` CHANGE compression compression__ decimal(5,2) DEFAULT 1.00 NULL;
|
|
|
@ -1,10 +0,0 @@
|
||||||
UPDATE `vn`.`route` r
|
|
||||||
JOIN(SELECT r.id, wl.workcenterFk
|
|
||||||
FROM `vn`.`route` r
|
|
||||||
JOIN `vn`.`routeLog` rl ON rl.originFk = r.id
|
|
||||||
JOIN `vn`.`workerLabour` wl ON wl.workerFk = rl.userFk
|
|
||||||
AND r.created BETWEEN wl.started AND IFNULL(wl.ended, r.created)
|
|
||||||
WHERE r.created BETWEEN '2021-12-01' AND CURDATE()
|
|
||||||
AND rl.action = 'insert'
|
|
||||||
)sub ON sub.id = r.id
|
|
||||||
SET r.commissionWorkCenterFk = sub.workcenterFk;
|
|
|
@ -1,2 +0,0 @@
|
||||||
INSERT INTO `vn`.`sample` (code, description, isVisible, hasCompany, hasPreview, datepickerEnabled)
|
|
||||||
VALUES ('incoterms-authorization', 'Autorización de incoterms', 1, 1, 1, 0);
|
|
|
@ -1,18 +0,0 @@
|
||||||
ALTER TABLE `vn`.`itemShelving` DROP FOREIGN KEY itemShelving_fk2;
|
|
||||||
ALTER TABLE `vn`.`shelvingLog` DROP FOREIGN KEY shelvingLog_FK_ibfk_1;
|
|
||||||
ALTER TABLE `vn`.`smartTag` DROP FOREIGN KEY smartTag_shelving_fk;
|
|
||||||
ALTER TABLE `vn`.`workerShelving` DROP FOREIGN KEY workerShelving_shelving_fk;
|
|
||||||
|
|
||||||
ALTER TABLE `vn`.`shelving` DROP PRIMARY KEY;
|
|
||||||
ALTER TABLE `vn`.`shelving` ADD id INT auto_increment PRIMARY KEY NULL;
|
|
||||||
ALTER TABLE `vn`.`shelving` CHANGE id id int(11) auto_increment NOT NULL FIRST;
|
|
||||||
ALTER TABLE `vn`.`shelving` ADD CONSTRAINT shelving_UN UNIQUE KEY (code);
|
|
||||||
|
|
||||||
ALTER TABLE `vn`.`itemShelving` ADD CONSTRAINT itemShelving_fk2 FOREIGN KEY (shelvingFk) REFERENCES `vn`.`shelving`(code) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE `vn`.`shelvingLog` ADD CONSTRAINT shelvingLog_FK_ibfk_1 FOREIGN KEY (originFk) REFERENCES `vn`.`shelving`(code) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE `vn`.`smartTag` ADD CONSTRAINT smartTag_FK FOREIGN KEY (shelvingFk) REFERENCES `vn`.`shelving`(code) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE `vn`.`workerShelving` ADD CONSTRAINT workerShelving_FK_1 FOREIGN KEY (shelvingFk) REFERENCES `vn`.`shelving`(code) ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE vn.shelvingLog DROP FOREIGN KEY shelvingLog_FK_ibfk_1;
|
|
||||||
ALTER TABLE vn.shelvingLog MODIFY COLUMN originFk INT NOT NULL;
|
|
||||||
ALTER TABLE vn.shelvingLog ADD CONSTRAINT shelvingLog_FK FOREIGN KEY (originFk) REFERENCES vn.shelving(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
@ -1,21 +0,0 @@
|
||||||
DROP PROCEDURE IF EXISTS `vn`.`ticketRefund_beforeUpsert`;
|
|
||||||
|
|
||||||
DELIMITER $$
|
|
||||||
$$
|
|
||||||
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketRefund_beforeUpsert`(vRefundTicketFk INT, vOriginalTicketFk INT)
|
|
||||||
BEGIN
|
|
||||||
DECLARE vAlreadyExists BOOLEAN DEFAULT FALSE;
|
|
||||||
|
|
||||||
IF vRefundTicketFk = vOriginalTicketFk THEN
|
|
||||||
CALL util.throw('Original ticket and refund ticket has same id');
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
SELECT COUNT(*) INTO vAlreadyExists
|
|
||||||
FROM ticketRefund
|
|
||||||
WHERE originalTicketFk = vOriginalTicketFk;
|
|
||||||
|
|
||||||
IF vAlreadyExists > 0 THEN
|
|
||||||
CALL util.throw('This ticket is already a refund');
|
|
||||||
END IF;
|
|
||||||
END$$
|
|
||||||
DELIMITER ;
|
|
|
@ -1,13 +0,0 @@
|
||||||
CREATE TABLE `vn`.`claimObservation` (
|
|
||||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
|
||||||
`claimFk` int(10) unsigned NOT NULL,
|
|
||||||
`workerFk` int(10) unsigned DEFAULT NULL,
|
|
||||||
`text` text COLLATE utf8_unicode_ci NOT NULL,
|
|
||||||
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
KEY `worker_key` (`workerFk`),
|
|
||||||
KEY `claim_key` (`claimFk`),
|
|
||||||
KEY `claimObservation_created_IDX` (`created`) USING BTREE,
|
|
||||||
CONSTRAINT `claimObservation_ibfk_1` FOREIGN KEY (`claimFk`) REFERENCES `vn`.`claim` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
CONSTRAINT `claimObservation_ibfk_2` FOREIGN KEY (`workerFk`) REFERENCES `vn`.`worker` (`id`) ON UPDATE CASCADE
|
|
||||||
) COMMENT='Todas las observaciones referentes a una reclamación'
|
|
|
@ -1,2 +0,0 @@
|
||||||
INSERT INTO `vn`.`claimObservation` (`claimFk`, `text`, `created`)
|
|
||||||
SELECT `id`, `observation`, `created` FROM `vn`.`claim`
|
|
|
@ -1,2 +0,0 @@
|
||||||
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
|
|
||||||
VALUES ('Parking','*','*','ALLOW','ROLE','employee')
|
|
|
@ -1,2 +0,0 @@
|
||||||
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
|
|
||||||
VALUES ('Shelving','*','*','ALLOW','ROLE','employee')
|
|
|
@ -1,20 +0,0 @@
|
||||||
CREATE TABLE `vn`.`osTicketConfig` (
|
|
||||||
`id` int(11) NOT NULL,
|
|
||||||
`host` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`user` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`password` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`oldStatus` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`newStatusId` int(11) DEFAULT NULL,
|
|
||||||
`action` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`day` int(11) DEFAULT NULL,
|
|
||||||
`comment` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`hostDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`userDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`passwordDb` varchar(100) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
|
||||||
`portDb` int(11) DEFAULT NULL,
|
|
||||||
PRIMARY KEY (`id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
|
||||||
|
|
||||||
INSERT INTO `vn`.`osTicketConfig`(`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `action`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`)
|
|
||||||
VALUES
|
|
||||||
(0, 'https://cau.verdnatura.es/scp', NULL, NULL, 'open', 3, 'Cerrar', 60, 'Este CAU se ha cerrado automáticamente', NULL, NULL, NULL, NULL);
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalId)
|
||||||
|
VALUES ('WorkerDisableExcluded','*','*','ALLOW','hr');
|
|
@ -1,3 +1,3 @@
|
||||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
VALUES
|
VALUES
|
||||||
('OsTicket', '*', '*', 'ALLOW', 'ROLE', 'employee');
|
('Business', '*', '*', 'ALLOW', 'ROLE', 'hr');
|
|
@ -1,3 +1,3 @@
|
||||||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
VALUES
|
VALUES
|
||||||
('OsTicketConfig', '*', '*', 'ALLOW', 'ROLE', 'it');
|
('Sale', 'usesMana', '*', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,4 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('InvoiceIn', 'invoiceInPdf', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||||
|
('InvoiceIn', 'invoiceInEmail', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -0,0 +1,23 @@
|
||||||
|
DROP FUNCTION IF EXISTS `util`.`notification_send`;
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE DEFINER=`root`@`localhost` FUNCTION `util`.`notification_send`(vNotificationName VARCHAR(255), vParams TEXT, vAuthorFk INT)
|
||||||
|
RETURNS INT
|
||||||
|
MODIFIES SQL DATA
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Sends a notification.
|
||||||
|
*
|
||||||
|
* @param vNotificationName The notification name
|
||||||
|
* @param vParams The notification parameters formatted as JSON
|
||||||
|
* @param vAuthorFk The notification author or %NULL if there is no author
|
||||||
|
* @return The notification id
|
||||||
|
*/
|
||||||
|
|
||||||
|
INSERT INTO notificationQueue
|
||||||
|
SET notificationFk = vNotificationName,
|
||||||
|
params = vParams,
|
||||||
|
authorFk = vAuthorFk;
|
||||||
|
|
||||||
|
RETURN LAST_INSERT_ID();
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,63 @@
|
||||||
|
USE util;
|
||||||
|
|
||||||
|
CREATE TABLE notification(
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
`name` VARCHAR(255) UNIQUE,
|
||||||
|
`description` VARCHAR(255)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE notificationAcl(
|
||||||
|
notificationFk INT,
|
||||||
|
roleFk INT(10) unsigned,
|
||||||
|
PRIMARY KEY(notificationFk, roleFk)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE `util`.`notificationAcl` ADD CONSTRAINT `notificationAcl_ibfk_1` FOREIGN KEY (`notificationFk`) REFERENCES `util`.`notification` (`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE `util`.`notificationAcl` ADD CONSTRAINT `notificationAcl_ibfk_2` FOREIGN KEY (`roleFk`) REFERENCES `account`.`role`(`id`)
|
||||||
|
ON DELETE RESTRICT
|
||||||
|
ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE notificationSubscription(
|
||||||
|
notificationFk INT,
|
||||||
|
userFk INT(10) unsigned,
|
||||||
|
PRIMARY KEY(notificationFk, userFk)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE `util`.`notificationSubscription` ADD CONSTRAINT `notificationSubscription_ibfk_1` FOREIGN KEY (`notificationFk`) REFERENCES `util`.`notification` (`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE `util`.`notificationSubscription` ADD CONSTRAINT `notificationSubscription_ibfk_2` FOREIGN KEY (`userFk`) REFERENCES `account`.`user`(`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE notificationQueue(
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
notificationFk VARCHAR(255),
|
||||||
|
params JSON,
|
||||||
|
authorFk INT(10) unsigned NULL,
|
||||||
|
`status` ENUM('pending', 'sent', 'error') NOT NULL DEFAULT 'pending',
|
||||||
|
created DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX(notificationFk),
|
||||||
|
INDEX(authorFk),
|
||||||
|
INDEX(status)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE `util`.`notificationQueue` ADD CONSTRAINT `nnotificationQueue_ibfk_1` FOREIGN KEY (`notificationFk`) REFERENCES `util`.`notification` (`name`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE `util`.`notificationQueue` ADD CONSTRAINT `notificationQueue_ibfk_2` FOREIGN KEY (`authorFk`) REFERENCES `account`.`user`(`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE notificationConfig(
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
cleanDays MEDIUMINT
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO notificationConfig
|
||||||
|
SET cleanDays = 90;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue