itemDiary, filter, grid and new directive repeat-last #346

This commit is contained in:
Joan Sanchez 2018-07-04 08:50:34 +02:00
parent 2ab6eb5e83
commit 86a8ae0322
12 changed files with 217 additions and 80 deletions

View File

@ -1,8 +1,12 @@
Active: Activo
Add contact: Añadir contacto
Amount: Importe
Client: Cliente
Clients: Clientes
Comercial Name: Comercial
Contacts: Contactos
Basic data: Datos básicos
Back: Volver
Fiscal data: Datos Fiscales
Addresses: Consignatarios
Web access: Acceso web
@ -21,6 +25,4 @@ Credit : Crédito
Credit contracts: Contratos de crédito
Verified data: Datos comprobados
Mandate: Mandato
Amount: Importe
Back: Volver
Contacts: Contactos
Remove contact: Eliminar

View File

@ -8,3 +8,4 @@ import './on-error-src';
import './zoom-image';
import './visible-by';
import './bind';
import './repeat-last';

View File

@ -0,0 +1,21 @@
import ngModule from '../module';
/**
* Calls a passed function if is the last element from an ng-repeat.
*
* @attribute {String} onLast - Callback function
* @return {Object} The directive
*/
directive.$inject = ['$parse'];
export function directive($parse) {
return {
restrict: 'A',
link: function($scope, $element, $attrs) {
if ($scope.$last && $attrs.onLast) {
let fn = $parse($attrs.onLast);
fn($scope);
}
}
};
}
ngModule.directive('vnRepeatLast', directive);

View File

@ -110,7 +110,7 @@
"item": "$ctrl.item"
}
}, {
"url" : "/diary",
"url" : "/diary?q",
"state": "item.card.diary",
"component": "vn-item-diary",
"params": {

View File

@ -25,8 +25,8 @@ describe('Item', () => {
describe('_getItem()', () => {
it('should request to get the item', () => {
$httpBackend.whenGET('/item/api/Items/123?filter={"include":[{"relation":"itemType","scope":{"fields":["name","workerFk"],"include":{"relation":"worker","fields":["firstName","name"]}}},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"}]}').respond({data: 'item'});
$httpBackend.expectGET('/item/api/Items/123?filter={"include":[{"relation":"itemType","scope":{"fields":["name","workerFk"],"include":{"relation":"worker","fields":["firstName","name"]}}},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"}]}');
$httpBackend.whenGET('/item/api/Items/123?filter={"include":[{"relation":"itemType","scope":{"fields":["name","workerFk","warehouseFk"],"include":{"relation":"worker","fields":["firstName","name"]}}},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"}]}').respond({data: 'item'});
$httpBackend.expectGET('/item/api/Items/123?filter={"include":[{"relation":"itemType","scope":{"fields":["name","workerFk","warehouseFk"],"include":{"relation":"worker","fields":["firstName","name"]}}},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"}]}');
controller._getItem();
$httpBackend.flush();
});

View File

@ -35,7 +35,7 @@ class Controller {
include: [
{relation: "itemType",
scope: {
fields: ['name', 'workerFk'],
fields: ['name', 'workerFk', 'warehouseFk'],
include: {
relation: 'worker',
fields: ['firstName', 'name']

View File

@ -1,3 +1,11 @@
<vn-crud-model
vn-id="model"
url="item/api/Items/getDiary"
filter="::$ctrl.filter"
data="sales"
auto-load="false">
</vn-crud-model>
<vn-vertical>
<vn-card pad-large>
<vn-vertical>
@ -8,42 +16,42 @@
url="/item/api/Warehouses"
show-field="name"
value-field="id"
initial-data="$ctrl.warehouseFk"
field="$ctrl.warehouseFk"
label="Select warehouse">
initial-data="$ctrl.filter.where.warehouseFk"
field="$ctrl.filter.where.warehouseFk"
label="Select warehouse" on-change="$ctrl.onChange(value)">
</vn-autocomplete>
</vn-horizontal>
<table class="vn-grid">
<thead>
<tr>
<th number translate>Date</th>
<th number translate>State</th>
<th number translate>Origin</th>
<th number translate>Reference</th>
<th style="text-align: center" translate>Name</th>
<th number translate>In</th>
<th number translate>Out</th>
<th number translate>Balance</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="diary in $ctrl.diary">
<td number>{{diary.date | date:'dd/MM/yyyy HH:mm' }}</td>
<td number>{{diary.alertLevel | dashIfEmpty}}</td>
<td number>{{diary.origin | dashIfEmpty}}</td>
<td number>{{diary.reference | dashIfEmpty}}</td>
<td style="text-align: center">{{diary.name | dashIfEmpty}}</td>
<td number>{{diary.in | dashIfEmpty}}</td>
<td number>{{diary.out | dashIfEmpty}}</td>
<td number>{{diary.balance | dashIfEmpty}}</td>
</tr>
<tr ng-if="$ctrl.diary.length === 0" class="list list-element">
<td colspan="8" style="text-align: center" translate>No results</td>
</tr>
</tbody>
</table>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th>Date</vn-th>
<vn-th number>State</vn-th>
<vn-th number>Origin</vn-th>
<vn-th number>Reference</vn-th>
<vn-th field="name">Worker</vn-th>
<vn-th number>In</vn-th>
<vn-th number>Out</vn-th>
<vn-th number>Balance</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-class="{'warning': $ctrl.isToday(sale.date)}"
ng-repeat="sale in sales" vn-repeat-last on-last="$ctrl.scrollToActive()">
<vn-td>{{::sale.date | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<vn-td number>{{::sale.alertLevel | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.origin | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.reference | dashIfEmpty}}</vn-td>
<vn-td>{{sale.name | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.in | dashIfEmpty}}</vn-td>
<vn-td number>{{::sale.out | dashIfEmpty}}</vn-td>
<vn-td number><span class="balance">{{::sale.balance | dashIfEmpty}}</span></vn-td>
</vn-tr>
</vn-tbody>
<vn-empty-rows ng-if="model.data.length === 0" translate>
No results
</vn-empty-rows>
</vn-table>
</vn-vertical>
</vn-card>
<vn-paging margin-large-top vn-one index="$ctrl.diary" total="$ctrl.diary.count"></vn-paging>
<!-- <vn-auto-paging margin-large-top vn-one index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
</vn-vertical>

View File

@ -2,32 +2,96 @@ import ngModule from '../module';
import './style.scss';
class Controller {
constructor($scope, $http) {
this.$ = $scope;
constructor($scope, $http, $state, $window) {
this.$scope = $scope;
this.$http = $http;
this.diary = [];
this.$state = $state;
this.$window = $window;
}
set warehouseFk(value) {
this._getItemDiary(value);
this._warehouseFk = value;
$postLink() {
if (this.item)
this.filterBuilder();
}
get warehouseFk() {
return this._warehouseFk;
set item(value) {
this._item = value;
if (value && this.$scope.model)
this.filterBuilder();
}
_getItemDiary(warehouse) {
if (warehouse == null)
return;
let params = {itemFk: this.item.id, warehouseFk: warehouse};
this.$http.get(`/item/api/Items/getDiary?params=${JSON.stringify(params)}`).then(res => {
this.diary = res.data;
});
get item() {
return this._item;
}
get alertLevelIndex() {
let lines = this.$scope.model.data;
for (let i = 0; i < lines.length; i++) {
let isFutureDate = new Date(lines[i].date) > new Date();
let isGenreOut = lines[i].alertLevel != 0;
if (!isFutureDate && !isGenreOut)
return i;
}
}
onChange(value) {
if (!value) return;
this.filter.where.warehouseFk = value;
this.$scope.model.refresh();
}
/**
* Builds a filter with default values
* and aplies query params.
*/
filterBuilder() {
this.filter = {
where: {
itemFk: this.item.id,
warehouseFk: this.item.itemType.warehouseFk
}
};
let where = this.filter.where;
if (this.$state.params.q) {
let queryFilter = JSON.parse(this.$state.params.q);
where.warehouseFk = queryFilter.warehouseFk;
}
}
scrollToActive() {
let body = this.$window.document.body;
let lineIndex = this.alertLevelIndex;
let lines = body.querySelector('vn-tbody').children;
if (!lineIndex || !lines.length) return;
lines[lineIndex].scrollIntoView();
lines[lineIndex - 1].querySelector('.balance').classList.add('counter');
}
/**
* Compares a date with the current one
* @param {Object} date - Date to compare
* @return {Boolean} - Returns true if the two dates equals
*/
isToday(date) {
let today = new Date();
today.setHours(0, 0, 0, 0);
let comparedDate = new Date(date);
comparedDate.setHours(0, 0, 0, 0);
if (!(today - comparedDate))
return true;
}
}
Controller.$inject = ['$scope', '$http'];
Controller.$inject = ['$scope', '$http', '$state', '$window'];
ngModule.component('vnItemDiary', {
template: require('./index.html'),

View File

@ -17,25 +17,47 @@ describe('Item', () => {
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new();
controller = $componentController('vnItemDiary', {$scope: $scope});
controller.item = {id: 3};
controller.$scope.model = {};
}));
describe('set warehouseFk()', () => {
it(`should call _getItemDiary() with 2 and set warehouseFk`, () => {
spyOn(controller, '_getItemDiary');
controller.warehouseFk = 2;
describe('isToday()', () => {
it(`should call isToday() an return true if an specified date is the current date`, () => {
let date = new Date();
expect(controller._getItemDiary).toHaveBeenCalledWith(2);
expect(controller.warehouseFk).toEqual(2);
let result = controller.isToday(date);
expect(result).toBeTruthy();
});
it(`should call isToday() an return false if an specified date is the current date`, () => {
let date = '2018-07-03';
let result = controller.isToday(date);
expect(result).toBeFalsy();
});
});
describe('_getItemDiary()', () => {
it(`should make a request to get the diary hwen is called with a number`, () => {
$httpBackend.whenGET('/item/api/Items/getDiary?params={"itemFk":3,"warehouseFk":2}').respond({data: 'item'});
$httpBackend.expectGET('/item/api/Items/getDiary?params={"itemFk":3,"warehouseFk":2}');
controller._getItemDiary(2);
$httpBackend.flush();
describe('alertLevelIndex()', () => {
it(`should call alertLevelIndex() and return an index from line with alertLevel 0 and current date`, () => {
controller.$scope.model = {data: [
{name: 'My item 1', alertLevel: 3, date: '2018-05-02'},
{name: 'My item 2', alertLevel: 1, date: '2018-05-03'},
{name: 'My item 3', alertLevel: 0, date: new Date()}]
};
let result = controller.alertLevelIndex;
expect(result).toEqual(2);
});
});
describe('set item()', () => {
it(`should call filterBuilder()`, () => {
spyOn(controller, 'filterBuilder');
controller.item = {id: 1};
expect(controller.filterBuilder).toHaveBeenCalledWith();
expect(controller.item).toEqual({id: 1});
});
});
});

View File

@ -242,4 +242,15 @@ fieldset[disabled] .mdl-textfield .mdl-textfield__label,
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.counter {
background-color: $main-header;
color: $color-white;
border-radius: 3px;
padding: 5px
}
.counter.small {
font-size: 0.7em
}

View File

@ -3,12 +3,14 @@ module.exports = Self => {
description: 'Returns the ',
accessType: 'READ',
accepts: [{
arg: 'params',
type: 'object',
description: 'itemFk, warehouseFk'
arg: 'filter',
type: 'Object',
required: true,
description: 'Filter defining where and paginated data',
http: {source: 'query'}
}],
returns: {
arg: 'diary',
type: ['Object'],
root: true
},
http: {
@ -17,8 +19,9 @@ module.exports = Self => {
}
});
Self.getDiary = async params => {
let [diary] = await Self.rawSql(`CALL vn.itemDiary(?, ?)`, [params.itemFk, params.warehouseFk]);
Self.getDiary = async filter => {
let where = filter.where;
let [diary] = await Self.rawSql(`CALL vn.itemDiary(?, ?)`, [where.itemFk, where.warehouseFk]);
return diary;
};
};

View File

@ -24,10 +24,15 @@
},
"relations": {
"worker": {
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
}
"type": "belongsTo",
"model": "Worker",
"foreignKey": "workerFk"
},
"warehouse": {
"type": "belongsTo",
"model": "Warehouse",
"foreignKey": "warehouseFk"
}
},
"acls": [
{