#6943 - Client module redirect to Lilium #2924

Open
jsegarra wants to merge 5 commits from 6943_customer_redirectToLilium into dev
180 changed files with 8 additions and 11188 deletions

View File

@ -37,17 +37,11 @@ async function test() {
const specFiles = [
`./e2e/paths/01*/*[sS]pec.js`,
`./e2e/paths/02*/*[sS]pec.js`,
`./e2e/paths/03*/*[sS]pec.js`,
`./e2e/paths/04*/*[sS]pec.js`,
`./e2e/paths/05*/*[sS]pec.js`,
`./e2e/paths/07*/*[sS]pec.js`,
`./e2e/paths/08*/*[sS]pec.js`,
`./e2e/paths/09*/*[sS]pec.js`,
`./e2e/paths/10*/*[sS]pec.js`,
`./e2e/paths/11*/*[sS]pec.js`,
`./e2e/paths/12*/*[sS]pec.js`,
`./e2e/paths/13*/*[sS]pec.js`,
`./e2e/paths/**/*[sS]pec.js`
];

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,218 +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) {
if (!value) return;
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 ?? this.clientFk,
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: '<?',
clientFk: '<?',
companyFk: '<?',
description: '<?',
}
});

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
}

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