Merge branch 'dev' into 4617-use_tmp_in_ticketToInvoice
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
4a7902ae4d
|
@ -8,13 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [2316.01] - 2023-05-04
|
||||
|
||||
### Added
|
||||
-
|
||||
- (Usuarios -> Histórico) Nueva sección
|
||||
- (Roles -> Histórico) Nueva sección
|
||||
|
||||
### Changed
|
||||
-
|
||||
- (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral
|
||||
|
||||
### Fixed
|
||||
-
|
||||
-
|
||||
|
||||
|
||||
## [2314.01] - 2023-04-20
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ pipeline {
|
|||
}}}
|
||||
environment {
|
||||
NODE_ENV = ""
|
||||
TZ = 'Europe/Madrid'
|
||||
}
|
||||
parallel {
|
||||
stage('Frontend') {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
name: account
|
||||
columns:
|
||||
id: id
|
||||
name: name
|
||||
roleFk: role
|
||||
nickname: nickname
|
||||
lang: lang
|
||||
password: password
|
||||
bcryptPassword: bcrypt password
|
||||
active: active
|
||||
email: email
|
||||
emailVerified: email verified
|
||||
created: created
|
||||
updated: updated
|
||||
image: image
|
||||
hasGrant: has grant
|
||||
userFk: user
|
|
@ -0,0 +1,17 @@
|
|||
name: cuenta
|
||||
columns:
|
||||
id: id
|
||||
name: nombre
|
||||
roleFk: rol
|
||||
nickname: apodo
|
||||
lang: idioma
|
||||
password: contraseña
|
||||
bcryptPassword: contraseña bcrypt
|
||||
active: activo
|
||||
email: email
|
||||
emailVerified: email verificado
|
||||
created: creado
|
||||
updated: actualizado
|
||||
image: imagen
|
||||
hasGrant: tiene permiso
|
||||
userFk: usuario
|
|
@ -1,9 +1,9 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('campaign latest()', () => {
|
||||
it('should return the campaigns from the last year', async() => {
|
||||
const now = Date.vnNew();
|
||||
const result = await app.models.Campaign.latest();
|
||||
const result = await models.Campaign.latest();
|
||||
const randomIndex = Math.floor(Math.random() * result.length);
|
||||
const campaignDated = result[randomIndex].dated;
|
||||
|
||||
|
@ -14,7 +14,7 @@ describe('campaign latest()', () => {
|
|||
it('should return the campaigns from the current year', async() => {
|
||||
const now = Date.vnNew();
|
||||
const currentYear = now.getFullYear();
|
||||
const result = await app.models.Campaign.latest({
|
||||
const result = await models.Campaign.latest({
|
||||
where: {dated: {like: `%${currentYear}%`}}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('campaign upcoming()', () => {
|
||||
it('should return the upcoming campaign but from the last year', async() => {
|
||||
const response = await app.models.Campaign.upcoming();
|
||||
const response = await models.Campaign.upcoming();
|
||||
const campaignDated = response.dated;
|
||||
const now = Date.vnNew();
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ module.exports = Self => {
|
|||
Self.send = async(ctx, to, message) => {
|
||||
const models = Self.app.models;
|
||||
const accessToken = ctx.req.accessToken;
|
||||
const sender = await models.Account.findById(accessToken.userId);
|
||||
const sender = await models.VnUser.findById(accessToken.userId);
|
||||
const recipient = to.replace('@', '');
|
||||
|
||||
if (sender.name != recipient) {
|
||||
|
|
|
@ -29,8 +29,8 @@ module.exports = Self => {
|
|||
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const sender = await models.Account.findById(userId, {fields: ['id']});
|
||||
const recipient = await models.Account.findById(recipientId, null);
|
||||
const sender = await models.VnUser.findById(userId, {fields: ['id']});
|
||||
const recipient = await models.VnUser.findById(recipientId, null);
|
||||
|
||||
// Prevent sending messages to yourself
|
||||
if (recipientId == userId) return false;
|
||||
|
|
|
@ -58,7 +58,7 @@ module.exports = Self => {
|
|||
const models = Self.app.models;
|
||||
|
||||
const recipientName = chat.recipient.slice(1);
|
||||
const recipient = await models.Account.findOne({
|
||||
const recipient = await models.VnUser.findOne({
|
||||
where: {
|
||||
name: recipientName
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ module.exports = Self => {
|
|||
}
|
||||
|
||||
const models = Self.app.models;
|
||||
const sender = await models.Account.findById(senderFk);
|
||||
const sender = await models.VnUser.findById(senderFk);
|
||||
|
||||
const login = await Self.getServiceAuth();
|
||||
const avatar = `${login.host}/avatar/${sender.name}`;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('Chat notifyIssue()', () => {
|
||||
const ctx = {req: {accessToken: {userId: 1}}};
|
||||
ctx.req.__ = value => {
|
||||
return value;
|
||||
};
|
||||
const chatModel = app.models.Chat;
|
||||
const osTicketModel = app.models.OsTicket;
|
||||
const chatModel = models.Chat;
|
||||
const osTicketModel = models.OsTicket;
|
||||
const departmentId = 31;
|
||||
|
||||
it(`should not call to the send() method and neither return a response`, async() => {
|
||||
|
@ -29,7 +29,7 @@ describe('Chat notifyIssue()', () => {
|
|||
// eslint-disable-next-line max-len
|
||||
const expectedMessage = `@all ➔ There's a new urgent ticket:\r\n[ID: 00001 - Issue title @batman](https://cau.verdnatura.es/scp/tickets.php?id=1)`;
|
||||
|
||||
const department = await app.models.Department.findById(departmentId);
|
||||
const department = await models.Department.findById(departmentId);
|
||||
let orgChatName = department.chatName;
|
||||
await department.updateAttribute('chatName', 'IT');
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('Chat send()', () => {
|
||||
it('should return true as response', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 1}}};
|
||||
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
let response = await models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
|
||||
expect(response).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return false as response', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 18}}};
|
||||
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
let response = await models.Chat.send(ctx, '@salesPerson', 'I changed something');
|
||||
|
||||
expect(response).toEqual(false);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('getSectors()', () => {
|
||||
it('return list of sectors', async() => {
|
||||
let response = await app.models.Collection.getSectors();
|
||||
let response = await models.Collection.getSectors();
|
||||
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
expect(response[0].id).toEqual(1);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('newCollection()', () => {
|
||||
it('should return a new collection', async() => {
|
||||
pending('#3400 analizar que hacer con rutas de back collection');
|
||||
let ctx = {req: {accessToken: {userId: 1106}}};
|
||||
let response = await app.models.Collection.newCollection(ctx, 1, 1, 1);
|
||||
let response = await models.Collection.newCollection(ctx, 1, 1, 1);
|
||||
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
expect(response[0].ticketFk).toEqual(2);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('dms downloadFile()', () => {
|
||||
let dmsId = 1;
|
||||
|
@ -6,7 +6,7 @@ describe('dms downloadFile()', () => {
|
|||
it('should return a response for an employee with text content-type', async() => {
|
||||
let workerId = 1107;
|
||||
let ctx = {req: {accessToken: {userId: workerId}}};
|
||||
const result = await app.models.Dms.downloadFile(ctx, dmsId);
|
||||
const result = await models.Dms.downloadFile(ctx, dmsId);
|
||||
|
||||
expect(result[1]).toEqual('text/plain');
|
||||
});
|
||||
|
@ -16,7 +16,7 @@ describe('dms downloadFile()', () => {
|
|||
let ctx = {req: {accessToken: {userId: clientId}}};
|
||||
|
||||
let error;
|
||||
await app.models.Dms.downloadFile(ctx, dmsId).catch(e => {
|
||||
await models.Dms.downloadFile(ctx, dmsId).catch(e => {
|
||||
error = e;
|
||||
}).finally(() => {
|
||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('dms removeFile()', () => {
|
||||
let dmsId = 1;
|
||||
|
@ -8,7 +8,7 @@ describe('dms removeFile()', () => {
|
|||
let ctx = {req: {accessToken: {userId: clientId}}};
|
||||
|
||||
let error;
|
||||
await app.models.Dms.removeFile(ctx, dmsId).catch(e => {
|
||||
await models.Dms.removeFile(ctx, dmsId).catch(e => {
|
||||
error = e;
|
||||
}).finally(() => {
|
||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('dms updateFile()', () => {
|
||||
it(`should return an error for a user without enough privileges`, async() => {
|
||||
|
@ -11,7 +11,7 @@ describe('dms updateFile()', () => {
|
|||
let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: dmsTypeId}};
|
||||
|
||||
let error;
|
||||
await app.models.Dms.updateFile(ctx, dmsId, warehouseId, companyId, dmsTypeId).catch(e => {
|
||||
await models.Dms.updateFile(ctx, dmsId, warehouseId, companyId, dmsTypeId).catch(e => {
|
||||
error = e;
|
||||
}).finally(() => {
|
||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('dms uploadFile()', () => {
|
||||
it(`should return an error for a user without enough privileges`, async() => {
|
||||
|
@ -7,7 +7,7 @@ describe('dms uploadFile()', () => {
|
|||
let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: ticketDmsTypeId}};
|
||||
|
||||
let error;
|
||||
await app.models.Dms.uploadFile(ctx).catch(e => {
|
||||
await models.Dms.uploadFile(ctx).catch(e => {
|
||||
error = e;
|
||||
}).finally(() => {
|
||||
expect(error.message).toEqual(`You don't have enough privileges`);
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('scrub', {
|
||||
description: 'Deletes images without database reference',
|
||||
accessType: 'WRITE',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'collection',
|
||||
type: 'string',
|
||||
description: 'The collection name',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'remove',
|
||||
type: 'boolean',
|
||||
description: 'Delete instead of move images to trash'
|
||||
}, {
|
||||
arg: 'limit',
|
||||
type: 'integer',
|
||||
description: 'Maximum number of images to clean'
|
||||
}, {
|
||||
arg: 'dryRun',
|
||||
type: 'boolean',
|
||||
description: 'Simulate actions'
|
||||
}, {
|
||||
arg: 'skipLock',
|
||||
type: 'boolean',
|
||||
description: 'Wether to skip exclusive lock'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'integer',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/scrub`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.scrub = async function(collection, remove, limit, dryRun, skipLock) {
|
||||
const $ = Self.app.models;
|
||||
|
||||
const env = process.env.NODE_ENV;
|
||||
dryRun = dryRun || (env && env !== 'production');
|
||||
|
||||
const instance = await $.ImageCollection.findOne({
|
||||
fields: ['id'],
|
||||
where: {name: collection}
|
||||
});
|
||||
if (!instance)
|
||||
throw new UserError('Collection does not exist');
|
||||
|
||||
const container = await $.ImageContainer.container(collection);
|
||||
const rootPath = container.client.root;
|
||||
|
||||
let tx;
|
||||
let opts;
|
||||
const lockName = 'salix.Image.scrub';
|
||||
|
||||
if (!skipLock) {
|
||||
tx = await Self.beginTransaction({timeout: null});
|
||||
opts = {transaction: tx};
|
||||
|
||||
const [row] = await Self.rawSql(
|
||||
`SELECT GET_LOCK(?, 10) hasLock`, [lockName], opts);
|
||||
if (!row.hasLock)
|
||||
throw new UserError('Cannot obtain exclusive lock');
|
||||
}
|
||||
|
||||
try {
|
||||
const now = Date.vnNew().toJSON();
|
||||
const scrubDir = path.join(rootPath, '.scrub', now);
|
||||
|
||||
const collectionDir = path.join(rootPath, collection);
|
||||
const sizes = await fs.readdir(collectionDir);
|
||||
let cleanCount = 0;
|
||||
|
||||
mainLoop: for (const size of sizes) {
|
||||
const sizeDir = path.join(collectionDir, size);
|
||||
const scrubSizeDir = path.join(scrubDir, collection, size);
|
||||
const images = await fs.readdir(sizeDir);
|
||||
for (const image of images) {
|
||||
const imageName = path.parse(image).name;
|
||||
const count = await Self.count({
|
||||
collectionFk: collection,
|
||||
name: imageName
|
||||
}, opts);
|
||||
const exists = count > 0;
|
||||
let scrubDirCreated = false;
|
||||
if (!exists) {
|
||||
const srcFile = path.join(sizeDir, image);
|
||||
if (remove !== true) {
|
||||
if (!scrubDirCreated) {
|
||||
if (!dryRun)
|
||||
await fs.mkdir(scrubSizeDir, {recursive: true});
|
||||
scrubDirCreated = true;
|
||||
}
|
||||
const dstFile = path.join(scrubSizeDir, image);
|
||||
if (!dryRun) await fs.rename(srcFile, dstFile);
|
||||
} else {
|
||||
try {
|
||||
if (!dryRun) await fs.unlink(srcFile);
|
||||
} catch (err) {
|
||||
console.error(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
cleanCount++;
|
||||
if (limit && cleanCount == limit)
|
||||
break mainLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cleanCount;
|
||||
} finally {
|
||||
if (!skipLock) {
|
||||
try {
|
||||
await Self.rawSql(`DO RELEASE_LOCK(?)`, [lockName], opts);
|
||||
await tx.rollback();
|
||||
} catch (err) {
|
||||
console.error(err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('image download()', () => {
|
||||
const collection = 'user';
|
||||
|
@ -8,7 +8,7 @@ describe('image download()', () => {
|
|||
|
||||
it('should return the image content-type of the user', async() => {
|
||||
const userId = 9;
|
||||
const image = await app.models.Image.download(ctx, collection, size, userId);
|
||||
const image = await models.Image.download(ctx, collection, size, userId);
|
||||
const contentType = image[1];
|
||||
|
||||
expect(contentType).toEqual('image/png');
|
||||
|
@ -16,7 +16,7 @@ describe('image download()', () => {
|
|||
|
||||
it(`should return false if the user doesn't have image`, async() => {
|
||||
const userId = 1110;
|
||||
const image = await app.models.Image.download(ctx, collection, size, userId);
|
||||
const image = await models.Image.download(ctx, collection, size, userId);
|
||||
|
||||
expect(image).toBeFalse();
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('image upload()', () => {
|
||||
describe('as buyer', () => {
|
||||
|
@ -16,7 +16,7 @@ describe('image upload()', () => {
|
|||
|
||||
let error;
|
||||
try {
|
||||
await app.models.Image.upload(ctx);
|
||||
await models.Image.upload(ctx);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ describe('image upload()', () => {
|
|||
});
|
||||
|
||||
it('should call to the TempContainer upload method for the collection "catalog"', async() => {
|
||||
const containerModel = app.models.TempContainer;
|
||||
const containerModel = models.TempContainer;
|
||||
spyOn(containerModel, 'upload');
|
||||
|
||||
const ctx = {req: {accessToken: {userId: buyerId}},
|
||||
|
@ -36,7 +36,7 @@ describe('image upload()', () => {
|
|||
};
|
||||
|
||||
try {
|
||||
await app.models.Image.upload(ctx);
|
||||
await models.Image.upload(ctx);
|
||||
} catch (err) { }
|
||||
|
||||
expect(containerModel.upload).toHaveBeenCalled();
|
||||
|
@ -49,7 +49,7 @@ describe('image upload()', () => {
|
|||
const itemId = 4;
|
||||
|
||||
it('should be able to call to the TempContainer upload method for the collection "user"', async() => {
|
||||
const containerModel = app.models.TempContainer;
|
||||
const containerModel = models.TempContainer;
|
||||
spyOn(containerModel, 'upload');
|
||||
|
||||
const ctx = {req: {accessToken: {userId: marketingId}},
|
||||
|
@ -60,14 +60,14 @@ describe('image upload()', () => {
|
|||
};
|
||||
|
||||
try {
|
||||
await app.models.Image.upload(ctx);
|
||||
await models.Image.upload(ctx);
|
||||
} catch (err) { }
|
||||
|
||||
expect(containerModel.upload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to call to the TempContainer upload method for the collection "catalog"', async() => {
|
||||
const containerModel = app.models.TempContainer;
|
||||
const containerModel = models.TempContainer;
|
||||
spyOn(containerModel, 'upload');
|
||||
|
||||
const ctx = {req: {accessToken: {userId: marketingId}},
|
||||
|
@ -78,7 +78,7 @@ describe('image upload()', () => {
|
|||
};
|
||||
|
||||
try {
|
||||
await app.models.Image.upload(ctx);
|
||||
await models.Image.upload(ctx);
|
||||
} catch (err) { }
|
||||
|
||||
expect(containerModel.upload).toHaveBeenCalled();
|
||||
|
@ -91,7 +91,7 @@ describe('image upload()', () => {
|
|||
const itemId = 4;
|
||||
|
||||
it('should upload a file for the collection "user" and call to the TempContainer upload method', async() => {
|
||||
const containerModel = app.models.TempContainer;
|
||||
const containerModel = models.TempContainer;
|
||||
spyOn(containerModel, 'upload');
|
||||
|
||||
const ctx = {req: {accessToken: {userId: hhrrId}},
|
||||
|
@ -102,7 +102,7 @@ describe('image upload()', () => {
|
|||
};
|
||||
|
||||
try {
|
||||
await app.models.Image.upload(ctx);
|
||||
await models.Image.upload(ctx);
|
||||
} catch (err) { }
|
||||
|
||||
expect(containerModel.upload).toHaveBeenCalled();
|
||||
|
@ -118,7 +118,7 @@ describe('image upload()', () => {
|
|||
|
||||
let error;
|
||||
try {
|
||||
await app.models.Image.upload(ctx);
|
||||
await models.Image.upload(ctx);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
|
|
@ -12,13 +12,13 @@ module.exports = Self => {
|
|||
type: 'Number',
|
||||
description: 'The entity id',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
}, {
|
||||
arg: 'collection',
|
||||
type: 'string',
|
||||
description: 'The collection name',
|
||||
required: true
|
||||
}],
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('getStarredModules()', () => {
|
||||
|
@ -19,13 +19,13 @@ describe('getStarredModules()', () => {
|
|||
});
|
||||
|
||||
it(`should return the starred modules for a given user`, async() => {
|
||||
const newStarred = await app.models.StarredModule.create({workerFk: 9, moduleFk: 'customer', position: 1});
|
||||
const starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
||||
const newStarred = await models.StarredModule.create({workerFk: 9, moduleFk: 'customer', position: 1});
|
||||
const starredModules = await models.StarredModule.getStarredModules(ctx);
|
||||
|
||||
expect(starredModules.length).toEqual(1);
|
||||
expect(starredModules[0].moduleFk).toEqual('customer');
|
||||
|
||||
// restores
|
||||
await app.models.StarredModule.destroyById(newStarred.id);
|
||||
await models.StarredModule.destroyById(newStarred.id);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('setPosition()', () => {
|
||||
|
@ -21,7 +21,7 @@ describe('setPosition()', () => {
|
|||
});
|
||||
|
||||
it('should increase the orders module position by replacing it with clients and vice versa', async() => {
|
||||
const tx = await app.models.StarredModule.beginTransaction({});
|
||||
const tx = await models.StarredModule.beginTransaction({});
|
||||
|
||||
const filter = {
|
||||
where: {
|
||||
|
@ -32,24 +32,24 @@ describe('setPosition()', () => {
|
|||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
|
||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||
let orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||
let clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(orders.position).toEqual(1);
|
||||
expect(clients.position).toEqual(2);
|
||||
|
||||
await app.models.StarredModule.setPosition(ctx, 'customer', 'left', options);
|
||||
await models.StarredModule.setPosition(ctx, 'customer', 'left', options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
clients = await app.models.StarredModule.findOne(filter, options);
|
||||
clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'order';
|
||||
orders = await app.models.StarredModule.findOne(filter, options);
|
||||
orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(clients.position).toEqual(1);
|
||||
expect(orders.position).toEqual(2);
|
||||
|
@ -62,7 +62,7 @@ describe('setPosition()', () => {
|
|||
});
|
||||
|
||||
it('should decrease the orders module position by replacing it with clients and vice versa', async() => {
|
||||
const tx = await app.models.StarredModule.beginTransaction({});
|
||||
const tx = await models.StarredModule.beginTransaction({});
|
||||
|
||||
const filter = {
|
||||
where: {
|
||||
|
@ -73,24 +73,24 @@ describe('setPosition()', () => {
|
|||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
|
||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||
let orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||
let clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(orders.position).toEqual(1);
|
||||
expect(clients.position).toEqual(2);
|
||||
|
||||
await app.models.StarredModule.setPosition(ctx, 'order', 'right', options);
|
||||
await models.StarredModule.setPosition(ctx, 'order', 'right', options);
|
||||
|
||||
filter.where.moduleFk = 'order';
|
||||
orders = await app.models.StarredModule.findOne(filter, options);
|
||||
orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
clients = await app.models.StarredModule.findOne(filter, options);
|
||||
clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(orders.position).toEqual(2);
|
||||
expect(clients.position).toEqual(1);
|
||||
|
@ -103,7 +103,7 @@ describe('setPosition()', () => {
|
|||
});
|
||||
|
||||
it('should switch two modules after adding and deleting several modules', async() => {
|
||||
const tx = await app.models.StarredModule.beginTransaction({});
|
||||
const tx = await models.StarredModule.beginTransaction({});
|
||||
|
||||
const filter = {
|
||||
where: {
|
||||
|
@ -115,29 +115,29 @@ describe('setPosition()', () => {
|
|||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'item', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'zone', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'item', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'zone', options);
|
||||
|
||||
const items = await app.models.StarredModule.findOne(filter, options);
|
||||
const items = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'claim';
|
||||
const claims = await app.models.StarredModule.findOne(filter, options);
|
||||
const claims = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||
let clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'order';
|
||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||
let orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'zone';
|
||||
const zones = await app.models.StarredModule.findOne(filter, options);
|
||||
const zones = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(items.position).toEqual(1);
|
||||
expect(claims.position).toEqual(2);
|
||||
|
@ -145,13 +145,13 @@ describe('setPosition()', () => {
|
|||
expect(orders.position).toEqual(4);
|
||||
expect(zones.position).toEqual(5);
|
||||
|
||||
await app.models.StarredModule.setPosition(ctx, 'customer', 'right', options);
|
||||
await models.StarredModule.setPosition(ctx, 'customer', 'right', options);
|
||||
|
||||
filter.where.moduleFk = 'order';
|
||||
orders = await app.models.StarredModule.findOne(filter, options);
|
||||
orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
clients = await app.models.StarredModule.findOne(filter, options);
|
||||
clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(orders.position).toEqual(3);
|
||||
expect(clients.position).toEqual(4);
|
||||
|
@ -164,7 +164,7 @@ describe('setPosition()', () => {
|
|||
});
|
||||
|
||||
it('should switch two modules after adding and deleting a module between them', async() => {
|
||||
const tx = await app.models.StarredModule.beginTransaction({});
|
||||
const tx = await models.StarredModule.beginTransaction({});
|
||||
|
||||
const filter = {
|
||||
where: {
|
||||
|
@ -176,25 +176,25 @@ describe('setPosition()', () => {
|
|||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'item', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'zone', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'item', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'order', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'zone', options);
|
||||
|
||||
const items = await app.models.StarredModule.findOne(filter, options);
|
||||
const items = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
let clients = await app.models.StarredModule.findOne(filter, options);
|
||||
let clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'claim';
|
||||
const claims = await app.models.StarredModule.findOne(filter, options);
|
||||
const claims = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'order';
|
||||
let orders = await app.models.StarredModule.findOne(filter, options);
|
||||
let orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'zone';
|
||||
const zones = await app.models.StarredModule.findOne(filter, options);
|
||||
const zones = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(items.position).toEqual(1);
|
||||
expect(clients.position).toEqual(2);
|
||||
|
@ -202,14 +202,14 @@ describe('setPosition()', () => {
|
|||
expect(orders.position).toEqual(4);
|
||||
expect(zones.position).toEqual(5);
|
||||
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||
await app.models.StarredModule.setPosition(ctx, 'customer', 'right', options);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'claim', options);
|
||||
await models.StarredModule.setPosition(ctx, 'customer', 'right', options);
|
||||
|
||||
filter.where.moduleFk = 'customer';
|
||||
clients = await app.models.StarredModule.findOne(filter, options);
|
||||
clients = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
filter.where.moduleFk = 'order';
|
||||
orders = await app.models.StarredModule.findOne(filter, options);
|
||||
orders = await models.StarredModule.findOne(filter, options);
|
||||
|
||||
expect(orders.position).toEqual(2);
|
||||
expect(clients.position).toEqual(4);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('toggleStarredModule()', () => {
|
||||
|
@ -21,16 +21,16 @@ describe('toggleStarredModule()', () => {
|
|||
});
|
||||
|
||||
it('should create a new starred module and then remove it by calling the method again with same args', async() => {
|
||||
const starredModule = await app.models.StarredModule.toggleStarredModule(ctx, 'order');
|
||||
let starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
||||
const starredModule = await models.StarredModule.toggleStarredModule(ctx, 'order');
|
||||
let starredModules = await models.StarredModule.getStarredModules(ctx);
|
||||
|
||||
expect(starredModules.length).toEqual(1);
|
||||
expect(starredModule.moduleFk).toEqual('order');
|
||||
expect(starredModule.workerFk).toEqual(activeCtx.accessToken.userId);
|
||||
expect(starredModule.position).toEqual(starredModules.length);
|
||||
|
||||
await app.models.StarredModule.toggleStarredModule(ctx, 'order');
|
||||
starredModules = await app.models.StarredModule.getStarredModules(ctx);
|
||||
await models.StarredModule.toggleStarredModule(ctx, 'order');
|
||||
starredModules = await models.StarredModule.getStarredModules(ctx);
|
||||
|
||||
expect(starredModules.length).toEqual(0);
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ module.exports = Self => {
|
|||
let userId = ctx.req.accessToken.userId;
|
||||
let models = Self.app.models;
|
||||
|
||||
let user = await models.Account.findById(userId, {
|
||||
let user = await Self.findById(userId, {
|
||||
fields: ['id', 'name', 'nickname', 'email', 'lang'],
|
||||
include: {
|
||||
relation: 'userConfig',
|
|
@ -1,9 +1,14 @@
|
|||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('privileges', {
|
||||
Self.remoteMethod('privileges', {
|
||||
description: 'Change role and hasGrant if user has privileges',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
http: {source: 'context'}
|
||||
},
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
|
@ -39,9 +44,9 @@ module.exports = Self => {
|
|||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
const user = await models.Account.findById(userId, {fields: ['hasGrant']}, myOptions);
|
||||
const user = await Self.findById(userId, {fields: ['hasGrant']}, myOptions);
|
||||
|
||||
const userToUpdate = await models.Account.findById(id, {
|
||||
const userToUpdate = await Self.findById(id, {
|
||||
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password'],
|
||||
include: {
|
||||
relation: 'role',
|
||||
|
@ -54,7 +59,7 @@ module.exports = Self => {
|
|||
if (!user.hasGrant)
|
||||
throw new UserError(`You don't have grant privilege`);
|
||||
|
||||
const hasRoleFromUser = await models.Account.hasRole(userId, userToUpdate.role().name, myOptions);
|
||||
const hasRoleFromUser = await Self.hasRole(userId, userToUpdate.role().name, myOptions);
|
||||
|
||||
if (!hasRoleFromUser)
|
||||
throw new UserError(`You don't own the role and you can't assign it to another user`);
|
||||
|
@ -64,7 +69,7 @@ module.exports = Self => {
|
|||
|
||||
if (roleFk) {
|
||||
const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions);
|
||||
const hasRole = await models.Account.hasRole(userId, role.name, myOptions);
|
||||
const hasRole = await Self.hasRole(userId, role.name, myOptions);
|
||||
|
||||
if (!hasRole)
|
||||
throw new UserError(`You don't own the role and you can't assign it to another user`);
|
||||
|
@ -73,6 +78,6 @@ module.exports = Self => {
|
|||
}
|
||||
|
||||
await userToUpdate.save(userToUpdate);
|
||||
await models.UserAccount.sync(userToUpdate.name);
|
||||
await models.Account.sync(userToUpdate.name);
|
||||
};
|
||||
};
|
|
@ -20,7 +20,7 @@ module.exports = Self => {
|
|||
|
||||
const usesEmail = user.indexOf('@') !== -1;
|
||||
if (!usesEmail) {
|
||||
const account = await models.Account.findOne({
|
||||
const account = await models.VnUser.findOne({
|
||||
fields: ['email'],
|
||||
where: {name: user}
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ module.exports = Self => {
|
|||
}
|
||||
|
||||
try {
|
||||
await models.user.resetPassword({email: user, emailTemplate: 'recover-password'});
|
||||
await Self.resetPassword({email: user, emailTemplate: 'recover-password'});
|
||||
} catch (err) {
|
||||
if (err.code === 'EMAIL_NOT_FOUND')
|
||||
return;
|
|
@ -1,14 +1,14 @@
|
|||
const md5 = require('md5');
|
||||
const UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('login', {
|
||||
Self.remoteMethod('signIn', {
|
||||
description: 'Login a user with username/email and password',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'user',
|
||||
type: 'String',
|
||||
description: 'The user name or email',
|
||||
http: {source: 'form'},
|
||||
required: true
|
||||
}, {
|
||||
arg: 'password',
|
||||
|
@ -21,20 +21,20 @@ module.exports = Self => {
|
|||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/login`,
|
||||
path: `/signIn`,
|
||||
verb: 'POST'
|
||||
}
|
||||
});
|
||||
|
||||
Self.login = async function(user, password) {
|
||||
let $ = Self.app.models;
|
||||
Self.signIn = async function(user, password) {
|
||||
let models = Self.app.models;
|
||||
let token;
|
||||
let usesEmail = user.indexOf('@') !== -1;
|
||||
|
||||
let userInfo = usesEmail
|
||||
? {email: user}
|
||||
: {username: user};
|
||||
let instance = await $.User.findOne({
|
||||
let instance = await Self.findOne({
|
||||
fields: ['username', 'password'],
|
||||
where: userInfo
|
||||
});
|
||||
|
@ -42,29 +42,27 @@ module.exports = Self => {
|
|||
let where = usesEmail
|
||||
? {email: user}
|
||||
: {name: user};
|
||||
let account = await Self.findOne({
|
||||
fields: ['active', 'password'],
|
||||
let vnUser = await Self.findOne({
|
||||
fields: ['active'],
|
||||
where
|
||||
});
|
||||
|
||||
let validCredentials = instance && (
|
||||
await instance.hasPassword(password) ||
|
||||
account.password == md5(password || '')
|
||||
);
|
||||
let validCredentials = instance
|
||||
&& await instance.hasPassword(password);
|
||||
|
||||
if (validCredentials) {
|
||||
if (!account.active)
|
||||
if (!vnUser.active)
|
||||
throw new UserError('User disabled');
|
||||
|
||||
try {
|
||||
await $.UserAccount.sync(instance.username, password);
|
||||
await models.Account.sync(instance.username, password);
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
|
||||
let loginInfo = Object.assign({password}, userInfo);
|
||||
token = await $.User.login(loginInfo, 'user');
|
||||
token = await Self.login(loginInfo, 'user');
|
||||
return {token: token.id};
|
||||
};
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('account privileges()', () => {
|
||||
describe('VnUser privileges()', () => {
|
||||
const employeeId = 1;
|
||||
const developerId = 9;
|
||||
const sysadminId = 66;
|
||||
|
@ -10,13 +10,13 @@ describe('account privileges()', () => {
|
|||
|
||||
it('should throw an error when user not has privileges', async() => {
|
||||
const ctx = {req: {accessToken: {userId: developerId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
const tx = await models.VnUser.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await models.Account.privileges(ctx, employeeId, null, true, options);
|
||||
await models.VnUser.privileges(ctx, employeeId, null, true, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
@ -29,13 +29,13 @@ describe('account privileges()', () => {
|
|||
|
||||
it('should throw an error when user has privileges but not has the role', async() => {
|
||||
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
const tx = await models.VnUser.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await models.Account.privileges(ctx, employeeId, rootId, null, options);
|
||||
await models.VnUser.privileges(ctx, employeeId, rootId, null, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
@ -48,13 +48,13 @@ describe('account privileges()', () => {
|
|||
|
||||
it('should throw an error when user has privileges but not has the role from user', async() => {
|
||||
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
const tx = await models.VnUser.beginTransaction({});
|
||||
|
||||
let error;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
await models.Account.privileges(ctx, itBossId, developerId, null, options);
|
||||
await models.VnUser.privileges(ctx, itBossId, developerId, null, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
@ -67,7 +67,7 @@ describe('account privileges()', () => {
|
|||
|
||||
it('should change role', async() => {
|
||||
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
const tx = await models.VnUser.beginTransaction({});
|
||||
|
||||
const options = {transaction: tx};
|
||||
const agency = await models.Role.findOne({
|
||||
|
@ -79,8 +79,8 @@ describe('account privileges()', () => {
|
|||
let error;
|
||||
let result;
|
||||
try {
|
||||
await models.Account.privileges(ctx, clarkKent, agency.id, null, options);
|
||||
result = await models.Account.findById(clarkKent, null, options);
|
||||
await models.VnUser.privileges(ctx, clarkKent, agency.id, null, options);
|
||||
result = await models.VnUser.findById(clarkKent, null, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
|
@ -94,14 +94,14 @@ describe('account privileges()', () => {
|
|||
|
||||
it('should change hasGrant', async() => {
|
||||
const ctx = {req: {accessToken: {userId: sysadminId}}};
|
||||
const tx = await models.Account.beginTransaction({});
|
||||
const tx = await models.VnUser.beginTransaction({});
|
||||
|
||||
let error;
|
||||
let result;
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
await models.Account.privileges(ctx, clarkKent, null, true, options);
|
||||
result = await models.Account.findById(clarkKent, null, options);
|
||||
await models.VnUser.privileges(ctx, clarkKent, null, true, options);
|
||||
result = await models.VnUser.findById(clarkKent, null, options);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
|
@ -1,25 +1,25 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('account login()', () => {
|
||||
describe('VnUser signIn()', () => {
|
||||
describe('when credentials are correct', () => {
|
||||
it('should return the token', async() => {
|
||||
let login = await app.models.Account.login('salesAssistant', 'nightmare');
|
||||
let accessToken = await app.models.AccessToken.findById(login.token);
|
||||
let login = await models.VnUser.signIn('salesAssistant', 'nightmare');
|
||||
let accessToken = await models.AccessToken.findById(login.token);
|
||||
let ctx = {req: {accessToken: accessToken}};
|
||||
|
||||
expect(login.token).toBeDefined();
|
||||
|
||||
await app.models.Account.logout(ctx);
|
||||
await models.VnUser.signOut(ctx);
|
||||
});
|
||||
|
||||
it('should return the token if the user doesnt exist but the client does', async() => {
|
||||
let login = await app.models.Account.login('PetterParker', 'nightmare');
|
||||
let accessToken = await app.models.AccessToken.findById(login.token);
|
||||
let login = await models.VnUser.signIn('PetterParker', 'nightmare');
|
||||
let accessToken = await models.AccessToken.findById(login.token);
|
||||
let ctx = {req: {accessToken: accessToken}};
|
||||
|
||||
expect(login.token).toBeDefined();
|
||||
|
||||
await app.models.Account.logout(ctx);
|
||||
await models.VnUser.signOut(ctx);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,7 @@ describe('account login()', () => {
|
|||
let error;
|
||||
|
||||
try {
|
||||
await app.models.Account.login('IDontExist', 'TotallyWrongPassword');
|
||||
await models.VnUser.signIn('IDontExist', 'TotallyWrongPassword');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('account logout()', () => {
|
||||
describe('VnUser signOut()', () => {
|
||||
it('should logout and remove token after valid login', async() => {
|
||||
let loginResponse = await app.models.Account.login('buyer', 'nightmare');
|
||||
let accessToken = await app.models.AccessToken.findById(loginResponse.token);
|
||||
let loginResponse = await models.VnUser.signOut('buyer', 'nightmare');
|
||||
let accessToken = await models.AccessToken.findById(loginResponse.token);
|
||||
let ctx = {req: {accessToken: accessToken}};
|
||||
|
||||
let logoutResponse = await app.models.Account.logout(ctx);
|
||||
let tokenAfterLogout = await app.models.AccessToken.findById(loginResponse.token);
|
||||
let logoutResponse = await models.VnUser.signOut(ctx);
|
||||
let tokenAfterLogout = await models.AccessToken.findById(loginResponse.token);
|
||||
|
||||
expect(logoutResponse).toBeTrue();
|
||||
expect(tokenAfterLogout).toBeNull();
|
||||
|
@ -18,7 +18,7 @@ describe('account logout()', () => {
|
|||
let ctx = {req: {accessToken: {id: 'invalidToken'}}};
|
||||
|
||||
try {
|
||||
response = await app.models.Account.logout(ctx);
|
||||
response = await models.VnUser.signOut(ctx);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ describe('account logout()', () => {
|
|||
let ctx = {req: {accessToken: null}};
|
||||
|
||||
try {
|
||||
response = await app.models.Account.logout(ctx);
|
||||
response = await models.VnUser.signOut(ctx);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
{
|
||||
"Account": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"AccountingType": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
@ -125,10 +122,10 @@
|
|||
"UserConfigView": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"UserLog": {
|
||||
"Warehouse": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"Warehouse": {
|
||||
"VnUser": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"OsTicket": {
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||
const md5 = require('md5');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = Self => {
|
||||
require('../methods/account/login')(Self);
|
||||
require('../methods/account/logout')(Self);
|
||||
require('../methods/account/acl')(Self);
|
||||
require('../methods/account/change-password')(Self);
|
||||
require('../methods/account/set-password')(Self);
|
||||
require('../methods/account/recover-password')(Self);
|
||||
require('../methods/account/validate-token')(Self);
|
||||
require('../methods/account/privileges')(Self);
|
||||
|
||||
// Validations
|
||||
|
||||
Self.validatesFormatOf('email', {
|
||||
message: 'Invalid email',
|
||||
allowNull: true,
|
||||
allowBlank: true,
|
||||
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
|
||||
});
|
||||
|
||||
Self.validatesUniquenessOf('name', {
|
||||
message: `A client with that Web User name already exists`
|
||||
});
|
||||
|
||||
Self.observe('before save', async function(ctx) {
|
||||
if (ctx.currentInstance && ctx.currentInstance.id && ctx.data && ctx.data.password)
|
||||
ctx.data.password = md5(ctx.data.password);
|
||||
});
|
||||
|
||||
Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
||||
if (!ctx.args || !ctx.args.data.email) return;
|
||||
const models = Self.app.models;
|
||||
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const httpCtx = {req: loopBackContext.active};
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const headers = httpRequest.headers;
|
||||
const origin = headers.origin;
|
||||
const url = origin.split(':');
|
||||
|
||||
const userId = ctx.instance.id;
|
||||
const user = await models.user.findById(userId);
|
||||
|
||||
class Mailer {
|
||||
async send(verifyOptions, cb) {
|
||||
const params = {
|
||||
url: verifyOptions.verifyHref,
|
||||
recipient: verifyOptions.to,
|
||||
lang: ctx.req.getLocale()
|
||||
};
|
||||
|
||||
const email = new Email('email-verify', params);
|
||||
email.send();
|
||||
|
||||
cb(null, verifyOptions.to);
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
type: 'email',
|
||||
to: instance.email,
|
||||
from: {},
|
||||
redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`,
|
||||
template: false,
|
||||
mailer: new Mailer,
|
||||
host: url[1].split('/')[2],
|
||||
port: url[2],
|
||||
protocol: url[0],
|
||||
user: Self
|
||||
};
|
||||
|
||||
await user.verify(options);
|
||||
});
|
||||
|
||||
Self.remoteMethod('getCurrentUserData', {
|
||||
description: 'Gets the current user data',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ctx',
|
||||
type: 'object',
|
||||
http: {source: 'context'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
verb: 'GET',
|
||||
path: '/getCurrentUserData'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getCurrentUserData = async function(ctx) {
|
||||
let userId = ctx.req.accessToken.userId;
|
||||
return await Self.findById(userId, {
|
||||
fields: ['id', 'name', 'nickname']
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if user has a role.
|
||||
*
|
||||
* @param {Integer} userId The user id
|
||||
* @param {String} name The role name
|
||||
* @param {object} options Options
|
||||
* @return {Boolean} %true if user has the role, %false otherwise
|
||||
*/
|
||||
Self.hasRole = async function(userId, name, options) {
|
||||
let roles = await Self.getRoles(userId, options);
|
||||
return roles.some(role => role == name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all user roles.
|
||||
*
|
||||
* @param {Integer} userId The user id
|
||||
* @param {object} options Options
|
||||
* @return {object} User role list
|
||||
*/
|
||||
Self.getRoles = async(userId, options) => {
|
||||
let result = await Self.rawSql(
|
||||
`SELECT r.name
|
||||
FROM account.user u
|
||||
JOIN account.roleRole rr ON rr.role = u.role
|
||||
JOIN account.role r ON r.id = rr.inheritsFrom
|
||||
WHERE u.id = ?`, [userId], options);
|
||||
|
||||
let roles = [];
|
||||
for (const role of result)
|
||||
roles.push(role.name);
|
||||
|
||||
return roles;
|
||||
};
|
||||
};
|
|
@ -54,8 +54,8 @@ module.exports = Self => {
|
|||
const writeRole = dmsType.writeRole() && dmsType.writeRole().name;
|
||||
const requiredRole = readRole || writeRole;
|
||||
|
||||
const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole, options);
|
||||
const isRoot = await models.Account.hasRole(myUserId, 'root', options);
|
||||
const hasRequiredRole = await models.VnUser.hasRole(myUserId, requiredRole, options);
|
||||
const isRoot = await models.VnUser.hasRole(myUserId, 'root', options);
|
||||
|
||||
if (isRoot || hasRequiredRole)
|
||||
return true;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"model": "VnUser",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -53,8 +53,8 @@ module.exports = Self => {
|
|||
const writeRole = collection.writeRole() && collection.writeRole().name;
|
||||
const requiredRole = readRole || writeRole;
|
||||
|
||||
const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole, options);
|
||||
const isRoot = await models.Account.hasRole(myUserId, 'root', options);
|
||||
const hasRequiredRole = await models.VnUser.hasRole(myUserId, requiredRole, options);
|
||||
const isRoot = await models.VnUser.hasRole(myUserId, 'root', options);
|
||||
|
||||
if (isRoot || hasRequiredRole)
|
||||
return true;
|
||||
|
|
|
@ -5,6 +5,7 @@ const gm = require('gm');
|
|||
module.exports = Self => {
|
||||
require('../methods/image/download')(Self);
|
||||
require('../methods/image/upload')(Self);
|
||||
require('../methods/image/scrub')(Self);
|
||||
|
||||
Self.resize = async function({collectionName, srcFile, fileName, entityId}) {
|
||||
const models = Self.app.models;
|
||||
|
@ -29,13 +30,14 @@ module.exports = Self => {
|
|||
);
|
||||
|
||||
// Insert image row
|
||||
const imageName = path.parse(fileName).name;
|
||||
await models.Image.upsertWithWhere(
|
||||
{
|
||||
name: fileName,
|
||||
name: imageName,
|
||||
collectionFk: collectionName
|
||||
},
|
||||
{
|
||||
name: fileName,
|
||||
name: imageName,
|
||||
collectionFk: collectionName,
|
||||
updated: Date.vnNow() / 1000,
|
||||
}
|
||||
|
@ -49,7 +51,7 @@ module.exports = Self => {
|
|||
if (entity) {
|
||||
await entity.updateAttribute(
|
||||
collection.property,
|
||||
fileName
|
||||
imageName
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
"author": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"model": "VnUser",
|
||||
"foreignKey": "authorFk"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
},
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"model": "VnUser",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('loopback model Company', () => {
|
||||
it('should check that the company FTH doesnt exists', async() => {
|
||||
let result = await app.models.Company.findOne({where: {code: 'FTH'}});
|
||||
let result = await models.Company.findOne({where: {code: 'FTH'}});
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
const {models} = require('vn-loopback/server/server');
|
||||
describe('Dms', () => {
|
||||
const Dms = app.models.Dms;
|
||||
const Dms = models.Dms;
|
||||
|
||||
describe('getFile()', () => {
|
||||
it('should return a response with text content-type', async() => {
|
||||
|
@ -23,7 +23,7 @@ describe('Dms', () => {
|
|||
it('should return an error for a record does not exists', async() => {
|
||||
let error = {};
|
||||
try {
|
||||
await app.models.Dms.getFile('NotExistentId');
|
||||
await models.Dms.getFile('NotExistentId');
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('account recoverPassword()', () => {
|
||||
describe('VnUser recoverPassword()', () => {
|
||||
const userId = 1107;
|
||||
|
||||
const activeCtx = {
|
||||
|
@ -21,9 +21,9 @@ describe('account recoverPassword()', () => {
|
|||
|
||||
it('should send email with token', async() => {
|
||||
const userId = 1107;
|
||||
const user = await models.Account.findById(userId);
|
||||
const user = await models.VnUser.findById(userId);
|
||||
|
||||
await models.Account.recoverPassword(user.email);
|
||||
await models.VnUser.recoverPassword(user.email);
|
||||
|
||||
const result = await models.AccessToken.findOne({where: {userId: userId}});
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
const models = require('vn-loopback/server/server').models;
|
||||
|
||||
describe('loopback model Account', () => {
|
||||
describe('loopback model VnUser', () => {
|
||||
it('should return true if the user has the given role', async() => {
|
||||
let result = await models.Account.hasRole(1, 'employee');
|
||||
let result = await models.VnUser.hasRole(1, 'employee');
|
||||
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if the user doesnt have the given role', async() => {
|
||||
let result = await models.Account.hasRole(1, 'administrator');
|
||||
let result = await models.VnUser.hasRole(1, 'administrator');
|
||||
|
||||
expect(result).toBeFalsy();
|
||||
});
|
|
@ -26,7 +26,7 @@
|
|||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"model": "VnUser",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
"model": "Company",
|
||||
"foreignKey": "companyFk"
|
||||
},
|
||||
"account": {
|
||||
"VnUser": {
|
||||
"type": "belongsTo",
|
||||
"model": "Account",
|
||||
"model": "VnUser",
|
||||
"foreignKey": "userFk"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
const LoopBackContext = require('loopback-context');
|
||||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = function(Self) {
|
||||
Self.on('resetPasswordRequest', async function(info) {
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const httpCtx = {req: loopBackContext.active};
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const headers = httpRequest.headers;
|
||||
const origin = headers.origin;
|
||||
|
||||
const user = await Self.app.models.Account.findById(info.user.id);
|
||||
const params = {
|
||||
recipient: info.email,
|
||||
lang: user.lang,
|
||||
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
|
||||
};
|
||||
|
||||
const options = Object.assign({}, info.options);
|
||||
for (const param in options)
|
||||
params[param] = options[param];
|
||||
|
||||
const email = new Email(options.emailTemplate, params);
|
||||
|
||||
return email.send();
|
||||
});
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "user",
|
||||
"base": "User",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "salix.User"
|
||||
},
|
||||
"resetPasswordTokenTTL": "604800"
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "number",
|
||||
"forceId": false
|
||||
},
|
||||
"username":{
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
const vnModel = require('vn-loopback/common/models/vn-model');
|
||||
const LoopBackContext = require('loopback-context');
|
||||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = function(Self) {
|
||||
vnModel(Self);
|
||||
|
||||
require('../methods/vn-user/signIn')(Self);
|
||||
require('../methods/vn-user/acl')(Self);
|
||||
require('../methods/vn-user/recover-password')(Self);
|
||||
require('../methods/vn-user/validate-token')(Self);
|
||||
require('../methods/vn-user/privileges')(Self);
|
||||
|
||||
// Validations
|
||||
|
||||
Self.validatesFormatOf('email', {
|
||||
message: 'Invalid email',
|
||||
allowNull: true,
|
||||
allowBlank: true,
|
||||
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
|
||||
});
|
||||
|
||||
Self.validatesUniquenessOf('name', {
|
||||
message: `A client with that Web User name already exists`
|
||||
});
|
||||
|
||||
Self.remoteMethod('getCurrentUserData', {
|
||||
description: 'Gets the current user data',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'ctx',
|
||||
type: 'Object',
|
||||
http: {source: 'context'}
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: 'Object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
verb: 'GET',
|
||||
path: '/getCurrentUserData'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getCurrentUserData = async function(ctx) {
|
||||
let userId = ctx.req.accessToken.userId;
|
||||
return await Self.findById(userId, {
|
||||
fields: ['id', 'name', 'nickname']
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if user has a role.
|
||||
*
|
||||
* @param {Integer} userId The user id
|
||||
* @param {String} name The role name
|
||||
* @param {Object} options Options
|
||||
* @return {Boolean} %true if user has the role, %false otherwise
|
||||
*/
|
||||
Self.hasRole = async function(userId, name, options) {
|
||||
const roles = await Self.getRoles(userId, options);
|
||||
return roles.some(role => role == name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all user roles.
|
||||
*
|
||||
* @param {Integer} userId The user id
|
||||
* @param {Object} options Options
|
||||
* @return {Object} User role list
|
||||
*/
|
||||
Self.getRoles = async(userId, options) => {
|
||||
const result = await Self.rawSql(
|
||||
`SELECT r.name
|
||||
FROM account.user u
|
||||
JOIN account.roleRole rr ON rr.role = u.role
|
||||
JOIN account.role r ON r.id = rr.inheritsFrom
|
||||
WHERE u.id = ?`, [userId], options);
|
||||
|
||||
const roles = [];
|
||||
for (const role of result)
|
||||
roles.push(role.name);
|
||||
|
||||
return roles;
|
||||
};
|
||||
|
||||
Self.on('resetPasswordRequest', async function(info) {
|
||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
const httpCtx = {req: loopBackContext.active};
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const headers = httpRequest.headers;
|
||||
const origin = headers.origin;
|
||||
|
||||
const user = await Self.app.models.VnUser.findById(info.user.id);
|
||||
const params = {
|
||||
recipient: info.email,
|
||||
lang: user.lang,
|
||||
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
|
||||
};
|
||||
|
||||
const options = Object.assign({}, info.options);
|
||||
for (const param in options)
|
||||
params[param] = options[param];
|
||||
|
||||
const email = new Email(options.emailTemplate, params);
|
||||
|
||||
return email.send();
|
||||
});
|
||||
};
|
|
@ -1,11 +1,13 @@
|
|||
{
|
||||
"name": "Account",
|
||||
"base": "VnModel",
|
||||
"name": "VnUser",
|
||||
"base": "User",
|
||||
"validateUpsert": true,
|
||||
"options": {
|
||||
"mysql": {
|
||||
"table": "account.user"
|
||||
}
|
||||
},
|
||||
"resetPasswordTokenTTL": "604800",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
|
@ -15,6 +17,19 @@
|
|||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"mysql": {
|
||||
"columnName": "name"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"mysql": {
|
||||
"columnName": "bcryptPassword"
|
||||
}
|
||||
},
|
||||
"roleFk": {
|
||||
"type": "number",
|
||||
"mysql": {
|
||||
|
@ -27,10 +42,6 @@
|
|||
"lang": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"bcryptPassword": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -40,9 +51,6 @@
|
|||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailVerified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"created": {
|
||||
"type": "date"
|
||||
},
|
||||
|
@ -86,7 +94,7 @@
|
|||
},
|
||||
"acls": [
|
||||
{
|
||||
"property": "login",
|
||||
"property": "signIn",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
|
@ -100,13 +108,6 @@
|
|||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "logout",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"property": "validateToken",
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
|
@ -53,7 +53,7 @@ async function test() {
|
|||
const JunitReporter = require('jasmine-reporters');
|
||||
jasmine.addReporter(new JunitReporter.JUnitXmlReporter());
|
||||
|
||||
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
|
||||
jasmine.exitOnCompletion = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES ('VnUser', '*', '*', 'ALLOW', 'ROLE', 'employee');
|
||||
|
||||
INSERT INTO `salix`.`ACL` (id, model, property, accessType, permission, principalType, principalId)
|
||||
VALUES ('VnUser', 'acl', 'READ', 'ALLOW', 'ROLE', 'account');
|
||||
|
||||
INSERT INTO `salix`.`ACL` (id, model, property, accessType, permission, principalType, principalId)
|
||||
VALUES ('VnUser', 'getCurrentUserData', 'READ', 'ALLOW', 'ROLE', 'account');
|
||||
|
||||
INSERT INTO `salix`.`ACL` (id, model, property, accessType, permission, principalType, principalId)
|
||||
VALUES ('VnUser', 'changePassword', '*', 'ALLOW', 'ROLE', 'account');
|
||||
|
||||
|
||||
|
||||
UPDATE `hedera`.`imageCollection` t
|
||||
SET t.model = 'VnUser'
|
||||
WHERE t.id = 6;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('Receipt', 'receiptEmail', '*', 'ALLOW', 'ROLE', 'salesAssistant');
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `vn`.`printQueueArgs` MODIFY COLUMN value varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL;
|
|
@ -0,0 +1,5 @@
|
|||
DROP TRIGGER `vn`.`deviceProduction_afterInsert`;
|
||||
DROP TRIGGER `vn`.`deviceProduction_afterUpdate`;
|
||||
|
||||
DROP TRIGGER `vn`.`deviceProductionUser_afterDelete`;
|
||||
|
|
@ -1,12 +1,5 @@
|
|||
DROP TABLE IF EXISTS `vn`.`dmsRecover`;
|
||||
|
||||
ALTER TABLE `vn`.`delivery` DROP COLUMN addressFk;
|
||||
ALTER TABLE `vn`.`delivery` DROP CONSTRAINT delivery_ticketFk_FK;
|
||||
ALTER TABLE `vn`.`delivery` DROP COLUMN ticketFk;
|
||||
ALTER TABLE `vn`.`delivery` ADD ticketFk INT DEFAULT NULL;
|
||||
ALTER TABLE `vn`.`delivery` ADD CONSTRAINT delivery_ticketFk_FK FOREIGN KEY (`ticketFk`) REFERENCES `vn`.`ticket`(`id`);
|
||||
|
||||
DROP PROCEDURE IF EXISTS vn.route_getTickets;
|
||||
DROP PROCEDURE IF EXISTS `vn`.`route_getTickets`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
|
@ -17,54 +10,68 @@ BEGIN
|
|||
* de sus tickets.
|
||||
*
|
||||
* @param vRouteFk
|
||||
*
|
||||
* @select Información de los tickets
|
||||
*/
|
||||
|
||||
SELECT
|
||||
t.id Id,
|
||||
t.clientFk Client,
|
||||
a.id Address,
|
||||
t.packages Packages,
|
||||
a.street AddressName,
|
||||
a.postalCode PostalCode,
|
||||
a.city City,
|
||||
sub2.itemPackingTypeFk PackingType,
|
||||
c.phone ClientPhone,
|
||||
c.mobile ClientMobile,
|
||||
a.phone AddressPhone,
|
||||
a.mobile AddressMobile,
|
||||
d.longitude Longitude,
|
||||
d.latitude Latitude,
|
||||
wm.mediaValue SalePersonPhone,
|
||||
tob.Note Note,
|
||||
t.isSigned Signed
|
||||
FROM ticket t
|
||||
JOIN client c ON t.clientFk = c.id
|
||||
JOIN address a ON t.addressFk = a.id
|
||||
LEFT JOIN delivery d ON t.id = d.ticketFk
|
||||
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
|
||||
LEFT JOIN
|
||||
(SELECT tob.description Note, t.id
|
||||
FROM ticketObservation tob
|
||||
JOIN ticket t ON tob.ticketFk = t.id
|
||||
JOIN observationType ot ON ot.id = tob.observationTypeFk
|
||||
WHERE t.routeFk = vRouteFk
|
||||
AND ot.code = 'delivery'
|
||||
)tob ON tob.id = t.id
|
||||
LEFT JOIN
|
||||
(SELECT sub.ticketFk,
|
||||
CONCAT('(', GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk ORDER BY sub.items DESC SEPARATOR ','), ') ') itemPackingTypeFk
|
||||
FROM (SELECT s.ticketFk , i.itemPackingTypeFk, COUNT(*) items
|
||||
FROM ticket t
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
WHERE t.routeFk = vRouteFk
|
||||
GROUP BY t.id,i.itemPackingTypeFk)sub
|
||||
GROUP BY sub.ticketFk
|
||||
) sub2 ON sub2.ticketFk = t.id
|
||||
WHERE t.routeFk = vRouteFk
|
||||
GROUP BY t.id
|
||||
ORDER BY t.priority;
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT t.id Id,
|
||||
t.clientFk Client,
|
||||
a.id Address,
|
||||
a.nickname ClientName,
|
||||
t.packages Packages,
|
||||
a.street AddressName,
|
||||
a.postalCode PostalCode,
|
||||
a.city City,
|
||||
sub2.itemPackingTypeFk PackingType,
|
||||
c.phone ClientPhone,
|
||||
c.mobile ClientMobile,
|
||||
a.phone AddressPhone,
|
||||
a.mobile AddressMobile,
|
||||
d.longitude Longitude,
|
||||
d.latitude Latitude,
|
||||
wm.mediaValue SalePersonPhone,
|
||||
tob.description Note,
|
||||
t.isSigned Signed,
|
||||
t.priority
|
||||
FROM ticket t
|
||||
JOIN client c ON t.clientFk = c.id
|
||||
JOIN address a ON t.addressFk = a.id
|
||||
LEFT JOIN delivery d ON d.ticketFk = t.id
|
||||
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
|
||||
LEFT JOIN(
|
||||
SELECT tob.description, t.id
|
||||
FROM ticketObservation tob
|
||||
JOIN ticket t ON tob.ticketFk = t.id
|
||||
JOIN observationType ot ON ot.id = tob.observationTypeFk
|
||||
WHERE t.routeFk = vRouteFk
|
||||
AND ot.code = 'delivery'
|
||||
)tob ON tob.id = t.id
|
||||
LEFT JOIN(
|
||||
SELECT sub.ticketFk,
|
||||
CONCAT('(',
|
||||
GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk
|
||||
ORDER BY sub.items DESC SEPARATOR ','),
|
||||
') ') itemPackingTypeFk
|
||||
FROM (
|
||||
SELECT s.ticketFk, i.itemPackingTypeFk, COUNT(*) items
|
||||
FROM ticket t
|
||||
JOIN sale s ON s.ticketFk = t.id
|
||||
JOIN item i ON i.id = s.itemFk
|
||||
WHERE t.routeFk = vRouteFk
|
||||
GROUP BY t.id, i.itemPackingTypeFk
|
||||
)sub
|
||||
GROUP BY sub.ticketFk
|
||||
)sub2 ON sub2.ticketFk = t.id
|
||||
WHERE t.routeFk = vRouteFk
|
||||
ORDER BY d.id DESC
|
||||
LIMIT 10000000000000000000
|
||||
)sub3
|
||||
GROUP BY sub3.id
|
||||
ORDER BY sub3.priority;
|
||||
END$$
|
||||
DELIMITER ;
|
||||
|
||||
ALTER TABLE `vn`.`delivery` DROP FOREIGN KEY delivery_ticketFk_FK;
|
||||
ALTER TABLE `vn`.`delivery` DROP COLUMN ticketFk;
|
||||
ALTER TABLE `vn`.`delivery` ADD ticketFk INT DEFAULT NULL;
|
||||
ALTER TABLE `vn`.`delivery` ADD CONSTRAINT delivery_ticketFk_FK FOREIGN KEY (`ticketFk`) REFERENCES `vn`.`ticket`(`id`);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
UPDATE `salix`.`ACL`
|
||||
SET model = 'InvoiceOut'
|
||||
WHERE property IN ('negativeBases', 'negativeBasesCsv');
|
|
@ -0,0 +1,3 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES ('ClientInforma', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('ClientInforma', '*', 'WRITE', 'ALLOW', 'ROLE', 'financial');
|
|
@ -0,0 +1,16 @@
|
|||
ALTER TABLE `vn`.`client` ADD rating INT UNSIGNED DEFAULT NULL NULL COMMENT 'información proporcionada por Informa';
|
||||
ALTER TABLE `vn`.`client` ADD recommendedCredit INT UNSIGNED DEFAULT NULL NULL COMMENT 'información proporcionada por Informa';
|
||||
|
||||
CREATE TABLE `vn`.`clientInforma` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`clientFk` int(11) NOT NULL,
|
||||
`rating` int(10) unsigned DEFAULT NULL,
|
||||
`recommendedCredit` int(10) unsigned DEFAULT NULL,
|
||||
`workerFk` int(10) unsigned NOT NULL,
|
||||
`created` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `informaWorkers_fk_idx` (`workerFk`),
|
||||
KEY `informaClientFk` (`clientFk`),
|
||||
CONSTRAINT `informa_ClienteFk` FOREIGN KEY (`clientFk`) REFERENCES `client` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT `informa_workers_fk` FOREIGN KEY (`workerFk`) REFERENCES `worker` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='información proporcionada por Informa, se actualiza desde el hook de client (salix)';
|
|
@ -0,0 +1,64 @@
|
|||
DELETE FROM `salix`.`ACL` WHERE id=7;
|
||||
|
||||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES
|
||||
('Client', 'setRating', 'WRITE', 'ALLOW', 'ROLE', 'financial');
|
||||
|
||||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES
|
||||
('Client', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'addressesPropagateRe', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'canBeInvoiced', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'canCreateTicket', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'consumption', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'createAddress', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'createWithUser', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'extendedListFilter', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'getAverageInvoiced', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'getCard', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'getDebt', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'getMana', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'transactions', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'hasCustomerRole', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'isValidClient', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'lastActiveTickets', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'sendSms', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'setPassword', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'summary', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateAddress', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateFiscalData', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateUser', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'uploadFile', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'campaignMetricsPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'campaignMetricsEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'clientWelcomeHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'clientWelcomeEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'printerSetupHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'printerSetupEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'sepaCoreEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'letterDebtorPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'letterDebtorStHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'letterDebtorStEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'letterDebtorNdHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'letterDebtorNdEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'clientDebtStatementPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'clientDebtStatementHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'clientDebtStatementEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'creditRequestPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'creditRequestHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'creditRequestEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'incotermsAuthorizationPdf', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'incotermsAuthorizationHtml', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'incotermsAuthorizationEmail', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'consumptionSendQueued', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'filter', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'getClientOrSupplierReference', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'upsert', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'create', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'replaceById', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateAttributes', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateAttributes', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'deleteById', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'replaceOrCreate', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'updateAll', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('Client', 'upsertWithWhere', '*', 'ALLOW', 'ROLE', 'employee');
|
|
@ -0,0 +1,2 @@
|
|||
DROP PROCEDURE `vn`.`refund`;
|
||||
DROP PROCEDURE `vn`.`ticket_doRefund`;
|
|
@ -0,0 +1,9 @@
|
|||
-- vn.companyI18n definition
|
||||
CREATE TABLE `vn`.`companyI18n` (
|
||||
`companyFk` smallint(5) unsigned NOT NULL,
|
||||
`lang` char(2) CHARACTER SET utf8mb3 NOT NULL,
|
||||
`footnotes` longtext COLLATE utf8mb3_unicode_ci DEFAULT NULL,
|
||||
PRIMARY KEY (`companyFk`,`lang`),
|
||||
CONSTRAINT `companyI18n_FK` FOREIGN KEY (`companyFk`) REFERENCES `company` (`id`) ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `vn`.`company` ADD `web` varchar(100) NULL;
|
|
@ -0,0 +1,21 @@
|
|||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES
|
||||
('VnUser', '*', '*', 'ALLOW', 'ROLE', 'employee'),
|
||||
('VnUser','acl','READ','ALLOW','ROLE','account'),
|
||||
('VnUser','getCurrentUserData','READ','ALLOW','ROLE','account'),
|
||||
('VnUser','changePassword', 'WRITE', 'ALLOW', 'ROLE', 'account'),
|
||||
('Account','exists','READ','ALLOW','ROLE','account');
|
||||
|
||||
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
|
||||
VALUES
|
||||
('Account','exists','READ','ALLOW','ROLE','account');
|
||||
|
||||
DELETE FROM `salix`.`ACL` WHERE (model, property) = ('Account', 'acl');
|
||||
DELETE FROM `salix`.`ACL` WHERE (model, property) = ('Account', 'getCurrentUserData');
|
||||
DELETE FROM `salix`.`ACL` WHERE (model, property) = ('Account', 'changePassword');
|
||||
DELETE FROM `salix`.`ACL` WHERE model = 'UserAccount';
|
||||
|
||||
UPDATE `hedera`.`imageCollection` t
|
||||
SET t.model = 'VnUser'
|
||||
WHERE t.id = 6;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||
VALUES
|
||||
('UserLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
|
||||
('RoleLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
|
|
@ -98,20 +98,20 @@ INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType`
|
|||
VALUES
|
||||
(1, 978, 1, 0, 2000, 9, 0);
|
||||
|
||||
INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,`email`,`lang`, `image`)
|
||||
INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `bcryptPassword`, `password`,`role`,`active`,`email`,`lang`, `image`)
|
||||
VALUES
|
||||
(1101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL),
|
||||
(1111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL),
|
||||
(1112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL);
|
||||
(1101, 'BruceWayne', 'Bruce Wayne', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1102, 'PetterParker', 'Petter Parker', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1103, 'ClarkKent', 'Clark Kent', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1104, 'TonyStark', 'Tony Stark', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1105, 'MaxEisenhardt', 'Max Eisenhardt', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1106, 'DavidCharlesHaller', 'David Charles Haller', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1107, 'HankPym', 'Hank Pym', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1108, 'CharlesXavier', 'Charles Xavier', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1109, 'BruceBanner', 'Bruce Banner', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
|
||||
(1110, 'JessicaJones', 'Jessica Jones', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL),
|
||||
(1111, 'Missing', 'Missing', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL),
|
||||
(1112, 'Trash', 'Trash', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL);
|
||||
|
||||
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
|
||||
VALUES
|
||||
|
@ -173,10 +173,6 @@ INSERT INTO `vn`.`sector`(`id`, `description`, `warehouseFk`, `isPreviousPrepare
|
|||
(1, 'First sector', 1, 1, 'FIRST'),
|
||||
(2, 'Second sector', 2, 0, 'SECOND');
|
||||
|
||||
INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`)
|
||||
VALUES ('1106', '1', '1', 'H', '1', '1', '1'),
|
||||
('1107', '1', '1', 'V', '1', '2', '1');
|
||||
|
||||
INSERT INTO `vn`.`printer` (`id`, `name`, `path`, `isLabeler`, `sectorFk`, `ipAddress`)
|
||||
VALUES
|
||||
(1, 'printer1', 'path1', 0, 1 , NULL),
|
||||
|
@ -550,7 +546,8 @@ INSERT INTO `vn`.`supplier`(`id`, `name`, `nickname`,`account`,`countryFk`,`nif`
|
|||
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'),
|
||||
(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');
|
||||
(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'),
|
||||
(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');
|
||||
|
||||
INSERT INTO `vn`.`supplierContact`(`id`, `supplierFk`, `phone`, `mobile`, `email`, `observation`, `name`)
|
||||
VALUES
|
||||
|
@ -1200,6 +1197,11 @@ INSERT INTO `vn`.`train`(`id`, `name`)
|
|||
(1, 'Train1'),
|
||||
(2, 'Train2');
|
||||
|
||||
INSERT INTO `vn`.`operator` (`workerFk`, `numberOfWagons`, `trainFk`, `itemPackingTypeFk`, `warehouseFk`, `sectorFk`, `labelerFk`)
|
||||
VALUES
|
||||
('1106', '1', '1', 'H', '1', '1', '1'),
|
||||
('1107', '1', '1', 'V', '1', '1', '1');
|
||||
|
||||
INSERT INTO `vn`.`collection`(`id`, `workerFk`, `stateFk`, `created`, `trainFk`)
|
||||
VALUES
|
||||
(1, 1106, 5, DATE_ADD(util.VN_CURDATE(),INTERVAL +1 DAY), 1),
|
||||
|
@ -2788,7 +2790,7 @@ INSERT INTO `vn`.`profileType` (`id`, `name`)
|
|||
|
||||
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
|
||||
VALUES
|
||||
('lilium', 'dev', 'http://localhost:8080/#/'),
|
||||
('lilium', 'dev', 'http://localhost:9000/#/'),
|
||||
('salix', 'dev', 'http://localhost:5000/#!/');
|
||||
|
||||
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
|
||||
|
|
|
@ -68,6 +68,7 @@ TABLES=(
|
|||
time
|
||||
volumeConfig
|
||||
workCenter
|
||||
companyI18n
|
||||
)
|
||||
dump_tables ${TABLES[@]}
|
||||
|
||||
|
|
|
@ -156,14 +156,14 @@ let actions = {
|
|||
await this.waitForSpinnerLoad();
|
||||
},
|
||||
|
||||
accessToSection: async function(state) {
|
||||
accessToSection: async function(state, name = 'Others') {
|
||||
await this.waitForSelector('vn-left-menu');
|
||||
let nested = await this.evaluate(state => {
|
||||
return document.querySelector(`vn-left-menu li li > a[ui-sref="${state}"]`) != null;
|
||||
}, state);
|
||||
|
||||
if (nested) {
|
||||
let selector = 'vn-left-menu vn-item-section > vn-icon[icon=keyboard_arrow_down]';
|
||||
let selector = `vn-left-menu li[name="${name}"]`;
|
||||
await this.evaluate(selector => {
|
||||
document.querySelector(selector).scrollIntoViewIfNeeded();
|
||||
}, selector);
|
||||
|
@ -218,6 +218,23 @@ let actions = {
|
|||
return handle.jsonValue();
|
||||
},
|
||||
|
||||
getValue: async function(selector) {
|
||||
return await this.waitToGetProperty(selector, 'value');
|
||||
},
|
||||
|
||||
getValues: async function(selectorMap) {
|
||||
const values = {};
|
||||
for (const key in selectorMap)
|
||||
values[key] = await this.waitToGetProperty(selectorMap[key], 'value');
|
||||
return values;
|
||||
},
|
||||
|
||||
innerText: async function(selector) {
|
||||
const element = await this.$(selector);
|
||||
const handle = await element.getProperty('innerText');
|
||||
return handle.jsonValue();
|
||||
},
|
||||
|
||||
waitPropertyLength: async function(selector, property, minLength) {
|
||||
await this.waitForFunction((selector, property, minLength) => {
|
||||
const element = document.querySelector(selector);
|
||||
|
|
|
@ -283,12 +283,6 @@ export default {
|
|||
cancelEditAddressButton: 'vn-client-address-edit > form > vn-button-bar > vn-button > button',
|
||||
watcher: 'vn-client-address-edit vn-watcher'
|
||||
},
|
||||
clientWebAccess: {
|
||||
enableWebAccessCheckbox: 'vn-check[label="Enable web access"]',
|
||||
userName: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.name"]',
|
||||
email: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.email"]',
|
||||
saveButton: 'button[type=submit]'
|
||||
},
|
||||
clientNotes: {
|
||||
addNoteFloatButton: 'vn-float-button',
|
||||
note: 'vn-textarea[ng-model="$ctrl.note.text"]',
|
||||
|
@ -312,15 +306,6 @@ export default {
|
|||
clientMandate: {
|
||||
firstMandateText: 'vn-client-mandate vn-card vn-table vn-tbody > vn-tr'
|
||||
},
|
||||
clientLog: {
|
||||
lastModificationPreviousValue: 'vn-client-log vn-tr table tr td.before',
|
||||
lastModificationCurrentValue: 'vn-client-log vn-tr table tr td.after',
|
||||
namePreviousValue: 'vn-client-log vn-tr table tr:nth-child(1) td.before',
|
||||
nameCurrentValue: 'vn-client-log vn-tr table tr:nth-child(1) td.after',
|
||||
activePreviousValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.before',
|
||||
activeCurrentValue: 'vn-client-log vn-tr:nth-child(2) table tr:nth-child(2) td.after'
|
||||
|
||||
},
|
||||
clientBalance: {
|
||||
company: 'vn-client-balance-index vn-autocomplete[ng-model="$ctrl.companyId"]',
|
||||
newPaymentButton: `vn-float-button`,
|
||||
|
@ -414,7 +399,7 @@ export default {
|
|||
saveFieldsButton: '.vn-popover.shown vn-button[label="Save"] > button'
|
||||
},
|
||||
itemFixedPrice: {
|
||||
add: 'vn-fixed-price vn-icon-button[icon="add_circle"]',
|
||||
add: 'vn-fixed-price vn-icon-button[vn-tooltip="Add fixed price"]',
|
||||
firstItemID: 'vn-fixed-price tr:nth-child(2) vn-autocomplete[ng-model="price.itemFk"]',
|
||||
fourthFixedPrice: 'vn-fixed-price tr:nth-child(5)',
|
||||
fourthItemID: 'vn-fixed-price tr:nth-child(5) vn-autocomplete[ng-model="price.itemFk"]',
|
||||
|
@ -427,7 +412,18 @@ export default {
|
|||
fourthEnded: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.ended"]',
|
||||
fourthDeleteIcon: 'vn-fixed-price tr:nth-child(5) > td:nth-child(9) > vn-icon-button[icon="delete"]',
|
||||
orderColumnId: 'vn-fixed-price th[field="itemFk"]',
|
||||
removeWarehouseFilter: 'vn-searchbar > form > vn-textfield > div.container > div.prepend > prepend > div > span:nth-child(1) > vn-icon > i'
|
||||
removeWarehouseFilter: 'vn-searchbar > form > vn-textfield > div.container > div.prepend > prepend > div > span:nth-child(1) > vn-icon > i',
|
||||
generalSearchFilter: 'vn-fixed-price-search-panel vn-textfield[ng-model="$ctrl.filter.search"]',
|
||||
reignFilter: 'vn-fixed-price-search-panel vn-horizontal.item-category vn-one',
|
||||
typeFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="$ctrl.filter.typeFk"]',
|
||||
buyerFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="$ctrl.filter.buyerFk"]',
|
||||
warehouseFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="$ctrl.filter.warehouseFk"]',
|
||||
mineFilter: 'vn-fixed-price-search-panel vn-check[ng-model="$ctrl.filter.mine"]',
|
||||
hasMinPriceFilter: 'vn-fixed-price-search-panel vn-check[ng-model="$ctrl.filter.hasMinPrice"]',
|
||||
addTag: 'vn-fixed-price-search-panel vn-icon-button[icon="add_circle"]',
|
||||
tagFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="itemTag.tagFk"]',
|
||||
tagValueFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="itemTag.value"]',
|
||||
chip: 'vn-fixed-price-search-panel vn-chip > vn-icon'
|
||||
},
|
||||
itemCreateView: {
|
||||
temporalName: 'vn-item-create vn-textfield[ng-model="$ctrl.item.provisionalName"]',
|
||||
|
@ -744,6 +740,7 @@ export default {
|
|||
anyDocument: 'vn-ticket-dms-index > vn-data-viewer vn-tbody vn-tr'
|
||||
},
|
||||
ticketFuture: {
|
||||
searchResult: 'vn-ticket-future tbody tr',
|
||||
openAdvancedSearchButton: 'vn-searchbar .append vn-icon[icon="arrow_drop_down"]',
|
||||
originDated: 'vn-date-picker[label="Origin date"]',
|
||||
futureDated: 'vn-date-picker[label="Destination date"]',
|
||||
|
@ -759,7 +756,6 @@ export default {
|
|||
problems: 'vn-check[label="With problems"]',
|
||||
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
||||
moveButton: 'vn-button[vn-tooltip="Future tickets"]',
|
||||
acceptButton: '.vn-confirm.shown button[response="accept"]',
|
||||
firstCheck: 'tbody > tr:nth-child(1) > td > vn-check',
|
||||
multiCheck: 'vn-multi-check',
|
||||
tableId: 'vn-textfield[name="id"]',
|
||||
|
@ -989,9 +985,9 @@ export default {
|
|||
saveButton: 'vn-worker-basic-data button[type=submit]'
|
||||
},
|
||||
workerNotes: {
|
||||
addNoteFloatButton: 'vn-worker-note vn-float-button',
|
||||
note: 'vn-textarea[ng-model="$ctrl.note.text"]',
|
||||
saveButton: 'button[type=submit]',
|
||||
addNoteFloatButton: 'vn-worker-note vn-icon[icon="add"]',
|
||||
note: 'vn-note-worker-create vn-textarea[ng-model="$ctrl.note.text"]',
|
||||
saveButton: 'vn-note-worker-create button[type=submit]',
|
||||
firstNoteText: 'vn-worker-note .text'
|
||||
},
|
||||
workerPbx: {
|
||||
|
@ -1350,18 +1346,6 @@ export default {
|
|||
notes: 'vn-supplier-basic-data vn-textarea[ng-model="$ctrl.supplier.note"]',
|
||||
saveButton: 'vn-supplier-basic-data button[type="submit"]',
|
||||
},
|
||||
supplierFiscalData: {
|
||||
socialName: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.name"]',
|
||||
taxNumber: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.nif"]',
|
||||
account: 'vn-supplier-fiscal-data vn-textfield[ng-model="$ctrl.supplier.account"]',
|
||||
sageTaxType: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.sageTaxTypeFk"]',
|
||||
sageWihholding: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.sageWithholdingFk"]',
|
||||
postCode: 'vn-supplier-fiscal-data vn-datalist[ng-model="$ctrl.supplier.postCode"]',
|
||||
city: 'vn-supplier-fiscal-data vn-datalist[ng-model="$ctrl.supplier.city"]',
|
||||
province: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.provinceFk"]',
|
||||
country: 'vn-supplier-fiscal-data vn-autocomplete[ng-model="$ctrl.supplier.countryFk"]',
|
||||
saveButton: 'vn-supplier-fiscal-data button[type="submit"]',
|
||||
},
|
||||
supplierBillingData: {
|
||||
payMethod: 'vn-supplier-billing-data vn-autocomplete[ng-model="$ctrl.supplier.payMethodFk"]',
|
||||
payDem: 'vn-supplier-billing-data vn-autocomplete[ng-model="$ctrl.supplier.payDemFk"]',
|
||||
|
|
|
@ -79,27 +79,17 @@ describe('SmartTable SearchBar integration', () => {
|
|||
it('should order by first id', async() => {
|
||||
await page.loginAndModule('developer', 'item');
|
||||
await page.accessToSection('item.fixedPrice');
|
||||
await page.doSearch();
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value');
|
||||
|
||||
expect(result).toEqual('1');
|
||||
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '1');
|
||||
});
|
||||
|
||||
it('should order by last id', async() => {
|
||||
it('should order by last id, reload page and have same order', async() => {
|
||||
await page.waitToClick(selectors.itemFixedPrice.orderColumnId);
|
||||
const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value');
|
||||
|
||||
expect(result).toEqual('3');
|
||||
});
|
||||
|
||||
it('should reload page and have same order', async() => {
|
||||
await page.reload({
|
||||
waitUntil: 'networkidle2'
|
||||
});
|
||||
const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value');
|
||||
|
||||
expect(result).toEqual('3');
|
||||
await page.waitForTextInField(selectors.itemFixedPrice.firstItemID, '13');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,88 +1,56 @@
|
|||
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||
import selectors from '../../helpers/selectors';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Client Edit web access path', () => {
|
||||
const $ = {
|
||||
enableWebAccess: 'vn-client-web-access vn-check[label="Enable web access"]',
|
||||
userName: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.name"]',
|
||||
email: 'vn-client-web-access vn-textfield[ng-model="$ctrl.account.email"]',
|
||||
saveButton: 'vn-client-web-access button[type=submit]',
|
||||
nameValue: 'vn-client-log .change:nth-child(1) .basic-json:nth-child(1) vn-json-value',
|
||||
activeValue: 'vn-client-log .change:nth-child(2) .basic-json:nth-child(2) vn-json-value'
|
||||
};
|
||||
|
||||
describe('Client web access path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('salesPerson', 'client');
|
||||
await page.accessToSearchResult('max');
|
||||
await page.accessToSection('client.card.webAccess');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should uncheck the Enable web access checkbox', async() => {
|
||||
await page.waitToClick(selectors.clientWebAccess.enableWebAccessCheckbox);
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
it('should modify and save web access attributes', async() => {
|
||||
await page.accessToSection('client.card.webAccess');
|
||||
await page.click($.enableWebAccess);
|
||||
await page.click($.saveButton);
|
||||
const enableMessage = await page.waitForSnackbar();
|
||||
await page.overwrite($.userName, 'Legion');
|
||||
await page.overwrite($.email, 'legion@marvel.com');
|
||||
await page.click($.saveButton);
|
||||
const modifyMessage = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it(`should update the name`, async() => {
|
||||
await page.clearInput(selectors.clientWebAccess.userName);
|
||||
await page.write(selectors.clientWebAccess.userName, 'Legion');
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it(`should update the email`, async() => {
|
||||
await page.clearInput(selectors.clientWebAccess.email);
|
||||
await page.write(selectors.clientWebAccess.email, 'legion@marvel.com');
|
||||
await page.waitToClick(selectors.clientWebAccess.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section and confirm web access is now unchecked', async() => {
|
||||
await page.reloadSection('client.card.webAccess');
|
||||
const result = await page.checkboxState(selectors.clientWebAccess.enableWebAccessCheckbox);
|
||||
const hasAccess = await page.checkboxState($.enableWebAccess);
|
||||
const userName = await page.getValue($.userName);
|
||||
const email = await page.getValue($.email);
|
||||
|
||||
expect(result).toBe('unchecked');
|
||||
});
|
||||
|
||||
it('should confirm web access name have been updated', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientWebAccess.userName, 'value');
|
||||
|
||||
expect(result).toEqual('Legion');
|
||||
});
|
||||
|
||||
it('should confirm web access email have been updated', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.clientWebAccess.email, 'value');
|
||||
|
||||
expect(result).toEqual('legion@marvel.com');
|
||||
});
|
||||
|
||||
it(`should navigate to the log section`, async() => {
|
||||
await page.accessToSection('client.card.log');
|
||||
});
|
||||
const logName = await page.innerText($.nameValue);
|
||||
const logActive = await page.innerText($.activeValue);
|
||||
|
||||
it(`should confirm the last log shows the updated client name and no modifications on active checkbox`, async() => {
|
||||
let namePreviousValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.namePreviousValue, 'innerText');
|
||||
let nameCurrentValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.nameCurrentValue, 'innerText');
|
||||
expect(enableMessage.type).toBe('success');
|
||||
expect(modifyMessage.type).toBe('success');
|
||||
|
||||
expect(namePreviousValue).toEqual('MaxEisenhardt');
|
||||
expect(nameCurrentValue).toEqual('Legion');
|
||||
});
|
||||
expect(hasAccess).toBe('unchecked');
|
||||
expect(userName).toEqual('Legion');
|
||||
expect(email).toEqual('legion@marvel.com');
|
||||
|
||||
it(`should confirm the penultimate log shows the updated active and no modifications on client name`, async() => {
|
||||
let activePreviousValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.activePreviousValue, 'innerText');
|
||||
let activeCurrentValue = await page
|
||||
.waitToGetProperty(selectors.clientLog.activeCurrentValue, 'innerText');
|
||||
|
||||
expect(activePreviousValue).toEqual('✓');
|
||||
expect(activeCurrentValue).toEqual('✗');
|
||||
expect(logName).toEqual('Legion');
|
||||
expect(logActive).toEqual('✗');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,20 +4,69 @@ import getBrowser from '../../helpers/puppeteer';
|
|||
describe('Item fixed prices path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
let httpRequest;
|
||||
|
||||
beforeAll(async() => {
|
||||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
await page.loginAndModule('buyer', 'item');
|
||||
await page.accessToSection('item.fixedPrice');
|
||||
page.on('request', req => {
|
||||
if (req.url().includes(`FixedPrices/filter`))
|
||||
httpRequest = req.url();
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should filter using all the fields', async() => {
|
||||
await page.write(selectors.itemFixedPrice.generalSearchFilter, 'item');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
expect(httpRequest).toContain('search=item');
|
||||
|
||||
await page.click(selectors.itemFixedPrice.chip);
|
||||
await page.click(selectors.itemFixedPrice.reignFilter);
|
||||
|
||||
expect(httpRequest).toContain('categoryFk');
|
||||
|
||||
await page.autocompleteSearch(selectors.itemFixedPrice.typeFilter, 'Alstroemeria');
|
||||
|
||||
expect(httpRequest).toContain('typeFk');
|
||||
|
||||
await page.click(selectors.itemFixedPrice.chip);
|
||||
await page.autocompleteSearch(selectors.itemFixedPrice.buyerFilter, 'buyerNick');
|
||||
|
||||
expect(httpRequest).toContain('buyerFk');
|
||||
|
||||
await page.click(selectors.itemFixedPrice.chip);
|
||||
await page.autocompleteSearch(selectors.itemFixedPrice.warehouseFilter, 'Algemesi');
|
||||
|
||||
expect(httpRequest).toContain('warehouseFk');
|
||||
|
||||
await page.click(selectors.itemFixedPrice.chip);
|
||||
await page.click(selectors.itemFixedPrice.mineFilter);
|
||||
|
||||
expect(httpRequest).toContain('mine=true');
|
||||
|
||||
await page.click(selectors.itemFixedPrice.chip);
|
||||
await page.click(selectors.itemFixedPrice.hasMinPriceFilter);
|
||||
|
||||
expect(httpRequest).toContain('hasMinPrice=true');
|
||||
|
||||
await page.click(selectors.itemFixedPrice.chip);
|
||||
await page.click(selectors.itemFixedPrice.addTag);
|
||||
await page.autocompleteSearch(selectors.itemFixedPrice.tagFilter, 'Color');
|
||||
await page.autocompleteSearch(selectors.itemFixedPrice.tagValueFilter, 'Brown');
|
||||
|
||||
expect(httpRequest).toContain('tags');
|
||||
|
||||
await page.click(selectors.itemFixedPrice.chip);
|
||||
});
|
||||
|
||||
it('should click on the add new fixed price button', async() => {
|
||||
await page.waitToClick(selectors.itemFixedPrice.removeWarehouseFilter);
|
||||
await page.waitForSpinnerLoad();
|
||||
await page.waitToClick(selectors.itemFixedPrice.add);
|
||||
await page.waitForSelector(selectors.itemFixedPrice.fourthFixedPrice);
|
||||
});
|
||||
|
@ -36,10 +85,7 @@ describe('Item fixed prices path', () => {
|
|||
});
|
||||
|
||||
it('should reload the section and check the created price has the expected ID', async() => {
|
||||
await page.accessToSection('item.index');
|
||||
await page.accessToSection('item.fixedPrice');
|
||||
await page.waitToClick(selectors.itemFixedPrice.removeWarehouseFilter);
|
||||
await page.waitForSpinnerLoad();
|
||||
await page.goto(`http://localhost:5000/#!/item/fixed-price`);
|
||||
|
||||
const result = await page.waitToGetProperty(selectors.itemFixedPrice.fourthItemID, 'value');
|
||||
|
||||
|
|
|
@ -246,6 +246,7 @@ describe('Ticket Edit sale path', () => {
|
|||
it('should select the third sale and create a claim of it', async() => {
|
||||
await page.accessToSearchResult('16');
|
||||
await page.accessToSection('ticket.card.sale');
|
||||
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
|
||||
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
|
||||
await page.waitToClick(selectors.ticketSales.moreMenu);
|
||||
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
|
||||
|
|
|
@ -126,10 +126,11 @@ describe('Ticket Future path', () => {
|
|||
});
|
||||
|
||||
it('should check the three last tickets and move to the future', async() => {
|
||||
await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4);
|
||||
await page.waitToClick(selectors.ticketFuture.multiCheck);
|
||||
await page.waitToClick(selectors.ticketFuture.firstCheck);
|
||||
await page.waitToClick(selectors.ticketFuture.moveButton);
|
||||
await page.waitToClick(selectors.ticketFuture.acceptButton);
|
||||
await page.waitToClick(selectors.globalItems.acceptButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Tickets moved successfully!');
|
||||
|
|
|
@ -35,7 +35,7 @@ describe('InvoiceIn serial path', () => {
|
|||
});
|
||||
|
||||
it('should go to index and check if the search-panel has the correct params', async() => {
|
||||
await page.click(selectors.invoiceInSerial.goToIndex);
|
||||
await page.waitToClick(selectors.invoiceInSerial.goToIndex);
|
||||
const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams);
|
||||
const serial = await params[0].getProperty('title');
|
||||
const isBooked = await params[1].getProperty('title');
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('InvoiceIn negative bases path', () => {
|
||||
describe('InvoiceOut negative bases path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
const httpRequests = [];
|
||||
|
@ -9,11 +9,11 @@ describe('InvoiceIn negative bases path', () => {
|
|||
browser = await getBrowser();
|
||||
page = browser.page;
|
||||
page.on('request', req => {
|
||||
if (req.url().includes(`InvoiceIns/negativeBases`))
|
||||
if (req.url().includes(`InvoiceOuts/negativeBases`))
|
||||
httpRequests.push(req.url());
|
||||
});
|
||||
await page.loginAndModule('administrative', 'invoiceIn');
|
||||
await page.accessToSection('invoiceIn.negative-bases');
|
||||
await page.loginAndModule('administrative', 'invoiceOut');
|
||||
await page.accessToSection('invoiceOut.negative-bases');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
|
@ -1,7 +1,21 @@
|
|||
import selectors from '../../helpers/selectors.js';
|
||||
import getBrowser from '../../helpers/puppeteer';
|
||||
|
||||
describe('Supplier fiscal data path', () => {
|
||||
const $ = {
|
||||
saveButton: 'vn-supplier-fiscal-data button[type="submit"]',
|
||||
};
|
||||
const $inputs = {
|
||||
province: 'vn-supplier-fiscal-data [name="province"]',
|
||||
country: 'vn-supplier-fiscal-data [name="country"]',
|
||||
postcode: 'vn-supplier-fiscal-data [name="postcode"]',
|
||||
city: 'vn-supplier-fiscal-data [name="city"]',
|
||||
socialName: 'vn-supplier-fiscal-data [name="socialName"]',
|
||||
taxNumber: 'vn-supplier-fiscal-data [name="taxNumber"]',
|
||||
account: 'vn-supplier-fiscal-data [name="account"]',
|
||||
sageWithholding: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageWithholdingFk"]',
|
||||
sageTaxType: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageTaxTypeFk"]'
|
||||
};
|
||||
|
||||
fdescribe('Supplier fiscal data path', () => {
|
||||
let browser;
|
||||
let page;
|
||||
|
||||
|
@ -10,102 +24,44 @@ describe('Supplier fiscal data path', () => {
|
|||
page = browser.page;
|
||||
await page.loginAndModule('administrative', 'supplier');
|
||||
await page.accessToSearchResult('2');
|
||||
await page.accessToSection('supplier.card.fiscalData');
|
||||
});
|
||||
|
||||
afterAll(async() => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should attempt to edit the fiscal data but fail as the tax number is invalid', async() => {
|
||||
await page.clearInput(selectors.supplierFiscalData.city);
|
||||
await page.clearInput(selectors.supplierFiscalData.province);
|
||||
await page.clearInput(selectors.supplierFiscalData.country);
|
||||
await page.clearInput(selectors.supplierFiscalData.postCode);
|
||||
await page.write(selectors.supplierFiscalData.city, 'Valencia');
|
||||
await page.waitForTimeout(1000); // must repeat this action twice or fails. also #2699 may be a cool solution to this.
|
||||
await page.clearInput(selectors.supplierFiscalData.city);
|
||||
await page.write(selectors.supplierFiscalData.city, 'Valencia');
|
||||
await page.clearInput(selectors.supplierFiscalData.socialName);
|
||||
await page.write(selectors.supplierFiscalData.socialName, 'Farmer King SL');
|
||||
await page.clearInput(selectors.supplierFiscalData.taxNumber);
|
||||
await page.write(selectors.supplierFiscalData.taxNumber, 'Wrong tax number');
|
||||
await page.clearInput(selectors.supplierFiscalData.account);
|
||||
await page.write(selectors.supplierFiscalData.account, '0123456789');
|
||||
await page.autocompleteSearch(selectors.supplierFiscalData.sageWihholding, 'retencion estimacion objetiva');
|
||||
await page.autocompleteSearch(selectors.supplierFiscalData.sageTaxType, 'operaciones no sujetas');
|
||||
it('should attempt to edit the fiscal data and check data is saved', async() => {
|
||||
await page.accessToSection('supplier.card.fiscalData');
|
||||
await page.clearInput($inputs.province);
|
||||
await page.clearInput($inputs.country);
|
||||
await page.clearInput($inputs.postcode);
|
||||
await page.overwrite($inputs.city, 'Valencia');
|
||||
await page.overwrite($inputs.socialName, 'Farmer King SL');
|
||||
await page.overwrite($inputs.taxNumber, 'Wrong tax number');
|
||||
await page.overwrite($inputs.account, '0123456789');
|
||||
await page.autocompleteSearch($inputs.sageWithholding, 'retencion estimacion objetiva');
|
||||
await page.autocompleteSearch($inputs.sageTaxType, 'operaciones no sujetas');
|
||||
await page.click($.saveButton);
|
||||
const errorMessage = await page.waitForSnackbar();
|
||||
await page.overwrite($inputs.taxNumber, '12345678Z');
|
||||
await page.click($.saveButton);
|
||||
const successMessage = await page.waitForSnackbar();
|
||||
|
||||
await page.waitToClick(selectors.supplierFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Invalid Tax number');
|
||||
});
|
||||
|
||||
it('should save the changes as the tax number is valid this time', async() => {
|
||||
await page.clearInput(selectors.supplierFiscalData.taxNumber);
|
||||
await page.write(selectors.supplierFiscalData.taxNumber, '12345678Z');
|
||||
|
||||
await page.waitToClick(selectors.supplierFiscalData.saveButton);
|
||||
const message = await page.waitForSnackbar();
|
||||
|
||||
expect(message.text).toContain('Data saved!');
|
||||
});
|
||||
|
||||
it('should reload the section', async() => {
|
||||
await page.reloadSection('supplier.card.fiscalData');
|
||||
});
|
||||
const values = await page.getValues($inputs);
|
||||
|
||||
it('should check the socialName was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.socialName, 'value');
|
||||
|
||||
expect(result).toEqual('Farmer King SL');
|
||||
});
|
||||
|
||||
it('should check the taxNumber was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.taxNumber, 'value');
|
||||
|
||||
expect(result).toEqual('12345678Z');
|
||||
});
|
||||
|
||||
it('should check the account was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.account, 'value');
|
||||
|
||||
expect(result).toEqual('0123456789');
|
||||
});
|
||||
|
||||
it('should check the sageWihholding was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.sageWihholding, 'value');
|
||||
|
||||
expect(result).toEqual('RETENCION ESTIMACION OBJETIVA');
|
||||
});
|
||||
|
||||
it('should check the sageTaxType was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.sageTaxType, 'value');
|
||||
|
||||
expect(result).toEqual('Operaciones no sujetas');
|
||||
});
|
||||
|
||||
it('should check the postCode was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.postCode, 'value');
|
||||
|
||||
expect(result).toEqual('46000');
|
||||
});
|
||||
|
||||
it('should check the city was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.city, 'value');
|
||||
|
||||
expect(result).toEqual('Valencia');
|
||||
});
|
||||
|
||||
it('should check the province was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.province, 'value');
|
||||
|
||||
expect(result).toEqual('Province one (España)');
|
||||
});
|
||||
|
||||
it('should check the country was edited', async() => {
|
||||
const result = await page.waitToGetProperty(selectors.supplierFiscalData.country, 'value');
|
||||
|
||||
expect(result).toEqual('España');
|
||||
expect(errorMessage.text).toContain('Invalid Tax number');
|
||||
expect(successMessage.type).toBe('success');
|
||||
expect(values).toEqual({
|
||||
province: 'Province one (España)',
|
||||
country: 'España',
|
||||
postcode: '46000',
|
||||
city: 'Valencia',
|
||||
socialName: 'Farmer King SL',
|
||||
taxNumber: '12345678Z',
|
||||
account: '0123456789',
|
||||
sageWithholding: 'RETENCION ESTIMACION OBJETIVA',
|
||||
sageTaxType: 'Operaciones no sujetas'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('Account ACL path', () => {
|
|||
|
||||
it('should create new acl', async() => {
|
||||
await page.autocompleteSearch(selectors.accountAcl.role, 'sysadmin');
|
||||
await page.autocompleteSearch(selectors.accountAcl.model, 'UserAccount');
|
||||
await page.autocompleteSearch(selectors.accountAcl.model, 'Account');
|
||||
await page.autocompleteSearch(selectors.accountAcl.accessType, '*');
|
||||
await page.autocompleteSearch(selectors.accountAcl.permission, 'ALLOW');
|
||||
await page.waitToClick(selectors.accountAcl.save);
|
||||
|
|
|
@ -166,7 +166,7 @@ export default class Field extends FormInput {
|
|||
if (event.defaultPrevented) return;
|
||||
event.preventDefault();
|
||||
this.field = null;
|
||||
this.input.dispatchEvent(new Event('change'));
|
||||
this.element.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
buildInput(type) {
|
||||
|
|
|
@ -32,6 +32,7 @@ import './float-button';
|
|||
import './icon-menu';
|
||||
import './icon-button';
|
||||
import './input-number';
|
||||
import './json-value';
|
||||
import './label-value';
|
||||
import './range';
|
||||
import './input-time';
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import ngModule from '../../module';
|
||||
import Component from 'core/lib/component';
|
||||
import './style.scss';
|
||||
|
||||
const maxStrLen = 50;
|
||||
|
||||
/**
|
||||
* Displays pretty JSON value.
|
||||
*
|
||||
* @property {*} value The value
|
||||
*/
|
||||
export default class Controller extends Component {
|
||||
get value() {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(value) {
|
||||
const wasEmpty = this._value === undefined;
|
||||
this._value = value;
|
||||
|
||||
let text;
|
||||
let cssClass;
|
||||
const type = typeof value;
|
||||
|
||||
if (value == null) {
|
||||
text = '∅';
|
||||
cssClass = 'null';
|
||||
} else {
|
||||
cssClass = type;
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
text = value ? '✓' : '✗';
|
||||
cssClass = value ? 'true' : 'false';
|
||||
break;
|
||||
case 'string':
|
||||
text = value.length <= maxStrLen
|
||||
? value
|
||||
: value.substring(0, maxStrLen) + '...';
|
||||
break;
|
||||
case 'object':
|
||||
if (value instanceof Date) {
|
||||
const hasZeroTime =
|
||||
value.getHours() === 0 &&
|
||||
value.getMinutes() === 0 &&
|
||||
value.getSeconds() === 0;
|
||||
const format = hasZeroTime ? 'dd/MM/yyyy' : 'dd/MM/yyyy HH:mm:ss';
|
||||
text = this.$filter('date')(value, format);
|
||||
} else
|
||||
text = value;
|
||||
break;
|
||||
default:
|
||||
text = value;
|
||||
}
|
||||
}
|
||||
|
||||
const el = this.element;
|
||||
el.textContent = text;
|
||||
el.title = type == 'string' && value.length > maxStrLen ? value : '';
|
||||
|
||||
cssClass = `json-${cssClass}`;
|
||||
if (wasEmpty)
|
||||
el.classList.add(cssClass);
|
||||
else
|
||||
el.classList.replace(this.className, cssClass);
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnJsonValue', {
|
||||
controller: Controller,
|
||||
bindings: {
|
||||
value: '<?'
|
||||
}
|
||||
});
|
|
@ -0,0 +1,79 @@
|
|||
import './index';
|
||||
|
||||
describe('Salix Component vnLog', () => {
|
||||
let controller;
|
||||
let $scope;
|
||||
let $element;
|
||||
let el;
|
||||
|
||||
beforeEach(ngModule('vnCore'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope) => {
|
||||
$scope = $rootScope.$new();
|
||||
$element = angular.element('<json-value></json-value>');
|
||||
controller = $componentController('vnJsonValue', {$element, $scope});
|
||||
el = controller.element;
|
||||
}));
|
||||
|
||||
describe('set value()', () => {
|
||||
it('should display null symbol when value is null equivalent', () => {
|
||||
controller.value = null;
|
||||
|
||||
expect(el.textContent).toEqual('∅');
|
||||
expect(el.className).toContain('json-null');
|
||||
});
|
||||
|
||||
it('should display ballot when value is false', () => {
|
||||
controller.value = false;
|
||||
|
||||
expect(el.textContent).toEqual('✗');
|
||||
expect(el.className).toContain('json-false');
|
||||
});
|
||||
|
||||
it('should display check when value is true', () => {
|
||||
controller.value = true;
|
||||
|
||||
expect(el.textContent).toEqual('✓');
|
||||
expect(el.className).toContain('json-true');
|
||||
});
|
||||
|
||||
it('should display string when value is an string', () => {
|
||||
controller.value = 'Foo';
|
||||
|
||||
expect(el.textContent).toEqual('Foo');
|
||||
expect(el.className).toContain('json-string');
|
||||
});
|
||||
|
||||
it('should display only date when value is date with time set to zero', () => {
|
||||
const date = Date.vnNew();
|
||||
date.setHours(0, 0, 0, 0);
|
||||
controller.value = date;
|
||||
|
||||
expect(el.textContent).toEqual('01/01/2001');
|
||||
expect(el.className).toContain('json-object');
|
||||
});
|
||||
|
||||
it('should display full date without time when value is date with time', () => {
|
||||
const date = Date.vnNew();
|
||||
date.setHours(15, 45);
|
||||
controller.value = date;
|
||||
|
||||
expect(el.textContent).toEqual('01/01/2001 15:45:00');
|
||||
expect(el.className).toContain('json-object');
|
||||
});
|
||||
|
||||
it('should display object when value is an object', () => {
|
||||
controller.value = {foo: 'bar'};
|
||||
|
||||
expect(el.textContent).toEqual('[object Object]');
|
||||
expect(el.className).toContain('json-object');
|
||||
});
|
||||
|
||||
it('should display number when value is a number', () => {
|
||||
controller.value = 2050;
|
||||
|
||||
expect(el.textContent).toEqual('2050');
|
||||
expect(el.className).toContain('json-number');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
vn-json-value {
|
||||
display: inline;
|
||||
|
||||
&.json-string {
|
||||
color: #d172cc;
|
||||
}
|
||||
&.json-object {
|
||||
color: #d1a572;
|
||||
}
|
||||
&.json-number {
|
||||
color: #85d0ff;
|
||||
}
|
||||
&.json-true {
|
||||
color: #7dc489;
|
||||
}
|
||||
&.json-false {
|
||||
color: #c74949;
|
||||
}
|
||||
&.json-null {
|
||||
color: #cd7c7c;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
|
@ -41,10 +41,15 @@ vn-table {
|
|||
display: table-row;
|
||||
height: 48px;
|
||||
}
|
||||
vn-thead, .vn-thead,
|
||||
vn-tbody, .vn-tbody,
|
||||
vn-tfoot, .vn-tfoot,
|
||||
thead, tbody, tfoot {
|
||||
& > thead,
|
||||
& > tbody,
|
||||
& > tfoot,
|
||||
& > vn-thead,
|
||||
& > vn-tbody,
|
||||
& > vn-tfoot,
|
||||
& > .vn-thead,
|
||||
& > .vn-tbody,
|
||||
& > .vn-tfoot {
|
||||
& > * {
|
||||
display: table-row;
|
||||
|
||||
|
@ -111,14 +116,14 @@ vn-table {
|
|||
color: inherit;
|
||||
}
|
||||
}
|
||||
a.vn-tbody {
|
||||
& > a.vn-tbody {
|
||||
&.clickable {
|
||||
@extend %clickable;
|
||||
}
|
||||
}
|
||||
vn-tbody > *,
|
||||
.vn-tbody > *,
|
||||
tbody > * {
|
||||
& > vn-tbody > *,
|
||||
& > .vn-tbody > *,
|
||||
& > tbody > * {
|
||||
border-bottom: $border-thin;
|
||||
|
||||
&:last-child {
|
||||
|
|
|
@ -7,7 +7,7 @@ describe('Directive acl', () => {
|
|||
beforeEach(ngModule('vnCore'));
|
||||
|
||||
beforeEach(inject(($httpBackend, aclService) => {
|
||||
$httpBackend.whenGET('Accounts/acl')
|
||||
$httpBackend.whenGET('VnUsers/acl')
|
||||
.respond({
|
||||
user: {id: 1, name: 'myUser'},
|
||||
roles: [
|
||||
|
|
|
@ -4,7 +4,7 @@ describe('Service acl', () => {
|
|||
beforeEach(ngModule('vnCore'));
|
||||
|
||||
beforeEach(inject((_aclService_, $httpBackend) => {
|
||||
$httpBackend.when('GET', `Accounts/acl`).respond({
|
||||
$httpBackend.when('GET', `VnUsers/acl`).respond({
|
||||
roles: [
|
||||
{role: {name: 'foo'}},
|
||||
{role: {name: 'bar'}},
|
||||
|
|
|
@ -11,7 +11,7 @@ class AclService {
|
|||
}
|
||||
|
||||
load() {
|
||||
return this.$http.get('Accounts/acl').then(res => {
|
||||
return this.$http.get('VnUsers/acl').then(res => {
|
||||
this.user = res.data.user;
|
||||
this.roles = {};
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ export default class Auth {
|
|||
password: password || undefined
|
||||
};
|
||||
|
||||
return this.$http.post('Accounts/login', params).then(
|
||||
return this.$http.post('VnUsers/signIn', params).then(
|
||||
json => this.onLoginOk(json, remember));
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ export default class Auth {
|
|||
}
|
||||
|
||||
logout() {
|
||||
let promise = this.$http.post('Accounts/logout', null, {
|
||||
let promise = this.$http.post('VnUsers/logout', null, {
|
||||
headers: {Authorization: this.vnToken.token}
|
||||
}).catch(() => {});
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
$font-size: 11pt;
|
||||
$menu-width: 256px;
|
||||
$right-menu-width: 318px;
|
||||
$topbar-height: 56px;
|
||||
$mobile-width: 800px;
|
||||
$float-spacing: 20px;
|
||||
|
|
|
@ -13,7 +13,7 @@ export class Layout extends Component {
|
|||
}
|
||||
|
||||
getUserData() {
|
||||
this.$http.get('Accounts/getCurrentUserData').then(json => {
|
||||
this.$http.get('VnUsers/getCurrentUserData').then(json => {
|
||||
this.$.$root.user = json.data;
|
||||
window.localStorage.currentUserWorkerId = json.data.id;
|
||||
});
|
||||
|
|
|
@ -15,7 +15,7 @@ describe('Component vnLayout', () => {
|
|||
|
||||
describe('getUserData()', () => {
|
||||
it(`should set the user name property in the controller`, () => {
|
||||
$httpBackend.expect('GET', `Accounts/getCurrentUserData`).respond({name: 'batman'});
|
||||
$httpBackend.expect('GET', `VnUsers/getCurrentUserData`).respond({name: 'batman'});
|
||||
controller.getUserData();
|
||||
$httpBackend.flush();
|
||||
|
||||
|
|
|
@ -88,13 +88,13 @@ vn-layout {
|
|||
}
|
||||
&.right-menu {
|
||||
& > vn-topbar > .end {
|
||||
width: 80px + $right-menu-width;
|
||||
width: 80px + $menu-width;
|
||||
}
|
||||
& > .main-view {
|
||||
padding-right: $right-menu-width;
|
||||
padding-right: $menu-width;
|
||||
}
|
||||
[fixed-bottom-right] {
|
||||
right: $right-menu-width;
|
||||
right: $menu-width;
|
||||
}
|
||||
}
|
||||
& > .main-view {
|
||||
|
|
|
@ -3,71 +3,211 @@
|
|||
url="{{$ctrl.url}}"
|
||||
filter="$ctrl.filter"
|
||||
link="{originFk: $ctrl.originId}"
|
||||
where="{changedModel: $ctrl.changedModel,
|
||||
changedModelId: $ctrl.changedModelId}"
|
||||
where="{changedModel: $ctrl.changedModel, changedModelId: $ctrl.changedModelId}"
|
||||
data="$ctrl.logs"
|
||||
limit="20"
|
||||
order="creationDate DESC, id DESC"
|
||||
limit="20">
|
||||
</vn-crud-model>
|
||||
<vn-crud-model
|
||||
url="{{$ctrl.url}}/{{$ctrl.originId}}/models"
|
||||
data="models"
|
||||
order="changedModel"
|
||||
auto-load="true">
|
||||
</vn-crud-model>
|
||||
<vn-data-viewer model="model" class="vn-w-xl">
|
||||
<vn-card>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th field="creationDate">Date</vn-th>
|
||||
<vn-th field="userFk" shrink>User</vn-th>
|
||||
<vn-th field="changedModel" ng-if="$ctrl.showModelName" shrink>Model</vn-th>
|
||||
<vn-th field="action" shrink>Action</vn-th>
|
||||
<vn-th field="changedModelValue" ng-if="$ctrl.showModelName">Name</vn-th>
|
||||
<vn-th expand>Changes</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="log in $ctrl.logs">
|
||||
<vn-td shrink-datetime>
|
||||
{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}
|
||||
</vn-td>
|
||||
<vn-td>
|
||||
<span ng-class="{'link': log.user.worker.id, 'value': !log.user.worker.id}"
|
||||
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)"
|
||||
translate>{{::log.user.name || 'System' | translate}}
|
||||
<vn-data-viewer model="model" class="vn-w-md vn-px-sm">
|
||||
<div class="change vn-mb-sm" ng-repeat="log in $ctrl.logs">
|
||||
<div class="user-wrapper">
|
||||
<div
|
||||
class="user vn-mt-xs"
|
||||
title="{{::log.user.nickname || 'System'}}">
|
||||
<img
|
||||
ng-if="::log.user.worker"
|
||||
src="/api/Images/user/160x160/{{::log.user.worker.id}}/download?access_token={{::$ctrl.vnToken.token}}"
|
||||
ng-click="$ctrl.showWorkerDescriptor($event, log.user.worker.id)">
|
||||
</img>
|
||||
<div
|
||||
ng-if="::!log.user.worker"
|
||||
ng-class="::{system: !log.user}"
|
||||
ng-style="::$ctrl.userBgColor(log.user)"
|
||||
class="user-icon">
|
||||
{{::log.user ? log.user.name.charAt(0).toUpperCase() : 'S'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow bg-panel"></div>
|
||||
<div class="line"></div>
|
||||
</div>
|
||||
<vn-card class="detail vn-pa-sm">
|
||||
<div class="header vn-mb-sm">
|
||||
<div
|
||||
class="date text-secondary text-caption"
|
||||
title="{{::log.creationDate | date:'dd/MM/yyyy HH:mm'}}">
|
||||
{{::$ctrl.relativeDate(log.creationDate)}}
|
||||
</div>
|
||||
<span class="chip {{::$ctrl.actionsClass[log.action]}}" translate>
|
||||
{{::$ctrl.actionsText[log.action]}}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="model vn-mb-sm"
|
||||
title="{{::log.changedModelValue}}"
|
||||
ng-if="::log.changedModel || log.changedModelValue">
|
||||
<span class="model-name"
|
||||
ng-if="::$ctrl.showModelName"
|
||||
title="{{::log.changedModel}}">
|
||||
{{::log.changedModelI18n}}
|
||||
</span>
|
||||
<span class="model-id"
|
||||
ng-if="::log.changedModelId">
|
||||
#{{::log.changedModelId}}
|
||||
</span>
|
||||
<span class="model-value">
|
||||
{{::log.changedModelValue}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="changes {{::log.props.length ? 'props' : 'no-props'}}" vn-id="changes">
|
||||
<vn-icon icon="visibility"
|
||||
class="expand-button"
|
||||
ng-click="$ctrl.toggleAttributes(log, changes, true)">
|
||||
</vn-icon>
|
||||
<vn-icon icon="visibility_off"
|
||||
class="shrink-button"
|
||||
ng-click="$ctrl.toggleAttributes(log, changes, false)">
|
||||
</vn-icon>
|
||||
<div class="changes-wrapper">
|
||||
<span ng-if="::log.props.length"
|
||||
class="attributes">
|
||||
<span ng-if="!log.expand" ng-repeat="prop in ::log.props"
|
||||
class="basic-json">
|
||||
<span class="json-field"
|
||||
title="{{::prop.name}}">
|
||||
{{::prop.nameI18n}}:
|
||||
</span>
|
||||
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value><span ng-if="::!$last">,</span>
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td ng-if="$ctrl.showModelName">
|
||||
{{::log.changedModel}}
|
||||
</vn-td>
|
||||
<vn-td shrink translate>
|
||||
{{::$ctrl.actionsText[log.action]}}
|
||||
</vn-td>
|
||||
<vn-td ng-if="$ctrl.showModelName">
|
||||
{{::log.changedModelValue}}
|
||||
</vn-td>
|
||||
<vn-td expand>
|
||||
<table class="attributes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate class="field">Field</th>
|
||||
<th translate>Before</th>
|
||||
<th translate>After</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="prop in ::log.props">
|
||||
<td class="field">{{prop.name}}</td>
|
||||
<td class="before">{{prop.old}}</td>
|
||||
<td class="after">{{prop.new}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-if="log.description != null">
|
||||
{{::log.description}}
|
||||
<div ng-if="log.expand"
|
||||
class="expanded-json">
|
||||
<div ng-repeat="prop in ::log.props">
|
||||
<span class="json-field"
|
||||
title="{{::prop.name}}">
|
||||
{{::prop.nameI18n}}:
|
||||
</span>
|
||||
<vn-json-value value="::$ctrl.mainVal(prop, log.action)"></vn-json-value>
|
||||
<span ng-if="::log.action == 'update'">
|
||||
← <vn-json-value value="::prop.old"></vn-json-value>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
<vn-pagination model="model"></vn-pagination>
|
||||
</vn-card>
|
||||
</span>
|
||||
<span ng-if="::!log.props.length"
|
||||
class="description">
|
||||
{{::log.description}}
|
||||
</span>
|
||||
<span ng-if="::!log.description && !log.props.length"
|
||||
class="no-changes"
|
||||
translate>
|
||||
No changes
|
||||
</span>
|
||||
</div>
|
||||
</vn-card>
|
||||
</div>
|
||||
</div>
|
||||
</vn-data-viewer>
|
||||
<vn-side-menu side="right">
|
||||
<form vn-vertical
|
||||
ng-model-options="{updateOn: 'change blur'}"
|
||||
class="vn-pa-md filter">
|
||||
<h6
|
||||
class="text-secondary vn-mb-md"
|
||||
style="font-weight: normal;"
|
||||
translate>
|
||||
Filter
|
||||
</h6>
|
||||
<vn-textfield
|
||||
label="Name"
|
||||
ng-model="filter.changedModelValue">
|
||||
</vn-textfield>
|
||||
<vn-vertical>
|
||||
<vn-radio
|
||||
label="All"
|
||||
val="all"
|
||||
ng-model="filter.who">
|
||||
</vn-radio>
|
||||
<vn-radio
|
||||
label="User"
|
||||
val="user"
|
||||
ng-model="filter.who">
|
||||
</vn-radio>
|
||||
<vn-radio
|
||||
label="System"
|
||||
val="system"
|
||||
ng-model="filter.who">
|
||||
</vn-radio>
|
||||
</div>
|
||||
<vn-autocomplete
|
||||
ng-show="filter.who != 'system'"
|
||||
label="User"
|
||||
ng-model="filter.userFk"
|
||||
value-field="id"
|
||||
show-field="nickname"
|
||||
fields="['id', 'name', 'nickname']"
|
||||
search-function="$ctrl.searchUser($search)"
|
||||
url="{{$ctrl.url}}/{{$ctrl.originId}}/editors"
|
||||
order="nickname">
|
||||
<tpl-item>
|
||||
<div>{{nickname}}</div>
|
||||
<div class="text-secondary text-caption">{{name}}</div>
|
||||
</tpl-item>
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
label="Model"
|
||||
ng-model="filter.changedModel"
|
||||
value-field="changedModel"
|
||||
show-field="changedModel"
|
||||
data="models">
|
||||
</vn-autocomplete>
|
||||
<vn-textfield
|
||||
label="Id"
|
||||
ng-model="filter.changedModelId">
|
||||
</vn-textfield>
|
||||
<vn-vertical>
|
||||
<vn-check
|
||||
label="Creates"
|
||||
ng-model="filter.actions.insert">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Updates"
|
||||
ng-model="filter.actions.update">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Deletes"
|
||||
ng-model="filter.actions.delete">
|
||||
</vn-check>
|
||||
<vn-check
|
||||
label="Views"
|
||||
ng-model="filter.actions.select">
|
||||
</vn-check>
|
||||
</div>
|
||||
<vn-date-picker
|
||||
label="Date"
|
||||
ng-model="filter.from">
|
||||
</vn-date-picker>
|
||||
<vn-date-picker
|
||||
label="To"
|
||||
ng-model="filter.to">
|
||||
</vn-date-picker>
|
||||
<vn-button-bar vn-vertical class="vn-mt-sm">
|
||||
<vn-button
|
||||
label="Filter"
|
||||
ng-click="$ctrl.applyFilter(filter)">
|
||||
</vn-button>
|
||||
<vn-button
|
||||
label="Reset"
|
||||
class="flat"
|
||||
ng-click="$ctrl.resetFilter()"
|
||||
ng-if="model.userFilter">
|
||||
</vn-button>
|
||||
</vn-button-bar>
|
||||
</form>
|
||||
</vn-side-menu>
|
||||
<vn-worker-descriptor-popover vn-id="workerDescriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
|
|
|
@ -13,11 +13,17 @@ export default class Controller extends Section {
|
|||
delete: 'Deletes',
|
||||
select: 'Views'
|
||||
};
|
||||
this.actionsClass = {
|
||||
insert: 'success',
|
||||
update: 'warning',
|
||||
delete: 'alert',
|
||||
select: 'notice'
|
||||
};
|
||||
this.filter = {
|
||||
include: [{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
fields: ['nickname'],
|
||||
include: {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
|
@ -27,6 +33,20 @@ export default class Controller extends Section {
|
|||
},
|
||||
}],
|
||||
};
|
||||
|
||||
this.dateFilter = this.$filter('date');
|
||||
this.lang = this.$translate.use();
|
||||
this.today = Date.vnNew();
|
||||
this.today.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
this.resetFilter();
|
||||
this.$.$watch(
|
||||
() => this.$.filter,
|
||||
() => this.applyFilter(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
get logs() {
|
||||
|
@ -42,6 +62,7 @@ export default class Controller extends Section {
|
|||
const oldValues = log.oldInstance || empty;
|
||||
const newValues = log.newInstance || empty;
|
||||
const locale = validations[log.changedModel]?.locale || empty;
|
||||
log.changedModelI18n = locale.name || log.changedModel;
|
||||
|
||||
let props = Object.keys(oldValues).concat(Object.keys(newValues));
|
||||
props = [...new Set(props)];
|
||||
|
@ -49,9 +70,10 @@ export default class Controller extends Section {
|
|||
log.props = [];
|
||||
for (const prop of props) {
|
||||
log.props.push({
|
||||
name: locale[prop] || prop,
|
||||
old: this.formatValue(oldValues[prop]),
|
||||
new: this.formatValue(newValues[prop])
|
||||
name: prop,
|
||||
nameI18n: locale.columns?.[prop] || prop,
|
||||
old: this.castJsonValue(oldValues[prop]),
|
||||
new: this.castJsonValue(newValues[prop])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -61,37 +83,124 @@ export default class Controller extends Section {
|
|||
return !(this.changedModel && this.changedModelId);
|
||||
}
|
||||
|
||||
formatValue(value) {
|
||||
let type = typeof value;
|
||||
castJsonValue(value) {
|
||||
return typeof value === 'string' && validDate.test(value)
|
||||
? new Date(value)
|
||||
: value;
|
||||
}
|
||||
|
||||
if (type === 'string' && validDate.test(value)) {
|
||||
value = new Date(value);
|
||||
type = typeof value;
|
||||
}
|
||||
mainVal(prop, action) {
|
||||
return action == 'delete' ? prop.old : prop.new;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
return value ? '✓' : '✗';
|
||||
case 'object':
|
||||
if (value instanceof Date) {
|
||||
const hasZeroTime =
|
||||
value.getHours() === 0 &&
|
||||
value.getMinutes() === 0 &&
|
||||
value.getSeconds() === 0;
|
||||
const format = hasZeroTime ? 'dd/MM/yyyy' : 'dd/MM/yyyy HH:mm:ss';
|
||||
return this.$filter('date')(value, format);
|
||||
}
|
||||
else
|
||||
return value;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
toggleAttributes(log, changesEl, force) {
|
||||
log.expand = force;
|
||||
changesEl.classList.toggle('expanded', force);
|
||||
}
|
||||
|
||||
relativeDate(dateVal) {
|
||||
if (dateVal == null) return '';
|
||||
const date = new Date(dateVal);
|
||||
const dateZeroTime = new Date(dateVal);
|
||||
dateZeroTime.setHours(0, 0, 0, 0);
|
||||
const diff = Math.trunc((this.today.getTime() - dateZeroTime.getTime()) / (1000 * 3600 * 24));
|
||||
|
||||
let format;
|
||||
if (diff == 0)
|
||||
format = `'${this.$t('today')}'`;
|
||||
else if (diff == 1)
|
||||
format = `'${this.$t('yesterday')}'`;
|
||||
else if (diff > 1 && diff < 7)
|
||||
format = `'${date.toLocaleDateString(this.lang, {weekday: 'short'})}'`;
|
||||
else if (this.today.getFullYear() == date.getFullYear())
|
||||
format = `d '${date.toLocaleDateString(this.lang, {month: 'short'})}'`;
|
||||
else
|
||||
format = `dd/MM/yyyy`;
|
||||
|
||||
return this.dateFilter(date, `${format} HH:mm`);
|
||||
}
|
||||
|
||||
showWorkerDescriptor(event, workerId) {
|
||||
if (!workerId) return;
|
||||
this.$.workerDescriptor.show(event.target, workerId);
|
||||
}
|
||||
|
||||
resetFilter() {
|
||||
this.$.filter = {who: 'all'};
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
const filter = this.$.filter;
|
||||
|
||||
function getParam(prop, value) {
|
||||
if (value == null || value == '') return null;
|
||||
switch (prop) {
|
||||
case 'changedModelValue':
|
||||
return {[prop]: {like: `%${value}%`}};
|
||||
case 'who':
|
||||
switch (value) {
|
||||
case 'all':
|
||||
return null;
|
||||
case 'user':
|
||||
return {userFk: {neq: null}};
|
||||
case 'system':
|
||||
return {userFk: null};
|
||||
}
|
||||
case 'actions':
|
||||
const inq = [];
|
||||
for (const action in value) {
|
||||
if (value[action])
|
||||
inq.push(action);
|
||||
}
|
||||
return inq.length ? {action: {inq}} : null;
|
||||
case 'from':
|
||||
if (filter.to) {
|
||||
return {creationDate: {gte: value}};
|
||||
} else {
|
||||
const to = new Date(value);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
return {creationDate: {between: [value, to]}};
|
||||
}
|
||||
case 'to':
|
||||
const to = new Date(value);
|
||||
to.setHours(23, 59, 59, 999);
|
||||
return {creationDate: {lte: to}};
|
||||
default:
|
||||
return {[prop]: value};
|
||||
}
|
||||
}
|
||||
|
||||
const and = [];
|
||||
for (const prop in filter) {
|
||||
const param = getParam(prop, filter[prop]);
|
||||
if (param) and.push(param);
|
||||
}
|
||||
|
||||
const lbFilter = and.length ? {where: {and}} : null;
|
||||
return this.$.model.applyFilter(lbFilter);
|
||||
}
|
||||
|
||||
searchUser(search) {
|
||||
if (/^[0-9]+$/.test(search)) {
|
||||
return {id: search};
|
||||
} else {
|
||||
return {or: [
|
||||
{name: search},
|
||||
{nickname: {like: `%${search}%`}}
|
||||
]}
|
||||
}
|
||||
}
|
||||
|
||||
userBgColor(user) {
|
||||
if (!user) return;
|
||||
const name = user.name || '';
|
||||
let hash = 0;
|
||||
for (let i = 0; i < name.length; i++)
|
||||
hash += name.charCodeAt(i);
|
||||
return {
|
||||
backgroundColor: '#'+ colors[hash % colors.length]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnLog', {
|
||||
|
@ -105,3 +214,29 @@ ngModule.vnComponent('vnLog', {
|
|||
url: '@'
|
||||
}
|
||||
});
|
||||
|
||||
const colors = [
|
||||
'e2553d', // Coral
|
||||
'FFA07A', // Salmon
|
||||
'FFDAB9', // Peach
|
||||
'a17077', // Pink
|
||||
'bf0e99', // Pink light
|
||||
'52a500', // Green chartreuse
|
||||
'00aeae', // Cian
|
||||
'b754cf', // Purple middle
|
||||
'8a69cd', // Blue lavender
|
||||
'1fa8a1', // Green ocean
|
||||
'DC143C', // Red crimson
|
||||
'5681cf', // Blue steel
|
||||
'FF1493', // Ping intense
|
||||
'00d700', // Green lime
|
||||
'1E90FF', // Blue sky
|
||||
'8B008B', // Purple dark
|
||||
'cc7000', // Orange bright
|
||||
'00b5b8', // Turquoise
|
||||
'8B0000', // Red dark
|
||||
'008080', // Green bluish
|
||||
'2F4F4F', // Gray board
|
||||
'7e7e7e', // Gray
|
||||
'5d5d5d', // Gray dark
|
||||
];
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
import './index';
|
||||
|
||||
describe('Salix Component vnLog', () => {
|
||||
let controller;
|
||||
let $scope;
|
||||
let $element;
|
||||
|
||||
beforeEach(ngModule('salix'));
|
||||
|
||||
beforeEach(inject(($componentController, $rootScope) => {
|
||||
$scope = $rootScope.$new();
|
||||
$element = angular.element('<vn-log></vn-log>');
|
||||
controller = $componentController('vnLog', {$element, $scope});
|
||||
}));
|
||||
|
||||
describe('relativeDate()', () => {
|
||||
let date;
|
||||
|
||||
beforeEach(() => {
|
||||
date = Date.vnNew();
|
||||
});
|
||||
|
||||
it('should return empty string when date is null', () => {
|
||||
const ret = controller.relativeDate(null);
|
||||
|
||||
expect(ret).toEqual('');
|
||||
});
|
||||
|
||||
it('should return empty string when date is undefined', () => {
|
||||
const ret = controller.relativeDate(undefined);
|
||||
|
||||
expect(ret).toEqual('');
|
||||
});
|
||||
|
||||
it('should return today and time when date is today', () => {
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('today 12:00');
|
||||
});
|
||||
|
||||
it('should return yesterday and time when date is yesterday', () => {
|
||||
date.setDate(date.getDate() - 1);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('yesterday 12:00');
|
||||
});
|
||||
|
||||
it('should return abreviated weekday name and time when date is on past week', () => {
|
||||
date.setDate(date.getDate() - 3);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('Fri 12:00');
|
||||
});
|
||||
|
||||
it('should return abreviated month name, day number and time when date is on this year', () => {
|
||||
date.setDate(date.getDate() + 20);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('21 Jan 12:00');
|
||||
});
|
||||
|
||||
it('should return abreviated month name, day number, year and time when date is on different year', () => {
|
||||
date.setDate(date.getDate() - 20);
|
||||
const ret = controller.relativeDate(date);
|
||||
|
||||
expect(ret).toEqual('12/12/2000 12:00');
|
||||
});
|
||||
|
||||
it('should convert to date and return string when date is not a Date class instance', () => {
|
||||
const ret = controller.relativeDate(date.toJSON());
|
||||
|
||||
expect(ret).toEqual('today 12:00');
|
||||
});
|
||||
});
|
||||
|
||||
describe('castJsonValue()', () => {
|
||||
it('should return date when string has valid JSON date format', () => {
|
||||
const now = Date.vnNew();
|
||||
|
||||
const ret = controller.castJsonValue(now.toJSON());
|
||||
|
||||
expect(ret).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should return same value when is string with invalid JSON date format', () => {
|
||||
const ret = controller.castJsonValue('Foo');
|
||||
|
||||
expect(ret).toEqual('Foo');
|
||||
});
|
||||
|
||||
it('should return same value when is not an string', () => {
|
||||
const ret = controller.castJsonValue(1001);
|
||||
|
||||
expect(ret).toEqual(1001);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,3 +13,6 @@ Views: Visualiza
|
|||
System: Sistema
|
||||
note: nota
|
||||
Changes: Cambios
|
||||
No changes: No hay cambios
|
||||
today: hoy
|
||||
yesterday: ayer
|
||||
|
|
|
@ -1,66 +1,173 @@
|
|||
@import "variables";
|
||||
|
||||
vn-log {
|
||||
vn-td {
|
||||
vertical-align: initial !important;
|
||||
}
|
||||
.changes {
|
||||
display: none;
|
||||
}
|
||||
.label {
|
||||
color: $color-font-secondary;
|
||||
}
|
||||
.value {
|
||||
color: $color-font;
|
||||
}
|
||||
.change {
|
||||
display: flex;
|
||||
|
||||
@media screen and (max-width: 1570px) {
|
||||
vn-table .expendable {
|
||||
& > .user-wrapper {
|
||||
position: relative;
|
||||
padding-right: 10px;
|
||||
|
||||
& > .user {
|
||||
border-radius: 50%;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
|
||||
& > * {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
img {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.user-icon {
|
||||
font-size: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&.system {
|
||||
background-color: $color-main;
|
||||
}
|
||||
}
|
||||
}
|
||||
& > .arrow {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
position: absolute;
|
||||
transform: rotateY(0deg) rotate(45deg);
|
||||
top: 18px;
|
||||
right: -4px;
|
||||
z-index: 1;
|
||||
}
|
||||
& > .line {
|
||||
position: absolute;
|
||||
background-color: $color-main;
|
||||
width: 2px;
|
||||
left: 17px;
|
||||
z-index: -1;
|
||||
top: 44px;
|
||||
bottom: -8px;
|
||||
}
|
||||
}
|
||||
&:last-child > .user-wrapper > .line {
|
||||
display: none;
|
||||
}
|
||||
.changes {
|
||||
padding-top: 10px;
|
||||
display: block;
|
||||
.detail {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
|
||||
& > .header {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
& > .chip {
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
color: $color-font-bg;
|
||||
|
||||
&.notice {
|
||||
background-color: $color-notice-medium;
|
||||
}
|
||||
&.success {
|
||||
background-color: $color-success-medium;
|
||||
}
|
||||
&.warning {
|
||||
background-color: $color-main-medium;
|
||||
}
|
||||
&.alert {
|
||||
background-color: lighten($color-alert, 5%);
|
||||
}
|
||||
}
|
||||
.date {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
& > .model {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
& > .model-name {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
& > .model-value {
|
||||
font-style: italic;
|
||||
color: #c7bd2b;
|
||||
}
|
||||
& > .model-id {
|
||||
color: $color-font-secondary;
|
||||
font-size: .9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.attributes {
|
||||
width: 100%;
|
||||
.changes {
|
||||
overflow: hidden;
|
||||
background-color: rgba(255, 255, 255, .05);
|
||||
border-radius: 4px;
|
||||
color: $color-font-secondary;
|
||||
transition: max-height 150ms ease-in-out;
|
||||
max-height: 28px;
|
||||
position: relative;
|
||||
|
||||
tr {
|
||||
height: 10px;
|
||||
& > .expand-button,
|
||||
& > .shrink-button {
|
||||
display: none;
|
||||
}
|
||||
&.props {
|
||||
padding-right: 24px;
|
||||
|
||||
& > td {
|
||||
padding: 2px;
|
||||
& > .expand-button,
|
||||
& > .shrink-button {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 8px;
|
||||
font-size: inherit;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
& > td.field,
|
||||
& > th.field {
|
||||
width: 20%;
|
||||
color: gray;
|
||||
& > .expand-button {
|
||||
display: block;
|
||||
}
|
||||
& > td.before,
|
||||
& > th.before,
|
||||
& > td.after,
|
||||
& > th.after {
|
||||
width: 40%;
|
||||
white-space: pre-line;
|
||||
&.expanded {
|
||||
max-height: 500px;
|
||||
padding-right: 0;
|
||||
|
||||
& > .changes-wrapper {
|
||||
text-overflow: initial;
|
||||
white-space: initial;
|
||||
}
|
||||
& > .shrink-button {
|
||||
display: block;
|
||||
}
|
||||
& > .expand-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
& > .changes-wrapper {
|
||||
padding: 4px 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
& > .no-changes {
|
||||
font-style: italic;
|
||||
}
|
||||
.json-field {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ellipsis {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
max-width: 400px;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
}
|
||||
.no-ellipsize,
|
||||
[no-ellipsize] {
|
||||
text-overflow: '';
|
||||
white-space: normal;
|
||||
overflow: auto;
|
||||
}
|
||||
.alignSpan {
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export default class Controller {
|
|||
user: this.user
|
||||
};
|
||||
|
||||
this.$http.post('Accounts/recoverPassword', params)
|
||||
this.$http.post('VnUsers/recoverPassword', params)
|
||||
.then(() => {
|
||||
this.goToLogin();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethod('editors', {
|
||||
description: 'Get the list of entity editors',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'integer',
|
||||
description: 'The model id',
|
||||
required: true
|
||||
}, {
|
||||
arg: 'filter',
|
||||
type: 'Object',
|
||||
description: 'The user filter object'
|
||||
}
|
||||
],
|
||||
returns: {
|
||||
type: [Self],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/editors`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.editors = async(id, filter) => {
|
||||
const res = await Self.find({
|
||||
fields: ['userFk'],
|
||||
where: {originFk: id}
|
||||
});
|
||||
const userIds = new Set(res.map(x => x.userFk));
|
||||
|
||||
filter = mergeFilters(filter, {
|
||||
where: {id: {inq: [...userIds]}}
|
||||
});
|
||||
return await Self.app.models.VnUser.find(filter);
|
||||
};
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue