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-card pad-large>
<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"
ng-class="{'bg-main': address.isDefaultAddress,'bg-opacity-item': !address.isActive && !address.isDefaultAddress}">
<vn-horizontal style="align-items: center;">
@ -56,7 +63,6 @@
</vn-one>
</vn-horizontal>
</vn-card>
<vn-paging index="index" total="index.model.total"></vn-paging>
<vn-float-button
vn-bind="+"
fixed-bottom-right

View File

@ -1,20 +1,28 @@
import ngModule from '../../module';
class Controller {
constructor($http, $scope) {
constructor($http, $scope, $stateParams) {
this.$http = $http;
this.$scope = $scope;
this.$stateParams = $stateParams;
this.filter = {
include: {
observations: 'observationType'
},
order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC']
};
}
setDefault(address) {
if (address.isActive) {
let params = {isDefaultAddress: true};
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', {
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-id="watcher"
data="$ctrl.client"

View File

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

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-id="watcher"
data="$ctrl.client"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="$ctrl.submit()">
<form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large>
<vn-title>Pay method</vn-title>
<vn-horizontal>
@ -36,8 +36,8 @@
<vn-horizontal pad-small-v>
<vn-one>
<vn-check
label="Received core VNH"
field="$ctrl.client.hasCoreVnh"
label="Received LCR"
field="$ctrl.client.hasLcr"
vn-acl="administrative, salesAssistant">
</vn-check>
</vn-one>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,14 @@
<mg-ajax
path="/client/api/Clients/{{index.params.id}}/contacts"
options="mgIndex"
actions="$ctrl.contacts = index.model">
</mg-ajax>
<vn-crud-model
vn-id="model"
url="/client/api/ClientContacts"
link="{clientFk: $ctrl.$stateParams.id}"
data="contacts" on-data-change="$ctrl.onDataChange()">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.submit()">
<vn-card pad-large>
<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-one
label="Name"

View File

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

View File

@ -6,7 +6,7 @@
vn-one
label="Credit"
model="$ctrl.creditClassification.credit",
rule="CreditInsurance.credit"
rule="creditInsurance.credit"
step="1"
vn-focus>
</vn-textfield>
@ -20,7 +20,7 @@
vn-one
label="Since"
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-horizontal>
</vn-card>

View File

@ -14,15 +14,17 @@ class Controller {
submit() {
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 data = this.creditClassification;
data.clientFk = this.client.id;
this.$http.post(query, data).then((res, err) => {
if (res.data)
this.$http.post(query, data).then(res => {
if (res.data) {
this.card.reload();
this.$state.go('client.card.creditInsurance.index');
}
});
}
}
@ -32,6 +34,9 @@ Controller.$inject = ['$http', '$filter', '$state', '$scope', '$translate', 'vnA
ngModule.component('vnClientCreditInsuranceCreate', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnClientCard'
},
bindings: {
client: '<'
}

View File

@ -20,7 +20,7 @@ describe('Client', () => {
describe('_getClassifications()', () => {
it('should perform a GET query to define the classifications property in the controller', () => {
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.expectGET(query);

View File

@ -15,7 +15,7 @@
<div><vn-label translate>To</vn-label> {{classification.finished | date:'dd/MM/yyyy'}}</div>
</vn-one>
<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-label-value label="Credit"
value="{{::insurance.credit}}">

View File

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

View File

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

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-id="watcher"
data="$ctrl.insurance"
form="form"
save="post">
</vn-watcher>
<form name="form"
ng-submit="watcher.submitGo('client.card.creditInsurance.insurance.index', {classificationId: post.params.classificationId})">
<form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large>
<vn-title>New credit</vn-title>
<vn-horizontal>
@ -22,7 +21,7 @@
<vn-date-picker vn-one
label="Date"
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-horizontal>
<vn-horizontal>

View File

@ -1,18 +1,32 @@
import ngModule from '../../../module';
class Controller {
constructor($filter) {
constructor($scope, $state, $filter) {
this.$scope = $scope;
this.$state = $state;
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', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnClientCard'
},
bindings: {
client: '<'
}

View File

@ -12,10 +12,10 @@ describe('Client', () => {
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
$componentController = _$componentController_;
let $state = {params: {classificationId: 1}};
let $stateParams = {classificationId: 1};
$httpBackend = _$httpBackend_;
$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', () => {

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-card pad-large>
<vn-vertical>
<vn-title>Requested credits</vn-title>
<table class="vn-grid">
<thead>
<tr>
<th number translate>Credit</th>
<th number translate>Grade</th>
<th translate>Created</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="insurance in index.model track by insurance.id" class="list list-element">
<td number>{{::insurance.credit | currency: '€': 2}}</td>
<td number>{{::insurance.grade}}</td>
<td>{{::insurance.created | date: 'dd/MM/yyyy'}}</td>
</tr>
<tr ng-if="index.model.count === 0" class="list list-element">
<td colspan="6" style="text-align: center" translate>No results</td>
</tr>
</tbody>
</table>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th number>Credit</vn-th>
<vn-th number>Grade</vn-th>
<vn-th>Created</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="insurance in insurances">
<vn-td number>{{::insurance.credit | currency: ' €': 2}}</vn-td>
<vn-td number>{{::insurance.grade}}</vn-td>
<vn-td>{{::insurance.created | date: 'dd/MM/yyyy'}}</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-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card>
<vn-button-bar>
<button
@ -30,9 +41,8 @@
translate
ui-sref="client.card.creditInsurance.index">Back</button>
</vn-button-bar>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
</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">
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,19 +1,21 @@
import ngModule from '../../../module';
import FilterList from 'core/src/lib/filter-list';
class Controller extends FilterList {
constructor($scope, $timeout, $state, $http) {
super($scope, $timeout, $state);
this.modelName = 'creditClassificationFk';
this.modelId = $state.params.classificationId;
this.isClosed = true;
class Controller {
constructor($stateParams, $http) {
this.$stateParams = $stateParams;
this.$http = $http;
this.isClosed = true;
this.filter = {
include: [
{relation: 'classification'}
]
};
}
$onInit() {
let filter = {
fields: ['finished'],
where: {id: this.modelId}
where: {id: this.$stateParams.classificationId}
};
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', {
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-card pad-large>
<vn-vertical>
<vn-title>Credit</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h field="amount" text="Credit"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="created" text="Since" default-order="ASC"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="worker.firstName" text="Employee" order-locked></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal
vn-one class="list list-element text-center"
pad-small-bottom
ng-repeat="credit in index.model.instances track by credit.id">
<vn-one pad-medium-h>{{::credit.amount | number:2}} €</vn-one>
<vn-two pad-medium-h>{{::credit.created | date:'dd/MM/yyyy HH:mm'}}</vn-two>
<vn-two pad-medium-h>{{::credit.worker.firstName}} {{::credit.worker.name}}</vn-two>
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="amount">Credit</vn-th>
<vn-th field="created" default-order="DESC">Since</vn-th>
<vn-th>Employee</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="credit in credits track by credit.id">
<vn-td>{{::credit.amount | number:2}} €</vn-td>
<vn-td>{{::credit.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td>{{::credit.worker.firstName}} {{::credit.worker.name}}</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-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</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>
<a ui-sref="client.card.credit.create" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>

View File

@ -1,7 +1,24 @@
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', {
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-id="watcher"
data="$ctrl.client"

View File

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

View File

@ -1,7 +1,24 @@
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', {
template: require('./index.html'),
controller: FilterClientList
controller: Controller
});

View File

@ -10,12 +10,9 @@ export default class Controller {
exprBuilder(param, value) {
switch (param) {
case 'search':
return {
or: [
{id: value},
{name: {regexp: value}}
]
};
return /^\d+$/.test(value)
? {id: value}
: {name: {regexp: value}};
case 'phone':
return {
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-card pad-large>
<vn-vertical>
<vn-title>Invoices</vn-title>
<vn-vertical style="text-align: center;">
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one field="ref" text="Reference" default-order="ASC"></vn-column-header>
<vn-column-header vn-one field="issued" text="Issue date"></vn-column-header>
<vn-column-header vn-one field="dued" text="Due date"></vn-column-header>
<vn-column-header vn-one field="amount" text="Amount"></vn-column-header>
</vn-grid-header>
<vn-vertical>
<vn-one
ng-if="index.model.count > 0"
class="list list-content">
<vn-horizontal
class="list list-element"
pad-small-bottom
ng-repeat="invoice in index.model.instances track by invoice.id">
<vn-one>{{::invoice.ref}}</vn-one>
<vn-one>{{::invoice.issued | date:'dd/MM/yyyy' }}</vn-one>
<vn-one>{{::invoice.dued | date:'dd/MM/yyyy' }}</vn-one>
<vn-one>{{::invoice.amount | currency:'€':2}}</vn-one>
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
</vn-vertical>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
</vn-vertical>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="ref" default-order="DESC">Reference</vn-th>
<vn-th field="issued">Issue date</vn-th>
<vn-th field="dued">Due date</vn-th>
<vn-th field="amount">Amount</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="invoice in invoices">
<vn-td>{{::invoice.ref}}</vn-td>
<vn-td>{{::invoice.issued | date:'dd/MM/yyyy' }}</vn-td>
<vn-td>{{::invoice.dued | date:'dd/MM/yyyy' }}</vn-td>
<vn-td>{{::invoice.amount | currency:' €': 2}}</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-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</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>

View File

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

View File

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

View File

@ -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-card pad-large>
<vn-vertical>
<vn-title>Mandate</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h field="id" text="Id"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="companyFk" text="Company"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="mandateTypeFk" text="Type"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="created" text="Register date" default-order="ASC"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="finished" text="End date"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal
vn-one class="list list-element text-center"
pad-small-bottom
ng-repeat="mandate in index.model.instances track by mandate.id">
<vn-one pad-medium-h>{{::mandate.id}}</vn-one>
<vn-one pad-medium-h>{{::mandate.company.code}}</vn-one>
<vn-one pad-medium-h>{{::mandate.mandateType.name}}</vn-one>
<vn-one pad-medium-h>{{::mandate.created | date:'dd/MM/yyyy HH:mm' }}</vn-one>
<vn-one pad-medium-h>{{::mandate.finished | date:'dd/MM/yyyy HH:mm' || '-'}}</vn-one>
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="id">Id</vn-th>
<vn-th field="companyFk">Company</vn-th>
<vn-th field="mandateTypeFk">Type</vn-th>
<vn-th field="created" default-order="DESC">Register date</vn-th>
<vn-th field="finished">End date</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="mandate in mandates">
<vn-td>{{::mandate.id}}</vn-td>
<vn-td>{{::mandate.company.code}}</vn-td>
<vn-td>{{::mandate.mandateType.name}}</vn-td>
<vn-td>{{::mandate.created | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<vn-td>{{::mandate.finished | date:'dd/MM/yyyy HH:mm' || '-'}}</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-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</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>

View File

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

View File

@ -1,31 +1,12 @@
import ngModule from '../../module';
export default class Controller {
constructor($http, $state) {
this.$http = $http;
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});
constructor($stateParams) {
this.$stateParams = $stateParams;
}
}
Controller.$inject = ['$http', '$state'];
Controller.$inject = ['$stateParams'];
ngModule.component('vnClientNote', {
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-card pad-large>
<vn-vertical>
<vn-title>Recovery</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h field="started" text="Since" default-order="ASC"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="finished" text="To"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="amount" text="Amount"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="period" text="Period"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal
vn-one class="list list-element text-center"
pad-small-bottom
ng-repeat="recovery in index.model.instances track by $index">
<vn-none pad-medium-h orange>
<i class="material-icons pointer"
vn-tooltip="Finish that recovery period"
ng-if="!recovery.finished"
ng-click="$ctrl.setFinished(recovery)">lock</i>
</vn-none>
<vn-one pad-medium-h>{{::recovery.started | date:'dd/MM/yyyy' }}</vn-one>
<vn-one pad-medium-h>{{recovery.finished | date:'dd/MM/yyyy' }}</vn-one>
<vn-one pad-medium-h>{{::recovery.amount | currency:'€':0}}</vn-one>
<vn-one pad-medium-h>{{::recovery.period}}</vn-one>
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th></vn-th>
<vn-th field="started" default-order="DESC">Since</vn-th>
<vn-th field="finished">To</vn-th>
<vn-th field="amount">Amount</vn-th>
<vn-th field="period">Period</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="recovery in recoveries">
<vn-td>
<vn-icon
class="bright pointer"
icon="lock"
vn-tooltip="Finish that recovery period"
ng-if="!recovery.finished"
ng-click="$ctrl.setFinished(recovery)">
</vn-icon>
</vn-td>
<vn-td>{{::recovery.started | date:'dd/MM/yyyy' }}</vn-td>
<vn-td>{{recovery.finished | date:'dd/MM/yyyy' }}</vn-td>
<vn-td>{{::recovery.amount | currency:' €': 0}}</vn-td>
<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-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</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>
<a ui-sref="client.card.recovery.create" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>

View File

@ -1,22 +1,23 @@
import ngModule from '../../module';
import FilterClientList from '../../filter-client-list';
class Controller extends FilterClientList {
constructor($scope, $timeout, $state, $http) {
super($scope, $timeout, $state);
class Controller {
constructor($stateParams, $scope, $http) {
this.$stateParams = $stateParams;
this.$scope = $scope;
this.$http = $http;
}
setFinished(recovery) {
if (!recovery.finished) {
let params = {finished: Date.now()};
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', {
template: require('./index.html'),

View File

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

View File

@ -11,8 +11,12 @@ class Controller {
return;
this.$http.get(`/client/api/Clients/${this.client.id}/summary`).then(res => {
if (res && res.data)
if (res && 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>
<label class="mdl-textfield__label" translate>{{::$ctrl.label}}</label>
</div>
<i class="material-icons pointer"
ng-show="!$ctrl.disabled && $ctrl.hasValue && ($ctrl.hasFocus || $ctrl.hasMouseIn)"
ng-click="$ctrl.clear()">
clear
</i>
</div>
<vn-drop-down
vn-id="drop-down"

View File

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

View File

@ -21,6 +21,16 @@ describe('Component vnAutocomplete', () => {
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', () => {
it(`should set field controllers property`, () => {
controller.field = data.id;

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@ import './style.scss';
class Pagination extends Component {
constructor($element, $scope) {
super($element, $scope);
this.scrollOffset = 20;
this.scrollOffset = 150;
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('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.relocate();
@ -136,10 +127,10 @@ export default class Popover extends Component {
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 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 showTop = top + height + margin > window.innerHeight;

View File

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

View File

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

View File

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

View File

@ -105,14 +105,34 @@ export default class Controller extends Component {
}
pushFilterToState(filter) {
let history = window.history || {pushState: () => {
console.error('Error in history.pushState(): Browser incompatibility error');
}};
let aux = window.location.hash.split('?q=');
if (Object.keys(filter).length)
history.pushState({}, null, `${aux[0]}?q=${encodeURIComponent(JSON.stringify(filter))}`);
else
history.pushState({}, null, aux[0]);
let state = window.location.hash.split('?')[0];
let keys = Object.keys(filter);
if (keys.length) {
let hashFilter = {};
keys.forEach(key => {
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;
}
applyFilter(field = this.field, order = this.order) {
this.model.filter.order = `${field} ${order}`;
applyOrder(field = this.field, order = this.order) {
if (!field) return;
this.model.order = `${field} ${order}`;
this.model.refresh();
this.setActiveArrow();
}
$onChanges() {
if (this.model)
this.applyFilter();
if (this.model && this.model.filter)
this.applyOrder();
}
setActiveArrow() {

View File

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

View File

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

View File

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

View File

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

View File

@ -57,7 +57,7 @@ export default class Th {
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 './visible-by';
import './bind';
import './repeat-last';

View File

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

View File

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

View File

@ -88,7 +88,7 @@ describe('Item', () => {
describe('submit()', () => {
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 = [
{code: 123454, itemFk: 1, id: 1},
{code: 123454, itemFk: 1}
@ -96,7 +96,7 @@ describe('Item', () => {
controller.oldBarcodes = {1: {id: 1, code: 123454, itemFk: 1}};
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", () => {
@ -131,7 +131,7 @@ describe('Item', () => {
});
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 = [
{code: 1, itemFk: 1, id: 1},
{code: 2, itemFk: 1, id: 2}
@ -139,7 +139,7 @@ describe('Item', () => {
controller.barcodes = [];
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) {
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;
@ -98,7 +98,7 @@ export default class Controller {
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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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-card pad-large>
<vn-vertical>
<vn-title>Item history</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-two pad-medium-h field="description" text="Description"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="action" text="Action"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="userFk" text="Changed by"></vn-column-header>
<vn-column-header vn-one pad-medium-h field="creationDate" text="Date" default-order="ASC"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal
class="list list-element text-center"
pad-small-bottom
ng-repeat="itemLog in index.model.instances track by itemLog.id">
<vn-two pad-medium-h>{{::itemLog.description}}</vn-two>
<vn-one pad-medium-h>{{::itemLog.action}}</vn-one>
<vn-one pad-medium-h>{{::itemLog.user.name}}</vn-one>
<vn-one pad-medium-h>{{::itemLog.creationDate | date:'dd/MM/yyyy HH:mm'}}</vn-one>
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
<vn-horizontal vn-one class="list list-footer text-center"></vn-horizontal>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="description">Description</vn-th>
<vn-th field="action">Action</vn-th>
<vn-th field="userFk">Changed by</vn-th>
<vn-th field="creationDate" default-order="DESC">Date</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="itemLog in logs">
<vn-td>{{::itemLog.description}}</vn-td>
<vn-td>{{::itemLog.action}}</vn-td>
<vn-td>{{::itemLog.user.name}}</vn-td>
<vn-td>{{::itemLog.creationDate | date:'dd/MM/yyyy HH:mm'}}</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-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</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>

View File

@ -1,7 +1,25 @@
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', {
template: require('./index.html'),
controller: FilterItemList
controller: Controller
});

View File

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

View File

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

View File

@ -21,10 +21,10 @@
value="{{::$ctrl.item.size}}">
</vn-label-value>
<vn-label-value label="Type"
value="{{::$ctrl.item.itemType.name}}">
value="{{::$ctrl.item.type}}">
</vn-label-value>
<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-one>
<vn-horizontal class="buttons">

View File

@ -8,12 +8,12 @@ class ItemProduct {
clone(event) {
event.preventDefault();
this.ItemList.cloneItem(this.item);
this.index.cloneItem(this.item);
}
preview(event) {
event.preventDefault();
this.ItemList.showItemPreview(this.item);
this.index.showItemPreview(this.item);
}
}
@ -24,6 +24,6 @@ ngModule.component('vnItemProduct', {
},
controller: ItemProduct,
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-card pad-large>
<vn-vertical>
@ -6,63 +12,65 @@
<vn-date-picker
vn-one
label="Since"
model="$ctrl.entriesDate"
model="$ctrl.date"
ini-options="{dateFormat: 'd-m-Y'}">
</vn-date-picker>
<!--datepicker-->
</vn-horizontal>
<table class="vn-grid">
<thead>
<tr>
<th number vn-tooltip="Ignored">Ig</th>
<th number translate>Warehouse</th>
<th number translate>Landed</th>
<th number translate>Entry</th>
<th number vn-tooltip="Price Per Unit">P.P.U</th>
<th number vn-tooltip="Price Per Package">P.P.P</th>
<th number class="expendable" translate>Label</th>
<th number>Packing</th>
<th number>Grouping</th>
<th number class="expendable" translate>Stems</th>
<th number translate>Quantity</th>
<th number class="expendable" translate>Cost</th>
<th number translate>Cube</th>
<th number class="expendable" translate>Provider</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="entries in $ctrl.entries">
<td number>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th number vn-tooltip="Ignored">Ig</vn-th>
<vn-th number>Warehouse</vn-th>
<vn-th number>Landed</vn-th>
<vn-th number>Entry</vn-th>
<vn-th number vn-tooltip="Price Per Unit">P.P.U</vn-th>
<vn-th number vn-tooltip="Price Per Package">P.P.P</vn-th>
<vn-th number class="expendable">Label</vn-th>
<vn-th number>Packing</vn-th>
<vn-th number>Grouping</vn-th>
<vn-th number class="expendable">Stems</vn-th>
<vn-th number>Quantity</vn-th>
<vn-th number class="expendable">Cost</vn-th>
<vn-th number>Cube</vn-th>
<vn-th number class="expendable">Provider</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="entry in entries">
<vn-td number>
<vn-check
field="entries.isIgnored"
disabled="true">
</vn-check>
</td>
<td number>{{entries.warehouse| dashIfEmpty}}</td>
<td number>{{entries.landed | date:'dd/MM/yyyy HH:mm'}}</td>
<td number>{{entries.entryFk | dashIfEmpty}}</td>
<td number>{{entries.price2 | dashIfEmpty}}</td>
<td number>{{entries.price3 | dashIfEmpty}}</td>
<td number class="expendable">{{entries.stickers | dashIfEmpty}}</td>
<td number>
<div ng-class="{'round': entries.groupingMode == 2}">{{entries.packing | dashIfEmpty}}</div>
</td>
<td number>
<div ng-class="{'round': entries.groupingMode == 1}">{{entries.grouping | dashIfEmpty}}</div>
</td>
<td number class="expendable">{{entries.stems | dashIfEmpty}}</td>
<td number>{{entries.quantity}}</td>
<td number class="expendable">{{entries.buyingValue | dashIfEmpty}}</td>
<td number>{{entries.packageFk | dashIfEmpty}}</td>
<td number class="expendable">{{entries.supplier | dashIfEmpty}}</td>
</tr>
<tr ng-if="$ctrl.entries.length === 0" class="list list-element">
<td colspan="14" style="text-align: center" translate>No results</td>
</tr>
</tbody>
</table>
</vn-td>
<vn-td number>{{entry.warehouse| dashIfEmpty}}</vn-td>
<vn-td number>{{entry.landed | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td number>{{entry.entryFk | dashIfEmpty}}</vn-td>
<vn-td number>{{entry.price2 | dashIfEmpty}}</vn-td>
<vn-td number>{{entry.price3 | dashIfEmpty}}</vn-td>
<vn-td number class="expendable">{{entry.stickers | dashIfEmpty}}</vn-td>
<vn-td number>
<div ng-class="{'counter': entry.groupingMode == 2}">{{entry.packing | dashIfEmpty}}</div>
</vn-td>
<vn-td number>
<span ng-class="{'counter': entry.groupingMode == 1}">{{entry.grouping | dashIfEmpty}}</span>
</vn-td>
<vn-td number class="expendable">{{entry.stems | dashIfEmpty}}</vn-td>
<vn-td number>{{entry.quantity}}</vn-td>
<vn-td number class="expendable">{{entry.buyingValue | dashIfEmpty}}</vn-td>
<vn-td number>{{entry.packageFk | dashIfEmpty}}</vn-td>
<vn-td number class="expendable">{{entry.supplier | dashIfEmpty}}</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-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</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>

View File

@ -2,50 +2,38 @@ import ngModule from '../module';
import './style.scss';
class Controller {
constructor($scope, $http) {
this.$ = $scope;
this.$http = $http;
this.entries = [];
}
constructor($scope, $stateParams) {
this.$scope = $scope;
this.$stateParams = $stateParams;
set item(value) {
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();
let defaultDate = new Date();
defaultDate.setDate(defaultDate.getDate() - 75);
defaultDate.setHours(0, 0, 0, 0);
this._entriesDate = defaultDate;
this.filter = {
where: {
itemFk: $stateParams.id,
date: defaultDate
}
};
this._date = defaultDate;
}
_getLastEntries() {
if (!this.entriesDate || !this.item)
return;
let filter = {itemFk: this.item.id, date: this.entriesDate};
this.$http.get(`/item/api/Items/getLastEntries?filter=${JSON.stringify(filter)}`).then(res => {
this.entries = res.data;
});
set date(value) {
this._date = value;
if (!value) return;
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', {
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
Buyer: Comprador
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) {
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid'));
return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
}
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;
@ -138,7 +138,7 @@ export default class Controller {
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()', () => {
it("should return an error message 'The niche must be unique' when the niche warehouse isnt unique", () => {
controller.$scope.form = {};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.niches = [
{warehouseFk: 1, code: 123454, itemFk: 1, id: 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.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", () => {
@ -144,7 +144,7 @@ describe('Item', () => {
it("should return a message 'No changes to save' when there are no changes to apply", () => {
controller.$scope.form = {$setPristine: () => {}};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.oldNiches = [
{warehouseFk: 1, code: 1, itemFk: 1, id: 1},
{warehouseFk: 2, code: 2, itemFk: 1, id: 2}
@ -152,7 +152,7 @@ describe('Item', () => {
controller.niches = [];
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">
</vn-textfield>
</vn-horizontal>
<!--
<vn-horizontal ng-repeat="itemTag in filter.tags">
<vn-autocomplete
vn-id="tag"
@ -76,7 +75,6 @@
ng-click="filter.tags.push({})">
</vn-icon-button>
</vn-horizontal>
-->
<vn-horizontal margin-large-top>
<vn-submit label="Search"></vn-submit>
</vn-horizontal>

View File

@ -132,7 +132,7 @@ describe('Item', () => {
// TODO: Server validation should be implemented
xit("should return an error message 'The tag must be unique' when the tag value isnt unique", () => {
controller.$.form = [];
spyOn(controller.vnApp, 'showMessage').and.callThrough();
spyOn(controller.vnApp, 'showError').and.callThrough();
controller.tags = [
{typeFk: 1, value: 123454, itemFk: 1, id: 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.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", () => {

View File

@ -29,7 +29,7 @@ export default class Controller {
let url = `/item/api/Items/${this.$stateParams.id}/updateTaxes`;
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",
"state": "order",
"abstract": true,
"component": "ui-view"
"component": "ui-view",
"acl": ["developer"]
},
{
"url": "/index?q",

View File

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

View File

@ -153,8 +153,7 @@
"menu": {
"description": "Tracking",
"icon": "remove_red_eye"
},
"acl": ["production"]
}
},
{
"url": "/edit",
@ -162,15 +161,8 @@
"component": "vn-ticket-tracking-edit",
"params": {
"ticket": "$ctrl.ticket"
}
},
{
"url": "/create",
"state": "ticket.card.tracking.create",
"component": "vn-ticket-tracking-create",
"params": {
"client": "$ctrl.client"
}
},
"acl": ["production"]
},
{
"url" : "/sale-checked",
@ -195,6 +187,18 @@
"description": "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>
</vn-vertical>
<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-vertical>
<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>
<vn-watcher
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;">
<div margin-medium>
<div style="max-width: 70em; margin: 0 auto;" >
<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-button-bar>
<vn-submit label="Create"></vn-submit>
<vn-submit ng-click="$ctrl.onSubmit()" label="Save">
</vn-submit>
</vn-button-bar>
</div>
</form>
</div>

View File

@ -1,20 +1,18 @@
import ngModule from '../module';
class Controller {
constructor($scope, $state) {
constructor($scope, $http, $state) {
this.$ = $scope;
this.$http = $http;
this.$state = $state;
this.Ticket = {};
}
onSubmit() {
this.$.watcher.submit().then(
json => this.$state.go('ticket.card.data', {id: json.data.id})
);
async onSubmit() {
let newOrderID = await this.$.card.createOrder();
this.$state.go("ticket.card.summary", {id: newOrderID});
}
}
Controller.$inject = ['$scope', '$state'];
Controller.$inject = ['$scope', '$http', '$state'];
ngModule.component('vnTicketCreate', {
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"
show-field="name"
value-field="id"
field="$ctrl.ticket.clientFk"
initial-data="$ctrl.ticket.clientFk"
on-change="$ctrl.onChange()">
field="$ctrl.clientFk"
initial-data="$ctrl.clientFk">
</vn-autocomplete>
<vn-autocomplete vn-one
url="{{$ctrl.getAddresses()}}"
data="$ctrl.addresses"
label="Address"
show-field="nickname"
value-field="id"
field="$ctrl.ticket.addressFk">
<tpl-item>{{::nickname}}
<span ng-show="city || province">- {{::city}} ({{::province.name}})</span>
</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
url="/api/AgencyModes"

View File

@ -13,14 +13,48 @@ class Controller {
this.data.registerChild(this);
}
onChange() {
if (this.ticket)
this.ticket.addressFk = null;
set ticket(value) {
this._ticket = value;
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)
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) {

View File

@ -19,6 +19,25 @@ describe('ticket', () => {
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()', () => {
it('should check if all form fields are valid', () => {
controller.ticket = {
@ -31,8 +50,7 @@ describe('ticket', () => {
landed: new Date()
};
let result = controller.isFormInvalid();
expect(result).toBeFalsy();
expect(controller.isFormInvalid()).toBeFalsy();
});
});
@ -41,7 +59,7 @@ describe('ticket', () => {
let landed = new Date();
landed.setHours(0, 0, 0, 0);
controller.ticket = {
controller._ticket = {
id: 1,
clientFk: 1,
addressFk: 121,

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