Merge branch 'dev' into 3074-invoiceIn-Tax-crear-total
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
998f00da49
|
@ -2,7 +2,13 @@
|
|||
{
|
||||
// Carácter predeterminado de final de línea.
|
||||
"files.eol": "\n",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"editor.codeActionsOnSave": ["source.fixAll.eslint"],
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"json"
|
||||
]
|
||||
}
|
39
Dockerfile
39
Dockerfile
|
@ -1,32 +1,43 @@
|
|||
FROM debian:stretch-slim
|
||||
FROM debian:bullseye-slim
|
||||
ENV TZ Europe/Madrid
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# NodeJs
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
ca-certificates \
|
||||
gnupg2 \
|
||||
libfontconfig \
|
||||
&& apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
|
||||
libdbus-1-3 libexpat1 libfontconfig1 libgbm1 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 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
|
||||
&& curl -sL https://deb.nodesource.com/setup_12.x | bash - \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
|
||||
&& apt-get install -y --no-install-recommends nodejs \
|
||||
&& npm install -g npm@8.19.2
|
||||
|
||||
# Puppeteer
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
nodejs \
|
||||
&& apt-get purge -y --auto-remove \
|
||||
gnupg2 \
|
||||
libfontconfig lftp xvfb gconf-service libasound2 libatk1.0-0 libc6 \
|
||||
libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 \
|
||||
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/* \
|
||||
&& npm -g install pm2
|
||||
|
||||
# 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 loopback/package.json loopback/
|
||||
COPY print/package.json print/
|
||||
RUN npm install --only=prod
|
||||
RUN npm --prefix ./print install --only=prod ./print
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY loopback loopback
|
||||
COPY back back
|
||||
|
|
|
@ -62,13 +62,13 @@ pipeline {
|
|||
}
|
||||
}
|
||||
}
|
||||
// stage('Backend') {
|
||||
// steps {
|
||||
// nodejs('node-v14') {
|
||||
// sh 'gulp launchBackTest --ci'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// stage('Backend') {
|
||||
// steps {
|
||||
// nodejs('node-v14') {
|
||||
// sh 'npm run test:back:ci'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
|
|
|
@ -54,17 +54,17 @@ $ gulp docker
|
|||
|
||||
For client-side unit tests run from project's root.
|
||||
```
|
||||
$ jest
|
||||
$ npm run test:front
|
||||
```
|
||||
|
||||
For server-side unit tests run from project's root.
|
||||
```
|
||||
$ gulp backTest
|
||||
$ npm run test:back
|
||||
```
|
||||
|
||||
For end-to-end tests run from project's root.
|
||||
```
|
||||
$ gulp e2e
|
||||
$ npm run test:e2e
|
||||
```
|
||||
|
||||
## Visual Studio Code extensions
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
const baseTime = null; // new Date(2022, 0, 19, 8, 0, 0, 0);
|
||||
if (baseTime) {
|
||||
jasmine.clock().install();
|
||||
jasmine.clock().mockDate(baseTime);
|
||||
}
|
|
@ -23,7 +23,13 @@ module.exports = Self => {
|
|||
let models = Self.app.models;
|
||||
|
||||
let user = await models.Account.findById(userId, {
|
||||
fields: ['id', 'name', 'nickname', 'email']
|
||||
fields: ['id', 'name', 'nickname', 'email', 'lang'],
|
||||
include: {
|
||||
relation: 'userConfig',
|
||||
scope: {
|
||||
fields: ['darkMode']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let roles = await models.RoleMapping.find({
|
||||
|
|
|
@ -5,17 +5,17 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The user id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'oldPassword',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'The old password',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'newPassword',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'The new password',
|
||||
required: true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('privileges', {
|
||||
description: 'Change role and hasGrant if user has privileges',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The user id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'roleFk',
|
||||
type: 'number',
|
||||
description: 'The new role for user',
|
||||
},
|
||||
{
|
||||
arg: 'hasGrant',
|
||||
type: 'boolean',
|
||||
description: 'Whether to has grant'
|
||||
}
|
||||
],
|
||||
http: {
|
||||
path: `/:id/privileges`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.privileges = async function(ctx, id, roleFk, hasGrant, options) {
|
||||
if (!(hasGrant != null || roleFk)) return;
|
||||
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
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)
|
||||
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`);
|
||||
|
||||
if (hasGrant != null)
|
||||
userToUpdate.hasGrant = hasGrant;
|
||||
|
||||
if (roleFk) {
|
||||
const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions);
|
||||
const hasRole = await models.Account.hasRole(userId, role.name, myOptions);
|
||||
|
||||
if (!hasRole)
|
||||
throw new UserError(`You don't own the role and you can't assign it to another user`);
|
||||
|
||||
userToUpdate.roleFk = roleFk;
|
||||
}
|
||||
|
||||
await userToUpdate.save(userToUpdate);
|
||||
await models.UserAccount.sync(userToUpdate.name);
|
||||
};
|
||||
};
|
|
@ -1,16 +1,15 @@
|
|||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('setPassword', {
|
||||
description: 'Sets the user password',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The user id',
|
||||
http: {source: 'path'}
|
||||
}, {
|
||||
arg: 'newPassword',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
description: 'The new password',
|
||||
required: true
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('account changePassword()', () => {
|
||||
it('should throw an error when old password is wrong', async() => {
|
||||
let req = app.models.Account.changePassword(null, 1, 'wrongOldPass', 'newPass');
|
||||
let err;
|
||||
await models.Account.changePassword(1, 'wrongPassword', 'nightmare.9999')
|
||||
.catch(error => err = error.sqlMessage);
|
||||
|
||||
await expectAsync(req).toBeRejected();
|
||||
expect(err).toBeDefined();
|
||||
expect(err).toEqual('Invalid password');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('account privileges()', () => {
|
||||
const employeeId = 1;
|
||||
const developerId = 9;
|
||||
const sysadminId = 66;
|
||||
const itBossId = 104;
|
||||
const rootId = 100;
|
||||
const clarkKent = 1103;
|
||||
|
||||
it('should throw an error when user not has privileges', async() => {
|
||||
const ctx = {req: {accessToken: {userId: developerId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await models.Account.privileges(ctx, employeeId, null, true, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
await tx.rollback();
|
||||
}
|
||||
|
||||
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() => {
|
||||
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await models.Account.privileges(ctx, employeeId, rootId, 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 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() => {
|
||||
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const agency = await models.Role.findOne({
|
||||
where: {
|
||||
name: 'agency'
|
||||
}
|
||||
}, options);
|
||||
|
||||
let error;
|
||||
let result;
|
||||
try {
|
||||
await models.Account.privileges(ctx, clarkKent, agency.id, null, options);
|
||||
result = await models.Account.findById(clarkKent, null, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
await tx.rollback();
|
||||
}
|
||||
|
||||
expect(error).not.toBeDefined();
|
||||
expect(result.roleFk).toEqual(agency.id);
|
||||
});
|
||||
|
||||
it('should change hasGrant', async() => {
|
||||
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
|
||||
let error;
|
||||
let result;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
await models.Account.privileges(ctx, clarkKent, null, true, options);
|
||||
result = await models.Account.findById(clarkKent, null, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
error = e;
|
||||
await tx.rollback();
|
||||
}
|
||||
|
||||
expect(error).not.toBeDefined();
|
||||
expect(result.hasGrant).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
const axios = require('axios');
|
||||
const tokenLifespan = 10;
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('getServiceAuth', {
|
||||
description: 'Authenticates with the service and request a new token',
|
||||
accessType: 'READ',
|
||||
accepts: [],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/getServiceAuth`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getServiceAuth = async() => {
|
||||
if (!this.login)
|
||||
this.login = await requestToken();
|
||||
|
||||
if (!this.login) return;
|
||||
|
||||
if (Date.now() > this.login.expires)
|
||||
this.login = await requestToken();
|
||||
|
||||
return this.login;
|
||||
};
|
||||
|
||||
/**
|
||||
* Requests a new Rocketchat token
|
||||
*/
|
||||
async function requestToken() {
|
||||
const models = Self.app.models;
|
||||
const chatConfig = await models.ChatConfig.findOne();
|
||||
|
||||
const {data} = await axios.post(`${chatConfig.api}/login`, {
|
||||
user: chatConfig.user,
|
||||
password: chatConfig.password
|
||||
});
|
||||
|
||||
const requestData = data.data;
|
||||
if (requestData) {
|
||||
return {
|
||||
host: chatConfig.host,
|
||||
api: chatConfig.api,
|
||||
auth: {
|
||||
userId: requestData.userId,
|
||||
token: requestData.authToken
|
||||
},
|
||||
expires: Date.now() + (1000 * 60 * tokenLifespan)
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
|
@ -8,7 +8,7 @@ module.exports = Self => {
|
|||
},
|
||||
http: {
|
||||
path: `/notifyIssues`,
|
||||
verb: 'GET'
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const got = require('got');
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('send', {
|
||||
description: 'Send a RocketChat message',
|
||||
description: 'Creates a direct message in the chat model for a user or a channel',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'to',
|
||||
|
@ -31,121 +30,19 @@ module.exports = Self => {
|
|||
const recipient = to.replace('@', '');
|
||||
|
||||
if (sender.name != recipient) {
|
||||
let {body} = await sendMessage(sender, to, message);
|
||||
if (body)
|
||||
body = JSON.parse(body);
|
||||
else
|
||||
body = false;
|
||||
await models.Chat.create({
|
||||
senderFk: sender.id,
|
||||
recipient: to,
|
||||
dated: new Date(),
|
||||
checkUserStatus: 0,
|
||||
message: message,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
});
|
||||
|
||||
return body;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
async function sendMessage(sender, channel, message) {
|
||||
const config = await getConfig();
|
||||
const avatar = `${config.host}/avatar/${sender.name}`;
|
||||
const uri = `${config.api}/chat.postMessage`;
|
||||
|
||||
return sendAuth(uri, {
|
||||
'channel': channel,
|
||||
'avatar': avatar,
|
||||
'alias': sender.nickname,
|
||||
'text': message
|
||||
}).catch(async error => {
|
||||
if (error.statusCode === 401) {
|
||||
this.auth = null;
|
||||
|
||||
return sendMessage(sender, channel, message);
|
||||
}
|
||||
|
||||
throw new Error(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rocketchat token
|
||||
* @return {Object} userId and authToken
|
||||
*/
|
||||
async function getAuthToken() {
|
||||
if (!this.auth || this.auth && !this.auth.authToken) {
|
||||
const config = await getConfig();
|
||||
const uri = `${config.api}/login`;
|
||||
let {body} = await send(uri, {
|
||||
user: config.user,
|
||||
password: config.password
|
||||
});
|
||||
|
||||
if (body) {
|
||||
body = JSON.parse(body);
|
||||
this.auth = body.data;
|
||||
}
|
||||
}
|
||||
|
||||
return this.auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rocketchat config
|
||||
* @return {Object} Auth config
|
||||
*/
|
||||
async function getConfig() {
|
||||
if (!this.chatConfig) {
|
||||
const models = Self.app.models;
|
||||
|
||||
this.chatConfig = await models.ChatConfig.findOne();
|
||||
}
|
||||
|
||||
return this.chatConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send unauthenticated request
|
||||
* @param {*} uri - Request uri
|
||||
* @param {*} params - Request params
|
||||
* @param {*} options - Request options
|
||||
*
|
||||
* @return {Object} Request response
|
||||
*/
|
||||
async function send(uri, params, options = {}) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({
|
||||
body: JSON.stringify(
|
||||
{statusCode: 200, message: 'Fake notification sent'}
|
||||
)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
form: params
|
||||
};
|
||||
|
||||
if (options) Object.assign(defaultOptions, options);
|
||||
|
||||
return got.post(uri, defaultOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send authenticated request
|
||||
* @param {*} uri - Request uri
|
||||
* @param {*} body - Request params
|
||||
*
|
||||
* @return {Object} Request response
|
||||
*/
|
||||
async function sendAuth(uri, body) {
|
||||
const login = await getAuthToken();
|
||||
const options = {
|
||||
headers: {}
|
||||
};
|
||||
|
||||
if (login) {
|
||||
options.headers['X-Auth-Token'] = login.authToken;
|
||||
options.headers['X-User-Id'] = login.userId;
|
||||
}
|
||||
|
||||
return send(uri, body, options);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('sendCheckingPresence', {
|
||||
description: 'Sends a RocketChat message to a working worker or department channel',
|
||||
description: 'Creates a message in the chat model checking the user status',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'workerId',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The worker id of the destinatary'
|
||||
description: 'The recipient user id'
|
||||
},
|
||||
{
|
||||
arg: 'message',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The message'
|
||||
}],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
@ -33,30 +33,26 @@ module.exports = Self => {
|
|||
Object.assign(myOptions, options);
|
||||
|
||||
const models = Self.app.models;
|
||||
const account = await models.Account.findById(recipientId, null, myOptions);
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const sender = await models.Account.findById(userId);
|
||||
const recipient = await models.Account.findById(recipientId, null, myOptions);
|
||||
|
||||
// Prevent sending messages to yourself
|
||||
if (recipientId == userId) return false;
|
||||
|
||||
if (!account)
|
||||
if (!recipient)
|
||||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||
|
||||
const query = `SELECT worker_isWorking(?) isWorking`;
|
||||
const [result] = await Self.rawSql(query, [recipientId], myOptions);
|
||||
await models.Chat.create({
|
||||
senderFk: sender.id,
|
||||
recipient: `@${recipient.name}`,
|
||||
dated: new Date(),
|
||||
checkUserStatus: 1,
|
||||
message: message,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
});
|
||||
|
||||
if (!result.isWorking) {
|
||||
const workerDepartment = await models.WorkerDepartment.findById(recipientId, {
|
||||
include: {
|
||||
relation: 'department'
|
||||
}
|
||||
}, myOptions);
|
||||
const department = workerDepartment && workerDepartment.department();
|
||||
const channelName = department && department.chatName;
|
||||
|
||||
if (channelName)
|
||||
return Self.send(ctx, `#${channelName}`, `@${account.name} ➔ ${message}`);
|
||||
}
|
||||
|
||||
return Self.send(ctx, `@${account.name}`, message);
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
const axios = require('axios');
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('sendQueued', {
|
||||
description: 'Send a RocketChat message',
|
||||
accessType: 'WRITE',
|
||||
accepts: [],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/sendQueued`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.sendQueued = async() => {
|
||||
const models = Self.app.models;
|
||||
const maxAttempts = 3;
|
||||
const sentStatus = 1;
|
||||
const errorStatus = 2;
|
||||
|
||||
const chats = await models.Chat.find({
|
||||
where: {
|
||||
status: {neq: sentStatus},
|
||||
attempts: {lt: maxAttempts}
|
||||
}
|
||||
});
|
||||
|
||||
for (let chat of chats) {
|
||||
if (chat.checkUserStatus) {
|
||||
try {
|
||||
await Self.sendCheckingUserStatus(chat);
|
||||
await updateChat(chat, sentStatus);
|
||||
} catch (error) {
|
||||
await updateChat(chat, errorStatus, error);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await Self.sendMessage(chat.senderFk, chat.recipient, chat.message);
|
||||
await updateChat(chat, sentStatus);
|
||||
} catch (error) {
|
||||
await updateChat(chat, errorStatus, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check user status in Rocket
|
||||
*
|
||||
* @param {object} chat - The sender id
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
Self.sendCheckingUserStatus = async function sendCheckingUserStatus(chat) {
|
||||
const models = Self.app.models;
|
||||
|
||||
const recipientName = chat.recipient.slice(1);
|
||||
const recipient = await models.Account.findOne({
|
||||
where: {
|
||||
name: recipientName
|
||||
}
|
||||
});
|
||||
|
||||
const {data} = await Self.getUserStatus(recipient.name);
|
||||
if (data) {
|
||||
if (data.status === 'offline' || data.status === 'busy') {
|
||||
// Send message to department room
|
||||
const workerDepartment = await models.WorkerDepartment.findById(recipient.id, {
|
||||
include: {
|
||||
relation: 'department'
|
||||
}
|
||||
});
|
||||
const department = workerDepartment && workerDepartment.department();
|
||||
const channelName = department && department.chatName;
|
||||
|
||||
if (channelName)
|
||||
return Self.sendMessage(chat.senderFk, `#${channelName}`, `@${recipient.name} ➔ ${chat.message}`);
|
||||
else
|
||||
return Self.sendMessage(chat.senderFk, `@${recipient.name}`, chat.message);
|
||||
} else
|
||||
return Self.sendMessage(chat.senderFk, `@${recipient.name}`, chat.message);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a rocket message
|
||||
*
|
||||
* @param {object} senderFk - The sender id
|
||||
* @param {string} recipient - The user (@) or channel (#) to send the message
|
||||
* @param {string} message - The message to send
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
Self.sendMessage = async function sendMessage(senderFk, recipient, message) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({
|
||||
statusCode: 200,
|
||||
message: 'Fake notification sent'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const models = Self.app.models;
|
||||
const sender = await models.Account.findById(senderFk);
|
||||
|
||||
const login = await Self.getServiceAuth();
|
||||
const avatar = `${login.host}/avatar/${sender.name}`;
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
'X-Auth-Token': login.auth.token,
|
||||
'X-User-Id': login.auth.userId
|
||||
},
|
||||
};
|
||||
|
||||
return axios.post(`${login.api}/chat.postMessage`, {
|
||||
'channel': recipient,
|
||||
'avatar': avatar,
|
||||
'alias': sender.nickname,
|
||||
'text': message
|
||||
}, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update status and attempts of a chat
|
||||
*
|
||||
* @param {object} chat - The chat
|
||||
* @param {string} status - The new status
|
||||
* @param {string} error - The error
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
async function updateChat(chat, status, error) {
|
||||
return chat.updateAttributes({
|
||||
status: status,
|
||||
attempts: ++chat.attempts,
|
||||
error: error
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current user status on Rocketchat
|
||||
*
|
||||
* @param {string} username - The recipient user name
|
||||
* @return {Promise} - The request promise
|
||||
*/
|
||||
Self.getUserStatus = async function getUserStatus(username) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
return new Promise(resolve => {
|
||||
return resolve({
|
||||
data: {
|
||||
status: 'online'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const login = await Self.getServiceAuth();
|
||||
|
||||
const options = {
|
||||
params: {username},
|
||||
headers: {
|
||||
'X-Auth-Token': login.auth.token,
|
||||
'X-User-Id': login.auth.userId
|
||||
},
|
||||
};
|
||||
|
||||
return axios.get(`${login.api}/users.getStatus`, options);
|
||||
};
|
||||
};
|
|
@ -13,10 +13,9 @@ describe('Chat notifyIssue()', () => {
|
|||
spyOn(chatModel, 'send').and.callThrough();
|
||||
spyOn(osTicketModel, 'rawSql').and.returnValue([]);
|
||||
|
||||
const response = await chatModel.notifyIssues(ctx);
|
||||
await chatModel.notifyIssues(ctx);
|
||||
|
||||
expect(chatModel.send).not.toHaveBeenCalled();
|
||||
expect(response).toBeUndefined();
|
||||
});
|
||||
|
||||
it(`should return a response calling the send() method`, async() => {
|
||||
|
@ -27,16 +26,15 @@ describe('Chat notifyIssue()', () => {
|
|||
username: 'batman',
|
||||
subject: 'Issue title'}
|
||||
]);
|
||||
// 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 department = await app.models.Department.findById(departmentId);
|
||||
let orgChatName = department.chatName;
|
||||
await department.updateAttribute('chatName', 'IT');
|
||||
|
||||
const response = await chatModel.notifyIssues(ctx);
|
||||
await chatModel.notifyIssues(ctx);
|
||||
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.message).toEqual('Fake notification sent');
|
||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '#IT', expectedMessage);
|
||||
|
||||
// restores
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('Chat send()', () => {
|
||||
it('should return a "Fake notification sent" as response', async() => {
|
||||
it('should return true as response', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 1}}};
|
||||
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.message).toEqual('Fake notification sent');
|
||||
expect(response).toEqual(true);
|
||||
});
|
||||
|
||||
it('should retrun false as response', async() => {
|
||||
it('should return false as response', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 18}}};
|
||||
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
|
||||
expect(response).toBeFalsy();
|
||||
expect(response).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,46 +1,21 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Chat sendCheckingPresence()', () => {
|
||||
const today = new Date();
|
||||
today.setHours(6, 0);
|
||||
const ctx = {req: {accessToken: {userId: 1}}};
|
||||
const chatModel = app.models.Chat;
|
||||
const departmentId = 23;
|
||||
const workerId = 1107;
|
||||
it('should return true as response', async() => {
|
||||
const workerId = 1107;
|
||||
|
||||
it(`should call send() method with the worker name if he's currently working then return a response`, async() => {
|
||||
spyOn(chatModel, 'send').and.callThrough();
|
||||
let ctx = {req: {accessToken: {userId: 1}}};
|
||||
let response = await models.Chat.sendCheckingPresence(ctx, workerId, 'I changed something');
|
||||
|
||||
const timeEntry = await app.models.WorkerTimeControl.create({
|
||||
userFk: workerId,
|
||||
timed: today,
|
||||
manual: false,
|
||||
direction: 'in'
|
||||
});
|
||||
|
||||
const response = await chatModel.sendCheckingPresence(ctx, workerId, 'I changed something');
|
||||
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.message).toEqual('Fake notification sent');
|
||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', 'I changed something');
|
||||
|
||||
// restores
|
||||
await app.models.WorkerTimeControl.destroyById(timeEntry.id);
|
||||
expect(response).toEqual(true);
|
||||
});
|
||||
|
||||
it(`should call to send() method with the worker department channel if he's not currently working then return a response`, async() => {
|
||||
spyOn(chatModel, 'send').and.callThrough();
|
||||
it('should return false as response', async() => {
|
||||
const salesPersonId = 18;
|
||||
|
||||
const department = await app.models.Department.findById(departmentId);
|
||||
await department.updateAttribute('chatName', 'cooler');
|
||||
let ctx = {req: {accessToken: {userId: 18}}};
|
||||
let response = await models.Chat.sendCheckingPresence(ctx, salesPersonId, 'I changed something');
|
||||
|
||||
const response = await chatModel.sendCheckingPresence(ctx, workerId, 'I changed something');
|
||||
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.message).toEqual('Fake notification sent');
|
||||
expect(chatModel.send).toHaveBeenCalledWith(ctx, '#cooler', '@HankPym ➔ I changed something');
|
||||
|
||||
// restores
|
||||
await department.updateAttribute('chatName', null);
|
||||
expect(response).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('Chat sendCheckingPresence()', () => {
|
||||
const today = new Date();
|
||||
today.setHours(6, 0);
|
||||
const chatModel = models.Chat;
|
||||
|
||||
it(`should call to sendCheckingUserStatus()`, async() => {
|
||||
spyOn(chatModel, 'sendCheckingUserStatus').and.callThrough();
|
||||
|
||||
const chat = {
|
||||
checkUserStatus: 1,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
};
|
||||
|
||||
await chatModel.destroyAll();
|
||||
await chatModel.create(chat);
|
||||
|
||||
await chatModel.sendQueued();
|
||||
|
||||
expect(chatModel.sendCheckingUserStatus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(`should call to sendMessage() method`, async() => {
|
||||
spyOn(chatModel, 'sendMessage').and.callThrough();
|
||||
|
||||
const chat = {
|
||||
checkUserStatus: 0,
|
||||
status: 0,
|
||||
attempts: 0
|
||||
};
|
||||
|
||||
await chatModel.destroyAll();
|
||||
await chatModel.create(chat);
|
||||
|
||||
await chatModel.sendQueued();
|
||||
|
||||
expect(chatModel.sendMessage).toHaveBeenCalled();
|
||||
});
|
||||
});
|
|
@ -1,35 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('collectionFaults', {
|
||||
description: 'Update sale of a collection',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'shelvingFk',
|
||||
type: 'String',
|
||||
required: true,
|
||||
description: 'The shalving id'
|
||||
}, {
|
||||
arg: 'quantity',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The quantity to sale'
|
||||
}, {
|
||||
arg: 'itemFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The ticket id'
|
||||
}],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/collectionFaults`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.collectionFaults = async(shelvingFk, quantity, itemFk) => {
|
||||
query = `CALL vn.collection_faults(?,?,?)`;
|
||||
return await Self.rawSql(query, [shelvingFk, quantity, itemFk]);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('setSaleQuantity', {
|
||||
description: 'Update sale quantity',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'saleId',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The sale id'
|
||||
},
|
||||
{
|
||||
arg: 'quantity',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'The quantity to picked'
|
||||
}],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/setSaleQuantity`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.setSaleQuantity = async(saleId, quantity) => {
|
||||
const models = Self.app.models;
|
||||
|
||||
const sale = await models.Sale.findById(saleId);
|
||||
return await sale.updateAttributes({
|
||||
originalQuantity: sale.quantity,
|
||||
quantity: quantity
|
||||
});
|
||||
};
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
describe('collectionFaults()', () => {
|
||||
it('return shelving afected', async() => {
|
||||
let response = await app.models.Collection.collectionFaults('UXN', 0, 1);
|
||||
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
expect(response[0][0].shelvingFk).toEqual('UXN');
|
||||
});
|
||||
});
|
|
@ -2,10 +2,10 @@ const models = require('vn-loopback/server/server').models;
|
|||
|
||||
describe('ticket getCollection()', () => {
|
||||
it('should return a list of collections', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 1106}}};
|
||||
let ctx = {req: {accessToken: {userId: 1107}}};
|
||||
let response = await models.Collection.getCollection(ctx);
|
||||
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
expect(response[0].collectionFk).toEqual(1);
|
||||
expect(response[0].collectionFk).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
// #3400 analizar que hacer con rutas de back colletion
|
||||
xdescribe('newCollection()', () => {
|
||||
it('return a new collection', async() => {
|
||||
describe('newCollection()', () => {
|
||||
it('should return a new collection', async() => {
|
||||
pending('#3400 analizar que hacer con rutas de back collection');
|
||||
let ctx = {req: {accessToken: {userId: 1106}}};
|
||||
let response = await app.models.Collection.newCollection(ctx, 1, 1, 1);
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('setSaleQuantity()', () => {
|
||||
it('should change quantity sale', async() => {
|
||||
const saleId = 30;
|
||||
const newQuantity = 10;
|
||||
|
||||
const originalSale = await models.Sale.findById(saleId);
|
||||
|
||||
await models.Collection.setSaleQuantity(saleId, newQuantity);
|
||||
const updateSale = await models.Sale.findById(saleId);
|
||||
|
||||
expect(updateSale.originalQuantity).toEqual(originalSale.quantity);
|
||||
expect(updateSale.quantity).toEqual(newQuantity);
|
||||
});
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('updateCollectionSale()', () => {
|
||||
it('should return a new collection', async() => {
|
||||
const sectorOneWarehouseID = 1;
|
||||
let ctx = {req: {accessToken: {userId: 1106}}};
|
||||
ctx.args = {
|
||||
sale: 1,
|
||||
originalQuantity: 5,
|
||||
quantity: 5,
|
||||
quantityPicked: 5,
|
||||
ticketFk: 1,
|
||||
stateFk: 4,
|
||||
isNicho: false,
|
||||
shelvingFk: 'UXN',
|
||||
itemFk: 1,
|
||||
sectorFk: 1
|
||||
};
|
||||
let originalSaleTracking = await app.models.SaleTracking.findOne({
|
||||
where: {
|
||||
saleFk: ctx.args.sale,
|
||||
stateFk: ctx.args.stateFk
|
||||
}
|
||||
});
|
||||
let itemPlacement = await app.models.ItemPlacement.findOne({
|
||||
where: {
|
||||
itemFk: ctx.args.itemFk,
|
||||
warehouseFk: sectorOneWarehouseID
|
||||
}
|
||||
});
|
||||
const originalSale = await app.models.Sale.findById(ctx.args.sale);
|
||||
const originalItemShelving = await app.models.ItemShelving.findOne({where: {shelvingFk: ctx.args.shelvingFk, itemFk: ctx.args.itemFk}});
|
||||
const originalTicketLastState = await app.models.TicketLastState.findById(ctx.args.ticketFk);
|
||||
|
||||
let response = await app.models.Collection.updateCollectionSale(ctx);
|
||||
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
expect(response[0][0].id).toEqual(1);
|
||||
expect(response[0][0].quantity).toEqual(5);
|
||||
|
||||
// restores
|
||||
if (originalSaleTracking)
|
||||
await originalSaleTracking.save();
|
||||
else {
|
||||
originalSaleTracking = await app.models.SaleTracking.findOne({
|
||||
where: {
|
||||
saleFk: ctx.args.sale,
|
||||
stateFk: ctx.args.stateFk
|
||||
}
|
||||
});
|
||||
await originalSaleTracking.destroy();
|
||||
}
|
||||
await originalSale.save();
|
||||
const itemShelvingsToDestroy = await app.models.ItemShelving.find({where: {shelvingFk: ctx.args.shelvingFk, itemFk: ctx.args.itemFk}});
|
||||
for (itemShelving of itemShelvingsToDestroy)
|
||||
await itemShelving.destroy();
|
||||
|
||||
await originalItemShelving.save();
|
||||
await originalTicketLastState.save();
|
||||
await itemPlacement.save();
|
||||
});
|
||||
});
|
|
@ -1,90 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateCollectionSale', {
|
||||
description: 'Update sale of a collection',
|
||||
accessType: 'WRITE',
|
||||
accepts: [{
|
||||
arg: 'sale',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The sale id'
|
||||
}, {
|
||||
arg: 'originalQuantity',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The quantity to sale'
|
||||
},
|
||||
{
|
||||
arg: 'quantity',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The quantity to picked'
|
||||
},
|
||||
{
|
||||
arg: 'quantityPicked',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The quantity to picked'
|
||||
}, {
|
||||
arg: 'ticketFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The ticket id'
|
||||
}, {
|
||||
arg: 'stateFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The state id'
|
||||
}, {
|
||||
arg: 'isNicho',
|
||||
type: 'Boolean',
|
||||
required: true,
|
||||
description: 'Determine if sale is picked from nicho or not'
|
||||
}, {
|
||||
arg: 'shelvingFk',
|
||||
type: 'String',
|
||||
required: false,
|
||||
description: 'The shelving id'
|
||||
}, {
|
||||
arg: 'itemFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The item id'
|
||||
}, {
|
||||
arg: 'sectorFk',
|
||||
type: 'Number',
|
||||
required: true,
|
||||
description: 'The sector id'
|
||||
}],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/updateCollectionSale`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.updateCollectionSale = async ctx => {
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const args = ctx.args;
|
||||
|
||||
if (args.originalQuantity == args.quantity) {
|
||||
query = `CALL vn.collection_updateSale(?,?,?,?,?)`;
|
||||
await Self.rawSql(query, [args.sale, args.originalQuantity, userId, args.stateFk, args.ticketFk]);
|
||||
}
|
||||
|
||||
if (!args.isNicho) {
|
||||
query = `CALL vn.collection_faults(?,?,?)`;
|
||||
await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, args.itemFk]);
|
||||
} else {
|
||||
query = `CALL vn.sector_getWarehouse(?)`;
|
||||
const [result] = await Self.rawSql(query, [args.sectorFk]);
|
||||
|
||||
query = `CALL vn.itemPlacementSave(?,?,?)`;
|
||||
await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, result[0]['warehouseFk']]);
|
||||
}
|
||||
query = `CALL vn.sale_updateOriginalQuantity(?,?)`;
|
||||
return await Self.rawSql(query, [args.sale, args.quantity]);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('deleteTrashFiles', {
|
||||
description: 'Deletes files that have trash type',
|
||||
accessType: 'WRITE',
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/deleteTrashFiles`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.deleteTrashFiles = async options => {
|
||||
const myOptions = {};
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (process.env.NODE_ENV == 'test')
|
||||
throw new UserError(`Action not allowed on the test environment`);
|
||||
|
||||
const models = Self.app.models;
|
||||
const DmsContainer = models.DmsContainer;
|
||||
|
||||
const trashDmsType = await models.DmsType.findOne({
|
||||
where: {code: 'trash'}
|
||||
}, myOptions);
|
||||
|
||||
const date = new Date();
|
||||
date.setMonth(date.getMonth() - 4);
|
||||
|
||||
const dmsToDelete = await models.Dms.find({
|
||||
where: {
|
||||
and: [
|
||||
{dmsTypeFk: trashDmsType.id},
|
||||
{created: {lt: date}}
|
||||
]
|
||||
}
|
||||
}, myOptions);
|
||||
|
||||
for (let dms of dmsToDelete) {
|
||||
const pathHash = DmsContainer.getHash(dms.id);
|
||||
const dmsContainer = await DmsContainer.container(pathHash);
|
||||
try {
|
||||
const dstFile = path.join(dmsContainer.client.root, pathHash, dms.file);
|
||||
await fs.unlink(dstFile);
|
||||
} catch (err) {
|
||||
if (err.code != 'ENOENT')
|
||||
throw err;
|
||||
}
|
||||
|
||||
await dms.destroy(myOptions);
|
||||
|
||||
const dstFolder = path.join(dmsContainer.client.root, pathHash);
|
||||
try {
|
||||
await fs.rmdir(dstFolder);
|
||||
} catch (err) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
|
@ -9,35 +9,35 @@ module.exports = Self => {
|
|||
accepts: [
|
||||
{
|
||||
arg: 'warehouseId',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The warehouse id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'companyId',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The company id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'dmsTypeId',
|
||||
type: 'Number',
|
||||
type: 'number',
|
||||
description: 'The dms type id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'reference',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'description',
|
||||
type: 'String',
|
||||
type: 'string',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'hasFile',
|
||||
type: 'Boolean',
|
||||
type: 'boolean',
|
||||
description: 'True if has an attached file',
|
||||
required: true
|
||||
}],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
const got = require('got');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('checkFile', {
|
||||
description: 'Check if exist docuware file',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
description: 'The id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'fileCabinet',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The fileCabinet name'
|
||||
},
|
||||
{
|
||||
arg: 'dialog',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The dialog name'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'boolean',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/checkFile`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.checkFile = async function(ctx, id, fileCabinet, dialog) {
|
||||
const myUserId = ctx.req.accessToken.userId;
|
||||
if (!myUserId)
|
||||
return false;
|
||||
|
||||
const models = Self.app.models;
|
||||
const docuwareConfig = await models.DocuwareConfig.findOne();
|
||||
const docuwareInfo = await models.Docuware.findOne({
|
||||
where: {
|
||||
code: fileCabinet,
|
||||
dialogName: dialog
|
||||
}
|
||||
});
|
||||
|
||||
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 = {
|
||||
condition: [
|
||||
{
|
||||
DBName: find,
|
||||
Value: [id]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
// get fileCabinetId
|
||||
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 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
|
||||
Object.assign(options, {'body': JSON.stringify(searchFilter)});
|
||||
const response = await got.post(
|
||||
`${docuwareUrl}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`, options);
|
||||
JSON.parse(response.body).Items[0].Id;
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,120 @@
|
|||
/* eslint max-len: ["error", { "code": 180 }]*/
|
||||
const got = require('got');
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('download', {
|
||||
description: 'Download an docuware PDF',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
description: 'The id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'fileCabinet',
|
||||
type: 'string',
|
||||
description: 'The id',
|
||||
http: {source: 'path'}
|
||||
},
|
||||
{
|
||||
arg: 'dialog',
|
||||
type: 'string',
|
||||
description: 'The id',
|
||||
http: {source: 'path'}
|
||||
}
|
||||
],
|
||||
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/download/:fileCabinet/:dialog`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.download = async function(ctx, id, fileCabinet, dialog) {
|
||||
const myUserId = ctx.req.accessToken.userId;
|
||||
if (!myUserId)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
||||
const models = Self.app.models;
|
||||
const docuwareConfig = await models.DocuwareConfig.findOne();
|
||||
const docuwareInfo = await models.Docuware.findOne({
|
||||
where: {
|
||||
code: fileCabinet,
|
||||
dialogName: dialog
|
||||
}
|
||||
});
|
||||
|
||||
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 = {
|
||||
condition: [
|
||||
{
|
||||
DBName: find,
|
||||
Value: [id]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
// get fileCabinetId
|
||||
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 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
|
||||
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;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const got = require('got');
|
||||
|
||||
describe('docuware download()', () => {
|
||||
const ticketId = 1;
|
||||
const userId = 9;
|
||||
const ctx = {
|
||||
req: {
|
||||
|
||||
accessToken: {userId: userId},
|
||||
headers: {origin: 'http://localhost:5000'},
|
||||
}
|
||||
};
|
||||
|
||||
const fileCabinetName = 'deliveryClient';
|
||||
const dialogDisplayName = 'find';
|
||||
const dialogName = 'findTicket';
|
||||
|
||||
const gotGetResponse = {
|
||||
body: JSON.stringify(
|
||||
{
|
||||
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() => {
|
||||
const gotPostResponse = {
|
||||
body: JSON.stringify(
|
||||
{
|
||||
Items: [],
|
||||
})
|
||||
};
|
||||
|
||||
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(false);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const got = require('got');
|
||||
const stream = require('stream');
|
||||
|
||||
describe('docuware download()', () => {
|
||||
const userId = 9;
|
||||
const ticketId = 1;
|
||||
const ctx = {
|
||||
req: {
|
||||
|
||||
accessToken: {userId: userId},
|
||||
headers: {origin: 'http://localhost:5000'},
|
||||
}
|
||||
};
|
||||
|
||||
it('should return the downloaded file name', async() => {
|
||||
const fileCabinetName = 'deliveryClient';
|
||||
const dialogDisplayName = 'find';
|
||||
const dialogName = 'findTicket';
|
||||
const gotGetResponse = {
|
||||
body: JSON.stringify(
|
||||
{
|
||||
FileCabinet: [
|
||||
{Id: 12, Name: fileCabinetName}
|
||||
],
|
||||
Dialog: [
|
||||
{Id: 34, DisplayName: dialogDisplayName}
|
||||
]
|
||||
})
|
||||
};
|
||||
|
||||
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)));
|
||||
spyOn(got, 'stream').and.returnValue(new stream.PassThrough({objectMode: true}));
|
||||
|
||||
const result = await models.Docuware.download(ctx, ticketId, fileCabinetName, dialogName);
|
||||
|
||||
expect(result[1]).toEqual('application/pdf');
|
||||
expect(result[2]).toEqual(`filename="${ticketId}.pdf"`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`bucket`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12)
|
||||
SET
|
||||
bucket_id = @col2,
|
||||
bucket_type_id = @col4,
|
||||
description = @col5,
|
||||
x_size = @col6,
|
||||
y_size = @col7,
|
||||
z_size = @col8,
|
||||
entry_date = STR_TO_DATE(@col10, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col11, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col12, '%Y%m%d%H%i')
|
|
@ -0,0 +1,10 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`bucket_type`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6)
|
||||
SET
|
||||
bucket_type_id = @col2,
|
||||
description = @col3,
|
||||
entry_date = STR_TO_DATE(@col4, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col5, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col6, '%Y%m%d%H%i')
|
|
@ -0,0 +1,11 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`feature`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
item_id = @col2,
|
||||
feature_type_id = @col3,
|
||||
feature_value = @col4,
|
||||
entry_date = STR_TO_DATE(@col5, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col6, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col7, '%Y%m%d%H%i')
|
|
@ -0,0 +1,10 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`genus`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6)
|
||||
SET
|
||||
genus_id = @col2,
|
||||
latin_genus_name = @col3,
|
||||
entry_date = STR_TO_DATE(@col4, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col5, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col6, '%Y%m%d%H%i')
|
|
@ -0,0 +1,13 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`item`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12)
|
||||
SET
|
||||
id = @col2,
|
||||
product_name = @col4,
|
||||
name = @col5,
|
||||
plant_id = @col7,
|
||||
group_id = @col9,
|
||||
entry_date = STR_TO_DATE(@col10, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col11, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col12, '%Y%m%d%H%i')
|
|
@ -0,0 +1,12 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`item_feature`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8)
|
||||
SET
|
||||
item_id = @col2,
|
||||
feature = @col3,
|
||||
regulation_type = @col4,
|
||||
presentation_order = @col5,
|
||||
entry_date = STR_TO_DATE(@col6, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col7, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col8, '%Y%m%d%H%i')
|
|
@ -0,0 +1,10 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`item_group`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6)
|
||||
SET
|
||||
group_code = @col2,
|
||||
dutch_group_description = @col3,
|
||||
entry_date = STR_TO_DATE(@col4, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col5, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col6, '%Y%m%d%H%i')
|
|
@ -0,0 +1,11 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`plant`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9)
|
||||
SET
|
||||
plant_id = @col3,
|
||||
genus_id = @col4,
|
||||
specie_id = @col5,
|
||||
entry_date = STR_TO_DATE(@col7, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col8, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col9, '%Y%m%d%H%i')
|
|
@ -0,0 +1,11 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`specie`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
specie_id = @col2,
|
||||
genus_id = @col3,
|
||||
latin_species_name = @col4,
|
||||
entry_date = STR_TO_DATE(@col5, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col6, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col7, '%Y%m%d%H%i')
|
|
@ -0,0 +1,11 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`supplier`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7, @col8, @col9, @col10, @col11, @col12, @col13, @col14, @col15, @col16, @col17, @col18, @col19, @col20)
|
||||
SET
|
||||
GLNAddressCode = @col2,
|
||||
supplier_id = @col4,
|
||||
company_name = @col3,
|
||||
entry_date = STR_TO_DATE(@col9, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col10, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col11, '%Y%m%d%H%i')
|
|
@ -0,0 +1,11 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`type`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
type_id = @col2,
|
||||
type_group_id = @col3,
|
||||
description = @col4,
|
||||
entry_date = STR_TO_DATE(@col5, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col6, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col7, '%Y%m%d%H%i')
|
|
@ -0,0 +1,11 @@
|
|||
LOAD DATA LOCAL INFILE ?
|
||||
INTO TABLE `edi`.`value`
|
||||
FIELDS TERMINATED BY ';'
|
||||
LINES TERMINATED BY '\n' (@col1, @col2, @col3, @col4, @col5, @col6, @col7)
|
||||
SET
|
||||
type_id = @col2,
|
||||
type_value = @col3,
|
||||
type_description = @col4,
|
||||
entry_date = STR_TO_DATE(@col5, '%Y%m%d'),
|
||||
expiry_date = IFNULL(NULL,STR_TO_DATE(@col6, '%Y%m%d')),
|
||||
change_date_time = STR_TO_DATE(@col7, '%Y%m%d%H%i')
|
|
@ -0,0 +1,238 @@
|
|||
/* eslint no-console: "off" */
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('updateData', {
|
||||
description: 'Updates schema data from external provider',
|
||||
accessType: 'WRITE',
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/updateData`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.updateData = async() => {
|
||||
const models = Self.app.models;
|
||||
|
||||
// Get files checksum
|
||||
const tx = await Self.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
const files = await Self.rawSql('SELECT name, checksum, keyValue FROM edi.fileConfig', null, options);
|
||||
|
||||
const updatableFiles = [];
|
||||
for (const file of files) {
|
||||
const fileChecksum = await getChecksum(file);
|
||||
|
||||
if (file.checksum != fileChecksum) {
|
||||
updatableFiles.push({
|
||||
name: file.name,
|
||||
checksum: fileChecksum
|
||||
});
|
||||
} else
|
||||
console.debug(`File already updated, skipping...`);
|
||||
}
|
||||
|
||||
if (updatableFiles.length === 0)
|
||||
return false;
|
||||
|
||||
// Download files
|
||||
const container = await models.TempContainer.container('edi');
|
||||
const tempPath = path.join(container.client.root, container.name);
|
||||
|
||||
let remoteFile;
|
||||
let tempDir;
|
||||
let tempFile;
|
||||
|
||||
const fileNames = updatableFiles.map(file => file.name);
|
||||
|
||||
const tables = await Self.rawSql(`
|
||||
SELECT fileName, toTable, file
|
||||
FROM edi.tableConfig
|
||||
WHERE file IN (?)`, [fileNames], options);
|
||||
|
||||
for (const table of tables) {
|
||||
const fileName = table.file;
|
||||
|
||||
remoteFile = `codes/${fileName}.ZIP`;
|
||||
tempDir = `${tempPath}/${fileName}`;
|
||||
tempFile = `${tempPath}/${fileName}.zip`;
|
||||
|
||||
try {
|
||||
await fs.readFile(tempFile);
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
console.debug(`Downloading file ${fileName}...`);
|
||||
const downloadOutput = await downloadFile(remoteFile, tempFile);
|
||||
if (downloadOutput.error)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await extractFile(fileName, tempFile, tempDir);
|
||||
|
||||
console.debug(`Updating table ${table.toTable}...`);
|
||||
await dumpData(tempDir, table, options);
|
||||
}
|
||||
|
||||
// Update files checksum
|
||||
for (const file of updatableFiles) {
|
||||
console.log(`Updating file ${file.name} checksum...`);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.fileConfig
|
||||
SET checksum = ?
|
||||
WHERE name = ?`,
|
||||
[file.checksum, file.name], options);
|
||||
}
|
||||
|
||||
await tx.commit();
|
||||
|
||||
// Clean files
|
||||
try {
|
||||
console.debug(`Cleaning files...`);
|
||||
await fs.remove(tempPath);
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT')
|
||||
throw e;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
await tx.rollback();
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
let ftpClient;
|
||||
async function getFtpClient() {
|
||||
if (!ftpClient) {
|
||||
const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig');
|
||||
console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`);
|
||||
|
||||
const FtpClient = require('ftps');
|
||||
|
||||
ftpClient = new FtpClient({
|
||||
host: ftpConfig.host,
|
||||
username: ftpConfig.user,
|
||||
password: ftpConfig.password,
|
||||
procotol: 'ftp'
|
||||
});
|
||||
}
|
||||
|
||||
return ftpClient;
|
||||
}
|
||||
|
||||
async function getChecksum(file) {
|
||||
const ftpClient = await getFtpClient();
|
||||
console.debug(`Checking checksum for file ${file.name}...`);
|
||||
|
||||
ftpClient.cat(`codes/${file.name}.txt`);
|
||||
|
||||
const response = await new Promise((resolve, reject) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (err || response.error) {
|
||||
console.debug(`Error downloading checksum file... ${response.error}`);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
|
||||
if (response && response.data) {
|
||||
const fileContents = response.data;
|
||||
const rows = fileContents.split('\n');
|
||||
const row = rows[4];
|
||||
const columns = row.split(/\s+/);
|
||||
|
||||
let fileChecksum;
|
||||
if (file.keyValue)
|
||||
fileChecksum = columns[1];
|
||||
|
||||
if (!file.keyValue)
|
||||
fileChecksum = columns[0];
|
||||
|
||||
return fileChecksum;
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadFile(remoteFile, tempFile) {
|
||||
const ftpClient = await getFtpClient();
|
||||
|
||||
ftpClient.get(remoteFile, tempFile);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ftpClient.exec((err, response) => {
|
||||
if (err || response.error) {
|
||||
console.debug(`Error downloading file... ${response.error}`);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function extractFile(fileName, tempFile, tempDir) {
|
||||
const JSZip = require('jszip');
|
||||
|
||||
try {
|
||||
await fs.mkdir(tempDir);
|
||||
console.debug(`Extracting file ${fileName}...`);
|
||||
} catch (error) {
|
||||
if (error.code !== 'EEXIST')
|
||||
throw e;
|
||||
}
|
||||
|
||||
const fileStream = await fs.readFile(tempFile);
|
||||
if (fileStream) {
|
||||
const zip = new JSZip();
|
||||
const zipContents = await zip.loadAsync(fileStream);
|
||||
|
||||
if (!zipContents) return;
|
||||
|
||||
const fileNames = Object.keys(zipContents.files);
|
||||
|
||||
for (const fileName of fileNames) {
|
||||
const fileContent = await zip.file(fileName).async('nodebuffer');
|
||||
const dest = path.join(tempDir, fileName);
|
||||
await fs.writeFile(dest, fileContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function dumpData(tempDir, table, options) {
|
||||
const toTable = table.toTable;
|
||||
const baseName = table.fileName;
|
||||
|
||||
console.log(`Emptying table ${toTable}...`);
|
||||
const tableName = `edi.${toTable}`;
|
||||
await Self.rawSql(`DELETE FROM ??`, [tableName]);
|
||||
|
||||
const dirFiles = await fs.readdir(tempDir);
|
||||
const files = dirFiles.filter(file => file.startsWith(baseName));
|
||||
|
||||
for (const file of files) {
|
||||
console.log(`Dumping data from file ${file}...`);
|
||||
|
||||
const templatePath = path.join(__dirname, `./sql/${toTable}.sql`);
|
||||
const sqlTemplate = await fs.readFile(templatePath, 'utf8');
|
||||
const filePath = path.join(tempDir, file);
|
||||
|
||||
await Self.rawSql(sqlTemplate, [filePath], options);
|
||||
await Self.rawSql(`
|
||||
UPDATE edi.tableConfig
|
||||
SET updated = ?
|
||||
WHERE fileName = ?
|
||||
`, [new Date(), baseName], options);
|
||||
}
|
||||
|
||||
console.log(`Updated table ${toTable}\n`);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
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);
|
||||
const cleanDate = new Date();
|
||||
cleanDate.setDate(cleanDate.getDate() - config.cleanDays);
|
||||
|
||||
await models.NotificationQueue.destroyAll({
|
||||
where: {status: {inq: status}},
|
||||
created: {lt: cleanDate}
|
||||
}, myOptions);
|
||||
|
||||
if (tx) await tx.commit();
|
||||
} catch (e) {
|
||||
if (tx) await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
const {Email} = require('vn-print');
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
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', 'email', 'lang']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
}, 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().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);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,140 @@
|
|||
const jsdom = require('jsdom');
|
||||
const mysql = require('mysql');
|
||||
const FormData = require('form-data');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('closeTicket', {
|
||||
description: 'Close tickets without response from the user',
|
||||
accessType: 'READ',
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/closeTicket`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.closeTicket = async ctx => {
|
||||
const models = Self.app.models;
|
||||
const config = await models.OsTicketConfig.findOne();
|
||||
const ostUri = `${config.host}/login.php`;
|
||||
|
||||
if (!config.user || !config.password || !config.userDb || !config.passwordDb)
|
||||
return false;
|
||||
|
||||
const con = mysql.createConnection({
|
||||
host: `${config.hostDb}`,
|
||||
user: `${config.userDb}`,
|
||||
password: `${config.passwordDb}`,
|
||||
port: `${config.portDb}`
|
||||
});
|
||||
|
||||
const sql = `SELECT ot.ticket_id, ot.number
|
||||
FROM osticket.ost_ticket ot
|
||||
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 (
|
||||
SELECT ote.thread_id, MAX(ote.created) created, MAX(ote.updated) updated
|
||||
FROM osticket.ost_thread_entry ote
|
||||
WHERE ote.staff_id != 0 AND ote.type = 'R'
|
||||
GROUP BY ote.thread_id
|
||||
) sub ON sub.thread_id = ot2.id
|
||||
WHERE ot.isanswered = 1
|
||||
AND ots.state = '${config.oldStatus}'
|
||||
AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ${config.day} DAY)`;
|
||||
|
||||
let ticketsId = [];
|
||||
con.connect(err => {
|
||||
if (err) throw err;
|
||||
con.query(sql, (err, results) => {
|
||||
if (err) throw err;
|
||||
for (const result of results)
|
||||
ticketsId.push(result.ticket_id);
|
||||
});
|
||||
});
|
||||
|
||||
await getRequestToken();
|
||||
|
||||
async function getRequestToken() {
|
||||
const response = await fetch(ostUri);
|
||||
|
||||
const result = response.headers.get('set-cookie');
|
||||
const [firtHeader] = result.split(' ');
|
||||
const firtCookie = firtHeader.substring(0, firtHeader.length - 1);
|
||||
const body = await response.text();
|
||||
const dom = new jsdom.JSDOM(body);
|
||||
const token = dom.window.document.querySelector('[name="__CSRFToken__"]').value;
|
||||
|
||||
await login(token, firtCookie);
|
||||
}
|
||||
|
||||
async function login(token, firtCookie) {
|
||||
const data = {
|
||||
__CSRFToken__: token,
|
||||
do: 'scplogin',
|
||||
userid: config.user,
|
||||
passwd: config.password,
|
||||
ajax: 1
|
||||
};
|
||||
const params = {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams(data),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Cookie': firtCookie
|
||||
}
|
||||
};
|
||||
const response = await fetch(ostUri, params);
|
||||
const result = response.headers.get('set-cookie');
|
||||
const [firtHeader] = result.split(' ');
|
||||
const secondCookie = firtHeader.substring(0, firtHeader.length - 1);
|
||||
|
||||
await close(token, secondCookie);
|
||||
}
|
||||
|
||||
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.code;
|
||||
}
|
||||
|
||||
async function close(token, secondCookie) {
|
||||
for (const ticketId of ticketsId) {
|
||||
const lockCode = await getLockCode(token, secondCookie, ticketId);
|
||||
let form = new FormData();
|
||||
form.append('__CSRFToken__', token);
|
||||
form.append('id', ticketId);
|
||||
form.append('a', config.responseType);
|
||||
form.append('lockCode', lockCode);
|
||||
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);
|
||||
|
||||
const ostUri = `${config.host}/tickets.php?id=${ticketId}`;
|
||||
const params = {
|
||||
method: 'POST',
|
||||
body: form,
|
||||
headers: {
|
||||
'Cookie': secondCookie
|
||||
}
|
||||
};
|
||||
return fetch(ostUri, params);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('osTicketReportEmail', {
|
||||
description: 'Sends the buyer waste email',
|
||||
accessType: 'WRITE',
|
||||
accepts: [],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: '/osticket-report-email',
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.osTicketReportEmail = async ctx => {
|
||||
const models = Self.app.models;
|
||||
const printConfig = await models.PrintConfig.findOne();
|
||||
|
||||
const email = new Email('osticket-report', {
|
||||
recipient: printConfig.itRecipient,
|
||||
lang: ctx.req.getLocale()
|
||||
});
|
||||
|
||||
return email.send();
|
||||
};
|
||||
};
|
|
@ -44,6 +44,12 @@
|
|||
"DmsType": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Docuware": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"DocuwareConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"EmailUser": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
@ -62,9 +68,30 @@
|
|||
"Language": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"MachineWorker": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"MobileAppVersionControl": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Module": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Notification": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"NotificationAcl": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"NotificationConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"NotificationQueue": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"NotificationSubscription": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Province": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
@ -89,6 +116,9 @@
|
|||
"Town": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Url": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"UserConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
@ -103,6 +133,15 @@
|
|||
},
|
||||
"OsTicket": {
|
||||
"dataSource": "osticket"
|
||||
},
|
||||
"OsTicketConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Edi": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"PrintConfig": {
|
||||
"dataSource": "vn"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ module.exports = Self => {
|
|||
require('../methods/account/change-password')(Self);
|
||||
require('../methods/account/set-password')(Self);
|
||||
require('../methods/account/validate-token')(Self);
|
||||
require('../methods/account/privileges')(Self);
|
||||
|
||||
// Validations
|
||||
|
||||
|
@ -77,7 +78,7 @@ module.exports = Self => {
|
|||
`SELECT r.name
|
||||
FROM account.user u
|
||||
JOIN account.roleRole rr ON rr.role = u.role
|
||||
JOIN account.role r ON r.id = rr.inheritsFrom
|
||||
JOIN account.role r ON r.id = rr.inheritsFrom
|
||||
WHERE u.id = ?`, [userId], options);
|
||||
|
||||
let roles = [];
|
||||
|
|
|
@ -47,7 +47,10 @@
|
|||
"type": "date"
|
||||
},
|
||||
"image": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"hasGrant": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
@ -71,6 +74,11 @@
|
|||
"type": "hasOne",
|
||||
"model": "Worker",
|
||||
"foreignKey": "userFk"
|
||||
},
|
||||
"userConfig": {
|
||||
"type": "hasOne",
|
||||
"model": "UserConfig",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
|
@ -94,6 +102,13 @@
|
|||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "privileges",
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
},
|
||||
"maxAmount": {
|
||||
"type": "number"
|
||||
},
|
||||
"daysInFuture": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "MobileAppVersionControl",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "vn.mobileAppVersionControl"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"appName": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"isVersionCritical": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"bic": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -8,35 +8,35 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"bank": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"account": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"accountingTypeFk": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true,
|
||||
"mysql": {
|
||||
"columnName": "cash"
|
||||
}
|
||||
},
|
||||
"entityFk": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"isActive": {
|
||||
"type": "Boolean",
|
||||
"type": "boolean",
|
||||
"required": true
|
||||
},
|
||||
"currencyFk": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
|
|
|
@ -10,20 +10,20 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"host": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"api": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/chat/getServiceAuth')(Self);
|
||||
require('../methods/chat/send')(Self);
|
||||
require('../methods/chat/sendCheckingPresence')(Self);
|
||||
require('../methods/chat/notifyIssues')(Self);
|
||||
require('../methods/chat/sendQueued')(Self);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,42 @@
|
|||
{
|
||||
"name": "Chat",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "chat"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"senderFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"recipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"dated": {
|
||||
"type": "date"
|
||||
},
|
||||
"checkUserStatus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"attempts": {
|
||||
"type": "number"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
"property": "validations",
|
||||
"accessType": "EXECUTE",
|
||||
|
|
|
@ -2,6 +2,5 @@ module.exports = Self => {
|
|||
require('../methods/collection/getCollection')(Self);
|
||||
require('../methods/collection/newCollection')(Self);
|
||||
require('../methods/collection/getSectors')(Self);
|
||||
require('../methods/collection/updateCollectionSale')(Self);
|
||||
require('../methods/collection/collectionFaults')(Self);
|
||||
require('../methods/collection/setSaleQuantity')(Self);
|
||||
};
|
||||
|
|
|
@ -10,17 +10,14 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"code": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"expired": {
|
||||
"type": "date"
|
||||
},
|
||||
"isOfficial": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
|
@ -21,7 +21,7 @@
|
|||
"type": "string"
|
||||
},
|
||||
"isUeeMember": {
|
||||
"type": "Boolean"
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -9,17 +9,17 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"forceId": false
|
||||
},
|
||||
"date": {
|
||||
"type": "Date"
|
||||
"type": "date"
|
||||
},
|
||||
"m3":{
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"warehouseFk":{
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@ module.exports = Self => {
|
|||
require('../methods/dms/uploadFile')(Self);
|
||||
require('../methods/dms/removeFile')(Self);
|
||||
require('../methods/dms/updateFile')(Self);
|
||||
require('../methods/dms/deleteTrashFiles')(Self);
|
||||
|
||||
Self.checkRole = async function(ctx, id) {
|
||||
const models = Self.app.models;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
|
@ -36,7 +36,7 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"created": {
|
||||
"type": "Date"
|
||||
"type": "date"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "DocuwareConfig",
|
||||
"description": "Docuware config",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "docuwareConfig"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"property": "*",
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/docuware/download')(Self);
|
||||
require('../methods/docuware/checkFile')(Self);
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "Docuware",
|
||||
"description": "Docuware sections",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "docuware"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"fileCabinetName": {
|
||||
"type": "string"
|
||||
},
|
||||
"dialogName": {
|
||||
"type": "string"
|
||||
},
|
||||
"find": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"property": "*",
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/edi/updateData')(Self);
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Edi",
|
||||
"base": "VnModel"
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
"properties": {
|
||||
"userFk": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"email": {
|
||||
|
|
|
@ -8,20 +8,20 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"width": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"height": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"crop": {
|
||||
"type": "Boolean",
|
||||
"type": "boolean",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
|
|
|
@ -8,32 +8,32 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"name": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"desc": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"maxWidth": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"maxHeight": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"model": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"property": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "MachineWorker",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "vn.machineWorker"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true
|
||||
},
|
||||
"workerFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"machineFk": {
|
||||
"type": "number"
|
||||
},
|
||||
"inTime": {
|
||||
"type": "date",
|
||||
"mysql": {
|
||||
"columnName": "inTimed"
|
||||
}
|
||||
},
|
||||
"outTime": {
|
||||
"type": "date",
|
||||
"mysql": {
|
||||
"columnName": "outTimed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"name": "OsTicketConfig",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "osTicketConfig"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier"
|
||||
},
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"oldStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"newStatusId": {
|
||||
"type": "number"
|
||||
},
|
||||
"day": {
|
||||
"type": "number"
|
||||
},
|
||||
"comment": {
|
||||
"type": "string"
|
||||
},
|
||||
"hostDb": {
|
||||
"type": "string"
|
||||
},
|
||||
"userDb": {
|
||||
"type": "string"
|
||||
},
|
||||
"passwordDb": {
|
||||
"type": "string"
|
||||
},
|
||||
"portDb": {
|
||||
"type": "number"
|
||||
},
|
||||
"responseType": {
|
||||
"type": "string"
|
||||
},
|
||||
"fromEmailId": {
|
||||
"type": "number"
|
||||
},
|
||||
"replyTo": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = Self => {
|
||||
require('../methods/osticket/osTicketReportEmail')(Self);
|
||||
require('../methods/osticket/closeTicket')(Self);
|
||||
};
|
|
@ -1,12 +1,5 @@
|
|||
{
|
||||
"name": "OsTicket",
|
||||
"base": "VnModel",
|
||||
"acls": [{
|
||||
"property": "validations",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}]
|
||||
"base": "VnModel"
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
"properties": {
|
||||
"code": {
|
||||
"id": true,
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "PrintConfig",
|
||||
"description": "Print config",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "salix.printConfig"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"itRecipient": {
|
||||
"type": "string"
|
||||
},
|
||||
"incidencesEmail": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"acls": [{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}]
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"id": true,
|
||||
"description": "Identifier",
|
||||
"mysql": {
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"name": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,18 +9,18 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"userFk": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"tableCode": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"configuration": {
|
||||
"type": "Object"
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -9,20 +9,23 @@
|
|||
"properties": {
|
||||
"userFk": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"warehouseFk": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"companyFk": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"created": {
|
||||
"type": "Date"
|
||||
"type": "date"
|
||||
},
|
||||
"updated": {
|
||||
"type": "Date"
|
||||
"type": "date"
|
||||
},
|
||||
"darkMode": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -9,40 +9,40 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"forceId": false
|
||||
},
|
||||
"originFk": {
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"userFk": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"action": {
|
||||
"type": "String",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"changedModel": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"oldInstance": {
|
||||
"type": "Object"
|
||||
"type": "object"
|
||||
},
|
||||
"newInstance": {
|
||||
"type": "Object"
|
||||
"type": "object"
|
||||
},
|
||||
"creationDate": {
|
||||
"type": "Date"
|
||||
"type": "date"
|
||||
},
|
||||
"changedModelId": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"changedModelValue": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"forceId": false
|
||||
},
|
||||
"username":{
|
||||
|
|
|
@ -10,23 +10,20 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"forceId": false
|
||||
},
|
||||
"name": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"isInventory": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"isManaged":{
|
||||
"type": "boolean"
|
||||
},
|
||||
"hasStowaway":{
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"verbose": true,
|
||||
"watch": [
|
||||
"back/**/*.js",
|
||||
"modules/**/*.js"
|
||||
],
|
||||
"ignore": [
|
||||
"modules/account/front/**/*",
|
||||
"modules/claim/front/**/*",
|
||||
"modules/client/front/**/*",
|
||||
"modules/entry/front/**/*",
|
||||
"modules/invoiceIn/front/**/*",
|
||||
"modules/invoiceOut/front/**/*",
|
||||
"modules/item/front/**/*",
|
||||
"modules/monitor/front/**/*",
|
||||
"modules/order/front/**/*",
|
||||
"modules/route/front/**/*",
|
||||
"modules/supplier/front/**/*",
|
||||
"modules/ticket/front/**/*",
|
||||
"modules/travel/front/**/*",
|
||||
"modules/shelving/front/**/*",
|
||||
"modules/worker/front/**/*",
|
||||
"modules/zone/front/**/*"
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue