Merge pull request '2824-ticket_totalWith_and_without_VAT' (#571) from 2824-ticket_totalWith_and_without_VAT into dev
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
Reviewed-on: #571 Reviewed-by: Joan Sanchez <joan@verdnatura.es>
This commit is contained in:
commit
fc6fc73df7
|
@ -2242,4 +2242,9 @@ INSERT INTO `vn`.`duaInvoiceIn`(`id`, `duaFk`, `invoiceInFk`)
|
|||
(7, 7, 7),
|
||||
(8, 8, 8),
|
||||
(9, 9, 9),
|
||||
(10, 10, 10);
|
||||
(10, 10, 10);
|
||||
|
||||
INSERT INTO `vn`.`ticketRecalc`(`ticketFk`)
|
||||
SELECT `id` FROM `vn`.`ticket`;
|
||||
|
||||
CALL `vn`.`ticket_doRecalc`();
|
|
@ -37,11 +37,11 @@ module.exports = Self => {
|
|||
}, {
|
||||
arg: 'active',
|
||||
type: 'Boolean',
|
||||
description: 'Whether the the item is or not active',
|
||||
description: 'Whether the item is or not active',
|
||||
}, {
|
||||
arg: 'visible',
|
||||
type: 'Boolean',
|
||||
description: 'Whether the the item is or not visible',
|
||||
description: 'Whether the item is or not visible',
|
||||
}, {
|
||||
arg: 'typeFk',
|
||||
type: 'Integer',
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
module.exports = function(Self) {
|
||||
Self.remoteMethodCtx('canBeInvoiced', {
|
||||
description: 'Change property isEqualizated in all client addresses',
|
||||
description: 'Whether the ticket can or not be invoiced',
|
||||
accessType: 'READ',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'string',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'Client id',
|
||||
description: 'The ticket id',
|
||||
http: {source: 'path'}
|
||||
}
|
||||
],
|
||||
|
@ -23,8 +23,9 @@ module.exports = function(Self) {
|
|||
});
|
||||
|
||||
Self.canBeInvoiced = async id => {
|
||||
let ticket = await Self.app.models.Ticket.findById(id, {fields: ['id', 'refFk', 'shipped']});
|
||||
let ticketTotal = await Self.app.models.Ticket.getTotal(id);
|
||||
let ticket = await Self.app.models.Ticket.findById(id, {
|
||||
fields: ['id', 'refFk', 'shipped', 'totalWithVat']
|
||||
});
|
||||
|
||||
let query = `SELECT vn.hasSomeNegativeBase(?) AS hasSomeNegativeBase`;
|
||||
let [result] = await Self.rawSql(query, [id]);
|
||||
|
@ -33,7 +34,7 @@ module.exports = function(Self) {
|
|||
let today = new Date();
|
||||
let shipped = new Date(ticket.shipped);
|
||||
|
||||
if (ticket.refFk || ticketTotal == 0 || shipped.getTime() > today.getTime() || hasSomeNegativeBase)
|
||||
if (ticket.refFk || ticket.totalWithVat == 0 || shipped.getTime() > today.getTime() || hasSomeNegativeBase)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -203,6 +203,8 @@ module.exports = Self => {
|
|||
t.routeFk,
|
||||
t.warehouseFk,
|
||||
t.clientFk,
|
||||
t.totalWithoutVat,
|
||||
t.totalWithVat,
|
||||
io.id AS invoiceOutId,
|
||||
a.provinceFk,
|
||||
p.name AS province,
|
||||
|
@ -246,6 +248,7 @@ module.exports = Self => {
|
|||
stmts.push(stmt);
|
||||
|
||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.ticketGetProblems');
|
||||
|
||||
stmts.push(`
|
||||
CREATE TEMPORARY TABLE tmp.ticketGetProblems
|
||||
(INDEX (ticketFk))
|
||||
|
@ -255,23 +258,15 @@ module.exports = Self => {
|
|||
LEFT JOIN alertLevel al ON al.alertLevel = f.alertLevel
|
||||
WHERE (al.code = 'FREE' OR f.alertLevel IS NULL)
|
||||
AND f.shipped >= CURDATE()`);
|
||||
stmts.push('CALL ticketGetProblems()');
|
||||
|
||||
stmts.push('DROP TEMPORARY TABLE IF EXISTS tmp.ticket');
|
||||
stmts.push(`
|
||||
CREATE TEMPORARY TABLE tmp.ticket
|
||||
(INDEX (ticketFk)) ENGINE = MEMORY
|
||||
SELECT id ticketFk FROM tmp.filter`);
|
||||
stmts.push('CALL ticketGetTotal()');
|
||||
stmts.push('CALL ticketGetProblems()');
|
||||
|
||||
stmt = new ParameterizedSQL(`
|
||||
SELECT
|
||||
f.*,
|
||||
tt.total,
|
||||
tp.*
|
||||
FROM tmp.filter f
|
||||
LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = f.id
|
||||
LEFT JOIN tmp.ticketTotal tt ON tt.ticketFk = f.id`);
|
||||
LEFT JOIN tmp.ticketProblems tp ON tp.ticketFk = f.id`);
|
||||
|
||||
if (args.problems != undefined && (!args.from && !args.to))
|
||||
throw new UserError('Choose a date range or days forward');
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getTaxes', {
|
||||
description: 'Returns ticket taxes',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'ticket id',
|
||||
http: {source: 'path'}
|
||||
}],
|
||||
returns: {
|
||||
type: 'number',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/getTaxes`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getTaxes = async ticketFk => {
|
||||
let query = `CALL vn.ticketGetTaxAdd(?)`;
|
||||
let [taxes] = await Self.rawSql(query, [ticketFk]);
|
||||
|
||||
return taxes;
|
||||
};
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getTotal', {
|
||||
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/getTotal`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getTotal = async ticketFk => {
|
||||
let query = `SELECT vn.ticketGetTotal(?) AS amount`;
|
||||
let [total] = await Self.rawSql(query, [ticketFk]);
|
||||
|
||||
return total.amount ? total.amount : 0.00;
|
||||
};
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
module.exports = Self => {
|
||||
Self.remoteMethod('getVAT', {
|
||||
description: 'Returns ticket total VAT',
|
||||
accessType: 'READ',
|
||||
accepts: [{
|
||||
arg: 'id',
|
||||
type: 'number',
|
||||
required: true,
|
||||
description: 'ticket id',
|
||||
http: {source: 'path'}
|
||||
}],
|
||||
returns: {
|
||||
type: 'number',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/:id/getVAT`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.getVAT = async ticketFk => {
|
||||
let totalTax = 0.00;
|
||||
let taxes = await Self.app.models.Ticket.getTaxes(ticketFk);
|
||||
|
||||
taxes.forEach(tax => {
|
||||
totalTax += tax.tax;
|
||||
});
|
||||
|
||||
return Math.round(totalTax * 100) / 100;
|
||||
};
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
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.1);
|
||||
});
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('ticket getTotal()', () => {
|
||||
it('should return the total of a ticket', async() => {
|
||||
let result = await app.models.Ticket.getTotal(1);
|
||||
|
||||
expect(result).toEqual(891.87);
|
||||
});
|
||||
|
||||
it(`should return zero if the ticket doesn't have lines`, async() => {
|
||||
let result = await app.models.Ticket.getTotal(21);
|
||||
|
||||
expect(result).toEqual(0);
|
||||
});
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
const app = require('vn-loopback/server/server');
|
||||
|
||||
describe('ticket getVAT()', () => {
|
||||
it('should return the ticket VAT', async() => {
|
||||
await app.models.Ticket.getVAT(1)
|
||||
.then(response => {
|
||||
expect(response).toEqual(84.64);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
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(809.23);
|
||||
});
|
||||
|
||||
it(`should return 0 if the ticket doesn't have lines`, async() => {
|
||||
let result = await app.models.Ticket.subtotal(21);
|
||||
|
||||
expect(result).toEqual(0.00);
|
||||
});
|
||||
});
|
|
@ -14,23 +14,15 @@ 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 totalWithoutVat for 1 ticket', async() => {
|
||||
let result = await app.models.Ticket.summary(1);
|
||||
|
||||
expect(Math.round(result.subtotal * 100) / 100).toEqual(809.23);
|
||||
});
|
||||
|
||||
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(84.64);
|
||||
expect(result.totalWithoutVat).toEqual(807.23);
|
||||
});
|
||||
|
||||
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 expectedTotal = Math.round(total * 100) / 100;
|
||||
|
||||
expect(result.total).toEqual(expectedTotal);
|
||||
expect(result.totalWithVat).toEqual(891.87);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
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;
|
||||
};
|
||||
};
|
|
@ -23,9 +23,6 @@ module.exports = Self => {
|
|||
let models = Self.app.models;
|
||||
let summaryObj = await getTicketData(Self, ticketFk);
|
||||
summaryObj.sales = await getSales(models.Sale, 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',
|
||||
|
|
|
@ -6,16 +6,12 @@ module.exports = Self => {
|
|||
require('../methods/ticket/getVolume')(Self);
|
||||
require('../methods/ticket/getTotalVolume')(Self);
|
||||
require('../methods/ticket/summary')(Self);
|
||||
require('../methods/ticket/getTotal')(Self);
|
||||
require('../methods/ticket/getTaxes')(Self);
|
||||
require('../methods/ticket/subtotal')(Self);
|
||||
require('../methods/ticket/priceDifference')(Self);
|
||||
require('../methods/ticket/componentUpdate')(Self);
|
||||
require('../methods/ticket/new')(Self);
|
||||
require('../methods/ticket/isEditable')(Self);
|
||||
require('../methods/ticket/setDeleted')(Self);
|
||||
require('../methods/ticket/restore')(Self);
|
||||
require('../methods/ticket/getVAT')(Self);
|
||||
require('../methods/ticket/getSales')(Self);
|
||||
require('../methods/ticket/getSalesPersonMana')(Self);
|
||||
require('../methods/ticket/filter')(Self);
|
||||
|
|
|
@ -12,30 +12,30 @@
|
|||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"type": "number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"shipped": {
|
||||
"type": "Date",
|
||||
"type": "date",
|
||||
"required": true
|
||||
},
|
||||
"landed": {
|
||||
"type": "Date"
|
||||
"type": "date"
|
||||
},
|
||||
"nickname": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"location": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"solution": {
|
||||
"type": "String"
|
||||
"type": "string"
|
||||
},
|
||||
"packages": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"updated": {
|
||||
"type": "Date",
|
||||
"type": "date",
|
||||
"mysql": {
|
||||
"columnName": "created"
|
||||
}
|
||||
|
@ -44,16 +44,22 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"priority": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"zoneFk": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"zonePrice": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"zoneBonus": {
|
||||
"type": "Number"
|
||||
"type": "number"
|
||||
},
|
||||
"totalWithVat": {
|
||||
"type": "number"
|
||||
},
|
||||
"totalWithoutVat": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
<vn-td>{{::ticket.warehouse}}</vn-td>
|
||||
<vn-td number>
|
||||
<span class="chip {{$ctrl.totalPriceColor(ticket)}}">
|
||||
{{::ticket.total | currency: 'EUR': 2}}
|
||||
{{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td actions>
|
||||
|
|
|
@ -49,7 +49,7 @@ export default class Controller extends Section {
|
|||
throw new UserError('You cannot make a payment on account from multiple clients');
|
||||
|
||||
for (let ticket of checkedTickets) {
|
||||
this.$.balanceCreateDialog.amountPaid += ticket.total;
|
||||
this.$.balanceCreateDialog.amountPaid += ticket.totalWithVat;
|
||||
this.$.balanceCreateDialog.clientFk = ticket.clientFk;
|
||||
description.push(`${ticket.id}`);
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ export default class Controller extends Section {
|
|||
}
|
||||
|
||||
totalPriceColor(ticket) {
|
||||
const total = parseInt(ticket.total);
|
||||
const total = parseInt(ticket.totalWithVat);
|
||||
if (total > 0 && total < 50)
|
||||
return 'warning';
|
||||
}
|
||||
|
|
|
@ -6,18 +6,18 @@ describe('Component vnTicketIndex', () => {
|
|||
let tickets = [{
|
||||
id: 1,
|
||||
clientFk: 1,
|
||||
total: 10.5,
|
||||
checked: false
|
||||
checked: false,
|
||||
totalWithVat: 10.5
|
||||
}, {
|
||||
id: 2,
|
||||
clientFk: 1,
|
||||
total: 20.5,
|
||||
checked: true
|
||||
checked: true,
|
||||
totalWithVat: 20.5
|
||||
}, {
|
||||
id: 3,
|
||||
clientFk: 1,
|
||||
total: 30,
|
||||
checked: true
|
||||
checked: true,
|
||||
totalWithVat: 30
|
||||
}];
|
||||
|
||||
beforeEach(ngModule('ticket'));
|
||||
|
@ -76,7 +76,6 @@ describe('Component vnTicketIndex', () => {
|
|||
jest.spyOn(controller.$.balanceCreateDialog, 'show').mockReturnThis();
|
||||
|
||||
controller.$.model = {data: tickets};
|
||||
controller.$.balanceCreateDialog.amountPaid = 0;
|
||||
controller.openBalanceDialog();
|
||||
|
||||
let description = controller.$.balanceCreateDialog.description;
|
||||
|
|
|
@ -39,8 +39,8 @@ class Controller extends Section {
|
|||
|
||||
getSubTotal() {
|
||||
if (!this.$params.id || !this.sales) return;
|
||||
this.$http.get(`Tickets/${this.$params.id}/subtotal`).then(res => {
|
||||
this.subtotal = res.data || 0.0;
|
||||
this.$http.get(`Tickets/${this.$params.id}`).then(res => {
|
||||
this.subtotal = res.data.totalWithoutVat || 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -62,8 +62,8 @@ class Controller extends Section {
|
|||
getVat() {
|
||||
this.VAT = 0.0;
|
||||
if (!this.$params.id || !this.sales) return;
|
||||
this.$http.get(`Tickets/${this.$params.id}/getVAT`).then(res => {
|
||||
this.VAT = res.data || 0.0;
|
||||
this.$http.get(`Tickets/${this.$params.id}`).then(res => {
|
||||
this.VAT = res.data.totalWithVat - res.data.totalWithoutVat || 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -84,13 +84,13 @@ describe('Ticket', () => {
|
|||
|
||||
describe('getSubTotal()', () => {
|
||||
it('should make an HTTP GET query and then set the subtotal property', () => {
|
||||
const expectedAmount = 128;
|
||||
const expectedResponse = {totalWithoutVat: 128};
|
||||
|
||||
$httpBackend.expect('GET', 'Tickets/1/subtotal').respond(200, expectedAmount);
|
||||
$httpBackend.expect('GET', 'Tickets/1').respond(200, expectedResponse);
|
||||
controller.getSubTotal();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.subtotal).toEqual(expectedAmount);
|
||||
expect(controller.subtotal).toEqual(expectedResponse.totalWithoutVat);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -125,13 +125,15 @@ describe('Ticket', () => {
|
|||
describe('getVat()', () => {
|
||||
it('should make an HTTP GET query and return the ticket VAT', () => {
|
||||
controller.edit = {};
|
||||
const expectedAmount = 67;
|
||||
const expectedResponse = {totalWithVat: 1000, totalWithoutVat: 999};
|
||||
|
||||
$httpBackend.expect('GET', 'Tickets/1/getVAT').respond(200, expectedAmount);
|
||||
const expectedVAT = expectedResponse.totalWithVat - expectedResponse.totalWithoutVat;
|
||||
|
||||
$httpBackend.expect('GET', 'Tickets/1').respond(200, expectedResponse);
|
||||
controller.getVat();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(controller.VAT).toEqual(expectedAmount);
|
||||
expect(controller.VAT).toEqual(expectedVAT);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -99,9 +99,9 @@
|
|||
</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><strong>Total</strong></vn-label> <strong>{{$ctrl.summary.total | currency: 'EUR':2}}</strong></p>
|
||||
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.summary.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||
<p><vn-label translate>VAT</vn-label> {{$ctrl.summary.totalWithVat - $ctrl.summary.totalWithoutVat | currency: 'EUR':2}}</p>
|
||||
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.summary.totalWithVat | currency: 'EUR':2}}</strong></p>
|
||||
</vn-one>
|
||||
<vn-auto name="sales">
|
||||
<h4>
|
||||
|
|
Loading…
Reference in New Issue