Compare commits
55 Commits
5f26553092
...
2a93dcf5b8
Author | SHA1 | Date |
---|---|---|
Robert Ferrús | 2a93dcf5b8 | |
Jorge Penadés | 0b1bbbaaa6 | |
Jorge Penadés | 611b45bae0 | |
Alex Moreno | ee7dd66ce9 | |
Alex Moreno | 35e74a7635 | |
Alex Moreno | ab69dcd4fe | |
Javier Segarra | b241efc961 | |
Alex Moreno | 016e4ae37c | |
Carlos Satorres | e7d3ef0763 | |
Jorge Penadés | 6121eeaa3a | |
Carlos Satorres | 9cdb7c3cca | |
Javi Gallego | 69e555e7b4 | |
Javier Segarra | 7611acb8f2 | |
Javier Segarra | 80f8037f58 | |
Javier Segarra | 58855a7cdd | |
Javier Segarra | 6b354a20ad | |
Javier Segarra | 138d11b7b0 | |
Javier Segarra | 1576ab992d | |
Javier Segarra | 46774b2e73 | |
Javier Segarra | 9176cdb4cb | |
Javier Segarra | ed6c0924d7 | |
Javier Segarra | 48b82b02ca | |
Javi Gallego | d246b5f9a8 | |
Guillermo Bonet | b3cc020ea7 | |
Guillermo Bonet | 5e9a62eac2 | |
Alex Moreno | 2e22984e77 | |
Javier Segarra | 63533459b0 | |
Jorge Penadés | f2673c3818 | |
Guillermo Bonet | 1a5cc67312 | |
Javi Gallego | 2fa56cce4b | |
Alex Moreno | 37555cbb63 | |
Juan Ferrer | c7291e3367 | |
Juan Ferrer | 74772472d5 | |
Carlos Satorres | 23235e2b44 | |
Alex Moreno | 3fb1ed9dfe | |
Carlos Satorres | f9d5cad412 | |
Carlos Satorres | 461cb3d2ba | |
Carlos Satorres | 5d18c211a0 | |
Carlos Satorres | 8a568f180a | |
Carlos Satorres | ebe467f721 | |
Carlos Satorres | 2b455488a3 | |
Carlos Satorres | 6b07ee4eb1 | |
Juan Ferrer | bf7c67dbde | |
Juan Ferrer | 0a2fd9cf5d | |
Guillermo Bonet | 2847b993d3 | |
Guillermo Bonet | a4cbc33ee1 | |
Guillermo Bonet | b7da144967 | |
Guillermo Bonet | eb98de71b1 | |
Guillermo Bonet | 9f92bc4b4c | |
Guillermo Bonet | 9668468f94 | |
Guillermo Bonet | f6d46396b5 | |
Guillermo Bonet | cab3548c9b | |
Guillermo Bonet | 0dbb77fc64 | |
Guillermo Bonet | 9f6d034f9c | |
Guillermo Bonet | d1df8009a6 |
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -14,14 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [2348.01] - 2023-11-30
|
## [2348.01] - 2023-11-30
|
||||||
|
|
||||||
### Added
|
### Características Añadidas 🆕
|
||||||
- (Ticket -> Adelantar) Permite mover lineas sin generar negativos
|
- **Tickets → Adelantar:** Permite mover lineas sin generar negativos
|
||||||
- (Ticket -> Adelantar) Permite modificar la fecha de los tickets
|
- **Tickets → Adelantar:** Permite modificar la fecha de los tickets
|
||||||
- (Trabajadores -> Notificaciones) Nueva sección (lilium)
|
- **Trabajadores → Notificaciones:** Nueva sección (lilium)
|
||||||
|
|
||||||
### Changed
|
### Correcciones 🛠️
|
||||||
### Fixed
|
- **Tickets → RocketChat:** Arreglada detección de cambios
|
||||||
- (Ticket -> RocketChat) Arreglada detección de cambios
|
|
||||||
|
|
||||||
|
|
||||||
## [2346.01] - 2023-11-16
|
## [2346.01] - 2023-11-16
|
||||||
|
|
|
@ -49,13 +49,7 @@ module.exports = Self => {
|
||||||
if (vnUser.twoFactor)
|
if (vnUser.twoFactor)
|
||||||
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
throw new ForbiddenError(null, 'REQUIRES_2FA');
|
||||||
}
|
}
|
||||||
const validateLogin = await Self.validateLogin(user, password);
|
return Self.validateLogin(user, password, ctx);
|
||||||
await Self.app.models.SignInLog.create({
|
|
||||||
token: validateLogin.token,
|
|
||||||
userFk: vnUser.id,
|
|
||||||
ip: ctx.req.ip
|
|
||||||
});
|
|
||||||
return validateLogin;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.passExpired = async vnUser => {
|
Self.passExpired = async vnUser => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ const {models} = require('vn-loopback/server/server');
|
||||||
|
|
||||||
describe('VnUser Sign-in()', () => {
|
describe('VnUser Sign-in()', () => {
|
||||||
const employeeId = 1;
|
const employeeId = 1;
|
||||||
const unauthCtx = {
|
const unAuthCtx = {
|
||||||
req: {
|
req: {
|
||||||
headers: {},
|
headers: {},
|
||||||
connection: {
|
connection: {
|
||||||
|
@ -15,20 +15,21 @@ describe('VnUser Sign-in()', () => {
|
||||||
const {VnUser, AccessToken, SignInLog} = models;
|
const {VnUser, AccessToken, SignInLog} = models;
|
||||||
describe('when credentials are correct', () => {
|
describe('when credentials are correct', () => {
|
||||||
it('should return the token if user uses email', async() => {
|
it('should return the token if user uses email', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'salesAssistant@mydomain.com', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'salesAssistant@mydomain.com', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
let signInLog = await SignInLog.find({where: {token: accessToken.id}});
|
let signInLog = await SignInLog.find({where: {token: accessToken.id}});
|
||||||
|
|
||||||
expect(signInLog.length).toEqual(1);
|
expect(signInLog.length).toEqual(1);
|
||||||
expect(signInLog[0].userFk).toEqual(accessToken.userId);
|
expect(signInLog[0].userFk).toEqual(accessToken.userId);
|
||||||
|
expect(signInLog[0].owner).toEqual(true);
|
||||||
expect(login.token).toBeDefined();
|
expect(login.token).toBeDefined();
|
||||||
|
|
||||||
await VnUser.logout(ctx.req.accessToken.id);
|
await VnUser.logout(ctx.req.accessToken.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the token', async() => {
|
it('should return the token', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'salesAssistant', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'salesAssistant', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the token if the user doesnt exist but the client does', async() => {
|
it('should return the token if the user doesnt exist but the client does', async() => {
|
||||||
let login = await VnUser.signIn(unauthCtx, 'PetterParker', 'nightmare');
|
let login = await VnUser.signIn(unAuthCtx, 'PetterParker', 'nightmare');
|
||||||
let accessToken = await AccessToken.findById(login.token);
|
let accessToken = await AccessToken.findById(login.token);
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
let ctx = {req: {accessToken: accessToken}};
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await VnUser.signIn(unauthCtx, 'IDontExist', 'TotallyWrongPassword');
|
await VnUser.signIn(unAuthCtx, 'IDontExist', 'TotallyWrongPassword');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +75,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await employee.updateAttribute('twoFactor', 'email', options);
|
await employee.updateAttribute('twoFactor', 'email', options);
|
||||||
|
|
||||||
await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
|
await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
@ -99,7 +100,7 @@ describe('VnUser Sign-in()', () => {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
await employee.updateAttribute('passExpired', yesterday, options);
|
await employee.updateAttribute('passExpired', yesterday, options);
|
||||||
|
|
||||||
await VnUser.signIn(unauthCtx, 'employee', 'nightmare', options);
|
await VnUser.signIn(unAuthCtx, 'employee', 'nightmare', options);
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
|
|
@ -124,20 +124,42 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
return email.send();
|
return email.send();
|
||||||
});
|
});
|
||||||
Self.signInValidate = (user, userToken) => {
|
|
||||||
|
/**
|
||||||
|
* Sign-in validate
|
||||||
|
* @param {String} user The user
|
||||||
|
* @param {Object} userToken Options
|
||||||
|
* @param {Object} token accessToken
|
||||||
|
* @param {Object} ctx context
|
||||||
|
*/
|
||||||
|
Self.signInValidate = async(user, userToken, token, ctx) => {
|
||||||
const [[key, value]] = Object.entries(Self.userUses(user));
|
const [[key, value]] = Object.entries(Self.userUses(user));
|
||||||
if (userToken[key].toLowerCase().trim() !== value.toLowerCase().trim()) {
|
const isOwner = Self.rawSql(`SELECT ? = ? `, [userToken[key], value]);
|
||||||
console.error('ERROR!!! - Signin with other user', userToken, user);
|
await Self.app.models.SignInLog.create({
|
||||||
|
userName: user,
|
||||||
|
token: token.id,
|
||||||
|
userFk: userToken.id,
|
||||||
|
ip: ctx.req.ip,
|
||||||
|
owner: isOwner
|
||||||
|
});
|
||||||
|
if (!isOwner)
|
||||||
throw new UserError('Try again');
|
throw new UserError('Try again');
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.validateLogin = async function(user, password) {
|
/**
|
||||||
|
* Validate login params
|
||||||
|
* @param {String} user The user
|
||||||
|
* @param {String} password
|
||||||
|
* @param {Object} ctx context
|
||||||
|
*/
|
||||||
|
Self.validateLogin = async function(user, password, ctx) {
|
||||||
const loginInfo = Object.assign({password}, Self.userUses(user));
|
const loginInfo = Object.assign({password}, Self.userUses(user));
|
||||||
const token = await Self.login(loginInfo, 'user');
|
const token = await Self.login(loginInfo, 'user');
|
||||||
|
|
||||||
const userToken = await token.user.get();
|
const userToken = await token.user.get();
|
||||||
Self.signInValidate(user, userToken);
|
|
||||||
|
if (ctx)
|
||||||
|
await Self.signInValidate(user, userToken, token, ctx);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Self.app.models.Account.sync(userToken.name, password);
|
await Self.app.models.Account.sync(userToken.name, password);
|
||||||
|
@ -187,8 +209,8 @@ module.exports = function(Self) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
||||||
.filter(acl => acl.property != 'changePassword');
|
.filter(acl => acl.property != 'changePassword');
|
||||||
|
|
||||||
Self.userSecurity = async(ctx, userId, options) => {
|
Self.userSecurity = async(ctx, userId, options) => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
@ -226,10 +248,12 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
const env = process.env.NODE_ENV;
|
const env = process.env.NODE_ENV;
|
||||||
const liliumUrl = await Self.app.models.Url.findOne({
|
const liliumUrl = await Self.app.models.Url.findOne({
|
||||||
where: {and: [
|
where: {
|
||||||
{appName: 'lilium'},
|
and: [
|
||||||
{environment: env}
|
{appName: 'lilium'},
|
||||||
]}
|
{environment: env}
|
||||||
|
]
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
class Mailer {
|
class Mailer {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `signInLog`
|
-- Table structure for table `signInLog`
|
||||||
-- Description: log to debug cross-login error
|
-- Description: log to debug cross-login error
|
||||||
|
@ -13,7 +12,9 @@ CREATE TABLE `account`.`signInLog` (
|
||||||
`token` varchar(255) NOT NULL ,
|
`token` varchar(255) NOT NULL ,
|
||||||
`userFk` int(10) unsigned DEFAULT NULL,
|
`userFk` int(10) unsigned DEFAULT NULL,
|
||||||
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
`creationDate` timestamp NULL DEFAULT current_timestamp(),
|
||||||
|
`userName` varchar(30) NOT NULL,
|
||||||
`ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
`ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
|
||||||
|
`owner` tinyint(1) DEFAULT 1,
|
||||||
KEY `userFk` (`userFk`),
|
KEY `userFk` (`userFk`),
|
||||||
CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
);
|
);
|
|
@ -1,6 +1,14 @@
|
||||||
|
CREATE ROLE 'salix';
|
||||||
|
GRANT 'salix' TO 'root'@'%';
|
||||||
|
SET DEFAULT ROLE 'salix' FOR 'root'@'%';
|
||||||
|
|
||||||
CREATE SCHEMA IF NOT EXISTS `vn2008`;
|
CREATE SCHEMA IF NOT EXISTS `vn2008`;
|
||||||
CREATE SCHEMA IF NOT EXISTS `tmp`;
|
CREATE SCHEMA IF NOT EXISTS `tmp`;
|
||||||
|
|
||||||
|
CREATE ROLE 'salix';
|
||||||
|
GRANT 'salix' TO 'root'@'%';
|
||||||
|
SET DEFAULT ROLE 'salix' FOR 'root'@'%';
|
||||||
|
|
||||||
UPDATE `util`.`config`
|
UPDATE `util`.`config`
|
||||||
SET `environment`= 'development';
|
SET `environment`= 'development';
|
||||||
|
|
||||||
|
@ -362,7 +370,7 @@ INSERT INTO `vn`.`contactChannel`(`id`, `name`)
|
||||||
|
|
||||||
INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city`,`postcode`,`phone`,`mobile`,`isRelevant`,`email`,`iban`,`dueDay`,`accountingAccount`,`isEqualizated`,`provinceFk`,`hasToInvoice`,`credit`,`countryFk`,`isActive`,`gestdocFk`,`quality`,`payMethodFk`,`created`,`isToBeMailed`,`contactChannelFk`,`hasSepaVnl`,`hasCoreVnl`,`hasCoreVnh`,`riskCalculated`,`clientTypeFk`, `hasToInvoiceByAddress`,`isTaxDataChecked`,`isFreezed`,`creditInsurance`,`isCreatedAsServed`,`hasInvoiceSimplified`,`salesPersonFk`,`isVies`,`eypbc`, `businessTypeFk`,`typeFk`)
|
INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city`,`postcode`,`phone`,`mobile`,`isRelevant`,`email`,`iban`,`dueDay`,`accountingAccount`,`isEqualizated`,`provinceFk`,`hasToInvoice`,`credit`,`countryFk`,`isActive`,`gestdocFk`,`quality`,`payMethodFk`,`created`,`isToBeMailed`,`contactChannelFk`,`hasSepaVnl`,`hasCoreVnl`,`hasCoreVnh`,`riskCalculated`,`clientTypeFk`, `hasToInvoiceByAddress`,`isTaxDataChecked`,`isFreezed`,`creditInsurance`,`isCreatedAsServed`,`hasInvoiceSimplified`,`salesPersonFk`,`isVies`,`eypbc`, `businessTypeFk`,`typeFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1101, 'Bruce Wayne', '84612325V', 'BATMAN', 'Alfred', '1007 MOUNTAIN DRIVE, GOTHAM', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','loses'),
|
(1101, 'Bruce Wayne', '84612325V', 'BATMAN', 'Alfred', '1007 MOUNTAIN DRIVE, GOTHAM', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceWayne@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
||||||
(1102, 'Petter Parker', '87945234L', 'SPIDER MAN', 'Aunt May', '20 INGRAM STREET, QUEENS, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
(1102, 'Petter Parker', '87945234L', 'SPIDER MAN', 'Aunt May', '20 INGRAM STREET, QUEENS, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'PetterParker@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
||||||
(1103, 'Clark Kent', '06815934E', 'SUPER MAN', 'lois lane', '344 CLINTON STREET, APARTAMENT 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
(1103, 'Clark Kent', '06815934E', 'SUPER MAN', 'lois lane', '344 CLINTON STREET, APARTAMENT 3-D', 'Gotham', 46460, 1111111111, 222222222, 1, 'ClarkKent@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 0, 19, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
||||||
(1104, 'Tony Stark', '06089160W', 'IRON MAN', 'Pepper Potts', '10880 MALIBU POINT, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
(1104, 'Tony Stark', '06089160W', 'IRON MAN', 'Pepper Potts', '10880 MALIBU POINT, 90265', 'Gotham', 46460, 1111111111, 222222222, 1, 'TonyStark@mydomain.com', NULL, 0, 1234567890, 0, 2, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 18, 0, 1, 'florist','normal'),
|
||||||
|
@ -372,8 +380,8 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city
|
||||||
(1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 5, 1, 300, 13, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
(1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 5, 1, 300, 13, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist','normal'),
|
||||||
(1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist','normal'),
|
(1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist','normal'),
|
||||||
(1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist','normal'),
|
(1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist','normal'),
|
||||||
(1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','normal'),
|
(1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','loses'),
|
||||||
(1112, 'Trash', NULL, 'GARBAGE MAN', 'Unknown name', 'NEW YORK CITY, UNDERGROUND', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','normal');
|
(1112, 'Trash', NULL, 'GARBAGE MAN', 'Unknown name', 'NEW YORK CITY, UNDERGROUND', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','loses');
|
||||||
|
|
||||||
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
|
INSERT INTO `vn`.`client`(`id`, `name`, `fi`, `socialName`, `contact`, `street`, `city`, `postcode`, `isRelevant`, `email`, `iban`,`dueDay`,`accountingAccount`, `isEqualizated`, `provinceFk`, `hasToInvoice`, `credit`, `countryFk`, `isActive`, `gestdocFk`, `quality`, `payMethodFk`,`created`, `isTaxDataChecked`)
|
||||||
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1
|
SELECT id, name, CONCAT(RPAD(CONCAT(id,9),8,id),'A'), CONCAT(name, 'Social'), CONCAT(name, 'Contact'), CONCAT(name, 'Street'), 'GOTHAM', 46460, 1, CONCAT(name,'@mydomain.com'), NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1,NULL, 10, 5, util.VN_CURDATE(), 1
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
const NotFoundError = require('vn-loopback/util/not-found-error');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethod('test', {
|
Self.remoteMethod('test', {
|
||||||
|
@ -9,7 +10,8 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.test = async function() {
|
Self.test = async function() {
|
||||||
let connector = await Self.getSynchronizer();
|
const connector = await Self.getLinker();
|
||||||
|
if (!connector) throw new NotFoundError('Linker not configured');
|
||||||
await connector.test();
|
await connector.test();
|
||||||
};
|
};
|
||||||
};
|
};
|
|
@ -26,24 +26,46 @@ module.exports = Self => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.sync = async function(userName, password, force, options) {
|
Self.sync = async function(userName, password, force, options) {
|
||||||
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
let tx;
|
||||||
|
|
||||||
if (typeof options == 'object')
|
if (typeof options == 'object')
|
||||||
Object.assign(myOptions, options);
|
Object.assign(myOptions, options);
|
||||||
|
|
||||||
const models = Self.app.models;
|
if (!myOptions.transaction) {
|
||||||
const user = await models.VnUser.findOne({
|
tx = await Self.beginTransaction({});
|
||||||
fields: ['id', 'password'],
|
myOptions.transaction = tx;
|
||||||
where: {name: userName}
|
};
|
||||||
}, myOptions);
|
|
||||||
|
|
||||||
if (user && password && !await user.hasPassword(password))
|
try {
|
||||||
throw new ForbiddenError('Wrong password');
|
const user = await models.VnUser.findOne({
|
||||||
|
fields: ['id', 'password'],
|
||||||
|
where: {name: userName}
|
||||||
|
}, myOptions);
|
||||||
|
|
||||||
const isSync = !await models.UserSync.exists(userName, myOptions);
|
if (user && password && !await user.hasPassword(password))
|
||||||
|
throw new ForbiddenError('Wrong password');
|
||||||
|
|
||||||
if (!force && isSync && user) return;
|
const isSync = !await models.UserSync.exists(userName, myOptions);
|
||||||
await models.AccountConfig.syncUser(userName, password);
|
|
||||||
await models.UserSync.destroyById(userName, myOptions);
|
if (!force && isSync && user) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Self.rawSql(`
|
||||||
|
SELECT id
|
||||||
|
FROM account.user
|
||||||
|
WHERE id = ?
|
||||||
|
FOR UPDATE`, [user.id], myOptions);
|
||||||
|
|
||||||
|
await models.AccountConfig.syncUser(userName, password);
|
||||||
|
await models.UserSync.destroyById(userName, myOptions);
|
||||||
|
if (tx) await tx.commit();
|
||||||
|
} catch (err) {
|
||||||
|
if (tx) await tx.rollback();
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,14 +3,14 @@ const app = require('vn-loopback/server/server');
|
||||||
const UserError = require('vn-loopback/util/user-error');
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
module.exports = function(Self, options) {
|
module.exports = function(Self, options) {
|
||||||
require('../methods/account-synchronizer/test')(Self);
|
require('../methods/account-linker/test')(Self);
|
||||||
|
|
||||||
Self.once('attached', function() {
|
Self.once('attached', function() {
|
||||||
app.models.AccountConfig.addSynchronizer(Self);
|
app.models.AccountConfig.addLinker(Self);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mixin for user synchronizers.
|
* Mixin for account linkers.
|
||||||
*
|
*
|
||||||
* @property {Array<Model>} $
|
* @property {Array<Model>} $
|
||||||
* @property {Object} accountConfig
|
* @property {Object} accountConfig
|
||||||
|
@ -18,12 +18,12 @@ module.exports = function(Self, options) {
|
||||||
*/
|
*/
|
||||||
let Mixin = {
|
let Mixin = {
|
||||||
/**
|
/**
|
||||||
* Initalizes the synchronizer.
|
* Initalizes the linker.
|
||||||
*/
|
*/
|
||||||
async init() {},
|
async init() {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deinitalizes the synchronizer.
|
* Deinitalizes the linker.
|
||||||
*/
|
*/
|
||||||
async deinit() {},
|
async deinit() {},
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ module.exports = function(Self, options) {
|
||||||
async syncRoles() {},
|
async syncRoles() {},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests synchronizer configuration.
|
* Tests linker configuration.
|
||||||
*/
|
*/
|
||||||
async test() {
|
async test() {
|
||||||
try {
|
try {
|
|
@ -3,94 +3,85 @@ const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Object.assign(Self, {
|
Object.assign(Self, {
|
||||||
synchronizers: [],
|
linkers: [],
|
||||||
|
|
||||||
addSynchronizer(synchronizer) {
|
addLinker(linker) {
|
||||||
this.synchronizers.push(synchronizer);
|
this.linkers.push(linker);
|
||||||
},
|
},
|
||||||
|
|
||||||
async getInstance() {
|
async initEngine() {
|
||||||
let instance = await Self.findOne({
|
const accountConfig = await Self.findOne({
|
||||||
fields: ['homedir', 'shell', 'idBase']
|
fields: ['homedir', 'shell', 'idBase']
|
||||||
});
|
});
|
||||||
await instance.synchronizerInit();
|
const mailConfig = await models.MailConfig.findOne({
|
||||||
return instance;
|
fields: ['domain']
|
||||||
|
});
|
||||||
|
|
||||||
|
const linkers = [];
|
||||||
|
|
||||||
|
for (const Linker of Self.linkers) {
|
||||||
|
const linker = await Linker.getLinker();
|
||||||
|
if (!linker) continue;
|
||||||
|
Object.assign(linker, {accountConfig});
|
||||||
|
await linker.init();
|
||||||
|
linkers.push(linker);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(accountConfig, {
|
||||||
|
linkers,
|
||||||
|
domain: mailConfig.domain
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
accountConfig,
|
||||||
|
linkers
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async deinitEngine(engine) {
|
||||||
|
for (const linker of engine.linkers)
|
||||||
|
await linker.deinit();
|
||||||
|
},
|
||||||
|
|
||||||
|
async syncUser(userName, password) {
|
||||||
|
const engine = await Self.initEngine();
|
||||||
|
try {
|
||||||
|
await Self.syncUserBase(engine, userName, password, true);
|
||||||
|
} finally {
|
||||||
|
await Self.deinitEngine(engine);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async syncUsers() {
|
async syncUsers() {
|
||||||
let instance = await Self.getInstance();
|
const engine = await Self.initEngine();
|
||||||
|
|
||||||
|
let usersToSync = new Set();
|
||||||
|
for (const linker of engine.linkers)
|
||||||
|
await linker.getUsers(usersToSync);
|
||||||
|
|
||||||
let usersToSync = await instance.synchronizerGetUsers();
|
|
||||||
usersToSync = Array.from(usersToSync.values())
|
usersToSync = Array.from(usersToSync.values())
|
||||||
.sort((a, b) => a.localeCompare(b));
|
.sort((a, b) => a.localeCompare(b));
|
||||||
|
|
||||||
for (let userName of usersToSync) {
|
for (let userName of usersToSync) {
|
||||||
try {
|
try {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log(`Synchronizing user '${userName}'`);
|
console.log(`Synchronizing user '${userName}'`);
|
||||||
await instance.synchronizerSyncUser(userName);
|
|
||||||
|
await Self.syncUserBase(engine, userName);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.log(` -> User '${userName}' sinchronized`);
|
console.log(` -> User '${userName}' sinchronized`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.error(` -> User '${userName}' synchronization error:`, err.message);
|
console.error(` -> User '${userName}' synchronization error:`, err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await instance.synchronizerDeinit();
|
await Self.deinitEngine(engine);
|
||||||
await Self.syncRoles();
|
await Self.syncRoles();
|
||||||
},
|
},
|
||||||
|
|
||||||
async syncUser(userName, password) {
|
async syncUserBase(engine, userName, password, syncGroups) {
|
||||||
let instance = await Self.getInstance();
|
|
||||||
try {
|
|
||||||
await instance.synchronizerSyncUser(userName, password, true);
|
|
||||||
} finally {
|
|
||||||
await instance.synchronizerDeinit();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async syncRoles() {
|
|
||||||
let instance = await Self.getInstance();
|
|
||||||
try {
|
|
||||||
await instance.synchronizerSyncRoles();
|
|
||||||
} finally {
|
|
||||||
await instance.synchronizerDeinit();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async getSynchronizer() {
|
|
||||||
return await Self.findOne();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.assign(Self.prototype, {
|
|
||||||
async synchronizerInit() {
|
|
||||||
let mailConfig = await models.MailConfig.findOne({
|
|
||||||
fields: ['domain']
|
|
||||||
});
|
|
||||||
|
|
||||||
let synchronizers = [];
|
|
||||||
|
|
||||||
for (let Synchronizer of Self.synchronizers) {
|
|
||||||
let synchronizer = await Synchronizer.getSynchronizer();
|
|
||||||
if (!synchronizer) continue;
|
|
||||||
Object.assign(synchronizer, {
|
|
||||||
accountConfig: this
|
|
||||||
});
|
|
||||||
await synchronizer.init();
|
|
||||||
synchronizers.push(synchronizer);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(this, {
|
|
||||||
synchronizers,
|
|
||||||
domain: mailConfig.domain
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
async synchronizerDeinit() {
|
|
||||||
for (let synchronizer of this.synchronizers)
|
|
||||||
await synchronizer.deinit();
|
|
||||||
},
|
|
||||||
|
|
||||||
async synchronizerSyncUser(userName, password, syncGroups) {
|
|
||||||
if (!userName) return;
|
if (!userName) return;
|
||||||
userName = userName.toLowerCase();
|
userName = userName.toLowerCase();
|
||||||
|
|
||||||
|
@ -98,7 +89,7 @@ module.exports = Self => {
|
||||||
if (['administrator', 'root'].indexOf(userName) >= 0)
|
if (['administrator', 'root'].indexOf(userName) >= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let user = await models.VnUser.findOne({
|
const user = await models.VnUser.findOne({
|
||||||
where: {name: userName},
|
where: {name: userName},
|
||||||
fields: [
|
fields: [
|
||||||
'id',
|
'id',
|
||||||
|
@ -130,27 +121,28 @@ module.exports = Self => {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
let info = {
|
const info = {
|
||||||
user,
|
user,
|
||||||
hasAccount: false
|
hasAccount: false
|
||||||
};
|
};
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
let exists = await models.Account.exists(user.id);
|
const exists = await models.Account.exists(user.id);
|
||||||
|
const {accountConfig} = engine;
|
||||||
Object.assign(info, {
|
Object.assign(info, {
|
||||||
hasAccount: user.active && exists,
|
hasAccount: user.active && exists,
|
||||||
corporateMail: `${userName}@${this.domain}`,
|
corporateMail: `${userName}@${accountConfig.domain}`,
|
||||||
uidNumber: this.idBase + user.id
|
uidNumber: accountConfig.idBase + user.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let errs = [];
|
const errs = [];
|
||||||
|
|
||||||
for (let synchronizer of this.synchronizers) {
|
for (const linker of engine.linkers) {
|
||||||
try {
|
try {
|
||||||
await synchronizer.syncUser(userName, info, password);
|
await linker.syncUser(userName, info, password);
|
||||||
if (syncGroups)
|
if (syncGroups)
|
||||||
await synchronizer.syncUserGroups(userName, info);
|
await linker.syncUserGroups(userName, info);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errs.push(err);
|
errs.push(err);
|
||||||
}
|
}
|
||||||
|
@ -159,18 +151,16 @@ module.exports = Self => {
|
||||||
if (errs.length) throw errs[0];
|
if (errs.length) throw errs[0];
|
||||||
},
|
},
|
||||||
|
|
||||||
async synchronizerGetUsers() {
|
async syncRoles() {
|
||||||
let usersToSync = new Set();
|
const engine = await Self.initEngine();
|
||||||
|
try {
|
||||||
|
await Self.rawSql(`CALL account.role_sync`);
|
||||||
|
|
||||||
for (let synchronizer of this.synchronizers)
|
for (const linker of engine.linkers)
|
||||||
await synchronizer.getUsers(usersToSync);
|
await linker.syncRoles();
|
||||||
|
} finally {
|
||||||
return usersToSync;
|
await Self.deinitEngine(engine);
|
||||||
},
|
}
|
||||||
|
|
||||||
async synchronizerSyncRoles() {
|
|
||||||
for (let synchronizer of this.synchronizers)
|
|
||||||
await synchronizer.syncRoles();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@ const nthash = require('smbhash').nthash;
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
const shouldSync = process.env.NODE_ENV !== 'test';
|
const shouldSync = process.env.NODE_ENV !== 'test';
|
||||||
|
|
||||||
Self.getSynchronizer = async function() {
|
Self.getLinker = async function() {
|
||||||
return await Self.findOne({
|
return await Self.findOne({
|
||||||
fields: [
|
fields: [
|
||||||
'server',
|
'server',
|
||||||
|
@ -24,6 +24,7 @@ module.exports = Self => {
|
||||||
this.client = ldap.createClient({
|
this.client = ldap.createClient({
|
||||||
url: this.server
|
url: this.server
|
||||||
});
|
});
|
||||||
|
this.client.on('error', () => {});
|
||||||
await this.client.bind(this.rdn, this.password);
|
await this.client.bind(this.rdn, this.password);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mixins": {
|
"mixins": {
|
||||||
"AccountSynchronizer": {}
|
"AccountLinker": {}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.getSynchronizer = async function() {
|
Self.getLinker = async function() {
|
||||||
let NODE_ENV = process.env.NODE_ENV;
|
let NODE_ENV = process.env.NODE_ENV;
|
||||||
if (!NODE_ENV || NODE_ENV == 'development')
|
if (!NODE_ENV || NODE_ENV == 'development')
|
||||||
return null;
|
return null;
|
||||||
|
@ -45,6 +45,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUpdatable) {
|
if (!isUpdatable) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.warn(`RoleConfig.syncUser(): User '${userName}' cannot be updated, not managed by me`);
|
console.warn(`RoleConfig.syncUser(): User '${userName}' cannot be updated, not managed by me`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -82,6 +83,7 @@ module.exports = Self => {
|
||||||
[mysqlUser, this.userHost]);
|
[mysqlUser, this.userHost]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code == 'ER_REVOKE_GRANTS')
|
if (err.code == 'ER_REVOKE_GRANTS')
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.warn(`${err.code}: ${err.sqlMessage}: ${err.sql}`);
|
console.warn(`${err.code}: ${err.sqlMessage}: ${err.sql}`);
|
||||||
else
|
else
|
||||||
throw err;
|
throw err;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mixins": {
|
"mixins": {
|
||||||
"AccountSynchronizer": {}
|
"AccountLinker": {}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
|
|
@ -9,7 +9,7 @@ module.exports = Self => {
|
||||||
Self.observe(hook, async() => {
|
Self.observe(hook, async() => {
|
||||||
try {
|
try {
|
||||||
await Self.rawSql(`
|
await Self.rawSql(`
|
||||||
CREATE EVENT account.role_sync
|
CREATE DEFINER = CURRENT_ROLE EVENT account.role_sync
|
||||||
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 5 SECOND
|
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 5 SECOND
|
||||||
DO CALL role_sync;
|
DO CALL role_sync;
|
||||||
`);
|
`);
|
||||||
|
|
|
@ -13,7 +13,7 @@ const UserAccountControlFlags = {
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
const shouldSync = process.env.NODE_ENV !== 'test';
|
const shouldSync = process.env.NODE_ENV !== 'test';
|
||||||
|
|
||||||
Self.getSynchronizer = async function() {
|
Self.getLinker = async function() {
|
||||||
return await Self.findOne({
|
return await Self.findOne({
|
||||||
fields: [
|
fields: [
|
||||||
'host',
|
'host',
|
||||||
|
@ -39,6 +39,7 @@ module.exports = Self => {
|
||||||
url: `ldaps://${this.adController}:636`,
|
url: `ldaps://${this.adController}:636`,
|
||||||
tlsOptions: {rejectUnauthorized: this.verifyCert}
|
tlsOptions: {rejectUnauthorized: this.verifyCert}
|
||||||
});
|
});
|
||||||
|
adClient.on('error', () => {});
|
||||||
await adClient.bind(bindDn, this.adPassword);
|
await adClient.bind(bindDn, this.adPassword);
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
adClient,
|
adClient,
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mixins": {
|
"mixins": {
|
||||||
"AccountSynchronizer": {}
|
"AccountLinker": {}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
|
|
@ -25,7 +25,15 @@
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"ip": {
|
"ip": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"userName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"type": "boolean",
|
||||||
|
"required": true,
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relations": {
|
"relations": {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
const app = require('vn-loopback/server/server');
|
const app = require('vn-loopback/server/server');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.getSynchronizer = async function() {
|
Self.getLinker = async function() {
|
||||||
return await Self.findOne({fields: ['id']});
|
return await Self.findOne({fields: ['id']});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mixins": {
|
"mixins": {
|
||||||
"AccountSynchronizer": {}
|
"AccountLinker": {}
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
@ -16,4 +16,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,6 @@
|
||||||
Deactivate user
|
Deactivate user
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item
|
<vn-item
|
||||||
ng-if="$ctrl.user.active"
|
|
||||||
ng-click="syncUser.show()"
|
ng-click="syncUser.show()"
|
||||||
name="synchronizeUser"
|
name="synchronizeUser"
|
||||||
vn-acl="it"
|
vn-acl="it"
|
||||||
|
@ -166,7 +165,7 @@
|
||||||
vn-id="syncUser"
|
vn-id="syncUser"
|
||||||
on-accept="$ctrl.onSync()"
|
on-accept="$ctrl.onSync()"
|
||||||
on-close="$ctrl.onSyncClose()">
|
on-close="$ctrl.onSyncClose()">
|
||||||
<tpl-title ng-translate>
|
<tpl-title translate>
|
||||||
Do you want to synchronize user?
|
Do you want to synchronize user?
|
||||||
</tpl-title>
|
</tpl-title>
|
||||||
<tpl-body>
|
<tpl-body>
|
||||||
|
|
|
@ -112,7 +112,7 @@ module.exports = Self => {
|
||||||
{
|
{
|
||||||
relation: 'taxTypeSage',
|
relation: 'taxTypeSage',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ['vat']
|
fields: ['vat', 'rate']
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ describe('SalesMonitor salesFilter()', () => {
|
||||||
const result = await models.SalesMonitor.salesFilter(ctx, filter, options);
|
const result = await models.SalesMonitor.salesFilter(ctx, filter, options);
|
||||||
const firstRow = result[0];
|
const firstRow = result[0];
|
||||||
|
|
||||||
expect(result.length).toEqual(15);
|
expect(result.length).toEqual(12);
|
||||||
expect(firstRow.alertLevel).not.toEqual(0);
|
expect(firstRow.alertLevel).not.toEqual(0);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
|
|
|
@ -68,7 +68,7 @@ describe('ticket filter()', () => {
|
||||||
const filter = {};
|
const filter = {};
|
||||||
const result = await models.Ticket.filter(ctx, filter, options);
|
const result = await models.Ticket.filter(ctx, filter, options);
|
||||||
|
|
||||||
expect(result.length).toEqual(9);
|
expect(result.length).toEqual(6);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('ticket getSalesPersonMana()', () => {
|
||||||
|
|
||||||
const mana = await models.Ticket.getSalesPersonMana(1, options);
|
const mana = await models.Ticket.getSalesPersonMana(1, options);
|
||||||
|
|
||||||
expect(mana).toEqual(73);
|
expect(mana).toEqual(124);
|
||||||
|
|
||||||
await tx.rollback();
|
await tx.rollback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -202,9 +202,9 @@ export default class Controller extends Section {
|
||||||
if (!ticket.landed) {
|
if (!ticket.landed) {
|
||||||
const newLanded = await this.getLanded({
|
const newLanded = await this.getLanded({
|
||||||
shipped: this.$.model.userParams.dateToAdvance,
|
shipped: this.$.model.userParams.dateToAdvance,
|
||||||
addressFk: ticket.addressFk,
|
addressFk: ticket.futureAddressFk,
|
||||||
agencyModeFk: ticket.agencyModeFk,
|
agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk,
|
||||||
warehouseFk: ticket.warehouseFk
|
warehouseFk: ticket.futureWarehouseFk
|
||||||
});
|
});
|
||||||
if (!newLanded)
|
if (!newLanded)
|
||||||
throw new Error(this.$t(`No delivery zone available for this landing date`));
|
throw new Error(this.$t(`No delivery zone available for this landing date`));
|
||||||
|
@ -213,13 +213,13 @@ export default class Controller extends Section {
|
||||||
ticket.zoneFk = newLanded.zoneFk;
|
ticket.zoneFk = newLanded.zoneFk;
|
||||||
}
|
}
|
||||||
const params = {
|
const params = {
|
||||||
clientFk: ticket.clientFk,
|
clientFk: ticket.futureClientFk,
|
||||||
nickname: ticket.nickname,
|
nickname: ticket.nickname,
|
||||||
agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk,
|
agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk,
|
||||||
addressFk: ticket.addressFk,
|
addressFk: ticket.futureAddressFk,
|
||||||
zoneFk: ticket.zoneFk ?? ticket.futureZoneFk,
|
zoneFk: ticket.zoneFk ?? ticket.futureZoneFk,
|
||||||
warehouseFk: ticket.warehouseFk,
|
warehouseFk: ticket.futureWarehouseFk,
|
||||||
companyFk: ticket.companyFk,
|
companyFk: ticket.futureCompanyFk,
|
||||||
shipped: this.$.model.userParams.dateToAdvance,
|
shipped: this.$.model.userParams.dateToAdvance,
|
||||||
landed: ticket.landed,
|
landed: ticket.landed,
|
||||||
isDeleted: false,
|
isDeleted: false,
|
||||||
|
|
|
@ -70,17 +70,18 @@
|
||||||
fields="['started', 'ended']"
|
fields="['started', 'ended']"
|
||||||
ng-model="$ctrl.businessId"
|
ng-model="$ctrl.businessId"
|
||||||
search-function="{businessFk: $search}"
|
search-function="{businessFk: $search}"
|
||||||
|
show-field="businessFk"
|
||||||
value-field="businessFk"
|
value-field="businessFk"
|
||||||
order="businessFk DESC"
|
order="businessFk DESC"
|
||||||
limit="5">
|
limit="5">
|
||||||
<tpl-item>
|
<tpl-item>
|
||||||
<div>#{{businessFk}}</div>
|
<div>#{{businessFk}}</div>
|
||||||
<div class="text-caption text-secondary">
|
<div class="text-caption text-secondary">
|
||||||
{{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}}
|
{{started | date: 'dd/MM/yyyy'}} - {{ended ? (ended | date: 'dd/MM/yyyy') : 'Indef.'}}
|
||||||
</div>
|
</div>
|
||||||
</tpl-item>
|
</tpl-item>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</div>
|
</div>
|
||||||
<div name="absenceTypes" class="input vn-py-md" style="overflow: hidden;">
|
<div name="absenceTypes" class="input vn-py-md" style="overflow: hidden;">
|
||||||
<vn-chip ng-repeat="absenceType in absenceTypes" ng-class="::{'selectable': $ctrl.isSubordinate}" ng-click="$ctrl.pick(absenceType)">
|
<vn-chip ng-repeat="absenceType in absenceTypes" ng-class="::{'selectable': $ctrl.isSubordinate}" ng-click="$ctrl.pick(absenceType)">
|
||||||
<vn-avatar ng-style="{backgroundColor: absenceType.rgb}">
|
<vn-avatar ng-style="{backgroundColor: absenceType.rgb}">
|
||||||
|
@ -110,3 +111,4 @@
|
||||||
message="This item will be deleted"
|
message="This item will be deleted"
|
||||||
question="Are you sure you want to continue?">
|
question="Are you sure you want to continue?">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
||||||
|
|
|
@ -173,8 +173,6 @@ class Controller extends Section {
|
||||||
]}
|
]}
|
||||||
};
|
};
|
||||||
this.$.model.applyFilter(filter, params).then(() => {
|
this.$.model.applyFilter(filter, params).then(() => {
|
||||||
if (!this.card.hasWorkCenter) return;
|
|
||||||
|
|
||||||
this.getWorkedHours(this.started, this.ended);
|
this.getWorkedHours(this.started, this.ended);
|
||||||
this.getAbsences();
|
this.getAbsences();
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ module.exports = Self => {
|
||||||
async function doCalc(ctx) {
|
async function doCalc(ctx) {
|
||||||
try {
|
try {
|
||||||
await Self.rawSql(`
|
await Self.rawSql(`
|
||||||
CREATE EVENT zoneClosure_doRecalc
|
CREATE DEFINER = CURRENT_ROLE EVENT zoneClosure_doRecalc
|
||||||
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 15 SECOND
|
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 15 SECOND
|
||||||
DO CALL zoneClosure_recalc;
|
DO CALL zoneClosure_recalc;
|
||||||
`);
|
`);
|
||||||
|
|
Loading…
Reference in New Issue