Intro
Most libraries have a straightforward overdue fine policy: charge a fixed amount for every day a book remains overdue. But some institutions use a progressive model where the fine increases as the overdue period becomes longer.
I faced this problem in my own library while implementing Koha. Our library has an approved policy of charging incremental fines for overdue books. The idea is simple: the longer a book remains overdue, the higher the daily fine becomes. However, Koha’s standard fine configuration does not provide a straightforward way to implement this kind of progressive fine structure.
So, I looked for a practical solution and eventually customised Koha’s Overdues.pm file to implement the required fine calculation.
It worked.
Until I upgraded Koha.
During the upgrade, Koha downloaded the latest versions of its core files from the central repository. This included Overdues.pm, which meant that my customised file was replaced with the standard version. The result was not just that my progressive fine calculation disappeared. The customisation was no longer compatible with the upgraded system, and it caused problems with the working Koha installation.
That experience taught me an important lesson about customising an open-source application: getting a custom feature to work is only half the job. You also need to make sure the customisation can survive future upgrades.
In this article, I document what happened, how I implemented the incremental fine policy, what went wrong during the Koha upgrade, and how I changed my approach to make the system safer and more upgrade-friendly.
This is not a theoretical discussion. It is based on a real problem I encountered while managing a production library system and the lessons I learned while trying to make Koha do something required by our library policy.
The policy I was implementing
A conventional fine policy charges something like ₹5 per day, every day. Simple, but it doesn’t always reflect what the library actually wants. A progressive structure makes the penalty more meaningful as the delay grows.
The library’s approved policy looked like this:
- Days 1–7 overdue: ₹25 per day
- Days 8–14 overdue: ₹50 per day
- Days 15–21 overdue: ₹75 per day
- Beyond the third week: as per the library’s approved policy
I’m deliberately not prescribing a fourth-week rate here, because fine policies are institutional decisions. They shouldn’t be hard-coded into a technical article just to make the numbers tidy. There was also a proposal to cap the fine at three times the cost of the book, but the library committee didn’t accept it, so it’s not part of the implemented policy. Technical configuration should follow approved library policy, not the other way around.
Why progressive fines don’t fit a single rate field
Before changing anything in Koha, it helps to understand how the standard calculation works. A normal Koha fine is straightforward:
Fine = overdue days × fixed daily rate
So 10 overdue days at ₹25 gives ₹250.
A progressive policy is different. For those same 10 days, the math breaks into segments:
- From Day 1 to Day 7 —> 7 × ₹25 = ₹175
- Day 8 to Day 10 —> 3 × ₹50 = ₹150
- Total: ₹325
So the fine is a sum of per-period fines, which is exactly why you can’t represent it by entering a single daily rate in the circulation rules screen. The rate applies to each segment of the overdue period, not retroactively to all days.
Expressed as an algorithm:
- If overdue days ≤ 7: fine = days × 25
- If 7 < days ≤ 14: fine = (7 × 25) + ((days − 7) × 50)
- If 14 < days ≤ 21: fine = (7 × 25) + (7 × 50) + ((days − 14) × 75)
Take 18 overdue days as an example. First 7 days at ₹25 = ₹175. Next 7 days at ₹50 = ₹350. Remaining 4 days at ₹75 = ₹300. Total = ₹825. That’s very different from 18 × ₹75 = ₹1,350, which would wrongly apply the third-week rate to the whole period.
The cumulative mistake people keep making
This is probably the single most common error when implementing progressive fines. The rate applies to the days that fall in its band — it does not get applied to all overdue days. The table below makes it obvious:
| Overdue days | Correct calculation | Total |
|---|---|---|
| 3 | 3 × ₹25 | ₹75 |
| 7 | 7 × ₹25 | ₹175 |
| 10 | 7 × ₹25 + 3 × ₹50 | ₹325 |
| 14 | 7 × ₹25 + 7 × ₹50 | ₹525 |
| 18 | 7 × ₹25 + 7 × ₹50 + 4 × ₹75 | ₹825 |
| 21 | 7 × ₹25 + 7 × ₹50 + 7 × ₹75 | ₹1,050 |
The cumulative approach matters both for writing the code and for explaining the policy to circulation staff.
Where Koha actually calculates fines, and why that matters
Administrators looking to customise overdue calculations will quickly run into Koha’s Perl codebase. Historically, much of the overdue logic has lived in C4/Overdues.pm, and in older Koha releases you could make targeted edits there and get results.
Here’s the thing, though: Koha is actively developed. Functions, modules, database interactions, and processing workflows change between releases. A modification that worked perfectly in Koha 23.05 may stop working, produce incorrect fines, conflict with updated code, be overwritten during an upgrade, or make future upgrades painful. So treating a source-code edit as a controlled software change rather than a quick config tweak is essential.
Starting with the built-in configuration
Before writing any custom code, configure the standard circulation rules properly. In Koha 24.05, go to Administration → Circulation and Fines Rules. From there you can set the fine amount, charging interval, grace period, maximum fine, suspension rules, loan period, renewals, and overdue notices. The exact fields depend on your version and circulation-rule setup.
In my case the base configuration handled the first-week rate, and the progressive logic was layered on top where Koha’s built-in options couldn’t express the multi-tier structure.
Moving from 23.05 to 24.05: what broke, and what I learned
In my earlier Koha 23.05 installation I had implemented the progressive logic by modifying the overdue calculation code, and it worked for the required policy. After moving to 24.05, the same modification couldn’t simply be copied into the new installation.
The lesson is simple and worth repeating: never assume that a customisation made to one Koha release will remain compatible with the next. A major upgrade is an opportunity to review your customisations, not to blindly restore them.
The safest sequence is to take a complete database backup, spin up a test or staging server that matches production, implement the logic there, test multiple overdue scenarios, have staff verify, and only then deploy. Never make an experimental change directly on the production system, a library’s circulation database holds years of transactions, and it’s a spectacularly bad place to discover that a Perl edit had an unexpected side effect.
When I did the upgrade, I separated the system into a few distinct layers and reviewed each one individually: Koha’s standard functionality, institutional configuration, custom code, custom SQL reports, and external integrations. That clean-slate mindset made the whole process far less painful than trying to clone the old server onto the new one.
Testing the algorithm
Most fine-calculation bugs appear at the boundaries, so I built a small set of test scenarios and ran them against the test environment:
- 1 day overdue → 1 × ₹25 = ₹25
- 7 days overdue → 7 × ₹25 = ₹175
- 8 days overdue → 7 × ₹25 + 1 × ₹50 = ₹225
- 14 days overdue → 7 × ₹25 + 7 × ₹50 = ₹525
- 15 days overdue → 7 × ₹25 + 7 × ₹50 + 1 × ₹75 = ₹600
- 21 days overdue → 7 × ₹25 + 7 × ₹50 + 7 × ₹75 = ₹1,050
I also tested the transition days themselves: 6→7→8, 13→14→15, and 20→21→22. If the code uses > and >= carelessly, a single day can be counted twice or skipped entirely. Day 8, for instance, must contribute ₹50, not ₹25.
Beyond the pure math, a few behavioural settings need testing too. If a grace period is configured, you have to decide whether the first fine-bearing day is day 1 after the due date or day 1 after the grace period, don’t implement the algorithm until that’s agreed. You should also test both an item that is currently overdue (to see what staff see before check-in) and an item that gets returned, because the displayed fine and the final assessed fine aren’t always calculated at the same stage.
Finally, check that the scheduled jobs responsible for overdue processing are actually running. A perfectly written fine algorithm is useless if the cron that executes it never fires. On most Koha servers you can inspect this with ls -l /etc/cron.d/ or sudo crontab -l, and confirm against the release documentation.
Verifying with SQL reports (without shooting yourself in the foot)
SQL reports are extremely useful for validating a custom fine policy. A basic query to inspect recorded transactions looks something like:
SELECT borrowernumber, amount, description, date
FROM accountlines
ORDER BY date DESC;
But always verify the database schema for your exact Koha release before deploying SQL reports to production, don’t blindly copy queries from an older version.
A useful administrative report might show the card number, borrower, item, due date, overdue days, and computed fine. That kind of report is handy for testing, auditing, committee review, and troubleshooting abnormal transactions.
Where SQL turns dangerous is when people use it to directly change fines. Avoid casually running UPDATE accountlines SET amount = ... just to correct a calculation. Koha’s financial records involve additional fields and accounting relationships, and editing them by hand can break the audit trail. If a fine is wrong, use Koha’s supported staff-interface mechanisms wherever possible.
Keeping customisations maintainable
If you modify Koha source code directly, future package upgrades can replace your modified files. Your change can quietly disappear after an apt upgrade or a Koha package upgrade, which is a nasty surprise in a production library.
A few habits make this manageable. First, keep custom SQL reports and scripts in a separate location, something like /opt/koha-custom/ with reports/, scripts/, and documentation/ subdirectories, so they don’t get lost in the application tree during an upgrade. Second, maintain a small test dataset of dummy users and items — TEST-STUDENT-01, TEST-FACULTY-01, and so on — with controlled overdue transactions covering the key boundary days. This lets you re-test after every upgrade without disturbing real borrower accounts.
Third, and most importantly, keep a written customisation record for every production change. Mine looks roughly like this:
Koha Version: 24.05.x
Customisation: Progressive overdue fine
Policy: Days 1–7 ₹25/day, Days 8–14 ₹50/day, Days 15–21 ₹75/day
Files Modified: [exact files]
Reason: Implementation of approved library fine policy
Date Implemented: [date]
Tested By: [name]
Approved By: [authority]
That single document has saved me hours when the next Koha release arrived.
The mistakes worth naming explicitly
A few errors came up often enough that they deserve to be called out:
Applying the new rate to all days. 18 days overdue is not 18 × ₹75 = ₹1,350. It’s 7 × ₹25 + 7 × ₹50 + 4 × ₹75 = ₹825. The rate applies to its corresponding period.
Forgetting the boundary day. If days 1–7 are ₹25 and days 8–14 are ₹50, then day 8 contributes ₹50 not ₹25.
Copying old custom code into a new Koha version. This is one of the most dangerous shortcuts. Instead of overwriting new files with old ones, understand the business requirement, compare the old and new Koha code, identify changed functions, adapt the implementation, and test thoroughly. The business requirement is stable; the implementation is not.
Forgetting that upgrades overwrite changes. Always document which files were modified, why, what lines or functions changed, which Koha version was used, who approved it, how it was tested, and how to restore the standard file.
Maximum fines, grace periods, and policy scope
A maximum fine is a separate policy decision, lets say, a flat ₹500, or three times the item cost. But a progressive fine system and a maximum fine are two different rules, and the library committee needs to decide on each of them: progressive rate, maximum fine, grace period, fine exemptions, lost-book policy, and so on. Don’t implement a maximum simply because it’s technically convenient. In my case, the proposed cap of three times the book cost wasn’t accepted by the committee, so it wasn’t part of the final policy.
Communicating the policy to borrowers
Technical configuration is only half the job. Borrowers should be able to understand the policy, and it should never come as a surprise to the person being charged. Something like:
Books returned after the due date will attract overdue fines according to the library’s progressive fine policy. The applicable rate increases with the length of the overdue period.
Publish the exact institutional policy through the library website, OPAC, membership rules, borrower registration documents, notices, and the student handbook.
A checklist before you go live
Before deploying progressive fines to production, verify that the approved policy is documented; first, second, and third-week rates are confirmed; the fourth-week or long-term policy is confirmed; grace period and maximum-fine policy are settled; the Koha version is recorded; a database backup has been taken; a test server is available; custom code is documented; fine calculation and boundary conditions are tested; SQL reports are tested; existing fines are verified; staff have tested the workflow; a rollback procedure is documented; and production deployment is approved.
What this actually taught me
Progressive fines are a good example of the difference between library policy and software configuration. The library decides how overdue material should be fined; Koha decides how that policy can be implemented. The two should not be confused.
My move from Koha 23.05 to 24.05 reinforced a principle I now take for granted: customising an open-source library system means planning for the next upgrade, not just solving today’s problem. A change that works perfectly today can become a maintenance headache tomorrow if it’s undocumented or tightly coupled to the application’s internal source code.
The objective was never simply to make Koha calculate ₹25, ₹50, and ₹75 correctly. The objective was to implement the library’s policy accurately, transparently, and in a way that can survive future Koha upgrades. That is ultimately what makes a library automation system maintainable.


