Merge branch 'dev' of https://git.verdnatura.es/salix into dev

This commit is contained in:
Javi Gallego 2018-03-19 18:26:34 +01:00
commit a817cb58bf
122 changed files with 1952 additions and 309 deletions

View File

@ -220,6 +220,57 @@
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"
} }
},
{
"url": "/credit-classification",
"abstract": true,
"state": "clientCard.creditClassification",
"component": "ui-view",
"acl": ["creditInsurance"]
},
{
"url": "/list",
"state": "clientCard.creditClassification.list",
"component": "vn-client-credit-classification-list",
"params": {
"client": "$ctrl.client"
},
"menu": {
"description": "Credit contracts",
"icon": "security"
},
"acl": ["creditInsurance"]
},
{
"url": "/create",
"state": "clientCard.creditClassification.create",
"component": "vn-client-credit-classification-create",
"params": {
"client": "$ctrl.client"
}
},
{
"url": "/credit-insurance",
"abstract": true,
"state": "clientCard.creditInsurance",
"component": "ui-view",
"acl": ["creditInsurance"]
},
{
"url": "/:classificationId/list",
"state": "clientCard.creditInsurance.list",
"component": "vn-client-credit-insurance-list",
"params": {
"client": "$ctrl.client"
}
},
{
"url": "/:classificationId/create",
"state": "clientCard.creditInsurance.create",
"component": "vn-client-credit-insurance-create",
"params": {
"client": "$ctrl.client"
}
} }
] ]
} }

View File

@ -54,6 +54,7 @@
<mg-ajax path="/client/api/ObservationTypes" options="mgIndex as observationsTypes"></mg-ajax> <mg-ajax path="/client/api/ObservationTypes" options="mgIndex as observationsTypes"></mg-ajax>
<vn-horizontal ng-repeat="observation in $ctrl.observations track by $index"> <vn-horizontal ng-repeat="observation in $ctrl.observations track by $index">
<vn-autocomplete <vn-autocomplete
ng-if="!observation.id"
vn-one vn-one
initial-data="observation.observationType" initial-data="observation.observationType"
field="observation.observationTypeFk" field="observation.observationTypeFk"
@ -61,6 +62,13 @@
show-field="description" show-field="description"
label="Observation type"> label="Observation type">
</vn-autocomplete> </vn-autocomplete>
<vn-textfield
ng-if="observation.id"
vn-one
label="Observation type"
model="observation.observationType.description"
disabled="true">
</vn-textfield>
<vn-textfield <vn-textfield
vn-two vn-two
margin-large-right margin-large-right

View File

@ -13,8 +13,8 @@ export default class Controller {
id: parseInt($state.params.addressId) id: parseInt($state.params.addressId)
}; };
this.observations = []; this.observations = [];
this.observationsOld = {}; this.oldObservations = {};
this.observationsRemoved = []; this.removedObservations = [];
} }
_setDirtyForm() { _setDirtyForm() {
@ -22,6 +22,7 @@ export default class Controller {
this.$scope.form.$setDirty(); this.$scope.form.$setDirty();
} }
} }
_unsetDirtyForm() { _unsetDirtyForm() {
if (this.$scope.form) { if (this.$scope.form) {
this.$scope.form.$setPristine(); this.$scope.form.$setPristine();
@ -37,16 +38,17 @@ export default class Controller {
if (item) { if (item) {
this.observations.splice(index, 1); this.observations.splice(index, 1);
if (item.id) { if (item.id) {
this.observationsRemoved.push(item.id); this.removedObservations.push(item.id);
this._setDirtyForm(); this._setDirtyForm();
} }
} }
if (this.observations.length === 0 && Object.keys(this.observationsOld).length === 0) { if (this.observations.length === 0 && Object.keys(this.oldObservations).length === 0) {
this._unsetDirtyForm(); this._unsetDirtyForm();
} }
} }
_submitObservations(objectObservations) {
return this.$http.post(`/client/api/AddressObservations/crudAddressObservations`, objectObservations); _submitObservations(observationsObject) {
return this.$http.post(`/client/api/AddressObservations/crudAddressObservations`, observationsObject);
} }
_observationsEquals(ob1, ob2) { _observationsEquals(ob1, ob2) {
@ -58,12 +60,12 @@ export default class Controller {
return false; return false;
} }
let canWatcherSubmit = this.$scope.watcher.dataChanged(); let canSubmitWatcher = this.$scope.watcher.dataChanged();
let canObservationsSubmit; let canSubmitObservations;
let repeatedTypes = false; let repeatedTypes = false;
let types = []; let types = [];
let observationsObj = { let observationsObj = {
delete: this.observationsRemoved, delete: this.removedObservations,
create: [], create: [],
update: [] update: []
}; };
@ -82,26 +84,26 @@ export default class Controller {
if (isNewObservation && observation.observationTypeFk && observation.description) { if (isNewObservation && observation.observationTypeFk && observation.description) {
observationsObj.create.push(observation); observationsObj.create.push(observation);
} else if (!isNewObservation && !this._observationsEquals(this.observationsOld[observation.id], observation)) { } else if (!isNewObservation && !this._observationsEquals(this.oldObservations[observation.id], observation)) {
observationsObj.update.push(observation); observationsObj.update.push(observation);
} }
} }
canObservationsSubmit = observationsObj.update.length > 0 || observationsObj.create.length > 0 || observationsObj.delete.length > 0; canSubmitObservations = observationsObj.update.length > 0 || observationsObj.create.length > 0 || observationsObj.delete.length > 0;
if (repeatedTypes) { if (repeatedTypes) {
this.vnApp.showMessage( this.vnApp.showMessage(
this.$translate.instant('The observation type must be unique') this.$translate.instant('The observation type must be unique')
); );
} else if (canWatcherSubmit && !canObservationsSubmit) { } else if (canSubmitWatcher && !canSubmitObservations) {
this.$scope.watcher.submit().then(() => { this.$scope.watcher.submit().then(() => {
this.$state.go('clientCard.addresses.list', {id: this.$state.params.id}); this.$state.go('clientCard.addresses.list', {id: this.$state.params.id});
}); });
} else if (!canWatcherSubmit && canObservationsSubmit) { } else if (!canSubmitWatcher && canSubmitObservations) {
this._submitObservations(observationsObj).then(() => { this._submitObservations(observationsObj).then(() => {
this.$state.go('clientCard.addresses.list', {id: this.$state.params.id}); this.$state.go('clientCard.addresses.list', {id: this.$state.params.id});
}); });
} else if (canWatcherSubmit && canObservationsSubmit) { } else if (canSubmitWatcher && canSubmitObservations) {
this.$q.all([this.$scope.watcher.submit(), this._submitObservations(observationsObj)]).then(() => { this.$q.all([this.$scope.watcher.submit(), this._submitObservations(observationsObj)]).then(() => {
this.$state.go('clientCard.addresses.list', {id: this.$state.params.id}); this.$state.go('clientCard.addresses.list', {id: this.$state.params.id});
}); });
@ -113,7 +115,7 @@ export default class Controller {
this._unsetDirtyForm(); this._unsetDirtyForm();
} }
$onInit() { _getAddressNotes() {
let filter = { let filter = {
where: {addressFk: this.address.id}, where: {addressFk: this.address.id},
include: {relation: 'observationType'} include: {relation: 'observationType'}
@ -121,10 +123,14 @@ export default class Controller {
this.$http.get(`/client/api/AddressObservations?filter=${JSON.stringify(filter)}`).then(res => { this.$http.get(`/client/api/AddressObservations?filter=${JSON.stringify(filter)}`).then(res => {
this.observations = res.data; this.observations = res.data;
res.data.forEach(item => { res.data.forEach(item => {
this.observationsOld[item.id] = Object.assign({}, item); this.oldObservations[item.id] = Object.assign({}, item);
}); });
}); });
} }
$onInit() {
this._getAddressNotes();
}
} }
Controller.$inject = ['$state', '$scope', '$http', '$q', '$translate', 'vnApp']; Controller.$inject = ['$state', '$scope', '$http', '$q', '$translate', 'vnApp'];

View File

@ -23,3 +23,7 @@ import './invoices/invoices';
import './summary/client-summary'; import './summary/client-summary';
import './recovery-list/recovery-list'; import './recovery-list/recovery-list';
import './recovery-create/recovery-create'; import './recovery-create/recovery-create';
import './credit-classification-list/credit-classification-list';
import './credit-classification-create/credit-classification-create';
import './credit-insurance-list/credit-insurance-list';
import './credit-insurance-create/credit-insurance-create';

View File

@ -0,0 +1,24 @@
<mg-ajax path="/client/api/clients/{{post.params.id}}/creditClassifications" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.creditClassification"
form="form"
save="post">
</vn-watcher>
<form name="form" ng-submit="watcher.submitGo('clientCard.creditClassification.list')" pad-medium>
<vn-card pad-large>
<vn-title>New contract</vn-title>
<vn-horizontal>
<vn-date-picker
vn-one
label="Since"
model="$ctrl.creditClassification.started"
ini-options="{dateFormat: 'd-m-Y'}"
vn-focus>
</vn-date-picker>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,20 @@
import ngModule from '../module';
class Controller {
constructor($filter, $state) {
this.creditClassification = {
started: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm')
};
this.$state = $state;
}
}
Controller.$inject = ['$filter', '$state'];
ngModule.component('vnClientCreditClassificationCreate', {
template: require('./credit-classification-create.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -0,0 +1,43 @@
<vn-vertical pad-medium>
<vn-card pad-large>
<vn-title vn-one>Contract credit insurance</vn-title>
<vn-horizontal ng-repeat="classification in $ctrl.classifications track by classification.id" class="pad-medium-top" style="align-items: center;">
<vn-one border-radius class="pad-small border-solid" ng-class="{'bg-dark-item': !classification.finished,'bg-opacity-item': classification.finished}">
<vn-horizontal style="align-items: center;">
<vn-none pad-medium-h style="color:#FFA410;">
<i class="material-icons pointer"
ng-if="!classification.finished"
vn-tooltip="Close contract"
tooltip-position="left"
ng-click="$ctrl.closeContract(classification)">lock_outline</i>
</vn-none>
<vn-one 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 pad-medium-h>
<vn-one ng-repeat="insurance in classification.creditInsurances track by insurance.id">
<vn-label translate>Credit</vn-label> <span>{{insurance.credit}}</span>
<vn-label translate>Grade</vn-label>
<span ng-if="!insurance.grade">-</span>
<span ng-if="insurance.grade">{{insurance.grade}}</span>
<vn-label translate>Date</vn-label> <span>{{insurance.created | date:'dd/MM/yyyy' }}</span>
</vn-one>
</vn-vertical>
<a vn-auto ui-sref="clientCard.creditInsurance.list({classificationId: {{classification.id}}})">
<vn-icon-button icon="edit" vn-tooltip="Edit contract" tooltip-position="left"></vn-icon-button>
</a>
</vn-horizontal>
</vn-one>
</vn-horizontal>
</vn-card>
<vn-float-button
ng-if="$ctrl.canCreateNew()"
vn-tooltip="New contract"
tooltip-position="left"
fixed-bottom-right
ui-sref="clientCard.creditClassification.create"
icon="add"
label="Add">
</vn-float-button>
</vn-vertical>

View File

@ -0,0 +1,68 @@
import ngModule from '../module';
class Controller {
constructor($http, $scope) {
this.$http = $http;
this.$scope = $scope;
}
$onChanges() {
if (this.client && this.client.id)
this._getClassifications(this.client.id);
}
_getClassifications(clientId) {
let filter = {
include: [
{
relation: 'creditInsurances',
scope: {
fields: ['id', 'credit', 'created', 'grade'],
order: 'created DESC',
limit: 2
}
}
],
where: {client: clientId}
};
filter = encodeURIComponent(JSON.stringify(filter));
let query = `/client/api/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) {
let params = {finished: Date.now()};
this.$http.patch(`/client/api/CreditClassifications/${classification.id}`, params).then(() => {
this._getClassifications(this.client.id);
});
}
}
Controller.$inject = ['$http', '$scope'];
ngModule.component('vnClientCreditClassificationList', {
template: require('./credit-classification-list.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -0,0 +1,80 @@
import './credit-classification-list.js';
describe('Client', () => {
describe('Component vnClientCreditClassificationList', () => {
let $componentController;
let controller;
let $httpBackend;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_) => {
$componentController = _$componentController_;
$httpBackend = _$httpBackend_;
controller = $componentController('vnClientCreditClassificationList');
}));
describe('_getClassifications()', () => {
it('should perform a GET query to define the classifications property in the controller', () => {
let res = ['some classifications'];
let query = '/client/api/CreditClassifications?filter=%7B%22include%22%3A%5B%7B%22relation%22%3A%22creditInsurances%22%2C%22scope%22%3A%7B%22fields%22%3A%5B%22id%22%2C%22credit%22%2C%22created%22%2C%22grade%22%5D%2C%22order%22%3A%22created%20DESC%22%2C%22limit%22%3A2%7D%7D%5D%2C%22where%22%3A%7B%7D%7D';
$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.now()},
{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.now()},
{finished: Date.now()}
];
let result = controller.canCreateNew();
expect(result).toBeTruthy();
});
});
describe('closeContract()', () => {
it('should perform a GET query to set a due date to a contract', () => {
let res = ['pepinillos'];
controller.client = {id: 101};
let classification = {id: 1};
let query = '/client/api/CreditClassifications/1';
spyOn(controller, '_getClassifications');
$httpBackend.whenPATCH(query).respond(res);
$httpBackend.expectPATCH(query);
controller.closeContract(classification);
$httpBackend.flush();
expect(controller._getClassifications).toHaveBeenCalledWith(101);
});
});
});
});

View File

@ -0,0 +1,4 @@
Contract credit insurance: Contratos de seguro de crédito
New contract: Nuevo contrato
Close contract: Cerrar contrato
Edit contract: Modificar contrato

View File

@ -0,0 +1,42 @@
<mg-ajax path="/client/api/CreditClassifications/{{post.params.classificationId}}/creditInsurances" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.insurance"
form="form"
save="post">
</vn-watcher>
<form name="form"
ng-submit="watcher.submitGo('clientCard.creditInsurance.list', {classificationId: post.params.classificationId})"
pad-medium>
<vn-card pad-large>
<vn-title>New credit</vn-title>
<vn-horizontal>
<vn-textfield
vn-one
margin-medium-right
label="Credit"
model="$ctrl.insurance.credit",
rule="CreditInsurance.credit"
step="1"
vn-focus>
</vn-textfield>
<vn-date-picker vn-one
label="Date"
model="$ctrl.insurance.created"
ini-options="{enableTime: true, dateFormat: 'd-m-Y h:i', time_24hr: true}">
</vn-date-picker>
</vn-horizontal>
<vn-horizontal>
<vn-textfield
vn-one
argin-medium-right
label="Grade"
model="$ctrl.insurance.grade"
rule="CreditInsurance.grade">
</vn-textfield>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,20 @@
import ngModule from '../module';
class Controller {
constructor($state, $filter) {
this.insurance = {
created: $filter('date')(new Date(), 'yyyy-MM-dd HH:mm')
};
this.classificationId = $state.params.classificationId;
}
}
Controller.$inject = ['$state', '$filter'];
ngModule.component('vnClientCreditInsuranceCreate', {
template: require('./credit-insurance-create.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

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

View File

@ -0,0 +1,28 @@
<mg-ajax path="/client/api/CreditClassifications/{{index.params.classificationId}}/creditInsurances" options="vnIndex"></mg-ajax>
<vn-vertical pad-medium>
<vn-card pad-large>
<vn-vertical>
<vn-title>Requested credits</vn-title>
<vn-grid-header>
<vn-column-header vn-one pad-medium-h text="Amount"></vn-column-header>
<vn-column-header vn-one pad-medium-h text="Grade"></vn-column-header>
<vn-column-header vn-two pad-medium-h text="Date"></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
<vn-horizontal
vn-one class="list list-element text-center"
pad-small-bottom
ng-repeat="insurance in index.model track by insurance.id">
<vn-one pad-medium-h>{{insurance.credit}}</vn-one>
<vn-one pad-medium-h>{{insurance.grade}}</vn-one>
<vn-two pad-medium-h>{{insurance.created | date: 'dd/MM/yyyy'}}</vn-two>
</vn-horizontal>
</vn-one>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
</vn-vertical>
</vn-card>
</vn-vertical>
<a ui-sref="clientCard.creditInsurance.create({classificationId: {{index.params.classificationId}}})"
fixed-bottom-right vn-tooltip="New credit" tooltip-position="left">
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,17 @@
import ngModule from '../module';
class Controller {
constructor($state) {
this.classificationId = $state.params.classificationId;
}
}
Controller.$inject = ['$state'];
ngModule.component('vnClientCreditInsuranceList', {
template: require('./credit-insurance-list.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -0,0 +1 @@
Requested credits: Créditos solicitados

View File

@ -25,30 +25,36 @@
<span ng-if="!$ctrl.client.creditInsurance">-</span> <span ng-if="!$ctrl.client.creditInsurance">-</span>
</div> </div>
</div> </div>
<div class="footer"> <vn-horizontal pad-medium-bottom class="footer">
<vn-icon <vn-icon
vn-tooltip="Client inactive" vn-tooltip="Client inactive"
tooltip-position = "right" tooltip-position = "right"
icon="person" icon="icon-disabled"
ng-class="{bright: $ctrl.client.isActive == false}"> ng-class="{bright: $ctrl.client.isActive == false}">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
vn-tooltip="Client Frozen" vn-tooltip="Client Frozen"
tooltip-position = "right" tooltip-position = "right"
icon="mail" icon="icon-frozen"
ng-class="{bright: $ctrl.client.isFreezed == true}"> ng-class="{bright: $ctrl.client.isFreezed == true}">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
vn-tooltip="Web Account inactive" vn-tooltip="Web Account inactive"
tooltip-position = "right" tooltip-position = "right"
icon="phone" icon="icon-noweb"
ng-class="{bright: $ctrl.client.account.active == false}"> ng-class="{bright: $ctrl.client.account.active == false}">
</vn-icon> </vn-icon>
<vn-icon <vn-icon
vn-tooltip="Client has debt" vn-tooltip="Client has debt"
tooltip-position = "right" tooltip-position = "right"
icon="power" icon="icon-risk"
ng-class="{bright: $ctrl.clientDebt < 0}"> ng-class="{bright: $ctrl.clientDebt < 0}">
</vn-icon> </vn-icon>
</div> <vn-icon
vn-tooltip="Client not checked"
tooltip-position = "right"
icon="icon-no036"
ng-class="{bright: $ctrl.client.isTaxDataChecked == false}">
</vn-icon>
</vn-horizontal>
</vn-card> </vn-card>

View File

@ -89,13 +89,20 @@
</vn-check> </vn-check>
<vn-check <vn-check
vn-one vn-one
label="Frozen"
field="$ctrl.client.isFreezed"
vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check>
<vn-check
vn-two
label="Invoice by address" label="Invoice by address"
field="$ctrl.client.hasToInvoiceByAddress" field="$ctrl.client.hasToInvoiceByAddress"
vn-acl="administrative, salesAssistant, salesPerson" vn-acl="administrative, salesAssistant, salesPerson"
acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}"> acl-conditional-to-salesPerson="{{!$ctrl.client.isTaxDataChecked}}">
</vn-check> </vn-check>
<vn-check <vn-check
vn-one vn-two
label="Verified data" label="Verified data"
field="$ctrl.client.isTaxDataChecked" field="$ctrl.client.isTaxDataChecked"
vn-acl="administrative, salesAssistant, salesAssistant"> vn-acl="administrative, salesAssistant, salesAssistant">

View File

@ -17,7 +17,7 @@ Save: Guardar
Pay method : Forma de pago Pay method : Forma de pago
Address: Consignatario Address: Consignatario
Credit : Crédito Credit : Crédito
Secured credit: Crédito asegurado Credit contracts: Contratos de crédito
Verified data: Datos comprobados Verified data: Datos comprobados
Mandate: Mandato Mandate: Mandato
Amount: Importe Amount: Importe

View File

@ -123,8 +123,8 @@
<vn-one margin-medium> <vn-one margin-medium>
<h5 translate>Recovery</h5> <h5 translate>Recovery</h5>
<vn-vertical ng-if="$ctrl.recovery"> <vn-vertical ng-if="$ctrl.recovery">
<p><vn-label translate>Since</vn-label> {{$ctrl.recovery.started}}</p> <p><vn-label translate>Since</vn-label> {{$ctrl.recovery.started | date:'dd/MM/yyyy'}}</p>
<p><vn-label translate>To</vn-label> {{$ctrl.recovery.finished}}</p> <p><vn-label translate>To</vn-label> {{$ctrl.recovery.finished | date:'dd/MM/yyyy'}}</p>
<p><vn-label translate>Amount</vn-label> {{$ctrl.recovery.amount | currency:'€':2}}</p> <p><vn-label translate>Amount</vn-label> {{$ctrl.recovery.amount | currency:'€':2}}</p>
<p><vn-label translate>Period</vn-label> {{$ctrl.recovery.period}}</p> <p><vn-label translate>Period</vn-label> {{$ctrl.recovery.period}}</p>
</vn-vertical> </vn-vertical>
@ -145,8 +145,8 @@
</p> </p>
<p> <p>
<vn-label translate>Secured credit</vn-label> <vn-label translate>Secured credit</vn-label>
<b ng-if="!$ctrl.summary.creditInsurance">-</b> <span ng-if="!$ctrl.summary.creditInsurance">-</span>
<b ng-if="$ctrl.summary.creditInsurance">{{$ctrl.summary.creditInsurance | currency:'€':2}} <span ng-if="$ctrl.summary.creditInsurance">{{$ctrl.summary.creditInsurance | currency:'€':2}}</span>
</p> </p>
</vn-one> </vn-one>
</vn-horizontal> </vn-horizontal>

View File

@ -1,53 +1,29 @@
import ngModule from '../module'; import ngModule from '../module';
class ClientSummary { class Controller {
constructor($http) { constructor($http) {
this.$http = $http; this.$http = $http;
} }
set client(value) { $onChanges() {
if (!value) if (!this.client || !this.client.id)
return; return;
this.getSummary();
this.getGreuse();
this.getRecoveries();
}
getSummary() {
let filter = { let filter = {
include: [ include: [
{ {relation: 'account', scope: {fields: ['name', 'active']}},
relation: 'account', {relation: 'salesPerson', scope: {fields: ['name']}},
scope: { {relation: 'country', scope: {fields: ['country']}},
fields: ['name', 'active'] {relation: 'province', scope: {fields: ['name']}},
} {relation: 'contactChannel', scope: {fields: ['name']}},
}, {relation: 'payMethod', scope: {fields: ['name']}},
{
relation: 'salesPerson',
scope: {
fields: ['name']
}
},
{
relation: 'country',
scope: {
fields: ['country']
}
},
{
relation: 'province',
scope: {
fields: ['name']
}
},
{
relation: 'contactChannel',
scope: {
fields: ['name']
}
},
{
relation: 'payMethod',
scope: {
fields: ['name']
}
},
{ {
relation: 'addresses', relation: 'addresses',
scope: { scope: {
@ -57,43 +33,46 @@ class ClientSummary {
} }
] ]
}; };
filter = encodeURIComponent(JSON.stringify(filter));
let clientSummary = `/client/api/Clients/${value.id}?filter=${JSON.stringify(filter)}`; let query = `/client/api/Clients/${this.client.id}?filter=${filter}`;
this.$http.get(query).then(res => {
this.$http.get(encodeURIComponent(clientSummary)).then(res => {
if (res.data) { if (res.data) {
this.summary = res.data; this.summary = res.data;
this.address = res.data.addresses[0]; this.address = res.data.addresses[0];
} }
}); });
}
let greugeSum = `/client/api/Greuges/${value.id}/sumAmount`; getGreuse() {
let query = `/client/api/Greuges/${this.client.id}/sumAmount`;
this.$http.get(encodeURIComponent(greugeSum)).then(res => { this.$http.get(query).then(res => {
if (res.data) if (res.data)
this.greuge = res.data; this.greuge = res.data;
}); });
}
let recoveryFilter = { getRecoveries() {
let filter = {
where: { where: {
and: [{clientFk: value.id}, {or: [{finished: null}, {finished: {gt: Date.now()}}]}] and: [{clientFk: this.client.id}, {or: [{finished: null}, {finished: {gt: Date.now()}}]}]
}, },
limit: 1 limit: 1
}; };
filter = encodeURIComponent(JSON.stringify(filter));
let recovery = `/client/api/Recoveries?filter=${JSON.stringify(recoveryFilter)}`; let query = `/client/api/Recoveries?filter=${filter}`;
this.$http.get(query).then(res => {
this.$http.get(encodeURIComponent(recovery)).then(res => {
if (res.data) if (res.data)
this.recovery = res.data[0]; this.recovery = res.data[0];
}); });
} }
} }
ClientSummary.$inject = ['$http']; Controller.$inject = ['$http'];
ngModule.component('vnClientSummary', { ngModule.component('vnClientSummary', {
template: require('./client-summary.html'), template: require('./client-summary.html'),
controller: ClientSummary, controller: Controller,
bindings: { bindings: {
client: '<' client: '<'
} }

View File

@ -3,3 +3,4 @@ Enable web access: Habilitar acceso web
New password: Nueva contraseña New password: Nueva contraseña
Repeat password: Repetir contraseña Repeat password: Repetir contraseña
Change password: Cambiar contraseña Change password: Cambiar contraseña
Client must be checked to activate: No se puede activar un cliente si no esta verificado (036)

View File

@ -23,6 +23,7 @@
vn-one vn-one
margin-medium-top margin-medium-top
label="User" label="User"
info="Client must be checked to activate"
field="$ctrl.account.name"> field="$ctrl.account.name">
</vn-textfield> </vn-textfield>
</vn-horizontal> </vn-horizontal>

View File

@ -16,8 +16,8 @@
</div> </div>
<label class="mdl-textfield__label" translate>{{::$ctrl.label}}</label> <label class="mdl-textfield__label" translate>{{::$ctrl.label}}</label>
</div> </div>
<vn-drop-down
vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(value)">
</vn-drop-down>
</div> </div>
<vn-drop-down
vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(value)">
</vn-drop-down>

View File

@ -45,6 +45,9 @@ export default class DropDown extends Component {
this._search = value; this._search = value;
this.$.model.clear(); this.$.model.clear();
if (value != null)
this._activeOption = 0;
this.$timeout.cancel(this.searchTimeout); this.$timeout.cancel(this.searchTimeout);
this.searchTimeout = this.$timeout(() => { this.searchTimeout = this.$timeout(() => {
this.refreshModel(); this.refreshModel();
@ -86,8 +89,8 @@ export default class DropDown extends Component {
* @param {String} search The initial search term or %null * @param {String} search The initial search term or %null
*/ */
show(search) { show(search) {
this.search = search;
this._activeOption = -1; this._activeOption = -1;
this.search = search;
this.buildList(); this.buildList();
this.$.popover.parent = this.parent; this.$.popover.parent = this.parent;
this.$.popover.show(); this.$.popover.show();
@ -101,7 +104,7 @@ export default class DropDown extends Component {
} }
/** /**
* Activates a option and scrolls the drop-down to that option. * Activates an option and scrolls the drop-down to that option.
* *
* @param {Number} option The option index * @param {Number} option The option index
*/ */
@ -122,7 +125,7 @@ export default class DropDown extends Component {
} }
/** /**
* Activates a option. * Activates an option.
* *
* @param {Number} option The option index * @param {Number} option The option index
*/ */
@ -146,10 +149,11 @@ export default class DropDown extends Component {
* @param {Number} option The option index * @param {Number} option The option index
*/ */
selectOption(option) { selectOption(option) {
if (option != -1) { let data = this.$.model.data;
let data = this.$.model.data; let item = option != -1 && data ? data[option] : null;
let item = data ? data[option] : null;
let value = item ? item[this.valueField] : null; if (item) {
let value = item[this.valueField];
if (this.multiple) { if (this.multiple) {
if (!Array.isArray(this.selection)) { if (!Array.isArray(this.selection)) {
@ -252,6 +256,9 @@ export default class DropDown extends Component {
let nOpts = data ? data.length - 1 : 0; let nOpts = data ? data.length - 1 : 0;
switch (event.keyCode) { switch (event.keyCode) {
case 9: // Tab
this.selectOption(option);
return;
case 13: // Enter case 13: // Enter
this.selectOption(option); this.selectOption(option);
break; break;

View File

@ -88,6 +88,9 @@ export default class Popover extends Component {
this.showTimeout = this.$timeout(() => { this.showTimeout = this.$timeout(() => {
this.element.style.display = 'none'; this.element.style.display = 'none';
this.showTimeout = null; this.showTimeout = null;
if (this.onClose)
this.onClose();
}, 250); }, 250);
this.document.removeEventListener('keydown', this.docKeyDownHandler); this.document.removeEventListener('keydown', this.docKeyDownHandler);
@ -95,12 +98,6 @@ export default class Popover extends Component {
if (this.deregisterCallback) if (this.deregisterCallback)
this.deregisterCallback(); this.deregisterCallback();
if (this.parent)
this.parent.focus();
if (this.onClose)
this.onClose();
} }
/** /**
@ -189,35 +186,3 @@ ngModule.component('vnPopover', {
onClose: '&?' onClose: '&?'
} }
}); });
class PopoverService {
constructor($document, $compile, $transitions, $rootScope) {
this.$compile = $compile;
this.$rootScope = $rootScope;
this.$document = $document;
this.stack = [];
}
show(child, parent, $scope) {
let element = this.$compile('<vn-popover/>')($scope || this.$rootScope)[0];
let popover = element.$ctrl;
popover.parent = parent;
popover.child = child;
popover.show();
popover.onClose = () => {
this.$document[0].body.removeChild(element);
if ($scope) $scope.$destroy();
};
this.$document[0].body.appendChild(element);
return popover;
}
showComponent(componentTag, $scope, parent) {
let $newScope = $scope.$new();
let childElement = this.$compile(`<${componentTag}/>`)($newScope)[0];
this.show(childElement, parent, $newScope);
return childElement;
}
}
PopoverService.$inject = ['$document', '$compile', '$transitions', '$rootScope'];
ngModule.service('vnPopover', PopoverService);

View File

@ -1,7 +1,9 @@
.icon-barcode:before { content: '\e800'; } /* '' */ .icon-tags:before { content: '\e800'; } /* '' */
.icon-volume:before { content: '\e801'; } /* '' */ .icon-volume:before { content: '\e801'; } /* '' */
.icon-bucket:before { content: '\e802'; } /* '' */ .icon-barcode:before { content: '\e802'; } /* '' */
.icon-bucket:before { content: '\e803'; } /* '' */
.icon-frozen:before { content: '\e808'; } /* '' */
.icon-disabled:before { content: '\e80b'; } /* '' */ .icon-disabled:before { content: '\e80b'; } /* '' */
.icon-invoices:before { content: '\e80c'; } /* '' */ .icon-invoices:before { content: '\e80c'; } /* '' */
.icon-noweb:before { content: '\e812'; } /* '' */ .icon-noweb:before { content: '\e812'; } /* '' */
@ -15,4 +17,6 @@
.icon-addperson:before { content: '\e81e'; } /* '' */ .icon-addperson:before { content: '\e81e'; } /* '' */
.icon-bin:before { content: '\e81f'; } /* '' */ .icon-bin:before { content: '\e81f'; } /* '' */
.icon-sms:before { content: '\e820'; } /* '' */ .icon-sms:before { content: '\e820'; } /* '' */
.icon-tags:before { content: '\e821'; } /* '' */ .icon-ticket:before { content: '\e821'; } /* '' */
.icon-tax:before { content: '\e822'; } /* '' */
.icon-no036:before { content: '\e823'; } /* '' */

View File

@ -58,7 +58,7 @@
"component": "vn-item-tax", "component": "vn-item-tax",
"menu": { "menu": {
"description": "Tax", "description": "Tax",
"icon": "folder" "icon": "icon-tax"
} }
}, },
{ {

View File

@ -13,6 +13,7 @@ export default class Controller {
this.removedBarcodes = []; this.removedBarcodes = [];
this.oldBarcodes = {}; this.oldBarcodes = {};
} }
_setIconAdd() { _setIconAdd() {
if (this.barcodes.length) { if (this.barcodes.length) {
this.barcodes.map(element => { this.barcodes.map(element => {
@ -30,11 +31,13 @@ export default class Controller {
this.$scope.form.$setDirty(); this.$scope.form.$setDirty();
} }
} }
_unsetDirtyForm() { _unsetDirtyForm() {
if (this.$scope.form) { if (this.$scope.form) {
this.$scope.form.$setPristine(); this.$scope.form.$setPristine();
} }
} }
_equalBarcodes(oldBarcode, newBarcode) { _equalBarcodes(oldBarcode, newBarcode) {
return oldBarcode.id === newBarcode.id && oldBarcode.code === newBarcode.code; return oldBarcode.id === newBarcode.id && oldBarcode.code === newBarcode.code;
} }

View File

@ -8,7 +8,8 @@
<vn-title>Item Niches</vn-title> <vn-title>Item Niches</vn-title>
<vn-horizontal ng-repeat="itemNiche in $ctrl.niches track by $index"> <vn-horizontal ng-repeat="itemNiche in $ctrl.niches track by $index">
<vn-autocomplete <vn-autocomplete
vn-three ng-if="!itemNiche.id"
vn-one
data="$ctrl.warehouses" data="$ctrl.warehouses"
show-field="name" show-field="name"
value-field="id" value-field="id"
@ -18,7 +19,14 @@
vn-acl="buyer,replenisher"> vn-acl="buyer,replenisher">
</vn-autocomplete> </vn-autocomplete>
<vn-textfield <vn-textfield
vn-three ng-if="itemNiche.id"
vn-one
label="Warehouse"
model="itemNiche.warehouse.name"
disabled="true">
</vn-textfield>
<vn-textfield
vn-two
label="code" label="code"
model="itemNiche.code" model="itemNiche.code"
rule="itemNiche.code" rule="itemNiche.code"

View File

@ -31,6 +31,7 @@ export default class Controller {
this.$scope.form.$setDirty(); this.$scope.form.$setDirty();
} }
} }
_unsetDirtyForm() { _unsetDirtyForm() {
if (this.$scope.form) { if (this.$scope.form) {
this.$scope.form.$setPristine(); this.$scope.form.$setPristine();
@ -70,6 +71,7 @@ export default class Controller {
where: {itemFk: this.params.id}, where: {itemFk: this.params.id},
include: {relation: 'warehouse'} include: {relation: 'warehouse'}
}; };
this.$http.get(`/item/api/ItemNiches?filter=${JSON.stringify(filter)}`).then(response => { this.$http.get(`/item/api/ItemNiches?filter=${JSON.stringify(filter)}`).then(response => {
this.niches = response.data; this.niches = response.data;
this.setOldNiches(response); this.setOldNiches(response);
@ -95,6 +97,7 @@ export default class Controller {
create: [], create: [],
update: [] update: []
}; };
this.niches.forEach(niche => { this.niches.forEach(niche => {
let isNewNiche = !niche.id; let isNewNiche = !niche.id;

View File

@ -92,7 +92,7 @@ describe('Item', () => {
}); });
describe('submit()', () => { describe('submit()', () => {
it("should return an error message 'The niche must be unique' when the niche code isnt unique", () => { it("should return an error message 'The niche must be unique' when the niche warehouse isnt unique", () => {
controller.$scope.form = {}; controller.$scope.form = {};
spyOn(controller.vnApp, 'showMessage').and.callThrough(); spyOn(controller.vnApp, 'showMessage').and.callThrough();
controller.niches = [ controller.niches = [

View File

@ -8,6 +8,7 @@
<vn-title>Item tags</vn-title> <vn-title>Item tags</vn-title>
<vn-horizontal ng-repeat="itemTag in $ctrl.instancedItemTags track by $index"> <vn-horizontal ng-repeat="itemTag in $ctrl.instancedItemTags track by $index">
<vn-autocomplete <vn-autocomplete
ng-if="!itemTag.id"
vn-one vn-one
initial-data="itemTag.tag" initial-data="itemTag.tag"
field="itemTag.tagFk" field="itemTag.tagFk"
@ -16,6 +17,13 @@
label="Tag" label="Tag"
vn-acl="buyer"> vn-acl="buyer">
</vn-autocomplete> </vn-autocomplete>
<vn-textfield
ng-if="itemTag.id"
vn-one
label="Tag"
model="itemTag.tag.name"
disabled="true">
</vn-textfield>
<vn-textfield <vn-textfield
vn-three vn-three
label="Value" label="Value"

View File

@ -1,14 +1,18 @@
<form ng-submit="$ctrl.onSubmit()"> <form ng-submit="$ctrl.onSubmit()">
<vn-horizontal> <vn-horizontal>
<vn-textfield vn-one label="Search" model="$ctrl.stringSearch"></vn-textfield> <vn-textfield vn-one label="Search" model="$ctrl.stringSearch"></vn-textfield>
<vn-icon <vn-icon
pad-medium-top pad-medium-top
ng-if="$ctrl.advanced" ng-if="$ctrl.advanced"
ng-click="$ctrl.onpenFilters($event)" ng-click="$ctrl.onpenFilters($event)"
icon="keyboard_arrow_down" icon="keyboard_arrow_down"
style="cursor: pointer;"> style="cursor: pointer;">
</vn-icon> </vn-icon>
<vn-button ng-if="$ctrl.label" vn-none label="{{$ctrl.label}}"></vn-button> <vn-button ng-if="$ctrl.label" vn-none label="{{$ctrl.label}}"></vn-button>
<vn-icon-button ng-if="!$ctrl.label" icon="search" ng-click="$ctrl.clearFilter()"></vn-icon-button> <vn-icon-button ng-if="!$ctrl.label" icon="search" ng-click="$ctrl.clearFilter()"></vn-icon-button>
</vn-horizontal> </vn-horizontal>
</form> </form>
<vn-popover
vn-id="popover"
on-close="$ctrl.onPopoverClose()">
</vn-popover>

View File

@ -1,12 +1,10 @@
import ngModule from '../../module'; import ngModule from '../../module';
export default class Controller { export default class Controller {
constructor($element, $scope, $document, $compile, vnPopover, $timeout, $state, $transitions) { constructor($element, $scope, $compile, $timeout, $state, $transitions) {
this.element = $element[0]; this.element = $element[0];
this.$scope = $scope; this.$ = $scope;
this.$document = $document;
this.$compile = $compile; this.$compile = $compile;
this.vnPopover = vnPopover;
this.$timeout = $timeout; this.$timeout = $timeout;
this.stringSearch = ''; this.stringSearch = '';
this.$state = $state; this.$state = $state;
@ -91,23 +89,32 @@ export default class Controller {
filter = this.getFiltersFromString(this.stringSearch); filter = this.getFiltersFromString(this.stringSearch);
} }
this.child = this.vnPopover.showComponent(this.popover, this.$scope, this.element); this.$child = this.$compile(`<${this.popover}/>`)(this.$.$new());
// XXX: ¿Existe una forma más adecuada de acceder al controlador de un componente? var childCtrl = this.$child.isolateScope().$ctrl;
var childCtrl = angular.element(this.child).isolateScope().$ctrl;
childCtrl.filter = Object.assign({}, filter); childCtrl.filter = Object.assign({}, filter);
childCtrl.onSubmit = filter => this.onChildSubmit(filter); childCtrl.onSubmit = filter => this.onChildSubmit(filter);
if (this.data) if (this.data)
childCtrl.data = Object.assign({}, this.data); childCtrl.data = Object.assign({}, this.data);
event.preventDefault(); event.preventDefault();
this.$.popover.parent = this.element;
this.$.popover.child = this.$child[0];
this.$.popover.show();
}
onPopoverClose() {
this.$child.scope().$destroy();
this.$child.remove();
this.$child = null;
} }
onChildSubmit(filter) { onChildSubmit(filter) {
this.$.popover.hide();
this.stringSearch = this.createStringFromObject(filter); this.stringSearch = this.createStringFromObject(filter);
this.clearFilter(); this.clearFilter();
this.$timeout(() => { this.$timeout(() => this.onSubmit());
this.onSubmit();
});
} }
onSubmit() { onSubmit() {
@ -120,12 +127,6 @@ export default class Controller {
if (this.onSearch) if (this.onSearch)
this.onSearch(); this.onSearch();
if (angular.element(this.child)) {
if (angular.element(this.child).scope())
angular.element(this.child).scope().$destroy();
angular.element(this.child).remove();
}
delete this.child;
this.pushFiltersToState(filter); this.pushFiltersToState(filter);
} }
@ -139,12 +140,10 @@ export default class Controller {
let filter = JSON.parse(decodeURIComponent(this.$state.params.q)); let filter = JSON.parse(decodeURIComponent(this.$state.params.q));
this.stringSearch = this.createStringFromObject(filter); this.stringSearch = this.createStringFromObject(filter);
} }
this.$timeout(() => { this.$timeout(() => this.onSubmit());
this.onSubmit();
});
} }
} }
Controller.$inject = ['$element', '$scope', '$document', '$compile', 'vnPopover', '$timeout', '$state', '$transitions']; Controller.$inject = ['$element', '$scope', '$compile', '$timeout', '$state', '$transitions'];
ngModule.component('vnSearchbar', { ngModule.component('vnSearchbar', {
template: require('./searchbar.html'), template: require('./searchbar.html'),

View File

@ -19,3 +19,8 @@ phone: Teléfono
creditInsurance: Crédito Asegurado creditInsurance: Crédito Asegurado
Return to module index: Volver a la página principal del módulo Return to module index: Volver a la página principal del módulo
Preview: Vista previa Preview: Vista previa
Client has debt: Cliente con riesgo
Web Account inactive: Sin acceso Web
Client Frozen: Cliente congelado
Client inactive: Cliente inactivo
Client not checked: Cliente no comprobado

View File

@ -215,7 +215,7 @@ vn-main-block {
color: #ffa410; color: #ffa410;
margin-left: .5em; margin-left: .5em;
transition: opacity 250ms ease-out; transition: opacity 250ms ease-out;
font-size: 2em;
&:hover { &:hover {
opacity: 1; opacity: 1;
} }

View File

@ -1,14 +1,92 @@
{ {
"module": "ticket", "module": "ticket",
"name": "Tickets", "name": "Tickets",
"icon": "receipt", "icon": "icon-ticket",
"validations": false, "validations": false,
"routes": [ "routes": [
{ {
"url": "/tickets", "url": "/ticket",
"state": "tickets", "state": "ticket",
"component": "vn-ticket-index", "abstract": true,
"acl": ["developer"] "component": "ui-view"
},
{
"url": "/list?q",
"state": "ticket.list",
"component": "vn-ticket-list"
},
{
"url": "/create",
"state": "ticket.create",
"component": "vn-ticket-create"
},
{
"url": "/:id",
"state": "ticket.card",
"abstract": true,
"component": "vn-ticket-card"
},
{
"url": "/summary",
"state": "ticket.card.summary",
"component": "vn-ticket-summary",
"params": {
"ticket": "$ctrl.ticket"
}
},
{
"url" : "/data",
"state": "ticket.card.data",
"component": "vn-ticket-data",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Basic data",
"icon": "settings"
}
},
{
"url": "/observations",
"state": "ticket.card.observations",
"component": "vn-ticket-observations",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Notes",
"icon": "insert_drive_file"
}
},
{
"url" : "/package",
"abstract": true,
"state": "ticket.card.package",
"component": "ui-view"
},
{
"url": "/list",
"state": "ticket.card.package.list",
"component": "vn-ticket-package-list",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Packages",
"icon": "icon-bucket"
}
},
{
"url" : "/review",
"state": "ticket.card.review",
"component": "vn-ticket-review",
"params": {
"ticket": "$ctrl.ticket"
},
"menu": {
"description": "Review",
"icon": "remove_red_eye"
}
} }
] ]
} }

View File

@ -0,0 +1,11 @@
<vn-main-block>
<vn-horizontal>
<vn-auto margin-medium-v class="left-block">
<vn-item-descriptor ticket="$ctrl.ticket"></vn-ticket-descriptor>
<vn-left-menu></vn-left-menu>
</vn-auto>
<vn-one>
<vn-vertical margin-medium ui-view></vn-vertical>
</vn-one>
</vn-horizontal>
</vn-main-block>

View File

@ -0,0 +1,35 @@
import ngModule from '../module';
class TicketCard {
constructor($http, $state, $timeout) {
this.$http = $http;
this.$state = $state;
this.$timeout = $timeout;
this.ticket = null;
}
_getTicket() {
let filter = {
};
this.$http.get(`/ticket/api/Tickets/${this.$state.params.id}?filter=${JSON.stringify(filter)}`)
.then(res => {
if (res.data && res.data.id) {
this.$timeout(() => {
this.ticket = res.data;
});
}
}
);
}
$onInit() {
this._getTicket();
}
}
TicketCard.$inject = ['$http', '$state', '$timeout'];
ngModule.component('vnTicketCard', {
template: require('./ticket-card.html'),
controller: TicketCard
});

View File

@ -0,0 +1,18 @@
<mg-ajax path="/item/api/Tickets" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.ticket"
form="form"
save="post">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()" margin-medium>
<div style="max-width: 70em; margin: 0 auto;">
<vn-card pad-large>
<vn-title>New ticket</vn-title>
</vn-card>
<vn-button-bar>
<vn-submit label="Create"></vn-submit>
</vn-button-bar>
</div>
</form>

View File

@ -0,0 +1,21 @@
import ngModule from '../module';
class TicketCreate {
constructor($scope, $state) {
this.$ = $scope;
this.$state = $state;
this.Ticket = {};
}
onSubmit() {
this.$.watcher.submit().then(
json => this.$state.go('ticket.card.data', {id: json.data.id})
);
}
}
TicketCreate.$inject = ['$scope', '$state'];
ngModule.component('vnTicketCreate', {
template: require('./ticket-create.html'),
controller: TicketCreate
});

View File

View File

View File

@ -1,10 +1,10 @@
<a <a
ui-sref="clientCard.basicData({ id: {{::$ctrl.ticket.id}} })" ui-sref="ticket.card.summary({ id: {{::$ctrl.ticket.id}} })"
translate-attr="{title: 'View client'}" translate-attr="{title: 'View client'}"
class="vn-list-item"> class="vn-list-item">
<vn-horizontal ng-click="$ctrl.onClick($event)"> <vn-horizontal ng-click="$ctrl.onClick($event)">
<vn-one> <vn-one>
<h6>{{::$ctrl.ticket.name}}</h6> <h6>{{::$ctrl.ticket.nickname}}</h6>
<div><vn-label translate>Id</vn-label> {{::$ctrl.ticket.id}}</div> <div><vn-label translate>Id</vn-label> {{::$ctrl.ticket.id}}</div>
</vn-one> </vn-one>
<vn-horizontal class="buttons"> <vn-horizontal class="buttons">

View File

@ -13,7 +13,7 @@ class Controller {
ngModule.component('vnTicketItem', { ngModule.component('vnTicketItem', {
controller: Controller, controller: Controller,
template: require('./item.html'), template: require('./ticket-item.html'),
bindings: { bindings: {
ticket: '<' ticket: '<'
} }

View File

@ -1,4 +1,4 @@
<mg-ajax path="/client/api/Clients/filter" options="mgIndex"></mg-ajax> <mg-ajax path="/ticket/api/Tickets/filter" options="vnIndexNonAuto"></mg-ajax>
<div margin-medium> <div margin-medium>
<div class="vn-list"> <div class="vn-list">
<vn-card> <vn-card>
@ -19,6 +19,6 @@
<vn-paging index="index" total="index.model.count"></vn-paging> <vn-paging index="index" total="index.model.count"></vn-paging>
</div> </div>
</div> </div>
<a ui-sref="create" fixed-bottom-right> <a ui-sref="ticket.create" fixed-bottom-right>
<vn-float-button icon="person_add"></vn-float-button> <vn-float-button icon="add"></vn-float-button>
</a> </a>

View File

@ -1,5 +1,6 @@
import ngModule from '../module'; import ngModule from '../module';
import './item'; import './ticket-item';
import './style.scss';
export default class Controller { export default class Controller {
search(index) { search(index) {
@ -8,7 +9,7 @@ export default class Controller {
} }
Controller.$inject = []; Controller.$inject = [];
ngModule.component('vnTicketIndex', { ngModule.component('vnTicketList', {
template: require('./index.html'), template: require('./ticket-list.html'),
controller: Controller controller: Controller
}); });

View File

@ -1 +1,6 @@
Tickets: Tickets Tickets: Tickets
Notes: Notas
Observation type: Tipo de observación
Description: Descripción
The observation type must be unique: El tipo de observación debe ser único
Some fields are invalid: Algunos campos no son válidos

View File

@ -0,0 +1,60 @@
<vn-watcher
vn-id="watcher"
url="/client/api/Addresses"
id-field="id"
data="$ctrl.address"
form="form">
</vn-watcher>
<form name="form" ng-submit="$ctrl.submit()">
<vn-card pad-large>
<vn-one margin-medium-top>
<vn-title>Notes</vn-title>
<mg-ajax path="/ticket/api/ObservationTypes" options="mgIndex as observationTypes"></mg-ajax>
<vn-horizontal ng-repeat="ticketObservation in $ctrl.ticketObservations track by $index">
<vn-autocomplete
ng-if="!ticketObservation.id"
vn-one
initial-data="ticketObservation.observationType"
field="ticketObservation.observationTypeFk"
data="observationTypes.model"
show-field="description"
label="Observation type">
</vn-autocomplete>
<vn-textfield
ng-if="ticketObservation.id"
vn-one
label="Observation type"
model="ticketObservation.observationType.description"
disabled="true">
</vn-textfield>
<vn-textfield
vn-two
margin-large-right
label="Description"
model="ticketObservation.description">
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
pointer
medium-grey
vn-tooltip="Remove note"
tooltip-position="left"
icon="remove_circle_outline"
ng-click="$ctrl.removeObservation($index)">
</vn-icon>
</vn-auto>
</vn-horizontal>
</vn-one>
<vn-one>
<vn-icon
pointer margin-medium-left vn-tooltip="Add note"
tooltip-position="right" orange icon="add_circle"
ng-if="observationTypes.model.length > $ctrl.ticketObservations.length"
ng-click="$ctrl.addObservation()">
</vn-icon>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,140 @@
import ngModule from '../module';
class TicketObservations {
constructor($stateParams, $scope, $http, $translate, vnApp) {
this.params = $stateParams;
this.$scope = $scope;
this.$http = $http;
this.$translate = $translate;
this.vnApp = vnApp;
this.ticketObservations = [];
this.oldObservations = {};
this.removedObservations = [];
}
_setIconAdd() {
if (this.ticketObservations.length) {
this.ticketObservations.map(element => {
element.showAddIcon = false;
return true;
});
this.ticketObservations[this.ticketObservations.length - 1].showAddIcon = true;
}
}
_setDirtyForm() {
if (this.$scope.form) {
this.$scope.form.$setDirty();
}
}
_unsetDirtyForm() {
if (this.$scope.form) {
this.$scope.form.$setPristine();
}
}
addObservation() {
this.ticketObservations.push({description: null, ticketFk: this.params.id, showAddIcon: true});
this._setIconAdd();
}
removeObservation(index) {
let item = this.ticketObservations[index];
if (item) {
this.ticketObservations.splice(index, 1);
this._setIconAdd();
if (item.id) {
this.removedObservations.push(item.id);
this._setDirtyForm();
}
}
}
_equalObservations(oldObservation, newObservation) {
return oldObservation.id === newObservation.id && oldObservation.observationTypeFk === newObservation.observationTypeFk && oldObservation.description === newObservation.description;
}
setOldObservations(response) {
this._setIconAdd();
response.data.forEach(observation => {
this.oldObservations[observation.id] = Object.assign({}, observation);
});
}
getObservations() {
let filter = {
where: {ticketFk: this.params.id},
include: ['observationType']
};
this.$http.get(`/ticket/api/TicketObservations?filter=${JSON.stringify(filter)}`).then(response => {
this.ticketObservations = response.data;
this.setOldObservations(response);
});
}
submit() {
let typesDefined = [];
let repeatedType = false;
let canSubmit;
let observationsObj = {
delete: this.removedObservations,
create: [],
update: []
};
this.ticketObservations.forEach(observation => {
let isNewObservation = !observation.id;
delete observation.showAddIcon;
if (typesDefined.indexOf(observation.observationTypeFk) !== -1) {
repeatedType = true;
return;
}
typesDefined.push(observation.observationTypeFk);
if (isNewObservation && observation.description && observation.observationTypeFk) {
observationsObj.create.push(observation);
}
if (!isNewObservation && !this._equalObservations(this.oldObservations[observation.id], observation)) {
observationsObj.update.push(observation);
}
});
if (this.$scope.form.$invalid) {
return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid'));
}
if (repeatedType) {
return this.vnApp.showMessage(this.$translate.instant('The observation type must be unique'));
}
canSubmit = observationsObj.update.length > 0 || observationsObj.create.length > 0 || observationsObj.delete.length > 0;
if (canSubmit) {
return this.$http.post(`/ticket/api/TicketObservations/crudTicketObservations`, observationsObj).then(() => {
this.getObservations();
this._unsetDirtyForm();
});
}
this.vnApp.showMessage(this.$translate.instant('No changes to save'));
}
$onInit() {
this.getObservations();
}
}
TicketObservations.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnApp'];
ngModule.component('vnTicketObservations', {
template: require('./ticket-observations.html'),
controller: TicketObservations,
bindings: {
ticket: '<'
}
});

View File

@ -0,0 +1,143 @@
import './ticket-observations.js';
describe('ticket', () => {
describe('Component vnTicketObservations', () => {
let $componentController;
let $state;
let controller;
let $httpBackend;
beforeEach(() => {
angular.mock.module('ticket');
});
beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => {
$componentController = _$componentController_;
$state = _$state_;
$httpBackend = _$httpBackend_;
controller = $componentController('vnTicketObservations', {$state: $state});
}));
describe('add / remove observation', () => {
it('should add one empty observation into controller observations collection and call _setIconAdd()', () => {
controller.ticketObservations = [];
spyOn(controller, '_setIconAdd').and.callThrough();
controller.addObservation();
expect(controller._setIconAdd).toHaveBeenCalledWith();
expect(controller.ticketObservations.length).toEqual(1);
expect(controller.ticketObservations[0].id).toBe(undefined);
expect(controller.ticketObservations[0].showAddIcon).toBeTruthy();
});
it('should remove an observation that occupies the position in the index given and call _setIconAdd()', () => {
let index = 2;
controller.ticketObservations = [
{id: 1, observationTypeFk: 1, description: 'one', showAddIcon: false},
{id: 2, observationTypeFk: 2, description: 'two', showAddIcon: false},
{id: 3, observationTypeFk: 3, description: 'three', showAddIcon: true}
];
spyOn(controller, '_setIconAdd').and.callThrough();
controller.removeObservation(index);
expect(controller._setIconAdd).toHaveBeenCalledWith();
expect(controller.ticketObservations.length).toEqual(2);
expect(controller.ticketObservations[0].showAddIcon).toBeFalsy();
expect(controller.ticketObservations[1].showAddIcon).toBeTruthy();
expect(controller.ticketObservations[index]).toBe(undefined);
});
});
describe('_equalObservations()', () => {
it('should return true if two observations are equals independent of control attributes', () => {
let observationOne = {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: true};
let observationTwo = {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: false};
let equals = controller._equalObservations(observationOne, observationTwo);
expect(equals).toBeTruthy();
});
it('should return false if two observations aint equals independent of control attributes', () => {
let observationOne = {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: true};
let observationTwo = {id: 1, observationTypeFk: 1, description: 'two', showAddIcon: true};
let equals = controller._equalObservations(observationOne, observationTwo);
expect(equals).toBeFalsy();
});
});
describe('get Observations()', () => {
it('should perform a GET query to receive the ticket observations', () => {
let res = [{id: 1, observationTypeFk: 1, description: 'one'}];
$httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond(res);
$httpBackend.expectGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`);
controller.getObservations();
$httpBackend.flush();
});
});
describe('submit()', () => {
it("should return an error message 'The observation type must be unique'", () => {
controller.$scope.form = {};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
controller.ticketObservations = [
{id: 1, observationTypeFk: 1, description: 'one', itemFk: 1},
{observationTypeFk: 1, description: 'one', itemFk: 1}
];
controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one', itemFk: 1}};
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The observation type must be unique');
});
it("should perfom a query to delete observations", () => {
controller.$scope.form = {$setPristine: () => {}};
controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one'}};
controller.ticketObservations = [];
controller.removedObservations = [1];
$httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond([]);
$httpBackend.expectPOST(`/ticket/api/TicketObservations/crudTicketObservations`).respond('ok!');
controller.submit();
$httpBackend.flush();
});
it("should perfom a query to update observations", () => {
controller.$scope.form = {$setPristine: () => {}};
controller.ticketObservations = [{id: 1, observationTypeFk: 1, description: 'number one!'}];
controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one'}};
$httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond([]);
$httpBackend.expectPOST(`/ticket/api/TicketObservations/crudTicketObservations`).respond('ok!');
controller.submit();
$httpBackend.flush();
});
it("should perfom a query to create new observation", () => {
controller.$scope.form = {$setPristine: () => {}};
controller.ticketObservations = [{observationTypeFk: 2, description: 'two'}];
$httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond([]);
$httpBackend.expectPOST(`/ticket/api/TicketObservations/crudTicketObservations`).respond('ok!');
controller.submit();
$httpBackend.flush();
});
it("should return a message 'No changes to save' when there are no changes to apply", () => {
controller.$scope.form = {$setPristine: () => {}};
spyOn(controller.vnApp, 'showMessage').and.callThrough();
controller.oldObservations = [
{id: 1, observationTypeFk: 1, description: 'one', showAddIcon: false},
{id: 2, observationTypeFk: 2, description: 'two', showAddIcon: true}
];
controller.ticketObservations = [];
controller.submit();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save');
});
});
});
});

View File

@ -0,0 +1,56 @@
<mg-ajax
path="/ticket/api/Tickets/{{index.params.id}}/packages"
options="mgIndex">
</mg-ajax>
<vn-card pad-large>
<vn-title>Packages</vn-title>
<vn-one>
<vn-horizontal ng-repeat="package in index.model track by package.id">
<vn-autocomplete vn-one
margin-large-right
url="/ticket/api/Packagings/listPackaging"
label="Package"
show-field="name"
value-field="packagingFk"
field="package.packagingFk">
<tpl-item>{{id}} : {{name}}</tpl-item>
</vn-autocomplete>
<vn-textfield
vn-one
margin-large-right
label="Quantity"
model="package.quantity">
</vn-textfield>
<vn-textfield
vn-one
margin-large-right
label="Added"
model="package.created | date: 'dd/MM/yyyy'"
disabled="true"
ng-readonly="true">
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
pointer
medium-grey
vn-tooltip="Remove package"
tooltip-position = "left"
icon="remove_circle_outline"
ng-click="$ctrl.removePackage($index)">
</vn-icon>
</vn-one>
</vn-horizontal>
</vn-one>
<vn-one>
<vn-icon
pointer
margin-medium-left
vn-tooltip="Add package"
tooltip-position = "right"
orange
icon="add_circle"
ng-click="$ctrl.addPackage()">
</vn-icon>
</vn-one>
</vn-card>

View File

@ -0,0 +1,19 @@
import ngModule from '../../module';
class Controller {
construct($http, $scope) {
this.$http = $http;
this.$ = $scope;
}
}
Controller.$inject = ['$http', '$scope'];
ngModule.component('vnTicketPackageList', {
template: require('./package-list.html'),
controller: Controller,
bindings: {
ticket: '<'
}
});

View File

@ -0,0 +1,6 @@
Packages: Embalajes
Package: Embalaje
Quantity: Cantidad
Added: Añadido
Add package: Añadir embalaje
Remove package: Quitar embalaje

View File

@ -0,0 +1,4 @@
date : Fecha
Employee : Empleado
State: Estado
Review: Revision

View File

@ -0,0 +1,23 @@
<mg-ajax path="" options="vnIndexNonAuto"></mg-ajax>
<vn-vertical pad-medium>
<vn-card pad-large>
<vn-vertical>
<vn-title>Review</vn-title>
<vn-grid-header on-order="$ctrl.onOrder(field, order)">
<vn-column-header vn-one pad-medium-h field="date" text="date"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="employee" text="Employee" default-order="ASC"></vn-column-header>
<vn-column-header vn-two pad-medium-h field="state" text="State" order-locked></vn-column-header>
</vn-grid-header>
<vn-one class="list list-content">
</vn-horizontal>
</vn-one>
<vn-one class="text-center pad-small-v" ng-if="index.model.count === 0" translate>No results</vn-one>
<vn-horizontal vn-one class="list list-footer"></vn-horizontal>
<vn-paging vn-one margin-large-top index="index" total="index.model.count"></vn-paging>
</vn-vertical>
</vn-card>
</vn-vertical>
<a ui-sref="clientCard.ticket.create" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,18 @@
import ngModule from '../module';
class ticketReview {
construct($http, $scope) {
this.$http = $http;
this.$ = $scope;
}
}
ticketReview.$inject = ['$http', '$scope'];
ngModule.component('vnTicketReview', {
template: require('./review.html'),
controller: ticketReview,
bindings: {
ticket: '<'
}
});

View File

@ -0,0 +1,9 @@
<vn-card class="summary">
<vn-vertical pad-medium>
<vn-auto>
<h5 text-center pad-small-v class="tittle">Ticket Summary</h5>
</vn-auto>
<vn-horizontal>
</vn-horizontal>
</vn-vertical>
</vn-card>

View File

@ -0,0 +1,12 @@
import ngModule from '../module';
class TicketSummary {}
TicketSummary.$inject = [];
ngModule.component('vnTicketSummary', {
template: require('./ticket-summary.html'),
controller: TicketSummary,
bindings: {
ticket: '<'
}
});

View File

@ -1,4 +1,10 @@
export * from './module'; export * from './module';
import './index/index'; import './list/ticket-list';
import './create/ticket-create';
import './card/ticket-card';
import './summary/ticket-summary';
import './data/ticket-data';
import './notes/ticket-observations';
import './package/list/package-list';
import './review/review';

View File

@ -11,7 +11,8 @@ export default {
}, },
moduleAccessView: { moduleAccessView: {
clientsSectionButton: `${components.vnModuleContainer}[ui-sref="clients"]`, clientsSectionButton: `${components.vnModuleContainer}[ui-sref="clients"]`,
itemsSectionButton: `${components.vnModuleContainer}[ui-sref="item.index"]` itemsSectionButton: `${components.vnModuleContainer}[ui-sref="item.index"]`,
ticketsSectionButton: `${components.vnModuleContainer}[ui-sref="ticket.list"]`
}, },
clientsIndex: { clientsIndex: {
searchClientInput: `${components.vnTextfield}`, searchClientInput: `${components.vnTextfield}`,
@ -188,24 +189,29 @@ export default {
itemTags: { itemTags: {
goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]', goToItemIndexButton: 'vn-item-descriptor [ui-sref="item.index"]',
tagsButton: `${components.vnMenuItem}[ui-sref="item.card.tags"]`, tagsButton: `${components.vnMenuItem}[ui-sref="item.card.tags"]`,
firstRemoveTagButton: `vn-item-tags vn-horizontal:nth-child(2) > ${components.vnIcon}[icon="remove_circle_outline"]`,
firstTagSelect: `vn-item-tags vn-horizontal:nth-child(2) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`, firstTagSelect: `vn-item-tags vn-horizontal:nth-child(2) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`,
firstTagDisabled: `vn-item-tags vn-horizontal:nth-child(2) > vn-textfield[label="Tag"] > div > input`,
firstTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(2) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, firstTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(2) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
firstValueInput: `vn-item-tags vn-horizontal:nth-child(2) > vn-textfield[label="Value"] > div > input`, firstValueInput: `vn-item-tags vn-horizontal:nth-child(2) > vn-textfield[label="Value"] > div > input`,
firstRelevancyInput: `vn-horizontal:nth-child(2) > vn-textfield[label="Relevancy"] > div > input`, firstRelevancyInput: `vn-horizontal:nth-child(2) > vn-textfield[label="Relevancy"] > div > input`,
secondTagSelect: `vn-item-tags vn-horizontal:nth-child(3) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`, secondTagSelect: `vn-item-tags vn-horizontal:nth-child(3) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`,
secondTagDisabled: `vn-item-tags vn-horizontal:nth-child(3) > vn-textfield[label="Tag"] > div > input`,
secondTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(3) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, secondTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(3) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
secondValueInput: `vn-item-tags vn-horizontal:nth-child(3) > vn-textfield[label="Value"] > div > input`, secondValueInput: `vn-item-tags vn-horizontal:nth-child(3) > vn-textfield[label="Value"] > div > input`,
secondRelevancyInput: `vn-horizontal:nth-child(3) > vn-textfield[label="Relevancy"] > div > input`, secondRelevancyInput: `vn-horizontal:nth-child(3) > vn-textfield[label="Relevancy"] > div > input`,
thirdTagSelect: `vn-item-tags vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`, thirdTagSelect: `vn-item-tags vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`,
thirdTagDisabled: `vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Tag"] > div > input`,
thirdTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, thirdTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
thirdValueInput: `vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] > div > input`, thirdValueInput: `vn-item-tags vn-horizontal:nth-child(4) > vn-textfield[label="Value"] > div > input`,
thirdRelevancyInput: `vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] > div > input`, thirdRelevancyInput: `vn-horizontal:nth-child(4) > vn-textfield[label="Relevancy"] > div > input`,
fourthTagSelect: `vn-item-tags vn-horizontal:nth-child(5) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`, fourthTagSelect: `vn-item-tags vn-horizontal:nth-child(5) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`,
fourthTagDisabled: `vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Tag"] > div > input`,
fourthTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(5) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`, fourthTagSelectOptionOne: `vn-item-tags vn-horizontal:nth-child(5) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(1)`,
fourthValueInput: `vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] > div > input`, fourthValueInput: `vn-item-tags vn-horizontal:nth-child(5) > vn-textfield[label="Value"] > div > input`,
fourthRelevancyInput: `vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] > div > input`, fourthRelevancyInput: `vn-horizontal:nth-child(5) > vn-textfield[label="Relevancy"] > div > input`,
fifthRemoveTagButton: `vn-item-tags vn-horizontal:nth-child(6) > ${components.vnIcon}[icon="remove_circle_outline"]`,
fifthTagSelect: `vn-item-tags vn-horizontal:nth-child(6) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`, fifthTagSelect: `vn-item-tags vn-horizontal:nth-child(6) > ${components.vnAutocomplete}[field="itemTag.tagFk"] input`,
fifthTagDisabled: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Tag"] > div > input`,
fifthTagSelectOptionFive: `vn-item-tags vn-horizontal:nth-child(6) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(5)`, fifthTagSelectOptionFive: `vn-item-tags vn-horizontal:nth-child(6) > ${components.vnAutocomplete}[field="itemTag.tagFk"] vn-drop-down ul > li:nth-child(5)`,
fifthValueInput: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] > div > input`, fifthValueInput: `vn-item-tags vn-horizontal:nth-child(6) > vn-textfield[label="Value"] > div > input`,
fifthRelevancyInput: `vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] > div > input`, fifthRelevancyInput: `vn-horizontal:nth-child(6) > vn-textfield[label="Relevancy"] > div > input`,
@ -233,14 +239,17 @@ export default {
nicheButton: `${components.vnMenuItem}[ui-sref="item.card.niche"]`, nicheButton: `${components.vnMenuItem}[ui-sref="item.card.niche"]`,
addNicheButton: `${components.vnIcon}[icon="add_circle"]`, addNicheButton: `${components.vnIcon}[icon="add_circle"]`,
firstWarehouseSelect: `${components.vnAutocomplete}[field="itemNiche.warehouseFk"] input`, firstWarehouseSelect: `${components.vnAutocomplete}[field="itemNiche.warehouseFk"] input`,
firstWarehouseDisabled: `vn-horizontal:nth-child(2) > vn-textfield[label="Warehouse"] > div > input`,
firstWarehouseSelectSecondOption: `${components.vnAutocomplete}[field="itemNiche.warehouseFk"] vn-drop-down ul > li:nth-child(2)`, firstWarehouseSelectSecondOption: `${components.vnAutocomplete}[field="itemNiche.warehouseFk"] vn-drop-down ul > li:nth-child(2)`,
firstCodeInput: `vn-horizontal:nth-child(2) > vn-textfield[label="code"] > div > input`,
secondWarehouseSelect: `vn-horizontal:nth-child(3) > ${components.vnAutocomplete}[field="itemNiche.warehouseFk"] input`, secondWarehouseSelect: `vn-horizontal:nth-child(3) > ${components.vnAutocomplete}[field="itemNiche.warehouseFk"] input`,
thirdWarehouseSelect: `vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemNiche.warehouseFk"] input`, secondWarehouseDisabled: `vn-horizontal:nth-child(3) > vn-textfield[label="Warehouse"] > div > input`,
thirdWarehouseSelectFourthOption: `vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemNiche.warehouseFk"] vn-drop-down ul > li:nth-child(4)`, secondCodeInput: `vn-horizontal:nth-child(3) > vn-textfield[label="code"] > div > input`,
secondNicheRemoveButton: `vn-horizontal:nth-child(3) > vn-one > ${components.vnIcon}[icon="remove_circle_outline"]`, secondNicheRemoveButton: `vn-horizontal:nth-child(3) > vn-one > ${components.vnIcon}[icon="remove_circle_outline"]`,
firstCodeInput: `vn-horizontal:nth-child(2) > ${components.vnTextfield}`, thirdWarehouseSelect: `vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemNiche.warehouseFk"] input`,
secondCodeInput: `vn-horizontal:nth-child(3) > ${components.vnTextfield}`, thirdWarehouseDisabled: `vn-horizontal:nth-child(4) > vn-textfield[label="Warehouse"] > div > input`,
thirdCodeInput: `vn-horizontal:nth-child(4) > ${components.vnTextfield}`, thirdWarehouseSelectFourthOption: `vn-horizontal:nth-child(4) > ${components.vnAutocomplete}[field="itemNiche.warehouseFk"] vn-drop-down ul > li:nth-child(4)`,
thirdCodeInput: `vn-horizontal:nth-child(4) > vn-textfield[label="code"] > div > input`,
submitNichesButton: `${components.vnSubmit}` submitNichesButton: `${components.vnSubmit}`
}, },
itemBotanical: { itemBotanical: {
@ -261,5 +270,25 @@ export default {
niche: `${components.vnItemSummary} > vn-horizontal:nth-child(2) > vn-one:nth-child(2) > vn-vertical > p:nth-child(2)`, niche: `${components.vnItemSummary} > vn-horizontal:nth-child(2) > vn-one:nth-child(2) > vn-vertical > p:nth-child(2)`,
botanical: `${components.vnItemSummary} > vn-horizontal:nth-child(2) > vn-one:nth-child(3) > vn-vertical > p`, botanical: `${components.vnItemSummary} > vn-horizontal:nth-child(2) > vn-one:nth-child(3) > vn-vertical > p`,
barcode: `${components.vnItemSummary} > vn-horizontal:nth-child(2) > vn-one:nth-child(4) > vn-vertical > p` barcode: `${components.vnItemSummary} > vn-horizontal:nth-child(2) > vn-one:nth-child(4) > vn-vertical > p`
},
ticketsIndex: {
createTicketButton: `${components.vnFloatButton}`,
searchResult: `vn-ticket-item a`,
searchTicketInput: `${components.vnTextfield}`,
searchButton: `${components.vnSearchBar} > vn-icon-button > button`
},
ticketNotes: {
notesButton: `${components.vnMenuItem}[ui-sref="ticket.card.observations"]`,
firstNoteRemoveButton: `${components.vnIcon}[icon="remove_circle_outline"]`,
addNoteButton: `${components.vnIcon}[icon="add_circle"]`,
firstNoteSelect: `${components.vnAutocomplete}[field="ticketObservation.observationTypeFk"] input`,
firstNoteSelectSecondOption: `${components.vnAutocomplete}[field="ticketObservation.observationTypeFk"] vn-drop-down ul > li:nth-child(2)`,
firstNoteDisabled: `vn-textfield[label="Observation type"] > div > input`,
firstDescriptionInput: `vn-textfield[label="Description"] > div > input`,
submitNotesButton: `${components.vnSubmit}`
},
ticketPackages: {
packagesButton: `${components.vnMenuItem}[ui-sref="ticket.card.package.list"]`,
firstPackageSelect: `${components.vnAutocomplete}[label="Package"] input`
} }
}; };

View File

@ -61,7 +61,7 @@ describe('lock verified data path', () => {
.wait(selectors.clientFiscalData.verifiedDataCheckboxInput) .wait(selectors.clientFiscalData.verifiedDataCheckboxInput)
.evaluate(selector => { .evaluate(selector => {
return document.querySelector(selector).className; return document.querySelector(selector).className;
}, 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > vn-client-fiscal-data > form > vn-card > div > vn-horizontal:nth-child(5) > vn-check:nth-child(3) > label') }, 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > vn-client-fiscal-data > form > vn-card > div > vn-horizontal:nth-child(5) > vn-check:nth-child(4) > label')
.then(result => { .then(result => {
expect(result).toContain('is-disabled'); expect(result).toContain('is-disabled');
}); });
@ -452,7 +452,7 @@ describe('lock verified data path', () => {
.wait(selectors.clientFiscalData.verifiedDataCheckboxInput) .wait(selectors.clientFiscalData.verifiedDataCheckboxInput)
.evaluate(selector => { .evaluate(selector => {
return document.querySelector(selector).className; return document.querySelector(selector).className;
}, 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > vn-client-fiscal-data > form > vn-card > div > vn-horizontal:nth-child(5) > vn-check:nth-child(3) > label') }, 'body > vn-app > vn-vertical > vn-vertical > vn-client-card > vn-main-block > vn-horizontal > vn-one > vn-vertical > vn-client-fiscal-data > form > vn-card > div > vn-horizontal:nth-child(5) > vn-check:nth-child(4) > label')
.then(result => { .then(result => {
expect(result).toContain('is-disabled'); expect(result).toContain('is-disabled');
}); });

View File

@ -66,9 +66,10 @@ describe('edit item basic data path', () => {
it(`should confirm the item name was edited`, () => { it(`should confirm the item name was edited`, () => {
return nightmare return nightmare
.click(selectors.itemNiches.firstCodeInput) .click(selectors.itemNiches.nicheButton)
.wait(selectors.itemNiches.firstCodeInput) .wait(selectors.itemNiches.firstWarehouseDisabled)
.waitToClick(selectors.itemBasicData.basicDataButton) .waitToClick(selectors.itemBasicData.basicDataButton)
.waitForTextInInput(selectors.itemBasicData.nameInput, 'Rose of Purity')
.getInputValue(selectors.itemBasicData.nameInput) .getInputValue(selectors.itemBasicData.nameInput)
.then(result => { .then(result => {
expect(result).toEqual('Rose of Purity'); expect(result).toEqual('Rose of Purity');

View File

@ -61,7 +61,7 @@ describe('add item tax path', () => {
it(`should confirm the item tax class was edited`, () => { it(`should confirm the item tax class was edited`, () => {
return nightmare return nightmare
.click(selectors.itemTags.tagsButton) .click(selectors.itemTags.tagsButton)
.wait(selectors.itemTags.firstTagSelect) .wait(selectors.itemTags.firstTagDisabled)
.waitToClick(selectors.itemTax.taxButton) .waitToClick(selectors.itemTax.taxButton)
.waitForTextInInput(selectors.itemTax.firstClassSelect, 'general') .waitForTextInInput(selectors.itemTax.firstClassSelect, 'general')
.getInputValue(selectors.itemTax.firstClassSelect) .getInputValue(selectors.itemTax.firstClassSelect)

View File

@ -43,22 +43,14 @@ describe('create item tags path', () => {
}); });
}); });
it(`should create a new tag, edit another and delete a former one`, () => { it(`should create a new tag and delete a former one`, () => {
return nightmare return nightmare
.waitToClick(selectors.itemTags.firstTagSelect) .waitToClick(selectors.itemTags.firstRemoveTagButton)
.waitToClick(selectors.itemTags.firstTagSelectOptionOne)
.clearInput(selectors.itemTags.firstValueInput)
.type(selectors.itemTags.firstValueInput, 'Dark Blue')
.clearInput(selectors.itemTags.firstRelevancyInput)
.type(selectors.itemTags.firstRelevancyInput, '2')
.waitToClick(selectors.itemTags.fifthRemoveTagButton)
.waitToClick(selectors.itemTags.addItemTagButton) .waitToClick(selectors.itemTags.addItemTagButton)
.waitToClick(selectors.itemTags.fifthTagSelect) .waitToClick(selectors.itemTags.fifthTagSelect)
.waitToClick(selectors.itemTags.fifthTagSelectOptionFive) .waitToClick(selectors.itemTags.fifthTagSelectOptionFive)
.type(selectors.itemTags.fifthValueInput, 'Thanos') .type(selectors.itemTags.fifthValueInput, 'Thanos')
.type(selectors.itemTags.fifthRelevancyInput, '1') .type(selectors.itemTags.fifthRelevancyInput, '1')
.clearInput(selectors.itemTags.secondRelevancyInput)
.type(selectors.itemTags.secondRelevancyInput, '5')
.click(selectors.itemTags.submitItemTagsButton) .click(selectors.itemTags.submitItemTagsButton)
.waitForSnackbar() .waitForSnackbar()
.then(result => { .then(result => {
@ -71,8 +63,8 @@ describe('create item tags path', () => {
.click(selectors.itemBasicData.basicDataButton) .click(selectors.itemBasicData.basicDataButton)
.wait(selectors.itemBasicData.nameInput) .wait(selectors.itemBasicData.nameInput)
.click(selectors.itemTags.tagsButton) .click(selectors.itemTags.tagsButton)
.waitForTextInInput(selectors.itemTags.firstTagSelect, 'Owner') .waitForTextInInput(selectors.itemTags.firstTagDisabled, 'Owner')
.getInputValue(selectors.itemTags.firstTagSelect) .getInputValue(selectors.itemTags.firstTagDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Owner'); expect(result).toEqual('Owner');
}); });
@ -98,19 +90,19 @@ describe('create item tags path', () => {
it(`should confirm the second select is the expected one`, () => { it(`should confirm the second select is the expected one`, () => {
return nightmare return nightmare
.waitForTextInInput(selectors.itemTags.secondTagSelect, 'Color') .waitForTextInInput(selectors.itemTags.secondTagDisabled, 'Location')
.getInputValue(selectors.itemTags.secondTagSelect) .getInputValue(selectors.itemTags.secondTagDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Color'); expect(result).toEqual('Location');
}); });
}); });
it(`should confirm the second value is the expected one`, () => { it(`should confirm the second value is the expected one`, () => {
return nightmare return nightmare
.waitForTextInInput(selectors.itemTags.secondValueInput, 'Dark Blue') .waitForTextInInput(selectors.itemTags.secondValueInput, 'Gamoras hideout')
.getInputValue(selectors.itemTags.secondValueInput) .getInputValue(selectors.itemTags.secondValueInput)
.then(result => { .then(result => {
expect(result).toEqual('Dark Blue'); expect(result).toEqual('Gamoras hideout');
}); });
}); });
@ -125,8 +117,8 @@ describe('create item tags path', () => {
it(`should confirm the third select is the expected one`, () => { it(`should confirm the third select is the expected one`, () => {
return nightmare return nightmare
.waitForTextInInput(selectors.itemTags.thirdTagSelect, 'Shape') .waitForTextInInput(selectors.itemTags.thirdTagDisabled, 'Shape')
.getInputValue(selectors.itemTags.thirdTagSelect) .getInputValue(selectors.itemTags.thirdTagDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Shape'); expect(result).toEqual('Shape');
}); });
@ -152,8 +144,8 @@ describe('create item tags path', () => {
it(`should confirm the fourth select is the expected one`, () => { it(`should confirm the fourth select is the expected one`, () => {
return nightmare return nightmare
.waitForTextInInput(selectors.itemTags.fourthTagSelect, 'Power') .waitForTextInInput(selectors.itemTags.fourthTagDisabled, 'Power')
.getInputValue(selectors.itemTags.fourthTagSelect) .getInputValue(selectors.itemTags.fourthTagDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Power'); expect(result).toEqual('Power');
}); });
@ -179,19 +171,19 @@ describe('create item tags path', () => {
it(`should confirm the fifth select is the expected one`, () => { it(`should confirm the fifth select is the expected one`, () => {
return nightmare return nightmare
.waitForTextInInput(selectors.itemTags.fifthTagSelect, 'Location') .waitForTextInInput(selectors.itemTags.fifthTagDisabled, 'Color')
.getInputValue(selectors.itemTags.fifthTagSelect) .getInputValue(selectors.itemTags.fifthTagDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Location'); expect(result).toEqual('Color');
}); });
}); });
it(`should confirm the fifth value is the expected one`, () => { it(`should confirm the fifth value is the expected one`, () => {
return nightmare return nightmare
.waitForTextInInput(selectors.itemTags.fifthValueInput, 'Gamoras hideout') .waitForTextInInput(selectors.itemTags.fifthValueInput, 'Yellow')
.getInputValue(selectors.itemTags.fifthValueInput) .getInputValue(selectors.itemTags.fifthValueInput)
.then(result => { .then(result => {
expect(result).toEqual('Gamoras hideout'); expect(result).toEqual('Yellow');
}); });
}); });

View File

@ -43,13 +43,9 @@ describe('create item niche path', () => {
}); });
}); });
it(`should click create a new niche, edit another and delete a former one`, () => { it(`should click create a new niche and delete a former one`, () => {
return nightmare return nightmare
.waitToClick(selectors.itemNiches.addNicheButton) .waitToClick(selectors.itemNiches.addNicheButton)
.waitToClick(selectors.itemNiches.firstWarehouseSelect)
.waitToClick(selectors.itemNiches.firstWarehouseSelectSecondOption)
.clearInput(selectors.itemNiches.firstCodeInput)
.type(selectors.itemNiches.firstCodeInput, 'A2')
.waitToClick(selectors.itemNiches.secondNicheRemoveButton) .waitToClick(selectors.itemNiches.secondNicheRemoveButton)
.waitToClick(selectors.itemNiches.thirdWarehouseSelect) .waitToClick(selectors.itemNiches.thirdWarehouseSelect)
.waitToClick(selectors.itemNiches.thirdWarehouseSelectFourthOption) .waitToClick(selectors.itemNiches.thirdWarehouseSelectFourthOption)
@ -66,21 +62,21 @@ describe('create item niche path', () => {
.click(selectors.itemBasicData.basicDataButton) .click(selectors.itemBasicData.basicDataButton)
.wait(selectors.itemBasicData.nameInput) .wait(selectors.itemBasicData.nameInput)
.click(selectors.itemNiches.nicheButton) .click(selectors.itemNiches.nicheButton)
.waitForTextInInput(selectors.itemNiches.firstWarehouseSelect, 'Warehouse Two') .waitForTextInInput(selectors.itemNiches.firstWarehouseDisabled, 'Warehouse One')
.getInputValue(selectors.itemNiches.firstWarehouseSelect) .getInputValue(selectors.itemNiches.firstWarehouseDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Warehouse Two'); expect(result).toEqual('Warehouse One');
return nightmare return nightmare
.getInputValue(selectors.itemNiches.firstCodeInput); .getInputValue(selectors.itemNiches.firstCodeInput);
}) })
.then(result => { .then(result => {
expect(result).toEqual('A2'); expect(result).toEqual('A1');
}); });
}); });
it(`should confirm the second niche is the expected one`, () => { it(`should confirm the second niche is the expected one`, () => {
return nightmare return nightmare
.getInputValue(selectors.itemNiches.secondWarehouseSelect) .getInputValue(selectors.itemNiches.secondWarehouseDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Warehouse Three'); expect(result).toEqual('Warehouse Three');
return nightmare return nightmare
@ -93,7 +89,7 @@ describe('create item niche path', () => {
it(`should confirm the third niche is the expected one`, () => { it(`should confirm the third niche is the expected one`, () => {
return nightmare return nightmare
.getInputValue(selectors.itemNiches.thirdWarehouseSelect) .getInputValue(selectors.itemNiches.thirdWarehouseDisabled)
.then(result => { .then(result => {
expect(result).toEqual('Warehouse Four'); expect(result).toEqual('Warehouse Four');
return nightmare return nightmare

View File

@ -0,0 +1,77 @@
import selectors from '../../helpers/selectors.js';
import createNightmare from '../../helpers/helpers';
describe('create item niche path', () => {
const nightmare = createNightmare();
beforeAll(() => {
return nightmare
.waitForLogin('developer');
});
it('should access to the tickets index by clicking the tickets button', () => {
return nightmare
.click(selectors.moduleAccessView.ticketsSectionButton)
.wait(selectors.ticketsIndex.createTicketButton)
.parsedUrl()
.then(url => {
expect(url.hash).toEqual('#!/ticket/list');
});
});
it('should search for the ticket with id 1', () => {
return nightmare
.wait(selectors.ticketsIndex.searchTicketInput)
.type(selectors.ticketsIndex.searchTicketInput, '1')
.click(selectors.ticketsIndex.searchButton)
.waitForNumberOfElements(selectors.ticketsIndex.searchResult, 1)
.countSearchResults(selectors.ticketsIndex.searchResult)
.then(result => {
expect(result).toEqual(1);
});
});
it(`should click on the search result to access to the ticket notes`, () => {
return nightmare
.waitForTextInElement(selectors.ticketsIndex.searchResult, '1')
.waitToClick(selectors.ticketsIndex.searchResult)
.waitToClick(selectors.ticketNotes.notesButton)
.waitForURL('observations')
.url()
.then(url => {
expect(url).toContain('observations');
});
});
it(`should click create a new note and delete a former one`, () => {
return nightmare
.waitToClick(selectors.ticketNotes.firstNoteRemoveButton)
.waitToClick(selectors.ticketNotes.addNoteButton)
.waitToClick(selectors.ticketNotes.firstNoteSelect)
.waitForTextInElement(selectors.ticketNotes.firstNoteSelectSecondOption, 'observation two')
.waitToClick(selectors.ticketNotes.firstNoteSelectSecondOption)
.type(selectors.ticketNotes.firstDescriptionInput, 'description')
.click(selectors.ticketNotes.submitNotesButton)
.waitForSnackbar()
.then(result => {
expect(result).toContain('Data saved!');
});
});
it(`should confirm the note is the expected one`, () => {
return nightmare
.click(selectors.ticketPackages.packagesButton)
.wait(selectors.ticketPackages.firstPackageSelect)
.click(selectors.ticketNotes.notesButton)
.waitForTextInInput(selectors.ticketNotes.firstNoteDisabled, 'observation two')
.getInputValue(selectors.ticketNotes.firstNoteDisabled)
.then(result => {
expect(result).toEqual('observation two');
return nightmare
.getInputValue(selectors.ticketNotes.firstDescriptionInput);
})
.then(result => {
expect(result).toEqual('description');
});
});
});

16
package-lock.json generated
View File

@ -105,7 +105,7 @@
"angular": { "angular": {
"version": "1.6.8", "version": "1.6.8",
"resolved": "https://registry.npmjs.org/angular/-/angular-1.6.8.tgz", "resolved": "https://registry.npmjs.org/angular/-/angular-1.6.8.tgz",
"integrity": "sha1-W+N4pYvpGlSJ54tZxFGM2f0nP/s=" "integrity": "sha512-9WErZIOw1Cu1V5Yxdvxz/6YpND8ntdP71fdPpufPFJvZodZXqCjQBYrHqEoMZreO5i84O3D/Jw/vepoFt68Azw=="
}, },
"angular-cookies": { "angular-cookies": {
"version": "1.6.4", "version": "1.6.4",
@ -8375,6 +8375,16 @@
} }
} }
}, },
"gulp-env": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/gulp-env/-/gulp-env-0.4.0.tgz",
"integrity": "sha1-g3BkaUmjJJPcBtrZSgZDKW+q2+g=",
"dev": true,
"requires": {
"ini": "1.3.4",
"through2": "2.0.1"
}
},
"gulp-extend": { "gulp-extend": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/gulp-extend/-/gulp-extend-0.2.0.tgz", "resolved": "https://registry.npmjs.org/gulp-extend/-/gulp-extend-0.2.0.tgz",
@ -10985,7 +10995,7 @@
"karma-firefox-launcher": { "karma-firefox-launcher": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz", "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz",
"integrity": "sha1-LEcDBFLwRTHrfRPU/HZpYwu5Mzk=", "integrity": "sha512-LbZ5/XlIXLeQ3cqnCbYLn+rOVhuMIK9aZwlP6eOLGzWdo1UVp7t6CN3DP4SafiRLjexKwHeKHDm0c38Mtd3VxA==",
"dev": true "dev": true
}, },
"karma-jasmine": { "karma-jasmine": {
@ -11006,7 +11016,7 @@
"karma-webpack": { "karma-webpack": {
"version": "2.0.9", "version": "2.0.9",
"resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-2.0.9.tgz", "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-2.0.9.tgz",
"integrity": "sha1-YciAkffdkQY1E0wDKyZqRlr/tX8=", "integrity": "sha512-F1j3IG/XhiMzcunAXbWXH95uizjzr3WdTzmVWlta8xqxcCtAu9FByCb4sccIMxaVFAefpgnUW9KlCo0oLvIX6A==",
"dev": true, "dev": true,
"requires": { "requires": {
"async": "0.9.2", "async": "0.9.2",

View File

@ -0,0 +1,14 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
return {
where: {
creditClassification: params.creditClassification
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order
};
}
};

View File

@ -33,7 +33,7 @@
"model": "Client", "model": "Client",
"foreignKey": "client" "foreignKey": "client"
}, },
"creditInsurance": { "creditInsurances": {
"type": "hasMany", "type": "hasMany",
"model": "CreditInsurance", "model": "CreditInsurance",
"foreignKey": "creditClassification" "foreignKey": "creditClassification"

View File

@ -0,0 +1,22 @@
module.exports = function(Self) {
require('../methods/creditInsurance/filter.js')(Self);
Self.validateBinded('credit', Self.validateCredit, {
message: 'The credit must be an integer greater than or equal to zero',
allowNull: false, // FIXME: Ignored by loopback when it's false
allowBlank: false
});
Self.validateCredit = function(credit) {
return (credit >= 0 && credit % 1 == 0);
};
Self.validateBinded('grade', Self.validateGrade, {
message: 'The grade must be an integer greater than or equal to zero',
allowNull: true
});
Self.validateGrade = function(grade) {
return (typeof grade === 'undefined' || (grade >= 0 && grade % 1 == 0));
};
};

View File

@ -27,7 +27,7 @@
} }
}, },
"relations": { "relations": {
"creditClassification": { "classification": {
"type": "belongsTo", "type": "belongsTo",
"model": "CreditClassification", "model": "CreditClassification",
"foreignKey": "creditClassification" "foreignKey": "creditClassification"

View File

@ -397,6 +397,12 @@ INSERT INTO `vn`.`item`(`id`, `name`,`typeFk`,`size`,`inkFk`,`category`,`stems`,
(4, 'Mark I', 1, 60, 'AMR', 'EXT', 1, 1, 'Iron Mans first armor', 1, 05080000, 1, 2, 0, NULL, 0, 66090, 2), (4, 'Mark I', 1, 60, 'AMR', 'EXT', 1, 1, 'Iron Mans first armor', 1, 05080000, 1, 2, 0, NULL, 0, 66090, 2),
(5, 'Mjolnir', 3, 30, 'AZR', 'EXT', 1, 2, 'Thors hammer!', 2, 06021010, 1, 2, 0, NULL, 0, 67350, 2); (5, 'Mjolnir', 3, 30, 'AZR', 'EXT', 1, 2, 'Thors hammer!', 2, 06021010, 1, 2, 0, NULL, 0, 67350, 2);
INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `isBox`, `created`, `itemFk`, `counter`, `checked`, `workerFk`)
VALUES
( 1, 1, 1, 0, CURDATE(), 1, 0, 2, 1),
( 2, 1, 1, 1, CURDATE(), 2, 1, 0, 2),
( 3, 2, 2, 2, CURDATE(), 3, 2, 0, NULL);
INSERT INTO `vn`.`packaging`(`id`, `volume`, `width`, `height`, `depth`, `isPackageReturnable`, `created`, `itemFk`, `price`) INSERT INTO `vn`.`packaging`(`id`, `volume`, `width`, `height`, `depth`, `isPackageReturnable`, `created`, `itemFk`, `price`)
VALUES VALUES
(1, 0.00, 10, 10, 0, 0, CURDATE(), 1, 1.50), (1, 0.00, 10, 10, 0, 0, CURDATE(), 1, 1.50),
@ -415,6 +421,29 @@ INSERT INTO `vn`.`sale`(`id`, `itemFk`, `ticketFk`, `concept`, `quantity`, `pric
( 2, 1, 1, 'Gem of Time', 2 , 1.5, 0, 0, 0, CURDATE()), ( 2, 1, 1, 'Gem of Time', 2 , 1.5, 0, 0, 0, CURDATE()),
( 3, 2, 2, 'Mjolnir' , 10, 4 , 0, 0, 0, CURDATE()); ( 3, 2, 2, 'Mjolnir' , 10, 4 , 0, 0, 0, CURDATE());
INSERT INTO `vn`.`saleChecked`(`saleFk`, `isChecked`)
VALUES
( 1, 0),
( 2, 1);
INSERT INTO `vn`.`componentTypeRate`(`id`, `type`)
VALUES
( 1, 'agency'),
( 2, 'client'),
( 3, 'company');
INSERT INTO `vn`.`componentRate`(`id`, `name`, `componentTypeRate`, `classRate`, `tax`, `isRenewable`)
VALUES
( 1, 'Special price', 1, NULL, NULL, 1),
( 2, 'Delivery', 2, NULL, NULL, 1),
( 3, 'Mana', 3, 1, NULL, 0 );
INSERT INTO `vn`.`saleComponent`(`saleFk`, `componentFk`, `value`)
VALUES
( 1, 1, 0.5),
( 2, 2, 1.2 );
INSERT INTO `vn`.`itemBarcode`(`id`, `itemFk`, `code`) INSERT INTO `vn`.`itemBarcode`(`id`, `itemFk`, `code`)
VALUES VALUES
(1, 1 ,1 ), (1, 1 ,1 ),

View File

@ -7,3 +7,8 @@ INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `pri
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('TicketPackaging', '*', '*', 'ALLOW', 'ROLE', 'employee'); INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('TicketPackaging', '*', '*', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Packaging', '*', 'READ', 'ALLOW', 'ROLE', 'employee'); INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Packaging', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Packaging', '*', 'WRITE', 'ALLOW', 'ROLE', 'logistic'); INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Packaging', '*', 'WRITE', 'ALLOW', 'ROLE', 'logistic');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SaleChecked', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('SaleComponent', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Expedition', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Expedition', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Expedition', '*', 'WRITE', 'ALLOW', 'ROLE', 'production');

View File

@ -0,0 +1,11 @@
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `vn`.`saleChecked` AS
SELECT
`m`.`Id_Movimiento` AS `saleFk`,
`m`.`checked` AS `isChecked`
FROM
`vn2008`.`Movimientos_checked` `m`;

View File

@ -0,0 +1,30 @@
USE `vn`;
DROP procedure IF EXISTS `ticketVolume`;
DELIMITER $$
USE `vn`$$
CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`ticketVolume`(IN vTicketId INT)
BEGIN
DECLARE vWarehouseId INTEGER;
DECLARE vShippedDate DATE;
DROP TEMPORARY TABLE IF EXISTS ticketVolume;
SELECT warehouseFk, shipped INTO vWarehouseId,vShippedDate FROM vn.ticket WHERE id = vTicketId;
CREATE TEMPORARY TABLE IF NOT EXISTS ticketVolume ENGINE MEMORY
SELECT itemFk,quantity, concept, VolUd as m3_uni, volume as m3, @m3:= @m3 + ifnull(volume,0) as m3_total
FROM
(
SELECT round(r.cm3 / 1000000,3) as VolUd ,s.quantity, round(r.cm3 * s.quantity / 1000000,3) as volume,
s.itemFk, s.concept, @m3:= 0, @vol:=0, t.agencyModeFk
FROM sale s
JOIN vn.ticket t on t.id = s.ticketFk
JOIN bi.rotacion r ON r.Id_Article = s.itemFk AND r.warehouse_id = t.warehouseFk
WHERE s.ticketFk = vTicketId
) sub;
END$$
DELIMITER ;

View File

@ -0,0 +1,19 @@
USE `vn`;
CREATE
OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%`
SQL SECURITY DEFINER
VIEW `vn`.`expedition` AS
SELECT
`e`.`expeditions_id` AS `id`,
`e`.`agency_id` AS `agencyModeFk`,
`e`.`ticket_id` AS `ticketFk`,
`e`.`EsBulto` AS `isBox`,
`e`.`odbc_date` AS `created`,
`e`.`Id_Article` AS `itemFk`,
`e`.`counter` AS `counter`,
`e`.`checked` AS `checked`,
`e`.`workerFk` AS `workerFk`
FROM
`vn2008`.`expeditions` `e`

View File

@ -1,3 +1,3 @@
module.exports = function(Self) { module.exports = function(Self) {
require('../methods/item/crudItemBarcodes.js')(Self); require('../methods/item-barcode/crudItemBarcodes.js')(Self);
}; };

View File

@ -1,3 +1,3 @@
module.exports = function(Self) { module.exports = function(Self) {
require('../methods/item/getLog.js')(Self); require('../methods/item-log/getLog.js')(Self);
}; };

View File

@ -1,3 +1,3 @@
module.exports = function(Self) { module.exports = function(Self) {
require('../methods/item/crudItemNiches.js')(Self); require('../methods/item-niche/crudItemNiches.js')(Self);
}; };

View File

@ -1,3 +1,3 @@
module.exports = function(Self) { module.exports = function(Self) {
require('../methods/item/crudItemTags.js')(Self); require('../methods/item-tag/crudItemTags.js')(Self);
}; };

View File

@ -1,22 +1,4 @@
{ {
"Item": {
"dataSource": "vn"
},
"ItemType": {
"dataSource": "vn"
},
"Ink": {
"dataSource": "vn"
},
"Origin": {
"dataSource": "vn"
},
"Producer": {
"dataSource": "vn"
},
"Intrastat": {
"dataSource": "vn"
},
"TaxClass": { "TaxClass": {
"dataSource": "vn" "dataSource": "vn"
}, },
@ -26,9 +8,6 @@
"TaxType": { "TaxType": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Expence": {
"dataSource": "vn"
},
"ItemTag": { "ItemTag": {
"dataSource": "vn" "dataSource": "vn"
}, },

View File

@ -5,5 +5,10 @@
"The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado", "The default consignee can not be unchecked": "No se puede desmarcar el consignatario predeterminado",
"Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado", "Unable to default a disabled consignee": "No se puede poner predeterminado un consignatario desactivado",
"El método de pago seleccionado requiere que se especifique el IBAN": "El método de pago seleccionado requiere que se especifique el IBAN", "El método de pago seleccionado requiere que se especifique el IBAN": "El método de pago seleccionado requiere que se especifique el IBAN",
"can't be blank": "can't be blank" "can't be blank": "can't be blank",
"DNI Incorrecto": "DNI Incorrecto",
"Ya existe un usuario con ese nombre": "Ya existe un usuario con ese nombre",
"ValidationError: The `Item` instance is not valid. Details: `originFk` Cannot be blank (value: undefined).": "ValidationError: The `Item` instance is not valid. Details: `originFk` Cannot be blank (value: undefined).",
"Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `Articles_ibfk_5` FOREIGN KEY (`tipo_id`) REFERENCES `Tipos` (`tipo_id`) ON UPDATE CASCADE)": "Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `Articles_ibfk_5` FOREIGN KEY (`tipo_id`) REFERENCES `Tipos` (`tipo_id`) ON UPDATE CASCADE)",
"Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `expenceFk` FOREIGN KEY (`expenceFk`) REFERENCES `Gastos` (`Id_Gasto`) ON UPDATE CASCADE)": "Error: ER_NO_REFERENCED_ROW_2: Cannot add or update a child row: a foreign key constraint fails (`vn2008`.`Articles`, CONSTRAINT `expenceFk` FOREIGN KEY (`expenceFk`) REFERENCES `Gastos` (`Id_Gasto`) ON UPDATE CASCADE)"
} }

View File

@ -1,4 +1,4 @@
var UserError = require('../../../../loopback/common/helpers').UserError; var UserError = require('../../helpers').UserError;
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('clone', { Self.remoteMethod('clone', {
@ -27,7 +27,7 @@ module.exports = Self => {
id: itemId id: itemId
}, },
include: [ include: [
{relation: "itemTag", scope: {order: "priority ASC", include: {relation: "tag"}}} {relation: 'itemTag', scope: {order: 'priority ASC', include: {relation: 'tag'}}}
] ]
}; };

View File

@ -0,0 +1,42 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
let filters = {
where: {},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'created DESC'
};
delete params.page;
delete params.size;
delete params.order;
if (params.search) {
filters.where.and = [
{
or: [
{id: params.search},
{name: {regexp: params.search}}
]
}
];
delete params.search;
}
Object.keys(params).forEach(
key => {
if (filters.where.and) {
let filter = {};
filter[key] = (key === 'nickname') ? {regexp: params[key]} : params[key];
filters.where.and.push(filter);
} else {
filters.where[key] = (key === 'nickname') ? {regexp: params[key]} : params[key];
}
}
);
return filters;
}
};

View File

@ -41,5 +41,13 @@
"model": "AgencyType", "model": "AgencyType",
"foreignKey": "agencyTypeFk" "foreignKey": "agencyTypeFk"
} }
} },
"acls": [
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
} }

View File

@ -156,6 +156,11 @@
"type": "hasMany", "type": "hasMany",
"model": "Greuge", "model": "Greuge",
"foreignKey": "clientFk" "foreignKey": "clientFk"
},
"creditClassifications": {
"type": "hasMany",
"model": "CreditClassification",
"foreignKey": "client"
} }
} }
} }

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