This commit is contained in:
Carlos Jimenez 2018-02-07 09:01:44 +01:00
commit 2c712b4f84
561 changed files with 53181 additions and 25278 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -1,5 +1,6 @@
extends: [eslint:recommended, google, plugin:jasmine/recommended]
installedESLint: true
parserOptions:
ecmaVersion: 2017
plugins:
- jasmine
env:
@ -18,3 +19,4 @@ rules:
no-eq-null: 0
no-console: 0
no-warning-comments: 0
no-empty: 0

5
.gitignore vendored
View File

@ -1,7 +1,4 @@
.DS_Store
node_modules
build
npm-debug.log
debug.log
datasources.development.json
.idea
docker-compose.yml

39
.vscode/launch.json vendored
View File

@ -2,45 +2,10 @@
"version": "0.2.0",
"configurations": [
{
"name": "Iniciar",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/services/auth/server/server.js",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}",
"preLaunchTask": null,
"runtimeExecutable": null,
"runtimeArgs": [
"--nolazy"
],
"env": {
"NODE_ENV": "development"
},
"console": "internalConsole",
"sourceMaps": false,
"outFiles": []
},
{
"name": "Asociar",
"type": "node",
"request": "attach",
"port": 5858,
"address": "localhost",
"restart": false,
"sourceMaps": false,
"outFiles": [],
"localRoot": "${workspaceRoot}",
"remoteRoot": null
},
{
"name": "Asociar al proceso",
"type": "node",
"request": "attach",
"processId": "${command:PickProcess}",
"port": 5858,
"sourceMaps": false,
"outFiles": []
"name": "Attach by Process ID",
"processId": "${command:PickProcess}"
}
]
}

View File

@ -1,13 +1,11 @@
FROM node:6.9.1
FROM node:8.9.4
COPY auth /app
COPY loopback /loopback
COPY . /app
COPY ../loopback /loopback
WORKDIR /app
RUN npm install
RUN npm -g install pm2
CMD ["pm2-docker", "./server/server.js"]

10
Jenkinsfile vendored
View File

@ -43,12 +43,18 @@ node
stage ("Stopping/Removing Docker")
{
sh "docker-compose down --rmi all"
step{
env.TAG = env.BUILD_NUMBER - 1;
sh "docker-compose down --rmi 'all'"
}
}
stage ("Generar dockers")
{
sh "docker-compose up -d --build"
step{
env.TAG = env.BUILD_NUMBER + 1;
sh "docker-compose up -d --build"
}
}
}
}

17
LICENSE Normal file
View File

@ -0,0 +1,17 @@
Copyright (C) 2018 - Verdnatura Levante S.L.
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
On Debian systems, the complete text of the GNU General Public
License can be found in "/usr/share/common-licenses/GPL-3".

View File

@ -1,53 +1,64 @@
# Project Title
# Salix
One Paragraph of project description goes here
This project is an Enterprise resource planning (ERP) integrated management of core business processes, in real-time and mediated by software and technology developed with the stack listed below.
### Prerequisites
Salix is also the scientific name of a beautifull tree! :)
For testing purposes you will need to install globally the following items:
npm install -g karma
npm install -g karma-cli
## Prerequisites
## Getting Started // ### Installing
Required applications.
Pull from repo.
* Node.js = 8.9.4
* NGINX
* Docker
install nodejs v6 or above.
You will need to install globally the following items.
```
$ npm install -g karma-cli gulp webpack nodemon
```
install nginx globally.
## Getting Started // Installing
Ask a senior dev for the datasources.development.json files required to run the project.
Pull from repository.
on root run:
npm install
gulp install
Run this commands on project root directory to install Node dependencies.
```
$ npm install
$ gulp install
```
lauching nginx:
./dev.sh
Launch application in developer environment.
```
$ gulp
```
launching frontend:
gulp client
Also you can run backend and frontend as separately gulp tasks (including NGINX).
```
$ gulp client
$ gulp services
```
launching backend:
gulp services
Manually reset fixtures.
```
$ gulp docker
```
## Running the tests
## Running the unit tests
for client-side unit tests run from project's root:
karma start
For client-side unit tests run from project's root.
```
$ karma start
```
for server-side unit tests run from project's root:
npm run testWatch or test for single run
For server-side unit tests run from project's root.
```
$ npm run test
```
### Break down into end to end tests
on root run:
gulp docker
wait 10 secs for db to be ready
npm run e2e
For end-to-end tests run from project's root.
```
$ gulp e2e
```
## Built With
@ -57,12 +68,6 @@ npm run e2e
* [loopback](https://loopback.io/)
* [docker](https://www.docker.com/)
* [gulp.js](https://gulpjs.com/)
## Versioning
We use [SourceTree](https://www.sourcetreeapp.com/) for versioning. For the versions available, see the [salix project](https://git.verdnatura.es).
## License
This project is licensed under the MIT License
* [Karma](https://karma-runner.github.io/)
* [Jasmine](https://jasmine.github.io/)
* [Nightmare](http://www.nightmarejs.org/)

View File

@ -1,10 +0,0 @@
{
"name": "@salix/auth",
"version": "0.0.0",
"description": "",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://git.verdnatura.es/salix"
}
}

View File

@ -1,6 +0,0 @@
{
"User": "User",
"Password": "Password",
"Do not close session": "Do not close session",
"Enter": "Enter"
}

View File

@ -0,0 +1,4 @@
User: User
Password: Password
Do not close session: Do not close session
Enter: Enter

View File

@ -1,6 +0,0 @@
{
"User": "Usuario",
"Password": "Contraseña",
"Do not close session": "No cerrar sesión",
"Enter": "Entrar"
}

View File

@ -0,0 +1,4 @@
User: Usuario
Password: Contraseña
Do not close session: No cerrar sesión
Enter: Entrar

View File

@ -48,7 +48,6 @@ export default class Controller {
onLoginErr(json) {
this.loading = false;
this.password = '';
this.focusUser();
let message;
@ -64,6 +63,7 @@ export default class Controller {
}
this.showMessage(message);
this.focusUser();
}
focusUser() {
this.$.userField.select();

View File

@ -1,10 +0,0 @@
{
"name": "@salix/client",
"version": "0.0.0",
"description": "",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://git.verdnatura.es/salix"
}
}

View File

@ -26,7 +26,7 @@
"client": "$ctrl.client"
},
"menu": {
"description": "Datos básicos",
"description": "Basic data",
"icon": "person"
}
}, {
@ -37,7 +37,7 @@
"client": "$ctrl.client"
},
"menu": {
"description": "Datos fiscales",
"description": "Fiscal data",
"icon": "account_balance"
}
}, {
@ -64,7 +64,7 @@
"client": "$ctrl.client"
},
"menu": {
"description": "Consignatarios",
"description": "Addresses",
"icon": "local_shipping"
}
}, {
@ -83,7 +83,7 @@
"client": "$ctrl.client"
},
"menu": {
"description": "Acceso web",
"description": "Web access",
"icon": "language"
}
}, {
@ -99,13 +99,67 @@
"client": "$ctrl.client"
},
"menu": {
"description": "Notas",
"description": "Notes",
"icon": "insert_drive_file"
}
}, {
"url": "/create",
"state": "clientCard.notes.create",
"component": "vn-note-create"
}, {
"url": "/credit",
"abstract": true,
"state": "clientCard.credit",
"component": "ui-view"
}, {
"url": "/list",
"state": "clientCard.credit.list",
"component": "vn-client-credit-list",
"params": {
"client": "$ctrl.client"
},
"menu": {
"description": "Credit",
"icon": "credit_card"
}
}, {
"url": "/create",
"state": "clientCard.credit.create",
"component": "vn-client-credit-create",
"params": {
"client": "$ctrl.client"
}
}, {
"url": "/greuge",
"abstract": true,
"state": "clientCard.greuge",
"component": "ui-view"
}, {
"url": "/list",
"state": "clientCard.greuge.list",
"component": "vn-client-greuge-list",
"params": {
"client": "$ctrl.client"
},
"menu": {
"description": "Greuge",
"icon": "work"
}
}, {
"url": "/create",
"state": "clientCard.greuge.create",
"component": "vn-client-greuge-create",
"params": {
"client": "$ctrl.client"
}
}, {
"url": "/mandate",
"state": "clientCard.mandate",
"component": "vn-client-mandate",
"menu": {
"description": "Mandate",
"icon": "pan_tool"
}
}
]
}

View File

@ -13,7 +13,7 @@
<vn-check vn-one label="Default" field="$ctrl.address.isDefaultAddress"></vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Consignee" field="$ctrl.address.consignee" vn-focus></vn-textfield>
<vn-textfield vn-one label="Consignee" field="$ctrl.address.nickname" vn-focus></vn-textfield>
<vn-textfield vn-one label="Street address" field="$ctrl.address.street"></vn-textfield>
</vn-horizontal>
<vn-horizontal>

View File

@ -4,7 +4,7 @@ export default class Controller {
constructor($state) {
this.address = {
clientFk: parseInt($state.params.id),
isEnabled: true
isActive: true
};
}
}

View File

@ -19,7 +19,7 @@ describe('Client', () => {
it('should define and set address property', () => {
expect(controller.address.clientFk).toBe(1234);
expect(controller.address.isEnabled).toBe(true);
expect(controller.address.isActive).toBe(true);
});
});
});

View File

@ -1,11 +0,0 @@
{
"Street address": "Dirección postal",
"Default": "Predeterminado",
"Consignee": "Consignatario",
"Postcode": "Código postal",
"Town/City": "Ciudad",
"Province": "Provincia",
"Agency": "Agencia",
"Phone": "Teléfono",
"Mobile": "Móvil"
}

View File

@ -0,0 +1,9 @@
Street address: Dirección postal
Default: Predeterminado
Consignee: Consignatario
Postcode: Código postal
Town/City: Ciudad
Province: Provincia
Agency: Agencia
Phone: Teléfono
Mobile: Móvil

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/Addresses/{{edit.params.addressId}}" actions="$ctrl.address=edit.model" options="mgEdit"></mg-ajax>
<mg-ajax path="/client/api/Addresses/{{edit.params.addressId}}" actions="$ctrl.address=edit.model;$ctrl._setIconAdd();" options="mgEdit"></mg-ajax>
<vn-watcher
vn-id="watcher"
url="/client/api/Addresses"
@ -6,20 +6,20 @@
data="$ctrl.address"
form="form">
</vn-watcher>
<form name="form" ng-submit="watcher.submitBack()" pad-medium>
<form name="form" ng-submit="$ctrl.submit()" pad-medium>
<vn-card>
<vn-vertical pad-large>
<vn-title>Address</vn-title>
<vn-horizontal>
<vn-one>
<vn-check label="enabled" field="$ctrl.address.isEnabled"></vn-check>
<vn-check label="Enabled" field="$ctrl.address.isActive"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Is equalizated" field="$ctrl.address.isEqualizated"></vn-check>
<vn-check label="Is equalizated" field="$ctrl.address.isEqualizated" vn-acl="administrative"></vn-check>
</vn-one>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Consignee" field="$ctrl.address.consignee" vn-focus></vn-textfield>
<vn-textfield vn-one label="Consignee" field="$ctrl.address.nickname" vn-focus></vn-textfield>
<vn-textfield vn-one label="Street" field="$ctrl.address.street"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
@ -36,8 +36,8 @@
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
initial-data="$ctrl.address.defaultAgency"
field="$ctrl.address.defaultAgencyFk"
initial-data="$ctrl.address.agency"
field="$ctrl.address.agencyFk"
url="/client/api/AgencyModes"
show-field="name"
value-field="id"
@ -46,6 +46,46 @@
<vn-textfield vn-one label="Phone" field="$ctrl.address.phone"></vn-textfield>
<vn-textfield vn-one label="Mobile" field="$ctrl.address.mobile"></vn-textfield>
</vn-horizontal>
<vn-one margin-medium-top>
<vn-title>Notes</vn-title>
<mg-ajax path="/client/api/ObservationTypes" options="mgIndex as observationsTypes"></mg-ajax>
<vn-horizontal ng-repeat="observation in $ctrl.observations track by $index">
<vn-autocomplete
vn-one
initial-data = "observation.observationType"
field = "observation.observationTypeFk"
data = "observationsTypes.model"
show-field = "description"
label = "Observation type"
order = "description ASC"
filter-search="{where: {description: {regexp: 'search'}} }"
>
<tpl-item>{{$parent.$parent.item.description}}</tpl-item>
</vn-autocomplete>
<vn-textfield vn-three label="Description" model="observation.description"></vn-textfield>
<vn-one pad-medium-top>
<vn-icon
pointer
medium-grey
icon="remove_circle_outline"
ng-click="$ctrl.removeObservation($index)"
>
</vn-icon>
<vn-icon
pointer
margin-medium-left
orange
icon="add_circle"
ng-if = "observation.showAddIcon"
ng-click="$ctrl.addObservation()"
></vn-icon>
</vn-one>
</vn-horizontal>
</vn-one>
</vn-vertical>
</vn-card>
<vn-button-bar>

View File

@ -1,13 +1,137 @@
import ngModule from '../module';
export default class Controller {
constructor($state) {
constructor($state, $scope, $http, $q, $translate, vnApp) {
this.$state = $state;
this.$scope = $scope;
this.$http = $http;
this.$q = $q;
this.$translate = $translate;
this.vnApp = vnApp;
this.address = {
id: parseInt($state.params.addressId)
};
this.observations = [];
this.observationsDictionary = {};
this.observationsRemoved = [];
}
_setIconAdd() {
if (this.observations.length) {
this.observations.map(element => {
element.showAddIcon = false;
return true;
});
this.observations[this.observations.length - 1].showAddIcon = true;
} else {
this.addObservation();
}
}
_setDirtyForm() {
if (this.$scope.form) {
this.$scope.form.$setDirty();
}
}
_unsetDirtyForm() {
if (this.$scope.form) {
this.$scope.form.$setPristine();
}
}
addObservation() {
this.observations.push({observationTypeFk: null, addressFk: this.address.id, description: null, showAddIcon: true});
this._setIconAdd();
}
removeObservation(index) {
let item = this.observations[index];
if (item) {
this.observations.splice(index, 1);
this._setIconAdd();
if (item.id) {
this.observationsRemoved.push(item.id);
this._setDirtyForm();
}
}
}
_submitObservations(objectObservations) {
return this.$http.post(`/client/api/AddressObservations/crudAddressObservations`, objectObservations);
}
submit() {
this._unsetDirtyForm();
let submitWatcher = this.$scope.watcher.dataChanged();
let submitObservations;
let repeatedTypes = false;
let observationsObj = {
delete: this.observationsRemoved,
create: [],
update: []
};
for (let i = 0; i < this.observations.length; i++) {
let observation = this.observations[i];
// only one observation is allowed for each of its types
if (this.observationsDictionary[observation.observationTypeFk] !== undefined && // IF the dictionary contains the type
(
// AND (is a new Observation OR is old but with distinct Id) --> repeated
!observation.id || (observation.id && this.observationsDictionary[observation.observationTypeFk].id !== observation.id)
)
) {
repeatedTypes = true;
break;
}
if (!observation.id && observation.observationTypeFk && observation.description) {
observationsObj.create.push(observation);
} else if (observation.id && this.observationsDictionary[observation.observationTypeFk].description !== observation.description) {
observationsObj.update.push(observation);
}
this.observationsDictionary[observation.observationTypeFk] = observation;
}
submitObservations = observationsObj.update.length > 0 || observationsObj.create.length > 0 || observationsObj.delete.length > 0;
if (repeatedTypes) {
this.vnApp.showMessage(
this.$translate.instant('you can not repeat the types of observations')
);
} else if (submitWatcher && !submitObservations) {
this.$scope.watcher.submit().then(() => {
this.$state.go('clientCard.addresses.list', {id: this.$state.params.id});
});
} else if (!submitWatcher && submitObservations) {
this._submitObservations(observationsObj).then(() => {
this.$state.go('clientCard.addresses.list', {id: this.$state.params.id});
});
} else if (submitWatcher && submitObservations) {
this.$q.all([this.$scope.watcher.submit(), this._submitObservations(observationsObj)]).then(() => {
this.$state.go('clientCard.addresses.list', {id: this.$state.params.id});
});
} else {
this.vnApp.showMessage(
this.$translate.instant('No changes to save')
);
}
}
$onInit() {
let filter = {
where: {addressFk: this.address.id},
include: {relation: 'observationType'}
};
this.$http.get(`/client/api/AddressObservations?filter=${JSON.stringify(filter)}`).then(res => {
this.observations = res.data;
res.data.forEach(item => {
this.observationsDictionary[item.observationTypeFk] = Object.assign({}, item);
});
});
}
}
Controller.$inject = ['$state'];
Controller.$inject = ['$state', '$scope', '$http', '$q', '$translate', 'vnApp'];
ngModule.component('vnAddressEdit', {
template: require('./address-edit.html'),

View File

@ -5,20 +5,33 @@ describe('Client', () => {
let $componentController;
let $state;
let controller;
let $httpBackend;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, _$state_) => {
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
$componentController = _$componentController_;
$state = _$state_;
$state.params.addressId = '1234';
$httpBackend = _$httpBackend_;
$state.params.addressId = '1';
controller = $componentController('vnAddressEdit', {$state: $state});
}));
it('should define and set address property', () => {
expect(controller.address.id).toBe(1234);
expect(controller.address.id).toBe(1);
});
describe('$onInit()', () => {
it('should perform a GET query to receive the address observations', () => {
let filter = {where: {addressFk: 1}, include: {relation: 'observationType'}};
let res = ['some notes'];
$httpBackend.when('GET', `/client/api/AddressObservations?filter=${JSON.stringify(filter)}`).respond(res);
$httpBackend.expectGET(`/client/api/AddressObservations?filter=${JSON.stringify(filter)}`);
controller.$onInit();
$httpBackend.flush();
});
});
});
});

View File

@ -1,4 +0,0 @@
{
"Enabled": "Activo",
"Is equalizated": "Recargo de equivalencia"
}

View File

@ -0,0 +1,2 @@
Enabled: Activo
Is equalizated: Recargo de equivalencia

View File

@ -1,33 +1,39 @@
<mg-ajax path="/client/api/Clients/{{index.params.id}}/addressesList" options="mgIndex"></mg-ajax>
<mg-ajax path="/client/api/Clients/{{index.params.id}}/listAddresses" options="mgIndex"></mg-ajax>
<vn-vertical pad-medium>
<vn-card>
<vn-vertical pad-large>
<vn-horizontal>
<vn-title vn-one>Addresses</vn-title>
</vn-horizontal>
<vn-horizontal ng-repeat="i in index.model track by i.id" class="pad-medium-top" style="align-items: center;">
<vn-auto style="border-radius: .5em;" class="pad-small border-solid"
ng-class="{'bg-dark-item': i.isDefaultAddress,'bg-opacity-item': !i.isEnabled && !i.isDefaultAddress}">
<vn-horizontal ng-repeat="address in index.model.items track by address.id" class="pad-medium-top" style="align-items: center;">
<vn-one border-radius class="pad-small border-solid"
ng-class="{'bg-dark-item': address.isDefaultAddress,'bg-opacity-item': !address.isActive && !address.isDefaultAddress}">
<vn-horizontal style="align-items: center;">
<vn-none pad-medium-h style="color:#FFA410;">
<i class="material-icons" ng-if="i.isDefaultAddress">star</i>
<i class="material-icons pointer" ng-if="!i.isDefaultAddress&&i.isEnabled" vn-tooltip="Set as default" tooltip-position="left" ng-click="$ctrl.setDefault(i.id)">star_border</i>
<i class="material-icons" ng-if="address.isDefaultAddress">star</i>
<i class="material-icons pointer" ng-if="!address.isDefaultAddress" vn-tooltip="Set as default" tooltip-position="left" ng-click="$ctrl.setDefault(address.id)">star_border</i>
</vn-none>
<vn-auto>
<div><b>{{::i.consignee}}</b></div>
<div>{{::i.street}}</div>
<div>{{::i.city}}, {{::i.province}}</div>
<div>{{::i.phone}}, {{::i.mobile}}</div>
</vn-auto>
<a vn-empty ui-sref="clientCard.addresses.edit({addressId: {{i.id}}})">
<vn-one border-solid-right>
<div><b>{{::address.nickname}}</b></div>
<div>{{::address.street}}</div>
<div>{{::address.city}}, {{::address.province}}</div>
<div>{{::address.phone}}, {{::address.mobile}}</div>
</vn-one>
<vn-vertical vn-one pad-medium-h>
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'pad-small-top': $index}">
<b margin-medium-right>{{::observation.observationType.description}}:</b>
<span>{{::observation.description}}</span>
</vn-one>
</vn-vertical>
<a vn-auto ui-sref="clientCard.addresses.edit({addressId: {{address.id}}})">
<vn-icon-button icon="edit"></vn-icon-button>
</a>
</vn-horizontal>
</vn-auto>
</vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-paging index="index"></vn-paging>
<vn-paging index="index" total="index.model.total"></vn-paging>
<vn-float-button
fixed-bottom-right
ui-sref="clientCard.addresses.create"

View File

@ -6,9 +6,10 @@ class ClientAddresses {
this.$scope = $scope;
}
setDefault(id) {
this.$http.patch(`/client/api/Addresses/${id}`, {id: id, isDefaultAddress: true}).then(() => {
this.$scope.index.accept();
});
let params = {isDefaultAddress: true};
this.$http.patch(`/client/api/Addresses/${id}`, params).then(
() => this.$scope.index.accept()
);
}
}
ClientAddresses.$inject = ['$http', '$scope'];

View File

@ -1,4 +0,0 @@
{
"Addresses": "Consignatarios",
"Set as default": "Establecer como predeterminado"
}

View File

@ -0,0 +1 @@
Set as default: Establecer como predeterminado

View File

@ -19,8 +19,7 @@
<vn-textfield vn-one
label="Email"
field="$ctrl.client.email"
info="You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main"
>
info="You can save multiple emails">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
@ -30,10 +29,9 @@
url="/client/api/Clients/activeSalesPerson"
show-field="name"
value-field="id"
select-fields="surname"
select-fields="name"
label="Salesperson"
filter-search="{where: {or: [{name: {regexp: 'search'}}, {surname: {regexp: 'search'}}]}}"
>
filter-search="{where: {or: [{name: {regexp: 'search'}}, {name: {regexp: 'search'}}]}}">
</vn-autocomplete>
<vn-autocomplete vn-one
initial-data="$ctrl.client.contactChannel"

View File

@ -1,14 +0,0 @@
{
"Basic data": "Datos básicos",
"Comercial Name": "Nombre comercial",
"Tax number": "NIF/CIF",
"Social name": "Razón social",
"Phone": "Teléfono",
"Mobile": "Móvil",
"Fax": "Fax",
"Email": "Correo electrónico",
"Salesperson": "Comercial",
"Channel": "Canal",
"You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main": "Puede guardar varios correos electrónicos encadenándolos mediante comas sin espacios, ejemplo: user@dominio.com,user2@dominio.com siendo el primer correo electrónico el principal",
"Contact": "Contacto"
}

View File

@ -0,0 +1,14 @@
Comercial Name: Nombre comercial
Tax number: NIF/CIF
Social name: Razón social
Phone: Teléfono
Mobile: Móvil
Fax: Fax
Email: Correo electrónico
Salesperson: Comercial
Channel: Canal
You can save multiple emails: >-
Puede guardar varios correos electrónicos encadenándolos mediante comas
sin espacios, ejemplo: user@dominio.com, user2@dominio.com siendo el primer
correo electrónico el principal
Contact: Contacto

View File

@ -19,28 +19,24 @@
label="Pay method">
</vn-autocomplete>
<vn-textfield vn-two label="IBAN" field="$ctrl.client.iban" vn-acl="administrative"></vn-textfield>
<vn-textfield vn-one label="Vencimiento" field="$ctrl.client.dueDay" vn-acl="administrative"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Crédito" field="$ctrl.client.credit" vn-acl="administrative"></vn-textfield>
<vn-textfield vn-one label="Crédito asegurado" field="$ctrl.client.creditInsurance" vn-acl="administrative"></vn-textfield>
<vn-textfield vn-one label="Due day" field="$ctrl.client.dueDay" vn-acl="administrative"></vn-textfield>
</vn-horizontal>
<vn-horizontal margin-medium-bottom>
<vn-one>
<vn-check label="Recibido core VNH" field="$ctrl.client.coreVnh" vn-acl="administrative"></vn-check>
<vn-check label="Received core VNH" field="$ctrl.client.hasCoreVnh" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Recibido core VNL" field="$ctrl.client.coreVnl" vn-acl="administrative"></vn-check>
<vn-check label="Received core VNL" field="$ctrl.client.hasCoreVnl" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Recibido B2B VNL" field="$ctrl.client.sepaVnl" vn-acl="administrative"></vn-check>
<vn-check label="Received B2B VNL" field="$ctrl.client.hasSepaVnl" vn-acl="administrative"></vn-check>
</vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Guardar" vn-acl="administrative"></vn-submit>
<vn-submit label="Save" vn-acl="administrative"></vn-submit>
</vn-button-bar>
</form>
<vn-dialog

View File

@ -17,9 +17,6 @@ export default class Controller {
this.billData.payMethodFk = this.client.payMethodFk;
this.billData.iban = this.client.iban;
this.billData.dueDay = this.client.dueDay;
this.billData.discount = this.client.discount;
this.billData.credit = this.client.credit;
this.billData.creditInsurance = this.client.creditInsurance;
}
}
submit() {
@ -43,10 +40,8 @@ export default class Controller {
}
returnDialog(response) {
if (response === 'ACCEPT') {
this.$http.post(`/mailer/manuscript/payment-update/${this.client.id}`).then(
() => {
this.vnApp.showMessage(this.translate.instant('Notification sent!'));
}
this.$http.post(`/mailer/notification/payment-update/${this.client.id}`).then(
() => this.vnApp.showMessage(this.translate.instant('Notification sent!'))
);
}
}

View File

@ -14,6 +14,7 @@ describe('Client', () => {
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$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};
@ -25,9 +26,6 @@ describe('Client', () => {
describe('copyData()', () => {
it(`should define billData using client's data`, () => {
controller.client = {
credit: 1000000000000,
creditInsurance: null,
discount: 99,
dueDay: 0,
iban: null,
payMethodFk: 1
@ -72,8 +70,8 @@ describe('Client', () => {
describe('returnDialog()', () => {
it('should request to send notification email', () => {
controller.client = {id: '123'};
$httpBackend.when('POST', `/mailer/manuscript/payment-update/${controller.client.id}`).respond('done');
$httpBackend.expectPOST(`/mailer/manuscript/payment-update/${controller.client.id}`);
$httpBackend.when('POST', `/mailer/notification/payment-update/${controller.client.id}`).respond('done');
$httpBackend.expectPOST(`/mailer/notification/payment-update/${controller.client.id}`);
controller.returnDialog('ACCEPT');
$httpBackend.flush();
});

View File

@ -1,14 +0,0 @@
{
"Changed terms": "Has modificado las condiciones de pago",
"Notify customer?" : "¿Deseas notificar al cliente de dichos cambios?",
"No": "No",
"Yes, notify": "Sí, notificar",
"Notification sent!": "¡Notificación enviada!",
"Notification error": "Error al enviar notificación",
"You changes the equivalent tax": "Has cambiado el recargo de equivalencia",
"Do you want to spread the change to their consignees?" : "¿Deseas propagar el cambio a sus consignatarios?",
"Yes, propagate": "Si, propagar",
"Equivalent tax spreaded": "Recargo de equivalencia propagado",
"Invoice by address": "Facturar por consignatario",
"Equalization tax": "Recargo de equivalencia"
}

View File

@ -0,0 +1,17 @@
Changed terms: Has modificado las condiciones de pago
Notify customer?: ¿Deseas notificar al cliente de dichos cambios?
No: No
Yes, notify: Sí, notificar
Notification sent!: ¡Notificación enviada!
Notification error: Error al enviar notificación
You changes the equivalent tax: Has cambiado el recargo de equivalencia
Do you want to spread the change to their consignees?: ¿Deseas propagar el cambio a sus consignatarios?
Yes, propagate: Si, propagar
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 core VNL: Recibido core VNL
Received B2B VNL: Recibido B2B VNL
SAVE: GUARDAR

View File

@ -1,18 +1,16 @@
<vn-horizontal>
<vn-main-block>
<mg-ajax
path="/client/api/Clients/{{edit.params.id}}/card"
actions="$ctrl.client = edit.model"
options="mgEdit">
</mg-ajax>
<vn-empty style="min-width: 18em; padding-left: 1em; padding-bottom: 1em;">
<vn-descriptor
client="$ctrl.client"
active="$ctrl.client.active"
class="display-block" >
</vn-descriptor>
<vn-left-menu></vn-left-menu>
</vn-empty>
<vn-auto>
<vn-vertical style="max-width: 70em; margin: 0 auto;" ui-view></vn-vertical>
</vn-auto>
</vn-horizontal>
<vn-horizontal>
<vn-auto class="left-block">
<vn-descriptor client="$ctrl.client"></vn-descriptor>
<vn-left-menu></vn-left-menu>
</vn-auto>
<vn-one>
<vn-vertical ui-view></vn-vertical>
</vn-one>
</vn-horizontal>
</vn-main-block>

View File

@ -1,5 +1,4 @@
import ngModule from '../module';
import './style.css';
export default class Controller {
constructor() {

View File

@ -1,3 +0,0 @@
vn-descriptor {
font-family: raleway-bold;
}

View File

@ -14,3 +14,8 @@ import './address-edit/address-edit';
import './notes/notes';
import './note-create/note-create';
import './web-access/web-access';
import './credit-list/credit-list';
import './credit-create/credit-create';
import './greuge-list/greuge-list';
import './greuge-create/greuge-create';
import './mandate/mandate';

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/Clients/createUserProfile" options="vnPost"></mg-ajax>
<mg-ajax path="/client/api/Clients/createWithUser" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.client"
@ -19,14 +19,21 @@
<vn-textfield vn-one label="User name" field="$ctrl.client.userName"></vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one label="Email" field="$ctrl.client.email" info="You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main"></vn-textfield>
<vn-one></vn-one>
<vn-textfield vn-one label="Email" field="$ctrl.client.email" info="You can save multiple emails"></vn-textfield>
<vn-autocomplete vn-one
field="$ctrl.client.salesPersonFk"
url="/client/api/Clients/activeSalesPerson"
show-field="name"
value-field="id"
select-fields="name"
label="Salesperson"
filter-search="{where: {or: [{name: {regexp: 'search'}}, {name: {regexp: 'search'}}]}}">
</vn-autocomplete>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Create and edit"></vn-submit>
<vn-button label="Create" ng-click="watcher.submitBack()"></vn-button>
<vn-submit label="Create"></vn-submit>
</vn-button-bar>
</div>
</form>

View File

@ -1,10 +0,0 @@
{
"Name": "Nombre",
"Tax number": "NIF/CIF",
"Business name": "Razón social",
"User name": "Nombre de usuario",
"Email": "Correo electrónico",
"Create and edit": "Crear y editar",
"Create": "Crear",
"You can save multiple emails by chaining them using comma without spaces, example: user@domain.com,user2@domain.com the first email will be considered as the main": "Puede guardar varios correos electrónicos encadenándolos mediante comas sin espacios, ejemplo: user@dominio.com,user2@dominio.com siendo el primer correo electrónico el principal"
}

View File

@ -0,0 +1,11 @@
Name: Nombre
Tax number: NIF/CIF
Business name: Razón social
User name: Nombre de usuario
Email: Correo electrónico
Create and edit: Crear y editar
Create: Crear
You can save multiple emails: >-
Puede guardar varios correos electrónicos encadenándolos mediante comas
sin espacios, ejemplo: user@dominio.com, user2@dominio.com siendo el primer
correo electrónico el principal

View File

@ -0,0 +1,23 @@
<mg-ajax path="/client/api/Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.client"
form="form"
save="patch">
</vn-watcher>
<form name="form" ng-submit="watcher.submitGo('clientCard.credit.list')" pad-medium>
<vn-card >
<vn-vertical pad-large>
<vn-title>Add credit</vn-title>
<vn-horizontal>
<vn-textfield vn-one label="Credit" field="$ctrl.client.credit" type="number" vn-focus></vn-textfield>
<vn-one></vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,8 @@
import ngModule from '../module';
ngModule.component('vnClientCreditCreate', {
template: require('./credit-create.html'),
bindings: {
client: '<'
}
});

View File

@ -0,0 +1 @@
Add credit: Añadir crédito

View File

@ -0,0 +1,27 @@
<mg-ajax path="/client/api/ClientCredits/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-card pad-medium>
<vn-vertical pad-large>
<vn-title vn-one margin-large-bottom>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-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
</vn-vertical>
</vn-card>
<a ui-sref="clientCard.credit.create" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,9 @@
import ngModule from '../module';
import FilterClientList from '../filterClientList';
class ClientCreditList extends FilterClientList {}
ngModule.component('vnClientCreditList', {
template: require('./credit-list.html'),
controller: ClientCreditList
});

View File

@ -0,0 +1,3 @@
Since : Desde
Employee : Empleado
No results: Sin resultados

View File

@ -1,15 +1,18 @@
<vn-card>
<vn-vertical class="margin-medium" pad-medium-top pad-medium-bottom>
<vn-horizontal>
<a vn-one ui-sref="clients">
<i class="material-icons descriptor-icon">person</i>
</a>
<vn-vertical vn-two>
<div class="margin-none">{{::$ctrl.client.id}}</div>
<div class="margin-none">{{$ctrl.client.name}}</div>
<div class="margin-none">{{$ctrl.client.phone}}</div>
<vn-switch label="Activo" model="$ctrl.active"></vn-switch>
<vn-card margin-medium-v>
<vn-vertical>
<vn-auto class="descriptor-header pointer" ui-sref="clients">
<i class="material-icons">person</i>
</vn-auto>
<vn-auto pad-medium>
<vn-vertical>
<vn-horizontal ng-repeat="(field, title) in $ctrl.fieldsToShow">
<strong vn-auto>{{::title}}:</strong>
<vn-auto margin-small-left>
<span ng-if="field.includes('credit')">{{$ctrl.client[field] || 0 | number:2}} €</span>
<span ng-if="!field.includes('credit')">{{$ctrl.client[field]}}</span>
</vn-auto>
</vn-horizontal>
</vn-vertical>
</vn-horizontal>
</vn-auto>
</vn-vertical>
</vn-card>

View File

@ -1,26 +1,32 @@
import ngModule from '../module';
export default class Controller {
constructor($scope, $http) {
this.$http = $http;
constructor($translate) {
this.$translate = $translate;
// CLient fields to display
this.fields = ['id', 'name', 'phone', 'credit', 'creditInsurance'];
this.fieldsToShow = {};
}
set active(value) {
if (this._active !== value && this._active !== undefined)
this.$http.put(`/client/api/Clients/${this.client.id}/activate`);
this._active = value;
// concat 2 Arrays without duplicates
_concatFields(a, b) {
return a.concat(b.filter(item => a.indexOf(item) < 0));
}
get active() {
return this._active;
$onInit() {
let fields = (this.moreFields && this.moreFields instanceof Array) ? this._concatFields(this.fields, this.moreFields) : this.fields;
fields.forEach(field => {
this.fieldsToShow[field] = this.$translate.instant(field);
});
}
}
Controller.$inject = ['$scope', '$http'];
Controller.$inject = ['$translate'];
ngModule.component('vnDescriptor', {
template: require('./descriptor.html'),
controller: Controller,
bindings: {
client: '<',
active: '<'
moreFields: '<?'
}
});

View File

@ -3,30 +3,32 @@ import './descriptor.js';
describe('Client', () => {
describe('Component vnDescriptor', () => {
let $componentController;
let $scope;
let $translate;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope) => {
beforeEach(angular.mock.inject((_$componentController_, _$translate_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
controller = $componentController('vnDescriptor', {$scope: $scope});
$translate = _$translate_;
controller = $componentController('vnDescriptor', {$translate: $translate});
}));
describe('set active', () => {
it('should check if active is defined and diferent from the new value', () => {
controller.client = {id: 1};
describe('onInit()', () => {
it('should create Object with basic fields', () => {
controller.client = {
id: 1,
name: "Peter Parker",
phone: null,
mobile: "666666",
credit: 300,
creditInsurance: null
};
controller.$onInit();
expect(controller._active).toBe(undefined);
controller.active = false;
expect(controller._active).toBe(false);
controller.active = true;
expect(controller._active).toBe(true);
expect(controller.fieldsToShow.id).toBe('id');
});
});
});

View File

@ -0,0 +1,40 @@
// Generic object to list models, related to the client, with mgCrud
export default class FilterClientList {
constructor($scope, $timeout, $state) {
this.$ = $scope;
this.$timeout = $timeout;
this.$state = $state;
this.waitingMgCrud = 0;
this.clientFk = $state.params.id;
}
onOrder(field, order) {
this.filter(`${field} ${order}`);
}
filter(order) {
if (this.$.index && this.clientFk) {
this.waitingMgCrud = 0;
this.$.index.filter = {
page: 1,
size: 10,
clientFk: this.clientFk
};
if (order) {
this.$.index.filter.order = order;
}
this.$.index.accept();
} else if (!this.clientFk) {
throw new Error('Error: ClientFk not found');
} else if (this.waitingMgCrud > 3) {
throw new Error('Error: Magic Crud is not loaded');
} else {
this.waitingMgCrud++;
this.$timeout(() => {
this.filter(order);
}, 250);
}
}
}
FilterClientList.$inject = ['$scope', '$timeout', '$state'];

View File

@ -10,8 +10,9 @@
<vn-vertical pad-large>
<vn-title>Fiscal data</vn-title>
<vn-horizontal>
<vn-textfield autofocus vn-two label="Social name" field="$ctrl.client.socialName" vn-acl="administrative"></vn-textfield>
<vn-textfield vn-one label="Tax number" field="$ctrl.client.fi" vn-acl="administrative"></vn-textfield>
<vn-textfield autofocus vn-two label="Social name" field="$ctrl.client.socialName" vn-acl="administrative"></vn-textfield>
<vn-textfield vn-one label="Tax number" field="$ctrl.client.fi" vn-acl="administrative"></vn-textfield>
<vn-check vn-one label="Is equalizated" field="$ctrl.client.isEqualizated" vn-acl="administrative"></vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-two label="Street" field="$ctrl.client.street" vn-focus vn-acl="administrative"></vn-textfield>
@ -26,57 +27,53 @@
show-field="name"
value-field="id"
label="Province"
vn-acl="administrative"
>
vn-acl="administrative">
</vn-autocomplete>
<vn-autocomplete vn-one
initial-data="$ctrl.client.country"
field="$ctrl.client.countryFk"
url="/client/api/Countries"
show-field="name"
show-field="country"
value-field="id"
label="Country"
vn-acl="administrative"
>
vn-acl="administrative">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal margin-small-bottom>
<vn-one>
<vn-check label="Equalization tax" field="$ctrl.client.equalizationTax" vn-acl="administrative" vn-acl="administrative"></vn-check>
<vn-check label="Active" field="$ctrl.client.isActive" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Invoice by address" field="$ctrl.client.hasToInvoiceByAddress" vn-acl="administrative" vn-acl="administrative"></vn-check>
<vn-check label="Invoice by address" field="$ctrl.client.hasToInvoiceByAddress" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Verified data" field="$ctrl.client.isTaxDataChecked" vn-acl="administrative"></vn-check>
</vn-one>
</vn-horizontal>
<vn-horizontal>
<vn-one>
<vn-check label="Has to invoice" field="$ctrl.client.hasToInvoice" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check vn-one label="Invoice by mail" field="$ctrl.client.isToBeMailed" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check vn-one label="Vies" field="$ctrl.client.isVies" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one></vn-one>
</vn-horizontal>
<vn-horizontal>
<vn-one>
<vn-check label="Has to invoice" field="$ctrl.client.hasToInvoice" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Invoice by mail" field="$ctrl.client.invoiceByEmail" vn-acl="administrative"></vn-check>
</vn-one>
<vn-one>
<vn-check label="Vies" field="$ctrl.client.vies" vn-acl="administrative" vn-acl="administrative"></vn-check>
</vn-one>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-submit label="Save" vn-acl="administrative"></vn-submit>
</vn-button-bar>
</form>
<vn-dialog
vn-id="propagate-equalizationTax"
on-response="$ctrl.returnDialogEt(response)"
>
vn-id="propagate-isEqualizated"
on-response="$ctrl.returnDialogEt(response)">
<tpl-body>
<vn-vertical>
<vn-one text-center translate>You changes the equivalent tax</vn-one>
<vn-one text-center translate>Do you want to spread the change to their consignees?</vn-one>
<vn-one text-center translate>You changes the equivalen
<vn-one text-center translate>Do you want to spread the change to their consig
</vn-vertical>
</tpl-body>
<tpl-buttons>

View File

@ -6,7 +6,7 @@ export default class ClientFiscalData {
this.$http = $http;
this.vnApp = vnApp;
this.translate = $translate;
this.equalizationTax = undefined;
this.isEqualizated = undefined;
this.copyData();
}
@ -16,7 +16,7 @@ export default class ClientFiscalData {
copyData() {
if (this.client) {
this.equalizationTax = this.client.equalizationTax;
this.isEqualizated = this.client.isEqualizated;
}
}
@ -26,16 +26,16 @@ export default class ClientFiscalData {
}
checkEtChanges() {
let equals = this.equalizationTax == this.client.equalizationTax;
this.equalizationTax = this.client.equalizationTax;
let equals = this.isEqualizated == this.client.isEqualizated;
this.isEqualizated = this.client.isEqualizated;
if (!equals)
this.$.propagateEqualizationTax.show();
this.$.propagateIsEqualizated.show();
}
returnDialogEt(response) {
if (response === 'ACCEPT') {
this.$http.patch(`/client/api/Clients/${this.client.id}/addressesPropagateRe`, {isEqualizated: this.client.equalizationTax}).then(
this.$http.patch(`/client/api/Clients/${this.client.id}/addressesPropagateRe`, {isEqualizated: this.client.isEqualizated}).then(
res => {
if (res.data)
this.vnApp.showMessage(this.translate.instant('Equivalent tax spreaded'));

View File

@ -0,0 +1,31 @@
import './fiscal-data.js';
describe('Client', () => {
describe('Component vnClientFiscalData', () => {
let $componentController;
let $httpBackend;
let $scope;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$httpBackend_) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
controller = $componentController('vnClientFiscalData', {$scope: $scope});
}));
describe('returnDialogEt()', () => {
it('should request to patch the propagation of tax status', () => {
controller.client = {id: 123, isEqualizated: false};
$httpBackend.when('PATCH', `/client/api/Clients/${controller.client.id}/addressesPropagateRe`, {isEqualizated: controller.client.isEqualizated}).respond('done');
$httpBackend.expectPATCH(`/client/api/Clients/${controller.client.id}/addressesPropagateRe`, {isEqualizated: controller.client.isEqualizated});
controller.returnDialogEt('ACCEPT');
$httpBackend.flush();
});
});
});
});

View File

@ -0,0 +1,36 @@
<mg-ajax path="/client/api/greuges" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.greuge"
form="form"
save="post">
</vn-watcher>
<form pad-medium name="form" ng-submit="$ctrl.onSubmit()">
<vn-card>
<vn-vertical pad-medium>
<vn-title vn-one margin-large-bottom>Add Greuge</vn-title>
<vn-horizontal>
<vn-textfield vn-one margin-medium-right label="Amount" field="$ctrl.greuge.amount" type="number" step="1" vn-focus></vn-textfield>
<vn-date-picker vn-one
label="Date"
model="$ctrl.greuge.shipped"
ini-options="{enableTime: true, dateFormat: 'd-m-Y h:i', time_24hr: true}"
>
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one margin-medium-right label="Comment" field="$ctrl.greuge.description"></vn-textfield>
<vn-autocomplete vn-one
field="$ctrl.greuge.greugeTypeFk"
url="/client/api/greugeTypes"
label="Type"
>
</vn-autocomplete>
</vn-horizontal>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,25 @@
import ngModule from '../module';
class ClientGreugeCreate {
constructor($scope, $state, $filter) {
this.$ = $scope;
this.$state = $state;
this.greuge = {
shipped: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm')
};
}
onSubmit() {
this.greuge.clientFk = this.$state.params.id;
this.$.watcher.submit().then(
() => {
this.$state.go('clientCard.greuge.list');
}
);
}
}
ClientGreugeCreate.$inject = ['$scope', '$state', '$filter'];
ngModule.component('vnClientGreugeCreate', {
template: require('./greuge-create.html'),
controller: ClientGreugeCreate
});

View File

@ -0,0 +1,39 @@
import './greuge-create.js';
describe('Client', () => {
describe('Component vnClientGreugeCreate', () => {
let $componentController;
let $scope;
let $state;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$state = _$state_;
$scope.watcher = {
submit: () => {
return {
then: callback => {
callback();
}
};
}
};
controller = $componentController('vnClientGreugeCreate', {$scope: $scope});
}));
describe('onSubmit()', () => {
it('should call the function go() on $state to go to the greuges list', () => {
spyOn($state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('clientCard.greuge.list');
});
});
});
});

View File

@ -0,0 +1,36 @@
<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-card pad-medium>
<vn-vertical pad-medium>
<vn-title vn-one margin-large-bottom>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-paging margin-large-top vn-one index="index" total="index.model.count"></vn-paging>
</vn-vertical>
</vn-card>
<a ui-sref="clientCard.greuge.create" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,9 @@
import ngModule from '../module';
import FilterClientList from '../filterClientList';
class ClientGreugeList extends FilterClientList {}
ngModule.component('vnClientGreugeList', {
template: require('./greuge-list.html'),
controller: ClientGreugeList
});

View File

@ -0,0 +1,5 @@
Date: Fecha
Comment: Comentario
Amount: Importe
Type: Tipo
Add Greuge: Añadir Greuge

View File

@ -3,12 +3,13 @@
<div style="max-width: 40em; margin: 0 auto;">
<vn-card>
<vn-horizontal pad-medium>
<vn-searchbar vn-auto
<vn-searchbar vn-one
index="index"
on-search="$ctrl.search(index)"
advanced="true"
search="$ctrl.model.search"
popover="vn-client-search-panel">
popover="vn-client-search-panel"
ignore-keys = "['page', 'size', 'search']"
>
</vn-searchbar>
</vn-horizontal>
</vn-card>

View File

@ -7,7 +7,6 @@ export default class Controller {
this.model = {};
}
search(index) {
index.filter.search = this.model.search;
index.accept();
}
}

View File

@ -18,19 +18,19 @@ describe('Client', () => {
expect(controller.model).toEqual({});
});
describe('search()', () => {
it(`should set model's search to the search input`, () => {
controller.model.search = 'batman';
let index = {
filter: {},
accept: () => {
return 'accepted';
}
};
controller.search(index);
// describe('search()', () => {
// it(`should set model's search to the search input`, () => {
// controller.model.search = 'batman';
// let index = {
// filter: {},
// accept: () => {
// return 'accepted';
// }
// };
// controller.search(index);
expect(index.filter.search).toBe('batman');
});
});
// expect(index.filter.search).toBe('batman');
// });
// });
});
});

View File

@ -1,7 +1,7 @@
<a ui-sref="clientCard.basicData({ id: {{$ctrl.client.id}} })" pad-medium border-solid-bottom>
<div class="vn-item-client-name">{{$ctrl.client.name}}</div>
<div><span translate>Client id</span>: <b>{{$ctrl.client.id}}</b></div>
<div><span translate>Phone</span>: <b>{{$ctrl.client.phone | phone}}</b></div>
<div><span translate>Town/City</span>: <b>{{$ctrl.client.city}}</b></div>
<div><span translate>Email</span>: <b>{{$ctrl.client.email}}</b></div>
<a ui-sref="clientCard.basicData({ id: {{::$ctrl.client.id}} })" pad-medium border-solid-bottom>
<div class="vn-item-client-name">{{::$ctrl.client.name}}</div>
<div><span translate>Client id</span>: <b>{{::$ctrl.client.id}}</b></div>
<div><span translate>Phone</span>: <b>{{::$ctrl.client.phone | phone}}</b></div>
<div><span translate>Town/City</span>: <b>{{::$ctrl.client.city}}</b></div>
<div><span translate>Email</span>: <b>{{::$ctrl.client.email}}</b></div>
</a>

View File

@ -1,7 +0,0 @@
{
"Client id": "Id cliente",
"Phone": "Teléfono",
"Town/City": "Ciudad",
"Email": "Correo electrónico",
"Create client": "Crear cliente"
}

View File

@ -0,0 +1,5 @@
Client id: Id cliente
Phone: Teléfono
Town/City: Ciudad
Email: Correo electrónico
Create client: Crear cliente

View File

@ -12,5 +12,5 @@ vn-item-client a:hover {
}
.vn-item-client-name {
font-family: raleway-bold;
font-family: vn-font-bold;
}

View File

@ -1,4 +0,0 @@
{
"Client": "Client",
"Clients": "Clients"
}

View File

@ -0,0 +1,2 @@
Client: Client
Clients: Clients

View File

@ -1,14 +0,0 @@
{
"Client": "Cliente",
"Clients": "Clientes",
"Fiscal data": "Datos Fiscales",
"Has to invoice": "Factura",
"Invoice by mail": "Factura impresa",
"Country": "País",
"Street": "Domicilio fiscal",
"City": "Municipio",
"Postcode": "Código postal",
"Province": "Provincia",
"Save": "Guardar",
"Pay method" : "Forma de pago"
}

View File

@ -0,0 +1,23 @@
Active: Activo
Client: Cliente
Clients: Clientes
Basic data: Datos básicos
Fiscal data: Datos Fiscales
Addresses: Consignatarios
Web access: Acceso web
Notes: Notas
Has to invoice: Factura
Invoice by mail: Factura impresa
Country: País
Street: Domicilio fiscal
City: Municipio
Postcode: Código postal
Province: Provincia
Save: Guardar
Pay method : Forma de pago
Address: Consignatario
Credit : Crédito
Secured credit: Crédito asegurado
Verified data: Datos comprobados
Mandate: Mandato
Amount: Importe

View File

@ -0,0 +1,3 @@
Company: Empresa
Register date: Fecha alta
End date: Fecha baja

View File

@ -0,0 +1,31 @@
<mg-ajax path="/client/api/Mandates/filter" options="vnIndexNonAuto"></mg-ajax>
<vn-card pad-medium>
<vn-vertical pad-medium>
<vn-title vn-one margin-large-bottom>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-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
</vn-vertical>
</vn-card>

View File

@ -0,0 +1,7 @@
import ngModule from '../module';
import FilterClientList from '../filterClientList';
ngModule.component('vnClientMandate', {
template: require('./mandate.html'),
controller: FilterClientList
});

View File

@ -1,4 +0,0 @@
{
"New note": "Nueva nota",
"Note": "Nota"
}

View File

@ -0,0 +1,2 @@
New note: Nueva nota
Note: Nota

View File

@ -1,3 +0,0 @@
{
"Notes": "Notas"
}

View File

@ -1,13 +1,19 @@
<vn-card ng-show="$ctrl.observations.length" pad-medium>
<vn-vertical pad-large>
<vn-title>Notes</vn-title>
<vn-horizontal ng-repeat="n in $ctrl.observations" margin-small-bottom style="align-items: center;">
<vn-auto style="border-radius: .3em;" class="pad-small border-solid">
<div class="notes-date">{{::n.created | date:'dd/MM/yyyy HH:mm'}}</div>
<div class="notes-date">{{::n.employee.name}}</div>
<div>{{::n.text}}</div>
</vn-auto>
</vn-horizontal>
<vn-one
ng-repeat="n in $ctrl.observations"
pad-small border-solid
border-radius
margin-small-bottom style="align-items: center;">
<vn-horizontal>
<vn-one >{{::n.worker.firstName}} {{::n.worker.name}}</vn-one>
<vn-auto>{{::n.created | date:'dd/MM/yyyy HH:mm'}}</vn-auto>
</vn-horizontal>
<vn-horizontal>
<b>{{::n.text}}</b>
</vn-horizontal>
</vn-one>
</vn-vertical>
</vn-card>
<vn-float-button

View File

@ -15,7 +15,8 @@ describe('Client', () => {
$componentController = _$componentController_;
$state = _$state_;
$httpBackend = _$httpBackend_;
controller = $componentController('vnClientNotes', {$httpBackend: $httpBackend, $state: $state});
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
controller = $componentController('vnClientNotes', {$state: $state});
}));
describe('$onChanges()', () => {

View File

@ -1,3 +0,0 @@
.notes-date {
font-family: raleway-bold;
}

View File

@ -1,10 +0,0 @@
{
"Client id": "Id cliente",
"Tax number": "NIF/CIF",
"Name": "Nombre",
"Social name": "Razon social",
"Town/City": "Ciudad",
"Postcode": "Código postal",
"Email": "Correo electrónico",
"Phone": "Teléfono"
}

View File

@ -0,0 +1,8 @@
Client id: Id cliente
Tax number: NIF/CIF
Name: Nombre
Social name: Razon social
Town/City: Ciudad
Postcode: Código postal
Email: Correo electrónico
Phone: Teléfono

View File

@ -1,28 +1,16 @@
import ngModule from '../module';
export default class Controller {
constructor(sessionStorage) {
this.sessionStorage = sessionStorage;
constructor() {
// onSubmit() is defined by @vnSearchbar
this.onSubmit = () => {};
}
onSearch() {
this.setStorageValue();
this.onSubmit(this.filter);
}
$onChanges() {
var value = this.sessionStorage.get('filter');
if (value !== undefined)
this.filter = value;
}
setStorageValue() {
this.sessionStorage.set('filter', this.filter);
}
}
Controller.$inject = ['sessionStorage'];
Controller.$inject = [];
ngModule.component('vnClientSearchPanel', {
template: require('./search-panel.html'),

View File

@ -1,41 +0,0 @@
import './search-panel.js';
describe('Client', () => {
describe('Component vnClientSearchPanel', () => {
let $componentController;
let sessionStorage;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, _sessionStorage_) => {
$componentController = _$componentController_;
sessionStorage = _sessionStorage_;
controller = $componentController('vnClientSearchPanel', {sessionStorage: sessionStorage});
}));
describe('onSearch()', () => {
it('should call setStorageValue() and onSubmit()', () => {
spyOn(controller, 'setStorageValue');
spyOn(controller, 'onSubmit');
controller.setStorageValue();
controller.onSubmit();
expect(controller.setStorageValue).toHaveBeenCalledWith();
expect(controller.onSubmit).toHaveBeenCalledWith();
});
});
describe('$onChanges()', () => {
it('should set filter properties using the search values', () => {
expect(controller.filter).not.toBeDefined();
spyOn(sessionStorage, 'get').and.returnValue({data: 'data'});
controller.$onChanges();
expect(controller.filter).toBe(sessionStorage.get({data: 'data'}));
});
});
});
});

View File

@ -1,8 +0,0 @@
{
"User": "Usuario",
"Enable web access": "Habilitar acceso web",
"Web access": "Acceso web",
"New password": "Nueva contraseña",
"Repeat password": "Repetir contraseña",
"Change password": "Cambiar contraseña"
}

View File

@ -0,0 +1,5 @@
User: Usuario
Enable web access: Habilitar acceso web
New password: Nueva contraseña
Repeat password: Repetir contraseña
Change password: Cambiar contraseña

View File

@ -16,7 +16,7 @@ export default class Controller {
isCustomer() {
if (this.client && this.client.id) {
this.$http.get(`/client/api/Clients/${this.client.id}/getRoleCustomer`).then(res => {
this.$http.get(`/client/api/Clients/${this.client.id}/hasCustomerRole`).then(res => {
this.canChangePassword = (res.data) ? res.data.isCustomer : false;
});
} else {

View File

@ -15,6 +15,7 @@ describe('Component VnClientWebAccess', () => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
vnApp = _vnApp_;
spyOn(vnApp, 'showError');
controller = $componentController('vnClientWebAccess', {$scope: $scope});
@ -37,8 +38,8 @@ describe('Component VnClientWebAccess', () => {
controller.client = {id: '1234'};
controller.isCustomer();
$httpBackend.when('GET', `/client/api/Clients/${controller.client.id}/getRoleCustomer`).respond('ok');
$httpBackend.expectGET(`/client/api/Clients/${controller.client.id}/getRoleCustomer`);
$httpBackend.when('GET', `/client/api/Clients/${controller.client.id}/hasCustomerRole`).respond('ok');
$httpBackend.expectGET(`/client/api/Clients/${controller.client.id}/hasCustomerRole`);
$httpBackend.flush();
});
});

View File

@ -1,10 +0,0 @@
{
"name": "@salix/core",
"version": "0.0.0",
"description": "",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://git.verdnatura.es/salix"
}
}

View File

@ -1,6 +1,12 @@
<vn-vertical ng-click="$ctrl.showDropDown = true">
<vn-textfield vn-one label="{{$ctrl.label}}" model="$ctrl.displayValue" readonly="$ctrl.readonly"></vn-textfield>
<vn-drop-down vn-one
<vn-vertical ng-click="$ctrl.showDropDown = true" tabindex="0">
<vn-textfield vn-auto
label="{{$ctrl.label}}"
model="$ctrl.displayValue"
readonly="$ctrl.readonly"
tab-index="-1"
>
</vn-textfield>
<vn-drop-down vn-auto
items="$ctrl.items"
show="$ctrl.showDropDown"
selected="$ctrl.field"
@ -11,5 +17,6 @@
filter-action="$ctrl.findItems(search)"
item-width="$ctrl.width"
multiple="$ctrl.multiple"
><vn-item ng-transclude="tplItem">{{$parent.item.name}}</vn-item></vn-drop-down>
parent = "$ctrl.element"
><vn-item ng-transclude="tplItem">{{$parent.item[$ctrl.showField]}}</vn-item></vn-drop-down>
</vn-vertical>

View File

@ -19,14 +19,15 @@ class Autocomplete extends Component {
this._field = null;
this._preLoad = false;
this.maxRow = 10;
this.showField = this.showField || 'name';
this.valueField = this.valueField || 'id';
this.order = this.order || 'name ASC';
this.showField = 'name';
this.valueField = 'id';
this.items = copyObject(this.data) || [];
this.displayValueMultiCheck = [];
this._multiField = [];
this.readonly = true;
this.removeLoadMore = false;
this.form = null;
this.findForm = false;
}
get showDropDown() {
@ -87,6 +88,7 @@ class Autocomplete extends Component {
if (this.multiple) {
this.setMultiField(value[this.valueField]);
}
this.setDirtyForm();
} else {
this.setValue(value);
}
@ -191,6 +193,10 @@ class Autocomplete extends Component {
return fields;
}
getOrder() {
return this.order ? this.order : `${this.showField} ASC`;
}
findItems(search) {
if (this.url && search && !this.finding) {
this.maxRow = false;
@ -204,7 +210,7 @@ class Autocomplete extends Component {
Object.assign(filter.where, this.filter.where);
}
}
filter.order = this.order;
filter.order = this.getOrder();
let json = JSON.stringify(filter);
this.finding = true;
this.$http.get(`${this.url}?filter=${json}`).then(
@ -248,7 +254,7 @@ class Autocomplete extends Component {
filter.skip = this.items.length;
}
filter.limit = this.maxRow;
filter.order = this.order;
filter.order = this.getOrder();
}
if (this.filter) {
Object.assign(filter, this.filter);
@ -284,6 +290,22 @@ class Autocomplete extends Component {
}
}
}
_parentForm() {
this.findForm = true;
let formScope = this.$scope;
while (formScope && !formScope.form && formScope.$id > 1) {
formScope = formScope.$parent;
}
this.form = formScope ? formScope.form || null : null;
}
setDirtyForm() {
if (!this.form && !this.findForm) {
this._parentForm();
}
if (this.form) {
this.form.$setDirty();
}
}
$onInit() {
this.findMore = this.url && this.maxRow;

View File

@ -16,6 +16,7 @@ describe('Component vnAutocomplete', () => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$timeout = _$timeout_;
$element = angular.element('<div></div>');
controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
@ -120,7 +121,6 @@ describe('Component vnAutocomplete', () => {
it(`should perform a query if the item id isn't present in the controller.items property`, () => {
controller.url = 'test.com';
$httpBackend.whenGET(`${controller.url}?filter={"fields":{"id":true,"name":true},"where":{"id":3}}`).respond();
$httpBackend.expectGET(`${controller.url}?filter={"fields":{"id":true,"name":true},"where":{"id":3}}`);
controller.items = [{id: 1, name: 'test1'}, {id: 2, name: 'Bruce Wayne'}];
controller.field = 3;
@ -136,7 +136,6 @@ describe('Component vnAutocomplete', () => {
it(`should set field performing a query as the item id isn't present in the controller.items property`, () => {
controller.url = 'test.com';
$httpBackend.whenGET(`${controller.url}?filter={"fields":{"id":true,"name":true},"where":{"id":3}}`).respond();
$httpBackend.expectGET(`${controller.url}?filter={"fields":{"id":true,"name":true},"where":{"id":3}}`);
controller.items = [{id: 1, name: 'test1'}, {id: 2, name: 'Bruce Wayne'}];
controller.field = 3;
@ -150,7 +149,7 @@ describe('Component vnAutocomplete', () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.url = 'test.com';
let search = 'The Joker';
let json = JSON.stringify({where: {name: {regexp: search}}, order: controller.order});
let json = JSON.stringify({where: {name: {regexp: search}}, order: controller.getOrder()});
$httpBackend.whenGET(`${controller.url}?filter=${json}`).respond([{id: 3, name: 'The Joker'}]);
$httpBackend.expectGET(`${controller.url}?filter=${json}`);
controller.findItems(search);
@ -163,8 +162,8 @@ describe('Component vnAutocomplete', () => {
let controller = $componentController('vnAutocomplete', {$scope, $element, $httpBackend, $timeout});
controller.url = 'test.com';
let search = 'The Joker';
controller.filterSearch = "{where: {surname: {regexp: 'search'}}}";
let json = JSON.stringify({where: {surname: {regexp: search}}, order: controller.order});
controller.filterSearch = "{where: {name: {regexp: 'search'}}}";
let json = JSON.stringify({where: {name: {regexp: search}}, order: controller.getOrder()});
$httpBackend.whenGET(`${controller.url}?filter=${json}`).respond([{id: 3, name: 'The Joker'}]);
$httpBackend.expectGET(`${controller.url}?filter=${json}`);
controller.findItems(search);
@ -177,7 +176,7 @@ describe('Component vnAutocomplete', () => {
controller.url = 'test.com';
let search = 'Joker';
controller.multiple = true;
let json = JSON.stringify({where: {name: {regexp: search}}, order: controller.order});
let json = JSON.stringify({where: {name: {regexp: search}}, order: controller.getOrder()});
$httpBackend.whenGET(`${controller.url}?filter=${json}`).respond([{id: 3, name: 'The Joker'}, {id: 4, name: 'Joker'}]);
$httpBackend.expectGET(`${controller.url}?filter=${json}`);
controller.findItems(search);

View File

@ -18,18 +18,23 @@ ul.vn-autocomplete {
}
&.load-more {
color: #ffa410;
font-weight: bold;
font-family: vn-font-bold;
padding: .4em .8em;
}
}
}
vn-autocomplete {
position: relative;
vn-vertical {
outline:none;
}
.mdl-chip__action {
position: absolute;
top: 0px;
right: -6px;
margin: 22px 0px;
background-color: white;
background: transparent;
}
.material-icons {
font-size: 18px;

View File

@ -1,11 +1,22 @@
<div class="{{$ctrl.className}}" ng-mouseover="$ctrl.mouseIsOver = true" ng-mouseleave="$ctrl.mouseIsOver = false">
<vn-horizontal ng-if="$ctrl.text" class="orderly" ng-click="$ctrl.onClick()">
<vn-none class="title" translate>
{{::$ctrl.text}}
<vn-horizontal ng-if="$ctrl.text" class="orderly">
<vn-none
class="title"
ng-class="{'noDrop': $ctrl.orderLocked, 'pointer' : !$ctrl.orderLocked}"
ng-click="$ctrl.onClick($event)" translate>
{{::$ctrl.text}}
</vn-none>
<vn-none>
<vn-icon icon="arrow_drop_down" ng-if="$ctrl.showArrow('DESC')"></vn-icon>
<vn-icon icon="arrow_drop_up" ng-if="$ctrl.showArrow('ASC')"></vn-icon>
<vn-none ng-if="!$ctrl.orderLocked">
<vn-icon
icon="arrow_drop_down"
ng-class="{'active': $ctrl.showArrow('DESC')}"
ng-click="$ctrl.onClick($event, 'DESC')"
></vn-icon>
<vn-icon
icon="arrow_drop_up"
ng-class="{'active': $ctrl.showArrow('ASC')}"
ng-click="$ctrl.onClick($event, 'ASC')"
></vn-icon>
</vn-none>
</vn-horizontal>
<ng-transclude ng-if="!$ctrl.text"></ng-transclude>

View File

@ -1,19 +1,29 @@
import {module} from '../module';
export default class ColumnHeader {
constructor() {
constructor($attrs) {
this.order = undefined;
this.mouseIsOver = false;
this.orderLocked = ($attrs.orderLocked !== undefined);
}
onClick() {
if (this.order === 'ASC') {
this.order = 'DESC';
} else {
this.order = 'ASC';
onClick(event, order) {
if (!this.orderLocked) {
if (order) {
this.order = order;
} else if (this.order === 'ASC') {
this.order = 'DESC';
} else {
this.order = 'ASC';
}
this.gridHeader.selectColum(this);
}
this.gridHeader.selectColum(this);
if (event)
event.preventDefault();
}
showArrow(type) {
if (this.orderLocked)
return false;
let showArrow = (this.gridHeader && this.gridHeader.currentColumn && this.gridHeader.currentColumn.field === this.field && this.order === type);
let showOther = (this.gridHeader && this.gridHeader.currentColumn && this.gridHeader.currentColumn.field === this.field && this.order !== type);
if (type === 'DESC' && this.mouseIsOver && !showOther) {
@ -22,13 +32,13 @@ export default class ColumnHeader {
return showArrow;
}
$onInit() {
if (this.defaultOrder) {
if (this.defaultOrder && !this.orderLocked) {
this.order = this.defaultOrder;
this.onClick();
}
}
}
ColumnHeader.$inject = [];
ColumnHeader.$inject = ['$attrs'];
module.component('vnColumnHeader', {
template: require('./column-header.html'),

View File

@ -3,6 +3,8 @@ import './column-header.js';
describe('Component vnColumnHeader', () => {
let $componentController;
let controller;
let $event;
let $attrs;
beforeEach(() => {
angular.mock.module('client');
@ -10,14 +12,18 @@ describe('Component vnColumnHeader', () => {
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
controller = $componentController('vnColumnHeader', {});
$event = {
preventDefault: () => {}
};
$attrs = {};
controller = $componentController('vnColumnHeader', {$attrs});
}));
describe('onClick()', () => {
it(`should change the ordenation to DESC (descendant) if it was ASC (ascendant)`, () => {
controller.gridHeader = {selectColum: () => {}};
controller.order = 'ASC';
controller.onClick();
controller.onClick($event);
expect(controller.order).toEqual('DESC');
});
@ -25,7 +31,7 @@ describe('Component vnColumnHeader', () => {
it(`should change the ordenation to ASC (ascendant) if it wasnt ASC`, () => {
controller.gridHeader = {selectColum: () => {}};
controller.order = 'DESC or any other value that might occur';
controller.onClick();
controller.onClick($event);
expect(controller.order).toEqual('ASC');
});
@ -34,7 +40,7 @@ describe('Component vnColumnHeader', () => {
controller.gridHeader = {selectColum: () => {}};
controller.order = 'Change me!';
spyOn(controller.gridHeader, 'selectColum');
controller.onClick();
controller.onClick($event);
expect(controller.gridHeader.selectColum).toHaveBeenCalledWith(controller);
});

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