diff --git a/.vscode/settings.json b/.vscode/settings.json
index 40ec5c0d37..36b7e21d84 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,7 +3,7 @@
// Carácter predeterminado de final de línea.
"files.eol": "\n",
"editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
+ "source.fixAll.eslint": "explicit"
},
"search.useIgnoreFiles": false,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dfdc563fb4..1907f46bd2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [2352.01] - 2023-12-28
+## [2402.01] - 2024-01-11
+
+### Added
+### Changed
+### Fixed
+
+## [2400.01] - 2024-01-04
### Added
### Changed
@@ -13,9 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2350.01] - 2023-12-14
-### Added
-### Changed
-### Fixed
+### Características Añadidas 🆕
+- **Tickets → Expediciones:** Añadido soporte para Viaexpress
## [2348.01] - 2023-11-30
diff --git a/back/methods/dms/removeFile.js b/back/methods/dms/removeFile.js
index a9ff368834..dc55b4d385 100644
--- a/back/methods/dms/removeFile.js
+++ b/back/methods/dms/removeFile.js
@@ -22,8 +22,8 @@ module.exports = Self => {
Self.removeFile = async(ctx, id, options) => {
const models = Self.app.models;
- let tx;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
diff --git a/back/methods/docuware/specs/upload.spec.js b/back/methods/docuware/specs/upload.spec.js
index 3b8c55a504..866499b665 100644
--- a/back/methods/docuware/specs/upload.spec.js
+++ b/back/methods/docuware/specs/upload.spec.js
@@ -24,15 +24,40 @@ describe('docuware upload()', () => {
});
it('should try upload file', async() => {
+ const tx = await models.Docuware.beginTransaction({});
spyOn(ticketModel, 'deliveryNotePdf').and.returnValue(new Promise(resolve => resolve({})));
let error;
try {
- await models.Docuware.upload(ctx, ticketIds, fileCabinetName);
+ const options = {transaction: tx};
+ const user = await models.UserConfig.findById(userId, null, options);
+ await user.updateAttribute('tabletFk', 'Tablet1', options);
+ await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options);
+
+ await tx.rollback();
} catch (e) {
- error = e.message;
+ error = e;
+ await tx.rollback();
}
- expect(error).toEqual('Action not allowed on the test environment');
+ expect(error.message).toEqual('Action not allowed on the test environment');
+ });
+
+ it('should throw error when not have tablet assigned', async() => {
+ const tx = await models.Docuware.beginTransaction({});
+ spyOn(ticketModel, 'deliveryNotePdf').and.returnValue(new Promise(resolve => resolve({})));
+
+ let error;
+ try {
+ const options = {transaction: tx};
+ await models.Docuware.upload(ctx, ticketIds, fileCabinetName, options);
+
+ await tx.rollback();
+ } catch (e) {
+ error = e;
+ await tx.rollback();
+ }
+
+ expect(error.message).toEqual('This user does not have an assigned tablet');
});
});
diff --git a/back/methods/docuware/upload.js b/back/methods/docuware/upload.js
index 7055bf8d5c..27be72295e 100644
--- a/back/methods/docuware/upload.js
+++ b/back/methods/docuware/upload.js
@@ -29,12 +29,24 @@ module.exports = Self => {
}
});
- Self.upload = async function(ctx, ticketIds, fileCabinet) {
+ Self.upload = async function(ctx, ticketIds, fileCabinet, options) {
delete ctx.args.ticketIds;
const models = Self.app.models;
const action = 'store';
- const options = await Self.getOptions();
+ const myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const userConfig = await models.UserConfig.findById(ctx.req.accessToken.userId, {
+ fields: ['tabletFk']
+ }, myOptions);
+
+ if (!userConfig?.tabletFk)
+ throw new UserError('This user does not have an assigned tablet');
+
+ const docuwareOptions = await Self.getOptions();
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
@@ -45,7 +57,7 @@ module.exports = Self => {
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
id,
type: 'deliveryNote'
- });
+ }, myOptions);
// get ticket data
const ticket = await models.Ticket.findById(id, {
include: [{
@@ -54,7 +66,7 @@ module.exports = Self => {
fields: ['id', 'name', 'fi']
}
}]
- });
+ }, myOptions);
// upload file
const templateJson = {
@@ -102,7 +114,7 @@ module.exports = Self => {
{
'FieldName': 'FILTRO_TABLET',
'ItemElementName': 'string',
- 'Item': 'Tablet1',
+ 'Item': userConfig.tabletFk,
}
]
};
@@ -116,11 +128,11 @@ module.exports = Self => {
const deleteJson = {
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
};
- const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
- await axios.put(deleteUri, deleteJson, options.headers);
+ const deleteUri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
+ await axios.put(deleteUri, deleteJson, docuwareOptions.headers);
}
- const uploadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
+ const uploadUri = `${docuwareOptions.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
const FormData = require('form-data');
const data = new FormData();
@@ -130,7 +142,7 @@ module.exports = Self => {
headers: {
'Content-Type': 'multipart/form-data',
'X-File-ModifiedDate': Date.vnNew(),
- 'Cookie': options.headers.headers.Cookie,
+ 'Cookie': docuwareOptions.headers.headers.Cookie,
...data.getHeaders()
},
};
@@ -141,11 +153,11 @@ module.exports = Self => {
const $t = ctx.req.__;
const message = $t('Failed to upload delivery note', {id});
if (uploaded.length)
- await models.TicketTracking.setDelivered(ctx, uploaded);
+ await models.TicketTracking.setDelivered(ctx, uploaded, myOptions);
throw new UserError(message);
}
uploaded.push(id);
}
- return models.TicketTracking.setDelivered(ctx, ticketIds);
+ return models.TicketTracking.setDelivered(ctx, ticketIds, myOptions);
};
};
diff --git a/back/methods/vn-user/renew-token.js b/back/methods/vn-user/renew-token.js
index 9850267d6b..d00085d8a9 100644
--- a/back/methods/vn-user/renew-token.js
+++ b/back/methods/vn-user/renew-token.js
@@ -1,4 +1,4 @@
-const UserError = require('vn-loopback/util/user-error');
+const {models} = require('vn-loopback/server/server');
module.exports = Self => {
Self.remoteMethodCtx('renewToken', {
@@ -16,20 +16,31 @@ module.exports = Self => {
});
Self.renewToken = async function(ctx) {
- const models = Self.app.models;
- const token = ctx.req.accessToken;
+ const {accessToken: token} = ctx.req;
- const now = new Date();
+ // Check if current token is valid
+
+ const {renewPeriod, courtesyTime} = await models.AccessTokenConfig.findOne({
+ fields: ['renewPeriod', 'courtesyTime']
+ });
+ const now = Date.now();
const differenceMilliseconds = now - token.created;
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
+ const isNotExceeded = differenceSeconds < renewPeriod - courtesyTime;
+ if (isNotExceeded)
+ return token;
- const fields = ['renewPeriod', 'courtesyTime'];
- const accessTokenConfig = await models.AccessTokenConfig.findOne({fields});
+ // Schedule to remove current token
+ setTimeout(async() => {
+ try {
+ await Self.logout(token.id);
+ } catch (err) {
+ // eslint-disable-next-line no-console
+ console.error(err);
+ }
+ }, courtesyTime * 1000);
- if (differenceSeconds < accessTokenConfig.renewPeriod - accessTokenConfig.courtesyTime)
- throw new UserError(`The renew period has not been exceeded`, 'periodNotExceeded');
-
- await Self.logout(token.id);
+ // Create new accessToken
const user = await Self.findById(token.userId);
const accessToken = await user.createAccessToken();
diff --git a/back/methods/vn-user/specs/renew-token.spec.js b/back/methods/vn-user/specs/renew-token.spec.js
new file mode 100644
index 0000000000..8d9bbf11ce
--- /dev/null
+++ b/back/methods/vn-user/specs/renew-token.spec.js
@@ -0,0 +1,50 @@
+const {models} = require('vn-loopback/server/server');
+describe('Renew Token', () => {
+ const startingTime = Date.now();
+ let ctx = null;
+ beforeAll(async() => {
+ const unAuthCtx = {
+ req: {
+ headers: {},
+ connection: {
+ remoteAddress: '127.0.0.1'
+ },
+ getLocale: () => 'en'
+ },
+ args: {}
+ };
+ let login = await models.VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare');
+ let accessToken = await models.AccessToken.findById(login.token);
+ ctx = {req: {accessToken: accessToken}};
+ });
+
+ beforeEach(() => {
+ jasmine.clock().install();
+ jasmine.clock().mockDate(new Date(startingTime));
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
+ it('should renew token', async() => {
+ const mockDate = new Date(startingTime + 26600000);
+ jasmine.clock().mockDate(mockDate);
+ const {id} = await models.VnUser.renewToken(ctx);
+
+ expect(id).not.toEqual(ctx.req.accessToken.id);
+ });
+
+ it('NOT should renew', async() => {
+ let error;
+ let response;
+ try {
+ response = await models.VnUser.renewToken(ctx);
+ } catch (e) {
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ expect(response.id).toEqual(ctx.req.accessToken.id);
+ });
+});
diff --git a/back/methods/vn-user/specs/sign-in.spec.js b/back/methods/vn-user/specs/sign-in.spec.js
index 1c4b4af51b..a14dd301ef 100644
--- a/back/methods/vn-user/specs/sign-in.spec.js
+++ b/back/methods/vn-user/specs/sign-in.spec.js
@@ -20,10 +20,7 @@ describe('VnUser Sign-in()', () => {
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(signInLog[0].owner).toEqual(true);
- expect(login.token).toBeDefined();
+ expect(signInLog.length).toEqual(0);
await VnUser.logout(ctx.req.accessToken.id);
});
diff --git a/back/methods/vn-user/validate-token.js b/back/methods/vn-user/validate-token.js
deleted file mode 100644
index 7bccfe0b1d..0000000000
--- a/back/methods/vn-user/validate-token.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = Self => {
- Self.remoteMethod('validateToken', {
- description: 'Validates the current logged user token',
- returns: {
- type: 'Boolean',
- root: true
- },
- http: {
- path: `/validateToken`,
- verb: 'GET'
- }
- });
-
- Self.validateToken = async function() {
- return true;
- };
-};
diff --git a/back/models/docuwareTablet.json b/back/models/docuwareTablet.json
new file mode 100644
index 0000000000..dbbf62f56e
--- /dev/null
+++ b/back/models/docuwareTablet.json
@@ -0,0 +1,17 @@
+{
+ "name": "docuwareTablet",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "docuwareTablet"
+ }
+ },
+ "properties": {
+ "tablet": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ }
+ }
+}
diff --git a/back/models/specs/mailAliasAccount.spec.js b/back/models/specs/mailAliasAccount.spec.js
new file mode 100644
index 0000000000..c13cc7ae84
--- /dev/null
+++ b/back/models/specs/mailAliasAccount.spec.js
@@ -0,0 +1,73 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('loopback model MailAliasAccount', () => {
+ it('should fail to add a mail Alias if the worker doesnt have ACLs', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 57}};
+ await models.MailAliasAccount.create({mailAlias: 2, account: 5}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toEqual('The alias cant be modified');
+ });
+
+ it('should add a mail Alias', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ await models.MailAliasAccount.create({mailAlias: 2, account: 5}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+
+ it('should add a mail Alias of an inherit role', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ await models.MailAliasAccount.create({mailAlias: 3, account: 5}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+
+ it('should delete a mail Alias', async() => {
+ const tx = await models.MailAliasAccount.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 1}};
+ const mailAclId = 2;
+ await models.MailAliasAccount.destroyAll({id: mailAclId}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+});
+
diff --git a/back/models/user-config.json b/back/models/user-config.json
index 52125dc012..5c5df1b9ef 100644
--- a/back/models/user-config.json
+++ b/back/models/user-config.json
@@ -26,6 +26,9 @@
},
"darkMode": {
"type": "boolean"
+ },
+ "tabletFk": {
+ "type": "string"
}
},
"relations": {
@@ -43,6 +46,11 @@
"type": "belongsTo",
"model": "VnUser",
"foreignKey": "userFk"
- }
+ },
+ "Tablet": {
+ "type": "belongsTo",
+ "model": "docuwareTablet",
+ "foreignKey": "tabletFk"
+ }
}
}
diff --git a/back/models/vn-user.js b/back/models/vn-user.js
index e14cd30eac..b1d09f0c0b 100644
--- a/back/models/vn-user.js
+++ b/back/models/vn-user.js
@@ -10,7 +10,6 @@ module.exports = function(Self) {
require('../methods/vn-user/sign-in')(Self);
require('../methods/vn-user/acl')(Self);
require('../methods/vn-user/recover-password')(Self);
- require('../methods/vn-user/validate-token')(Self);
require('../methods/vn-user/privileges')(Self);
require('../methods/vn-user/validate-auth')(Self);
require('../methods/vn-user/renew-token')(Self);
@@ -135,15 +134,16 @@ module.exports = function(Self) {
Self.signInValidate = async(user, userToken, token, ctx) => {
const [[key, value]] = Object.entries(Self.userUses(user));
const isOwner = Self.rawSql(`SELECT ? = ? `, [userToken[key], value]);
- await Self.app.models.SignInLog.create({
- userName: user,
- token: token.id,
- userFk: userToken.id,
- ip: ctx.req.ip,
- owner: isOwner
- });
- if (!isOwner)
- throw new UserError('Try again');
+ if (!isOwner) {
+ await Self.app.models.SignInLog.create({
+ userName: user,
+ token: token.id,
+ userFk: userToken.id,
+ ip: ctx.req.ip,
+ owner: isOwner
+ });
+ throw new UserError('Try again');
+ }
};
/**
diff --git a/back/models/vn-user.json b/back/models/vn-user.json
index 0f6daff5ac..d0687098d5 100644
--- a/back/models/vn-user.json
+++ b/back/models/vn-user.json
@@ -95,34 +95,30 @@
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
- },
- {
- "property": "recoverPassword",
- "accessType": "EXECUTE",
- "principalType": "ROLE",
- "principalId": "$everyone",
- "permission": "ALLOW"
- },
- {
- "property": "validateToken",
- "accessType": "EXECUTE",
- "principalType": "ROLE",
- "principalId": "$authenticated",
- "permission": "ALLOW"
- },
- {
- "property": "validateAuth",
+ }, {
+ "property": "recoverPassword",
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
- },
- {
+ }, {
+ "property": "validateAuth",
+ "accessType": "EXECUTE",
+ "principalType": "ROLE",
+ "principalId": "$everyone",
+ "permission": "ALLOW"
+ }, {
"property": "privileges",
"accessType": "*",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
+ }, {
+ "property": "renewToken",
+ "accessType": "WRITE",
+ "principalType": "ROLE",
+ "principalId": "$authenticated",
+ "permission": "ALLOW"
}
],
"scopes": {
diff --git a/db/changes/234601/00-updateCourtesyTime.sql b/db/changes/234601/00-updateCourtesyTime.sql
new file mode 100644
index 0000000000..4751b2e032
--- /dev/null
+++ b/db/changes/234601/00-updateCourtesyTime.sql
@@ -0,0 +1,4 @@
+-- Auto-generated SQL script #202311061003
+UPDATE salix.accessTokenConfig
+ SET courtesyTime=60
+ WHERE id=1;
diff --git a/db/changes/235201/00-aclsMails.sql b/db/changes/235201/00-aclsMails.sql
new file mode 100644
index 0000000000..5cfea40301
--- /dev/null
+++ b/db/changes/235201/00-aclsMails.sql
@@ -0,0 +1,8 @@
+-- Definición de la tabla mailAliasACL
+
+CREATE OR REPLACE TABLE `account`.`mailAliasAcl` (
+ `mailAliasFk` int(10) unsigned NOT NULL,
+ `roleFk` int(10) unsigned NOT NULL,
+ FOREIGN KEY (`mailAliasFk`) REFERENCES `account`.`mailAlias` (`id`),
+ FOREIGN KEY (`roleFk`) REFERENCES `account`.`role` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
diff --git a/db/changes/235201/00-alterTable.sql b/db/changes/240001/00-alterTable.sql
similarity index 100%
rename from db/changes/235201/00-alterTable.sql
rename to db/changes/240001/00-alterTable.sql
diff --git a/db/changes/235201/00-clientCreditLimitToRoleCreditLimit.sql b/db/changes/240001/00-clientCreditLimitToRoleCreditLimit.sql
similarity index 74%
rename from db/changes/235201/00-clientCreditLimitToRoleCreditLimit.sql
rename to db/changes/240001/00-clientCreditLimitToRoleCreditLimit.sql
index bf4cc60029..2bc0f830dd 100644
--- a/db/changes/235201/00-clientCreditLimitToRoleCreditLimit.sql
+++ b/db/changes/240001/00-clientCreditLimitToRoleCreditLimit.sql
@@ -1,4 +1,4 @@
RENAME TABLE `vn`.`clientCreditLimit` TO `vn`.`roleCreditLimit`;
-ALTER TABLE `vn`.`clientCreditLimit` DROP FOREIGN KEY `clientCreditLimit_FK`;
+ALTER TABLE `vn`.`roleCreditLimit` DROP FOREIGN KEY `clientCreditLimit_FK`;
ALTER TABLE `vn`.`roleCreditLimit` ADD CONSTRAINT `roleCreditLimit_FK` FOREIGN KEY (`roleFk`) REFERENCES `account`.`role`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/db/changes/240001/00-truncate-where-signInLog.sql b/db/changes/240001/00-truncate-where-signInLog.sql
new file mode 100644
index 0000000000..93d80d7161
--- /dev/null
+++ b/db/changes/240001/00-truncate-where-signInLog.sql
@@ -0,0 +1 @@
+DELETE FROM `account`.`signInLog` where owner <> FALSE
diff --git a/db/changes/235201/01-procedures.sql b/db/changes/240001/01-procedures.sql
similarity index 100%
rename from db/changes/235201/01-procedures.sql
rename to db/changes/240001/01-procedures.sql
diff --git a/db/changes/235201/02-views.sql b/db/changes/240001/02-views.sql
similarity index 76%
rename from db/changes/235201/02-views.sql
rename to db/changes/240001/02-views.sql
index a0f41594da..86a1049a79 100644
--- a/db/changes/235201/02-views.sql
+++ b/db/changes/240001/02-views.sql
@@ -1,9 +1,12 @@
+CREATE SCHEMA IF NOT EXISTS `vn2008`;
+USE `vn`;
+
CREATE OR REPLACE DEFINER=`root`@`localhost`
SQL SECURITY DEFINER
- VIEW `vn`.`ticketState`
+ VIEW `ticketState`
AS SELECT `tt`.`created` AS `updated`,
`tt`.`stateFk` AS `stateFk`,
- `tt`.`userFk` AS `userFk`,
+ `tt`.`userFk` AS `workerFk`,
`tls`.`ticketFk` AS `ticketFk`,
`s`.`id` AS `state`,
`s`.`order` AS `productionOrder`,
@@ -15,10 +18,10 @@ AS SELECT `tt`.`created` AS `updated`,
`s`.`isPicked` AS `isPicked`
FROM (
(
- `vn`.`ticketLastState` `tls`
- JOIN `vn`.`ticketTracking` `tt` ON(`tt`.`id` = `tls`.`ticketTrackingFk`)
+ `ticketLastState` `tls`
+ JOIN `ticketTracking` `tt` ON(`tt`.`id` = `tls`.`ticketTrackingFk`)
)
- JOIN `vn`.`state` `s` ON(`s`.`id` = `tt`.`stateFk`)
+ JOIN `state` `s` ON(`s`.`id` = `tt`.`stateFk`)
);
CREATE OR REPLACE DEFINER=`root`@`localhost`
@@ -33,14 +36,15 @@ AS SELECT `tt`.`id` AS `inter_id`,
`tt`.`supervisorFk` AS `Id_supervisor`
FROM `vn`.`ticketTracking` `tt`;
-CREATE OR REPLACE
-ALGORITHM = UNDEFINED VIEW `ticketStateToday` AS
-SELECT
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `ticketStateToday`
+AS SELECT
`ts`.`ticket` AS `ticket`,
`ts`.`state` AS `state`,
`ts`.`productionOrder` AS `productionOrder`,
`ts`.`alertLevel` AS `alertLevel`,
- `ts`.`userFk` AS `userFk`,
+ `ts`.`worker` AS `worker`,
`ts`.`code` AS `code`,
`ts`.`updated` AS `updated`,
`ts`.`isPicked` AS `isPicked`
diff --git a/db/changes/235201/.gitkeep b/db/changes/240201/.gitkeep
similarity index 100%
rename from db/changes/235201/.gitkeep
rename to db/changes/240201/.gitkeep
diff --git a/db/changes/240201/00-tabletDocuware.sql b/db/changes/240201/00-tabletDocuware.sql
new file mode 100644
index 0000000000..ffa0226b33
--- /dev/null
+++ b/db/changes/240201/00-tabletDocuware.sql
@@ -0,0 +1,10 @@
+-- vn.docuwareTablet definition
+
+CREATE TABLE `vn`.`docuwareTablet` (
+ `tablet` varchar(100) NOT NULL PRIMARY KEY,
+ `description` varchar(255) DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
+
+ALTER TABLE `vn`.`userConfig`
+ADD COLUMN tabletFk varchar(100) DEFAULT NULL,
+ADD FOREIGN KEY (tabletFk) REFERENCES `vn`.`docuwareTablet`(tablet);
diff --git a/db/changes/240201/00-timecontrol.sql b/db/changes/240201/00-timecontrol.sql
new file mode 100644
index 0000000000..c3ddf5d96c
--- /dev/null
+++ b/db/changes/240201/00-timecontrol.sql
@@ -0,0 +1,17 @@
+DELETE FROM `salix`.`ACL`
+ WHERE model = 'VnUser'
+ AND property = 'renewToken';
+
+INSERT INTO `account`.`role` (name, description)
+ VALUES ('timeControl','Tablet para fichar');
+
+INSERT INTO `account`.`roleInherit` (role, inheritsFrom)
+ VALUES (127, 11);
+
+INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
+ VALUES
+ ('WorkerTimeControl', 'login', 'READ', 'ALLOW', 'ROLE', 'timeControl'),
+ ('WorkerTimeControl', 'getClockIn', 'READ', 'ALLOW', 'ROLE', 'timeControl'),
+ ('WorkerTimeControl', 'clockIn', 'WRITE', 'ALLOW', 'ROLE', 'timeControl');
+
+CALL `account`.`role_sync`();
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index c94433e61e..6e676f3e6d 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -2967,9 +2967,9 @@ INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
(2, 1, 50, 2),
(3, 1, 0, 3);
-INSERT INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `renewInterval`)
+INSERT INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `courtesyTime`, `renewInterval`)
VALUES
- (1, 21600, 300);
+ (1, 21600, 60, 300);
INSERT INTO `vn`.`travelConfig` (`id`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `companyFk`)
VALUES
@@ -3009,3 +3009,14 @@ INSERT INTO `vn`.`invoiceCorrectionType` (`id`, `description`)
(1, 'Error in VAT calculation'),
(2, 'Error in sales details'),
(3, 'Error in customer data');
+
+INSERT INTO `account`.`mailAliasAcl` (`mailAliasFk`, `roleFk`)
+ VALUES
+ (1, 1),
+ (2, 9),
+ (3, 15);
+
+INSERT INTO `vn`.`docuwareTablet` (`tablet`,`description`)
+ VALUES
+ ('Tablet1','Jarvis tablet'),
+ ('Tablet2','Avengers tablet');
diff --git a/db/dump/structure.sql b/db/dump/structure.sql
index 1db4252f48..694f745efa 100644
--- a/db/dump/structure.sql
+++ b/db/dump/structure.sql
@@ -26391,6 +26391,7 @@ CREATE TABLE `cplusCorrectingType` (
) ENGINE=InnoDBDEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
+
--
-- Table structure for table `cplusRectificationType`
--
diff --git a/front/core/locale/es.yml b/front/core/locale/es.yml
index 96c34d98ca..17e955ff52 100644
--- a/front/core/locale/es.yml
+++ b/front/core/locale/es.yml
@@ -68,3 +68,4 @@ Load more results: Cargar más resultados
Send cau: Enviar cau
By sending this ticket, all the data related to the error, the section, the user, etc., are already sent.: Al enviar este cau ya se envían todos los datos relacionados con el error, la sección, el usuario, etc
ExplainReason: Explique el motivo por el que no deberia aparecer este fallo
+You already have the mailAlias: Ya tienes este alias de correo
diff --git a/front/core/services/token.js b/front/core/services/token.js
index 426fe2b731..c8cb4f6bb6 100644
--- a/front/core/services/token.js
+++ b/front/core/services/token.js
@@ -82,7 +82,7 @@ export default class Token {
if (!data) return;
this.renewPeriod = data.renewPeriod;
this.stopRenewer();
- this.inservalId = setInterval(() => this.checkValidity(), data.renewInterval * 1000);
+ this.intervalId = setInterval(() => this.checkValidity(), data.renewInterval * 1000);
});
}
@@ -103,17 +103,13 @@ export default class Token {
const token = res.data;
this.set(token.id, now, token.ttl, this.remember);
})
- .catch(res => {
- if (res.data?.error?.code !== 'periodNotExceeded')
- throw res;
- })
.finally(() => {
this.checking = false;
});
}
stopRenewer() {
- clearInterval(this.inservalId);
+ clearInterval(this.intervalId);
}
}
Token.$inject = ['vnInterceptor', '$http', '$rootScope'];
diff --git a/front/salix/locale/es.yml b/front/salix/locale/es.yml
index 8ed58a4e4a..044d0d043b 100644
--- a/front/salix/locale/es.yml
+++ b/front/salix/locale/es.yml
@@ -18,7 +18,7 @@ Show summary: Mostrar vista previa
What is new: Novedades de la versión
Settings: Ajustes
There is a new version, click here to reload: Hay una nueva versión, pulse aquí para recargar
-This ticket is locked.: Este ticket está bloqueado
+This ticket is locked: Este ticket está bloqueado
# Actions
diff --git a/loopback/locale/en.json b/loopback/locale/en.json
index 7d5b5ed47f..508c173444 100644
--- a/loopback/locale/en.json
+++ b/loopback/locale/en.json
@@ -183,7 +183,7 @@
"Social name should be uppercase": "Social name should be uppercase",
"Street should be uppercase": "Street should be uppercase",
"You don't have enough privileges.": "You don't have enough privileges.",
- "This ticket is locked.": "This ticket is locked.",
+ "This ticket is locked": "This ticket is locked",
"This ticket is not editable.": "This ticket is not editable.",
"The ticket doesn't exist.": "The ticket doesn't exist.",
"The sales do not exists": "The sales do not exists",
@@ -200,5 +200,6 @@
"Try again": "Try again",
"keepPrice": "keepPrice",
"Cannot past travels with entries": "Cannot past travels with entries",
- "It was not able to remove the next expeditions:": "It was not able to remove the next expeditions: {{expeditions}}"
-}
\ No newline at end of file
+ "It was not able to remove the next expeditions:": "It was not able to remove the next expeditions: {{expeditions}}",
+ "Incorrect pin": "Incorrect pin."
+}
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index 01384efb42..e2b90983b0 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -312,7 +312,7 @@
"You cannot assign/remove an alias that you are not assigned to": "No puede asignar/eliminar un alias que no tenga asignado",
"This invoice has a linked vehicle.": "Esta factura tiene un vehiculo vinculado",
"You don't have enough privileges.": "No tienes suficientes permisos.",
- "This ticket is locked.": "Este ticket está bloqueado.",
+ "This ticket is locked": "Este ticket está bloqueado.",
"This ticket is not editable.": "Este ticket no es editable.",
"The ticket doesn't exist.": "No existe el ticket.",
"Social name should be uppercase": "La razón social debe ir en mayúscula",
@@ -329,5 +329,9 @@
"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",
"Cannot past travels with entries": "No se pueden pasar envíos con entradas",
- "It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}"
+ "It was not able to remove the next expeditions:": "No se pudo eliminar las siguientes expediciones: {{expeditions}}",
+ "This user does not have an assigned tablet": "Este usuario no tiene tablet asignada",
+ "Incorrect pin": "Pin incorrecto.",
+ "You already have the mailAlias": "Ya tienes este alias de correo",
+ "The alias cant be modified": "Este alias de correo no puede ser modificado"
}
\ No newline at end of file
diff --git a/loopback/server/boot/date.js b/loopback/server/boot/date.js
index 8107455625..d592dc416b 100644
--- a/loopback/server/boot/date.js
+++ b/loopback/server/boot/date.js
@@ -1,6 +1,5 @@
module.exports = () => {
- Date.vnUTC = () => {
- const env = process.env.NODE_ENV;
+ Date.vnUTC = (env = process.env.NODE_ENV) => {
if (!env || env === 'development')
return new Date(Date.UTC(2001, 0, 1, 11));
diff --git a/loopback/server/middleware.json b/loopback/server/middleware.json
index 31a2f113b5..cfc6932175 100644
--- a/loopback/server/middleware.json
+++ b/loopback/server/middleware.json
@@ -39,7 +39,7 @@
"./middleware/salix-version": {}
},
"parse": {
- "body-parser#json":{}
+ "body-parser#json":{}
},
"routes": {
"loopback#rest": {
diff --git a/modules/account/back/methods/account/specs/change-password.spec.js b/modules/account/back/methods/account/specs/change-password.spec.js
index 2fa3010afb..c799602128 100644
--- a/modules/account/back/methods/account/specs/change-password.spec.js
+++ b/modules/account/back/methods/account/specs/change-password.spec.js
@@ -2,7 +2,7 @@ const {models} = require('vn-loopback/server/server');
describe('account changePassword()', () => {
const userId = 70;
- const unauthCtx = {
+ const unAuthCtx = {
req: {
headers: {},
connection: {
@@ -79,7 +79,7 @@ describe('account changePassword()', () => {
passExpired: yesterday
}
, options);
- await models.VnUser.signIn(unauthCtx, 'trainee', 'nightmare', options);
+ await models.VnUser.signIn(unAuthCtx, 'trainee', 'nightmare', options);
} catch (e) {
if (e.message != 'Pass expired')
throw e;
diff --git a/modules/account/back/model-config.json b/modules/account/back/model-config.json
index b4bd6dbafd..0cd43d0cec 100644
--- a/modules/account/back/model-config.json
+++ b/modules/account/back/model-config.json
@@ -14,6 +14,9 @@
"MailAliasAccount": {
"dataSource": "vn"
},
+ "MailAliasAcl": {
+ "dataSource": "vn"
+ },
"MailConfig": {
"dataSource": "vn"
},
diff --git a/modules/account/back/models/mail-alias-account.js b/modules/account/back/models/mail-alias-account.js
index 6f5213f24b..5eb5614081 100644
--- a/modules/account/back/models/mail-alias-account.js
+++ b/modules/account/back/models/mail-alias-account.js
@@ -2,54 +2,44 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
+ Self.rewriteDbError(function(err) {
+ if (err.code === 'ER_DUP_ENTRY')
+ return new UserError(`You already have the mailAlias`);
+ return err;
+ });
+
Self.observe('before save', async ctx => {
const changes = ctx.currentInstance || ctx.instance;
- await Self.hasGrant(ctx, changes.mailAlias);
+ await checkModifyPermission(ctx, changes.mailAlias);
});
Self.observe('before delete', async ctx => {
const mailAliasAccount = await Self.findById(ctx.where.id);
- await Self.hasGrant(ctx, mailAliasAccount.mailAlias);
+ await checkModifyPermission(ctx, mailAliasAccount.mailAlias);
});
- /**
- * Checks if current user has
- * grant to add/remove alias
- *
- * @param {Object} ctx - Request context
- * @param {Interger} mailAlias - mailAlias id
- * @return {Boolean} True for user with grant
- */
- Self.hasGrant = async function(ctx, mailAlias) {
+ async function checkModifyPermission(ctx, mailAliasFk) {
+ const userId = ctx.options.accessToken.userId;
const models = Self.app.models;
- const accessToken = {req: {accessToken: ctx.options.accessToken}};
- const userId = accessToken.req.accessToken.userId;
- const canEditAlias = await models.ACL.checkAccessAcl(accessToken, 'MailAliasAccount', 'canEditAlias', 'WRITE');
- if (canEditAlias) return true;
+ const roles = await models.RoleMapping.find({
+ fields: ['roleId'],
+ where: {principalId: userId}
+ });
- const user = await models.VnUser.findById(userId, {fields: ['hasGrant']});
- if (!user.hasGrant)
- throw new UserError(`You don't have grant privilege`);
-
- const account = await models.Account.findById(userId, {
- fields: ['id'],
- include: {
- relation: 'aliases',
- scope: {
- fields: ['mailAlias']
- }
+ const availableMailAlias = await models.MailAliasAcl.findOne({
+ fields: ['mailAliasFk'],
+ include: {relation: 'mailAlias'},
+ where: {
+ roleFk: {
+ inq: roles.map(role => role.roleId),
+ },
+ mailAliasFk
}
});
- const aliases = account.aliases().map(alias => alias.mailAlias);
-
- const hasAlias = aliases.includes(mailAlias);
- if (!hasAlias)
- throw new UserError(`You cannot assign/remove an alias that you are not assigned to`);
-
- return true;
- };
+ if (!availableMailAlias) throw new UserError('The alias cant be modified');
+ }
};
diff --git a/modules/account/back/models/mail-alias-acl.json b/modules/account/back/models/mail-alias-acl.json
new file mode 100644
index 0000000000..014b95d144
--- /dev/null
+++ b/modules/account/back/models/mail-alias-acl.json
@@ -0,0 +1,31 @@
+{
+ "name": "MailAliasAcl",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "account.mailAliasAcl"
+ }
+ },
+ "properties": {
+ "mailAliasFk": {
+ "id": true,
+ "type": "number"
+ },
+ "roleFk": {
+ "id": true,
+ "type": "number"
+ }
+ },
+ "relations": {
+ "mailAlias": {
+ "type": "belongsTo",
+ "model": "MailAlias",
+ "foreignKey": "mailAliasFk"
+ },
+ "role": {
+ "type": "belongsTo",
+ "model": "Role",
+ "foreignKey": "roleFk"
+ }
+ }
+}
diff --git a/modules/claim/back/methods/claim-dms/removeFile.js b/modules/claim/back/methods/claim-dms/removeFile.js
index edc714235f..28e78c9d70 100644
--- a/modules/claim/back/methods/claim-dms/removeFile.js
+++ b/modules/claim/back/methods/claim-dms/removeFile.js
@@ -1,3 +1,5 @@
+const UserError = require('vn-loopback/util/user-error');
+
module.exports = Self => {
Self.remoteMethodCtx('removeFile', {
description: 'Removes a claim document',
@@ -19,8 +21,8 @@ module.exports = Self => {
});
Self.removeFile = async(ctx, id, options) => {
- let tx;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -31,19 +33,18 @@ module.exports = Self => {
}
try {
- const models = Self.app.models;
- const targetClaimDms = await models.ClaimDms.findById(id, null, myOptions);
- const targetDms = await models.Dms.findById(targetClaimDms.dmsFk, null, myOptions);
- const trashDmsType = await models.DmsType.findOne({where: {code: 'trash'}}, myOptions);
+ const claimDms = await Self.findById(id, null, myOptions);
- await models.Dms.removeFile(ctx, targetClaimDms.dmsFk, myOptions);
- await targetClaimDms.destroy(myOptions);
+ const targetDms = await Self.app.models.Dms.removeFile(ctx, claimDms.dmsFk, myOptions);
- await targetDms.updateAttribute('dmsTypeFk', trashDmsType.id, myOptions);
+ if (!targetDms || ! claimDms)
+ throw new UserError('Try again');
+
+ const claimDmsDestroyed = await claimDms.destroy(myOptions);
if (tx) await tx.commit();
- return targetDms;
+ return claimDmsDestroyed;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/claim/back/methods/claim/uploadFile.js b/modules/claim/back/methods/claim/uploadFile.js
index 3d0737cf81..4fd041456a 100644
--- a/modules/claim/back/methods/claim/uploadFile.js
+++ b/modules/claim/back/methods/claim/uploadFile.js
@@ -1,6 +1,3 @@
-const UserError = require('vn-loopback/util/user-error');
-const fs = require('fs-extra');
-const path = require('path');
module.exports = Self => {
Self.remoteMethodCtx('uploadFile', {
@@ -57,96 +54,33 @@ module.exports = Self => {
});
Self.uploadFile = async(ctx, id, options) => {
- const tx = await Self.beginTransaction({});
+ const {Dms, ClaimDms} = Self.app.models;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
- if (!myOptions.transaction)
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
myOptions.transaction = tx;
+ }
- const models = Self.app.models;
- const promises = [];
- const TempContainer = models.TempContainer;
- const ClaimContainer = models.ClaimContainer;
- const fileOptions = {};
- const args = ctx.args;
-
- let srcFile;
try {
- const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId, myOptions);
- if (!hasWriteRole)
- throw new UserError(`You don't have enough privileges`);
+ const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
- // Upload file to temporary path
- const tempContainer = await TempContainer.container('dms');
- const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions);
- const files = Object.values(uploaded.files).map(file => {
- return file[0];
- });
-
- const addedDms = [];
- for (const uploadedFile of files) {
- const newDms = await createDms(ctx, uploadedFile, myOptions);
- const pathHash = ClaimContainer.getHash(newDms.id);
-
- const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
- srcFile = path.join(file.client.root, file.container, file.name);
-
- const claimContainer = await ClaimContainer.container(pathHash);
- const dstFile = path.join(claimContainer.client.root, pathHash, newDms.file);
-
- await fs.move(srcFile, dstFile, {
- overwrite: true
- });
-
- addedDms.push(newDms);
- }
-
- addedDms.forEach(dms => {
- const newClaimDms = models.ClaimDms.create({
- claimFk: id,
- dmsFk: dms.id
- }, myOptions);
-
- promises.push(newClaimDms);
- });
- const resolvedPromises = await Promise.all(promises);
+ const promises = uploadedFiles.map(dms => ClaimDms.create({
+ claimFk: id,
+ dmsFk: dms.id
+ }, myOptions));
+ await Promise.all(promises);
if (tx) await tx.commit();
- return resolvedPromises;
+ return uploadedFiles;
} catch (e) {
if (tx) await tx.rollback();
-
- if (fs.existsSync(srcFile))
- await fs.unlink(srcFile);
-
throw e;
}
};
-
- async function createDms(ctx, file, myOptions) {
- const models = Self.app.models;
- const myUserId = ctx.req.accessToken.userId;
- const args = ctx.args;
-
- const newDms = await models.Dms.create({
- workerFk: myUserId,
- dmsTypeFk: args.dmsTypeId,
- companyFk: args.companyId,
- warehouseFk: args.warehouseId,
- reference: args.reference,
- description: args.description,
- contentType: file.type,
- hasFile: args.hasFile
- }, myOptions);
-
- let fileName = file.name;
- const extension = models.DmsContainer.getFileExtension(fileName);
- fileName = `${newDms.id}.${extension}`;
-
- return newDms.updateAttribute('file', fileName, myOptions);
- }
};
diff --git a/modules/client/back/methods/client-dms/removeFile.js b/modules/client/back/methods/client-dms/removeFile.js
index 786a329289..bc9a0f7194 100644
--- a/modules/client/back/methods/client-dms/removeFile.js
+++ b/modules/client/back/methods/client-dms/removeFile.js
@@ -19,9 +19,8 @@ module.exports = Self => {
});
Self.removeFile = async(ctx, id, options) => {
- const models = Self.app.models;
- let tx;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -34,13 +33,16 @@ module.exports = Self => {
try {
const clientDms = await Self.findById(id, null, myOptions);
- await models.Dms.removeFile(ctx, clientDms.dmsFk, myOptions);
+ const targetDms = await Self.app.models.Dms.removeFile(ctx, clientDms.dmsFk, myOptions);
- const destroyedClient = await clientDms.destroy(myOptions);
+ if (!targetDms || !clientDms)
+ throw new UserError('Try again');
+
+ const clientDmsDestroyed = await clientDms.destroy(myOptions);
if (tx) await tx.commit();
- return destroyedClient;
+ return clientDmsDestroyed;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/client/back/methods/client/filter.js b/modules/client/back/methods/client/filter.js
index 47d5f6d2f5..f805c4be95 100644
--- a/modules/client/back/methods/client/filter.js
+++ b/modules/client/back/methods/client/filter.js
@@ -107,17 +107,29 @@ module.exports = Self => {
return {or: [
{'c.phone': {like: `%${value}%`}},
{'c.mobile': {like: `%${value}%`}},
+ {'a.phone': {like: `%${value}%`}},
]};
case 'zoneFk':
- param = 'a.postalCode';
- return {[param]: {inq: postalCode}};
+ return {'a.postalCode': {inq: postalCode}};
+ case 'city':
+ return {or: [
+ {'c.city': {like: `%${value}%`}},
+ {'a.city': {like: `%${value}%`}}
+ ]};
+ case 'postcode':
+ return {or: [
+ {'c.postcode': value},
+ {'a.postalCode': value}
+ ]};
+ case 'provinceFk':
+ return {or: [
+ {'p.id': value},
+ {'a.provinceFk': value}
+ ]};
case 'name':
case 'salesPersonFk':
case 'fi':
case 'socialName':
- case 'city':
- case 'postcode':
- case 'provinceFk':
case 'email':
param = `c.${param}`;
return {[param]: {like: `%${value}%`}};
@@ -134,24 +146,29 @@ module.exports = Self => {
c.fi,
c.socialName,
c.phone,
+ a.phone,
c.mobile,
c.city,
+ a.city,
c.postcode,
+ a.postalCode,
c.email,
c.isActive,
c.isFreezed,
- p.id AS provinceFk,
+ p.id AS provinceClientFk,
+ a.provinceFk AS provinceAddressFk,
p.name AS province,
u.id AS salesPersonFk,
u.name AS salesPerson
FROM client c
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN province p ON p.id = c.provinceFk
- JOIN vn.address a ON a.clientFk = c.id
+ JOIN address a ON a.clientFk = c.id
`
);
stmt.merge(conn.makeWhere(filter.where));
+ stmt.merge('GROUP BY c.id');
stmt.merge(conn.makePagination(filter));
const clientsIndex = stmts.push(stmt) - 1;
diff --git a/modules/client/back/methods/client/uploadFile.js b/modules/client/back/methods/client/uploadFile.js
index 99ede27c67..1a59659557 100644
--- a/modules/client/back/methods/client/uploadFile.js
+++ b/modules/client/back/methods/client/uploadFile.js
@@ -55,9 +55,9 @@ module.exports = Self => {
});
Self.uploadFile = async(ctx, id, options) => {
- const models = Self.app.models;
- let tx;
+ const {Dms, ClientDms} = Self.app.models;
const myOptions = {};
+ let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
@@ -67,23 +67,20 @@ module.exports = Self => {
myOptions.transaction = tx;
}
- const promises = [];
-
try {
- const uploadedFiles = await models.Dms.uploadFile(ctx, myOptions);
- uploadedFiles.forEach(dms => {
- const newClientDms = models.ClientDms.create({
+ const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
+ const promises = uploadedFiles.map(dms =>
+ ClientDms.create({
clientFk: id,
dmsFk: dms.id
- }, myOptions);
+ }, myOptions)
- promises.push(newClientDms);
- });
- const resolvedPromises = await Promise.all(promises);
+ );
+ await Promise.all(promises);
if (tx) await tx.commit();
- return resolvedPromises;
+ return uploadedFiles;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
index ae9c404afd..96c7893160 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/negativeBases.js
@@ -90,7 +90,7 @@ module.exports = Self => {
AND t.refFk IS NULL
AND c.typeFk IN ('normal','trust')
GROUP BY t.clientFk, negativeBase.taxableBase
- HAVING amount <> 0`, [args.from, args.to]));
+ HAVING amount < 0`, [args.from, args.to]));
stmt = new ParameterizedSQL(`
SELECT f.*
diff --git a/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js b/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js
index d70a8fce52..87e9a67eae 100644
--- a/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js
+++ b/modules/invoiceOut/back/methods/invoiceOut/negativeBasesCsv.js
@@ -10,13 +10,17 @@ module.exports = Self => {
type: 'date',
description: 'From date',
required: true
- },
- {
+ }, {
arg: 'to',
type: 'date',
description: 'To date',
required: true
- }],
+ }, {
+ arg: 'filter',
+ type: 'object',
+ description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string'
+ },
+ ],
returns: [
{
arg: 'body',
diff --git a/modules/item/back/locale/item-shelving/en.yml b/modules/item/back/locale/item-shelving/en.yml
new file mode 100644
index 0000000000..997815b2cf
--- /dev/null
+++ b/modules/item/back/locale/item-shelving/en.yml
@@ -0,0 +1,13 @@
+name: itemShelving
+columns:
+ id: id
+ itemFk: item
+ shelvingFk: shelving
+ visible: visible
+ created: created
+ grouping: grouping
+ packing: packing
+ packagingFk: package
+ userFk: user
+ isChecked: isChecked
+ buyFk: buy
diff --git a/modules/item/back/locale/item-shelving/es.yml b/modules/item/back/locale/item-shelving/es.yml
new file mode 100644
index 0000000000..f00dfd6c5e
--- /dev/null
+++ b/modules/item/back/locale/item-shelving/es.yml
@@ -0,0 +1,13 @@
+name: artículo del carro
+columns:
+ id: id
+ itemFk: artículo
+ shelvingFk: matrícula carro
+ visible: visible
+ created: creado
+ grouping: grouping
+ packing: packing
+ packagingFk: embalaje
+ userFk: usuario
+ isChecked: está revisado
+ buyFk: compra
\ No newline at end of file
diff --git a/modules/route/back/locale/route/en.yml b/modules/route/back/locale/route/en.yml
index 96aaddb72e..d86cbe3422 100644
--- a/modules/route/back/locale/route/en.yml
+++ b/modules/route/back/locale/route/en.yml
@@ -17,3 +17,18 @@ columns:
agencyModeFk: agency
routeFk: route
zoneFk: zone
+ name: name
+ beachFk: beach
+ ticketPacked: tickets packed
+ ticketFree: tickets free
+ ticketProduction: tickets production
+ packages: packages
+ note: note
+ dated: dated
+ dockFk: dock
+ priority: priority
+ etd: etd
+ expeditionTruckFk: truck
+ m3boxes: m3 boxes
+ bufferFk: buffer
+ isPickingAllowed: is picking allowed
\ No newline at end of file
diff --git a/modules/route/back/locale/route/es.yml b/modules/route/back/locale/route/es.yml
index c0a4347917..baefb6433e 100644
--- a/modules/route/back/locale/route/es.yml
+++ b/modules/route/back/locale/route/es.yml
@@ -17,3 +17,18 @@ columns:
agencyModeFk: agencia
routeFk: ruta
zoneFk: zona
+ name: nombre
+ beachFk: playa
+ ticketPacked: tickets encajados
+ ticketFree: tickets libres
+ ticketProduction: tickets producción
+ packages: paquetes
+ note: nota
+ dated: fecha
+ dockFk: muelle
+ priority: prioridad
+ etd: etd
+ expeditionTruckFk: camión
+ m3boxes: m3 cajas
+ bufferFk: buffer
+ isPickingAllowed: está permitido recoger
\ No newline at end of file
diff --git a/modules/shelving/back/models/sector.json b/modules/shelving/back/models/sector.json
index 47d66bd8d0..5ff67491bb 100644
--- a/modules/shelving/back/models/sector.json
+++ b/modules/shelving/back/models/sector.json
@@ -20,18 +20,10 @@
"type": "number",
"required": true
},
- "isPreviousPreparedByPacking": {
- "type": "boolean",
- "required": true
- },
"code": {
"type": "string",
"required": false
},
- "isPreviousPrepared": {
- "type": "boolean",
- "required": true
- },
"isPackagingArea": {
"type": "boolean",
"required": true
diff --git a/modules/ticket/back/methods/ticket-dms/removeFile.js b/modules/ticket/back/methods/ticket-dms/removeFile.js
index f48dc7fef1..6fba4c5527 100644
--- a/modules/ticket/back/methods/ticket-dms/removeFile.js
+++ b/modules/ticket/back/methods/ticket-dms/removeFile.js
@@ -19,7 +19,6 @@ module.exports = Self => {
});
Self.removeFile = async(ctx, id, options) => {
- const models = Self.app.models;
const myOptions = {};
let tx;
@@ -32,18 +31,18 @@ module.exports = Self => {
}
try {
- const targetTicketDms = await models.TicketDms.findById(id, null, myOptions);
- const targetDms = await models.Dms.findById(targetTicketDms.dmsFk, null, myOptions);
- const trashDmsType = await models.DmsType.findOne({where: {code: 'trash'}}, myOptions);
+ const ticketDms = await Self.findById(id, null, myOptions);
- await models.Dms.removeFile(ctx, targetTicketDms.dmsFk, myOptions);
- await targetTicketDms.destroy(myOptions);
+ const targetDms = await Self.app.models.Dms.removeFile(ctx, ticketDms.dmsFk, myOptions);
- await targetDms.updateAttribute('dmsTypeFk', trashDmsType.id, myOptions);
+ if (!targetDms || !ticketDms)
+ throw new UserError('Try again');
+
+ const ticketDmsDestroyed = await ticketDms.destroy(myOptions);
if (tx) await tx.commit();
- return targetDms;
+ return ticketDmsDestroyed;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/ticket/back/methods/ticket/isEditableOrThrow.js b/modules/ticket/back/methods/ticket/isEditableOrThrow.js
index f8285cecd8..41438be3a9 100644
--- a/modules/ticket/back/methods/ticket/isEditableOrThrow.js
+++ b/modules/ticket/back/methods/ticket/isEditableOrThrow.js
@@ -41,7 +41,7 @@ module.exports = Self => {
throw new ForbiddenError(`This ticket is not editable.`);
if (isLocked && !isWeekly)
- throw new ForbiddenError(`This ticket is locked.`);
+ throw new ForbiddenError(`This ticket is locked`);
if (isWeekly && !canEditWeeklyTicket)
throw new ForbiddenError(`You don't have enough privileges.`);
diff --git a/modules/ticket/back/methods/ticket/specs/addSale.spec.js b/modules/ticket/back/methods/ticket/specs/addSale.spec.js
index 8c0e39becf..72c9541d90 100644
--- a/modules/ticket/back/methods/ticket/specs/addSale.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/addSale.spec.js
@@ -89,6 +89,6 @@ describe('ticket addSale()', () => {
error = e;
}
- expect(error.message).toEqual(`This ticket is locked.`);
+ expect(error.message).toEqual(`This ticket is locked`);
});
});
diff --git a/modules/ticket/back/methods/ticket/specs/isEditableOrThrow.spec.js b/modules/ticket/back/methods/ticket/specs/isEditableOrThrow.spec.js
index 6c89bac268..bdf5473251 100644
--- a/modules/ticket/back/methods/ticket/specs/isEditableOrThrow.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/isEditableOrThrow.spec.js
@@ -40,7 +40,7 @@ describe('ticket isEditableOrThrow()', () => {
expect(error.message).toEqual(`This ticket is not editable.`);
});
- it('should throw an error as this ticket is locked.', async() => {
+ it('should throw an error as This ticket is locked', async() => {
const tx = await models.Ticket.beginTransaction({});
let error;
try {
@@ -57,7 +57,7 @@ describe('ticket isEditableOrThrow()', () => {
await tx.rollback();
}
- expect(error.message).toEqual(`This ticket is locked.`);
+ expect(error.message).toEqual(`This ticket is locked`);
});
it('should throw an error as you do not have enough privileges.', async() => {
diff --git a/modules/ticket/back/methods/ticket/specs/recalculateComponents.spec.js b/modules/ticket/back/methods/ticket/specs/recalculateComponents.spec.js
index 383c2c6d51..d358a79f5b 100644
--- a/modules/ticket/back/methods/ticket/specs/recalculateComponents.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/recalculateComponents.spec.js
@@ -39,6 +39,6 @@ describe('ticket recalculateComponents()', () => {
error = e;
}
- expect(error).toEqual(new ForbiddenError(`This ticket is locked.`));
+ expect(error).toEqual(new ForbiddenError(`This ticket is locked`));
});
});
diff --git a/modules/ticket/back/methods/ticket/specs/transferClient.spec.js b/modules/ticket/back/methods/ticket/specs/transferClient.spec.js
index c09c20083d..5a9edd17e4 100644
--- a/modules/ticket/back/methods/ticket/specs/transferClient.spec.js
+++ b/modules/ticket/back/methods/ticket/specs/transferClient.spec.js
@@ -23,7 +23,7 @@ describe('Ticket transferClient()', () => {
error = e;
}
- expect(error.message).toEqual(`This ticket is locked.`);
+ expect(error.message).toEqual(`This ticket is locked`);
});
it('should be assigned a different clientFk', async() => {
diff --git a/modules/ticket/back/methods/ticket/uploadFile.js b/modules/ticket/back/methods/ticket/uploadFile.js
index 4de9904e14..5b79aa9737 100644
--- a/modules/ticket/back/methods/ticket/uploadFile.js
+++ b/modules/ticket/back/methods/ticket/uploadFile.js
@@ -47,7 +47,7 @@ module.exports = Self => {
});
Self.uploadFile = async(ctx, id, options) => {
- const models = Self.app.models;
+ const {Dms, TicketDms} = Self.app.models;
const myOptions = {};
let tx;
@@ -59,22 +59,19 @@ module.exports = Self => {
myOptions.transaction = tx;
}
- const promises = [];
try {
- const uploadedFiles = await models.Dms.uploadFile(ctx, myOptions);
- uploadedFiles.forEach(dms => {
- const newTicketDms = models.TicketDms.create({
- ticketFk: id,
- dmsFk: dms.id
- }, myOptions);
+ const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
- promises.push(newTicketDms);
- });
- const resolvedPromises = await Promise.all(promises);
+ const promises = uploadedFiles.map(dms => TicketDms.create({
+ ticketFk: id,
+ dmsFk: dms.id
+ }, myOptions));
+
+ await Promise.all(promises);
if (tx) await tx.commit();
- return resolvedPromises;
+ return uploadedFiles;
} catch (e) {
if (tx) await tx.rollback();
throw e;
diff --git a/modules/ticket/back/models/ticket-state.json b/modules/ticket/back/models/ticket-state.json
index 3ee50fac06..3dd3229397 100644
--- a/modules/ticket/back/models/ticket-state.json
+++ b/modules/ticket/back/models/ticket-state.json
@@ -34,9 +34,9 @@
"foreignKey": "stateFk"
},
"user": {
- "type": "belongsTo",
- "model": "VnUser",
- "foreignKey": "userFk"
- }
+ "type": "belongsTo",
+ "model": "VnUser",
+ "foreignKey": "workerFk"
+ }
}
}
diff --git a/modules/ticket/back/models/ticket-tracking.json b/modules/ticket/back/models/ticket-tracking.json
index 8b0617145c..ac0eb9a699 100644
--- a/modules/ticket/back/models/ticket-tracking.json
+++ b/modules/ticket/back/models/ticket-tracking.json
@@ -20,9 +20,6 @@
},
"stateFk": {
"type": "number"
- },
- "userFk": {
- "type": "number"
}
},
"relations": {
@@ -37,9 +34,9 @@
"foreignKey": "stateFk"
},
"user": {
- "type": "belongsTo",
- "model": "VnUser",
- "foreignKey": "userFk"
- }
+ "type": "belongsTo",
+ "model": "VnUser",
+ "foreignKey": "userFk"
+ }
}
}
diff --git a/modules/ticket/front/tracking/index/index.html b/modules/ticket/front/tracking/index/index.html
index 12c4778c99..10ee6d8488 100644
--- a/modules/ticket/front/tracking/index/index.html
+++ b/modules/ticket/front/tracking/index/index.html
@@ -23,9 +23,9 @@
{{::tracking.state.name}}
- {{::tracking.worker.user.name || 'System' | translate}}
+ ng-class="{'link': tracking.user.id}"
+ ng-click="workerDescriptor.show($event, tracking.user.id)">
+ {{::tracking.user.name || 'System' | translate}}
{{::tracking.created | date:'dd/MM/yyyy HH:mm'}}
diff --git a/modules/worker/back/methods/calendar/absences.js b/modules/worker/back/methods/calendar/absences.js
index 8420ed7705..8a5d189871 100644
--- a/modules/worker/back/methods/calendar/absences.js
+++ b/modules/worker/back/methods/calendar/absences.js
@@ -39,6 +39,7 @@ module.exports = Self => {
started.setFullYear(year);
started.setMonth(0);
started.setDate(1);
+ started.setHours(0, 0, 0, 0);
const ended = Date.vnNew();
ended.setFullYear(year);
diff --git a/modules/worker/back/methods/worker-dms/removeFile.js b/modules/worker/back/methods/worker-dms/removeFile.js
index b441c56ce1..8eb6c05fde 100644
--- a/modules/worker/back/methods/worker-dms/removeFile.js
+++ b/modules/worker/back/methods/worker-dms/removeFile.js
@@ -18,13 +18,35 @@ module.exports = Self => {
}
});
- Self.removeFile = async(ctx, id) => {
- const models = Self.app.models;
- const workerDms = await Self.findById(id);
+ Self.removeFile = async(ctx, dmsFk, options) => {
+ const myOptions = {};
+ let tx;
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
- await models.Dms.removeFile(ctx, workerDms.dmsFk);
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
- return workerDms.destroy();
+ try {
+ const WorkerDms = await Self.findOne({
+ where: {document: dmsFk}
+ }, myOptions);
+
+ const targetDms = await Self.app.models.Dms.removeFile(ctx, dmsFk, myOptions);
+
+ if (!targetDms || !WorkerDms)
+ throw new UserError('Try again');
+
+ const workerDmsDestroyed = await WorkerDms.destroy(myOptions);
+ if (tx) await tx.commit();
+
+ return workerDmsDestroyed;
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
};
};
diff --git a/modules/worker/back/methods/worker-time-control/addTimeEntry.js b/modules/worker/back/methods/worker-time-control/addTimeEntry.js
index cc652fb90b..5dbac51ca2 100644
--- a/modules/worker/back/methods/worker-time-control/addTimeEntry.js
+++ b/modules/worker/back/methods/worker-time-control/addTimeEntry.js
@@ -43,16 +43,9 @@ module.exports = Self => {
const isTeamBoss = await models.ACL.checkAccessAcl(ctx, 'Worker', 'isTeamBoss', 'WRITE');
const isHimself = userId == workerId;
- if (!isSubordinate || (isSubordinate && isHimself && !isTeamBoss))
+ if (!isSubordinate || (isHimself && !isTeamBoss))
throw new UserError(`You don't have enough privileges`);
- query = `CALL vn.workerTimeControl_clockIn(?,?,?)`;
- const [response] = await Self.rawSql(query, [workerId, args.timed, args.direction], myOptions);
- if (response[0] && response[0].error)
- throw new UserError(response[0].error);
-
- await models.WorkerTimeControl.resendWeeklyHourEmail(ctx, workerId, args.timed, myOptions);
-
- return response;
+ return Self.clockIn(workerId, args.timed, args.direction, myOptions);
};
};
diff --git a/modules/worker/back/methods/worker-time-control/clockIn.js b/modules/worker/back/methods/worker-time-control/clockIn.js
new file mode 100644
index 0000000000..44e0c547ae
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/clockIn.js
@@ -0,0 +1,45 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethod('clockIn', {
+ description: 'Check if the employee can clock in',
+ accessType: 'WRITE',
+ accepts: [
+ {
+ arg: 'workerFk',
+ type: 'number',
+ required: true,
+ },
+ {
+ arg: 'timed',
+ type: 'date'
+ },
+ {
+ arg: 'direction',
+ type: 'string'
+ },
+
+ ],
+ http: {
+ path: `/clockIn`,
+ verb: 'POST'
+ },
+ returns: {
+ type: 'Object',
+ root: true
+ }
+ });
+
+ Self.clockIn = async(workerFk, timed, direction, options) => {
+ const myOptions = {};
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const query = 'CALL vn.workerTimeControl_clockIn(?, ?, ?)';
+ const [[response]] = await Self.rawSql(query, [workerFk, timed, direction], myOptions);
+ if (response && response.error)
+ throw new UserError(response.error);
+
+ return response;
+ };
+};
diff --git a/modules/worker/back/methods/worker-time-control/getClockIn.js b/modules/worker/back/methods/worker-time-control/getClockIn.js
new file mode 100644
index 0000000000..4707006439
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/getClockIn.js
@@ -0,0 +1,32 @@
+module.exports = Self => {
+ Self.remoteMethod('getClockIn', {
+ description: 'Shows the clockings for each day, in columns per day',
+ accessType: 'READ',
+ accepts: [
+ {
+ arg: 'workerFk',
+ type: 'int',
+ required: true,
+ },
+
+ ],
+ http: {
+ path: `/getClockIn`,
+ verb: 'GET'
+ },
+ returns: {
+ type: ['Object'],
+ root: true
+ },
+ });
+
+ Self.getClockIn = async(workerFk, options) => {
+ const myOptions = {};
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const query = `CALL vn.workerTimeControl_getClockIn(?, ?)`;
+ const [result] = await Self.rawSql(query, [workerFk, Date.vnNew()], myOptions);
+ return result;
+ };
+};
diff --git a/modules/worker/back/methods/worker-time-control/login.js b/modules/worker/back/methods/worker-time-control/login.js
new file mode 100644
index 0000000000..9aa4bd1452
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/login.js
@@ -0,0 +1,35 @@
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('login', {
+ description: 'Consult the user\'s information and the buttons that must be activated after logging in',
+ accessType: 'READ',
+ accepts: [
+ {
+ arg: 'pin',
+ type: 'string',
+ required: true
+ },
+ ],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/login`,
+ verb: 'POST'
+ }
+ });
+
+ Self.login = async(ctx, pin, options) => {
+ const myOptions = {};
+ const $t = ctx.req.__;
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const query = `CALL vn.workerTimeControl_login(?)`;
+ const [[user]] = await Self.rawSql(query, [pin], myOptions);
+ if (!user) throw new UserError($t('Incorrect pin'));
+ return user;
+ };
+};
diff --git a/modules/worker/back/methods/worker-time-control/resendWeeklyHourEmail.js b/modules/worker/back/methods/worker-time-control/resendWeeklyHourEmail.js
index 2452a29f9c..8964584551 100644
--- a/modules/worker/back/methods/worker-time-control/resendWeeklyHourEmail.js
+++ b/modules/worker/back/methods/worker-time-control/resendWeeklyHourEmail.js
@@ -1,6 +1,6 @@
module.exports = Self => {
Self.remoteMethodCtx('resendWeeklyHourEmail', {
- description: 'Adds a new hour registry',
+ description: 'Send the records for the week of the date provided',
accessType: 'WRITE',
accepts: [{
arg: 'id',
diff --git a/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js b/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js
new file mode 100644
index 0000000000..9cd3ed1c06
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/specs/clockIn.spec.js
@@ -0,0 +1,581 @@
+const models = require('vn-loopback/server/server').models;
+const LoopBackContext = require('loopback-context');
+
+describe('workerTimeControl clockIn()', () => {
+ const workerId = 9;
+ const salesBossId = 19;
+ const hankPymId = 1107;
+ const jessicaJonesId = 1110;
+ const HHRRId = 37;
+ const teamBossId = 13;
+ const monday = 1;
+ const tuesday = 2;
+ const thursday = 4;
+ const friday = 5;
+ const sunday = 7;
+ const inTime = '2001-01-01T00:00:00.000Z';
+ const activeCtx = {
+ accessToken: {userId: 50},
+ };
+ const ctx = {req: activeCtx};
+
+ beforeAll(() => {
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: activeCtx
+ });
+ });
+
+ it('should correctly clock in', async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+
+ try {
+ const options = {transaction: tx};
+ await models.WorkerTimeControl.clockIn(workerId, inTime, 'in', options);
+ const isClockIn = await models.WorkerTimeControl.findOne({
+ where: {
+ userFk: workerId
+ }
+ }, options);
+
+ expect(isClockIn).toBeDefined();
+ expect(isClockIn.direction).toBe('in');
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ describe('as Role errors', () => {
+ it('should add if the current user is team boss and the target user is himself', async() => {
+ activeCtx.accessToken.userId = teamBossId;
+ const workerId = teamBossId;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
+
+ const todayAtOne = Date.vnNew();
+ todayAtOne.setHours(1, 0, 0, 0);
+
+ ctx.args = {timed: todayAtOne, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ expect(createdTimeEntry.id).toBeDefined();
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('should delete the created time entry for the team boss as himself', async() => {
+ activeCtx.accessToken.userId = teamBossId;
+ const workerId = teamBossId;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
+
+ const todayAtOne = Date.vnNew();
+ todayAtOne.setHours(1, 0, 0, 0);
+
+ ctx.args = {timed: todayAtOne, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ expect(createdTimeEntry.id).toBeDefined();
+
+ await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
+
+ const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
+
+ expect(deletedTimeEntry).toBeNull();
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+
+ it('should edit the created time entry for the team boss as HHRR', async() => {
+ activeCtx.accessToken.userId = HHRRId;
+ const workerId = teamBossId;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ try {
+ const options = {transaction: tx};
+
+ const todayAtOne = Date.vnNew();
+ todayAtOne.setHours(1, 0, 0, 0);
+
+ ctx.args = {timed: todayAtOne, direction: 'in'};
+ const createdTimeEntry = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ expect(createdTimeEntry.id).toBeDefined();
+
+ ctx.args = {direction: 'out'};
+ const updatedTimeEntry = await models.WorkerTimeControl.updateTimeEntry(
+ ctx, createdTimeEntry.id, options
+ );
+
+ expect(updatedTimeEntry.direction).toEqual('out');
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ throw e;
+ }
+ });
+ });
+
+ describe('as saleBoss editor', () => {
+ let workerId;
+ beforeEach(() => {
+ activeCtx.accessToken.userId = salesBossId;
+ workerId = hankPymId;
+ });
+
+ it('should fail to add a time entry if the target user has an absence that day', async() => {
+ const date = Date.vnNew();
+ date.setHours(8, 0, 0);
+ date.setDate(date.getDate() - 16);
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+ try {
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`No está permitido trabajar`);
+ });
+
+ it('should fail to add a time entry for a worker without an existing contract', async() => {
+ const date = Date.vnNew();
+ date.setFullYear(date.getFullYear() - 2);
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+ try {
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`No hay un contrato en vigor`);
+ });
+
+ it('should fail to add a time entry for a worker without an existing contract and exceeding time', async() => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 2);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+ date.setHours(0, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date.setHours(20, 0, 1);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Superado el tiempo máximo entre entrada y salida`);
+ });
+
+ describe('direction errors', () => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 1);
+ let error;
+ it('should throw an error when trying "in" direction twice', async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date.setHours(10, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Dirección incorrecta`);
+ });
+
+ it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ date.setHours(9, 0, 0);
+ ctx.args = {timed: date, direction: 'middle'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date.setHours(10, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Dirección incorrecta`);
+ });
+
+ it('Should throw an error when trying "out" before closing a "middle" couple', async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ date.setHours(9, 0, 0);
+ ctx.args = {timed: date, direction: 'middle'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date.setHours(10, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Dirección incorrecta`);
+ });
+
+ it('should throw an error when trying "middle" after "out"', async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ date.setHours(9, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date.setHours(10, 0, 0);
+ ctx.args = {timed: date, direction: 'middle'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Dirección incorrecta`);
+ });
+
+ it('should throw an error when trying "out" direction twice', async() => {
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ date.setHours(9, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date.setHours(10, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Dirección incorrecta`);
+ });
+ });
+
+ describe('12h rest', () => {
+ activeCtx.accessToken.userId = salesBossId;
+ const workerId = hankPymId;
+ it('should throw an error when the 12h rest is not fulfilled yet', async() => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 2);
+ date = weekDay(date, monday);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ date.setHours(16, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date = weekDay(date, tuesday);
+ date.setHours(4, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Descanso diario`);
+ });
+
+ it('should not fail as the 12h rest is fulfilled', async() => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 2);
+ date = weekDay(date, monday);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ date.setHours(16, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date = weekDay(date, tuesday);
+ date.setHours(4, 1, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).not.toBeDefined;
+ });
+ });
+
+ describe('for 3500kg drivers with enforced 9h rest', () => {
+ activeCtx.accessToken.userId = salesBossId;
+ const workerId = jessicaJonesId;
+ it('should throw an error when the 9h enforced rest is not fulfilled', async() => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 2);
+ date = weekDay(date, monday);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ date.setHours(16, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date = weekDay(date, tuesday);
+ date.setHours(1, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Descanso diario`);
+ });
+
+ it('should not fail when the 9h enforced rest is fulfilled', async() => {
+ let date = Date.vnNew();
+ date.setDate(date.getDate() - 2);
+ date = weekDay(date, monday);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ date.setHours(8, 0, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ date.setHours(16, 0, 0);
+ ctx.args = {timed: date, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ try {
+ date = weekDay(date, tuesday);
+ date.setHours(1, 1, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).not.toBeDefined;
+ });
+ });
+
+ describe('for 72h weekly rest', () => {
+
+ it('should throw an error when work 11 consecutive days', async() => {
+ let date = Date.vnNew();
+ date.setMonth(date.getMonth() - 1);
+ date.setDate(1);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ await populateWeek(date, monday, sunday, ctx, workerId, options);
+ date = nextWeek(date);
+ await populateWeek(date, monday, thursday, ctx, workerId, options);
+ try {
+ date = weekDay(date, friday);
+ date.setHours(10, 0, 1);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Descanso semanal`);
+ });
+
+ it('should throw an error when the 72h weekly rest is not fulfilled', async() => {
+
+ let date = Date.vnNew();
+ date.setMonth(date.getMonth() - 1);
+ date.setDate(1);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ await populateWeek(date, monday, sunday, ctx, workerId, options);
+ date = nextWeek(date);
+ await populateWeek(date, monday, thursday, ctx, workerId, options);
+
+ try {
+ date = weekDay(date, sunday);
+ date.setHours(17, 59, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toBe(`Descanso semanal`);
+ });
+
+ it('should throw an error when the 72h weekly rest is fulfilled', async() => {
+
+ let date = Date.vnNew();
+ date.setMonth(date.getMonth() - 1);
+ date.setDate(1);
+ let error;
+
+ const tx = await models.WorkerTimeControl.beginTransaction({});
+ const options = {transaction: tx};
+
+ await populateWeek(date, monday, sunday, ctx, workerId, options);
+ date = nextWeek(date);
+ await populateWeek(date, monday, thursday, ctx, workerId, options);
+
+ try {
+ date = weekDay(date, sunday);
+ date.setHours(18, 00, 0);
+ ctx.args = {timed: date, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).not.toBeDefined;
+ });
+ });
+ });
+});
+
+function weekDay(date, dayToSet) {
+ const currentDay = date.getDay();
+ const distance = dayToSet - currentDay;
+
+ date.setDate(date.getDate() + distance);
+ return date;
+}
+
+function nextWeek(date) {
+ const sunday = 7;
+ const currentDay = date.getDay();
+ let newDate = date;
+ if (currentDay != 0)
+ newDate = weekDay(date, sunday);
+
+ newDate.setDate(newDate.getDate() + 1);
+ return newDate;
+}
+
+async function populateWeek(date, dayStart, dayEnd, ctx, workerId, options) {
+ const dateStart = new Date(weekDay(date, dayStart));
+ const dateEnd = new Date(dateStart);
+ dateEnd.setDate(dateStart.getDate() + dayEnd);
+
+ for (let i = dayStart; i <= dayEnd; i++) {
+ dateStart.setHours(10, 0, 0);
+ ctx.args = {timed: dateStart, direction: 'in'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ dateStart.setHours(18, 0, 0);
+ ctx.args = {timed: dateStart, direction: 'out'};
+ await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ dateStart.setDate(dateStart.getDate() + 1);
+ }
+}
diff --git a/modules/worker/back/methods/worker-time-control/specs/getClockIn.spec.js b/modules/worker/back/methods/worker-time-control/specs/getClockIn.spec.js
new file mode 100644
index 0000000000..d75ffac704
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/specs/getClockIn.spec.js
@@ -0,0 +1,16 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('workerTimeControl getClockIn()', () => {
+ it('should correctly get the timetable of a worker', async() => {
+ const response = await models.WorkerTimeControl.getClockIn(1106, {});
+
+ expect(response.length).toEqual(4);
+ const [inHrs, middleOutHrs, middleInHrs, outHrs] = response;
+
+ expect(inHrs['0daysAgo']).toEqual('07:00');
+ expect(middleOutHrs['0daysAgo']).toEqual('10:00');
+ expect(middleInHrs['0daysAgo']).toEqual('10:20');
+ expect(outHrs['0daysAgo']).toEqual('14:50');
+ });
+});
+
diff --git a/modules/worker/back/methods/worker-time-control/specs/login.spec.js b/modules/worker/back/methods/worker-time-control/specs/login.spec.js
new file mode 100644
index 0000000000..d9f2dbb393
--- /dev/null
+++ b/modules/worker/back/methods/worker-time-control/specs/login.spec.js
@@ -0,0 +1,34 @@
+const models = require('vn-loopback/server/server').models;
+const LoopBackContext = require('loopback-context');
+const UserError = require('vn-loopback/util/user-error');
+
+describe('workerTimeControl login()', () => {
+ let ctx;
+ beforeAll(async() => {
+ ctx = {
+ accessToken: {userId: 9},
+ req: {
+ headers: {origin: 'http://localhost'},
+ __: key => key
+ }
+ };
+ spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
+ active: ctx
+ });
+ });
+
+ it('should correctly login', async() => {
+ const response = await models.WorkerTimeControl.login(ctx, 9);
+
+ expect(response.name).toBe('developer');
+ });
+
+ it('should throw UserError if pin is not provided', async() => {
+ try {
+ await models.WorkerTimeControl.login(ctx);
+ } catch (error) {
+ expect(error).toBeInstanceOf(UserError);
+ expect(error.message).toBe('Incorrect pin');
+ }
+ });
+});
diff --git a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
index 42ec6290a2..92c01792f5 100644
--- a/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
+++ b/modules/worker/back/methods/worker-time-control/specs/timeEntry.spec.js
@@ -3,18 +3,10 @@ const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('workerTimeControl add/delete timeEntry()', () => {
- const HHRRId = 37;
- const teamBossId = 13;
const employeeId = 1;
- const salesPersonId = 1106;
const salesBossId = 19;
const hankPymId = 1107;
- const jessicaJonesId = 1110;
const monday = 1;
- const tuesday = 2;
- const thursday = 4;
- const friday = 5;
- const sunday = 7;
const activeCtx = {
accessToken: {userId: 50},
};
@@ -61,560 +53,11 @@ describe('workerTimeControl add/delete timeEntry()', () => {
expect(error.statusCode).toBe(400);
expect(error.message).toBe(`You don't have enough privileges`);
});
-
- it('should add if the current user is team boss and the target user is himself', async() => {
- activeCtx.accessToken.userId = teamBossId;
- const workerId = teamBossId;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- try {
- const options = {transaction: tx};
-
- const todayAtOne = Date.vnNew();
- todayAtOne.setHours(1, 0, 0, 0);
-
- ctx.args = {timed: todayAtOne, direction: 'in'};
- const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- expect(createdTimeEntry.id).toBeDefined();
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should try but fail to delete his own time entry', async() => {
- activeCtx.accessToken.userId = salesBossId;
- const workerId = salesBossId;
-
- let error;
- const tx = await models.WorkerTimeControl.beginTransaction({});
- try {
- const options = {transaction: tx};
-
- const todayAtOne = Date.vnNew();
- todayAtOne.setHours(1, 0, 0, 0);
-
- ctx.args = {timed: todayAtOne, direction: 'in'};
- const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- activeCtx.accessToken.userId = salesPersonId;
- await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
-
- await tx.rollback();
- } catch (e) {
- error = e;
- await tx.rollback();
- }
-
- expect(error).toBeDefined();
- expect(error.statusCode).toBe(400);
- expect(error.message).toBe(`You don't have enough privileges`);
- });
-
- it('should delete the created time entry for the team boss as himself', async() => {
- activeCtx.accessToken.userId = teamBossId;
- const workerId = teamBossId;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- try {
- const options = {transaction: tx};
-
- const todayAtOne = Date.vnNew();
- todayAtOne.setHours(1, 0, 0, 0);
-
- ctx.args = {timed: todayAtOne, direction: 'in'};
- const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- expect(createdTimeEntry.id).toBeDefined();
-
- await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
-
- const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
-
- expect(deletedTimeEntry).toBeNull();
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should delete the created time entry for the team boss as HHRR', async() => {
- activeCtx.accessToken.userId = HHRRId;
- const workerId = teamBossId;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- try {
- const options = {transaction: tx};
-
- const todayAtOne = Date.vnNew();
- todayAtOne.setHours(1, 0, 0, 0);
-
- ctx.args = {timed: todayAtOne, direction: 'in'};
- const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- expect(createdTimeEntry.id).toBeDefined();
-
- await models.WorkerTimeControl.deleteTimeEntry(ctx, createdTimeEntry.id, options);
-
- const deletedTimeEntry = await models.WorkerTimeControl.findById(createdTimeEntry.id, null, options);
-
- expect(deletedTimeEntry).toBeNull();
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
-
- it('should edit the created time entry for the team boss as HHRR', async() => {
- activeCtx.accessToken.userId = HHRRId;
- const workerId = teamBossId;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- try {
- const options = {transaction: tx};
-
- const todayAtOne = Date.vnNew();
- todayAtOne.setHours(1, 0, 0, 0);
-
- ctx.args = {timed: todayAtOne, direction: 'in'};
- const [createdTimeEntry] = await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- expect(createdTimeEntry.id).toBeDefined();
-
- ctx.args = {direction: 'out'};
- const updatedTimeEntry = await models.WorkerTimeControl.updateTimeEntry(ctx, createdTimeEntry.id, options);
-
- expect(updatedTimeEntry.direction).toEqual('out');
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
- });
});
describe('WorkerTimeControl_clockIn calls', () => {
- let workerId;
- beforeEach(() => {
- activeCtx.accessToken.userId = salesBossId;
- workerId = hankPymId;
- });
- it('should fail to add a time entry if the target user has an absence that day', async() => {
- const date = Date.vnNew();
- date.setHours(8, 0, 0);
- date.setDate(date.getDate() - 16);
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
- try {
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
+ beforeEach(() => activeCtx.accessToken.userId = salesBossId);
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`No está permitido trabajar`);
- });
-
- it('should fail to add a time entry for a worker without an existing contract', async() => {
- const date = Date.vnNew();
- date.setFullYear(date.getFullYear() - 2);
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
- try {
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`No hay un contrato en vigor`);
- });
-
- it('should fail to add a time entry for a worker without an existing contract', async() => {
- let date = Date.vnNew();
- date.setDate(date.getDate() - 2);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
- date.setHours(0, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date.setHours(20,0, 1);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Superado el tiempo máximo entre entrada y salida`);
- });
-
- describe('direction errors', () => {
- let date = Date.vnNew();
- date.setDate(date.getDate() - 1);
- let error;
- it('should throw an error when trying "in" direction twice', async() => {
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date.setHours(10, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Dirección incorrecta`);
- });
-
- it('should throw an error when trying "in" direction after insert "in" and "middle"', async() => {
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- date.setHours(9, 0, 0);
- ctx.args = {timed: date, direction: 'middle'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date.setHours(10, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Dirección incorrecta`);
- });
-
- it('Should throw an error when trying "out" before closing a "middle" couple', async() => {
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- date.setHours(9, 0, 0);
- ctx.args = {timed: date, direction: 'middle'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date.setHours(10, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Dirección incorrecta`);
- });
-
- it('should throw an error when trying "middle" after "out"', async() => {
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- date.setHours(9, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date.setHours(10, 0, 0);
- ctx.args = {timed: date, direction: 'middle'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Dirección incorrecta`);
- });
-
- it('should throw an error when trying "out" direction twice', async() => {
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- date.setHours(9, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date.setHours(10, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Dirección incorrecta`);
- });
- });
-
- describe('12h rest', () => {
- activeCtx.accessToken.userId = salesBossId;
- const workerId = hankPymId;
- it('should throw an error when the 12h rest is not fulfilled yet', async() => {
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 2);
- date = weekDay(date, monday);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- date.setHours(16, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date = weekDay(date, tuesday);
- date.setHours(4, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Descanso diario`);
- });
-
- it('should not fail as the 12h rest is fulfilled', async() => {
- let date = Date.vnNew();
- date.setDate(date.getDate() - 2);
- date = weekDay(date, monday);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- date.setHours(16, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date = weekDay(date, tuesday);
- date.setHours(4, 1, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error).not.toBeDefined;
- });
- });
-
- describe('for 3500kg drivers with enforced 9h rest', () => {
- activeCtx.accessToken.userId = salesBossId;
- const workerId = jessicaJonesId;
- it('should throw an error when the 9h enforced rest is not fulfilled', async() => {
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 2);
- date = weekDay(date, monday);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- date.setHours(16, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date = weekDay(date, tuesday);
- date.setHours(1, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Descanso diario`);
- });
-
- it('should not fail when the 9h enforced rest is fulfilled', async() => {
-
- let date = Date.vnNew();
- date.setDate(date.getDate() - 2);
- date = weekDay(date, monday);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- date.setHours(8, 0, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- date.setHours(16, 0, 0);
- ctx.args = {timed: date, direction: 'out'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- try {
- date = weekDay(date, tuesday);
- date.setHours(1, 1, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
-
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error).not.toBeDefined;
- });
- });
-
- describe('for 72h weekly rest', () => {
-
- it('should throw an error when work 11 consecutive days', async() => {
- let date = Date.vnNew();
- date.setMonth(date.getMonth() - 1);
- date.setDate(1);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- await populateWeek(date, monday, sunday, ctx, workerId, options);
- date = nextWeek(date);
- await populateWeek(date, monday, thursday, ctx, workerId, options);
- try {
- date = weekDay(date, friday);
- date.setHours(10, 0, 1);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Descanso semanal`);
- });
-
- it('should throw an error when the 72h weekly rest is not fulfilled', async() => {
-
- let date = Date.vnNew();
- date.setMonth(date.getMonth() - 1);
- date.setDate(1);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- await populateWeek(date, monday, sunday, ctx, workerId, options);
- date = nextWeek(date);
- await populateWeek(date, monday, thursday, ctx, workerId, options);
-
- try {
- date = weekDay(date, sunday);
- date.setHours(17, 59, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error.message).toBe(`Descanso semanal`);
- });
-
- it('should throw an error when the 72h weekly rest is fulfilled', async() => {
-
- let date = Date.vnNew();
- date.setMonth(date.getMonth() - 1);
- date.setDate(1);
- let error;
-
- const tx = await models.WorkerTimeControl.beginTransaction({});
- const options = {transaction: tx};
-
- await populateWeek(date, monday, sunday, ctx, workerId, options);
- date = nextWeek(date);
- await populateWeek(date, monday, thursday, ctx, workerId, options);
-
- try {
- date = weekDay(date, sunday);
- date.setHours(18, 00, 0);
- ctx.args = {timed: date, direction: 'in'};
- await models.WorkerTimeControl.addTimeEntry(ctx, workerId, options);
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- error = e;
- }
-
- expect(error).not.toBeDefined;
- });
- });
-
describe('WorkerTimeControl_calculate calls', () => {
let dated = Date.vnNew();
dated.setDate(dated.getDate() - 7);
@@ -836,25 +279,6 @@ function weekDay(date, dayToSet) {
return date;
}
-function nextWeek(date) {
- const sunday = 7;
- const currentDay = date.getDay();
- let newDate = date;
- if (currentDay != 0)
- newDate = weekDay(date, sunday);
-
- newDate.setDate(newDate.getDate() + 1);
- return newDate;
-}
-
-function lastWeek(date) {
- const monday = 1;
- newDate = weekDay(date, monday);
-
- newDate.setDate(newDate.getDate() - 1);
- return newDate;
-}
-
async function populateWeek(date, dayStart, dayEnd, ctx, workerId, options) {
const dateStart = new Date(weekDay(date, dayStart));
const dateEnd = new Date(dateStart);
diff --git a/modules/worker/back/methods/worker/uploadFile.js b/modules/worker/back/methods/worker/uploadFile.js
index 588cfe4bde..c3526cbb18 100644
--- a/modules/worker/back/methods/worker/uploadFile.js
+++ b/modules/worker/back/methods/worker/uploadFile.js
@@ -47,30 +47,33 @@ module.exports = Self => {
});
Self.uploadFile = async(ctx, id) => {
- const models = Self.app.models;
- const promises = [];
- const tx = await Self.beginTransaction({});
+ const {Dms, WorkerDms} = Self.app.models;
+ const myOptions = {};
+ let tx;
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction) {
+ tx = await Self.beginTransaction({});
+ myOptions.transaction = tx;
+ }
try {
- const options = {transaction: tx};
-
- const uploadedFiles = await models.Dms.uploadFile(ctx, options);
- uploadedFiles.forEach(dms => {
- const newWorkerDms = models.WorkerDms.create({
+ const uploadedFiles = await Dms.uploadFile(ctx, myOptions);
+ const promises = uploadedFiles.map(dms =>
+ WorkerDms.create({
workerFk: id,
dmsFk: dms.id
- }, options);
+ }, myOptions));
+ await Promise.all(promises);
- promises.push(newWorkerDms);
- });
- const resolvedPromises = await Promise.all(promises);
+ if (tx) await tx.commit();
- await tx.commit();
-
- return resolvedPromises;
- } catch (err) {
- await tx.rollback();
- throw err;
+ return uploadedFiles;
+ } catch (e) {
+ if (tx) await tx.rollback();
+ throw e;
}
};
};
diff --git a/modules/worker/back/models/worker-time-control.js b/modules/worker/back/models/worker-time-control.js
index d5da680cf8..1457c7a462 100644
--- a/modules/worker/back/models/worker-time-control.js
+++ b/modules/worker/back/models/worker-time-control.js
@@ -10,6 +10,9 @@ module.exports = Self => {
require('../methods/worker-time-control/weeklyHourRecordEmail')(Self);
require('../methods/worker-time-control/getMailStates')(Self);
require('../methods/worker-time-control/resendWeeklyHourEmail')(Self);
+ require('../methods/worker-time-control/login')(Self);
+ require('../methods/worker-time-control/getClockIn')(Self);
+ require('../methods/worker-time-control/clockIn')(Self);
Self.rewriteDbError(function(err) {
if (err.code === 'ER_DUP_ENTRY')
diff --git a/package-lock.json b/package-lock.json
index 78ef939870..012fb50e75 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "salix-back",
- "version": "23.50.01",
+ "version": "24.02.01",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "salix-back",
- "version": "23.50.01",
+ "version": "24.02.01",
"license": "GPL-3.0",
"dependencies": {
"axios": "^1.2.2",
diff --git a/package.json b/package.json
index 66a5cd2fa6..ab3d99e19f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "salix-back",
- "version": "23.52.01",
+ "version": "24.02.01",
"author": "Verdnatura Levante SL",
"description": "Salix backend",
"license": "GPL-3.0",