Merge branch 'dev' into 3817-updatePortfolio
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
ad8eace5cb
|
@ -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}');
|
||||||
|
|
|
@ -318,6 +318,8 @@ export default class SmartTable extends Component {
|
||||||
for (let column of columns) {
|
for (let column of columns) {
|
||||||
const field = column.getAttribute('field');
|
const field = column.getAttribute('field');
|
||||||
const cell = document.createElement('td');
|
const cell = document.createElement('td');
|
||||||
|
cell.setAttribute('centered', '');
|
||||||
|
|
||||||
if (field) {
|
if (field) {
|
||||||
let input;
|
let input;
|
||||||
let options;
|
let options;
|
||||||
|
@ -331,6 +333,15 @@ export default class SmartTable extends Component {
|
||||||
continue;
|
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) {
|
if (options && options.autocomplete) {
|
||||||
let props = ``;
|
let props = ``;
|
||||||
|
|
||||||
|
@ -346,16 +357,29 @@ export default class SmartTable extends Component {
|
||||||
on-change="$ctrl.searchByColumn('${field}')"
|
on-change="$ctrl.searchByColumn('${field}')"
|
||||||
clear-disabled="true"
|
clear-disabled="true"
|
||||||
/>`)(this.$inputsScope);
|
/>`)(this.$inputsScope);
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (options && options.checkbox) {
|
||||||
input = this.$compile(`
|
input = this.$compile(`
|
||||||
<vn-textfield
|
<vn-check
|
||||||
class="dense"
|
class="dense"
|
||||||
name="${field}"
|
name="${field}"
|
||||||
ng-model="searchProps['${field}']"
|
ng-model="searchProps['${field}']"
|
||||||
ng-keydown="$ctrl.searchWithEvent($event, '${field}')"
|
on-change="$ctrl.searchByColumn('${field}')"
|
||||||
clear-disabled="true"
|
triple-state="true"
|
||||||
/>`)(this.$inputsScope);
|
/>`)(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]);
|
cell.appendChild(input[0]);
|
||||||
}
|
}
|
||||||
searchRow.appendChild(cell);
|
searchRow.appendChild(cell);
|
||||||
|
@ -372,13 +396,12 @@ export default class SmartTable extends Component {
|
||||||
|
|
||||||
searchByColumn(field) {
|
searchByColumn(field) {
|
||||||
const searchCriteria = this.$inputsScope.searchProps[field];
|
const searchCriteria = this.$inputsScope.searchProps[field];
|
||||||
const emptySearch = searchCriteria == '' || null;
|
const emptySearch = searchCriteria === '' || searchCriteria == null;
|
||||||
|
|
||||||
const filters = this.filterSanitizer(field);
|
const filters = this.filterSanitizer(field);
|
||||||
|
|
||||||
if (filters && filters.userFilter)
|
if (filters && filters.userFilter)
|
||||||
this.model.userFilter = filters.userFilter;
|
this.model.userFilter = filters.userFilter;
|
||||||
|
|
||||||
if (!emptySearch)
|
if (!emptySearch)
|
||||||
this.addFilter(field, this.$inputsScope.searchProps[field]);
|
this.addFilter(field, this.$inputsScope.searchProps[field]);
|
||||||
else this.model.refresh();
|
else this.model.refresh();
|
||||||
|
|
|
@ -224,5 +224,7 @@
|
||||||
"The agency is already assigned to another autonomous": "La agencia ya está asignada a otro autónomo",
|
"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",
|
"date in the future": "Fecha en el futuro",
|
||||||
"reference duplicated": "Referencia duplicada",
|
"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"
|
||||||
}
|
}
|
|
@ -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];
|
||||||
|
};
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -31,6 +31,7 @@ module.exports = Self => {
|
||||||
require('../methods/client/createReceipt')(Self);
|
require('../methods/client/createReceipt')(Self);
|
||||||
require('../methods/client/updatePortfolio')(Self);
|
require('../methods/client/updatePortfolio')(Self);
|
||||||
require('../methods/client/checkDuplicated')(Self);
|
require('../methods/client/checkDuplicated')(Self);
|
||||||
|
require('../methods/client/extendedListFilter')(Self);
|
||||||
|
|
||||||
// Validations
|
// Validations
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
@ -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
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
Mailing: Env. emails
|
||||||
|
Sage tr. type: Tipo tr. sage
|
||||||
|
Yes: Sí
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import "variables";
|
||||||
|
|
||||||
|
vn-chip.success,
|
||||||
|
vn-chip.alert {
|
||||||
|
color: $color-font-bg
|
||||||
|
}
|
|
@ -47,3 +47,4 @@ import './consumption-search-panel';
|
||||||
import './defaulter';
|
import './defaulter';
|
||||||
import './notification';
|
import './notification';
|
||||||
import './unpaid';
|
import './unpaid';
|
||||||
|
import './extended-list';
|
||||||
|
|
|
@ -33,6 +33,7 @@ Search client by id or name: Buscar clientes por identificador o nombre
|
||||||
# Sections
|
# Sections
|
||||||
|
|
||||||
Clients: Clientes
|
Clients: Clientes
|
||||||
|
Extended list: Listado extendido
|
||||||
Defaulter: Morosos
|
Defaulter: Morosos
|
||||||
New client: Nuevo cliente
|
New client: Nuevo cliente
|
||||||
Fiscal data: Datos fiscales
|
Fiscal data: Datos fiscales
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
"menus": {
|
"menus": {
|
||||||
"main": [
|
"main": [
|
||||||
{"state": "client.index", "icon": "person"},
|
{"state": "client.index", "icon": "person"},
|
||||||
|
{"state": "client.extendedList", "icon": "person"},
|
||||||
{"state": "client.notification", "icon": "campaign"},
|
{"state": "client.notification", "icon": "campaign"},
|
||||||
{"state": "client.defaulter", "icon": "icon-defaulter"}
|
{"state": "client.defaulter", "icon": "icon-defaulter"}
|
||||||
],
|
],
|
||||||
|
@ -381,6 +382,12 @@
|
||||||
"component": "vn-client-unpaid",
|
"component": "vn-client-unpaid",
|
||||||
"acl": ["administrative"],
|
"acl": ["administrative"],
|
||||||
"description": "Unpaid"
|
"description": "Unpaid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/extended-list",
|
||||||
|
"state": "client.extendedList",
|
||||||
|
"component": "vn-client-extended-list",
|
||||||
|
"description": "Extended list"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Client id: Id cliente
|
Client id: Id cliente
|
||||||
Tax number: NIF/CIF
|
Tax number: NIF/CIF
|
||||||
Name: Nombre
|
Name: Nombre
|
||||||
Social name: Razon social
|
Social name: Razón social
|
||||||
Town/City: Ciudad
|
Town/City: Ciudad
|
||||||
Postcode: Código postal
|
Postcode: Código postal
|
||||||
Email: E-mail
|
Email: E-mail
|
||||||
|
|
|
@ -53,7 +53,7 @@ export default class Controller extends Section {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'shippedDate',
|
field: 'shippedDate',
|
||||||
searchable: false
|
datepicker: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'theoreticalHour',
|
field: 'theoreticalHour',
|
||||||
|
|
Loading…
Reference in New Issue