Merge branch 'dev' of https://git.verdnatura.es/salix into dev

This commit is contained in:
Javi Gallego 2018-07-16 12:34:03 +02:00
commit 77e0494b37
183 changed files with 3680 additions and 1674 deletions

View File

@ -1,8 +1,15 @@
<mg-ajax path="/client/api/Clients/{{index.params.id}}/listAddresses" options="mgIndex"></mg-ajax> <vn-crud-model
vn-id="model"
url="/client/api/Addresses"
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
data="addresses">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-title vn-one>Addresses</vn-title> <vn-title vn-one>Addresses</vn-title>
<vn-horizontal ng-repeat="address in index.model.items track by address.id" class="pad-medium-top" style="align-items: center;"> <vn-horizontal ng-repeat="address in addresses" class="pad-medium-top" style="align-items: center;">
<vn-one border-radius class="pad-small border-solid" <vn-one border-radius class="pad-small border-solid"
ng-class="{'bg-main': address.isDefaultAddress,'bg-opacity-item': !address.isActive && !address.isDefaultAddress}"> ng-class="{'bg-main': address.isDefaultAddress,'bg-opacity-item': !address.isActive && !address.isDefaultAddress}">
<vn-horizontal style="align-items: center;"> <vn-horizontal style="align-items: center;">
@ -56,7 +63,6 @@
</vn-one> </vn-one>
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>
<vn-paging index="index" total="index.model.total"></vn-paging>
<vn-float-button <vn-float-button
vn-bind="+" vn-bind="+"
fixed-bottom-right fixed-bottom-right

View File

@ -1,20 +1,28 @@
import ngModule from '../../module'; import ngModule from '../../module';
class Controller { class Controller {
constructor($http, $scope) { constructor($http, $scope, $stateParams) {
this.$http = $http; this.$http = $http;
this.$scope = $scope; this.$scope = $scope;
this.$stateParams = $stateParams;
this.filter = {
include: {
observations: 'observationType'
},
order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC']
};
} }
setDefault(address) { setDefault(address) {
if (address.isActive) { if (address.isActive) {
let params = {isDefaultAddress: true}; let params = {isDefaultAddress: true};
this.$http.patch(`/client/api/Addresses/${address.id}`, params).then( this.$http.patch(`/client/api/Addresses/${address.id}`, params).then(
() => this.$scope.index.accept() () => this.$scope.model.refresh()
); );
} }
} }
} }
Controller.$inject = ['$http', '$scope']; Controller.$inject = ['$http', '$scope', '$stateParams'];
ngModule.component('vnClientAddressIndex', { ngModule.component('vnClientAddressIndex', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax> <mg-ajax path="/client/api/Clients/{{patch.params.id}}/updateBasicData" options="vnPatch"></mg-ajax>
<vn-watcher <vn-watcher
vn-id="watcher" vn-id="watcher"
data="$ctrl.client" data="$ctrl.client"

View File

@ -16,53 +16,48 @@ describe('Client', () => {
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
$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();
let submit = jasmine.createSpy('submit').and.returnValue(Promise.resolve()); $scope.watcher = {
$scope.watcher = {submit}; submit: () => {
return {
then: callback => {
callback();
}
};
}
};
$httpBackend.get = jasmine.createSpy('get').and.returnValue(Promise.resolve()); $httpBackend.get = jasmine.createSpy('get').and.returnValue(Promise.resolve());
controller = $componentController('vnClientBillingData', {$scope: $scope}, {$http: $httpBackend}); controller = $componentController('vnClientBillingData', {$scope: $scope}, {$http: $httpBackend});
controller.client = {id: 101, name: 'Client name', payMethodFk: 4};
})); }));
describe('copyData()', () => { describe('client()', () => {
it(`should define billData using client's data`, () => { it(`should call setter client`, () => {
controller.client = { expect(controller.orgData).toEqual(controller.client);
dueDay: 0,
iban: null,
payMethodFk: 1
};
controller.billData = {};
controller.copyData(controller.client);
expect(controller.billData).toEqual(controller.client);
}); });
}); });
describe('submit()', () => { describe('hasPaymethodChanged()', () => {
it(`should call submit() on the watcher then receive a callback`, done => { it(`should call hasPaymethodChanged() and return true if there are changes on payMethod data`, () => {
spyOn(controller, 'checkPaymentChanges'); controller.client.payMethodFk = 5;
controller.submit()
.then(() => { expect(controller.hasPaymethodChanged()).toBeTruthy();
expect(controller.$.watcher.submit).toHaveBeenCalledWith(); });
expect(controller.checkPaymentChanges).toHaveBeenCalledWith();
done(); it(`should call hasPaymethodChanged() and return false if there are no changes on payMethod data`, () => {
}); controller.client.payMethodFk = 4;
expect(controller.hasPaymethodChanged()).toBeFalsy();
}); });
}); });
describe('checkPaymentChanges()', () => { describe('onSubmit()', () => {
it(`should not call sendMail.show() if there are no changes on billing data`, () => { it(`should call notifyChanges() if there are changes on payMethod data`, () => {
controller.billData = {marvelHero: 'Silver Surfer'}; spyOn(controller, 'notifyChanges');
controller.client = {marvelHero: 'Silver Surfer'}; controller.client.payMethodFk = 5;
controller.checkPaymentChanges(); controller.onSubmit();
expect(controller.$http.get).not.toHaveBeenCalled(); expect(controller.hasPaymethodChanged()).toBeTruthy();
}); expect(controller.notifyChanges).toHaveBeenCalledWith();
it(`should call sendMail.show() if there are changes on billing data object`, () => {
controller.billData = {id: '123', marvelHero: 'Silver Surfer'};
controller.client = {id: '123', marvelHero: 'Spider-Man'};
controller.checkPaymentChanges();
expect(controller.$http.get).toHaveBeenCalled();
}); });
}); });
}); });

View File

@ -1,11 +1,11 @@
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax> <mg-ajax path="/client/api/Clients/{{patch.params.id}}/updateBillingData" options="vnPatch"></mg-ajax>
<vn-watcher <vn-watcher
vn-id="watcher" vn-id="watcher"
data="$ctrl.client" data="$ctrl.client"
form="form" form="form"
save="patch"> save="patch">
</vn-watcher> </vn-watcher>
<form name="form" ng-submit="$ctrl.submit()"> <form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large> <vn-card pad-large>
<vn-title>Pay method</vn-title> <vn-title>Pay method</vn-title>
<vn-horizontal> <vn-horizontal>
@ -36,8 +36,8 @@
<vn-horizontal pad-small-v> <vn-horizontal pad-small-v>
<vn-one> <vn-one>
<vn-check <vn-check
label="Received core VNH" label="Received LCR"
field="$ctrl.client.hasCoreVnh" field="$ctrl.client.hasLcr"
vn-acl="administrative, salesAssistant"> vn-acl="administrative, salesAssistant">
</vn-check> </vn-check>
</vn-one> </vn-one>

View File

@ -2,47 +2,42 @@ import ngModule from '../module';
export default class Controller { export default class Controller {
constructor($scope, $http, vnApp, $translate) { constructor($scope, $http, vnApp, $translate) {
this.$ = $scope; this.$scope = $scope;
this.$http = $http; this.$http = $http;
this.vnApp = vnApp; this.vnApp = vnApp;
this.translate = $translate; this.translate = $translate;
this.billData = {};
this.copyData();
} }
$onChanges() { set client(value) {
this.copyData(); this._client = value;
if (value)
this.orgData = Object.assign({}, value);
} }
copyData() { get client() {
if (this.client) { return this._client;
this.billData.payMethodFk = this.client.payMethodFk;
this.billData.iban = this.client.iban;
this.billData.dueDay = this.client.dueDay;
}
} }
submit() { onSubmit() {
return this.$.watcher.submit().then( this.$scope.watcher.submit().then(() => {
() => this.checkPaymentChanges()); if (this.hasPaymethodChanged())
this.notifyChanges();
});
} }
checkPaymentChanges() { notifyChanges() {
let equals = true; this.$http.get(`/mailer/notification/payment-update/${this.client.id}`).then(
Object.keys(this.billData).forEach( () => this.vnApp.showMessage(this.translate.instant('Notification sent!'))
val => {
if (this.billData[val] !== this.client[val]) {
this.billData[val] = this.client[val];
equals = false;
}
}
); );
}
if (!equals) { hasPaymethodChanged() {
this.$http.get(`/mailer/notification/payment-update/${this.client.id}`).then( let payMethod = this.orgData.payMethodFk != this.client.payMethodFk;
() => this.vnApp.showMessage(this.translate.instant('Notification sent!')) let iban = this.orgData.iban != this.client.iban;
); let dueDay = this.orgData.dueDay != this.client.dueDay;
}
return payMethod || iban || dueDay;
} }
} }
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate']; Controller.$inject = ['$scope', '$http', 'vnApp', '$translate'];

View File

@ -9,7 +9,6 @@ Equivalent tax spreaded: Equivalent tax spreaded
Invoice by address: Invoice by address Invoice by address: Invoice by address
Equalization tax: Equalization tax Equalization tax: Equalization tax
Due day: Due day Due day: Due day
Received core VNH: VNH core received
Received core VNL: VNL core received Received core VNL: VNL core received
Received B2B VNL: VNL B2B received Received B2B VNL: VNL B2B received
Save: Save Save: Save

View File

@ -9,7 +9,7 @@ Equivalent tax spreaded: Recargo de equivalencia propagado
Invoice by address: Facturar por consignatario Invoice by address: Facturar por consignatario
Equalization tax: Recargo de equivalencia Equalization tax: Recargo de equivalencia
Due day: Vencimiento Due day: Vencimiento
Received core VNH: Recibido core VNH Received LCR: Recibido LCR
Received core VNL: Recibido core VNL Received core VNL: Recibido core VNL
Received B2B VNL: Recibido B2B VNL Received B2B VNL: Recibido B2B VNL
Save: Guardar Save: Guardar

View File

@ -19,7 +19,7 @@ describe('Client', () => {
$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();
$scope.form = {$invalid: false}; $scope.form = {$invalid: false};
$scope.index = {accept: () => {}}; $scope.model = {refresh: () => {}};
controller = $componentController('vnClientContactIndex', {$scope: $scope}, {$state: $state}); controller = $componentController('vnClientContactIndex', {$scope: $scope}, {$state: $state});
controller.client = { controller.client = {
id: 101 id: 101
@ -70,11 +70,11 @@ describe('Client', () => {
describe('submit()', () => { describe('submit()', () => {
it("should perfom a query to delete contacts", () => { it("should perfom a query to delete contacts", () => {
controller._oldContacts = []; controller.oldContacts = [];
controller._oldContacts[1] = {id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'}; controller.oldContacts[1] = {id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'};
controller._oldContacts[2] = {id: 2, clientFk: 101, name: 'My contact 2', phone: '123456789'}; controller.oldContacts[2] = {id: 2, clientFk: 101, name: 'My contact 2', phone: '123456789'};
controller._contacts = [ controller.contacts = [
{id: 2, name: 'My contact 2', phone: '123456789'} {id: 2, name: 'My contact 2', phone: '123456789'}
]; ];
controller.removedContacts = [1]; controller.removedContacts = [1];
@ -93,11 +93,11 @@ describe('Client', () => {
}); });
it("should perfom a query to update contacts", () => { it("should perfom a query to update contacts", () => {
controller._oldContacts = []; controller.oldContacts = [];
controller._oldContacts[1] = {id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'}; controller.oldContacts[1] = {id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'};
controller._oldContacts[2] = {id: 2, clientFk: 101, name: 'My contact 2', phone: '123456789'}; controller.oldContacts[2] = {id: 2, clientFk: 101, name: 'My contact 2', phone: '123456789'};
controller._contacts = [ controller.contacts = [
{id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'}, {id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'},
{id: 2, clientFk: 101, name: 'My contact 2', phone: '111111111'} {id: 2, clientFk: 101, name: 'My contact 2', phone: '111111111'}
]; ];
@ -119,11 +119,11 @@ describe('Client', () => {
}); });
it("should perfom a query to create new contact", () => { it("should perfom a query to create new contact", () => {
controller._oldContacts = []; controller.oldContacts = [];
controller._oldContacts[1] = {id: 1, name: 'My contact 1', phone: '123456789'}; controller.oldContacts[1] = {id: 1, name: 'My contact 1', phone: '123456789'};
controller._oldContacts[2] = {id: 2, name: 'My contact 2', phone: '123456789'}; controller.oldContacts[2] = {id: 2, name: 'My contact 2', phone: '123456789'};
controller._contacts = [ controller.contacts = [
{id: 1, name: 'My contact 1', phone: '123456789'}, {id: 1, name: 'My contact 1', phone: '123456789'},
{id: 2, name: 'My contact 2', phone: '123456789'}, {id: 2, name: 'My contact 2', phone: '123456789'},
{name: 'My contact 3', phone: '123456789'} {name: 'My contact 3', phone: '123456789'}

View File

@ -1,12 +1,14 @@
<mg-ajax <vn-crud-model
path="/client/api/Clients/{{index.params.id}}/contacts" vn-id="model"
options="mgIndex" url="/client/api/ClientContacts"
actions="$ctrl.contacts = index.model"> link="{clientFk: $ctrl.$stateParams.id}"
</mg-ajax> data="contacts" on-data-change="$ctrl.onDataChange()">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.submit()"> <form name="form" ng-submit="$ctrl.submit()">
<vn-card pad-large> <vn-card pad-large>
<vn-title>Contacts</vn-title> <vn-title>Contacts</vn-title>
<vn-horizontal ng-repeat="contact in index.model track by $index"> <vn-horizontal ng-repeat="contact in contacts">
<vn-textfield <vn-textfield
vn-one vn-one
label="Name" label="Name"

View File

@ -1,25 +1,18 @@
import ngModule from '../module'; import ngModule from '../module';
class Controller { class Controller {
constructor($http, $scope, $translate, vnApp) { constructor($http, $scope, $stateParams, $translate, vnApp) {
this.$http = $http; this.$http = $http;
this.$scope = $scope; this.$scope = $scope;
this.$stateParams = $stateParams;
this.$translate = $translate; this.$translate = $translate;
this.vnApp = vnApp; this.vnApp = vnApp;
this.removedContacts = []; this.removedContacts = [];
} }
/** onDataChange() {
* Setter for contacts and original contacts this.contacts = this.$scope.model.data;
* @param {Object} value - Contacts object this.oldContacts = this.$scope.model.data;
*/
set contacts(value) {
this._contacts = value;
this.oldContacts = value;
}
get contacts() {
return this._contacts;
} }
/** /**
@ -64,7 +57,7 @@ class Controller {
this.$http.post(query, data).then(() => { this.$http.post(query, data).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!')); this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
this.$scope.index.accept(); this.$scope.model.refresh();
}); });
} }
@ -85,7 +78,7 @@ class Controller {
add() { add() {
let data = { let data = {
clientFk: this.client.id, clientFk: this.client.id,
name: 'Teléfono', name: this.$translate.instant('Phone'),
phone: null phone: null
}; };
this.contacts.push(data); this.contacts.push(data);
@ -115,7 +108,7 @@ class Controller {
} }
} }
Controller.$inject = ['$http', '$scope', '$translate', 'vnApp']; Controller.$inject = ['$http', '$scope', '$stateParams', '$translate', 'vnApp'];
ngModule.component('vnClientContactIndex', { ngModule.component('vnClientContactIndex', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -6,7 +6,7 @@
vn-one vn-one
label="Credit" label="Credit"
model="$ctrl.creditClassification.credit", model="$ctrl.creditClassification.credit",
rule="CreditInsurance.credit" rule="creditInsurance.credit"
step="1" step="1"
vn-focus> vn-focus>
</vn-textfield> </vn-textfield>
@ -20,7 +20,7 @@
vn-one vn-one
label="Since" label="Since"
model="$ctrl.creditClassification.started" model="$ctrl.creditClassification.started"
ini-options="{dateFormat: 'd-m-Y'}"> ini-options="{enableTime: true, dateFormat: 'd-m-Y', time_24hr: true}">
</vn-date-picker> </vn-date-picker>
</vn-horizontal> </vn-horizontal>
</vn-card> </vn-card>

View File

@ -14,15 +14,17 @@ class Controller {
submit() { submit() {
if (this.$scope.form.$invalid) if (this.$scope.form.$invalid)
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid')); return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
let query = `/client/api/creditClassifications/createWithInsurance`; let query = `/client/api/creditClassifications/createWithInsurance`;
let data = this.creditClassification; let data = this.creditClassification;
data.clientFk = this.client.id; data.clientFk = this.client.id;
this.$http.post(query, data).then((res, err) => { this.$http.post(query, data).then(res => {
if (res.data) if (res.data) {
this.card.reload();
this.$state.go('client.card.creditInsurance.index'); this.$state.go('client.card.creditInsurance.index');
}
}); });
} }
} }
@ -32,6 +34,9 @@ Controller.$inject = ['$http', '$filter', '$state', '$scope', '$translate', 'vnA
ngModule.component('vnClientCreditInsuranceCreate', { ngModule.component('vnClientCreditInsuranceCreate', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
require: {
card: '^vnClientCard'
},
bindings: { bindings: {
client: '<' client: '<'
} }

View File

@ -20,7 +20,7 @@ describe('Client', () => {
describe('_getClassifications()', () => { describe('_getClassifications()', () => {
it('should perform a GET query to define the classifications property in the controller', () => { it('should perform a GET query to define the classifications property in the controller', () => {
let res = ['some classifications']; let res = ['some classifications'];
let query = '/client/api/CreditClassifications?filter=%7B%22order%22%3A%22finished%20ASC%2C%20started%20DESC%22%2C%22include%22%3A%5B%7B%22relation%22%3A%22creditInsurances%22%2C%22scope%22%3A%7B%22fields%22%3A%5B%22id%22%2C%22credit%22%2C%22created%22%2C%22grade%22%5D%2C%22order%22%3A%22created%20DESC%22%2C%22limit%22%3A2%7D%7D%5D%2C%22where%22%3A%7B%7D%7D'; let query = '/client/api/CreditClassifications?filter=%7B%22order%22%3A%22finished%20ASC%2C%20started%20DESC%22%2C%22include%22%3A%5B%7B%22relation%22%3A%22insurances%22%2C%22scope%22%3A%7B%22fields%22%3A%5B%22id%22%2C%22credit%22%2C%22created%22%2C%22grade%22%5D%2C%22order%22%3A%22created%20DESC%22%2C%22limit%22%3A2%7D%7D%5D%2C%22where%22%3A%7B%7D%7D';
$httpBackend.whenGET(query).respond(res); $httpBackend.whenGET(query).respond(res);
$httpBackend.expectGET(query); $httpBackend.expectGET(query);

View File

@ -15,7 +15,7 @@
<div><vn-label translate>To</vn-label> {{classification.finished | date:'dd/MM/yyyy'}}</div> <div><vn-label translate>To</vn-label> {{classification.finished | date:'dd/MM/yyyy'}}</div>
</vn-one> </vn-one>
<vn-vertical vn-one pad-medium-h> <vn-vertical vn-one pad-medium-h>
<vn-horizontal ng-repeat="insurance in classification.creditInsurances track by insurance.id"> <vn-horizontal ng-repeat="insurance in classification.insurances track by insurance.id">
<vn-one> <vn-one>
<vn-label-value label="Credit" <vn-label-value label="Credit"
value="{{::insurance.credit}}"> value="{{::insurance.credit}}">

View File

@ -17,7 +17,7 @@ class Controller {
order: 'finished ASC, started DESC', order: 'finished ASC, started DESC',
include: [ include: [
{ {
relation: 'creditInsurances', relation: 'insurances',
scope: { scope: {
fields: ['id', 'credit', 'created', 'grade'], fields: ['id', 'credit', 'created', 'grade'],
order: 'created DESC', order: 'created DESC',

View File

@ -3,5 +3,4 @@ New contract: Nuevo contrato
Close contract: Cerrar contrato Close contract: Cerrar contrato
Edit contract: Modificar contrato Edit contract: Modificar contrato
View credits: Ver créditos View credits: Ver créditos
Are you sure you want to close this contract?: ¿Seguro que quieres cerrar este contrato? Are you sure you want to close this contract?: ¿Seguro que quieres cerrar este contrato?
Grade: Grado

View File

@ -1,12 +1,11 @@
<mg-ajax path="/client/api/CreditClassifications/{{post.params.classificationId}}/creditInsurances" options="vnPost"></mg-ajax> <mg-ajax path="/client/api/CreditClassifications/{{post.params.classificationId}}/insurances" options="vnPost"></mg-ajax>
<vn-watcher <vn-watcher
vn-id="watcher" vn-id="watcher"
data="$ctrl.insurance" data="$ctrl.insurance"
form="form" form="form"
save="post"> save="post">
</vn-watcher> </vn-watcher>
<form name="form" <form name="form" ng-submit="$ctrl.onSubmit()">
ng-submit="watcher.submitGo('client.card.creditInsurance.insurance.index', {classificationId: post.params.classificationId})">
<vn-card pad-large> <vn-card pad-large>
<vn-title>New credit</vn-title> <vn-title>New credit</vn-title>
<vn-horizontal> <vn-horizontal>
@ -22,7 +21,7 @@
<vn-date-picker vn-one <vn-date-picker vn-one
label="Date" label="Date"
model="$ctrl.insurance.created" model="$ctrl.insurance.created"
ini-options="{enableTime: true, dateFormat: 'd-m-Y h:i', time_24hr: true}"> ini-options="{enableTime: true, dateFormat: 'd-m-Y', time_24hr: true}">
</vn-date-picker> </vn-date-picker>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>

View File

@ -1,18 +1,32 @@
import ngModule from '../../../module'; import ngModule from '../../../module';
class Controller { class Controller {
constructor($filter) { constructor($scope, $state, $filter) {
this.$scope = $scope;
this.$state = $state;
this.insurance = { this.insurance = {
created: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm') created: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm:ss')
}; };
} }
onSubmit() {
let params = {classificationId: this.$state.params.classificationId};
let state = 'client.card.creditInsurance.insurance.index';
this.$scope.watcher.submitGo(state, params).then(() => {
this.card.reload();
});
}
} }
Controller.$inject = ['$filter']; Controller.$inject = ['$scope', '$state', '$filter'];
ngModule.component('vnClientCreditInsuranceInsuranceCreate', { ngModule.component('vnClientCreditInsuranceInsuranceCreate', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
require: {
card: '^vnClientCard'
},
bindings: { bindings: {
client: '<' client: '<'
} }

View File

@ -12,10 +12,10 @@ describe('Client', () => {
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => { beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
$componentController = _$componentController_; $componentController = _$componentController_;
let $state = {params: {classificationId: 1}}; let $stateParams = {classificationId: 1};
$httpBackend = _$httpBackend_; $httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({}); $httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
controller = $componentController('vnClientCreditInsuranceInsuranceIndex', {$state: $state}); controller = $componentController('vnClientCreditInsuranceInsuranceIndex', {$stateParams: $stateParams});
})); }));
it('should perform a query to GET credit the credit classification', () => { it('should perform a query to GET credit the credit classification', () => {

View File

@ -1,28 +1,39 @@
<mg-ajax path="/client/api/CreditClassifications/{{index.params.classificationId}}/creditInsurances" options="vnIndex"></mg-ajax> <vn-crud-model
vn-id="model"
url="/client/api/CreditInsurances"
link="{creditClassification: $ctrl.$stateParams.classificationId}"
limit="20"
data="insurances">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
<vn-title>Requested credits</vn-title> <vn-title>Requested credits</vn-title>
<table class="vn-grid"> <vn-table model="model">
<thead> <vn-thead>
<tr> <vn-tr>
<th number translate>Credit</th> <vn-th number>Credit</vn-th>
<th number translate>Grade</th> <vn-th number>Grade</vn-th>
<th translate>Created</th> <vn-th>Created</vn-th>
</tr> </vn-tr>
</thead> </vn-thead>
<tbody> <vn-tbody>
<tr ng-repeat="insurance in index.model track by insurance.id" class="list list-element"> <vn-tr ng-repeat="insurance in insurances">
<td number>{{::insurance.credit | currency: '€': 2}}</td> <vn-td number>{{::insurance.credit | currency: ' €': 2}}</vn-td>
<td number>{{::insurance.grade}}</td> <vn-td number>{{::insurance.grade}}</vn-td>
<td>{{::insurance.created | date: 'dd/MM/yyyy'}}</td> <vn-td>{{::insurance.created | date: 'dd/MM/yyyy'}}</vn-td>
</tr> </vn-tr>
<tr ng-if="index.model.count === 0" class="list list-element"> </vn-tbody>
<td colspan="6" style="text-align: center" translate>No results</td> <vn-empty-rows ng-if="model.data.length === 0" translate>
</tr> No results
</tbody> </vn-empty-rows>
</table> </vn-table>
</vn-vertical> </vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-button-bar> <vn-button-bar>
<button <button
@ -30,9 +41,8 @@
translate translate
ui-sref="client.card.creditInsurance.index">Back</button> ui-sref="client.card.creditInsurance.index">Back</button>
</vn-button-bar> </vn-button-bar>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
</vn-vertical> </vn-vertical>
<a ui-sref="client.card.creditInsurance.insurance.create({classificationId: {{index.params.classificationId}}})" <a ui-sref="client.card.creditInsurance.insurance.create({classificationId: {{$ctrl.$stateParams.classificationId}}})"
fixed-bottom-right vn-tooltip="New credit" vn-bind="+" ng-if="!$ctrl.isClosed"> fixed-bottom-right vn-tooltip="New credit" vn-bind="+" ng-if="!$ctrl.isClosed">
<vn-float-button icon="add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>
</a> </a>

View File

@ -1,19 +1,21 @@
import ngModule from '../../../module'; import ngModule from '../../../module';
import FilterList from 'core/src/lib/filter-list';
class Controller extends FilterList { class Controller {
constructor($scope, $timeout, $state, $http) { constructor($stateParams, $http) {
super($scope, $timeout, $state); this.$stateParams = $stateParams;
this.modelName = 'creditClassificationFk';
this.modelId = $state.params.classificationId;
this.isClosed = true;
this.$http = $http; this.$http = $http;
this.isClosed = true;
this.filter = {
include: [
{relation: 'classification'}
]
};
} }
$onInit() { $onInit() {
let filter = { let filter = {
fields: ['finished'], fields: ['finished'],
where: {id: this.modelId} where: {id: this.$stateParams.classificationId}
}; };
filter = encodeURIComponent(JSON.stringify(filter)); filter = encodeURIComponent(JSON.stringify(filter));
@ -25,7 +27,7 @@ class Controller extends FilterList {
} }
} }
Controller.$inject = ['$scope', '$timeout', '$state', '$http']; Controller.$inject = ['$stateParams', '$http'];
ngModule.component('vnClientCreditInsuranceInsuranceIndex', { ngModule.component('vnClientCreditInsuranceInsuranceIndex', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -1,29 +1,41 @@
<mg-ajax path="/client/api/ClientCredits/filter" options="vnIndexNonAuto"></mg-ajax> <vn-crud-model
vn-id="model"
url="/client/api/ClientCredits"
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="credits">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
<vn-title>Credit</vn-title> <vn-title>Credit</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)"> <vn-table model="model">
<vn-column-header vn-one pad-medium-h field="amount" text="Credit"></vn-column-header> <vn-thead>
<vn-column-header vn-two pad-medium-h field="created" text="Since" default-order="ASC"></vn-column-header> <vn-tr>
<vn-column-header vn-two pad-medium-h field="worker.firstName" text="Employee" order-locked></vn-column-header> <vn-th field="amount">Credit</vn-th>
</vn-grid-header> <vn-th field="created" default-order="DESC">Since</vn-th>
<vn-one class="list list-content"> <vn-th>Employee</vn-th>
<vn-horizontal </vn-tr>
vn-one class="list list-element text-center" </vn-thead>
pad-small-bottom <vn-tbody>
ng-repeat="credit in index.model.instances track by credit.id"> <vn-tr ng-repeat="credit in credits track by credit.id">
<vn-one pad-medium-h>{{::credit.amount | number:2}} €</vn-one> <vn-td>{{::credit.amount | number:2}} €</vn-td>
<vn-two pad-medium-h>{{::credit.created | date:'dd/MM/yyyy HH:mm'}}</vn-two> <vn-td>{{::credit.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-two pad-medium-h>{{::credit.worker.firstName}} {{::credit.worker.name}}</vn-two> <vn-td>{{::credit.worker.firstName}} {{::credit.worker.name}}</vn-td>
</vn-horizontal> </vn-tr>
</vn-one> </vn-tbody>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one> <vn-empty-rows ng-if="model.data.length === 0" translate>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal> No results
</vn-empty-rows>
</vn-table>
</vn-vertical> </vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging vn-one margin-large-top index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
</vn-vertical> </vn-vertical>
<a ui-sref="client.card.credit.create" vn-bind="+" fixed-bottom-right> <a ui-sref="client.card.credit.create" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>

View File

@ -1,7 +1,24 @@
import ngModule from '../../module'; import ngModule from '../../module';
import FilterClientList from '../../filter-client-list';
class Controller {
constructor($stateParams) {
this.$stateParams = $stateParams;
this.filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['firstName', 'name']
}
}
]
};
}
}
Controller.$inject = ['$stateParams'];
ngModule.component('vnClientCreditIndex', { ngModule.component('vnClientCreditIndex', {
template: require('./index.html'), template: require('./index.html'),
controller: FilterClientList controller: Controller
}); });

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax> <mg-ajax path="/client/api/Clients/{{patch.params.id}}/updateFiscalData/" options="vnPatch"></mg-ajax>
<vn-watcher <vn-watcher
vn-id="watcher" vn-id="watcher"
data="$ctrl.client" data="$ctrl.client"

View File

@ -1,37 +1,58 @@
<mg-ajax path="/client/api/greuges/filter" options="vnIndexNonAuto"></mg-ajax> <vn-crud-model
<mg-ajax path="/client/api/greuges/{{edit.params.id}}/sumAmount" options="mgEdit"></mg-ajax> vn-id="model"
url="/client/api/greuges"
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="greuges">
</vn-crud-model>
<mg-ajax
path="/client/api/greuges/{{$ctrl.$stateParams.id}}/sumAmount"
options="mgEdit">
</mg-ajax>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
<vn-title>Greuge</vn-title> <vn-title>Greuge</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)"> <vn-table model="model">
<vn-column-header vn-one pad-medium-h field="shipped" text="Date" default-order="ASC"></vn-column-header> <vn-thead>
<vn-column-header vn-two pad-medium-h field="description" text="Comment"></vn-column-header> <vn-tr>
<vn-column-header vn-one pad-medium-h field="amount" text="Amount"></vn-column-header> <vn-th field="shipped" default-order="DESC">Date</vn-th>
<vn-column-header vn-one pad-medium-h field="greugeTypeFk" text="Type"></vn-column-header> <vn-th field="description">Comment</vn-th>
</vn-grid-header> <vn-th field="amount">Amount</vn-th>
<vn-one class="list list-content"> <vn-th field="greugeTypeFk">Type</vn-th>
<vn-horizontal </vn-tr>
class="list list-element text-center" </vn-thead>
pad-small-bottom <vn-tbody>
ng-repeat="greuge in index.model.instances track by greuge.id"> <vn-tr ng-repeat="greuge in greuges">
<vn-one pad-medium-h>{{::greuge.shipped | date:'dd/MM/yyyy HH:mm' }}</vn-one> <vn-td>{{::greuge.shipped | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<vn-two pad-medium-h>{{::greuge.description}}</vn-two> <vn-td>{{::greuge.description}}</vn-td>
<vn-one pad-medium-h>{{::greuge.amount | number:2}} €</vn-one> <vn-td>{{::greuge.amount | currency: ' €': 2}}</vn-td>
<vn-one pad-medium-h>{{::greuge.greugeType.name}}</vn-one> <vn-td>{{::greuge.greugeType.name}}</vn-td>
</vn-horizontal> </vn-tr>
</vn-one> </vn-tbody>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one> <vn-empty-rows ng-if="model.data.length === 0" translate>
<vn-horizontal vn-one class="list list-footer text-center"> No results
<vn-one pad-medium-h></vn-one> </vn-empty-rows>
<vn-two pad-medium-h></vn-two> <vn-tfoot ng-if="model.data.length > 0">
<vn-one pad-medium-h ng-if="index.model.count > 0">{{edit.model.sumAmount | number:2}} €</vn-one> <vn-tr>
<vn-one pad-medium-h></vn-one> <vn-td></vn-td>
</vn-horizontal> <vn-td></vn-td>
<vn-td>
<strong>{{edit.model.sumAmount | currency: ' €': 2}}</strong>
</vn-td>
<vn-td></vn-td>
</vn-tr>
</vn-tfoot>
</vn-table>
</vn-vertical> </vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-paging margin-large-top vn-one index="index" total="index.model.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>
<a ui-sref="client.card.greuge.create" vn-bind="+" fixed-bottom-right> <a ui-sref="client.card.greuge.create" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>

View File

@ -1,7 +1,24 @@
import ngModule from '../../module'; import ngModule from '../../module';
import FilterClientList from '../../filter-client-list';
class Controller {
constructor($stateParams) {
this.$stateParams = $stateParams;
this.filter = {
include: [
{
relation: "greugeType",
scope: {
fields: ["id", "name"]
}
}
]
};
}
}
Controller.$inject = ['$stateParams'];
ngModule.component('vnClientGreugeIndex', { ngModule.component('vnClientGreugeIndex', {
template: require('./index.html'), template: require('./index.html'),
controller: FilterClientList controller: Controller
}); });

View File

@ -10,12 +10,9 @@ export default class Controller {
exprBuilder(param, value) { exprBuilder(param, value) {
switch (param) { switch (param) {
case 'search': case 'search':
return { return /^\d+$/.test(value)
or: [ ? {id: value}
{id: value}, : {name: {regexp: value}};
{name: {regexp: value}}
]
};
case 'phone': case 'phone':
return { return {
or: [ or: [

View File

@ -1,35 +1,42 @@
<mg-ajax path="/client/api/InvoiceOuts/filter" options="vnIndexNonAuto"></mg-ajax> <vn-crud-model
vn-id="model"
url="/client/api/InvoiceOuts"
filter="{}"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="invoices">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
<vn-title>Invoices</vn-title> <vn-title>Invoices</vn-title>
<vn-vertical style="text-align: center;"> <vn-table model="model">
<vn-grid-header on-order="$ctrl.onOrder(field, order)"> <vn-thead>
<vn-column-header vn-one field="ref" text="Reference" default-order="ASC"></vn-column-header> <vn-tr>
<vn-column-header vn-one field="issued" text="Issue date"></vn-column-header> <vn-th field="ref" default-order="DESC">Reference</vn-th>
<vn-column-header vn-one field="dued" text="Due date"></vn-column-header> <vn-th field="issued">Issue date</vn-th>
<vn-column-header vn-one field="amount" text="Amount"></vn-column-header> <vn-th field="dued">Due date</vn-th>
</vn-grid-header> <vn-th field="amount">Amount</vn-th>
<vn-vertical>
<vn-one </vn-tr>
ng-if="index.model.count > 0" </vn-thead>
class="list list-content"> <vn-tbody>
<vn-horizontal <vn-tr ng-repeat="invoice in invoices">
class="list list-element" <vn-td>{{::invoice.ref}}</vn-td>
pad-small-bottom <vn-td>{{::invoice.issued | date:'dd/MM/yyyy' }}</vn-td>
ng-repeat="invoice in index.model.instances track by invoice.id"> <vn-td>{{::invoice.dued | date:'dd/MM/yyyy' }}</vn-td>
<vn-one>{{::invoice.ref}}</vn-one> <vn-td>{{::invoice.amount | currency:' €': 2}}</vn-td>
<vn-one>{{::invoice.issued | date:'dd/MM/yyyy' }}</vn-one> </vn-tr>
<vn-one>{{::invoice.dued | date:'dd/MM/yyyy' }}</vn-one> </vn-tbody>
<vn-one>{{::invoice.amount | currency:'€':2}}</vn-one> <vn-empty-rows ng-if="model.data.length === 0" translate>
</vn-horizontal> No results
</vn-one> </vn-empty-rows>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one> </vn-table>
</vn-vertical>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
</vn-vertical>
</vn-vertical> </vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-paging margin-large-top vn-one index="index" total="index.model.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

@ -1,13 +1,12 @@
import ngModule from '../module'; import ngModule from '../module';
import FilterClientList from '../filter-client-list';
class Controller extends FilterClientList { class Controller {
constructor($scope, $timeout, $state, $stateParams) { constructor($stateParams) {
super($scope, $timeout, $state); this.$stateParams = $stateParams;
$scope.$stateParams = $stateParams;
} }
} }
Controller.$inject = ['$scope', '$timeout', '$state', '$stateParams'];
Controller.$inject = ['$stateParams'];
ngModule.component('vnClientInvoice', { ngModule.component('vnClientInvoice', {
template: require('./index.html'), template: require('./index.html'),

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

@ -1,31 +1,43 @@
<mg-ajax path="/client/api/Mandates/filter" options="vnIndexNonAuto"></mg-ajax> <vn-crud-model
vn-id="model"
url="/client/api/Mandates"
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="mandates">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
<vn-title>Mandate</vn-title> <vn-title>Mandate</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)"> <vn-table model="model">
<vn-column-header vn-one pad-medium-h field="id" text="Id"></vn-column-header> <vn-thead>
<vn-column-header vn-one pad-medium-h field="companyFk" text="Company"></vn-column-header> <vn-tr>
<vn-column-header vn-one pad-medium-h field="mandateTypeFk" text="Type"></vn-column-header> <vn-th field="id">Id</vn-th>
<vn-column-header vn-one pad-medium-h field="created" text="Register date" default-order="ASC"></vn-column-header> <vn-th field="companyFk">Company</vn-th>
<vn-column-header vn-one pad-medium-h field="finished" text="End date"></vn-column-header> <vn-th field="mandateTypeFk">Type</vn-th>
</vn-grid-header> <vn-th field="created" default-order="DESC">Register date</vn-th>
<vn-one class="list list-content"> <vn-th field="finished">End date</vn-th>
<vn-horizontal </vn-tr>
vn-one class="list list-element text-center" </vn-thead>
pad-small-bottom <vn-tbody>
ng-repeat="mandate in index.model.instances track by mandate.id"> <vn-tr ng-repeat="mandate in mandates">
<vn-one pad-medium-h>{{::mandate.id}}</vn-one> <vn-td>{{::mandate.id}}</vn-td>
<vn-one pad-medium-h>{{::mandate.company.code}}</vn-one> <vn-td>{{::mandate.company.code}}</vn-td>
<vn-one pad-medium-h>{{::mandate.mandateType.name}}</vn-one> <vn-td>{{::mandate.mandateType.name}}</vn-td>
<vn-one pad-medium-h>{{::mandate.created | date:'dd/MM/yyyy HH:mm' }}</vn-one> <vn-td>{{::mandate.created | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<vn-one pad-medium-h>{{::mandate.finished | date:'dd/MM/yyyy HH:mm' || '-'}}</vn-one> <vn-td>{{::mandate.finished | date:'dd/MM/yyyy HH:mm' || '-'}}</vn-td>
</vn-horizontal> </vn-tr>
</vn-one> </vn-tbody>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one> <vn-empty-rows ng-if="model.data.length === 0" translate>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal> No results
</vn-empty-rows>
</vn-table>
</vn-vertical> </vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging vn-one margin-large-top index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
</vn-vertical> </vn-vertical>

View File

@ -1,7 +1,30 @@
import ngModule from '../module'; import ngModule from '../module';
import FilterClientList from '../filter-client-list';
class Controller {
constructor($stateParams) {
this.$stateParams = $stateParams;
this.filter = {
include: [
{
relation: "mandateType",
scope: {
fields: ["id", "name"]
}
},
{
relation: "company",
scope: {
fields: ["id", "code"]
}
}
]
};
}
}
Controller.$inject = ['$stateParams'];
ngModule.component('vnClientMandate', { ngModule.component('vnClientMandate', {
template: require('./index.html'), template: require('./index.html'),
controller: FilterClientList controller: Controller
}); });

View File

@ -1,25 +1,31 @@
<vn-crud-model
vn-id="model"
url="/client/api/clientObservations"
filter="{order: 'created DESC'}"
link="{clientFk: $ctrl.$stateParams.id}"
data="notes">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card ng-show="$ctrl.observations.length" pad-large> <vn-card pad-large>
<vn-title>Notes</vn-title> <vn-title>Notes</vn-title>
<vn-vertical <vn-vertical
ng-repeat="n in $ctrl.observations" ng-repeat="note in notes"
pad-small pad-small
border-solid border-solid
border-radius border-radius
margin-small-bottom> margin-small-bottom>
<vn-horizontal margin-small-bottom style="color: #666"> <vn-horizontal margin-small-bottom style="color: #666">
<vn-one>{{::n.worker.firstName}} {{::n.worker.name}}</vn-one> <vn-one>{{::note.worker.firstName}} {{::note.worker.name}}</vn-one>
<vn-auto>{{::n.created | date:'dd/MM/yyyy HH:mm'}}</vn-auto> <vn-auto>{{::note.created | date:'dd/MM/yyyy HH:mm'}}</vn-auto>
</vn-horizontal> </vn-horizontal>
<vn-horizontal class="text"> <vn-horizontal class="text">
{{::n.text}} {{::note.text}}
</vn-horizontal> </vn-horizontal>
</vn-vertical> </vn-vertical>
</vn-card> </vn-card>
</vn-vertical> </vn-vertical>
<vn-float-button
fixed-bottom-right <a ui-sref="client.card.note.create({id: $ctrl.$stateParams.id})" vn-bind="+" fixed-bottom-right>
ng-click="$ctrl.newObservation()" <vn-float-button icon="add"></vn-float-button>
vn-bind="+" </a>
icon="add">
</vn-float-button>

View File

@ -1,31 +1,12 @@
import ngModule from '../../module'; import ngModule from '../../module';
export default class Controller { export default class Controller {
constructor($http, $state) { constructor($stateParams) {
this.$http = $http; this.$stateParams = $stateParams;
this.$state = $state;
}
$onChanges() {
if (this.client) {
this.getObservation(this.client.id);
}
}
getObservation(clientId) {
let json = JSON.stringify({where: {clientFk: this.client.id}, order: 'created DESC'});
this.$http.get(`/client/api/clientObservations?filter=${json}`).then(
json => {
this.observations = json.data;
}
);
}
newObservation() {
this.$state.go("client.card.note.create", {id: this.client.id});
} }
} }
Controller.$inject = ['$http', '$state'];
Controller.$inject = ['$stateParams'];
ngModule.component('vnClientNote', { ngModule.component('vnClientNote', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -1,58 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientNote', () => {
let $componentController;
let $state;
let $httpBackend;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
$componentController = _$componentController_;
$state = _$state_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
controller = $componentController('vnClientNote', {$state: $state});
}));
describe('$onChanges()', () => {
it(`should call getObservation() with the client id`, () => {
controller.client = {
id: 1234
};
spyOn(controller, 'getObservation').and.returnValue();
controller.$onChanges();
expect(controller.getObservation).toHaveBeenCalledWith(1234);
});
});
describe('$getObservation()', () => {
it(`should request to GET the client notes`, () => {
controller.client = {id: '1234'};
let jsonString = JSON.stringify({where: {clientFk: '1234'}, order: 'created DESC'});
let json = {data: 'some data'};
$httpBackend.when('GET', `/client/api/clientObservations?filter=${jsonString}`).respond(json);
$httpBackend.expectGET(`/client/api/clientObservations?filter=${jsonString}`, {Accept: 'application/json, text/plain, */*'});
controller.getObservation();
$httpBackend.flush();
expect(controller.observations).toEqual(json);
});
});
describe('$newObservation()', () => {
it(`should redirect the user to the newObservation view`, () => {
controller.client = {id: '1234'};
spyOn(controller.$state, 'go');
controller.newObservation();
expect(controller.$state.go).toHaveBeenCalledWith('client.card.note.create', Object({id: '1234'}));
});
});
});
});

View File

@ -1,37 +1,53 @@
<mg-ajax path="/client/api/Recoveries/filter" options="vnIndexNonAuto"></mg-ajax> <vn-crud-model
vn-id="model"
url="/client/api/Recoveries"
filter="{}"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="recoveries">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
<vn-title>Recovery</vn-title> <vn-title>Recovery</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)"> <vn-table model="model">
<vn-column-header vn-one pad-medium-h field="started" text="Since" default-order="ASC"></vn-column-header> <vn-thead>
<vn-column-header vn-one pad-medium-h field="finished" text="To"></vn-column-header> <vn-tr>
<vn-column-header vn-one pad-medium-h field="amount" text="Amount"></vn-column-header> <vn-th></vn-th>
<vn-column-header vn-one pad-medium-h field="period" text="Period"></vn-column-header> <vn-th field="started" default-order="DESC">Since</vn-th>
</vn-grid-header> <vn-th field="finished">To</vn-th>
<vn-one class="list list-content"> <vn-th field="amount">Amount</vn-th>
<vn-horizontal <vn-th field="period">Period</vn-th>
vn-one class="list list-element text-center" </vn-tr>
pad-small-bottom </vn-thead>
ng-repeat="recovery in index.model.instances track by $index"> <vn-tbody>
<vn-none pad-medium-h orange> <vn-tr ng-repeat="recovery in recoveries">
<i class="material-icons pointer" <vn-td>
vn-tooltip="Finish that recovery period" <vn-icon
ng-if="!recovery.finished" class="bright pointer"
ng-click="$ctrl.setFinished(recovery)">lock</i> icon="lock"
</vn-none> vn-tooltip="Finish that recovery period"
<vn-one pad-medium-h>{{::recovery.started | date:'dd/MM/yyyy' }}</vn-one> ng-if="!recovery.finished"
<vn-one pad-medium-h>{{recovery.finished | date:'dd/MM/yyyy' }}</vn-one> ng-click="$ctrl.setFinished(recovery)">
<vn-one pad-medium-h>{{::recovery.amount | currency:'€':0}}</vn-one> </vn-icon>
<vn-one pad-medium-h>{{::recovery.period}}</vn-one> </vn-td>
</vn-horizontal> <vn-td>{{::recovery.started | date:'dd/MM/yyyy' }}</vn-td>
</vn-one> <vn-td>{{recovery.finished | date:'dd/MM/yyyy' }}</vn-td>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one> <vn-td>{{::recovery.amount | currency:' €': 0}}</vn-td>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal> <vn-td>{{::recovery.period}}</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-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging vn-one margin-large-top index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
</vn-vertical> </vn-vertical>
<a ui-sref="client.card.recovery.create" vn-bind="+" fixed-bottom-right> <a ui-sref="client.card.recovery.create" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>

View File

@ -1,22 +1,23 @@
import ngModule from '../../module'; import ngModule from '../../module';
import FilterClientList from '../../filter-client-list';
class Controller extends FilterClientList { class Controller {
constructor($scope, $timeout, $state, $http) { constructor($stateParams, $scope, $http) {
super($scope, $timeout, $state); this.$stateParams = $stateParams;
this.$scope = $scope;
this.$http = $http; this.$http = $http;
} }
setFinished(recovery) { setFinished(recovery) {
if (!recovery.finished) { if (!recovery.finished) {
let params = {finished: Date.now()}; let params = {finished: Date.now()};
this.$http.patch(`/client/api/Recoveries/${recovery.id}`, params).then( this.$http.patch(`/client/api/Recoveries/${recovery.id}`, params).then(
() => this.$.index.accept() () => this.$scope.model.refresh()
); );
} }
} }
} }
Controller.$inject = ['$scope', '$timeout', '$state', '$http']; Controller.$inject = ['$stateParams', '$scope', '$http'];
ngModule.component('vnClientRecoveryIndex', { ngModule.component('vnClientRecoveryIndex', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -118,8 +118,8 @@
</vn-label-value> </vn-label-value>
<p> <p>
<vn-check <vn-check
label="Received core VNH" label="Received LCR"
field="$ctrl.summary.hasCoreVnh" field="$ctrl.summary.hasLcr"
disabled="true"> disabled="true">
</vn-check> </vn-check>
</p> </p>
@ -199,9 +199,16 @@
<vn-label-value label="Credit" <vn-label-value label="Credit"
value="{{$ctrl.summary.credit | currency:'€ ':2}}"> value="{{$ctrl.summary.credit | currency:'€ ':2}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Secured credit" <vn-horizontal>
value="{{$ctrl.summary.creditInsurance | currency:'€ ':2}}"> <vn-one>
</vn-label-value> <vn-label-value label="Secured credit"
value="{{$ctrl.summary.creditInsurance | currency:'€ ':2}}">
</vn-label-value>
</vn-one>
<vn-one title="Grade">
{{$ctrl.grade ? '&nbsp;/ ' + $ctrl.grade : '&nbsp;/ - '}}
</vn-one>
</vn-horizontal>
</vn-one> </vn-one>
</vn-horizontal> </vn-horizontal>
</vn-vertical> </vn-vertical>

View File

@ -11,8 +11,12 @@ class Controller {
return; return;
this.$http.get(`/client/api/Clients/${this.client.id}/summary`).then(res => { this.$http.get(`/client/api/Clients/${this.client.id}/summary`).then(res => {
if (res && res.data) if (res && res.data) {
this.summary = res.data; this.summary = res.data;
if (res.data.classifications.length)
this.grade = res.data.classifications[0].insurances[0].grade;
}
}); });
} }
} }

View File

@ -1,7 +0,0 @@
<vn-horizontal margin-medium-top>
<vn-one></vn-one>
<vn-auto>
<vn-spinner enable="$ctrl.watchScroll"></vn-spinner>
</vn-auto>
<vn-one></vn-one>
</vn-horizontal>

View File

@ -1,123 +0,0 @@
import ngModule from '../../module';
import getWatchers from '../../lib/get-watchers';
class AutoPaging {
constructor($http, $window, $element, $timeout, vnApp, $translate, $scope) {
this.$http = $http;
this.$window = $window;
this.$element = $element;
this.$timeout = $timeout;
this.vnApp = vnApp;
this.$translate = $translate;
this.$scope = $scope;
this.numPerPage = null;
this.maxItems = 0;
this.watchScroll = false;
this.waitingNewPage = false;
this.handlerScroll = this.onScroll.bind(this);
}
get numPages() {
return this.numPerPage ? Math.ceil(this.maxItems / this.numPerPage) : 0;
}
loadNewPage() {
this.index.filter.page++;
this.waitingNewPage = true;
this.index.accept().then(res => {
this.$timeout(() => {
res.instances.forEach(item => {
this.items.push(item);
});
this.$scope.$apply();
this.checkWatchers();
});
if (this.index.filter.page == this.numPages) {
this.cancelScroll();
}
this.waitingNewPage = false;
});
}
checkPosition() {
let element = this.$element[0].querySelector('vn-spinner');
let position = element.getBoundingClientRect();
let isVisible = position.y < document.body.offsetHeight + 150;
if (this.currentPage < this.numPages && isVisible && !this.waitingNewPage) {
this.loadNewPage();
}
}
onScroll() {
this.checkPosition();
}
startScroll() {
this.watchScroll = true;
this.checkPosition();
let mainView = this.$window.document.querySelector('.main-view > ui-view.ng-scope');
angular.element(mainView).bind("scroll", this.handlerScroll);
}
cancelScroll() {
this.watchScroll = false;
let mainView = this.$window.document.querySelector('.main-view > ui-view.ng-scope');
angular.element(mainView).unbind("scroll", this.handlerScroll);
}
checkScroll() {
if (this.numPages > this.currentPage && !this.watchScroll) {
this.startScroll();
} else if (this.numPages <= this.currentPage && this.watchScroll) {
this.cancelScroll();
}
}
checkWatchers() {
let watchers = getWatchers();
if (watchers > 3000 && this.watchScroll) {
this.cancelScroll();
this.vnApp.showMessage(
this.$translate.instant('Auto-scroll interrupted, please adjust the search')
);
}
}
$onChanges(changes) {
if (!this.index) return;
this.numPerPage = this.index.filter.size;
this.currentPage = this.index.filter.page;
this.currentInstances = this.items;
if (changes.total)
this.maxItems = changes.total.currentValue;
this.checkScroll();
}
$postLink() {
this.checkScroll();
}
$doCheck() {
if (this.index && this.index.filter && this.index.filter.page && this.index.filter.page != this.currentPage) {
this.currentPage = this.index.filter.page;
this.checkScroll();
}
}
}
AutoPaging.$inject = ['$http', '$window', '$element', '$timeout', 'vnApp', '$translate', '$scope'];
ngModule.component('vnAutoPaging', {
template: require('./auto-paging.html'),
bindings: {
index: '<',
total: '<',
items: '<'
},
controller: AutoPaging
});

View File

@ -1,61 +0,0 @@
import './auto-paging.js';
import template from './auto-paging.html';
describe('Component vnAutoPaging', () => {
let $http;
let $window;
let $element;
let $timeout;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_, _$window_, _$timeout_) => {
$http = _$httpBackend_;
$window = _$window_;
$timeout = _$timeout_;
$element = angular.element(`<div>${template}</div>`);
controller = _$componentController_('vnAutoPaging', {$http, $window, $element, $timeout});
}));
describe('onChanges: actions when index object changes', () => {
beforeEach(() => {
controller.index = {
filter: {
size: 5,
page: 1
}
};
controller.items = [1, 2, 3, 4, 5];
});
it('call startScroll() if there are pages to load', () => {
let changes = {
total: {
currentValue: 20
}
};
spyOn(controller, 'startScroll');
controller.$onChanges(changes);
expect(controller.startScroll).toHaveBeenCalled();
});
it('call stopScroll() if there are not pages to load', () => {
let changes = {
total: {
currentValue: 5
}
};
controller.watchScroll = true;
spyOn(controller, 'cancelScroll');
controller.$onChanges(changes);
expect(controller.cancelScroll).toHaveBeenCalled();
});
});
});

View File

@ -17,11 +17,6 @@
</div> </div>
<label class="mdl-textfield__label" translate>{{::$ctrl.label}}</label> <label class="mdl-textfield__label" translate>{{::$ctrl.label}}</label>
</div> </div>
<i class="material-icons pointer"
ng-show="!$ctrl.disabled && $ctrl.hasValue && ($ctrl.hasFocus || $ctrl.hasMouseIn)"
ng-click="$ctrl.clear()">
clear
</i>
</div> </div>
<vn-drop-down <vn-drop-down
vn-id="drop-down" vn-id="drop-down"

View File

@ -31,6 +31,15 @@ export default class Autocomplete extends Input {
this.element.querySelector('.mdl-textfield')); this.element.querySelector('.mdl-textfield'));
} }
set url(value) {
this._url = value;
this.refreshSelection();
}
get url() {
return this._url;
}
/** /**
* @type {any} The autocomplete value. * @type {any} The autocomplete value.
*/ */

View File

@ -21,6 +21,16 @@ describe('Component vnAutocomplete', () => {
controller = _$componentController_('vnAutocomplete', {$element, $scope, $httpBackend, $transclude: null}); controller = _$componentController_('vnAutocomplete', {$element, $scope, $httpBackend, $transclude: null});
})); }));
describe('url() setter', () => {
it(`should set url controllers property and call refreshSelection`, () => {
spyOn(controller, "refreshSelection");
controller.url = "url";
expect(controller.url).toEqual("url");
expect(controller.refreshSelection).toHaveBeenCalledWith();
});
});
describe('field() setter/getter', () => { describe('field() setter/getter', () => {
it(`should set field controllers property`, () => { it(`should set field controllers property`, () => {
controller.field = data.id; controller.field = data.id;

View File

@ -5,6 +5,7 @@ vn-drop-down {
.dropdown { .dropdown {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: inherit;
& > .filter { & > .filter {
position: relative; position: relative;

View File

@ -26,6 +26,7 @@ export default class IconMenu extends Input {
onClick(event) { onClick(event) {
event.preventDefault(); event.preventDefault();
if (this.onOpen) this.onOpen();
this.showDropDown(); this.showDropDown();
} }
@ -81,7 +82,8 @@ ngModule.component('vnIconMenu', {
multiple: '<?', multiple: '<?',
onChange: '&?', onChange: '&?',
icon: '@?', icon: '@?',
translateFields: '<?' translateFields: '<?',
onOpen: '&?'
}, },
transclude: { transclude: {
tplItem: '?tplItem' tplItem: '?tplItem'

View File

@ -34,7 +34,6 @@ import './float-button/float-button';
import './step-control/step-control'; import './step-control/step-control';
import './label-value/label-value'; import './label-value/label-value';
import './paging/paging'; import './paging/paging';
import './auto-paging/auto-paging';
import './pagination/pagination'; import './pagination/pagination';
import './searchbar/searchbar'; import './searchbar/searchbar';
import './table'; import './table';

View File

@ -14,7 +14,7 @@ import './style.scss';
class Pagination extends Component { class Pagination extends Component {
constructor($element, $scope) { constructor($element, $scope) {
super($element, $scope); super($element, $scope);
this.scrollOffset = 20; this.scrollOffset = 150;
this.scrollHandler = e => this.onScroll(e); this.scrollHandler = e => this.onScroll(e);
} }

View File

@ -75,15 +75,6 @@ export default class Popover extends Component {
this.document.addEventListener('keydown', this.docKeyDownHandler); this.document.addEventListener('keydown', this.docKeyDownHandler);
this.document.addEventListener('focusin', this.docFocusInHandler); this.document.addEventListener('focusin', this.docFocusInHandler);
let firstFocusable = this.element.querySelector('input, textarea');
if (firstFocusable) {
firstFocusable.addEventListener('focus', () => {
firstFocusable.select();
});
setTimeout(() => {
firstFocusable.focus();
}, 200);
}
this.deregisterCallback = this.$transitions.onStart({}, () => this.hide()); this.deregisterCallback = this.$transitions.onStart({}, () => this.hide());
this.relocate(); this.relocate();
@ -136,10 +127,10 @@ export default class Popover extends Component {
let arrowHeight = Math.sqrt(Math.pow(arrowRect.height, 2) * 2) / 2; let arrowHeight = Math.sqrt(Math.pow(arrowRect.height, 2) * 2) / 2;
let top = parentRect.top + parentRect.height + arrowHeight;
let left = parentRect.left;
let height = popoverRect.height; let height = popoverRect.height;
let width = Math.max(popoverRect.width, parentRect.width); let width = Math.max(popoverRect.width, parentRect.width);
let top = parentRect.top + parentRect.height + arrowHeight;
let left = parentRect.left + parentRect.width / 2 - width / 2;
let margin = 10; let margin = 10;
let showTop = top + height + margin > window.innerHeight; let showTop = top + height + margin > window.innerHeight;

View File

@ -19,25 +19,24 @@
} }
& > .popover { & > .popover {
position: absolute; position: absolute;
display: flex;
box-shadow: 0 .1em .4em rgba(1, 1, 1, .4); box-shadow: 0 .1em .4em rgba(1, 1, 1, .4);
z-index: 0;
& > .arrow { & > .arrow {
width: 1em; width: 1em;
height: 1em; height: 1em;
margin: -.5em; margin: -.5em;
background-color: white; background-color: white;
box-shadow: 0 .1em .4em rgba(1, 1, 1, .4); box-shadow: 0 .1em .4em rgba(1, 1, 1, .4);
position: absolute; position: absolute;
transform: rotate(45deg); transform: rotate(45deg);
z-index: 0; z-index: -1;
} }
& > .content { & > .content {
width: 100%;
border-radius: .1em; border-radius: .1em;
overflow: auto;
background-color: white; background-color: white;
z-index: 1; height: inherit;
overflow: auto;
} }
} }
} }

View File

@ -19,7 +19,7 @@ export default class CrudModel extends ModelProxy {
this.refresh(); this.refresh();
} }
refresh(usFilter, usData) { refresh(usFilter) {
if (!this.url) return; if (!this.url) return;
let myFilter = { let myFilter = {
@ -27,8 +27,7 @@ export default class CrudModel extends ModelProxy {
where: mergeWhere(this.link, this.where), where: mergeWhere(this.link, this.where),
include: this.include, include: this.include,
order: this.order, order: this.order,
limit: this.limit, limit: this.limit
userData: this.userData
}; };
let filter = this.filter; let filter = this.filter;
@ -47,9 +46,16 @@ export default class CrudModel extends ModelProxy {
sendRequest(filter, append) { sendRequest(filter, append) {
this.cancelRequest(); this.cancelRequest();
this.canceler = this.$q.defer(); this.canceler = this.$q.defer();
let options = {timeout: this.canceler.promise};
let json = encodeURIComponent(JSON.stringify(filter)); let options = {
return this.$http.get(`${this.url}?filter=${json}`, options).then( timeout: this.canceler.promise,
params: {filter: filter}
};
if (this.userParams instanceof Object)
Object.assign(options.params, this.userParams);
return this.$http.get(this.url, options).then(
json => this.onRemoteDone(json, filter, append), json => this.onRemoteDone(json, filter, append),
json => this.onRemoteError(json) json => this.onRemoteError(json)
); );
@ -118,7 +124,7 @@ export default class CrudModel extends ModelProxy {
return changes; return changes;
} }
save(ignoreChanges) { save() {
let changes = this.getChanges(); let changes = this.getChanges();
if (!changes) if (!changes)
@ -146,7 +152,7 @@ ngModule.component('vnCrudModel', {
order: '@?', order: '@?',
limit: '<?', limit: '<?',
filter: '<?', filter: '<?',
userData: '<?', userParams: '<?',
primaryKey: '@?', primaryKey: '@?',
autoLoad: '<?' autoLoad: '<?'
} }
@ -225,8 +231,6 @@ function mergeFilters(src, dst) {
res.order = src.order; res.order = src.order;
if (src.limit) if (src.limit)
res.limit = src.limit; res.limit = src.limit;
if (src.userData)
res.userData = src.userData;
return res; return res;
} }

View File

@ -9,6 +9,15 @@ export default class RestModel {
this.clear(); this.clear();
} }
set staticData(value) {
this._staticData = value;
this.refresh();
}
get staticData() {
return this._staticData;
}
get isLoading() { get isLoading() {
return this.canceler != null; return this.canceler != null;
} }
@ -48,11 +57,11 @@ export default class RestModel {
this.clear(); this.clear();
this.sendRequest(filter); this.sendRequest(filter);
} else if (this.staticData) { } else if (this._staticData) {
if (search) if (search)
this.data = this.$filter('filter')(this.staticData, search); this.data = this.$filter('filter')(this._staticData, search);
else else
this.data = this.staticData; this.data = this._staticData;
this.dataChanged(); this.dataChanged();
} }
} }

View File

@ -105,14 +105,34 @@ export default class Controller extends Component {
} }
pushFilterToState(filter) { pushFilterToState(filter) {
let history = window.history || {pushState: () => { let state = window.location.hash.split('?')[0];
console.error('Error in history.pushState(): Browser incompatibility error'); let keys = Object.keys(filter);
}};
let aux = window.location.hash.split('?q='); if (keys.length) {
if (Object.keys(filter).length) let hashFilter = {};
history.pushState({}, null, `${aux[0]}?q=${encodeURIComponent(JSON.stringify(filter))}`);
else keys.forEach(key => {
history.pushState({}, null, aux[0]); let value = filter[key];
if (value instanceof Date)
hashFilter[key] = value;
else
switch (typeof value) {
case 'number':
case 'string':
case 'boolean':
hashFilter[key] = value;
}
});
let search = encodeURIComponent(JSON.stringify(hashFilter));
state += `?q=${search}`;
}
if (!window.history)
throw new Error('Browser incompatibility: window.history not found');
window.history.pushState({}, null, state);
} }
/** /**

View File

@ -18,15 +18,17 @@ export default class Table {
this.order = order; this.order = order;
} }
applyFilter(field = this.field, order = this.order) { applyOrder(field = this.field, order = this.order) {
this.model.filter.order = `${field} ${order}`; if (!field) return;
this.model.order = `${field} ${order}`;
this.model.refresh(); this.model.refresh();
this.setActiveArrow(); this.setActiveArrow();
} }
$onChanges() { $onChanges() {
if (this.model) if (this.model && this.model.filter)
this.applyFilter(); this.applyOrder();
} }
setActiveArrow() { setActiveArrow() {

View File

@ -13,6 +13,7 @@ vn-table {
display: table-header-group; display: table-header-group;
vn-th[field] { vn-th[field] {
position: relative;
cursor: pointer cursor: pointer
} }
@ -77,6 +78,7 @@ vn-table {
} }
vn-td, vn-th { vn-td, vn-th {
vertical-align: middle;
display: table-cell; display: table-cell;
text-align: left; text-align: left;
padding: 10px; padding: 10px;
@ -84,6 +86,10 @@ vn-table {
&[number]{ &[number]{
text-align: right; text-align: right;
} }
vn-icon.bright, i.bright {
color: #f7931e;
}
} }
} }
} }

View File

@ -1,28 +1,29 @@
@import "colors"; @import "colors";
vn-textfield { vn-textfield {
margin: 20px 0!important; margin: 20px 0!important;
display: block; display: inline-block;
.leftIcons, .rightIcons, .suffix{ & > .container {
display: inline-flex;
color: $secondary-font-color;
& .material-icons{
font-size: 20px!important
}
}
.leftIcons{
margin-right: 3px;
}
.container{
width: 100%; width: 100%;
position: relative; position: relative;
padding-bottom: 2px; padding-bottom: 2px;
display: flex;
& > .textField {
width: 100%;
display: flex;
align-items: center;
position: relative;
padding-top: 4px;
}
} }
.textField{ .leftIcons, .rightIcons, .suffix {
width: 100%; display: flex;
display: inline-flex; color: $secondary-font-color;
position: relative;
padding: 4px 0; .material-icons {
font-size: 20px !important
}
} }
.infix { .infix {
position: relative; position: relative;
@ -31,43 +32,61 @@ vn-textfield {
width: 100%; width: 100%;
min-width: 0; min-width: 0;
} }
i.pointer { i.clear {
visibility: hidden; visibility: hidden;
cursor: pointer;
&:hover {
color: #222;
}
}
&:hover i.clear {
visibility: visible;
} }
i.visible { i.visible {
visibility: visible; visibility: visible;
} }
label { label {
position: absolute; position: absolute;
bottom: 2px; bottom: 0;
padding: 4px 0!important;
pointer-events: none; pointer-events: none;
color: $secondary-font-color; color: $secondary-font-color;
transition-duration: .2s; transition-duration: .2s;
transition-timing-function: cubic-bezier(.4,0,.2,1); transition-timing-function: cubic-bezier(.4,0,.2,1);
} }
&.not-empty label{
bottom: 24px;
color: $main-01;
padding: 0;
font-size: 12px;
}
input { input {
outline: none; outline: none;
border: none; border: none;
font-family: "Helvetica","Arial",sans-serif; font-family: "Helvetica", "Arial", sans-serif;
display: block; display: block;
font-size: 16px; font-size: 16px;
width: 100%; width: 100%;
background: 0 0; background: 0 0;
color: inherit; color: inherit;
padding: 4px;
box-sizing: border-box;
border-bottom: 0!important;
&[type=number] { &[type=number] {
-moz-appearance: textfield; -moz-appearance: textfield;
&::-webkit-outer-spin-button, &::-webkit-outer-spin-button,
&::-webkit-inner-spin-button{ &::-webkit-inner-spin-button {
-webkit-appearance: none; -webkit-appearance: none;
margin: 0; margin: 0;
} }
} }
&:invalid { &:invalid {
box-shadow:none; box-shadow: none;
} }
} }
.underline{ .underline {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
height: 1px; height: 1px;
@ -76,49 +95,40 @@ vn-textfield {
width: 100%; width: 100%;
background-color: rgba(0,0,0,.12); background-color: rgba(0,0,0,.12);
} }
.selected.underline{ .selected.underline {
background-color: rgb(255,152,0); background-color: rgb(255,152,0);
height: 2px; height: 2px;
left: 50%; left: 50%;
width: 0px!important; width: 0px !important;
transition-duration: 0.2s; transition-duration: 0.2s;
transition-timing-function: cubic-bezier(.4,0,.2,1); transition-timing-function: cubic-bezier(.4,0,.2,1);
} }
&.not-empty {
& label { div.selected {
bottom: 24px;
color: $main-01;
font-size: 12px;
}
}
div.selected{
&.container{ &.container{
border-bottom: 0px; border-bottom: 0px;
} }
& label { label {
bottom: 24px; bottom: 24px;
color: $main-01; color: $main-01;
font-size: 12px; font-size: 12px;
} }
& .selected.underline{ .selected.underline{
left: 0; left: 0;
width: 100%!important; width: 100%!important;
} }
} }
& > div.container > div.textField > div.infix.invalid{ & > div.container > div.textField > div.infix.invalid {
@extend div.selected; @extend div.selected;
& > span.mdl-textfield__error{ & > span.mdl-textfield__error {
visibility: visible; visibility: visible;
margin-top: 9px;
} }
& > label{ & > label {
color: #d50000; color: #d50000;
} }
} }
.infix.invalid + .underline { .infix.invalid + .underline {
&{ background-color: #d50000;
background-color: #d50000;
}
} }
} }

View File

@ -1,36 +1,30 @@
<div class="container" <div class="container"
ng-class="{selected: $ctrl.hasFocus}" ng-class="{selected: $ctrl.hasFocus}">
ng-mouseenter="$ctrl.hasMouseIn = true"
ng-mouseleave="$ctrl.hasMouseIn = false"
style="display: inline-flex"
>
<div class="textField"> <div class="textField">
<div class="leftIcons"> <div class="leftIcons"></div>
</div>
<div class="infix"> <div class="infix">
<input <input
type="{{$ctrl.type}}" class="mdl-textfield__input"
name="{{$ctrl.name}}" type="{{$ctrl.type}}"
ng-model="$ctrl.value" name="{{$ctrl.name}}"
vn-validation="{{$ctrl.rule}}" ng-model="$ctrl.value"
ng-disabled="$ctrl.disabled" vn-validation="{{$ctrl.rule}}"
ng-readonly="$ctrl.readonly" ng-disabled="$ctrl.disabled"
ng-focus="$ctrl.hasFocus = true" ng-readonly="$ctrl.readonly"
ng-blur="$ctrl.hasFocus = false" ng-focus="$ctrl.hasFocus = true"
tabindex="{{$ctrl.input.tabindex}}" ng-blur="$ctrl.hasFocus = false"
/> tabindex="{{$ctrl.input.tabindex}}"/>
<label class="label" translate>{{::$ctrl.label}}</label> <label class="label" translate>{{::$ctrl.label}}</label>
</div> </div>
<div class="underline"></div> <div class="underline"></div>
<div class="selected underline"></div> <div class="selected underline"></div>
<div class="suffix"> <div class="suffix">
<i class="material-icons pointer" <i class="material-icons clear"
translate-attr="{title: 'Clear'}"
ng-class="{visible: ng-class="{visible:
!$ctrl.disabled !$ctrl.disabled
&& $ctrl.hasValue && $ctrl.hasValue
&& ($ctrl.hasFocus || $ctrl.hasMouseIn) && ($ctrl.hasFocus)
&& !$ctrl.unclearable}" && !$ctrl.unclearable}"
ng-click="$ctrl.clear()"> ng-click="$ctrl.clear()">
clear clear

View File

@ -44,6 +44,7 @@ export default class Textfield extends Input {
if (this.hasValue) this.element.classList.add('not-empty'); if (this.hasValue) this.element.classList.add('not-empty');
else this.element.classList.remove('not-empty'); else this.element.classList.remove('not-empty');
this.mdlUpdate();
} }
get value() { get value() {
return this._value; return this._value;
@ -57,6 +58,10 @@ export default class Textfield extends Input {
set vnTabIndex(value) { set vnTabIndex(value) {
this.input.tabindex = value; this.input.tabindex = value;
} }
mdlUpdate() {
let mdlElement = this.element.firstChild.MaterialTextfield;
if (mdlElement) mdlElement.updateClasses_();
}
clear() { clear() {
this.value = null; this.value = null;
this.input.focus(); this.input.focus();

View File

@ -57,7 +57,7 @@ export default class Th {
this.updateArrow(); this.updateArrow();
this.table.applyFilter(this.field, this.order); this.table.applyOrder(this.field, this.order);
} }
/** /**

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

@ -88,7 +88,7 @@ describe('Item', () => {
describe('submit()', () => { describe('submit()', () => {
it("should return an error message 'The barcode must be unique' when the code isnt unique", () => { it("should return an error message 'The barcode must be unique' when the code isnt unique", () => {
spyOn(controller.vnApp, 'showMessage').and.callThrough(); spyOn(controller.vnApp, 'showError').and.callThrough();
controller.barcodes = [ controller.barcodes = [
{code: 123454, itemFk: 1, id: 1}, {code: 123454, itemFk: 1, id: 1},
{code: 123454, itemFk: 1} {code: 123454, itemFk: 1}
@ -96,7 +96,7 @@ describe('Item', () => {
controller.oldBarcodes = {1: {id: 1, code: 123454, itemFk: 1}}; controller.oldBarcodes = {1: {id: 1, code: 123454, itemFk: 1}};
controller.submit(); controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The barcode must be unique'); expect(controller.vnApp.showError).toHaveBeenCalledWith('The barcode must be unique');
}); });
it("should perfom a query to delete barcodes", () => { it("should perfom a query to delete barcodes", () => {
@ -131,7 +131,7 @@ describe('Item', () => {
}); });
it("should return a message 'No changes to save' when there are no changes to apply", () => { it("should return a message 'No changes to save' when there are no changes to apply", () => {
spyOn(controller.vnApp, 'showMessage').and.callThrough(); spyOn(controller.vnApp, 'showError').and.callThrough();
controller.oldBarcodes = [ controller.oldBarcodes = [
{code: 1, itemFk: 1, id: 1}, {code: 1, itemFk: 1, id: 1},
{code: 2, itemFk: 1, id: 2} {code: 2, itemFk: 1, id: 2}
@ -139,7 +139,7 @@ describe('Item', () => {
controller.barcodes = []; controller.barcodes = [];
controller.submit(); controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save'); expect(controller.vnApp.showError).toHaveBeenCalledWith('No changes to save');
}); });
}); });
}); });

View File

@ -86,7 +86,7 @@ export default class Controller {
} }
if (repeatedBarcodes) { if (repeatedBarcodes) {
return this.vnApp.showMessage(this.$translate.instant('The barcode must be unique')); return this.vnApp.showError(this.$translate.instant('The barcode must be unique'));
} }
canSubmit = barcodesObj.update.length > 0 || barcodesObj.create.length > 0 || barcodesObj.delete.length > 0; canSubmit = barcodesObj.update.length > 0 || barcodesObj.create.length > 0 || barcodesObj.delete.length > 0;
@ -98,7 +98,7 @@ export default class Controller {
this.$scope.watcher.notifySaved(); this.$scope.watcher.notifySaved();
}); });
} }
this.vnApp.showMessage(this.$translate.instant('No changes to save')); this.vnApp.showError(this.$translate.instant('No changes to save'));
} }
setOldBarcodes(response) { setOldBarcodes(response) {

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,10 @@
<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 +15,41 @@
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>Id</vn-th>
<th number translate>Origin</th> <vn-th>State</vn-th>
<th number translate>Reference</th> <vn-th>Reference</vn-th>
<th style="text-align: center" translate>Name</th> <vn-th>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.origin | dashIfEmpty}}</vn-td>
<td number>{{diary.reference | dashIfEmpty}}</td> <vn-td>{{::sale.stateName | dashIfEmpty}}</vn-td>
<td style="text-align: center">{{diary.name | dashIfEmpty}}</td> <vn-td>{{::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

@ -1,29 +1,41 @@
<mg-ajax path="/item/api/ItemLogs/getLog" options="vnIndexNonAuto"></mg-ajax> <vn-crud-model
vn-id="model"
url="/item/api/ItemLogs"
filter="::$ctrl.filter"
link="{originFk: $ctrl.$stateParams.id}"
limit="20"
data="logs">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
<vn-title>Item history</vn-title> <vn-title>Item history</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)"> <vn-table model="model">
<vn-column-header vn-two pad-medium-h field="description" text="Description"></vn-column-header> <vn-thead>
<vn-column-header vn-one pad-medium-h field="action" text="Action"></vn-column-header> <vn-tr>
<vn-column-header vn-one pad-medium-h field="userFk" text="Changed by"></vn-column-header> <vn-th field="description">Description</vn-th>
<vn-column-header vn-one pad-medium-h field="creationDate" text="Date" default-order="ASC"></vn-column-header> <vn-th field="action">Action</vn-th>
</vn-grid-header> <vn-th field="userFk">Changed by</vn-th>
<vn-one class="list list-content"> <vn-th field="creationDate" default-order="DESC">Date</vn-th>
<vn-horizontal </vn-tr>
class="list list-element text-center" </vn-thead>
pad-small-bottom <vn-tbody>
ng-repeat="itemLog in index.model.instances track by itemLog.id"> <vn-tr ng-repeat="itemLog in logs">
<vn-two pad-medium-h>{{::itemLog.description}}</vn-two> <vn-td>{{::itemLog.description}}</vn-td>
<vn-one pad-medium-h>{{::itemLog.action}}</vn-one> <vn-td>{{::itemLog.action}}</vn-td>
<vn-one pad-medium-h>{{::itemLog.user.name}}</vn-one> <vn-td>{{::itemLog.user.name}}</vn-td>
<vn-one pad-medium-h>{{::itemLog.creationDate | date:'dd/MM/yyyy HH:mm'}}</vn-one> <vn-td>{{::itemLog.creationDate | date:'dd/MM/yyyy HH:mm'}}</vn-td>
</vn-horizontal> </vn-tr>
</vn-one> </vn-tbody>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one> <vn-empty-rows ng-if="model.data.length === 0" translate>
<vn-horizontal vn-one class="list list-footer text-center"></vn-horizontal> No results
</vn-empty-rows>
</vn-table>
</vn-vertical> </vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging vn-one margin-large-top index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
</vn-vertical> </vn-vertical>

View File

@ -1,7 +1,25 @@
import ngModule from '../module'; import ngModule from '../module';
import FilterItemList from '../filter-item-list';
class Controller {
constructor($stateParams) {
this.$stateParams = $stateParams;
this.filter = {
include: [{
relation: "item"
},
{
relation: "user",
scope: {
fields: ["name"]
}
}]
};
}
}
Controller.$inject = ['$stateParams'];
ngModule.component('vnItemHistory', { ngModule.component('vnItemHistory', {
template: require('./index.html'), template: require('./index.html'),
controller: FilterItemList controller: Controller
}); });

View File

@ -1,8 +1,8 @@
<vn-crud-model <vn-crud-model
vn-id="model" vn-id="model"
url="/item/api/Items" url="/item/api/Items/filter"
filter="::$ctrl.filter"
limit="8" limit="8"
order="name ASC"
data="items" data="items"
auto-load="false"> auto-load="false">
</vn-crud-model> </vn-crud-model>

View File

@ -8,38 +8,23 @@ class Controller {
this.$state = $state; this.$state = $state;
this.$ = $scope; this.$ = $scope;
this.itemSelected = null; this.itemSelected = null;
this.filter = {
include: [
{relation: 'itemType',
scope: {
fields: ['name', 'workerFk'],
include: {
relation: 'worker',
fields: ['firstName', 'name']
}
}
}
],
order: 'name ASC'
};
} }
exprBuilder(param, value) { exprBuilder(param, value) {
switch (param) { switch (param) {
case 'search': case 'search':
return { return /^\d+$/.test(value)
or: [ ? {id: value}
{id: value}, : {name: {like: `%${value}%`}};
{name: {regexp: value}}
]
};
case 'name': case 'name':
case 'description': case 'description':
return {[param]: {regexp: value}}; return {[param]: {like: `%${value}%`}};
case 'id': case 'id':
case 'typeFk': case 'typeFk':
return {[param]: value}; return {[param]: value};
case 'tags':
this.$.model.userParams = {tags: value};
break;
} }
} }

View File

@ -21,10 +21,10 @@
value="{{::$ctrl.item.size}}"> value="{{::$ctrl.item.size}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Type" <vn-label-value label="Type"
value="{{::$ctrl.item.itemType.name}}"> value="{{::$ctrl.item.type}}">
</vn-label-value> </vn-label-value>
<vn-label-value label="Buyer" <vn-label-value label="Buyer"
value="{{::$ctrl.item.itemType.worker.firstName}} {{::$ctrl.item.itemType.worker.name}}"> value="{{::$ctrl.item.firstName}} {{::$ctrl.item.worker}}">
</vn-label-value> </vn-label-value>
</vn-one> </vn-one>
<vn-horizontal class="buttons"> <vn-horizontal class="buttons">

View File

@ -8,12 +8,12 @@ class ItemProduct {
clone(event) { clone(event) {
event.preventDefault(); event.preventDefault();
this.ItemList.cloneItem(this.item); this.index.cloneItem(this.item);
} }
preview(event) { preview(event) {
event.preventDefault(); event.preventDefault();
this.ItemList.showItemPreview(this.item); this.index.showItemPreview(this.item);
} }
} }
@ -24,6 +24,6 @@ ngModule.component('vnItemProduct', {
}, },
controller: ItemProduct, controller: ItemProduct,
require: { require: {
ItemList: '^vnItemIndex' index: '^vnItemIndex'
} }
}); });

View File

@ -1,3 +1,9 @@
<vn-crud-model
vn-id="model"
url="/item/api/Items/getLastEntries"
filter="::$ctrl.filter"
data="entries">
</vn-crud-model>
<vn-vertical> <vn-vertical>
<vn-card pad-large> <vn-card pad-large>
<vn-vertical> <vn-vertical>
@ -6,63 +12,65 @@
<vn-date-picker <vn-date-picker
vn-one vn-one
label="Since" label="Since"
model="$ctrl.entriesDate" model="$ctrl.date"
ini-options="{dateFormat: 'd-m-Y'}"> ini-options="{dateFormat: 'd-m-Y'}">
</vn-date-picker> </vn-date-picker>
<!--datepicker--> <!--datepicker-->
</vn-horizontal> </vn-horizontal>
<table class="vn-grid"> <vn-table model="model">
<thead> <vn-thead>
<tr> <vn-tr>
<th number vn-tooltip="Ignored">Ig</th> <vn-th number vn-tooltip="Ignored">Ig</vn-th>
<th number translate>Warehouse</th> <vn-th number>Warehouse</vn-th>
<th number translate>Landed</th> <vn-th number>Landed</vn-th>
<th number translate>Entry</th> <vn-th number>Entry</vn-th>
<th number vn-tooltip="Price Per Unit">P.P.U</th> <vn-th number vn-tooltip="Price Per Unit">P.P.U</vn-th>
<th number vn-tooltip="Price Per Package">P.P.P</th> <vn-th number vn-tooltip="Price Per Package">P.P.P</vn-th>
<th number class="expendable" translate>Label</th> <vn-th number class="expendable">Label</vn-th>
<th number>Packing</th> <vn-th number>Packing</vn-th>
<th number>Grouping</th> <vn-th number>Grouping</vn-th>
<th number class="expendable" translate>Stems</th> <vn-th number class="expendable">Stems</vn-th>
<th number translate>Quantity</th> <vn-th number>Quantity</vn-th>
<th number class="expendable" translate>Cost</th> <vn-th number class="expendable">Cost</vn-th>
<th number translate>Cube</th> <vn-th number>Cube</vn-th>
<th number class="expendable" translate>Provider</th> <vn-th number class="expendable">Provider</vn-th>
</tr> </vn-tr>
</thead> </vn-thead>
<tbody> <vn-tbody>
<tr ng-repeat="entries in $ctrl.entries"> <vn-tr ng-repeat="entry in entries">
<td number> <vn-td number>
<vn-check <vn-check
field="entries.isIgnored" field="entries.isIgnored"
disabled="true"> disabled="true">
</vn-check> </vn-check>
</td> </vn-td>
<td number>{{entries.warehouse| dashIfEmpty}}</td> <vn-td number>{{entry.warehouse| dashIfEmpty}}</vn-td>
<td number>{{entries.landed | date:'dd/MM/yyyy HH:mm'}}</td> <vn-td number>{{entry.landed | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<td number>{{entries.entryFk | dashIfEmpty}}</td> <vn-td number>{{entry.entryFk | dashIfEmpty}}</vn-td>
<td number>{{entries.price2 | dashIfEmpty}}</td> <vn-td number>{{entry.price2 | dashIfEmpty}}</vn-td>
<td number>{{entries.price3 | dashIfEmpty}}</td> <vn-td number>{{entry.price3 | dashIfEmpty}}</vn-td>
<td number class="expendable">{{entries.stickers | dashIfEmpty}}</td> <vn-td number class="expendable">{{entry.stickers | dashIfEmpty}}</vn-td>
<td number> <vn-td number>
<div ng-class="{'round': entries.groupingMode == 2}">{{entries.packing | dashIfEmpty}}</div> <div ng-class="{'counter': entry.groupingMode == 2}">{{entry.packing | dashIfEmpty}}</div>
</td> </vn-td>
<td number> <vn-td number>
<div ng-class="{'round': entries.groupingMode == 1}">{{entries.grouping | dashIfEmpty}}</div> <span ng-class="{'counter': entry.groupingMode == 1}">{{entry.grouping | dashIfEmpty}}</span>
</td> </vn-td>
<td number class="expendable">{{entries.stems | dashIfEmpty}}</td> <vn-td number class="expendable">{{entry.stems | dashIfEmpty}}</vn-td>
<td number>{{entries.quantity}}</td> <vn-td number>{{entry.quantity}}</vn-td>
<td number class="expendable">{{entries.buyingValue | dashIfEmpty}}</td> <vn-td number class="expendable">{{entry.buyingValue | dashIfEmpty}}</vn-td>
<td number>{{entries.packageFk | dashIfEmpty}}</td> <vn-td number>{{entry.packageFk | dashIfEmpty}}</vn-td>
<td number class="expendable">{{entries.supplier | dashIfEmpty}}</td> <vn-td number class="expendable">{{entry.supplier | dashIfEmpty}}</vn-td>
</tr> </vn-tr>
<tr ng-if="$ctrl.entries.length === 0" class="list list-element"> </vn-tbody>
<td colspan="14" style="text-align: center" translate>No results</td> <vn-empty-rows ng-if="model.data.length === 0" translate>
</tr> No results
</tbody> </vn-empty-rows>
</table> </vn-table>
</vn-vertical> </vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card> </vn-card>
<vn-paging margin-large-top vn-one index="$ctrl.entries" total="$ctrl.entries.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,50 +2,38 @@ import ngModule from '../module';
import './style.scss'; import './style.scss';
class Controller { class Controller {
constructor($scope, $http) { constructor($scope, $stateParams) {
this.$ = $scope; this.$scope = $scope;
this.$http = $http; this.$stateParams = $stateParams;
this.entries = [];
}
set item(value) { let defaultDate = new Date();
this._item = value;
this._defaultEntriesDate();
this._getLastEntries();
}
get item() {
return this._item;
}
set entriesDate(value) {
this._entriesDate = value;
this._getLastEntries();
}
get entriesDate() {
return this._entriesDate;
}
_defaultEntriesDate() {
let defaultDate;
defaultDate = new Date();
defaultDate.setDate(defaultDate.getDate() - 75); defaultDate.setDate(defaultDate.getDate() - 75);
defaultDate.setHours(0, 0, 0, 0); defaultDate.setHours(0, 0, 0, 0);
this._entriesDate = defaultDate;
this.filter = {
where: {
itemFk: $stateParams.id,
date: defaultDate
}
};
this._date = defaultDate;
} }
_getLastEntries() { set date(value) {
if (!this.entriesDate || !this.item) this._date = value;
return;
let filter = {itemFk: this.item.id, date: this.entriesDate}; if (!value) return;
this.$http.get(`/item/api/Items/getLastEntries?filter=${JSON.stringify(filter)}`).then(res => {
this.entries = res.data; this.filter.where.date = value;
}); this.$scope.model.refresh();
}
get date() {
return this._date;
} }
} }
Controller.$inject = ['$scope', '$http']; Controller.$inject = ['$scope', '$stateParams'];
ngModule.component('vnItemLastEntries', { ngModule.component('vnItemLastEntries', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -1,71 +0,0 @@
import './index.js';
describe('Item', () => {
describe('Component vnItemLastEntries', () => {
let $componentController;
let $scope;
let controller;
let $httpBackend;
let defaultDate;
beforeEach(() => {
angular.mock.module('item');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new();
controller = $componentController('vnItemLastEntries', {$scope: $scope});
controller.item = {id: 3};
}));
describe('set item()', () => {
it(`should set item and call _defaultEntriesDate() and _getLastEntries()`, () => {
spyOn(controller, '_defaultEntriesDate');
spyOn(controller, '_getLastEntries');
controller.item = [];
expect(controller._defaultEntriesDate).toHaveBeenCalledWith();
expect(controller._getLastEntries).toHaveBeenCalledWith();
expect(controller.item).toEqual([]);
});
});
describe('set entriesDate()', () => {
it(`should set entriesDate and call _getLastEntries()`, () => {
spyOn(controller, '_getLastEntries');
controller.item = [];
controller.entriesDate = new Date();
expect(controller._getLastEntries).toHaveBeenCalledWith();
expect(controller.item).toEqual([]);
});
});
describe('_defaultEntriesDate()', () => {
it(`should set entriesDate to a date 75 days ago`, () => {
defaultDate = new Date();
defaultDate.setDate(defaultDate.getDate() - 75);
defaultDate.setHours(0, 0, 0, 0);
controller._defaultEntriesDate();
expect(controller.entriesDate).toEqual(defaultDate);
});
});
describe('_getLastEntries()', () => {
it(`should make a GET if entriesDate and item are defined`, () => {
controller._defaultEntriesDate();
let filter = {itemFk: controller.item.id, date: controller.entriesDate};
$httpBackend.whenGET(`/item/api/Items/getLastEntries?filter=${JSON.stringify(filter)}`).respond([]);
$httpBackend.expectGET(`/item/api/Items/getLastEntries?filter=${JSON.stringify(filter)}`);
controller._getLastEntries();
$httpBackend.flush();
// expect(controller.entriesDate).toEqual(defaultDate);
});
});
});
});

View File

@ -50,4 +50,5 @@ Add barcode: Añadir código de barras
Remove barcode: Quitar código de barras Remove barcode: Quitar código de barras
Buyer: Comprador Buyer: Comprador
No results: Sin resultados No results: Sin resultados
Tag: Etiqueta Tag: Etiqueta
Worker: Trabajador

View File

@ -122,11 +122,11 @@ export default class Controller {
}); });
if (this.$scope.form.$invalid) { if (this.$scope.form.$invalid) {
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid')); return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
} }
if (repeatedWarehouse) { if (repeatedWarehouse) {
return this.vnApp.showMessage(this.$translate.instant('The niche must be unique')); return this.vnApp.showError(this.$translate.instant('The niche must be unique'));
} }
canSubmit = nichesObj.update.length > 0 || nichesObj.create.length > 0 || nichesObj.delete.length > 0; canSubmit = nichesObj.update.length > 0 || nichesObj.create.length > 0 || nichesObj.delete.length > 0;
@ -138,7 +138,7 @@ export default class Controller {
this.$scope.watcher.notifySaved(); this.$scope.watcher.notifySaved();
}); });
} }
this.vnApp.showMessage(this.$translate.instant('No changes to save')); this.vnApp.showError(this.$translate.instant('No changes to save'));
} }
} }

View File

@ -98,7 +98,7 @@ describe('Item', () => {
describe('submit()', () => { describe('submit()', () => {
it("should return an error message 'The niche must be unique' when the niche warehouse isnt unique", () => { it("should return an error message 'The niche must be unique' when the niche warehouse isnt unique", () => {
controller.$scope.form = {}; controller.$scope.form = {};
spyOn(controller.vnApp, 'showMessage').and.callThrough(); spyOn(controller.vnApp, 'showError').and.callThrough();
controller.niches = [ controller.niches = [
{warehouseFk: 1, code: 123454, itemFk: 1, id: 1}, {warehouseFk: 1, code: 123454, itemFk: 1, id: 1},
{warehouseFk: 1, code: 123454, itemFk: 1} {warehouseFk: 1, code: 123454, itemFk: 1}
@ -106,7 +106,7 @@ describe('Item', () => {
controller.oldNiches = {1: {warehouseFk: 1, id: 1, code: 123454, itemFk: 1}}; controller.oldNiches = {1: {warehouseFk: 1, id: 1, code: 123454, itemFk: 1}};
controller.submit(); controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The niche must be unique'); expect(controller.vnApp.showError).toHaveBeenCalledWith('The niche must be unique');
}); });
it("should perfom a query to delete niches", () => { it("should perfom a query to delete niches", () => {
@ -144,7 +144,7 @@ describe('Item', () => {
it("should return a message 'No changes to save' when there are no changes to apply", () => { it("should return a message 'No changes to save' when there are no changes to apply", () => {
controller.$scope.form = {$setPristine: () => {}}; controller.$scope.form = {$setPristine: () => {}};
spyOn(controller.vnApp, 'showMessage').and.callThrough(); spyOn(controller.vnApp, 'showError').and.callThrough();
controller.oldNiches = [ controller.oldNiches = [
{warehouseFk: 1, code: 1, itemFk: 1, id: 1}, {warehouseFk: 1, code: 1, itemFk: 1, id: 1},
{warehouseFk: 2, code: 2, itemFk: 1, id: 2} {warehouseFk: 2, code: 2, itemFk: 1, id: 2}
@ -152,7 +152,7 @@ describe('Item', () => {
controller.niches = []; controller.niches = [];
controller.submit(); controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save'); expect(controller.vnApp.showError).toHaveBeenCalledWith('No changes to save');
}); });
}); });
}); });

View File

@ -30,7 +30,6 @@
model="filter.description"> model="filter.description">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>
<!--
<vn-horizontal ng-repeat="itemTag in filter.tags"> <vn-horizontal ng-repeat="itemTag in filter.tags">
<vn-autocomplete <vn-autocomplete
vn-id="tag" vn-id="tag"
@ -76,7 +75,6 @@
ng-click="filter.tags.push({})"> ng-click="filter.tags.push({})">
</vn-icon-button> </vn-icon-button>
</vn-horizontal> </vn-horizontal>
-->
<vn-horizontal margin-large-top> <vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit> <vn-submit label="Search"></vn-submit>
</vn-horizontal> </vn-horizontal>

View File

@ -132,7 +132,7 @@ describe('Item', () => {
// TODO: Server validation should be implemented // TODO: Server validation should be implemented
xit("should return an error message 'The tag must be unique' when the tag value isnt unique", () => { xit("should return an error message 'The tag must be unique' when the tag value isnt unique", () => {
controller.$.form = []; controller.$.form = [];
spyOn(controller.vnApp, 'showMessage').and.callThrough(); spyOn(controller.vnApp, 'showError').and.callThrough();
controller.tags = [ controller.tags = [
{typeFk: 1, value: 123454, itemFk: 1, id: 1}, {typeFk: 1, value: 123454, itemFk: 1, id: 1},
{typeFk: 1, value: 123454, itemFk: 1} {typeFk: 1, value: 123454, itemFk: 1}
@ -140,7 +140,7 @@ describe('Item', () => {
controller.orgTags = {1: {typeFk: 1, id: 1, value: 123454, itemFk: 1}}; controller.orgTags = {1: {typeFk: 1, id: 1, value: 123454, itemFk: 1}};
controller.submit(); controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The tag must be unique'); expect(controller.vnApp.showError).toHaveBeenCalledWith('The tag must be unique');
}); });
it("should perfom a query to delete tags", () => { it("should perfom a query to delete tags", () => {

View File

@ -29,7 +29,7 @@ export default class Controller {
let url = `/item/api/Items/${this.$stateParams.id}/updateTaxes`; let url = `/item/api/Items/${this.$stateParams.id}/updateTaxes`;
this.$http.post(url, data).then( this.$http.post(url, data).then(
() => this.vnApp.showMessage(this._.instant('Data saved!')) () => this.vnApp.showSuccess(this._.instant('Data saved!'))
); );
} }
} }

View File

@ -8,7 +8,8 @@
"url": "/order", "url": "/order",
"state": "order", "state": "order",
"abstract": true, "abstract": true,
"component": "ui-view" "component": "ui-view",
"acl": ["developer"]
}, },
{ {
"url": "/index?q", "url": "/index?q",

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

@ -153,8 +153,7 @@
"menu": { "menu": {
"description": "Tracking", "description": "Tracking",
"icon": "remove_red_eye" "icon": "remove_red_eye"
}, }
"acl": ["production"]
}, },
{ {
"url": "/edit", "url": "/edit",
@ -162,15 +161,8 @@
"component": "vn-ticket-tracking-edit", "component": "vn-ticket-tracking-edit",
"params": { "params": {
"ticket": "$ctrl.ticket" "ticket": "$ctrl.ticket"
} },
}, "acl": ["production"]
{
"url": "/create",
"state": "ticket.card.tracking.create",
"component": "vn-ticket-tracking-create",
"params": {
"client": "$ctrl.client"
}
}, },
{ {
"url" : "/sale-checked", "url" : "/sale-checked",
@ -195,6 +187,18 @@
"description": "Components", "description": "Components",
"icon": "icon-components" "icon": "icon-components"
} }
},
{
"url" : "/sale-tracking",
"state": "ticket.card.saleTracking",
"component": "vn-ticket-sale-tracking",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Sale tracking",
"icon": "assignment"
}
} }
] ]
} }

View File

@ -67,7 +67,6 @@
</table> </table>
</vn-vertical> </vn-vertical>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging> <vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
<!-- <vn-auto-paging vn-one margin-large-top index="index" total="index.model.count" items="$ctrl.instances"></vn-auto-paging> -->
</vn-card> </vn-card>
</vn-vertical> </vn-vertical>
<vn-item-descriptor-popover vn-id="descriptor"></vn-item-descriptor-popover> <vn-item-descriptor-popover vn-id="descriptor"></vn-item-descriptor-popover>

View File

@ -0,0 +1,34 @@
<vn-title>New order</vn-title>
<vn-autocomplete
vn-focus
vn-id="client"
url="/api/Clients"
label="Client"
show-field="name"
value-field="id"
field="$ctrl.clientFk">
<tpl-item>{{id}}: {{name}}</tpl-item>
</vn-autocomplete>
<vn-autocomplete
disabled="!$ctrl.clientFk"
url="{{ $ctrl.clientFk ? '/api/Clients/'+ $ctrl.clientFk +'/addresses' : null }}"
select-fields=["nickname","street","city"]
field="$ctrl.addressFk"
show-field="nickname"
value-field="id"
label="Address">
<tpl-item>{{nickname}}: {{street}}, {{city}}</tpl-item>
</vn-autocomplete>
<vn-date-picker
label="Landed"
model="$ctrl.landed"
ini-options="{enableTime: false}">
</vn-date-picker>
<vn-autocomplete
disabled="!$ctrl.clientFk || !$ctrl.landed"
data="$ctrl._avaibleAgencies"
label="Agency"
show-field="agency"
value-field="id"
field="$ctrl.ticket.agencyModeFk">
</vn-autocomplete>

View File

@ -0,0 +1,93 @@
import ngModule from '../module';
class Controller {
constructor($scope, $http, vnApp, $translate) {
this.$scope = $scope;
this.$http = $http;
this.translate = $translate;
this.vnApp = vnApp;
this.ticket = {};
}
set ticket(value) {
if (value) {
this._ticket = value;
}
}
get ticket() {
return this._ticket;
}
set clientFk(value) {
this.ticket.clientFk = value;
this.addressFk = null;
}
get clientFk() {
return this.ticket.clientFk;
}
set addressFk(value) {
this.ticket.addressFk = value;
this.getAvaibleAgencies();
}
get addressFk() {
return this.ticket.addressFk;
}
set landed(value) {
this.ticket.landed = value;
this.getAvaibleAgencies();
}
get landed() {
return this.ticket.landed;
}
get warehouseFk() {
return this.ticket.warehouseFk;
}
getAvaibleAgencies() {
this.ticket.agencyModeFk = null;
if (this.ticket.landed && this.ticket.addressFk) {
let filter = {addressFk: this.ticket.addressFk, landed: this.ticket.landed};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/api/Agencies/landsThatDay?filter=${filter}`;
this.$http.get(query).then(res => {
this._avaibleAgencies = res.data[0];
});
}
}
onSubmit() {
this.createOrder();
}
createOrder() {
let params = {
landed: this.ticket.landed,
addressFk: this.ticket.addressFk,
agencyModeFk: this.ticket.agencyModeFk
};
this.$http.post(`order/api/Orders/new`, params).then(res => {
this.vnApp.showSuccess(this.translate.instant('Data saved!'));
return res.data.id;
}).catch(e => {
this.vnApp.showError(this.translate.instant(e.data.error.message));
});
}
}
Controller.$inject = ['$scope', '$http', 'vnApp', '$translate'];
ngModule.component('vnTicketCreateCard', {
template: require('./card.html'),
controller: Controller,
bindings: {
ticket: '<?'
}
});

View File

@ -0,0 +1,79 @@
import './card.js';
describe('Ticket', () => {
describe('Component vnTicketCreateCard', () => {
let $componentController;
let $scope;
let controller;
let $httpBackend;
beforeEach(() => {
angular.mock.module('ticket');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new();
controller = $componentController('vnTicketCreateCard', {$scope: $scope});
controller.item = {id: 3};
}));
describe('set clientFk', () => {
it(`should set addressFk to null and clientFk to a value`, () => {
controller.clientFk = 2;
expect(controller.clientFk).toEqual(2);
expect(controller.ticket.addressFk).toBe(null);
});
});
describe('set addressFk', () => {
it(`should set agencyModeFk property to null and addressFk to a value`, () => {
controller.addressFk = 101;
expect(controller.addressFk).toEqual(101);
expect(controller.ticket.agencyModeFk).toBe(null);
});
});
describe('getAvaibleAgencies()', () => {
it(`should make a query if landed and addressFk exists`, () => {
controller.ticket.addressFk = 101;
controller.ticket.landed = 101;
let filter = {addressFk: controller.ticket.addressFk, landed: controller.ticket.landed};
filter = encodeURIComponent(JSON.stringify(filter));
$httpBackend.whenGET(`/api/Agencies/landsThatDay?filter=${filter}`).respond({data: 1});
$httpBackend.expectGET(`/api/Agencies/landsThatDay?filter=${filter}`);
controller.getAvaibleAgencies();
$httpBackend.flush();
});
});
describe('onSubmit()', () => {
it(`should call createOrder()`, () => {
spyOn(controller, 'createOrder');
controller.onSubmit();
expect(controller.createOrder).toHaveBeenCalledWith();
});
});
describe('createOrder()', () => {
it(`should make a query`, () => {
controller.ticket.addressFk = 101;
controller.ticket.agencyModeFk = 101;
controller.ticket.landed = 101;
$httpBackend.whenPOST('order/api/Orders/new').respond({data: 1});
$httpBackend.expectPOST('order/api/Orders/new');
controller.createOrder();
$httpBackend.flush();
});
});
});
});

View File

@ -1,18 +1,11 @@
<mg-ajax path="/item/api/Tickets" options="vnPost"></mg-ajax> <div margin-medium>
<vn-watcher <div style="max-width: 70em; margin: 0 auto;" >
vn-id="watcher"
data="$ctrl.ticket"
form="form"
save="post">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
<div style="max-width: 70em; margin: 0 auto;">
<vn-card pad-large> <vn-card pad-large>
<vn-title>New ticket</vn-title> <vn-ticket-create-card vn-id="card" on-save=""></vn-ticket-create-card>
</vn-card> </vn-card>
<vn-button-bar> <vn-button-bar>
<vn-submit label="Create"></vn-submit> <vn-submit ng-click="$ctrl.onSubmit()" label="Save">
</vn-submit>
</vn-button-bar> </vn-button-bar>
</div> </div>
</form> </div>

View File

@ -1,20 +1,18 @@
import ngModule from '../module'; import ngModule from '../module';
class Controller { class Controller {
constructor($scope, $state) { constructor($scope, $http, $state) {
this.$ = $scope; this.$ = $scope;
this.$http = $http;
this.$state = $state; this.$state = $state;
this.Ticket = {};
} }
onSubmit() { async onSubmit() {
this.$.watcher.submit().then( let newOrderID = await this.$.card.createOrder();
json => this.$state.go('ticket.card.data', {id: json.data.id}) this.$state.go("ticket.card.summary", {id: newOrderID});
);
} }
} }
Controller.$inject = ['$scope', '$http', '$state'];
Controller.$inject = ['$scope', '$state'];
ngModule.component('vnTicketCreate', { ngModule.component('vnTicketCreate', {
template: require('./index.html'), template: require('./index.html'),

View File

@ -0,0 +1,40 @@
import './index.js';
describe('Ticket', () => {
describe('Component vnTicketCreate', () => {
let $componentController;
let $scope;
let controller;
let $state;
beforeEach(() => {
angular.mock.module('ticket');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$scope.card = {createOrder: () => {}};
$state = _$state_;
controller = $componentController('vnTicketCreate', {$scope: $scope, $state: $state});
}));
describe('onSubmit()', () => {
it(`should call createOrder()`, () => {
spyOn(controller.$.card, 'createOrder');
controller.onSubmit();
expect(controller.$.card.createOrder).toHaveBeenCalledWith();
});
xit(`should call go()`, () => {
spyOn(controller.$state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith();
});
});
});
});

View File

@ -0,0 +1,6 @@
You can't create an order for a frozen client: No puedes crear una orden a un cliente congelado
You can't create an order for a inactive client: No puedes crear una orden a un cliente inactivo
You can't create an order for a client that doesn't has tax data verified:
No puedes crear una orden a un cliente cuyos datos fiscales no han sido verificados
You can't create an order for a client that has a debt: No puedes crear una orden a un cliente que tiene deuda
New order: Nueva orden

View File

@ -8,16 +8,18 @@
label="Client" label="Client"
show-field="name" show-field="name"
value-field="id" value-field="id"
field="$ctrl.ticket.clientFk" field="$ctrl.clientFk"
initial-data="$ctrl.ticket.clientFk" initial-data="$ctrl.clientFk">
on-change="$ctrl.onChange()">
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-autocomplete vn-one
url="{{$ctrl.getAddresses()}}" data="$ctrl.addresses"
label="Address" label="Address"
show-field="nickname" show-field="nickname"
value-field="id" value-field="id"
field="$ctrl.ticket.addressFk"> field="$ctrl.ticket.addressFk">
<tpl-item>{{::nickname}}
<span ng-show="city || province">- {{::city}} ({{::province.name}})</span>
</tpl-item>
</vn-autocomplete> </vn-autocomplete>
<vn-autocomplete vn-one <vn-autocomplete vn-one
url="/api/AgencyModes" url="/api/AgencyModes"

View File

@ -13,14 +13,48 @@ class Controller {
this.data.registerChild(this); this.data.registerChild(this);
} }
onChange() { set ticket(value) {
if (this.ticket) this._ticket = value;
this.ticket.addressFk = null;
if (!value || !value.id) return;
this.onChange(value.clientFk);
} }
getAddresses() { get ticket() {
return this._ticket;
}
set clientFk(value) {
this.ticket.clientFk = value;
this.ticket.addressFk = null;
this.onChange(value);
}
get clientFk() {
if (this.ticket) if (this.ticket)
return `/api/Clients/${this.ticket.clientFk}/addresses`; return this.ticket.clientFk;
}
onChange(value) {
let filter = {
include: [
{
relation: 'province',
scope: {
fields: ['name']
}
}
]
};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/api/Clients/${value}/addresses?filter=${filter}`;
this.$http.get(query).then(res => {
if (res.data)
this.addresses = res.data;
});
} }
async onStepChange(state) { async onStepChange(state) {

View File

@ -19,6 +19,25 @@ describe('ticket', () => {
controller = $componentController('vnTicketDataStepOne', {$state: $state}); controller = $componentController('vnTicketDataStepOne', {$state: $state});
})); }));
describe('ticket() setter', () => {
it('should set ticket property and call onChange() method ', () => {
spyOn(controller, 'onChange');
controller.ticket = {id: 1, clientFk: 101};
expect(controller.onChange).toHaveBeenCalledWith(101);
});
});
describe('clientFk() setter', () => {
it('should set clientFk property and call onChange() method ', () => {
spyOn(controller, 'onChange');
controller.ticket = {id: 1, clientFk: 101};
controller.clientFk = 102;
expect(controller.onChange).toHaveBeenCalledWith(102);
});
});
describe('isFormInvalid()', () => { describe('isFormInvalid()', () => {
it('should check if all form fields are valid', () => { it('should check if all form fields are valid', () => {
controller.ticket = { controller.ticket = {
@ -31,8 +50,7 @@ describe('ticket', () => {
landed: new Date() landed: new Date()
}; };
let result = controller.isFormInvalid(); expect(controller.isFormInvalid()).toBeFalsy();
expect(result).toBeFalsy();
}); });
}); });
@ -41,7 +59,7 @@ describe('ticket', () => {
let landed = new Date(); let landed = new Date();
landed.setHours(0, 0, 0, 0); landed.setHours(0, 0, 0, 0);
controller.ticket = { controller._ticket = {
id: 1, id: 1,
clientFk: 1, clientFk: 1,
addressFk: 121, addressFk: 121,

Some files were not shown because too many files have changed in this diff Show More