3622-feat(client_defaulter): implemented smart-table #881
|
@ -99,6 +99,11 @@ smart-table table {
|
|||
}
|
||||
}
|
||||
}
|
||||
& > td > textarea {
|
||||
background-color: $color-font-light;
|
||||
color: $color-font-bg;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
.vn-check {
|
||||
margin: 0;
|
||||
|
|
|
@ -58,16 +58,16 @@ module.exports = Self => {
|
|||
DISTINCT c.id clientFk,
|
||||
c.name clientName,
|
||||
c.salesPersonFk,
|
||||
u.name salesPersonName,
|
||||
u.nickname salesPersonName,
|
||||
d.amount,
|
||||
co.created,
|
||||
CONCAT(DATE(co.created), ' ', co.text) observation,
|
||||
uw.id workerFk,
|
||||
uw.name workerName,
|
||||
uw.nickname workerName,
|
||||
c.creditInsurance,
|
||||
d.defaulterSinced
|
||||
FROM vn.defaulter d
|
||||
JOIN vn.client c ON c.id = d.clientFk
|
||||
JOIN vn.client c ON c.id = d.clientFk
|
||||
LEFT JOIN vn.clientObservation co ON co.clientFk = c.id
|
||||
LEFT JOIN account.user u ON u.id = c.salesPersonFk
|
||||
LEFT JOIN account.user uw ON uw.id = co.workerFk
|
||||
|
|
|
@ -15,17 +15,18 @@
|
|||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-data-viewer
|
||||
model="model"
|
||||
class="vn-w-xl">
|
||||
<vn-card>
|
||||
<vn-tool-bar>
|
||||
<div class="vn-pa-md">
|
||||
<vn-card>
|
||||
<smart-table
|
||||
model="model"
|
||||
options="$ctrl.smartTableOptions"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-actions>
|
||||
<div>
|
||||
<div class="totalBox" style="text-align: center;">
|
||||
<h6 translate>Total</h6>
|
||||
<vn-label-value
|
||||
label="Balance due"
|
||||
value="{{$ctrl.balanceDueTotal}} €">
|
||||
value="{{$ctrl.balanceDueTotal | currency: 'EUR': 2}}">
|
||||
</vn-label-value>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,90 +39,98 @@
|
|||
icon="icon-notes">
|
||||
</vn-button>
|
||||
</div>
|
||||
</vn-tool-bar>
|
||||
<vn-table model="model">
|
||||
<vn-thead>
|
||||
<vn-tr>
|
||||
<vn-th shrink>
|
||||
<vn-multi-check
|
||||
model="model">
|
||||
</vn-multi-check>
|
||||
</vn-th>
|
||||
<vn-th field="clientName">Client</vn-th>
|
||||
<vn-th field="salesPersonFk">Comercial</vn-th>
|
||||
<vn-th
|
||||
field="amount"
|
||||
vn-tooltip="Balance due"
|
||||
number>
|
||||
Balance D.
|
||||
</vn-th>
|
||||
<vn-th
|
||||
vn-tooltip="Worker who made the last observation"
|
||||
shrink>
|
||||
Author
|
||||
</vn-th>
|
||||
<vn-th expand>Last observation</vn-th>
|
||||
<vn-th
|
||||
vn-tooltip="Credit insurance"
|
||||
number>
|
||||
Credit I.
|
||||
</vn-th>
|
||||
<vn-th shrink-datetime>From</vn-th>
|
||||
</vn-tr>
|
||||
</vn-thead>
|
||||
<vn-tbody>
|
||||
<vn-tr ng-repeat="defaulter in defaulters">
|
||||
<vn-td shrink>
|
||||
<vn-check
|
||||
ng-model="defaulter.checked"
|
||||
vn-click-stop>
|
||||
</vn-check>
|
||||
</vn-td>
|
||||
<vn-td>
|
||||
<span
|
||||
vn-click-stop="clientDescriptor.show($event, defaulter.clientFk)"
|
||||
title ="{{::defaulter.clientName}}"
|
||||
class="link">
|
||||
{{::defaulter.clientName}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td>
|
||||
<span
|
||||
title="{{::defaulter.salesPersonName}}"
|
||||
vn-click-stop="workerDescriptor.show($event, defaulter.salesPersonFk)"
|
||||
class="link" >
|
||||
{{::defaulter.salesPersonName | dashIfEmpty}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td number>{{::defaulter.amount}}</vn-td>
|
||||
<vn-td shrink>
|
||||
<span
|
||||
title="{{::defaulter.workerName}}"
|
||||
vn-click-stop="workerDescriptor.show($event, defaulter.workerFk)"
|
||||
class="link" >
|
||||
{{::defaulter.workerName | dashIfEmpty}}
|
||||
</span>
|
||||
</vn-td>
|
||||
<vn-td expand>
|
||||
<vn-textarea
|
||||
vn-three
|
||||
disabled="true"
|
||||
label="Observation"
|
||||
ng-model="defaulter.observation">
|
||||
</vn-textarea>
|
||||
</vn-td>
|
||||
<vn-td number>{{::defaulter.creditInsurance}}</vn-td>
|
||||
<vn-td shrink-datetime>{{::defaulter.defaulterSinced | date: 'dd/MM/yyyy'}}</vn-td>
|
||||
</vn-tr>
|
||||
</vn-tbody>
|
||||
</vn-table>
|
||||
</vn-card>
|
||||
</vn-data-viewer>
|
||||
</slot-actions>
|
||||
<slot-table>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th shrink>
|
||||
<vn-multi-check
|
||||
model="model">
|
||||
</vn-multi-check>
|
||||
</th>
|
||||
<th field="clientName">
|
||||
<span translate>Client</span>
|
||||
</th>
|
||||
<th field="salesPersonFk">
|
||||
<span translate>Comercial</span>
|
||||
</th>
|
||||
<th
|
||||
field="amount"
|
||||
vn-tooltip="Balance due">
|
||||
<span translate>Balance D.</span>
|
||||
</th>
|
||||
<th
|
||||
field="workerFk"
|
||||
vn-tooltip="Worker who made the last observation">
|
||||
<span translate>Author</span>
|
||||
</th>
|
||||
<th field="observation">
|
||||
<span translate>Last observation</span>
|
||||
</th>
|
||||
<th
|
||||
vn-tooltip="Credit insurance"
|
||||
field="creditInsurance" >
|
||||
<span translate>Credit I.</span>
|
||||
</th>
|
||||
<th field="defaulterSinced">
|
||||
<span translate>From</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="defaulter in defaulters">
|
||||
<td shrink>
|
||||
<vn-check
|
||||
ng-model="defaulter.checked"
|
||||
vn-click-stop>
|
||||
</vn-check>
|
||||
</td>
|
||||
<td title="{{::defaulter.clientName}}">
|
||||
<span
|
||||
vn-click-stop="clientDescriptor.show($event, defaulter.clientFk)"
|
||||
title ="{{::defaulter.clientName}}"
|
||||
class="link">
|
||||
{{::defaulter.clientName}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
title="{{::defaulter.salesPersonName}}"
|
||||
vn-click-stop="workerDescriptor.show($event, defaulter.salesPersonFk)"
|
||||
class="link">
|
||||
{{::defaulter.salesPersonName | dashIfEmpty}}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{::defaulter.amount | currency: 'EUR': 2}}</td>
|
||||
<td>
|
||||
<span
|
||||
title="{{::defaulter.workerName}}"
|
||||
vn-click-stop="workerDescriptor.show($event, defaulter.workerFk)"
|
||||
class="link">
|
||||
{{::defaulter.workerName | dashIfEmpty}}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<textarea
|
||||
disabled="true"
|
||||
class="chip {{::$ctrl.chipColor(defaulter.created)}}"
|
||||
ng-model="defaulter.observation">
|
||||
</textarea>
|
||||
</td>
|
||||
<td>{{::defaulter.creditInsurance | currency: 'EUR': 2}}</td>
|
||||
<td>{{::defaulter.defaulterSinced | date: 'dd/MM/yyyy'}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</slot-table>
|
||||
</smart-table>
|
||||
</vn-card>
|
||||
<vn-client-descriptor-popover
|
||||
vn-id="clientDescriptor">
|
||||
vn-id="client-descriptor">
|
||||
</vn-client-descriptor-popover>
|
||||
<vn-worker-descriptor-popover
|
||||
vn-id="workerDescriptor">
|
||||
vn-id="worker-descriptor">
|
||||
</vn-worker-descriptor-popover>
|
||||
<vn-popup vn-id="dialog-summary-client">
|
||||
<vn-client-summary
|
||||
|
@ -129,37 +138,6 @@
|
|||
</vn-client-summary>
|
||||
</vn-popup>
|
||||
|
||||
<!--Context menu-->
|
||||
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
<slot-menu>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.filterBySelection()">
|
||||
Filter by selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.excludeSelection()">
|
||||
Exclude selection
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isFilterAllowed()"
|
||||
ng-click="contextmenu.removeFilter()">
|
||||
Remove filter
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-click="contextmenu.removeAllFilters()">
|
||||
Remove all filters
|
||||
</vn-item>
|
||||
<vn-item translate
|
||||
ng-if="contextmenu.isActionAllowed()"
|
||||
ng-click="contextmenu.copyValue()">
|
||||
Copy value
|
||||
</vn-item>
|
||||
</slot-menu>
|
||||
</vn-contextmenu>
|
||||
|
||||
<!-- Dialog of add notes button -->
|
||||
<vn-dialog
|
||||
vn-id="notesDialog"
|
||||
|
|
|
@ -6,17 +6,70 @@ export default class Controller extends Section {
|
|||
constructor($element, $) {
|
||||
super($element, $);
|
||||
this.defaulter = {};
|
||||
|
||||
this.smartTableOptions = {
|
||||
activeButtons: {
|
||||
search: true
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'clientName',
|
||||
autocomplete: {
|
||||
url: 'Clients',
|
||||
showField: 'name',
|
||||
valueField: 'name'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'salesPersonFk',
|
||||
autocomplete: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
where: `{role: 'salesPerson'}`,
|
||||
searchFunction: '{firstName: $search}',
|
||||
showField: 'nickname',
|
||||
valueField: 'id',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'amount',
|
||||
searchable: false
|
||||
},
|
||||
{
|
||||
field: 'workerFk',
|
||||
autocomplete: {
|
||||
url: 'Workers/activeWithInheritedRole',
|
||||
where: `{role: 'salesPerson'}`,
|
||||
searchFunction: '{firstName: $search}',
|
||||
showField: 'nickname',
|
||||
alexm marked this conversation as resolved
|
||||
valueField: 'id',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'observation',
|
||||
autocomplete: {
|
||||
url: 'Defaulters/filter',
|
||||
showField: 'observation',
|
||||
valueField: 'observation',
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'creditInsurance',
|
||||
searchable: false
|
||||
},
|
||||
{
|
||||
field: 'defaulterSinced',
|
||||
searchable: false
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
get balanceDueTotal() {
|
||||
let balanceDueTotal = 0;
|
||||
const defaulters = this.$.model.data || [];
|
||||
|
||||
if (this.checked.length > 0) {
|
||||
for (let defaulter of this.checked)
|
||||
balanceDueTotal += defaulter.amount;
|
||||
|
||||
return balanceDueTotal;
|
||||
}
|
||||
for (let defaulter of defaulters)
|
||||
balanceDueTotal += defaulter.amount;
|
||||
|
||||
return balanceDueTotal;
|
||||
}
|
||||
|
@ -32,6 +85,22 @@ export default class Controller extends Section {
|
|||
return checkedLines;
|
||||
}
|
||||
|
||||
chipColor(date) {
|
||||
const day = 24 * 60 * 60 * 1000;
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const observationShipped = new Date(date);
|
||||
observationShipped.setHours(0, 0, 0, 0);
|
||||
|
||||
const difference = today - observationShipped;
|
||||
|
||||
if (difference > (day * 20))
|
||||
return 'alert';
|
||||
if (difference > (day * 10))
|
||||
return 'warning';
|
||||
}
|
||||
|
||||
onResponse() {
|
||||
if (!this.defaulter.observation)
|
||||
throw new UserError(`The message can't be empty`);
|
||||
|
@ -52,7 +121,10 @@ export default class Controller extends Section {
|
|||
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'observation':
|
||||
return {[`observation`]: value};
|
||||
case 'clientName':
|
||||
case 'workerFk':
|
||||
case 'salesPersonFk':
|
||||
return {[`d.${param}`]: value};
|
||||
}
|
||||
|
|
|
@ -39,11 +39,7 @@ describe('client defaulter', () => {
|
|||
describe('balanceDueTotal() getter', () => {
|
||||
it('should return balance due total', () => {
|
||||
const data = controller.$.model.data;
|
||||
data[1].checked = true;
|
||||
data[2].checked = true;
|
||||
|
||||
const checkedRows = controller.checked;
|
||||
const expectedAmount = checkedRows[0].amount + checkedRows[1].amount;
|
||||
const expectedAmount = data[0].amount + data[1].amount + data[2].amount;
|
||||
|
||||
const result = controller.balanceDueTotal;
|
||||
|
||||
|
@ -51,6 +47,31 @@ describe('client defaulter', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('chipColor()', () => {
|
||||
it('should return undefined when the date is the present', () => {
|
||||
let today = new Date();
|
||||
let result = controller.chipColor(today);
|
||||
|
||||
expect(result).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should return warning when the date is 10 days in the past', () => {
|
||||
let pastDate = new Date();
|
||||
pastDate = pastDate.setDate(pastDate.getDate() - 11);
|
||||
let result = controller.chipColor(pastDate);
|
||||
|
||||
expect(result).toEqual('warning');
|
||||
});
|
||||
|
||||
it('should return alert when the date is 20 days in the past', () => {
|
||||
let pastDate = new Date();
|
||||
pastDate = pastDate.setDate(pastDate.getDate() - 21);
|
||||
let result = controller.chipColor(pastDate);
|
||||
|
||||
expect(result).toEqual('alert');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onResponse()', () => {
|
||||
it('should return error for empty message', () => {
|
||||
let error;
|
||||
|
|
Loading…
Reference in New Issue
Disable search by observation