Merge branch 'dev' into 3817-updatePortfolio
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2022-06-09 07:52:52 +00:00
commit ad8eace5cb
15 changed files with 898 additions and 9 deletions

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`defaultViewConfig` (tableCode, columns)
VALUES ('clientsDetail', '{"id":true,"phone":true,"city":true,"socialName":true,"salesPersonFk":true,"email":true,"name":false,"fi":false,"credit":false,"creditInsurance":false,"mobile":false,"street":false,"countryFk":false,"provinceFk":false,"postcode":false,"created":false,"businessTypeFk":false,"payMethodFk":false,"sageTaxTypeFk":false,"sageTransactionTypeFk":false,"isActive":false,"isVies":false,"isTaxDataChecked":false,"isEqualizated":false,"isFreezed":false,"hasToInvoice":false,"hasToInvoiceByAddress":false,"isToBeMailed":false,"hasLcr":false,"hasCoreVnl":false,"hasSepaVnl":false}');

View File

@ -318,6 +318,8 @@ export default class SmartTable extends Component {
for (let column of columns) {
const field = column.getAttribute('field');
const cell = document.createElement('td');
cell.setAttribute('centered', '');
if (field) {
let input;
let options;
@ -331,6 +333,15 @@ export default class SmartTable extends Component {
continue;
}
input = this.$compile(`
<vn-textfield
class="dense"
name="${field}"
ng-model="searchProps['${field}']"
ng-keydown="$ctrl.searchWithEvent($event, '${field}')"
clear-disabled="true"
/>`)(this.$inputsScope);
if (options && options.autocomplete) {
let props = ``;
@ -346,16 +357,29 @@ export default class SmartTable extends Component {
on-change="$ctrl.searchByColumn('${field}')"
clear-disabled="true"
/>`)(this.$inputsScope);
} else {
}
if (options && options.checkbox) {
input = this.$compile(`
<vn-textfield
<vn-check
class="dense"
name="${field}"
ng-model="searchProps['${field}']"
ng-keydown="$ctrl.searchWithEvent($event, '${field}')"
clear-disabled="true"
on-change="$ctrl.searchByColumn('${field}')"
triple-state="true"
/>`)(this.$inputsScope);
}
if (options && options.datepicker) {
input = this.$compile(`
<vn-date-picker
class="dense"
name="${field}"
ng-model="searchProps['${field}']"
on-change="$ctrl.searchByColumn('${field}')"
/>`)(this.$inputsScope);
}
cell.appendChild(input[0]);
}
searchRow.appendChild(cell);
@ -372,13 +396,12 @@ export default class SmartTable extends Component {
searchByColumn(field) {
const searchCriteria = this.$inputsScope.searchProps[field];
const emptySearch = searchCriteria == '' || null;
const emptySearch = searchCriteria === '' || searchCriteria == null;
const filters = this.filterSanitizer(field);
if (filters && filters.userFilter)
this.model.userFilter = filters.userFilter;
if (!emptySearch)
this.addFilter(field, this.$inputsScope.searchProps[field]);
else this.model.refresh();

View File

@ -224,5 +224,7 @@
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
"date in the future": "Fecha en el futuro",
"reference duplicated": "Referencia duplicada",
"This ticket is already a refund": "Este ticket ya es un abono"
"This ticket is already a refund": "Este ticket ya es un abono",
"isWithoutNegatives": "isWithoutNegatives",
"routeFk": "routeFk"
}

View File

@ -0,0 +1,159 @@
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const buildFilter = require('vn-loopback/util/filter').buildFilter;
const mergeFilters = require('vn-loopback/util/filter').mergeFilters;
module.exports = Self => {
Self.remoteMethodCtx('extendedListFilter', {
description: 'Find all clients matched by the filter',
accessType: 'READ',
accepts: [
{
arg: 'filter',
type: 'object',
},
{
arg: 'search',
type: 'string',
description: `If it's and integer searchs by id, otherwise it searchs by name`,
},
{
arg: 'name',
type: 'string',
description: 'The client name',
},
{
arg: 'salesPersonFk',
type: 'number',
},
{
arg: 'fi',
type: 'string',
description: 'The client fiscal id',
},
{
arg: 'socialName',
type: 'string',
},
{
arg: 'city',
type: 'string',
},
{
arg: 'postcode',
type: 'string',
},
{
arg: 'provinceFk',
type: 'number',
},
{
arg: 'email',
type: 'string',
},
{
arg: 'phone',
type: 'string',
},
],
returns: {
type: ['object'],
root: true
},
http: {
path: `/extendedListFilter`,
verb: 'GET'
}
});
Self.extendedListFilter = async(ctx, filter, options) => {
const conn = Self.dataSource.connector;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const where = buildFilter(ctx.args, (param, value) => {
switch (param) {
case 'search':
return /^\d+$/.test(value)
? {'c.id': {inq: value}}
: {'c.name': {like: `%${value}%`}};
case 'name':
case 'salesPersonFk':
case 'fi':
case 'socialName':
case 'city':
case 'postcode':
case 'provinceFk':
case 'email':
case 'phone':
param = `c.${param}`;
return {[param]: value};
}
});
filter = mergeFilters(filter, {where});
const stmts = [];
const stmt = new ParameterizedSQL(
`SELECT
c.id,
c.name,
c.socialName,
c.fi,
c.credit,
c.creditInsurance,
c.phone,
c.mobile,
c.street,
c.city,
c.postcode,
c.email,
c.created,
c.isActive,
c.isVies,
c.isTaxDataChecked,
c.isEqualizated,
c.isFreezed,
c.hasToInvoice,
c.hasToInvoiceByAddress,
c.isToBeMailed,
c.hasSepaVnl,
c.hasLcr,
c.hasCoreVnl,
ct.id AS countryFk,
ct.country,
p.id AS provinceFk,
p.name AS province,
u.id AS salesPersonFk,
u.name AS salesPerson,
bt.code AS businessTypeFk,
bt.description AS businessType,
pm.id AS payMethodFk,
pm.name AS payMethod,
sti.CodigoIva AS sageTaxTypeFk,
sti.Iva AS sageTaxType,
stt.CodigoTransaccion AS sageTransactionTypeFk,
stt.Transaccion AS sageTransactionType
FROM client c
LEFT JOIN account.user u ON u.id = c.salesPersonFk
LEFT JOIN country ct ON ct.id = c.countryFk
LEFT JOIN province p ON p.id = c.provinceFk
LEFT JOIN businessType bt ON bt.code = c.businessTypeFk
LEFT JOIN payMethod pm ON pm.id = c.payMethodFk
LEFT JOIN sage.TiposIva sti ON sti.CodigoIva = c.taxTypeSageFk
LEFT JOIN sage.TiposTransacciones stt ON stt.CodigoTransaccion = c.transactionTypeSageFk
`
);
stmt.merge(conn.makeWhere(filter.where));
stmt.merge(conn.makePagination(filter));
const clientsIndex = stmts.push(stmt) - 1;
const sql = ParameterizedSQL.join(stmts, ';');
const result = await conn.executeStmt(sql, myOptions);
return clientsIndex === 0 ? result : result[clientsIndex];
};
};

View File

@ -0,0 +1,180 @@
const { models } = require('vn-loopback/server/server');
describe('client extendedListFilter()', () => {
it('should return the clients matching the filter with a limit of 20 rows', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {}};
const filter = {limit: '20'};
const result = await models.Client.extendedListFilter(ctx, filter, options);
expect(result.length).toEqual(20);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the client "Bruce Wayne" matching the search argument with his name', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {search: 'Bruce Wayne'}};
const filter = {};
const result = await models.Client.extendedListFilter(ctx, filter, options);
const firstResult = result[0];
expect(result.length).toEqual(1);
expect(firstResult.name).toEqual('Bruce Wayne');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the client "Bruce Wayne" matching the search argument with his id', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {search: '1101'}};
const filter = {};
const result = await models.Client.extendedListFilter(ctx, filter, options);
const firstResult = result[0];
expect(result.length).toEqual(1);
expect(firstResult.name).toEqual('Bruce Wayne');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the client "Bruce Wayne" matching the name argument', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {name: 'Bruce Wayne'}};
const filter = {};
const result = await models.Client.extendedListFilter(ctx, filter, options);
const firstResult = result[0];
expect(result.length).toEqual(1);
expect(firstResult.name).toEqual('Bruce Wayne');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the clients matching the "salesPersonFk" argument', async() => {
const tx = await models.Client.beginTransaction({});
const salesPersonId = 18;
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {salesPersonFk: salesPersonId}};
const filter = {};
const result = await models.Client.extendedListFilter(ctx, filter, options);
const randomIndex = Math.floor(Math.random() * result.length);
const randomResultClient = result[randomIndex];
expect(result.length).toBeGreaterThanOrEqual(5);
expect(randomResultClient.salesPersonFk).toEqual(salesPersonId);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the clients matching the "fi" argument', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {fi: '251628698'}};
const filter = {};
const result = await models.Client.extendedListFilter(ctx, filter, options);
const firstClient = result[0];
expect(result.length).toEqual(1);
expect(firstClient.name).toEqual('Max Eisenhardt');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the clients matching the "city" argument', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {city: 'Silla'}};
const filter = {};
const result = await models.Client.extendedListFilter(ctx, filter, options);
const randomIndex = Math.floor(Math.random() * result.length);
const randomResultClient = result[randomIndex];
expect(result.length).toBeGreaterThanOrEqual(20);
expect(randomResultClient.city.toLowerCase()).toEqual('silla');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should return the clients matching the "postcode" argument', async() => {
const tx = await models.Client.beginTransaction({});
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 1}}, args: {postcode: '46460'}};
const filter = {};
const result = await models.Client.extendedListFilter(ctx, filter, options);
const randomIndex = Math.floor(Math.random() * result.length);
const randomResultClient = result[randomIndex];
expect(result.length).toBeGreaterThanOrEqual(20);
expect(randomResultClient.postcode).toEqual('46460');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -31,6 +31,7 @@ module.exports = Self => {
require('../methods/client/createReceipt')(Self);
require('../methods/client/updatePortfolio')(Self);
require('../methods/client/checkDuplicated')(Self);
require('../methods/client/extendedListFilter')(Self);
// Validations

View File

@ -0,0 +1,319 @@
<vn-crud-model
vn-id="model"
url="Clients/extendedListFilter"
limit="20">
</vn-crud-model>
<vn-portal slot="topbar">
<vn-searchbar
vn-focus
panel="vn-client-search-panel"
placeholder="Search client"
info="Search client by id or name"
auto-state="false"
model="model">
</vn-searchbar>
</vn-portal>
<vn-card>
<smart-table
model="model"
view-config-id="clientsDetail"
options="$ctrl.smartTableOptions"
expr-builder="$ctrl.exprBuilder(param, value)">
<slot-table>
<table>
<thead>
<tr>
<th></th>
<th field="id">
<span translate>Identifier</span>
</th>
<th field="name">
<span translate>Name</span>
</th>
<th field="socialName">
<span translate>Social name</span>
</th>
<th field="fi">
<span translate>Tax number</span>
</th>
<th field="salesPersonFk">
<span translate>Salesperson</span>
</th>
<th field="credit">
<span translate>Credit</span>
</th>
<th field="creditInsurance">
<span translate>Credit insurance</span>
</th>
<th field="phone">
<span translate>Phone</span>
</th>
<th field="mobile">
<span translate>Mobile</span>
</th>
<th field="street">
<span translate>Street</span>
</th>
<th field="countryFk">
<span translate>Country</span>
</th>
<th field="provinceFk">
<span translate>Province</span>
</th>
<th field="city">
<span translate>City</span>
</th>
<th field="postcode">
<span translate>Postcode</span>
</th>
<th field="email">
<span translate>Email</span>
</th>
<th field="created">
<span translate>Created</span>
</th>
<th field="businessTypeFk">
<span translate>Business type</span>
</th>
<th field="payMethodFk">
<span translate>Billing data</span>
</th>
<th field="sageTaxTypeFk">
<span translate>Sage tax type</span>
</th>
<th field="sageTransactionTypeFk">
<span translate>Sage tr. type</span>
</th>
<th field="isActive" centered>
<span translate>Active</span>
</th>
<th field="isVies" centered>
<span translate>Vies</span>
</th>
<th field="isTaxDataChecked" centered>
<span translate>Verified data</span>
</th>
<th field="isEqualizated" centered>
<span translate>Is equalizated</span>
</th>
<th field="isFreezed" centered>
<span translate>Freezed</span>
</th>
<th field="hasToInvoice" centered>
<span translate>Invoice</span>
</th>
<th field="hasToInvoiceByAddress" centered>
<span translate>Invoice by address</span>
</th>
<th field="isToBeMailed" centered>
<span translate>Mailing</span>
</th>
<th field="hasLcr" centered>
<span translate>Received LCR</span>
</th>
<th field="hasCoreVnl" centered>
<span translate>Received core VNL</span>
</th>
<th field="hasSepaVnl" centered>
<span translate>Received B2B VNL</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="client in model.data"
vn-anchor="::{
state: 'client.card.summary',
params: {id: client.id}
}">
<td>
<vn-icon-button ng-show="::client.isActive == false"
vn-tooltip="Client inactive"
icon="icon-disabled">
</vn-icon-button>
<vn-icon-button ng-show="::client.isActive && client.isFreezed == true"
vn-tooltip="Client frozen"
icon="icon-frozen">
</vn-icon-button>
</td>
<td>
<span
vn-click-stop="clientDescriptor.show($event, client.id)"
class="link">
{{::client.id}}
</span>
</td>
<td>{{::client.name}}</td>
<td>{{::client.socialName}}</td>
<td>{{::client.fi}}</td>
<td>
<span
vn-click-stop="workerDescriptor.show($event, client.salesPersonFk)"
ng-class="{'link': client.salesPersonFk}">
{{::client.salesPerson | dashIfEmpty}}
</span>
</td>
<td>{{::client.credit}}</td>
<td>{{::client.creditInsurance | dashIfEmpty}}</td>
<td>{{::client.phone | dashIfEmpty}}</td>
<td>{{::client.mobile | dashIfEmpty}}</td>
<td>{{::client.street | dashIfEmpty}}</td>
<td>{{::client.country | dashIfEmpty}}</td>
<td>{{::client.province | dashIfEmpty}}</td>
<td>{{::client.city | dashIfEmpty}}</td>
<td>{{::client.postcode | dashIfEmpty}}</td>
<td>{{::client.email | dashIfEmpty}}</td>
<td>{{::client.created | date:'dd/MM/yyyy'}}</td>
<td>{{::client.businessType | dashIfEmpty}}</td>
<td>{{::client.payMethod | dashIfEmpty}}</td>
<td>{{::client.sageTaxType | dashIfEmpty}}</td>
<td>{{::client.sageTransactionType | dashIfEmpty}}</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isActive,
'alert': !client.isActive,
}">
{{ ::client.isActive ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isVies,
'alert': !client.isVies,
}">
{{ ::client.isVies ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isTaxDataChecked,
'alert': !client.isTaxDataChecked,
}">
{{ ::client.isTaxDataChecked ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isEqualizated,
'alert': !client.isEqualizated,
}">
{{ ::client.isEqualizated ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isFreezed,
'alert': !client.isFreezed,
}">
{{ ::client.isFreezed ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasToInvoice,
'alert': !client.hasToInvoice,
}">
{{ ::client.hasToInvoice ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasToInvoiceByAddress,
'alert': !client.hasToInvoiceByAddress,
}">
{{ ::client.hasToInvoiceByAddress ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.isToBeMailed,
'alert': !client.isToBeMailed,
}">
{{ ::client.isToBeMailed ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasLcr,
'alert': !client.hasLcr,
}">
{{ ::client.hasLcr ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasCoreVnl,
'alert': !client.hasCoreVnl,
}">
{{ ::client.hasCoreVnl ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td centered>
<vn-chip ng-class="::{
'success': client.hasSepaVnl,
'alert': !client.hasSepaVnl,
}">
{{ ::client.hasSepaVnl ? 'Yes' : 'No' | translate}}
</vn-chip>
</td>
<td shrink>
<vn-horizontal class="buttons">
<vn-icon-button vn-anchor="{state: 'ticket.index', params: {q: {clientFk: client.id} } }"
vn-tooltip="Client tickets"
icon="icon-ticket">
</vn-icon-button>
<vn-icon-button
vn-click-stop="$ctrl.preview(client)"
vn-tooltip="Preview"
icon="preview">
</vn-icon-button>
</vn-horizontal>
</td>
</tr>
</tbody>
</table>
</slot-table>
</smart-table>
</vn-card>
<a ui-sref="client.create" vn-tooltip="New client" vn-bind="+" fixed-bottom-right>
<vn-float-button icon="add"></vn-float-button>
</a>
<vn-client-descriptor-popover
vn-id="client-descriptor">
</vn-client-descriptor-popover>
<vn-worker-descriptor-popover
vn-id="worker-descriptor">
</vn-worker-descriptor-popover>
<vn-popup vn-id="preview">
<vn-client-summary
client="$ctrl.clientSelected">
</vn-client-summary>
</vn-popup>
<vn-contextmenu
vn-id="contextmenu"
targets="['smart-table']"
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>
</slot-menu>
</vn-contextmenu>

View File

@ -0,0 +1,184 @@
import ngModule from '../module';
import Section from 'salix/components/section';
import './style.scss';
class Controller extends Section {
constructor($element, $) {
super($element, $);
this.smartTableOptions = {
activeButtons: {
search: true,
shownColumns: true,
},
columns: [
{
field: 'socialName',
autocomplete: {
url: 'Clients',
showField: 'socialName',
valueField: 'socialName',
}
},
{
field: 'created',
datepicker: true
},
{
field: 'countryFk',
autocomplete: {
url: 'Countries',
showField: 'country',
}
},
{
field: 'provinceFk',
autocomplete: {
url: 'Provinces'
}
},
{
field: 'salesPersonFk',
autocomplete: {
url: 'Workers/activeWithInheritedRole',
where: `{role: 'salesPerson'}`,
searchFunction: '{firstName: $search}',
showField: 'nickname',
valueField: 'id',
}
},
{
field: 'businessTypeFk',
autocomplete: {
url: 'BusinessTypes',
valueField: 'code',
showField: 'description',
}
},
{
field: 'payMethodFk',
autocomplete: {
url: 'PayMethods',
}
},
{
field: 'sageTaxTypeFk',
autocomplete: {
url: 'SageTaxTypes',
showField: 'vat',
}
},
{
field: 'sageTransactionTypeFk',
autocomplete: {
url: 'SageTransactionTypes',
showField: 'transaction',
}
},
{
field: 'isActive',
checkbox: true
},
{
field: 'isVies',
checkbox: true
},
{
field: 'isTaxDataChecked',
checkbox: true
},
{
field: 'isEqualizated',
checkbox: true
},
{
field: 'isFreezed',
checkbox: true
},
{
field: 'hasToInvoice',
checkbox: true
},
{
field: 'hasToInvoiceByAddress',
checkbox: true
},
{
field: 'isToBeMailed',
checkbox: true
},
{
field: 'hasSepaVnl',
checkbox: true
},
{
field: 'hasLcr',
checkbox: true
},
{
field: 'hasCoreVnl',
checkbox: true
}
]
};
}
exprBuilder(param, value) {
switch (param) {
case 'created':
return {'c.created': {
between: this.dateRange(value)}
};
case 'id':
case 'name':
case 'socialName':
case 'fi':
case 'credit':
case 'creditInsurance':
case 'phone':
case 'mobile':
case 'street':
case 'city':
case 'postcode':
case 'email':
case 'isActive':
case 'isVies':
case 'isTaxDataChecked':
case 'isEqualizated':
case 'isFreezed':
case 'hasToInvoice':
case 'hasToInvoiceByAddress':
case 'isToBeMailed':
case 'hasSepaVnl':
case 'hasLcr':
case 'hasCoreVnl':
case 'countryFk':
case 'provinceFk':
case 'salesPersonFk':
case 'businessTypeFk':
case 'payMethodFk':
case 'sageTaxTypeFk':
case 'sageTransactionTypeFk':
return {[`c.${param}`]: value};
}
}
dateRange(value) {
const minHour = new Date(value);
minHour.setHours(0, 0, 0, 0);
const maxHour = new Date(value);
maxHour.setHours(23, 59, 59, 59);
return [minHour, maxHour];
}
preview(client) {
this.clientSelected = client;
this.$.preview.show();
}
}
ngModule.vnComponent('vnClientExtendedList', {
template: require('./index.html'),
controller: Controller
});

View File

@ -0,0 +1,3 @@
Mailing: Env. emails
Sage tr. type: Tipo tr. sage
Yes:

View File

@ -0,0 +1,6 @@
@import "variables";
vn-chip.success,
vn-chip.alert {
color: $color-font-bg
}

View File

@ -47,3 +47,4 @@ import './consumption-search-panel';
import './defaulter';
import './notification';
import './unpaid';
import './extended-list';

View File

@ -33,6 +33,7 @@ Search client by id or name: Buscar clientes por identificador o nombre
# Sections
Clients: Clientes
Extended list: Listado extendido
Defaulter: Morosos
New client: Nuevo cliente
Fiscal data: Datos fiscales

View File

@ -7,6 +7,7 @@
"menus": {
"main": [
{"state": "client.index", "icon": "person"},
{"state": "client.extendedList", "icon": "person"},
{"state": "client.notification", "icon": "campaign"},
{"state": "client.defaulter", "icon": "icon-defaulter"}
],
@ -381,6 +382,12 @@
"component": "vn-client-unpaid",
"acl": ["administrative"],
"description": "Unpaid"
},
{
"url": "/extended-list",
"state": "client.extendedList",
"component": "vn-client-extended-list",
"description": "Extended list"
}
]
}

View File

@ -1,7 +1,7 @@
Client id: Id cliente
Tax number: NIF/CIF
Name: Nombre
Social name: Razon social
Social name: Razón social
Town/City: Ciudad
Postcode: Código postal
Email: E-mail

View File

@ -53,7 +53,7 @@ export default class Controller extends Section {
},
{
field: 'shippedDate',
searchable: false
datepicker: true
},
{
field: 'theoreticalHour',