Commit Graph

62029 Commits

Author SHA1 Message Date
Martin Renvoize 53d498e145
Bug 40445: (QA follow-up) Show cashup summary modal when completing from registers.pl
When a single-register cashup is completed from the Cash summary page
(registers.pl), pos/register.pl redirected back to registers.pl without
the '#cashup-<id>' hash, so the hash-watcher in registers.tt never
triggered the cashup summary modal. Completing the same cashup from the
Transaction history page (register.pl) worked because that branch
already appended the hash.

Append '#cashup-<id>' to the registers.pl redirect URL too, so both the
'Record cashup' (quick) and 'Complete cashup' (two-phase) actions on the
Cash summary page open the summary modal on return.

Test plan:
1. Have at least one register with cash transactions.
2. From Cash summary (registers.pl), click 'Record cashup' on a register
   with no cashup in progress; complete it.
   => On return, cashup summary modal opens automatically.
3. Start a two-phase cashup; from Cash summary click 'Complete cashup'
   and confirm.
   => On return, cashup summary modal opens automatically.
4. From Transaction history (register.pl), complete a cashup as before.
   => Modal still opens (regression check).

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:41 +00:00
Martin Renvoize c51f4b7cca
Bug 40445: (QA follow-up) Add missing [% USE raw %] in cashup_summary.inc
The cashup summary modal include uses the \$raw filter on
Koha.CSPNonce, but never declared [% USE raw %]. The QA template
tester (which processes templates through a fresh Template::Toolkit
instance) reports "valid_template: filter not found" because the
\$raw plugin is not loaded.

Add [% USE raw %] at the top of the include, matching the convention
used by other includes that emit nonce attributes (e.g.
includes/str/cataloging_additem.inc).

Test plan:
1. Run qa-test-tools against the bug branch -- cashup_summary.inc
   passes valid_template.
2. Open the cashup summary modal -- the <style> tag still receives
   a CSP nonce and the embedded styles render correctly.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:40 +00:00
Martin Renvoize 7798332412
Bug 40445: (follow-up) Show actual cash removed alongside expected on discrepancy
The cashup summary modal labelled the per-payment-type cash row "Cash
removed from register", but the value came from the session
transactions (with CASHUP_SURPLUS/DEFICIT lines excluded), so it
represented the *expected* cash, not the amount actually counted at
cashup. When a deficit or surplus existed, the reconciliation row
appeared as the difference between two figures the user could not
both see.

Render two rows when surplus_total or deficit_total is set on a
completed cashup: "Expected cash total" (from total_grouped) and
"Cash removed from register" (from cash_register_actions.amount,
already exposed as data.amount). The reconciliation row below now
visibly equals the difference. Balanced cashups and the in-progress
preview keep the original single-row display, and the negative-cashup
branch flips the labels to "Expected cash to add to register" /
"Cash added to register".

Test plan:
1. Configure a cash register and record a few cash payments.
2. Trigger a cashup, enter the exact expected amount; open the
   summary modal -- a single "Cash removed from register" row is
   shown matching the expected total.
3. Repeat with an amount lower than expected -- "Expected cash
   total" and "Cash removed from register" both appear, and the
   "Cashup deficit" row is the difference.
4. Repeat with an amount higher than expected -- the actual row
   exceeds the expected row and "Cashup surplus" matches the gap.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:39 +00:00
Martin Renvoize 86d034e628
Bug 40445: (follow-up) Clarify cashup summary cash row wording and sign
A cashup is the process of removing cash from the till to send it
to the bank, so labelling the headline cash line "Cash added to
register" was confusing. Re-word it to "Cash removed from register"
in the typical case and "Cash added to register" in the edge case
where the cashier has had to top up the till to cover refunds.

While doing this, align the sign convention of total_grouped with
that of the overall total field on the summary: positive means cash
collected (i.e. removed from the register at cashup time), negative
means cash had to be added back into the register. Previously
total_grouped used the raw accounting sign (credits negative, debits
positive), so a typical $2 cash session would display "Total: 2.00"
and "Cash added to register: -2.00", which was internally
inconsistent. The conditions on the cash and other-payment-type rows
in cashup_modal.js are swapped to match the new convention.

Test plan:
1. Apply the patch and rebuild JS (yarn js:build) inside KTD.
2. As a staff user with permission to manage cash registers, take
   payments via several payment types (some cash, some not).
3. Trigger a cashup and view the cashup summary modal.
4. Confirm the "Total" line and the per-payment-type lines all
   display as positive amounts in the typical "money was collected"
   scenario, with the cash row labelled "Cash removed from
   register".
5. Issue some refunds large enough to exceed cash collected, then
   cash up again.
6. Confirm the cash row is labelled "Cash added to register" with a
   negative amount and the per-non-cash-type rows are labelled
   "{type} to add" where applicable.
7. Run prove t/db_dependent/Koha/Cash/Register/Cashup.t and prove
   t/db_dependent/Koha/Cash/Register.t inside KTD; both should
   pass.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:38 +00:00
Martin Renvoize 463b92df52
Bug 40445: (QA follow-up) Add nonce, POD, and test isolation
This patch resolves three QA findings on the cashup reconciliation
work:

* Add a CSP nonce to the inline <style> block in cashup_summary.inc
  so it is not flagged by the forbidden_patterns check.
* Document the private _get_session_end method on
  Koha::Cash::Register::Cashup so pod_coverage passes.
* Mock CashupReconciliationNoteRequired to 0 in
  t/db_dependent/Koha/Cash/Register/Cashup.t so the test does not
  depend on the site preference value (it previously failed when
  the syspref was set to "Require" because surpluses and deficits
  are created without a reconciliation note in this test).

Test plan:
1. Apply the patch.
2. Set CashupReconciliationNoteRequired to "Require".
3. Run prove t/db_dependent/Koha/Cash/Register/Cashup.t -- it should
   pass.
4. Set CashupReconciliationNoteRequired to "Don't require" and
   re-run -- it should still pass.
5. Run koha-qa.pl on the bug branch -- pod_coverage on
   Koha/Cash/Register/Cashup.pm and forbidden_patterns on
   cashup_summary.inc should now pass.

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:37 +00:00
Martin Renvoize b7a68f1f49
Bug 40445: Add configuration options and UI enhancements
This patch adds configuration options for cashup reconciliation and
enhances the user interface with preview functionality and improved
interaction patterns.

Configuration Options:

1. CashupReconciliationNoteRequired (System Preference):
   - Optional validation requiring reconciliation notes when discrepancies exist
   - Ensures documentation of surplus/deficit causes when needed
   - Can be disabled for less stringent workflows
   - Applied at cashup completion time

2. CashupReconciliationNoteAuthorisedValue (System Preference):
   - Allows use of authorized value dropdown for reconciliation notes
   - Provides standardized note options (e.g., "Till count error", "Cash removed")
   - Falls back to free text textarea when not configured
   - Improves consistency and reporting capabilities

UI Enhancements:

1. Cashup Summary Preview:
   - Preview cashup summary for in-progress cashups
   - Shows current transaction totals before completion
   - Clearly indicates preview vs. final summary status
   - Helps staff verify amounts before committing

2. Improved User Experience:
   - Conditional note fields based on configuration
   - Authorized value dropdowns when configured
   - Clear validation messages
   - Responsive UI updates

Backend changes:
- Enhanced Koha::REST::V1::CashRegisters::Cashups with preview support
- Database atomicupdate script for system preferences
- Validation logic for required notes

Frontend changes:
- Preview modal functionality in cashup_modal.js
- Conditional rendering of note input fields
- Authorized value dropdown integration
- Enhanced summary display logic

Test plan:
1. Apply patches and run database update
2. Configure CashupReconciliationNoteRequired:
   - Enable preference
   - Perform cashup with discrepancy without note - should fail
   - Add note and retry - should succeed
   - Disable preference - verify note optional
3. Configure CashupReconciliationNoteAuthorisedValue:
   - Create AV category (e.g., CASHUP_NOTE) with values
   - Set preference to category name
   - Verify dropdown appears in cashup modal
   - Clear preference - verify textarea shown
4. Test cashup preview:
   - Start cashup on register
   - Click "Preview cashup summary"
   - Verify current totals displayed
   - Add transaction and re-preview - verify updated
   - Complete cashup - verify final summary

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:36 +00:00
Martin Renvoize 9db47a47c1
Bug 40445: Implement optional two-phase cashup workflow
This patch implements an optional two-phase cashup workflow for point
of sale operations, along with improved error handling and validation.

Two-Phase Cashup Workflow:
- Staff can initiate cashup (CASHUP_START) and complete later
- Prevents new transactions on registers with cashups in progress
- Tracks cashup sessions with start/end timestamps
- Supports both quick cashup (immediate) and staged cashup workflows
- Updates batch cashup workflow operations across multiple registers

Error Handling & Validation:
- Centralized exception handling for cashup operations
- Clear validation messages for missing parameters
- Prevents cashup when no cash transactions exist
- Improved handling of zero/negative amounts
- Informative error messages for user guidance

Negative Amount Support:
- Supports negative cashup amounts for cashup deficits
- Automatic detection when actual cash is less than expected amount
- Creates appropriate CASHUP_DEFICIT records
- UI handles both positive and negative reconciliation amounts
- Updated calculations and display logic throughout

Modal Refactoring:
- Eliminates code duplication between register.tt and registers.tt
- Centralized cashup modal functionality in cashup_modals.js
- Shared confirm_cashup.inc and trigger_cashup.inc templates
- Consistent user experience across all cashup workflows
- Improved maintainability and code organization

Backend changes (Koha::Cash::Register):
- start_cashup(): Creates CASHUP_START action to begin session
- cashup_in_progress(): Detects active cashup sessions
- add_cashup(): Enhanced with two-phase completion support
- outstanding_accountlines(): Respects cashup session boundaries

Frontend changes:
- Interactive modals for starting and completing cashups
- Real-time calculation displays
- Support for reconciliation with actual amounts
- Batch operations UI for multiple registers
- Responsive error messaging and validation feedback

Test plan:
1. Apply patches and restart services
2. Run prove t/db_dependent/Koha/Cash/Register.t
3. Run prove t/db_dependent/Koha/Cash/Register/Cashup.t
4. Test single register workflows:
   - Start cashup, add transactions, verify blocked
   - Complete cashup with reconciliation amounts
   - Test with positive, negative, and zero amounts
5. Test multi-register workflows:
   - Select multiple registers on registers page
   - Perform batch cashup operations
   - Verify each register processes correctly
6. Test error conditions:
   - Attempt cashup with no transactions
   - Attempt cashup with invalid amounts
   - Verify clear error messages displayed

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:35 +00:00
Martin Renvoize 6d5c2f463f
Bug 40445: Add cashup reconciliation functionality
Implements cashup reconciliation allowing staff to record actual cash
amounts and track surplus/deficit discrepancies.

Backend changes:
- Enhanced add_cashup() to accept actual_amount and optional notes
- Automatic CASHUP_SURPLUS/DEFICIT accountline creation for discrepancies
- Transaction handling ensures atomicity of cashup and reconciliation
- outstanding_accountlines() excludes reconciliation entries

Frontend changes:
- Interactive modal requiring actual amount entry
- Real-time surplus/deficit calculation
- Conditional note field for discrepancies (1000 char limit)
- Enhanced summary modal displays reconciliation details
- Full audit trail with timestamps and manager links

Test plan:
1. Apply all patches and restart services
2. Run prove t/db_dependent/Koha/Cash/Register.t
3. Create test transactions on a register
4. Perform cashup with balanced amount - verify no reconciliation accountlines
5. Perform cashup with surplus - verify CASHUP_SURPLUS credit created
6. Perform cashup with deficit - verify CASHUP_DEFICIT debit created
7. Add notes to reconciliation - verify stored correctly
8. Start new cashup - verify previous reconciliation excluded from outstanding
9. View cashup summary - verify reconciliation displayed prominently

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:34 +00:00
Martin Renvoize 6a38111462
Bug 40445: Add CASHUP_SURPLUS and CASHUP_DEFICIT account types
This patch adds two new system account types to support cashup reconciliation:

- CASHUP_SURPLUS (credit type): Used when actual cash found exceeds expected amount
- CASHUP_DEFICIT (debit type): Used when actual cash found is less than expected amount

Both types are system-managed and cannot be manually added by staff. They will be
automatically created during cashup processes when discrepancies are detected.

Changes include:
- Atomicupdate script for existing installations
- Mandatory YAML files for fresh installations

Sponsored-by: OpenFifth <https://openfifth.co.uk/>
Signed-off-by: Jackie Usher <jackie.usher@westsussex.gov.uk>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:33 +00:00
Tomás Cohen Arazi 86d54c2c4a
Bug 42391: (follow-up) Add missing warning assertions for Object::store
The previous follow-up restored warn in Object::store but missed
several test files that trigger DB exceptions via ->store(). These
tests now fail Test::NoWarnings because the warning is emitted but
not expected.

This patch adds warning_like wrappers to 15 additional test files,
asserting that the expected DB exception warning is emitted.

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
   k$ prove t/db_dependent/api/v1/biblios.t \
       t/db_dependent/Koha/Holds.t \
       t/db_dependent/api/v1/return_claims.t \
       t/db_dependent/api/v1/libraries.t \
       t/db_dependent/Koha/Booking.t \
       t/db_dependent/api/v1/patrons.t \
       t/db_dependent/SIP/Message.t \
       t/db_dependent/Koha/Patron.t \
       t/db_dependent/Koha/Acquisition/Fund.t \
       t/db_dependent/api/v1/acquisitions_orders.t \
       t/db_dependent/Koha/ILL/Requests.t \
       t/db_dependent/Koha/Patron/Relationship.t \
       t/db_dependent/Koha/Hold.t \
       t/db_dependent/Circulation/Returns.t \
       t/db_dependent/api/v1/bookings.t
=> SUCCESS: Tests pass!
3. Sign off :-D

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-27 12:12:29 +00:00
Tomás Cohen Arazi 4189d42ee6
Bug 42391: (follow-up) Restore DB exception warnings in Object::store
Bug 42391 removed the warn from Koha::Object::store when it moved
exception translation to the exception_action hook. This lost useful
debugging information from the Plack error logs.

This patch restores the warning by adding a try/catch in store() that
stringifies and warns the exception before rethrowing. The warning now
contains the translated Koha exception message (e.g. 'Broken FK
constraint') rather than the raw DBI error, since translation happens
before the exception reaches store().

Note: delete() does not warn. Ideally the logging would live in the
ExceptionMapper itself (via an injected logger), covering all code paths
uniformly. That is a larger refactor tracked separately.

Tests that assert on these warnings are updated to match the new
message format.

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
   k$ prove t/db_dependent/Koha/Object.t \
       t/db_dependent/Koha/Objects.t \
       t/db_dependent/Koha/Schema/ExceptionMapper.t \
       t/db_dependent/Koha/Item.t \
       t/db_dependent/Koha/Suggestions.t \
       t/db_dependent/Koha/Account.t \
       t/db_dependent/Koha/ApiKey.t \
       t/db_dependent/Koha/Checkouts/ReturnClaim.t \
       t/db_dependent/Circulation/MarkIssueReturned.t \
       t/db_dependent/Suggestions.t \
       t/db_dependent/Biblio.t
=> SUCCESS: Tests pass!
3. Sign off :-D

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-21 15:40:23 +00:00
Pedro Amorim 880f33a4bb
Bug 41604: (follow-up) Wait for the page to be ready before searching in patrons_search.t
Bug 41604 made every page do a bit more work when it loads. This
Selenium test didn't wait for that work to finish before typing into
the search box, so sometimes it searched too early and got the wrong
results.

This adds a short wait after each page load so the test only searches
once the page is actually ready.

Introduced by: Bug 41604: Save user decision per column when visibility changes

Test plan:
prove t/db_dependent/selenium/patrons_search.t

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-21 15:40:19 +00:00
Pedro Amorim 037d38b00c
Bug 42772: Adjust yarn.lock
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:45:02 +00:00
Jonathan Druart f89e68c220
Bug 42772: Cypress.env => cy.env
Warning: The allowCypressEnv configuration option is enabled. This allows any browser code to read values from Cypress.env(). This is insecure and will be removed in a future major version.

1. Replace Cypress.env() calls with cy.env() (for sensitive values) or Cypress.expose() (for public configuration)
2. Set allowCypressEnv: false in your Cypress configuration to disable Cypress.env()

Learn more: https://on.cypress.io/cypress-env-migration

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:45:01 +00:00
Jonathan Druart 7b307694fa
Bug 42772: Remove experimentalStudio from config
The experimentalStudio option was removed in Cypress version 15.4.0.

Cypress Studio is now available for all users.

You can safely remove this option from your config.

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:45:00 +00:00
Jonathan Druart f357dd4103
Bug 42772: Upgrade Cypress to 15.16.0
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:59 +00:00
Kyle M Hall 54fc07f408
Bug 42391: (QA follow-up): Fix unit tests
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:58 +00:00
Kyle M Hall 22d988e8b4
Bug 42391: (QA follow-up): Clarify exception_action ref-passthrough comment
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:57 +00:00
Kyle M Hall 9c04c7bbb4
Bug 42391: (QA follow-up): Remove unused Try::Tiny from Koha::Item
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:56 +00:00
Tomás Cohen Arazi 62e003b88f
Bug 42391: (follow-up) Rename ExceptionTranslator to ExceptionMapper
The Koha::Schema::Util:: namespace adds unnecessary nesting for a
single-purpose class. This follow-up moves it to
Koha::Schema::ExceptionMapper, which sits alongside Koha::Schema
itself — a better fit given it's tightly coupled to the Schema's
exception_action hook.

Changes:
- Koha::Schema::Util::ExceptionTranslator → Koha::Schema::ExceptionMapper
- Update Koha::Schema to use the new module
- Move test file accordingly
- Remove now-empty Koha/Schema/Util/ directory

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:55 +00:00
Tomás Cohen Arazi 033cfd772c
Bug 42391: (follow-up) Add NotNull, NotInStorage and UnhandledDBError translations
Extend the ExceptionTranslator to handle two additional MySQL error
patterns:

- NOT NULL violations ('Column X cannot be null' and 'X doesn't have
  a default value') → Koha::Exceptions::Object::NotNull
- Not in database errors → Koha::Exceptions::Object::NotInStorage

Add Koha::Exceptions::Object::NotNull exception class with a property
field identifying the offending column.

Add Koha::Exceptions::Object::UnhandledDBError as a catch-all for DBI
errors that don't match any known pattern. This closes the gap where
unmatched MySQL errors could leak as DBIx::Class::Exception to callers
(e.g. templates that no longer catch that type).

The exception_action handler now distinguishes three cases:
- Koha exception objects (from txn_do re-throws): passed through via
  'die $msg' to preserve the original exception type. Without this,
  txn_do's rollback-and-rethrow would stringify the Koha exception
  and wrap it in UnhandledDBError, losing the specific type.
- DBI errors (prefixed 'DBI Exception:'): translated if possible,
  otherwise wrapped in UnhandledDBError.
- DBIC internal errors (e.g. 'Not in database'): translated if
  possible, otherwise thrown as DBIx::Class::Exception.

Note: NotInStorage translation works for update operations but not for
delete-on-detached-Row where DBIC bypasses the Schema's exception_action.
Those cases remain DBIx::Class::Exception.

Update tests:
- OAIServers.t, Z3950Servers.t: assert NotNull with property check
- Hold.t: assert NotNull, remove warn suppression hack
- Objects.t: update-not-in-storage cases assert NotInStorage
- Upload.t: delete-not-in-storage stays DBIx::Class::Exception

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:54 +00:00
Tomás Cohen Arazi face3c96d0
Bug 42391: (follow-up) Fix Objects.t case 3 to test FK deletion properly
The 'Delete a patron that cannot be deleted' test (case 3) was using a
stale $r_us object from case 2 (not in storage), so it was not actually
testing FK constraint deletion. The assertion on DBIx::Class::Exception
passed by accident.

This follow-up:
- Fetches a fresh Koha::Patron object for case 3
- Clears $e_us/$e_them before the new test
- Asserts Koha::Exceptions::Object::FKConstraintDeletion, which is what
  exception_action now produces for FK deletion errors

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:53 +00:00
Tomás Cohen Arazi 66b738f1eb
Bug 42391: Use exception_action to translate DBIC exceptions at the source
DBIx::Class provides an exception_action hook that intercepts every
throw_exception call before it becomes a DBIx::Class::Exception. By
installing this hook on Koha::Schema via a connection() override, all
database exceptions are automatically translated into Koha exceptions
at the source.

This eliminates the need for translate_exception catch blocks in every
caller:
- Remove try/catch from Koha::Object::store
- Remove try/catch from Koha::Object::delete
- Remove try/catch from Koha::Item::add_to_bundle
- Remove translate_exception convenience method from Koha::Schema
- Remove safe_do (already removed in previous follow-up)

ExceptionTranslator is refactored to work on plain error message strings
instead of DBIx::Class::Exception objects, since exception_action
receives the message before wrapping.

Unmatched errors fall through to DBIx::Class::Exception->throw() as
the default, preserving backward compatibility.

The warn $_->{msg} that was in Object::store is no longer needed since
exceptions are translated before they propagate. Tests that asserted on
that warning (via warning_like) are updated to use throws_ok directly.

Test plan:
1. Apply patch
2. Run:
   $ ktd --shell
  k$ prove t/db_dependent/Koha/Schema/Util/ExceptionTranslator.t
=> SUCCESS: Tests pass!
  k$ prove t/db_dependent/Koha/Object.t
=> SUCCESS: Tests pass!
  k$ prove t/db_dependent/Koha/Item.t
=> SUCCESS: Tests pass!
  k$ prove t/db_dependent/Koha/Patron/Modifications.t
=> SUCCESS: Tests pass!
3. Sign off :-D

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:52 +00:00
Owen Leonard 8bc90cbb5e
Bug 42440: Remove event attributes from labeled MARC detail template
This patch removes a redundant "onchange" event attribute from the
framework selection dropdown. This event is already registered in the JS
block.

The patch also does a little bit of cleanup to the JS: Combining two
<script> tags which don't need to be separate and moving a function
outside of the $(document).ready block.

To test, apply the patch and search for a bibliographic record in the
catalog.

- From the detail page, click the "Labeled MARC" tag in the sidebar (you
  may need to enable the "viewLabeledMARC" preference).
- Try changing the "With framework" selection. The page should reload
  with the selected framework.

Sponsored-by: Athens County Public Libraries
Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:51 +00:00
Andrew Fuerste Henry 22621e24cd
Bug 36762: [QA Follow-up]: Tidy branchtransfers.pl
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:50 +00:00
Emmi Takkinen ed992358bf
Bug 36762: Allow to ignore hold when creating a transfer
When one tries to transfer an item that could fill a pending hold
to a library that is not the holds pickup library, Koha gives only
a option to transfer item to pickup library. This patch adds
new option to ignore hold and transfer item to the library user
selected in transfer tool.

To test:
1. Log into Library A.
2. Find or create a biblio with 1 holdable item at Library A.
3. Place a hold for the biblio with pickup at Library B.
4. Navigate to Circulation > Transfers.
5. Attempt to transfer item to Library C.
=> Note that Koha only allows to transfer item to Library B,
cancel hold and attempt transfer or return to the transfer tool.
6. Apply this patch.
7. Attempt to transfer item again.
=> Note that there is now an option to ignore the hold and transfer
item to Library C.
=> When option to transfer item to the Library C is selected, confirm
that Koha tells that transfer was made to Library C and a new transfer
was made to database.

Sponsored-by: Koha-Suomi Oy
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:49 +00:00
Emmi Takkinen 2994d2c484
Bug 36762: Fix setting transfer for hold via branchtransfers.pl
When one tries to transfer an item that could fill a pending hold
to a library that is not the holds pickup library, Koha gives only
a option to transfer item to pickup library. When this option
is chosen, Koha tells that item is being transfered to selected
library not to pickup library but fails to create the transfer.
This patch fixes this by setting op variable as "cud-transfer"
and tobranchcd as holds pickup library so that transfer is actually
made and target library is displaued correctly.

To test:
1. Log into Library A.
2. Find or create a biblio with 1 holdable item at Library A.
3. Place a hold for pickup at Library B.
4. Navigate to Circulation > Transfers.
5. Attempt to send your item to Library C.
6. Koha gives you three options. Pick the first one:
"Transfer to Library B."
7. Koha tells you it has transferred the item to Library C.
8. Go to detail.pl for your title, confirm the holdings table
says "there is an item level hold" on your item.
9. Click to the Holds tab for your title, confirm the hold claims
to be in transit.
10. Query the branchtransfers table
SELECT * FROM branchtransfers WHERE itemnumber = [your item]
=> Note that no new transfer was made.
11. Query reserves SELECT * FROM reserves WHERE biblionumber = [your biblio]
=> Confirm your hold has found=T, itemnumber=[your item], and item_level_hold=0
12. Revert trasfer from biblios holds page.
13. Apply this patch.
14. Attempt the transfer again.
=> Koha should now tell that transfer was made to Libary B.
15. Check branchtransfers table again.
=> New transfer should have been made to Library B.

Sponsored-by: Koha-Suomi Oy
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:48 +00:00
Martin Renvoize 3abe3f1485
Bug 40206: (QA follow-up) Fix JS error and misleading message on disabled curbside pickups page
The early-exit added for the CurbsidePickup syspref check left
policy/curbside_pickups unset, but intranet-bottom.inc's jsinclude
still referenced them unconditionally, causing a client-side
TypeError on every render of the disabled state. Guard the whole
jsinclude block with disabled_for_branch.

Also distinguish the two reasons the page can be disabled: the
CurbsidePickup syspref being off globally vs. this branch simply
not having an enabled curbside pickup policy configured. The
message and link now point staff to the correct admin page for
each case, instead of always suggesting the syspref (which may
already be on).

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:47 +00:00
Tomas Cohen Arazi c68bacc5c2
Bug 40206: Fix wrong layout in curbside pickups when 'CircSidebar' enabled
This patch solves the following issues:

* Template flow issue: the template called STOP which prevented the
  WRAPPER to complete and affected how things were rendered.
* Redirect to 404 if feature disabled. This is common practice in other
  modules. The fact things can be executed even with the feature
  disabled is not ok (syspref).
* Shortcircuit earlier. Similar to the above, if the feature is
  disabled for the current branch, no DB queries and stuff should take
  place. Notice the lack of checks actually makes the controller perform
  the actions. It is just that it is not displayed afterwards!

To test:
1. Enable the `CurbsidePickup` syspref.
2. 'Activate' the `CircSidebar` syspref.
3. Be on a branch that doesn't have them enabled
4. Go to Circulation > Check in
   => SUCCESS: A nice left-hand sidebar is displayed with access to
      Circulation actions
5. Click on 'Curbside pickups'
   => FAIL: The sidebar doesn't display. boo!
6. Apply this patch
7. Restart plack
   $ ktd --shell
   $ koha-plack --restart kohadev
8. Repeat 4
   => SUCCESS: It renders much better!
9. Sign off :-D

Signed-off-by: Owen Leonard <oleonard@myacpl.org>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 12:44:43 +00:00
Pedro Amorim 2f227048ae
Bug 42280: (RM follow-up) Tidy categories.tt
Leftover untidy JS from bug 26355 (multi-line jQuery .on() calls,
missing semicolon) in the clone/clear message handlers.

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:16:04 +00:00
Jan Kissig a4a53ea03c
Bug 42280: (QA follow-up) Split JS in uage_statistics to TT and non TT block. Auto tidy
This splits remaining JS into a block with and without TT interpolation. Auto tidy

Small changes on add_to_map() call:

To test:
a) go to Administration -> Shar eyour usage statistics
b) in dev console check Koha.prefs.UsageStatsGeolocation is [] when no coordinates are set
c) add some coordinates to main library (first Geolocation input field) and others
d) check that Main and other libraries are drwan on the map when reentering usage statistics page
e) No console errors should be shown

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:16:03 +00:00
Jan Kissig 8de6874de5
Bug 42280: (QA follow-up) Split JS in transfer_limits to TT and non TT block. Auto tidy
This splits remaining JS into a block with and without TT interpolation. Auto tidy

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:16:02 +00:00
Jan Kissig 5ce434644c
Bug 42280: (QA follow-up) Split JS in restrictions to TT and non TT block. Auto tidy
This splits remaining JS into a block with and without TT interpolation. Auto tidy

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:16:01 +00:00
Jan Kissig 1987d0a930
Bug 42280: (QA follow-up) Split JS in preferences to TT and non TT block. Auto tidy
This splits remaining JS into a block with and without TT interpolation. Auto tidy

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:16:00 +00:00
Jan Kissig 1153112951
Bug 42280: (QA follow-up) Split JS in matching-rules to TT and non TT block. Auto tidy
This splits remaining JS into a block with and without TT interpolation. Auto tidy

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:59 +00:00
Jan Kissig c2f1576a3d
Bug 42280: (QA follow-up) Split JS in curbside_pickup to TT and non TT block. Auto tidy
This splits remaining JS into a block with and without TT interpolation. Auto tidy

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:58 +00:00
Jonathan Druart f52abf6d79
Bug 42280: Tidy all script tags - admin (auto tidy)
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Jan Kissig <bibliothek@th-wildau.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:57 +00:00
Jonathan Druart 70bb50988b
Bug 42280: Tidy all script tags - admin
In order to tidy all the script tags of the .tt files we isolate the
interpolation of Template::Toolkit variables in a separate script tag.

This patch focuses on the administration module.

The 2 main changes are in the following files:
* aqplan.tt
"0" should be correctly formatted
* categories.tt
trivial change about table settings

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Jan Kissig <bibliothek@th-wildau.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:56 +00:00
Jonathan Druart 0de23cde12
Bug 42291: Prevent XSS
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Jan Kissig <bibliothek@th-wildau.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:54 +00:00
Jan Kissig d194215dfe
Bug 42291: (follow-up) Move remaining JS to non TT script block
This removes all TT interpolations from the build_dropdown function and moves it to the non TT script block.

To test:
a) create several private or public lists via /cgi-bin/koha/virtualshelves/shelves.pl
b) either the number of private or public list should be > 10 to
c) add some items to a list
d) on this lists content site click 'Add to' to open drop down menu
e) the menu should offer up to 10 private or 10 public list options and a [ more lists ] option

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:53 +00:00
Jonathan Druart 56036ac098
Bug 42291: Tidy all script tags - shelves (auto tidy)
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Jan Kissig <bibliothek@th-wildau.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:52 +00:00
Jonathan Druart f73b0db9ae
Bug 42291: Tidy all script tags - shelves
In order to tidy all the script tags of the .tt files we isolate the
interpolation of Template::Toolkit variables in a separate script tag.

This patch focuses on the virtual shelves module.

Trivial changes, the existing JS variables are moved in a separate
script tag.
We could have gone further with the list of shelves (the build_dropdown
function) , but that's for another day...

Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Jan Kissig <bibliothek@th-wildau.de>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:51 +00:00
Jonathan Druart 9ac2f291b3
Bug 41061: Remove unused calendar JS functions
Since the introduction of Maskito by bug 33703 we do no longer need to
valid dates when the flatpickr widget is closed.

This patch removes the following JS function from staff and OPAC:
* is_valid_date
* validate_date
* get_dateformat_str

Test plan:
0. Do not apply this patch and confirm that the date string received by
   those functions has already been correctly formatted
1. Apply the patch and git grep to make sure no occurrence exists in the
   code
2. Confirm that you are still able to select dates with flatpickr

Signed-off-by: Emmi Takkinen <emmi.takkinen@koha-suomi.fi>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:50 +00:00
Pedro Amorim c798a0c49b
Bug 41084: (RM follow-up): Remove atomicupdate file
Should have been part of e5aa0c0

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-20 09:15:49 +00:00
Lucas Gass 1265f90523
Bug 43005: Remove messaging preferences check
1. Go to Patron record -> Details
2. Under "Library use" click 'Edit'
3. From here change the patron category, no pop-up
4. APPLY PATCH and try again
5. Now you should see the pop-up modal

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-17 13:49:20 +00:00
Kyle M Hall 506f6ed2a2
Bug 43034: Fix t/db_dependent/Koha/EDI.t failure when SearchEngine is Elasticsearch
Bug 21820 made the Zebra queue update conditional on the new
ElasticsearchEnableZebraQueue system preference, which defaults to off.
The 'basic_quote_processing' subtest in t/db_dependent/Koha/EDI.t counts
the log lines process_quote emits, and two of those are the
"specialUpdate biblioserver" traces that ModZebra logs only when the
Zebra queue path runs. When the SearchEngine preference is set to
Elasticsearch, that path no longer runs by default, so the subtest now
records 6 log lines instead of 8 and fails:

    #   Failed test '8 log lines recorded for passed quote file'
    #          got: '6'
    #     expected: '8'
    #   Failed test 'Trace recorded biblioserver index'

Test Plan:
1) Set the SearchEngine system preference to 'Elasticsearch'
2) prove t/db_dependent/Koha/EDI.t
3) Note the basic_quote_processing subtest fails with "got: '6', expected: '8'"
4) Apply this patch
5) prove t/db_dependent/Koha/EDI.t
6) Note the tests pass!

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-17 13:49:19 +00:00
Martin Renvoize 77fc25f184
Bug 42915: (QA follow-up) Remove unused filters_options variable
filters_options was introduced with the coded_values library filtering
patch but never referenced anywhere in the file.

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-17 13:49:18 +00:00
Martin Renvoize 1cf8f9ffc5
Bug 42915: (QA follow-up) Restore fallback message for holds with no patron data
The "Add patron names to data in column def in holds table" patch
dropped the null-patron handling in the patron column's render
function, so a hold row whose patron embed comes back null (e.g. a
user without permission to view the patron on that hold, per Bug
23269's test plan) now renders a blank cell instead of a useful
message.

Restore the fallback, sourced from `all_libraries` (populated by this
same table's include) rather than the separate `libraries` global,
and check `row.patron` directly instead of the now-composite `data`
column key.

To test:
1. Have holds on a bib from patrons the logged in user cannot see
   (e.g. IndependentBranches, or a reduced permission set)
2. View the record's Holds tab
3. Confirm rows with no visible patron show "A patron from library
   <library>" instead of a blank Patron cell

Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-17 13:49:17 +00:00
Martin Renvoize e2685f88c9
Bug 42915: (QA follow-up) Declare all_libraries once, unconditionally, in request.tt
holds_table.inc is INCLUDEd in a loop from reserve/request.tt whenever
HoldsSplitQueue splits the holds queue by branch/itemtype, or when
holds for more than one biblio are shown at once (multi_hold). Each
inclusion appended its own <script> block declaring `all_libraries`,
and since top-level `let`/`const` in separate inline <script> tags
share the page's global lexical scope, the second and subsequent
occurrences threw "Identifier 'all_libraries' has already been
declared", breaking those script blocks and the pickup-library
filtering that depends on `all_libraries` being set.

Worse, that script block is only emitted when a biblio's holds are
actually rendered (biblioloo.total_holds > 0), but holds.js is loaded
unconditionally by reserve/request.tt and reads `all_libraries` at
top-level parse time. Placing the very first hold on a title (0
existing holds) therefore threw:

    ReferenceError: all_libraries is not defined

breaking every hold-management handler defined below that line in
holds.js.

Fix both by declaring `all_libraries` exactly once, unconditionally,
in request.tt's existing page-global script block (alongside the
other unconditional globals such as HidePatronName and
HoldsSplitQueue), before Asset.js("js/holds.js") loads, instead of
duplicating it inside the per-title/per-split holds_table.inc
include.

To test:
1. Search for a title with no existing holds and start placing one
2. Without this patch: browser console shows "ReferenceError:
   all_libraries is not defined" as soon as the page loads
3. With this patch: no console errors
4. Set HoldsSplitQueue to "pickup library", place holds on a title
   for two different pickup libraries, confirm both split tables
   render with working patron-name and pickup-library filtering and
   no console errors
5. Confirm the patron's own Holds tab (moremember.pl/circulation.pl)
   still loads without console errors

Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-17 13:49:15 +00:00
Brendan Lawlor 6532c4660a
Bug 42915: (follow-up) Fix holds table display on patron record holds tab
To test:
1. Have a patron with multiple holds
2. Go to the patron record's hold tab
3. Confirm the table loads and no errors are logged to the console

Sponsored-by: CLAMS
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
2026-07-17 13:49:15 +00:00