Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into test
gitea/salix/test This commit looks good Details

This commit is contained in:
Joan Sanchez 2019-11-21 13:03:15 +01:00
commit 1841848b06
16 changed files with 271 additions and 13 deletions

View File

@ -0,0 +1,77 @@
USE `vn`;
DROP procedure IF EXISTS `ticket_recalcComponents`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `ticket_recalcComponents`(IN vTicketFk BIGINT )
proc: BEGIN
/**
* Este procedimiento trata de "rebionizar" un ticket,
* eliminando los componentes existentes e insertandolos de nuevo
*
* @param vTicketFk Id del ticket
* @return tmp.buyUltimate
*/
DECLARE vShipped DATE;
DECLARE vWarehouseFk SMALLINT;
DECLARE vAgencyModeFk INT;
DECLARE vAddressFk INT;
DECLARE vLanded DATE;
DECLARE vIsTicketEditable BOOLEAN;
DECLARE vZoneFk INTEGER;
SELECT (IFNULL(ts.alertLevel,0) >0 or IFNULL(t.refFk,"") != "") = FALSE, t.zoneFk
INTO vIsTicketEditable, vZoneFk
FROM ticket t LEFT JOIN ticketState ts ON t.id = ts.ticket
WHERE id = vTicketFk;
SELECT warehouseFk, date(shipped), addressFk, agencyModeFk, landed
INTO vWarehouseFk, vShipped, vAddressFk, vAgencyModeFk, vLanded
FROM ticket
WHERE id = vTicketFk;
CALL zoneGetShippedWarehouse(vLanded, vAddressFk , vAgencyModeFk);
CALL vn.buyUltimate (vWarehouseFk, vShipped); -- rellena la tabla buyUltimate con la ultima compra
DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
CREATE TEMPORARY TABLE tmp.ticketLot
SELECT vWarehouseFk warehouseFk, NULL available,
s.itemFk, bu.buyFk
FROM sale s
LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
WHERE s.ticketFk = vTicketFk
GROUP BY s.itemFk;
CALL vn.catalog_componentCalculate(vZoneFk, vAddressFk, vShipped);
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
CREATE TEMPORARY TABLE tmp.sale
(PRIMARY KEY (saleFk)) ENGINE = MEMORY
SELECT id saleFk, vWarehouseFk warehouseFk
FROM sale s
WHERE s.ticketFk = vTicketFk;
CALL vn.ticketComponentUpdateSale(IF(vIsTicketEditable,1,6)); -- si el ticket esta facturado, respeta los precios
IF vLanded IS NULL THEN
CALL zoneGetLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk);
UPDATE vn2008.Tickets t
SET t.landing = (SELECT landed FROM tmp.zoneGetLanded)
WHERE Id_Ticket = vTicketFk;
DROP TEMPORARY TABLE tmp.zoneGetLanded;
END IF;
DROP TEMPORARY TABLE tmp.buyUltimate;
DROP TEMPORARY TABLE tmp.ticketComponentPrice;
DROP TEMPORARY TABLE tmp.ticketComponent;
DROP TEMPORARY TABLE tmp.sale;
END$$
DELIMITER ;

View File

@ -0,0 +1,77 @@
USE `vn`;
DROP procedure IF EXISTS `ticket_recalcComponentsForcePrice`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `ticket_recalcComponentsForcePrice`(IN vTicketFk BIGINT, vIsTicketEditable BOOLEAN )
proc: BEGIN
/**
* Este procedimiento trata de "rebionizar" un ticket,
* eliminando los componentes existentes e insertandolos de nuevo
*
* @param vTicketFk Id del ticket
* @return tmp.buyUltimate
*/
DECLARE vShipped DATE;
DECLARE vWarehouseFk SMALLINT;
DECLARE vAgencyModeFk INT;
DECLARE vAddressFk INT;
DECLARE vLanded DATE;
DECLARE vZoneFk INTEGER;
IF vIsTicketEditable IS NULL THEN
SELECT (IFNULL(ts.alertLevel,0) >0 or IFNULL(t.refFk,"") != "") = FALSE, t.zoneFk
INTO vIsTicketEditable, vZoneFk
FROM ticket t LEFT JOIN ticketState ts ON t.id = ts.ticket
WHERE id = vTicketFk;
END IF;
SELECT warehouseFk, date(shipped), addressFk, agencyModeFk, landed
INTO vWarehouseFk, vShipped, vAddressFk, vAgencyModeFk, vLanded
FROM ticket
WHERE id = vTicketFk;
CALL zoneGetShippedWarehouse(vLanded, vAddressFk , vAgencyModeFk);
CALL vn.buyUltimate (vWarehouseFk, vShipped); -- rellena la tabla buyUltimate con la ultima compra
DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
CREATE TEMPORARY TABLE tmp.ticketLot
SELECT vWarehouseFk warehouseFk, NULL available,
s.itemFk, bu.buyFk
FROM sale s
LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
WHERE s.ticketFk = vTicketFk
GROUP BY s.itemFk;
CALL vn.catalog_componentCalculate(vZoneFk, vAddressFk, vShipped);
DROP TEMPORARY TABLE IF EXISTS tmp.sale;
CREATE TEMPORARY TABLE tmp.sale
(PRIMARY KEY (saleFk)) ENGINE = MEMORY
SELECT id saleFk, vWarehouseFk warehouseFk
FROM sale s
WHERE s.ticketFk = vTicketFk;
CALL vn.ticketComponentUpdateSale(IF(vIsTicketEditable,1,6)); -- si el ticket esta facturado, respeta los precios
IF vLanded IS NULL THEN
CALL zoneGetLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk);
UPDATE vn2008.Tickets t
SET t.landing = (SELECT landed FROM tmp.zoneGetLanded)
WHERE Id_Ticket = vTicketFk;
DROP TEMPORARY TABLE tmp.zoneGetLanded;
END IF;
DROP TEMPORARY TABLE tmp.buyUltimate;
DROP TEMPORARY TABLE tmp.ticketComponentPrice;
DROP TEMPORARY TABLE tmp.ticketComponent;
DROP TEMPORARY TABLE tmp.sale;
END$$
DELIMITER ;

View File

@ -1,6 +1,7 @@
import ngModule from '../../module'; import ngModule from '../../module';
import Dialog from '../dialog'; import Dialog from '../dialog';
import template from './confirm.html'; import template from './confirm.html';
import './style.scss';
export default class Confirm extends Dialog { export default class Confirm extends Dialog {
constructor($element, $, $transclude) { constructor($element, $, $transclude) {

View File

@ -0,0 +1,3 @@
.vn-confirm .window {
max-width: 30em
}

View File

@ -1,6 +1,6 @@
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('calculate', { Self.remoteMethodCtx('recalculatePrice', {
description: 'Calculates the price of a sale and its components', description: 'Calculates the price of a sale and its components',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
@ -15,12 +15,12 @@ module.exports = Self => {
root: true root: true
}, },
http: { http: {
path: `/:id/calculate`, path: `/:id/recalculatePrice`,
verb: 'post' verb: 'post'
} }
}); });
Self.calculate = async(ctx, id) => { Self.recalculatePrice = async(ctx, id) => {
const models = Self.app.models; const models = Self.app.models;
const sale = await Self.findById(id); const sale = await Self.findById(id);

View File

@ -1,11 +1,11 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('sale calculate()', () => { describe('sale recalculatePrice()', () => {
const saleId = 7; const saleId = 7;
it('should update the sale price', async() => { it('should update the sale price', async() => {
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const response = await app.models.Sale.calculate(ctx, saleId); const response = await app.models.Sale.recalculatePrice(ctx, saleId);
expect(response.affectedRows).toBeDefined(); expect(response.affectedRows).toBeDefined();
}); });
@ -13,7 +13,7 @@ describe('sale calculate()', () => {
it('should throw an error if the ticket is not editable', async() => { it('should throw an error if the ticket is not editable', async() => {
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const immutableSaleId = 1; const immutableSaleId = 1;
await app.models.Sale.calculate(ctx, immutableSaleId) await app.models.Sale.recalculatePrice(ctx, immutableSaleId)
.catch(response => { .catch(response => {
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`)); expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response; error = response;

View File

@ -0,0 +1,31 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('recalculateComponents', {
description: 'Calculates the price of a sale and its components',
accessType: 'WRITE',
accepts: [{
arg: 'id',
description: 'The ticket id',
type: 'number',
required: true,
http: {source: 'path'}
}],
returns: {
type: 'Number',
root: true
},
http: {
path: `/:id/recalculateComponents`,
verb: 'POST'
}
});
Self.recalculateComponents = async(ctx, id) => {
const isEditable = await Self.isEditable(ctx, id);
if (!isEditable)
throw new UserError(`The current ticket can't be modified`);
return Self.rawSql('CALL vn.ticket_recalcComponents(?)', [id]);
};
};

View File

@ -0,0 +1,24 @@
const app = require('vn-loopback/server/server');
describe('ticket recalculateComponents()', () => {
const ticketId = 11;
it('should update the ticket components', async() => {
const ctx = {req: {accessToken: {userId: 9}}};
const response = await app.models.Ticket.recalculateComponents(ctx, ticketId);
expect(response.affectedRows).toBeDefined();
});
it('should throw an error if the ticket is not editable', async() => {
const ctx = {req: {accessToken: {userId: 9}}};
const immutableTicketId = 1;
await app.models.Ticket.recalculateComponents(ctx, immutableTicketId)
.catch(response => {
expect(response).toEqual(new Error(`The current ticket can't be modified`));
error = response;
});
expect(error).toBeDefined();
});
});

View File

@ -5,7 +5,7 @@ module.exports = Self => {
require('../methods/sale/updatePrice')(Self); require('../methods/sale/updatePrice')(Self);
require('../methods/sale/updateQuantity')(Self); require('../methods/sale/updateQuantity')(Self);
require('../methods/sale/updateConcept')(Self); require('../methods/sale/updateConcept')(Self);
require('../methods/sale/calculate')(Self); require('../methods/sale/recalculatePrice')(Self);
Self.validatesPresenceOf('concept', { Self.validatesPresenceOf('concept', {
message: `Concept cannot be blank` message: `Concept cannot be blank`

View File

@ -26,6 +26,7 @@ module.exports = Self => {
require('../methods/ticket/addSale')(Self); require('../methods/ticket/addSale')(Self);
require('../methods/ticket/transferSales')(Self); require('../methods/ticket/transferSales')(Self);
require('../methods/ticket/canHaveStowaway')(Self); require('../methods/ticket/canHaveStowaway')(Self);
require('../methods/ticket/recalculateComponents')(Self);
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
if (ctx.isNewInstance) return; if (ctx.isNewInstance) return;

View File

@ -198,6 +198,13 @@
<vn-confirm <vn-confirm
vn-id="confirm-delivery-note" vn-id="confirm-delivery-note"
on-accept="$ctrl.sendDeliveryNote()" on-accept="$ctrl.sendDeliveryNote()"
question="Send Delivery Note" question="Are you sure you want to send it?"
message="Are you sure you want to send it?"> message="Send Delivery Note">
</vn-confirm>
<vn-confirm
vn-id="recalculate-components-confirmation"
on-accept="$ctrl.recalculateComponents()"
question="Are you sure you want to recalculate the components?"
message="Recalculate components">
</vn-confirm> </vn-confirm>

View File

@ -35,6 +35,11 @@ class Controller extends Component {
callback: this.showRegenerateInvoiceDialog, callback: this.showRegenerateInvoiceDialog,
show: () => this.hasInvoice() show: () => this.hasInvoice()
}, },
{
name: 'Recalculate components',
callback: this.comfirmRecalculateComponents,
show: () => this.isEditable
},
]; ];
} }
@ -287,9 +292,26 @@ class Controller extends Component {
return this.ticket.refFk !== null; return this.ticket.refFk !== null;
} }
/**
* Shows a delivery-note send confirmation
*/
confirmDeliveryNote() { confirmDeliveryNote() {
this.$.confirmDeliveryNote.show(); this.$.confirmDeliveryNote.show();
} }
/**
* Shows an invoice confirmation
*/
comfirmRecalculateComponents() {
this.$.recalculateComponentsConfirmation.show();
}
recalculateComponents() {
const query = `Tickets/${this.ticket.id}/recalculateComponents`;
this.$http.post(query).then(res => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
});
}
} }
Controller.$inject = ['$element', '$scope', 'aclService', '$httpParamSerializer']; Controller.$inject = ['$element', '$scope', 'aclService', '$httpParamSerializer'];

View File

@ -1,6 +1,6 @@
import './index.js'; import './index.js';
describe('Ticket Component vnTicketDescriptor', () => { fdescribe('Ticket Component vnTicketDescriptor', () => {
let $httpParamSerializer; let $httpParamSerializer;
let $httpBackend; let $httpBackend;
let controller; let controller;
@ -210,4 +210,17 @@ describe('Ticket Component vnTicketDescriptor', () => {
expect(controller.isTicketModule).toHaveBeenCalledWith(); expect(controller.isTicketModule).toHaveBeenCalledWith();
}); });
}); });
describe('recalculateComponents()', () => {
it('should make a query and show a success snackbar', () => {
spyOn(controller.vnApp, 'showSuccess');
$httpBackend.when('POST', 'Tickets/2/recalculateComponents').respond();
$httpBackend.expect('POST', 'Tickets/2/recalculateComponents').respond();
controller.recalculateComponents();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
});
});
}); });

View File

@ -26,3 +26,5 @@ Are you sure you want to regenerate the invoice?: ¿Seguro que quieres regenerar
Invoice sent for a regeneration, will be available in a few minutes: La factura ha sido enviada para ser regenerada, estará disponible en unos minutos Invoice sent for a regeneration, will be available in a few minutes: La factura ha sido enviada para ser regenerada, estará disponible en unos minutos
Shipped hour updated: Hora de envio modificada Shipped hour updated: Hora de envio modificada
Deleted ticket: Ticket eliminado Deleted ticket: Ticket eliminado
Recalculate components: Recalcular componentes
Are you sure you want to recalculate the components?: ¿Seguro que quieres recalcular los componentes?

View File

@ -548,7 +548,7 @@ class Controller {
calculateSalePrice() { calculateSalePrice() {
const sale = this.checkedLines()[0]; const sale = this.checkedLines()[0];
const query = `Sales/${sale.id}/calculate`; const query = `Sales/${sale.id}/recalculatePrice`;
this.$http.post(query).then(res => { this.$http.post(query).then(res => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!')); this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
this.$scope.model.refresh(); this.$scope.model.refresh();

View File

@ -349,7 +349,7 @@ describe('Ticket', () => {
it('should make an HTTP post query ', () => { it('should make an HTTP post query ', () => {
controller.sales[0].checked = true; controller.sales[0].checked = true;
$httpBackend.when('POST', `Sales/4/calculate`).respond(200); $httpBackend.when('POST', `Sales/4/recalculatePrice`).respond(200);
$httpBackend.whenGET(`Tickets/1/subtotal`).respond(200, 227.5); $httpBackend.whenGET(`Tickets/1/subtotal`).respond(200, 227.5);
$httpBackend.whenGET(`Tickets/1/getVAT`).respond(200, 10.5); $httpBackend.whenGET(`Tickets/1/getVAT`).respond(200, 10.5);
$httpBackend.whenGET(`Tickets/1/isEditable`).respond(); $httpBackend.whenGET(`Tickets/1/isEditable`).respond();