#972 ticket.tracking
This commit is contained in:
parent
5a53195574
commit
87a0a0bccc
|
@ -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: {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ describe('ticket changeState()', () => {
|
|||
});
|
||||
|
||||
it('should throw an error if the ticket is not editable and the user isnt production', async() => {
|
||||
let ctx = {req: {accessToken: {userId: 110}}};
|
||||
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 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: 50}}};
|
||||
let params = {ticketFk: 20, stateFk: 3};
|
||||
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}}};
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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'});
|
||||
};
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
"params": {
|
||||
"ticket": "$ctrl.ticket"
|
||||
},
|
||||
"acl": ["production", "administrative"]
|
||||
"acl": ["production", "administrative", "salesPerson"]
|
||||
},
|
||||
{
|
||||
"url" : "/sale-checked",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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>
|
|
@ -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']
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue