Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5811-ticket.expedition_deleteViaexpress
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
3f8d1ff617
|
@ -10,5 +10,9 @@
|
||||||
"eslint.format.enable": true,
|
"eslint.format.enable": true,
|
||||||
"[javascript]": {
|
"[javascript]": {
|
||||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||||
}
|
},
|
||||||
|
"cSpell.words": [
|
||||||
|
"salix",
|
||||||
|
"fdescribe"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
32
CHANGELOG.md
32
CHANGELOG.md
|
@ -5,27 +5,34 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [2348.01] - 2023-11-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- (Ticket -> Adelantar) Permite mover lineas sin generar negativos
|
||||||
|
- (Ticket -> Adelantar) Permite modificar la fecha de los tickets
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
### Fixed
|
||||||
|
- (Ticket -> RocketChat) Arreglada detección de cambios
|
||||||
|
|
||||||
|
|
||||||
## [2346.01] - 2023-11-16
|
## [2346.01] - 2023-11-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
### Changed
|
### Changed
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|
||||||
## [2342.01] - 2023-11-02
|
## [2342.01] - 2023-11-02
|
||||||
|
|
||||||
### Added
|
|
||||||
### Changed
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
## [2340.01] - 2023-10-05
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- (Usuarios -> Foto) Se muestra la foto del trabajador
|
- (Usuarios -> Foto) Se muestra la foto del trabajador
|
||||||
|
|
||||||
### Changed
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- (Usuarios -> Historial) Abre el descriptor del usuario correctamente
|
- (Usuarios -> Historial) Abre el descriptor del usuario correctamente
|
||||||
|
|
||||||
|
|
||||||
|
## [2340.01] - 2023-10-05
|
||||||
|
|
||||||
## [2338.01] - 2023-09-21
|
## [2338.01] - 2023-09-21
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -35,17 +42,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Changed
|
### Changed
|
||||||
- (Trabajadores -> Calendario) Icono de check arreglado cuando pulsas un tipo de dia
|
- (Trabajadores -> Calendario) Icono de check arreglado cuando pulsas un tipo de dia
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
## [2336.01] - 2023-09-07
|
## [2336.01] - 2023-09-07
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
|
|
||||||
## [2334.01] - 2023-08-24
|
## [2334.01] - 2023-08-24
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
10
Dockerfile
10
Dockerfile
|
@ -1,4 +1,4 @@
|
||||||
FROM debian:bullseye-slim
|
FROM debian:bookworm-slim
|
||||||
ENV TZ Europe/Madrid
|
ENV TZ Europe/Madrid
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
@ -25,7 +25,13 @@ RUN apt-get update \
|
||||||
libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
|
libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \
|
||||||
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \
|
libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \
|
||||||
libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
|
libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
|
||||||
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
|
fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
|
||||||
|
|
||||||
|
# Extra dependencies
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
samba-common-bin samba-dsdb-modules\
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& npm -g install pm2
|
&& npm -g install pm2
|
||||||
|
|
||||||
|
|
40
README.md
40
README.md
|
@ -8,7 +8,7 @@ Salix is also the scientific name of a beautifull tree! :)
|
||||||
|
|
||||||
Required applications.
|
Required applications.
|
||||||
|
|
||||||
* Node.js >= 16.x LTS
|
* Node.js
|
||||||
* Docker
|
* Docker
|
||||||
* Git
|
* Git
|
||||||
|
|
||||||
|
@ -17,20 +17,7 @@ You will need to install globally the following items.
|
||||||
$ sudo npm install -g jest gulp-cli
|
$ sudo npm install -g jest gulp-cli
|
||||||
```
|
```
|
||||||
|
|
||||||
For the usage of jest --watch on macOs.
|
## Installing dependencies and launching
|
||||||
```
|
|
||||||
$ brew install watchman
|
|
||||||
```
|
|
||||||
* [watchman](https://facebook.github.io/watchman/)
|
|
||||||
|
|
||||||
## Linux Only Prerequisites
|
|
||||||
|
|
||||||
Your user must be on the docker group to use it so you will need to run this command:
|
|
||||||
```
|
|
||||||
$ sudo usermod -a -G docker yourusername
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting Started // Installing
|
|
||||||
|
|
||||||
Pull from repository.
|
Pull from repository.
|
||||||
|
|
||||||
|
@ -76,29 +63,6 @@ In Visual Studio Code we use the ESLint extension.
|
||||||
ext install dbaeumer.vscode-eslint
|
ext install dbaeumer.vscode-eslint
|
||||||
```
|
```
|
||||||
|
|
||||||
Gitlens for visualization of code authorship
|
|
||||||
```
|
|
||||||
ext install eamodio.gitlens
|
|
||||||
```
|
|
||||||
|
|
||||||
Spanish language pack
|
|
||||||
```
|
|
||||||
ext install ms-ceintl.vscode-language-pack-es
|
|
||||||
```
|
|
||||||
|
|
||||||
### Recommended extensions
|
|
||||||
|
|
||||||
Material icon Theme
|
|
||||||
```
|
|
||||||
ext install pkief.material-icon-theme
|
|
||||||
```
|
|
||||||
|
|
||||||
Material UI Themes
|
|
||||||
```
|
|
||||||
ext install equinusocio.vsc-material-theme
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Built With
|
## Built With
|
||||||
|
|
||||||
* [angularjs](https://angularjs.org/)
|
* [angularjs](https://angularjs.org/)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getList', {
|
||||||
|
description: 'Get list of the available and active notification subscriptions',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
description: 'User to modify',
|
||||||
|
http: {source: 'path'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:id/getList`,
|
||||||
|
verb: 'GET'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.getList = async(id, options) => {
|
||||||
|
const activeNotificationsMap = new Map();
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const availableNotificationsMap = await Self.getAvailable(id, myOptions);
|
||||||
|
const activeNotifications = await Self.app.models.NotificationSubscription.find({
|
||||||
|
fields: ['id', 'notificationFk'],
|
||||||
|
include: {relation: 'notification'},
|
||||||
|
where: {userFk: id}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
for (active of activeNotifications) {
|
||||||
|
activeNotificationsMap.set(active.notificationFk, {
|
||||||
|
id: active.id,
|
||||||
|
notificationFk: active.notificationFk,
|
||||||
|
name: active.notification().name,
|
||||||
|
description: active.notification().description,
|
||||||
|
active: true
|
||||||
|
});
|
||||||
|
availableNotificationsMap.delete(active.notificationFk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
active: [...activeNotificationsMap.entries()],
|
||||||
|
available: [...availableNotificationsMap.entries()]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,13 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('NotificationSubscription getList()', () => {
|
||||||
|
it('should return a list of available and active notifications of a user', async() => {
|
||||||
|
const userId = 9;
|
||||||
|
const {active, available} = await models.NotificationSubscription.getList(userId);
|
||||||
|
const notifications = await models.Notification.find({});
|
||||||
|
const totalAvailable = notifications.length - active.length;
|
||||||
|
|
||||||
|
expect(active.length).toEqual(2);
|
||||||
|
expect(available.length).toEqual(totalAvailable);
|
||||||
|
});
|
||||||
|
});
|
|
@ -49,8 +49,13 @@ module.exports = Self => {
|
||||||
if (vnUser.twoFactor)
|
if (vnUser.twoFactor)
|
||||||
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
||||||
}
|
}
|
||||||
|
const validateLogin = await Self.validateLogin(user, password);
|
||||||
return Self.validateLogin(user, password);
|
await Self.app.models.SignInLog.create({
|
||||||
|
token: validateLogin.token,
|
||||||
|
userFk: vnUser.id,
|
||||||
|
ip: ctx.req.ip
|
||||||
|
});
|
||||||
|
return validateLogin;
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.passExpired = async vnUser => {
|
Self.passExpired = async vnUser => {
|
||||||
|
|
|
@ -12,8 +12,21 @@ describe('VnUser Sign-in()', () => {
|
||||||
},
|
},
|
||||||
args: {}
|
args: {}
|
||||||
};
|
};
|
||||||
const {VnUser, AccessToken} = models;
|
const {VnUser, AccessToken, SignInLog} = models;
|
||||||
describe('when credentials are correct', () => {
|
describe('when credentials are correct', () => {
|
||||||
|
it('should return the token if user uses email', async() => {
|
||||||
|
let login = await VnUser.signIn(unauthCtx, 'salesAssistant@mydomain.com', 'nightmare');
|
||||||
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
let signInLog = await SignInLog.find({where: {token: accessToken.id}});
|
||||||
|
|
||||||
|
expect(signInLog.length).toEqual(1);
|
||||||
|
expect(signInLog[0].userFk).toEqual(accessToken.userId);
|
||||||
|
expect(login.token).toBeDefined();
|
||||||
|
|
||||||
|
await VnUser.logout(ctx.req.accessToken.id);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return the token', async() => {
|
it('should return the token', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'salesAssistant', 'nightmare');
|
let login = await VnUser.signIn(unauthCtx, 'salesAssistant', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
|
|
|
@ -1,62 +1,74 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
require('../methods/notification/getList')(Self);
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
|
await checkModifyPermission(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.observe('before delete', async function(ctx) {
|
||||||
|
await checkModifyPermission(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function checkModifyPermission(ctx) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
const instance = ctx.instance;
|
||||||
const userId = ctx.options.accessToken.userId;
|
const userId = ctx.options.accessToken.userId;
|
||||||
const user = await ctx.instance.userFk;
|
|
||||||
const modifiedUser = await getUserToModify(null, user, models);
|
|
||||||
|
|
||||||
if (userId != modifiedUser.id && userId != modifiedUser.bossFk)
|
let notificationFk;
|
||||||
throw new UserError('You dont have permission to modify this user');
|
let workerId;
|
||||||
});
|
|
||||||
|
|
||||||
Self.remoteMethod('deleteNotification', {
|
if (instance) {
|
||||||
description: 'Deletes a notification subscription',
|
notificationFk = instance.notificationFk;
|
||||||
accepts: [
|
workerId = instance.userFk;
|
||||||
{
|
} else {
|
||||||
arg: 'ctx',
|
const notificationSubscription = await models.NotificationSubscription.findById(ctx.where.id);
|
||||||
type: 'object',
|
notificationFk = notificationSubscription.notificationFk;
|
||||||
http: {source: 'context'}
|
workerId = notificationSubscription.userFk;
|
||||||
},
|
|
||||||
{
|
|
||||||
arg: 'notificationId',
|
|
||||||
type: 'number',
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
],
|
|
||||||
returns: {
|
|
||||||
type: 'object',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
|
||||||
verb: 'POST',
|
|
||||||
path: '/deleteNotification'
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
Self.deleteNotification = async function(ctx, notificationId) {
|
const worker = await models.Worker.findById(workerId, {fields: ['id', 'bossFk']});
|
||||||
const models = Self.app.models;
|
const available = await Self.getAvailable(workerId);
|
||||||
const user = ctx.req.accessToken.userId;
|
const hasAcl = available.has(notificationFk);
|
||||||
const modifiedUser = await getUserToModify(notificationId, null, models);
|
|
||||||
|
|
||||||
if (user != modifiedUser.id && user != modifiedUser.bossFk)
|
if (!hasAcl || (userId != worker.id && userId != worker.bossFk))
|
||||||
throw new UserError('You dont have permission to modify this user');
|
throw new UserError('The notification subscription of this worker cant be modified');
|
||||||
|
|
||||||
await models.NotificationSubscription.destroyById(notificationId);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getUserToModify(notificationId, userFk, models) {
|
|
||||||
let userToModify = userFk;
|
|
||||||
if (notificationId) {
|
|
||||||
const subscription = await models.NotificationSubscription.findById(notificationId);
|
|
||||||
userToModify = subscription.userFk;
|
|
||||||
}
|
|
||||||
return await models.Worker.findOne({
|
|
||||||
fields: ['id', 'bossFk'],
|
|
||||||
where: {
|
|
||||||
id: userToModify
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self.getAvailable = async function(userId, options) {
|
||||||
|
const availableNotificationsMap = new Map();
|
||||||
|
const models = Self.app.models;
|
||||||
|
|
||||||
|
const myOptions = {};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const roles = await models.RoleMapping.find({
|
||||||
|
fields: ['roleId'],
|
||||||
|
where: {principalId: userId}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const availableNotifications = await models.NotificationAcl.find({
|
||||||
|
fields: ['notificationFk', 'roleFk'],
|
||||||
|
include: {relation: 'notification'},
|
||||||
|
where: {
|
||||||
|
roleFk: {
|
||||||
|
inq: roles.map(role => role.roleId),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
for (available of availableNotifications) {
|
||||||
|
availableNotificationsMap.set(available.notificationFk, {
|
||||||
|
id: null,
|
||||||
|
notificationFk: available.notificationFk,
|
||||||
|
name: available.notification().name,
|
||||||
|
description: available.notification().description,
|
||||||
|
active: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return availableNotificationsMap;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,74 +1,126 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('loopback model NotificationSubscription', () => {
|
describe('loopback model NotificationSubscription', () => {
|
||||||
it('Should fail to delete a notification if the user is not editing itself or a subordinate', async() => {
|
it('should fail to add a notification subscription if the worker doesnt have ACLs', async() => {
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const user = 9;
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 62}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to add a notification subscription if the user isnt editing itself or subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 1}};
|
||||||
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 9}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail to delete a notification subscription if the user isnt editing itself or subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const notificationSubscriptionId = 2;
|
const notificationSubscriptionId = 2;
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.message).toContain('You dont have permission to modify this user');
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual('The notification subscription of this worker cant be modified');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should delete a notification if the user is editing itself', async() => {
|
it('should add a notification subscription if the user is editing itself', async() => {
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
const user = 9;
|
await models.NotificationSubscription.create({notificationFk: 2, userFk: 9}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a notification subscription if the user is editing itself', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
|
const notificationSubscriptionId = 6;
|
||||||
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a notification subscription if the user is editing a subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 9}};
|
||||||
|
await models.NotificationSubscription.create({notificationFk: 1, userFk: 5}, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a notification subscription if the user is editing a subordinate', async() => {
|
||||||
|
const tx = await models.NotificationSubscription.beginTransaction({});
|
||||||
|
let error;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx, accessToken: {userId: 19}};
|
||||||
const notificationSubscriptionId = 4;
|
const notificationSubscriptionId = 4;
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
|
|
||||||
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
expect(deletedNotification).toBeNull();
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
throw e;
|
error = e;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
it('Should delete a notification if the user is editing a subordinate', async() => {
|
expect(error).toBeUndefined();
|
||||||
const tx = await models.NotificationSubscription.beginTransaction({});
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
const user = 9;
|
|
||||||
const notificationSubscriptionId = 5;
|
|
||||||
const ctx = {req: {accessToken: {userId: user}}};
|
|
||||||
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
|
|
||||||
|
|
||||||
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
|
|
||||||
|
|
||||||
expect(deletedNotification).toBeNull();
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ const vnModel = require('vn-loopback/common/models/vn-model');
|
||||||
const {Email} = require('vn-print');
|
const {Email} = require('vn-print');
|
||||||
const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
vnModel(Self);
|
vnModel(Self);
|
||||||
|
@ -92,7 +93,11 @@ module.exports = function(Self) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.on('resetPasswordRequest', async function(info) {
|
Self.on('resetPasswordRequest', async function(info) {
|
||||||
const url = await Self.app.models.Url.getUrl();
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
|
const httpCtx = {req: loopBackContext.active};
|
||||||
|
const httpRequest = httpCtx.req.http.req;
|
||||||
|
const headers = httpRequest.headers;
|
||||||
|
const origin = headers.origin;
|
||||||
|
|
||||||
const defaultHash = '/reset-password?access_token=$token$';
|
const defaultHash = '/reset-password?access_token=$token$';
|
||||||
const recoverHashes = {
|
const recoverHashes = {
|
||||||
|
@ -108,7 +113,7 @@ module.exports = function(Self) {
|
||||||
const params = {
|
const params = {
|
||||||
recipient: info.email,
|
recipient: info.email,
|
||||||
lang: user.lang,
|
lang: user.lang,
|
||||||
url: url.slice(0, -1) + recoverHash
|
url: origin + '/#!' + recoverHash
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = Object.assign({}, info.options);
|
const options = Object.assign({}, info.options);
|
||||||
|
@ -119,12 +124,21 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
return email.send();
|
return email.send();
|
||||||
});
|
});
|
||||||
|
Self.signInValidate = (user, userToken) => {
|
||||||
|
const [[key, value]] = Object.entries(Self.userUses(user));
|
||||||
|
if (userToken[key].toLowerCase().trim() !== value.toLowerCase().trim()) {
|
||||||
|
console.error('ERROR!!! - Signin with other user', userToken, user);
|
||||||
|
throw new UserError('Try again');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Self.validateLogin = async function(user, password) {
|
Self.validateLogin = async function(user, password) {
|
||||||
let loginInfo = Object.assign({password}, Self.userUses(user));
|
const loginInfo = Object.assign({password}, Self.userUses(user));
|
||||||
token = await Self.login(loginInfo, 'user');
|
const token = await Self.login(loginInfo, 'user');
|
||||||
|
|
||||||
const userToken = await token.user.get();
|
const userToken = await token.user.get();
|
||||||
|
Self.signInValidate(user, userToken);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Self.app.models.Account.sync(userToken.name, password);
|
await Self.app.models.Account.sync(userToken.name, password);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -220,8 +234,11 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
class Mailer {
|
class Mailer {
|
||||||
async send(verifyOptions, cb) {
|
async send(verifyOptions, cb) {
|
||||||
|
const url = new URL(verifyOptions.verifyHref);
|
||||||
|
if (process.env.NODE_ENV) url.port = '';
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
url: verifyOptions.verifyHref,
|
url: url.href,
|
||||||
recipient: verifyOptions.to
|
recipient: verifyOptions.to
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ BEGIN
|
||||||
clientFk,
|
clientFk,
|
||||||
dued,
|
dued,
|
||||||
companyFk,
|
companyFk,
|
||||||
cplusInvoiceType477Fk
|
siiTypeInvoiceOutFk
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -96,7 +96,7 @@ BEGIN
|
||||||
clientFk,
|
clientFk,
|
||||||
dued,
|
dued,
|
||||||
companyFk,
|
companyFk,
|
||||||
cplusInvoiceType477Fk
|
siiTypeInvoiceOutFk
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -96,7 +96,7 @@ BEGIN
|
||||||
clientFk,
|
clientFk,
|
||||||
dued,
|
dued,
|
||||||
companyFk,
|
companyFk,
|
||||||
cplusInvoiceType477Fk
|
siiTypeInvoiceOutFk
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
UPDATE `vn`.`workerTimeControlConfig`
|
||||||
|
SET `timeToBreakTime` = 18000;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`workerTimeControlConfig`
|
||||||
|
DROP COLUMN IF EXISTS `maxTimeToBreak`;
|
||||||
|
ALTER TABLE `vn`.`workerTimeControlConfig`
|
||||||
|
ADD COLUMN maxTimeToBreak INT DEFAULT 3600 NULL;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`workerTimeControlConfig`
|
||||||
|
DROP COLUMN IF EXISTS `maxWorkShortCycle`;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`workerTimeControlConfig`
|
||||||
|
ADD COLUMN `maxWorkShortCycle` INT(10) UNSIGNED DEFAULT 561600
|
||||||
|
COMMENT 'Máximo tiempo que un trabajador puede estar trabajando con el que adquirirá el derecho a un descanso semanal corto';
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`workerTimeControlConfig`
|
||||||
|
DROP COLUMN IF EXISTS `maxWorkLongCycle`;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`workerTimeControlConfig`
|
||||||
|
ADD COLUMN `maxWorkLongCycle` INT(10) UNSIGNED DEFAULT 950400
|
||||||
|
COMMENT 'Máximo tiempo que un trabajador puede estar trabajando con el que adquirirá el derecho a un descanso semanal largo';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `vn`.`workerTimeControlError` (
|
||||||
|
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`code` char(35) NOT NULL,
|
||||||
|
`description` varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `code` (`code`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||||
|
|
||||||
|
INSERT IGNORE INTO `vn`.`workerTimeControlError` (`code`, `description`)
|
||||||
|
VALUES
|
||||||
|
('IS_NOT_ALLOWED_FUTURE', 'No se permite fichar a futuro'),
|
||||||
|
('INACTIVE_BUSINESS', 'No hay un contrato en vigor'),
|
||||||
|
('IS_NOT_ALLOWED_WORK', 'No está permitido trabajar'),
|
||||||
|
('ODD_WORKERTIMECONTROL', 'Fichadas impares'),
|
||||||
|
('DAY_MAX_TIME', 'Superado el tiempo máximo entre entrada y salida'),
|
||||||
|
('BREAK_DAY', 'Descanso diario'),
|
||||||
|
('BREAK_WEEK', 'Descanso semanal'),
|
||||||
|
('WRONG_DIRECTION', 'Dirección incorrecta'),
|
||||||
|
('UNDEFINED_ERROR', 'Error sin definir');
|
|
@ -0,0 +1,194 @@
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`timeControl_calculate`(
|
||||||
|
vDatedFrom DATETIME,
|
||||||
|
vDatedTo DATETIME)
|
||||||
|
BEGIN
|
||||||
|
/*
|
||||||
|
* Agrupa por trabajador y día, el tiempo de trabajo y descanso retribuido(si tiene).
|
||||||
|
* Los registros horarios incorrectos (tmp.timeControlError) no se considerarán.
|
||||||
|
* Si un trabajador ha trabajado más de un cierto umbral de tiempo (vTimeToBreakTime)
|
||||||
|
* y no ha tenido descansos que superen un parámetro determinado(vMaxTimeToBreak),
|
||||||
|
* se le añadirá un tiempo de descanso (vBreakTime) a sus horas trabajadas.
|
||||||
|
* El tiempo de descanso solo se añade si el trabajador realmente disfrutó del descanso.
|
||||||
|
* Si disfrutó de menos tiempo de descanso, solo se añade el tiempo que disfrutó.
|
||||||
|
*
|
||||||
|
* @param vDatedFrom
|
||||||
|
* @param vDatedTo
|
||||||
|
*
|
||||||
|
* @return tmp.timeControlCalculate
|
||||||
|
* (workerFk, dated, timeWorkSeconds, timeWorkSexagesimal, timeWorkDecimal, timed)
|
||||||
|
*/
|
||||||
|
DECLARE vHourSeconds INTEGER;
|
||||||
|
DECLARE vDatedFromYesterday DATETIME;
|
||||||
|
DECLARE vDatedToTomorrow DATETIME;
|
||||||
|
DECLARE vTimeToBreakTime INT;
|
||||||
|
DECLARE vBreakTime INT;
|
||||||
|
DECLARE vMaxTimeToBreak INT;
|
||||||
|
|
||||||
|
SELECT DATE_SUB(vDatedFrom, INTERVAL 1 DAY), DATE_ADD(vDatedTo, INTERVAL 1 DAY)
|
||||||
|
INTO vDatedFromYesterday, vDatedToTomorrow;
|
||||||
|
|
||||||
|
SELECT timeToBreakTime, breakTime, maxTimeToBreak, TIME_TO_SEC('01:00:00')
|
||||||
|
INTO vTimeToBreakTime, vBreakTime, vMaxTimeToBreak, vHourSeconds
|
||||||
|
FROM workerTimeControlConfig
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
CALL timeControl_getError(vDatedFromYesterday, vDatedToTomorrow);
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.workerTimeControl
|
||||||
|
(INDEX(userFk, timed), INDEX(timed), INDEX(direction))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT wtc.userFk,
|
||||||
|
wtc.timed,
|
||||||
|
DATE(wtc.timed) dated,
|
||||||
|
wtc.direction,
|
||||||
|
TRUE isReal
|
||||||
|
FROM workerTimeControl wtc
|
||||||
|
JOIN tmp.`user` u ON u.userFk = wtc.userFk
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT wtc.userFk, MIN(wtc.timed) firstIn
|
||||||
|
FROM workerTimeControl wtc
|
||||||
|
JOIN tmp.`user` u ON u.userFk = wtc.userFk
|
||||||
|
LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
|
||||||
|
WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
|
||||||
|
AND wtc.direction = 'in'
|
||||||
|
AND tce.id IS NULL
|
||||||
|
GROUP BY userFk
|
||||||
|
) fi ON wtc.userFk = fi.userFk
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT wtc.userFk, MAX(wtc.timed) lastOut
|
||||||
|
FROM workerTimeControl wtc
|
||||||
|
JOIN tmp.`user` u ON u.userFk = wtc.userFk
|
||||||
|
LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
|
||||||
|
WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
|
||||||
|
AND wtc.direction = 'out'
|
||||||
|
AND tce.id IS NULL
|
||||||
|
GROUP BY userFk
|
||||||
|
) lo ON wtc.userFk = lo.userFk
|
||||||
|
LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
|
||||||
|
WHERE wtc.timed BETWEEN fi.firstIn AND lo.lastOut
|
||||||
|
AND tce.id IS NULL
|
||||||
|
ORDER BY wtc.userFk, wtc.timed;
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.wtcToinsert
|
||||||
|
(INDEX(timed))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
WITH wtc AS(
|
||||||
|
SELECT timed,
|
||||||
|
userFk,
|
||||||
|
dated,
|
||||||
|
direction,
|
||||||
|
LEAD(dated) OVER
|
||||||
|
(PARTITION BY userFk, dated ORDER BY timed) nextDay,
|
||||||
|
LEAD(userFk) OVER
|
||||||
|
(PARTITION BY userFk ORDER BY timed) nextUserFk,
|
||||||
|
ROW_NUMBER() OVER (ORDER BY userFk, timed) MOD 2 isOdd
|
||||||
|
FROM tmp.workerTimeControl
|
||||||
|
WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
|
||||||
|
ORDER BY userFk, timed
|
||||||
|
), wtcToinsert AS(
|
||||||
|
SELECT userFk,
|
||||||
|
dated,
|
||||||
|
IF(userFk = nextUserFk
|
||||||
|
AND nextDay IS NULL
|
||||||
|
AND isOdd
|
||||||
|
AND direction <> 'out', TRUE, FALSE) outNextDay,
|
||||||
|
IF(userFk = nextUserFk
|
||||||
|
AND nextDay IS NULL
|
||||||
|
AND NOT isOdd
|
||||||
|
AND direction <> 'out', TRUE, FALSE) outNextDayWhitBreak
|
||||||
|
FROM wtc
|
||||||
|
HAVING outNextDay OR outNextDayWhitBreak
|
||||||
|
)SELECT userFk, util.dayEnd(dated) timed, 'out' direction
|
||||||
|
FROM wtcToinsert
|
||||||
|
WHERE outNextDay
|
||||||
|
UNION ALL
|
||||||
|
SELECT userFk, dated + INTERVAL 1 DAY, 'in'
|
||||||
|
FROM wtcToinsert
|
||||||
|
WHERE outNextDay
|
||||||
|
UNION ALL
|
||||||
|
SELECT userFk, util.dayEnd(dated) - INTERVAL 1 SECOND, 'middle'
|
||||||
|
FROM wtcToinsert
|
||||||
|
WHERE outNextDayWhitBreak
|
||||||
|
UNION ALL
|
||||||
|
SELECT userFk, util.dayEnd(dated), 'out'
|
||||||
|
FROM wtcToinsert
|
||||||
|
WHERE outNextDayWhitBreak
|
||||||
|
UNION ALL
|
||||||
|
SELECT userFk, dated + INTERVAL 1 DAY, 'in'
|
||||||
|
FROM wtcToinsert
|
||||||
|
WHERE outNextDayWhitBreak
|
||||||
|
UNION ALL
|
||||||
|
SELECT userFk, dated + INTERVAL 1 DAY + INTERVAL 1 SECOND, 'middle'
|
||||||
|
FROM wtcToinsert
|
||||||
|
WHERE outNextDayWhitBreak;
|
||||||
|
|
||||||
|
INSERT INTO tmp.workerTimeControl (userFk, timed, dated, direction, isReal)
|
||||||
|
SELECT userFk, timed, DATE(timed), direction, FALSE
|
||||||
|
FROM tmp.wtcToinsert;
|
||||||
|
|
||||||
|
SET @accumulatedForBreakTime = 0;
|
||||||
|
SET @oldrealDay = NULL;
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.timeControlCalculate
|
||||||
|
WITH workerTimed AS (
|
||||||
|
SELECT
|
||||||
|
userFk,
|
||||||
|
dated,
|
||||||
|
timed,
|
||||||
|
(direction ='in' AND isReal) breakPoint,
|
||||||
|
SUM(CASE WHEN (direction ='in' AND isReal) THEN TRUE ELSE FALSE END)
|
||||||
|
OVER (ORDER BY userFk, timed) AS realDay,
|
||||||
|
TIMESTAMPDIFF(SECOND, LAG(timed)
|
||||||
|
OVER (PARTITION BY userFk, dated ORDER BY timed), timed) gapTime,
|
||||||
|
ROW_NUMBER()
|
||||||
|
OVER (PARTITION BY userFk, dated ORDER BY timed) MOD 2 isOdd
|
||||||
|
FROM tmp.workerTimeControl
|
||||||
|
WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
|
||||||
|
), accumulated AS (
|
||||||
|
SELECT SUM(IF(isOdd, 0, gapTime))
|
||||||
|
OVER (PARTITION BY userFk,dated ORDER BY userFk,timed) accumulatedWorkTime,
|
||||||
|
SUM(IF(NOT isOdd OR breakPoint, 0, IFNULL(gapTime, 0)))
|
||||||
|
OVER (PARTITION BY realDay ORDER BY realDay,timed) accumulatedBreakTime,
|
||||||
|
IF(realDay <> @oldrealDay OR (isOdd AND gapTime >= vMaxTimeToBreak),
|
||||||
|
@accumulatedForBreakTime := 0,
|
||||||
|
@accumulatedForBreakTime := @accumulatedForBreakTime +
|
||||||
|
IF(isOdd, 0, gapTime )) accumulatedForBreakTime,
|
||||||
|
@oldrealDay := realDay,
|
||||||
|
userFk,
|
||||||
|
dated,
|
||||||
|
realDay
|
||||||
|
FROM workerTimed
|
||||||
|
), totalWorked AS (
|
||||||
|
SELECT userFk,
|
||||||
|
dated,
|
||||||
|
MAX(accumulatedWorkTime) +
|
||||||
|
IF(MAX(accumulatedForBreakTime) >= vTimeToBreakTime,
|
||||||
|
LEAST(vBreakTime, MAX(accumulatedBreakTime)),
|
||||||
|
0) timeWorkSeconds
|
||||||
|
FROM accumulated
|
||||||
|
GROUP BY userFk, dated
|
||||||
|
)SELECT tw.userFk,
|
||||||
|
tw.dated,
|
||||||
|
timeWorkSeconds,
|
||||||
|
SEC_TO_TIME(timeWorkSeconds) timeWorkSexagesimal,
|
||||||
|
timeWorkSeconds / vHourSeconds timeWorkDecimal,
|
||||||
|
sub.tableTimed
|
||||||
|
FROM totalWorked tw
|
||||||
|
JOIN (
|
||||||
|
SELECT userFk,
|
||||||
|
dated,
|
||||||
|
GROUP_CONCAT(DATE_FORMAT(timed, "%H:%i") ORDER BY timed ASC
|
||||||
|
SEPARATOR ' - ')tableTimed
|
||||||
|
FROM tmp.workerTimeControl
|
||||||
|
WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
|
||||||
|
AND isReal
|
||||||
|
GROUP BY userFk, dated
|
||||||
|
)sub ON sub.dated = tw.dated
|
||||||
|
AND sub.userFk = tw.userFk
|
||||||
|
WHERE tw.dated BETWEEN vDatedFrom AND vDatedTo;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tmp.timeControlError;
|
||||||
|
DROP TEMPORARY TABLE tmp.wtcToinsert;
|
||||||
|
DROP TEMPORARY TABLE tmp.workerTimeControl;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,286 @@
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`workerTimeControl_clockIn`(
|
||||||
|
vWorkerFk INT,
|
||||||
|
vTimed DATETIME,
|
||||||
|
vDirection VARCHAR(10)
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Verifica si el empleado puede fichar
|
||||||
|
* @param vWorkerFk Identificador del trabajador
|
||||||
|
* @param vTimed valor de la fichada, IF vTimed IS NULL vTimed = NOW
|
||||||
|
* @param vDirection solo se pueden pasa los valores del campo
|
||||||
|
* workerTimeControl.direction ENUM('in', 'out', 'middle')
|
||||||
|
* @return Si todo es correcto, retorna el número de id la tabla workerTimeControl.
|
||||||
|
* Si hay algún problema, devuelve el mesaje que se debe mostrar al usuario
|
||||||
|
* Solo retorna el primer problema, en caso de no ocurrir ningún error se añadirá
|
||||||
|
* fichada a la tabla vn.workerTimeControl
|
||||||
|
*/
|
||||||
|
|
||||||
|
DECLARE vLastIn DATETIME;
|
||||||
|
DECLARE vLastOut DATETIME;
|
||||||
|
DECLARE vNextIn DATETIME;
|
||||||
|
DECLARE vNextOut DATETIME;
|
||||||
|
DECLARE vNextDirection ENUM('in', 'out');
|
||||||
|
DECLARE vLastDirection ENUM('in', 'out');
|
||||||
|
DECLARE vDayMaxTime INTEGER;
|
||||||
|
DECLARE vDayBreak INT;
|
||||||
|
DECLARE vShortWeekBreak INT;
|
||||||
|
DECLARE vLongWeekBreak INT;
|
||||||
|
DECLARE vWeekScope INT;
|
||||||
|
DECLARE vMailTo VARCHAR(50) DEFAULT NULL;
|
||||||
|
DECLARE vUserName VARCHAR(50) DEFAULT NULL;
|
||||||
|
DECLARE vIsError BOOLEAN DEFAULT FALSE;
|
||||||
|
DECLARE vErrorMessage VARCHAR(255) DEFAULT NULL;
|
||||||
|
DECLARE vErrorCode VARCHAR(50);
|
||||||
|
DECLARE vDated DATE;
|
||||||
|
DECLARE vIsAllowedToWork VARCHAR(50);
|
||||||
|
DECLARE vIsManual BOOLEAN DEFAULT TRUE;
|
||||||
|
DECLARE vMaxWorkShortCycle INT;
|
||||||
|
DECLARE vMaxWorkLongCycle INT;
|
||||||
|
|
||||||
|
DECLARE EXIT HANDLER FOR SQLSTATE '45000'
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
SELECT CONCAT(u.name, '@verdnatura.es'),
|
||||||
|
CONCAT(w.firstName, ' ', w.lastName)
|
||||||
|
INTO vMailTo, vUserName
|
||||||
|
FROM account.user u
|
||||||
|
JOIN worker w ON w.bossFk = u.id
|
||||||
|
WHERE w.id = vWorkerFk;
|
||||||
|
|
||||||
|
SELECT `description` INTO vErrorMessage
|
||||||
|
FROM workerTimeControlError
|
||||||
|
WHERE `code` = vErrorCode;
|
||||||
|
|
||||||
|
IF vErrorMessage IS NULL THEN
|
||||||
|
SET vErrorMessage = 'Error sin definir';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT vErrorMessage `error`;
|
||||||
|
SELECT CONCAT(vUserName,
|
||||||
|
' no ha podido fichar por el siguiente problema: ',
|
||||||
|
vErrorMessage)
|
||||||
|
INTO vErrorMessage;
|
||||||
|
|
||||||
|
CALL mail_insert( vMailTo, vMailTo, 'Error al fichar', vErrorMessage);
|
||||||
|
END;
|
||||||
|
|
||||||
|
IF (vTimed IS NULL) THEN
|
||||||
|
SET vTimed = util.VN_NOW();
|
||||||
|
SET vIsManual = FALSE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SET vDated = DATE(vTimed);
|
||||||
|
|
||||||
|
SELECT IF(pc.name = 'Conductor +3500kg',
|
||||||
|
wc.dayBreakDriver,
|
||||||
|
wc.dayBreak),
|
||||||
|
wc.shortWeekBreak,
|
||||||
|
wc.longWeekBreak,
|
||||||
|
wc.weekScope,
|
||||||
|
wc.dayMaxTime,
|
||||||
|
wc.maxWorkShortCycle,
|
||||||
|
wc.maxWorkLongCycle
|
||||||
|
INTO vDayBreak,
|
||||||
|
vShortWeekBreak,
|
||||||
|
vLongWeekBreak,
|
||||||
|
vWeekScope,
|
||||||
|
vDayMaxTime,
|
||||||
|
vMaxWorkShortCycle,
|
||||||
|
vMaxWorkLongCycle
|
||||||
|
FROM business b
|
||||||
|
JOIN professionalCategory pc
|
||||||
|
ON pc.id = b.workerBusinessProfessionalCategoryFk
|
||||||
|
JOIN workerTimeControlConfig wc
|
||||||
|
WHERE b.workerFk = vWorkerFk
|
||||||
|
AND vDated BETWEEN b.started AND IFNULL(b.ended, vDated);
|
||||||
|
|
||||||
|
-- CONTRATO EN VIGOR
|
||||||
|
IF vDayBreak IS NULL THEN
|
||||||
|
SET vErrorCode = 'INACTIVE_BUSINESS';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- FICHADAS A FUTURO
|
||||||
|
IF vTimed > util.VN_NOW() + INTERVAL 1 MINUTE THEN
|
||||||
|
SET vErrorCode = 'IS_NOT_ALLOWED_FUTURE';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- VERIFICAR SI ESTÁ PERMITIDO TRABAJAR
|
||||||
|
CALL timeBusiness_calculateByWorker(vWorkerFk, vDated, vDated);
|
||||||
|
SELECT isAllowedToWork INTO vIsAllowedToWork
|
||||||
|
FROM tmp.timeBusinessCalculate;
|
||||||
|
DROP TEMPORARY TABLE tmp.timeBusinessCalculate;
|
||||||
|
|
||||||
|
IF NOT vIsAllowedToWork THEN
|
||||||
|
SET vErrorCode = 'IS_NOT_ALLOWED_WORK';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- DIRECCION CORRECTA
|
||||||
|
CALL workerTimeControl_direction(vWorkerFk, vTimed);
|
||||||
|
IF (SELECT
|
||||||
|
IF(IF(option1 IN ('inMiddle', 'outMiddle'),
|
||||||
|
'middle',
|
||||||
|
option1) <> vDirection
|
||||||
|
AND IF(option2 IN ('inMiddle', 'outMiddle'),
|
||||||
|
'middle',
|
||||||
|
IFNULL(option2, '')) <> vDirection,
|
||||||
|
TRUE ,
|
||||||
|
FALSE)
|
||||||
|
FROM tmp.workerTimeControlDirection
|
||||||
|
) THEN
|
||||||
|
SET vIsError = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
|
||||||
|
IF vIsError THEN
|
||||||
|
SET vErrorCode = 'WRONG_DIRECTION';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- FICHADAS IMPARES
|
||||||
|
SELECT timed INTO vLastIn
|
||||||
|
FROM workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND direction = 'in'
|
||||||
|
AND timed < vTimed
|
||||||
|
ORDER BY timed DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF (SELECT IF(vDirection = 'in',
|
||||||
|
MOD(COUNT(*), 2) ,
|
||||||
|
IF (vDirection = 'out', NOT MOD(COUNT(*), 2), FALSE))
|
||||||
|
FROM workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND timed BETWEEN vLastIn AND vTimed
|
||||||
|
) THEN
|
||||||
|
SET vErrorCode = 'ODD_WORKERTIMECONTROL';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- DESCANSO DIARIO
|
||||||
|
SELECT timed INTO vLastOut
|
||||||
|
FROM workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND direction = 'out'
|
||||||
|
AND timed < vTimed
|
||||||
|
ORDER BY timed DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
SELECT timed INTO vNextIn
|
||||||
|
FROM workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND direction = 'in'
|
||||||
|
AND timed > vTimed
|
||||||
|
ORDER BY timed ASC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
CASE vDirection
|
||||||
|
WHEN 'in' THEN
|
||||||
|
IF UNIX_TIMESTAMP(vTimed) - UNIX_TIMESTAMP(vLastOut) <= vDayBreak THEN
|
||||||
|
SET vIsError = TRUE;
|
||||||
|
END IF;
|
||||||
|
WHEN 'out' THEN
|
||||||
|
IF UNIX_TIMESTAMP(vNextIn) - UNIX_TIMESTAMP(vTimed) <= vDayBreak THEN
|
||||||
|
SET vIsError = TRUE;
|
||||||
|
END IF;
|
||||||
|
ELSE BEGIN END;
|
||||||
|
END CASE;
|
||||||
|
|
||||||
|
IF vIsError THEN
|
||||||
|
SET vErrorCode = 'BREAK_DAY';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
IF (vDirection IN('in', 'out')) THEN
|
||||||
|
-- VERIFICA MAXIMO TIEMPO DESDE ENTRADA HASTA LA SALIDA
|
||||||
|
|
||||||
|
SELECT timed INTO vNextOut
|
||||||
|
FROM workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND direction = 'out'
|
||||||
|
AND timed > vTimed
|
||||||
|
ORDER BY timed ASC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
SELECT direction INTO vNextDirection
|
||||||
|
FROM workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND direction IN('in','out')
|
||||||
|
AND timed > vTimed
|
||||||
|
ORDER BY timed ASC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
SELECT direction INTO vLastDirection
|
||||||
|
FROM workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND direction IN('in', 'out')
|
||||||
|
AND timed < vTimed
|
||||||
|
ORDER BY timed ASC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF (vDirection ='in'
|
||||||
|
AND vNextDirection = 'out'
|
||||||
|
AND UNIX_TIMESTAMP(vNextOut) - UNIX_TIMESTAMP(vTimed) > vDayMaxTime) OR
|
||||||
|
(vDirection ='out'
|
||||||
|
AND vLastDirection = 'in'
|
||||||
|
AND UNIX_TIMESTAMP(vTimed) -UNIX_TIMESTAMP(vLastIn) > vDayMaxTime) THEN
|
||||||
|
SET vErrorCode = 'DAY_MAX_TIME';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- VERIFICA DESCANSO SEMANAL
|
||||||
|
|
||||||
|
WITH wtc AS(
|
||||||
|
(SELECT timed
|
||||||
|
FROM vn.workerTimeControl
|
||||||
|
WHERE userFk = vWorkerFk
|
||||||
|
AND direction IN ('in', 'out')
|
||||||
|
AND timed BETWEEN vTimed - INTERVAL (vWeekScope * 2) SECOND
|
||||||
|
AND vTimed + INTERVAL (vWeekScope * 2) SECOND )
|
||||||
|
UNION
|
||||||
|
(SELECT vTimed)
|
||||||
|
), wtcGap AS(
|
||||||
|
SELECT timed,
|
||||||
|
TIMESTAMPDIFF(SECOND, LAG(timed) OVER (ORDER BY timed), timed) gap
|
||||||
|
FROM wtc
|
||||||
|
ORDER BY timed
|
||||||
|
), wtcBreak AS(
|
||||||
|
SELECT timed,
|
||||||
|
IF(IFNULL(gap, 0) > vShortWeekBreak, TRUE, FALSE) hasShortBreak,
|
||||||
|
IF(IFNULL(gap, 0) > vLongWeekBreak, TRUE, FALSE) hasLongBreak
|
||||||
|
FROM wtcGap
|
||||||
|
ORDER BY timed
|
||||||
|
), wtcBreakCounter AS(
|
||||||
|
SELECT timed,
|
||||||
|
SUM(hasShortBreak) OVER (ORDER BY timed) breakCounter ,
|
||||||
|
LEAD(hasLongBreak) OVER (ORDER BY timed) nextHasLongBreak
|
||||||
|
FROM wtcBreak
|
||||||
|
)SELECT TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed)) > vMaxWorkLongCycle OR
|
||||||
|
(TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed))> vMaxWorkShortCycle
|
||||||
|
AND NOT SUM(IFNULL(nextHasLongBreak, 1)))
|
||||||
|
hasError INTO vIsError
|
||||||
|
FROM wtcBreakCounter
|
||||||
|
GROUP BY breakCounter
|
||||||
|
HAVING hasError
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF vIsError THEN
|
||||||
|
SET vErrorCode = 'BREAK_WEEK';
|
||||||
|
CALL util.throw(vErrorCode);
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- SE PERMITE FICHAR
|
||||||
|
INSERT INTO workerTimeControl(userFk, timed, direction, `manual`)
|
||||||
|
VALUES(vWorkerFk, vTimed, vDirection, vIsManual);
|
||||||
|
|
||||||
|
SELECT LAST_INSERT_ID() id;
|
||||||
|
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -1,3 +1,43 @@
|
||||||
|
CREATE SCHEMA IF NOT EXISTS `vn2008`;
|
||||||
|
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
||||||
|
SQL SECURITY DEFINER
|
||||||
|
VIEW `vn`.`awbVolume`
|
||||||
|
AS SELECT `d`.`awbFk` AS `awbFk`,
|
||||||
|
`b`.`stickers` * `i`.`density` * IF(
|
||||||
|
`p`.`volume` > 0,
|
||||||
|
`p`.`volume`,
|
||||||
|
`p`.`width` * `p`.`depth` * IF(`p`.`height` = 0, `i`.`size` + 10, `p`.`height`)
|
||||||
|
) / (`vc`.`aerealVolumetricDensity` * 1000) AS `volume`,
|
||||||
|
`b`.`id` AS `buyFk`
|
||||||
|
FROM (
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
`vn`.`buy` `b`
|
||||||
|
JOIN `vn`.`item` `i` ON(`b`.`itemFk` = `i`.`id`)
|
||||||
|
)
|
||||||
|
JOIN `vn`.`itemType` `it` ON(`i`.`typeFk` = `it`.`id`)
|
||||||
|
)
|
||||||
|
JOIN `vn`.`packaging` `p` ON(`p`.`id` = `b`.`packagingFk`)
|
||||||
|
)
|
||||||
|
JOIN `vn`.`entry` `e` ON(`b`.`entryFk` = `e`.`id`)
|
||||||
|
)
|
||||||
|
JOIN `vn`.`travel` `t` ON(`t`.`id` = `e`.`travelFk`)
|
||||||
|
)
|
||||||
|
JOIN `vn`.`duaEntry` `de` ON(`de`.`entryFk` = `e`.`id`)
|
||||||
|
)
|
||||||
|
JOIN `vn`.`dua` `d` ON(`d`.`id` = `de`.`duaFk`)
|
||||||
|
)
|
||||||
|
JOIN `vn`.`volumeConfig` `vc`
|
||||||
|
)
|
||||||
|
WHERE `t`.`shipped` > makedate(year(`util`.`VN_CURDATE`()) - 1, 1);
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
||||||
SQL SECURITY DEFINER
|
SQL SECURITY DEFINER
|
||||||
VIEW `vn2008`.`Compres`
|
VIEW `vn2008`.`Compres`
|
||||||
|
@ -108,39 +148,3 @@ FROM (
|
||||||
LEFT JOIN `edi`.`supplier` `s` ON(`e`.`pro` = `s`.`supplier_id`)
|
LEFT JOIN `edi`.`supplier` `s` ON(`e`.`pro` = `s`.`supplier_id`)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE OR REPLACE DEFINER=`root`@`localhost`
|
|
||||||
SQL SECURITY DEFINER
|
|
||||||
VIEW `vn`.`awbVolume`
|
|
||||||
AS SELECT `d`.`awbFk` AS `awbFk`,
|
|
||||||
`b`.`stickers` * `i`.`density` * IF(
|
|
||||||
`p`.`volume` > 0,
|
|
||||||
`p`.`volume`,
|
|
||||||
`p`.`width` * `p`.`depth` * IF(`p`.`height` = 0, `i`.`size` + 10, `p`.`height`)
|
|
||||||
) / (`vc`.`aerealVolumetricDensity` * 1000) AS `volume`,
|
|
||||||
`b`.`id` AS `buyFk`
|
|
||||||
FROM (
|
|
||||||
(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
(
|
|
||||||
`vn`.`buy` `b`
|
|
||||||
JOIN `vn`.`item` `i` ON(`b`.`itemFk` = `i`.`id`)
|
|
||||||
)
|
|
||||||
JOIN `vn`.`itemType` `it` ON(`i`.`typeFk` = `it`.`id`)
|
|
||||||
)
|
|
||||||
JOIN `vn`.`packaging` `p` ON(`p`.`id` = `b`.`packagingFk`)
|
|
||||||
)
|
|
||||||
JOIN `vn`.`entry` `e` ON(`b`.`entryFk` = `e`.`id`)
|
|
||||||
)
|
|
||||||
JOIN `vn`.`travel` `t` ON(`t`.`id` = `e`.`travelFk`)
|
|
||||||
)
|
|
||||||
JOIN `vn`.`duaEntry` `de` ON(`de`.`entryFk` = `e`.`id`)
|
|
||||||
)
|
|
||||||
JOIN `vn`.`dua` `d` ON(`d`.`id` = `de`.`duaFk`)
|
|
||||||
)
|
|
||||||
JOIN `vn`.`volumeConfig` `vc`
|
|
||||||
)
|
|
||||||
WHERE `t`.`shipped` > makedate(year(`util`.`VN_CURDATE`()) - 1, 1);
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
UPDATE `salix`.`ACL`
|
||||||
|
SET `property` = 'state',
|
||||||
|
`model` = 'Ticket'
|
||||||
|
WHERE `property` = 'changeState';
|
|
@ -0,0 +1,98 @@
|
||||||
|
|
||||||
|
-- Place your SQL code here
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`productionConfig` ADD shortageAddressFk int(11) COMMENT 'Consignatario por defecto para añadir un item de alta';
|
||||||
|
ALTER TABLE `vn`.`productionConfig` ADD CONSTRAINT productionConfig_FK FOREIGN KEY (shortageAddressFk) REFERENCES vn.address(id) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE `vn`.`sale` MODIFY COLUMN originalQuantity double(9,1) DEFAULT NULL NULL COMMENT 'Se utiliza para notificar a través de rocket los cambios de quantity';
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`ACL` ( model, property, accessType, permission, principalType, principalId) VALUES( 'AddressShortage', '*', 'READ', 'ALLOW', 'ROLE', 'production');
|
||||||
|
|
||||||
|
-- vn.addressShortage definition
|
||||||
|
|
||||||
|
CREATE TABLE `vn`.`addressShortage` (
|
||||||
|
`addressFk` int(11) NOT NULL,
|
||||||
|
PRIMARY KEY (`addressFk`),
|
||||||
|
CONSTRAINT `addressShortage_FK` FOREIGN KEY (`addressFk`) REFERENCES `address` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||||
|
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_setVisibleDiscard`(
|
||||||
|
vItemFk INT,
|
||||||
|
vWarehouseFk INT,
|
||||||
|
vQuantity INT,
|
||||||
|
vAddressFk INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Procedimiento para dar dar de baja/alta un item, si vAddressFk es NULL se entiende que se da de alta y se toma el addressFk de la configuración
|
||||||
|
*
|
||||||
|
* @param vItemFk Identificador del ítem
|
||||||
|
* @param vWarehouseFk id del warehouse
|
||||||
|
* @param vQuantity a dar de alta/baja
|
||||||
|
* @param vAddressFk id address
|
||||||
|
*/
|
||||||
|
DECLARE vTicketFk INT;
|
||||||
|
DECLARE vClientFk INT;
|
||||||
|
DECLARE vDefaultCompanyFk INT;
|
||||||
|
DECLARE vCalc INT;
|
||||||
|
DECLARE vAddressShortage INT;
|
||||||
|
|
||||||
|
SELECT barcodeToItem(vItemFk) INTO vItemFk;
|
||||||
|
|
||||||
|
SELECT DEFAULT(companyFk) INTO vDefaultCompanyFk
|
||||||
|
FROM vn.ticket LIMIT 1;
|
||||||
|
|
||||||
|
IF vAddressFk IS NULL THEN
|
||||||
|
SELECT pc.shortageAddressFk INTO vAddressShortage
|
||||||
|
FROM productionConfig pc ;
|
||||||
|
ELSE
|
||||||
|
SET vAddressShortage = vAddressFk;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT a.clientFk INTO vClientFk
|
||||||
|
FROM address a
|
||||||
|
WHERE a.id = vAddressFk;
|
||||||
|
|
||||||
|
SELECT t.id INTO vTicketFk
|
||||||
|
FROM ticket t
|
||||||
|
JOIN address a ON a.id = t.addressFk
|
||||||
|
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
|
WHERE t.warehouseFk = vWarehouseFk
|
||||||
|
AND a.id = vAddressShortage
|
||||||
|
AND DATE(t.shipped) = util.VN_CURDATE()
|
||||||
|
AND ts.code = 'DELIVERED'
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
CALL cache.visible_refresh(vCalc, TRUE, vWarehouseFk);
|
||||||
|
|
||||||
|
IF vTicketFk IS NULL THEN
|
||||||
|
CALL ticket_add(
|
||||||
|
vClientFk,
|
||||||
|
util.VN_CURDATE(),
|
||||||
|
vWarehouseFk,
|
||||||
|
vDefaultCompanyFk,
|
||||||
|
vAddressFk,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
util.VN_CURDATE(),
|
||||||
|
account.myUser_getId(),
|
||||||
|
FALSE,
|
||||||
|
vTicketFk);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
INSERT INTO sale(ticketFk, itemFk, concept, quantity)
|
||||||
|
SELECT vTicketFk,
|
||||||
|
vItemFk,
|
||||||
|
CONCAT(longName,' ', worker_getCode(), ' ', LEFT(CAST(util.VN_NOW() AS TIME),5)),
|
||||||
|
vQuantity
|
||||||
|
FROM item
|
||||||
|
WHERE id = vItemFk;
|
||||||
|
|
||||||
|
UPDATE cache.visible
|
||||||
|
SET visible = visible - vQuantity
|
||||||
|
WHERE calc_id = vCalc
|
||||||
|
AND item_id = vItemFk;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -21,11 +21,11 @@ DELETE FROM `salix`.`ACL`
|
||||||
'getSummary'
|
'getSummary'
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
|
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
|
||||||
VALUES ('Claim','filter','READ','ALLOW','ROLE','claimViewer');
|
VALUES ('Claim','filter','READ','ALLOW','ROLE','claimViewer');
|
||||||
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
|
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
|
||||||
VALUES ('Claim','find','READ','ALLOW','ROLE','claimViewer');
|
VALUES ('Claim','find','READ','ALLOW','ROLE','claimViewer');
|
||||||
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
|
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
|
||||||
VALUES ('Claim','findById','READ','ALLOW','ROLE','claimViewer');
|
VALUES ('Claim','findById','READ','ALLOW','ROLE','claimViewer');
|
||||||
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
|
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
|
||||||
VALUES ('Claim','getSummary','READ','ALLOW','ROLE','claimViewer');
|
VALUES ('Claim','getSummary','READ','ALLOW','ROLE','claimViewer');
|
|
@ -0,0 +1,95 @@
|
||||||
|
ALTER TABLE `vn`.`client` MODIFY COLUMN `credit` decimal(10,2) unsigned DEFAULT 0.00 NOT NULL;
|
||||||
|
|
||||||
|
DELETE FROM `salix`.`ACL` WHERE `model` = 'Client' AND `property` = 'create';
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`client_beforeUpdate`
|
||||||
|
BEFORE UPDATE ON `client`
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
DECLARE vText VARCHAR(255) DEFAULT NULL;
|
||||||
|
DECLARE vPayMethodFk INT;
|
||||||
|
|
||||||
|
SET NEW.editorFk = account.myUser_getId();
|
||||||
|
|
||||||
|
IF NOT(NEW.credit <=> OLD.credit) THEN
|
||||||
|
INSERT INTO clientCredit
|
||||||
|
SET clientFk = NEW.id,
|
||||||
|
amount = NEW.credit,
|
||||||
|
workerFk = NEW.editorFk;
|
||||||
|
END IF;
|
||||||
|
-- Comprueba que el formato de los teléfonos es válido
|
||||||
|
|
||||||
|
IF !(NEW.phone <=> OLD.phone) AND (NEW.phone <> '') THEN
|
||||||
|
CALL pbx.phone_isValid(NEW.phone);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF !(NEW.mobile <=> OLD.mobile) AND (NEW.mobile <> '')THEN
|
||||||
|
CALL pbx.phone_isValid(NEW.mobile);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT id INTO vPayMethodFk
|
||||||
|
FROM vn.payMethod
|
||||||
|
WHERE code = 'bankDraft';
|
||||||
|
|
||||||
|
IF NEW.payMethodFk = vPayMethodFk AND NEW.dueDay = 0 THEN
|
||||||
|
SET NEW.dueDay = 5;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Avisar al comercial si ha llegado la documentación sepa/core
|
||||||
|
|
||||||
|
IF NEW.hasSepaVnl AND !OLD.hasSepaVnl THEN
|
||||||
|
SET vText = 'Sepa de VNL';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NEW.hasCoreVnl AND !OLD.hasCoreVnl THEN
|
||||||
|
SET vText = 'Core de VNL';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF vText IS NOT NULL
|
||||||
|
THEN
|
||||||
|
INSERT INTO mail(receiver, replyTo, `subject`, body)
|
||||||
|
SELECT
|
||||||
|
CONCAT(IF(ac.id,u.name, 'jgallego'), '@verdnatura.es'),
|
||||||
|
'administracion@verdnatura.es',
|
||||||
|
CONCAT('Cliente ', NEW.id),
|
||||||
|
CONCAT('Recibida la documentación: ', vText)
|
||||||
|
FROM worker w
|
||||||
|
LEFT JOIN account.user u ON w.id = u.id AND u.active
|
||||||
|
LEFT JOIN account.account ac ON ac.id = u.id
|
||||||
|
WHERE w.id = NEW.salesPersonFk;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NEW.salespersonFk IS NULL AND OLD.salespersonFk IS NOT NULL THEN
|
||||||
|
IF (SELECT COUNT(clientFk)
|
||||||
|
FROM clientProtected
|
||||||
|
WHERE clientFk = NEW.id
|
||||||
|
) > 0 THEN
|
||||||
|
CALL util.throw("HAS_CLIENT_PROTECTED");
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF !(NEW.salesPersonFk <=> OLD.salesPersonFk) THEN
|
||||||
|
SET NEW.lastSalesPersonFk = IFNULL(NEW.salesPersonFk, OLD.salesPersonFk);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF !(NEW.businessTypeFk <=> OLD.businessTypeFk) AND (NEW.businessTypeFk = 'individual' OR OLD.businessTypeFk = 'individual') THEN
|
||||||
|
SET NEW.isTaxDataChecked = 0;
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`client_AfterInsert`
|
||||||
|
AFTER INSERT ON `client`
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
IF NEW.credit IS NOT NULL AND NEW.credit THEN
|
||||||
|
INSERT INTO clientCredit
|
||||||
|
SET clientFk = NEW.id,
|
||||||
|
workerFk = NEW.editorFk,
|
||||||
|
amount = NEW.credit;
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`expeditionState_BeforeInsert`
|
||||||
|
BEFORE INSERT ON `expeditionState`
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
SET NEW.userFk = IFNULL(NEW.userFk, account.myUser_getId());
|
||||||
|
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setState`(
|
||||||
|
vSelf INT,
|
||||||
|
vStateCode VARCHAR(255) COLLATE utf8_general_ci
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Modifica el estado de un ticket si se cumplen las condiciones necesarias.
|
||||||
|
*
|
||||||
|
* @param vSelf el id del ticket
|
||||||
|
* @param vStateCode estado a modificar del ticket
|
||||||
|
*/
|
||||||
|
DECLARE vticketAlertLevel INT;
|
||||||
|
DECLARE vTicketStateCode VARCHAR(255);
|
||||||
|
DECLARE vCanChangeState BOOL;
|
||||||
|
DECLARE vPackedAlertLevel INT;
|
||||||
|
DECLARE vZoneFk INT;
|
||||||
|
|
||||||
|
SELECT s.alertLevel, s.`code`, t.zoneFk
|
||||||
|
INTO vticketAlertLevel, vTicketStateCode, vZoneFk
|
||||||
|
FROM state s
|
||||||
|
JOIN ticketTracking tt ON tt.stateFk = s.id
|
||||||
|
JOIN ticket t ON t.id = tt.ticketFk
|
||||||
|
WHERE tt.ticketFk = vSelf
|
||||||
|
ORDER BY tt.created DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
SELECT id INTO vPackedAlertLevel FROM alertLevel WHERE code = 'PACKED';
|
||||||
|
|
||||||
|
IF vStateCode = 'OK' AND vZoneFk IS NULL THEN
|
||||||
|
CALL util.throw('ASSIGN_ZONE_FIRST');
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SET vCanChangeState = (
|
||||||
|
vStateCode <> 'ON_CHECKING' OR
|
||||||
|
vticketAlertLevel < vPackedAlertLevel
|
||||||
|
)AND NOT (
|
||||||
|
vTicketStateCode IN ('CHECKED', 'CHECKING')
|
||||||
|
AND vStateCode IN ('PREPARED', 'ON_PREPARATION')
|
||||||
|
);
|
||||||
|
|
||||||
|
IF vCanChangeState THEN
|
||||||
|
INSERT INTO ticketTracking (stateFk, ticketFk, workerFk)
|
||||||
|
SELECT id, vSelf, account.myUser_getId()
|
||||||
|
FROM state
|
||||||
|
WHERE `code` = vStateCode COLLATE utf8_unicode_ci;
|
||||||
|
|
||||||
|
IF vStateCode = 'PACKED' THEN
|
||||||
|
CALL ticket_doCmr(vSelf);
|
||||||
|
END IF;
|
||||||
|
ELSE
|
||||||
|
CALL util.throw('INCORRECT_TICKET_STATE');
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,6 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES
|
||||||
|
('CplusRectificationType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||||
|
('SiiTypeInvoiceOut', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||||
|
('InvoiceCorrectionType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
|
||||||
|
('InvoiceOut', 'transferInvoice', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
|
|
@ -0,0 +1 @@
|
||||||
|
CALL `account`.`role_sync`();
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `signInLog`
|
||||||
|
-- Description: log to debug cross-login error
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `account`.`signInLog`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `account`.`signInLog` (
|
||||||
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`token` varchar(255) NOT NULL ,
|
||||||
|
`userFk` int(10) unsigned DEFAULT NULL,
|
||||||
|
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
`ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
||||||
|
KEY `userFk` (`userFk`),
|
||||||
|
CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE account.sambaConfig
|
||||||
|
ADD userDn varchar(255) NOT NULL COMMENT 'Base DN for users without domain DN part';
|
|
@ -0,0 +1,4 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||||
|
VALUES
|
||||||
|
('Application', 'executeProc', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||||
|
('Application', 'executeFunc', '*', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,3 @@
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('NotificationSubscription', 'getList', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,152 @@
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canAdvance`(vDateFuture DATE, vDateToAdvance DATE, vWarehouseFk INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Devuelve los tickets y la cantidad de lineas de venta que se pueden adelantar.
|
||||||
|
*
|
||||||
|
* @param vDateFuture Fecha de los tickets que se quieren adelantar.
|
||||||
|
* @param vDateToAdvance Fecha a cuando se quiere adelantar.
|
||||||
|
* @param vWarehouseFk Almacén
|
||||||
|
*/
|
||||||
|
DECLARE vDateInventory DATE;
|
||||||
|
|
||||||
|
SELECT inventoried INTO vDateInventory FROM config;
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.stock
|
||||||
|
(itemFk INT PRIMARY KEY,
|
||||||
|
amount INT)
|
||||||
|
ENGINE = MEMORY;
|
||||||
|
|
||||||
|
INSERT INTO tmp.stock(itemFk, amount)
|
||||||
|
SELECT itemFk, SUM(quantity) amount FROM
|
||||||
|
(
|
||||||
|
SELECT itemFk, quantity
|
||||||
|
FROM itemTicketOut
|
||||||
|
WHERE shipped >= vDateInventory
|
||||||
|
AND shipped < vDateFuture
|
||||||
|
AND warehouseFk = vWarehouseFk
|
||||||
|
UNION ALL
|
||||||
|
SELECT itemFk, quantity
|
||||||
|
FROM itemEntryIn
|
||||||
|
WHERE landed >= vDateInventory
|
||||||
|
AND landed <= vDateToAdvance
|
||||||
|
AND isVirtualStock = FALSE
|
||||||
|
AND warehouseInFk = vWarehouseFk
|
||||||
|
UNION ALL
|
||||||
|
SELECT itemFk, quantity
|
||||||
|
FROM itemEntryOut
|
||||||
|
WHERE shipped >= vDateInventory
|
||||||
|
AND shipped < vDateFuture
|
||||||
|
AND warehouseOutFk = vWarehouseFk
|
||||||
|
) t
|
||||||
|
GROUP BY itemFk HAVING amount != 0;
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.filter
|
||||||
|
(INDEX (id))
|
||||||
|
SELECT
|
||||||
|
origin.ticketFk futureId,
|
||||||
|
dest.ticketFk id,
|
||||||
|
dest.state,
|
||||||
|
origin.futureState,
|
||||||
|
origin.futureIpt,
|
||||||
|
dest.ipt,
|
||||||
|
origin.workerFk,
|
||||||
|
origin.futureLiters,
|
||||||
|
origin.futureLines,
|
||||||
|
dest.shipped,
|
||||||
|
origin.shipped futureShipped,
|
||||||
|
dest.totalWithVat,
|
||||||
|
origin.totalWithVat futureTotalWithVat,
|
||||||
|
dest.agency,
|
||||||
|
dest.agencyModeFk,
|
||||||
|
origin.futureAgency,
|
||||||
|
origin.agencyModeFk futureAgencyModeFk,
|
||||||
|
dest.lines,
|
||||||
|
dest.liters,
|
||||||
|
origin.futureLines - origin.hasStock AS notMovableLines,
|
||||||
|
(origin.futureLines = origin.hasStock) AS isFullMovable,
|
||||||
|
dest.zoneFk,
|
||||||
|
origin.futureZoneFk,
|
||||||
|
origin.futureZoneName,
|
||||||
|
origin.classColor futureClassColor,
|
||||||
|
dest.classColor,
|
||||||
|
origin.clientFk futureClientFk,
|
||||||
|
origin.addressFk futureAddressFk,
|
||||||
|
origin.warehouseFk futureWarehouseFk,
|
||||||
|
origin.companyFk futureCompanyFk,
|
||||||
|
IFNULL(dest.nickname, origin.nickname) nickname,
|
||||||
|
dest.landed
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
s.ticketFk,
|
||||||
|
c.salesPersonFk workerFk,
|
||||||
|
t.shipped,
|
||||||
|
t.totalWithVat,
|
||||||
|
st.name futureState,
|
||||||
|
am.name futureAgency,
|
||||||
|
count(s.id) futureLines,
|
||||||
|
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
|
||||||
|
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
|
||||||
|
SUM((s.quantity <= IFNULL(st.amount,0))) hasStock,
|
||||||
|
z.id futureZoneFk,
|
||||||
|
z.name futureZoneName,
|
||||||
|
st.classColor,
|
||||||
|
t.clientFk,
|
||||||
|
t.nickname,
|
||||||
|
t.addressFk,
|
||||||
|
t.warehouseFk,
|
||||||
|
t.companyFk,
|
||||||
|
t.agencyModeFk
|
||||||
|
FROM ticket t
|
||||||
|
JOIN client c ON c.id = t.clientFk
|
||||||
|
JOIN sale s ON s.ticketFk = t.id
|
||||||
|
JOIN saleVolume sv ON sv.saleFk = s.id
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
|
JOIN state st ON st.id = ts.stateFk
|
||||||
|
JOIN agencyMode am ON t.agencyModeFk = am.id
|
||||||
|
JOIN zone z ON t.zoneFk = z.id
|
||||||
|
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
|
||||||
|
LEFT JOIN tmp.stock st ON st.itemFk = i.id
|
||||||
|
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
|
||||||
|
AND t.warehouseFk = vWarehouseFk
|
||||||
|
GROUP BY t.id
|
||||||
|
) origin
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
t.id ticketFk,
|
||||||
|
st.name state,
|
||||||
|
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
|
||||||
|
t.shipped,
|
||||||
|
t.totalWithVat,
|
||||||
|
am.name agency,
|
||||||
|
CAST(SUM(litros) AS DECIMAL(10,0)) liters,
|
||||||
|
CAST(COUNT(*) AS DECIMAL(10,0)) `lines`,
|
||||||
|
st.classColor,
|
||||||
|
t.clientFk,
|
||||||
|
t.nickname,
|
||||||
|
t.addressFk,
|
||||||
|
t.zoneFk,
|
||||||
|
t.warehouseFk,
|
||||||
|
t.companyFk,
|
||||||
|
t.landed,
|
||||||
|
t.agencyModeFk
|
||||||
|
FROM ticket t
|
||||||
|
JOIN sale s ON s.ticketFk = t.id
|
||||||
|
JOIN saleVolume sv ON sv.saleFk = s.id
|
||||||
|
JOIN item i ON i.id = s.itemFk
|
||||||
|
JOIN ticketState ts ON ts.ticketFk = t.id
|
||||||
|
JOIN state st ON st.id = ts.stateFk
|
||||||
|
JOIN agencyMode am ON t.agencyModeFk = am.id
|
||||||
|
LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
|
||||||
|
WHERE t.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
|
||||||
|
AND t.warehouseFk = vWarehouseFk
|
||||||
|
AND st.order <= 5
|
||||||
|
GROUP BY t.id
|
||||||
|
) dest ON dest.addressFk = origin.addressFk
|
||||||
|
WHERE origin.hasStock;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tmp.stock;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -275,13 +275,13 @@ INSERT INTO `cplusInvoiceType472` VALUES (1,'F1 - Factura'),(2,'F2 - Factura sim
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Dumping data for table `cplusInvoiceType477`
|
-- Dumping data for table `siiTypeInvoiceOut`
|
||||||
--
|
--
|
||||||
|
|
||||||
LOCK TABLES `cplusInvoiceType477` WRITE;
|
LOCK TABLES `siiTypeInvoiceOut` WRITE;
|
||||||
/*!40000 ALTER TABLE `cplusInvoiceType477` DISABLE KEYS */;
|
/*!40000 ALTER TABLE `siiTypeInvoiceOut` DISABLE KEYS */;
|
||||||
INSERT INTO `cplusInvoiceType477` VALUES (1,'F1 - Factura'),(2,'F2 - Factura simplificada (ticket)'),(3,'F3 - Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),(4,'F4 - Asiento resumen de facturas'),(5,'R1 - Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),(6,'R2 - Factura rectificativa (Art. 80.3)'),(7,'R3 - Factura rectificativa (Art. 80.4)'),(8,'R4 - Factura rectificativa (Resto)'),(9,'R5 - Factura rectificativa en facturas simplificadas');
|
INSERT INTO `siiTypeInvoiceOut` VALUES (1,'F1 - Factura'),(2,'F2 - Factura simplificada (ticket)'),(3,'F3 - Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),(4,'F4 - Asiento resumen de facturas'),(5,'R1 - Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),(6,'R2 - Factura rectificativa (Art. 80.3)'),(7,'R3 - Factura rectificativa (Art. 80.4)'),(8,'R4 - Factura rectificativa (Resto)'),(9,'R5 - Factura rectificativa en facturas simplificadas');
|
||||||
/*!40000 ALTER TABLE `cplusInvoiceType477` ENABLE KEYS */;
|
/*!40000 ALTER TABLE `siiTypeInvoiceOut` ENABLE KEYS */;
|
||||||
UNLOCK TABLES;
|
UNLOCK TABLES;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
|
|
@ -367,7 +367,7 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
|
||||||
(1105, 'Max Eisenhardt', '251628698', 'MAGNETO', 'Rogue', 'UNKNOWN WHEREABOUTS', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
(1105, 'Max Eisenhardt', '251628698', 'MAGNETO', 'Rogue', 'UNKNOWN WHEREABOUTS', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
||||||
(1106, 'DavidCharlesHaller', '53136686Q', 'LEGION', 'Charles Xavier', 'CITY OF NEW YORK, NEW YORK, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
(1106, 'DavidCharlesHaller', '53136686Q', 'LEGION', 'Charles Xavier', 'CITY OF NEW YORK, NEW YORK, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
||||||
(1107, 'Hank Pym', '09854837G', 'ANT MAN', 'Hawk', 'ANTHILL, SAN FRANCISCO, CALIFORNIA', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
(1107, 'Hank Pym', '09854837G', 'ANT MAN', 'Hawk', 'ANTHILL, SAN FRANCISCO, CALIFORNIA', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
||||||
(1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
(1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 5, 1, 300, 13, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
||||||
(1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist','normal'),
|
(1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist','normal'),
|
||||||
(1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist','normal'),
|
(1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist','normal'),
|
||||||
(1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','normal'),
|
(1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','normal'),
|
||||||
|
@ -405,7 +405,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr
|
||||||
(5, 'Max Eisenhardt', 'Unknown Whereabouts', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 1),
|
(5, 'Max Eisenhardt', 'Unknown Whereabouts', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 1),
|
||||||
(6, 'DavidCharlesHaller', 'Evil hideout', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 1),
|
(6, 'DavidCharlesHaller', 'Evil hideout', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 1),
|
||||||
(7, 'Hank Pym', 'Anthill', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 1),
|
(7, 'Hank Pym', 'Anthill', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 1),
|
||||||
(8, 'Charles Xavier', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 1),
|
(8, 'Charles Xavier', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 5, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 1),
|
||||||
(9, 'Bruce Banner', 'Somewhere in New York', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1109, 2, NULL, NULL, 0, 1),
|
(9, 'Bruce Banner', 'Somewhere in New York', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1109, 2, NULL, NULL, 0, 1),
|
||||||
(10, 'Jessica Jones', 'NYCC 2015 Poster', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 1),
|
(10, 'Jessica Jones', 'NYCC 2015 Poster', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 1),
|
||||||
(11, 'Missing', 'The space', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1111, 10, NULL, NULL, 0, 1),
|
(11, 'Missing', 'The space', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1111, 10, NULL, NULL, 0, 1),
|
||||||
|
@ -437,7 +437,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr
|
||||||
(125, 'The plastic cell', 'address 25', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 0),
|
(125, 'The plastic cell', 'address 25', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 0),
|
||||||
(126, 'Many places', 'address 26', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 0),
|
(126, 'Many places', 'address 26', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 0),
|
||||||
(127, 'Your pocket', 'address 27', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 0),
|
(127, 'Your pocket', 'address 27', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 0),
|
||||||
(128, 'Cerebro', 'address 28', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 0),
|
(128, 'Cerebro', 'address 28', 'Gotham', 46460, 5, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 0),
|
||||||
(129, 'Luke Cages Bar', 'address 29', 'Gotham', 'EC170150', 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 0),
|
(129, 'Luke Cages Bar', 'address 29', 'Gotham', 'EC170150', 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 0),
|
||||||
(130, 'Non valid address', 'address 30', 'Gotham', 46460, 1, 1111111111, 222222222, 0, 1101, 2, NULL, NULL, 0, 0);
|
(130, 'Non valid address', 'address 30', 'Gotham', 46460, 1, 1111111111, 222222222, 0, 1101, 2, NULL, NULL, 0, 0);
|
||||||
|
|
||||||
|
@ -470,22 +470,22 @@ CREATE TEMPORARY TABLE tmp.address
|
||||||
WHERE `defaultAddressFk` IS NULL;
|
WHERE `defaultAddressFk` IS NULL;
|
||||||
DROP TEMPORARY TABLE tmp.address;
|
DROP TEMPORARY TABLE tmp.address;
|
||||||
|
|
||||||
INSERT INTO `vn`.`clientCredit`(`id`, `clientFk`, `workerFk`, `amount`, `created`)
|
INSERT INTO `vn`.`clientCredit`(`clientFk`, `workerFk`, `amount`, `created`)
|
||||||
VALUES
|
VALUES
|
||||||
(1 , 1101, 5, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 MONTH)),
|
(1101, 5, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 MONTH)),
|
||||||
(2 , 1101, 5, 900, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 MONTH)),
|
(1101, 5, 900, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 MONTH)),
|
||||||
(3 , 1101, 5, 800, DATE_ADD(util.VN_CURDATE(), INTERVAL -9 MONTH)),
|
(1101, 5, 800, DATE_ADD(util.VN_CURDATE(), INTERVAL -9 MONTH)),
|
||||||
(4 , 1101, 5, 700, DATE_ADD(util.VN_CURDATE(), INTERVAL -8 MONTH)),
|
(1101, 5, 700, DATE_ADD(util.VN_CURDATE(), INTERVAL -8 MONTH)),
|
||||||
(5 , 1101, 5, 600, DATE_ADD(util.VN_CURDATE(), INTERVAL -7 MONTH)),
|
(1101, 5, 600, DATE_ADD(util.VN_CURDATE(), INTERVAL -7 MONTH)),
|
||||||
(6 , 1101, 5, 500, DATE_ADD(util.VN_CURDATE(), INTERVAL -6 MONTH)),
|
(1101, 5, 500, DATE_ADD(util.VN_CURDATE(), INTERVAL -6 MONTH)),
|
||||||
(7 , 1101, 5, 400, DATE_ADD(util.VN_CURDATE(), INTERVAL -5 MONTH)),
|
(1101, 5, 400, DATE_ADD(util.VN_CURDATE(), INTERVAL -5 MONTH)),
|
||||||
(8 , 1101, 9, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
|
(1101, 9, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
|
||||||
(9 , 1101, 9, 200, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
|
(1101, 9, 200, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
|
||||||
(10, 1101, 9, 100, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
|
(1101, 9, 100, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
|
||||||
(11, 1101, 9, 50 , DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
(1101, 9, 50 , DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
||||||
(12, 1102, 9, 800, util.VN_CURDATE()),
|
(1102, 9, 800, util.VN_CURDATE()),
|
||||||
(14, 1104, 9, 90 , util.VN_CURDATE()),
|
(1104, 9, 90 , util.VN_CURDATE()),
|
||||||
(15, 1105, 9, 90 , util.VN_CURDATE());
|
(1105, 9, 90 , util.VN_CURDATE());
|
||||||
|
|
||||||
INSERT INTO `vn`.`clientCreditLimit`(`id`, `maxAmount`, `roleFk`)
|
INSERT INTO `vn`.`clientCreditLimit`(`id`, `maxAmount`, `roleFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -604,7 +604,7 @@ INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaF
|
||||||
|
|
||||||
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'T', 1014.24, util.VN_CURDATE(), 1101, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(1, 'T', 1026.24, util.VN_CURDATE(), 1101, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
(2, 'T', 121.36, util.VN_CURDATE(), 1102, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(2, 'T', 121.36, util.VN_CURDATE(), 1102, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
(3, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(3, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
(4, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
(4, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
|
||||||
|
@ -2003,6 +2003,10 @@ UPDATE `vn`.`business` b
|
||||||
SET b.`departmentFk` = 43
|
SET b.`departmentFk` = 43
|
||||||
WHERE b.id IN(18, 19);
|
WHERE b.id IN(18, 19);
|
||||||
|
|
||||||
|
UPDATE `vn`.`business` b
|
||||||
|
SET b.`started` = b.`started` - INTERVAL 100 DAY
|
||||||
|
WHERE b.id = 1107;
|
||||||
|
|
||||||
INSERT INTO `vn`.`workCenterHoliday` (`workCenterFk`, `days`, `year`)
|
INSERT INTO `vn`.`workCenterHoliday` (`workCenterFk`, `days`, `year`)
|
||||||
VALUES
|
VALUES
|
||||||
('1', '27.5', YEAR(util.VN_CURDATE())),
|
('1', '27.5', YEAR(util.VN_CURDATE())),
|
||||||
|
@ -2051,22 +2055,22 @@ INSERT INTO `vn`.`absenceType` (`id`, `name`, `rgb`, `code`, `holidayEntitlement
|
||||||
|
|
||||||
INSERT INTO `vn`.`calendar` (`businessFk`, `dayOffTypeFk`, `dated`)
|
INSERT INTO `vn`.`calendar` (`businessFk`, `dayOffTypeFk`, `dated`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 6, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
|
(1, 6, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 10 DAY, util.VN_CURDATE() + INTERVAL 10 DAY)),
|
||||||
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
|
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 10 DAY, util.VN_CURDATE() + INTERVAL 10 DAY)),
|
||||||
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 11 DAY))),
|
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 11 DAY, util.VN_CURDATE() + INTERVAL 11 DAY)),
|
||||||
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -12 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 12 DAY))),
|
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 12 DAY, util.VN_CURDATE() + INTERVAL 12 DAY)),
|
||||||
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -20 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 20 DAY))),
|
(1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 20 DAY, util.VN_CURDATE() + INTERVAL 20 DAY)),
|
||||||
(1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -13 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 8 DAY))),
|
(1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 13 DAY, util.VN_CURDATE() + INTERVAL 8 DAY)),
|
||||||
(1106, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -14 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 9 DAY))),
|
(1106, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 14 DAY, util.VN_CURDATE() + INTERVAL 9 DAY)),
|
||||||
(1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 7 DAY))),
|
(1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 15 DAY, util.VN_CURDATE() + INTERVAL 7 DAY)),
|
||||||
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
|
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 10 DAY, util.VN_CURDATE() + INTERVAL 10 DAY)),
|
||||||
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 11 DAY))),
|
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 11 DAY, util.VN_CURDATE() + INTERVAL 11 DAY)),
|
||||||
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -12 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 12 DAY))),
|
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 12 DAY, util.VN_CURDATE() + INTERVAL 12 DAY)),
|
||||||
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -20 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 20 DAY))),
|
(1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 20 DAY, util.VN_CURDATE() + INTERVAL 20 DAY)),
|
||||||
(1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -13 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 8 DAY))),
|
(1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 13 DAY, util.VN_CURDATE() + INTERVAL 8 DAY)),
|
||||||
(1107, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -14 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 9 DAY))),
|
(1107, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 14 DAY, util.VN_CURDATE() + INTERVAL 9 DAY)),
|
||||||
(1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 7 DAY))),
|
(1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 15 DAY, util.VN_CURDATE() + INTERVAL 7 DAY)),
|
||||||
(1107, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL - 16 DAY));
|
(1107, 2, util.VN_CURDATE() - INTERVAL 16 DAY);
|
||||||
|
|
||||||
INSERT INTO `vn`.`smsConfig` (`id`, `uri`, `title`, `apiKey`)
|
INSERT INTO `vn`.`smsConfig` (`id`, `uri`, `title`, `apiKey`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2754,9 +2758,9 @@ INSERT INTO `vn`.`sectorCollectionSaleGroup` (`sectorCollectionFk`, `saleGroupFk
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1);
|
(1, 1);
|
||||||
|
|
||||||
INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`, `teleworkingStart`, `teleworkingStartBreakTime`)
|
INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`, `teleworkingStart`, `teleworkingStartBreakTime`, `maxTimeToBreak`, `maxWorkShortCycle`, `maxWorkLongCycle`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13, 28800, 32400);
|
(1, 43200, 32400, 129600, 259200, 1080000, '', 'imap.verdnatura.es', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.00, 0.33, 40, '22:00:00', '06:00:00', 72000, 1200, 18000, 72000, 6, 13, 28800, 32400, 3600, 561600, 950400);
|
||||||
|
|
||||||
INSERT INTO `vn`.`host` (`id`, `code`, `description`, `warehouseFk`, `bankFk`)
|
INSERT INTO `vn`.`host` (`id`, `code`, `description`, `warehouseFk`, `bankFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2784,6 +2788,11 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
|
||||||
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 9),
|
(1, 9),
|
||||||
|
(1, 1),
|
||||||
|
(2, 1),
|
||||||
|
(3, 9),
|
||||||
|
(4, 1),
|
||||||
|
(5, 9),
|
||||||
(6, 9);
|
(6, 9);
|
||||||
|
|
||||||
INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `authorFk`, `status`, `created`)
|
INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `authorFk`, `status`, `created`)
|
||||||
|
@ -2796,6 +2805,8 @@ INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1109),
|
(1, 1109),
|
||||||
(1, 1110),
|
(1, 1110),
|
||||||
|
(2, 1110),
|
||||||
|
(4, 1110),
|
||||||
(2, 1109),
|
(2, 1109),
|
||||||
(1, 9),
|
(1, 9),
|
||||||
(1, 3),
|
(1, 3),
|
||||||
|
@ -2876,7 +2887,9 @@ INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
|
||||||
|
|
||||||
INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
|
INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1);
|
(1, 1),
|
||||||
|
(2, 20),
|
||||||
|
(7, 1);
|
||||||
|
|
||||||
INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`, `payMethodFk`, `businessTypeFk`)
|
INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`, `payMethodFk`, `businessTypeFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2977,3 +2990,9 @@ INSERT INTO vn.XDiario (id, ASIEN, FECHA, SUBCTA, CONTRA, CONCEPTO, EURODEBE, EU
|
||||||
INSERT INTO `vn`.`mistakeType` (`id`, `description`)
|
INSERT INTO `vn`.`mistakeType` (`id`, `description`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Incorrect quantity');
|
(1, 'Incorrect quantity');
|
||||||
|
|
||||||
|
INSERT INTO `vn`.`invoiceCorrectionType` (`id`, `description`)
|
||||||
|
VALUES
|
||||||
|
(1, 'Error in VAT calculation'),
|
||||||
|
(2, 'Error in sales details'),
|
||||||
|
(3, 'Error in customer data');
|
||||||
|
|
|
@ -2352,6 +2352,90 @@ BEGIN
|
||||||
END IF;
|
END IF;
|
||||||
END ;;
|
END ;;
|
||||||
DELIMITER ;
|
DELIMITER ;
|
||||||
|
|
||||||
|
|
||||||
|
DELIMITER ;;
|
||||||
|
CREATE DEFINER=`root`@`localhost` FUNCTION `account`.`user_hasRoutinePriv`(vType ENUM('PROCEDURE', 'FUNCTION'),
|
||||||
|
vChain VARCHAR(100),
|
||||||
|
vUserFk INT
|
||||||
|
) RETURNS tinyint(1)
|
||||||
|
READS SQL DATA
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search if the user has privileges on routines.
|
||||||
|
*
|
||||||
|
* @param vType procedure or function
|
||||||
|
* @param vChain string passed with this syntax dbName.tableName
|
||||||
|
* @param vUserFk user to ckeck
|
||||||
|
* @return vHasPrivilege
|
||||||
|
*/
|
||||||
|
DECLARE vHasPrivilege BOOL DEFAULT FALSE;
|
||||||
|
DECLARE vDb VARCHAR(50);
|
||||||
|
DECLARE vObject VARCHAR(50);
|
||||||
|
DECLARE vChainExists BOOL;
|
||||||
|
DECLARE vExecutePriv INT DEFAULT 262144;
|
||||||
|
-- 262144 = CONV(1000000000000000000, 2, 10)
|
||||||
|
-- 1000000000000000000 execution permission expressed in binary base
|
||||||
|
|
||||||
|
SET vDb = SUBSTRING_INDEX(vChain, '.', 1);
|
||||||
|
SET vChain = SUBSTRING(vChain, LENGTH(vDb) + 2);
|
||||||
|
SET vObject = SUBSTRING_INDEX(vChain, '.', 1);
|
||||||
|
|
||||||
|
SELECT COUNT(*) INTO vChainExists
|
||||||
|
FROM mysql.proc
|
||||||
|
WHERE db = vDb
|
||||||
|
AND `name` = vObject
|
||||||
|
AND `type` = vType
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF NOT vChainExists THEN
|
||||||
|
RETURN FALSE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS tRole;
|
||||||
|
CREATE TEMPORARY TABLE tRole
|
||||||
|
(INDEX (`name`))
|
||||||
|
ENGINE = MEMORY
|
||||||
|
SELECT r.`name`
|
||||||
|
FROM user u
|
||||||
|
JOIN roleRole rr ON rr.role = u.role
|
||||||
|
JOIN `role` r ON r.id = rr.inheritsFrom
|
||||||
|
WHERE u.id = vUserFk;
|
||||||
|
|
||||||
|
SELECT TRUE INTO vHasPrivilege
|
||||||
|
FROM mysql.global_priv gp
|
||||||
|
JOIN tRole tr ON tr.name = gp.`User`
|
||||||
|
OR CONCAT('$', tr.name) = gp.`User`
|
||||||
|
WHERE JSON_VALUE(gp.Priv, '$.access') >= vExecutePriv
|
||||||
|
AND gp.Host = ''
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF NOT vHasPrivilege THEN
|
||||||
|
SELECT TRUE INTO vHasPrivilege
|
||||||
|
FROM mysql.db db
|
||||||
|
JOIN tRole tr ON tr.name = db.`User`
|
||||||
|
WHERE db.Db = vDb
|
||||||
|
AND db.Execute_priv = 'Y';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF NOT vHasPrivilege THEN
|
||||||
|
SELECT TRUE INTO vHasPrivilege
|
||||||
|
FROM mysql.procs_priv pp
|
||||||
|
JOIN tRole tr ON tr.name = pp.`User`
|
||||||
|
WHERE pp.Db = vDb
|
||||||
|
AND pp.Routine_name = vObject
|
||||||
|
AND pp.Routine_type = vType
|
||||||
|
AND pp.Proc_priv = 'Execute'
|
||||||
|
LIMIT 1;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tRole;
|
||||||
|
RETURN vHasPrivilege;
|
||||||
|
END ;;
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
|
||||||
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
/*!50003 SET sql_mode = @saved_sql_mode */ ;
|
||||||
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
/*!50003 SET character_set_client = @saved_cs_client */ ;
|
||||||
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
/*!50003 SET character_set_results = @saved_cs_results */ ;
|
||||||
|
@ -25993,13 +26077,13 @@ CREATE TABLE `cplusInvoiceType472` (
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `cplusInvoiceType477`
|
-- Table structure for table `siiTypeInvoiceOut`
|
||||||
--
|
--
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `cplusInvoiceType477`;
|
DROP TABLE IF EXISTS `siiTypeInvoiceOut`;
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
CREATE TABLE `cplusInvoiceType477` (
|
CREATE TABLE `siiTypeInvoiceOut` (
|
||||||
`id` int(10) unsigned NOT NULL,
|
`id` int(10) unsigned NOT NULL,
|
||||||
`description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
`description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
|
@ -29450,16 +29534,16 @@ CREATE TABLE `invoiceCorrection` (
|
||||||
`correctingFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificativa',
|
`correctingFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificativa',
|
||||||
`correctedFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificada',
|
`correctedFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificada',
|
||||||
`cplusRectificationTypeFk` int(10) unsigned NOT NULL,
|
`cplusRectificationTypeFk` int(10) unsigned NOT NULL,
|
||||||
`cplusInvoiceType477Fk` int(10) unsigned NOT NULL,
|
`siiTypeInvoiceOutFk` int(10) unsigned NOT NULL,
|
||||||
`invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3,
|
`invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3,
|
||||||
PRIMARY KEY (`correctingFk`),
|
PRIMARY KEY (`correctingFk`),
|
||||||
KEY `correctedFk_idx` (`correctedFk`),
|
KEY `correctedFk_idx` (`correctedFk`),
|
||||||
KEY `invoiceCorrection_ibfk_1_idx` (`cplusRectificationTypeFk`),
|
KEY `invoiceCorrection_ibfk_1_idx` (`cplusRectificationTypeFk`),
|
||||||
KEY `cplusInvoiceTyoeFk_idx` (`cplusInvoiceType477Fk`),
|
KEY `cplusInvoiceTyoeFk_idx` (`siiTypeInvoiceOutFk`),
|
||||||
KEY `invoiceCorrectionTypeFk_idx` (`invoiceCorrectionTypeFk`),
|
KEY `invoiceCorrectionTypeFk_idx` (`invoiceCorrectionTypeFk`),
|
||||||
CONSTRAINT `corrected_fk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
CONSTRAINT `corrected_fk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
CONSTRAINT `correcting_fk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
CONSTRAINT `correcting_fk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
CONSTRAINT `cplusInvoiceTyoeFk` FOREIGN KEY (`cplusInvoiceType477Fk`) REFERENCES `cplusInvoiceType477` (`id`) ON UPDATE CASCADE,
|
CONSTRAINT `cplusInvoiceTyoeFk` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE,
|
||||||
CONSTRAINT `invoiceCorrectionType_Fk33` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE,
|
CONSTRAINT `invoiceCorrectionType_Fk33` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE,
|
||||||
CONSTRAINT `invoiceCorrection_ibfk_1` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE
|
CONSTRAINT `invoiceCorrection_ibfk_1` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Relacion entre las facturas rectificativas y las rectificadas.';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Relacion entre las facturas rectificativas y las rectificadas.';
|
||||||
|
@ -30130,7 +30214,7 @@ CREATE TABLE `invoiceOut` (
|
||||||
`companyFk` int(10) unsigned NOT NULL DEFAULT 442,
|
`companyFk` int(10) unsigned NOT NULL DEFAULT 442,
|
||||||
`hasPdf` tinyint(3) unsigned NOT NULL DEFAULT 0,
|
`hasPdf` tinyint(3) unsigned NOT NULL DEFAULT 0,
|
||||||
`booked` date DEFAULT NULL,
|
`booked` date DEFAULT NULL,
|
||||||
`cplusInvoiceType477Fk` int(10) unsigned NOT NULL DEFAULT 1,
|
`siiTypeInvoiceOutFk` int(10) unsigned NOT NULL DEFAULT 1,
|
||||||
`cplusTaxBreakFk` int(10) unsigned NOT NULL DEFAULT 1,
|
`cplusTaxBreakFk` int(10) unsigned NOT NULL DEFAULT 1,
|
||||||
`cplusSubjectOpFk` int(10) unsigned NOT NULL DEFAULT 1,
|
`cplusSubjectOpFk` int(10) unsigned NOT NULL DEFAULT 1,
|
||||||
`cplusTrascendency477Fk` int(10) unsigned NOT NULL DEFAULT 1,
|
`cplusTrascendency477Fk` int(10) unsigned NOT NULL DEFAULT 1,
|
||||||
|
@ -30140,13 +30224,13 @@ CREATE TABLE `invoiceOut` (
|
||||||
KEY `Id_Cliente` (`clientFk`),
|
KEY `Id_Cliente` (`clientFk`),
|
||||||
KEY `empresa_id` (`companyFk`),
|
KEY `empresa_id` (`companyFk`),
|
||||||
KEY `Fecha` (`issued`),
|
KEY `Fecha` (`issued`),
|
||||||
KEY `Facturas_ibfk_2_idx` (`cplusInvoiceType477Fk`),
|
KEY `Facturas_ibfk_2_idx` (`siiTypeInvoiceOutFk`),
|
||||||
KEY `Facturas_ibfk_3_idx` (`cplusSubjectOpFk`),
|
KEY `Facturas_ibfk_3_idx` (`cplusSubjectOpFk`),
|
||||||
KEY `Facturas_ibfk_4_idx` (`cplusTaxBreakFk`),
|
KEY `Facturas_ibfk_4_idx` (`cplusTaxBreakFk`),
|
||||||
KEY `Facturas_ibfk_5_idx` (`cplusTrascendency477Fk`),
|
KEY `Facturas_ibfk_5_idx` (`cplusTrascendency477Fk`),
|
||||||
KEY `Facturas_idx_Vencimiento` (`dued`),
|
KEY `Facturas_idx_Vencimiento` (`dued`),
|
||||||
KEY `invoiceOut_serial` (`serial`),
|
KEY `invoiceOut_serial` (`serial`),
|
||||||
CONSTRAINT `invoiceOut_ibfk_2` FOREIGN KEY (`cplusInvoiceType477Fk`) REFERENCES `cplusInvoiceType477` (`id`) ON UPDATE CASCADE,
|
CONSTRAINT `invoiceOut_ibfk_2` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE,
|
||||||
CONSTRAINT `invoiceOut_ibfk_3` FOREIGN KEY (`cplusSubjectOpFk`) REFERENCES `cplusSubjectOp` (`id`) ON UPDATE CASCADE,
|
CONSTRAINT `invoiceOut_ibfk_3` FOREIGN KEY (`cplusSubjectOpFk`) REFERENCES `cplusSubjectOp` (`id`) ON UPDATE CASCADE,
|
||||||
CONSTRAINT `invoiceOut_ibfk_4` FOREIGN KEY (`cplusTaxBreakFk`) REFERENCES `cplusTaxBreak` (`id`) ON UPDATE CASCADE,
|
CONSTRAINT `invoiceOut_ibfk_4` FOREIGN KEY (`cplusTaxBreakFk`) REFERENCES `cplusTaxBreak` (`id`) ON UPDATE CASCADE,
|
||||||
CONSTRAINT `invoiceOut_serial` FOREIGN KEY (`serial`) REFERENCES `invoiceOutSerial` (`code`),
|
CONSTRAINT `invoiceOut_serial` FOREIGN KEY (`serial`) REFERENCES `invoiceOutSerial` (`code`),
|
||||||
|
@ -30308,7 +30392,7 @@ CREATE TABLE `invoiceOutSerial` (
|
||||||
`isTaxed` tinyint(1) NOT NULL DEFAULT 1,
|
`isTaxed` tinyint(1) NOT NULL DEFAULT 1,
|
||||||
`taxAreaFk` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT 'NATIONAL',
|
`taxAreaFk` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT 'NATIONAL',
|
||||||
`isCEE` tinyint(1) NOT NULL DEFAULT 0,
|
`isCEE` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
`cplusInvoiceType477Fk` int(10) unsigned DEFAULT 1,
|
`siiTypeInvoiceOutFk` int(10) unsigned DEFAULT 1,
|
||||||
`footNotes` longtext DEFAULT NULL,
|
`footNotes` longtext DEFAULT NULL,
|
||||||
`isRefEditable` tinyint(4) NOT NULL DEFAULT 0,
|
`isRefEditable` tinyint(4) NOT NULL DEFAULT 0,
|
||||||
`type` enum('global','quick') DEFAULT NULL,
|
`type` enum('global','quick') DEFAULT NULL,
|
||||||
|
@ -58288,7 +58372,7 @@ BEGIN
|
||||||
io.cplusTrascendency477Fk AS TIPOCLAVE,
|
io.cplusTrascendency477Fk AS TIPOCLAVE,
|
||||||
io.cplusTaxBreakFk AS TIPOEXENCI,
|
io.cplusTaxBreakFk AS TIPOEXENCI,
|
||||||
io.cplusSubjectOpFk AS TIPONOSUJE,
|
io.cplusSubjectOpFk AS TIPONOSUJE,
|
||||||
io.cplusInvoiceType477Fk AS TIPOFACT,
|
io.siiTypeInvoiceOutFk AS TIPOFACT,
|
||||||
ic.cplusRectificationTypeFk AS TIPORECTIF,
|
ic.cplusRectificationTypeFk AS TIPORECTIF,
|
||||||
io.companyFk,
|
io.companyFk,
|
||||||
RIGHT(io.ref, LENGTH(io.ref) - 1) AS invoiceNum,
|
RIGHT(io.ref, LENGTH(io.ref) - 1) AS invoiceNum,
|
||||||
|
@ -58868,7 +58952,7 @@ BEGIN
|
||||||
clientFk,
|
clientFk,
|
||||||
dued,
|
dued,
|
||||||
companyFk,
|
companyFk,
|
||||||
cplusInvoiceType477Fk
|
siiTypeInvoiceOutFk
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -46,7 +46,7 @@ TABLES=(
|
||||||
bookingPlanner
|
bookingPlanner
|
||||||
businessType
|
businessType
|
||||||
cplusInvoiceType472
|
cplusInvoiceType472
|
||||||
cplusInvoiceType477
|
siiTypeInvoiceOut
|
||||||
cplusRectificationType
|
cplusRectificationType
|
||||||
cplusSubjectOp
|
cplusSubjectOp
|
||||||
cplusTaxBreak
|
cplusTaxBreak
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
const app = require('vn-loopback/server/server');
|
|
||||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
|
||||||
|
|
||||||
describe('timeControl_calculateByUser()', () => {
|
|
||||||
it(`should return today's worked hours`, async() => {
|
|
||||||
let start = Date.vnNew();
|
|
||||||
start.setHours(0, 0, 0, 0);
|
|
||||||
start.setDate(start.getDate() - 1);
|
|
||||||
|
|
||||||
let end = Date.vnNew();
|
|
||||||
end.setHours(0, 0, 0, 0);
|
|
||||||
end.setDate(end.getDate() + 1);
|
|
||||||
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
workerID: 1106,
|
|
||||||
start: start,
|
|
||||||
end: end
|
|
||||||
};
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
|
|
||||||
params.workerID,
|
|
||||||
params.start,
|
|
||||||
params.end
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
let tableIndex = stmts.push('SELECT * FROM tmp.timeControlCalculate') - 1;
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
let result = await app.models.Ticket.rawStmt(sql);
|
|
||||||
|
|
||||||
let [timeControlCalculateTable] = result[tableIndex];
|
|
||||||
|
|
||||||
expect(timeControlCalculateTable.timeWorkSeconds).toEqual(28200);
|
|
||||||
});
|
|
||||||
// #2261
|
|
||||||
xit(`should return the worked hours between last sunday and monday`, async() => {
|
|
||||||
let lastSunday = Date.vnNew();
|
|
||||||
let daysSinceSunday = lastSunday.getDay();
|
|
||||||
if (daysSinceSunday === 0) // this means today is sunday but you need the previous sunday :)
|
|
||||||
daysSinceSunday = 7;
|
|
||||||
lastSunday.setHours(23, 0, 0, 0);
|
|
||||||
lastSunday.setDate(lastSunday.getDate() - daysSinceSunday);
|
|
||||||
|
|
||||||
let monday = Date.vnNew();
|
|
||||||
let daysSinceMonday = daysSinceSunday - 1; // aiming for monday (today could be monday)
|
|
||||||
monday.setHours(7, 0, 0, 0);
|
|
||||||
monday.setDate(monday.getDate() - daysSinceMonday);
|
|
||||||
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
|
|
||||||
const workerID = 1108;
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`
|
|
||||||
INSERT INTO vn.workerTimeControl(userFk, timed, manual, direction)
|
|
||||||
VALUES
|
|
||||||
(?, ?, 1, 'in'),
|
|
||||||
(?, ?, 1, 'out')
|
|
||||||
`, [
|
|
||||||
workerID,
|
|
||||||
lastSunday,
|
|
||||||
workerID,
|
|
||||||
monday
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
|
|
||||||
workerID,
|
|
||||||
lastSunday,
|
|
||||||
monday
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
let tableIndex = stmts.push('SELECT * FROM tmp.timeControlCalculate') - 1;
|
|
||||||
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
let result = await app.models.Ticket.rawStmt(sql);
|
|
||||||
|
|
||||||
let [timeControlCalculateTable] = result[tableIndex];
|
|
||||||
|
|
||||||
expect(timeControlCalculateTable.timeWorkSeconds).toEqual(30000);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,580 +0,0 @@
|
||||||
const app = require('vn-loopback/server/server');
|
|
||||||
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
|
|
||||||
|
|
||||||
// #2261 xdescribe dbtest workerTimeControl_check()
|
|
||||||
xdescribe('worker workerTimeControl_check()', () => {
|
|
||||||
it(`should throw an error if the worker can't sign on that tablet`, async() => {
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
const workerId = 1110;
|
|
||||||
const tabletId = 2;
|
|
||||||
let err;
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
try {
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err.sqlMessage).toEqual('No perteneces a este departamento.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check that the worker can sign on that tablet', async() => {
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
const workerId = 1110;
|
|
||||||
const tabletId = 1;
|
|
||||||
let err;
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
try {
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err).not.toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker with a special category has not finished the 9h break', async() => {
|
|
||||||
const workerId = 1110;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let sql;
|
|
||||||
let error;
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-17,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(SECOND,-32399,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.sqlMessage).toEqual('Descansos 9 h');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check f the worker with a special category has finished the 9h break', async() => {
|
|
||||||
const workerId = 1110;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let err;
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-17,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(SECOND,-32401,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err).not.toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker has not finished the 12h break', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let sql;
|
|
||||||
let error;
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-20,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(SECOND,-43199,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error.sqlMessage).toEqual('Descansos 12 h');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker has finished the 12h break', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let err;
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-20,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(SECOND,-43201,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err).not.toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker has odd entry records', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let err;
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in")`, [
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err.sqlMessage).toEqual('Dias con fichadas impares');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker try to sign on a holiday day', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let err;
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-20,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err.sqlMessage).toEqual('Holidays');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker try to sign with your contract ended', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let err;
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`UPDATE vn.business SET ended = DATE_ADD(CURDATE(), INTERVAL -1 DAY) WHERE id = ?`, [
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-20,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err.sqlMessage).toEqual('No hay un contrato en vigor');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker has not finished the 36h weekly break', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
|
|
||||||
stmts.push('SET @warn := NULL');
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-120,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-112,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-144,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-136,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-168,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-160,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-192,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-184,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
let warningMessageIndex = stmts.push('SELECT @warn AS warning') - 1;
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
let result = await app.models.Worker.rawStmt(sql);
|
|
||||||
|
|
||||||
expect(result[warningMessageIndex][0].warning).toEqual('Descansos 36 h');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check if the worker has finished the 36h weekly break', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
|
|
||||||
stmts.push('SET @warn := NULL');
|
|
||||||
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
let warningMessageIndex = stmts.push('SELECT @warn AS warning') - 1;
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
let result = await app.models.Worker.rawStmt(sql);
|
|
||||||
|
|
||||||
expect(result[warningMessageIndex][0].warning).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the worker has not finished the 72h biweekly break', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let err;
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-120,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-112,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-144,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-136,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-168,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-160,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-192,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-184,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-216,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-208,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-240,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-232,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-264,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-256,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-289,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-280,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
stmts.push('SELECT @warn AS warning') - 1;
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err.sqlMessage).toEqual('Descansos 72 h');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check if the worker has finished the 72h biweekly break', async() => {
|
|
||||||
const workerId = 1109;
|
|
||||||
const tabletId = 1;
|
|
||||||
let stmts = [];
|
|
||||||
let stmt;
|
|
||||||
let err;
|
|
||||||
stmts.push('START TRANSACTION');
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
|
|
||||||
VALUES
|
|
||||||
(?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-120,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-112,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-144,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-136,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-168,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-160,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-192,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-184,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-216,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-208,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-240,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-232,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-264,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-256,NOW()),0,"out"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-288,NOW()),0,"in"),
|
|
||||||
(?,TIMESTAMPADD(HOUR,-280,NOW()),0,"out")`, [
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId,
|
|
||||||
workerId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
|
|
||||||
stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
|
|
||||||
workerId,
|
|
||||||
tabletId
|
|
||||||
]);
|
|
||||||
stmts.push(stmt);
|
|
||||||
stmts.push('ROLLBACK');
|
|
||||||
|
|
||||||
stmts.push('SELECT @warn AS warning') - 1;
|
|
||||||
|
|
||||||
let sql = ParameterizedSQL.join(stmts, ';');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await app.models.Worker.rawStmt(sql);
|
|
||||||
} catch (e) {
|
|
||||||
await app.models.Worker.rawSql('ROLLBACK');
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(err).not.toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -722,7 +722,7 @@ export default {
|
||||||
isFullMovable: 'vn-check[ng-model="filter.isFullMovable"]',
|
isFullMovable: 'vn-check[ng-model="filter.isFullMovable"]',
|
||||||
warehouseFk: 'vn-autocomplete[label="Warehouse"]',
|
warehouseFk: 'vn-autocomplete[label="Warehouse"]',
|
||||||
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
||||||
moveButton: 'vn-button[vn-tooltip="Advance tickets"]',
|
moveButton: 'vn-button[vn-tooltip="Advance tickets with negatives"]',
|
||||||
acceptButton: '.vn-confirm.shown button[response="accept"]',
|
acceptButton: '.vn-confirm.shown button[response="accept"]',
|
||||||
firstCheck: 'tbody > tr:nth-child(2) > td > vn-check',
|
firstCheck: 'tbody > tr:nth-child(2) > td > vn-check',
|
||||||
tableId: 'vn-textfield[name="id"]',
|
tableId: 'vn-textfield[name="id"]',
|
||||||
|
|
|
@ -212,7 +212,7 @@ describe('Ticket Edit sale path', () => {
|
||||||
|
|
||||||
it('should log in as salesAssistant and navigate to ticket sales', async() => {
|
it('should log in as salesAssistant and navigate to ticket sales', async() => {
|
||||||
await page.loginAndModule('salesAssistant', 'ticket');
|
await page.loginAndModule('salesAssistant', 'ticket');
|
||||||
await page.accessToSearchResult('17');
|
await page.accessToSearchResult('15');
|
||||||
await page.accessToSection('ticket.card.sale');
|
await page.accessToSection('ticket.card.sale');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ describe('Ticket Edit sale path', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should confirm the transfered quantity is the correct one', async() => {
|
it('should confirm the transfered quantity is the correct one', async() => {
|
||||||
const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText');
|
const result = await page.waitToGetProperty(selectors.ticketSales.firstSaleQuantityCell, 'innerText');
|
||||||
|
|
||||||
expect(result).toContain('20');
|
expect(result).toContain('20');
|
||||||
});
|
});
|
||||||
|
@ -370,7 +370,7 @@ describe('Ticket Edit sale path', () => {
|
||||||
await page.waitToClick(selectors.ticketSales.moveToNewTicketButton);
|
await page.waitToClick(selectors.ticketSales.moveToNewTicketButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
expect(message.text).toContain(`You can't create a ticket for a inactive client`);
|
expect(message.text).toContain(`You can't create a ticket for an inactive client`);
|
||||||
|
|
||||||
await page.closePopup();
|
await page.closePopup();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.execute = async(ctx, type, query, params, options) => {
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
const models = Self.app.models;
|
||||||
|
params = params ?? [];
|
||||||
|
|
||||||
|
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const chain = query.split(' ')[1];
|
||||||
|
|
||||||
|
const [canExecute] = await models.ProcsPriv.rawSql(
|
||||||
|
'SELECT account.user_hasRoutinePriv(?,?,?)',
|
||||||
|
[type, chain, userId],
|
||||||
|
myOptions);
|
||||||
|
|
||||||
|
if (!Object.values(canExecute)[0]) throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
|
||||||
|
|
||||||
|
const argString = params.map(() => '?').join(',');
|
||||||
|
|
||||||
|
const response = await models.ProcsPriv.rawSql(query + `(${argString})`, params, myOptions);
|
||||||
|
if (!Array.isArray(response)) return;
|
||||||
|
return response[0];
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,41 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('executeFunc', {
|
||||||
|
description: 'Return result of function',
|
||||||
|
accessType: 'EXECUTE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'routine',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The routine name',
|
||||||
|
required: true,
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'schema',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The routine schema',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'params',
|
||||||
|
type: ['any'],
|
||||||
|
description: 'The params array',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'any',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:routine/execute-func`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.executeFunc = async(ctx, routine, schema, params, options) => {
|
||||||
|
const query = `SELECT ${schema}.${routine}`;
|
||||||
|
|
||||||
|
const response = await Self.execute(ctx, 'FUNCTION', query, params, options);
|
||||||
|
return Object.values(response)[0];
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,39 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('executeProc', {
|
||||||
|
description: 'Return result of procedure',
|
||||||
|
accessType: 'EXECUTE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'routine',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The routine name',
|
||||||
|
required: true,
|
||||||
|
http: {source: 'path'}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'schema',
|
||||||
|
type: 'string',
|
||||||
|
description: 'The routine schema',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'params',
|
||||||
|
type: ['any'],
|
||||||
|
description: 'The params array',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'any',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/:routine/execute-proc`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.executeProc = async(ctx, routine, schema, params, options) => {
|
||||||
|
const query = `CALL ${schema}.${routine}`;
|
||||||
|
return Self.execute(ctx, 'PROCEDURE', query, params, options);
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,161 @@
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('Application execute()/executeProc()/executeFunc()', () => {
|
||||||
|
const userWithoutPrivileges = 1;
|
||||||
|
const userWithPrivileges = 9;
|
||||||
|
const userWithInheritedPrivileges = 120;
|
||||||
|
let tx;
|
||||||
|
|
||||||
|
function getCtx(userId) {
|
||||||
|
return {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId},
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(async() => {
|
||||||
|
tx = await models.Application.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
await models.Application.rawSql(`
|
||||||
|
CREATE OR REPLACE PROCEDURE vn.myProcedure(vMyParam INT)
|
||||||
|
BEGIN
|
||||||
|
SELECT vMyParam myParam, t.*
|
||||||
|
FROM ticket t
|
||||||
|
LIMIT 2;
|
||||||
|
END
|
||||||
|
`, null, options);
|
||||||
|
|
||||||
|
await models.Application.rawSql(`
|
||||||
|
CREATE OR REPLACE FUNCTION bs.myFunction(vMyParam INT) RETURNS int(11)
|
||||||
|
BEGIN
|
||||||
|
RETURN vMyParam;
|
||||||
|
END
|
||||||
|
`, null, options);
|
||||||
|
|
||||||
|
await models.Application.rawSql(`
|
||||||
|
GRANT EXECUTE ON PROCEDURE vn.myProcedure TO developer;
|
||||||
|
GRANT EXECUTE ON FUNCTION bs.myFunction TO developer;
|
||||||
|
`, null, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when execute procedure and not have privileges', async() => {
|
||||||
|
const ctx = getCtx(userWithoutPrivileges);
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
await models.Application.execute(
|
||||||
|
ctx,
|
||||||
|
'PROCEDURE',
|
||||||
|
'CALL vn.myProcedure',
|
||||||
|
[1],
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should execute procedure and get data', async() => {
|
||||||
|
const ctx = getCtx(userWithPrivileges);
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const response = await models.Application.execute(
|
||||||
|
ctx,
|
||||||
|
'PROCEDURE',
|
||||||
|
'CALL vn.myProcedure',
|
||||||
|
[1],
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.length).toEqual(2);
|
||||||
|
expect(response[0].myParam).toEqual(1);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Application executeProc()', () => {
|
||||||
|
it('should execute procedure and get data (executeProc)', async() => {
|
||||||
|
const ctx = getCtx(userWithPrivileges);
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const response = await models.Application.executeProc(
|
||||||
|
ctx,
|
||||||
|
'myProcedure',
|
||||||
|
'vn',
|
||||||
|
[1],
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.length).toEqual(2);
|
||||||
|
expect(response[0].myParam).toEqual(1);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Application executeFunc()', () => {
|
||||||
|
it('should execute function and get data', async() => {
|
||||||
|
const ctx = getCtx(userWithPrivileges);
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const response = await models.Application.executeFunc(
|
||||||
|
ctx,
|
||||||
|
'myFunction',
|
||||||
|
'bs',
|
||||||
|
[1],
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response).toEqual(1);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should execute function and get data with user with inherited privileges', async() => {
|
||||||
|
const ctx = getCtx(userWithInheritedPrivileges);
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const response = await models.Application.executeFunc(
|
||||||
|
ctx,
|
||||||
|
'myFunction',
|
||||||
|
'bs',
|
||||||
|
[1],
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response).toEqual(1);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,4 +2,7 @@
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
require('../methods/application/status')(Self);
|
require('../methods/application/status')(Self);
|
||||||
require('../methods/application/post')(Self);
|
require('../methods/application/post')(Self);
|
||||||
|
require('../methods/application/execute')(Self);
|
||||||
|
require('../methods/application/executeProc')(Self);
|
||||||
|
require('../methods/application/executeFunc')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"name": "ProcsPriv",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "mysql.procs_priv"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"id": 1,
|
||||||
|
"type": "string",
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "Routine_name"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"id": 3,
|
||||||
|
"type": "string",
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "Db"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "user"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"id": 2,
|
||||||
|
"type": "string",
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "Routine_type"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"host": {
|
||||||
|
"type": "string",
|
||||||
|
"mysql": {
|
||||||
|
"columnName": "Host"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
"Agency cannot be blank": "Agency cannot be blank",
|
"Agency cannot be blank": "Agency cannot be blank",
|
||||||
"The IBAN does not have the correct format": "The IBAN does not have the correct format",
|
"The IBAN does not have the correct format": "The IBAN does not have the correct format",
|
||||||
"You can't make changes on the basic data of an confirmed order or with rows": "You can't make changes on the basic data of an confirmed order or with rows",
|
"You can't make changes on the basic data of an confirmed order or with rows": "You can't make changes on the basic data of an confirmed order or with rows",
|
||||||
"You can't create a ticket for a inactive client": "You can't create a ticket for a inactive client",
|
"You can't create a ticket for an inactive client": "You can't create a ticket for an inactive client",
|
||||||
"Worker cannot be blank": "Worker cannot be blank",
|
"Worker cannot be blank": "Worker cannot be blank",
|
||||||
"You must delete the claim id %d first": "You must delete the claim id %d first",
|
"You must delete the claim id %d first": "You must delete the claim id %d first",
|
||||||
"You don't have enough privileges": "You don't have enough privileges",
|
"You don't have enough privileges": "You don't have enough privileges",
|
||||||
|
@ -188,7 +188,14 @@
|
||||||
"The ticket doesn't exist.": "The ticket doesn't exist.",
|
"The ticket doesn't exist.": "The ticket doesn't exist.",
|
||||||
"The sales do not exists": "The sales do not exists",
|
"The sales do not exists": "The sales do not exists",
|
||||||
"Ticket without Route": "Ticket without route",
|
"Ticket without Route": "Ticket without route",
|
||||||
|
"Select a different client": "Select a different client",
|
||||||
|
"Fill all the fields": "Fill all the fields",
|
||||||
|
"Error while generating PDF": "Error while generating PDF",
|
||||||
|
"Can't invoice to future": "Can't invoice to future",
|
||||||
|
"This ticket is already invoiced": "This ticket is already invoiced",
|
||||||
|
"Negative basis of tickets: 23": "Negative basis of tickets: 23",
|
||||||
"Booking completed": "Booking complete",
|
"Booking completed": "Booking complete",
|
||||||
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation",
|
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation",
|
||||||
"You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets"
|
"You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets",
|
||||||
|
"Try again": "Try again"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
|
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
|
||||||
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
|
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
|
||||||
"Can't be blank": "No puede estar en blanco",
|
"Can't be blank": "No puede estar en blanco",
|
||||||
"Invalid TIN": "NIF/CIF invalido",
|
"Invalid TIN": "NIF/CIF inválido",
|
||||||
"TIN must be unique": "El NIF/CIF debe ser único",
|
"TIN must be unique": "El NIF/CIF debe ser único",
|
||||||
"A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web",
|
"A client with that Web User name already exists": "Ya existe un cliente con ese Usuario Web",
|
||||||
"Is invalid": "Is invalid",
|
"Is invalid": "Es inválido",
|
||||||
"Quantity cannot be zero": "La cantidad no puede ser cero",
|
"Quantity cannot be zero": "La cantidad no puede ser cero",
|
||||||
"Enter an integer different to zero": "Introduce un entero distinto de cero",
|
"Enter an integer different to zero": "Introduce un entero distinto de cero",
|
||||||
"Package cannot be blank": "El embalaje no puede estar en blanco",
|
"Package cannot be blank": "El embalaje no puede estar en blanco",
|
||||||
|
@ -55,17 +55,17 @@
|
||||||
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
|
"You must delete the claim id %d first": "Antes debes borrar la reclamación %d",
|
||||||
"You don't have enough privileges": "No tienes suficientes permisos",
|
"You don't have enough privileges": "No tienes suficientes permisos",
|
||||||
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
|
"Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF",
|
||||||
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos basicos de una orden con artículos",
|
"You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos básicos de una orden con artículos",
|
||||||
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no esta permitido el uso de la letra ñ",
|
"INVALID_USER_NAME": "El nombre de usuario solo debe contener letras minúsculas o, a partir del segundo carácter, números o subguiones, no está permitido el uso de la letra ñ",
|
||||||
"You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado",
|
"You can't create a ticket for a frozen client": "No puedes crear un ticket para un cliente congelado",
|
||||||
"You can't create a ticket for a inactive client": "No puedes crear un ticket para un cliente inactivo",
|
"You can't create a ticket for an inactive client": "No puedes crear un ticket para un cliente inactivo",
|
||||||
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
|
"Tag value cannot be blank": "El valor del tag no puede quedar en blanco",
|
||||||
"ORDER_EMPTY": "Cesta vacía",
|
"ORDER_EMPTY": "Cesta vacía",
|
||||||
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
|
"You don't have enough privileges to do that": "No tienes permisos para cambiar esto",
|
||||||
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
|
"NO SE PUEDE DESACTIVAR EL CONSIGNAT": "NO SE PUEDE DESACTIVAR EL CONSIGNAT",
|
||||||
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
|
"Error. El NIF/CIF está repetido": "Error. El NIF/CIF está repetido",
|
||||||
"Street cannot be empty": "Dirección no puede estar en blanco",
|
"Street cannot be empty": "Dirección no puede estar en blanco",
|
||||||
"City cannot be empty": "Cuidad no puede estar en blanco",
|
"City cannot be empty": "Ciudad no puede estar en blanco",
|
||||||
"Code cannot be blank": "Código no puede estar en blanco",
|
"Code cannot be blank": "Código no puede estar en blanco",
|
||||||
"You cannot remove this department": "No puedes eliminar este departamento",
|
"You cannot remove this department": "No puedes eliminar este departamento",
|
||||||
"The extension must be unique": "La extensión debe ser unica",
|
"The extension must be unique": "La extensión debe ser unica",
|
||||||
|
@ -102,8 +102,8 @@
|
||||||
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
|
"You can't delete a confirmed order": "No puedes borrar un pedido confirmado",
|
||||||
"The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
|
"The social name has an invalid format": "El nombre fiscal tiene un formato incorrecto",
|
||||||
"Invalid quantity": "Cantidad invalida",
|
"Invalid quantity": "Cantidad invalida",
|
||||||
"This postal code is not valid": "This postal code is not valid",
|
"This postal code is not valid": "Este código postal no es válido",
|
||||||
"is invalid": "is invalid",
|
"is invalid": "es inválido",
|
||||||
"The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto",
|
"The postcode doesn't exist. Please enter a correct one": "El código postal no existe. Por favor, introduce uno correcto",
|
||||||
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
|
"The department name can't be repeated": "El nombre del departamento no puede repetirse",
|
||||||
"This phone already exists": "Este teléfono ya existe",
|
"This phone already exists": "Este teléfono ya existe",
|
||||||
|
@ -112,8 +112,8 @@
|
||||||
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
|
"You cannot delete a ticket that part of it is being prepared": "No puedes eliminar un ticket en el que una parte que está siendo preparada",
|
||||||
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
|
"You must delete all the buy requests first": "Debes eliminar todas las peticiones de compra primero",
|
||||||
"You should specify a date": "Debes especificar una fecha",
|
"You should specify a date": "Debes especificar una fecha",
|
||||||
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fín",
|
"You should specify at least a start or end date": "Debes especificar al menos una fecha de inicio o de fin",
|
||||||
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fín",
|
"Start date should be lower than end date": "La fecha de inicio debe ser menor que la fecha de fin",
|
||||||
"You should mark at least one week day": "Debes marcar al menos un día de la semana",
|
"You should mark at least one week day": "Debes marcar al menos un día de la semana",
|
||||||
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
|
"Swift / BIC can't be empty": "Swift / BIC no puede estar vacío",
|
||||||
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
|
"Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios",
|
||||||
|
@ -144,15 +144,15 @@
|
||||||
"Unable to clone this travel": "No ha sido posible clonar este travel",
|
"Unable to clone this travel": "No ha sido posible clonar este travel",
|
||||||
"This thermograph id already exists": "La id del termógrafo ya existe",
|
"This thermograph id already exists": "La id del termógrafo ya existe",
|
||||||
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
|
"Choose a date range or days forward": "Selecciona un rango de fechas o días en adelante",
|
||||||
"ORDER_ALREADY_CONFIRMED": "ORDER_ALREADY_CONFIRMED",
|
"ORDER_ALREADY_CONFIRMED": "ORDEN YA CONFIRMADA",
|
||||||
"Invalid password": "Invalid password",
|
"Invalid password": "Invalid password",
|
||||||
"Password does not meet requirements": "La contraseña no cumple los requisitos",
|
"Password does not meet requirements": "La contraseña no cumple los requisitos",
|
||||||
"Role already assigned": "Role already assigned",
|
"Role already assigned": "Rol ya asignado",
|
||||||
"Invalid role name": "Invalid role name",
|
"Invalid role name": "Nombre de rol no válido",
|
||||||
"Role name must be written in camelCase": "Role name must be written in camelCase",
|
"Role name must be written in camelCase": "El nombre del rol debe escribirse en camelCase",
|
||||||
"Email already exists": "Email already exists",
|
"Email already exists": "El correo ya existe",
|
||||||
"User already exists": "User already exists",
|
"User already exists": "El/La usuario/a ya existe",
|
||||||
"Absence change notification on the labour calendar": "Notificacion de cambio de ausencia en el calendario laboral",
|
"Absence change notification on the labour calendar": "Notificación de cambio de ausencia en el calendario laboral",
|
||||||
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
|
"Record of hours week": "Registro de horas semana {{week}} año {{year}} ",
|
||||||
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
|
"Created absence": "El empleado <strong>{{author}}</strong> ha añadido una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> para el día {{dated}}.",
|
||||||
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
|
"Deleted absence": "El empleado <strong>{{author}}</strong> ha eliminado una ausencia de tipo '{{absenceType}}' a <a href='{{{workerUrl}}}'><strong>{{employee}}</strong></a> del día {{dated}}.",
|
||||||
|
@ -224,7 +224,7 @@
|
||||||
"date in the future": "Fecha en el futuro",
|
"date in the future": "Fecha en el futuro",
|
||||||
"reference duplicated": "Referencia duplicada",
|
"reference duplicated": "Referencia duplicada",
|
||||||
"This ticket is already a refund": "Este ticket ya es un abono",
|
"This ticket is already a refund": "Este ticket ya es un abono",
|
||||||
"isWithoutNegatives": "isWithoutNegatives",
|
"isWithoutNegatives": "Sin negativos",
|
||||||
"routeFk": "routeFk",
|
"routeFk": "routeFk",
|
||||||
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
|
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
|
||||||
"No hay un contrato en vigor": "No hay un contrato en vigor",
|
"No hay un contrato en vigor": "No hay un contrato en vigor",
|
||||||
|
@ -317,10 +317,15 @@
|
||||||
"The ticket doesn't exist.": "No existe el ticket.",
|
"The ticket doesn't exist.": "No existe el ticket.",
|
||||||
"Social name should be uppercase": "La razón social debe ir en mayúscula",
|
"Social name should be uppercase": "La razón social debe ir en mayúscula",
|
||||||
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula",
|
"Street should be uppercase": "La dirección fiscal debe ir en mayúscula",
|
||||||
"The response is not a PDF": "La respuesta no es un PDF",
|
|
||||||
"Ticket without Route": "Ticket sin ruta",
|
"Ticket without Route": "Ticket sin ruta",
|
||||||
|
"Select a different client": "Seleccione un cliente distinto",
|
||||||
|
"Fill all the fields": "Rellene todos los campos",
|
||||||
|
"The response is not a PDF": "La respuesta no es un PDF",
|
||||||
"Booking completed": "Reserva completada",
|
"Booking completed": "Reserva completada",
|
||||||
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
|
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
|
||||||
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina",
|
"Incoterms data for consignee is missing": "Faltan los datos de los Incoterms para el consignatario",
|
||||||
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina"
|
"The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada",
|
||||||
|
"User disabled": "Usuario desactivado",
|
||||||
|
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima",
|
||||||
|
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima"
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,5 +49,13 @@
|
||||||
},
|
},
|
||||||
"Container": {
|
"Container": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"ProcsPriv": {
|
||||||
|
"dataSource": "vn",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "mysql.procs_priv"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethod('sync', {
|
Self.remoteMethod('sync', {
|
||||||
|
@ -32,9 +33,13 @@ module.exports = Self => {
|
||||||
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const user = await models.VnUser.findOne({
|
const user = await models.VnUser.findOne({
|
||||||
fields: ['id'],
|
fields: ['id', 'password'],
|
||||||
where: {name: userName}
|
where: {name: userName}
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
|
if (user && password && !await user.hasPassword(password))
|
||||||
|
throw new ForbiddenError('Wrong password');
|
||||||
|
|
||||||
const isSync = !await models.UserSync.exists(userName, myOptions);
|
const isSync = !await models.UserSync.exists(userName, myOptions);
|
||||||
|
|
||||||
if (!force && isSync && user) return;
|
if (!force && isSync && user) return;
|
||||||
|
@ -42,4 +47,3 @@ module.exports = Self => {
|
||||||
await models.UserSync.destroyById(userName, myOptions);
|
await models.UserSync.destroyById(userName, myOptions);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,9 @@
|
||||||
"SambaConfig": {
|
"SambaConfig": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"SignInLog": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"Sip": {
|
"Sip": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@ const crypto = require('crypto');
|
||||||
const nthash = require('smbhash').nthash;
|
const nthash = require('smbhash').nthash;
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
const shouldSync = process.env.NODE_ENV === 'production';
|
const shouldSync = process.env.NODE_ENV !== 'test';
|
||||||
|
|
||||||
Self.getSynchronizer = async function() {
|
Self.getSynchronizer = async function() {
|
||||||
return await Self.findOne({
|
return await Self.findOne({
|
||||||
|
@ -140,6 +140,7 @@ module.exports = Self => {
|
||||||
try {
|
try {
|
||||||
if (shouldSync)
|
if (shouldSync)
|
||||||
await client.del(dn);
|
await client.del(dn);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log(` -> User '${userName}' removed from LDAP`);
|
console.log(` -> User '${userName}' removed from LDAP`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name !== 'NoSuchObjectError') throw e;
|
if (e.name !== 'NoSuchObjectError') throw e;
|
||||||
|
|
|
@ -27,8 +27,7 @@ module.exports = Self => {
|
||||||
const [row] = await Self.rawSql(
|
const [row] = await Self.rawSql(
|
||||||
`SELECT COUNT(*) AS nRows
|
`SELECT COUNT(*) AS nRows
|
||||||
FROM mysql.user
|
FROM mysql.user
|
||||||
WHERE User = ?
|
WHERE User = ? AND Host = ?`,
|
||||||
AND Host = ?`,
|
|
||||||
[mysqlUser, this.userHost]
|
[mysqlUser, this.userHost]
|
||||||
);
|
);
|
||||||
let userExists = row.nRows > 0;
|
let userExists = row.nRows > 0;
|
||||||
|
@ -38,8 +37,7 @@ module.exports = Self => {
|
||||||
const [row] = await Self.rawSql(
|
const [row] = await Self.rawSql(
|
||||||
`SELECT Priv AS priv
|
`SELECT Priv AS priv
|
||||||
FROM mysql.global_priv
|
FROM mysql.global_priv
|
||||||
WHERE User = ?
|
WHERE User = ? AND Host = ?`,
|
||||||
AND Host = ?`,
|
|
||||||
[mysqlUser, this.userHost]
|
[mysqlUser, this.userHost]
|
||||||
);
|
);
|
||||||
const priv = row && JSON.parse(row.priv);
|
const priv = row && JSON.parse(row.priv);
|
||||||
|
@ -88,10 +86,18 @@ module.exports = Self => {
|
||||||
else
|
else
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
await Self.rawSql('GRANT ? TO ?@?',
|
|
||||||
[role, mysqlUser, this.userHost]);
|
|
||||||
|
|
||||||
if (role) {
|
const [row] = await Self.rawSql(
|
||||||
|
`SELECT COUNT(*) AS nRows
|
||||||
|
FROM mysql.user
|
||||||
|
WHERE User = ? AND Host = ''`,
|
||||||
|
[role]
|
||||||
|
);
|
||||||
|
const roleExists = row.nRows > 0;
|
||||||
|
|
||||||
|
if (roleExists) {
|
||||||
|
await Self.rawSql('GRANT ? TO ?@?',
|
||||||
|
[role, mysqlUser, this.userHost]);
|
||||||
await Self.rawSql('SET DEFAULT ROLE ? FOR ?@?',
|
await Self.rawSql('SET DEFAULT ROLE ? FOR ?@?',
|
||||||
[role, mysqlUser, this.userHost]);
|
[role, mysqlUser, this.userHost]);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
const ldap = require('../util/ldapjs-extra');
|
const ldap = require('../util/ldapjs-extra');
|
||||||
const ssh = require('node-ssh');
|
const execFile = require('child_process').execFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summary of userAccountControl flags:
|
* Summary of userAccountControl flags:
|
||||||
|
@ -11,6 +11,8 @@ const UserAccountControlFlags = {
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
|
const shouldSync = process.env.NODE_ENV !== 'test';
|
||||||
|
|
||||||
Self.getSynchronizer = async function() {
|
Self.getSynchronizer = async function() {
|
||||||
return await Self.findOne({
|
return await Self.findOne({
|
||||||
fields: [
|
fields: [
|
||||||
|
@ -19,6 +21,7 @@ module.exports = Self => {
|
||||||
'adController',
|
'adController',
|
||||||
'adUser',
|
'adUser',
|
||||||
'adPassword',
|
'adPassword',
|
||||||
|
'userDn',
|
||||||
'verifyCert'
|
'verifyCert'
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -26,88 +29,123 @@ module.exports = Self => {
|
||||||
|
|
||||||
Object.assign(Self.prototype, {
|
Object.assign(Self.prototype, {
|
||||||
async init() {
|
async init() {
|
||||||
let sshClient = new ssh.NodeSSH();
|
const baseDn = this.adDomain
|
||||||
await sshClient.connect({
|
.split('.')
|
||||||
host: this.adController,
|
.map(part => `dc=${part}`)
|
||||||
username: this.adUser,
|
.join(',');
|
||||||
password: this.adPassword
|
const bindDn = `cn=${this.adUser},cn=Users,${baseDn}`;
|
||||||
});
|
|
||||||
|
|
||||||
let adUser = `cn=${this.adUser},${this.usersDn()}`;
|
const adClient = ldap.createClient({
|
||||||
|
|
||||||
let adClient = ldap.createClient({
|
|
||||||
url: `ldaps://${this.adController}:636`,
|
url: `ldaps://${this.adController}:636`,
|
||||||
tlsOptions: {rejectUnauthorized: this.verifyCert}
|
tlsOptions: {rejectUnauthorized: this.verifyCert}
|
||||||
});
|
});
|
||||||
await adClient.bind(adUser, this.adPassword);
|
await adClient.bind(bindDn, this.adPassword);
|
||||||
|
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
sshClient,
|
adClient,
|
||||||
adClient
|
fullUsersDn: `${this.userDn},${baseDn}`,
|
||||||
|
bindDn
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async deinit() {
|
async deinit() {
|
||||||
await this.sshClient.dispose();
|
|
||||||
await this.adClient.unbind();
|
await this.adClient.unbind();
|
||||||
},
|
},
|
||||||
|
|
||||||
usersDn() {
|
async sambaTool(command, args = []) {
|
||||||
let dnBase = this.adDomain
|
let authArgs = [
|
||||||
.split('.')
|
'--URL', `ldaps://${this.adController}`,
|
||||||
.map(part => `dc=${part}`)
|
'--simple-bind-dn', this.bindDn,
|
||||||
.join(',');
|
'--password', this.adPassword
|
||||||
return `cn=Users,${dnBase}`;
|
];
|
||||||
|
if (!this.verifyCert)
|
||||||
|
authArgs.push('--option', 'tls verify peer = no_check');
|
||||||
|
|
||||||
|
const allArgs = [command].concat(
|
||||||
|
args, authArgs
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!shouldSync) return;
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
execFile('samba-tool', allArgs, (err, stdout, stderr) => {
|
||||||
|
if (err)
|
||||||
|
reject(err);
|
||||||
|
else
|
||||||
|
resolve({stdout, stderr});
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async syncUser(userName, info, password) {
|
async getAdUser(userName) {
|
||||||
let {sshClient} = this;
|
const sambaUser = await this.adClient.searchOne(this.fullUsersDn, {
|
||||||
|
|
||||||
let sambaUser = await this.adClient.searchOne(this.usersDn(), {
|
|
||||||
scope: 'sub',
|
scope: 'sub',
|
||||||
attributes: ['userAccountControl'],
|
attributes: [
|
||||||
|
'dn',
|
||||||
|
'userAccountControl',
|
||||||
|
'uidNumber',
|
||||||
|
'accountExpires',
|
||||||
|
'mail'
|
||||||
|
],
|
||||||
filter: `(&(objectClass=user)(sAMAccountName=${userName}))`
|
filter: `(&(objectClass=user)(sAMAccountName=${userName}))`
|
||||||
});
|
});
|
||||||
let isEnabled = sambaUser
|
if (sambaUser) {
|
||||||
&& !(sambaUser.userAccountControl & UserAccountControlFlags.ACCOUNTDISABLE);
|
for (const intProp of ['uidNumber', 'userAccountControl']) {
|
||||||
|
if (sambaUser[intProp] != null)
|
||||||
if (process.env.NODE_ENV === 'test')
|
sambaUser[intProp] = parseInt(sambaUser[intProp]);
|
||||||
return;
|
}
|
||||||
|
}
|
||||||
|
return sambaUser;
|
||||||
|
},
|
||||||
|
|
||||||
|
async syncUser(userName, info, password) {
|
||||||
|
let sambaUser = await this.getAdUser(userName);
|
||||||
|
let entry;
|
||||||
|
|
||||||
if (info.hasAccount) {
|
if (info.hasAccount) {
|
||||||
if (!sambaUser) {
|
if (!sambaUser) {
|
||||||
await sshClient.exec('samba-tool user create', [
|
await this.sambaTool('user', [
|
||||||
userName,
|
'create', userName,
|
||||||
'--uid-number', `${info.uidNumber}`,
|
'--userou', this.userDn,
|
||||||
'--mail-address', info.corporateMail,
|
|
||||||
'--random-password'
|
'--random-password'
|
||||||
]);
|
]);
|
||||||
await sshClient.exec('samba-tool user setexpiry', [
|
sambaUser = await this.getAdUser(userName);
|
||||||
userName,
|
|
||||||
'--noexpiry'
|
|
||||||
]);
|
|
||||||
await sshClient.exec('mkhomedir_helper', [
|
|
||||||
userName,
|
|
||||||
'0027'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (!isEnabled) {
|
|
||||||
await sshClient.exec('samba-tool user enable', [
|
|
||||||
userName
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
if (password) {
|
if (password) {
|
||||||
await sshClient.exec('samba-tool user setpassword', [
|
await this.sambaTool('user', [
|
||||||
userName,
|
'setpassword', userName,
|
||||||
'--newpassword', password
|
'--newpassword', password
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else if (isEnabled) {
|
|
||||||
await sshClient.exec('samba-tool user disable', [
|
entry = {
|
||||||
userName
|
userAccountControl: sambaUser.userAccountControl
|
||||||
]);
|
& ~UserAccountControlFlags.ACCOUNTDISABLE,
|
||||||
|
uidNumber: info.uidNumber,
|
||||||
|
accountExpires: 0,
|
||||||
|
mail: info.corporateMail
|
||||||
|
};
|
||||||
|
} else if (sambaUser) {
|
||||||
|
entry = {
|
||||||
|
userAccountControl: sambaUser.userAccountControl
|
||||||
|
| UserAccountControlFlags.ACCOUNTDISABLE
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log(` -> User '${userName}' disabled on Samba`);
|
console.log(` -> User '${userName}' disabled on Samba`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sambaUser && entry) {
|
||||||
|
const changes = [];
|
||||||
|
for (const prop in entry) {
|
||||||
|
if (sambaUser[prop] == entry[prop]) continue;
|
||||||
|
changes.push(new ldap.Change({
|
||||||
|
operation: 'replace',
|
||||||
|
modification: {
|
||||||
|
[prop]: entry[prop]
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (changes.length && shouldSync)
|
||||||
|
await this.adClient.modify(sambaUser.dn, changes);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,14 +155,15 @@ module.exports = Self => {
|
||||||
*/
|
*/
|
||||||
async getUsers(usersToSync) {
|
async getUsers(usersToSync) {
|
||||||
const LDAP_MATCHING_RULE_BIT_AND = '1.2.840.113556.1.4.803';
|
const LDAP_MATCHING_RULE_BIT_AND = '1.2.840.113556.1.4.803';
|
||||||
let filter = `!(userAccountControl:${LDAP_MATCHING_RULE_BIT_AND}:=${UserAccountControlFlags.ACCOUNTDISABLE})`;
|
const filter = `!(userAccountControl:${LDAP_MATCHING_RULE_BIT_AND}`
|
||||||
|
+ `:=${UserAccountControlFlags.ACCOUNTDISABLE})`;
|
||||||
|
|
||||||
let opts = {
|
const opts = {
|
||||||
scope: 'sub',
|
scope: 'sub',
|
||||||
attributes: ['sAMAccountName'],
|
attributes: ['sAMAccountName'],
|
||||||
filter: `(&(objectClass=user)(${filter}))`
|
filter: `(&(objectClass=user)(${filter}))`
|
||||||
};
|
};
|
||||||
await this.adClient.searchForeach(this.usersDn(), opts,
|
await this.adClient.searchForeach(this.fullUsersDn, opts,
|
||||||
o => usersToSync.add(o.sAMAccountName));
|
o => usersToSync.add(o.sAMAccountName));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,6 +28,10 @@
|
||||||
"adPassword": {
|
"adPassword": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"userDn": {
|
||||||
|
"type": "string",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
"verifyCert": {
|
"verifyCert": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"name": "SignInLog",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "account.signInLog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"required": true,
|
||||||
|
"type": "string",
|
||||||
|
"description": "Token's user"
|
||||||
|
},
|
||||||
|
"creationDate": {
|
||||||
|
"type": "date"
|
||||||
|
},
|
||||||
|
"userFk": {
|
||||||
|
"required": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"ip": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"user": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "VnUser",
|
||||||
|
"foreignKey": "userFk"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"order": ["creationDate DESC", "id DESC"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,40 +12,40 @@
|
||||||
<vn-card class="vn-pa-lg" vn-focus>
|
<vn-card class="vn-pa-lg" vn-focus>
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Homedir base"
|
label="Homedir base"
|
||||||
ng-model="$ctrl.config.homedir"
|
ng-model="$ctrl.config.homedir"
|
||||||
rule="AccountConfig">
|
rule="AccountConfig">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Shell"
|
label="Shell"
|
||||||
ng-model="$ctrl.config.shell"
|
ng-model="$ctrl.config.shell"
|
||||||
rule="AccountConfig">
|
rule="AccountConfig">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
label="User and role base id"
|
label="User and role base id"
|
||||||
ng-model="$ctrl.config.idBase"
|
ng-model="$ctrl.config.idBase"
|
||||||
rule="AccountConfig">
|
rule="AccountConfig">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
label="Min"
|
label="Min"
|
||||||
ng-model="$ctrl.config.min"
|
ng-model="$ctrl.config.min"
|
||||||
rule="AccountConfig">
|
rule="AccountConfig">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
label="Max"
|
label="Max"
|
||||||
ng-model="$ctrl.config.max"
|
ng-model="$ctrl.config.max"
|
||||||
rule="AccountConfig">
|
rule="AccountConfig">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
label="Warn"
|
label="Warn"
|
||||||
ng-model="$ctrl.config.warn"
|
ng-model="$ctrl.config.warn"
|
||||||
rule="AccountConfig">
|
rule="AccountConfig">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
<vn-input-number
|
<vn-input-number
|
||||||
label="Inact"
|
label="Inact"
|
||||||
ng-model="$ctrl.config.inact"
|
ng-model="$ctrl.config.inact"
|
||||||
rule="AccountConfig">
|
rule="AccountConfig">
|
||||||
</vn-input-number>
|
</vn-input-number>
|
||||||
|
@ -61,10 +61,6 @@
|
||||||
label="Synchronize all"
|
label="Synchronize all"
|
||||||
ng-click="$ctrl.onSynchronizeAll()">
|
ng-click="$ctrl.onSynchronizeAll()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-button
|
|
||||||
label="Synchronize user"
|
|
||||||
ng-click="syncUser.show()">
|
|
||||||
</vn-button>
|
|
||||||
<vn-button
|
<vn-button
|
||||||
label="Synchronize roles"
|
label="Synchronize roles"
|
||||||
ng-click="$ctrl.onSynchronizeRoles()">
|
ng-click="$ctrl.onSynchronizeRoles()">
|
||||||
|
@ -77,25 +73,3 @@
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</form>
|
||||||
<vn-dialog
|
|
||||||
vn-id="syncUser"
|
|
||||||
on-accept="$ctrl.onUserSync()"
|
|
||||||
on-close="$ctrl.onSyncClose()">
|
|
||||||
<tpl-body>
|
|
||||||
<vn-textfield
|
|
||||||
label="Username"
|
|
||||||
ng-model="$ctrl.syncUser"
|
|
||||||
vn-focus>
|
|
||||||
</vn-textfield>
|
|
||||||
<vn-textfield
|
|
||||||
label="Password"
|
|
||||||
ng-model="$ctrl.syncPassword"
|
|
||||||
type="password"
|
|
||||||
info="If password is not specified, just user attributes are synchronized">
|
|
||||||
</vn-textfield>
|
|
||||||
</tpl-body>
|
|
||||||
<tpl-buttons>
|
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
|
||||||
<button response="accept" translate>Synchronize</button>
|
|
||||||
</tpl-buttons>
|
|
||||||
</vn-dialog>
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import Section from 'salix/components/section';
|
import Section from 'salix/components/section';
|
||||||
import UserError from 'core/lib/user-error';
|
|
||||||
|
|
||||||
export default class Controller extends Section {
|
export default class Controller extends Section {
|
||||||
onSynchronizeAll() {
|
onSynchronizeAll() {
|
||||||
|
@ -8,27 +7,10 @@ export default class Controller extends Section {
|
||||||
this.$http.patch(`Accounts/syncAll`);
|
this.$http.patch(`Accounts/syncAll`);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUserSync() {
|
|
||||||
if (!this.syncUser)
|
|
||||||
throw new UserError('Please enter the username');
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
password: this.syncPassword,
|
|
||||||
force: true
|
|
||||||
};
|
|
||||||
return this.$http.patch(`Accounts/${this.syncUser}/sync`, params)
|
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('User synchronized!')));
|
|
||||||
}
|
|
||||||
|
|
||||||
onSynchronizeRoles() {
|
onSynchronizeRoles() {
|
||||||
this.$http.patch(`RoleInherits/sync`)
|
this.$http.patch(`RoleInherits/sync`)
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Roles synchronized!')));
|
.then(() => this.vnApp.showSuccess(this.$t('Roles synchronized!')));
|
||||||
}
|
}
|
||||||
|
|
||||||
onSyncClose() {
|
|
||||||
this.syncUser = '';
|
|
||||||
this.syncPassword = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.component('vnAccountAccounts', {
|
ngModule.component('vnAccountAccounts', {
|
||||||
|
|
|
@ -3,7 +3,6 @@ Homedir base: Directorio base para carpetas de usuario
|
||||||
Shell: Intérprete de línea de comandos
|
Shell: Intérprete de línea de comandos
|
||||||
User and role base id: Id base usuarios y roles
|
User and role base id: Id base usuarios y roles
|
||||||
Synchronize all: Sincronizar todo
|
Synchronize all: Sincronizar todo
|
||||||
Synchronize user: Sincronizar usuario
|
|
||||||
Synchronize roles: Sincronizar roles
|
Synchronize roles: Sincronizar roles
|
||||||
If password is not specified, just user attributes are synchronized: >-
|
If password is not specified, just user attributes are synchronized: >-
|
||||||
Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario
|
Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario
|
||||||
|
@ -12,5 +11,4 @@ Users synchronized!: ¡Usuarios sincronizados!
|
||||||
Username: Nombre de usuario
|
Username: Nombre de usuario
|
||||||
Synchronize: Sincronizar
|
Synchronize: Sincronizar
|
||||||
Please enter the username: Por favor introduce el nombre de usuario
|
Please enter the username: Por favor introduce el nombre de usuario
|
||||||
User synchronized!: ¡Usuario sincronizado!
|
|
||||||
Roles synchronized!: ¡Roles sincronizados!
|
Roles synchronized!: ¡Roles sincronizados!
|
||||||
|
|
|
@ -67,6 +67,15 @@
|
||||||
translate>
|
translate>
|
||||||
Deactivate user
|
Deactivate user
|
||||||
</vn-item>
|
</vn-item>
|
||||||
|
<vn-item
|
||||||
|
ng-if="$ctrl.user.active"
|
||||||
|
ng-click="syncUser.show()"
|
||||||
|
name="synchronizeUser"
|
||||||
|
vn-acl="it"
|
||||||
|
vn-acl-action="remove"
|
||||||
|
translate>
|
||||||
|
Synchronize
|
||||||
|
</vn-item>
|
||||||
</slot-menu>
|
</slot-menu>
|
||||||
<slot-body>
|
<slot-body>
|
||||||
<div class="attributes">
|
<div class="attributes">
|
||||||
|
@ -153,6 +162,32 @@
|
||||||
<button response="accept" translate>Change password</button>
|
<button response="accept" translate>Change password</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="syncUser"
|
||||||
|
on-accept="$ctrl.onSync()"
|
||||||
|
on-close="$ctrl.onSyncClose()">
|
||||||
|
<tpl-title ng-translate>
|
||||||
|
Do you want to synchronize user?
|
||||||
|
</tpl-title>
|
||||||
|
<tpl-body>
|
||||||
|
<vn-check
|
||||||
|
label="Synchronize password"
|
||||||
|
ng-model="$ctrl.shouldSyncPassword"
|
||||||
|
info="If password is not specified, just user attributes are synchronized"
|
||||||
|
vn-focus>
|
||||||
|
</vn-check>
|
||||||
|
<vn-textfield
|
||||||
|
label="Password"
|
||||||
|
ng-model="$ctrl.syncPassword"
|
||||||
|
type="password"
|
||||||
|
ng-if="$ctrl.shouldSyncPassword">
|
||||||
|
</vn-textfield>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
|
<button response="accept" translate>Synchronize</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
||||||
<vn-popup vn-id="summary">
|
<vn-popup vn-id="summary">
|
||||||
<vn-user-summary user="$ctrl.user"></vn-user-summary>
|
<vn-user-summary user="$ctrl.user"></vn-user-summary>
|
||||||
</vn-popup>
|
</vn-popup>
|
||||||
|
|
|
@ -120,6 +120,20 @@ class Controller extends Descriptor {
|
||||||
this.vnApp.showSuccess(this.$t(message));
|
this.vnApp.showSuccess(this.$t(message));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSync() {
|
||||||
|
const params = {force: true};
|
||||||
|
if (this.shouldSyncPassword)
|
||||||
|
params.password = this.syncPassword;
|
||||||
|
|
||||||
|
return this.$http.patch(`Accounts/${this.user.name}/sync`, params)
|
||||||
|
.then(() => this.vnApp.showSuccess(this.$t('User synchronized!')));
|
||||||
|
}
|
||||||
|
|
||||||
|
onSyncClose() {
|
||||||
|
this.shouldSyncPassword = false;
|
||||||
|
this.syncPassword = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngModule.component('vnUserDescriptor', {
|
ngModule.component('vnUserDescriptor', {
|
||||||
|
|
|
@ -22,6 +22,10 @@ Old password: Contraseña antigua
|
||||||
New password: Nueva contraseña
|
New password: Nueva contraseña
|
||||||
Repeat password: Repetir contraseña
|
Repeat password: Repetir contraseña
|
||||||
Password changed succesfully!: ¡Contraseña modificada correctamente!
|
Password changed succesfully!: ¡Contraseña modificada correctamente!
|
||||||
|
Synchronize: Sincronizar
|
||||||
|
Do you want to synchronize user?: ¿Quieres sincronizar el usuario?
|
||||||
|
Synchronize password: Sincronizar contraseña
|
||||||
|
User synchronized!: ¡Usuario sincronizado!
|
||||||
Role changed succesfully!: ¡Rol modificado correctamente!
|
Role changed succesfully!: ¡Rol modificado correctamente!
|
||||||
Password requirements: >
|
Password requirements: >
|
||||||
La contraseña debe tener al menos {{ length }} caracteres de longitud,
|
La contraseña debe tener al menos {{ length }} caracteres de longitud,
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<vn-card class="vn-pa-lg" vn-focus>
|
<vn-card class="vn-pa-lg" vn-focus>
|
||||||
<vn-vertical>
|
<vn-vertical>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Enable synchronization"
|
label="Enable synchronization"
|
||||||
ng-model="watcher.hasData">
|
ng-model="watcher.hasData">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
|
@ -20,28 +20,33 @@
|
||||||
ng-if="watcher.hasData"
|
ng-if="watcher.hasData"
|
||||||
class="vn-mt-md">
|
class="vn-mt-md">
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="AD domain"
|
label="AD domain"
|
||||||
ng-model="$ctrl.config.adDomain"
|
ng-model="$ctrl.config.adDomain"
|
||||||
rule="SambaConfig">
|
rule="SambaConfig">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="Domain controller"
|
label="Domain controller"
|
||||||
ng-model="$ctrl.config.adController"
|
ng-model="$ctrl.config.adController"
|
||||||
rule="SambaConfig">
|
rule="SambaConfig">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="AD user"
|
label="AD user"
|
||||||
ng-model="$ctrl.config.adUser"
|
ng-model="$ctrl.config.adUser"
|
||||||
rule="SambaConfig">
|
rule="SambaConfig">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
<vn-textfield
|
<vn-textfield
|
||||||
label="AD password"
|
label="AD password"
|
||||||
ng-model="$ctrl.config.adPassword"
|
ng-model="$ctrl.config.adPassword"
|
||||||
type="password"
|
type="password"
|
||||||
rule="SambaConfig">
|
rule="SambaConfig">
|
||||||
</vn-textfield>
|
</vn-textfield>
|
||||||
|
<vn-textfield
|
||||||
|
label="User DN (without domain part)"
|
||||||
|
ng-model="$ctrl.config.userDn"
|
||||||
|
rule="SambaConfig">
|
||||||
|
</vn-textfield>
|
||||||
<vn-check
|
<vn-check
|
||||||
label="Verify certificate"
|
label="Verify certificate"
|
||||||
ng-model="$ctrl.config.verifyCert">
|
ng-model="$ctrl.config.verifyCert">
|
||||||
</vn-check>
|
</vn-check>
|
||||||
</vn-vertical>
|
</vn-vertical>
|
||||||
|
@ -63,4 +68,4 @@
|
||||||
ng-click="watcher.loadOriginalData()">
|
ng-click="watcher.loadOriginalData()">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-button-bar>
|
</vn-button-bar>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -3,6 +3,7 @@ Domain controller: Controlador de dominio
|
||||||
AD domain: Dominio AD
|
AD domain: Dominio AD
|
||||||
AD user: Usuario AD
|
AD user: Usuario AD
|
||||||
AD password: Contraseña AD
|
AD password: Contraseña AD
|
||||||
|
User DN (without domain part): DN usuarios (sin la parte del dominio)
|
||||||
Verify certificate: Verificar certificado
|
Verify certificate: Verificar certificado
|
||||||
Test connection: Probar conexión
|
Test connection: Probar conexión
|
||||||
Samba connection established!: ¡Conexión con Samba establecida!
|
Samba connection established!: ¡Conexión con Samba establecida!
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
"AddressObservation": {
|
"AddressObservation": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"AddressShortage": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"BankEntity": {
|
"BankEntity": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "AddressShortage",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "addressShortage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"addressFk": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"relations": {
|
||||||
|
"address": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "Address",
|
||||||
|
"foreignKey": "addressFk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,9 @@
|
||||||
},
|
},
|
||||||
"maxCreditRows": {
|
"maxCreditRows": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"defaultCredit": {
|
||||||
|
"type": "number"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -450,14 +450,14 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (lastCredit && lastCredit.amount == 0) {
|
if (lastCredit && lastCredit.amount == 0) {
|
||||||
const zeroCreditEditor =
|
const zeroCreditEditor =
|
||||||
await models.ACL.checkAccessAcl(accessToken, 'Client', 'zeroCreditEditor', 'WRITE');
|
await models.ACL.checkAccessAcl(accessToken, 'Client', 'zeroCreditEditor', 'WRITE');
|
||||||
const lastCreditIsNotEditable =
|
const lastCreditIsNotEditable =
|
||||||
await models.ACL.checkAccessAcl(
|
await models.ACL.checkAccessAcl(
|
||||||
{req: {accessToken: {userId: lastCredit.workerFk}}},
|
{req: {accessToken: {userId: lastCredit.workerFk}}},
|
||||||
'Client',
|
'Client',
|
||||||
'zeroCreditEditor',
|
'zeroCreditEditor',
|
||||||
'WRITE'
|
'WRITE'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (lastCreditIsNotEditable && !zeroCreditEditor)
|
if (lastCreditIsNotEditable && !zeroCreditEditor)
|
||||||
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
|
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
|
||||||
|
@ -483,12 +483,6 @@ module.exports = Self => {
|
||||||
if (userRequiredRoles <= 0)
|
if (userRequiredRoles <= 0)
|
||||||
throw new UserError(`You don't have enough privileges to set this credit amount`);
|
throw new UserError(`You don't have enough privileges to set this credit amount`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await models.ClientCredit.create({
|
|
||||||
amount: changes.credit,
|
|
||||||
clientFk: finalState.id,
|
|
||||||
workerFk: userId
|
|
||||||
}, ctx.options);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.changeCreditManagement = async function changeCreditManagement(ctx, finalState, changes) {
|
Self.changeCreditManagement = async function changeCreditManagement(ctx, finalState, changes) {
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "Account",
|
"model": "VnUser",
|
||||||
"foreignKey": "id"
|
"foreignKey": "id"
|
||||||
},
|
},
|
||||||
"payMethod": {
|
"payMethod": {
|
||||||
|
|
|
@ -62,13 +62,13 @@ describe('Client Model', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const ctx = {options};
|
const ctx = {options};
|
||||||
|
|
||||||
// Set credit to zero by a financialBoss
|
|
||||||
const financialBoss = await models.VnUser.findOne({
|
const financialBoss = await models.VnUser.findOne({
|
||||||
where: {name: 'financialBoss'}
|
where: {name: 'financialBoss'}
|
||||||
}, options);
|
}, options);
|
||||||
ctx.options.accessToken = {userId: financialBoss.id};
|
ctx.options.accessToken = {userId: financialBoss.id};
|
||||||
|
|
||||||
await models.Client.changeCredit(ctx, instance, {credit: 0});
|
const testClient = await models.Client.findById(instance.id, options);
|
||||||
|
await testClient.updateAttributes({credit: 0}, ctx.options);
|
||||||
|
|
||||||
const salesAssistant = await models.VnUser.findOne({
|
const salesAssistant = await models.VnUser.findOne({
|
||||||
where: {name: 'salesAssistant'}
|
where: {name: 'salesAssistant'}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('new', {
|
||||||
|
description: 'Creates a new invoiceIn due day',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: {
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'The invoiceIn id',
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: '/new',
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.new = async(ctx, id, options) => {
|
||||||
|
let tx;
|
||||||
|
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Self.rawSql(`CALL vn.invoiceInDueDay_calculate(?)`, [id], myOptions);
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,45 @@
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
|
describe('invoiceInDueDay new()', () => {
|
||||||
|
beforeAll(async() => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly create a new due day', async() => {
|
||||||
|
const userId = 9;
|
||||||
|
const invoiceInFk = 6;
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
|
||||||
|
accessToken: {userId: userId},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tx = await models.InvoiceIn.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await models.InvoiceInDueDay.destroyAll({
|
||||||
|
invoiceInFk
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
await models.InvoiceInDueDay.new(ctx, invoiceInFk, options);
|
||||||
|
|
||||||
|
const result = await models.InvoiceInDueDay.find({where: {invoiceInFk}}, options);
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/invoice-in-due-day/new')(Self);
|
||||||
|
};
|
|
@ -23,7 +23,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.makePdfAndNotify = async function(ctx, id, printerFk) {
|
Self.makePdfAndNotify = async function(ctx, id, printerFk, options) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
options = typeof options == 'object'
|
options = typeof options == 'object'
|
||||||
|
|
|
@ -3,7 +3,7 @@ const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('InvoiceOut refund()', () => {
|
describe('InvoiceOut refund()', () => {
|
||||||
const userId = 5;
|
const userId = 5;
|
||||||
const ctx = {req: {accessToken: userId}};
|
const ctx = {req: {accessToken: userId}, args: {}};
|
||||||
const withWarehouse = true;
|
const withWarehouse = true;
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: userId},
|
accessToken: {userId: userId},
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('InvoiceOut tranferInvoice()', () => {
|
||||||
|
const activeCtx = {
|
||||||
|
accessToken: {userId: 5},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const ctx = {req: activeCtx};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
|
||||||
|
active: activeCtx
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the id of the created issued invoice', async() => {
|
||||||
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const args = {
|
||||||
|
id: '1',
|
||||||
|
ref: 'T4444444',
|
||||||
|
newClientFk: 1,
|
||||||
|
cplusRectificationId: 1,
|
||||||
|
siiTypeInvoiceOutId: 1,
|
||||||
|
invoiceCorrectionTypeId: 1
|
||||||
|
};
|
||||||
|
ctx.args = args;
|
||||||
|
try {
|
||||||
|
const result = await models.InvoiceOut.transferInvoice(
|
||||||
|
ctx,
|
||||||
|
options);
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an UserError when it is the same client', async() => {
|
||||||
|
const tx = await models.InvoiceOut.beginTransaction({});
|
||||||
|
const options = {transaction: tx};
|
||||||
|
const args = {
|
||||||
|
id: '1',
|
||||||
|
ref: 'T1111111',
|
||||||
|
newClientFk: 1101,
|
||||||
|
cplusRectificationId: 1,
|
||||||
|
siiTypeInvoiceOutId: 1,
|
||||||
|
invoiceCorrectionTypeId: 1
|
||||||
|
};
|
||||||
|
ctx.args = args;
|
||||||
|
try {
|
||||||
|
await models.InvoiceOut.transferInvoice(
|
||||||
|
ctx,
|
||||||
|
options);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe(`Select a different client`);
|
||||||
|
await tx.rollback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,111 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('transferInvoice', {
|
||||||
|
description: 'Transfer an issued invoice to another client',
|
||||||
|
accessType: 'WRITE',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'id',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
description: 'Issued invoice id'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'ref',
|
||||||
|
type: 'string',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'newClientFk',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'cplusRectificationId',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'siiTypeInvoiceOutId',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arg: 'invoiceCorrectionTypeId',
|
||||||
|
type: 'number',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'boolean',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: '/transferInvoice',
|
||||||
|
verb: 'post'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.transferInvoice = async(ctx, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||||
|
const args = ctx.args;
|
||||||
|
let tx;
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const {clientFk} = await models.InvoiceOut.findById(args.id);
|
||||||
|
|
||||||
|
if (clientFk == args.newClientFk)
|
||||||
|
throw new UserError(`Select a different client`);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const filterRef = {where: {refFk: args.ref}};
|
||||||
|
const tickets = await models.Ticket.find(filterRef, myOptions);
|
||||||
|
const ticketsIds = tickets.map(ticket => ticket.id);
|
||||||
|
await models.Ticket.refund(ctx, ticketsIds, null, myOptions);
|
||||||
|
|
||||||
|
const filterTicket = {where: {ticketFk: {inq: ticketsIds}}};
|
||||||
|
|
||||||
|
const services = await models.TicketService.find(filterTicket, myOptions);
|
||||||
|
const servicesIds = services.map(service => service.id);
|
||||||
|
|
||||||
|
const sales = await models.Sale.find(filterTicket, myOptions);
|
||||||
|
const salesIds = sales.map(sale => sale.id);
|
||||||
|
|
||||||
|
const clonedTickets = await models.Sale.clone(ctx, salesIds, servicesIds, null, false, false, myOptions);
|
||||||
|
const clonedTicketIds = [];
|
||||||
|
|
||||||
|
for (const clonedTicket of clonedTickets) {
|
||||||
|
await clonedTicket.updateAttribute('clientFk', args.newClientFk, myOptions);
|
||||||
|
clonedTicketIds.push(clonedTicket.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoiceIds = await models.Ticket.invoiceTickets(ctx, clonedTicketIds, myOptions);
|
||||||
|
const [invoiceId] = invoiceIds;
|
||||||
|
|
||||||
|
await models.InvoiceCorrection.create({
|
||||||
|
correctingFk: invoiceId,
|
||||||
|
correctedFk: args.id,
|
||||||
|
cplusRectificationTypeFk: args.cplusRectificationId,
|
||||||
|
siiTypeInvoiceOutFk: args.siiTypeInvoiceOutId,
|
||||||
|
invoiceCorrectionTypeFk: args.invoiceCorrectionTypeId
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
if (tx) {
|
||||||
|
await tx.commit();
|
||||||
|
await models.InvoiceOut.makePdfAndNotify(ctx, invoiceId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoiceId;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -31,5 +31,17 @@
|
||||||
},
|
},
|
||||||
"ZipConfig": {
|
"ZipConfig": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"CplusRectificationType": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"InvoiceCorrectionType": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"InvoiceCorrection": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"SiiTypeInvoiceOut": {
|
||||||
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "CplusRectificationType",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "cplusRectificationType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "InvoiceCorrectionType",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "invoiceCorrectionType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "InvoiceCorrection",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "invoiceCorrection"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"correctingFk": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"correctedFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"cplusRectificationTypeFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"siiTypeInvoiceOutFk": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"invoiceCorrectionTypeFk": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ module.exports = Self => {
|
||||||
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
require('../methods/invoiceOut/getInvoiceDate')(Self);
|
||||||
require('../methods/invoiceOut/negativeBases')(Self);
|
require('../methods/invoiceOut/negativeBases')(Self);
|
||||||
require('../methods/invoiceOut/negativeBasesCsv')(Self);
|
require('../methods/invoiceOut/negativeBasesCsv')(Self);
|
||||||
|
require('../methods/invoiceOut/transferInvoice')(Self);
|
||||||
|
|
||||||
Self.filePath = async function(id, options) {
|
Self.filePath = async function(id, options) {
|
||||||
const fields = ['ref', 'issued'];
|
const fields = ['ref', 'issued'];
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "SiiTypeInvoiceOut",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "siiTypeInvoiceOut"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number",
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,19 @@
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="CplusRectificationTypes"
|
||||||
|
data="cplusRectificationTypes"
|
||||||
|
order="description">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="SiiTypeInvoiceOuts"
|
||||||
|
data="siiTypeInvoiceOut">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="InvoiceCorrectionTypes"
|
||||||
|
data="invoiceCorrectionTypes">
|
||||||
|
</vn-crud-model>
|
||||||
|
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
icon="more_vert"
|
icon="more_vert"
|
||||||
|
@ -5,6 +21,13 @@
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
<vn-menu vn-id="menu">
|
<vn-menu vn-id="menu">
|
||||||
<vn-list>
|
<vn-list>
|
||||||
|
<vn-item
|
||||||
|
vn-acl="administrative"
|
||||||
|
vn-acl-action="remove"
|
||||||
|
ng-click="transferInvoice.show()"
|
||||||
|
translate>
|
||||||
|
Transfer invoice to...
|
||||||
|
</vn-item>
|
||||||
<vn-item class="dropdown"
|
<vn-item class="dropdown"
|
||||||
vn-click-stop="showInvoiceMenu.show($event, 'left')"
|
vn-click-stop="showInvoiceMenu.show($event, 'left')"
|
||||||
name="showInvoicePdf"
|
name="showInvoicePdf"
|
||||||
|
@ -157,3 +180,71 @@
|
||||||
<button response="accept" translate>Confirm</button>
|
<button response="accept" translate>Confirm</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
|
|
||||||
|
<vn-dialog
|
||||||
|
vn-id="transferInvoice"
|
||||||
|
title="transferInvoice"
|
||||||
|
on-accept="$ctrl.transferInvoice()">
|
||||||
|
<tpl-body>
|
||||||
|
<section class="transferInvoice">
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
vn-id="client"
|
||||||
|
required="true"
|
||||||
|
url="Clients"
|
||||||
|
label="Client"
|
||||||
|
show-field="name"
|
||||||
|
value-field="id"
|
||||||
|
search-function="{or: [{id: $search}, {name: {like: '%'+ $search +'%'}}]}"
|
||||||
|
ng-model="$ctrl.invoiceOut.client.id"
|
||||||
|
initial-data="$ctrl.invoiceOut.client.id"
|
||||||
|
order="id">
|
||||||
|
<tpl-item>
|
||||||
|
#{{id}} - {{::name}}
|
||||||
|
</tpl-item>
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
vn-id="cplusRectificationType"
|
||||||
|
required="true"
|
||||||
|
data="cplusRectificationTypes"
|
||||||
|
show-field="description"
|
||||||
|
value-field="id"
|
||||||
|
ng-model="$ctrl.cplusRectificationType"
|
||||||
|
search-function="{or: [{id: $search}, {description: {like: '%'+ $search +'%'}}]}"
|
||||||
|
label="Cplus Type">
|
||||||
|
<tpl-item>
|
||||||
|
{{::description}}
|
||||||
|
</tpl-item>
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
<vn-horizontal>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
vn-id="cplusInvoiceType"
|
||||||
|
data="siiTypeInvoiceOut"
|
||||||
|
show-field="description"
|
||||||
|
value-field="id"
|
||||||
|
required="true"
|
||||||
|
ng-model="$ctrl.siiTypeInvoiceOut"
|
||||||
|
search-function="{or: [{id: $search}, {description: {like: '%'+ $search +'%'}}]}"
|
||||||
|
label="Class">
|
||||||
|
</vn-autocomplete>
|
||||||
|
<vn-autocomplete
|
||||||
|
vn-one
|
||||||
|
vn-id="invoiceCorrectionType"
|
||||||
|
data="invoiceCorrectionTypes"
|
||||||
|
ng-model="$ctrl.invoiceCorrectionType"
|
||||||
|
show-field="description"
|
||||||
|
value-field="id"
|
||||||
|
required="true"
|
||||||
|
label="Type">
|
||||||
|
</vn-autocomplete>
|
||||||
|
</vn-horizontal>
|
||||||
|
</section>
|
||||||
|
</tpl-body>
|
||||||
|
<tpl-buttons>
|
||||||
|
<button response="accept" translate>Transfer client</button>
|
||||||
|
</tpl-buttons>
|
||||||
|
</vn-dialog>
|
||||||
|
|
|
@ -125,6 +125,22 @@ class Controller extends Section {
|
||||||
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
this.$state.go('ticket.card.sale', {id: refundTicket.id});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transferInvoice() {
|
||||||
|
const params = {
|
||||||
|
id: this.invoiceOut.id,
|
||||||
|
ref: this.invoiceOut.ref,
|
||||||
|
newClientFk: this.invoiceOut.client.id,
|
||||||
|
cplusRectificationId: this.cplusRectificationType,
|
||||||
|
siiTypeInvoiceOutId: this.siiTypeInvoiceOut,
|
||||||
|
invoiceCorrectionTypeId: this.invoiceCorrectionType
|
||||||
|
};
|
||||||
|
this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => {
|
||||||
|
const invoiceId = res.data;
|
||||||
|
this.vnApp.showSuccess(this.$t('Invoice trasfered!'));
|
||||||
|
this.$state.go('invoiceOut.card.summary', {id: invoiceId});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
|
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
The following refund tickets have been created: "The following refund tickets have been created: {{ticketIds}}"
|
The following refund tickets have been created: "The following refund tickets have been created: {{ticketIds}}"
|
||||||
|
Transfer invoice to...: Transfer invoice to...
|
||||||
|
Cplus Type: Cplus Type
|
|
@ -21,3 +21,5 @@ The invoice PDF document has been regenerated: El documento PDF de la factura ha
|
||||||
The email can't be empty: El correo no puede estar vacío
|
The email can't be empty: El correo no puede estar vacío
|
||||||
The following refund tickets have been created: "Se han creado los siguientes tickets de abono: {{ticketIds}}"
|
The following refund tickets have been created: "Se han creado los siguientes tickets de abono: {{ticketIds}}"
|
||||||
Refund...: Abono...
|
Refund...: Abono...
|
||||||
|
Transfer invoice to...: Transferir factura a...
|
||||||
|
Cplus Type: Cplus Tipo
|
||||||
|
|
|
@ -21,4 +21,10 @@ vn-invoice-out-descriptor-menu {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 1000px) {
|
||||||
|
.transferInvoice {
|
||||||
|
min-width: $width-md;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ export default class Controller extends Section {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
|
|
||||||
this.vnReport = vnReport;
|
this.vnReport = vnReport;
|
||||||
const now = new Date();
|
const now = Date.vnNew();
|
||||||
const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||||
const lastDayOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
const lastDayOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||||
this.params = {
|
this.params = {
|
||||||
|
|
|
@ -20,8 +20,7 @@ module.exports = Self => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'addressFk',
|
arg: 'addressFk',
|
||||||
type: 'Number',
|
type: 'Any',
|
||||||
required: true,
|
|
||||||
description: 'The address id'
|
description: 'The address id'
|
||||||
}],
|
}],
|
||||||
http: {
|
http: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ItemShelving",
|
"name": "ItemShelving",
|
||||||
"base": "VnModel",
|
"base": "Loggable",
|
||||||
"options": {
|
"options": {
|
||||||
"mysql": {
|
"mysql": {
|
||||||
"table": "itemShelving"
|
"table": "itemShelving"
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
name: saleGroup
|
||||||
|
columns:
|
||||||
|
id: id
|
||||||
|
created: created
|
||||||
|
userFk: user
|
||||||
|
parkingFk: parking
|
||||||
|
sectorFk: sector
|
||||||
|
ticketFk: ticket
|
||||||
|
editorFk: editor
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
name: saleGroup
|
||||||
|
columns:
|
||||||
|
id: id
|
||||||
|
created: creado
|
||||||
|
userFk: usuario
|
||||||
|
parkingFk: parking
|
||||||
|
sectorFk: sector
|
||||||
|
ticketFk: ticket
|
||||||
|
editorFk: editor
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethod('addExpeditionState', {
|
Self.remoteMethodCtx('addExpeditionState', {
|
||||||
description: 'Update an expedition state',
|
description: 'Update an expedition state',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
|
@ -12,18 +12,15 @@ module.exports = Self => {
|
||||||
description: 'Array of objects containing expeditionFk and stateCode'
|
description: 'Array of objects containing expeditionFk and stateCode'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: {
|
|
||||||
type: 'boolean',
|
|
||||||
root: true
|
|
||||||
},
|
|
||||||
http: {
|
http: {
|
||||||
path: `/addExpeditionState`,
|
path: `/addExpeditionState`,
|
||||||
verb: 'post'
|
verb: 'post'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.addExpeditionState = async(expeditions, options) => {
|
Self.addExpeditionState = async(ctx, expeditions, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
let tx;
|
let tx;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
|
@ -51,6 +48,7 @@ module.exports = Self => {
|
||||||
await models.ExpeditionState.create({
|
await models.ExpeditionState.create({
|
||||||
expeditionFk: expedition.expeditionFk,
|
expeditionFk: expedition.expeditionFk,
|
||||||
typeFk,
|
typeFk,
|
||||||
|
userFk: userId,
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const models = require('vn-loopback/server/server').models;
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('expeditionState addExpeditionState()', () => {
|
describe('expeditionState addExpeditionState()', () => {
|
||||||
|
const ctx = {req: {accessToken: {userId: 9}}};
|
||||||
it('should update the expedition states', async() => {
|
it('should update the expedition states', async() => {
|
||||||
const tx = await models.ExpeditionState.beginTransaction({});
|
const tx = await models.ExpeditionState.beginTransaction({});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const payload = [
|
const payload = [
|
||||||
|
@ -13,7 +13,7 @@ describe('expeditionState addExpeditionState()', () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
await models.ExpeditionState.addExpeditionState(payload, options);
|
await models.ExpeditionState.addExpeditionState(ctx, payload, options);
|
||||||
|
|
||||||
const expeditionState = await models.ExpeditionState.findOne({
|
const expeditionState = await models.ExpeditionState.findOne({
|
||||||
where: {id: 5}
|
where: {id: 5}
|
||||||
|
@ -39,7 +39,7 @@ describe('expeditionState addExpeditionState()', () => {
|
||||||
stateCode: 'DUMMY'
|
stateCode: 'DUMMY'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
await models.ExpeditionState.addExpeditionState(payload, options);
|
await models.ExpeditionState.addExpeditionState(ctx, payload, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -62,7 +62,7 @@ describe('expeditionState addExpeditionState()', () => {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
await models.ExpeditionState.addExpeditionState(payload, options);
|
await models.ExpeditionState.addExpeditionState(ctx, payload, options);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.clone = async(ctx, salesIds, servicesIds, withWarehouse, group, negative, options) => {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
const newTickets = [];
|
||||||
|
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
if (!myOptions.transaction) {
|
||||||
|
tx = await Self.beginTransaction({});
|
||||||
|
myOptions.transaction = tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const salesFilter = {
|
||||||
|
where: {id: {inq: salesIds}},
|
||||||
|
include: {
|
||||||
|
relation: 'components',
|
||||||
|
scope: {
|
||||||
|
fields: ['saleFk', 'componentFk', 'value']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const sales = await models.Sale.find(salesFilter, myOptions);
|
||||||
|
let ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
|
||||||
|
|
||||||
|
const mappedTickets = new Map();
|
||||||
|
|
||||||
|
if (group) ticketsIds = [ticketsIds[0]];
|
||||||
|
|
||||||
|
for (let ticketId of ticketsIds) {
|
||||||
|
const newTicket = await createTicket(
|
||||||
|
ctx,
|
||||||
|
ticketId,
|
||||||
|
withWarehouse,
|
||||||
|
negative,
|
||||||
|
myOptions
|
||||||
|
);
|
||||||
|
newTickets.push(newTicket);
|
||||||
|
mappedTickets.set(ticketId, newTicket.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const sale of sales) {
|
||||||
|
const newTicketId = mappedTickets.get(sale.ticketFk);
|
||||||
|
|
||||||
|
const createdSale = await models.Sale.create({
|
||||||
|
ticketFk: newTicketId,
|
||||||
|
itemFk: sale.itemFk,
|
||||||
|
quantity: negative ? - sale.quantity : sale.quantity,
|
||||||
|
concept: sale.concept,
|
||||||
|
price: sale.price,
|
||||||
|
discount: sale.discount,
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
|
const components = sale.components();
|
||||||
|
for (const component of components)
|
||||||
|
component.saleFk = createdSale.id;
|
||||||
|
|
||||||
|
await models.SaleComponent.create(components, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (servicesIds && servicesIds.length) {
|
||||||
|
const servicesFilter = {
|
||||||
|
where: {id: {inq: servicesIds}}
|
||||||
|
};
|
||||||
|
const services = await models.TicketService.find(servicesFilter, myOptions);
|
||||||
|
|
||||||
|
for (const service of services) {
|
||||||
|
const newTicketId = mappedTickets.get(service.ticketFk);
|
||||||
|
|
||||||
|
await models.TicketService.create({
|
||||||
|
description: service.description,
|
||||||
|
quantity: negative ? - service.quantity : service.quantity,
|
||||||
|
price: service.price,
|
||||||
|
taxClassFk: service.taxClassFk,
|
||||||
|
ticketFk: newTicketId,
|
||||||
|
ticketServiceTypeFk: service.ticketServiceTypeFk,
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
return newTickets;
|
||||||
|
} catch (e) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createTicket(
|
||||||
|
ctx,
|
||||||
|
ticketId,
|
||||||
|
withWarehouse,
|
||||||
|
negative,
|
||||||
|
myOptions
|
||||||
|
) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const now = Date.vnNew();
|
||||||
|
|
||||||
|
const ticket = await models.Ticket.findById(ticketId, null, myOptions);
|
||||||
|
ctx.args.clientId = ticket.clientFk;
|
||||||
|
ctx.args.shipped = now;
|
||||||
|
ctx.args.landed = now;
|
||||||
|
ctx.args.warehouseId = withWarehouse ? ticket.warehouseFk : null;
|
||||||
|
ctx.args.companyId = ticket.companyFk;
|
||||||
|
ctx.args.addressId = ticket.addressFk;
|
||||||
|
|
||||||
|
const newTicket = await models.Ticket.new(ctx, myOptions);
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
await models.TicketRefund.create({
|
||||||
|
originalTicketFk: ticketId,
|
||||||
|
refundTicketFk: newTicket.id
|
||||||
|
}, myOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newTicket;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
|
@ -5,7 +5,8 @@ module.exports = Self => {
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'salesIds',
|
arg: 'salesIds',
|
||||||
type: ['number']
|
type: ['number'],
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'servicesIds',
|
arg: 'servicesIds',
|
||||||
|
@ -40,122 +41,23 @@ module.exports = Self => {
|
||||||
myOptions.transaction = tx;
|
myOptions.transaction = tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
let refundTicket = null;
|
|
||||||
try {
|
try {
|
||||||
const refundAgencyMode = await models.AgencyMode.findOne({
|
const refundsTicket = await models.Sale.clone(
|
||||||
include: {
|
ctx,
|
||||||
relation: 'zones',
|
salesIds,
|
||||||
scope: {
|
servicesIds,
|
||||||
limit: 1,
|
withWarehouse,
|
||||||
field: ['id', 'name']
|
false,
|
||||||
}
|
true,
|
||||||
},
|
myOptions
|
||||||
where: {code: 'refund'}
|
);
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
const refoundZoneId = refundAgencyMode.zones()[0].id;
|
|
||||||
|
|
||||||
if (salesIds.length) {
|
|
||||||
const salesFilter = {
|
|
||||||
where: {id: {inq: salesIds}},
|
|
||||||
include: {
|
|
||||||
relation: 'components',
|
|
||||||
scope: {
|
|
||||||
fields: ['saleFk', 'componentFk', 'value']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const sales = await models.Sale.find(salesFilter, myOptions);
|
|
||||||
const ticketsIds = [...new Set(sales.map(sale => sale.ticketFk))];
|
|
||||||
|
|
||||||
const now = Date.vnNew();
|
|
||||||
const [firstTicketId] = ticketsIds;
|
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
|
|
||||||
|
|
||||||
for (const sale of sales) {
|
|
||||||
const createdSale = await models.Sale.create({
|
|
||||||
ticketFk: refundTicket.id,
|
|
||||||
itemFk: sale.itemFk,
|
|
||||||
quantity: - sale.quantity,
|
|
||||||
concept: sale.concept,
|
|
||||||
price: sale.price,
|
|
||||||
discount: sale.discount,
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
const components = sale.components();
|
|
||||||
for (const component of components)
|
|
||||||
component.saleFk = createdSale.id;
|
|
||||||
|
|
||||||
await models.SaleComponent.create(components, myOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!refundTicket) {
|
|
||||||
const servicesFilter = {
|
|
||||||
where: {id: {inq: servicesIds}}
|
|
||||||
};
|
|
||||||
const services = await models.TicketService.find(servicesFilter, myOptions);
|
|
||||||
const firstTicketId = services[0].ticketFk;
|
|
||||||
|
|
||||||
const now = Date.vnNew();
|
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
refundTicket = await createTicketRefund(firstTicketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (servicesIds && servicesIds.length > 0) {
|
|
||||||
const servicesFilter = {
|
|
||||||
where: {id: {inq: servicesIds}}
|
|
||||||
};
|
|
||||||
const services = await models.TicketService.find(servicesFilter, myOptions);
|
|
||||||
for (const service of services) {
|
|
||||||
await models.TicketService.create({
|
|
||||||
description: service.description,
|
|
||||||
quantity: - service.quantity,
|
|
||||||
price: service.price,
|
|
||||||
taxClassFk: service.taxClassFk,
|
|
||||||
ticketFk: refundTicket.id,
|
|
||||||
ticketServiceTypeFk: service.ticketServiceTypeFk,
|
|
||||||
}, myOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = `CALL vn.ticket_recalc(?, NULL)`;
|
|
||||||
await Self.rawSql(query, [refundTicket.id], myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return refundTicket;
|
return refundsTicket[0];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function createTicketRefund(ticketId, now, refundAgencyMode, refoundZoneId, withWarehouse, myOptions) {
|
|
||||||
const models = Self.app.models;
|
|
||||||
|
|
||||||
const filter = {include: {relation: 'address'}};
|
|
||||||
const ticket = await models.Ticket.findById(ticketId, filter, myOptions);
|
|
||||||
|
|
||||||
const refundTicket = await models.Ticket.create({
|
|
||||||
clientFk: ticket.clientFk,
|
|
||||||
shipped: now,
|
|
||||||
addressFk: ticket.address().id,
|
|
||||||
agencyModeFk: refundAgencyMode.id,
|
|
||||||
nickname: ticket.address().nickname,
|
|
||||||
warehouseFk: withWarehouse ? ticket.warehouseFk : null,
|
|
||||||
companyFk: ticket.companyFk,
|
|
||||||
landed: now,
|
|
||||||
zoneFk: refoundZoneId
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
await models.TicketRefund.create({
|
|
||||||
refundTicketFk: refundTicket.id,
|
|
||||||
originalTicketFk: ticket.id,
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
return refundTicket;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Sale refund()', () => {
|
describe('Sale refund()', () => {
|
||||||
const userId = 5;
|
const userId = 5;
|
||||||
const ctx = {req: {accessToken: userId}};
|
const ctx = {req: {accessToken: userId}, args: {}};
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId},
|
accessToken: {userId},
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,7 @@ describe('Sale refund()', () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
const ticketsBefore = await models.Ticket.find({}, options);
|
||||||
|
|
||||||
const ticket = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
|
const ticket = await models.Sale.refund(ctx, salesIds, servicesIds, withWarehouse, options);
|
||||||
|
|
||||||
|
@ -61,12 +62,13 @@ describe('Sale refund()', () => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}, options);
|
}, options);
|
||||||
|
const ticketsAfter = await models.Ticket.find({}, options);
|
||||||
const salesLength = refundedTicket.ticketSales().length;
|
const salesLength = refundedTicket.ticketSales().length;
|
||||||
const componentsLength = refundedTicket.ticketSales()[0].components().length;
|
const componentsLength = refundedTicket.ticketSales()[0].components().length;
|
||||||
|
|
||||||
expect(refundedTicket).toBeDefined();
|
expect(refundedTicket).toBeDefined();
|
||||||
expect(salesLength).toEqual(2);
|
expect(salesLength).toEqual(1);
|
||||||
|
expect(ticketsBefore.length).toEqual(ticketsAfter.length - 2);
|
||||||
expect(componentsLength).toEqual(4);
|
expect(componentsLength).toEqual(4);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
|
|
@ -64,7 +64,10 @@ module.exports = Self => {
|
||||||
const sale = await models.Sale.findById(id, filter, myOptions);
|
const sale = await models.Sale.findById(id, filter, myOptions);
|
||||||
|
|
||||||
const oldQuantity = sale.quantity;
|
const oldQuantity = sale.quantity;
|
||||||
const result = await sale.updateAttributes({quantity: newQuantity}, myOptions);
|
const result = await sale.updateAttributes({
|
||||||
|
quantity: newQuantity,
|
||||||
|
originalQuantity: newQuantity
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue