#972 ticket.tracking

This commit is contained in:
Gerard 2019-01-22 10:04:42 +01:00
parent 5a53195574
commit 87a0a0bccc
12 changed files with 220 additions and 49 deletions

View File

@ -386,7 +386,8 @@ export default {
trackingButton: `vn-left-menu a[ui-sref="ticket.card.tracking.index"]`,
createStateButton: `${components.vnFloatButton}`,
stateAutocomplete: 'vn-ticket-tracking-edit vn-autocomplete[field="$ctrl.ticket.stateFk"]',
saveButton: `${components.vnSubmit}`
saveButton: `${components.vnSubmit}`,
cancelButton: `vn-ticket-tracking-edit vn-button[ui-sref="ticket.card.tracking.index"]`
},
ticketBasicData: {
basicDataButton: `vn-left-menu a[ui-sref="ticket.card.data.stepOne"]`,
@ -437,8 +438,8 @@ export default {
saveServiceButton: `${components.vnSubmit}`
},
createStateView: {
stateAutocomplete: `vn-autocomplete[field="$ctrl.ticket.stateFk"]`,
clearStateInputButton: `vn-autocomplete[field="$ctrl.ticket.stateFk"] > div > div > div > vn-icon > i`,
stateAutocomplete: `vn-autocomplete[field="$ctrl.stateFk"]`,
clearStateInputButton: `vn-autocomplete[field="$ctrl.stateFk"] > div > div > div > vn-icon > i`,
saveStateButton: `${components.vnSubmit}`
},
claimsIndex: {

View File

@ -25,27 +25,19 @@ describe('Ticket Create new tracking state path', () => {
.click(selectors.createStateView.saveStateButton)
.waitForLastSnackbar();
expect(result).toEqual('No changes to save');
expect(result).toEqual('State cannot be blank');
});
it(`should attempt create a new state then clear and save it`, async() => {
let result = await nightmare
.autocompleteSearch(selectors.createStateView.stateAutocomplete, '¿Fecha?')
.waitToClick(selectors.createStateView.clearStateInputButton)
.click(selectors.createStateView.saveStateButton)
.waitToClick(selectors.createStateView.saveStateButton)
.waitForLastSnackbar();
expect(result).toEqual('Data saved!');
expect(result).toEqual('State cannot be blank');
});
it('should again access to the create state view by clicking the create floating button', async() => {
let url = await nightmare
.click(selectors.ticketTracking.createStateButton)
.wait(selectors.createStateView.stateAutocomplete)
.parsedUrl();
expect(url.hash).toContain('tracking/edit');
});
it(`should create a new state`, async() => {
let result = await nightmare

View File

@ -18,6 +18,7 @@
"That payment method requires an IBAN": "El método de pago seleccionado requiere un IBAN",
"That payment method requires a BIC": "El método de pago seleccionado requiere un BIC",
"State cannot be blank": "El estado no puede estar en blanco",
"Worker cannot be blank": "El trabajador no puede estar en blanco",
"Cannot change the payment method if no salesperson": "No se puede cambiar la forma de pago si no hay comercial asignado",
"can't be blank": "El campo no puede estar vacío",
"Observation type cannot be blank": "El tipo de observación no puede estar en blanco",

View File

@ -25,18 +25,25 @@ module.exports = Self => {
let models = Self.app.models;
let isProduction;
let isEditable = await Self.app.models.Ticket.isEditable(params.ticketFk);
let assignedState = await Self.app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}});
let isAssigned = assignedState.id === params.stateFk;
let currentUserId;
if (ctx.req.accessToken) {
let token = ctx.req.accessToken;
let currentUserId = token && token.userId;
isProduction = await models.Account.hasRole(currentUserId, 'Production');
currentUserId = token && token.userId;
isProduction = await models.Account.hasRole(currentUserId, 'production');
isSalesperson = await models.Account.hasRole(currentUserId, 'salesPerson');
}
if ((!isEditable && !isProduction) || (isEditable && !isAssigned && isSalesperson) || (!isSalesperson && !isProduction))
throw new UserError(`You don't have enough privileges to change the state of this ticket`);
if (!isAssigned) {
let worker = await models.Worker.findOne({where: {userFk: currentUserId}});
params.workerFk = worker.id;
}
if (!isEditable && !isProduction)
throw new UserError(`You don't have enough privileges to change the state of this ticket`);
return await Self.app.models.TicketTracking.create(params);
};
};

View File

@ -3,18 +3,18 @@ const app = require(`${serviceRoot}/server/server`);
describe('ticket changeState()', () => {
let ticket;
beforeAll(async () => {
beforeAll(async() => {
let originalTicket = await app.models.Ticket.findOne({where: {id: 16}});
originalTicket.id = null;
ticket = await app.models.Ticket.create(originalTicket);
});
afterAll(async () => {
afterAll(async() => {
await app.models.Ticket.destroyById(ticket.id);
});
it('should throw an error if the ticket is not editable and the user isnt production', async () => {
let ctx = {req: {accessToken: {userId: 110}}};
it('should throw an error if the ticket is not editable and the user isnt production', async() => {
let ctx = {req: {accessToken: {userId: 18}}};
let params = {ticketFk: 2, stateFk: 3};
let error;
try {
@ -26,26 +26,66 @@ describe('ticket changeState()', () => {
expect(error).toEqual(new Error(`You don't have enough privileges to change the state of this ticket`));
});
it('should be able to create a ticket tracking line for a not editable ticket if the user has the production role', async () => {
let ctx = {req: {accessToken: {userId: 50}}};
let params = {ticketFk: 20, stateFk: 3};
it('should throw an error if the state is assigned and theres not worker in params', async() => {
let ctx = {req: {accessToken: {userId: 18}}};
let assignedState = await app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}});
let params = {ticketFk: 11, stateFk: assignedState.id};
let error;
try {
await app.models.TicketTracking.changeState(ctx, params);
} catch (e) {
error = e;
}
expect(error.message).toEqual('La instancia `TicketTracking` no es válida. Detalles: `workerFk` Worker cannot be blank (value: undefined).');
});
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 params = {ticketFk: 11, stateFk: 3};
let error;
try {
await app.models.TicketTracking.changeState(ctx, params);
} catch (e) {
error = e;
}
expect(error.message).toEqual(`You don't have enough privileges to change the state of this ticket`);
});
it('should be able to create a ticket tracking line for a not editable ticket if the user has the production role', async() => {
let ctx = {req: {accessToken: {userId: 49}}};
let params = {ticketFk: ticket.id, stateFk: 3};
let res = await app.models.TicketTracking.changeState(ctx, params);
expect(res.__data.ticketFk).toBe(params.ticketFk);
expect(res.__data.stateFk).toBe(params.stateFk);
expect(res.__data.workerFk).toBe(50);
expect(res.__data.workerFk).toBe(49);
expect(res.__data.id).toBeDefined();
});
it('return an array with the created ticket tracking line', async () => {
let ctx = {req: {accessToken: {userId: 108}}};
it('return an array with the created ticket tracking line', async() => {
let ctx = {req: {accessToken: {userId: 49}}};
let params = {ticketFk: ticket.id, stateFk: 3};
let res = await app.models.TicketTracking.changeState(ctx, params);
expect(res.__data.ticketFk).toBe(params.ticketFk);
expect(res.__data.stateFk).toBe(params.stateFk);
expect(res.__data.workerFk).toBe(110);
expect(res.__data.workerFk).toBe(49);
expect(res.__data.id).toBeDefined();
});
it('return an array with the created ticket tracking line when the user is salesperson, uses the state assigned and thes a workerFk given', async() => {
let ctx = {req: {accessToken: {userId: 18}}};
let assignedState = await app.models.State.findOne({where: {code: 'PICKER_DESIGNED'}});
let params = {ticketFk: ticket.id, stateFk: assignedState.id, workerFk: 1};
let res = await app.models.TicketTracking.changeState(ctx, params);
expect(res.__data.ticketFk).toBe(params.ticketFk);
expect(res.__data.stateFk).toBe(params.stateFk);
expect(res.__data.workerFk).toBe(params.workerFk);
expect(res.__data.workerFk).toBe(1);
expect(res.__data.id).toBeDefined();
});
});

View File

@ -2,4 +2,5 @@ module.exports = function(Self) {
require('../methods/ticket-tracking/changeState')(Self);
Self.validatesPresenceOf('stateFk', {message: 'State cannot be blank'});
Self.validatesPresenceOf('workerFk', {message: 'Worker cannot be blank'});
};

View File

@ -142,7 +142,7 @@
"params": {
"ticket": "$ctrl.ticket"
},
"acl": ["production", "administrative"]
"acl": ["production", "administrative", "salesPerson"]
},
{
"url" : "/sale-checked",

View File

@ -1,9 +1,8 @@
<mg-ajax path="/ticket/api/TicketTrackings/changeState" options="vnPost"></mg-ajax>
<mg-ajax path="/api/TicketTrackings/changeState" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.ticket"
form="form"
save="post">
data="$ctrl.params"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" compact>
<vn-card pad-large>
@ -11,11 +10,23 @@
<vn-horizontal>
<vn-autocomplete
vn-one
field="$ctrl.ticket.stateFk"
field="$ctrl.stateFk"
url="/ticket/api/States"
label="State"
vn-focus>
</vn-autocomplete>
<vn-autocomplete
vn-one
ng-if="$ctrl.isPickerDesignedState"
field="$ctrl.workerFk"
url="/client/api/Clients/activeWorkersWithRole"
show-field="firstName"
search-function="{firstName: $search}"
value-field="id"
where="{role: 'employee'}"
label="Worker">
<tpl-item>{{firstName}} {{name}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>

View File

@ -1,7 +1,8 @@
import ngModule from '../../module';
class Controller {
constructor($scope, $state, vnApp, $translate) {
constructor($scope, $state, vnApp, $translate, $http) {
this.$http = $http;
this.$ = $scope;
this.$state = $state;
this.vnApp = vnApp;
@ -9,17 +10,60 @@ class Controller {
this.ticket = {
ticketFk: $state.params.id
};
this.params = {ticketFk: $state.params.id};
}
$onInit() {
this.getPickerDesignedState();
}
set stateFk(value) {
this.params.stateFk = value;
this.isPickerDesignedState = this.getIsPickerDesignedState(value);
}
get stateFk() {
return this.params.stateFk;
}
set workerFk(value) {
this.params.workerFk = value;
}
get workerFk() {
return this.params.workerFk;
}
getPickerDesignedState() {
let filter = {
where: {
code: 'PICKER_DESIGNED'
}
};
let json = encodeURIComponent(JSON.stringify(filter));
this.$http.get(`/api/States?filter=${json}`).then(res => {
if (res && res.data)
this.pickerDesignedState = res.data[0].id;
});
}
getIsPickerDesignedState(value) {
if (value == this.pickerDesignedState)
return true;
return false;
}
onSubmit() {
this.$.watcher.submit().then(
() => {
this.$http.post(`/api/TicketTrackings/changeState`, this.params).then(() => {
this.$.watcher.updateOriginalData();
this.card.reload();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
this.$state.go('ticket.card.tracking.index');
}
);
});
}
}
Controller.$inject = ['$scope', '$state', 'vnApp', '$translate'];
Controller.$inject = ['$scope', '$state', 'vnApp', '$translate', '$http'];
ngModule.component('vnTicketTrackingEdit', {
template: require('./index.html'),

View File

@ -0,0 +1,74 @@
import './index';
describe('Ticket', () => {
describe('Component vnTicketTrackingEdit', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('ticket'));
beforeEach(angular.mock.inject(($componentController, _$httpBackend_, $translate, vnApp) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnTicketTrackingEdit');
controller.ticket = {id: 1};
controller.$ = {watcher: {updateOriginalData: () => {}}};
controller.card = {reload: () => {}};
controller.vnApp = {showSuccess: () => {}};
controller.$translate = $translate;
controller.vnApp = vnApp;
}));
describe('stateFk setter/getter', () => {
it('should set params.stateFk and set isPickerDesignedState', () => {
let stateFk = {id: 1};
controller.stateFk = stateFk;
expect(controller.params.stateFk).toEqual(stateFk);
expect(controller.isPickerDesignedState).toEqual(false);
});
});
describe('workerFk setter', () => {
it('should set params.workerFk', () => {
controller.workerFk = 1;
expect(controller.params.workerFk).toEqual(1);
});
});
describe('getPickerDesignedState()', () => {
it('should get the state that has the code PICKER_DESIGNED', () => {
let filter = {
where: {
code: 'PICKER_DESIGNED'
}
};
let json = encodeURIComponent(JSON.stringify(filter));
$httpBackend.expectGET(`/api/States?filter=${json}`).respond([{id: 22}]);
controller.getPickerDesignedState();
$httpBackend.flush();
expect(controller.pickerDesignedState).toEqual(22);
});
});
describe('onSubmit()', () => {
it('should POST the data, call updateOriginalData, reload, showSuccess and go functions', () => {
controller.params = {stateFk: 22, workerFk: 101};
spyOn(controller.card, 'reload');
spyOn(controller.$.watcher, 'updateOriginalData');
spyOn(controller.vnApp, 'showSuccess');
spyOn(controller.$state, 'go');
$httpBackend.expectPOST(`/api/TicketTrackings/changeState`, controller.params).respond({});
controller.onSubmit();
$httpBackend.flush();
expect(controller.card.reload).toHaveBeenCalledWith();
expect(controller.$.watcher.updateOriginalData).toHaveBeenCalledWith();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith(controller.$translate.instant('Data saved!'));
expect(controller.$state.go).toHaveBeenCalledWith('ticket.card.tracking.index');
});
});
});
});

View File

@ -34,6 +34,6 @@
<vn-pagination model="model"></vn-pagination>
</vn-card>
</vn-vertical>
<a ui-sref="ticket.card.tracking.edit" vn-bind="+" vn-visible-by="production, administrative" fixed-bottom-right>
<a ui-sref="ticket.card.tracking.edit" vn-bind="+" vn-visible-by="production, administrative, salesperson" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -6,15 +6,15 @@ class Controller {
this.filter = {
include: [
{
relation: "worker",
relation: 'worker',
scope: {
fields: ["firstName", "name"]
fields: ['firstName', 'name']
}
},
{
relation: "state",
relation: 'state',
scope: {
fields: ["name"]
fields: ['name']
}
}
]