Merge pull request '3620-feat(claim): implemented smart-table and change state in summary' (#887) from 3620-claim_change_state into dev
gitea/salix/pipeline/head This commit looks good Details

Reviewed-on: #887
Reviewed-by: Carlos Jimenez Ruiz <carlosjr@verdnatura.es>
This commit is contained in:
Carlos Jimenez Ruiz 2022-03-02 10:12:51 +00:00
commit d923209c2d
10 changed files with 243 additions and 92 deletions

View File

@ -94,19 +94,14 @@ module.exports = Self => {
? {'cl.id': value} ? {'cl.id': value}
: { : {
or: [ or: [
{'c.name': {like: `%${value}%`}} {'cl.socialName': {like: `%${value}%`}}
] ]
}; };
case 'client':
return {'c.name': {like: `%${value}%`}};
case 'id': case 'id':
return {'cl.id': value};
case 'clientFk':
return {'c.id': value};
case 'claimStateFk': case 'claimStateFk':
return {'cl.claimStateFk': value}; case 'priority':
return {[`cl.${param}`]: value};
case 'salesPersonFk': case 'salesPersonFk':
return {'c.salesPersonFk': value};
case 'attenderFk': case 'attenderFk':
return {'cl.workerFk': value}; return {'cl.workerFk': value};
case 'created': case 'created':
@ -123,12 +118,23 @@ module.exports = Self => {
const stmts = []; const stmts = [];
const stmt = new ParameterizedSQL( const stmt = new ParameterizedSQL(
`SELECT cl.id, c.name, cl.clientFk, cl.workerFk, u.name AS userName, cs.description, cl.created `SELECT *
FROM claim cl FROM (
LEFT JOIN client c ON c.id = cl.clientFk SELECT
LEFT JOIN worker w ON w.id = cl.workerFk cl.id,
LEFT JOIN account.user u ON u.id = w.userFk cl.clientFk,
LEFT JOIN claimState cs ON cs.id = cl.claimStateFk` c.socialName,
cl.workerFk,
u.name AS workerName,
cs.description,
cl.created,
cs.priority,
cl.claimStateFk
FROM claim cl
LEFT JOIN client c ON c.id = cl.clientFk
LEFT JOIN worker w ON w.id = cl.workerFk
LEFT JOIN account.user u ON u.id = w.userFk
LEFT JOIN claimState cs ON cs.id = cl.claimStateFk ) cl`
); );
stmt.merge(conn.makeSuffix(filter)); stmt.merge(conn.makeSuffix(filter));

View File

@ -1,5 +1,5 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('getSummary', { Self.remoteMethodCtx('getSummary', {
description: 'Return the claim summary', description: 'Return the claim summary',
accessType: 'READ', accessType: 'READ',
accepts: [{ accepts: [{
@ -19,7 +19,7 @@ module.exports = Self => {
} }
}); });
Self.getSummary = async(id, options) => { Self.getSummary = async(ctx, id, options) => {
const myOptions = {}; const myOptions = {};
if (typeof options == 'object') if (typeof options == 'object')
@ -135,6 +135,7 @@ module.exports = Self => {
const res = await Promise.all(promises); const res = await Promise.all(promises);
summary.isEditable = await Self.isEditable(ctx, id, myOptions);
[summary.claim] = res[0]; [summary.claim] = res[0];
summary.salesClaimed = res[1]; summary.salesClaimed = res[1];
summary.developments = res[2]; summary.developments = res[2];

View File

@ -25,7 +25,7 @@ describe('claim filter()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const result = await app.models.Claim.filter({args: {filter: {}, search: 'Tony Stark'}}, null, options); const result = await app.models.Claim.filter({args: {filter: {}, search: 'Iron man'}}, null, options);
expect(result.length).toEqual(1); expect(result.length).toEqual(1);
expect(result[0].id).toEqual(4); expect(result[0].id).toEqual(4);

View File

@ -3,17 +3,24 @@ const app = require('vn-loopback/server/server');
describe('claim getSummary()', () => { describe('claim getSummary()', () => {
it('should return summary with claim, salesClaimed, developments and actions defined ', async() => { it('should return summary with claim, salesClaimed, developments and actions defined ', async() => {
const tx = await app.models.Claim.beginTransaction({}); const tx = await app.models.Claim.beginTransaction({});
const ctx = {
req: {
accessToken: {
userId: 9
}
}
};
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
const result = await app.models.Claim.getSummary(1, options); const result = await app.models.Claim.getSummary(ctx, 1, options);
const keys = Object.keys(result); const keys = Object.keys(result);
expect(keys).toContain('claim'); expect(keys).toContain('claim');
expect(keys).toContain('salesClaimed'); expect(keys).toContain('salesClaimed');
expect(keys).toContain('developments'); expect(keys).toContain('developments');
expect(keys).toContain('actions'); expect(keys).toContain('actions');
expect(keys).toContain('isEditable');
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -1,59 +1,74 @@
<vn-auto-search <vn-auto-search
model="model"> model="model">
</vn-auto-search> </vn-auto-search>
<vn-data-viewer <vn-card>
model="model" <smart-table
class="vn-w-lg"> model="model"
<vn-card> options="$ctrl.smartTableOptions"
<vn-table model="model"> expr-builder="$ctrl.exprBuilder(param, value)">
<vn-thead> <slot-table>
<vn-tr> <table>
<vn-th field="id" number>Id</vn-th> <thead>
<vn-th field="clientFk">Client</vn-th> <tr>
<vn-th field="created" center shrink-date>Created</vn-th> <th field="id" shrink>
<vn-th field="workerFk">Worker</vn-th> <span translate>Id</span>
<vn-th field="claimStateFk">State</vn-th> </th>
<vn-th></vn-th> <th field="clientFk">
</vn-tr> <span translate>Client</span>
</vn-thead> </th>
<vn-tbody> <th field="created" center shrink-date>
<a <span translate>Created</span>
ng-repeat="claim in model.data" </th>
class="{{::$ctrl.compareDate(ticket.shipped)}} clickable vn-tr search-result" <th field="salesPersonFk">
ui-sref="claim.card.summary({id: claim.id})"> <span translate>Worker</span>
<vn-td number>{{::claim.id}}</vn-td> </th>
<vn-td expand> <th field="claimStateFk">
<span <span translate>State</span>
vn-click-stop="clientDescriptor.show($event, claim.clientFk)" </th>
class="link"> <th></th>
{{::claim.name}} </tr>
</span> </thead>
</vn-td> <tbody>
<vn-td center shrink-date>{{::claim.created | date:'dd/MM/yyyy'}}</vn-td> <tr
<vn-td expand> ng-repeat="claim in model.data"
<span vn-anchor="::{
vn-click-stop="workerDescriptor.show($event, claim.workerFk)" state: 'claim.card.summary',
class="link" > params: {id: claim.id}
{{::claim.userName}} }">
</span> <td>{{::claim.id}}</td>
</vn-td> <td>
<vn-td> <span
<span class="chip {{::$ctrl.stateColor(claim)}}"> vn-click-stop="clientDescriptor.show($event, claim.clientFk)"
{{::claim.description}} class="link">
</span> {{::claim.socialName}}
</vn-td> </span>
<vn-td shrink> </td>
<vn-icon-button <td center shrink-date>{{::claim.created | date:'dd/MM/yyyy'}}</td>
vn-click-stop="$ctrl.preview(claim)" <td>
vn-tooltip="Preview" <span
icon="preview"> vn-click-stop="workerDescriptor.show($event, claim.workerFk)"
</vn-icon-button> class="link" >
</vn-td> {{::claim.workerName}}
</a> </span>
</vn-tbody> </td>
</vn-table> <td>
</vn-card> <span class="chip {{::$ctrl.stateColor(claim)}}">
</vn-data-viewer> {{::claim.description}}
</span>
</td>
<td shrink>
<vn-icon-button
vn-click-stop="$ctrl.preview(claim)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<vn-client-descriptor-popover <vn-client-descriptor-popover
vn-id="clientDescriptor"> vn-id="clientDescriptor">
</vn-client-descriptor-popover> </vn-client-descriptor-popover>
@ -62,6 +77,7 @@
</vn-worker-descriptor-popover> </vn-worker-descriptor-popover>
<vn-popup vn-id="summary"> <vn-popup vn-id="summary">
<vn-claim-summary <vn-claim-summary
claim="$ctrl.claimSelected"> claim="$ctrl.claimSelected"
parent-reload="$ctrl.reload()">
</vn-claim-summary> </vn-claim-summary>
</vn-popup> </vn-popup>

View File

@ -1,7 +1,69 @@
import ngModule from '../module'; import ngModule from '../module';
import Section from 'salix/components/section'; import Section from 'salix/components/section';
export default class Controller extends Section { class Controller extends Section {
constructor($element, $) {
super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true
},
columns: [
{
field: 'clientFk',
autocomplete: {
url: 'Clients',
showField: 'socialName',
valueField: 'socialName'
}
},
{
field: 'workerFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'name',
valueField: 'id',
}
},
{
field: 'claimStateFk',
autocomplete: {
url: 'ClaimStates',
showField: 'description',
valueField: 'id',
}
},
{
field: 'created',
searchable: false
}
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'clientFk':
return {['cl.socialName']: value};
case 'id':
case 'claimStateFk':
case 'priority':
return {[`cl.${param}`]: value};
case 'salesPersonFk':
case 'attenderFk':
return {'cl.workerFk': value};
case 'created':
value.setHours(0, 0, 0, 0);
to = new Date(value);
to.setHours(23, 59, 59, 999);
return {'cl.created': {between: [value, to]}};
}
}
stateColor(claim) { stateColor(claim) {
switch (claim.description) { switch (claim.description) {
case 'Pendiente': case 'Pendiente':
@ -17,6 +79,10 @@ export default class Controller extends Section {
this.claimSelected = claim; this.claimSelected = claim;
this.$.summary.show(); this.$.summary.show();
} }
reload() {
this.$.model.refresh();
}
} }
ngModule.vnComponent('vnClaimIndex', { ngModule.vnComponent('vnClaimIndex', {

View File

@ -1,9 +1,7 @@
import ngModule from '../module'; import ngModule from '../module';
import ModuleMain from 'salix/components/module-main'; import ModuleMain from 'salix/components/module-main';
export default class Claim extends ModuleMain {}
ngModule.vnComponent('vnClaim', { ngModule.vnComponent('vnClaim', {
controller: Claim, controller: ModuleMain,
template: require('./index.html') template: require('./index.html')
}); });

View File

@ -12,6 +12,15 @@
<vn-icon-button icon="launch"></vn-icon-button> <vn-icon-button icon="launch"></vn-icon-button>
</a> </a>
<span>{{::$ctrl.summary.claim.id}} - {{::$ctrl.summary.claim.client.name}}</span> <span>{{::$ctrl.summary.claim.id}} - {{::$ctrl.summary.claim.client.name}}</span>
<vn-button-menu
disabled="!$ctrl.summary.isEditable"
class="message"
label="Change state"
value-field="id"
show-field="description"
url="claimStates"
on-change="$ctrl.changeState(value)">
</vn-button-menu>
</h5> </h5>
<vn-horizontal> <vn-horizontal>
<vn-one> <vn-one>

View File

@ -10,7 +10,25 @@ class Controller extends Summary {
$onChanges() { $onChanges() {
if (this.claim && this.claim.id) if (this.claim && this.claim.id)
this.getSummary(); this.loadData();
}
loadData() {
return this.$http.get(`Claims/${this.claim.id}/getSummary`).then(res => {
if (res && res.data)
this.summary = res.data;
});
}
reload() {
this.loadData()
.then(() => {
if (this.card)
this.card.reload();
if (this.parentReload)
this.parentReload();
});
} }
get isSalesPerson() { get isSalesPerson() {
@ -29,8 +47,10 @@ class Controller extends Summary {
this._claim = value; this._claim = value;
// Get DMS on summary load // Get DMS on summary load
if (value) if (value) {
this.$.$applyAsync(() => this.loadDms()); this.$.$applyAsync(() => this.loadDms());
this.loadData();
}
} }
loadDms() { loadDms() {
@ -40,15 +60,24 @@ class Controller extends Summary {
this.$.model.refresh(); this.$.model.refresh();
} }
getSummary() {
this.$http.get(`Claims/${this.claim.id}/getSummary`).then(response => {
this.summary = response.data;
});
}
getImagePath(dmsId) { getImagePath(dmsId) {
return this.vnFile.getPath(`/api/dms/${dmsId}/downloadFile`); return this.vnFile.getPath(`/api/dms/${dmsId}/downloadFile`);
} }
changeState(value) {
const params = {
id: this.claim.id,
claimStateFk: value
};
this.$http.patch(`Claims/updateClaim/${this.claim.id}`, params)
.then(() => {
this.reload();
})
.then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
});
}
} }
Controller.$inject = ['$element', '$scope', 'vnFile']; Controller.$inject = ['$element', '$scope', 'vnFile'];
@ -57,6 +86,11 @@ ngModule.vnComponent('vnClaimSummary', {
template: require('./index.html'), template: require('./index.html'),
controller: Controller, controller: Controller,
bindings: { bindings: {
claim: '<' claim: '<',
model: '<?',
parentReload: '&'
},
require: {
card: '?^vnClaimCard'
} }
}); });

View File

@ -18,23 +18,37 @@ describe('Claim', () => {
controller.$.model = crudModel; controller.$.model = crudModel;
})); }));
describe('getSummary()', () => { describe('loadData()', () => {
it('should perform a query to set summary', () => { it('should perform a query to set summary', () => {
$httpBackend.expect('GET', `Claims/1/getSummary`).respond(200, 24); $httpBackend.when('GET', `Claims/1/getSummary`).respond(200, 24);
controller.getSummary(); controller.loadData();
$httpBackend.flush(); $httpBackend.flush();
expect(controller.summary).toEqual(24); expect(controller.summary).toEqual(24);
}); });
}); });
describe('changeState()', () => {
it('should make an HTTP post query, then call the showSuccess()', () => {
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
const expectedParams = {id: 1, claimStateFk: 1};
$httpBackend.when('GET', `Claims/1/getSummary`).respond(200, 24);
$httpBackend.expect('PATCH', `Claims/updateClaim/1`, expectedParams).respond(200);
controller.changeState(1);
$httpBackend.flush();
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
});
});
describe('$onChanges()', () => { describe('$onChanges()', () => {
it('should call getSummary when item.id is defined', () => { it('should call loadData when $onChanges is called', () => {
jest.spyOn(controller, 'getSummary'); jest.spyOn(controller, 'loadData');
controller.$onChanges(); controller.$onChanges();
expect(controller.getSummary).toHaveBeenCalledWith(); expect(controller.loadData).toHaveBeenCalledWith();
}); });
}); });
}); });