3852-client.search-panel2 #1111
|
@ -0,0 +1,54 @@
|
|||
DROP PROCEDURE IF EXISTS `vn`.`zone_getPostalCode`;
|
||||
|
||||
DELIMITER $$
|
||||
$$
|
||||
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`zone_getPostalCode`(vSelf INT)
|
||||
BEGIN
|
||||
/**
|
||||
* Devuelve los códigos postales incluidos en una zona
|
||||
*/
|
||||
DECLARE vGeoFk INT DEFAULT NULL;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp.zoneNodes;
|
||||
CREATE TEMPORARY TABLE tmp.zoneNodes (
|
||||
geoFk INT,
|
||||
name VARCHAR(100),
|
||||
parentFk INT,
|
||||
sons INT,
|
||||
isChecked BOOL DEFAULT 0,
|
||||
zoneFk INT,
|
||||
PRIMARY KEY zoneNodesPk (zoneFk, geoFk),
|
||||
INDEX(geoFk))
|
||||
ENGINE = MEMORY;
|
||||
|
||||
CALL zone_getLeaves2(vSelf, NULL , NULL);
|
||||
|
||||
UPDATE tmp.zoneNodes zn
|
||||
SET isChecked = 0
|
||||
WHERE parentFk IS NULL;
|
||||
|
||||
myLoop: LOOP
|
||||
SET vGeoFk = NULL;
|
||||
SELECT geoFk INTO vGeoFk
|
||||
FROM tmp.zoneNodes zn
|
||||
WHERE NOT isChecked
|
||||
LIMIT 1;
|
||||
|
||||
CALL zone_getLeaves2(vSelf, vGeoFk, NULL);
|
||||
UPDATE tmp.zoneNodes
|
||||
SET isChecked = TRUE
|
||||
WHERE geoFk = vGeoFk;
|
||||
|
||||
IF vGeoFk IS NULL THEN
|
||||
LEAVE myLoop;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
DELETE FROM tmp.zoneNodes
|
||||
WHERE sons > 0;
|
||||
|
||||
SELECT zn.geoFk, zn.name
|
||||
FROM tmp.zoneNodes zn
|
||||
JOIN zone z ON z.id = zn.zoneFk;
|
||||
END$$
|
||||
DELIMITER ;
|
|
@ -393,7 +393,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr
|
|||
(126, 'Many places', 'address 26', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 0),
|
||||
(127, 'Your pocket', 'address 27', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 0),
|
||||
(128, 'Cerebro', 'address 28', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 0),
|
||||
(129, 'Luke Cages Bar', 'address 29', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 0),
|
||||
(129, 'Luke Cages Bar', 'address 29', 'Gotham', 'EC170150', 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 0),
|
||||
(130, 'Non valid address', 'address 30', 'Gotham', 46460, 1, 1111111111, 222222222, 0, 1101, 2, NULL, NULL, 0, 0);
|
||||
|
||||
INSERT INTO `vn`.`address`( `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `isActive`, `clientFk`, `agencyModeFk`, `isDefaultAddress`)
|
||||
|
@ -2047,6 +2047,7 @@ INSERT INTO `vn`.`zoneIncluded` (`zoneFk`, `geoFk`, `isIncluded`)
|
|||
(8, 4, 0),
|
||||
(8, 5, 0),
|
||||
(8, 1, 1),
|
||||
(9, 7, 1),
|
||||
(10, 14, 1);
|
||||
|
||||
INSERT INTO `vn`.`zoneEvent`(`zoneFk`, `type`, `dated`)
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
|
||||
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('filter', {
|
||||
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',
|
||||
},
|
||||
{
|
||||
arg: 'zoneFk',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
returns: {
|
||||
type: ['object'],
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
path: `/filter`,
|
||||
verb: 'GET'
|
||||
}
|
||||
});
|
||||
|
||||
Self.filter = async(ctx, filter, options) => {
|
||||
const conn = Self.dataSource.connector;
|
||||
const myOptions = {};
|
||||
const postalCode = [];
|
||||
const args = ctx.args;
|
||||
|
||||
if (typeof options == 'object')
|
||||
Object.assign(myOptions, options);
|
||||
|
||||
if (args.zoneFk) {
|
||||
query = `CALL vn.zone_getPostalCode(?)`;
|
||||
const [geos] = await Self.rawSql(query, [args.zoneFk]);
|
||||
for (let geo of geos)
|
||||
postalCode.push(geo.name);
|
||||
}
|
||||
|
||||
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};
|
||||
case 'zoneFk':
|
||||
param = 'a.postalCode';
|
||||
return {[param]: {inq: postalCode}};
|
||||
}
|
||||
});
|
||||
|
||||
filter = mergeFilters(filter, {where});
|
||||
|
||||
const stmts = [];
|
||||
const stmt = new ParameterizedSQL(
|
||||
`SELECT
|
||||
DISTINCT c.id,
|
||||
c.name,
|
||||
c.fi,
|
||||
c.socialName,
|
||||
c.phone,
|
||||
c.city,
|
||||
c.postcode,
|
||||
c.email,
|
||||
c.isActive,
|
||||
c.isFreezed,
|
||||
p.id AS provinceFk,
|
||||
p.name AS province,
|
||||
u.id AS salesPersonFk,
|
||||
u.name AS salesPerson
|
||||
FROM client c
|
||||
LEFT JOIN account.user u ON u.id = c.salesPersonFk
|
||||
LEFT JOIN province p ON p.id = c.provinceFk
|
||||
JOIN vn.address a ON a.clientFk = c.id
|
||||
`
|
||||
);
|
||||
|
||||
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,199 @@
|
|||
const {models} = require('vn-loopback/server/server');
|
||||
|
||||
describe('client filter()', () => {
|
||||
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: '8'};
|
||||
const result = await models.Client.filter(ctx, filter, options);
|
||||
|
||||
expect(result.length).toEqual(8);
|
||||
|
||||
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.filter(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.filter(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.filter(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.filter(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.filter(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: 'Gotham'}};
|
||||
const filter = {};
|
||||
const result = await models.Client.filter(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('gotham');
|
||||
|
||||
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.filter(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;
|
||||
}
|
||||
});
|
||||
|
||||
it('should return the clients matching the "zoneFk" argument', async() => {
|
||||
const tx = await models.Client.beginTransaction({});
|
||||
|
||||
try {
|
||||
const options = {transaction: tx};
|
||||
|
||||
const ctx = {req: {accessToken: {userId: 1}}, args: {zoneFk: 9}};
|
||||
const filter = {};
|
||||
const result = await models.Client.filter(ctx, filter, options);
|
||||
|
||||
expect(result.length).toBe(1);
|
||||
|
||||
await tx.rollback();
|
||||
} catch (e) {
|
||||
await tx.rollback();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
|
@ -47,4 +47,5 @@ module.exports = Self => {
|
|||
require('../methods/client/incotermsAuthorizationHtml')(Self);
|
||||
require('../methods/client/incotermsAuthorizationEmail')(Self);
|
||||
require('../methods/client/consumptionSendQueued')(Self);
|
||||
require('../methods/client/filter')(Self);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<vn-crud-model
|
||||
vn-id="model"
|
||||
url="Clients"
|
||||
url="Clients/filter"
|
||||
order="id DESC"
|
||||
limit="8"
|
||||
data="clients">
|
||||
|
@ -10,8 +10,7 @@
|
|||
vn-focus
|
||||
panel="vn-client-search-panel"
|
||||
info="Search client by id or name"
|
||||
model="model"
|
||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||
model="model">
|
||||
</vn-searchbar>
|
||||
</vn-portal>
|
||||
<vn-portal slot="menu">
|
||||
|
|
|
@ -2,32 +2,6 @@ import ngModule from '../module';
|
|||
import ModuleMain from 'salix/components/module-main';
|
||||
|
||||
export default class Client extends ModuleMain {
|
||||
exprBuilder(param, value) {
|
||||
switch (param) {
|
||||
case 'search':
|
||||
return /^\d+$/.test(value)
|
||||
? {id: value}
|
||||
: {or: [{name: {like: `%${value}%`}}, {socialName: {like: `%${value}%`}}]};
|
||||
case 'phone':
|
||||
return {
|
||||
or: [
|
||||
{phone: value},
|
||||
{mobile: value}
|
||||
]
|
||||
};
|
||||
case 'name':
|
||||
case 'socialName':
|
||||
case 'city':
|
||||
case 'email':
|
||||
return {[param]: {like: `%${value}%`}};
|
||||
case 'id':
|
||||
case 'fi':
|
||||
case 'postcode':
|
||||
case 'provinceFk':
|
||||
case 'salesPersonFk':
|
||||
return {[param]: value};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngModule.vnComponent('vnClient', {
|
||||
|
|
|
@ -58,6 +58,13 @@
|
|||
value-field="id"
|
||||
label="Province">
|
||||
</vn-autocomplete>
|
||||
<vn-autocomplete
|
||||
ng-model="filter.zoneFk"
|
||||
url="Zones"
|
||||
show-field="name"
|
||||
value-field="id"
|
||||
label="Zone">
|
||||
</vn-autocomplete>
|
||||
</vn-horizontal>
|
||||
<vn-horizontal>
|
||||
<vn-textfield
|
||||
|
|
Loading…
Reference in New Issue