Added new section client contacts #365 - CR: Javi

This commit is contained in:
Joan Sanchez 2018-06-26 09:41:27 +02:00
parent 38914a060f
commit dfa1d1edb2
15 changed files with 493 additions and 4 deletions

View File

@ -280,6 +280,24 @@
"params": {
"client": "$ctrl.client"
}
},
{
"url": "/contact",
"abstract": true,
"state": "client.card.contact",
"component": "ui-view"
},
{
"url": "/index",
"state": "client.card.contact.index",
"component": "vn-client-contact-index",
"params": {
"client": "$ctrl.client"
},
"menu": {
"description": "Contacts",
"icon": "contact_phone"
}
}
]
}

View File

@ -27,3 +27,4 @@ import './credit-insurance/index';
import './credit-insurance/create';
import './credit-insurance/insurance/index';
import './credit-insurance/insurance/create';
import './contact';

View File

@ -0,0 +1,158 @@
import './index.js';
describe('Client', () => {
describe('Component vnClientContactIndex', () => {
let $componentController;
let $scope;
let $state;
let controller;
let $httpBackend;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, $rootScope, _$state_, _$httpBackend_) => {
$componentController = _$componentController_;
$state = _$state_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
$scope = $rootScope.$new();
$scope.form = {$invalid: false};
$scope.index = {accept: () => {}};
controller = $componentController('vnClientContactIndex', {$scope: $scope}, {$state: $state});
controller.client = {
id: 101
};
}));
describe('add / remove tags', () => {
it('should add one empty contact into controller contacts collection', () => {
controller.contacts = [];
controller.add();
expect(controller.contacts.length).toEqual(1);
expect(controller.contacts[0].id).toBe(undefined);
});
it('should remove a contact that occupies the position in the index', () => {
let index = 2;
controller.contacts = [
{id: 1, name: 'My contact 1', phone: '123456789'},
{id: 2, name: 'My contact 2', phone: '123456789'},
{id: 3, name: 'My contact 3', phone: '123456789'}
];
controller.remove(index);
expect(controller.contacts.length).toEqual(2);
expect(controller.contacts[index]).toBe(undefined);
});
});
describe('isEqual()', () => {
it('should return true if two contacts are equals', () => {
let contact1 = {id: 1, name: 'My contact 1', phone: '123456789'};
let contact2 = {id: 1, name: 'My contact 1', phone: '123456789'};
let equals = controller.isEqual(contact1, contact2);
expect(equals).toBeTruthy();
});
it('should return false if two contacts aint equal', () => {
let contact1 = {id: 1, name: 'My contact 1', phone: '123456789'};
let contact2 = {id: 2, name: 'My contact 2', phone: '123456789'};
let equals = controller.isEqual(contact1, contact2);
expect(equals).toBeFalsy();
});
});
describe('submit()', () => {
it("should perfom a query to delete contacts", () => {
controller._oldContacts = [];
controller._oldContacts[1] = {id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'};
controller._oldContacts[2] = {id: 2, clientFk: 101, name: 'My contact 2', phone: '123456789'};
controller._contacts = [
{id: 2, name: 'My contact 2', phone: '123456789'}
];
controller.removedContacts = [1];
let newData = {
clientFk: 101,
delete: [1],
create: [],
update: []
};
$httpBackend.whenPOST(`/client/api/ClientContacts/crud`, newData).respond(200, true);
$httpBackend.expectPOST(`/client/api/ClientContacts/crud`, newData);
controller.submit();
$httpBackend.flush();
});
it("should perfom a query to update contacts", () => {
controller._oldContacts = [];
controller._oldContacts[1] = {id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'};
controller._oldContacts[2] = {id: 2, clientFk: 101, name: 'My contact 2', phone: '123456789'};
controller._contacts = [
{id: 1, clientFk: 101, name: 'My contact 1', phone: '123456789'},
{id: 2, clientFk: 101, name: 'My contact 2', phone: '111111111'}
];
controller.removedContacts = [];
let newData = {
clientFk: 101,
delete: [],
create: [],
update: [
{id: 2, clientFk: 101, name: 'My contact 2', phone: '111111111'}
]
};
$httpBackend.whenPOST(`/client/api/ClientContacts/crud`, newData).respond(200, true);
$httpBackend.expectPOST(`/client/api/ClientContacts/crud`, newData);
controller.submit();
$httpBackend.flush();
});
it("should perfom a query to create new contact", () => {
controller._oldContacts = [];
controller._oldContacts[1] = {id: 1, name: 'My contact 1', phone: '123456789'};
controller._oldContacts[2] = {id: 2, name: 'My contact 2', phone: '123456789'};
controller._contacts = [
{id: 1, name: 'My contact 1', phone: '123456789'},
{id: 2, name: 'My contact 2', phone: '123456789'},
{name: 'My contact 3', phone: '123456789'}
];
controller.removedContacts = [];
let newData = {
clientFk: 101,
delete: [],
create: [{name: 'My contact 3', phone: '123456789'}],
update: []
};
$httpBackend.whenPOST(`/client/api/ClientContacts/crud`, newData).respond(200, true);
$httpBackend.expectPOST(`/client/api/ClientContacts/crud`, newData);
controller.submit();
$httpBackend.flush();
});
it("should throw 'No changes to save' error when there are no changes to apply", () => {
let newData = {
delete: [],
create: [],
update: []
};
let hasChanges = controller.hasChanges(newData);
expect(hasChanges).toBeFalsy();
});
});
});
});

View File

@ -0,0 +1,44 @@
<mg-ajax
path="/client/api/Clients/{{index.params.id}}/contacts"
options="mgIndex"
actions="$ctrl.contacts = index.model">
</mg-ajax>
<form name="form" ng-submit="$ctrl.submit()">
<vn-card pad-large>
<vn-title>Contacts</vn-title>
<vn-horizontal ng-repeat="contact in index.model track by $index">
<vn-textfield
vn-one
label="Name"
model="contact.name"
rule="clientContact.name">
</vn-textfield>
<vn-textfield
vn-one
label="Phone"
model="contact.phone"
rule="clientContact.phone" vn-focus>
</vn-textfield>
<vn-auto pad-medium-top>
<vn-icon
pointer
medium-grey
vn-tooltip="Remove contact"
icon="remove_circle_outline"
ng-click="$ctrl.remove($index)">
</vn-icon>
</vn-one>
</vn-horizontal>
<vn-one>
<vn-icon-button
vn-bind="+"
vn-tooltip="Add contact"
icon="add_circle"
ng-click="$ctrl.add()">
</vn-icon-button>
</vn-one>
</vn-card>
<vn-button-bar>
<vn-submit label="Save"></vn-submit>
</vn-button-bar>
</form>

View File

@ -0,0 +1,126 @@
import ngModule from '../module';
class Controller {
constructor($http, $scope, $translate, vnApp) {
this.$http = $http;
this.$scope = $scope;
this.$translate = $translate;
this.vnApp = vnApp;
this.removedContacts = [];
}
/**
* Setter for contacts and original contacts
* @param {Object} value - Contacts object
*/
set contacts(value) {
this._contacts = value;
this.oldContacts = value;
}
get contacts() {
return this._contacts;
}
/**
* Saves the original contacts
* @param {Object} value - Default values
*/
set oldContacts(value) {
this._oldContacts = [];
this.removedContacts = [];
value.forEach(item => {
this._oldContacts[item.id] = Object.assign({}, item);
});
}
get oldContacts() {
return this._oldContacts;
}
submit() {
let query = `/client/api/ClientContacts/crud`;
let data = {
clientFk: this.client.id,
delete: this.removedContacts,
create: [],
update: []
};
this.contacts.forEach(item => {
if (typeof item.id === 'undefined')
data.create.push(item);
if (typeof item.id !== 'undefined' && !this.isEqual(item, this.oldContacts[item.id]))
data.update.push(item);
});
if (!this.hasChanges(data))
return this.vnApp.showError(this.$translate.instant('No changes to save'));
if (this.$scope.form.$invalid)
return this.vnApp.showError(this.$translate.instant('Some fields are invalid'));
this.$http.post(query, data).then(() => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
this.$scope.index.accept();
});
}
/**
* Remove contact from list
* @param {Int} index - Contact array position
*/
remove(index) {
if (this.contacts[index] && this.contacts[index].id)
this.removedContacts.push(this.contacts[index].id);
this.contacts.splice(index, 1);
}
/**
* Add contact to list
*/
add() {
let data = {
clientFk: this.client.id,
name: 'Teléfono',
phone: null
};
this.contacts.push(data);
}
/**
* Returns true/false if the new
* contact equals the old one
* @param {Object} newContact - New contact
* @param {Object} oldContact - Old contact
* @return {Boolean} - True if are equals
*/
isEqual(newContact, oldContact) {
return newContact.name === oldContact.name && newContact.phone == oldContact.phone;
}
/**
* Checks if there's any changes to do
* @param {Object} data - Crud data
* @return {Boolean} - Returns true if there's any changes to do
*/
hasChanges(data) {
if (data.create.length || data.update.length || data.delete.length)
return true;
return false;
}
}
Controller.$inject = ['$http', '$scope', '$translate', 'vnApp'];
ngModule.component('vnClientContactIndex', {
template: require('./index.html'),
controller: Controller,
bindings: {
client: '<'
}
});

View File

@ -22,4 +22,5 @@ Credit contracts: Contratos de crédito
Verified data: Datos comprobados
Mandate: Mandato
Amount: Importe
Back: Volver
Back: Volver
Contacts: Contactos

View File

@ -0,0 +1,32 @@
module.exports = Self => {
Self.remoteMethod('crud', {
description: 'Client contact crud',
accepts: [{
arg: 'data',
type: 'object',
http: {source: 'body'}
}],
returns: {
root: true,
type: 'boolean'
},
http: {
verb: 'post',
path: '/crud'
}
});
Self.crud = async data => {
let models = Self.app.models;
await Promise.all(data.delete.map(contactId => {
return models.ClientContact.destroyById(contactId);
}));
let upsert = data.update.concat(data.create);
await Promise.all(upsert.map(contact => {
return models.ClientContact.upsert(contact);
}));
};
};

View File

@ -0,0 +1,52 @@
const app = require(`${servicesDir}/client/server/server`);
describe('Client crud', () => {
afterAll(async() => {
await app.models.ClientContact.destroyById(4113);
});
it('should perfom a query to create new contacts', async() => {
let data = {
delete: [],
create: [
{id: 4113, clientFk: 101, name: 'My contact', phone: '111111111'}
],
update: []
};
await app.models.ClientContact.crud(data);
let contacts = await app.models.ClientContact.find();
expect(contacts.length).toEqual(25);
});
it('should perfom a query to update contacts', async() => {
let data = {
delete: [],
create: [],
update: [
{id: 4113, name: 'My contact 2 updated', phone: '222222222'}
]
};
await app.models.ClientContact.crud(data);
let contacts = await app.models.ClientContact.findById(4113);
expect(contacts.name).toEqual('My contact 2 updated');
expect(contacts.phone).toEqual('222222222');
});
it('should perfom a query to delete contacts', async() => {
let data = {
delete: [4113],
create: [],
update: []
};
await app.models.ClientContact.crud(data);
let contacts = await app.models.ClientContact.find();
expect(contacts.length).toEqual(24);
});
});

View File

@ -0,0 +1,11 @@
module.exports = Self => {
require('../methods/client-contact/crud')(Self);
Self.validatesPresenceOf('name', {
message: 'Name cannot be blank'
});
Self.validatesPresenceOf('phone', {
message: 'Phone cannot be blank'
});
};

View File

@ -0,0 +1,32 @@
{
"name": "ClientContact",
"description": "Client phone contacts",
"base": "VnModel",
"options": {
"mysql": {
"table": "clientContact",
"database": "vn"
}
},
"validateUpsert": true,
"properties": {
"id": {
"type": "Number",
"id": true,
"description": "Identifier"
},
"name": {
"type": "string"
},
"phone": {
"type": "string"
}
},
"relations": {
"client": {
"type": "belongsTo",
"model": "Client",
"foreignKey": "clientFk"
}
}
}

View File

@ -8,6 +8,9 @@
"ClientObservation": {
"dataSource": "vn"
},
"ClientContact": {
"dataSource": "vn"
},
"Greuge": {
"dataSource": "vn"
},

View File

@ -18,5 +18,9 @@
"That payment method requires an IBAN": "That payment method requires an IBAN",
"State cannot be blank": "State cannot be blank",
"Cannot change the payment method if no salesperson": "Cannot change the payment method if no salesperson",
"Only manager can change the credit": "Only manager can change the credit"
"Only manager can change the credit": "Only manager can change the credit",
"Name cannot be blank": "Name cannot be blank",
"Phone cannot be blank": "Phone cannot be blank",
"ValidationError: La instancia `ClientContact` no es válida. Detalles: `name` Name cannot be blank (value: null).": "ValidationError: La instancia `ClientContact` no es válida. Detalles: `name` Name cannot be blank (value: null).",
"La instancia `ClientContact` no es válida. Detalles: `name` Name cannot be blank (value: null).": "La instancia `ClientContact` no es válida. Detalles: `name` Name cannot be blank (value: null)."
}

View File

@ -24,5 +24,7 @@
"The credit must be an integer greater than or equal to zero": "The credit must be an integer greater than or equal to zero",
"The grade must be similar to the last one": "El grade debe ser similar al último",
"NO_AGENCY_AVAILABLE": "NO_AGENCY_AVAILABLE",
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente"
"Only manager can change the credit": "Solo el gerente puede cambiar el credito de este cliente",
"Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco"
}

View File

@ -4,7 +4,7 @@ describe('sale priceDifference()', () => {
it('should call the priceDifference method and return a NO_AGENCY_AVAILABLE signal error', done => {
let data = {
landed: Date.now(),
addressFk: 121,
addressFk: 121,
agencyModeFk: 1,
warehouseFk: 1
};

View File

@ -161,6 +161,11 @@
"type": "hasMany",
"model": "CreditClassification",
"foreignKey": "client"
},
"contacts": {
"type": "hasMany",
"model": "ClientContact",
"foreignKey": "clientFk"
}
}
}