This commit is contained in:
Vicente Falco 2017-12-04 11:14:15 +01:00
commit 6e3e46e761
54 changed files with 684 additions and 102 deletions

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,59 @@
"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"
}
}
]
}

View File

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

View File

@ -1,5 +1,4 @@
{
"Basic data": "Datos básicos",
"Comercial Name": "Nombre comercial",
"Tax number": "NIF/CIF",
"Social name": "Razón social",

View File

@ -21,10 +21,6 @@
<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-horizontal>
<vn-horizontal margin-medium-bottom>
<vn-one>
<vn-check label="Recibido core VNH" field="$ctrl.client.hasCoreVnh" vn-acl="administrative"></vn-check>

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() {

View File

@ -25,9 +25,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 +69,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,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: vn-font-bold;
}

View File

@ -14,3 +14,7 @@ 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';

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,3 @@
{
"Add credit": "Añadir crédito"
}

View File

@ -0,0 +1,29 @@
<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="employee.name" 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.employee.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,5 @@
{
"Since" : "Desde",
"Employee" : "Empleado",
"No results": "Sin resultados"
}

View File

@ -1,14 +1,31 @@
<vn-card>
<vn-vertical class="margin-medium" pad-medium-top pad-medium-bottom>
<vn-vertical class="margin-large" 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-vertical>
</vn-horizontal>
<vn-horizontal>
<vn-one>
<span translate>Credit</span>:
</vn-one>
<vn-auto>
{{$ctrl.client.credit || 0}} €
</vn-auto>
</vn-horizontal>
<vn-horizontal>
<vn-one>
<span translate>Secured credit</span>:
</vn-one>
<vn-auto>
{{$ctrl.client.creditInsurance || 0}} €
</vn-auto>
</vn-horizontal>
</vn-vertical>
</vn-card>

View File

@ -0,0 +1,37 @@
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.waitingMgCrud > 3) {
throw new Error('Magic Crud is not loaded');
} else {
this.waitingMgCrud++;
this.$timeout(() => {
this.filter(order);
}, 250);
}
}
}
FilterClientList.$inject = ['$scope', '$timeout', '$state'];

View File

@ -0,0 +1,30 @@
<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 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="Importe" field="$ctrl.greuge.amount" type="number" vn-focus></vn-textfield>
<vn-date-picker vn-one label="Date" model="$ctrl.greuge.shipped"></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,23 @@
import ngModule from '../module';
class ClientGreugeCreate {
constructor($scope, $state) {
this.$ = $scope;
this.$state = $state;
this.greuge = {};
}
onSubmit() {
this.greuge.clientFk = this.$state.params.id;
this.$.watcher.submit().then(
() => {
this.$state.go('clientCard.greuge.list');
}
);
}
}
ClientGreugeCreate.$inject = ['$scope', '$state'];
ngModule.component('vnClientGreugeCreate', {
template: require('./greuge-create.html'),
controller: ClientGreugeCreate
});

View File

@ -0,0 +1,38 @@
<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,7 @@
{
"Date" : "Fecha",
"Comment" : "Comentario",
"Amount" : "Importe",
"Type": "Tipo",
"Add Greuge": "Añadir Greuge"
}

View File

@ -2,7 +2,11 @@
"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",
@ -12,5 +16,7 @@
"Province": "Provincia",
"Save": "Guardar",
"Pay method" : "Forma de pago",
"Address": "Consignatario"
"Address": "Consignatario",
"Credit" : "Crédito",
"Secured credit": "Crédito asegurado"
}

View File

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

View File

@ -1,7 +1,6 @@
{
"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

@ -1,5 +1,5 @@
<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-horizontal ng-if="$ctrl.text" class="orderly" ng-class="{'pointer' : !$ctrl.orderLocked}" ng-click="$ctrl.onClick()">
<vn-none class="title" translate>
{{::$ctrl.text}}
</vn-none>

View File

@ -1,11 +1,13 @@
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.orderLocked) {
if (this.order === 'ASC') {
this.order = 'DESC';
} else {
@ -13,7 +15,11 @@ export default class ColumnHeader {
}
this.gridHeader.selectColum(this);
}
}
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 +28,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,7 @@ import './column-header.js';
describe('Component vnColumnHeader', () => {
let $componentController;
let controller;
let $attrs;
beforeEach(() => {
angular.mock.module('client');
@ -10,7 +11,8 @@ describe('Component vnColumnHeader', () => {
beforeEach(angular.mock.inject(_$componentController_ => {
$componentController = _$componentController_;
controller = $componentController('vnColumnHeader', {});
$attrs = {};
controller = $componentController('vnColumnHeader', {$attrs});
}));
describe('onClick()', () => {

View File

@ -5,6 +5,7 @@
ng-blur="$ctrl.hasFocus = false"
ng-mouseenter="$ctrl.hasMouseIn = true"
ng-mouseleave="$ctrl.hasMouseIn = false"
ng-click="$ctrl.onClick()"
>
<input type="text"
class="mdl-textfield__input"

View File

@ -59,6 +59,11 @@ class DatePicker extends Component {
onClear() {
this.modelView = null;
}
onClick() {
if (this.vp) {
this.vp.open();
}
}
mdlUpdate() {
let mdlField = this.element.firstChild.MaterialTextfield;
if (mdlField)
@ -167,14 +172,23 @@ class DatePicker extends Component {
return this.iniOptions;
}
$onInit() {
initPicker() {
this.iniOptions = this._getOptions();
this.isTimePicker = (this.iniOptions && this.iniOptions.enableTime && this.iniOptions.noCalendar);
this.vp = new Flatpickr(this.input, this.iniOptions);
}
$onDestroy() {
destroyPicker() {
if (this.vp)
this.vp.destroy();
this.vp = undefined;
}
$onInit() {
this.initPicker();
}
$onDestroy() {
this.destroyPicker();
}
}
DatePicker.$inject = ['$element', '$translate', '$filter', '$timeout'];

View File

@ -140,14 +140,6 @@ describe('Component vnDropDown', () => {
});
describe('$onChanges()', () => {
it(`should set the top css of the $element`, () => {
let argumentObject = {show: true, top: {currentValue: 100}};
spyOn(controller.$element, 'css');
controller.$onChanges(argumentObject);
expect(controller.$element.css).toHaveBeenCalledWith('top', '100px');
});
it(`should set the width css of the $element`, () => {
let argumentObject = {show: true, itemWidth: {currentValue: 100}};
spyOn(controller.$element, 'css');
@ -295,26 +287,40 @@ describe('Component vnDropDown', () => {
controller.selectItem(item);
expect(controller.selected).toEqual(item);
expect(controller._show).toEqual(true);
expect(controller._show).not.toBeDefined();
});
});
describe('loadItems()', () => {
it(`should set controller._show to true`, () => {
it(`should set controller.show to true`, () => {
controller.show = false;
controller.itemsFiltered = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller.loadItems();
expect(controller._show).toEqual(true);
expect(controller.show).toEqual(true);
});
it(`should call loadMore() and then set controller._show to true`, () => {
it(`should call loadMore() and then set controller._show to true if there are items`, () => {
controller.showLoadMore = () => {};
controller.loadMore = () => {};
spyOn(controller, 'loadMore');
controller.itemsFiltered = [{id: 1, name: 'Batman'}, {id: 2, name: 'Bruce'}, {id: 3, name: 'Logan'}, {id: 4, name: 'Wolverine'}];
controller.loadItems();
expect(controller._show).toEqual(true);
expect(controller.loadMore).toHaveBeenCalledWith();
});
it(`should call loadMore() and then set controller._show to undefined if there are not items`, () => {
controller.showLoadMore = () => {};
controller.loadMore = () => {};
spyOn(controller, 'loadMore');
controller.itemsFiltered = [];
controller.loadItems();
expect(controller._show).not.toBeDefined();
expect(controller.loadMore).toHaveBeenCalledWith();
});
});
describe('$onInit()', () => {

View File

@ -2,7 +2,6 @@ vn-grid-header {
border-bottom: 3px solid #9D9D9D;
font-weight: bold;
.orderly{
cursor: pointer;
text-align: center;
white-space: nowrap;
justify-content: center;

View File

@ -68,9 +68,9 @@ export default class Watcher extends Component {
*
* @param {String} state The state name
*/
submitGo(state) {
submitGo(state, params) {
return this.submit().then(
() => this.$state.go(state)
() => this.$state.go(state, params || {})
);
}
/**

View File

@ -100,11 +100,11 @@ describe('Component vnWatcher', () => {
it(`should call controller.$state.go() function after calling controllers submit() function`, done => {
spyOn(controller, 'submit').and.returnValue(Promise.resolve());
spyOn(controller.$state, 'go');
let state = 'the state';
let state = 'the.State';
controller.submitGo(state)
.then(() => {
expect(controller.submit).toHaveBeenCalledWith();
expect(controller.$state.go).toHaveBeenCalledWith(state);
expect(controller.$state.go).toHaveBeenCalledWith(state, {});
done();
});
});

View File

@ -9,7 +9,7 @@ export default class MenuActions {
switchItem() {
if (!this.items || !this.items.length) return;
let stateName = this.$state.current.name.replace('create', 'list').replace('edit', 'list');
let stateName = this.$state.current.name.replace('.create', '.list').replace('.edit', '.list');
for (let i = 0; i < this.items.length; i++) {
this.items[i].active = (this.items[i].href === stateName);

View File

@ -47,9 +47,8 @@ function config($stateProvider, $urlRouterProvider, aclServiceProvider, modulesF
for (let i = 0; i < count; i++) {
let route = fileRoutes[i];
if (aclService.routeHasPermission(route)) {
$stateProvider.state(route.state, {
let configRoute = {
url: route.url,
abstract: route.abstract || false,
template: `<${route.component} ${getParams(route)}></${route.component}>`,
resolve: {
loader: loader(moduleName, validations)
@ -57,7 +56,13 @@ function config($stateProvider, $urlRouterProvider, aclServiceProvider, modulesF
data: {
routes: fileRoutes
}
});
};
if (route.abstract)
configRoute.abstract = true;
if (route.routeParams)
configRoute.params = route.routeParams;
$stateProvider.state(route.state, configRoute);
} else if (route.state === mainModule.state) {
break;
}

View File

@ -116,12 +116,7 @@ module.exports = function(Client) {
}
function maxCb(_, instances) {
if (!instances) {
done(generateErrorCredit());
return;
}
if (instances.length !== 1 || instances[0].employeeFk == userId || instances[0].amount > 0) {
if (!instances || instances.length !== 1 || instances[0].employeeFk == userId || instances[0].amount > 0) {
done();
return;
}

View File

@ -0,0 +1,20 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
return {
where: {
clientFk: params.clientFk
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'shipped DESC',
include: {
relation: "greugeType",
scope: {
fields: ["id", "name"]
}
}
};
}
};

View File

@ -0,0 +1,34 @@
module.exports = Self => {
Self.remoteMethod('sumAmount', {
description: 'returns sum greuge.ammount from client',
accessType: 'READ',
accepts: [{
arg: 'id',
type: 'number',
required: true,
description: 'clientFk',
http: {source: 'path'}
}],
returns: {
arg: 'sumAmount'
},
http: {
path: `/:id/sumAmount`,
verb: 'get'
}
});
Self.sumAmount = (clientFk, callback) => {
let query = `SELECT sum(amount) as sumAmount FROM vn.greuge WHERE clientFk = ?`;
Self.rawSql(query, [clientFk], callback).then(response => {
if (response.length) {
callback(null, response[0].sumAmount);
} else {
callback(null, 0);
}
})
.catch(reject => {
callback(reject, null);
});
};
};

View File

@ -0,0 +1,31 @@
module.exports = function(Self) {
Self.installMethod('filter', filterParams, filterResults);
function filterParams(params) {
return {
where: {
clientFk: params.clientFk
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'created DESC',
include: {
relation: "employee",
scope: {
fields: ["id", "name", "surname"]
}
}
};
}
function filterResults(instances) {
let result = JSON.parse(JSON.stringify(instances));
if (result && result.instances && result.instances.length) {
result.instances.forEach((element, i) => {
result.instances[i].employee.name = `${element.employee.name} ${element.employee.surname}`;
delete result.instances[i].employee.surname;
});
}
return result;
}
};

View File

@ -1,5 +1,5 @@
{
"name": "ClientCredit",
"name": "clientCredit",
"base": "VnModel",
"validateUpsert": true,
"properties": {

View File

@ -0,0 +1,14 @@
{
"name": "greugeType",
"base": "VnModel",
"properties": {
"id": {
"id": true,
"type": "Number",
"description": "Identifier"
},
"name": {
"type": "String"
}
}
}

View File

@ -0,0 +1,4 @@
module.exports = function(Self) {
require('../methods/greuge/filter.js')(Self);
require('../methods/greuge/totalGreuge.js')(Self);
};

View File

@ -0,0 +1,35 @@
{
"name": "greuge",
"base": "VnModel",
"properties": {
"id": {
"id": true,
"type": "Number",
"description": "Identifier"
},
"description": {
"type": "String"
},
"amount": {
"type": "Number"
},
"shipped": {
"type": "date"
},
"created": {
"type": "date"
}
},
"relations": {
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "clientFk"
},
"greugeType": {
"type": "belongsTo",
"model": "greugeType",
"foreignKey": "greugeTypeFk"
}
}
}

View File

@ -1,4 +1,7 @@
module.exports = fi => {
if (fi === undefined || fi === null) {
return true;
}
let dni = fi;
let getLetterDni = dni => {
const regExpDni = 'TRWAGMYFPDXBNJZSQVHLCKE';

View File

@ -27,8 +27,8 @@
"Client": {
"dataSource": "vn"
},
"ClientCredit": {
"dataSource": "salix"
"clientCredit": {
"dataSource": "vn"
},
"ClientCreditLimit": {
"dataSource": "salix"
@ -59,5 +59,11 @@
},
"CreditClassification": {
"dataSource": "salix"
},
"greuge": {
"dataSource": "vn"
},
"greugeType": {
"dataSource": "vn"
}
}

View File

@ -130,7 +130,7 @@ module.exports = function(Self) {
};
};
Self.installMethod = function(methodName, filterCb) {
Self.installMethod = function(methodName, filterCb, filterResult) {
this.remoteMethod(methodName, {
description: 'List items using a filter',
accessType: 'READ',
@ -161,9 +161,13 @@ module.exports = function(Self) {
var response = {};
function returnValues() {
if (response.instances !== undefined && response.count !== undefined)
if (response.instances !== undefined && response.count !== undefined) {
if (filterResult)
cb(null, filterResult(response));
else
cb(null, response);
}
}
function error() {
cb(null, response);

View File

@ -12,8 +12,12 @@
"database": ""
},
"pdf": {
"format": "A4",
"header": {
"height": "120px"
},
"footer": {
"height": "100px"
"height": "130px"
}
}
}

View File

@ -54,4 +54,13 @@ router.get('/sepa-core/:clientId', function(request, response, next) {
});
});
router.get('/sepa-core-view/:clientId', function(request, response, next) {
template.get('sepa-core', {clientId: request.params.clientId}, (error, result) => {
if (error)
return response.status(400).json({message: error.message});
response.send(result.body);
});
});
module.exports = router;

View File

@ -5,8 +5,8 @@ img {
#pageHeader-first {
border-bottom: 1px solid #DDD;
font-family: arial, sans-serif;
padding-bottom: 10px;
text-align: center;
padding: 10px 20px;
font-size: 10px;
color: #555
}

View File

@ -17,12 +17,11 @@
<h1>{{_.title}}</h1>
</div>
<!-- Title block end -->
<div class="panel">
<div class="verticalText">{{_.toCompleteBySupplier}}</div>
<div class="row inline">
<div class="text">{{_.orderReference}}</div>
<div class="control">{{mandateCode}}</div>
<div class="description">test descripcion</div>
</div>
<div class="row inline">
<div class="text">{{_.supplierIdentifier}}</div>
@ -46,10 +45,11 @@
</div>
</div>
<p style="text-align: justify;font-size:10px">{{_.bodyDescription}}</p>
<p class="font small">{{_.bodyDescription}}</p>
<p class="font small"><strong>{{_.clientAdvice}}</strong></p>
<p>{{_.clientAdvice}}</p>
<div class="panel">
<div class="verticalText">{{_.toCompleteByClient}}</div>
<div class="row inline">
<div class="text">{{_.clientName}}</div>
<div class="control">{{clientName}}</div>
@ -68,7 +68,7 @@
<div class="control">{{clientCountry}}</div>
</div>
<div class="row inline">
<div class="text">{{_.swift}}</div>
<div class="text font verticalAlign">{{_.swift}}</div>
<div class="control">
{{#swiftFields}}
<div class="box"></div>
@ -83,12 +83,28 @@
<div class="box"></div>
{{/accountNumberFields}}
</div>
<div class="description">
<div class="line"><span>{{_.accountNumberFormat}}</span></div>
</div>
</div>
<div class="row inline">
<div class="text">{{_.paymentType}}</div>
<div class="text font verticalAlign">{{_.paymentType}}</div>
<div class="control">
<div class="columns">
<div class="size33">
<div class="size25">
<div class="box crossed">X</div>
<span style="padding:10px;line-height:30px">Recurrente</span>
</div>
<div class="size25 font verticalAlign">{{_.recurrent}}</div>
</div>
<div class="size33 font centered">O</div>
<div class="size33">
<div class="size25">
<div class="box"></div>
</div>
<div class="size25 font verticalAlign">{{_.unique}}</div>
</div>
</div>
</div>
</div>
<div class="row inline">
@ -100,10 +116,8 @@
<div class="control"></div>
</div>
</div>
<div style="position:absolute;right:-50px;-webkit-transform: rotate(90deg);font-size:10px">A cumplimentar por el deudor</div>
<p class="small">TODOS LOS CAMPOS HAN DE SER CUMPLIMENTADOS OBLIGATORIAMENTE.</p>
<p class="small">UNA VEZ FIRMADA ESTA ORDEN DE DOMICILIACIÓN DEBE SER ENVIADA AL ACREEDOR PARA SU CUSTODIA Y ES RECOMENDABLE FACILITAR UNA COPIA A SU ENTIDAD BANCARIA.</p>
<p class="font small">{{_.mandatoryFields}}</p>
<p class="font small">{{_.sendOrder}}</p>
</div>
<!-- Body block end -->

View File

@ -1,5 +1,7 @@
{
"title": "Orden de domiciliación de adeudo directo SEPA CORE",
"toCompleteBySupplier": "A cumplimentar por el acreedor",
"toCompleteByClient": "A cumplimentar por el deudor",
"bodyDescription": "Mediante la firma de esta orden de domiciliación, el deudor autoriza (A) al acreedor a enviar instrucciones a la entidad del deudor para adeudar su cuenta y (B) a la entidad para efectuar los adeudos en su cuenta siguiendo las instrucciones del acreedor.Como parte de sus derechos, el deudor está legitimado al reembolso por su entidad en los términos y condiciones del contrato suscrito con la misma. La solicitud de reembolso deberá efectuarse dentro de las ocho semanas que adeudo en cuenta. Puede obtener información adicional sobre sus derechos en su entidad financiera.",
"orderReference": "Referencia de la orden de domiciliación",
"supplierIdentifier": "Identificador del acreedor",
@ -15,10 +17,15 @@
"swift": "Swift BIC",
"accountNumber": "Número de cuenta - IBAN",
"accountHolder": "(Titular/es de la cuenta de cargo)",
"accountNumberFormat": "En España el IBAN consta de 24 posiciones comenzando siempre por ES",
"paymentType": "Tipo de pago",
"recurrent": "Recurrente",
"unique": "Único",
"signLocation": "Fecha - Localidad",
"sign": "Firma del deudor y sello",
"fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla _ www.verdnatura.es _ clientes@verdnatura.es",
"mandatoryFields": "TODOS LOS CAMPOS HAN DE SER CUMPLIMENTADOS OBLIGATORIAMENTE.",
"sendOrder": "UNA VEZ FIRMADA ESTA ORDEN DE DOMICILIACIÓN DEBE SER ENVIADA AL ACREEDOR PARA SU CUSTODIA Y ES RECOMENDABLE FACILITAR UNA COPIA A SU ENTIDAD BANCARIA.",
"fiscalAddress": "VERDNATURA LEVANTE SL, B97367486 Avda. Espioca, 100, 46460 Silla · www.verdnatura.es · clientes@verdnatura.es",
"privacy": "- AVISO - Este mensaje es privado y confidencial, y debe ser utilizado exclusivamente por la persona destinataria del mismo. Si usted ha recibido este mensaje por error, le rogamos lo comunique al remitente y borre dicho mensaje y cualquier documento adjunto que pudiera contener. Verdnatura Levante SL no renuncia a la confidencialidad ni a ningún privilegio por causa de transmisión errónea o mal funcionamiento. Igualmente no se hace responsable de los cambios, alteraciones, errores u omisiones que pudieran hacerse al mensaje una vez enviado.",
"privacyLaw": "En cumplimiento de lo dispuesto en la Ley Orgánica 15/1999, de Protección de Datos de Carácter Personal, le comunicamos que los datos personales que facilite se incluirán en ficheros automatizados de VERDNATURA LEVANTE S.L., pudiendo en todo momento ejercitar los derechos de acceso, rectificación, cancelación y oposición, comunicándolo por escrito al domicilio social de la entidad. La finalidad del fichero es la gestión administrativa, contabilidad, y facturación."
}

View File

@ -7,7 +7,7 @@ img {
max-width: 90%;
margin: 0 auto;
font-size: 14px;
color: #555
color: #000
}
body .title {

View File

@ -1,14 +1,18 @@
body {
margin: 0 auto;
width: 210mm
}
.panel {
border: 1px solid #DDD;
margin-bottom: 10px;
position: relative;
padding:10px
}
.row {
margin-bottom: 15px;
overflow: hidden;
content: '';
clear: both
overflow: hidden
}
.row .text {
@ -21,14 +25,34 @@
box-sizing: border-box
}
.row .text, .row .control {
overflow: hidden
}
.row .description {
position: relative;
padding-top: 2px;
overflow: hidden;
font-size: 8px;
display: block;
color: #999
}
.row .v-align {
padding-top: 5px;
line-height: 21px
.row .line {
border-bottom: 1px solid #DDD;
border-right: 1px solid #DDD;
border-left: 1px solid #DDD;
margin-top: 10px;
color: #999;
padding: 5px
}
.row .description span {
background-color: #FFF;
margin: -5px 0 0 50px;
display: block;
padding: 5px;
float: left
}
.row:last-child {
@ -49,6 +73,11 @@
float: left
}
.row.inline .description {
position: static;
overflow: visible
}
.box {
border-top: 1px solid #CCC;
border-right: 1px solid #CCC;
@ -62,6 +91,11 @@
float: left
}
.box.crossed {
font-weight: 100;
font-size: 16px
}
.row .control .box:first-child {
border-left: 1px solid #CCC;
}
@ -70,6 +104,67 @@ p {
text-align: justify
}
p.small {
.font.small {
font-size: 10px
}
.font.verticalAlign {
height: 27px;
line-height: 27px
}
.font.centered {
height: 27px;
line-height: 27px;
text-align: center
}
.verticalText {
-moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
position: absolute;
text-align: center;
font-size: .65em;
width: 200px;
right: -115px;
top: 50%
}
.columns:after {
display: block;
content: ' ';
clear: both
}
.columns .size100 {
width: 100%;
float: left
}
.columns .size75 {
width: 75%;
float: left
}
.columns .size50 {
width: 50%;
float: left
}
.columns .size33 {
width: 33.33%;
float: left
}
.columns .size25 {
width: 25%;
float: left
}