#232 components section plus unit tests
This commit is contained in:
parent
946aa7d8e8
commit
dafb554792
|
@ -1,17 +1,25 @@
|
||||||
.vn-grid {
|
.vn-grid {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
td, th{
|
|
||||||
|
& > thead,
|
||||||
|
& > tbody,
|
||||||
|
& > tfoot {
|
||||||
|
tr {
|
||||||
|
td, th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
|
&[number]{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
& > thead, & > tbody {
|
& > thead, & > tbody {
|
||||||
border-bottom: 3px solid #9D9D9D;
|
border-bottom: 3px solid #9D9D9D;
|
||||||
}
|
}
|
||||||
& > tbody > tr{
|
& > tbody > tr {
|
||||||
border-bottom: 1px solid #9D9D9D;
|
border-bottom: 1px solid #9D9D9D;
|
||||||
}
|
}
|
||||||
td[number], th[number]{
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -182,6 +182,18 @@
|
||||||
"description": "Sale checked",
|
"description": "Sale checked",
|
||||||
"icon": "assignment"
|
"icon": "assignment"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url" : "/components",
|
||||||
|
"state": "ticket.card.components",
|
||||||
|
"component": "vn-ticket-components",
|
||||||
|
"params": {
|
||||||
|
"ticket": "$ctrl.ticket"
|
||||||
|
},
|
||||||
|
"menu": {
|
||||||
|
"description": "Components",
|
||||||
|
"icon": "icon-components"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
<vn-main-block>
|
<vn-main-block>
|
||||||
<vn-horizontal>
|
<vn-horizontal>
|
||||||
<vn-auto margin-medium-v class="left-block">
|
<vn-auto class="left-block">
|
||||||
<vn-ticket-descriptor ticket="$ctrl.ticket"></vn-ticket-descriptor>
|
<vn-ticket-descriptor ticket="$ctrl.ticket"></vn-ticket-descriptor>
|
||||||
<vn-left-menu></vn-left-menu>
|
<vn-left-menu></vn-left-menu>
|
||||||
</vn-auto>
|
</vn-auto>
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
<mg-ajax path="/ticket/api/Sales/saleComponentFilter" options="vnIndex" actions="$ctrl.sales = index.model.instances"></mg-ajax>
|
||||||
|
<vn-vertical>
|
||||||
|
<vn-card pad-large>
|
||||||
|
<vn-vertical>
|
||||||
|
<vn-title>Components</vn-title>
|
||||||
|
<table class="vn-grid">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th number translate>Item</th>
|
||||||
|
<th translate>Description</th>
|
||||||
|
<th number translate>Quantity</th>
|
||||||
|
<th translate>Serie</th>
|
||||||
|
<th translate>Components</th>
|
||||||
|
<th number translate>Import</th>
|
||||||
|
<th number translate>Total import</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td number colspan="7">
|
||||||
|
<span translate>Base</span> {{::$ctrl.base() | currency:'€':3}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td number colspan="7">
|
||||||
|
<span translate>Margin</span> {{::$ctrl.profitMargin() | currency:'€':3}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td number colspan="7">
|
||||||
|
<span translate>Total</span> {{::$ctrl.total() | currency:'€':3}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
<tbody ng-repeat="sale in $ctrl.sales track by sale.id">
|
||||||
|
<tr>
|
||||||
|
<td rowspan="{{
|
||||||
|
::sale.components.length + 1
|
||||||
|
}}" number>{{::sale.itemFk}}</td>
|
||||||
|
<td rowspan="{{
|
||||||
|
::sale.components.length + 1
|
||||||
|
}}">{{::sale.item.name}}</td>
|
||||||
|
<td rowspan="{{
|
||||||
|
::sale.components.length + 1
|
||||||
|
}}" number>{{::sale.quantity}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr
|
||||||
|
ng-repeat="component in sale.components track by component.componentFk">
|
||||||
|
<td ng-class="::{
|
||||||
|
first: $index == 0,last: $index == sale.components.length - 1
|
||||||
|
}">{{::component.componentRate.componentType.type}}</td>
|
||||||
|
<td ng-class="::{
|
||||||
|
first: $index == 0,last: $index == sale.components.length - 1
|
||||||
|
}">{{::component.componentRate.name}}</td>
|
||||||
|
<td ng-class="::{
|
||||||
|
first: $index == 0,last: $index == sale.components.length - 1
|
||||||
|
}" number>{{::component.value | currency:'€':3}}</td>
|
||||||
|
<td ng-class="::{
|
||||||
|
first: $index == 0,last: $index == sale.components.length - 1
|
||||||
|
}" number>{{::sale.quantity * component.value | currency:'€':3}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</vn-vertical>
|
||||||
|
<vn-paging margin-large-top vn-one index="index" total="index.model.count"></vn-paging>
|
||||||
|
</vn-card>
|
||||||
|
</vn-vertical>
|
|
@ -0,0 +1,45 @@
|
||||||
|
import ngModule from '../module';
|
||||||
|
import './style.scss';
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
total() {
|
||||||
|
let sum;
|
||||||
|
if (this.sales) {
|
||||||
|
sum = 0;
|
||||||
|
for (let sale of this.sales)
|
||||||
|
for (let component of sale.components)
|
||||||
|
sum += sale.quantity * component.value;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
base() {
|
||||||
|
let sum;
|
||||||
|
if (this.sales) {
|
||||||
|
sum = 0;
|
||||||
|
for (let sale of this.sales)
|
||||||
|
for (let component of sale.components)
|
||||||
|
if (component.componentRate.name == 'valor de compra')
|
||||||
|
sum += sale.quantity * component.value;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
profitMargin() {
|
||||||
|
let sum;
|
||||||
|
if (this.sales) {
|
||||||
|
sum = 0;
|
||||||
|
for (let sale of this.sales)
|
||||||
|
for (let component of sale.components)
|
||||||
|
if (component.componentRate.name != 'valor de compra')
|
||||||
|
sum += sale.quantity * component.value;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngModule.component('vnTicketComponents', {
|
||||||
|
template: require('./component.html'),
|
||||||
|
controller: Controller,
|
||||||
|
bindings: {
|
||||||
|
ticket: '<'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,92 @@
|
||||||
|
import './component.js';
|
||||||
|
|
||||||
|
describe('ticket', () => {
|
||||||
|
describe('Component vnTicketComponents', () => {
|
||||||
|
let $componentController;
|
||||||
|
let controller;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
angular.mock.module('ticket');
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(angular.mock.inject(_$componentController_ => {
|
||||||
|
$componentController = _$componentController_;
|
||||||
|
controller = $componentController('vnTicketComponents');
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('total()', () => {
|
||||||
|
it('should return the sum from all componenets in each sale', () => {
|
||||||
|
controller.sales = [{
|
||||||
|
components: [
|
||||||
|
{componentRate: {name: 'valor de compra'}, value: 5},
|
||||||
|
{componentRate: {name: 'reparto'}, value: 5},
|
||||||
|
{componentRate: {name: 'recobro'}, value: 5}
|
||||||
|
],
|
||||||
|
quantity: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
components: [
|
||||||
|
{componentRate: {name: 'valor de compra'}, value: 1},
|
||||||
|
{componentRate: {name: 'reparto'}, value: 1},
|
||||||
|
{componentRate: {name: 'recobro'}, value: 1}
|
||||||
|
],
|
||||||
|
quantity: 5
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let result = controller.total();
|
||||||
|
|
||||||
|
expect(result).toEqual(30);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('base()', () => {
|
||||||
|
it(`should return the sum from all 'valor de compra' componenets in each sale`, () => {
|
||||||
|
controller.sales = [{
|
||||||
|
components: [
|
||||||
|
{componentRate: {name: 'valor de compra'}, value: 5},
|
||||||
|
{componentRate: {name: 'reparto'}, value: 5},
|
||||||
|
{componentRate: {name: 'recobro'}, value: 5}
|
||||||
|
],
|
||||||
|
quantity: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
components: [
|
||||||
|
{componentRate: {name: 'valor de compra'}, value: 1},
|
||||||
|
{componentRate: {name: 'reparto'}, value: 1},
|
||||||
|
{componentRate: {name: 'recobro'}, value: 1}
|
||||||
|
],
|
||||||
|
quantity: 5
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let result = controller.base();
|
||||||
|
|
||||||
|
expect(result).toEqual(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('profitMargin()', () => {
|
||||||
|
it(`should return the sum from all componenets but 'valor de compra' in each sale`, () => {
|
||||||
|
controller.sales = [{
|
||||||
|
components: [
|
||||||
|
{componentRate: {name: 'valor de compra'}, value: 5},
|
||||||
|
{componentRate: {name: 'reparto'}, value: 5},
|
||||||
|
{componentRate: {name: 'recobro'}, value: 5}
|
||||||
|
],
|
||||||
|
quantity: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
components: [
|
||||||
|
{componentRate: {name: 'valor de compra'}, value: 1},
|
||||||
|
{componentRate: {name: 'reparto'}, value: 1},
|
||||||
|
{componentRate: {name: 'recobro'}, value: 1}
|
||||||
|
],
|
||||||
|
quantity: 5
|
||||||
|
}
|
||||||
|
];
|
||||||
|
let result = controller.profitMargin();
|
||||||
|
|
||||||
|
expect(result).toEqual(20);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,27 @@
|
||||||
|
vn-ticket-components .vn-grid {
|
||||||
|
tbody:not(:last-child) {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
tfoot tr:first-child td {
|
||||||
|
padding-top: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
padding-top: .1em !important;
|
||||||
|
padding-bottom: .1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.first {
|
||||||
|
padding-top: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.last {
|
||||||
|
padding-bottom: 10px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr:not(:first-child):not(:last-child), {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ Basic data: Datos básicos
|
||||||
Checked: Comprobado
|
Checked: Comprobado
|
||||||
Client: Cliente
|
Client: Cliente
|
||||||
Company: Empresa
|
Company: Empresa
|
||||||
|
Components: Componentes
|
||||||
Counter: Contador
|
Counter: Contador
|
||||||
Created : Añadido
|
Created : Añadido
|
||||||
Date : Fecha
|
Date : Fecha
|
||||||
|
@ -15,13 +16,15 @@ Description: Descripción
|
||||||
Discount: Descuento
|
Discount: Descuento
|
||||||
Employee : Empleado
|
Employee : Empleado
|
||||||
Expedition: Expedición
|
Expedition: Expedición
|
||||||
|
Import: Importe
|
||||||
Is checked: Comprobado
|
Is checked: Comprobado
|
||||||
Item: Articulo
|
Item: Articulo
|
||||||
Landing: Llegada
|
Landing: Llegada
|
||||||
Landed: F. entrega
|
Landed: F. entrega
|
||||||
Name: Nombre
|
Margin: Margen
|
||||||
m³ per unit: m³ por unidad
|
m³ per unit: m³ por unidad
|
||||||
m³ per quantity: m³ por cantidad
|
m³ per quantity: m³ por cantidad
|
||||||
|
Name: Nombre
|
||||||
Notes: Notas
|
Notes: Notas
|
||||||
New price: Nuevo precio
|
New price: Nuevo precio
|
||||||
New : Nuevo
|
New : Nuevo
|
||||||
|
@ -40,6 +43,7 @@ Some fields are invalid: Algunos campos no son válidos
|
||||||
State: Estado
|
State: Estado
|
||||||
The observation type must be unique: El tipo de observación debe ser único
|
The observation type must be unique: El tipo de observación debe ser único
|
||||||
Tickets: Tickets
|
Tickets: Tickets
|
||||||
|
Total import: Importe total
|
||||||
Tracking: Revisión
|
Tracking: Revisión
|
||||||
Volume: Volumen
|
Volume: Volumen
|
||||||
Warehouse: Almacén
|
Warehouse: Almacén
|
||||||
|
|
|
@ -18,3 +18,4 @@ import './tracking/index';
|
||||||
import './tracking/edit/edit';
|
import './tracking/edit/edit';
|
||||||
import './fetched-tags/fetched-tags';
|
import './fetched-tags/fetched-tags';
|
||||||
import './sale-checked/sale-checked';
|
import './sale-checked/sale-checked';
|
||||||
|
import './component/component';
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
<td number>{{::sale.itemFk}}</td>
|
<td number>{{::sale.itemFk}}</td>
|
||||||
<td><vn-fetched-tags sale="sale"/></td>
|
<td><vn-fetched-tags sale="sale"/></td>
|
||||||
<td number>{{::sale.quantity}}</td>
|
<td number>{{::sale.quantity}}</td>
|
||||||
<td number>{{::sale.volume.m3_uni}}</td>
|
<td number>{{::sale.volume.m3_uni | number:3}}</td>
|
||||||
<td number>{{::sale.volume.volumeTimesQuantity}}</td>
|
<td number>{{::sale.volume.volumeTimesQuantity | number:3}}</td>
|
||||||
<td number>{{::sale.volume.m3_total}}</td>
|
<td number>{{::sale.volume.m3_total | number:3}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -10,28 +10,28 @@ module.exports = Self => {
|
||||||
limit: params.size,
|
limit: params.size,
|
||||||
order: params.order || 'concept ASC',
|
order: params.order || 'concept ASC',
|
||||||
include: [{
|
include: [{
|
||||||
relation: "item",
|
relation: 'item',
|
||||||
scope: {
|
scope: {
|
||||||
include: {
|
include: {
|
||||||
relation: "itemTag",
|
relation: 'itemTag',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ["tagFk", "value"],
|
fields: ['tagFk', 'value'],
|
||||||
include: {
|
include: {
|
||||||
relation: "tag",
|
relation: 'tag',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ["name"]
|
fields: ['name']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
limit: 6
|
limit: 6
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fields: ["itemFk", "name"]
|
fields: ['itemFk', 'name']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
relation: "isChecked",
|
relation: 'isChecked',
|
||||||
scope: {
|
scope: {
|
||||||
fields: ["isChecked"]
|
fields: ['isChecked']
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.installMethod('saleComponentFilter', filterParams);
|
||||||
|
|
||||||
|
function filterParams(params) {
|
||||||
|
return {
|
||||||
|
where: {
|
||||||
|
ticketFk: params.ticketFk
|
||||||
|
},
|
||||||
|
skip: (params.page - 1) * params.size,
|
||||||
|
limit: params.size,
|
||||||
|
order: params.order || 'concept ASC',
|
||||||
|
include: [{
|
||||||
|
relation: "item",
|
||||||
|
scope: {
|
||||||
|
include: {
|
||||||
|
relation: "itemTag",
|
||||||
|
scope: {
|
||||||
|
fields: ["tagFk", "value"],
|
||||||
|
include: {
|
||||||
|
relation: "tag",
|
||||||
|
scope: {
|
||||||
|
fields: ["name"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
limit: 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fields: ["itemFk", "name"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: "components",
|
||||||
|
scope: {
|
||||||
|
fields: ["componentFk", "value"],
|
||||||
|
include: {
|
||||||
|
relation: "componentRate",
|
||||||
|
scope: {
|
||||||
|
fields: ["componentTypeRate", "name"],
|
||||||
|
include: {
|
||||||
|
relation: "componentType",
|
||||||
|
scope: {
|
||||||
|
fields: ["type"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
|
@ -28,6 +28,13 @@
|
||||||
"type": "Number"
|
"type": "Number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"relations": {
|
||||||
|
"componentType": {
|
||||||
|
"type": "belongsTo",
|
||||||
|
"model": "ComponentTypeRate",
|
||||||
|
"foreignKey": "componentTypeRate"
|
||||||
|
}
|
||||||
|
},
|
||||||
"acls": [
|
"acls": [
|
||||||
{
|
{
|
||||||
"accessType": "READ",
|
"accessType": "READ",
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "ComponentTypeRate",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "componentTypeRate"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"id": true,
|
||||||
|
"type": "Number",
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
require('../methods/sale/filter.js')(Self);
|
require('../methods/sale/filter.js')(Self);
|
||||||
|
require('../methods/sale/saleComponentFilter.js')(Self);
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,6 +51,11 @@
|
||||||
"type": "hasOne",
|
"type": "hasOne",
|
||||||
"model": "SaleChecked",
|
"model": "SaleChecked",
|
||||||
"foreignKey": "saleFk"
|
"foreignKey": "saleFk"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"type": "hasMany",
|
||||||
|
"model": "SaleComponent",
|
||||||
|
"foreignKey": "saleFk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
},
|
},
|
||||||
"componentRate": {
|
"componentRate": {
|
||||||
"type": "belongsTo",
|
"type": "belongsTo",
|
||||||
"model": "componentRate",
|
"model": "ComponentRate",
|
||||||
"foreignKey": "componentFk"
|
"foreignKey": "componentFk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
"ComponentRate": {
|
"ComponentRate": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"ComponentTypeRate": {
|
||||||
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
"SaleComponent": {
|
"SaleComponent": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue