Merge branch 'dev' into 2940-invoiceInTax-section
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Javi Gallego 2021-06-18 07:07:52 +02:00
commit a6c093cf16
47 changed files with 1186 additions and 676 deletions

View File

@ -806,19 +806,6 @@ INSERT INTO `vn`.`priceFixed`(`id`, `itemFk`, `rate0`, `rate1`, `rate2`, `rate3`
(2, 3, 10, 10, 10, 10, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 0, 1, CURDATE()),
(3, 5, 8.5, 10, 7.5, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL +1 MONTH), 1, 2, CURDATE());
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`)
VALUES
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 1, 1, 18),
(2, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 2, 1, 18),
(3, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 2, 3, 1, 18),
(4, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 4, 4, 1, 18),
(5, 1, 2, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 1, 1, 18),
(6, 7, 3, 71, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), 1, 1, 1, 18),
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), 1, 1, 1, 18),
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), 1, 1, 1, 18),
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 1, 1, 1, 18),
(10, 7, 7, 71, CURDATE(), 1, 1, 1, 18);
INSERT INTO `vn`.`expeditionBoxVol`(`boxFk`, `m3`, `ratio`)
VALUES
(71,0.141,1);
@ -834,6 +821,19 @@ INSERT INTO `vn`.`packaging`(`id`, `volume`, `width`, `height`, `depth`, `isPack
('cc', 1640038.00, 56.00, 220.00, 128.00, 1, CURDATE(), 15, 90.00),
('pallet 100', 2745600.00, 100.00, 220.00, 120.00, 1, CURDATE(), 16, 0.00);
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`, `externalId`, `packagingFk`)
VALUES
(1, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 15, 1, 1, 18, 'UR9000006041', 94),
(2, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), 16, 2, 1, 18, 'UR9000006041', 94),
(3, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 3, 1, 18, 'UR9000006041', 94),
(4, 1, 1, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 4, 1, 18, 'UR9000006041', 94),
(5, 1, 2, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
(6, 7, 3, 71, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL, 1, 1, 18, NULL, 94),
(7, 2, 4, 71, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL, 1, 1, 18, NULL, 94),
(8, 3, 5, 71, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), NULL, 1, 1, 18, NULL, 94),
(9, 3, 6, 71, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), NULL, 1, 1, 18, NULL, 94),
(10, 7, 7, 71, CURDATE(), NULL, 1, 1, 18, NULL, 94);
INSERT INTO `vn`.`ticketPackaging`(`id`, `ticketFk`, `packagingFk`, `quantity`, `created`, `pvp`)
VALUES
(1, 1, 2, 2, CURDATE(), NULL),

View File

@ -1,7 +1,7 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Travel descriptor path', () => {
// Tarea #2972
xdescribe('Travel descriptor path', () => {
let browser;
let page;

View File

@ -43,14 +43,14 @@ export default class Contextmenu {
get row() {
if (!this.target) return null;
return this.target.closest('vn-tr, .vn-tr');
return this.target.closest('[ng-repeat]');
}
get rowIndex() {
if (!this.row) return null;
const table = this.row.closest('vn-table, .vn-table');
const tBody = table.querySelector('vn-tbody, .vn-tbody');
const rows = tBody.querySelectorAll('vn-tr, .vn-tr');
const rows = table.querySelectorAll('[ng-repeat]');
return Array.from(rows).findIndex(
rowItem => rowItem == this.row

View File

@ -229,4 +229,8 @@ vn-table.scrollable.sm,
vn-table.scrollable.lg,
.vn-table.scrollable.lg {
max-height: 700px
}
.tableWrapper {
overflow-x: auto;
}

View File

@ -51,6 +51,8 @@
</vn-data-viewer>
<vn-float-button
ng-if="$ctrl.canCreateNew()"
vn-acl="insurance"
vn-acl-action="remove"
vn-tooltip="New contract"
fixed-bottom-right
ui-sref="client.card.creditInsurance.create"

View File

@ -30,6 +30,8 @@
</div>
<a
ng-if="!$ctrl.isClosed"
vn-acl="insurance"
vn-acl-action="remove"
ui-sref="client.card.creditInsurance.insurance.create({classificationId: {{$ctrl.$params.classificationId}}})"
fixed-bottom-right
vn-tooltip="New credit"

View File

@ -240,14 +240,12 @@
"url": "/credit-insurance",
"abstract": true,
"state": "client.card.creditInsurance",
"component": "ui-view",
"acl": ["insurance"]
"component": "ui-view"
}, {
"url": "/index",
"state": "client.card.creditInsurance.index",
"component": "vn-client-credit-insurance-index",
"description": "Credit contracts",
"acl": ["insurance"],
"params": {
"client": "$ctrl.client"
}
@ -256,6 +254,7 @@
"state": "client.card.creditInsurance.create",
"component": "vn-client-credit-insurance-create",
"description": "New insurance",
"acl": ["insurance"],
"params": {
"client": "$ctrl.client"
}
@ -279,6 +278,7 @@
"state": "client.card.creditInsurance.insurance.create",
"component": "vn-client-credit-insurance-insurance-create",
"description": "New credit",
"acl": ["insurance"],
"params": {
"client": "$ctrl.client"
}

View File

@ -1,3 +1,12 @@
<vn-crud-model
vn-id="ticketsModel"
auto-load="true"
url="Tickets/filter"
link="{'t.clientFk': $ctrl.$params.id}"
limit="5"
data="tickets"
order="shippedDate DESC, shippedHour ASC">
</vn-crud-model>
<vn-card class="summary">
<h5>
<a ng-if="::$ctrl.summary.id"
@ -269,14 +278,112 @@
ng-class="{alert: $ctrl.summary.defaulters[0].amount}"
info="Deviated invoices minus payments">
</vn-label-value>
<vn-vertical ng-if="$ctrl.summary.recovery.started">
<vn-label-value label="Recovery since"
value="{{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}">
<vn-label-value label="Recovery since"
ng-if="$ctrl.summary.recovery.started"
value="{{$ctrl.summary.recovery.started | date:'dd/MM/yyyy'}}">
</vn-label-value>
</vn-vertical>
</vn-one>
</vn-horizontal>
<vn-horizontal>
<vn-one>
<h4 translate>Latest tickets</h4>
<vn-table model="ticketsModel" class="scrollable sm">
<vn-thead>
<vn-tr>
<vn-th field="id" number>Id</vn-th>
<vn-th field="nickname" expand>Client</vn-th>
<vn-th field="salesPersonFk">Salesperson</vn-th>
<vn-th field="shipped" shrink-date>Date</vn-th>
<vn-th field="stateFk">State</vn-th>
<vn-th shrink>Total</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="ticket in ticketsModel.data"
class="clickable vn-tr search-result"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<vn-td number>{{::ticket.id}}</vn-td>
<vn-td expand>
<span
title="{{::ticket.nickname}}"
vn-click-stop="clientDescriptor.show($event, ticket.clientFk)"
class="link">
{{::ticket.nickname}}
</span>
</vn-td>
<vn-td>
<span
title="{{::ticket.userName}}"
vn-click-stop="workerDescriptor.show($event, ticket.salesPersonFk)"
class="link">
{{::ticket.userName | dashIfEmpty}}
</span>
</vn-td>
<vn-td shrink-date>
<span class="chip {{::$ctrl.chipColor(ticket.shipped)}}">
{{::ticket.shipped | date: 'dd/MM/yyyy'}}
</span>
</vn-td>
<vn-td>
<span
ng-show="::ticket.refFk"
title="{{::ticket.refFk}}"
vn-click-stop="invoiceOutDescriptor.show($event, ticket.invoiceOutId)"
class="link">
{{::ticket.refFk}}
</span>
<span
ng-show="::!ticket.refFk"
class="chip {{::$ctrl.stateColor(ticket)}}">
{{::ticket.state}}
</span>
</vn-td>
<vn-td shrink>
<span class="chip {{$ctrl.totalPriceColor(ticket)}}">
{{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}}
</span>
</vn-td>
<vn-td actions>
<vn-icon-button
vn-anchor="::{
state: 'ticket.card.sale',
params: {id: ticket.id},
target: '_blank'
}"
vn-tooltip="Go to lines"
icon="icon-lines">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(ticket)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-td>
</a>
</vn-tbody>
</vn-table>
<vn-pagination
model="ticketsModel"
class="vn-pt-xs"
scroll-selector="vn-table[model='ticketsModel']"
scroll-offset="100">
</vn-pagination>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-client-descriptor-popover
vn-id="clientDescriptor">
</vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
</vn-worker-descriptor-popover>
<vn-invoice-out-descriptor-popover
vn-id="invoiceOutDescriptor">
</vn-invoice-out-descriptor-popover>
<vn-popup vn-id="summary">
<vn-ticket-summary
ticket="$ctrl.selectedTicket"
model="model">
</vn-ticket-summary>
</vn-popup>

View File

@ -39,6 +39,43 @@ class Controller extends Summary {
if (rate)
return rate * 100;
}
stateColor(ticket) {
if (ticket.alertLevelCode === 'OK')
return 'success';
else if (ticket.alertLevelCode === 'FREE')
return 'notice';
else if (ticket.alertLevel === 1)
return 'warning';
else if (ticket.alertLevel === 0)
return 'alert';
}
chipColor(date) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const ticketShipped = new Date(date);
ticketShipped.setHours(0, 0, 0, 0);
const difference = today - ticketShipped;
if (difference == 0)
return 'warning';
if (difference < 0)
return 'success';
}
totalPriceColor(ticket) {
const total = parseInt(ticket.totalWithVat);
if (total > 0 && total < 50)
return 'warning';
}
preview(ticket) {
this.selectedTicket = ticket;
this.$.summary.show();
}
}
ngModule.vnComponent('vnClientSummary', {

View File

@ -4,15 +4,15 @@ describe('Client', () => {
describe('Component vnClientSummary', () => {
let controller;
let $httpBackend;
let $scope;
let $window;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
beforeEach(inject(($componentController, _$httpBackend_, _$window_) => {
$window = _$window_;
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-client-summary></vn-client-summary>');
controller = $componentController('vnClientSummary', {$element, $scope});
controller = $componentController('vnClientSummary', {$element});
controller.client = {id: 101};
}));
@ -48,5 +48,82 @@ describe('Client', () => {
expect(result).toEqual(300);
});
});
describe('chipColor()', () => {
it('should return warning when the date is the present', () => {
let today = new Date();
let result = controller.chipColor(today);
expect(result).toEqual('warning');
});
it('should return success when the date is in the future', () => {
let futureDate = new Date();
futureDate = futureDate.setDate(futureDate.getDate() + 10);
let result = controller.chipColor(futureDate);
expect(result).toEqual('success');
});
it('should return undefined when the date is in the past', () => {
let pastDate = new Date();
pastDate = pastDate.setDate(pastDate.getDate() - 10);
let result = controller.chipColor(pastDate);
expect(result).toEqual(undefined);
});
});
describe('stateColor()', () => {
it('should return "success" when the alertLevelCode property is "OK"', () => {
const result = controller.stateColor({alertLevelCode: 'OK'});
expect(result).toEqual('success');
});
it('should return "notice" when the alertLevelCode property is "FREE"', () => {
const result = controller.stateColor({alertLevelCode: 'FREE'});
expect(result).toEqual('notice');
});
it('should return "warning" when the alertLevel property is "1', () => {
const result = controller.stateColor({alertLevel: 1});
expect(result).toEqual('warning');
});
it('should return "alert" when the alertLevel property is "0"', () => {
const result = controller.stateColor({alertLevel: 0});
expect(result).toEqual('alert');
});
});
describe('totalPriceColor()', () => {
it('should return "warning" when the ticket amount is less than 50€', () => {
const result = controller.totalPriceColor({totalWithVat: '8.50'});
expect(result).toEqual('warning');
});
});
describe('preview()', () => {
it('should show the dialog summary', () => {
controller.$.summary = {show: () => {}};
jest.spyOn(controller.$.summary, 'show');
const ticket = {id: 1, clientFk: 101};
const event = new MouseEvent('click', {
view: $window,
bubbles: true,
cancelable: true
});
controller.preview(event, ticket);
expect(controller.$.summary.show).toHaveBeenCalledWith();
});
});
});
});

View File

@ -19,3 +19,4 @@ Solunion's maximum risk: Riesgo máximo asumido por Solunion
Invoices minus payments: Facturas menos recibos
Deviated invoices minus payments: Facturas fuera de plazo menos recibos
Go to the client: Ir al cliente
Latest tickets: Últimos tickets

View File

@ -1,6 +1,8 @@
@import "variables";
vn-client-summary {
vn-client-summary .summary {
max-width: $width-lg;
.alert span {
color: $color-alert !important
}

View File

@ -25,7 +25,7 @@
<vn-tr ng-repeat="transaction in transactions">
<vn-td shrink>
<vn-icon
vn-tooltip="{{$ctrl.getFormattedMessage(transaction)}}"
vn-tooltip="{{::$ctrl.getFormattedMessage(transaction)}}"
ng-show="::((transaction.errorMessage || transaction.responseMessage) && !transaction.isConfirmed)"
icon="clear">
</vn-icon>

View File

@ -48,21 +48,15 @@ module.exports = Self => {
switch (field) {
case 'size':
case 'density':
case 'minPrice':
case 'description':
case 'packingOut':
modelName = 'Item';
identifier = 'itemFk';
break;
case 'quantity':
case 'buyingValue':
case 'freightValue':
case 'packing':
case 'grouping':
case 'groupingMode':
case 'comissionValue':
case 'packageValue':
case 'price2':
case 'price3':
case 'weight':
modelName = 'Buy';
identifier = 'id';

View File

@ -10,54 +10,59 @@ module.exports = Self => {
accepts: [
{
arg: 'filter',
type: 'Object',
type: 'object',
description: 'Filter defining where, order, offset, and limit - must be a JSON-encoded string',
},
{
arg: 'search',
type: 'String',
type: 'string',
description: `If it's and integer searchs by id, otherwise it searchs by name`,
},
{
arg: 'id',
type: 'Integer',
type: 'integer',
description: 'Item id',
},
{
arg: 'tags',
type: ['Object'],
type: ['object'],
description: 'List of tags to filter with',
http: {source: 'query'}
},
{
arg: 'description',
type: 'String',
type: 'string',
description: 'The item description',
},
{
arg: 'salesPersonFk',
type: 'Integer',
type: 'integer',
description: 'The buyer of the item',
},
{
arg: 'active',
type: 'Boolean',
type: 'boolean',
description: 'Whether the item is or not active',
},
{
arg: 'visible',
type: 'Boolean',
type: 'boolean',
description: 'Whether the item is or not visible',
},
{
arg: 'typeFk',
type: 'Integer',
type: 'integer',
description: 'Type id',
},
{
arg: 'categoryFk',
type: 'Integer',
type: 'integer',
description: 'Category id',
},
{
arg: 'packingOut',
type: 'integer',
description: 'the packingOut',
}
],
returns: {
@ -84,7 +89,10 @@ module.exports = Self => {
? {'i.id': value}
: {'i.name': {like: `%${value}%`}};
case 'id':
return {'i.id': value};
case 'packingOut':
case 'typeFk':
param = `i.${param}`;
return {[param]: value};
case 'description':
return {'i.description': {like: `%${value}%`}};
case 'categoryFk':
@ -93,8 +101,6 @@ module.exports = Self => {
return {'it.workerFk': value};
case 'code':
return {'it.code': value};
case 'typeFk':
return {'i.typeFk': value};
case 'active':
return {'i.isActive': value};
case 'visible':
@ -126,6 +132,7 @@ module.exports = Self => {
i.description,
i.name,
i.subName,
i.packingOut,
i.tag5,
i.value5,
i.tag6,

View File

@ -10,182 +10,185 @@
</vn-watcher>
<div class="vn-w-xl">
<vn-card class="vn-pa-lg">
<vn-horizontal class="header">
<vn-tool-bar class="vn-mb-md">
<vn-button
disabled="$ctrl.selectedBuys() == 0"
ng-click="deleteBuys.show()"
vn-tooltip="Delete buy(s)"
icon="delete">
</vn-button>
</vn-tool-bar>
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
<p><vn-label translate>VAT</vn-label> {{$ctrl.ticket.totalWithVat - $ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.ticket.totalWithVat | currency: 'EUR':2}}</strong></p>
</vn-one>
</vn-horizontal>
<table class="vn-table">
<thead>
<tr>
<th shrink>
<vn-multi-check model="model" on-change="$ctrl.resetChanges()">
</vn-multi-check>
</th>
<th translate center>Item</th>
<th translate center>Quantity</th>
<th translate center>Package</th>
<th translate>Stickers</th>
<th translate>Weight</th>
<th translate>Packing</th>
<th translate>Grouping</th>
<th translate>Buying value</th>
<th translate expand>Grouping price</th>
<th translate expand>Packing price</th>
<th translate>Import</th>
</tr>
</thead>
<tbody ng-repeat="buy in $ctrl.buys">
<tr>
<td shrink>
<vn-check tabindex="-1" ng-model="buy.checked">
</vn-check>
</td>
<td shrink>
<span
ng-if="buy.id"
ng-click="itemDescriptor.show($event, buy.item.id)"
class="link">
{{::buy.item.id | zeroFill:6}}
</span>
<vn-autocomplete ng-if="!buy.id" class="dense"
vn-focus
url="Items"
ng-model="buy.itemFk"
show-field="name"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
on-change="$ctrl.saveBuy(buy)"
order="id DESC"
tabindex="1">
<tpl-item>
{{::id}} - {{::name}}
</tpl-item>
</vn-autocomplete>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.quantity | dashIfEmpty}}"
ng-model="buy.quantity"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td center>
<vn-autocomplete
vn-one
title="{{::buy.packageFk | dashIfEmpty}}"
url="Packagings"
show-field="id"
value-field="id"
where="{isBox: true}"
ng-model="buy.packageFk"
on-change="$ctrl.saveBuy(buy)">
</vn-autocomplete>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.stickers | dashIfEmpty}}"
ng-model="buy.stickers"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.weight | dashIfEmpty}}"
ng-model="buy.weight"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.packing | dashIfEmpty}}"
ng-model="buy.packing"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.grouping | dashIfEmpty}}"
ng-model="buy.grouping"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.buyingValue | dashIfEmpty}}"
ng-model="buy.buyingValue"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.price2 | dashIfEmpty}}"
ng-model="buy.price2"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.price3 | dashIfEmpty}}"
ng-model="buy.price3"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<span
ng-if="buy.quantity != null && buy.buyingValue != null"
title="{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}">
{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}
</span>
</td>
</tr>
<tr class="dark-row">
<td shrink>
</td>
<td shrink>
<span translate-attr="{title: 'Item type'}">
{{::buy.item.itemType.code}}
</span>
</td>
<td number shrink>
<span translate-attr="{title: 'Item size'}">
{{::buy.item.size}}
</span>
</td>
<td center>
<span translate-attr="{title: 'Minimum price'}">
{{::buy.item.minPrice | currency: 'EUR':2}}
</span>
</td>
<td vn-fetched-tags colspan="9">
<vn-one title="{{::buy.item.name}}">{{::buy.item.name}}</vn-one>
<vn-fetched-tags
max-length="6"
item="::buy.item"
tabindex="-1">
</vn-fetched-tags>
</td>
</tr>
</tbody>
</table>
<div>
<vn-icon-button
vn-one
vn-tooltip="Add buy"
vn-bind="+"
icon="add_circle"
ng-click="model.insert({})">
</vn-icon-button>
<div class="tableWrapper">
<vn-horizontal class="header">
<vn-tool-bar class="vn-mb-md">
<vn-button
disabled="$ctrl.selectedBuys() == 0"
ng-click="deleteBuys.show()"
vn-tooltip="Delete buy(s)"
icon="delete">
</vn-button>
</vn-tool-bar>
<vn-one class="taxes" ng-if="$ctrl.sales.length > 0">
<p><vn-label translate>Subtotal</vn-label> {{$ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
<p><vn-label translate>VAT</vn-label> {{$ctrl.ticket.totalWithVat - $ctrl.ticket.totalWithoutVat | currency: 'EUR':2}}</p>
<p><vn-label><strong>Total</strong></vn-label> <strong>{{$ctrl.ticket.totalWithVat | currency: 'EUR':2}}</strong></p>
</vn-one>
</vn-horizontal>
<table class="vn-table">
<thead>
<tr>
<th shrink>
<vn-multi-check model="model" on-change="$ctrl.resetChanges()">
</vn-multi-check>
</th>
<th translate center>Item</th>
<th translate center>Quantity</th>
<th translate center>Package</th>
<th translate>Stickers</th>
<th translate>Weight</th>
<th translate>Packing</th>
<th translate>Grouping</th>
<th translate>Buying value</th>
<th translate expand>Grouping price</th>
<th translate expand>Packing price</th>
<th translate>Import</th>
</tr>
</thead>
<tbody ng-repeat="buy in $ctrl.buys">
<tr>
<td shrink>
<vn-check tabindex="-1" ng-model="buy.checked">
</vn-check>
</td>
<td shrink>
<span
ng-if="buy.id"
ng-click="itemDescriptor.show($event, buy.item.id)"
class="link">
{{::buy.item.id | zeroFill:6}}
</span>
<vn-autocomplete ng-if="!buy.id" class="dense"
vn-focus
url="Items"
ng-model="buy.itemFk"
show-field="name"
value-field="id"
search-function="$ctrl.itemSearchFunc($search)"
on-change="$ctrl.saveBuy(buy)"
order="id DESC"
tabindex="1">
<tpl-item>
{{::id}} - {{::name}}
</tpl-item>
</vn-autocomplete>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.quantity | dashIfEmpty}}"
ng-model="buy.quantity"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td center>
<vn-autocomplete
vn-one
title="{{::buy.packageFk | dashIfEmpty}}"
url="Packagings"
show-field="id"
value-field="id"
where="{isBox: true}"
ng-model="buy.packageFk"
on-change="$ctrl.saveBuy(buy)">
</vn-autocomplete>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.stickers | dashIfEmpty}}"
ng-model="buy.stickers"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.weight | dashIfEmpty}}"
ng-model="buy.weight"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.packing | dashIfEmpty}}"
ng-model="buy.packing"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.grouping | dashIfEmpty}}"
ng-model="buy.grouping"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.buyingValue | dashIfEmpty}}"
ng-model="buy.buyingValue"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.price2 | dashIfEmpty}}"
ng-model="buy.price2"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<vn-input-number class="dense"
title="{{::buy.price3 | dashIfEmpty}}"
ng-model="buy.price3"
on-change="$ctrl.saveBuy(buy)">
</vn-input-number>
</td>
<td>
<span
ng-if="buy.quantity != null && buy.buyingValue != null"
title="{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}">
{{buy.quantity * buy.buyingValue | currency: 'EUR':2}}
</span>
</td>
</tr>
<tr class="dark-row">
<td shrink>
</td>
<td shrink>
<span translate-attr="{title: 'Item type'}">
{{::buy.item.itemType.code}}
</span>
</td>
<td number shrink>
<span translate-attr="{title: 'Item size'}">
{{::buy.item.size}}
</span>
</td>
<td center>
<span translate-attr="{title: 'Minimum price'}">
{{::buy.item.minPrice | currency: 'EUR':2}}
</span>
</td>
<td vn-fetched-tags colspan="8">
<vn-one title="{{::buy.item.name}}">{{::buy.item.name}}</vn-one>
<vn-fetched-tags
max-length="6"
item="::buy.item"
tabindex="-1">
</vn-fetched-tags>
</td>
</tr>
<tr><td></td></tr>
</tbody>
</table>
<div>
<vn-icon-button
vn-one
vn-tooltip="Add buy"
vn-bind="+"
icon="add_circle"
ng-click="model.insert({})">
</vn-icon-button>
</div>
</div>
</vn-card>
</div>

View File

@ -3,19 +3,34 @@
vn-entry-buy-index vn-card {
max-width: $width-xl;
.dark-row {
background-color: lighten($color-marginal, 10%);
}
tbody tr:nth-child(1) {
border-top: 1px solid $color-marginal;
thead tr {
border-left: 1px solid white;
border-right: 1px solid white;
}
tbody tr:nth-child(1),
tbody tr:nth-child(2) {
border-left: 1px solid $color-marginal;
border-right: 1px solid $color-marginal;
}
tbody tr:nth-child(2) {
border-bottom: 1px solid $color-marginal;
}
tbody{
border-bottom: 1px solid $color-marginal;
}
tbody:last-child {
border-bottom: 0;
}
tbody tr:nth-child(3) {
height: inherit
}

View File

@ -57,6 +57,7 @@
<vn-th field="ektFk">Ekt</vn-th>
<vn-th field="weight">Weight</vn-th>
<vn-th field="packageFk" expand>PackageName</vn-th>
<vn-th field="packingOut" expand>PackingOut</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
@ -147,6 +148,7 @@
<vn-td number>{{::buy.ektFk | dashIfEmpty}}</vn-td>
<vn-td number>{{::buy.weight}}</vn-td>
<vn-td number>{{::buy.packageFk}}</vn-td>
<vn-td number>{{::buy.packingOut}}</vn-td>
</a>
</vn-tbody>
</vn-table>

View File

@ -16,20 +16,14 @@ export default class Controller extends Section {
if (this._columns) return this._columns;
this._columns = [
{field: 'quantity', displayName: this.$t('Quantity')},
{field: 'buyingValue', displayName: this.$t('Buying value')},
{field: 'freightValue', displayName: this.$t('Freight value')},
{field: 'packing', displayName: this.$t('Packing')},
{field: 'grouping', displayName: this.$t('Grouping')},
{field: 'comissionValue', displayName: this.$t('Commission value')},
{field: 'packageValue', displayName: this.$t('Package value')},
{field: 'price2', displayName: this.$t('Grouping price')},
{field: 'price3', displayName: this.$t('Packing price')},
{field: 'weight', displayName: this.$t('Weight')},
{field: 'description', displayName: this.$t('Description')},
{field: 'minPrice', displayName: this.$t('Min price')},
{field: 'size', displayName: this.$t('Size')},
{field: 'density', displayName: this.$t('Density')}
{field: 'density', displayName: this.$t('Density')},
{field: 'packingOut', displayName: this.$t('PackingOut')}
];
return this._columns;
@ -59,10 +53,14 @@ export default class Controller extends Section {
}
onEditAccept() {
const rowsToEdit = [];
for (let row of this.checked)
rowsToEdit.push({id: row.id, itemFk: row.itemFk});
let data = {
field: this.editedColumn.field,
newValue: this.editedColumn.newValue,
lines: this.checked
lines: rowsToEdit
};
return this.$http.post('Buys/editLatestBuys', data)

View File

@ -13,4 +13,5 @@ Minimun amount: Cantidad mínima de compra
Field to edit: Campo a editar
PackageName: Cubo
Edit: Editar
buy(s): compra(s)
buy(s): compra(s)
PackingOut: Packing envíos

View File

@ -137,6 +137,9 @@
"minPrice": {
"type": "number"
},
"packingOut": {
"type": "number"
},
"hasMinPrice": {
"type": "boolean"
},

View File

@ -10,6 +10,11 @@
Clients on website
</vn-one>
<vn-none>
<vn-icon class="arrow"
icon="keyboard_arrow_up"
vn-tooltip="Minimize/Maximize"
ng-click="$ctrl.main.toggle()">
</vn-icon>
<vn-icon
icon="refresh"
vn-tooltip="Refresh"
@ -17,7 +22,7 @@
</vn-icon>
</vn-none>
</vn-horizontal>
<vn-card>
<vn-card vn-id="card">
<vn-table model="model" class="scrollable sm">
<vn-thead>
<vn-tr>
@ -52,12 +57,6 @@
</vn-tr>
</vn-tbody>
</vn-table>
<div
ng-if="!model.data.length"
class="empty-rows vn-pa-sm"
translate>
No results
</div>
<vn-pagination
model="model"
class="vn-pt-xs"

View File

@ -26,5 +26,8 @@ export default class Controller extends Section {
ngModule.vnComponent('vnMonitorSalesClients', {
template: require('./index.html'),
controller: Controller
controller: Controller,
require: {
main: '^vnMonitorIndex'
}
});

View File

@ -1,11 +1,5 @@
<vn-horizontal>
<vn-three class="vn-mr-sm">
<vn-monitor-sales-tickets></vn-monitor-sales-tickets>
</vn-three>
<vn-one>
<vn-vertical>
<vn-monitor-sales-clients class="vn-mb-sm"></vn-monitor-sales-clients>
<vn-monitor-sales-orders></vn-monitor-sales-orders>
</vn-vertical>
</vn-one>
</vn-horizontal>
<vn-monitor-sales-clients class="vn-mb-sm"></vn-monitor-sales-clients>
<vn-monitor-sales-orders></vn-monitor-sales-orders>
</vn-horizontal>
<vn-monitor-sales-tickets></vn-monitor-sales-tickets>

View File

@ -2,7 +2,22 @@ import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
export default class Controller extends Section {
toggle() {
const monitor = this.element.querySelector('vn-horizontal');
const isHidden = monitor.classList.contains('hidden');
if (!isHidden)
monitor.classList.add('hidden');
else
monitor.classList.remove('hidden');
}
}
ngModule.vnComponent('vnMonitorIndex', {
template: require('./index.html'),
controller: Section
controller: Controller,
require: {
main: '^vnMonitorIndex'
}
});

View File

@ -4,4 +4,5 @@ Recent order actions: Acciones recientes en pedidos
Search tickets: Buscar tickets
Delete selected elements: Eliminar los elementos seleccionados
All the selected elements will be deleted. Are you sure you want to continue?: Todos los elementos seleccionados serán eliminados. ¿Seguro que quieres continuar?
Component lack: Faltan componentes
Component lack: Faltan componentes
Minimize/Maximize: Minimizar/Maximizar

View File

@ -1,4 +1,4 @@
<vn-crud-model
<vn-crud-model auto-load="true"
vn-id="model"
url="SalesMonitors/ordersFilter"
limit="6"
@ -15,6 +15,11 @@
ng-if="$ctrl.totalChecked > 0"
ng-click="delete.show()">
</vn-icon>
<vn-icon class="arrow"
icon="keyboard_arrow_up"
vn-tooltip="Minimize/Maximize"
ng-click="$ctrl.main.toggle()">
</vn-icon>
<vn-icon
icon="refresh"
vn-tooltip="Refresh"
@ -31,13 +36,13 @@
model="model">
</vn-multi-check>
</vn-th>
<vn-th field="date_make" shrink-datetime default-order="DESC">Date</vn-th>
<vn-th field="date_send" shrink-datetime>Date</vn-th>
<vn-th field="clientFk">Client</vn-th>
<vn-th>Import</vn-th>
<vn-th field="salesPersonFk" shrink>SalesPerson</vn-th>
</vn-tr>
</vn-thead>
<a ng-repeat="order in model.data"
class="clickable vn-tbody search-result"
class="clickable vn-tbody"
ui-sref="order.card.summary({id: {{::order.id}}})" target="_blank">
<vn-tr>
<vn-td>
@ -47,7 +52,7 @@
</vn-check>
</vn-td>
<vn-td>
<span class="chip success">
<span class="chip {{::$ctrl.chipColor(order.date_send)}}">
{{::order.date_send | date: 'dd/MM/yyyy'}}
</span>
</vn-td>
@ -59,11 +64,18 @@
{{::order.clientName}}
</span>
</vn-td>
<vn-td number>{{::order.import | currency: 'EUR':2}}</vn-td>
<vn-td>
<span
title="{{::order.salesPerson}}"
vn-click-stop="workerDescriptor.show($event, order.salesPersonFk)"
class="link">
{{::order.salesPerson | dashIfEmpty}}
</span>
</vn-td>
</vn-tr>
<vn-tr>
<vn-td></vn-td>
<vn-td shrink-datetime>
<vn-td>
<span>
{{::order.date_make | date: 'dd/MM/yyyy HH:mm'}}
</span>
@ -74,22 +86,11 @@
</span>
</vn-td>
<vn-td>
<span
title="{{::order.salesPerson}}"
vn-click-stop="workerDescriptor.show($event, order.salesPersonFk)"
class="link">
{{::order.salesPerson | dashIfEmpty}}
</span>
{{::order.import | currency: 'EUR':2}}
</vn-td>
</vn-tr>
</a>
</vn-table>
<div
ng-if="!model.data.length"
class="empty-rows vn-pa-sm"
translate>
No results
</div>
<vn-pagination
model="model"
class="vn-pt-xs"
@ -103,6 +104,35 @@
<vn-client-descriptor-popover
vn-id="clientDescriptor">
</vn-client-descriptor-popover>
<vn-contextmenu vn-id="contextmenu" targets="['vn-monitor-sales-orders vn-table']" model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.filterBySelection()">
Filter by selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()">
Exclude selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()">
Remove filter
</vn-item>
<vn-item translate
ng-click="contextmenu.removeAllFilters()">
Remove all filters
</vn-item>
<vn-item translate
ng-if="contextmenu.isActionAllowed()"
ng-click="contextmenu.copyValue()">
Copy value
</vn-item>
</slot-menu>
</vn-contextmenu>
<vn-confirm
vn-id="delete"
on-accept="$ctrl.onDelete()"

View File

@ -24,9 +24,51 @@ export default class Controller extends Section {
this.$http.post(query, params).then(
() => this.$.model.refresh());
}
chipColor(date) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const orderLanded = new Date(date);
orderLanded.setHours(0, 0, 0, 0);
const difference = today - orderLanded;
if (difference == 0)
return 'warning';
if (difference < 0)
return 'success';
if (difference > 0)
return 'alert';
}
exprBuilder(param, value) {
switch (param) {
case 'date_send':
return {[`o.date_send`]: {
between: this.dateRange(value)}
};
case 'clientFk':
return {[`c.id`]: value};
case 'salesPersonFk':
return {[`c.${param}`]: value};
}
}
dateRange(value) {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
}
}
ngModule.vnComponent('vnMonitorSalesOrders', {
template: require('./index.html'),
controller: Controller
controller: Controller,
require: {
main: '^vnMonitorIndex'
}
});

View File

@ -2,7 +2,8 @@
vn-monitor-sales-orders {
vn-table.scrollable {
max-height: 350px
max-height: 350px;
overflow-x: hidden
}
vn-table a.vn-tbody {

View File

@ -21,36 +21,17 @@
ng-model="filter.orderFk">
</vn-textfield>
</vn-horizontal>
<section class="vn-px-md">
<vn-horizontal class="manifold-panel vn-pa-md">
<vn-date-picker
vn-one
label="From"
ng-model="filter.from"
on-change="$ctrl.from = value">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
ng-model="filter.to"
on-change="$ctrl.to = value">
</vn-date-picker>
<vn-none class="or vn-px-md" translate>Or</vn-none>
<vn-input-number
vn-one
min="0"
step="1"
label="Days onward"
ng-model="filter.scopeDays"
on-change="$ctrl.scopeDays = value"
display-controls="true">
</vn-input-number>
<vn-icon color-marginal
icon="info"
vn-tooltip="Cannot choose a range of dates and days onward at the same time">
</vn-icon>
</vn-horizontal>
</section>
<vn-horizontal class="vn-px-lg">
<vn-input-number
vn-one
min="0"
step="1"
label="Days onward"
ng-model="filter.scopeDays"
on-change="$ctrl.scopeDays = value"
display-controls="true">
</vn-input-number>
</vn-horizontal>
<vn-horizontal class="vn-px-lg">
<vn-textfield
vn-one

View File

@ -3,54 +3,83 @@
vn-monitor-index {
.header {
padding: 12px 0;
padding: 12px 0 5px 0;
color: gray;
font-size: 1.2rem;
border-bottom: 2px solid $color-font-secondary;
margin-bottom: 10px;
& > vn-none > vn-icon {
@extend %clickable-light;
color: $color-button;
font-size: 1.4rem;
}
}
.empty-rows {
color: $color-font-secondary;
text-align: center;
vn-none > .arrow {
transition: transform 200ms;
}
}
vn-vertical {
position: fixed;
width: 400px
vn-monitor-sales-clients {
vn-card {
margin-right: 15px;
}
.header {
padding-right: 15px;
& > vn-none > .arrow {
display: none
}
}
}
vn-table.scrollable {
height: 300px
}
vn-horizontal {
flex-wrap: wrap
}
.hidden {
vn-card {
display: none
}
.header > vn-none > .arrow {
transform: rotate(180deg);
}
}
}
@media (max-width:1500px) {
@media (max-width:1150px) {
vn-monitor-index {
& > vn-horizontal {
flex-direction: column-reverse;
flex-direction: column;
vn-monitor-sales-clients,
vn-monitor-sales-orders {
width: 100%
}
vn-monitor-sales-clients {
margin-bottom: 15px
}
}
& > vn-horizontal > vn-one {
flex: none;
width: 100%;
vn-monitor-sales-clients {
vn-card {
margin-right: 0
}
& > vn-vertical {
position: initial;
flex-direction: row;
width: 100%;
vn-monitor-sales-clients {
margin-right: 15px
}
vn-table.scrollable {
height: 300px
.header {
padding-right: 0;
& > vn-none > .arrow {
display: inline-block
}
}
}
}
}
}

View File

@ -115,6 +115,14 @@ describe('Component vnMonitorSalesTickets', () => {
});
});
describe('totalPriceColor()', () => {
it('should return "warning" when the ticket amount is less than 50€', () => {
const result = controller.totalPriceColor({totalWithVat: '8.50'});
expect(result).toEqual('warning');
});
});
describe('preview()', () => {
it('should show the dialog summary', () => {
controller.$.summary = {show: () => {}};

View File

@ -49,6 +49,7 @@
rule>
</vn-input-number>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
label="Save"

View File

@ -34,14 +34,14 @@ module.exports = Self => {
e.checked,
i2.name freightItemName,
e.itemFk,
u.nickname userNickname,
u.name userName,
e.created,
e.externalId,
i3.name packagingName,
i3.id packagingItemFk,
e.packagingFk,
es.workerFk expeditionScanWorkerFk,
su.nickname scannerUserNickname,
su.name scannerUserName,
es.scanned
FROM
vn.expedition e

View File

@ -17,6 +17,8 @@ describe('ticket makeInvoice()', () => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
done();
});
afterAll(async done => {

View File

@ -8,60 +8,62 @@
</vn-crud-model>
<vn-data-viewer model="model" class="vn-w-xl">
<vn-card class="vn-mt-md">
<table class="vn-table">
<thead>
<tr>
<th number translate>Item</th>
<th translate expand>Description</th>
<th number translate>Quantity</th>
<th translate>Serie</th>
<th translate>Components</th>
<th number translate>Import</th>
<th number translate>Total</th>
</tr>
</thead>
<tbody ng-repeat="sale in components track by sale.id">
<tr class="initial">
<td rowspan="{{::sale.components.length + 1}}" number>
<span
ng-click="itemDescriptor.show($event, sale.itemFk, sale.id)"
class="link">
{{sale.itemFk | zeroFill:6}}
</span>
</td>
<td rowspan="{{::sale.components.length + 1}}" vn-fetched-tags>
<vn-one title="{{::sale.item.name}}">{{::sale.item.name}}</vn-one>
<vn-one ng-if="::sale.item.subName">
<h3 title="{{::sale.item.subName}}">{{::sale.item.subName}}</h3>
</vn-one>
<vn-fetched-tags
max-length="6"
item="::sale.item"
tabindex="-1">
</vn-fetched-tags>
</td>
<td rowspan="{{::sale.components.length + 1}}" number>
{{::sale.quantity}}
</td>
</tr>
<tr
ng-repeat="saleComponent in sale.components track by saleComponent.componentFk"
class="components">
<td>
{{::saleComponent.component.componentType.type}}
</td>
<td>
{{::saleComponent.component.name}}
</td>
<td number>
{{::saleComponent.value | currency: 'EUR':3}}
</td>
<td number>
{{::sale.quantity * saleComponent.value | currency: 'EUR':3}}
</td>
</tr>
</tbody>
</table>
<div class="tableWrapper">
<table class="vn-table">
<thead>
<tr>
<th number translate>Item</th>
<th translate expand>Description</th>
<th number translate>Quantity</th>
<th translate>Serie</th>
<th translate>Components</th>
<th number translate>Import</th>
<th number translate>Total</th>
</tr>
</thead>
<tbody ng-repeat="sale in components track by sale.id">
<tr class="initial">
<td rowspan="{{::sale.components.length + 1}}" number>
<span
ng-click="itemDescriptor.show($event, sale.itemFk, sale.id)"
class="link">
{{sale.itemFk | zeroFill:6}}
</span>
</td>
<td rowspan="{{::sale.components.length + 1}}" vn-fetched-tags>
<vn-one title="{{::sale.item.name}}">{{::sale.item.name}}</vn-one>
<vn-one ng-if="::sale.item.subName">
<h3 title="{{::sale.item.subName}}">{{::sale.item.subName}}</h3>
</vn-one>
<vn-fetched-tags
max-length="6"
item="::sale.item"
tabindex="-1">
</vn-fetched-tags>
</td>
<td rowspan="{{::sale.components.length + 1}}" number>
{{::sale.quantity}}
</td>
</tr>
<tr
ng-repeat="saleComponent in sale.components track by saleComponent.componentFk"
class="components">
<td>
{{::saleComponent.component.componentType.type}}
</td>
<td>
{{::saleComponent.component.name}}
</td>
<td number>
{{::saleComponent.value | currency: 'EUR':3}}
</td>
<td number>
{{::sale.quantity * saleComponent.value | currency: 'EUR':3}}
</td>
</tr>
</tbody>
</table>
</div>
</vn-card>
</vn-data-viewer>
<vn-side-menu side="right">

View File

@ -15,14 +15,14 @@
<vn-th></vn-th>
<vn-th field="itemFk" number>Expedition</vn-th>
<vn-th field="itemFk" number>Item</vn-th>
<vn-th field="name">Name</vn-th>
<vn-th field="isBox">Package type</vn-th>
<vn-th field="packageItemName">Name</vn-th>
<vn-th field="freightItemName">Package type</vn-th>
<vn-th field="counter" number>Counter</vn-th>
<vn-th field="externalId" number>externalId</vn-th>
<vn-th field="worker">Packager</vn-th>
<vn-th field="workerFk">Packager</vn-th>
<vn-th field="created" expand>Created</vn-th>
<vn-th field="scanned" expand>Scanned</vn-th>
<vn-th field="expeditionScanWorkerFk">Palletizer</vn-th>
<vn-th field="scanned" expand>Scanned</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
@ -44,21 +44,21 @@
<vn-td>{{::expedition.packageItemName}}</vn-td>
<vn-td>{{::expedition.freightItemName}}</vn-td>
<vn-td number>{{::expedition.counter}}</vn-td>
<vn-td number>{{::expedition.externalId}}</vn-td>
<vn-td expand>
<vn-td expand>{{::expedition.externalId}}</vn-td>
<vn-td>
<span
class="link"
ng-click="workerDescriptor.show($event, expedition.workerFk)">
{{::expedition.userNickname | dashIfEmpty}}
{{::expedition.userName | dashIfEmpty}}
</span>
</vn-td>
<vn-td expand>{{::expedition.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td expand>{{::expedition.scanned | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td expand>
<vn-td shrink-datetime>{{::expedition.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td>
<span class="link" ng-click="workerDescriptor.show($event, expedition.expeditionScanWorkerFk)">
{{::expedition.scannerUserNickname | dashIfEmpty}}
{{::expedition.scannerUserName | dashIfEmpty}}
</span>
</vn-td>
<vn-td shrink-datetime>{{::expedition.scanned | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>

View File

@ -5,149 +5,149 @@
model="model"
class="vn-mb-xl vn-w-xl">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</vn-th>
<vn-th class="icon-field"></vn-th>
<vn-th field="id">Id</vn-th>
<vn-th field="salesPersonFk" class="expendable">Salesperson</vn-th>
<vn-th field="shipped" shrink-date>Date</vn-th>
<vn-th>Hour</vn-th>
<vn-th field="zoneHour" shrink>Closure</vn-th>
<vn-th field="nickname">Alias</vn-th>
<vn-th field="provinceFk" class="expendable">Province</vn-th>
<vn-th field="stateFk" >State</vn-th>
<vn-th field="zoneFk">Zone</vn-th>
<vn-th field="warehouseFk">Warehouse</vn-th>
<vn-th number>Total</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="ticket in model.data"
class="clickable vn-tr search-result"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<vn-td>
<vn-check
ng-model="ticket.checked"
vn-click-stop>
</vn-check>
</vn-td>
<vn-td class="icon-field">
<vn-icon
ng-show="::ticket.isTaxDataChecked === 0"
translate-attr="{title: 'No verified data'}"
class="bright"
icon="icon-no036">
</vn-icon>
<vn-icon
ng-show="::ticket.hasTicketRequest"
translate-attr="{title: 'Purchase request'}"
class="bright"
icon="icon-100">
</vn-icon>
<vn-icon
ng-show="::ticket.isAvailable === 0"
translate-attr="{title: 'Not available'}"
class="bright"
icon="icon-unavailable">
</vn-icon>
<vn-icon
ng-show="::ticket.isFreezed"
translate-attr="{title: 'Client frozen'}"
class="bright"
icon="icon-frozen">
</vn-icon>
<vn-icon
ng-show="::ticket.risk"
title="{{::$ctrl.$t('Risk')}}: {{ticket.risk}}"
class="bright"
icon="icon-risk">
</vn-icon>
<vn-icon
ng-show="::ticket.hasComponentLack"
translate-attr="{title: 'Component lack'}"
class="bright"
icon="icon-components">
</vn-icon>
</vn-td>
<vn-td shrink>{{::ticket.id}}</vn-td>
<vn-td class="expendable">
<span
title="{{::ticket.userName}}"
vn-click-stop="workerDescriptor.show($event, ticket.salesPersonFk)"
class="link">
{{::ticket.userName | dashIfEmpty}}
</span>
</vn-td>
<vn-td shrink-date>
<span class="chip {{$ctrl.compareDate(ticket.shipped)}}">
{{::ticket.shipped | date: 'dd/MM/yyyy'}}
</span>
</vn-td>
<vn-td shrink>{{::ticket.shipped | date: 'HH:mm'}}</vn-td>
<vn-td shrink>{{::ticket.zoneLanding | date: 'HH:mm'}}</vn-td>
<vn-td>
<span
title="{{::ticket.nickname}}"
vn-click-stop="clientDescriptor.show($event, ticket.clientFk)"
class="link">
{{::ticket.nickname}}
</span>
</vn-td>
<vn-td class="expendable">{{::ticket.province}}</vn-td>
<vn-td class="expendable">
<span
ng-show="ticket.refFk"
title="{{::ticket.refFk}}"
vn-click-stop="invoiceOutDescriptor.show($event, ticket.invoiceOutId)"
class="link">
{{::ticket.refFk}}
</span>
<span
ng-show="!ticket.refFk"
class="chip {{$ctrl.stateColor(ticket)}}">
{{ticket.state}}
</span>
</vn-td>
<vn-td>
<span
title="{{::ticket.zoneName}}"
vn-click-stop="zoneDescriptor.show($event, ticket.zoneFk)"
class="link">
{{::ticket.zoneName | dashIfEmpty}}
</span>
</vn-td>
<vn-td>{{::ticket.warehouse}}</vn-td>
<vn-td number>
<span class="chip {{$ctrl.totalPriceColor(ticket)}}">
{{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}}
</span>
</vn-td>
<vn-td actions>
<vn-icon-button
vn-anchor="::{
state: 'ticket.card.sale',
params: {id: ticket.id},
target: '_blank'
}"
vn-tooltip="Go to lines"
icon="icon-lines">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(ticket)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-td>
</a>
</vn-tbody>
</vn-table>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</vn-th>
<vn-th class="icon-field"></vn-th>
<vn-th field="id">Id</vn-th>
<vn-th field="salesPersonFk" class="expendable">Salesperson</vn-th>
<vn-th field="shipped" shrink-date>Date</vn-th>
<vn-th>Hour</vn-th>
<vn-th field="zoneHour" shrink>Closure</vn-th>
<vn-th field="nickname">Alias</vn-th>
<vn-th field="provinceFk" class="expendable">Province</vn-th>
<vn-th field="stateFk" >State</vn-th>
<vn-th field="zoneFk">Zone</vn-th>
<vn-th field="warehouseFk">Warehouse</vn-th>
<vn-th number>Total</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<a ng-repeat="ticket in model.data"
class="clickable vn-tr search-result"
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
<vn-td>
<vn-check
ng-model="ticket.checked"
vn-click-stop>
</vn-check>
</vn-td>
<vn-td class="icon-field">
<vn-icon
ng-show="::ticket.isTaxDataChecked === 0"
translate-attr="{title: 'No verified data'}"
class="bright"
icon="icon-no036">
</vn-icon>
<vn-icon
ng-show="::ticket.hasTicketRequest"
translate-attr="{title: 'Purchase request'}"
class="bright"
icon="icon-100">
</vn-icon>
<vn-icon
ng-show="::ticket.isAvailable === 0"
translate-attr="{title: 'Not available'}"
class="bright"
icon="icon-unavailable">
</vn-icon>
<vn-icon
ng-show="::ticket.isFreezed"
translate-attr="{title: 'Client frozen'}"
class="bright"
icon="icon-frozen">
</vn-icon>
<vn-icon
ng-show="::ticket.risk"
title="{{::$ctrl.$t('Risk')}}: {{ticket.risk}}"
class="bright"
icon="icon-risk">
</vn-icon>
<vn-icon
ng-show="::ticket.hasComponentLack"
translate-attr="{title: 'Component lack'}"
class="bright"
icon="icon-components">
</vn-icon>
</vn-td>
<vn-td shrink>{{::ticket.id}}</vn-td>
<vn-td class="expendable">
<span
title="{{::ticket.userName}}"
vn-click-stop="workerDescriptor.show($event, ticket.salesPersonFk)"
class="link">
{{::ticket.userName | dashIfEmpty}}
</span>
</vn-td>
<vn-td shrink-date>
<span class="chip {{$ctrl.compareDate(ticket.shipped)}}">
{{::ticket.shipped | date: 'dd/MM/yyyy'}}
</span>
</vn-td>
<vn-td shrink>{{::ticket.shipped | date: 'HH:mm'}}</vn-td>
<vn-td shrink>{{::ticket.zoneLanding | date: 'HH:mm'}}</vn-td>
<vn-td>
<span
title="{{::ticket.nickname}}"
vn-click-stop="clientDescriptor.show($event, ticket.clientFk)"
class="link">
{{::ticket.nickname}}
</span>
</vn-td>
<vn-td class="expendable">{{::ticket.province}}</vn-td>
<vn-td class="expendable">
<span
ng-show="ticket.refFk"
title="{{::ticket.refFk}}"
vn-click-stop="invoiceOutDescriptor.show($event, ticket.invoiceOutId)"
class="link">
{{::ticket.refFk}}
</span>
<span
ng-show="!ticket.refFk"
class="chip {{$ctrl.stateColor(ticket)}}">
{{ticket.state}}
</span>
</vn-td>
<vn-td>
<span
title="{{::ticket.zoneName}}"
vn-click-stop="zoneDescriptor.show($event, ticket.zoneFk)"
class="link">
{{::ticket.zoneName | dashIfEmpty}}
</span>
</vn-td>
<vn-td>{{::ticket.warehouse}}</vn-td>
<vn-td number>
<span class="chip {{$ctrl.totalPriceColor(ticket)}}">
{{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}}
</span>
</vn-td>
<vn-td actions>
<vn-icon-button
vn-anchor="::{
state: 'ticket.card.sale',
params: {id: ticket.id},
target: '_blank'
}"
vn-tooltip="Go to lines"
icon="icon-lines">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(ticket)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-td>
</a>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<div fixed-bottom-right>

View File

@ -18,6 +18,13 @@
translate>
Clone travel and his entries
</vn-item>
<vn-item
id="delete"
ng-click="delete.show()"
ng-show="$ctrl.isBuyer && !$ctrl.entries.length"
translate>
Delete travel
</vn-item>
<a class="vn-item"
ui-sref="entry.create({travelFk: $ctrl.travel.id})"
name="addEntry"
@ -35,6 +42,14 @@
message="All it's properties will be copied">
</vn-confirm>
<!-- Delete travel popup -->
<vn-confirm
vn-id="delete"
on-accept="$ctrl.onDeleteAccept()"
question="Do you want to delete this travel?"
message="The travel will be deleted">
</vn-confirm>
<!-- Clone travel popup -->
<vn-confirm
vn-id="cloneWithEntries"

View File

@ -44,14 +44,23 @@ class Controller extends Section {
}
]
};
return this.$http.get(`Travels/${this.travelId}`, {filter})
this.$http.get(`Travels/${this.travelId}`, {filter})
.then(res => this.travel = res.data);
this.$http.get(`Travels/${this.travelId}/getEntries`)
.then(res => this.entries = res.data);
}
get isBuyer() {
return this.aclService.hasAny(['buyer']);
}
onDeleteAccept() {
this.$http.delete(`Travels/${this.travelId}`)
.then(() => this.$state.go('travel.index'))
.then(() => this.vnApp.showSuccess(this.$t('Travel deleted')));
}
onCloneAccept() {
const params = JSON.stringify({
ref: this.travel.ref,

View File

@ -40,6 +40,21 @@ describe('Travel Component vnTravelDescriptorMenu', () => {
});
});
describe('onDeleteAccept()', () => {
it('should perform a delete query', () => {
jest.spyOn(controller.$state, 'go').mockReturnValue('ok');
controller.travelId = 1;
$httpBackend.when('GET', `Travels/${controller.travelId}`).respond(200);
$httpBackend.when('GET', `Travels/${controller.travelId}/getEntries`).respond(200);
$httpBackend.expect('DELETE', `Travels/${controller.travelId}`).respond(200);
controller.onDeleteAccept();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('travel.index');
});
});
describe('onCloneWithEntriesAccept()', () => {
it('should make an HTTP query and then call to the $state.go method with the returned id', () => {
jest.spyOn(controller.$state, 'go').mockReturnValue('ok');

View File

@ -2,3 +2,7 @@ Clone travel: Clonar envío
Add entry: Añadir entrada
Clone travel and his entries: Clonar travel y sus entradas
Do you want to clone this travel and all containing entries?: ¿Quieres clonar este travel y todas las entradas que contiene?
Delete travel: Eliminar envío
The travel will be deleted: El envío será eliminado
Do you want to delete this travel?: ¿Quieres eliminar este envío?
Travel deleted: Envío eliminado

255
package-lock.json generated
View File

@ -2851,12 +2851,6 @@
"stack-chain": "^1.3.7"
}
},
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"dev": true
},
"async-settle": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
@ -3597,16 +3591,16 @@
}
},
"browserslist": {
"version": "4.16.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz",
"integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==",
"version": "4.16.6",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz",
"integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30001181",
"colorette": "^1.2.1",
"electron-to-chromium": "^1.3.649",
"caniuse-lite": "^1.0.30001219",
"colorette": "^1.2.2",
"electron-to-chromium": "^1.3.723",
"escalade": "^3.1.1",
"node-releases": "^1.1.70"
"node-releases": "^1.1.71"
}
},
"bser": {
@ -3824,9 +3818,9 @@
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
},
"caniuse-lite": {
"version": "1.0.30001200",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001200.tgz",
"integrity": "sha512-ic/jXfa6tgiPBAISWk16jRI2q8YfjxHnSG7ddSL1ptrIP8Uy11SayFrjXRAk3NumHpDb21fdTkbTxb/hOrFrnQ==",
"version": "1.0.30001237",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz",
"integrity": "sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw==",
"dev": true
},
"canonical-json": {
@ -4589,21 +4583,67 @@
}
},
"css-select": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
"integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==",
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz",
"integrity": "sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==",
"dev": true,
"requires": {
"boolbase": "^1.0.0",
"css-what": "^3.2.1",
"domutils": "^1.7.0",
"nth-check": "^1.0.2"
"css-what": "^5.0.0",
"domhandler": "^4.2.0",
"domutils": "^2.6.0",
"nth-check": "^2.0.0"
},
"dependencies": {
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"dev": true
},
"domhandler": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz",
"integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==",
"dev": true,
"requires": {
"domelementtype": "^2.2.0"
}
},
"domutils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz",
"integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==",
"dev": true,
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
},
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true
}
}
},
"css-what": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
"integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.0.1.tgz",
"integrity": "sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg==",
"dev": true
},
"cssesc": {
@ -5031,9 +5071,9 @@
"dev": true
},
"dns-packet": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
"integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
"integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
"dev": true,
"requires": {
"ip": "^1.1.0",
@ -5282,9 +5322,9 @@
"integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA=="
},
"electron-to-chromium": {
"version": "1.3.687",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.687.tgz",
"integrity": "sha512-IpzksdQNl3wdgkzf7dnA7/v10w0Utf1dF2L+B4+gKrloBrxCut+au+kky3PYvle3RMdSxZP+UiCZtLbcYRxSNQ==",
"version": "1.3.752",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz",
"integrity": "sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==",
"dev": true
},
"elliptic": {
@ -8336,9 +8376,9 @@
}
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
"hpack.js": {
@ -8709,12 +8749,12 @@
"integrity": "sha512-wcGvY31MpFNHIkUcXHHnvrE4IKYlpvitJw5P/1u892gMBAM46muQ+RH7UN1d+Ntnfx5apnOnVY6vcLmrWHOLwg=="
},
"httpntlm": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz",
"integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=",
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.7.7.tgz",
"integrity": "sha512-Pv2Rvrz8H0qv1Dne5mAdZ9JegG1uc6Vu5lwLflIY6s8RKHdZQbW39L4dYswSgqMDT0pkJILUTKjeyU0VPNRZjA==",
"requires": {
"httpreq": ">=0.4.22",
"underscore": "~1.7.0"
"underscore": "~1.12.1"
}
},
"httpreq": {
@ -13392,9 +13432,9 @@
}
},
"node-releases": {
"version": "1.1.71",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz",
"integrity": "sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==",
"version": "1.1.73",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz",
"integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==",
"dev": true
},
"node-sass": {
@ -13639,9 +13679,9 @@
"dev": true
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==",
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
"integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
"dev": true
},
"now-and-later": {
@ -13673,12 +13713,12 @@
}
},
"nth-check": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz",
"integrity": "sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==",
"dev": true,
"requires": {
"boolbase": "~1.0.0"
"boolbase": "^1.0.0"
}
},
"number-is-nan": {
@ -14555,9 +14595,9 @@
"integrity": "sha1-Su7rfa3mb8qKk2XdqfawBXQctiE="
},
"postcss": {
"version": "7.0.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz",
"integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==",
"version": "7.0.36",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.36.tgz",
"integrity": "sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==",
"dev": true,
"requires": {
"chalk": "^2.4.2",
@ -15442,16 +15482,16 @@
"dev": true
},
"renderkid": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz",
"integrity": "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==",
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz",
"integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==",
"dev": true,
"requires": {
"css-select": "^2.0.2",
"dom-converter": "^0.2",
"htmlparser2": "^3.10.1",
"lodash": "^4.17.20",
"strip-ansi": "^3.0.0"
"css-select": "^4.1.3",
"dom-converter": "^0.2.0",
"htmlparser2": "^6.1.0",
"lodash": "^4.17.21",
"strip-ansi": "^3.0.1"
},
"dependencies": {
"ansi-regex": {
@ -15460,6 +15500,61 @@
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"dev": true
},
"domhandler": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.2.0.tgz",
"integrity": "sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==",
"dev": true,
"requires": {
"domelementtype": "^2.2.0"
}
},
"domutils": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.7.0.tgz",
"integrity": "sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg==",
"dev": true,
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
},
"entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true
},
"htmlparser2": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"dev": true,
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.0.0",
"domutils": "^2.5.2",
"entities": "^2.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@ -16669,6 +16764,22 @@
"requires": {
"httpntlm": "1.6.1",
"nodemailer-shared": "1.1.0"
},
"dependencies": {
"httpntlm": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz",
"integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=",
"requires": {
"httpreq": ">=0.4.22",
"underscore": "~1.7.0"
}
},
"underscore": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
"integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk="
}
}
},
"snakeize": {
@ -18603,9 +18714,9 @@
}
},
"underscore": {
"version": "1.7.0",
"resolved": "http://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
"integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk="
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
"integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw=="
},
"underscore.string": {
"version": "3.3.5",
@ -20112,12 +20223,20 @@
}
},
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
},
"dependencies": {
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"dev": true
}
}
},
"yargs": {
@ -20402,9 +20521,9 @@
}
},
"ws": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz",
"integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw=="
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz",
"integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw=="
},
"x-xss-protection": {
"version": "1.3.0",

View File

@ -24,7 +24,7 @@ module.exports = {
*
* @return {Object} - Pool connection
*/
getConnection(name) {
getConnection(name = 'default') {
let pool = this.pool;
return new Promise((resolve, reject) => {
pool.getConnection(name, function(error, connection) {

View File

@ -15,22 +15,11 @@ module.exports = {
continent: this.continent
};
const connection = await db.pool.getConnection();
await this.fetchTravels(args, connection);
const travels = await this.rawSql(`
SELECT * FROM tmp.travel`, connection);
const entries = await this.fetchEntries(connection);
await this.rawSql(`
DROP TEMPORARY TABLE
tmp.travel`, connection);
await connection.release();
const travels = await this.fetchTravels(args);
const travelIds = travels.map(travel => travel.id);
const entries = await this.fetchEntries(travelIds);
const map = new Map();
for (let travel of travels)
map.set(travel.id, travel);
@ -71,7 +60,7 @@ module.exports = {
}
},
methods: {
fetchTravels(args, connection) {
fetchTravels(args) {
const where = db.buildWhere(args, (key, value) => {
switch (key) {
case 'shippedFrom':
@ -91,14 +80,11 @@ module.exports = {
query = db.merge(query, where);
query = db.merge(query, 'GROUP BY t.id');
return this.rawSql(query, connection);
return this.rawSql(query);
},
fetchEntries(connection) {
let query = this.getSqlFromDef('entries');
query = db.merge(query, 'GROUP BY e.id');
return this.rawSql(query, connection);
fetchEntries(travelIds) {
return this.rawSqlFromDef('entries', [travelIds]);
}
},
components: {

View File

@ -1,15 +1,17 @@
SELECT
e.id,
e.travelFk,
e.ref,
s.name AS supplierName,
SUM(b.stickers) AS stickers,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
FROM tmp.travel t
JOIN entry e ON e.travelFk = t.id
JOIN buy b ON b.entryFk = e.id
JOIN packaging pkg ON pkg.id = b.packageFk
JOIN item i ON i.id = b.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN supplier s ON s.id = e.supplierFk
e.id,
e.travelFk,
e.ref,
s.name AS supplierName,
SUM(b.stickers) AS stickers,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
FROM travel t
JOIN entry e ON e.travelFk = t.id
JOIN buy b ON b.entryFk = e.id
JOIN packaging pkg ON pkg.id = b.packageFk
JOIN item i ON i.id = b.itemFk
JOIN itemType it ON it.id = i.typeFk
JOIN supplier s ON s.id = e.supplierFk
WHERE t.id IN(?)
GROUP BY e.id

View File

@ -1,24 +1,21 @@
CREATE TEMPORARY TABLE tmp.travel
(INDEX (id))
ENGINE = MEMORY
SELECT
t.id,
t.ref,
t.shipped,
t.landed,
t.kg,
SUM(b.stickers) AS stickers,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
FROM travel t
LEFT JOIN supplier s ON s.id = t.cargoSupplierFk
LEFT JOIN entry e ON e.travelFk = t.id
LEFT JOIN buy b ON b.entryFk = e.id
LEFT JOIN packaging pkg ON pkg.id = b.packageFk
LEFT JOIN item i ON i.id = b.itemFk
LEFT JOIN itemType it ON it.id = i.typeFk
JOIN warehouse w ON w.id = t.warehouseInFk
JOIN warehouse wo ON wo.id = t.warehouseOutFk
JOIN country c ON c.id = wo.countryFk
LEFT JOIN continent cnt ON cnt.id = c.continentFk
JOIN agencyMode am ON am.id = t.agencyFk
SELECT
t.id,
t.ref,
t.shipped,
t.landed,
t.kg,
SUM(b.stickers) AS stickers,
CAST(SUM(i.density * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as loadedKg,
CAST(SUM(167.5 * b.stickers * IF(pkg.volume, pkg.volume, pkg.width * pkg.depth * pkg.height) / 1000000 ) as DECIMAL(10,0)) as volumeKg
FROM travel t
LEFT JOIN supplier s ON s.id = t.cargoSupplierFk
LEFT JOIN entry e ON e.travelFk = t.id
LEFT JOIN buy b ON b.entryFk = e.id
LEFT JOIN packaging pkg ON pkg.id = b.packageFk
LEFT JOIN item i ON i.id = b.itemFk
LEFT JOIN itemType it ON it.id = i.typeFk
JOIN warehouse w ON w.id = t.warehouseInFk
JOIN warehouse wo ON wo.id = t.warehouseOutFk
JOIN country c ON c.id = wo.countryFk
LEFT JOIN continent cnt ON cnt.id = c.continentFk
JOIN agencyMode am ON am.id = t.agencyFk