Added sample create #440

This commit is contained in:
Joan Sanchez 2018-07-31 11:08:22 +02:00
parent 81c6546e96
commit 4103f7fcf6
22 changed files with 473 additions and 39 deletions

View File

@ -296,14 +296,8 @@
}, },
{ {
"url": "/contact", "url": "/contact",
"abstract": true,
"state": "client.card.contact", "state": "client.card.contact",
"component": "ui-view" "component": "vn-client-contact",
},
{
"url": "/index",
"state": "client.card.contact.index",
"component": "vn-client-contact-index",
"description": "Contacts", "description": "Contacts",
"params": { "params": {
"client": "$ctrl.client" "client": "$ctrl.client"
@ -311,6 +305,33 @@
"menu": { "menu": {
"icon": "contact_phone" "icon": "contact_phone"
} }
},
{
"url": "/sample",
"abstract": true,
"state": "client.card.sample",
"component": "ui-view"
},
{
"url": "/index",
"state": "client.card.sample.index",
"component": "vn-client-sample-index",
"description": "Samples",
"params": {
"client": "$ctrl.client"
},
"menu": {
"icon": "mail"
}
},
{
"url": "/create",
"state": "client.card.sample.create",
"component": "vn-client-sample-create",
"description": "Send sample",
"params": {
"client": "$ctrl.client"
}
} }
] ]
} }

View File

@ -9,7 +9,10 @@
<vn-card pad-large> <vn-card pad-large>
<vn-title>Basic data</vn-title> <vn-title>Basic data</vn-title>
<vn-horizontal> <vn-horizontal>
<vn-textfield vn-one label="Comercial Name" field="$ctrl.client.name" vn-focus></vn-textfield> <vn-textfield vn-one
label="Comercial Name"
field="$ctrl.client.name" vn-focus>
</vn-textfield>
</vn-horizontal> </vn-horizontal>
<vn-horizontal> <vn-horizontal>
<vn-textfield vn-one label="Contact" field="$ctrl.client.contact"></vn-textfield> <vn-textfield vn-one label="Contact" field="$ctrl.client.contact"></vn-textfield>

View File

@ -29,3 +29,5 @@ import './credit-insurance/create';
import './credit-insurance/insurance/index'; import './credit-insurance/insurance/index';
import './credit-insurance/insurance/create'; import './credit-insurance/insurance/create';
import './contact'; import './contact';
import './sample/index';
import './sample/create';

View File

@ -1,7 +1,7 @@
import './index.js'; import './index.js';
describe('Client', () => { describe('Client', () => {
describe('Component vnClientContactIndex', () => { describe('Component vnClientContact', () => {
let $componentController; let $componentController;
let $scope; let $scope;
let $state; let $state;
@ -20,7 +20,7 @@ describe('Client', () => {
$scope = $rootScope.$new(); $scope = $rootScope.$new();
$scope.form = {$invalid: false}; $scope.form = {$invalid: false};
$scope.model = {refresh: () => {}}; $scope.model = {refresh: () => {}};
controller = $componentController('vnClientContactIndex', {$scope: $scope}, {$state: $state}); controller = $componentController('vnClientContact', {$scope: $scope}, {$state: $state});
controller.client = { controller.client = {
id: 101 id: 101
}; };

View File

@ -110,7 +110,7 @@ class Controller {
Controller.$inject = ['$http', '$scope', '$stateParams', '$translate', 'vnApp']; Controller.$inject = ['$http', '$scope', '$stateParams', '$translate', 'vnApp'];
ngModule.component('vnClientContactIndex', { ngModule.component('vnClientContact', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
bindings: { bindings: {

View File

@ -16,7 +16,7 @@
<vn-card pad-large> <vn-card pad-large>
<vn-horizontal> <vn-horizontal>
<vn-title vn-two>Greuge</vn-title> <vn-title vn-two>Greuge</vn-title>
<div class="totalBox"> <div class="totalBox" ng-if="model.data.length > 0">
<vn-label-value label="Total" <vn-label-value label="Total"
value="{{edit.model.sumAmount | currency: ' €': 2}}"> value="{{edit.model.sumAmount | currency: ' €': 2}}">
</vn-label-value> </vn-label-value>
@ -43,16 +43,6 @@
<vn-empty-rows ng-if="model.data.length === 0" translate> <vn-empty-rows ng-if="model.data.length === 0" translate>
No results No results
</vn-empty-rows> </vn-empty-rows>
<vn-tfoot ng-if="model.data.length > 0">
<vn-tr>
<vn-td></vn-td>
<vn-td></vn-td>
<vn-td>
</vn-td>
<vn-td></vn-td>
</vn-tr>
</vn-tfoot>
</vn-table> </vn-table>
</vn-vertical> </vn-vertical>
<vn-pagination <vn-pagination

View File

@ -17,6 +17,10 @@ Remove contact: Quitar contacto
Client ticket list: Listado de tickets del cliente Client ticket list: Listado de tickets del cliente
Add contact: Añadir contacto Add contact: Añadir contacto
Create: Crear Create: Crear
Sent: Enviado
Worker: Trabajador
Send: Enviar
Sample: Plantilla
# Sections # Sections
Clients: Clientes Clients: Clientes
@ -43,4 +47,6 @@ Credit contracts: Contratos de crédito
New contract: Nuevo contrato New contract: Nuevo contrato
Edit contract: Editar contrato Edit contract: Editar contrato
Requested credits: Créditos solicitados Requested credits: Créditos solicitados
Contacts: Contactos Contacts: Contactos
Samples: Plantillas
Send sample: Enviar plantilla

View File

@ -0,0 +1,46 @@
<mg-ajax path="/client/api/ClientSamples" options="vnPost"></mg-ajax>
<vn-watcher
vn-id="watcher"
data="$ctrl.clientSample"
form="form"
save="post">
</vn-watcher>
<form name="form" ng-submit="$ctrl.onSubmit()">
<vn-card pad-large>
<vn-title>Send sample</vn-title>
<vn-horizontal>
<vn-autocomplete
vn-one
vn-id="sampleType"
field="$ctrl.clientSample.typeFk"
model="ClientSample.typeFk"
select-fields=["id","description","code","hasCompany"]
url="/client/api/Samples"
show-field="description"
value-field="id"
label="Sample">
</vn-autocomplete>
<vn-autocomplete
vn-one
field="$ctrl.clientSample.companyFk"
model="ClientSample.companyFk"
url="/client/api/Companies"
show-field="code"
value-field="id"
label="Company"
ng-if="sampleType.selection.hasCompany">
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Send"></vn-submit>
<vn-button label="Preview" ng-click="$ctrl.showPreview($event)"></vn-button>
<vn-button ui-sref="client.card.sample.index" label="Cancel"></vn-button>
</vn-button-bar>
</form>
<vn-dialog
vn-id="show-preview"
on-open="$ctrl.onPreviewOpen()">
<tpl-body></tpl-body>
</vn-dialog>

View File

@ -0,0 +1,77 @@
import ngModule from '../../module';
import './style.scss';
class Controller {
constructor($scope, $state, $http, vnApp, $translate) {
this.$scope = $scope;
this.$state = $state;
this.$stateParams = $state.params;
this.$http = $http;
this.vnApp = vnApp;
this.$translate = $translate;
this.clientSample = {
clientFk: this.$stateParams.id
};
}
showPreview(event) {
event.preventDefault();
let sampleType = this.$scope.sampleType.selection;
let queryParams;
if (!sampleType)
return this.vnApp.showError(this.$translate.instant('Choose a sample'));
if (sampleType.hasCompany && !this.clientSample.companyFk)
return this.vnApp.showError(this.$translate.instant('Choose a company'));
if (sampleType.hasCompany) {
queryParams = `${sampleType.code}/${this.clientSample.companyFk}/${this.$stateParams.id}/preview`;
} else {
queryParams = `${sampleType.code}/${this.$stateParams.id}/preview`;
}
let query = `/mailer/notification/${queryParams}`;
this.$http.get(query).then(res => {
if (res.data) {
let dialog = this.$scope.showPreview.element;
let body = dialog.querySelector('tpl-body');
body.innerHTML = res.data;
this.$scope.showPreview.show();
}
});
}
onSubmit() {
this.$scope.watcher.check();
this.$scope.watcher.realSubmit().then(() =>
this.sendSample()
);
}
sendSample() {
let sampleType = this.$scope.sampleType.selection;
let queryParams;
if (sampleType.hasCompany) {
queryParams = `${sampleType.code}/${this.clientSample.companyFk}/${this.$stateParams.id}`;
} else {
queryParams = `${sampleType.code}/${this.$stateParams.id}`;
}
let query = `/mailer/notification/${queryParams}`;
this.$http.get(query).then(res => {
if (res) {
this.vnApp.showSuccess(this.$translate.instant('Notification sent!'));
this.$state.go('client.card.sample.index');
}
});
}
}
Controller.$inject = ['$scope', '$state', '$http', 'vnApp', '$translate'];
ngModule.component('vnClientSampleCreate', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,2 @@
Choose a sample: Selecciona una plantilla
Choose a company: Selecciona una empresa

View File

@ -0,0 +1,149 @@
import './index';
fdescribe('Client', () => {
describe('Component vnClientSampleCreate', () => {
let $componentController;
let $scope;
let $httpBackend;
let $state;
let controller;
beforeEach(() => {
angular.mock.module('client');
});
beforeEach(angular.mock.inject((_$componentController_, _$httpBackend_, $rootScope, _$state_) => {
$componentController = _$componentController_;
$scope = $rootScope.$new();
$scope.sampleType = {};
$scope.watcher = {
check: () => {},
realSubmit: () => {
return {
then: callback => {
callback();
}
};
}
};
$scope.showPreview = {
element: {
querySelector: () => {
return {
innerHTML: () => {}
};
}
},
show: () => {}
};
$state = _$state_;
$state.params.id = 101;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', /\/locale\/\w+\/[a-z]{2}\.json/).respond({});
controller = $componentController('vnClientSampleCreate', {$scope: $scope}, {$state: $state});
}));
describe('showPreview()', () => {
it(`should perform a query (GET) and open a sample preview`, () => {
spyOn(controller.$scope.showPreview, 'show');
controller.$scope.sampleType.selection = {
hasCompany: false,
code: 'MyReport'
};
controller.clientSample = {
clientFk: 101,
typeFK: 1
};
let event = {preventDefault: () => {}};
$httpBackend.whenGET(`/mailer/notification/MyReport/101/preview`).respond(true);
$httpBackend.expectGET(`/mailer/notification/MyReport/101/preview`);
controller.showPreview(event);
$httpBackend.flush();
expect(controller.$scope.showPreview.show).toHaveBeenCalledWith();
});
it(`should perform a query (GET) with companyFk param and open a sample preview`, () => {
spyOn(controller.$scope.showPreview, 'show');
controller.$scope.sampleType.selection = {
hasCompany: true,
code: 'MyReport'
};
controller.clientSample = {
clientFk: 101,
companyFk: 442,
typeFK: 1
};
let event = {preventDefault: () => {}};
$httpBackend.whenGET(`/mailer/notification/MyReport/442/101/preview`).respond(true);
$httpBackend.expectGET(`/mailer/notification/MyReport/442/101/preview`);
controller.showPreview(event);
$httpBackend.flush();
expect(controller.$scope.showPreview.show).toHaveBeenCalledWith();
});
});
describe('onSubmit()', () => {
it(`should call sendSample() method`, () => {
spyOn(controller, 'sendSample');
controller.onSubmit();
expect(controller.sendSample).toHaveBeenCalledWith();
});
});
describe('sendSample()', () => {
it(`should perform a query (GET) and call go() method`, () => {
spyOn(controller.$state, 'go');
controller.$scope.sampleType.selection = {
hasCompany: false,
code: 'MyReport'
};
controller.clientSample = {
clientFk: 101,
typeFK: 1
};
$httpBackend.whenGET(`/mailer/notification/MyReport/101`).respond(true);
$httpBackend.expectGET(`/mailer/notification/MyReport/101`);
controller.sendSample();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('client.card.sample.index');
});
it(`should perform a query (GET) with companyFk param and call go() method`, () => {
spyOn(controller.$state, 'go');
controller.$scope.sampleType.selection = {
hasCompany: true,
code: 'MyReport'
};
controller.clientSample = {
clientFk: 101,
companyFk: 442,
typeFK: 1
};
$httpBackend.whenGET(`/mailer/notification/MyReport/442/101`).respond(true);
$httpBackend.expectGET(`/mailer/notification/MyReport/442/101`);
controller.sendSample();
$httpBackend.flush();
expect(controller.$state.go).toHaveBeenCalledWith('client.card.sample.index');
});
});
});
});

View File

@ -0,0 +1,12 @@
vn-client-sample-create {
vn-dialog {
& > div {
padding: 0 !important
}
tpl-body {
min-width: 800px;
max-height: 700px;
}
}
}

View File

@ -0,0 +1,48 @@
<vn-crud-model
vn-id="model"
url="/client/api/ClientSamples"
filter="::$ctrl.filter"
link="{clientFk: $ctrl.$stateParams.id}"
limit="20"
data="samples">
</vn-crud-model>
<vn-vertical>
<vn-card pad-large>
<vn-horizontal>
<vn-title vn-two>Samples</vn-title>
</vn-horizontal>
<vn-vertical>
<vn-table model="model">
<vn-thead>
<vn-tr>
<vn-th field="created" default-order="DESC">Sent</vn-th>
<vn-th>Description</vn-th>
<vn-th field="workerFk">Worker</vn-th>
<vn-th field="companyFk">Company</vn-th>
</vn-tr>
</vn-thead>
<vn-tbody>
<vn-tr ng-repeat="sample in samples">
<vn-td>{{::sample.created | date:'dd/MM/yyyy HH:mm' }}</vn-td>
<vn-td>{{::sample.type.description}}</vn-td>
<vn-td>{{::sample.worker.firstName}} {{::sample.worker.name}}</vn-td>
<vn-td>{{::sample.company.code}}</vn-td>
</vn-tr>
</vn-tbody>
<vn-empty-rows ng-if="model.data.length === 0" translate>
No results
</vn-empty-rows>
</vn-table>
</vn-vertical>
<vn-pagination
model="model"
scroll-selector="ui-view">
</vn-pagination>
</vn-card>
</vn-vertical>
<a ui-sref="client.card.sample.create" vn-tooltip="Send sample"
vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>

View File

@ -0,0 +1,36 @@
import ngModule from '../../module';
class Controller {
constructor($stateParams) {
this.$stateParams = $stateParams;
this.filter = {
include: [
{
relation: "type",
scope: {
fields: ["code", "description"]
}
},
{
relation: "worker",
scope: {
fields: ["firstName", "name"]
}
},
{
relation: "company",
scope: {
fields: ["code"]
}
}
]
};
}
}
Controller.$inject = ['$stateParams'];
ngModule.component('vnClientSampleIndex', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,20 @@
module.exports = Self => {
Self.validatesPresenceOf('typeFk', {
message: 'Sample type cannot be blank'
});
Self.observe('before save', async function(ctx) {
let models = Self.app.models;
let data = ctx.instance;
let sample = await models.Sample.findById(data.typeFk);
if (sample.hasCompany && !data.companyFk)
throw new Error('Choose a company');
let filter = {where: {userFk: ctx.options.accessToken.userId}};
let worker = await Self.app.models.Worker.findOne(filter);
data.workerFk = worker.id;
});
};

View File

@ -20,8 +20,14 @@
}, },
"isVisible": { "isVisible": {
"type": "Number" "type": "Number"
},
"hasCompany": {
"type": "Number"
} }
}, },
"scope": {
"where": {"isVisible": 1}
},
"acls": [ "acls": [
{ {
"accessType": "READ", "accessType": "READ",

View File

@ -1,4 +1,9 @@
USE `vn`; USE `vn`;
ALTER TABLE `vn2008`.`escritos`
ADD COLUMN `hasCompany` VARCHAR(45) NOT NULL DEFAULT 0 AFTER `visible`;
CREATE CREATE
OR REPLACE ALGORITHM = UNDEFINED OR REPLACE ALGORITHM = UNDEFINED
DEFINER = `root`@`%` DEFINER = `root`@`%`
@ -8,7 +13,9 @@ VIEW `sample` AS
`e`.`id` AS `id`, `e`.`id` AS `id`,
`e`.`abrev` AS `code`, `e`.`abrev` AS `code`,
`e`.`descripcion` AS `description`, `e`.`descripcion` AS `description`,
`e`.`visible` AS `isVisible` `e`.`visible` AS `isVisible`,
`e`.`hasCompany` AS `hasCompany`
FROM FROM
`vn2008`.`escritos` `e`; `vn2008`.`escritos` `e`;
DROP VIEW `vn`.`clientNotificationType`;

View File

@ -15,3 +15,4 @@ VIEW `clientSample` AS
FROM FROM
`vn2008`.`escritos_det` `e`; `vn2008`.`escritos_det` `e`;
DROP VIEW `vn`.`clientNotification`;

View File

@ -22,5 +22,6 @@
"Name cannot be blank": "Name cannot be blank", "Name cannot be blank": "Name cannot be blank",
"Phone cannot be blank": "Phone cannot be blank", "Phone cannot be blank": "Phone cannot be blank",
"Observation type cannot be blank": "Observation type cannot be blank", "Observation type cannot be blank": "Observation type cannot be blank",
"NO_AGENCY_AVAILABLE": "NO_AGENCY_AVAILABLE" "NO_AGENCY_AVAILABLE": "NO_AGENCY_AVAILABLE",
"can't be blank": "can't be blank"
} }

View File

@ -27,5 +27,6 @@
"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", "Name cannot be blank": "El nombre no puede estar en blanco",
"Phone cannot be blank": "El teléfono no puede estar en blanco", "Phone cannot be blank": "El teléfono no puede estar en blanco",
"Period cannot be blank": "El periodo no puede estar en blanco" "Period cannot be blank": "El periodo no puede estar en blanco",
"Choose a company": "Selecciona una empresa"
} }

View File

@ -3,7 +3,7 @@ var database = require(path.join(__dirname, '../../database.js'));
var format = require(path.join(__dirname, '../../util/format.js')); var format = require(path.join(__dirname, '../../util/format.js'));
module.exports = class LetterDebtor { module.exports = class LetterDebtor {
getData(params, cb) { async getData(params, cb) {
let query = `SELECT let query = `SELECT
c.id clientId, c.id clientId,
m.code mandateCode, m.code mandateCode,
@ -29,13 +29,18 @@ module.exports = class LetterDebtor {
LEFT JOIN country sc ON sc.id = s.countryFk LEFT JOIN country sc ON sc.id = s.countryFk
LEFT JOIN province sp ON sp.id = s.provinceFk LEFT JOIN province sp ON sp.id = s.provinceFk
WHERE c.id = ?`; WHERE c.id = ?`;
database.pool.query(query, [params.clientId], (error, result) => { try {
if (error || result.length == 0) let [result] = await database.pool.query(query, [params.clientId]);
return cb(new Error('No template data found'));
if (!result)
throw new Error('No body data found');
Object.assign(this, result);
Object.assign(this, result[0]);
cb(); cb();
}); } catch (e) {
cb(e);
}
} }
// Swift BIC fields // Swift BIC fields

View File

@ -3,7 +3,7 @@ var database = require(path.join(__dirname, '../../database.js'));
var format = require(path.join(__dirname, '../../util/format.js')); var format = require(path.join(__dirname, '../../util/format.js'));
module.exports = class SepaCore { module.exports = class SepaCore {
getData(params, cb) { async getData(params, cb) {
let query = `SELECT let query = `SELECT
c.id clientId, c.id clientId,
m.code mandateCode, m.code mandateCode,
@ -29,13 +29,14 @@ module.exports = class SepaCore {
LEFT JOIN country sc ON sc.id = s.countryFk LEFT JOIN country sc ON sc.id = s.countryFk
LEFT JOIN province sp ON sp.id = s.provinceFk LEFT JOIN province sp ON sp.id = s.provinceFk
WHERE c.id = ?`; WHERE c.id = ?`;
database.pool.query(query, [params.clientId], (error, result) => {
if (error || result.length == 0)
return cb(new Error('No template data found'));
Object.assign(this, result[0]); let [result] = await database.pool.query(query, [params.clientId]);
cb();
}); if (!result || !result.length)
return cb(new Error('No template data found'));
Object.assign(this, result[0]);
cb();
} }
// Swift BIC fields // Swift BIC fields