#1250 ticket.line al mover todas las lineas delete

This commit is contained in:
Carlos Jimenez Ruiz 2019-05-24 15:30:50 +02:00
parent 736cef1ebe
commit a3311f6093
12 changed files with 249 additions and 158 deletions

View File

@ -25,7 +25,6 @@
"Observation type must be unique": "El tipo de observación no puede repetirse",
"The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero",
"The grade must be similar to the last one": "El grade debe ser similar al último",
"NO_AGENCY_AVAILABLE": "NO_AGENCY_AVAILABLE",
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente",
"Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco",
@ -54,7 +53,6 @@
"Agency cannot be blank": "La agencia no puede quedar en blanco",
"You can't make changes on a client with verified data": "No puedes hacer cambios en un cliente con datos comprobados",
"This address doesn't exist": "Este consignatario no existe",
"The sales of this ticket can't be modified": "Los movimientos de este tiquet no pueden ser modificadas",
"You can't create an order for a inactive client": "You can't create an order for a inactive client",
"You can't create an order for a client that doesn't has tax data verified": "You can't create an order for a client that doesn't has tax data verified",
"You must delete the claim id %d first": "Antes debes borrar la reclamacion %d",
@ -84,5 +82,8 @@
"You cannot add or modify services to an invoiced ticket": "No puedes añadir o modificar servicios a un ticket facturado",
"This ticket can not be modified": "Este ticket no puede ser modificado",
"The introduced hour already exists": "Esta hora ya ha sido introducida",
"INFINITE_LOOP": "Existe una dependencia entre dos Jefes"
"INFINITE_LOOP": "Existe una dependencia entre dos Jefes",
"The sales of the current ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"The sales of the receiver ticket can't be modified": "Las lineas del ticket al que envias no pueden ser modificadas",
"NO_AGENCY_AVAILABLE": "No hay agencias disponibles"
}

View File

@ -1,76 +0,0 @@
let UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('moveToNewTicket', {
description: 'Change the state of a ticket',
accessType: '',
accepts: [{
arg: 'ticketParams',
type: 'object',
required: true,
description: '[sales IDs], newTicketFk, actualTicketFk',
http: {source: 'body'}
}, {
arg: 'sales',
type: 'object',
required: true,
description: '[sales IDs]',
http: {source: 'body'}
}],
returns: {
type: 'string',
root: true
},
http: {
path: `/moveToNewTicket`,
verb: 'post'
}
});
Self.moveToNewTicket = async(ctx, params) => {
let userId = ctx.req.accessToken.userId;
let model = Self.app.models;
let thisTicketIsEditable = await model.Ticket.isEditable(params.ticket.oldTicketFk);
if (!thisTicketIsEditable)
throw new UserError(`The sales of this ticket can't be modified`);
let travelDates = await model.Agency.getFirstShipped(params.ticket);
let shipped = new Date(travelDates.vShipped);
shipped.setMinutes(shipped.getMinutes() + shipped.getTimezoneOffset());
let landed = new Date(travelDates.vLanded);
landed.setMinutes(landed.getMinutes() + landed.getTimezoneOffset());
let newTicketParams = {
clientFk: params.ticket.clientFk,
addressFk: params.ticket.addressFk,
agencyModeFk: params.ticket.agencyModeFk,
warehouseFk: params.ticket.warehouseFk,
shipped: shipped,
landed: landed,
userId: userId
};
let transaction = await Self.beginTransaction({});
try {
let newTicket = await model.Ticket.new(ctx, newTicketParams, {transaction: transaction});
let selectedSalesId = [];
params.sales.forEach(sale => {
selectedSalesId.push(sale.id);
});
await model.Sale.updateAll(
{id: {inq: selectedSalesId}},
{ticketFk: newTicket.id},
{transaction});
await transaction.commit();
return newTicket;
} catch (e) {
await transaction.rollback();
throw e;
}
};
};

View File

@ -1,14 +1,13 @@
let UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethod('moveToTicket', {
description: 'Moves a line to a given ticket',
accessType: '',
Self.remoteMethodCtx('moveToTicket', {
description: 'Moves lines to a new or a given ticket',
accepts: [{
arg: 'params',
type: 'object',
required: true,
description: '[sales IDs], newTicketFk, actualTicketFk',
description: 'currentTicket, receiverTicket, [sales IDs], removeEmptyTicket',
http: {source: 'body'}
}],
returns: {
@ -21,16 +20,69 @@ module.exports = Self => {
}
});
Self.moveToTicket = async params => {
let thisTicketIsEditable = await Self.app.models.Ticket.isEditable(params.actualTicketFk);
if (!thisTicketIsEditable)
throw new UserError(`The sales of this ticket can't be modified`);
Self.moveToTicket = async(ctx, params) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
let currentTicket = await models.Ticket.findById(params.currentTicket.currentTicketId);
let newTicketData = {};
let receiverTicket = params.receiverTicket;
let transaction = await Self.beginTransaction({});
let options = {transaction};
let newTicketIsEditable = await Self.app.models.Ticket.isEditable(params.newTicketFk);
if (!newTicketIsEditable)
throw new UserError(`The sales of this ticket can't be modified`);
let isCurrentTicketEditable = await models.Ticket.isEditable(params.currentTicket.currentTicketId);
if (!isCurrentTicketEditable)
throw new UserError(`The sales of the current ticket can't be modified`);
for (let i = 0; i < params.sales.length; i++)
await Self.app.models.Sale.update({id: params.sales[i].id}, {ticketFk: params.newTicketFk});
if (params.receiverTicket.id) {
let isReceiverTicketEditable = await models.Ticket.isEditable(params.receiverTicket.id);
if (!isReceiverTicketEditable)
throw new UserError(`The sales of the receiver ticket can't be modified`);
}
if (!params.receiverTicket.id) {
let travelDates = await models.Agency.getFirstShipped(params.currentTicket);
let shipped = new Date(travelDates.vShipped);
shipped.setMinutes(shipped.getMinutes() + shipped.getTimezoneOffset());
let landed = new Date(travelDates.vLanded);
landed.setMinutes(landed.getMinutes() + landed.getTimezoneOffset());
newTicketData = {
clientFk: params.currentTicket.clientFk,
addressFk: params.currentTicket.addressFk,
agencyModeFk: params.currentTicket.agencyModeFk,
warehouseFk: params.currentTicket.warehouseFk,
shipped: shipped,
landed: landed,
userId: userId
};
}
try {
if (!params.receiverTicket.id)
receiverTicket = await models.Ticket.new(ctx, newTicketData, options);
let promises = [];
for (let sale of params.sales) {
promises.push(
models.Sale.update(
{id: sale.id},
{ticketFk: receiverTicket.id},
options
)
);
}
if (params.removeEmptyTicket)
promises.push(currentTicket.updateAttributes({isDeleted: true}, options));
await Promise.all(promises);
await transaction.commit();
return receiverTicket;
} catch (error) {
await transaction.rollback();
throw error;
}
};
};

View File

@ -1,14 +1,22 @@
const app = require('vn-loopback/server/server');
describe('sale moveToTicket()', () => {
let createdTicketId;
afterAll(async done => {
await app.models.Ticket.destroyById(createdTicketId);
done();
});
it('should throw an error if the ticket is not editable', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
let error;
let params = {actualTicketFk: 10};
const params = {currentTicket: {currentTicketId: 10}};
await app.models.Sale.moveToTicket(params)
await app.models.Sale.moveToTicket(ctx, params)
.catch(response => {
expect(response.message).toEqual(`The sales of this ticket can't be modified`);
expect(response.message).toEqual(`The sales of the current ticket can't be modified`);
error = response;
});
@ -16,64 +24,83 @@ describe('sale moveToTicket()', () => {
});
it('should throw an error if the receiving ticket is not editable', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
let error;
let params = {actualTicketFk: 1, newTicketFk: 10};
const params = {currentTicket: {currentTicketId: 16}, receiverTicket: {id: 1}};
await app.models.Sale.moveToTicket(params)
await app.models.Sale.moveToTicket(ctx, params)
.catch(response => {
expect(response.message).toEqual(`The sales of this ticket can't be modified`);
expect(response.message).toEqual(`The sales of the receiver ticket can't be modified`);
error = response;
});
expect(error).toBeDefined();
});
it('should transfer the sales from one ticket to another', async() => {
let senderTicketSales = await app.models.Ticket.getSales(11);
let receiverTicketSales = await app.models.Ticket.getSales(13);
it('should transfer the sales from one ticket to a new one', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
let currentTicket = await app.models.Ticket.findById(8);
currentTicket.currentTicketId = currentTicket.id;
currentTicket.id = undefined;
expect(senderTicketSales.length).toEqual(2);
expect(receiverTicketSales.length).toEqual(0);
let currentTicketSales = await app.models.Ticket.getSales(currentTicket.currentTicketId);
expect(currentTicketSales.length).toEqual(2);
let params = {
actualTicketFk: 11,
newTicketFk: 13,
currentTicket: currentTicket,
receiverTicket: {id: undefined},
sales: [
{id: 7},
{id: 8}]
{id: 13},
{id: 14}
]
};
await app.models.Sale.moveToTicket(params);
let createdTicket = await app.models.Sale.moveToTicket(ctx, params);
createdTicketId = createdTicket.id;
senderTicketSales = await app.models.Ticket.getSales(11);
receiverTicketSales = await app.models.Ticket.getSales(13);
currentTicketSales = await app.models.Ticket.getSales(currentTicket.currentTicketId);
receiverTicketSales = await app.models.Ticket.getSales(createdTicket.id);
expect(senderTicketSales.length).toEqual(0);
expect(currentTicketSales.length).toEqual(0);
expect(receiverTicketSales.length).toEqual(2);
});
it('should transfers back the sales', async() => {
let senderTicketSales = await app.models.Ticket.getSales(13);
let receiverTicketSales = await app.models.Ticket.getSales(11);
it('should transfers back the sales and set the created ticket as deleted', async() => {
const ctx = {req: {accessToken: {userId: 101}}};
let receiverTicketId = 8;
let currentTicket = await app.models.Ticket.findById(createdTicketId);
currentTicket.currentTicketId = createdTicketId;
currentTicket.id = undefined;
expect(senderTicketSales.length).toEqual(2);
let createdTicket = await app.models.Ticket.findById(createdTicketId);
let createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
let receiverTicketSales = await app.models.Ticket.getSales(receiverTicketId);
expect(createdTicket.isDeleted).toBeFalsy();
expect(createdTicketSales.length).toEqual(2);
expect(receiverTicketSales.length).toEqual(0);
let params = {
actualTicketFk: 13,
newTicketFk: 11,
removeEmptyTicket: true,
currentTicket: currentTicket,
receiverTicket: {id: receiverTicketId},
sales: [
{id: 7},
{id: 8}]
{id: 13},
{id: 14}
]
};
await app.models.Sale.moveToTicket(params);
await app.models.Sale.moveToTicket(ctx, params);
senderTicketSales = await app.models.Ticket.getSales(13);
receiverTicketSales = await app.models.Ticket.getSales(11);
createdTicket = await app.models.Ticket.findById(createdTicketId);
expect(senderTicketSales.length).toEqual(0);
createdTicketSales = await app.models.Ticket.getSales(createdTicketId);
receiverTicketSales = await app.models.Ticket.getSales(receiverTicketId);
expect(createdTicket.isDeleted).toBeTruthy();
expect(createdTicketSales.length).toEqual(0);
expect(receiverTicketSales.length).toEqual(2);
});
});

View File

@ -0,0 +1,37 @@
module.exports = function(Self) {
Self.remoteMethod('checkEmptiness', {
description: 'Checks if the ticket has no packages, componenets and purchase requests',
accessType: 'READ',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
description: 'Ticket id',
http: {source: 'path'}
}
],
returns: {
arg: 'data',
type: 'boolean',
root: true
},
http: {
path: `/:id/checkEmptiness`,
verb: 'get'
}
});
Self.checkEmptiness = async id => {
const packages = await Self.app.models.TicketPackaging.find({where: {ticketFk: id}});
const services = await Self.app.models.TicketService.find({where: {ticketFk: id}});
const purchaseRequests = await Self.app.models.TicketRequest.find({where: {ticketFk: id}});
emptyTicket = !packages.length && !services.length && !purchaseRequests.length;
if (emptyTicket)
return true;
return false;
};
};

View File

@ -0,0 +1,27 @@
const app = require('vn-loopback/server/server');
describe('ticket checkEmptiness()', () => {
it('should return false if the ticket contains any packages', async() => {
let result = await app.models.Ticket.checkEmptiness(3);
expect(result).toBeFalsy();
});
it('should return false if the ticket contains any services', async() => {
let result = await app.models.Ticket.checkEmptiness(8);
expect(result).toBeFalsy();
});
it('should return false if the ticket contains any purchase request', async() => {
let result = await app.models.Ticket.checkEmptiness(11);
expect(result).toBeFalsy();
});
it('should return true if the ticket does not contain any packages, services or purchase request', async() => {
let result = await app.models.Ticket.checkEmptiness(4);
expect(result).toBeTruthy();
});
});

View File

@ -1,25 +1,25 @@
const app = require('vn-loopback/server/server');
describe('ticket isEditable()', () => {
it('should return false if the ticket given is not editable', async () => {
it('should return false if the ticket given is not editable', async() => {
let result = await app.models.Ticket.isEditable(2);
expect(result).toEqual(false);
});
it('should return false if the ticket given does not exist', async () => {
it('should return false if the ticket given does not exist', async() => {
let result = await app.models.Ticket.isEditable(99999);
expect(result).toEqual(false);
});
it('should return false if the ticket given isDeleted', async () => {
it('should return false if the ticket given isDeleted', async() => {
let result = await app.models.Ticket.isEditable(21);
expect(result).toEqual(false);
});
it('should return true if the ticket given is editable', async () => {
it('should return true if the ticket given is editable', async() => {
let result = await app.models.Ticket.isEditable(16);
expect(result).toEqual(true);

View File

@ -3,7 +3,6 @@ module.exports = Self => {
require('../methods/sale/priceDifference')(Self);
require('../methods/sale/moveToTicket')(Self);
require('../methods/sale/reserve')(Self);
require('../methods/sale/moveToNewTicket')(Self);
require('../methods/sale/removes')(Self);
require('../methods/sale/updateDiscount')(Self);
require('../methods/sale/updatePrice')(Self);

View File

@ -21,4 +21,5 @@ module.exports = Self => {
require('../methods/ticket/canBeInvoiced')(Self);
require('../methods/ticket/makeInvoice')(Self);
require('../methods/ticket/updateEditableTicket')(Self);
require('../methods/ticket/checkEmptiness')(Self);
};

View File

@ -252,7 +252,7 @@
<tr
class="clickable"
ng-repeat="ticket in $ctrl.lastThreeTickets track by ticket.id"
ng-click="$ctrl.moveLines(ticket.id)">
ng-click="$ctrl.checkEmptiness(ticket.id)">
<td number>{{::ticket.id}}</td>
<td number>{{::ticket.shipped | dateTime: 'dd/MM/yyyy HH:mm'}}</td>
<td number>{{::ticket.agencyName}}</td>
@ -263,18 +263,18 @@
<vn-horizontal>
<vn-textfield
label="Move to ticket"
model="$ctrl.moveToTicketFk"
model="$ctrl.receiverTicketId"
type="number">
</vn-textfield>
<vn-icon-button
icon="arrow_forward_ios"
ng-click="$ctrl.moveLines($ctrl.moveToTicketFk)">
ng-click="$ctrl.checkEmptiness($ctrl.receiverTicketId)">
</vn-icon-button>
</vn-horizontal>
<vn-horizontal>
<vn-button
label="New ticket"
ng-click="$ctrl.linesToNewTicket()">
ng-click="$ctrl.checkEmptiness()">
</vn-button>
<vn-icon
color-secondary
@ -295,6 +295,12 @@
message="Continue anyway?"
on-response="$ctrl.onRemoveLinesClick(response)">
</vn-confirm>
<vn-confirm
vn-id="delete-ticket"
question="Do you want to delete it?"
message="This ticket is now empty"
on-response="$ctrl.moveLines(response)">
</vn-confirm>
<vn-float-button
ng-show="$ctrl.isEditable"
ng-click="$ctrl.newOrderFromTicket()"

View File

@ -38,7 +38,7 @@ class Controller {
loadSubTotal() {
if (!this.$stateParams.id || !this.sales) return;
this.$http.get(`/ticket/api/Tickets/${this.$stateParams.id}/subtotal`).then(res => {
this.$http.get(`/api/Tickets/${this.$stateParams.id}/subtotal`).then(res => {
this.subtotal = res.data || 0.0;
});
}
@ -50,7 +50,7 @@ class Controller {
loadVAT() {
this.VAT = 0.0;
if (!this.$stateParams.id || !this.sales) return;
this.$http.get(`/ticket/api/Tickets/${this.$stateParams.id}/getVAT`).then(res => {
this.$http.get(`/api/Tickets/${this.$stateParams.id}/getVAT`).then(res => {
this.VAT = res.data || 0.0;
});
}
@ -103,29 +103,27 @@ class Controller {
return lines;
}
// Change State
onStateOkClick() {
let filter = {where: {code: 'OK'}, fields: ['id']};
let json = encodeURIComponent(JSON.stringify(filter));
this.$http.get(`/ticket/api/States?filter=${json}`).then(res => {
this.$http.get(`/api/States?filter=${json}`).then(res => {
this.onStateChange(res.data[0].id);
});
}
onStateChange(value) {
let params = {ticketFk: this.$state.params.id, stateFk: value};
this.$http.post(`/ticket/api/TicketTrackings/changeState`, params).then(() => {
this.$http.post(`/api/TicketTrackings/changeState`, params).then(() => {
this.card.reload();
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
});
}
// Remove Lines
onRemoveLinesClick(response) {
if (response === 'ACCEPT') {
let sales = this.getCheckedLines();
let params = {sales: sales, actualTicketFk: this.ticket.id};
let query = `/ticket/api/Sales/removes`;
let query = `/api/Sales/removes`;
this.$http.post(query, params).then(() => {
this.removeInstances(sales);
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
@ -143,11 +141,10 @@ class Controller {
this.$scope.deleteLines.show();
}
// Move Lines
showTransferPopover(event) {
let filter = {clientFk: this.ticket.clientFk, ticketFk: this.ticket.id};
let json = encodeURIComponent(JSON.stringify(filter));
let query = `/ticket/api/Tickets/threeLastActive?filter=${json}`;
let query = `/api/Tickets/threeLastActive?filter=${json}`;
this.$http.get(query).then(res => {
this.lastThreeTickets = res.data;
});
@ -155,31 +152,50 @@ class Controller {
this.$scope.transfer.show();
}
moveLines(ticketID) {
let sales = this.getCheckedLines();
let params = {sales: sales, newTicketFk: ticketID, actualTicketFk: this.ticket.id};
this.$http.post(`/ticket/api/Sales/moveToTicket`, params).then(() => {
this.goToTicket(ticketID);
checkEmptiness(receiverTicketId) {
let sales = this.getCheckedLines();
let areAllSalesSelected = sales.length === this.$scope.model.data.length;
this.receiverTicketId = receiverTicketId;
if (areAllSalesSelected) {
let query = `/api/Tickets/${this.ticket.id}/checkEmptiness`;
this.$http.get(query).then(res => {
if (res.data)
this.$scope.deleteTicket.show();
if (!res.data)
this.moveLines(false);
});
}
linesToNewTicket() {
let ticket = {
oldTicketFk: this.ticket.id,
if (!areAllSalesSelected)
this.moveLines(false);
}
moveLines(removeEmptyTicket) {
let sales = this.getCheckedLines();
let currentTicketData = {
currentTicketId: this.ticket.id,
clientFk: this.ticket.clientFk,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk,
warehouseFk: this.ticket.warehouseFk
};
let sales = this.getCheckedLines();
let params = {
currentTicket: currentTicketData,
receiverTicket: this.receiverTicketId ? {id: this.receiverTicketId} : currentTicketData,
sales: sales,
removeEmptyTicket: removeEmptyTicket
};
this.$http.post(`/api/Sales/MoveToNewTicket`, {ticket: ticket, sales: sales}).then(res => {
let url = this.$state.href('ticket.card.sale', {id: res.data.id}, {absolute: true});
window.open(url, '_blank');
this.$scope.transfer.hide();
this.$scope.model.refresh();
this.$http.post(`/api/Sales/moveToTicket`, params).then(res => {
if (res.data) {
this.receiverTicketId = null;
this.goToTicket(res.data.id);
}
});
}
@ -230,7 +246,6 @@ class Controller {
this.$scope.popover.relocate();
}
// Edit Line
showEditPricePopover(event, sale) {
this.sale = sale;
this.editedPrice = this.sale.price;

View File

@ -25,3 +25,5 @@ SMSAvailability: >-
Verdnatura le comunica: Pedido {{ticketFk}} día {{created | date: "dd/MM/yyyy"}}.
{{notAvailables}} no disponible/s. Disculpe las molestias.
Continue anyway?: ¿Continuar de todas formas?
This ticket is now empty: El ticket ha quedado vacio
Do you want to delete it?: ¿Quieres borrarlo?