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

View File

@ -8,3 +8,4 @@ import './on-error-src';
import './zoom-image'; import './zoom-image';
import './visible-by'; import './visible-by';
import './bind'; 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" "item": "$ctrl.item"
} }
}, { }, {
"url" : "/diary", "url" : "/diary?q",
"state": "item.card.diary", "state": "item.card.diary",
"component": "vn-item-diary", "component": "vn-item-diary",
"params": { "params": {

View File

@ -25,8 +25,8 @@ describe('Item', () => {
describe('_getItem()', () => { describe('_getItem()', () => {
it('should request to get the item', () => { 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.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"],"include":{"relation":"worker","fields":["firstName","name"]}}},{"relation":"origin"},{"relation":"ink"},{"relation":"producer"},{"relation":"intrastat"},{"relation":"expence"}]}'); $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(); controller._getItem();
$httpBackend.flush(); $httpBackend.flush();
}); });

View File

@ -35,7 +35,7 @@ class Controller {
include: [ include: [
{relation: "itemType", {relation: "itemType",
scope: { scope: {
fields: ['name', 'workerFk'], fields: ['name', 'workerFk', 'warehouseFk'],
include: { include: {
relation: 'worker', relation: 'worker',
fields: ['firstName', 'name'] 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-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
@ -8,42 +16,42 @@
url="/item/api/Warehouses" url="/item/api/Warehouses"
show-field="name" show-field="name"
value-field="id" value-field="id"
initial-data="$ctrl.warehouseFk" initial-data="$ctrl.filter.where.warehouseFk"
field="$ctrl.warehouseFk" field="$ctrl.filter.where.warehouseFk"
label="Select warehouse"> label="Select warehouse" on-change="$ctrl.onChange(value)">
</vn-autocomplete> </vn-autocomplete>
</vn-horizontal> </vn-horizontal>
<table class="vn-grid"> <vn-table model="model">
<thead> <vn-thead>
<tr> <vn-tr>
<th number translate>Date</th> <vn-th>Date</vn-th>
<th number translate>State</th> <vn-th number>State</vn-th>
<th number translate>Origin</th> <vn-th number>Origin</vn-th>
<th number translate>Reference</th> <vn-th number>Reference</vn-th>
<th style="text-align: center" translate>Name</th> <vn-th field="name">Worker</vn-th>
<th number translate>In</th> <vn-th number>In</vn-th>
<th number translate>Out</th> <vn-th number>Out</vn-th>
<th number translate>Balance</th> <vn-th number>Balance</vn-th>
</tr> </vn-tr>
</thead> </vn-thead>
<tbody> <vn-tbody>
<tr ng-repeat="diary in $ctrl.diary"> <vn-tr ng-class="{'warning': $ctrl.isToday(sale.date)}"
<td number>{{diary.date | date:'dd/MM/yyyy HH:mm' }}</td> ng-repeat="sale in sales" vn-repeat-last on-last="$ctrl.scrollToActive()">
<td number>{{diary.alertLevel | dashIfEmpty}}</td> <vn-td>{{::sale.date | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<td number>{{diary.origin | dashIfEmpty}}</td> <vn-td number>{{::sale.alertLevel | dashIfEmpty}}</vn-td>
<td number>{{diary.reference | dashIfEmpty}}</td> <vn-td number>{{::sale.origin | dashIfEmpty}}</vn-td>
<td style="text-align: center">{{diary.name | dashIfEmpty}}</td> <vn-td number>{{::sale.reference | dashIfEmpty}}</vn-td>
<td number>{{diary.in | dashIfEmpty}}</td> <vn-td>{{sale.name | dashIfEmpty}}</vn-td>
<td number>{{diary.out | dashIfEmpty}}</td> <vn-td number>{{::sale.in | dashIfEmpty}}</vn-td>
<td number>{{diary.balance | dashIfEmpty}}</td> <vn-td number>{{::sale.out | dashIfEmpty}}</vn-td>
</tr> <vn-td number><span class="balance">{{::sale.balance | dashIfEmpty}}</span></vn-td>
<tr ng-if="$ctrl.diary.length === 0" class="list list-element"> </vn-tr>
<td colspan="8" style="text-align: center" translate>No results</td> </vn-tbody>
</tr> <vn-empty-rows ng-if="model.data.length === 0" translate>
</tbody> No results
</table> </vn-empty-rows>
</vn-table>
</vn-vertical> </vn-vertical>
</vn-card> </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> </vn-vertical>

View File

@ -2,32 +2,96 @@ import ngModule from '../module';
import './style.scss'; import './style.scss';
class Controller { class Controller {
constructor($scope, $http) { constructor($scope, $http, $state, $window) {
this.$ = $scope; this.$scope = $scope;
this.$http = $http; this.$http = $http;
this.diary = []; this.$state = $state;
this.$window = $window;
} }
set warehouseFk(value) { $postLink() {
this._getItemDiary(value); if (this.item)
this._warehouseFk = value; this.filterBuilder();
} }
get warehouseFk() { set item(value) {
return this._warehouseFk; this._item = value;
if (value && this.$scope.model)
this.filterBuilder();
} }
_getItemDiary(warehouse) { get item() {
if (warehouse == null) return this._item;
return; }
let params = {itemFk: this.item.id, warehouseFk: warehouse};
this.$http.get(`/item/api/Items/getDiary?params=${JSON.stringify(params)}`).then(res => { get alertLevelIndex() {
this.diary = res.data; 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', { ngModule.component('vnItemDiary', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -17,25 +17,47 @@ describe('Item', () => {
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({}); $httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new(); $scope = $rootScope.$new();
controller = $componentController('vnItemDiary', {$scope: $scope}); controller = $componentController('vnItemDiary', {$scope: $scope});
controller.item = {id: 3}; controller.$scope.model = {};
})); }));
describe('set warehouseFk()', () => { describe('isToday()', () => {
it(`should call _getItemDiary() with 2 and set warehouseFk`, () => { it(`should call isToday() an return true if an specified date is the current date`, () => {
spyOn(controller, '_getItemDiary'); let date = new Date();
controller.warehouseFk = 2;
expect(controller._getItemDiary).toHaveBeenCalledWith(2); let result = controller.isToday(date);
expect(controller.warehouseFk).toEqual(2);
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()', () => { describe('alertLevelIndex()', () => {
it(`should make a request to get the diary hwen is called with a number`, () => { it(`should call alertLevelIndex() and return an index from line with alertLevel 0 and current date`, () => {
$httpBackend.whenGET('/item/api/Items/getDiary?params={"itemFk":3,"warehouseFk":2}').respond({data: 'item'}); controller.$scope.model = {data: [
$httpBackend.expectGET('/item/api/Items/getDiary?params={"itemFk":3,"warehouseFk":2}'); {name: 'My item 1', alertLevel: 3, date: '2018-05-02'},
controller._getItemDiary(2); {name: 'My item 2', alertLevel: 1, date: '2018-05-03'},
$httpBackend.flush(); {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; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
overflow: hidden; 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 ', description: 'Returns the ',
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
arg: 'params', arg: 'filter',
type: 'object', type: 'Object',
description: 'itemFk, warehouseFk' required: true,
description: 'Filter defining where and paginated data',
http: {source: 'query'}
}], }],
returns: { returns: {
arg: 'diary', type: ['Object'],
root: true root: true
}, },
http: { http: {
@ -17,8 +19,9 @@ module.exports = Self => {
} }
}); });
Self.getDiary = async params => { Self.getDiary = async filter => {
let [diary] = await Self.rawSql(`CALL vn.itemDiary(?, ?)`, [params.itemFk, params.warehouseFk]); let where = filter.where;
let [diary] = await Self.rawSql(`CALL vn.itemDiary(?, ?)`, [where.itemFk, where.warehouseFk]);
return diary; return diary;
}; };
}; };

View File

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