Bug 40876: Apply 'exact' to all attributes when column filtering

If you select an option in a column filter, the "exact" behaviour will
only apply to the first attribute mapped to this column

eg. on the patron search we have "Libraries" mapped to library.name and
library.library_id. If you select Centerville/CPL, the generated q
parameter will be {name: CPL, id: {like: "%CPL%"}}

This is caused by:
    if (value.match(/^\^(.*)\$$/)) {
        value = value.replace(/^\^/, "").replace(/\$$/, "");
On the second parameter we have lost the ^$ markers

Moving it out of the loop fixes the problem (and does not impact the
other behaviours: we search the same value for all the attributes

Test plan:
Create a library with code=CPLL and use if for a patron
Go to http://localhost:8081/cgi-bin/koha/members/members-home.pl
Click search
Select "Centerville" in the "Libraries" column filter
=> Without this patch you get the patrons from CPL and CPLL
=> With this patch applied only patrons from CPL are listed

Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
(cherry picked from commit 9e437a4089)
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
This commit is contained in:
Jonathan Druart 2025-09-25 12:02:12 +02:00 committed by Paul Derscheid
parent cfa17e1bc5
commit 66e8f837dd
No known key found for this signature in database
GPG Key ID: C25F9FD44CED595B
2 changed files with 100 additions and 8 deletions

View File

@ -493,18 +493,20 @@ function _dt_default_ajax(params) {
function build_query(col, value) {
var parts = [];
var attributes = col.data.split(":");
let criteria = options.criteria;
if (value.match(/^\^(.*)\$$/)) {
value = value.replace(/^\^/, "").replace(/\$$/, "");
criteria = "exact";
} else {
// escape SQL LIKE special characters %
value = value.replace(/(\%|\\)/g, "\\$1");
}
for (var i = 0; i < attributes.length; i++) {
var part = {};
var attr = attributes[i];
let default_build = true;
let criteria = options.criteria;
if (value.match(/^\^(.*)\$$/)) {
value = value.replace(/^\^/, "").replace(/\$$/, "");
criteria = "exact";
} else {
// escape SQL LIKE special characters %
value = value.replace(/(\%|\\)/g, "\\$1");
}
let built_value;
if (col.type == "date") {

View File

@ -180,4 +180,94 @@ describe("Filters", () => {
).should("have.value", null);
});
});
describe("Exact search for all attributes", () => {
// In this case the library column is bind to library.name and library.library_id
it("From the form", () => {
cy.task("buildSampleObjects", {
object: "patron",
count: RESTdefaultPageSize,
values: {},
}).then(patrons => {
// Needs more properties to not explode
// account_balace: balance_str.escapeHtml(...).format_price is not a function
patrons = patrons.map(p => ({ ...p, account_balance: 0 }));
cy.intercept("GET", "/api/v1/patrons*", {
statusCode: 200,
body: patrons,
headers: {
"X-Base-Total-Count": baseTotalCount,
"X-Total-Count": baseTotalCount,
},
}).as("searchPatrons");
cy.visit("/cgi-bin/koha/members/members-home.pl");
cy.window().then(win => {
win.categories_map = patrons.reduce((map, p) => {
map[p.category_id.toLowerCase()] = p.category_id;
return map;
}, {});
});
cy.get("form.patron_search_form .branchcode_filter").select(
"CPL"
);
cy.get("form.patron_search_form input[type='submit']").click();
cy.wait("@searchPatrons").then(interception => {
const q = interception.request.query.q;
expect(q).to.equal(
'[{"library.name":"CPL"},{"me.library_id":"CPL"}]'
);
});
});
});
it("From the column filter", () => {
cy.task("buildSampleObjects", {
object: "patron",
count: RESTdefaultPageSize,
values: {},
}).then(patrons => {
// Needs more properties to not explode
// account_balace: balance_str.escapeHtml(...).format_price is not a function
patrons = patrons.map(p => ({ ...p, account_balance: 0 }));
cy.intercept("GET", "/api/v1/patrons*", {
statusCode: 200,
body: patrons,
headers: {
"X-Base-Total-Count": baseTotalCount,
"X-Total-Count": baseTotalCount,
},
}).as("searchPatrons");
cy.visit("/cgi-bin/koha/members/members-home.pl");
cy.window().then(win => {
win.categories_map = patrons.reduce((map, p) => {
map[p.category_id.toLowerCase()] = p.category_id;
return map;
}, {});
});
cy.get("form.patron_search_form input[type='submit']").click();
cy.wait("@searchPatrons");
cy.get(
`#${table_id} thead tr th[data-filter='libraries'] select`
).select("^CPL$");
cy.wait("@searchPatrons").then(interception => {
const q = interception.request.query.q;
expect(q).to.equal(
'[{"library.name":"CPL"},{"me.library_id":"CPL"}]'
);
});
});
});
});
});