ticket subtotal should check service prices #1151
gitea/salix/dev This commit has test failures Details

This commit is contained in:
Joan Sanchez 2019-02-21 08:43:04 +01:00
parent 0f496b9500
commit 06be535115
16 changed files with 213 additions and 22 deletions

View File

@ -1,12 +1,12 @@
const app = require('vn-loopback/server/server');
describe('Client card', () => {
describe('Client get', () => {
it('should call the card() method to receive a formated card of Bruce Wayne', async() => {
let id = 101;
let result = await app.models.Client.getCard(id);
expect(result.id).toEqual(101);
expect(result.name).toEqual('Bruce Wayne');
expect(result.debt).toEqual(579.1);
expect(result.debt).toEqual(595.81);
});
});

View File

@ -17,7 +17,7 @@ describe('client summary()', () => {
it('should return a summary object containing debt', async() => {
let result = await app.models.Client.summary(101);
expect(result.debt.debt).toEqual(579.1);
expect(result.debt.debt).toEqual(595.81);
});
it('should return a summary object containing averageInvoiced', async() => {

View File

@ -4,6 +4,6 @@ describe('ticket getTaxes()', () => {
it('should return the tax of a given ticket', async() => {
let result = await app.models.Ticket.getTaxes(1);
expect(result[0].tax).toEqual(7.44);
expect(result[0].tax).toEqual(7.64);
});
});

View File

@ -4,7 +4,7 @@ describe('ticket getTotal()', () => {
it('should return the total of a ticket', async() => {
let result = await app.models.Ticket.getTotal(1);
expect(result).toEqual(155.89);
expect(result).toEqual(158.09);
});
it(`should return zero if the ticket doesn't have lines`, async() => {

View File

@ -4,7 +4,7 @@ describe('ticket getVAT()', () => {
it('should call the getVAT method and return the response', async() => {
await app.models.Ticket.getVAT(1)
.then(response => {
expect(response).toEqual(20.29);
expect(response).toEqual(20.49);
});
});

View File

@ -0,0 +1,15 @@
const app = require('vn-loopback/server/server');
describe('ticket subtotal()', () => {
it('should return the subtotal of a ticket', async() => {
let result = await app.models.Ticket.subtotal(1);
expect(result).toEqual(137.60);
});
it(`should return zero if the ticket doesn't have lines`, async() => {
let result = await app.models.Ticket.subtotal(13);
expect(result).toEqual(0.00);
});
});

View File

@ -14,21 +14,21 @@ describe('ticket summary()', () => {
expect(result.sales.length).toEqual(4);
});
it('should return a summary object containing subTotal for 1 ticket', async() => {
it('should return a summary object containing subtotal for 1 ticket', async() => {
let result = await app.models.Ticket.summary(1);
expect(Math.round(result.subTotal * 100) / 100).toEqual(135.60);
expect(Math.round(result.subtotal * 100) / 100).toEqual(137.60);
});
it('should return a summary object containing VAT for 1 ticket', async() => {
let result = await app.models.Ticket.summary(1);
expect(Math.round(result.VAT * 100) / 100).toEqual(20.29);
expect(Math.round(result.vat * 100) / 100).toEqual(20.49);
});
it('should return a summary object containing total for 1 ticket', async() => {
let result = await app.models.Ticket.summary(1);
let total = result.subTotal + result.VAT;
let total = result.subtotal + result.vat;
let expectedTotal = Math.round(total * 100) / 100;
expect(result.total).toEqual(expectedTotal);

View File

@ -0,0 +1,40 @@
module.exports = Self => {
Self.remoteMethod('subtotal', {
description: 'Returns the total of a ticket',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'ticket id',
http: {source: 'path'}
}],
returns: {
type: 'number',
root: true
},
http: {
path: `/:id/subtotal`,
verb: 'GET'
}
});
Self.subtotal = async ticketFk => {
const sale = Self.app.models.Sale;
const ticketSales = await sale.find({where: {ticketFk}});
const ticketService = Self.app.models.TicketService;
const ticketServices = await ticketService.find({where: {ticketFk}});
let subtotal = 0.00;
ticketSales.forEach(sale => {
subtotal += sale.price * sale.quantity * ((100 - sale.discount) / 100);
});
ticketServices.forEach(service => {
subtotal += service.price * service.quantity;
});
return Math.round(subtotal * 100) / 100;
};
};

View File

@ -23,9 +23,9 @@ module.exports = Self => {
let models = Self.app.models;
let summaryObj = await getTicketData(Self, ticketFk);
summaryObj.sales = await getSales(models.Sale, ticketFk);
summaryObj.subTotal = getSubTotal(summaryObj.sales);
summaryObj.VAT = await models.Ticket.getVAT(ticketFk);
summaryObj.total = await models.Ticket.getTotal(ticketFk);
summaryObj.subtotal = await models.Ticket.subtotal(ticketFk);
summaryObj.vat = await models.Ticket.getVAT(ticketFk);
summaryObj.total = summaryObj.subtotal + summaryObj.vat;
summaryObj.packagings = await models.TicketPackaging.find({
where: {ticketFk: ticketFk},
include: [{relation: 'packaging',

View File

@ -5,6 +5,7 @@ module.exports = Self => {
require('../methods/ticket/summary')(Self);
require('../methods/ticket/getTotal')(Self);
require('../methods/ticket/getTaxes')(Self);
require('../methods/ticket/subtotal')(Self);
require('../methods/ticket/componentUpdate')(Self);
require('../methods/ticket/new')(Self);
require('../methods/ticket/isEditable')(Self);

View File

@ -46,7 +46,7 @@
</vn-button>
</vn-tool-bar>
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.subTotal | currency: 'EUR':2}}</p>
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.subtotal | currency: 'EUR':2}}</p>
<p><vn-label translate>VAT</vn-label> {{$ctrl.VAT | currency: 'EUR':2}}</p>
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.total | currency: 'EUR':2}}</strong></p>
</vn-one>

View File

@ -36,9 +36,10 @@ class Controller {
}
loadSubTotal() {
this.subTotal = 0.0;
if (!this.sales) return;
this.subTotal = this.sales.reduce((sum, sale) => sum + this.getSaleTotal(sale), 0.0);
if (!this.$stateParams.id || !this.sales) return;
this.$http.get(`/ticket/api/Tickets/${this.$stateParams.id}/subtotal`).then(res => {
this.subtotal = res.data || 0.0;
});
}
getSaleTotal(sale) {
@ -54,7 +55,7 @@ class Controller {
}
get total() {
return this.subTotal + this.VAT;
return this.subtotal + this.VAT;
}
onMoreOpen() {

View File

@ -41,6 +41,7 @@ describe('Ticket', () => {
$httpBackend = _$httpBackend_;
$httpBackend.whenGET(/api\/Tickets\/1\/getSales.*/).respond(sales);
$httpBackend.whenGET(`/ticket/api/Tickets/1/getVAT`).respond(200, 10.5);
$httpBackend.whenGET(`/ticket/api/Tickets/1/subtotal`).respond(200, 227.5);
$element = $compile('<vn-ticket-sale ticket="ticket"></vn-ticket-sale>')($scope);
controller = $element.controller('vnTicketSale');
@ -67,9 +68,9 @@ describe('Ticket', () => {
});
});
describe('total/VAT/subTotal properties', () => {
describe('total/VAT/subtotal properties', () => {
it('should fill total, VAT and subTotal', () => {
expect(controller.subTotal).toEqual(227.5);
expect(controller.subtotal).toEqual(227.5);
expect(controller.VAT).toEqual(10.5);
expect(controller.total).toEqual(238);
});

View File

@ -46,8 +46,8 @@
</vn-label-value>
</vn-one>
<vn-one class="taxes">
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.summary.subTotal | currency: 'EUR':2}}</p>
<p><vn-label translate>VAT</vn-label> {{$ctrl.summary.VAT | currency: 'EUR':2}}</p>
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.summary.subtotal | currency: 'EUR':2}}</p>
<p><vn-label translate>VAT</vn-label> {{$ctrl.summary.vat | currency: 'EUR':2}}</p>
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.summary.total | currency: 'EUR':2}}</strong></p>
</vn-one>
<vn-auto name="sales">

View File

@ -0,0 +1,99 @@
USE `vn`;
DROP procedure IF EXISTS `ticketGetTax`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `ticketGetTax`()
READS SQL DATA
BEGIN
/**
* Calcula la base imponible, el IVA y el recargo de equivalencia para
* un conjunto de tickets.
*
* @table tmp.ticket(ticketFk) Identificadores de los tickets a calcular
* @return tmp.ticketAmount
* @return tmp.ticketTax Impuesto desglosado para cada ticket.
*/
DROP TEMPORARY TABLE IF EXISTS tmp.addressCompany;
CREATE TEMPORARY TABLE tmp.addressCompany
(INDEX (addressFk, companyFk))
ENGINE = MEMORY
SELECT DISTINCT t.addressFk, t.companyFk
FROM tmp.ticket tmpTicket
JOIN ticket t ON t.id = tmpTicket.ticketFk;
CALL addressTaxArea ();
/** Solo se calcula la base imponible (taxableBase) y el impuesto se calculará posteriormente
* No se debería cambiar el sistema por problemas con los decimales
*/
DROP TEMPORARY TABLE IF EXISTS tmp.ticketTax;
CREATE TEMPORARY TABLE tmp.ticketTax
(INDEX (ticketFk))
ENGINE = MEMORY
SELECT tmpTicket.ticketFk,
bp.pgcFk,
SUM(s.quantity * s.price * (100 - s.discount)/100 ) AS taxableBase,
pgc.rate,
tc.code
FROM tmp.ticket tmpTicket
JOIN sale s ON s.ticketFk = tmpTicket.ticketFk
JOIN item i ON i.id = s.itemFk
JOIN ticket t ON t.id = tmpTicket.ticketFk
JOIN supplier su ON su.id = t.companyFk
JOIN tmp.addressTaxArea ata
ON ata.addressFk = t.addressFk AND ata.companyFk = t.companyFk
JOIN itemTaxCountry itc
ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
JOIN bookingPlanner bp
ON bp.countryFk = su.countryFk
AND bp.taxAreaFk = ata.areaFk
AND bp.taxClassFk = itc.taxClassFk
JOIN pgc ON pgc.code = bp.pgcFk
JOIN taxClass tc ON tc.id = bp.taxClassFk
GROUP BY tmpTicket.ticketFk, pgc.code,pgc.rate
HAVING taxableBase != 0;
DROP TEMPORARY TABLE IF EXISTS tmp.ticketServiceTax;
CREATE TEMPORARY TABLE tmp.ticketServiceTax
(INDEX (ticketFk))
ENGINE = MEMORY
SELECT tt.ticketFk,
SUM(ts.quantity * ts.price) AS taxableBase,
pgc.rate,
tc.code
FROM tmp.ticketTax tt
JOIN ticketService ts ON ts.ticketFk = tt.ticketFk
JOIN ticket t ON t.id = tt.ticketFk
JOIN supplier su ON su.id = t.companyFk
JOIN tmp.addressTaxArea ata
ON ata.addressFk = t.addressFk AND ata.companyFk = t.companyFk
JOIN bookingPlanner bp
ON bp.countryFk = su.countryFk
AND bp.taxAreaFk = ata.areaFk
AND bp.taxClassFk = ts.taxClassFk
JOIN pgc ON pgc.code = bp.pgcFk AND pgc.rate = tt.rate
JOIN taxClass tc ON tc.id = bp.taxClassFk
GROUP BY tt.ticketFk, tt.code,tt.rate
HAVING taxableBase != 0;
UPDATE tmp.ticketTax tt
JOIN tmp.ticketServiceTax ts ON tt.ticketFk = ts.ticketFk AND tt.code = ts.code AND tt.rate = ts.rate
SET tt.taxableBase = tt.taxableBase + ts.taxableBase;
DROP TEMPORARY TABLE IF EXISTS tmp.ticketAmount;
CREATE TEMPORARY TABLE tmp.ticketAmount
(INDEX (ticketFk))
ENGINE = MEMORY
SELECT ticketFk, taxableBase, SUM(CAST(taxableBase * rate / 100 AS DECIMAL(10, 2))) tax,code
FROM tmp.ticketTax
GROUP BY ticketFk, code;
DROP TEMPORARY TABLE IF EXISTS tmp.addressCompany;
DROP TEMPORARY TABLE IF EXISTS tmp.addressTaxArea;
END$$
DELIMITER ;

View File

@ -0,0 +1,34 @@
DROP PROCEDURE IF EXISTS vn.ticketGetTaxAdd;
DELIMITER $$
$$
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`ticketGetTaxAdd`(vTicketFk INT)
BEGIN
/**
* Añade un ticket a la tabla tmp.ticket para calcular
* el IVA y el recargo de equivalencia y devuelve el resultado.
*/
DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
CREATE TEMPORARY TABLE tmp.ticket
ENGINE = MEMORY
SELECT vTicketFk ticketFk;
CALL vn.ticketGetTax();
SELECT
tt.ticketFk,
CAST(tt.taxableBase AS DECIMAL(10, 2)) AS taxableBase,
CAST(tt.rate * tt.taxableBase / 100 AS DECIMAL(10, 2)) AS tax,
pgc.*,
CAST(IF(pe.equFk IS NULL, taxableBase, 0) AS DECIMAL(10, 2)) AS Base,
pgc.rate / 100 as vatPercent
FROM tmp.ticketTax tt
JOIN vn.pgc ON pgc.code = tt.pgcFk
LEFT JOIN vn.pgcEqu pe ON pe.equFk = pgc.code;
DROP TEMPORARY TABLE tmp.ticket;
DROP TEMPORARY TABLE tmp.ticketTax;
DROP TEMPORARY TABLE tmp.ticketAmount;
END$$
DELIMITER ;