From 319ca680443c4c729d3820fa5cc58fd606be850f Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Thu, 25 Sep 2025 12:02:12 +0200 Subject: [PATCH] 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 Signed-off-by: Paul Derscheid Signed-off-by: Lucas Gass (cherry picked from commit 9e437a4089b72e2c9a2fa4ae3de57183ff2c7279) Signed-off-by: Paul Derscheid (cherry picked from commit 66e8f837dd0bb56082bb61e50e4651de9f90633f) Signed-off-by: Baptiste Wojtkowski --- koha-tmpl/intranet-tmpl/prog/js/datatables.js | 12 +-- .../KohaTable/PatronSearch_spec.ts | 90 +++++++++++++++++++ 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/js/datatables.js b/koha-tmpl/intranet-tmpl/prog/js/datatables.js index 1a5d49c11c..693e9cad54 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/datatables.js +++ b/koha-tmpl/intranet-tmpl/prog/js/datatables.js @@ -455,11 +455,8 @@ function _dt_default_ajax (params){ function build_query(col, value){ var parts = []; - var attributes = col.data.split(':'); - for (var i=0;i { ).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"}]' + ); + }); + }); + }); + }); });