#232 components section plus unit tests
This commit is contained in:
parent
946aa7d8e8
commit
dafb554792
|
@ -1,17 +1,25 @@
|
|||
.vn-grid {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
td, th{
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
|
||||
& > thead,
|
||||
& > tbody,
|
||||
& > tfoot {
|
||||
tr {
|
||||
td, th {
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
|
||||
&[number]{
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
& > thead, & > tbody {
|
||||
border-bottom: 3px solid #9D9D9D;
|
||||
}
|
||||
& > tbody > tr{
|
||||
& > tbody > tr {
|
||||
border-bottom: 1px solid #9D9D9D;
|
||||
}
|
||||
td[number], th[number]{
|
||||
text-align: right;
|
||||
}
|
||||
}
|
|
@ -182,6 +182,18 @@
|
|||
"description": "Sale checked",
|
||||
"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-horizontal>
|
||||
<vn-auto margin-medium-v class="left-block">
|
||||
<vn-auto class="left-block">
|
||||
<vn-ticket-descriptor ticket="$ctrl.ticket"></vn-ticket-descriptor>
|
||||
<vn-left-menu></vn-left-menu>
|
||||
</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
|
||||
Client: Cliente
|
||||
Company: Empresa
|
||||
Components: Componentes
|
||||
Counter: Contador
|
||||
Created : Añadido
|
||||
Date : Fecha
|
||||
|
@ -15,13 +16,15 @@ Description: Descripción
|
|||
Discount: Descuento
|
||||
Employee : Empleado
|
||||
Expedition: Expedición
|
||||
Import: Importe
|
||||
Is checked: Comprobado
|
||||
Item: Articulo
|
||||
Landing: Llegada
|
||||
Landed: F. entrega
|
||||
Name: Nombre
|
||||
Margin: Margen
|
||||
m³ per unit: m³ por unidad
|
||||
m³ per quantity: m³ por cantidad
|
||||
Name: Nombre
|
||||
Notes: Notas
|
||||
New price: Nuevo precio
|
||||
New : Nuevo
|
||||
|
@ -40,7 +43,8 @@ Some fields are invalid: Algunos campos no son válidos
|
|||
State: Estado
|
||||
The observation type must be unique: El tipo de observación debe ser único
|
||||
Tickets: Tickets
|
||||
Total import: Importe total
|
||||
Tracking: Revisión
|
||||
Volume: Volumen
|
||||
Warehouse: Almacén
|
||||
Worker: Trabajador
|
||||
Worker: Trabajador
|
||||
|
|
|
@ -18,3 +18,4 @@ import './tracking/index';
|
|||
import './tracking/edit/edit';
|
||||
import './fetched-tags/fetched-tags';
|
||||
import './sale-checked/sale-checked';
|
||||
import './component/component';
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
<td number>{{::sale.itemFk}}</td>
|
||||
<td><vn-fetched-tags sale="sale"/></td>
|
||||
<td number>{{::sale.quantity}}</td>
|
||||
<td number>{{::sale.volume.m3_uni}}</td>
|
||||
<td number>{{::sale.volume.volumeTimesQuantity}}</td>
|
||||
<td number>{{::sale.volume.m3_total}}</td>
|
||||
<td number>{{::sale.volume.m3_uni | number:3}}</td>
|
||||
<td number>{{::sale.volume.volumeTimesQuantity | number:3}}</td>
|
||||
<td number>{{::sale.volume.m3_total | number:3}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -10,28 +10,28 @@ module.exports = Self => {
|
|||
limit: params.size,
|
||||
order: params.order || 'concept ASC',
|
||||
include: [{
|
||||
relation: "item",
|
||||
relation: 'item',
|
||||
scope: {
|
||||
include: {
|
||||
relation: "itemTag",
|
||||
relation: 'itemTag',
|
||||
scope: {
|
||||
fields: ["tagFk", "value"],
|
||||
fields: ['tagFk', 'value'],
|
||||
include: {
|
||||
relation: "tag",
|
||||
relation: 'tag',
|
||||
scope: {
|
||||
fields: ["name"]
|
||||
fields: ['name']
|
||||
}
|
||||
},
|
||||
limit: 6
|
||||
}
|
||||
},
|
||||
fields: ["itemFk", "name"]
|
||||
fields: ['itemFk', 'name']
|
||||
}
|
||||
},
|
||||
{
|
||||
relation: "isChecked",
|
||||
relation: 'isChecked',
|
||||
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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
|
@ -2,38 +2,45 @@
|
|||
"name": "ComponentRate",
|
||||
"base": "VnModel",
|
||||
"options": {
|
||||
"mysql": {
|
||||
"mysql": {
|
||||
"table": "componentRate"
|
||||
}
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"name": {
|
||||
"type": "String"
|
||||
},
|
||||
"classRate": {
|
||||
"type": "Number"
|
||||
},
|
||||
"tax": {
|
||||
"type": "Number"
|
||||
},
|
||||
"isRenewable": {
|
||||
"type": "Number"
|
||||
},
|
||||
"componentTypeRate": {
|
||||
"type": "Number"
|
||||
}
|
||||
"id": {
|
||||
"id": true,
|
||||
"type": "Number",
|
||||
"description": "Identifier"
|
||||
},
|
||||
"name": {
|
||||
"type": "String"
|
||||
},
|
||||
"classRate": {
|
||||
"type": "Number"
|
||||
},
|
||||
"tax": {
|
||||
"type": "Number"
|
||||
},
|
||||
"isRenewable": {
|
||||
"type": "Number"
|
||||
},
|
||||
"componentTypeRate": {
|
||||
"type": "Number"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"componentType": {
|
||||
"type": "belongsTo",
|
||||
"model": "ComponentTypeRate",
|
||||
"foreignKey": "componentTypeRate"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
require('../methods/sale/filter.js')(Self);
|
||||
require('../methods/sale/saleComponentFilter.js')(Self);
|
||||
};
|
||||
|
|
|
@ -51,6 +51,11 @@
|
|||
"type": "hasOne",
|
||||
"model": "SaleChecked",
|
||||
"foreignKey": "saleFk"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"type": "hasMany",
|
||||
"model": "SaleComponent",
|
||||
"foreignKey": "saleFk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
},
|
||||
"componentRate": {
|
||||
"type": "belongsTo",
|
||||
"model": "componentRate",
|
||||
"model": "ComponentRate",
|
||||
"foreignKey": "componentFk"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
"ComponentRate": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ComponentTypeRate": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"SaleComponent": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue