merge
gitea/salix/dev This commit has test failures
Details
gitea/salix/dev This commit has test failures
Details
This commit is contained in:
commit
413d2f2855
|
@ -23,7 +23,7 @@ export default {
|
||||||
clientsIndex: {
|
clientsIndex: {
|
||||||
searchClientInput: `${components.vnTextfield}`,
|
searchClientInput: `${components.vnTextfield}`,
|
||||||
searchButton: `vn-searchbar vn-icon[icon="search"]`,
|
searchButton: `vn-searchbar vn-icon[icon="search"]`,
|
||||||
searchResult: `vn-item-client a`,
|
searchResult: `vn-client-index .vn-list-item`,
|
||||||
createClientButton: `${components.vnFloatButton}`,
|
createClientButton: `${components.vnFloatButton}`,
|
||||||
othersButton: `vn-left-menu li[name="Others"] > a`
|
othersButton: `vn-left-menu li[name="Others"] > a`
|
||||||
},
|
},
|
||||||
|
|
|
@ -152,44 +152,46 @@ vn-tool-bar {
|
||||||
.vn-list {
|
.vn-list {
|
||||||
max-width: 36em;
|
max-width: 36em;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
|
||||||
a.vn-list-item {
|
|
||||||
@extend %clickable;
|
|
||||||
}
|
|
||||||
.vn-list-item {
|
|
||||||
padding: $pad-medium;
|
|
||||||
border-bottom: $border-thin solid $color-spacer;
|
|
||||||
|
|
||||||
display: block;
|
a.vn-list-item {
|
||||||
text-decoration: none;
|
@extend %clickable;
|
||||||
color: inherit;
|
}
|
||||||
|
.vn-list-item {
|
||||||
& > vn-horizontal {
|
border-bottom: $border-thin solid $color-spacer;
|
||||||
& > vn-one {
|
display: block;
|
||||||
overflow: hidden;
|
text-decoration: none;
|
||||||
}
|
color: inherit;
|
||||||
& > .buttons {
|
|
||||||
align-items: center;
|
& > vn-horizontal {
|
||||||
|
padding: $pad-medium;
|
||||||
vn-icon-button {
|
|
||||||
opacity: .4;
|
& > vn-one {
|
||||||
color: $color-main;
|
overflow: hidden;
|
||||||
margin-left: .5em;
|
}
|
||||||
transition: opacity 250ms ease-out;
|
& > .buttons {
|
||||||
padding: 0;
|
align-items: center;
|
||||||
font-size: 2em;
|
|
||||||
|
vn-icon-button {
|
||||||
&:hover {
|
opacity: .4;
|
||||||
opacity: 1;
|
color: $color-main;
|
||||||
|
margin-left: .5em;
|
||||||
|
transition: opacity 250ms ease-out;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 2em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
vn-empty-rows {
|
||||||
vn-empty-rows.vn-list-item {
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1.5em;
|
padding: 1.5em;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** START - FORM ELEMENTS DISABLED **/
|
/** START - FORM ELEMENTS DISABLED **/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"module": "agency",
|
"module": "agency",
|
||||||
"name": "Agencies",
|
"name": "Agencies",
|
||||||
"icon" : "local_shipping",
|
"icon" : "icon-delivery",
|
||||||
"validations" : true,
|
"validations" : true,
|
||||||
"menu": [
|
"menu": [
|
||||||
{"state": "zone.card.basicData", "icon": "settings"},
|
{"state": "zone.card.basicData", "icon": "settings"},
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const app = require('vn-loopback/server/server');
|
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() => {
|
it('should call the card() method to receive a formated card of Bruce Wayne', async() => {
|
||||||
let id = 101;
|
let id = 101;
|
||||||
let result = await app.models.Client.getCard(id);
|
let result = await app.models.Client.getCard(id);
|
||||||
|
|
||||||
expect(result.id).toEqual(101);
|
expect(result.id).toEqual(101);
|
||||||
expect(result.name).toEqual('Bruce Wayne');
|
expect(result.name).toEqual('Bruce Wayne');
|
||||||
expect(result.debt).toEqual(579.1);
|
expect(result.debt).toEqual(595.81);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe('client summary()', () => {
|
||||||
it('should return a summary object containing debt', async() => {
|
it('should return a summary object containing debt', async() => {
|
||||||
let result = await app.models.Client.summary(101);
|
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() => {
|
it('should return a summary object containing averageInvoiced', async() => {
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Controller {
|
||||||
tooltip: 'Client ticket list'
|
tooltip: 'Client ticket list'
|
||||||
},
|
},
|
||||||
btnTwo: {
|
btnTwo: {
|
||||||
icon: 'shopping_cart',
|
icon: 'icon-basket',
|
||||||
state: `order.create({clientFk: ${value.id}})`,
|
state: `order.create({clientFk: ${value.id}})`,
|
||||||
tooltip: 'New order'
|
tooltip: 'New order'
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,42 @@
|
||||||
</vn-searchbar>
|
</vn-searchbar>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-card margin-medium-v>
|
<vn-card margin-medium-v>
|
||||||
<vn-item-client
|
<a
|
||||||
class="searchResult"
|
|
||||||
ng-repeat="client in clients track by client.id"
|
ng-repeat="client in clients track by client.id"
|
||||||
client="::client">
|
ui-sref="client.card.summary({ id: {{::client.id}} })"
|
||||||
</vn-item-client>
|
translate-attr="{title: 'View client'}"
|
||||||
|
class="vn-list-item searchResult">
|
||||||
|
<vn-horizontal ng-click="$ctrl.onClick($event)">
|
||||||
|
<vn-one>
|
||||||
|
<h6>{{::client.name}}</h6>
|
||||||
|
<vn-label-value label="Id"
|
||||||
|
value="{{::client.id}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Phone"
|
||||||
|
value="{{::client.phone | phone}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Town/City"
|
||||||
|
value="{{::client.city}}">
|
||||||
|
</vn-label-value>
|
||||||
|
<vn-label-value label="Email"
|
||||||
|
value="{{::client.email}}">
|
||||||
|
</vn-label-value>
|
||||||
|
</vn-one>
|
||||||
|
<vn-horizontal class="buttons">
|
||||||
|
<vn-icon-button
|
||||||
|
ng-click="$ctrl.preview($event)"
|
||||||
|
vn-tooltip="Preview"
|
||||||
|
icon="desktop_windows">
|
||||||
|
</vn-icon-button>
|
||||||
|
</vn-horizontal>
|
||||||
|
</vn-horizontal>
|
||||||
|
</a>
|
||||||
|
<vn-empty-rows translate ng-if="model.data.length === 0">
|
||||||
|
No results
|
||||||
|
</vn-empty-rows>
|
||||||
|
<vn-empty-rows translate ng-if="model.data === null">
|
||||||
|
Enter a new search
|
||||||
|
</vn-empty-rows>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-pagination model="model"></vn-pagination>
|
<vn-pagination model="model"></vn-pagination>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import ngModule from '../module';
|
import ngModule from '../module';
|
||||||
import './item-client';
|
|
||||||
|
|
||||||
export default class Controller {
|
export default class Controller {
|
||||||
constructor($scope, $stateParams) {
|
constructor($scope, $stateParams) {
|
||||||
|
@ -33,7 +32,13 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openSummary(client) {
|
onClick(event) {
|
||||||
|
if (event.defaultPrevented)
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
openSummary(client, event) {
|
||||||
|
event.preventDefault();
|
||||||
this.clientSelected = client;
|
this.clientSelected = client;
|
||||||
this.$.dialogSummaryClient.show();
|
this.$.dialogSummaryClient.show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
<a
|
|
||||||
ui-sref="client.card.summary({ id: {{::$ctrl.client.id}} })"
|
|
||||||
translate-attr="{title: 'View client'}"
|
|
||||||
class="vn-list-item">
|
|
||||||
<vn-horizontal ng-click="$ctrl.onClick($event)">
|
|
||||||
<vn-one>
|
|
||||||
<h6>{{::$ctrl.client.name}}</h6>
|
|
||||||
<vn-label-value label="Id"
|
|
||||||
value="{{::$ctrl.client.id}}">
|
|
||||||
</vn-label-value>
|
|
||||||
<vn-label-value label="Phone"
|
|
||||||
value="{{::$ctrl.client.phone | phone}}">
|
|
||||||
</vn-label-value>
|
|
||||||
<vn-label-value label="Town/City"
|
|
||||||
value="{{::$ctrl.client.city}}">
|
|
||||||
</vn-label-value>
|
|
||||||
<vn-label-value label="Email"
|
|
||||||
value="{{::$ctrl.client.email}}">
|
|
||||||
</vn-label-value>
|
|
||||||
</vn-one>
|
|
||||||
<vn-horizontal class="buttons">
|
|
||||||
<vn-icon-button
|
|
||||||
ng-click="$ctrl.preview($event)"
|
|
||||||
vn-tooltip="Preview"
|
|
||||||
icon="desktop_windows">
|
|
||||||
</vn-icon-button>
|
|
||||||
</vn-horizontal>
|
|
||||||
</vn-horizontal>
|
|
||||||
</a>
|
|
|
@ -1,24 +0,0 @@
|
||||||
import ngModule from '../module';
|
|
||||||
|
|
||||||
class Controller {
|
|
||||||
onClick(event) {
|
|
||||||
if (event.defaultPrevented)
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
preview(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.list.openSummary(this.client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ngModule.component('vnItemClient', {
|
|
||||||
template: require('./item-client.html'),
|
|
||||||
controller: Controller,
|
|
||||||
bindings: {
|
|
||||||
client: '<'
|
|
||||||
},
|
|
||||||
require: {
|
|
||||||
list: '^vnClientIndex'
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
vn-item-client {
|
|
||||||
display: block;
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@
|
||||||
{"state": "client.card.basicData", "icon": "settings"},
|
{"state": "client.card.basicData", "icon": "settings"},
|
||||||
{"state": "client.card.fiscalData", "icon": "account_balance"},
|
{"state": "client.card.fiscalData", "icon": "account_balance"},
|
||||||
{"state": "client.card.billingData", "icon": "icon-payment"},
|
{"state": "client.card.billingData", "icon": "icon-payment"},
|
||||||
{"state": "client.card.address.index", "icon": "local_shipping"},
|
{"state": "client.card.address.index", "icon": "icon-delivery"},
|
||||||
{"state": "client.card.note.index", "icon": "insert_drive_file"},
|
{"state": "client.card.note.index", "icon": "insert_drive_file"},
|
||||||
{"state": "client.card.credit.index", "icon": "credit_card"},
|
{"state": "client.card.credit.index", "icon": "credit_card"},
|
||||||
{"state": "client.card.greuge.index", "icon": "work"},
|
{"state": "client.card.greuge.index", "icon": "work"},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"module": "item",
|
"module": "item",
|
||||||
"name": "Items",
|
"name": "Items",
|
||||||
"icon": "inbox",
|
"icon": "icon-item",
|
||||||
"validations" : true,
|
"validations" : true,
|
||||||
"dependencies": ["worker", "client", "ticket"],
|
"dependencies": ["worker", "client", "ticket"],
|
||||||
"menu": [
|
"menu": [
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"module": "order",
|
"module": "order",
|
||||||
"name": "Orders",
|
"name": "Orders",
|
||||||
"icon": "shopping_cart",
|
"icon": "icon-basket",
|
||||||
"validations": true,
|
"validations": true,
|
||||||
"dependencies": ["worker", "item", "ticket"],
|
"dependencies": ["worker", "item", "ticket"],
|
||||||
"menu": [
|
"menu": [
|
||||||
{"state": "order.card.basicData", "icon": "settings"},
|
{"state": "order.card.basicData", "icon": "settings"},
|
||||||
{"state": "order.card.catalog", "icon": "shopping_cart"},
|
{"state": "order.card.catalog", "icon": "icon-basket"},
|
||||||
{"state": "order.card.volume", "icon": "icon-volume"},
|
{"state": "order.card.volume", "icon": "icon-volume"},
|
||||||
{"state": "order.card.line", "icon": "icon-lines"}
|
{"state": "order.card.line", "icon": "icon-lines"}
|
||||||
],
|
],
|
||||||
|
|
|
@ -25,23 +25,17 @@ module.exports = Self => {
|
||||||
Self.filter = async filter => {
|
Self.filter = async filter => {
|
||||||
const stmt = new ParameterizedSQL(
|
const stmt = new ParameterizedSQL(
|
||||||
`SELECT
|
`SELECT
|
||||||
e.id,
|
e.id, e.ticketFk, e.isBox,
|
||||||
e.ticketFk,
|
i1.name namePackage, e.counter,
|
||||||
e.isBox,
|
e.checked, i2.name nameBox,
|
||||||
i1.name namePackage,
|
e.itemFk, u.nickname userNickname,
|
||||||
e.counter,
|
u.id userId, e.created, e.externalId
|
||||||
e.checked,
|
|
||||||
i2.name nameBox,
|
|
||||||
e.itemFk,
|
|
||||||
u.nickname userNickname,
|
|
||||||
u.id userId,
|
|
||||||
e.created
|
|
||||||
FROM
|
FROM
|
||||||
vn.expedition e
|
vn.expedition e
|
||||||
LEFT JOIN vn.item i2 ON i2.id = e.itemFk
|
LEFT JOIN vn.item i2 ON i2.id = e.itemFk
|
||||||
INNER JOIN vn.item i1 ON i1.id = e.isBox
|
INNER JOIN vn.item i1 ON i1.id = e.isBox
|
||||||
LEFT JOIN vn.worker w ON w.id = e.workerFk
|
LEFT JOIN vn.worker w ON w.id = e.workerFk
|
||||||
JOIN account.user u ON u.id = w.id
|
JOIN account.user u ON u.id = w.userFk
|
||||||
`);
|
`);
|
||||||
stmt.merge(Self.buildSuffix(filter, 'e'));
|
stmt.merge(Self.buildSuffix(filter, 'e'));
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,6 @@ describe('ticket getTaxes()', () => {
|
||||||
it('should return the tax of a given ticket', async() => {
|
it('should return the tax of a given ticket', async() => {
|
||||||
let result = await app.models.Ticket.getTaxes(1);
|
let result = await app.models.Ticket.getTaxes(1);
|
||||||
|
|
||||||
expect(result[0].tax).toEqual(7.44);
|
expect(result[0].tax).toEqual(7.64);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ describe('ticket getTotal()', () => {
|
||||||
it('should return the total of a ticket', async() => {
|
it('should return the total of a ticket', async() => {
|
||||||
let result = await app.models.Ticket.getTotal(1);
|
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() => {
|
it(`should return zero if the ticket doesn't have lines`, async() => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ describe('ticket getVAT()', () => {
|
||||||
it('should call the getVAT method and return the response', async() => {
|
it('should call the getVAT method and return the response', async() => {
|
||||||
await app.models.Ticket.getVAT(1)
|
await app.models.Ticket.getVAT(1)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
expect(response).toEqual(20.29);
|
expect(response).toEqual(20.49);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -14,21 +14,21 @@ describe('ticket summary()', () => {
|
||||||
expect(result.sales.length).toEqual(4);
|
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);
|
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() => {
|
it('should return a summary object containing VAT for 1 ticket', async() => {
|
||||||
let result = await app.models.Ticket.summary(1);
|
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() => {
|
it('should return a summary object containing total for 1 ticket', async() => {
|
||||||
let result = await app.models.Ticket.summary(1);
|
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;
|
let expectedTotal = Math.round(total * 100) / 100;
|
||||||
|
|
||||||
expect(result.total).toEqual(expectedTotal);
|
expect(result.total).toEqual(expectedTotal);
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -23,9 +23,9 @@ module.exports = Self => {
|
||||||
let models = Self.app.models;
|
let models = Self.app.models;
|
||||||
let summaryObj = await getTicketData(Self, ticketFk);
|
let summaryObj = await getTicketData(Self, ticketFk);
|
||||||
summaryObj.sales = await getSales(models.Sale, ticketFk);
|
summaryObj.sales = await getSales(models.Sale, ticketFk);
|
||||||
summaryObj.subTotal = getSubTotal(summaryObj.sales);
|
summaryObj.subtotal = await models.Ticket.subtotal(ticketFk);
|
||||||
summaryObj.VAT = await models.Ticket.getVAT(ticketFk);
|
summaryObj.vat = await models.Ticket.getVAT(ticketFk);
|
||||||
summaryObj.total = await models.Ticket.getTotal(ticketFk);
|
summaryObj.total = summaryObj.subtotal + summaryObj.vat;
|
||||||
summaryObj.packagings = await models.TicketPackaging.find({
|
summaryObj.packagings = await models.TicketPackaging.find({
|
||||||
where: {ticketFk: ticketFk},
|
where: {ticketFk: ticketFk},
|
||||||
include: [{relation: 'packaging',
|
include: [{relation: 'packaging',
|
||||||
|
@ -149,14 +149,4 @@ module.exports = Self => {
|
||||||
};
|
};
|
||||||
return await Self.app.models.TicketRequest.find(filter);
|
return await Self.app.models.TicketRequest.find(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubTotal(sales) {
|
|
||||||
let subTotal = 0.00;
|
|
||||||
|
|
||||||
sales.forEach(sale => {
|
|
||||||
subTotal += sale.quantity * sale.price;
|
|
||||||
});
|
|
||||||
|
|
||||||
return subTotal;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@ module.exports = Self => {
|
||||||
require('../methods/ticket/summary')(Self);
|
require('../methods/ticket/summary')(Self);
|
||||||
require('../methods/ticket/getTotal')(Self);
|
require('../methods/ticket/getTotal')(Self);
|
||||||
require('../methods/ticket/getTaxes')(Self);
|
require('../methods/ticket/getTaxes')(Self);
|
||||||
|
require('../methods/ticket/subtotal')(Self);
|
||||||
require('../methods/ticket/componentUpdate')(Self);
|
require('../methods/ticket/componentUpdate')(Self);
|
||||||
require('../methods/ticket/new')(Self);
|
require('../methods/ticket/new')(Self);
|
||||||
require('../methods/ticket/isEditable')(Self);
|
require('../methods/ticket/isEditable')(Self);
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th></vn-th>
|
<vn-th></vn-th>
|
||||||
|
<vn-th field="itemFk" number>Expedition</vn-th>
|
||||||
|
<vn-th field="itemFk" number>Envialia</vn-th>
|
||||||
<vn-th field="itemFk" number>Item</vn-th>
|
<vn-th field="itemFk" number>Item</vn-th>
|
||||||
<vn-th field="name">Name</vn-th>
|
<vn-th field="name">Name</vn-th>
|
||||||
<vn-th field="isBox">Package type</vn-th>
|
<vn-th field="isBox">Package type</vn-th>
|
||||||
|
@ -30,6 +32,8 @@
|
||||||
vn-tooltip="Delete expedition">
|
vn-tooltip="Delete expedition">
|
||||||
</vn-icon-button>
|
</vn-icon-button>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
|
<vn-td number>{{expedition.id | zeroFill:6}}</vn-td>
|
||||||
|
<vn-td number>{{expedition.externalId | zeroFill:6}}</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
<span
|
<span
|
||||||
ng-class="{link: expedition.itemFk}"
|
ng-class="{link: expedition.itemFk}"
|
||||||
|
@ -41,7 +45,6 @@
|
||||||
<vn-td>{{::expedition.nameBox}}</vn-td>
|
<vn-td>{{::expedition.nameBox}}</vn-td>
|
||||||
<vn-td number>{{::expedition.counter}}</vn-td>
|
<vn-td number>{{::expedition.counter}}</vn-td>
|
||||||
<vn-td number>{{::expedition.checked}}</vn-td>
|
<vn-td number>{{::expedition.checked}}</vn-td>
|
||||||
<vn-td>{{::expedition.userNickname}}</vn-td>
|
|
||||||
<vn-td expand>
|
<vn-td expand>
|
||||||
<span
|
<span
|
||||||
class="link"
|
class="link"
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
</vn-button>
|
</vn-button>
|
||||||
</vn-tool-bar>
|
</vn-tool-bar>
|
||||||
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
|
<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 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>
|
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.total | currency: 'EUR':2}}</strong></p>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
|
|
|
@ -36,9 +36,10 @@ class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSubTotal() {
|
loadSubTotal() {
|
||||||
this.subTotal = 0.0;
|
if (!this.$stateParams.id || !this.sales) return;
|
||||||
if (!this.sales) return;
|
this.$http.get(`/ticket/api/Tickets/${this.$stateParams.id}/subtotal`).then(res => {
|
||||||
this.subTotal = this.sales.reduce((sum, sale) => sum + this.getSaleTotal(sale), 0.0);
|
this.subtotal = res.data || 0.0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getSaleTotal(sale) {
|
getSaleTotal(sale) {
|
||||||
|
@ -54,7 +55,7 @@ class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
get total() {
|
get total() {
|
||||||
return this.subTotal + this.VAT;
|
return this.subtotal + this.VAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMoreOpen() {
|
onMoreOpen() {
|
||||||
|
|
|
@ -41,6 +41,7 @@ describe('Ticket', () => {
|
||||||
$httpBackend = _$httpBackend_;
|
$httpBackend = _$httpBackend_;
|
||||||
$httpBackend.whenGET(/api\/Tickets\/1\/getSales.*/).respond(sales);
|
$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/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);
|
$element = $compile('<vn-ticket-sale ticket="ticket"></vn-ticket-sale>')($scope);
|
||||||
controller = $element.controller('vnTicketSale');
|
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', () => {
|
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.VAT).toEqual(10.5);
|
||||||
expect(controller.total).toEqual(238);
|
expect(controller.total).toEqual(238);
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,8 +46,8 @@
|
||||||
</vn-label-value>
|
</vn-label-value>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-one class="taxes">
|
<vn-one class="taxes">
|
||||||
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.summary.subTotal | 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 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><strong>Total</strong></vn-label> <strong>{{$ctrl.summary.total | currency: 'EUR':2}}</strong></p>
|
||||||
</vn-one>
|
</vn-one>
|
||||||
<vn-auto name="sales">
|
<vn-auto name="sales">
|
||||||
|
|
|
@ -48,6 +48,12 @@
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
</a>
|
</a>
|
||||||
|
<vn-empty-rows translate ng-if="model.data.length === 0">
|
||||||
|
No results
|
||||||
|
</vn-empty-rows>
|
||||||
|
<vn-empty-rows translate ng-if="model.data === null">
|
||||||
|
Enter a new search
|
||||||
|
</vn-empty-rows>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
<vn-pagination model="model"></vn-pagination>
|
<vn-pagination model="model"></vn-pagination>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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 ;
|
|
@ -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 ;
|
Loading…
Reference in New Issue