Proformas => búsquedas rápidas
This commit is contained in:
parent
26e2ee59bd
commit
e8f40b1495
@ -0,0 +1,70 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { Criteria, CriteriaToSequelizeConverter } from "@repo/rdx-criteria/server";
|
||||
import { Op, Sequelize, type WhereOptions } from "sequelize";
|
||||
|
||||
import { buildProformaQuickSearchWhere } from "./proforma-quick-search.ts";
|
||||
|
||||
function buildWhereSql(quickSearch: string): string {
|
||||
const database = new Sequelize("mysql://root:pass@localhost:3306/erp_test", {
|
||||
logging: false,
|
||||
});
|
||||
const criteria = Criteria.fromPrimitives([], "proforma_date", "DESC", 25, 0, quickSearch);
|
||||
const criteriaConverter = new CriteriaToSequelizeConverter();
|
||||
const criteriaQuery = criteriaConverter.convert(criteria, {
|
||||
searchableFields: ["proforma_reference", "reference", "description"],
|
||||
fullTextTableAlias: "ProformaModel",
|
||||
enableFullText: true,
|
||||
database,
|
||||
strictMode: true,
|
||||
});
|
||||
const where = {
|
||||
[Op.and]: [
|
||||
{ company_id: "company-1" },
|
||||
buildProformaQuickSearchWhere({
|
||||
quickSearch,
|
||||
fullTextWhere: criteriaQuery.where as WhereOptions,
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
return database.getQueryInterface().queryGenerator.whereQuery(where);
|
||||
}
|
||||
|
||||
test("genera fallback LIKE para referencia documental con guion", () => {
|
||||
const sql = buildWhereSql("PF-0066");
|
||||
|
||||
assert.match(sql, /MATCH\(`ProformaModel`\.`proforma_reference`, `ProformaModel`\.`reference`, `ProformaModel`\.`description`\)/);
|
||||
assert.match(sql, /\+PF\* \+0066\*/);
|
||||
assert.match(sql, /`proforma_reference` LIKE '%PF-0066%'/);
|
||||
assert.match(sql, /`proforma_number` LIKE '%PF-0066%'/);
|
||||
});
|
||||
|
||||
test("incluye el TIN del current_customer en quick search", () => {
|
||||
const sql = buildWhereSql("B12345678");
|
||||
|
||||
assert.match(sql, /`current_customer`\.`tin` LIKE '%B12345678%'/);
|
||||
});
|
||||
|
||||
test("incluye nombre y trade name del current_customer en quick search", () => {
|
||||
const sql = buildWhereSql("Acme");
|
||||
|
||||
assert.match(sql, /`current_customer`\.`name` LIKE '%Acme%'/);
|
||||
assert.match(sql, /`current_customer`\.`trade_name` LIKE '%Acme%'/);
|
||||
});
|
||||
|
||||
test("acepta operadores reservados de MariaDB sin generar SQL inválida", () => {
|
||||
const sql = buildWhereSql('+"PF-0066"(test)');
|
||||
|
||||
assert.equal(typeof sql, "string");
|
||||
assert.ok(sql.length > 0);
|
||||
assert.match(sql, /\+PF\* \+0066\* \+test\*/);
|
||||
assert.match(sql, /LIKE '%\+"PF-0066"\(test\)%'/);
|
||||
});
|
||||
|
||||
test("mantiene el aislamiento por empresa en la cláusula final", () => {
|
||||
const sql = buildWhereSql("PF-0066");
|
||||
|
||||
assert.match(sql, /`company_id` = 'company-1'/);
|
||||
});
|
||||
@ -0,0 +1,55 @@
|
||||
import type { ProformaModel } from "../../../../common";
|
||||
|
||||
import {
|
||||
Op,
|
||||
Sequelize,
|
||||
type InferAttributes,
|
||||
type WhereOptions,
|
||||
} from "sequelize";
|
||||
|
||||
type BuildProformaQuickSearchWhereParams = {
|
||||
quickSearch?: string;
|
||||
fullTextWhere?: WhereOptions<InferAttributes<ProformaModel>>;
|
||||
customerAssociationAlias?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Añade condiciones explícitas para campos que no quedan bien cubiertos por FULLTEXT.
|
||||
*
|
||||
* Mantiene el MATCH ... AGAINST existente para las columnas indexadas, pero agrega
|
||||
* búsquedas `LIKE` sobre la referencia visible y el recipient del listado.
|
||||
*/
|
||||
export function buildProformaQuickSearchWhere(
|
||||
params: BuildProformaQuickSearchWhereParams
|
||||
): WhereOptions<InferAttributes<ProformaModel>> | undefined {
|
||||
const term = params.quickSearch?.trim();
|
||||
|
||||
if (!term) {
|
||||
return params.fullTextWhere;
|
||||
}
|
||||
|
||||
const likePattern = `%${term}%`;
|
||||
const customerAssociationAlias = params.customerAssociationAlias ?? "current_customer";
|
||||
|
||||
const clauses: WhereOptions<InferAttributes<ProformaModel>>[] = [
|
||||
{ proforma_reference: { [Op.like]: likePattern } },
|
||||
{ proforma_number: { [Op.like]: likePattern } },
|
||||
{ reference: { [Op.like]: likePattern } },
|
||||
{ description: { [Op.like]: likePattern } },
|
||||
Sequelize.where(Sequelize.col(`${customerAssociationAlias}.name`), {
|
||||
[Op.like]: likePattern,
|
||||
}) as unknown as WhereOptions<InferAttributes<ProformaModel>>,
|
||||
Sequelize.where(Sequelize.col(`${customerAssociationAlias}.trade_name`), {
|
||||
[Op.like]: likePattern,
|
||||
}) as unknown as WhereOptions<InferAttributes<ProformaModel>>,
|
||||
Sequelize.where(Sequelize.col(`${customerAssociationAlias}.tin`), {
|
||||
[Op.like]: likePattern,
|
||||
}) as unknown as WhereOptions<InferAttributes<ProformaModel>>,
|
||||
];
|
||||
|
||||
if (params.fullTextWhere) {
|
||||
clauses.unshift(params.fullTextWhere);
|
||||
}
|
||||
|
||||
return { [Op.or]: clauses };
|
||||
}
|
||||
@ -41,6 +41,7 @@ import {
|
||||
ProformaTaxModel,
|
||||
} from "../../../../common";
|
||||
import type { SequelizeProformaV2DomainMapper, SequelizeProformaV2SummaryMapper } from "../mappers";
|
||||
import { buildProformaQuickSearchWhere } from "./proforma-quick-search";
|
||||
|
||||
export class SequelizeProformaRepositoryV2
|
||||
extends SequelizeRepository<Proforma>
|
||||
@ -406,6 +407,10 @@ export class SequelizeProformaRepositoryV2
|
||||
database: this.database,
|
||||
strictMode: true,
|
||||
});
|
||||
const quickSearchWhere = buildProformaQuickSearchWhere({
|
||||
quickSearch: criteria.quickSearch,
|
||||
fullTextWhere: criteriaQuery.where as WhereOptions<InferAttributes<ProformaModel>>,
|
||||
});
|
||||
|
||||
/**
|
||||
* Restricciones propias del repositorio.
|
||||
@ -431,7 +436,7 @@ export class SequelizeProformaRepositoryV2
|
||||
baseWhere,
|
||||
scopeWhere,
|
||||
...(options.where ? [options.where] : []),
|
||||
...(criteriaQuery.where ? [criteriaQuery.where] : []),
|
||||
...(quickSearchWhere ? [quickSearchWhere] : []),
|
||||
],
|
||||
};
|
||||
|
||||
@ -587,19 +592,31 @@ export class SequelizeProformaRepositoryV2
|
||||
database: this.database,
|
||||
strictMode: true,
|
||||
});
|
||||
const quickSearchWhere = buildProformaQuickSearchWhere({
|
||||
quickSearch: criteria.quickSearch,
|
||||
fullTextWhere: criteriaQuery.where as WhereOptions<InferAttributes<ProformaModel>>,
|
||||
});
|
||||
|
||||
const where: WhereOptions<InferAttributes<ProformaModel>> = {
|
||||
[Op.and]: [
|
||||
{ company_id: companyId.toString() },
|
||||
this.buildScopeWhere(scope),
|
||||
...(options.where ? [options.where] : []),
|
||||
...(criteriaQuery.where ? [criteriaQuery.where] : []),
|
||||
...(quickSearchWhere ? [quickSearchWhere] : []),
|
||||
],
|
||||
};
|
||||
|
||||
const rows = (await ProformaModel.findAll({
|
||||
attributes: ["status", [fn("COUNT", col("ProformaModel.id")), "count"]],
|
||||
group: ["status"],
|
||||
include: [
|
||||
{
|
||||
model: this.database.models.CustomerModel,
|
||||
as: "current_customer",
|
||||
required: false,
|
||||
attributes: [],
|
||||
},
|
||||
],
|
||||
paranoid: scope !== "deleted",
|
||||
raw: true,
|
||||
transaction,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user