#6335 -ticketAdvance_splitOrCreate #1817

Merged
alexm merged 15 commits from 6335-ticketAdvance_splitOrCreate into dev 2023-11-17 06:55:59 +00:00
6 changed files with 123 additions and 74 deletions
Showing only changes of commit a80715583c - Show all commits

View File

@ -8,8 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2346.01] - 2023-11-16 ## [2346.01] - 2023-11-16
### Added ### Added
- (Ticket -> Adelantar) Permite mover lineas sin generar negativos
- (Ticket -> Adelantar) Permite modificar la fecha de los tickets
### Changed ### Changed
### Fixed ### Fixed
- (Ticket -> RocketChat) Arreglada detección de cambios
## [2342.01] - 2023-11-02 ## [2342.01] - 2023-11-02

View File

@ -224,7 +224,9 @@
"date in the future": "Fecha en el futuro", "date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada", "reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono", "This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "isWithoutNegatives", "isWithoutNegatives": "Sin negativos",
"newTicket": "Nuevo ticket",
"keepPrice": "Mantener precios",
"routeFk": "routeFk", "routeFk": "routeFk",
"Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador", "Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador",
"No hay un contrato en vigor": "No hay un contrato en vigor", "No hay un contrato en vigor": "No hay un contrato en vigor",
@ -321,9 +323,13 @@
"Select a different client": "Seleccione un cliente distinto", "Select a different client": "Seleccione un cliente distinto",
"Fill all the fields": "Rellene todos los campos", "Fill all the fields": "Rellene todos los campos",
"The response is not a PDF": "La respuesta no es un PDF", "The response is not a PDF": "La respuesta no es un PDF",
"Ticket without Route": "Ticket sin ruta",
"Booking completed": "Reserva completada", "Booking completed": "Reserva completada",
"The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación",
"The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina",
"quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina" "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina",
"__cachedRelations": "__cachedRelations",
"__data": "__data",
"__strict": "__strict",
"__persisted": "__persisted",
"client": "client"
} }

View File

@ -163,7 +163,7 @@ module.exports = Self => {
} }
} }
const originalTicket = await models.Ticket.findOne({ const ticketToChange = await models.Ticket.findOne({
where: {id: args.id}, where: {id: args.id},
fields: [ fields: [
'id', 'id',
@ -196,13 +196,7 @@ module.exports = Self => {
}, },
] ]
}, myOptions); }, myOptions);
const originalTicket = JSON.parse(JSON.stringify(ticketToChange));
Outdated
Review

changes

changes
args.routeFk = null;
if (args.isWithoutNegatives === false) delete args.isWithoutNegatives;
const updatedTicket = Object.assign({}, args);
delete updatedTicket.ctx;
delete updatedTicket.option;
const ticketChages = { const ticketChages = {
clientFk: args.clientFk, clientFk: args.clientFk,
nickname: args.nickname, nickname: args.nickname,
@ -219,18 +213,21 @@ module.exports = Self => {
let response; let response;
if (args.keepPrice) { if (args.keepPrice) {
ticketChages.routeFk = null; ticketChages.routeFk = null;
response = await originalTicket.updateAttributes(ticketChages, myOptions); response = await ticketToChange.updateAttributes(ticketChages, myOptions);
} else { } else {
const hasToBeUnrouted = true; const hasToBeUnrouted = true;
const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
response = await Self.rawSql( response = await Self.rawSql(
query, 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[args.id].concat(Object.values(ticketChages), [hasToBeUnrouted, args.option]), [args.id].concat(Object.values(ticketChages), [hasToBeUnrouted, args.option]),
myOptions myOptions
); );
} }
if (args.isWithoutNegatives === false) delete args.isWithoutNegatives;
const updatedTicket = Object.assign({}, args, JSON.parse(JSON.stringify(ticketToChange)));
delete updatedTicket.ctx;
delete updatedTicket.option;
if (originalTicket.addressFk != updatedTicket.addressFk && args.id) { if (ticketToChange.addressFk != updatedTicket.addressFk && args.id) {
await models.TicketObservation.destroyAll({ await models.TicketObservation.destroyAll({
ticketFk: args.id ticketFk: args.id
}, myOptions); }, myOptions);
@ -252,15 +249,20 @@ module.exports = Self => {
await models.TicketObservation.create(clonedObservations, myOptions); await models.TicketObservation.create(clonedObservations, myOptions);
} }
} }
const changes = loggable.getChanges(originalTicket, updatedTicket); const changes = loggable.getChanges(originalTicket, updatedTicket);
const hasChanges = Object.keys(changes.old).length > 0 || Object.keys(changes.new).length > 0; const hasChanges = Object.keys(changes.old).length > 0 || Object.keys(changes.new).length > 0;
if (hasChanges) { if (hasChanges) {
delete changes.old.client;
delete changes.old.address;
delete changes.new.client;
delete changes.new.address;
const oldProperties = await loggable.translateValues(Self, changes.old); const oldProperties = await loggable.translateValues(Self, changes.old);
const newProperties = await loggable.translateValues(Self, changes.new); const newProperties = await loggable.translateValues(Self, changes.new);
const salesPersonId = originalTicket.client().salesPersonFk; const salesPersonId = ticketToChange.client().salesPersonFk;
if (salesPersonId) { if (salesPersonId) {
const url = await Self.app.models.Url.getUrl(); const url = await Self.app.models.Url.getUrl();
@ -283,7 +285,7 @@ module.exports = Self => {
response.id = args.id; response.id = args.id;
console.log(originalTicketId, args.newTicket ? ` TRANSFER TO → ` : ` CREATE NEW TICKET → `, args.id); console.log(originalTicketId, args.newTicket ? ` TRANSFER TO → ` : ` CREATE NEW TICKET → `, args.id);
if (tx) await tx.rollback(); if (tx) await tx.commit();
return response; return response;
} catch (e) { } catch (e) {

View File

@ -29,7 +29,7 @@
</vn-button> </vn-button>
<vn-button <vn-button
disabled="$ctrl.checked.length === 0" disabled="$ctrl.checked.length === 0"
icon="swap_horiz" icon="alt_route"
ng-click="splitTickets.show($event)" ng-click="splitTickets.show($event)"
vn-tooltip="Advance tickets without negatives"> vn-tooltip="Advance tickets without negatives">
</vn-button> </vn-button>
@ -121,7 +121,7 @@
<span <span
ng-click="ticketDescriptor.show($event, ticket.id)" ng-click="ticketDescriptor.show($event, ticket.id)"
class="link"> class="link">
{{::ticket.id | dashIfEmpty}} {{::ticket.id}}
</span> </span>
</td> </td>
<td>{{::ticket.ipt | dashIfEmpty}}</td> <td>{{::ticket.ipt | dashIfEmpty}}</td>
@ -137,7 +137,7 @@
<span <span
class="chip {{$ctrl.totalPriceColor(ticket.totalWithVat)}}" class="chip {{$ctrl.totalPriceColor(ticket.totalWithVat)}}"
title="{{$ctrl.totalPriceTitle(ticket.totalWithVat) | translate}}"> title="{{$ctrl.totalPriceTitle(ticket.totalWithVat) | translate}}">
{{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}} {{::ticket.totalWithVat | currency: 'EUR': 2}}
</span> </span>
</td> </td>
<td separator> <td separator>
@ -193,22 +193,24 @@
<!-- Split progress --> <!-- Split progress -->
<vn-dialog <vn-dialog
vn-id="splitProgress" vn-id="splitProgress"
message="Split progress"> message="Progress">
<tpl-body> <tpl-body>
<vn-vertical> <vn-vertical>
<vn-spinner <vn-spinner
enable="splitProgress.enable"> enable="splitProgress.enable">
</vn-spinner> </vn-spinner>
<vn-label-value <vn-label-value
label="Progres" label="Total progress"
value="[{{$ctrl.progress.length}}/{{$ctrl.checked.length}}]"> value="[{{$ctrl.progress.length}}/{{::$ctrl.checked.length}}]">
</vn-label-value> </vn-label-value>
<span class="vn-mt-md" ng-if="$ctrl.splitErrors.length" translate>Error list</span>
<vn-vertical ng-repeat="error in $ctrl.splitErrors">
<vn-label-value <vn-label-value
ng-if="$ctrl.splitErrors.length" label="{{error.id}}"
label="Errors list" value="{{error.reason}}">
value="{{$ctrl.splitErrors}}">
</vn-label-value> </vn-label-value>
</vn-vertical> </vn-vertical>
</vn-vertical>
</tpl-body> </tpl-body>
<tpl-buttons> <tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}" ng-click="splitProgress.cancel = true"/> <input type="button" response="cancel" translate-attr="{value: 'Cancel'}" ng-click="splitProgress.cancel = true"/>

View File

@ -120,10 +120,16 @@ export default class Controller extends Section {
'\n' + this.$t(`Destination agency`, {agency: agency}); '\n' + this.$t(`Destination agency`, {agency: agency});
} }
moveTicketsAdvance() { async moveTicketsAdvance() {
let ticketsToMove = []; let ticketsToMove = [];
// falta ver que hacer con los nulls for (const ticket of this.checked) {
for (ticket of this.checked) { if (!ticket.id) {
try {
const {query, params} = await this.requestComponentUpdate(ticket, false);
this.$http.post(query, params);
} catch (e) {}
continue;
}
ticketsToMove.push({ ticketsToMove.push({
originId: ticket.futureId, originId: ticket.futureId,
destinationId: ticket.id, destinationId: ticket.id,
@ -136,10 +142,12 @@ export default class Controller extends Section {
const params = {tickets: ticketsToMove}; const params = {tickets: ticketsToMove};
return this.$http.post('Tickets/merge', params) return this.$http.post('Tickets/merge', params)
.then(() => { .then(() => {
this.$.model.refresh(); this.refresh();
this.vnApp.showSuccess(this.$t('Success')); if (ticketsToMove.length)
this.vnApp.showSuccess(this.$t('Success', {tickets: ticketsToMove.length}));
}); });
} }
async getLanded(params) { async getLanded(params) {
const query = `Agencies/getLanded`; const query = `Agencies/getLanded`;
return this.$http.get(query, {params}).then(res => { return this.$http.get(query, {params}).then(res => {
@ -160,20 +168,52 @@ export default class Controller extends Section {
this.$.splitProgress.enable = true; this.$.splitProgress.enable = true;
this.$.splitProgress.cancel = false; this.$.splitProgress.cancel = false;
this.$.splitProgress.show(); this.$.splitProgress.show();
for (const ticket of this.checked) { for (const ticket of this.checked) {
if (this.$.splitProgress.cancel) break; if (this.$.splitProgress.cancel) break;
try { try {
const {query, params} = await this.requestComponentUpdate(ticket, true);
this.$http.post(query, params)
.catch(e => {
this.splitErrors.push({id: ticket.futureId, reason: e.message});
})
.finally(() => this.progressAdd(ticket.futureId));
} catch (e) {
this.splitErrors.push({id: ticket.futureId, reason: e.message});
this.progressAdd(ticket.futureId);
}
}
}
progressAdd(ticketId) {
this.progress.push(ticketId);
if (this.progress.length == this.checked.length) {
this.$.splitProgress.enable = false;
this.refresh();
if ((this.progress.length - this.splitErrors.length) > 0) {
this.vnApp.showSuccess(this.$t('Success', {
tickets: this.progress.length - this.splitErrors.length
}));
}
}
}
async requestComponentUpdate(ticket, isWithoutNegatives) {
const query = `tickets/${ticket.futureId}/componentUpdate`;
if (!ticket.landed) { if (!ticket.landed) {
console.log(ticket);
const newLanded = await this.getLanded({ const newLanded = await this.getLanded({
shipped: this.$.model.userParams.dateToAdvance, shipped: this.$.model.userParams.dateToAdvance,
addressFk: ticket.addressFk, addressFk: ticket.addressFk,
agencyModeFk: ticket.agencyModeFk, agencyModeFk: ticket.agencyModeFk,
warehouseFk: ticket.warehouseFk warehouseFk: ticket.warehouseFk
}); });
ticket.landed = newLanded?.landed; if (!newLanded)
ticket.zoneFk = newLanded?.zoneFk; throw new Error(this.$t(`No delivery zone available for this landing date`));
ticket.landed = newLanded.landed;
ticket.zoneFk = newLanded.zoneFk;
} }
const query = `tickets/${ticket.futureId}/componentUpdate`;
const params = { const params = {
clientFk: ticket.clientFk, clientFk: ticket.clientFk,
Outdated
Review

dest OR origin

dest OR origin
nickname: ticket.nickname, nickname: ticket.nickname,
@ -185,28 +225,18 @@ export default class Controller extends Section {
shipped: this.$.model.userParams.dateToAdvance, shipped: this.$.model.userParams.dateToAdvance,
landed: ticket.landed, landed: ticket.landed,
isDeleted: false, isDeleted: false,
isWithoutNegatives: true, isWithoutNegatives,
withWarningAccept: false,
newTicket: ticket.id ?? undefined, newTicket: ticket.id ?? undefined,
keepPrice: true keepPrice: true
}; };
console.log(params);
return {query, params};
}
this.$http.post(query, params) refresh() {
.catch(e => {
this.splitErrors.push({id: ticket.futureId, reason: e.message});
})
.finally(() => {
this.progress.push(ticket.futureId);
if (this.progress.length == this.checked.length) {
this.$.splitProgress.enable = false;
this.$.model.refresh(); this.$.model.refresh();
this.vnApp.showSuccess(this.$t('Success'));
this.$checkAll = null; this.$checkAll = null;
} }
});
} catch (e) {}
}
}
exprBuilder(param, value) { exprBuilder(param, value) {
switch (param) { switch (param) {

View File

@ -1,7 +1,7 @@
Advance tickets: Adelantar tickets Advance tickets: Adelantar tickets
Search advance tickets by date: Busca tickets para adelantar por fecha Search advance tickets by date: Busca tickets para adelantar por fecha
Advance confirmation: ¿Desea adelantar {{checked}} tickets? Advance confirmation: ¿Desea adelantar {{checked}} tickets?
Success: Tickets movidos correctamente Success: "{{tickets}} Tickets movidos correctamente"
Lines: Líneas Lines: Líneas
Liters: Litros Liters: Litros
Item Packing Type: Encajado Item Packing Type: Encajado
@ -9,3 +9,7 @@ Origin agency: "Agencia origen: {{agency}}"
Destination agency: "Agencia destino: {{agency}}" Destination agency: "Agencia destino: {{agency}}"
Less than 50€: Menor a 50€ Less than 50€: Menor a 50€
Not Movable: No movibles Not Movable: No movibles
Error list: Lista de errores
Progress: Progreso
Total progress: Progreso total
Advance tickets (without negatives): Adelantar tickets (sin negativos)