Merge dev - test #1881
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -14,14 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [2348.01] - 2023-11-30
|
## [2348.01] - 2023-11-30
|
||||||
|
|
||||||
### Added
|
### Características Añadidas 🆕
|
||||||
- (Ticket -> Adelantar) Permite mover lineas sin generar negativos
|
- **Tickets → Adelantar:** Permite mover lineas sin generar negativos
|
||||||
- (Ticket -> Adelantar) Permite modificar la fecha de los tickets
|
- **Tickets → Adelantar:** Permite modificar la fecha de los tickets
|
||||||
- (Trabajadores -> Notificaciones) Nueva sección (lilium)
|
- **Trabajadores → Notificaciones:** Nueva sección (lilium)
|
||||||
|
|
||||||
### Changed
|
### Correcciones 🛠️
|
||||||
### Fixed
|
- **Tickets → RocketChat:** Arreglada detección de cambios
|
||||||
- (Ticket -> RocketChat) Arreglada detección de cambios
|
|
||||||
|
|
||||||
|
|
||||||
## [2346.01] - 2023-11-16
|
## [2346.01] - 2023-11-16
|
||||||
|
|
|
@ -49,13 +49,7 @@ module.exports = Self => {
|
||||||
if (vnUser.twoFactor)
|
if (vnUser.twoFactor)
|
||||||
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
||||||
}
|
}
|
||||||
const validateLogin = await Self.validateLogin(user, password);
|
return Self.validateLogin(user, password, ctx);
|
||||||
await Self.app.models.SignInLog.create({
|
|
||||||
token: validateLogin.token,
|
|
||||||
userFk: vnUser.id,
|
|
||||||
ip: ctx.req.ip
|
|
||||||
});
|
|
||||||
return validateLogin;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.passExpired = async vnUser => {
|
Self.passExpired = async vnUser => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ const {models} = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('VnUser Sign-in()', () => {
|
describe('VnUser Sign-in()', () => {
|
||||||
const employeeId = 1;
|
const employeeId = 1;
|
||||||
const unauthCtx = {
|
const unAuthCtx = {
|
||||||
req: {
|
req: {
|
||||||
headers: {},
|
headers: {},
|
||||||
connection: {
|
connection: {
|
||||||
|
@ -15,20 +15,21 @@ describe('VnUser Sign-in()', () => {
|
||||||
const {VnUser, AccessToken, SignInLog} = models;
|
const {VnUser, AccessToken, SignInLog} = models;
|
||||||
describe('when credentials are correct', () => {
|
describe('when credentials are correct', () => {
|
||||||
it('should return the token if user uses email', async() => {
|
it('should return the token if user uses email', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'salesAssistant@mydomain.com', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'salesAssistant@mydomain.com', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
let signInLog = await SignInLog.find({where: {token: accessToken.id}});
|
let signInLog = await SignInLog.find({where: {token: accessToken.id}});
|
||||||
|
|
||||||
expect(signInLog.length).toEqual(1);
|
expect(signInLog.length).toEqual(1);
|
||||||
expect(signInLog[0].userFk).toEqual(accessToken.userId);
|
expect(signInLog[0].userFk).toEqual(accessToken.userId);
|
||||||
|
expect(signInLog[0].owner).toEqual(true);
|
||||||
expect(login.token).toBeDefined();
|
expect(login.token).toBeDefined();
|
||||||
|
|
||||||
await VnUser.logout(ctx.req.accessToken.id);
|
await VnUser.logout(ctx.req.accessToken.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the token', async() => {
|
it('should return the token', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'salesAssistant', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the token if the user doesnt exist but the client does', async() => {
|
it('should return the token if the user doesnt exist but the client does', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'PetterParker', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'PetterParker', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await VnUser.signIn(unauthCtx, 'IDontExist', 'TotallyWrongPassword');
|
await VnUser.signIn(unAuthCtx, 'IDontExist', 'TotallyWrongPassword');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +75,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await employee.updateAttribute('twoFactor', 'email', options);
|
await employee.updateAttribute('twoFactor', 'email', options);
|
||||||
|
|
||||||
await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
|
await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
@ -99,7 +100,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await employee.updateAttribute('passExpired', yesterday, options);
|
await employee.updateAttribute('passExpired', yesterday, options);
|
||||||
|
|
||||||
await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
|
await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
|
|
@ -124,20 +124,42 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
return email.send();
|
return email.send();
|
||||||
});
|
});
|
||||||
Self.signInValidate = (user, userToken) => {
|
|
||||||
|
/**
|
||||||
|
* Sign-in validate
|
||||||
|
* @param {String} user The user
|
||||||
|
* @param {Object} userToken Options
|
||||||
|
* @param {Object} token accessToken
|
||||||
|
* @param {Object} ctx context
|
||||||
|
*/
|
||||||
|
Self.signInValidate = async(user, userToken, token, ctx) => {
|
||||||
const [[key, value]] = Object.entries(Self.userUses(user));
|
const [[key, value]] = Object.entries(Self.userUses(user));
|
||||||
if (userToken[key].toLowerCase().trim() !== value.toLowerCase().trim()) {
|
const isOwner = Self.rawSql(`SELECT ? = ? `, [userToken[key], value]);
|
||||||
console.error('ERROR!!! - Signin with other user', userToken, user);
|
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');
|
throw new UserError('Try again');
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.validateLogin = async function(user, password) {
|
/**
|
||||||
|
* Validate login params
|
||||||
|
* @param {String} user The user
|
||||||
|
* @param {String} password
|
||||||
|
* @param {Object} ctx context
|
||||||
|
*/
|
||||||
|
Self.validateLogin = async function(user, password, ctx) {
|
||||||
const loginInfo = Object.assign({password}, Self.userUses(user));
|
const loginInfo = Object.assign({password}, Self.userUses(user));
|
||||||
const token = await Self.login(loginInfo, 'user');
|
const token = await Self.login(loginInfo, 'user');
|
||||||
|
|
||||||
const userToken = await token.user.get();
|
const userToken = await token.user.get();
|
||||||
Self.signInValidate(user, userToken);
|
|
||||||
|
if (ctx)
|
||||||
|
await Self.signInValidate(user, userToken, token, ctx);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Self.app.models.Account.sync(userToken.name, password);
|
await Self.app.models.Account.sync(userToken.name, password);
|
||||||
|
@ -187,8 +209,8 @@ module.exports = function(Self) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
||||||
.filter(acl => acl.property != 'changePassword');
|
.filter(acl => acl.property != 'changePassword');
|
||||||
|
|
||||||
Self.userSecurity = async(ctx, userId, options) => {
|
Self.userSecurity = async(ctx, userId, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
@ -226,10 +248,12 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
const env = process.env.NODE_ENV;
|
const env = process.env.NODE_ENV;
|
||||||
const liliumUrl = await Self.app.models.Url.findOne({
|
const liliumUrl = await Self.app.models.Url.findOne({
|
||||||
where: {and: [
|
where: {
|
||||||
{appName: 'lilium'},
|
and: [
|
||||||
{environment: env}
|
{appName: 'lilium'},
|
||||||
]}
|
{environment: env}
|
||||||
|
]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class Mailer {
|
class Mailer {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `signInLog`
|
-- Table structure for table `signInLog`
|
||||||
-- Description: log to debug cross-login error
|
-- Description: log to debug cross-login error
|
||||||
|
@ -13,7 +12,9 @@ CREATE TABLE `account`.`signInLog` (
|
||||||
`token` varchar(255) NOT NULL ,
|
`token` varchar(255) NOT NULL ,
|
||||||
`userFk` int(10) unsigned DEFAULT NULL,
|
`userFk` int(10) unsigned DEFAULT NULL,
|
||||||
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
`userName` varchar(30) NOT NULL,
|
||||||
`ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
`ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
||||||
|
`owner` tinyint(1) DEFAULT 1,
|
||||||
KEY `userFk` (`userFk`),
|
KEY `userFk` (`userFk`),
|
||||||
CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
);
|
);
|
|
@ -5,6 +5,10 @@ SET DEFAULT ROLE 'salix' FOR 'root'@'%';
|
||||||
CREATE SCHEMA IF NOT EXISTS `vn2008`;
|
CREATE SCHEMA IF NOT EXISTS `vn2008`;
|
||||||
CREATE SCHEMA IF NOT EXISTS `tmp`;
|
CREATE SCHEMA IF NOT EXISTS `tmp`;
|
||||||
|
|
||||||
|
CREATE ROLE 'salix';
|
||||||
|
GRANT 'salix' TO 'root'@'%';
|
||||||
|
SET DEFAULT ROLE 'salix' FOR 'root'@'%';
|
||||||
|
|
||||||
UPDATE `util`.`config`
|
UPDATE `util`.`config`
|
||||||
SET `environment`= 'development';
|
SET `environment`= 'development';
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,15 @@
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"ip": {
|
"ip": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"userName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"type": "boolean",
|
||||||
|
"required": true,
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -202,9 +202,9 @@ export default class Controller extends Section {
|
||||||
if (!ticket.landed) {
|
if (!ticket.landed) {
|
||||||
const newLanded = await this.getLanded({
|
const newLanded = await this.getLanded({
|
||||||
shipped: this.$.model.userParams.dateToAdvance,
|
shipped: this.$.model.userParams.dateToAdvance,
|
||||||
addressFk: ticket.addressFk,
|
addressFk: ticket.futureAddressFk,
|
||||||
agencyModeFk: ticket.agencyModeFk,
|
agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk,
|
||||||
warehouseFk: ticket.warehouseFk
|
warehouseFk: ticket.futureWarehouseFk
|
||||||
});
|
});
|
||||||
if (!newLanded)
|
if (!newLanded)
|
||||||
throw new Error(this.$t(`No delivery zone available for this landing date`));
|
throw new Error(this.$t(`No delivery zone available for this landing date`));
|
||||||
|
@ -213,13 +213,13 @@ export default class Controller extends Section {
|
||||||
ticket.zoneFk = newLanded.zoneFk;
|
ticket.zoneFk = newLanded.zoneFk;
|
||||||
}
|
}
|
||||||
const params = {
|
const params = {
|
||||||
clientFk: ticket.clientFk,
|
clientFk: ticket.futureClientFk,
|
||||||
nickname: ticket.nickname,
|
nickname: ticket.nickname,
|
||||||
agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk,
|
agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk,
|
||||||
addressFk: ticket.addressFk,
|
addressFk: ticket.futureAddressFk,
|
||||||
zoneFk: ticket.zoneFk ?? ticket.futureZoneFk,
|
zoneFk: ticket.zoneFk ?? ticket.futureZoneFk,
|
||||||
warehouseFk: ticket.warehouseFk,
|
warehouseFk: ticket.futureWarehouseFk,
|
||||||
companyFk: ticket.companyFk,
|
companyFk: ticket.futureCompanyFk,
|
||||||
shipped: this.$.model.userParams.dateToAdvance,
|
shipped: this.$.model.userParams.dateToAdvance,
|
||||||
landed: ticket.landed,
|
landed: ticket.landed,
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
|
|
|
@ -111,8 +111,10 @@ class Controller extends Section {
|
||||||
dayIndex.setDate(dayIndex.getDate() + 1);
|
dayIndex.setDate(dayIndex.getDate() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fetchHours();
|
if (this.worker) {
|
||||||
this.getWeekData();
|
this.fetchHours();
|
||||||
|
this.getWeekData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set weekTotalHours(totalHours) {
|
set weekTotalHours(totalHours) {
|
||||||
|
@ -171,8 +173,6 @@ class Controller extends Section {
|
||||||
]}
|
]}
|
||||||
};
|
};
|
||||||
this.$.model.applyFilter(filter, params).then(() => {
|
this.$.model.applyFilter(filter, params).then(() => {
|
||||||
if (!this.card.hasWorkCenter) return;
|
|
||||||
|
|
||||||
this.getWorkedHours(this.started, this.ended);
|
this.getWorkedHours(this.started, this.ended);
|
||||||
this.getAbsences();
|
this.getAbsences();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue