#998 worker.index, back tests fixed

This commit is contained in:
Juan Ferrer 2019-01-29 16:37:59 +01:00
parent bb88c9cfe4
commit f47fe9c1cc
16 changed files with 150 additions and 117 deletions

View File

@ -29,27 +29,9 @@
"Warehouse": { "Warehouse": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Worker": {
"dataSource": "vn"
},
"WorkerMana": {
"dataSource": "vn"
},
"WorkerTeam": {
"dataSource": "vn"
},
"WorkerTeamCollegues": {
"dataSource": "vn"
},
"Sip": { "Sip": {
"dataSource": "vn" "dataSource": "vn"
}, },
"PersonMedia": {
"dataSource": "vn"
},
"PersonDepartment": {
"dataSource": "vn"
},
"Route": { "Route": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -57,10 +57,7 @@ module.exports = Self => {
*/ */
Self.hasRole = async function(userId, name) { Self.hasRole = async function(userId, name) {
let roles = await Self.getRoles(userId); let roles = await Self.getRoles(userId);
return roles.find(role => role == name);
return roles.find(role => {
return role.toLowerCase() == name.toLowerCase();
});
}; };
/** /**
@ -81,7 +78,6 @@ module.exports = Self => {
for (role of result) for (role of result)
roles.push(role.name); roles.push(role.name);
return roles; return roles;
}; };
}; };

View File

@ -1,31 +0,0 @@
{
"name": "PersonDepartment",
"base": "VnModel",
"options": {
"mysql": {
"table": "personDepartment"
}
},
"properties": {
"name": {
"type": "String"
},
"firstName": {
"type": "String"
},
"workerFk": {
"id": true,
"type": "Number"
},
"department": {
"type": "Number"
}
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
}
}
}

View File

@ -1,25 +0,0 @@
{
"name": "PersonMedia",
"base": "VnModel",
"options": {
"mysql": {
"table": "personMedia"
}
},
"properties": {
"mediaValue": {
"type": "String"
},
"workerFk": {
"id": true,
"type": "Number"
}
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
}
}
}

View File

@ -5,10 +5,11 @@
* be understandable by people who do not have a technical profile. * be understandable by people who do not have a technical profile.
*/ */
module.exports = class UserError extends Error { module.exports = class UserError extends Error {
constructor(message, ...translateArgs) { constructor(message, code, ...translateArgs) {
super(message); super(message);
this.name = 'UserError'; this.name = 'UserError';
this.statusCode = 400; this.statusCode = 400;
this.code = code;
this.translateArgs = translateArgs; this.translateArgs = translateArgs;
} }
}; };

View File

@ -29,7 +29,7 @@ module.exports = Self => {
let token = ctx.req.accessToken; let token = ctx.req.accessToken;
let currentUserId = token && token.userId; let currentUserId = token && token.userId;
isSalesAssistant = await models.Account.hasRole(currentUserId, 'SalesAssistant'); isSalesAssistant = await models.Account.hasRole(currentUserId, 'salesAssistant');
if (!isSalesAssistant) { if (!isSalesAssistant) {
let oldClaim = await models.Claim.findById(params.id); let oldClaim = await models.Claim.findById(params.id);

View File

@ -6,10 +6,9 @@ module.exports = Self => {
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
arg: 'params', arg: 'params',
type: 'object',
required: true,
description: 'ticketFk, stateFk', description: 'ticketFk, stateFk',
http: {source: 'body'} type: 'object',
required: true
}], }],
returns: { returns: {
type: 'string', type: 'string',
@ -17,33 +16,30 @@ module.exports = Self => {
}, },
http: { http: {
path: `/changeState`, path: `/changeState`,
verb: 'post' verb: 'POST'
} }
}); });
Self.changeState = async(ctx, params) => { Self.changeState = async(ctx, params) => {
let userId = ctx.req.accessToken.userId;
let models = Self.app.models; let models = Self.app.models;
let isProduction;
let isEditable = await Self.app.models.Ticket.isEditable(params.ticketFk); let isEditable = await models.Ticket.isEditable(params.ticketFk);
let assignedState = await Self.app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}}); let assignedState = await models.State.findOne({where: {code: 'PICKER_DESIGNED'}});
let isAssigned = assignedState.id === params.stateFk; let isAssigned = assignedState.id === params.stateFk;
let currentUserId;
if (ctx.req.accessToken) { let isProduction = await models.Account.hasRole(userId, 'production');
let token = ctx.req.accessToken; let isSalesPerson = await models.Account.hasRole(userId, 'salesPerson');
currentUserId = token && token.userId; let isAllowed = isProduction || isSalesPerson && isEditable && isAssigned;
isProduction = await models.Account.hasRole(currentUserId, 'production');
isSalesperson = await models.Account.hasRole(currentUserId, 'salesPerson');
}
if ((!isEditable && !isProduction) || (isEditable && !isAssigned && isSalesperson) || (!isSalesperson && !isProduction)) if (!isAllowed)
throw new UserError(`You don't have enough privileges to change the state of this ticket`); throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED');
if (!isAssigned) { if (!isAssigned) {
let worker = await models.Worker.findOne({where: {userFk: currentUserId}}); let worker = await models.Worker.findOne({where: {userFk: userId}});
params.workerFk = worker.id; params.workerFk = worker.id;
} }
return await Self.app.models.TicketTracking.create(params); return await models.TicketTracking.create(params);
}; };
}; };

View File

@ -16,41 +16,44 @@ describe('ticket changeState()', () => {
it('should throw an error if the ticket is not editable and the user isnt production', async() => { it('should throw an error if the ticket is not editable and the user isnt production', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; let ctx = {req: {accessToken: {userId: 18}}};
let params = {ticketFk: 2, stateFk: 3}; let params = {ticketFk: 2, stateFk: 3};
let error;
let errCode;
try { try {
await app.models.TicketTracking.changeState(ctx, params); await app.models.TicketTracking.changeState(ctx, params);
} catch (e) { } catch (e) {
error = e; errCode = e.code;
} }
expect(error).toEqual(new Error(`You don't have enough privileges to change the state of this ticket`)); expect(errCode).toBe('ACCESS_DENIED');
}); });
it('should throw an error if the state is assigned and theres not worker in params', async() => { it('should throw an error if the state is assigned and theres not worker in params', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; let ctx = {req: {accessToken: {userId: 18}}};
let assignedState = await app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}}); let assignedState = await app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}});
let params = {ticketFk: 11, stateFk: assignedState.id}; let params = {ticketFk: 11, stateFk: assignedState.id};
let error;
let errCode;
try { try {
await app.models.TicketTracking.changeState(ctx, params); await app.models.TicketTracking.changeState(ctx, params);
} catch (e) { } catch (e) {
error = e; errCode = e.details.codes.workerFk[0];
} }
expect(error.message).toEqual('La instancia `TicketTracking` no es válida. Detalles: `workerFk` Worker cannot be blank (value: undefined).'); expect(errCode).toEqual('presence');
}); });
it('should throw an error if a worker thats not production tries to change the state to one thats not assigned', async() => { it('should throw an error if a worker thats not production tries to change the state to one thats not assigned', async() => {
let ctx = {req: {accessToken: {userId: 110}}}; let ctx = {req: {accessToken: {userId: 110}}};
let params = {ticketFk: 11, stateFk: 3}; let params = {ticketFk: 11, stateFk: 3};
let error;
let errCode;
try { try {
await app.models.TicketTracking.changeState(ctx, params); await app.models.TicketTracking.changeState(ctx, params);
} catch (e) { } catch (e) {
error = e; errCode = e.code;
} }
expect(error.message).toEqual(`You don't have enough privileges to change the state of this ticket`); expect(errCode).toBe('ACCESS_DENIED');
}); });
it('should be able to create a ticket tracking line for a not editable ticket if the user has the production role', async() => { it('should be able to create a ticket tracking line for a not editable ticket if the user has the production role', async() => {

View File

@ -24,7 +24,7 @@ module.exports = Self => {
Self.deleted = async(ctx, params) => { Self.deleted = async(ctx, params) => {
let claimOfATicket = await Self.app.models.Claim.findOne({where: {ticketFk: params.id}}); let claimOfATicket = await Self.app.models.Claim.findOne({where: {ticketFk: params.id}});
if (claimOfATicket) if (claimOfATicket)
throw new UserError('You must delete the claim id %d first', claimOfATicket.id); throw new UserError('You must delete the claim id %d first', 'DELETE_CLAIM_FIRST', claimOfATicket.id);
let currentTicket = await Self.app.models.Ticket.findById(params.id); let currentTicket = await Self.app.models.Ticket.findById(params.id);
await currentTicket.updateAttributes({isDeleted: '1'}); await currentTicket.updateAttributes({isDeleted: '1'});

View File

@ -1 +1,20 @@
{} {
"Worker": {
"dataSource": "vn"
},
"WorkerMana": {
"dataSource": "vn"
},
"WorkerTeam": {
"dataSource": "vn"
},
"WorkerTeamCollegues": {
"dataSource": "vn"
},
"WorkerMedia": {
"dataSource": "vn"
},
"WorkerDepartment": {
"dataSource": "vn"
}
}

View File

@ -0,0 +1,25 @@
{
"name": "WorkerDepartment",
"base": "VnModel",
"options": {
"mysql": {
"table": "personDepartment"
}
},
"properties": {
"workerFk": {
"id": true,
"type": "Number"
},
"department": {
"type": "String"
}
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
}
}
}

View File

@ -0,0 +1,25 @@
{
"name": "WorkerMedia",
"base": "VnModel",
"options": {
"mysql": {
"table": "personMedia"
}
},
"properties": {
"workerFk": {
"id": true,
"type": "Number"
},
"mediaValue": {
"type": "String"
}
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
}
}
}

View File

@ -32,6 +32,16 @@
"model": "Account", "model": "Account",
"foreignKey": "userFk" "foreignKey": "userFk"
}, },
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "userFk"
},
"department": {
"type": "belongsTo",
"model": "WorkerDepartment",
"foreignKey": "id"
},
"collegues": { "collegues": {
"type": "hasMany", "type": "hasMany",
"model": "WorkerTeamCollegues", "model": "WorkerTeamCollegues",

View File

@ -1,10 +1,10 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="/agency/api/Workers" url="/agency/api/Workers"
filter="::$ctrl.filter" include="::$ctrl.include"
limit="20" limit="20"
data="workers" data="workers"
auto-load="true"> auto-load="false">
</vn-crud-model> </vn-crud-model>
<div class="index-block"> <div class="index-block">
<div class="vn-list"> <div class="vn-list">
@ -23,12 +23,21 @@
class="vn-list-item"> class="vn-list-item">
<vn-horizontal ng-click="$ctrl.onClick($event)"> <vn-horizontal ng-click="$ctrl.onClick($event)">
<vn-one> <vn-one>
<h6>{{::$ctrl.worker.name}}</h6> <h6>{{::worker.firstName}} {{::worker.name}}</h6>
<vn-label-value label="Id" <vn-label-value label="Id"
value="{{::worker.id}}"> value="{{::worker.id}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Name" <vn-label-value label="User"
value="{{::worker.name}}"> value="{{::worker.user.name}}">
</vn-label-value>
<vn-label-value label="Email"
value="{{::worker.user.email}}">
</vn-label-value>
<vn-label-value label="Fiscal identifier"
value="{{::worker.client.fi}}">
</vn-label-value>
<vn-label-value label="Department"
value="{{::worker.department.department}}">
</vn-label-value> </vn-label-value>
</vn-one> </vn-one>
<vn-horizontal class="buttons"> <vn-horizontal class="buttons">

View File

@ -4,7 +4,19 @@ export default class Controller {
constructor($) { constructor($) {
Object.assign(this, { Object.assign(this, {
$, $,
selectedWorker: null selectedWorker: null,
include: [
{
relation: 'user',
scope: {fields: ['name', 'email']}
}, {
relation: 'client',
scope: {fields: ['fi']}
}, {
relation: 'department',
scope: {fields: ['department']}
}
]
}); });
} }
@ -13,8 +25,12 @@ export default class Controller {
case 'search': case 'search':
return /^\d+$/.test(value) return /^\d+$/.test(value)
? {id: value} ? {id: value}
: {name: {like: `%${value}%`}}; : {or: [
{firstName: {like: `%${value}%`}},
{name: {like: `%${value}%`}}
]};
case 'name': case 'name':
case 'firstName':
return {[param]: {like: `%${value}%`}}; return {[param]: {like: `%${value}%`}};
case 'id': case 'id':
return {[param]: value}; return {[param]: value};

View File

@ -17,6 +17,13 @@
<vn-textfield <vn-textfield
vn-one vn-one
label="Name" label="Name"
model="filter.firstName">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Last name"
model="filter.name"> model="filter.name">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>