feat(salix): refs #6943 #6943 Client module redirect to Lilium
gitea/salix/pipeline/pr-dev There was a failure building this commit Details

This commit is contained in:
Javier Segarra 2024-09-05 09:41:06 +02:00
parent 6df817ea5d
commit 79f7bb4296
179 changed files with 8 additions and 11181 deletions

View File

@ -1,193 +0,0 @@
<vn-watcher
vn-id="watcher"
url="Clients/{{$ctrl.$params.id}}/createAddress"
id-field="id"
data="$ctrl.address"
params="$ctrl.address"
insert-mode="true"
form="form">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Provinces/location"
data="provincesLocation"
order="id">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Incoterms"
data="incoterms"
order="name">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="CustomsAgents"
data="customsAgents"
order="fiscalName">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-check
vn-one
label="Default" ng-model="$ctrl.address.isDefaultAddress">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Consignee"
ng-model="$ctrl.address.nickname"
rule
vn-focus>
</vn-textfield>
<vn-textfield
vn-one
label="Street address"
ng-model="$ctrl.address.street"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-datalist vn-one
label="Postcode"
ng-model="$ctrl.address.postalCode"
selection="$ctrl.postcode"
url="Postcodes/location"
fields="['code','townFk']"
order="code, townFk"
value-field="code"
show-field="code"
rule>
<tpl-item>
{{code}} - {{town.name}} ({{town.province.name}},
{{town.province.country.name}})
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New postcode"
ng-click="postcode.open()"
vn-acl="deliveryAssistant"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-datalist>
<vn-datalist vn-id="town" vn-one
label="City"
ng-model="$ctrl.address.city"
selection="$ctrl.town"
url="Towns/location"
fields="['id', 'name', 'provinceFk']"
show-field="name"
value-field="name">
<tpl-item>
{{name}}, {{province.name}}
({{province.country.name}})
</tpl-item>
</vn-datalist>
<vn-autocomplete vn-id="province" vn-one
label="Province"
ng-model="$ctrl.address.provinceFk"
data="provincesLocation"
fields="['id', 'name', 'countryFk']"
show-field="name"
value-field="id"
rule>
<tpl-item>{{name}} ({{country.name}})</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
ng-model="$ctrl.address.agencyModeFk"
url="AgencyModes/isActive"
show-field="name"
value-field="id"
label="Agency">
</vn-autocomplete>
<vn-textfield
vn-one
label="Phone"
ng-model="$ctrl.address.phone"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Mobile"
ng-model="$ctrl.address.mobile"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
ng-model="$ctrl.address.incotermsFk"
data="incoterms"
show-field="name"
value-field="code"
label="Incoterms">
</vn-autocomplete>
<vn-autocomplete vn-one
ng-model="$ctrl.address.customsAgentFk"
data="customsAgents"
show-field="fiscalName"
value-field="id"
label="Customs agent">
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New customs agent"
ng-click="$ctrl.showCustomAgent($event)">
</vn-icon-button>
</append>
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button
label="Cancel"
ui-sref="client.card.address.index">
</vn-button>
</vn-button-bar>
</form>
<!-- New postcode dialog -->
<vn-geo-postcode vn-id="postcode"
on-response="$ctrl.onResponse($response)">
</vn-geo-postcode>
<!-- Create custom agent dialog -->
<vn-dialog class="edit"
vn-id="customAgent"
on-accept="$ctrl.onCustomAgentAccept()"
message="New customs agent">
<tpl-body>
<vn-horizontal>
<vn-textfield vn-one vn-focus
label="NIF"
ng-model="$ctrl.newCustomsAgent.nif"
required="true">
</vn-textfield>
<vn-textfield vn-one
label="Fiscal name"
ng-model="$ctrl.newCustomsAgent.fiscalName"
required="true">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Street"
ng-model="$ctrl.newCustomsAgent.street">
</vn-textfield>
<vn-textfield vn-one
label="Phone"
ng-model="$ctrl.newCustomsAgent.phone">
</vn-textfield>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Create</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,90 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.address = {
isActive: true,
isDefaultAddress: false
};
}
onSubmit() {
this.$.watcher.submit().then(res => {
if (this.address.isDefaultAddress)
this.client.defaultAddressFk = res.data.id;
this.$state.go('client.card.address.index');
});
}
showCustomAgent(event) {
if (event.defaultPrevented) return;
event.preventDefault();
this.$.customAgent.show();
}
onCustomAgentAccept() {
return this.$http.post(`CustomsAgents`, this.newCustomsAgent)
.then(res => this.address.customsAgentFk = res.data.id);
}
get town() {
return this._town;
}
// Town auto complete
set town(selection) {
this._town = selection;
if (!selection) return;
const province = selection.province;
const postcodes = selection.postcodes;
if (!this.address.provinceFk)
this.address.provinceFk = province.id;
if (postcodes.length === 1)
this.address.postalCode = postcodes[0].code;
}
get postcode() {
return this._postcode;
}
// Postcode auto complete
set postcode(selection) {
this._postcode = selection;
if (!selection) return;
const town = selection.town;
const province = town.province;
if (!this.address.city)
this.address.city = town.name;
if (!this.address.provinceFk)
this.address.provinceFk = province.id;
}
onResponse(response) {
this.address.postalCode = response.code;
this.address.city = response.city;
this.address.provinceFk = response.provinceFk;
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientAddressCreate', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,130 +0,0 @@
import './index';
import watcher from 'core/mocks/watcher';
describe('Client', () => {
describe('Component vnClientAddressCreate', () => {
let $scope;
let controller;
let $httpBackend;
let $element;
let $state;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$state_, _$httpBackend_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$state = _$state_;
$state.params.id = '1234';
$element = angular.element('<vn-client-address-create></vn-client-address-create>');
controller = $componentController('vnClientAddressCreate', {$element, $scope});
controller.$.watcher = watcher;
controller.$.watcher.submit = () => {
return {
then: callback => {
callback({data: {id: 124}});
}
};
};
controller.client = {id: 1101, defaultAddressFk: 121};
}));
it('should define and set address property', () => {
expect(controller.address.isActive).toBe(true);
});
describe('onSubmit()', () => {
it('should perform a PATCH and not set value to defaultAddressFk property', () => {
jest.spyOn(controller.$state, 'go');
controller.address.isDefaultAddress = false;
controller.onSubmit();
expect(controller.client.defaultAddressFk).toEqual(121);
expect(controller.$state.go).toHaveBeenCalledWith('client.card.address.index');
});
it('should perform a PATCH and set a value to defaultAddressFk property', () => {
jest.spyOn(controller.$state, 'go');
controller.address.isDefaultAddress = true;
controller.onSubmit();
expect(controller.client.defaultAddressFk).toEqual(124);
expect(controller.$state.go).toHaveBeenCalledWith('client.card.address.index');
});
});
describe('town() setter', () => {
it(`should set provinceFk property`, () => {
controller.town = {
provinceFk: 1,
code: 46001,
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
},
postcodes: []
};
expect(controller.address.provinceFk).toEqual(1);
});
it(`should set provinceFk property and fill the postalCode if there's just one`, () => {
controller.town = {
provinceFk: 1,
code: 46001,
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
},
postcodes: [{code: '46001'}]
};
expect(controller.address.provinceFk).toEqual(1);
expect(controller.address.postalCode).toEqual('46001');
});
});
describe('postcode() setter', () => {
it(`should set the town and province properties`, () => {
controller.postcode = {
townFk: 1,
code: 46001,
town: {
id: 1,
name: 'New York',
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
}
}
};
expect(controller.address.city).toEqual('New York');
expect(controller.address.provinceFk).toEqual(1);
});
});
describe('onCustomAgentAccept()', () => {
it(`should create a new customs agent and then set the customsAgentFk property on the address`, () => {
const expectedResult = {id: 1, fiscalName: 'Customs agent one'};
$httpBackend.when('POST', 'CustomsAgents').respond(200, expectedResult);
controller.onCustomAgentAccept();
$httpBackend.flush();
expect(controller.address.customsAgentFk).toEqual(1);
});
});
});
});

View File

@ -1,237 +0,0 @@
<mg-ajax
path="Addresses/{{edit.params.addressId}}"
actions="$ctrl.address = edit.model"
options="mgEdit">
</mg-ajax>
<vn-watcher
vn-id="watcher"
url="Clients/{{$ctrl.$params.id}}/updateAddress"
id-field="id"
data="$ctrl.address"
form="form">
</vn-watcher>
<vn-crud-model
vn-id="model"
url="AddressObservations"
fields="['id', 'addressFk', 'observationTypeFk', 'description']"
link="{addressFk: $ctrl.$params.addressId}"
data="observations"
auto-load="true">
</vn-crud-model>
<vn-crud-model
url="ObservationTypes"
fields="['id', 'description']"
data="types"
auto-load="true">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-check
vn-one
label="Enabled"
ng-model="$ctrl.address.isActive">
</vn-check>
<vn-check
vn-one
label="Is equalizated"
ng-model="$ctrl.address.isEqualizated"
vn-acl="administrative, salesAssistant">
</vn-check>
<vn-check
vn-one
label="Is Logiflora allowed"
ng-model="$ctrl.address.isLogifloraAllowed"
vn-acl="salesAssistant">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Consignee"
ng-model="$ctrl.address.nickname"
rule
vn-focus>
</vn-textfield>
<vn-textfield
vn-one
label="Street"
ng-model="$ctrl.address.street"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-datalist vn-one
label="Postcode"
ng-model="$ctrl.address.postalCode"
selection="$ctrl.postcode"
url="Postcodes/location"
fields="['code','townFk']"
order="code, townFk"
value-field="code"
show-field="code"
rule>
<tpl-item>
{{code}} - {{town.name}} ({{town.province.name}},
{{town.province.country.name}})
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New postcode"
ng-click="postcode.open()"
vn-acl="deliveryAssistant"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-datalist>
<vn-datalist vn-id="town" vn-one
label="City"
ng-model="$ctrl.address.city"
selection="$ctrl.town"
url="Towns/location"
fields="['id', 'name', 'provinceFk']"
show-field="name"
value-field="name">
<tpl-item>
{{name}}, {{province.name}}
({{province.country.name}})
</tpl-item>
</vn-datalist>
<vn-autocomplete vn-id="province" vn-one
label="Province"
ng-model="$ctrl.address.provinceFk"
url="Provinces/location"
fields="['id', 'name', 'countryFk']"
show-field="name"
value-field="id"
rule>
<tpl-item>{{name}} ({{country.name}})</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
ng-model="$ctrl.address.agencyModeFk"
url="AgencyModes/isActive"
show-field="name"
value-field="id"
label="Agency">
</vn-autocomplete>
<vn-textfield
vn-one
label="Phone"
ng-model="$ctrl.address.phone"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Mobile"
ng-model="$ctrl.address.mobile"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
ng-model="$ctrl.address.incotermsFk"
url="Incoterms"
show-field="name"
value-field="code"
label="Incoterms">
</vn-autocomplete>
<vn-autocomplete vn-one
ng-model="$ctrl.address.customsAgentFk"
url="CustomsAgents"
show-field="fiscalName"
value-field="id"
label="Customs agent">
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New customs agent"
ng-click="$ctrl.showCustomAgent($event)">
</vn-icon-button>
</append>
</vn-autocomplete>
</vn-horizontal>
<vn-title>Notes</vn-title>
<div name="observations">
<vn-horizontal ng-repeat="observation in observations">
<vn-autocomplete
vn-one
vn-focus
data="types"
ng-model="observation.observationTypeFk"
show-field="description"
label="Observation type"
rule="AddressObservation">
</vn-autocomplete>
<vn-textfield
vn-two
label="Description"
ng-model="observation.description"
rule="AddressObservation">
</vn-textfield>
<vn-none>
<vn-icon-button
vn-tooltip="Remove note"
icon="delete"
ng-click="$ctrl.removeObservation($index)"
tabindex="-1">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add note"
icon="add_circle"
ng-if="types.length > observations.length"
ng-click="model.insert()">
</vn-icon-button>
</div>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button ng-click="$ctrl.cancel()" label="Cancel"></vn-button>
</vn-button-bar>
</form>
<!-- New postcode dialog -->
<vn-geo-postcode vn-id="postcode"
on-response="$ctrl.onResponse($response)">
</vn-geo-postcode>
<!-- Create custom agent dialog -->
<vn-dialog class="edit"
vn-id="customAgent"
on-accept="$ctrl.onCustomAgentAccept()"
message="New customs agent">
<tpl-body>
<vn-horizontal>
<vn-textfield vn-one vn-focus
label="NIF"
ng-model="$ctrl.newCustomsAgent.nif"
required="true">
</vn-textfield>
<vn-textfield vn-one
label="Fiscal name"
ng-model="$ctrl.newCustomsAgent.fiscalName"
required="true">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield vn-one
label="Street"
ng-model="$ctrl.newCustomsAgent.street">
</vn-textfield>
<vn-textfield vn-one
label="Phone"
ng-model="$ctrl.newCustomsAgent.phone">
</vn-textfield>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Create</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,94 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
removeObservation(index) {
this.$.watcher.setDirty();
this.$.model.remove(index);
}
cancel() {
this.goToIndex();
}
goToIndex() {
this.$state.go('client.card.address.index');
}
onSubmit() {
this.$.watcher.submit()
.then(() => this.$.model.save(true))
.then(() => {
this.card.reload();
this.goToIndex();
});
}
showCustomAgent(event) {
if (event.defaultPrevented) return;
event.preventDefault();
this.$.customAgent.show();
}
onCustomAgentAccept() {
return this.$http.post(`CustomsAgents`, this.newCustomsAgent)
.then(res => this.address.customsAgentFk = res.data.id);
}
get town() {
return this._town;
}
// Town auto complete
set town(selection) {
const oldValue = this._town;
this._town = selection;
if (!selection || !oldValue) return;
const province = selection.province;
const postcodes = selection.postcodes;
if (!this.address.provinceFk)
this.address.provinceFk = province.id;
if (!this.address.postalCode && postcodes.length === 1)
this.address.postalCode = postcodes[0].code;
}
get postcode() {
return this._postcode;
}
// Postcode auto complete
set postcode(selection) {
const oldValue = this._postcode;
this._postcode = selection;
if (!selection || !oldValue) return;
const town = selection.town;
const province = town.province;
if (!this.address.city)
this.address.city = town.name;
if (!this.address.provinceFk)
this.address.provinceFk = province.id;
}
onResponse(response) {
this.address.postalCode = response.code;
this.address.city = response.city;
this.address.provinceFk = response.provinceFk;
}
}
ngModule.vnComponent('vnClientAddressEdit', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnClientCard'
}
});

View File

@ -1,77 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientAddressEdit', () => {
let $scope;
let controller;
let $httpBackend;
let $element;
let $state;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$state_, _$httpBackend_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$state = _$state_;
$state.params.addressId = '1';
$element = angular.element('<vn-client-address-edit></vn-client-address-edit>');
controller = $componentController('vnClientAddressEdit', {$element, $scope});
controller.address = {id: 1, customsAgentFk: null};
controller.$.watcher = {
setDirty: () => {},
setPristine: () => {},
realSubmit: () => {},
check: () => {},
notifySaved: () => {}
};
controller.$.model = {
remove: () => {},
save: () => {}
};
controller.card = {
reload: () => {}
};
}));
describe('removeObservation()', () => {
it('should call $.watcher.setDirty() and $.model.remove(index)', () => {
jest.spyOn(controller.$.watcher, 'setDirty');
jest.spyOn(controller.$.model, 'remove');
controller.removeObservation(1);
expect(controller.$.model.remove).toHaveBeenCalledWith(1);
expect(controller.$.watcher.setDirty).toHaveBeenCalledWith();
});
});
describe('cancel()', () => {
it('should call goToIndex()', () => {
jest.spyOn(controller, 'goToIndex');
controller.cancel();
expect(controller.goToIndex).toHaveBeenCalledWith();
});
});
describe('goToIndex()', () => {
it('should call $state.go("client.card.address.index")', () => {
jest.spyOn(controller.$state, 'go');
controller.goToIndex();
expect(controller.$state.go).toHaveBeenCalledWith('client.card.address.index');
});
});
describe('onCustomAgentAccept()', () => {
it(`should now create a new customs agent and then set the customsAgentFk property on the address`, () => {
const expectedResult = {id: 1, fiscalName: 'Customs agent one'};
$httpBackend.when('POST', 'CustomsAgents').respond(200, expectedResult);
controller.onCustomAgentAccept();
$httpBackend.flush();
expect(controller.address.customsAgentFk).toEqual(1);
});
});
});
});

View File

@ -1,91 +0,0 @@
<vn-crud-model
vn-id="model"
url="Clients/{{$ctrl.$params.id}}/addresses"
filter="$ctrl.filter"
limit="10"
data="$ctrl.addresses"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
placeholder="Search by consignee"
info="You can search by consignee id or name"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)"
auto-state="false">
</vn-searchbar>
</vn-portal>
<vn-data-viewer
model="model"
class="vn-w-md">
<vn-card class="vn-pa-md">
<div
ng-repeat="address in $ctrl.addresses"
class="address">
<a
ui-sref="client.card.address.edit({addressId: {{::address.id}}})"
class="vn-pa-sm border-solid border-radius"
ng-class="{'item-disabled': !address.isActive}"
translate-attr="{title: 'Edit consignee'}">
<vn-none
class="vn-pr-sm"
ng-click="$ctrl.onStarClick($event)">
<vn-icon-button
ng-if="$ctrl.isDefaultAddress(address)"
icon="star"
translate-attr="{title: 'Default address'}">
</vn-icon-button>
<vn-icon-button
ng-if="!$ctrl.isDefaultAddress(address)"
icon="star_border"
ng-click="$ctrl.setDefault(address)"
translate-attr="{title: 'Set as default'}">
</vn-icon-button>
</vn-none>
<vn-one
style="overflow: hidden; min-width: 14em;">
<div class="ellipsize"><b>{{::address.nickname}} - #{{::address.id}}</b></div>
<div class="ellipsize" name="street">{{::address.street}}</div>
<div class="ellipsize">
<span ng-show="::address.postalCode">{{::address.postalCode}} -</span>
<span ng-show="::address.city">{{::address.city}},</span>
<span ng-show="::address.province.name">{{::address.province.name}},</span>
{{::address.province.country.name}}
</div>
<div class="ellipsize">
{{::address.phone}}<span ng-if="::address.mobile">, </span>
{{::address.mobile}}
</div>
<vn-check
vn-one label="Is equalizated"
ng-model="address.isEqualizated"
disabled="true">
</vn-check>
<vn-check
vn-one label="Is Logiflora allowed"
ng-model="address.isLogifloraAllowed"
disabled="true">
</vn-check>
</vn-one>
<vn-vertical
vn-one
ng-if="address.observations.length"
class="vn-hide-narrow vn-px-md border-solid-left"
style="height: 6em; overflow: auto;">
<vn-one ng-repeat="observation in address.observations track by $index" ng-class="{'vn-pt-sm': $index}">
<b>{{::observation.observationType.description}}:</b>
<span>{{::observation.description}}</span>
</vn-one>
</vn-vertical>
</a>
</div>
</vn-card>
</vn-data-viewer>
<vn-float-button
vn-bind="+"
fixed-bottom-right
vn-tooltip="New consignee"
ui-sref="client.card.address.create"
icon="add"
label="Add">
</vn-float-button>

View File

@ -1,97 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
fields: [
'id',
'isDefaultAddress',
'isActive',
'nickname',
'street',
'city',
'provinceFk',
'phone',
'mobile',
'isEqualizated',
'isLogifloraAllowed',
'postalCode'
],
order: [
'isDefaultAddress DESC',
'isActive DESC',
'nickname ASC'],
include: [
{
relation: 'observations',
scope: {
include: 'observationType'
}
}, {
relation: 'province',
scope: {
fields: ['id', 'name', 'countryFk'],
include: {
relation: 'country',
scope: {
fields: ['id', 'name']
}
}
}
}
]
};
}
onStarClick(event) {
event.stopPropagation();
event.preventDefault();
}
setDefault(address) {
let query = `Clients/${this.$params.id}`;
let params = {defaultAddressFk: address.id};
this.$http.patch(query, params).then(res => {
if (res.data) {
this.client.defaultAddressFk = res.data.defaultAddressFk;
this.sortAddresses();
this.vnApp.showSuccess(this.$t('Data saved!'));
}
});
}
isDefaultAddress(address) {
return this.client && this.client.defaultAddressFk === address.id;
}
/**
* Sort address by default address
*/
sortAddresses() {
if (!this.client || !this.addresses) return;
this.addresses = this.addresses.sort((a, b) => {
return this.isDefaultAddress(b) - this.isDefaultAddress(a);
});
}
exprBuilder(param, value) {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {id: value}
: {nickname: {like: `%${value}%`}};
}
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientAddressIndex', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,84 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('Client', () => {
describe('Component vnClientAddressIndex', () => {
let controller;
let $scope;
let $stateParams;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$stateParams_, _$httpBackend_) => {
$stateParams = _$stateParams_;
$stateParams.id = 1;
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-client-address-index></vn-client-address-index>');
controller = $componentController('vnClientAddressIndex', {$element, $scope});
controller.client = {id: 1101, defaultAddressFk: 121};
controller.$.model = crudModel;
}));
describe('setDefault()', () => {
it('should perform a PATCH and set a value to defaultAddressFk property', () => {
jest.spyOn(controller, 'sortAddresses');
let address = {id: 1};
let data = {defaultAddressFk: address.id};
let expectedResult = {defaultAddressFk: address.id};
$httpBackend.expect('PATCH', `Clients/1`, data).respond(200, expectedResult);
controller.setDefault(address);
$httpBackend.flush();
expect(controller.client.defaultAddressFk).toEqual(1);
expect(controller.sortAddresses).toHaveBeenCalledWith();
});
});
describe('isDefaultAddress()', () => {
it('should return true if a passed address is the current default one', () => {
let address = {id: 121};
let result = controller.isDefaultAddress(address);
expect(result).toBeTruthy();
});
it('should return false if a passed address is the current default one', () => {
let address = {id: 1};
let result = controller.isDefaultAddress(address);
expect(result).toBeFalsy();
});
});
describe('sortAddresses()', () => {
it('should return an array of addresses sorted by client defaultAddressFk', () => {
controller.client.defaultAddressFk = 123;
controller.addresses = [
{id: 121, nickname: 'My address one'},
{id: 122, nickname: 'My address two'},
{id: 123, nickname: 'My address three'}];
controller.sortAddresses();
expect(controller.addresses[0].id).toEqual(123);
});
});
describe('exprBuilder()', () => {
it('should return a filter based on a search by id', () => {
const filter = controller.exprBuilder('search', '123');
expect(filter).toEqual({id: '123'});
});
it('should return a filter based on a search by name', () => {
const filter = controller.exprBuilder('search', 'Bruce Wayne');
expect(filter).toEqual({nickname: {like: '%Bruce Wayne%'}});
});
});
});
});

View File

@ -1,21 +0,0 @@
@import "variables";
@import "./effects";
vn-client-address-index {
.address {
padding-bottom: $spacing-md;
&:last-child {
padding-bottom: 0;
}
& > a {
@extend %clickable;
box-sizing: border-box;
display: flex;
align-items: center;
width: 100%;
color: inherit;
overflow: hidden;
}
}
}

View File

@ -1,30 +0,0 @@
# Index
Set as default: Establecer como predeterminado
Active first to set as default: Active primero para marcar como predeterminado
Search by consignee: Buscar por consignatario
You can search by consignee id or name: Puedes buscar por el id o nombre del consignatario
# Edit
Enabled: Activo
Is equalizated: Recargo de equivalencia
Observation type: Tipo de observación
Description: Descripción
The observation type must be unique: El tipo de observación ha de ser único
Remove note: Quitar nota
Add note: Añadir nota
Customs agent: Agente de aduanas
New customs agent: Nuevo agente de aduanas
# Create
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
# Common
Fiscal name: Nombre fiscal
Street: Dirección fiscal
Is Logiflora allowed: Compra directa en Holanda

View File

@ -1,106 +0,0 @@
<tpl-title translate>
New payment
</tpl-title>
<tpl-body>
<vn-crud-model
auto-load="true"
url="Companies"
data="companies"
order="code"
required="true">
</vn-crud-model>
<vn-horizontal>
<vn-date-picker
label="Date"
ng-model="$ctrl.receipt.payed"
vn-name="payed"
required="true">
</vn-date-picker>
<vn-autocomplete
data="companies"
label="Company"
show-field="code"
value-field="id"
ng-model="$ctrl.companyFk"
vn-name="company"
required="true">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
label="Bank"
url="Accountings"
show-field="bank"
value-field="id"
fields="['accountingTypeFk']"
include="{relation: 'accountingType'}"
ng-model="$ctrl.bankFk"
vn-name="bank"
search-function="$ctrl.bankSearchFunc($search)"
selection="$ctrl.bankSelection"
order="id"
required="true">
<tpl-item>{{id}}: {{bank}}</tpl-item>
</vn-autocomplete>
<vn-input-number
vn-focus
label="Amount"
ng-model="$ctrl.amountPaid"
vn-name="amountPaid"
step="0.01"
required="true"
max="$ctrl.maxAmount">
</vn-input-number>
</vn-horizontal>
<vn-vertical ng-show="$ctrl.bankSelection.accountingType.code == 'compensation'">
<h6 translate>Compensation</h6>
<vn-textfield
ng-model="$ctrl.receipt.compensationAccount"
vn-name="compensationAccount"
label="Compensation Account"
on-change="$ctrl.accountShortToStandard(value)">
</vn-textfield>
</vn-vertical>
<vn-horizontal>
<vn-textfield
label="Reference"
ng-model="$ctrl.receipt.description"
vn-name="description"
rule
required="true">
</vn-textfield>
</vn-horizontal>
<vn-vertical ng-show="$ctrl.bankSelection.accountingType.code == 'cash'">
<h6 translate>Cash</h6>
<vn-horizontal>
<vn-input-number
ng-model="$ctrl.deliveredAmount"
label="Delivered amount"
step="0.01"
vn-name="deliveredAmount">
</vn-input-number>
<vn-input-number
disabled="true"
ng-model="$ctrl.amountToReturn"
label="Amount to return"
vn-name="amountToReturn">
</vn-input-number>
</vn-horizontal>
</vn-vertical>
<vn-horizontal ng-show="$ctrl.bankSelection.accountingType.code == 'cash'">
<vn-check
label="View receipt"
ng-model="$ctrl.viewReceipt"
vn-name="viewReceipt">
</vn-check>
<vn-check
label="Send email"
ng-model="$ctrl.sendEmail"
vn-name="sendEmail">
</vn-check>
</vn-horizontal>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate vn-focus>Accept</button>
</tpl-buttons>

View File

@ -1,217 +0,0 @@
import ngModule from '../../module';
import Dialog from 'core/components/dialog';
class Controller extends Dialog {
constructor($element, $, $transclude, vnReport, vnEmail) {
super($element, $, $transclude);
this.vnReport = vnReport;
this.vnEmail = vnEmail;
this.receipt = {};
}
set payed(value) {
this.receipt.payed = value;
}
set amountPaid(value) {
this.receipt.amountPaid = value;
this.amountToReturn = this.deliveredAmount - value;
}
get amountPaid() {
return this.receipt.amountPaid;
}
set clientFk(value) {
this.receipt.clientFk = value;
const filter = {
fields: ['email'],
where: {
id: value
}
};
this.$http.get(`Clients/findOne`, {filter})
.then(res => {
this.receipt.email = res.data.email;
});
}
get clientFk() {
return this.receipt.clientFk;
}
get companyFk() {
if (!this.receipt.companyFk)
this.receipt.companyFk = this.vnConfig.companyFk;
return this.receipt.companyFk;
}
set companyFk(value) {
this.receipt.companyFk = value;
this.getAmountPaid();
}
set description(value) {
this.receipt.description = value;
this.originalDescription = value;
}
get description() {
return this.receipt.description;
}
get bankSelection() {
return this._bankSelection;
}
set bankSelection(value) {
this._bankSelection = value;
if (value) {
const accountingType = value.accountingType;
this.receipt.description = [];
this.viewReceipt = accountingType.code == 'cash';
if (accountingType.code == 'compensation')
this.receipt.description = '';
else {
if (accountingType.receiptDescription != null && accountingType.receiptDescription != '')
this.receipt.description.push(accountingType.receiptDescription);
if (this.originalDescription)
this.receipt.description.push(this.originalDescription);
this.receipt.description = this.receipt.description.join(', ');
}
this.maxAmount = accountingType && accountingType.maxAmount;
this.receipt.payed = Date.vnNew();
if (accountingType.daysInFuture)
this.receipt.payed.setDate(this.receipt.payed.getDate() + accountingType.daysInFuture);
}
}
set deliveredAmount(value) {
this._deliveredAmount = value;
this.amountToReturn = value - this.receipt.amountPaid;
}
get amountToReturn() {
return this._amountToReturn;
}
set amountToReturn(value) {
if (isNaN(value)) return;
value = value.toFixed(2);
if (Number.isInteger(value))
value = parseInt(value);
else value = parseFloat(value);
this._amountToReturn = value;
}
get deliveredAmount() {
return this._deliveredAmount;
}
get bankFk() {
if (!this.receipt.bankFk)
this.receipt.bankFk = this.vnConfig.bankFk;
return this.receipt.bankFk;
}
set bankFk(value) {
this.receipt.bankFk = value;
}
accountShortToStandard(value) {
if (value) {
this.receipt.compensationAccount = value.replace('.', '0'.repeat(11 - value.length));
const params = {bankAccount: this.receipt.compensationAccount};
this.$http.get(`Clients/getClientOrSupplierReference`, {params})
.then(res => {
if (res.data.clientId) {
this.receipt.description = this.$t('Client Compensation Reference', {
clientId: res.data.clientId,
clientName: res.data.clientName
});
} else {
this.receipt.description = this.$t('Supplier Compensation Reference', {
supplierId: res.data.supplierId,
supplierName: res.data.supplierName
});
}
});
} else
this.receipt.description = '';
}
getAmountPaid() {
const filter = {
where: {
clientFk: this.$params.id,
companyFk: this.receipt.companyFk
}
};
this.$http.get(`ClientRisks`, {filter}).then(res => {
this.receipt.amountPaid = (res.data.length && res.data[0].amount) || null;
});
}
responseHandler(response) {
if (response !== 'accept')
return super.responseHandler(response);
const exceededAmount = this.receipt.amountPaid > this.maxAmount;
const isCash = this.bankSelection.accountingType.code == 'cash';
if (isCash && exceededAmount)
return this.vnApp.showError(this.$t('Amount exceeded', {maxAmount: this.maxAmount}));
if (isCash && this.sendEmail && !this.receipt.email)
return this.vnApp.showError(this.$t('There is no assigned email for this client'));
let receiptId;
return this.$http.post(`Clients/${this.clientFk}/createReceipt`, this.receipt)
.then(res => {
receiptId = res.data.id;
super.responseHandler(response);
})
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
.then(() => {
if (!this.sendEmail || !isCash) return;
const params = {
recipient: this.receipt.email
};
this.vnEmail.send(`Receipts/${receiptId}/receipt-email`, params);
})
.then(() => {
if (this.viewReceipt)
this.vnReport.show(`Receipts/${receiptId}/receipt-pdf`);
});
}
bankSearchFunc($search) {
return /^\d+$/.test($search)
? {id: $search}
: {bank: {like: '%' + $search + '%'}};
}
}
Controller.$inject = ['$element', '$scope', '$transclude', 'vnReport', 'vnEmail'];
ngModule.vnComponent('vnClientBalanceCreate', {
slotTemplate: require('./index.html'),
controller: Controller,
bindings: {
payed: '<?',
bankFk: '<?',
amountPaid: '<?',
companyFk: '<?',
description: '<?',
clientFk: '<?'
}
});

View File

@ -1,140 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientBalancCreate', () => {
let controller;
let $httpBackend;
let $httpParamSerializer;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
let $scope = $rootScope.$new();
const $element = angular.element('<vn-client-balance-create></vn-client-balance-create>');
const $transclude = {
$$boundTransclude: {
$$slots: []
}
};
controller = $componentController('vnClientBalanceCreate', {$element, $scope, $transclude});
controller.receipt = {
clientFk: 1101,
companyFk: 442
};
controller.bankSelection = {accountingType: {code: 'myCode'}};
}));
describe('bankSelection() setter', () => {
it('should set the receipt description property', () => {
controller.originalDescription = 'Albaran: 1, 2';
controller.bankSelection = {
id: 1,
bank: 'Cash',
accountingType: {
id: 2,
receiptDescription: 'Cash'
}
};
expect(controller.receipt.description).toEqual('Cash, Albaran: 1, 2');
});
});
describe('amountToReturn() setter', () => {
it('should set the amount to return with a maximum of two decimals', () => {
controller.amountToReturn = 200.1451789;
expect(controller.amountToReturn).toEqual(200.15);
});
});
describe('getAmountPaid()', () => {
it('should make an http GET query and then set the receipt amountPaid property', () => {
controller.$params = {id: 1101};
const receipt = controller.receipt;
const filter = {
where: {
clientFk: 1101,
companyFk: 442
}
};
const serializedParams = $httpParamSerializer({filter});
$httpBackend.expect('GET', `ClientRisks?${serializedParams}`).respond([{amount: 20}]);
controller.getAmountPaid();
$httpBackend.flush();
expect(receipt.amountPaid).toEqual(20);
});
});
describe('responseHandler()', () => {
it('should make an http POST query and then call to the parent responseHandler() method', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.vnReport, 'show');
controller.$params = {id: 1101};
$httpBackend.expect('POST', `Clients/1101/createReceipt`).respond({id: 1});
controller.responseHandler('accept');
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.vnReport.show).not.toHaveBeenCalled();
});
it('should make an http POST query and then call to the report show() method', () => {
const receiptId = 1;
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.vnReport, 'show');
window.open = jest.fn();
controller.$params = {id: 1101};
controller.viewReceipt = true;
$httpBackend.expect('POST', `Clients/1101/createReceipt`).respond({id: receiptId});
controller.responseHandler('accept');
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
expect(controller.vnReport.show).toHaveBeenCalledWith(`Receipts/${receiptId}/receipt-pdf`);
});
});
describe('deliveredAmount() setter', () => {
it('should set the deliveredAmount property', () => {
controller.amountPaid = 999;
controller.deliveredAmount = 1000;
expect(controller.amountToReturn).toEqual(1);
});
});
describe('accountShortToStandard()', () => {
it('should get de account in stardard format', () => {
const shortAccount = '4.3';
controller.accountShortToStandard(shortAccount);
expect(controller.receipt.compensationAccount).toEqual('4000000003');
});
});
describe('bankSearchFunc()', () => {
it('should return the filter by id property for an input of a number', () => {
const bankId = 1;
const result = controller.bankSearchFunc(bankId);
expect(result).toEqual({id: bankId});
});
it('should return the filter by bank property for an input of an string', () => {
const bankName = 'Bank of America';
const result = controller.bankSearchFunc(bankName);
expect(result).toEqual({bank: {like: '%' + bankName + '%'}});
});
});
});
});

View File

@ -1,4 +0,0 @@
View receipt: Ver recibo
Amount exceeded: Según ley contra el fraude no se puede recibir cobros por importe igual o superior a {{maxAmount}}
Client Compensation Reference: "({{clientId}}) Ntro Cliente: {{clientName}}"
Supplier Compensation Reference: "({{supplierId}}) Ntro Proveedor: {{supplierName}}"

View File

@ -1,163 +0,0 @@
<vn-crud-model
vn-id="model"
url="Receipts/filter"
limit="20"
data="$ctrl.balances">
</vn-crud-model>
<vn-crud-model
vn-id="riskModel"
url="ClientRisks"
filter="$ctrl.filter"
data="$ctrl.clientRisks">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Companies"
data="companies"
order="code">
</vn-crud-model>
<vn-side-menu side="right">
<div class="vn-pa-md">
<vn-autocomplete
vn-one
vn-id="company"
ng-model="$ctrl.companyId"
data="companies"
show-field="code"
value-field="id"
label="Company">
</vn-autocomplete>
<div
class="totalBox"
style="text-align: center;"
ng-if="$ctrl.clientRisks.length">
<h6 translate>Total by company</h6>
<vn-label-value
ng-repeat="riskByCompany in $ctrl.clientRisks"
label="{{riskByCompany.company.code}}"
value="{{riskByCompany.amount | currency: 'EUR':2}}">
</vn-label-value>
</div>
</div>
</vn-side-menu>
<div class="vn-w-lg">
<vn-data-viewer model="model">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th expand>Date</vn-th>
<vn-th>Creation date</vn-th>
<vn-th>Employee</vn-th>
<vn-th>Reference</vn-th>
<vn-th number>Bank</vn-th>
<vn-th number>Debit</vn-th>
<vn-th number>Havings</vn-th>
<vn-th number>Balance</vn-th>
<vn-th center>Conciliated</vn-th>
<vn-th></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="balance in $ctrl.balances">
<vn-td expand>
<span title="{{::balance.payed | date:'dd/MM/yyyy'}}">
{{::balance.payed | date:'dd/MM/yyyy'}}
</span>
</vn-td>
<vn-td shrink-datetime>
<span title="{{::balance.created | date:'dd/MM/yyyy HH:mm'}}">
{{::balance.created | date:'dd/MM/yyyy HH:mm'}}
</span>
</vn-td>
<vn-td>
<span
vn-click-stop="workerDescriptor.show($event, balance.workerFk)"
class="link">
{{::balance.userName}}
</span>
</vn-td>
<vn-td-editable disabled="balance.isInvoice || !$ctrl.isAdministrative" expand>
<text>
<div ng-show="::balance.description">
<span
ng-if="balance.isInvoice"
title="{{'BILL' | translate: {ref: balance.description} }}"
vn-click-stop="$ctrl.showInvoiceOutDescriptor($event, balance)"
class="link">
{{'BILL' | translate: {ref: balance.description} }}
</span>
<span
ng-if="!balance.isInvoice"
title="{{::balance.description}}">
{{balance.description}}
</span>
</div>
</text>
<field>
<vn-textfield vn-acl="administrative" class="dense" vn-focus
ng-model="balance.description"
on-change="$ctrl.changeDescription(balance)">
</vn-textfield>
</field>
</vn-td-editable>
<vn-td number>{{::balance.bankFk}}</vn-td>
<vn-td number expand>{{::balance.debit | currency: 'EUR':2}}</vn-td>
<vn-td number expand>{{::balance.credit | currency: 'EUR':2}}</vn-td>
<vn-td number expand>{{balance.balance | currency: 'EUR':2}}</vn-td>
<vn-td center shrink>
<vn-check
ng-model="balance.isConciliate"
disabled="true">
</vn-check>
</vn-td>
<vn-td center shrink>
<a ng-show="balance.hasPdf"
target="_blank"
href="api/InvoiceOuts/{{::balance.id}}/download?access_token={{::$ctrl.vnToken.tokenMultimedia}}">
<vn-icon-button
icon="cloud_download"
title="{{'Download PDF' | translate}}">
</vn-icon-button>
</a>
</vn-td>
<vn-td center shrink ng-if="balance.isCompensation">
<vn-icon-button
vn-dialog="send_compensation"
icon="outgoing_mail"
title="{{'Send compensation' | translate}}">
</vn-icon-button>
</vn-td>
<vn-confirm
vn-id="send_compensation"
on-accept="$ctrl.sendEmail(balance)"
question="Notify compensation"
message="Send compensation">
</vn-confirm>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
</div>
<vn-float-button
vn-acl="salesAssistant"
vn-acl-action="remove"
icon="add"
vn-tooltip="New payment"
vn-bind="+"
fixed-bottom-right
ng-click="balanceCreate.show()">
</vn-float-button>
<vn-client-balance-create
vn-id="balance-create"
on-accept="$ctrl.getData()"
company-fk="$ctrl.companyId"
client-fk="$ctrl.$params.id">
</vn-client-balance-create>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
<vn-invoice-out-descriptor-popover
vn-id="invoiceOutDescriptor">
</vn-invoice-out-descriptor-popover>

View File

@ -1,105 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $, vnEmail) {
super($element, $);
this.vnEmail = vnEmail;
this.filter = {
include: {
relation: 'company',
scope: {
fields: ['code'],
},
}
};
}
get companyId() {
if (!this._companyId)
this.companyId = this.vnConfig.companyFk;
return this._companyId;
}
set companyId(value) {
this._companyId = value;
if (value) this.getData();
}
get balances() {
return this._balances;
}
set balances(value) {
this._balances = value;
const riskModel = this.$.riskModel;
if (value && riskModel.data)
this.getBalances();
}
get isAdministrative() {
return this.aclService.hasAny(['administrative']);
}
getData() {
return this.$.model.applyFilter(null, {
clientId: this.$params.id,
companyId: this.companyId
}).then(() => this.$.riskModel.applyFilter({
where: {
clientFk: this.$params.id,
companyFk: this.companyId
}
})).then(() => this.getBalances());
}
getCurrentBalance() {
const clientRisks = this.$.riskModel.data;
const selectedCompany = this.companyId;
const currentBalance = clientRisks.find(balance => {
return balance.companyFk === selectedCompany;
});
return currentBalance && currentBalance.amount;
}
getBalances() {
const balances = this.$.model.data;
balances.forEach((balance, index) => {
if (index === 0)
balance.balance = this.getCurrentBalance();
if (index > 0) {
let previousBalance = balances[index - 1];
balance.balance = previousBalance.balance - (previousBalance.debit - previousBalance.credit);
}
});
}
showInvoiceOutDescriptor(event, balance) {
if (!balance.isInvoice) return;
if (event.defaultPrevented) return;
this.$.invoiceOutDescriptor.show(event.target, balance.id);
}
changeDescription(balance) {
const params = {description: balance.description};
const endpoint = `Receipts/${balance.id}`;
this.$http.patch(endpoint, params)
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')));
}
sendEmail(balance) {
return this.vnEmail.send(`Receipts/${balance.id}/balance-compensation-email`);
}
}
Controller.$inject = ['$element', '$scope', 'vnEmail'];
ngModule.vnComponent('vnClientBalanceIndex', {
template: require('./index.html'),
controller: Controller,
});

View File

@ -1,169 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientBalanceIndex', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
let $scope = $rootScope.$new();
const $element = angular.element('<vn-client-balance-index></vn-client-balance-index>');
controller = $componentController('vnClientBalanceIndex', {$element, $scope});
controller.$.model = {applyFilter: () => {}};
controller.$.riskModel = {
applyFilter: () => {},
data:
[{
clientFk: 1101,
companyFk: 442,
amount: 713.24,
company: {
id: 442,
code: 'VNL'
}
}]
};
}));
describe('getData()', () => {
it('should apply the filters on he models and get the client balance', () => {
controller._companyId = 442;
controller.$params.id = 1101;
jest.spyOn(controller, 'getBalances').mockReturnThis();
jest.spyOn(controller.$.model, 'applyFilter').mockReturnValue(Promise.resolve());
jest.spyOn(controller.$.riskModel, 'applyFilter').mockReturnValue(Promise.resolve());
controller.getData().then(() => {
expect(controller.$.model.applyFilter).toHaveBeenCalledWith(null, {'clientId': 1101, 'companyId': 442});
expect(controller.$.riskModel.applyFilter).toHaveBeenCalledWith({'where': {'clientFk': 1101, 'companyFk': 442}});
expect(controller.getBalances).toHaveBeenCalledWith();
});
});
});
describe('company setter/getter', () => {
it('should return the company and then call getData()', () => {
jest.spyOn(controller, 'getData').mockReturnThis();
controller.companyId = 442;
expect(controller._companyId).toEqual(442);
expect(controller.getData).toHaveBeenCalledWith();
});
});
describe('getCurrentBalance()', () => {
it('should return the client balance amount', () => {
controller._companyId = 442;
let result = controller.getCurrentBalance();
expect(result).toEqual(713.24);
});
});
describe('getBalances()', () => {
it('should return the total client balance amount', () => {
jest.spyOn(controller, 'getCurrentBalance');
controller._companyId = 442;
controller.$.model = {data:
[{
id: 1,
debit: 1000,
credit: null
},
{
id: 2,
debit: null,
credit: 500
},
{
id: 3,
debit: null,
credit: 300
}
]};
controller.getBalances();
const expectedBalances = controller.$.model.data;
expect(expectedBalances[0].balance).toEqual(713.24);
expect(expectedBalances[1].balance).toEqual(-286.76);
expect(expectedBalances[2].balance).toEqual(213.24);
});
});
describe('balances() setter', () => {
it('should set the balances data and not call the getBalances() method', () => {
jest.spyOn(controller, 'getBalances');
controller.$.riskModel.data = null;
controller.balances = [{
id: 1,
debit: 1000,
credit: null
}, {
id: 2,
debit: null,
credit: 500
}, {
id: 3,
debit: null,
credit: 300
}];
expect(controller.balances).toBeDefined();
expect(controller.getBalances).not.toHaveBeenCalledWith();
});
it('should set the balances data and then call the getBalances() method', () => {
jest.spyOn(controller, 'getBalances').mockReturnThis();
controller.balances = [{
id: 1,
debit: 1000,
credit: null
}, {
id: 2,
debit: null,
credit: 500
}, {
id: 3,
debit: null,
credit: 300
}];
expect(controller.balances).toBeDefined();
expect(controller.getBalances).toHaveBeenCalledWith();
});
});
describe('changeDescription()', () => {
it('should make an http PATCH query', () => {
const expectedParams = {description: 'Web'};
$httpBackend.expect('PATCH', `Receipts/1`, expectedParams).respond(200);
controller.changeDescription({
id: 1,
description: 'Web',
accountingType: {
description: 'Cash'
}
});
$httpBackend.flush();
});
});
describe('sendEmail()', () => {
it('should send an email', () => {
jest.spyOn(controller.vnEmail, 'send');
const $data = {id: 1103};
controller.sendEmail($data);
const expectedPath = `Receipts/${$data.id}/balance-compensation-email`;
expect(controller.vnEmail.send).toHaveBeenCalledWith(expectedPath);
});
});
});
});

View File

@ -1,3 +0,0 @@
BILL: N/INV {{ref}}
Notify compensation: Do you want to report compensation to the client by mail?
Send compensation: Send compensation

View File

@ -1,13 +0,0 @@
Creation date: Fecha de creación
Reference: Referencia
Bank: Caja
Debit: Debe
Conciliated: Conciliado
New payment: Añadir pago
Havings: Haber
Balance: Balance
Total by company: Total por empresa
Download PDF: Descargar PDF
Send compensation: Enviar compensación
BILL: N/FRA {{ref}}
Notify compensation: ¿Desea informar de la compensación al cliente por correo?

View File

@ -1,2 +0,0 @@
Compensation: Compensación
Cash: Efectivo

View File

@ -1,103 +0,0 @@
<mg-ajax path="Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.client"
form="form"
save="patch">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="ContactChannels"
data="contactChannels">
</vn-crud-model>
<form name="form" vn-http-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-one
label="Comercial Name"
ng-model="$ctrl.client.name"
rule
vn-focus>
</vn-textfield>
<vn-autocomplete
vn-one
ng-model="$ctrl.client.businessTypeFk"
url="BusinessTypes"
show-field="description"
value-field="code"
label="Business type">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Contact"
ng-model="$ctrl.client.contact"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Email"
ng-model="$ctrl.client.email"
rule
info="You can save multiple emails">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Phone"
ng-model="$ctrl.client.phone"
rule>
</vn-textfield>
<vn-textfield
vn-one
label="Mobile"
ng-model="$ctrl.client.mobile"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-worker-autocomplete
vn-one
ng-model="$ctrl.client.salesPersonFk"
departments="['VT', 'shopping']"
show-field="nickname"
label="Salesperson"
vn-acl="salesAssistant">
</vn-worker-autocomplete>
<vn-autocomplete
vn-one
ng-model="$ctrl.client.contactChannelFk"
data="contactChannels"
label="Channel">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
ng-model="$ctrl.client.transferorFk"
url="Clients"
search-function="$ctrl.transferorSearchFunction($search)"
where="{id: {neq: $ctrl.client.id}}"
show-field="name"
value-field="id"
label="Previous client"
info="In case of a company succession, specify the grantor company"
rule>
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,22 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
transferorSearchFunction($search) {
return /^\d+$/.test($search)
? {id: $search}
: {name: {like: '%' + $search + '%'}};
}
onSubmit() {
return this.$.watcher.submit();
}
}
ngModule.vnComponent('vnClientBasicData', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,16 +0,0 @@
Comercial Name: Nombre comercial
Tax number: NIF/CIF
Social name: Razón social
Phone: Teléfono
Mobile: Móvil
Fax: Fax
Email: E-mail
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
Undo changes: Deshacer cambios
Business type: Tipo de negocio

View File

@ -1,103 +0,0 @@
<mg-ajax path="Clients/{{patch.params.id}}" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.client"
form="form"
save="patch">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="PayMethods"
data="paymethods">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-autocomplete
label="Billing data"
vn-acl="salesAssistant, hr"
ng-model="$ctrl.client.payMethodFk"
data="paymethods"
fields="['isIbanRequiredForClients']"
initial-data="$ctrl.client.payMethod">
</vn-autocomplete>
<vn-input-number
min="0"
step="1"
label="Due day"
ng-model="$ctrl.client.dueDay"
vn-acl="salesAssistant, hr"
rule>
</vn-input-number>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="IBAN"
ng-keyup="$ctrl.client.iban = $ctrl.client.iban.toUpperCase()"
ng-model="$ctrl.client.iban"
rule
on-change="$ctrl.autofillBic()"
vn-acl="salesAssistant, hr">
</vn-textfield>
<vn-autocomplete
label="Swift / BIC"
url="BankEntities"
ng-model="$ctrl.client.bankEntityFk"
fields="['name', 'bic']"
initial-data="$ctrl.client.bankEntityFk"
on-change="$ctrl.autofillBic()"
search-function="{or: [{bic: {like: $search +'%'}}, {name: {like: '%'+ $search +'%'}}]}"
value-field="id"
show-field="bic"
vn-acl="salesAssistant, hr"
disabled="$ctrl.ibanCountry == 'ES'">
<tpl-item>
<div>{{::bic}}</div>
<div class="text-secondary text-caption">{{::name}}</div>
</tpl-item>
<append>
<vn-icon-button
vn-auto
icon="add_circle"
vn-click-stop="bankEntity.show({countryFk: $ctrl.client.countryFk})"
vn-tooltip="New bank entity"
vn-acl="salesAssistant, hr">
</vn-icon-button>
</append>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Received LCR"
ng-model="$ctrl.client.hasLcr"
vn-acl="salesAssistant, hr">
</vn-check>
<vn-check
label="Received core VNL"
ng-model="$ctrl.client.hasCoreVnl"
vn-acl="salesAssistant, hr">
</vn-check>
<vn-check
label="Received B2B VNL"
ng-model="$ctrl.client.hasSepaVnl"
vn-acl="salesAssistant, hr">
</vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>
<vn-new-bank-entity
vn-id="bankEntity"
on-accept="$ctrl.onAccept($data)">
</vn-new-bank-entity>

View File

@ -1,77 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
get client() {
return this._client;
}
set client(value) {
this._client = value;
if (!value) return;
if (!value.bankEntityFk)
this.autofillBic();
}
onSubmit() {
let shouldNotify = false;
if (this.hasPaymethodChanges())
shouldNotify = true;
this.$.watcher.submit().then(() => {
if (shouldNotify)
this.vnApp.showMessage(this.$t('Notification sent!'));
});
}
hasPaymethodChanges() {
let orgData = this.$.watcher.orgData;
let payMethod = orgData.payMethodFk != this.client.payMethodFk;
let iban = orgData.iban != this.client.iban;
let dueDay = orgData.dueDay != this.client.dueDay;
return payMethod || iban || dueDay;
}
onAccept(data) {
this.client.bankEntityFk = data.id;
}
get ibanCountry() {
if (!this.client || !this.client.iban) return false;
let countryCode = this.client.iban.substr(0, 2);
return countryCode;
}
autofillBic() {
if (!this.client || !this.client.iban) return;
let bankEntityId = parseInt(this.client.iban.substr(4, 4));
let filter = {where: {id: bankEntityId}};
if (this.ibanCountry != 'ES') return;
this.$http.get(`BankEntities`, {filter}).then(response => {
const hasData = response.data && response.data[0];
if (hasData)
this.client.bankEntityFk = response.data[0].id;
else if (!hasData)
this.client.bankEntityFk = null;
});
}
}
ngModule.vnComponent('vnClientBillingData', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,94 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientBillingData', () => {
let $httpBackend;
let $scope;
let controller;
let vnApp;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _vnApp_) => {
let $element = angular.element('<vn-client-billing-data></vn-client-billing-data>');
$httpBackend = _$httpBackend_;
vnApp = _vnApp_;
$scope = $rootScope.$new();
$scope.watcher = {};
jest.spyOn(vnApp, 'showError');
controller = $componentController('vnClientBillingData', {$element, $scope});
controller.client = {id: 1101, name: 'Client name', payMethodFk: 4};
$scope.watcher.orgData = {id: 1101, name: 'Client name', payMethodFk: 4};
}));
describe('hasPaymethodChanges()', () => {
it(`should return true if there are changes on payMethod data`, () => {
controller.client.payMethodFk = 5;
expect(controller.hasPaymethodChanges()).toBeTruthy();
});
it(`should return false if there are no changes on payMethod data`, () => {
controller.client.payMethodFk = 4;
expect(controller.hasPaymethodChanges()).toBeFalsy();
});
});
describe('onAccept()', () => {
it('should assign the response id to the client bankEntityFk', () => {
const expectedResponse = {id: 999};
controller.onAccept(expectedResponse);
expect(controller.client.bankEntityFk).toEqual(expectedResponse.id);
});
});
describe('autofillBic()', () => {
it(`Should do nothing if there is not client`, () => {
controller.client = undefined;
controller.autofillBic();
expect(controller.client).toBeUndefined();
});
it(`Should do nothing if the iban is not spanish`, () => {
controller.client.iban = 'FR9121000418450200051332';
controller.autofillBic();
expect(controller.client.bankEntityFk).toBeUndefined();
});
it(`Should set the bankEntityId in the client`, () => {
controller.client.iban = 'ES9121000418450200051332';
$httpBackend.whenRoute('GET', `BankEntities`).respond([{id: 123}]);
controller.autofillBic();
$httpBackend.flush();
expect(controller.client.bankEntityFk).toEqual(123);
});
it(`Should set clients bankEntityFk to null if no bank entity founds`, () => {
controller.client.iban = 'ES9121000418450200051332';
$httpBackend.whenRoute('GET', `BankEntities`).respond([]);
controller.autofillBic();
$httpBackend.flush();
expect(controller.client.bankEntityFk).toBeNull();
});
});
describe('ibanCountry()', () => {
it('should return a country code from iban', () => {
controller.client.iban = 'ES123';
let countryCode = controller.ibanCountry;
expect(countryCode).toEqual('ES');
});
});
});
});

View File

@ -1,14 +0,0 @@
Changed terms: Payment terms have changed
Notify customer?: Do you want to notify customer?
No: No
Yes, notify: Yes, notify
Notification sent!: Notification sent!
Notification error: Error while sending notification
Yes, propagate: Yes, propagate
Equivalent tax spreaded: Equivalent tax spreaded
Invoice by address: Invoice by address
Equalization tax: Equalization tax
Due day: Due day
Received core VNL: VNL core received
Received B2B VNL: VNL B2B received
Save: Save

View File

@ -1,17 +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
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 LCR: Recibido LCR
Received core VNL: Recibido core VNL
Received B2B VNL: Recibido B2B VNL
Save: Guardar
New bank entity: Nueva entidad bancaria
Name can't be empty: El nombre no puede quedar vacío

View File

@ -1,5 +0,0 @@
<vn-portal slot="menu">
<vn-client-descriptor client="$ctrl.client"></vn-client-descriptor>
<vn-left-menu source="card"></vn-left-menu>
</vn-portal>
<ui-view></ui-view>

View File

@ -1,15 +0,0 @@
import ngModule from '../module';
import ModuleCard from 'salix/components/module-card';
export default class Controller extends ModuleCard {
reload() {
this.$http.get(`Clients/${this.$params.id}/getCard`)
.then(res => this.client = res.data);
}
}
ngModule.vnComponent('vnClientCard', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,28 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientCard', () => {
let controller;
let $httpBackend;
let data = {id: 1, name: 'fooName'};
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_, $stateParams) => {
$httpBackend = _$httpBackend_;
let $element = angular.element('<div></div>');
controller = $componentController('vnClientCard', {$element});
$stateParams.id = data.id;
$httpBackend.whenRoute('GET', 'Clients/:id/getCard').respond(data);
}));
it('should request data and set it on the controller', () => {
controller.$onInit();
$httpBackend.flush();
expect(controller.client).toEqual(data);
});
});
});

View File

@ -1,85 +0,0 @@
<div class="search-panel">
<form class="vn-pa-lg" ng-submit="$ctrl.onSearch()">
<vn-horizontal>
<vn-textfield vn-focus
vn-one
label="General search"
ng-model="filter.search"
vn-focus>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
label="Item id"
ng-model="filter.itemId">
</vn-textfield>
<vn-autocomplete
vn-one
ng-model="filter.buyerId"
url="TicketRequests/getItemTypeWorker"
search-function="{firstName: $search}"
show-field="nickname"
value-field="id"
label="Buyer">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
ng-model="filter.typeId"
url="ItemTypes"
show-field="name"
value-field="id"
label="Type"
fields="['categoryFk']"
include="'category'">
<tpl-item>
<div>{{name}}</div>
<div class="text-caption text-secondary">
{{category.name}}
</div>
</tpl-item>
</vn-autocomplete>
<vn-autocomplete vn-one
url="ItemCategories"
label="Category"
show-field="name"
value-field="id"
ng-model="filter.categoryId">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
url="Campaigns/latest"
label="Campaign"
translate-fields="['code']"
show-field="code"
value-field="id"
ng-model="filter.campaign"
order="dated DESC"
selection="$ctrl.campaignSelection"
search-function="{dated: {like: '%'+ $search +'%'}}">
<tpl-item>
{{code}} {{dated | date: 'yyyy'}}
</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-date-picker
vn-one
label="From"
ng-model="filter.from"
on-change="$ctrl.onChangeDate(value)">
</vn-date-picker>
<vn-date-picker
vn-one
label="To"
ng-model="filter.to"
on-change="$ctrl.onChangeDate(value)">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal class="vn-mt-lg">
<vn-submit label="Search"></vn-submit>
</vn-horizontal>
</form>
</div>

View File

@ -1,56 +0,0 @@
import ngModule from '../module';
import SearchPanel from 'core/components/searchbar/search-panel';
class Controller extends SearchPanel {
constructor($, $element) {
super($, $element);
this.filter = this.$.filter;
if (!this.dateParams)
this.getUpcomingCampaing();
}
get dateParams() {
if (this.$params.q) {
const params = JSON.parse(this.$params.q);
if (params.from || params.to)
return true;
}
return false;
}
getUpcomingCampaing() {
this.$http.get('Campaigns/upcoming').then(res => {
this.filter.campaign = res.data.id;
});
}
onChangeDate(value) {
if (value)
this.filter.campaign = null;
}
get campaignSelection() {
return this._campaignSelection;
}
set campaignSelection(value) {
this._campaignSelection = value;
if (value) {
const from = new Date(value.dated);
from.setDate(from.getDate() - value.scopeDays);
this.filter.to = value.dated;
this.filter.from = from;
}
}
}
ngModule.vnComponent('vnConsumptionSearchPanel', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,31 +0,0 @@
import './index.js';
describe('Client', () => {
describe('Component vnConsumptionSearchPanel', () => {
let $httpBackend;
let $element;
let controller;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$element = angular.element(`<div></div>`);
controller = $componentController('vnConsumptionSearchPanel', {$element});
controller.$.filter = {};
$httpBackend.expect('GET', 'Campaigns/upcoming').respond(200, {id: 1});
}));
describe('getUpcomingCampaing()', () => {
it(`should make an HTTP query and then set the campaign property`, () => {
$httpBackend.expect('GET', 'Campaigns/upcoming').respond(200, {id: 2, code: 'allSaints'});
controller.getUpcomingCampaing();
$httpBackend.flush();
const filter = controller.$.filter;
expect(filter.campaign).toEqual(2);
});
});
});
});

View File

@ -1,3 +0,0 @@
allSaints: All Saints Day
valentinesDay: Valentine's Day
mothersDay: Mother's day

View File

@ -1,7 +0,0 @@
Item id: Id artículo
From: Desde
To: Hasta
Campaign: Campaña
allSaints: Día de todos los Santos
valentinesDay: Día de San Valentín
mothersDay: Día de la madre

View File

@ -1,97 +0,0 @@
<vn-crud-model vn-id="model"
url="Clients/consumption"
link="{clientFk: $ctrl.$params.id}"
filter="::$ctrl.filter"
limit="20"
user-params="::$ctrl.filterParams"
data="sales"
order="itemTypeFk, itemName, itemSize, description">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
panel="vn-consumption-search-panel"
suggested-filter="$ctrl.filterParams"
info="Search by item id or name"
model="model"
auto-state="false">
</vn-searchbar>
</vn-portal>
<vn-data-viewer model="model">
<vn-card class="vn-pa-lg vn-w-lg">
<section class="header">
<vn-tool-bar class="vn-mb-md">
<vn-button disabled="!model.userParams.from || !model.userParams.to"
icon="picture_as_pdf"
ng-click="$ctrl.showReport()"
vn-tooltip="Open as PDF">
</vn-button>
<vn-button disabled="!model.userParams.from || !model.userParams.to"
icon="email"
ng-click="confirm.show()"
vn-tooltip="Send to email">
</vn-button>
<vn-check
label="Group by item"
on-change="$ctrl.changeGrouped(value)">
</vn-check>
</vn-tool-bar>
</section>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="itemFk" number>Item</vn-th>
<vn-th field="ticketFk" number>Ticket</vn-th>
<vn-th field="shipped" expand>Fecha</vn-th>
<vn-th field="description" expand>Description</vn-th>
<vn-th field="quantity" number>Quantity</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr
ng-repeat="sale in sales">
<vn-td number>
<span
ng-click="itemDescriptor.show($event, sale.itemFk)"
class="link">
{{::sale.itemFk}}
</span>
</vn-td>
<vn-td number>
<span
ng-click="ticketDescriptor.show($event, sale.ticketFk)"
class="link">
{{::sale.ticketFk}}
</span>
</vn-td>
<vn-td expand>{{::sale.shipped | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td vn-fetched-tags wide>
<div>
<vn-one title="{{::sale.concept}}">{{::sale.concept}}</vn-one>
<vn-one ng-if="::sale.subName">
<h3 title="{{::sale.subName}}">{{::sale.subName}}</h3>
</vn-one>
</div>
<vn-fetched-tags
max-length="6"
item="::sale"
tabindex="-1">
</vn-fetched-tags>
</vn-td>
<vn-td number>{{::sale.quantity | dashIfEmpty}}</vn-td>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-item-descriptor-popover
vn-id="item-descriptor"
warehouse-fk="$ctrl.vnConfig.warehouseFk">
</vn-item-descriptor-popover>
<vn-ticket-descriptor-popover
vn-id="ticket-descriptor">
</vn-ticket-descriptor-popover>
<vn-confirm
vn-id="confirm"
question="Please, confirm"
message="The consumption report will be sent"
on-accept="$ctrl.sendEmail()">
</vn-confirm>

View File

@ -1,75 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $, vnReport, vnEmail) {
super($element, $);
this.vnReport = vnReport;
this.vnEmail = vnEmail;
this.filter = {
where: {
isPackaging: false
}
};
this.setDefaultFilter();
}
setDefaultFilter() {
const minDate = Date.vnNew();
minDate.setHours(0, 0, 0, 0);
minDate.setMonth(minDate.getMonth() - 2);
const maxDate = Date.vnNew();
maxDate.setHours(23, 59, 59, 59);
this.filterParams = {
from: minDate,
to: maxDate
};
}
get reportParams() {
const userParams = this.$.model.userParams;
return Object.assign({
recipient: this.client.email,
recipientId: this.client.id
}, userParams);
}
showTicketDescriptor(event, sale) {
if (!sale.isTicket) return;
this.$.ticketDescriptor.show(event.target, sale.origin);
}
showReport() {
const path = `Clients/${this.client.id}/campaign-metrics-pdf`;
this.vnReport.show(path, this.reportParams);
}
sendEmail() {
const path = `Clients/${this.client.id}/campaign-metrics-email`;
this.vnEmail.send(path, this.reportParams);
}
changeGrouped(value) {
const model = this.$.model;
model.addFilter({}, {grouped: value});
}
}
Controller.$inject = ['$element', '$scope', 'vnReport', 'vnEmail'];
ngModule.vnComponent('vnClientConsumption', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
},
require: {
card: '^vnClientCard'
}
});

View File

@ -1,67 +0,0 @@
import './index.js';
import crudModel from 'core/mocks/crud-model';
describe('Client', () => {
describe('Component vnClientConsumption', () => {
let $scope;
let controller;
let $httpParamSerializer;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpParamSerializer_, _$httpBackend_) => {
$scope = $rootScope.$new();
$httpParamSerializer = _$httpParamSerializer_;
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-client-consumption></vn-client-consumption');
controller = $componentController('vnClientConsumption', {$element, $scope});
controller.$.model = crudModel;
controller.client = {
id: 1101
};
}));
describe('showReport()', () => {
it('should call the window.open function', () => {
jest.spyOn(window, 'open').mockReturnThis();
const now = Date.vnNew();
controller.$.model.userParams = {
from: now,
to: now
};
controller.showReport();
const clientId = controller.client.id;
const expectedParams = {
recipientId: clientId,
from: now,
to: now
};
const serializedParams = $httpParamSerializer(expectedParams);
const expectedPath = `api/Clients/${clientId}/campaign-metrics-pdf?${serializedParams}`;
expect(window.open).toHaveBeenCalledWith(expectedPath);
});
});
describe('sendEmail()', () => {
it('should make a GET query sending the report', () => {
const now = Date.vnNew();
controller.$.model.userParams = {
from: now,
to: now
};
const clientId = controller.client.id;
const expectedPath = `Clients/${clientId}/campaign-metrics-email`;
$httpBackend.expect('POST', expectedPath).respond({});
controller.sendEmail();
$httpBackend.flush();
});
});
});
});

View File

@ -1,6 +0,0 @@
Group by item: Agrupar por artículo
Open as PDF: Abrir como PDF
Send to email: Enviar por email
Search by item id or name: Buscar por id de artículo o nombre
The consumption report will be sent: Se enviará el informe de consumo
Please, confirm: Por favor, confirma

View File

@ -1,61 +0,0 @@
<vn-crud-model
vn-id="model"
url="ClientContacts"
fields="['id', 'name', 'phone', 'clientFk']"
link="{clientFk: $ctrl.$params.id}"
data="contacts"
auto-load="true">
</vn-crud-model>
<vn-watcher
vn-id="watcher"
data="contacts"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal ng-repeat="contact in contacts">
<vn-textfield
vn-one
label="Name"
ng-model="contact.name"
rule="ClientContact">
</vn-textfield>
<vn-textfield
vn-one
label="Phone"
ng-model="contact.phone"
rule="ClientContact"
vn-focus>
</vn-textfield>
<vn-none>
<vn-icon-button
vn-tooltip="Remove contact"
icon="delete"
tabindex="-1"
ng-click="model.remove($index)">
</vn-icon-button>
</vn-none>
</vn-horizontal>
<vn-one>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add contact"
icon="add_circle"
ng-click="$ctrl.add()">
</vn-icon-button>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
<!-- # #2680 Undo changes button bugs -->
<!-- <vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button> -->
</vn-button-bar>
</form>

View File

@ -1,28 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
class Controller extends Section {
add() {
this.$.model.insert({
clientFk: this.client.id,
name: this.$t('Phone'),
phone: null
});
}
onSubmit() {
this.$.watcher.check();
this.$.model.save().then(() => {
this.$.watcher.notifySaved();
this.$.watcher.updateOriginalData();
});
}
}
ngModule.vnComponent('vnClientContact', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,150 +0,0 @@
<vn-watcher
vn-id="watcher"
url="Clients/createWithUser"
data="$ctrl.client"
insert-mode="true"
form="form">
</vn-watcher>
<form name="form" vn-http-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-two
label="Comercial Name"
ng-model="$ctrl.client.name"
rule
vn-focus>
</vn-textfield>
<vn-worker-autocomplete
label="Salesperson"
ng-model="$ctrl.client.salesPersonFk"
departments="['VT']"
show-field="nickname">
</vn-worker-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-id="businessTypeFk"
ng-model="$ctrl.client.businessTypeFk"
url="BusinessTypes"
show-field="description"
value-field="code"
label="Business type"
rule>
</vn-autocomplete>
<vn-textfield
label="Tax number"
ng-model="$ctrl.client.fi"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="Business name"
ng-model="$ctrl.client.socialName"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-two
label="Street"
ng-model="$ctrl.client.street"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-datalist
label="Postcode"
ng-model="$ctrl.client.postcode"
selection="$ctrl.postcode"
url="Postcodes/location"
fields="['code','townFk']"
order="code, townFk"
value-field="code"
show-field="code"
rule>
<tpl-item>
{{code}} - {{town.name}} ({{town.province.name}},
{{town.province.country.name}})
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New postcode"
ng-click="postcode.open()"
vn-acl="deliveryAssistant"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-datalist>
<vn-datalist
vn-id="town"
label="City"
ng-model="$ctrl.client.city"
selection="$ctrl.town"
url="Towns/location"
fields="['id', 'name', 'provinceFk']"
value-field="name">
<tpl-item>
{{name}}, {{province.name}}
({{province.country.name}})
</tpl-item>
</vn-datalist>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-id="province"
label="Province"
ng-model="$ctrl.client.provinceFk"
selection="$ctrl.province"
url="Provinces/location"
fields="['id', 'name', 'countryFk']"
rule>
<tpl-item>{{name}} ({{country.name}})</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-id="country"
label="Country"
ng-model="$ctrl.client.countryFk"
url="Countries"
show-field="name">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
label="Web user"
ng-model="$ctrl.client.userName"
rule>
</vn-textfield>
<vn-textfield
label="Email"
ng-model="$ctrl.client.email"
rule
info="You can save multiple emails">
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Is equalizated"
ng-model="$ctrl.client.isEqualizated">
</vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Create">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="client.index">
</vn-button>
</vn-button-bar>
</form>
<!-- New postcode dialog -->
<vn-geo-postcode
vn-id="postcode"
on-response="$ctrl.onResponse($response)">
</vn-geo-postcode>

View File

@ -1,95 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.client = {
active: true
};
}
onSubmit() {
return this.$.watcher.submit().then(json => {
this.$state.go('client.card.basicData', {id: json.data.id});
});
}
get province() {
return this._province;
}
// Province auto complete
set province(selection) {
this._province = selection;
if (!selection) return;
const country = selection.country;
if (!this.client.countryFk)
this.client.countryFk = country.id;
}
get town() {
return this._town;
}
// Town auto complete
set town(selection) {
this._town = selection;
if (!selection) return;
const province = selection.province;
const country = province.country;
const postcodes = selection.postcodes;
if (!this.client.provinceFk)
this.client.provinceFk = province.id;
if (!this.client.countryFk)
this.client.countryFk = country.id;
if (postcodes.length === 1)
this.client.postcode = postcodes[0].code;
}
get postcode() {
return this._postcode;
}
// Postcode auto complete
set postcode(selection) {
this._postcode = selection;
if (!selection) return;
const town = selection.town;
const province = town.province;
const country = province.country;
if (!this.client.city)
this.client.city = town.name;
if (!this.client.provinceFk)
this.client.provinceFk = province.id;
if (!this.client.countryFk)
this.client.countryFk = country.id;
}
onResponse(response) {
this.client.postcode = response.code;
this.client.city = response.city;
this.client.provinceFk = response.provinceFk;
this.client.countryFk = response.countryFk;
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,122 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientCreate', () => {
let $scope;
let $state;
let controller;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$state_) => {
$scope = $rootScope.$new();
$state = _$state_;
$scope.watcher = {
submit: () => {
return {
then: callback => {
callback({data: {id: '1234'}});
}
};
}
};
const $element = angular.element('<vn-client-create></vn-client-create>');
controller = $componentController('vnClientCreate', {$element, $scope});
}));
it('should define and set scope, state and client properties', () => {
expect(controller.$).toBe($scope);
expect(controller.$state).toBe($state);
expect(controller.client.active).toBe(true);
});
describe('onSubmit()', () => {
it(`should call submit() on the watcher then expect a callback`, () => {
jest.spyOn($state, 'go');
controller.onSubmit();
expect(controller.$state.go).toHaveBeenCalledWith('client.card.basicData', {id: '1234'});
});
});
describe('province() setter', () => {
it(`should set countryFk property`, () => {
controller.client.countryFk = null;
controller.province = {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
};
expect(controller.client.countryFk).toEqual(2);
});
});
describe('town() setter', () => {
it(`should set provinceFk property`, () => {
controller.town = {
provinceFk: 1,
code: 46001,
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
},
postcodes: []
};
expect(controller.client.provinceFk).toEqual(1);
});
it(`should set provinceFk property and fill the postalCode if there's just one`, () => {
controller.town = {
provinceFk: 1,
code: 46001,
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
},
postcodes: [{code: '46001'}]
};
expect(controller.client.provinceFk).toEqual(1);
expect(controller.client.postcode).toEqual('46001');
});
});
describe('postcode() setter', () => {
it(`should set the town, provinceFk and contryFk properties`, () => {
controller.postcode = {
townFk: 1,
code: 46001,
town: {
id: 1,
name: 'New York',
province: {
id: 1,
name: 'New york',
country: {
id: 2,
name: 'USA'
}
}
}
};
expect(controller.client.city).toEqual('New York');
expect(controller.client.provinceFk).toEqual(1);
expect(controller.client.countryFk).toEqual(2);
});
});
});
});

View File

@ -1,11 +0,0 @@
Name: Nombre
Tax number: NIF/CIF
Business name: Razón social
Web user: Usuario Web
Email: E-mail
Create and edit: Crear y editar
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
The type of business must be filled in basic data: El tipo de negocio debe estar rellenado en datos básicos

View File

@ -1,37 +0,0 @@
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-input-number
vn-one
min="0"
label="Credit"
ng-model="$ctrl.creditClassification.credit"
rule
vn-focus>
</vn-input-number>
<vn-input-number
vn-one
min="0"
step="1"
label="Grade"
ng-model="$ctrl.creditClassification.grade"
rule>
</vn-input-number>
<vn-date-picker
vn-one
label="Since"
ng-model="$ctrl.creditClassification.started">
</vn-date-picker>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="client.card.creditInsurance.index"></vn-button>
</vn-button>
</vn-button-bar>
</form>

View File

@ -1,40 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.creditClassification = {
started: this.$filter('date')(Date.vnNew(), 'yyyy-MM-dd HH:mm')
};
}
onSubmit() {
if (this.$.form.$invalid)
return this.vnApp.showError(this.$t('Some fields are invalid'));
let query = `creditClassifications/createWithInsurance`;
let data = this.creditClassification;
data.clientFk = this.client.id;
this.$http.post(query, data).then(res => {
if (res.data) {
this.card.reload();
this.$state.go('client.card.creditInsurance.index');
}
});
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientCreditInsuranceCreate', {
template: require('./index.html'),
controller: Controller,
require: {
card: '^vnClientCard'
},
bindings: {
client: '<'
}
});

View File

@ -1,48 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientCreditInsuranceCreate', () => {
let controller;
let $scope;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
$scope.form = {
$invalid: false
};
const $element = angular.element('<vn-client-credit-insurance-create></vn-client-credit-insurance-create>');
controller = $componentController('vnClientCreditInsuranceCreate', {$element, $scope});
controller.client = {id: 1101};
controller.card = {
reload: () => {}
};
}));
describe('onSubmit()', () => {
it('should perform a POST query', () => {
let started = Date.vnNew();
controller.creditClassification = {
started: started,
credit: 300,
grade: 1
};
let newData = {
started: started,
credit: 300,
grade: 1,
clientFk: 1101
};
$httpBackend.whenPOST(`creditClassifications/createWithInsurance`, newData).respond(200, true);
$httpBackend.expectPOST(`creditClassifications/createWithInsurance`, newData);
controller.onSubmit();
$httpBackend.flush();
});
});
});
});

View File

@ -1,68 +0,0 @@
<vn-data-viewer
data="$ctrl.classifications"
class="vn-w-md">
<vn-card class="vn-pa-md">
<vn-horizontal
ng-repeat="classification in $ctrl.classifications track by classification.id"
class="vn-pb-md insurance"
style="align-items: center;">
<vn-one
class="vn-pa-sm border-solid border-radius"
ng-class="{'item-hightlight': !classification.finished,'item-disabled': classification.finished}">
<vn-horizontal style="align-items: center;">
<vn-none class="vn-px-md">
<vn-icon-button
ng-if="!classification.finished"
icon="lock"
vn-tooltip="Close contract"
ng-click="$ctrl.closeContract(classification)">
</vn-icon-button>
</vn-none>
<vn-one class="border-solid-right">
<div><vn-label translate>Since</vn-label> {{::classification.started | date:'dd/MM/yyyy'}}</div>
<div><vn-label translate>To</vn-label> {{classification.finished | date:'dd/MM/yyyy'}}</div>
</vn-one>
<vn-vertical vn-one class="vn-px-md">
<vn-horizontal ng-repeat="insurance in classification.insurances track by insurance.id">
<vn-one>
<vn-label-value label="Credit"
value="{{::insurance.credit}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value label="Grade"
value="{{::insurance.grade}}">
</vn-label-value>
</vn-one>
<vn-one>
<vn-label-value label="Date"
value="{{::insurance.created | date:'dd/MM/yyyy' }}">
</vn-label-value>
</vn-one>
</vn-horizontal>
</vn-vertical>
<a vn-auto ui-sref="client.card.creditInsurance.insurance.index({classificationId: {{classification.id}}})">
<vn-icon-button icon="preview" vn-tooltip="View credits"></vn-icon-button>
</a>
</vn-horizontal>
</vn-one>
</vn-horizontal>
</vn-card>
</vn-data-viewer>
<vn-float-button
ng-if="$ctrl.canCreateNew()"
vn-acl="insurance"
vn-acl-action="remove"
vn-tooltip="New contract"
fixed-bottom-right
ui-sref="client.card.creditInsurance.create"
icon="add"
vn-bind="+"
label="Add">
</vn-float-button>
<vn-confirm
vn-id="close-contract"
on-accept="$ctrl.returnDialog()"
question="Close contract"
message="Are you sure you want to close this contract?">
</vn-confirm>

View File

@ -1,68 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
$onChanges() {
if (this.client && this.client.id)
this._getClassifications(this.client.id);
}
_getClassifications(clientId) {
let filter = {
order: 'finished ASC, started DESC',
include: [
{
relation: 'insurances',
scope: {
fields: ['id', 'credit', 'created', 'grade'],
order: 'created DESC',
limit: 2
}
}
],
where: {client: clientId}
};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `CreditClassifications?filter=${filter}`;
this.$http.get(query).then(res => {
if (res.data)
this.classifications = res.data;
});
}
canCreateNew() {
if (!this.classifications)
return false;
let items = this.classifications;
for (let i = 0; i < items.length; i++) {
if (!items[i].finished)
return false;
}
return true;
}
closeContract(classification) {
this.classificationId = classification.id;
this.$.closeContract.show();
}
returnDialog() {
let params = {finished: Date.vnNow()};
this.$http.patch(`CreditClassifications/${this.classificationId}`, params).then(() => {
this._getClassifications(this.client.id);
});
}
}
ngModule.vnComponent('vnClientCreditInsuranceIndex', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,88 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientCreditInsuranceIndex', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
const $element = angular.element('<vn-client-credit-insurance-index></vn-client-credit-insurance-index>');
controller = $componentController('vnClientCreditInsuranceIndex', {$element, $scope});
controller.client = {id: 1101};
}));
describe('_getClassifications()', () => {
it('should perform a GET query to define the classifications property in the controller', () => {
let res = ['some classifications'];
let query = 'CreditClassifications?filter=%7B%22order%22%3A%22finished%20ASC%2C%20started%20DESC%22%2C%22include%22%3A%5B%7B%22relation%22%3A%22insurances%22%2C%22scope%22%3A%7B%22fields%22%3A%5B%22id%22%2C%22credit%22%2C%22created%22%2C%22grade%22%5D%2C%22order%22%3A%22created%20DESC%22%2C%22limit%22%3A2%7D%7D%5D%2C%22where%22%3A%7B%7D%7D';
$httpBackend.whenGET(query).respond(res);
$httpBackend.expectGET(query);
controller._getClassifications();
$httpBackend.flush();
expect(controller.classifications).toEqual(['some classifications']);
});
});
describe('canCreateNew()', () => {
it(`should return false if doesn't have classifications`, () => {
let result = controller.canCreateNew();
expect(result).toBeFalsy();
});
it(`should return false if finds a classification without due date`, () => {
controller.classifications = [
{finished: Date.vnNow()},
{finished: null}
];
let result = controller.canCreateNew();
expect(result).toBeFalsy();
});
it(`should return true if all classifications are defined with due date`, () => {
controller.classifications = [
{finished: Date.vnNow()},
{finished: Date.vnNow()}
];
let result = controller.canCreateNew();
expect(result).toBeTruthy();
});
});
describe('closeContract()', () => {
it('should define the classificationId property of the controller and then call the show method()', () => {
controller.$.closeContract = {show: () => {}};
jest.spyOn(controller.$.closeContract, 'show');
expect(controller.classificationId).toBeFalsy();
controller.closeContract({id: 1});
expect(controller.classificationId).toEqual(1);
expect(controller.$.closeContract.show).toHaveBeenCalledWith();
});
});
describe('returnDialog()', () => {
it('should call the returnDialog method and perform a PATCH query, then call _getClassifications method', () => {
jest.spyOn(controller, '_getClassifications').mockReturnThis();
controller.classificationId = 1;
$httpBackend.expect('PATCH', `CreditClassifications/1`).respond(200);
controller.returnDialog();
$httpBackend.flush();
expect(controller._getClassifications).toHaveBeenCalledWith(1101);
});
});
});
});

View File

@ -1,4 +0,0 @@
Contract credit insurance: Contratos de seguro de crédito
Close contract: Cerrar contrato
View credits: Ver créditos
Are you sure you want to close this contract?: ¿Seguro que quieres cerrar este contrato?

View File

@ -1,5 +0,0 @@
vn-client-credit-insurance-index{
.insurance:last-child {
padding-bottom: 0;
}
}

View File

@ -1,40 +0,0 @@
<vn-watcher
vn-id="watcher"
url="CreditClassifications/{{$ctrl.$params.classificationId}}/insurances"
data="$ctrl.insurance"
insert-mode="true"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-input-number
min="0"
label="Credit"
ng-model="$ctrl.insurance.credit"
rule="CreditInsurance"
vn-focus>
</vn-input-number>
<vn-date-picker
label="Date"
ng-model="$ctrl.insurance.created">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-input-number
min="0"
step="1"
label="Grade"
ng-model="$ctrl.insurance.grade"
rule="CreditInsurance">
</vn-input-number>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button
label="Cancel"
ui-sref="client.card.creditInsurance.insurance.index({classificationId: $ctrl.$params.classificationId})">
</vn-button>
</vn-button-bar>
</form>

View File

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

View File

@ -1,40 +0,0 @@
<vn-crud-model
vn-id="model"
url="CreditInsurances"
link="{creditClassificationFk: $ctrl.$params.classificationId}"
limit="20"
data="insurances"
auto-load="true">
</vn-crud-model>
<div class="vn-w-xs">
<vn-data-viewer model="model">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th>Created</vn-th>
<vn-th number>Grade</vn-th>
<vn-th number>Credit</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="insurance in insurances">
<vn-td>{{::insurance.created | date: 'dd/MM/yyyy'}}</vn-td>
<vn-td number>{{::insurance.grade}}</vn-td>
<vn-td number>{{::insurance.credit | currency: 'EUR': 2}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
</div>
<a
ng-if="!$ctrl.isClosed"
vn-acl="insurance"
vn-acl-action="remove"
ui-sref="client.card.creditInsurance.insurance.create({classificationId: {{$ctrl.$params.classificationId}}})"
fixed-bottom-right
vn-tooltip="New credit"
vn-bind="+">
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -1,38 +0,0 @@
import ngModule from '../../../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.isClosed = true;
this.filter = {
include: [
{relation: 'classification'}
]
};
}
$onInit() {
let filter = {
fields: ['finished'],
where: {id: this.$params.classificationId}
};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `CreditClassifications?filter=${filter}`;
this.$http.get(query).then(res => {
if (res.data)
this.isClosed = res.data[0].finished != null;
});
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientCreditInsuranceInsuranceIndex', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,33 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientCreditInsuranceInsuranceIndex', () => {
let controller;
let $httpBackend;
let $scope;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-client-credit-insurance-insurance-index></vn-client-credit-insurance-insurance-index>');
controller = $componentController('vnClientCreditInsuranceInsuranceIndex', {$element, $scope});
controller.$params = {classificationId: 1};
}));
describe('$onInit()', () => {
it('should perform a query to GET credit the credit classification', () => {
let res = [{finished: 'some value'}];
let query = 'CreditClassifications?filter=%7B%22fields%22%3A%5B%22finished%22%5D%2C%22where%22%3A%7B%22id%22%3A1%7D%7D';
$httpBackend.whenGET(query).respond(res);
$httpBackend.expectGET(query);
controller.$onInit();
$httpBackend.flush();
expect(controller.isClosed).toBe(true);
});
});
});
});

View File

@ -1,79 +0,0 @@
<mg-ajax path="Clients/{{post.params.id}}/setRating" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
url="Clients"
data="$ctrl.client"
id-value="$ctrl.$params.id"
insert-mode="true"
form="form"
save="post">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-input-number
vn-one
label="Rating"
ng-model="$ctrl.client.rating"
vn-focus
rule>
</vn-input-number>
<vn-input-number
vn-one
label="Recommended credit"
ng-model="$ctrl.client.recommendedCredit"
rule>
</vn-input-number>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Save">
</vn-submit>
</vn-button-bar>
</form>
<vn-crud-model
vn-id="model"
url="ClientInformas"
filter="$ctrl.filter"
link="{clientFk: $ctrl.$params.id}"
limit="20"
data="clientInformas"
order="created DESC"
auto-load="true">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-md">
<vn-card>
<vn-table model="model" class="vn-mt-lg">
<vn-thead>
<vn-tr>
<vn-th shrink-date field="created">Since</vn-th>
<vn-th field="workerFk">Employee</vn-th>
<vn-th field="rating" number>Rating</vn-th>
<vn-th field="recommendedCredit" number>Recommended credit</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="clientInforma in clientInformas">
<vn-td shrink-datetime>{{::clientInforma.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td shrink>
<span
ng-click="workerDescriptor.show($event, clientInforma.workerFk)"
class="link">
{{::clientInforma.worker.user.nickname}}
</span>
</vn-td>
<vn-td number>{{::clientInforma.rating}}</vn-td>
<vn-td number>{{::clientInforma.recommendedCredit | currency: 'EUR'}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -1,32 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
include: [{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: {
fields: ['nickname']
}
}
}
}],
};
}
onSubmit() {
this.$.watcher.submit()
.then(() => this.$state.reload());
}
}
ngModule.vnComponent('vnClientCreditManagement', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
Recommended credit: Crédito recomendado
Rating: Clasificación

View File

@ -1,37 +0,0 @@
<mg-ajax path="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="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-input-number
vn-one
min="0"
label="Credit"
ng-model="$ctrl.client.credit"
vn-focus
rule>
</vn-input-number>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit
ng-if="watcher.dataChanged()"
label="Save">
</vn-submit>
<vn-button
ng-click="$ctrl.cancel()"
label="Cancel">
</vn-button>
</vn-button-bar>
</form>
<vn-confirm
vn-id="confirmation"
on-accept="$ctrl.returnDialog()"
question="Esta modificación retrasará el plazo del próximo recobro"
message="¿Desea continuar?">
</vn-confirm>

View File

@ -1,42 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
onSubmit() {
this.$http.get(`Recoveries/${this.$params.id}/hasActiveRecovery`).then(res => {
let activeRecovery = res.data;
if (activeRecovery)
this.$.confirmation.show();
else
this.addCredit();
});
}
cancel() {
this.goToIndex();
}
returnDialog() {
this.addCredit();
}
goToIndex() {
this.$state.go('client.card.credit.index');
}
addCredit() {
this.$.watcher.submit().then(
() => {
this.goToIndex();
}
);
}
}
ngModule.vnComponent('vnClientCreditCreate', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,94 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientCreditCreate', () => {
let controller;
let $httpBackend;
let $state;
let $scope;
let client;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_, $rootScope, _$state_) => {
$scope = $rootScope.$new();
$scope.confirmation = {show: () => {
return {
then: () => {}
};
}};
$scope.watcher = {
submit: () => {
return {
then: callback => {
callback();
}
};
}
};
client = {credit: 0};
$state = _$state_;
$state.params.id = 1101;
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-client-credit-create></vn-client-credit-create>');
controller = $componentController('vnClientCreditCreate', {$element, $scope});
}));
describe('onSubmit()', () => {
it('should perform a query to check (GET) if the client has an active recovery', () => {
$httpBackend.whenGET(`Recoveries/1101/hasActiveRecovery`).respond(true);
$httpBackend.expectGET(`Recoveries/1101/hasActiveRecovery`);
controller.onSubmit();
$httpBackend.flush();
});
it('should call show() method when the client have a recovery', () => {
jest.spyOn(controller.$.confirmation, 'show');
$httpBackend.whenGET(`Recoveries/1101/hasActiveRecovery`).respond(true);
$httpBackend.expectGET(`Recoveries/1101/hasActiveRecovery`);
controller.onSubmit();
$httpBackend.flush();
expect(controller.$.confirmation.show).toHaveBeenCalledWith();
});
it('should call addCredit() method when the client doesnt have a recovery', () => {
jest.spyOn(controller, 'addCredit');
$httpBackend.whenGET(`Recoveries/1101/hasActiveRecovery`).respond(false);
$httpBackend.expectGET(`Recoveries/1101/hasActiveRecovery`);
controller.onSubmit();
$httpBackend.flush();
expect(controller.addCredit).toHaveBeenCalledWith();
});
});
describe('cancel()', () => {
it('should call goToIndex()', () => {
jest.spyOn(controller, 'goToIndex');
controller.cancel();
expect(controller.goToIndex).toHaveBeenCalledWith();
});
});
describe('returnDialog()', () => {
it('should call addCredit() when is called with accept', () => {
jest.spyOn(controller, 'addCredit');
controller.returnDialog();
expect(controller.addCredit).toHaveBeenCalledWith();
});
});
describe('addCredit()', () => {
it('should call the function go() on $state to go to the credit list', () => {
jest.spyOn($state, 'go');
client.credit = 1;
controller.addCredit();
expect(controller.$state.go).toHaveBeenCalledWith('client.card.credit.index');
});
});
});
});

View File

@ -1,50 +0,0 @@
<vn-crud-model
vn-id="model"
url="ClientCredits"
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$params.id}"
limit="20"
data="credits"
order="created DESC"
auto-load="true">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-md">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="created">Since</vn-th>
<vn-th field="workerFk">Employee</vn-th>
<vn-th field="amount" number>Credit</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="credit in credits track by credit.id">
<vn-td shrink-datetime>{{::credit.created | date:'dd/MM/yyyy HH:mm'}}</vn-td>
<vn-td>
<span
ng-click="workerDescriptor.show($event, credit.worker.id)"
class="link">
{{::credit.worker.user.name}}
</span>
</vn-td>
<vn-td number>{{::credit.amount | currency:'EUR':2}}</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-float-button
icon="add"
ui-sref="client.card.credit.create"
vn-acl="teamBoss"
vn-acl-action="remove"
vn-tooltip="New credit"
vn-bind="+"
fixed-bottom-right>
</vn-float-button>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>

View File

@ -1,31 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: {
fields: ['name']
}
}
}
}
]
};
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientCreditIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,2 +0,0 @@
Since : Desde
Employee : Empleado

View File

@ -1,200 +0,0 @@
<vn-crud-model
vn-id="model"
url="Defaulters/filter"
filter="::$ctrl.filter"
limit="20"
order="amount DESC"
data="$ctrl.defaulters"
on-data-change="$ctrl.reCheck()"
auto-load="true">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
placeholder="Search client"
info="Search client by id or name"
auto-state="false"
model="model">
</vn-searchbar>
</vn-portal>
<vn-card>
<smart-table
model="model"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-actions>
<div>
<div class="totalBox" style="text-align: center;">
<h6 translate>Total</h6>
<vn-label-value
label="Balance due"
value="{{$ctrl.balanceDueTotal | currency: 'EUR': 2}}">
</vn-label-value>
</div>
</div>
<div class="vn-pa-md">
<vn-button
disabled="$ctrl.checked.length == 0"
ng-click="notesDialog.show()"
name="notesDialog"
vn-tooltip="Add observation"
icon="icon-notes">
</vn-button>
</div>
</slot-actions>
<slot-table>
<table>
<thead>
<tr>
<th shrink>
<vn-multi-check
model="model">
</vn-multi-check>
</th>
<th field="clientFk">
<span translate>Client</span>
</th>
<th>
<span translate>Es trabajador</span>
</th>
<th field="salesPersonFk">
<span translate>Comercial</span>
</th>
<th field="countryFk">
<span translate>Country</span>
</th>
<th field="payMethod"
vn-tooltip="Pay Method">
<span translate>P.Method</span>
</th>
<th
field="amount"
vn-tooltip="Balance due">
<span translate>Balance D.</span>
</th>
<th
field="workerFk"
vn-tooltip="Worker who made the last observation">
<span translate>Author</span>
</th>
<th field="observation" expand>
<span translate>Last observation</span>
</th>
<th
vn-tooltip="Last observation date"
field="created">
<span translate>L. O. Date</span>
</th>
<th
vn-tooltip="Credit insurance"
field="creditInsurance"
shrink>
<span translate>Credit I.</span>
</th>
<th field="defaulterSinced">
<span translate>From</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="defaulter in $ctrl.defaulters">
<td shrink>
<vn-check
ng-model="defaulter.checked"
on-change="$ctrl.saveChecked(defaulter.clientFk)"
vn-click-stop>
</vn-check>
</td>
<td title="{{::defaulter.clientName}}">
<span
vn-click-stop="clientDescriptor.show($event, defaulter.clientFk)"
title ="{{::defaulter.clientName}}"
class="link">
{{::defaulter.clientName}}
</span>
</td>
<td>
<vn-check
ng-model="defaulter.isWorker"
disabled="true">
</vn-check>
</td>
<td>
<span
title="{{::defaulter.salesPersonName}}"
vn-click-stop="workerDescriptor.show($event, defaulter.salesPersonFk)"
class="link">
{{::defaulter.salesPersonName | dashIfEmpty}}
</span>
</td>
<td>
{{::defaulter.country}}
</td>
<td>
{{::defaulter.payMethod}}
</td>
<td>{{::defaulter.amount | currency: 'EUR': 2}}</td>
<td>
<span
title="{{::defaulter.workerName}}"
vn-click-stop="workerDescriptor.show($event, defaulter.workerFk)"
class="link">
{{::defaulter.workerName | dashIfEmpty}}
</span>
</td>
<td expand>
<vn-textarea
vn-three
disabled="true"
ng-model="defaulter.observation">
</vn-textarea>
</td>
<td shrink-date>
<span class="chip {{::$ctrl.chipColor(defaulter.created)}}">
{{::defaulter.created | date: 'dd/MM/yyyy'}}
</span>
</td>
<td shrink>{{::defaulter.creditInsurance | currency: 'EUR': 2}}</td>
<td shrink-date>{{::defaulter.defaulterSinced | date: 'dd/MM/yyyy'}}</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-client-descriptor-popover
vn-id="client-descriptor">
</vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-popup vn-id="dialog-summary-client">
<vn-client-summary
client="$ctrl.clientSelected">
</vn-client-summary>
</vn-popup>
<!-- Dialog of add notes button -->
<vn-dialog
vn-id="notesDialog"
on-accept="$ctrl.onResponse()">
<tpl-body>
<section class="SMSDialog">
<h5 class="vn-py-sm">{{$ctrl.$t('Add observation to all selected clients', {total: $ctrl.checked.length})}}</h5>
<vn-horizontal>
<vn-textarea vn-one
vn-id="message"
label="Message"
ng-model="$ctrl.defaulter.observation"
rows="3"
required="true"
rule>
</vn-textarea>
</vn-horizontal>
</section>
</tpl-body>
<tpl-buttons>
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
<button response="accept" translate>Save</button>
</tpl-buttons>
</vn-dialog>

View File

@ -1,199 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import UserError from 'core/lib/user-error';
export default class Controller extends Section {
constructor($element, $) {
super($element, $);
this.defaulter = {};
this.defaulters = [];
this.checkedDefaulers = [];
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'clientFk',
autocomplete: {
url: 'Clients',
showField: 'name',
valueField: 'id'
}
}, {
field: 'salesPersonFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
}, {
field: 'countryFk',
autocomplete: {
url: 'Countries',
showField: 'country',
valueField: 'id'
}
}, {
field: 'payMethodFk',
autocomplete: {
showField: 'name',
valueField: 'id'
}
},
{
field: 'workerFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
},
{
field: 'observation',
searchable: false
},
{
field: 'created',
datepicker: true
},
{
field: 'defaulterSinced',
datepicker: true
}
]
};
this.getBalanceDueTotal();
}
set defaulters(value) {
if (!value || !value.length) return;
for (let defaulter of value)
defaulter.isWorker = defaulter.businessTypeFk === 'worker';
this._defaulters = value;
}
get defaulters() {
return this._defaulters;
}
get checked() {
const clients = this.$.model.data || [];
const checkedLines = [];
for (let defaulter of clients) {
if (defaulter.checked)
checkedLines.push(defaulter);
}
return checkedLines;
}
saveChecked(clientId) {
this.checkedDefaulers = this.checkedDefaulers.includes(clientId) ?
this.checkedDefaulers.filter(id => id !== clientId) : [...this.checkedDefaulers, clientId];
}
reCheck() {
if (!this.$.model.data || !this.checkedDefaulers.length) return;
this.$.model.data.forEach(defaulter => {
defaulter.checked = this.checkedDefaulers.includes(defaulter.clientFk);
});
}
getBalanceDueTotal() {
this.$http.get('Defaulters/filter')
.then(res => {
if (!res.data) return 0;
this.balanceDueTotal = res.data.reduce(
(accumulator, currentValue) => {
return accumulator + (currentValue['amount'] || 0);
}, 0);
});
}
chipColor(date) {
const day = 24 * 60 * 60 * 1000;
const today = Date.vnNew();
today.setHours(0, 0, 0, 0);
const observationShipped = new Date(date);
observationShipped.setHours(0, 0, 0, 0);
const difference = today - observationShipped;
if (difference > (day * 20))
return 'alert';
if (difference > (day * 10))
return 'warning';
}
onResponse() {
if (!this.defaulter.observation)
throw new UserError(`The message can't be empty`);
const params = [];
for (let defaulter of this.checked) {
params.push({
text: this.defaulter.observation,
clientFk: defaulter.clientFk
});
}
this.$http.post(`ClientObservations`, params) .then(() => {
this.vnApp.showSuccess(this.$t('Observation saved!'));
this.sendMail();
this.$state.reload();
});
}
sendMail() {
const params = {
defaulters: this.checked,
observation: this.defaulter.observation,
};
this.$http.post(`Defaulters/observationEmail`, params);
}
exprBuilder(param, value) {
switch (param) {
case 'creditInsurance':
case 'amount':
case 'clientFk':
case 'workerFk':
case 'countryFk':
case 'payMethod':
case 'salesPersonFk':
return {[`d.${param}`]: value};
case 'created':
return {'d.created': {
between: this.dateRange(value)}
};
case 'defaulterSinced':
return {'d.defaulterSinced': {
between: this.dateRange(value)}
};
}
}
dateRange(value) {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
}
}
ngModule.vnComponent('vnClientDefaulter', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,179 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('client defaulter', () => {
describe('Component vnClientDefaulter', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-client-defaulter></vn-client-defaulter>');
controller = $componentController('vnClientDefaulter', {$element});
controller.$.model = crudModel;
controller.$.model.data = [
{clientFk: 1101, amount: 125},
{clientFk: 1102, amount: 500},
{clientFk: 1103, amount: 250}
];
}));
describe('checked() getter', () => {
it('should return the checked lines', () => {
const data = controller.$.model.data;
data[1].checked = true;
data[2].checked = true;
const checkedRows = controller.checked;
const firstCheckedRow = checkedRows[0];
const secondCheckedRow = checkedRows[1];
expect(firstCheckedRow.clientFk).toEqual(1102);
expect(secondCheckedRow.clientFk).toEqual(1103);
});
});
describe('chipColor()', () => {
it('should return undefined when the date is the present', () => {
let today = Date.vnNew();
let result = controller.chipColor(today);
expect(result).toEqual(undefined);
});
it('should return warning when the date is 10 days in the past', () => {
let pastDate = Date.vnNew();
pastDate = pastDate.setDate(pastDate.getDate() - 11);
let result = controller.chipColor(pastDate);
expect(result).toEqual('warning');
});
it('should return alert when the date is 20 days in the past', () => {
let pastDate = Date.vnNew();
pastDate = pastDate.setDate(pastDate.getDate() - 21);
let result = controller.chipColor(pastDate);
expect(result).toEqual('alert');
});
});
describe('onResponse()', () => {
it('should return error for empty message', () => {
let error;
try {
controller.onResponse();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe(`The message can't be empty`);
});
it('should return saved message', () => {
const data = controller.$.model.data;
data[1].checked = true;
controller.defaulter = {observation: 'My new observation'};
const params = [{text: controller.defaulter.observation, clientFk: data[1].clientFk}];
jest.spyOn(controller.vnApp, 'showSuccess');
$httpBackend.expect('GET', `Defaulters/filter`).respond(200);
$httpBackend.expect('POST', `ClientObservations`, params).respond(200, params);
$httpBackend.expect('POST', `Defaulters/observationEmail`).respond(200);
controller.onResponse();
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Observation saved!');
});
});
describe('exprBuilder()', () => {
it('should search by sales person', () => {
const expr = controller.exprBuilder('salesPersonFk', '5');
expect(expr).toEqual({'d.salesPersonFk': '5'});
});
it('should search by client', () => {
const expr = controller.exprBuilder('clientFk', '5');
expect(expr).toEqual({'d.clientFk': '5'});
});
});
describe('getBalanceDueTotal()', () => {
it('should return balance due total', () => {
const defaulters = controller.$.model.data;
$httpBackend.when('GET', `Defaulters/filter`).respond(defaulters);
controller.getBalanceDueTotal();
$httpBackend.flush();
expect(controller.balanceDueTotal).toEqual(875);
});
});
describe('dateRange()', () => {
it('should return two dates with the hours at the start and end of the given date', () => {
const now = Date.vnNew();
const today = now.getDate();
const dateRange = controller.dateRange(now);
const start = dateRange[0].toString();
const end = dateRange[1].toString();
expect(start).toContain(today);
expect(start).toContain('00:00:00');
expect(end).toContain(today);
expect(end).toContain('23:59:59');
});
});
describe('reCheck()', () => {
it(`should recheck buys`, () => {
controller.$.model.data = [
{checked: false, clientFk: 1},
{checked: false, clientFk: 2},
{checked: false, clientFk: 3},
{checked: false, clientFk: 4},
];
controller.checkedDefaulers = [1, 2];
controller.reCheck();
expect(controller.$.model.data[0].checked).toEqual(true);
expect(controller.$.model.data[1].checked).toEqual(true);
expect(controller.$.model.data[2].checked).toEqual(false);
expect(controller.$.model.data[3].checked).toEqual(false);
});
});
describe('saveChecked()', () => {
it(`should check buy`, () => {
const buyCheck = 3;
controller.checkedDefaulers = [1, 2];
controller.saveChecked(buyCheck);
expect(controller.checkedDefaulers[2]).toEqual(buyCheck);
});
it(`should uncheck buy`, () => {
const buyUncheck = 3;
controller.checkedDefaulers = [1, 2, 3];
controller.saveChecked(buyUncheck);
expect(controller.checkedDefaulers[2]).toEqual(undefined);
});
});
});
});

View File

@ -1,14 +0,0 @@
Add observation: Añadir observación
Add observation to all selected clients: Añadir observación a {{total}} cliente(s) seleccionado(s)
Balance D.: Saldo V.
Credit I.: Crédito A.
Last observation: Última observación
L. O. Date: Fecha Ú. O.
Last observation date: Fecha última observación
Search client: Buscar clientes
Worker who made the last observation: Trabajador que ha realizado la última observación
Email sended!: Email enviado!
Observation saved!: Observación añadida!
P.Method: F.Pago
Pay Method: Forma de Pago
Country: Pais

View File

@ -1,3 +0,0 @@
<slot-descriptor>
<vn-client-descriptor></vn-client-descriptor>
</slot-descriptor>

View File

@ -1,9 +0,0 @@
import ngModule from '../module';
import DescriptorPopover from 'salix/components/descriptor-popover';
class Controller extends DescriptorPopover {}
ngModule.vnComponent('vnClientDescriptorPopover', {
slotTemplate: require('./index.html'),
controller: Controller
});

View File

@ -1,136 +0,0 @@
<vn-descriptor-content
module="client"
description="$ctrl.client.name"
summary="$ctrl.$.summary">
<slot-menu>
<a class="vn-item"
ui-sref="ticket.create({clientFk: $ctrl.client.id})"
name="simpleTicket"
translate>
Simple ticket
</a>
<vn-item
ng-click="$ctrl.showSMSDialog()"
translate>
Send SMS
</vn-item>
</slot-menu>
<slot-body>
<div class="attributes">
<vn-label-value
label="Pay method"
value="{{$ctrl.client.payMethod.name}}">
</vn-label-value>
<vn-label-value
label="Credit"
value="{{$ctrl.client.credit | currency: 'EUR': 2}}">
</vn-label-value>
<vn-label-value
label="Secured credit"
value="{{$ctrl.client.creditInsurance | currency: 'EUR': 2}}">
</vn-label-value>
<vn-label-value
label="Risk"
value="{{$ctrl.client.debt | currency: 'EUR':2}}"
ng-class="{alert: $ctrl.client.debt > $ctrl.client.credit}"
info="Invoices minus payments plus orders not yet invoiced">
</vn-label-value>
<vn-label-value
label="Sales person">
<span
ng-click="workerDescriptor.show($event, $ctrl.client.salesPersonFk)"
class="link">
{{$ctrl.client.salesPersonUser.name}}
</span>
</vn-label-value>
<vn-label-value
label="Business type"
value="{{$ctrl.client.businessType.description}}">
</vn-label-value>
</div>
<div class="icons">
<vn-icon
vn-tooltip="Client inactive"
icon="icon-disabled"
ng-if="$ctrl.client.isActive == false">
</vn-icon>
<vn-icon
vn-tooltip="Client frozen"
icon="icon-frozen"
ng-if="$ctrl.client.isFreezed == true">
</vn-icon>
<vn-icon
vn-tooltip="Web Account inactive"
icon="icon-noweb"
ng-if="$ctrl.client.account.active == false">
</vn-icon>
<vn-icon
vn-tooltip="Client has debt"
icon="icon-risk"
ng-if="$ctrl.client.debt > $ctrl.client.credit">
</vn-icon>
<vn-icon
vn-tooltip="Client not checked"
icon="icon-no036"
ng-if="$ctrl.client.isTaxDataChecked == false">
</vn-icon>
<vn-icon-button
vn-tooltip="{{$ctrl.clientUnpaid()}}"
icon="icon-clientUnpaid"
ui-sref="client.card.unpaid"
ng-if="$ctrl.client.unpaid">
</vn-icon-button>
</div>
<div class="quicklinks">
<div ng-transclude="btnOne">
<vn-quick-link
tooltip="Client ticket list"
state="['ticket.index', {q: $ctrl.filter}]"
icon="icon-ticket">
</vn-quick-link>
</div>
<div ng-transclude="btnTwo">
<vn-quick-link
tooltip="Client invoices list"
state="['invoiceOut.index', {q: $ctrl.filter}]"
icon="icon-invoice">
</vn-quick-link>
</div>
<div ng-transclude="btnThree">
<vn-quick-link
tooltip="New order"
state="['order.create', {clientFk: $ctrl.id}]"
icon="icon-basketadd">
</vn-quick-link>
</div>
<div ng-transclude="btnFour">
<vn-quick-link
vn-acl="hr"
vn-acl-action="remove"
tooltip="Go to user"
state="['account.card.summary', {id: $ctrl.id}]"
icon="face">
</vn-quick-link>
</div>
<div ng-transclude="btnFive">
<vn-quick-link
ng-if="$ctrl.client.supplier.nif"
tooltip="Go to supplier"
state="['supplier.card.summary', {id: $ctrl.client.supplier.id}]"
icon="icon-supplier">
</vn-quick-link>
</div>
</div>
</slot-body>
</vn-descriptor-content>
<vn-sms-dialog
vn-id="sms"
on-send="$ctrl.onSmsSend($sms)"
sms="$ctrl.newSMS">
</vn-sms-dialog>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
<vn-popup vn-id="summary">
<vn-client-summary client="$ctrl.client"></vn-client-summary>
</vn-popup>

View File

@ -1,61 +0,0 @@
import ngModule from '../module';
import Descriptor from 'salix/components/descriptor';
class Controller extends Descriptor {
get entity() {
return super.entity;
}
set entity(value) {
super.entity = value;
if (value && this.$params.sendSMS)
this.showSMSDialog();
}
get client() {
return this.entity;
}
set client(value) {
this.entity = value;
}
get filter() {
return JSON.stringify({clientFk: this.id});
}
loadData() {
return this.getData(`Clients/${this.id}/getCard`)
.then(res => this.entity = res.data);
}
showSMSDialog() {
const client = this.client || {};
this.newSMS = {
destinationFk: this.id,
destination: this.$params.phone || client.mobile || client.phone,
message: this.$params.message || ''
};
this.$.sms.open();
}
onSmsSend(sms) {
return this.$http.post(`Clients/${this.id}/sendSms`, sms)
.then(() => this.vnApp.showSuccess(this.$t('SMS sent')));
}
clientUnpaid() {
return this.$t(`Unpaid`) + '<br/>'
+ this.$t(`Unpaid Dated`, {dated: this.client.unpaid.dated}) + '<br/>'
+ this.$t(`Unpaid Amount`, {amount: this.client.unpaid.amount});
}
}
ngModule.vnComponent('vnClientDescriptor', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,26 +0,0 @@
import './index';
describe('vnClientDescriptor', () => {
let controller;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnClientDescriptor', {$element: null});
}));
describe('loadData()', () => {
it(`should perform a get query to store the client data into the controller`, () => {
const id = 1;
const response = 'foo';
$httpBackend.expectGET(`Clients/${id}/getCard`).respond(response);
controller.id = id;
$httpBackend.flush();
expect(controller.client).toEqual(response);
});
});
});

View File

@ -1,11 +0,0 @@
Simple ticket: Ticket simple
View consumer report: Ver informe de consumo
From date: Fecha desde
To date: Fecha hasta
Go to user: Ir al usuario
Go to supplier: Ir al proveedor
Client invoices list: Listado de facturas del cliente
Pay method: Forma de pago
Unpaid Dated: "Fecha: {{dated | date:'dd/MM/yyyy'}}"
Unpaid Amount: "Importe: {{amount | currency: 'EUR':2}}"
Business type: Tipo de negocio

View File

@ -1,108 +0,0 @@
<mg-ajax path="dms/upload" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.dms">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Companies"
data="companies"
order="code">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses"
order="name">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="DmsTypes"
data="dmsTypes"
order="name">
</vn-crud-model>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-ma-md"
enctype="multipart/form-data">
<div class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-one
vn-focus
label="Reference"
ng-model="$ctrl.dms.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one
label="Company"
ng-model="$ctrl.dms.companyId"
data="companies"
show-field="code"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one
label="Type"
ng-model="$ctrl.dms.dmsTypeId"
data="dmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
label="Description"
ng-model="$ctrl.dms.description"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-input-file
vn-one
label="File"
ng-model="$ctrl.dms.files"
on-change="$ctrl.onFileChange($files)"
accept="{{$ctrl.allowedContentTypes}}"
required="true"
multiple="true">
<append>
<vn-icon vn-none
color-marginal
title="{{$ctrl.contentTypesInfo}}"
icon="info">
</vn-icon>
</append>
</vn-input-file>
</vn-horizontal>
<vn-vertical>
<vn-check
label="Generate identifier for original file"
ng-model="$ctrl.dms.hasFile">
</vn-check>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit
disabled="!watcher.dataChanged()"
label="Upload">
</vn-submit>
<vn-button
class="cancel"
label="Cancel"
ui-sref="client.card.dms.index">
</vn-button>
</vn-button-bar>
</div>
</form>

View File

@ -1,113 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.dms = {
files: [],
hasFile: false,
hasFileAttached: false
};
}
get client() {
return this._client;
}
set client(value) {
this._client = value;
if (value) {
this.setDefaultParams();
this.getAllowedContentTypes();
}
}
getAllowedContentTypes() {
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
const contentTypes = res.data.join(', ');
this.allowedContentTypes = contentTypes;
});
}
get contentTypesInfo() {
return this.$t('ContentTypesInfo', {
allowedContentTypes: this.allowedContentTypes
});
}
setDefaultParams() {
const params = {filter: {
where: {code: 'paymentsLaw'}
}};
this.$http.get('DmsTypes/findOne', {params}).then(res => {
const dmsType = res.data && res.data;
const companyId = this.vnConfig.companyFk;
const warehouseId = this.vnConfig.warehouseFk;
const defaultParams = {
reference: this.client.id,
warehouseId: warehouseId,
companyId: companyId,
dmsTypeId: dmsType.id,
description: this.$t('ClientFileDescription', {
dmsTypeName: dmsType.name,
clientId: this.client.id,
clientName: this.client.name
}).toUpperCase()
};
this.dms = Object.assign(this.dms, defaultParams);
});
}
onSubmit() {
const query = `clients/${this.client.id}/uploadFile`;
const options = {
method: 'POST',
url: query,
params: this.dms,
headers: {
'Content-Type': undefined
},
transformRequest: files => {
const formData = new FormData();
for (let i = 0; i < files.length; i++)
formData.append(files[i].name, files[i]);
return formData;
},
data: this.dms.files
};
this.$http(options).then(res => {
if (res) {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.$.watcher.updateOriginalData();
this.$state.go('client.card.dms.index');
}
});
}
onFileChange(files) {
let hasFileAttached = false;
if (files.length > 0)
hasFileAttached = true;
this.$.$applyAsync(() => {
this.dms.hasFileAttached = hasFileAttached;
});
}
}
Controller.$inject = ['$element', '$scope'];
ngModule.vnComponent('vnClientDmsCreate', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,74 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientDmsCreate', () => {
let controller;
let $scope;
let $httpBackend;
let $httpParamSerializer;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
$httpParamSerializer = _$httpParamSerializer_;
const $element = angular.element('<vn-client-create></vn-client-create>');
controller = $componentController('vnClientDmsCreate', {$element, $scope});
controller._client = {id: 1101, name: 'Bruce wayne'};
}));
describe('client() setter', () => {
it('should set the client data and then call setDefaultParams() and getAllowedContentTypes()', () => {
jest.spyOn(controller, 'setDefaultParams');
jest.spyOn(controller, 'getAllowedContentTypes');
controller.client = {
id: 15,
name: 'Bruce wayne'
};
expect(controller.client).toBeDefined();
expect(controller.setDefaultParams).toHaveBeenCalledWith();
expect(controller.getAllowedContentTypes).toHaveBeenCalledWith();
});
});
describe('setDefaultParams()', () => {
it('should perform a GET query and define the dms property on controller', () => {
const params = {filter: {
where: {code: 'paymentsLaw'}
}};
let serializedParams = $httpParamSerializer(params);
$httpBackend.expect('GET', `DmsTypes/findOne?${serializedParams}`).respond({id: 12, code: 'paymentsLaw'});
controller.setDefaultParams();
$httpBackend.flush();
expect(controller.dms).toBeDefined();
expect(controller.dms.reference).toEqual(1101);
expect(controller.dms.dmsTypeId).toEqual(12);
});
});
describe('onFileChange()', () => {
it('should set dms hasFileAttached property to true if has any files', () => {
const files = [{id: 1, name: 'MyFile'}];
controller.onFileChange(files);
$scope.$apply();
expect(controller.dms.hasFileAttached).toBeTruthy();
});
});
describe('getAllowedContentTypes()', () => {
it('should make an HTTP GET request to get the allowed content types', () => {
const expectedResponse = ['image/png', 'image/jpg'];
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond(expectedResponse);
controller.getAllowedContentTypes();
$httpBackend.flush();
expect(controller.allowedContentTypes).toBeDefined();
expect(controller.allowedContentTypes).toEqual('image/png, image/jpg');
});
});
});
});

View File

@ -1,7 +0,0 @@
vn-ticket-request {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
}

View File

@ -1,88 +0,0 @@
<vn-watcher
vn-id="watcher"
data="$ctrl.dms">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Warehouses"
data="warehouses">
</vn-crud-model>
<form
name="form"
ng-submit="$ctrl.onSubmit()"
class="vn-ma-md"
enctype="multipart/form-data">
<div class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-one
vn-focus
label="Reference"
ng-model="$ctrl.dms.reference"
rule>
</vn-textfield>
<vn-autocomplete vn-one required="true"
label="Company"
ng-model="$ctrl.dms.companyId"
url="Companies"
show-field="code"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete vn-one required="true"
label="Warehouse"
ng-model="$ctrl.dms.warehouseId"
data="warehouses"
show-field="name"
value-field="id">
</vn-autocomplete>
<vn-autocomplete vn-one required="true"
label="Type"
ng-model="$ctrl.dms.dmsTypeId"
url="DmsTypes"
show-field="name"
value-field="id">
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-textarea
vn-one
required="true"
label="Description"
ng-model="$ctrl.dms.description"
rule>
</vn-textarea>
</vn-horizontal>
<vn-horizontal>
<vn-input-file
vn-one
label="File"
ng-model="$ctrl.dms.files"
on-change="$ctrl.onFileChange($files)"
accept="{{$ctrl.allowedContentTypes}}"
required="true"
multiple="true">
<append>
<vn-icon vn-none
color-marginal
title="{{$ctrl.contentTypesInfo}}"
icon="info">
</vn-icon>
</append>
</vn-input-file>
</vn-horizontal>
<vn-vertical>
<vn-check disabled="true"
label="Generate identifier for original file"
ng-model="$ctrl.dms.hasFile">
</vn-check>
</vn-vertical>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
<vn-button ui-sref="client.card.dms.index" label="Cancel"></vn-button>
</vn-button-bar>
</div>
</form>

View File

@ -1,94 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
get client() {
return this._client;
}
set client(value) {
this._client = value;
if (value) {
this.setDefaultParams();
this.getAllowedContentTypes();
}
}
getAllowedContentTypes() {
this.$http.get('DmsContainers/allowedContentTypes').then(res => {
const contentTypes = res.data.join(', ');
this.allowedContentTypes = contentTypes;
});
}
get contentTypesInfo() {
return this.$t('ContentTypesInfo', {
allowedContentTypes: this.allowedContentTypes
});
}
setDefaultParams() {
const path = `Dms/${this.$params.dmsId}`;
this.$http.get(path).then(res => {
const dms = res.data && res.data;
this.dms = {
reference: dms.reference,
warehouseId: dms.warehouseFk,
companyId: dms.companyFk,
dmsTypeId: dms.dmsTypeFk,
description: dms.description,
hasFile: dms.hasFile,
hasFileAttached: false,
files: []
};
});
}
onSubmit() {
const query = `dms/${this.$params.dmsId}/updateFile`;
const options = {
method: 'POST',
url: query,
params: this.dms,
headers: {
'Content-Type': undefined
},
transformRequest: files => {
const formData = new FormData();
for (let i = 0; i < files.length; i++)
formData.append(files[i].name, files[i]);
return formData;
},
data: this.dms.files
};
this.$http(options).then(res => {
if (res) {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.$.watcher.updateOriginalData();
this.$state.go('client.card.dms.index');
}
});
}
onFileChange(files) {
let hasFileAttached = false;
if (files.length > 0)
hasFileAttached = true;
this.$.$applyAsync(() => {
this.dms.hasFileAttached = hasFileAttached;
});
}
}
ngModule.vnComponent('vnClientDmsEdit', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -1,81 +0,0 @@
import './index';
describe('Client', () => {
describe('Component vnClientDmsEdit', () => {
let controller;
let $scope;
let $httpBackend;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$scope = $rootScope.$new();
$httpBackend = _$httpBackend_;
const $element = angular.element('<vn-client-dms-edit></vn-client-dms-edit>');
controller = $componentController('vnClientDmsEdit', {$element, $scope});
controller._client = {id: 1};
controller.$params = {dmsId: 1};
}));
describe('client() setter', () => {
it('should set the client data and then call setDefaultParams() and getAllowedContentTypes()', () => {
jest.spyOn(controller, 'setDefaultParams');
jest.spyOn(controller, 'getAllowedContentTypes');
controller._client = undefined;
controller.client = {
id: 15
};
expect(controller.setDefaultParams).toHaveBeenCalledWith();
expect(controller.client).toBeDefined();
expect(controller.getAllowedContentTypes).toHaveBeenCalledWith();
});
});
describe('setDefaultParams()', () => {
it('should perform a GET query and define the dms property on controller', () => {
const dmsId = 1;
const expectedResponse = {
reference: 1101,
warehouseFk: 1,
companyFk: 442,
dmsTypeFk: 12,
description: 'Test',
hasFile: false,
hasFileAttached: false
};
$httpBackend.expect('GET', `Dms/${dmsId}`).respond(expectedResponse);
controller.setDefaultParams();
$httpBackend.flush();
expect(controller.dms).toBeDefined();
expect(controller.dms.reference).toEqual(1101);
expect(controller.dms.dmsTypeId).toEqual(12);
});
});
describe('onFileChange()', () => {
it('should set dms hasFileAttached property to true if has any files', () => {
const files = [{id: 1, name: 'MyFile'}];
controller.dms = {hasFileAttached: false};
controller.onFileChange(files);
$scope.$apply();
expect(controller.dms.hasFileAttached).toBeTruthy();
});
});
describe('getAllowedContentTypes()', () => {
it('should make an HTTP GET request to get the allowed content types', () => {
const expectedResponse = ['image/png', 'image/jpg'];
$httpBackend.expect('GET', `DmsContainers/allowedContentTypes`).respond(expectedResponse);
controller.getAllowedContentTypes();
$httpBackend.flush();
expect(controller.allowedContentTypes).toBeDefined();
expect(controller.allowedContentTypes).toEqual('image/png, image/jpg');
});
});
});
});

View File

@ -1,7 +0,0 @@
vn-ticket-request {
.vn-textfield {
margin: 0!important;
max-width: 100px;
}
}

View File

@ -1,115 +0,0 @@
<vn-crud-model
vn-id="model"
url="ClientDms"
link="{clientFk: $ctrl.$params.id}"
filter="::$ctrl.filter"
limit="20"
data="$ctrl.clientDms"
order="dmsFk DESC"
auto-load="true">
</vn-crud-model>
<vn-data-viewer
model="model"
class="vn-w-lg">
<vn-card>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="dmsFk" shrink>Id</vn-th>
<vn-th field="dmsTypeFk" shrink>Type</vn-th>
<vn-th field="hardCopyNumber" shrink number>Order</vn-th>
<vn-th field="reference" shrink>Reference</vn-th>
<vn-th expand>Description</vn-th>
<vn-th field="hasFile" shrink>Original</vn-th>
<vn-th shrink>File</vn-th>
<vn-th>Employee</vn-th>
<vn-th field="created">Created</vn-th>
<vn-th shrink></vn-th>
<vn-th shrink></vn-th>
<vn-th shrink></vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="document in $ctrl.clientDms">
<vn-td number shrink>{{::document.dmsFk}}</vn-td>
<vn-td shrink>
<span title="{{::document.dms.dmsType.name}}">
{{::document.dms.dmsType.name}}
</span>
</vn-td>
<vn-td shrink number>
<span class="chip" title="{{::document.dms.hardCopyNumber}}"
ng-class="{'message': document.dms.hardCopyNumber}">
{{::document.dms.hardCopyNumber}}
</span>
</vn-td>
<vn-td shrink>
<span title="{{::document.dms.reference}}">
{{::document.dms.reference}}
</span>
</vn-td>
<vn-td expand>
<span title="{{::document.dms.description}}">
{{::document.dms.description}}
</span>
</vn-td>
<vn-td shrink>
<vn-check
ng-model="document.dms.hasFile"
disabled="true">
</vn-check>
</vn-td>
<vn-td shrink>
<span title="{{'Download file' | translate}}" class="link"
ng-click="$ctrl.downloadFile(document.dmsFk)">
{{::document.dms.file}}
</span>
</vn-td>
<vn-td shrink>
<span class="link"
ng-click="workerDescriptor.show($event, document.dms.workerFk)">
{{::document.dms.worker.user.name | dashIfEmpty}}
</span></vn-td>
<vn-td shrink-datetime>
{{::document.dms.created | date:'dd/MM/yyyy HH:mm'}}
</vn-td>
<vn-td shrink>
<vn-icon-button title="{{'Download file' | translate}}"
icon="cloud_download"
ng-click="$ctrl.downloadFile(document.dmsFk)">
</vn-icon-button>
</vn-td>
<vn-td shrink>
<vn-icon-button ui-sref="client.card.dms.edit({dmsId: {{::document.dmsFk}}})"
icon="edit"
title="{{'Edit file' | translate}}">
</vn-icon-button>
</vn-td>
<vn-td shrink>
<vn-icon-button
icon="delete"
ng-click="confirm.show($index)"
title="{{'Remove file' | translate}}"
tabindex="-1">
</vn-icon-button>
</vn-td>
</vn-tr>
</vn-tbody>
</vn-table>
</vn-card>
</vn-data-viewer>
<vn-worker-descriptor-popover
vn-id="workerDescriptor">
</vn-worker-descriptor-popover>
<a ui-sref="client.card.dms.create"
vn-tooltip="Upload file"
vn-bind="+"
fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-confirm
vn-id="confirm"
message="This file will be deleted"
question="Are you sure you want to continue?"
on-accept="$ctrl.deleteDms($data)">
</vn-confirm>

View File

@ -1,64 +0,0 @@
import ngModule from '../../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $, vnFile) {
super($element, $, vnFile);
this.vnFile = vnFile;
this.filter = {
include: {
relation: 'dms',
scope: {
fields: [
'dmsTypeFk',
'reference',
'hardCopyNumber',
'workerFk',
'description',
'hasFile',
'file',
'created',
],
include: [{
relation: 'dmsType',
scope: {
fields: ['name']
}
}, {
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: {
fields: ['name']
}
},
}
}]
},
}
};
}
deleteDms(index) {
const dmsFk = this.clientDms[index].dmsFk;
return this.$http.post(`ClientDms/${dmsFk}/removeFile`)
.then(() => {
this.$.model.remove(index);
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
downloadFile(dmsId) {
this.vnFile.download(`api/dms/${dmsId}/downloadFile`);
}
}
Controller.$inject = ['$element', '$scope', 'vnFile'];
ngModule.vnComponent('vnClientDmsIndex', {
template: require('./index.html'),
controller: Controller,
});

View File

@ -1,37 +0,0 @@
import './index';
import crudModel from 'core/mocks/crud-model';
describe('Client', () => {
describe('Component vnClientDmsIndex', () => {
let $scope;
let $httpBackend;
let controller;
beforeEach(ngModule('client'));
beforeEach(inject(($componentController, $rootScope, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
$scope = $rootScope.$new();
controller = $componentController('vnClientDmsIndex', {$element: null, $scope});
controller.$.model = crudModel;
}));
describe('deleteDms()', () => {
it('should make an HTTP Post query', () => {
jest.spyOn(controller.vnApp, 'showSuccess');
jest.spyOn(controller.$.model, 'remove');
const dmsId = 1;
const dmsIndex = 0;
controller.clientDms = [{dmsFk: 1}];
$httpBackend.expectPOST(`ClientDms/${dmsId}/removeFile`).respond();
controller.deleteDms(dmsIndex);
$httpBackend.flush();
expect(controller.$.model.remove).toHaveBeenCalledWith(dmsIndex);
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
});
});

View File

@ -1,9 +0,0 @@
Type: Tipo
File management: Gestión documental
File: Fichero
Hard copy: Copia
This file will be deleted: Este fichero va a ser borrado
Are you sure?: Estas seguro?
File deleted: Fichero eliminado
Remove file: Eliminar fichero
Download file: Descargar fichero

View File

@ -1,6 +0,0 @@
vn-client-risk-index {
.totalBox {
display: table;
float: right;
}
}

View File

@ -1,2 +0,0 @@
ClientFileDescription: "{{dmsTypeName}} from client {{clientName}} id {{clientId}}"
ContentTypesInfo: Allowed file types {{allowedContentTypes}}

View File

@ -1,14 +0,0 @@
Upload file: Subir fichero
Edit file: Editar fichero
Upload: Subir
File: Fichero
ClientFileDescription: "{{dmsTypeName}} del cliente {{clientName}} id {{clientId}}"
ContentTypesInfo: "Tipos de archivo permitidos: {{allowedContentTypes}}"
Generate identifier for original file: Generar identificador para archivo original
File management: Gestión documental
Hard copy: Copia
This file will be deleted: Este fichero va a ser borrado
Are you sure?: Estas seguro?
File deleted: Fichero eliminado
Remove file: Eliminar fichero
Download file: Descargar fichero

View File

@ -1,319 +0,0 @@
<vn-crud-model
vn-id="model"
url="Clients/extendedListFilter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-client-search-panel"
placeholder="Search client"
info="Search client by id or name"
auto-state="false"
model="model">
</vn-searchbar>
</vn-portal>
<vn-card>
<smart-table
model="model"
view-config-id="clientsDetail"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-table>
<table>
<thead>
<tr>
<th></th>
<th field="id">
<span translate>Identifier</span>
</th>
<th field="name">
<span translate>Name</span>
</th>
<th field="socialName">
<span translate>Social name</span>
</th>
<th field="fi">
<span translate>Tax number</span>
</th>
<th field="salesPersonFk">
<span translate>Salesperson</span>
</th>
<th field="credit">
<span translate>Credit</span>
</th>
<th field="creditInsurance">
<span translate>Credit insurance</span>
</th>
<th field="phone">
<span translate>Phone</span>
</th>
<th field="mobile">
<span translate>Mobile</span>
</th>
<th field="street">
<span translate>Street</span>
</th>
<th field="countryFk">
<span translate>Country</span>
</th>
<th field="provinceFk">
<span translate>Province</span>
</th>
<th field="city">
<span translate>City</span>
</th>
<th field="postcode">
<span translate>Postcode</span>
</th>
<th field="email">
<span translate>Email</span>
</th>
<th field="created">
<span translate>Created</span>
</th>
<th field="businessTypeFk">
<span translate>Business type</span>
</th>
<th field="payMethodFk">
<span translate>Billing data</span>
</th>
<th field="sageTaxTypeFk">
<span translate>Sage tax type</span>
</th>
<th field="sageTransactionTypeFk">
<span translate>Sage tr. type</span>
</th>
<th field="isActive" centered>
<span translate>Active</span>
</th>
<th field="isVies" centered>
<span translate>Vies</span>
</th>
<th field="isTaxDataChecked" centered>
<span translate>Verified data</span>
</th>
<th field="isEqualizated" centered>
<span translate>Is equalizated</span>
</th>
<th field="isFreezed" centered>
<span translate>Freezed</span>
</th>
<th field="hasToInvoice" centered>
<span translate>Invoice</span>
</th>
<th field="hasToInvoiceByAddress" centered>
<span translate>Invoice by address</span>
</th>
<th field="isToBeMailed" centered>
<span translate>Mailing</span>
</th>
<th field="hasLcr" centered>
<span translate>Received LCR</span>
</th>
<th field="hasCoreVnl" centered>
<span translate>Received core VNL</span>
</th>
<th field="hasSepaVnl" centered>
<span translate>Received B2B VNL</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="client in model.data"
vn-anchor="::{
state: 'client.card.summary',
params: {id: client.id}
}">
<td>
<vn-icon-button ng-show="::client.isActive == false"
vn-tooltip="Client inactive"
icon="icon-disabled">
</vn-icon-button>
<vn-icon-button ng-show="::client.isActive && client.isFreezed == true"
vn-tooltip="Client frozen"
icon="icon-frozen">
</vn-icon-button>
</td>
<td>
<span
vn-click-stop="clientDescriptor.show($event, client.id)"
class="link">
{{::client.id}}
</span>
</td>
<td>{{::client.name}}</td>
<td>{{::client.socialName}}</td>
<td>{{::client.fi}}</td>
<td>
<span
vn-click-stop="workerDescriptor.show($event, client.salesPersonFk)"
ng-class="{'link': client.salesPersonFk}">
{{::client.salesPerson | dashIfEmpty}}
</span>
</td>
<td>{{::client.credit}}</td>
<td>{{::client.creditInsurance | dashIfEmpty}}</td>
<td>{{::client.phone | dashIfEmpty}}</td>
<td>{{::client.mobile | dashIfEmpty}}</td>
<td>{{::client.street | dashIfEmpty}}</td>
<td>{{::client.country | dashIfEmpty}}</td>
<td>{{::client.province | dashIfEmpty}}</td>
<td>{{::client.city | dashIfEmpty}}</td>
<td>{{::client.postcode | dashIfEmpty}}</td>
<td class="vn-w-xs" title="{{::client.email}}">{{::client.email | dashIfEmpty}}</td>
<td>{{::client.created | date:'dd/MM/yyyy'}}</td>
<td>{{::client.businessType | dashIfEmpty}}</td>
<td>{{::client.payMethod | dashIfEmpty}}</td>
<td>{{::client.sageTaxType | dashIfEmpty}}</td>
<td>{{::client.sageTransactionType | dashIfEmpty}}</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isActive,
'alert': !client.isActive,
}">
{{ ::client.isActive ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isVies,
'alert': !client.isVies,
}">
{{ ::client.isVies ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isTaxDataChecked,
'alert': !client.isTaxDataChecked,
}">
{{ ::client.isTaxDataChecked ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isEqualizated,
'alert': !client.isEqualizated,
}">
{{ ::client.isEqualizated ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isFreezed,
'alert': !client.isFreezed,
}">
{{ ::client.isFreezed ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasToInvoice,
'alert': !client.hasToInvoice,
}">
{{ ::client.hasToInvoice ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasToInvoiceByAddress,
'alert': !client.hasToInvoiceByAddress,
}">
{{ ::client.hasToInvoiceByAddress ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isToBeMailed,
'alert': !client.isToBeMailed,
}">
{{ ::client.isToBeMailed ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasLcr,
'alert': !client.hasLcr,
}">
{{ ::client.hasLcr ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasCoreVnl,
'alert': !client.hasCoreVnl,
}">
{{ ::client.hasCoreVnl ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasSepaVnl,
'alert': !client.hasSepaVnl,
}">
{{ ::client.hasSepaVnl ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td shrink>
<vn-horizontal class="buttons">
<vn-icon-button vn-anchor="{state: 'ticket.index', params: {q: {clientFk: client.id} } }"
vn-tooltip="Client tickets"
icon="icon-ticket">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(client)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-horizontal>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<a ui-sref="client.create" vn-tooltip="New client" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-client-descriptor-popover
vn-id="client-descriptor">
</vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-popup vn-id="preview">
<vn-client-summary
client="$ctrl.clientSelected">
</vn-client-summary>
</vn-popup>
<vn-contextmenu
vn-id="contextmenu"
targets="['smart-table']"
model="model"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-menu>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.filterBySelection()">
Filter by selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.excludeSelection()">
Exclude selection
</vn-item>
<vn-item translate
ng-if="contextmenu.isFilterAllowed()"
ng-click="contextmenu.removeFilter()">
Remove filter
</vn-item>
<vn-item translate
ng-click="contextmenu.removeAllFilters()">
Remove all filters
</vn-item>
</slot-menu>
</vn-contextmenu>

View File

@ -1,184 +0,0 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true,
shownColumns: true,
},
columns: [
{
field: 'socialName',
autocomplete: {
url: 'Clients',
showField: 'socialName',
valueField: 'socialName',
}
},
{
field: 'created',
datepicker: true
},
{
field: 'countryFk',
autocomplete: {
url: 'Countries',
showField: 'name',
}
},
{
field: 'provinceFk',
autocomplete: {
url: 'Provinces'
}
},
{
field: 'salesPersonFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'nickname',
valueField: 'id',
}
},
{
field: 'businessTypeFk',
autocomplete: {
url: 'BusinessTypes',
valueField: 'code',
showField: 'description',
}
},
{
field: 'payMethodFk',
autocomplete: {
url: 'PayMethods',
}
},
{
field: 'sageTaxTypeFk',
autocomplete: {
url: 'SageTaxTypes',
showField: 'vat',
}
},
{
field: 'sageTransactionTypeFk',
autocomplete: {
url: 'SageTransactionTypes',
showField: 'transaction',
}
},
{
field: 'isActive',
checkbox: true
},
{
field: 'isVies',
checkbox: true
},
{
field: 'isTaxDataChecked',
checkbox: true
},
{
field: 'isEqualizated',
checkbox: true
},
{
field: 'isFreezed',
checkbox: true
},
{
field: 'hasToInvoice',
checkbox: true
},
{
field: 'hasToInvoiceByAddress',
checkbox: true
},
{
field: 'isToBeMailed',
checkbox: true
},
{
field: 'hasSepaVnl',
checkbox: true
},
{
field: 'hasLcr',
checkbox: true
},
{
field: 'hasCoreVnl',
checkbox: true
}
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'created':
return {'c.created': {
between: this.dateRange(value)}
};
case 'id':
case 'name':
case 'socialName':
case 'fi':
case 'credit':
case 'creditInsurance':
case 'phone':
case 'mobile':
case 'street':
case 'city':
case 'postcode':
case 'email':
case 'isActive':
case 'isVies':
case 'isTaxDataChecked':
case 'isEqualizated':
case 'isFreezed':
case 'hasToInvoice':
case 'hasToInvoiceByAddress':
case 'isToBeMailed':
case 'hasSepaVnl':
case 'hasLcr':
case 'hasCoreVnl':
case 'countryFk':
case 'provinceFk':
case 'salesPersonFk':
case 'businessTypeFk':
case 'payMethodFk':
case 'sageTaxTypeFk':
case 'sageTransactionTypeFk':
return {[`c.${param}`]: value};
}
}
dateRange(value) {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
}
preview(client) {
this.clientSelected = client;
this.$.preview.show();
}
}
ngModule.vnComponent('vnClientExtendedList', {
template: require('./index.html'),
controller: Controller
});

View File

@ -1,3 +0,0 @@
Mailing: Env. emails
Sage tr. type: Tipo tr. sage
Yes:

View File

@ -1,6 +0,0 @@
@import "variables";
vn-chip.success,
vn-chip.alert {
color: $color-font-bg
}

View File

@ -1,219 +0,0 @@
<mg-ajax path="Clients/{{patch.params.id}}/updateFiscalData" options="vnPatch"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.client"
id-field="id"
form="form"
save="patch">
</vn-watcher>
<vn-crud-model
auto-load="true"
url="Provinces/location"
data="provincesLocation"
order="name">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="Countries"
data="countries"
order="name">
</vn-crud-model>
<vn-crud-model
auto-load="true"
url="SageTaxTypes"
data="sageTaxTypes"
order="vat">
</vn-crud-model>
<form name="form" ng-submit="$ctrl.onSubmit()" class="vn-w-md">
<vn-card class="vn-pa-lg">
<vn-horizontal>
<vn-textfield
vn-focus
label="Social name"
ng-model="$ctrl.client.socialName"
info="Only letters, numbers and spaces can be used"
required="true"
ng-keyup="$ctrl.client.socialName = $ctrl.client.socialName.toUpperCase()"
rule>
</vn-textfield>
<vn-textfield
label="Tax number"
ng-model="$ctrl.client.fi"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-two
label="Street"
ng-model="$ctrl.client.street"
ng-keyup="$ctrl.client.street = $ctrl.client.street.toUpperCase()"
rule>
</vn-textfield>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
ng-model="$ctrl.client.sageTaxTypeFk"
data="sageTaxTypes"
show-field="vat"
value-field="id"
label="Sage tax type"
vn-acl="salesAssistant"
rule>
</vn-autocomplete>
<vn-autocomplete
ng-model="$ctrl.client.sageTransactionTypeFk"
url="SageTransactionTypes"
show-field="transaction"
value-field="id"
label="Sage transaction type"
search-function="{or: [{id: $search}, {transaction: {like: '%'+ $search +'%'}}]}"
vn-acl="salesAssistant"
order="transaction"
rule>
<tpl-item>{{id}}: {{transaction}}</tpl-item>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-datalist
label="Postcode"
ng-model="$ctrl.client.postcode"
selection="$ctrl.postcode"
url="Postcodes/location"
fields="['code','townFk']"
order="code, townFk"
value-field="code"
show-field="code"
rule>
<tpl-item>
{{code}} - {{town.name}} ({{town.province.name}},
{{town.province.country.name}})
</tpl-item>
<append>
<vn-icon-button
icon="add_circle"
vn-tooltip="New postcode"
ng-click="postcode.open()"
vn-acl="deliveryAssistant"
vn-acl-action="remove">
</vn-icon-button>
</append>
</vn-datalist>
<vn-datalist
vn-id="town"
label="City"
ng-model="$ctrl.client.city"
selection="$ctrl.town"
url="Towns/location"
fields="['id', 'name', 'provinceFk']"
show-field="name"
value-field="name">
<tpl-item>
{{name}}, {{province.name}}
({{province.country.name}})
</tpl-item>
</vn-datalist>
</vn-horizontal>
<vn-horizontal>
<vn-autocomplete
vn-one
vn-id="province"
label="Province"
ng-model="$ctrl.client.provinceFk"
selection="$ctrl.province"
data="provincesLocation"
fields="['id', 'name', 'countryFk']"
show-field="name"
value-field="id"
rule>
<tpl-item>{{name}} ({{country.name}})</tpl-item>
</vn-autocomplete>
<vn-autocomplete
vn-one
vn-id="country"
ng-model="$ctrl.client.countryFk"
data="countries"
show-field="name"
value-field="id"
label="Country"
rule>
</vn-autocomplete>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Active"
ng-model="$ctrl.client.isActive">
</vn-check>
<vn-check
label="Frozen"
ng-model="$ctrl.client.isFreezed">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Has to invoice"
ng-model="$ctrl.client.hasToInvoice">
</vn-check>
<vn-check
label="Vies"
info="When activating it, do not enter the country code in the ID field."
ng-model="$ctrl.client.isVies">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Notify by email"
ng-model="$ctrl.client.isToBeMailed">
</vn-check>
<vn-check
label="Invoice by address"
ng-model="$ctrl.client.hasToInvoiceByAddress">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Is equalizated"
ng-model="$ctrl.client.isEqualizated"
info="In order to invoice, this field is not consulted, but the consignee's ET. When modifying this field if the invoice by address option is not checked, the change will be automatically propagated to all addresses, otherwise the user will be asked if he wants to propagate it or not."
on-change="$ctrl.onChangeEqualizated(value)">
</vn-check>
<vn-check
label="Verified data"
ng-model="$ctrl.client.isTaxDataChecked"
vn-acl="salesAssistant">
</vn-check>
</vn-horizontal>
<vn-horizontal>
<vn-check
label="Electronic invoice"
ng-model="$ctrl.client.hasElectronicInvoice"
vn-acl="administrative">
</vn-check>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit disabled="!watcher.dataChanged()" label="Save">
</vn-submit>
<vn-button
class="cancel"
label="Undo changes"
disabled="!watcher.dataChanged()"
ng-click="watcher.loadOriginalData()">
</vn-button>
</vn-button-bar>
</form>
<vn-confirm
vn-id="propagate-isEqualizated"
question="You changed the equalization tax"
message="Do you want to spread the change?"
on-accept="$ctrl.onAcceptEt()">
</vn-confirm>
<vn-confirm
vn-id="confirm-duplicatedClient"
message="Found a client with this data"
on-accept="$ctrl.onAcceptDuplication()">
</vn-confirm>
<vn-geo-postcode
vn-id="postcode"
on-response="$ctrl.onResponse($response)">
</vn-geo-postcode>

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