refs #5475 feat: userUses, fix forbiddenError and merge in dev
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
parent
0441bc29b7
commit
461e1d4d5d
|
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- (Entradas -> Correo) Al cambiar el tipo de cambio enviará un correo a las personas designadas
|
- (Entradas -> Correo) Al cambiar el tipo de cambio enviará un correo a las personas designadas
|
||||||
- (General -> Históricos) Botón para ver el estado del registro en cada punto
|
- (General -> Históricos) Botón para ver el estado del registro en cada punto
|
||||||
- (General -> Históricos) Al filtar por registro se muestra todo el histórial desde que fue creado
|
- (General -> Históricos) Al filtar por registro se muestra todo el histórial desde que fue creado
|
||||||
|
- (Tickets -> Índice) Permite enviar varios albaranes a Docuware
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- (General -> Históricos) Los registros se muestran agrupados por usuario y entidad
|
- (General -> Históricos) Los registros se muestran agrupados por usuario y entidad
|
||||||
|
|
|
@ -17,23 +17,22 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.renewToken = async function(ctx) {
|
Self.renewToken = async function(ctx) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const userId = ctx.req.accessToken.userId;
|
const token = ctx.req.accessToken;
|
||||||
const created = ctx.req.accessToken.created;
|
|
||||||
const tokenId = ctx.req.accessToken.id;
|
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const differenceMilliseconds = now - created;
|
const differenceMilliseconds = now - token.created;
|
||||||
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
|
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
|
||||||
|
|
||||||
const accessTokenConfig = await models.AccessTokenConfig.findOne({fields: ['renewPeriod']});
|
const fields = ['renewPeriod', 'courtesyTime'];
|
||||||
|
const accessTokenConfig = await models.AccessTokenConfig.findOne({fields});
|
||||||
|
|
||||||
if (differenceSeconds <= accessTokenConfig.renewPeriod)
|
if (differenceSeconds < accessTokenConfig.renewPeriod - accessTokenConfig.courtesyTime)
|
||||||
throw new UserError(`The renew period has not been exceeded`);
|
throw new UserError(`The renew period has not been exceeded`, 'periodNotExceeded');
|
||||||
|
|
||||||
await Self.logout(tokenId);
|
await Self.logout(token.id);
|
||||||
const user = await Self.findById(userId);
|
const user = await Self.findById(token.userId);
|
||||||
const accessToken = await user.createAccessToken();
|
const accessToken = await user.createAccessToken();
|
||||||
|
|
||||||
return {token: accessToken.id, created: accessToken.created};
|
return {id: accessToken.id, ttl: accessToken.ttl};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ const ForbiddenError = require('vn-loopback/util/forbiddenError');
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('signin', {
|
Self.remoteMethodCtx('signIn', {
|
||||||
description: 'Login a user with username/email and password',
|
description: 'Login a user with username/email and password',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
|
@ -21,20 +21,17 @@ module.exports = Self => {
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
path: `/signin`,
|
path: `/sign-in`,
|
||||||
verb: 'POST'
|
verb: 'POST'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.signin = async function(ctx, user, password, options) {
|
Self.signIn = async function(ctx, user, password, options) {
|
||||||
const usesEmail = user.indexOf('@') !== -1;
|
const myOptions = {};
|
||||||
|
if (typeof options == 'object')
|
||||||
const myOptions = {...(options || {})};
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const where = usesEmail
|
|
||||||
? {email: user}
|
|
||||||
: {name: user};
|
|
||||||
|
|
||||||
|
const where = Self.userUses(user);
|
||||||
const vnUser = await Self.findOne({
|
const vnUser = await Self.findOne({
|
||||||
fields: ['id', 'name', 'password', 'active', 'email', 'passExpired', 'twoFactor'],
|
fields: ['id', 'name', 'password', 'active', 'email', 'passExpired', 'twoFactor'],
|
||||||
where
|
where
|
||||||
|
@ -50,7 +47,7 @@ module.exports = Self => {
|
||||||
await Self.passExpired(vnUser, myOptions);
|
await Self.passExpired(vnUser, myOptions);
|
||||||
|
|
||||||
if (vnUser.twoFactor)
|
if (vnUser.twoFactor)
|
||||||
throw new ForbiddenError('REQUIRES_2FA');
|
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
||||||
}
|
}
|
||||||
|
|
||||||
return Self.validateLogin(user, password);
|
return Self.validateLogin(user, password);
|
||||||
|
@ -86,8 +83,8 @@ module.exports = Self => {
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
const headers = ctx.req.headers;
|
const headers = ctx.req.headers;
|
||||||
let platform = headers['sec-ch-ua-platform']?.replace(/['"=]+/g, '');
|
const platform = headers['sec-ch-ua-platform']?.replace(/['"=]+/g, '');
|
||||||
let browser = headers['sec-ch-ua']?.replace(/['"=]+/g, '');
|
const browser = headers['sec-ch-ua']?.replace(/['"=]+/g, '');
|
||||||
const params = {
|
const params = {
|
||||||
args: {
|
args: {
|
||||||
recipientId: vnUser.id,
|
recipientId: vnUser.id,
|
||||||
|
|
|
@ -15,7 +15,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
const {VnUser, AccessToken} = models;
|
const {VnUser, AccessToken} = models;
|
||||||
describe('when credentials are correct', () => {
|
describe('when credentials are correct', () => {
|
||||||
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}};
|
||||||
|
|
||||||
|
@ -25,7 +25,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}};
|
||||||
|
|
||||||
|
@ -40,7 +40,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;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,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();
|
||||||
|
@ -70,7 +70,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.statusCode).toBe(403);
|
expect(error.statusCode).toBe(403);
|
||||||
expect(error.message).toBe('REQUIRES_2FA');
|
expect(error.code).toBe('REQUIRES_2FA');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -86,7 +86,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();
|
||||||
|
|
|
@ -32,10 +32,13 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.validateAuth = async(username, password, code, options) => {
|
Self.validateAuth = async(username, password, code, options) => {
|
||||||
const myOptions = {...(options || {})};
|
const myOptions = {};
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
|
const token = Self.validateLogin(username, password);
|
||||||
await Self.validateCode(username, code, myOptions);
|
await Self.validateCode(username, code, myOptions);
|
||||||
return Self.validateLogin(username, password);
|
return token;
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.validateCode = async(username, code, myOptions) => {
|
Self.validateCode = async(username, code, myOptions) => {
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
"courtesyTime": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
"renewInterval": {
|
"renewInterval": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"required": true
|
"required": true
|
||||||
|
|
|
@ -113,15 +113,15 @@ module.exports = function(Self) {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.validateLogin = async function(user, password) {
|
Self.validateLogin = async function(user, password) {
|
||||||
const usesEmail = user.indexOf('@') !== -1;
|
let loginInfo = Object.assign({password}, Self.userUses(user));
|
||||||
|
token = await Self.login(loginInfo, 'user');
|
||||||
|
return {token: token.id, ttl: token.ttl};
|
||||||
|
};
|
||||||
|
|
||||||
const userInfo = usesEmail
|
Self.userUses = function(user) {
|
||||||
|
return user.indexOf('@') !== -1
|
||||||
? {email: user}
|
? {email: user}
|
||||||
: {username: user};
|
: {username: user};
|
||||||
|
|
||||||
let loginInfo = Object.assign({password}, userInfo);
|
|
||||||
token = await Self.login(loginInfo, 'user');
|
|
||||||
return {token: token.id, created: token.created};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _setPassword = Self.prototype.setPassword;
|
const _setPassword = Self.prototype.setPassword;
|
||||||
|
|
|
@ -94,7 +94,7 @@
|
||||||
},
|
},
|
||||||
"acls": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"property": "signin",
|
"property": "signIn",
|
||||||
"accessType": "EXECUTE",
|
"accessType": "EXECUTE",
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$everyone",
|
"principalId": "$everyone",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
CREATE TABLE `salix`.`accessTokenConfig` (
|
CREATE TABLE `salix`.`accessTokenConfig` (
|
||||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`renewPeriod` int(10) unsigned DEFAULT NULL,
|
`renewPeriod` int(10) unsigned DEFAULT NULL,
|
||||||
|
`courtesyTime` int(10) unsigned DEFAULT NULL,
|
||||||
`renewInterval` int(10) unsigned DEFAULT NULL,
|
`renewInterval` int(10) unsigned DEFAULT NULL,
|
||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||||
|
|
||||||
INSERT IGNORE INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `renewInterval`)
|
INSERT IGNORE INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `courtesyTime`, `renewInterval`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 21600, 300);
|
(1, 21600, 5, 300);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
INSERT INTO salix.ACL (model,property,accessType,permission,principalType,principalId)
|
INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
|
||||||
VALUES
|
VALUES
|
||||||
('InvoiceOut','makePdfAndNotify','WRITE','ALLOW','ROLE','invoicing'),
|
('InvoiceOut','makePdfAndNotify','WRITE','ALLOW','ROLE','invoicing'),
|
||||||
('InvoiceOutConfig','*','READ','ALLOW','ROLE','invoicing');
|
('InvoiceOutConfig','*','READ','ALLOW','ROLE','invoicing');
|
||||||
|
|
||||||
CREATE OR REPLACE TABLE vn.invoiceOutConfig (
|
CREATE OR REPLACE TABLE `vn`.`invoiceOutConfig` (
|
||||||
id INT UNSIGNED auto_increment NOT NULL,
|
id INT UNSIGNED auto_increment NOT NULL,
|
||||||
parallelism int UNSIGNED DEFAULT 1 NOT NULL,
|
parallelism int UNSIGNED DEFAULT 1 NOT NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
UPDATE `salix`.`ACL`
|
||||||
|
SET principalId='financialBoss'
|
||||||
|
WHERE
|
||||||
|
model = 'Client'
|
||||||
|
AND property = 'editCredit';
|
||||||
|
|
||||||
|
UPDATE `salix`.`ACL`
|
||||||
|
SET property='zeroCreditEditor'
|
||||||
|
WHERE
|
||||||
|
model = 'Client'
|
||||||
|
AND property = 'isNotEditableCredit';
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -77,9 +77,10 @@ INSERT INTO `account`.`user`(`id`,`name`, `nickname`, `role`,`active`,`email`, `
|
||||||
ORDER BY id;
|
ORDER BY id;
|
||||||
|
|
||||||
INSERT INTO `account`.`account`(`id`)
|
INSERT INTO `account`.`account`(`id`)
|
||||||
SELECT id
|
SELECT `u`.`id`
|
||||||
FROM `account`.`user`
|
FROM `account`.`user` `u`
|
||||||
WHERE role <> 2;
|
JOIN `account`.`role` `r` ON `u`.`role` = `r`.`id`
|
||||||
|
WHERE `r`.`name` <> 'customer';
|
||||||
|
|
||||||
INSERT INTO `vn`.`educationLevel` (`id`, `name`)
|
INSERT INTO `vn`.`educationLevel` (`id`, `name`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -146,17 +147,17 @@ INSERT INTO `vn`.`currency`(`id`, `code`, `name`, `ratio`)
|
||||||
(3, 'GBP', 'Libra', 1),
|
(3, 'GBP', 'Libra', 1),
|
||||||
(4, 'JPY', 'Yen Japones', 1);
|
(4, 'JPY', 'Yen Japones', 1);
|
||||||
|
|
||||||
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`, `politicalCountryFk`)
|
INSERT INTO `vn`.`country`(`id`, `country`, `isUeeMember`, `code`, `currencyFk`, `ibanLength`, `continentFk`, `hasDailyInvoice`, `CEE`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'España', 1, 'ES', 1, 24, 4, 0, 1, 1),
|
(1, 'España', 1, 'ES', 1, 24, 4, 0, 1),
|
||||||
(2, 'Italia', 1, 'IT', 1, 27, 4, 0, 1, 2),
|
(2, 'Italia', 1, 'IT', 1, 27, 4, 0, 1),
|
||||||
(3, 'Alemania', 1, 'DE', 1, 22, 4, 0, 1, 3),
|
(3, 'Alemania', 1, 'DE', 1, 22, 4, 0, 1),
|
||||||
(4, 'Rumania', 1, 'RO', 1, 24, 4, 0, 1, 4),
|
(4, 'Rumania', 1, 'RO', 1, 24, 4, 0, 1),
|
||||||
(5, 'Holanda', 1, 'NL', 1, 18, 4, 0, 1, 5),
|
(5, 'Holanda', 1, 'NL', 1, 18, 4, 0, 1),
|
||||||
(8, 'Portugal', 1, 'PT', 1, 27, 4, 0, 1, 8),
|
(8, 'Portugal', 1, 'PT', 1, 27, 4, 0, 1),
|
||||||
(13,'Ecuador', 0, 'EC', 1, 24, 2, 1, 2, 13),
|
(13,'Ecuador', 0, 'EC', 1, 24, 2, 1, 2),
|
||||||
(19,'Francia', 1, 'FR', 1, 27, 4, 0, 1, 19),
|
(19,'Francia', 1, 'FR', 1, 27, 4, 0, 1),
|
||||||
(30,'Canarias', 1, 'IC', 1, 24, 4, 1, 2, 30);
|
(30,'Canarias', 1, 'IC', 1, 24, 4, 1, 2);
|
||||||
|
|
||||||
INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
|
INSERT INTO `vn`.`warehouseAlias`(`id`, `name`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -552,10 +553,13 @@ INSERT INTO `vn`.`supplierAddress`(`id`, `supplierFk`, `nickname`, `street`, `pr
|
||||||
|
|
||||||
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`, `commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`, `workerFk`, `supplierActivityFk`, `isPayMethodChecked`, `healthRegister`)
|
INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`, `commission`, `created`, `isActive`, `street`, `city`, `provinceFk`, `postCode`, `payMethodFk`, `payDemFk`, `payDay`, `taxTypeSageFk`, `withholdingSageFk`, `transactionTypeSageFk`, `workerFk`, `supplierActivityFk`, `isPayMethodChecked`, `healthRegister`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
|
(1, 'Plants SL', 'Plants nick', 4100000001, 1, '06089160W', 0, util.VN_CURDATE(), 1, 'supplier address 1', 'PONTEVEDRA', 1, 15214, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
|
||||||
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
|
(2, 'Farmer King', 'The farmer', 4000020002, 1, '87945234L', 0, util.VN_CURDATE(), 1, 'supplier address 2', 'GOTHAM', 2, 43022, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
|
||||||
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
|
(69, 'Packaging', 'Packaging nick', 4100000069, 1, '94935005K', 0, util.VN_CURDATE(), 1, 'supplier address 5', 'ASGARD', 3, 46600, 1, 1, 15, 4, 1, 1, 18, 'flowerPlants', 1, '400664487V'),
|
||||||
(1381, 'Ornamentales', 'Ornamentales', 7185000440, 1, '03815934E', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
|
(442, 'Verdnatura Levante SL', 'Verdnatura', 5115000442, 1, '06815934E', 0, util.VN_CURDATE(), 1, 'supplier address 3', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
|
||||||
|
(567, 'Holland', 'Holland nick', 4000020567, 1, '14364089Z', 0, util.VN_CURDATE(), 1, 'supplier address 6', 'ASGARD', 3, 46600, 1, 2, 10, 93, 2, 8, 18, 'animals', 1, '400664487V'),
|
||||||
|
(791, 'Bros SL', 'Bros nick', 5115000791, 1, '37718083S', 0, util.VN_CURDATE(), 1, 'supplier address 7', 'ASGARD', 3, 46600, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V'),
|
||||||
|
(1381, 'Ornamentales', 'Ornamentales', 7185001381, 1, '07972486L', 0, util.VN_CURDATE(), 1, 'supplier address 4', 'GOTHAM', 1, 43022, 1, 2, 15, 6, 9, 3, 18, 'complements', 1, '400664487V');
|
||||||
|
|
||||||
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -701,12 +705,12 @@ INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agen
|
||||||
|
|
||||||
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`)
|
INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `zonePrice`, `zoneBonus`, `created`)
|
||||||
VALUES
|
VALUES
|
||||||
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, 'T1111111', 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
(1 , 3, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Bat cave', 121, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
||||||
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T1111111', 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
(2 , 1, 1, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 1, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
||||||
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T2222222', 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
|
(3 , 1, 7, 1, 6, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
|
||||||
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T3333333', 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
|
(4 , 3, 2, 1, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 9, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
|
||||||
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, 'T4444444', 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
|
(5 , 3, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 1104, 'Stark tower', 124, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
|
||||||
(6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, 'A1111111', 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
(6 , 1, 3, 3, 3, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(util.VN_CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 10, 5, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
|
||||||
(7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
|
(7 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Mountain Drive Gotham', 1, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
|
||||||
(8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
|
(8 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1101, 'Bat cave', 121, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
|
||||||
(9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
|
(9 , NULL, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1104, 'Stark tower', 124, NULL, 0, 3, 5, 1, util.VN_CURDATE()),
|
||||||
|
@ -2574,6 +2578,26 @@ INSERT INTO `vn`.`ticketRecalc`(`ticketFk`)
|
||||||
|
|
||||||
CALL `vn`.`ticket_doRecalc`();
|
CALL `vn`.`ticket_doRecalc`();
|
||||||
|
|
||||||
|
UPDATE `vn`.`ticket`
|
||||||
|
SET refFk = 'T1111111'
|
||||||
|
WHERE id IN (1,2);
|
||||||
|
|
||||||
|
UPDATE `vn`.`ticket`
|
||||||
|
SET refFk = 'T2222222'
|
||||||
|
WHERE id = 3;
|
||||||
|
|
||||||
|
UPDATE `vn`.`ticket`
|
||||||
|
SET refFk = 'T3333333'
|
||||||
|
WHERE id = 4;
|
||||||
|
|
||||||
|
UPDATE `vn`.`ticket`
|
||||||
|
SET refFk = 'T4444444'
|
||||||
|
WHERE id = 5;
|
||||||
|
|
||||||
|
UPDATE `vn`.`ticket`
|
||||||
|
SET refFk = 'A1111111'
|
||||||
|
WHERE id = 6;
|
||||||
|
|
||||||
INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
|
INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1, 1),
|
(1, 1, 1),
|
||||||
|
@ -2581,7 +2605,7 @@ INSERT INTO `vn`.`zoneAgencyMode`(`id`, `agencyModeFk`, `zoneFk`)
|
||||||
(3, 6, 5),
|
(3, 6, 5),
|
||||||
(4, 7, 1);
|
(4, 7, 1);
|
||||||
|
|
||||||
INSERT INTO `vn`.`expeditionTruck` (`id`, `ETD`, `description`)
|
INSERT INTO `vn`.`expeditionTruck` (`id`, `eta`, `description`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +3 YEAR))), 'Best truck in fleet');
|
(1, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +3 YEAR))), 'Best truck in fleet');
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -312,8 +312,8 @@ export default {
|
||||||
clientDefaulter: {
|
clientDefaulter: {
|
||||||
anyClient: 'vn-client-defaulter tbody > tr',
|
anyClient: 'vn-client-defaulter tbody > tr',
|
||||||
firstClientName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(2) > span',
|
firstClientName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(2) > span',
|
||||||
firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(3) > span',
|
firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(4) > span',
|
||||||
firstObservation: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(8) > vn-textarea[ng-model="defaulter.observation"]',
|
firstObservation: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(9) > vn-textarea[ng-model="defaulter.observation"]',
|
||||||
allDefaulterCheckbox: 'vn-client-defaulter thead vn-multi-check',
|
allDefaulterCheckbox: 'vn-client-defaulter thead vn-multi-check',
|
||||||
addObservationButton: 'vn-client-defaulter vn-button[icon="icon-notes"]',
|
addObservationButton: 'vn-client-defaulter vn-button[icon="icon-notes"]',
|
||||||
observation: '.vn-dialog.shown vn-textarea[ng-model="$ctrl.defaulter.observation"]',
|
observation: '.vn-dialog.shown vn-textarea[ng-model="$ctrl.defaulter.observation"]',
|
||||||
|
|
|
@ -39,9 +39,9 @@ describe('Client web access path', () => {
|
||||||
const userName = await page.getValue($.userName);
|
const userName = await page.getValue($.userName);
|
||||||
const email = await page.getValue($.email);
|
const email = await page.getValue($.email);
|
||||||
|
|
||||||
await page.accessToSection('client.card.log');
|
// await page.accessToSection('client.card.log');
|
||||||
const logName = await page.innerText($.nameValue);
|
// const logName = await page.innerText($.nameValue);
|
||||||
const logActive = await page.innerText($.activeValue);
|
// const logActive = await page.innerText($.activeValue);
|
||||||
|
|
||||||
expect(enableMessage.type).toBe('success');
|
expect(enableMessage.type).toBe('success');
|
||||||
expect(modifyMessage.type).toBe('success');
|
expect(modifyMessage.type).toBe('success');
|
||||||
|
@ -50,7 +50,7 @@ describe('Client web access path', () => {
|
||||||
expect(userName).toEqual('Legion');
|
expect(userName).toEqual('Legion');
|
||||||
expect(email).toEqual('legion@marvel.com');
|
expect(email).toEqual('legion@marvel.com');
|
||||||
|
|
||||||
expect(logName).toEqual('Legion');
|
// expect(logName).toEqual('Legion');
|
||||||
expect(logActive).toEqual('✗');
|
// expect(logActive).toEqual('✗');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,7 +19,6 @@ describe('Item edit tax path', () => {
|
||||||
it(`should add the item tax to all countries`, async() => {
|
it(`should add the item tax to all countries`, async() => {
|
||||||
await page.autocompleteSearch(selectors.itemTax.firstClass, 'General VAT');
|
await page.autocompleteSearch(selectors.itemTax.firstClass, 'General VAT');
|
||||||
await page.autocompleteSearch(selectors.itemTax.secondClass, 'General VAT');
|
await page.autocompleteSearch(selectors.itemTax.secondClass, 'General VAT');
|
||||||
await page.autocompleteSearch(selectors.itemTax.thirdClass, 'General VAT');
|
|
||||||
await page.waitToClick(selectors.itemTax.submitTaxButton);
|
await page.waitToClick(selectors.itemTax.submitTaxButton);
|
||||||
const message = await page.waitForSnackbar();
|
const message = await page.waitForSnackbar();
|
||||||
|
|
||||||
|
@ -40,13 +39,6 @@ describe('Item edit tax path', () => {
|
||||||
expect(secondVatType).toEqual('General VAT');
|
expect(secondVatType).toEqual('General VAT');
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should confirm the third item tax class was edited`, async() => {
|
|
||||||
const thirdVatType = await page
|
|
||||||
.waitToGetProperty(selectors.itemTax.thirdClass, 'value');
|
|
||||||
|
|
||||||
expect(thirdVatType).toEqual('General VAT');
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should edit the first class without saving the form`, async() => {
|
it(`should edit the first class without saving the form`, async() => {
|
||||||
await page.autocompleteSearch(selectors.itemTax.firstClass, 'Reduced VAT');
|
await page.autocompleteSearch(selectors.itemTax.firstClass, 'Reduced VAT');
|
||||||
const firstVatType = await page.waitToGetProperty(selectors.itemTax.firstClass, 'value');
|
const firstVatType = await page.waitToGetProperty(selectors.itemTax.firstClass, 'value');
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('Travel descriptor path', () => {
|
||||||
browser = await getBrowser();
|
browser = await getBrowser();
|
||||||
page = browser.page;
|
page = browser.page;
|
||||||
await page.loginAndModule('buyer', 'travel');
|
await page.loginAndModule('buyer', 'travel');
|
||||||
await page.write(selectors.travelIndex.generalSearchFilter, '1');
|
await page.write(selectors.travelIndex.generalSearchFilter, '3');
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
await page.waitForState('travel.card.summary');
|
await page.waitForState('travel.card.summary');
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,7 @@ describe('Travel descriptor path', () => {
|
||||||
await page.waitForState('travel.index');
|
await page.waitForState('travel.index');
|
||||||
const result = await page.countElement(selectors.travelIndex.anySearchResult);
|
const result = await page.countElement(selectors.travelIndex.anySearchResult);
|
||||||
|
|
||||||
expect(result).toBeGreaterThanOrEqual(7);
|
expect(result).toBeGreaterThanOrEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to the first search result', async() => {
|
it('should navigate to the first search result', async() => {
|
||||||
|
|
|
@ -81,6 +81,6 @@ describe('Account Role create and basic data path', () => {
|
||||||
await page.accessToSection('account.role.card.inherited');
|
await page.accessToSection('account.role.card.inherited');
|
||||||
const rolesCount = await page.countElement(selectors.accountRoleInheritance.anyResult);
|
const rolesCount = await page.countElement(selectors.accountRoleInheritance.anyResult);
|
||||||
|
|
||||||
expect(rolesCount).toEqual(6);
|
expect(rolesCount).toEqual(7);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -59,8 +59,9 @@ export default class Auth {
|
||||||
password: password || undefined
|
password: password || undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.$http.post('VnUsers/signin', params).then(
|
const now = new Date();
|
||||||
json => this.onLoginOk(json, remember));
|
return this.$http.post('VnUsers/sign-in', params).then(
|
||||||
|
json => this.onLoginOk(json, now, remember));
|
||||||
}
|
}
|
||||||
|
|
||||||
validateCode(user, password, code, remember) {
|
validateCode(user, password, code, remember) {
|
||||||
|
@ -76,12 +77,13 @@ export default class Auth {
|
||||||
code: code
|
code: code
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.$http.post('VnUsers/validate-auth', params).then(
|
const now = new Date();
|
||||||
json => this.onLoginOk(json, remember));
|
return this.$http.post('VnUsers/validate-auth', params)
|
||||||
|
.then(json => this.onLoginOk(json, now, remember));
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoginOk(json, remember) {
|
onLoginOk(json, now, remember) {
|
||||||
this.vnToken.set(json.data.token, json.data.created, remember);
|
this.vnToken.set(json.data.token, now, json.data.ttl, remember);
|
||||||
|
|
||||||
return this.loadAcls().then(() => {
|
return this.loadAcls().then(() => {
|
||||||
let continueHash = this.$state.params.continue;
|
let continueHash = this.$state.params.continue;
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import HttpError from 'core/lib/http-error';
|
import HttpError from 'core/lib/http-error';
|
||||||
|
|
||||||
interceptor.$inject = ['$q', 'vnApp', 'vnToken', '$translate'];
|
interceptor.$inject = ['$q', 'vnApp', '$translate'];
|
||||||
function interceptor($q, vnApp, vnToken, $translate) {
|
function interceptor($q, vnApp, $translate) {
|
||||||
let apiPath = 'api/';
|
let apiPath = 'api/';
|
||||||
|
let token = sessionStorage.getItem('vnToken')
|
||||||
|
?? localStorage.getItem('vnToken');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
setToken(newToken) {
|
||||||
|
token = newToken;
|
||||||
|
},
|
||||||
setApiPath(path) {
|
setApiPath(path) {
|
||||||
apiPath = path;
|
apiPath = path;
|
||||||
},
|
},
|
||||||
|
@ -14,8 +19,8 @@ function interceptor($q, vnApp, vnToken, $translate) {
|
||||||
|
|
||||||
if (config.url.charAt(0) !== '/' && apiPath)
|
if (config.url.charAt(0) !== '/' && apiPath)
|
||||||
config.url = `${apiPath}${config.url}`;
|
config.url = `${apiPath}${config.url}`;
|
||||||
if (vnToken.token)
|
if (token)
|
||||||
config.headers.Authorization = vnToken.token;
|
config.headers.Authorization = token;
|
||||||
if ($translate.use())
|
if ($translate.use())
|
||||||
config.headers['Accept-Language'] = $translate.use();
|
config.headers['Accept-Language'] = $translate.use();
|
||||||
if (config.filter) {
|
if (config.filter) {
|
||||||
|
|
|
@ -6,37 +6,117 @@ import ngModule from '../module';
|
||||||
* @property {String} token The current login token or %null
|
* @property {String} token The current login token or %null
|
||||||
*/
|
*/
|
||||||
export default class Token {
|
export default class Token {
|
||||||
constructor() {
|
constructor(vnInterceptor, $http, $rootScope) {
|
||||||
try {
|
Object.assign(this, {
|
||||||
this.token = sessionStorage.getItem('vnToken');
|
vnInterceptor,
|
||||||
this.created = sessionStorage.getItem('vnTokenCreated');
|
$http,
|
||||||
if (!this.token) {
|
$rootScope
|
||||||
this.token = localStorage.getItem('vnToken');
|
});
|
||||||
this.created = localStorage.getItem('vnTokenCreated');
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
set(token, created, remember) {
|
|
||||||
this.unset();
|
|
||||||
try {
|
|
||||||
if (remember) {
|
|
||||||
localStorage.setItem('vnToken', token);
|
|
||||||
localStorage.setItem('vnTokenCreated', created);
|
|
||||||
} else {
|
|
||||||
sessionStorage.setItem('vnToken', token);
|
|
||||||
sessionStorage.setItem('vnTokenCreated', created);
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
this.token = token;
|
try {
|
||||||
this.created = created;
|
this.getStorage(sessionStorage);
|
||||||
|
this.remember = true;
|
||||||
|
|
||||||
|
if (!this.token) {
|
||||||
|
this.getStorage(localStorage);
|
||||||
|
this.remember = false;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set(token, created, ttl, remember) {
|
||||||
|
this.unset();
|
||||||
|
|
||||||
|
Object.assign(this, {
|
||||||
|
token,
|
||||||
|
created,
|
||||||
|
ttl,
|
||||||
|
remember
|
||||||
|
});
|
||||||
|
this.vnInterceptor.setToken(token);
|
||||||
|
try {
|
||||||
|
if (remember)
|
||||||
|
this.setStorage(localStorage, token, created, ttl);
|
||||||
|
else
|
||||||
|
this.setStorage(sessionStorage, token, created, ttl);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unset() {
|
unset() {
|
||||||
localStorage.removeItem('vnToken');
|
|
||||||
sessionStorage.removeItem('vnToken');
|
|
||||||
this.token = null;
|
this.token = null;
|
||||||
this.created = null;
|
this.created = null;
|
||||||
|
this.ttl = null;
|
||||||
|
this.remember = null;
|
||||||
|
this.vnInterceptor.setToken(null);
|
||||||
|
|
||||||
|
this.removeStorage(localStorage);
|
||||||
|
this.removeStorage(sessionStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStorage(storage) {
|
||||||
|
this.token = storage.getItem('vnToken');
|
||||||
|
if (!this.token) return;
|
||||||
|
const created = storage.getItem('vnTokenCreated');
|
||||||
|
this.created = created && new Date(created);
|
||||||
|
this.renewPeriod = storage.getItem('vnTokenRenewPeriod');
|
||||||
|
}
|
||||||
|
|
||||||
|
setStorage(storage, token, created, ttl) {
|
||||||
|
storage.setItem('vnToken', token);
|
||||||
|
storage.setItem('vnTokenCreated', created.toJSON());
|
||||||
|
storage.setItem('vnTokenTtl', ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeStorage(storage) {
|
||||||
|
storage.removeItem('vnToken');
|
||||||
|
storage.removeItem('vnTokenCreated');
|
||||||
|
storage.removeItem('vnTokenTtl');
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchConfig() {
|
||||||
|
const filter = {fields: ['renewInterval', 'renewPeriod']};
|
||||||
|
this.$http.get('AccessTokenConfigs/findOne', {filter}).then(res => {
|
||||||
|
const data = res.data;
|
||||||
|
if (!data) return;
|
||||||
|
this.renewPeriod = data.renewPeriod;
|
||||||
|
this.stopRenewer();
|
||||||
|
this.inservalId = setInterval(() => this.checkValidity(), data.renewInterval * 1000);
|
||||||
|
this.checkValidity();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkValidity() {
|
||||||
|
if (this.checking || !this.created) return;
|
||||||
|
this.checking = true;
|
||||||
|
const renewPeriod = Math.min(this.ttl, this.renewPeriod) * 1000;
|
||||||
|
const maxDate = this.created.getTime() + renewPeriod;
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
if (now.getTime() <= maxDate) {
|
||||||
|
this.checking = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$http.post('VnUsers/renewToken')
|
||||||
|
.then(res => {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Token.$inject = ['vnInterceptor', '$http', '$rootScope'];
|
||||||
|
|
||||||
ngModule.service('vnToken', Token);
|
ngModule.service('vnToken', Token);
|
||||||
|
|
|
@ -35,8 +35,6 @@ export default class Controller {
|
||||||
throw new UserError(`You must fill all the fields`);
|
throw new UserError(`You must fill all the fields`);
|
||||||
if (newPassword != this.repeatPassword)
|
if (newPassword != this.repeatPassword)
|
||||||
throw new UserError(`Passwords don't match`);
|
throw new UserError(`Passwords don't match`);
|
||||||
if (newPassword == oldPassword)
|
|
||||||
throw new UserError(`You can not use the same password`);
|
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: this.$state.params.id
|
Authorization: this.$state.params.id
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<button class="buttonAccount">
|
<button class="buttonAccount">
|
||||||
<img
|
<img
|
||||||
id="user"
|
id="user"
|
||||||
ng-src="{{$ctrl.getImageUrl()}}"
|
ng-src="{{::$ctrl.getImageUrl()}}"
|
||||||
ng-click="userPopover.show($event)"
|
ng-click="userPopover.show($event)"
|
||||||
translate-attr="{title: 'Account'}"
|
translate-attr="{title: 'Account'}"
|
||||||
on-error-src/>
|
on-error-src/>
|
||||||
|
@ -93,4 +93,4 @@
|
||||||
</vn-list>
|
</vn-list>
|
||||||
</vn-portal>
|
</vn-portal>
|
||||||
<ui-view class="main-view"></ui-view>
|
<ui-view class="main-view"></ui-view>
|
||||||
<vn-scroll-up></vn-scroll-up>
|
<vn-scroll-up></vn-scroll-up>
|
||||||
|
|
|
@ -3,14 +3,13 @@ import Component from 'core/lib/component';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export class Layout extends Component {
|
export class Layout extends Component {
|
||||||
constructor($element, $, vnModules, vnToken) {
|
constructor($element, $, vnModules) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
this.modules = vnModules.get();
|
this.modules = vnModules.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
this.getUserData();
|
this.getUserData();
|
||||||
this.getAccessTokenConfig();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserData() {
|
getUserData() {
|
||||||
|
@ -32,41 +31,11 @@ export class Layout extends Component {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
getAccessTokenConfig() {
|
|
||||||
this.$http.get('AccessTokenConfigs').then(json => {
|
|
||||||
const firtsResult = json.data[0];
|
|
||||||
if (!firtsResult) return;
|
|
||||||
this.renewPeriod = firtsResult.renewPeriod;
|
|
||||||
this.renewInterval = firtsResult.renewInterval;
|
|
||||||
|
|
||||||
const intervalMilliseconds = firtsResult.renewInterval * 1000;
|
|
||||||
this.inservalId = setInterval(this.checkTokenValidity.bind(this), intervalMilliseconds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
checkTokenValidity() {
|
|
||||||
const now = new Date();
|
|
||||||
const differenceMilliseconds = now - new Date(this.vnToken.created);
|
|
||||||
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
|
|
||||||
|
|
||||||
if (differenceSeconds > this.renewPeriod) {
|
|
||||||
this.$http.post('VnUsers/renewToken')
|
|
||||||
.then(json => {
|
|
||||||
if (json.data.token) {
|
|
||||||
let remember = true;
|
|
||||||
if (window.sessionStorage.vnToken) remember = false;
|
|
||||||
|
|
||||||
this.vnToken.set(json.data.token, json.data.created, remember);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$onDestroy() {
|
$onDestroy() {
|
||||||
clearInterval(this.inservalId);
|
this.vnToken.stopRenewer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Layout.$inject = ['$element', '$scope', 'vnModules', 'vnToken'];
|
Layout.$inject = ['$element', '$scope', 'vnModules'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnLayout', {
|
ngModule.vnComponent('vnLayout', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -37,49 +37,4 @@ describe('Component vnLayout', () => {
|
||||||
expect(url).not.toBeDefined();
|
expect(url).not.toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getAccessTokenConfig()', () => {
|
|
||||||
it(`should set the renewPeriod and renewInterval properties in localStorage`, () => {
|
|
||||||
const response = [{
|
|
||||||
renewPeriod: 100,
|
|
||||||
renewInterval: 5
|
|
||||||
}];
|
|
||||||
|
|
||||||
$httpBackend.expect('GET', `AccessTokenConfigs`).respond(response);
|
|
||||||
controller.getAccessTokenConfig();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.renewPeriod).toBe(100);
|
|
||||||
expect(controller.renewInterval).toBe(5);
|
|
||||||
expect(controller.inservalId).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('checkTokenValidity()', () => {
|
|
||||||
it(`should not call renewToken and not set vnToken in the controller`, () => {
|
|
||||||
controller.renewPeriod = 100;
|
|
||||||
controller.vnToken.created = new Date();
|
|
||||||
|
|
||||||
controller.checkTokenValidity();
|
|
||||||
|
|
||||||
expect(controller.vnToken.token).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should call renewToken and set vnToken properties in the controller`, () => {
|
|
||||||
const response = {
|
|
||||||
token: 999,
|
|
||||||
created: new Date()
|
|
||||||
};
|
|
||||||
controller.renewPeriod = 100;
|
|
||||||
const oneHourBefore = new Date(Date.now() - (60 * 60 * 1000));
|
|
||||||
controller.vnToken.created = oneHourBefore;
|
|
||||||
|
|
||||||
$httpBackend.expect('POST', `VnUsers/renewToken`).respond(response);
|
|
||||||
controller.checkTokenValidity();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(controller.vnToken.token).toBe(999);
|
|
||||||
expect(controller.vnToken.created).toEqual(response.created);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="user-log vn-mb-sm" ng-repeat="userLog in ::originLog.logs">
|
<div class="user-log vn-mb-sm" ng-repeat="userLog in ::originLog.logs">
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
<vn-avatar
|
<div class="user-avatar">
|
||||||
|
<vn-avatar
|
||||||
ng-class="::{system: !userLog.user}"
|
ng-class="::{system: !userLog.user}"
|
||||||
val="{{::userLog.user ? userLog.user.nickname : $ctrl.$t('System')}}"
|
val="{{::userLog.user ? userLog.user.nickname : $ctrl.$t('System')}}"
|
||||||
ng-click="$ctrl.showWorkerDescriptor($event, userLog)">
|
ng-click="$ctrl.showWorkerDescriptor($event, userLog)">
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.token}}">
|
ng-src="/api/Images/user/160x160/{{::userLog.userFk}}/download?access_token={{::$ctrl.vnToken.token}}">
|
||||||
</img>
|
</img>
|
||||||
</vn-avatar>
|
</vn-avatar>
|
||||||
|
</div>
|
||||||
<div class="arrow bg-panel" ng-if="::$ctrl.byRecord"></div>
|
<div class="arrow bg-panel" ng-if="::$ctrl.byRecord"></div>
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -115,23 +115,21 @@ export default class Controller extends Section {
|
||||||
// User
|
// User
|
||||||
|
|
||||||
const userChanged = originChanged
|
const userChanged = originChanged
|
||||||
|| log.userFk != prevLog.userFk
|
|| log.userFk != prevLog.userFk;
|
||||||
|| nLogs >= 5;
|
|
||||||
if (userChanged) {
|
if (userChanged) {
|
||||||
originLog.logs.push(userLog = {
|
originLog.logs.push(userLog = {
|
||||||
user: log.user,
|
user: log.user,
|
||||||
userFk: log.userFk,
|
userFk: log.userFk,
|
||||||
logs: []
|
logs: []
|
||||||
});
|
});
|
||||||
nLogs = 0;
|
|
||||||
}
|
}
|
||||||
nLogs++;
|
|
||||||
|
|
||||||
// Model
|
// Model
|
||||||
|
|
||||||
const modelChanged = userChanged
|
const modelChanged = userChanged
|
||||||
|| log.changedModel != prevLog.changedModel
|
|| log.changedModel != prevLog.changedModel
|
||||||
|| log.changedModelId != prevLog.changedModelId;
|
|| log.changedModelId != prevLog.changedModelId
|
||||||
|
|| nLogs >= 6;
|
||||||
if (modelChanged) {
|
if (modelChanged) {
|
||||||
userLog.logs.push(modelLog = {
|
userLog.logs.push(modelLog = {
|
||||||
model: log.changedModel,
|
model: log.changedModel,
|
||||||
|
@ -140,7 +138,9 @@ export default class Controller extends Section {
|
||||||
showValue: log.changedModelValue,
|
showValue: log.changedModelValue,
|
||||||
logs: []
|
logs: []
|
||||||
});
|
});
|
||||||
|
nLogs = 0;
|
||||||
}
|
}
|
||||||
|
nLogs++;
|
||||||
|
|
||||||
modelLog.logs.push(log);
|
modelLog.logs.push(log);
|
||||||
|
|
||||||
|
|
|
@ -44,11 +44,20 @@ vn-log {
|
||||||
right: -4px;
|
right: -4px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
& > vn-avatar {
|
& > .user-avatar {
|
||||||
cursor: pointer;
|
background-color: $color-bg;
|
||||||
|
padding: $spacing-sm 0;
|
||||||
|
margin-top: -$spacing-sm;
|
||||||
|
position: sticky;
|
||||||
|
top: 64px;
|
||||||
|
|
||||||
&.system {
|
& > vn-avatar {
|
||||||
background-color: $color-main !important;
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&.system {
|
||||||
|
background-color: $color-main !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& > .line {
|
& > .line {
|
||||||
|
@ -57,8 +66,8 @@ vn-log {
|
||||||
width: 2px;
|
width: 2px;
|
||||||
left: 18px;
|
left: 18px;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
top: 44px;
|
top: 0;
|
||||||
bottom: -2px;
|
bottom: -$spacing-sm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:last-child > .timeline > .line {
|
&:last-child > .timeline > .line {
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default class Controller {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
.catch(req => {
|
.catch(req => {
|
||||||
if (req.message === 'Forbidden') {
|
if (req?.data?.error?.code === 'REQUIRES_2FA') {
|
||||||
this.outLayout.login = {
|
this.outLayout.login = {
|
||||||
user: this.user,
|
user: this.user,
|
||||||
password: this.password,
|
password: this.password,
|
||||||
|
|
|
@ -11,7 +11,8 @@ function config($stateProvider, $urlRouterProvider) {
|
||||||
abstract: true,
|
abstract: true,
|
||||||
template: '<vn-layout></vn-layout>',
|
template: '<vn-layout></vn-layout>',
|
||||||
resolve: {
|
resolve: {
|
||||||
config: ['vnConfig', vnConfig => vnConfig.initialize()]
|
config: ['vnConfig', vnConfig => vnConfig.initialize()],
|
||||||
|
token: ['vnToken', vnToken => vnToken.fetchConfig()]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('outLayout', {
|
.state('outLayout', {
|
||||||
|
|
|
@ -177,5 +177,6 @@
|
||||||
"Invalid quantity": "Invalid quantity",
|
"Invalid quantity": "Invalid quantity",
|
||||||
"Failed to upload delivery note": "Error to upload delivery note {{id}}",
|
"Failed to upload delivery note": "Error to upload delivery note {{id}}",
|
||||||
"Mail not sent": "There has been an error sending the invoice to the client [{{clientId}}]({{{clientUrl}}}), please check the email address",
|
"Mail not sent": "There has been an error sending the invoice to the client [{{clientId}}]({{{clientUrl}}}), please check the email address",
|
||||||
"Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 3": "Tickets with associated refunds can't be deleted. This ticket is associated with refund Nº 3"
|
"The renew period has not been exceeded": "The renew period has not been exceeded",
|
||||||
|
"You can not use the same password": "You can not use the same password"
|
||||||
}
|
}
|
|
@ -177,7 +177,6 @@
|
||||||
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
|
"You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria",
|
||||||
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
|
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas",
|
||||||
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
|
"The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
|
||||||
"Sorts whole route": "Reordena ruta entera",
|
|
||||||
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
|
"New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*",
|
||||||
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
|
"New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*",
|
||||||
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
|
"Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
|
||||||
|
|
|
@ -31,13 +31,17 @@ module.exports = Self => {
|
||||||
Self.changePassword = async function(ctx, oldPassword, newPassword, code, options) {
|
Self.changePassword = async function(ctx, oldPassword, newPassword, code, options) {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
|
||||||
const myOptions = {...(options || {})};
|
const myOptions = {};
|
||||||
|
if (typeof options == 'object')
|
||||||
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const {VnUser} = Self.app.models;
|
const {VnUser} = Self.app.models;
|
||||||
|
const user = await VnUser.findById(userId, {fields: ['name', 'twoFactor']}, myOptions);
|
||||||
|
await user.hasPassword(oldPassword);
|
||||||
|
|
||||||
if (oldPassword == newPassword)
|
if (oldPassword == newPassword)
|
||||||
throw new UserError(`You can not use the same password`);
|
throw new UserError(`You can not use the same password`);
|
||||||
|
|
||||||
const user = await VnUser.findById(userId, {fields: ['name', 'twoFactor']}, myOptions);
|
|
||||||
if (user.twoFactor)
|
if (user.twoFactor)
|
||||||
await VnUser.validateCode(user.name, code, myOptions);
|
await VnUser.validateCode(user.name, code, myOptions);
|
||||||
|
|
||||||
|
|
|
@ -23,5 +23,5 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.login = async(ctx, user, password, options) => Self.app.models.VnUser.signin(ctx, user, password, options);
|
Self.login = async(ctx, user, password, options) => Self.app.models.VnUser.signIn(ctx, user, password, options);
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,7 +79,7 @@ describe('account changePassword()', () => {
|
||||||
passExpired: yesterday
|
passExpired: yesterday
|
||||||
}
|
}
|
||||||
, options);
|
, options);
|
||||||
await models.VnUser.signin(unauthCtx, 'trainee', 'nightmare', options);
|
await models.VnUser.signIn(unauthCtx, 'trainee', 'nightmare', options);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message != 'Pass expired')
|
if (e.message != 'Pass expired')
|
||||||
throw e;
|
throw e;
|
||||||
|
|
|
@ -77,14 +77,6 @@ module.exports = Self => {
|
||||||
if (salesPersonId)
|
if (salesPersonId)
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||||
|
|
||||||
await models.ClaimLog.create({
|
|
||||||
originFk: args.id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'insert',
|
|
||||||
description: 'Claim-pickup-order sent',
|
|
||||||
changedModel: 'Mail'
|
|
||||||
});
|
|
||||||
|
|
||||||
const email = new Email('claim-pickup-order', params);
|
const email = new Email('claim-pickup-order', params);
|
||||||
|
|
||||||
return email.send();
|
return email.send();
|
||||||
|
|
|
@ -19,9 +19,6 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.confirmTransaction = async(ctx, id, options) => {
|
Self.confirmTransaction = async(ctx, id, options) => {
|
||||||
const models = Self.app.models;
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
let tx;
|
let tx;
|
||||||
const myOptions = {userId: ctx.req.accessToken.userId};
|
const myOptions = {userId: ctx.req.accessToken.userId};
|
||||||
|
|
||||||
|
@ -34,29 +31,8 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const oldTpvTransaction = await models.TpvTransaction.findById(id, null, myOptions);
|
|
||||||
|
|
||||||
const confirm = await Self.rawSql('CALL hedera.tpvTransaction_confirmById(?)', [id], myOptions);
|
const confirm = await Self.rawSql('CALL hedera.tpvTransaction_confirmById(?)', [id], myOptions);
|
||||||
|
|
||||||
const tpvTransaction = await models.TpvTransaction.findById(id, null, myOptions);
|
|
||||||
|
|
||||||
const oldInstance = {status: oldTpvTransaction.status};
|
|
||||||
const newInstance = {status: tpvTransaction.status};
|
|
||||||
|
|
||||||
const logRecord = {
|
|
||||||
originFk: tpvTransaction.clientFk,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'TpvTransaction',
|
|
||||||
changedModelId: id,
|
|
||||||
oldInstance: oldInstance,
|
|
||||||
newInstance: newInstance
|
|
||||||
};
|
|
||||||
|
|
||||||
await models.ClientLog.create(logRecord, myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return confirm;
|
return confirm;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
|
|
|
@ -30,34 +30,9 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.sendSms = async(ctx, id, destination, message, options) => {
|
Self.sendSms = async(ctx, id, destination, message) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
|
||||||
|
|
||||||
if (typeof options == 'object')
|
|
||||||
Object.assign(myOptions, options);
|
|
||||||
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
const sms = await models.Sms.send(ctx, destination, message);
|
const sms = await models.Sms.send(ctx, destination, message);
|
||||||
const logRecord = {
|
|
||||||
originFk: id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'insert',
|
|
||||||
changedModel: 'sms',
|
|
||||||
newInstance: {
|
|
||||||
destinationFk: id,
|
|
||||||
destination: destination,
|
|
||||||
message: message,
|
|
||||||
statusCode: sms.statusCode,
|
|
||||||
status: sms.status
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const clientLog = await models.ClientLog.create(logRecord, myOptions);
|
|
||||||
|
|
||||||
sms.logId = clientLog.id;
|
|
||||||
|
|
||||||
return sms;
|
return sms;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,10 +13,7 @@ describe('client sendSms()', () => {
|
||||||
|
|
||||||
const sms = await models.Client.sendSms(ctx, id, destination, message, options);
|
const sms = await models.Client.sendSms(ctx, id, destination, message, options);
|
||||||
|
|
||||||
const createdLog = await models.ClientLog.findById(sms.logId, null, options);
|
expect(sms).toBeDefined();
|
||||||
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
|
|
||||||
|
|
||||||
expect(json.message).toEqual(message);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -144,20 +144,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (taxDataChecked && !hasSageData)
|
if (taxDataChecked && !hasSageData)
|
||||||
throw new UserError(`You need to fill sage information before you check verified data`);
|
throw new UserError(`You need to fill sage information before you check verified data`);
|
||||||
if (args.despiteOfClient) {
|
|
||||||
const logRecord = {
|
|
||||||
originFk: clientId,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Client',
|
|
||||||
changedModelId: clientId,
|
|
||||||
description: $t(`Client checked as validated despite of duplication`, {
|
|
||||||
clientId: args.despiteOfClient
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
await models.ClientLog.create(logRecord, myOptions);
|
|
||||||
}
|
|
||||||
// Remove unwanted properties
|
// Remove unwanted properties
|
||||||
delete args.ctx;
|
delete args.ctx;
|
||||||
delete args.id;
|
delete args.id;
|
||||||
|
|
|
@ -60,6 +60,7 @@ module.exports = Self => {
|
||||||
DISTINCT c.id clientFk,
|
DISTINCT c.id clientFk,
|
||||||
c.name clientName,
|
c.name clientName,
|
||||||
c.salesPersonFk,
|
c.salesPersonFk,
|
||||||
|
c.businessTypeFk,
|
||||||
u.name salesPersonName,
|
u.name salesPersonName,
|
||||||
d.amount,
|
d.amount,
|
||||||
co.created,
|
co.created,
|
||||||
|
|
|
@ -401,22 +401,32 @@ module.exports = Self => {
|
||||||
Self.changeCredit = async function changeCredit(ctx, finalState, changes) {
|
Self.changeCredit = async function changeCredit(ctx, finalState, changes) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const userId = ctx.options.accessToken.userId;
|
const userId = ctx.options.accessToken.userId;
|
||||||
const accessToken = {req: {accessToken: ctx.options.accessToken} };
|
const accessToken = {req: {accessToken: ctx.options.accessToken}};
|
||||||
|
|
||||||
const canEditCredit = await models.ACL.checkAccessAcl(accessToken, 'Client', 'editCredit', 'WRITE');
|
const canEditCredit = await models.ACL.checkAccessAcl(accessToken, 'Client', 'editCredit', 'WRITE');
|
||||||
if (!canEditCredit) {
|
if (!canEditCredit) {
|
||||||
const lastCredit = await models.ClientCredit.findOne({
|
const lastCredit = await models.ClientCredit.findOne({
|
||||||
|
field: ['workerFk', 'amount'],
|
||||||
where: {
|
where: {
|
||||||
clientFk: finalState.id
|
clientFk: finalState.id
|
||||||
},
|
},
|
||||||
order: 'id DESC'
|
order: 'id DESC'
|
||||||
}, ctx.options);
|
}, ctx.options);
|
||||||
|
|
||||||
const lastAmount = lastCredit && lastCredit.amount;
|
if (lastCredit && lastCredit.amount == 0) {
|
||||||
const lastCreditIsNotEditable = !await models.ACL.checkAccessAcl(accessToken, 'Client', 'isNotEditableCredit', 'WRITE');
|
const zeroCreditEditor =
|
||||||
|
await models.ACL.checkAccessAcl(accessToken, 'Client', 'zeroCreditEditor', 'WRITE');
|
||||||
|
const lastCreditIsNotEditable =
|
||||||
|
await models.ACL.checkAccessAcl(
|
||||||
|
{req: {accessToken: {userId: lastCredit.workerFk}}},
|
||||||
|
'Client',
|
||||||
|
'zeroCreditEditor',
|
||||||
|
'WRITE'
|
||||||
|
);
|
||||||
|
|
||||||
if (lastAmount == 0 && lastCreditIsNotEditable)
|
if (lastCreditIsNotEditable && !zeroCreditEditor)
|
||||||
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
|
throw new UserError(`You can't change the credit set to zero from a financialBoss`);
|
||||||
|
}
|
||||||
|
|
||||||
const creditLimits = await models.ClientCreditLimit.find({
|
const creditLimits = await models.ClientCreditLimit.find({
|
||||||
fields: ['roleFk'],
|
fields: ['roleFk'],
|
||||||
|
@ -479,21 +489,6 @@ module.exports = Self => {
|
||||||
hasChanges = oldData.name != changes.name || oldData.active != changes.active;
|
hasChanges = oldData.name != changes.name || oldData.active != changes.active;
|
||||||
|
|
||||||
if (!hasChanges) return;
|
if (!hasChanges) return;
|
||||||
|
|
||||||
const isClient = await Self.app.models.Client.count({id: oldData.id});
|
|
||||||
if (isClient) {
|
|
||||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
||||||
const userId = loopBackContext.active.accessToken.userId;
|
|
||||||
const logRecord = {
|
|
||||||
originFk: oldData.id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'VnUser',
|
|
||||||
oldInstance: {name: oldData.name, active: oldData.active},
|
|
||||||
newInstance: {name: changes.name, active: changes.active}
|
|
||||||
};
|
|
||||||
await Self.app.models.ClientLog.create(logRecord);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,22 +60,22 @@ describe('Client Model', () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const context = {options};
|
const ctx = {options};
|
||||||
|
|
||||||
// Set credit to zero by a financialBoss
|
// Set credit to zero by a financialBoss
|
||||||
const financialBoss = await models.VnUser.findOne({
|
const financialBoss = await models.VnUser.findOne({
|
||||||
where: {name: 'financialBoss'}
|
where: {name: 'financialBoss'}
|
||||||
}, options);
|
}, options);
|
||||||
context.options.accessToken = {userId: financialBoss.id};
|
ctx.options.accessToken = {userId: financialBoss.id};
|
||||||
|
|
||||||
await models.Client.changeCredit(context, instance, {credit: 0});
|
await models.Client.changeCredit(ctx, instance, {credit: 0});
|
||||||
|
|
||||||
const salesAssistant = await models.VnUser.findOne({
|
const salesAssistant = await models.VnUser.findOne({
|
||||||
where: {name: 'salesAssistant'}
|
where: {name: 'salesAssistant'}
|
||||||
}, options);
|
}, options);
|
||||||
context.options.accessToken = {userId: salesAssistant.id};
|
ctx.options.accessToken = {userId: salesAssistant.id};
|
||||||
|
|
||||||
await models.Client.changeCredit(context, instance, {credit: 300});
|
await models.Client.changeCredit(ctx, instance, {credit: 300});
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -93,14 +93,14 @@ describe('Client Model', () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
const context = {options};
|
const ctx = {options};
|
||||||
|
|
||||||
const salesAssistant = await models.VnUser.findOne({
|
const salesAssistant = await models.VnUser.findOne({
|
||||||
where: {name: 'salesAssistant'}
|
where: {name: 'salesAssistant'}
|
||||||
}, options);
|
}, options);
|
||||||
context.options.accessToken = {userId: salesAssistant.id};
|
ctx.options.accessToken = {userId: salesAssistant.id};
|
||||||
|
|
||||||
await models.Client.changeCredit(context, instance, {credit: 99999});
|
await models.Client.changeCredit(ctx, instance, {credit: 99999});
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
filter="::$ctrl.filter"
|
filter="::$ctrl.filter"
|
||||||
limit="20"
|
limit="20"
|
||||||
order="amount DESC"
|
order="amount DESC"
|
||||||
data="defaulters"
|
data="$ctrl.defaulters"
|
||||||
on-data-change="$ctrl.reCheck()"
|
on-data-change="$ctrl.reCheck()"
|
||||||
auto-load="true">
|
auto-load="true">
|
||||||
</vn-crud-model>
|
</vn-crud-model>
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="vn-pa-md">
|
<div class="vn-pa-md">
|
||||||
<vn-button
|
<vn-button
|
||||||
ng-show="$ctrl.checked.length > 0"
|
disabled="$ctrl.checked.length == 0"
|
||||||
ng-click="notesDialog.show()"
|
ng-click="notesDialog.show()"
|
||||||
name="notesDialog"
|
name="notesDialog"
|
||||||
vn-tooltip="Add observation"
|
vn-tooltip="Add observation"
|
||||||
|
@ -54,6 +54,9 @@
|
||||||
<th field="clientFk">
|
<th field="clientFk">
|
||||||
<span translate>Client</span>
|
<span translate>Client</span>
|
||||||
</th>
|
</th>
|
||||||
|
<th>
|
||||||
|
<span translate>Es trabajador</span>
|
||||||
|
</th>
|
||||||
<th field="salesPersonFk">
|
<th field="salesPersonFk">
|
||||||
<span translate>Comercial</span>
|
<span translate>Comercial</span>
|
||||||
</th>
|
</th>
|
||||||
|
@ -94,7 +97,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="defaulter in defaulters">
|
<tr ng-repeat="defaulter in $ctrl.defaulters">
|
||||||
<td shrink>
|
<td shrink>
|
||||||
<vn-check
|
<vn-check
|
||||||
ng-model="defaulter.checked"
|
ng-model="defaulter.checked"
|
||||||
|
@ -110,6 +113,12 @@
|
||||||
{{::defaulter.clientName}}
|
{{::defaulter.clientName}}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<vn-check
|
||||||
|
ng-model="defaulter.isWorker"
|
||||||
|
disabled="true">
|
||||||
|
</vn-check>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span
|
<span
|
||||||
title="{{::defaulter.salesPersonName}}"
|
title="{{::defaulter.salesPersonName}}"
|
||||||
|
|
|
@ -6,6 +6,7 @@ export default class Controller extends Section {
|
||||||
constructor($element, $) {
|
constructor($element, $) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
this.defaulter = {};
|
this.defaulter = {};
|
||||||
|
this.defaulters = [];
|
||||||
this.checkedDefaulers = [];
|
this.checkedDefaulers = [];
|
||||||
|
|
||||||
this.smartTableOptions = {
|
this.smartTableOptions = {
|
||||||
|
@ -69,6 +70,18 @@ export default class Controller extends Section {
|
||||||
this.getBalanceDueTotal();
|
this.getBalanceDueTotal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set defaulters(value) {
|
||||||
|
if (!value || !value.length) return;
|
||||||
|
for (let defaulter of value)
|
||||||
|
defaulter.isWorker = defaulter.businessTypeFk === 'worker';
|
||||||
|
|
||||||
|
this._defaulters = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaulters() {
|
||||||
|
return this._defaulters;
|
||||||
|
}
|
||||||
|
|
||||||
get checked() {
|
get checked() {
|
||||||
const clients = this.$.model.data || [];
|
const clients = this.$.model.data || [];
|
||||||
const checkedLines = [];
|
const checkedLines = [];
|
||||||
|
|
|
@ -17,6 +17,7 @@ describe('InvoiceOut refund()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await models.TicketRefund.destroyAll(null, options);
|
||||||
const result = await models.InvoiceOut.refund(ctx, 'T1111111', withWarehouse, options);
|
const result = await models.InvoiceOut.refund(ctx, 'T1111111', withWarehouse, options);
|
||||||
|
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe('item getBalance()', () => {
|
||||||
const secondItemBalance = await models.Item.getBalance(ctx, secondFilter, options);
|
const secondItemBalance = await models.Item.getBalance(ctx, secondFilter, options);
|
||||||
|
|
||||||
expect(firstItemBalance[9].claimFk).toEqual(null);
|
expect(firstItemBalance[9].claimFk).toEqual(null);
|
||||||
expect(secondItemBalance[5].claimFk).toEqual(2);
|
expect(secondItemBalance[4].claimFk).toEqual(2);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -42,8 +42,9 @@ module.exports = Self => {
|
||||||
|
|
||||||
promises.push(Self.app.models.ItemTaxCountry.updateAll(
|
promises.push(Self.app.models.ItemTaxCountry.updateAll(
|
||||||
{id: tax.id},
|
{id: tax.id},
|
||||||
{taxClassFk: tax.taxClassFk}
|
{taxClassFk: tax.taxClassFk},
|
||||||
), myOptions);
|
myOptions
|
||||||
|
));
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.guessPriority = async(ctx, id) => {
|
Self.guessPriority = async(ctx, id) => {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
const $t = ctx.req.__; // $translate
|
|
||||||
const query = `CALL vn.routeGuessPriority(?)`;
|
const query = `CALL vn.routeGuessPriority(?)`;
|
||||||
const tx = await Self.beginTransaction({});
|
const tx = await Self.beginTransaction({});
|
||||||
|
|
||||||
|
@ -30,16 +29,6 @@ module.exports = Self => {
|
||||||
|
|
||||||
const priority = await Self.rawSql(query, [id], options);
|
const priority = await Self.rawSql(query, [id], options);
|
||||||
|
|
||||||
let logRecord = {
|
|
||||||
originFk: id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Route',
|
|
||||||
description: $t('Sorts whole route')
|
|
||||||
};
|
|
||||||
|
|
||||||
await Self.app.models.RouteLog.create(logRecord, options);
|
|
||||||
|
|
||||||
await tx.commit();
|
await tx.commit();
|
||||||
return priority;
|
return priority;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -31,18 +31,6 @@ describe('route updateVolume()', () => {
|
||||||
const updatedRoute = await app.models.Route.findById(routeId, null, options);
|
const updatedRoute = await app.models.Route.findById(routeId, null, options);
|
||||||
|
|
||||||
expect(updatedRoute.m3).not.toEqual(route.m3);
|
expect(updatedRoute.m3).not.toEqual(route.m3);
|
||||||
|
|
||||||
const logs = await app.models.RouteLog.find({
|
|
||||||
fields: ['id', 'newInstance']
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
const m3Log = logs.filter(log => {
|
|
||||||
if (log.newInstance)
|
|
||||||
return log.newInstance.m3 === updatedRoute.m3;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(m3Log.length).toEqual(1);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
|
|
@ -35,24 +35,9 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const originalRoute = await models.Route.findById(id, null, myOptions);
|
|
||||||
|
|
||||||
await Self.rawSql(`CALL vn.routeUpdateM3(?)`, [id], myOptions);
|
await Self.rawSql(`CALL vn.routeUpdateM3(?)`, [id], myOptions);
|
||||||
|
|
||||||
const updatedRoute = await models.Route.findById(id, null, myOptions);
|
const updatedRoute = await models.Route.findById(id, null, myOptions);
|
||||||
|
|
||||||
await models.RouteLog.create({
|
|
||||||
originFk: id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Route',
|
|
||||||
changedModelId: id,
|
|
||||||
oldInstance: {m3: originalRoute.m3},
|
|
||||||
newInstance: {m3: updatedRoute.m3}
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return updatedRoute;
|
return updatedRoute;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (tx) await tx.rollback();
|
if (tx) await tx.rollback();
|
||||||
|
|
|
@ -106,7 +106,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = `CALL vn.ticket_recalc(?)`;
|
const query = `CALL vn.ticket_recalc(?, NULL)`;
|
||||||
await Self.rawSql(query, [refundTicket.id], myOptions);
|
await Self.rawSql(query, [refundTicket.id], myOptions);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
|
@ -96,7 +96,7 @@ module.exports = Self => {
|
||||||
await sale.updateAttributes({price: newPrice}, myOptions);
|
await sale.updateAttributes({price: newPrice}, myOptions);
|
||||||
|
|
||||||
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
|
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
|
||||||
await Self.rawSql('CALL vn.ticket_recalc(?)', [sale.ticketFk], myOptions);
|
await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [sale.ticketFk], myOptions);
|
||||||
|
|
||||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
|
|
|
@ -97,22 +97,6 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
|
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
|
||||||
|
|
||||||
const logRecord = {
|
|
||||||
originFk: sale.ticketFk,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'ticketRequest',
|
|
||||||
newInstance: {
|
|
||||||
destinationFk: sale.ticketFk,
|
|
||||||
quantity: sale.quantity,
|
|
||||||
concept: sale.concept,
|
|
||||||
itemId: sale.itemFk,
|
|
||||||
ticketId: sale.ticketFk,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
await Self.app.models.TicketLog.create(logRecord, myOptions);
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
return sale;
|
return sale;
|
||||||
|
|
|
@ -85,7 +85,7 @@ module.exports = Self => {
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
|
|
||||||
await Self.rawSql('CALL vn.sale_calculateComponent(?, NULL)', [newSale.id], myOptions);
|
await Self.rawSql('CALL vn.sale_calculateComponent(?, NULL)', [newSale.id], myOptions);
|
||||||
await Self.rawSql('CALL vn.ticket_recalc(?)', [id], myOptions);
|
await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [id], myOptions);
|
||||||
|
|
||||||
const sale = await models.Sale.findById(newSale.id, {
|
const sale = await models.Sale.findById(newSale.id, {
|
||||||
include: {
|
include: {
|
||||||
|
|
|
@ -242,16 +242,6 @@ module.exports = Self => {
|
||||||
const oldProperties = await loggable.translateValues(Self, changes.old);
|
const oldProperties = await loggable.translateValues(Self, changes.old);
|
||||||
const newProperties = await loggable.translateValues(Self, changes.new);
|
const newProperties = await loggable.translateValues(Self, changes.new);
|
||||||
|
|
||||||
await models.TicketLog.create({
|
|
||||||
originFk: args.id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Ticket',
|
|
||||||
changedModelId: args.id,
|
|
||||||
oldInstance: oldProperties,
|
|
||||||
newInstance: newProperties
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
const salesPersonId = originalTicket.client().salesPersonFk;
|
const salesPersonId = originalTicket.client().salesPersonFk;
|
||||||
if (salesPersonId) {
|
if (salesPersonId) {
|
||||||
const origin = ctx.req.headers.origin;
|
const origin = ctx.req.headers.origin;
|
||||||
|
|
|
@ -52,22 +52,9 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
if (!ticket.originId || !ticket.destinationId) continue;
|
if (!ticket.originId || !ticket.destinationId) continue;
|
||||||
|
|
||||||
const ticketDestinationLogRecord = {
|
|
||||||
originFk: ticket.destinationId,
|
|
||||||
userFk: ctx.req.accessToken.userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Ticket',
|
|
||||||
changedModelId: ticket.destinationId,
|
|
||||||
changedModelValue: ticket.destinationId,
|
|
||||||
oldInstance: {},
|
|
||||||
newInstance: {mergedTicket: ticket.originId}
|
|
||||||
};
|
|
||||||
|
|
||||||
await models.Sale.updateAll({ticketFk: ticket.originId}, {ticketFk: ticket.destinationId}, myOptions);
|
await models.Sale.updateAll({ticketFk: ticket.originId}, {ticketFk: ticket.destinationId}, myOptions);
|
||||||
if (await models.Ticket.setDeleted(ctx, ticket.originId, myOptions)) {
|
if (await models.Ticket.setDeleted(ctx, ticket.originId, myOptions))
|
||||||
await models.TicketLog.create(ticketDestinationLogRecord, myOptions);
|
|
||||||
await models.Chat.sendCheckingPresence(ctx, ticket.workerFk, message);
|
await models.Chat.sendCheckingPresence(ctx, ticket.workerFk, message);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (tx)
|
if (tx)
|
||||||
await tx.commit();
|
await tx.commit();
|
||||||
|
|
|
@ -30,54 +30,12 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.sendSms = async(ctx, id, destination, message, options) => {
|
Self.sendSms = async(ctx, id, destination, message) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const sms = await models.Sms.send(ctx, destination, message);
|
||||||
let tx;
|
await models.TicketSms.create({
|
||||||
|
ticketFk: id,
|
||||||
if (typeof options == 'object')
|
smsFk: sms.id
|
||||||
Object.assign(myOptions, options);
|
});
|
||||||
|
|
||||||
if (!myOptions.transaction) {
|
|
||||||
tx = await Self.beginTransaction({});
|
|
||||||
myOptions.transaction = tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const sms = await models.Sms.send(ctx, destination, message);
|
|
||||||
|
|
||||||
const newTicketSms = {
|
|
||||||
ticketFk: id,
|
|
||||||
smsFk: sms.id
|
|
||||||
};
|
|
||||||
await models.TicketSms.create(newTicketSms);
|
|
||||||
|
|
||||||
const logRecord = {
|
|
||||||
originFk: id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'insert',
|
|
||||||
changedModel: 'sms',
|
|
||||||
newInstance: {
|
|
||||||
destinationFk: id,
|
|
||||||
destination: destination,
|
|
||||||
message: message,
|
|
||||||
statusCode: sms.statusCode,
|
|
||||||
status: sms.status
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ticketLog = await models.TicketLog.create(logRecord, myOptions);
|
|
||||||
|
|
||||||
sms.logId = ticketLog.id;
|
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
|
||||||
|
|
||||||
return sms;
|
|
||||||
} catch (e) {
|
|
||||||
if (tx) await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,12 +43,10 @@ describe('ticket merge()', () => {
|
||||||
|
|
||||||
await models.Ticket.merge(ctx, [tickets], options);
|
await models.Ticket.merge(ctx, [tickets], options);
|
||||||
|
|
||||||
const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets.destinationId}}, options);
|
|
||||||
const deletedTicket = await models.Ticket.findOne({where: {id: tickets.originId}}, options);
|
const deletedTicket = await models.Ticket.findOne({where: {id: tickets.originId}}, options);
|
||||||
const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets.destinationId}}, options);
|
const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets.destinationId}}, options);
|
||||||
const chatNotificationAfterMerge = await models.Chat.find(null, options);
|
const chatNotificationAfterMerge = await models.Chat.find(null, options);
|
||||||
|
|
||||||
expect(createdTicketLog.length).toEqual(1);
|
|
||||||
expect(deletedTicket.isDeleted).toEqual(true);
|
expect(deletedTicket.isDeleted).toEqual(true);
|
||||||
expect(salesTicketFuture.length).toEqual(2);
|
expect(salesTicketFuture.length).toEqual(2);
|
||||||
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);
|
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);
|
||||||
|
|
|
@ -12,19 +12,14 @@ describe('ticket sendSms()', () => {
|
||||||
const destination = 222222222;
|
const destination = 222222222;
|
||||||
const message = 'this is the message created in a test';
|
const message = 'this is the message created in a test';
|
||||||
|
|
||||||
const sms = await models.Ticket.sendSms(ctx, id, destination, message, options);
|
await models.Ticket.sendSms(ctx, id, destination, message, options);
|
||||||
|
|
||||||
const createdLog = await models.TicketLog.findById(sms.logId, null, options);
|
|
||||||
|
|
||||||
const filter = {
|
const filter = {
|
||||||
ticketFk: createdLog.originFk
|
ticketFk: id
|
||||||
};
|
};
|
||||||
const ticketSms = await models.TicketSms.findOne(filter, options);
|
const ticketSms = await models.TicketSms.findOne(filter, options);
|
||||||
|
|
||||||
const json = JSON.parse(JSON.stringify(createdLog.newInstance));
|
expect(ticketSms.ticketFk).toEqual(id);
|
||||||
|
|
||||||
expect(json.message).toEqual(message);
|
|
||||||
expect(ticketSms.ticketFk).toEqual(createdLog.originFk);
|
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -84,13 +84,6 @@ module.exports = Self => {
|
||||||
|
|
||||||
for (const sale of sales) {
|
for (const sale of sales) {
|
||||||
const originalSale = map.get(sale.id);
|
const originalSale = map.get(sale.id);
|
||||||
const originalSaleData = { // <-- Loopback modifies original instance on save
|
|
||||||
itemFk: originalSale.itemFk,
|
|
||||||
quantity: originalSale.quantity,
|
|
||||||
concept: originalSale.concept,
|
|
||||||
ticketFk: originalSale.ticketFk
|
|
||||||
};
|
|
||||||
|
|
||||||
if (sale.quantity == originalSale.quantity) {
|
if (sale.quantity == originalSale.quantity) {
|
||||||
query = `UPDATE sale
|
query = `UPDATE sale
|
||||||
SET ticketFk = ?
|
SET ticketFk = ?
|
||||||
|
@ -100,48 +93,6 @@ module.exports = Self => {
|
||||||
await transferPartialSale(
|
await transferPartialSale(
|
||||||
ticketId, originalSale, sale, myOptions);
|
ticketId, originalSale, sale, myOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log to original ticket
|
|
||||||
await models.TicketLog.create({
|
|
||||||
originFk: id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Sale',
|
|
||||||
changedModelId: sale.id,
|
|
||||||
oldInstance: {
|
|
||||||
item: originalSaleData.itemFk,
|
|
||||||
quantity: originalSaleData.quantity,
|
|
||||||
concept: originalSaleData.concept,
|
|
||||||
ticket: originalSaleData.ticketFk
|
|
||||||
},
|
|
||||||
newInstance: {
|
|
||||||
item: sale.itemFk,
|
|
||||||
quantity: sale.quantity,
|
|
||||||
concept: sale.concept,
|
|
||||||
ticket: ticketId
|
|
||||||
}
|
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
// Log to destination ticket
|
|
||||||
await models.TicketLog.create({
|
|
||||||
originFk: ticketId,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'Sale',
|
|
||||||
changedModelId: sale.id,
|
|
||||||
oldInstance: {
|
|
||||||
item: originalSaleData.itemFk,
|
|
||||||
quantity: originalSaleData.quantity,
|
|
||||||
concept: originalSaleData.concept,
|
|
||||||
ticket: originalSaleData.ticketFk
|
|
||||||
},
|
|
||||||
newInstance: {
|
|
||||||
item: sale.itemFk,
|
|
||||||
quantity: sale.quantity,
|
|
||||||
concept: sale.concept,
|
|
||||||
ticket: ticketId
|
|
||||||
}
|
|
||||||
}, myOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);
|
const isTicketEmpty = await models.Ticket.isEmpty(id, myOptions);
|
||||||
|
|
|
@ -147,7 +147,7 @@ module.exports = Self => {
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
|
await Self.rawSql('CALL vn.manaSpellersRequery(?)', [userId], myOptions);
|
||||||
await Self.rawSql('CALL vn.ticket_recalc(?)', [id], myOptions);
|
await Self.rawSql('CALL vn.ticket_recalc(?, NULL)', [id], myOptions);
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(id, {
|
const ticket = await models.Ticket.findById(id, {
|
||||||
include: {
|
include: {
|
||||||
|
|
|
@ -1,30 +1,4 @@
|
||||||
|
|
||||||
const LoopBackContext = require('loopback-context');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
// Methods
|
// Methods
|
||||||
require('./ticket-methods')(Self);
|
require('./ticket-methods')(Self);
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
|
||||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
|
||||||
const httpCtx = loopBackContext.active;
|
|
||||||
|
|
||||||
if (ctx.isNewInstance) return;
|
|
||||||
|
|
||||||
let changes = ctx.data || ctx.instance;
|
|
||||||
|
|
||||||
if (changes.routeFk === null && ctx.currentInstance.routeFk != null) {
|
|
||||||
let instance = JSON.parse(JSON.stringify(ctx.currentInstance));
|
|
||||||
let userId = httpCtx.accessToken.userId;
|
|
||||||
let logRecord = {
|
|
||||||
originFk: ctx.currentInstance.routeFk,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'delete',
|
|
||||||
changedModel: 'Route',
|
|
||||||
oldInstance: {ticket: instance.id},
|
|
||||||
newInstance: null
|
|
||||||
};
|
|
||||||
await Self.app.models.RouteLog.create(logRecord);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -156,7 +156,7 @@
|
||||||
icon="install_mobile"
|
icon="install_mobile"
|
||||||
ng-show="$ctrl.totalChecked > 0"
|
ng-show="$ctrl.totalChecked > 0"
|
||||||
ng-click="$ctrl.sendDocuware()"
|
ng-click="$ctrl.sendDocuware()"
|
||||||
vn-tooltip="Set as delivered and open delivery note(s)"
|
vn-tooltip="Set as delivered and send delivery note(s) to the tablet"
|
||||||
tooltip-position="left">
|
tooltip-position="left">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-button class="round vn-mb-sm"
|
<vn-button class="round vn-mb-sm"
|
||||||
|
|
|
@ -3,7 +3,7 @@ Go to lines: Ir a lineas
|
||||||
Not available: No disponible
|
Not available: No disponible
|
||||||
Not visible: No visible
|
Not visible: No visible
|
||||||
Payment on account...: Pago a cuenta...
|
Payment on account...: Pago a cuenta...
|
||||||
Set as delivered and open delivery note(s): Marcar como servido/s y abrir albarán/es
|
Set as delivered and send delivery note(s) to the tablet: Marcar como servido/s y enviar albarán/es a la tablet
|
||||||
Closure: Cierre
|
Closure: Cierre
|
||||||
You cannot make a payment on account from multiple clients: No puedes realizar un pago a cuenta de clientes diferentes
|
You cannot make a payment on account from multiple clients: No puedes realizar un pago a cuenta de clientes diferentes
|
||||||
Filter by selection: Filtro por selección
|
Filter by selection: Filtro por selección
|
||||||
|
@ -17,4 +17,4 @@ Quick invoice: Factura rápida
|
||||||
Multiple invoice: Factura múltiple
|
Multiple invoice: Factura múltiple
|
||||||
Make invoice...: Crear factura...
|
Make invoice...: Crear factura...
|
||||||
Invoice selected tickets: Facturar tickets seleccionados
|
Invoice selected tickets: Facturar tickets seleccionados
|
||||||
Are you sure to invoice tickets: ¿Seguro que quieres facturar {{ticketsAmount}} tickets?
|
Are you sure to invoice tickets: ¿Seguro que quieres facturar {{ticketsAmount}} tickets?
|
||||||
|
|
|
@ -25,9 +25,7 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.cloneWithEntries = async(ctx, id) => {
|
Self.cloneWithEntries = async(ctx, id) => {
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
const conn = Self.dataSource.connector;
|
const conn = Self.dataSource.connector;
|
||||||
const models = Self.app.models;
|
|
||||||
const travel = await Self.findById(id, {
|
const travel = await Self.findById(id, {
|
||||||
fields: [
|
fields: [
|
||||||
'id',
|
'id',
|
||||||
|
@ -81,18 +79,6 @@ module.exports = Self => {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
const oldProperties = await loggable.translateValues(Self, travel);
|
|
||||||
const newProperties = await loggable.translateValues(Self, newTravel);
|
|
||||||
await models.TravelLog.create({
|
|
||||||
originFk: newTravel.id,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'insert',
|
|
||||||
changedModel: 'Travel',
|
|
||||||
changedModelId: newTravel.id,
|
|
||||||
oldInstance: oldProperties,
|
|
||||||
newInstance: newProperties
|
|
||||||
});
|
|
||||||
|
|
||||||
return newTravel.id;
|
return newTravel.id;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,24 +30,8 @@ module.exports = Self => {
|
||||||
SET travelFk = NULL, dmsFk = NULL
|
SET travelFk = NULL, dmsFk = NULL
|
||||||
WHERE id = ?`, [id], {userId});
|
WHERE id = ?`, [id], {userId});
|
||||||
|
|
||||||
const oldInstance = {
|
|
||||||
travelFk: travelThermograph.travelFk,
|
|
||||||
dmsFk: travelThermograph.dmsFk
|
|
||||||
};
|
|
||||||
|
|
||||||
await models.TravelLog.create({
|
|
||||||
originFk: travelThermograph.travelFk,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'delete',
|
|
||||||
changedModel: 'TravelThermograph',
|
|
||||||
changedModelId: id,
|
|
||||||
oldInstance: oldInstance,
|
|
||||||
newInstance: {}
|
|
||||||
});
|
|
||||||
|
|
||||||
travelThermograph.travelFk = null;
|
travelThermograph.travelFk = null;
|
||||||
travelThermograph.dmsFk = null;
|
travelThermograph.dmsFk = null;
|
||||||
|
|
||||||
return travelThermograph;
|
return travelThermograph;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,6 +37,16 @@ class Controller extends SearchPanel {
|
||||||
}
|
}
|
||||||
|
|
||||||
applyFilters(param) {
|
applyFilters(param) {
|
||||||
|
if (typeof this.filter.scopeDays === 'number') {
|
||||||
|
const shippedFrom = Date.vnNew();
|
||||||
|
shippedFrom.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const shippedTo = new Date(shippedFrom.getTime());
|
||||||
|
shippedTo.setDate(shippedTo.getDate() + this.filter.scopeDays);
|
||||||
|
shippedTo.setHours(23, 59, 59, 999);
|
||||||
|
Object.assign(this.filter, {shippedFrom, shippedTo});
|
||||||
|
}
|
||||||
|
|
||||||
this.model.applyFilter({}, this.filter)
|
this.model.applyFilter({}, this.filter)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (param && this.model._orgData.length === 1)
|
if (param && this.model._orgData.length === 1)
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td expand>{{entry.supplierName}}</vn-td>
|
<vn-td expand>{{entry.supplierName}}</vn-td>
|
||||||
<vn-td shrink>{{entry.ref}}</vn-td>
|
<vn-td shrink>{{entry.reference}}</vn-td>
|
||||||
<vn-td shrink>{{entry.hb}}</vn-td>
|
<vn-td shrink>{{entry.hb}}</vn-td>
|
||||||
<vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td>
|
<vn-td shrink>{{entry.freightValue | currency: 'EUR': 2}}</vn-td>
|
||||||
<vn-td shrink>{{entry.packageValue | currency: 'EUR': 2}}</vn-td>
|
<vn-td shrink>{{entry.packageValue | currency: 'EUR': 2}}</vn-td>
|
||||||
|
|
|
@ -40,7 +40,6 @@ module.exports = Self => {
|
||||||
Self.updateWorkerTimeControlMail = async(ctx, options) => {
|
Self.updateWorkerTimeControlMail = async(ctx, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const args = ctx.args;
|
const args = ctx.args;
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
|
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
|
||||||
|
@ -57,9 +56,6 @@ module.exports = Self => {
|
||||||
|
|
||||||
if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`);
|
if (!workerTimeControlMail) throw new UserError(`There aren't records for this week`);
|
||||||
|
|
||||||
const oldState = workerTimeControlMail.state;
|
|
||||||
const oldReason = workerTimeControlMail.reason;
|
|
||||||
|
|
||||||
await workerTimeControlMail.updateAttributes({
|
await workerTimeControlMail.updateAttributes({
|
||||||
state: args.state,
|
state: args.state,
|
||||||
reason: args.reason || null
|
reason: args.reason || null
|
||||||
|
@ -70,22 +66,5 @@ module.exports = Self => {
|
||||||
sendedCounter: workerTimeControlMail.sendedCounter + 1
|
sendedCounter: workerTimeControlMail.sendedCounter + 1
|
||||||
}, myOptions);
|
}, myOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const logRecord = {
|
|
||||||
originFk: args.workerId,
|
|
||||||
userFk: userId,
|
|
||||||
action: 'update',
|
|
||||||
changedModel: 'WorkerTimeControlMail',
|
|
||||||
oldInstance: {
|
|
||||||
state: oldState,
|
|
||||||
reason: oldReason
|
|
||||||
},
|
|
||||||
newInstance: {
|
|
||||||
state: args.state,
|
|
||||||
reason: args.reason
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return models.WorkerLog.create(logRecord, myOptions);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-back",
|
"name": "salix-back",
|
||||||
"version": "23.26.01",
|
"version": "23.28.01",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|
|
@ -2,18 +2,22 @@
|
||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
<div class="grid-block vn-pa-ml">
|
<div class="grid-block vn-pa-ml">
|
||||||
<h1>{{ $t('title') }}</h1>
|
<h1>{{ $t('title') }}</h1>
|
||||||
<p v-html="$t('description')"></p>
|
<p>{{ $t('description') }}</p>
|
||||||
<p v-html="$t('device', [device])"></p>
|
<p>
|
||||||
<p v-html="$t('ip', [ip])"></p>
|
{{ $t('device') }}: <strong>{{ device }}</strong>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{$t('ip')}}: <strong>{{ ip }}</strong>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
<div class="grid-block vn-pa-ml">
|
<div class="grid-block vn-pa-ml">
|
||||||
<p>{{$t('Enter the following code to continue to your account')}}</p>
|
<p>{{ $t('Enter the following code to continue to your account') }}</p>
|
||||||
<div class="code vn-pa-sm vn-m-md">
|
<div class="code vn-pa-sm vn-m-md">
|
||||||
{{ code }}
|
{{ code }}
|
||||||
</div>
|
</div>
|
||||||
<p>{{$t('It expires in 5 minutes')}}</p>
|
<p>{{ $t('It expires in 5 minutes') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</email-body>
|
</email-body>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
subject: Verification code
|
subject: Verification code
|
||||||
title: Verification code
|
title: Verification code
|
||||||
description: Somebody did request a verification code for login. If you didn't request it, please ignore this email.
|
description: Somebody did request a verification code for login. If you didn't request it, please ignore this email.
|
||||||
device: 'Device: <strong>{0}</strong>'
|
device: 'Device'
|
||||||
ip: 'IP: <strong>{0}</strong>'
|
ip: 'IP'
|
||||||
Enter the following code to continue to your account: Enter the following code to continue to your account
|
Enter the following code to continue to your account: Enter the following code to continue to your account
|
||||||
It expires in 5 minutes: It expires in 5 minutes
|
It expires in 5 minutes: It expires in 5 minutes
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
subject: Código de verificación
|
subject: Código de verificación
|
||||||
title: Código de verificación
|
title: Código de verificación
|
||||||
description: Alguien ha solicitado un código de verificación para poder iniciar sesión. Si no lo has solicitado tu, ignora este email.
|
description: Alguien ha solicitado un código de verificación para poder iniciar sesión. Si no lo has solicitado tu, ignora este email.
|
||||||
device: 'Dispositivo: <strong>{0}</strong>'
|
device: 'Dispositivo'
|
||||||
ip: 'IP: <strong>{0}</strong>'
|
ip: 'IP'
|
||||||
Enter the following code to continue to your account: Introduce el siguiente código para poder continuar con tu cuenta
|
Enter the following code to continue to your account: Introduce el siguiente código para poder continuar con tu cuenta
|
||||||
It expires in 5 minutes: Expira en 5 minutos
|
It expires in 5 minutes: Expira en 5 minutos
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
subject: Code de vérification
|
subject: Code de vérification
|
||||||
title: Code de vérification
|
title: Code de vérification
|
||||||
description: Quelqu'un a demandé un code de vérification pour se connecter. Si ce n'était pas toi, ignore cet email.
|
description: Quelqu'un a demandé un code de vérification pour se connecter. Si ce n'était pas toi, ignore cet email.
|
||||||
device: 'Appareil: <strong>{0}</strong>'
|
device: 'Appareil'
|
||||||
ip: 'IP: <strong>{0}</strong>'
|
ip: 'IP'
|
||||||
Enter the following code to continue to your account: Entrez le code suivant pour continuer avec votre compte
|
Enter the following code to continue to your account: Entrez le code suivant pour continuer avec votre compte
|
||||||
It expires in 5 minutes: Il expire dans 5 minutes.
|
It expires in 5 minutes: Il expire dans 5 minutes.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
subject: Código de verificação
|
subject: Código de verificação
|
||||||
title: Código de verificação
|
title: Código de verificação
|
||||||
description: Alguém solicitou um código de verificação para entrar. Se você não fez essa solicitação, ignore este e-mail.
|
description: Alguém solicitou um código de verificação para entrar. Se você não fez essa solicitação, ignore este e-mail.
|
||||||
device: 'Dispositivo: <strong>{0}</strong>'
|
device: 'Dispositivo'
|
||||||
ip: 'IP: <strong>{0}</strong>'
|
ip: 'IP'
|
||||||
Enter the following code to continue to your account: Insira o seguinte código para continuar com sua conta.
|
Enter the following code to continue to your account: Insira o seguinte código para continuar com sua conta.
|
||||||
It expires in 5 minutes: Expira em 5 minutos.
|
It expires in 5 minutes: Expira em 5 minutos.
|
||||||
|
|
Loading…
Reference in New Issue