Accounts rewriting - comments wanted.
I'm in the process of rewriting the accounts system to improve its flexibility, and getting rid of some cruft. Also to make it easier to work with from a developer point of view. The ability to do things like partial payments (which I know have been implemented about three other times, but as more putty on the existing system) and other useful things is what's aiming to come out of this, as well as having a nicely maintainable subsystem. I'm attaching the perldoc of what I have so far, I'm interested in seeing if anyone has any ideas on problems that this design might have that I should work around before it gets too much further along. I also note that the accounts system seems to handle the returning of lost items, which strikes me as the wrong place to put it, so I'll try to pull that out to somewhere more sensible, or at the least isolate it. Mostly I'm just making sure that it'll be able to reproduce all the existing functionality, which seems to have accreted in that module, but if you have any ideas for other things that can be fitted in naturally as part of this, they may be considered too. $ perldoc -t -T Accounts.pm NAME C4::Accounts - Functions for dealing with borrower accounts in Koha SYNOPSIS use C4::Accounts; DESCRIPTION The functions here handle the account for a borrower. Adding fines and other charges, paying them off, looking up the information, and keeping it all in sync. The account system works by adding every transaction (a fine added, a payment made, a rental charge, etc.) to the "accountlines" table. Once added, this is never modified except to change certain flags (dispute and zeroed in particular.) To indicate charges being paid off, a record is added to the "accountoffsets" table that links a payment transaction to a charge transaction, along with the amount that payment contributes to the charge. If a payment is completely consumed, or a charge completely paid off, the 'zeroed' flag on that transaction is set. This is purely to save time when looking for outstanding charges or payments. Additionally to this, an "accounttotals" table is maintained that tracks the total outstanding. This is the difference between the payments put in and the charges added. It is used simply to get a quick lookup of how much a borrower owes or is in credit. This structure is intended to allow partial payments in particular, but also allows flexibility that will make other things possible, such as tracking what a particular payment is for and providing a clear audit trail of what has happened. FUNCTIONS charge_borrower $trans_num = charge_borrower($borrowernumber, $itemnumber, $amount, $type, $description, $user) This is the fundamental function for charging a borrower for something. It will add a record to the accountlines table. If the person is in credit, it may also apply that credit to this charge, if allowed to by the rules for this charge type. If this relates to an item, say a rental or fine, that should go into $itemnumber. $type is a text flag that indicates what this charge is for: N: New card F: Fine A: Account management L: Lost item R: Rental M: Sundry (anything else) $user should be the user ID that applied the charge, to aid in auditing. It may also be a text string if an ID doesn't make sense (e.g. cron jobs.) In general, this function will be called by something else that handles setting up the specifics but may be called directly if that's all you need. RETURNS The transaction number of the newly created transaction. add_payment ($trans_num, $credit) = add_payment($borrowernumber, $amount, $type, $description, $user, @transactions) This adds a payment for a borrower, indicating that they've given you money. $type is a text flag that indicates the nature of the payment. Currently this is only 'C' for 'credit'. $description is a human-readable description of the payment. $user is the borrower ID of the person putting through this payment, to aid in auditing. It may also be a text string if an ID doesn't make sense (e.g. cron jobs.) @transactions is a list of transactions that this payment will be used to pay off. It will pay off as many of these as it can. If there is money in the payment left over, then it will be credit for the user. It will not automatically pay off other charges. Often this will be called by another accounts function that handles adjusting other values in the system, but it may be called directly for simple cases. NOTES The $amount value is the amount that is being paid off. It should be a positive value, but will be converted to a negative value when written to the database. RETURNS If called in a scalar context, this returns the transaction number for the added payment. If called in a list context it returns that, and the amount of credit left over from this payment. should_use_credit_for should_use_credit_for($type) If the type of the charge is one that the system preferences say should be paid off using credit, then we say so by returning true. pay_from_credit $credit_left = pay_from_credit($transaction_number, $borrowernumber) This takes a particular transaction (that should be a charge), finds all the unused payments in the system for the borrower, and pays off this transaction using them, or as close to that as possible. Note: at the moment, $borrowernumber should be the same as the borrower that accrued the charge. RETURNS The amount of credit left for this borrower. pay $money_left = pay($charge_number, $payment_number) This links a charge to a payment, paying off some or all of the charge. It does this by writing an entry to the accountoffsets table that links them together. This doesn't support using just part of the payment, that's hopefully not going to happen. The exception to this is if the payment was part used earlier, or if the payment is larger than the charge. So really I'm just saying that it doesn't let you specify the amount. $charge_number and $payment_number may be scalars or arrayrefs (or a mix.) If they are arrayrefs, this will use each payment to pay of as much of the charges as it can, basically what you'd expect. Every charge that is paid off, or payment that is used up, will get its "zeroed" flag set to true. This will update the totals table also. RETURNS The amount of the payment(s) left over (from the provided list of payments only), or zero if it was all used up. get_transaction_info ($outstanding, $borrowernumber, $amount, $transactionnumber) = get_transaction_info($transaction_number) This gets information about a transaction, and returns either a scalar with just the amount outstanding on it, or a list containing: amount outstanding, borrowernumber, amount, transactionnumber. get_transactions @transactions = get_transactions(%filter_rules) This gets a list of transactions that match the supplied filter rules. The rules implmented are: "borrowernumber": restrict to a particular borrower number "zeroed": 0/1 - restrict to a particular zeroed status "paymentonly": if 1, this will restrict to entries that are only payment types. RETURNS A list of hashes that contain the database fields from the accountlines table. update_users_balance update_users_balance($borrowernumber, $adjustment) This updates a user's balance in the 'totals' table. $borrowernumber is the number of the borrower, $adjustment is the amount to change the total by. Keep in mind that we are tracking debt, so if they incur a fine, then it should be adjusted by a positive amount, and if they make a payment it should be negative. This is internal and not exported. -- Robin Sheat Catalyst IT Ltd. ✆ +64 4 803 2204 GPG: 5957 6D23 8B16 EFAB FEF8 7175 14D3 6485 A99C EB6D
One comment: $type should be user-definable, not hardcoded values. More extensible that way, since libraries are always good at coming up with new ways of doing things. This would allow for statistical reports on Accounts to a library-specific level of granularity; I just know if we do it hard-coded, someone is going to complain that they can't differentiate fine types at all get lumped into 'M'. Otherwise, looks good. If I have any more comments after re-reading, I'll post. Thanks, Robin! -Ian 2011/3/31 Robin Sheat <robin@catalyst.net.nz>
I'm in the process of rewriting the accounts system to improve its flexibility, and getting rid of some cruft. Also to make it easier to work with from a developer point of view. The ability to do things like partial payments (which I know have been implemented about three other times, but as more putty on the existing system) and other useful things is what's aiming to come out of this, as well as having a nicely maintainable subsystem.
I'm attaching the perldoc of what I have so far, I'm interested in seeing if anyone has any ideas on problems that this design might have that I should work around before it gets too much further along. I also note that the accounts system seems to handle the returning of lost items, which strikes me as the wrong place to put it, so I'll try to pull that out to somewhere more sensible, or at the least isolate it.
Mostly I'm just making sure that it'll be able to reproduce all the existing functionality, which seems to have accreted in that module, but if you have any ideas for other things that can be fitted in naturally as part of this, they may be considered too.
$ perldoc -t -T Accounts.pm NAME C4::Accounts - Functions for dealing with borrower accounts in Koha
SYNOPSIS use C4::Accounts;
DESCRIPTION The functions here handle the account for a borrower. Adding fines and other charges, paying them off, looking up the information, and keeping it all in sync.
The account system works by adding every transaction (a fine added, a payment made, a rental charge, etc.) to the "accountlines" table. Once added, this is never modified except to change certain flags (dispute and zeroed in particular.)
To indicate charges being paid off, a record is added to the "accountoffsets" table that links a payment transaction to a charge transaction, along with the amount that payment contributes to the charge. If a payment is completely consumed, or a charge completely paid off, the 'zeroed' flag on that transaction is set. This is purely to save time when looking for outstanding charges or payments.
Additionally to this, an "accounttotals" table is maintained that tracks the total outstanding. This is the difference between the payments put in and the charges added. It is used simply to get a quick lookup of how much a borrower owes or is in credit.
This structure is intended to allow partial payments in particular, but also allows flexibility that will make other things possible, such as tracking what a particular payment is for and providing a clear audit trail of what has happened.
FUNCTIONS charge_borrower $trans_num = charge_borrower($borrowernumber, $itemnumber, $amount, $type, $description, $user)
This is the fundamental function for charging a borrower for something. It will add a record to the accountlines table. If the person is in credit, it may also apply that credit to this charge, if allowed to by the rules for this charge type.
If this relates to an item, say a rental or fine, that should go into $itemnumber.
$type is a text flag that indicates what this charge is for:
N: New card F: Fine A: Account management L: Lost item R: Rental M: Sundry (anything else)
$user should be the user ID that applied the charge, to aid in auditing. It may also be a text string if an ID doesn't make sense (e.g. cron jobs.)
In general, this function will be called by something else that handles setting up the specifics but may be called directly if that's all you need.
RETURNS The transaction number of the newly created transaction.
add_payment ($trans_num, $credit) = add_payment($borrowernumber, $amount, $type, $description, $user, @transactions)
This adds a payment for a borrower, indicating that they've given you money.
$type is a text flag that indicates the nature of the payment. Currently this is only 'C' for 'credit'.
$description is a human-readable description of the payment.
$user is the borrower ID of the person putting through this payment, to aid in auditing. It may also be a text string if an ID doesn't make sense (e.g. cron jobs.)
@transactions is a list of transactions that this payment will be used to pay off. It will pay off as many of these as it can. If there is money in the payment left over, then it will be credit for the user. It will not automatically pay off other charges.
Often this will be called by another accounts function that handles adjusting other values in the system, but it may be called directly for simple cases.
NOTES The $amount value is the amount that is being paid off. It should be a positive value, but will be converted to a negative value when written to the database.
RETURNS If called in a scalar context, this returns the transaction number for the added payment. If called in a list context it returns that, and the amount of credit left over from this payment.
should_use_credit_for should_use_credit_for($type)
If the type of the charge is one that the system preferences say should be paid off using credit, then we say so by returning true.
pay_from_credit $credit_left = pay_from_credit($transaction_number, $borrowernumber)
This takes a particular transaction (that should be a charge), finds all the unused payments in the system for the borrower, and pays off this transaction using them, or as close to that as possible.
Note: at the moment, $borrowernumber should be the same as the borrower that accrued the charge.
RETURNS The amount of credit left for this borrower.
pay $money_left = pay($charge_number, $payment_number)
This links a charge to a payment, paying off some or all of the charge. It does this by writing an entry to the accountoffsets table that links them together.
This doesn't support using just part of the payment, that's hopefully not going to happen. The exception to this is if the payment was part used earlier, or if the payment is larger than the charge. So really I'm just saying that it doesn't let you specify the amount.
$charge_number and $payment_number may be scalars or arrayrefs (or a mix.) If they are arrayrefs, this will use each payment to pay of as much of the charges as it can, basically what you'd expect.
Every charge that is paid off, or payment that is used up, will get its "zeroed" flag set to true.
This will update the totals table also.
RETURNS The amount of the payment(s) left over (from the provided list of payments only), or zero if it was all used up.
get_transaction_info ($outstanding, $borrowernumber, $amount, $transactionnumber) = get_transaction_info($transaction_number)
This gets information about a transaction, and returns either a scalar with just the amount outstanding on it, or a list containing: amount outstanding, borrowernumber, amount, transactionnumber.
get_transactions @transactions = get_transactions(%filter_rules)
This gets a list of transactions that match the supplied filter rules. The rules implmented are:
"borrowernumber": restrict to a particular borrower number "zeroed": 0/1 - restrict to a particular zeroed status "paymentonly": if 1, this will restrict to entries that are only payment types.
RETURNS A list of hashes that contain the database fields from the accountlines table.
update_users_balance update_users_balance($borrowernumber, $adjustment)
This updates a user's balance in the 'totals' table.
$borrowernumber is the number of the borrower, $adjustment is the amount to change the total by. Keep in mind that we are tracking debt, so if they incur a fine, then it should be adjusted by a positive amount, and if they make a payment it should be negative.
This is internal and not exported.
-- Robin Sheat Catalyst IT Ltd. ✆ +64 4 803 2204 GPG: 5957 6D23 8B16 EFAB FEF8 7175 14D3 6485 A99C EB6D
_______________________________________________ Koha-devel mailing list Koha-devel@lists.koha-community.org http://lists.koha-community.org/cgi-bin/mailman/listinfo/koha-devel website : http://www.koha-community.org/ git : http://git.koha-community.org/ bugs : http://bugs.koha-community.org/
-- Ian Walls Lead Development Specialist ByWater Solutions Phone # (888) 900-8944 http://bywatersolutions.com ian.walls@bywatersolutions.com Twitter: @sekjal
Ian Walls schreef op do 31-03-2011 om 10:01 [-0400]:
One comment: $type should be user-definable, not hardcoded values. More extensible that way, since libraries are always good at coming up with new ways of doing things. This would allow for statistical reports on Accounts to a library-specific level of granularity; I just know if we do it hard-coded, someone is going to complain that they can't differentiate fine types at all get lumped into 'M'.
There's no logic behind the type, except perhaps 'F' to indicate fines that should be forgiven/removed when in dropbox mode. What I'll do for now is make sure I don't put anything in that's restricting them (except perhaps 'F',) and this will allow a future enhancement that could, say, pull the possible options from an authorised value or some such. On that note, what would people suggest for handling fines that should be cancelled in an accounting-sorta way? So far in my design, the only way to reduce/remove a charge is to attach a payment to it. The two options I can see are: 1) create an offset that doesn't link to a payment that cancels out the charge (this seems like it'll make things unbalanced) 2) add a 'cancelled' flag on the transaction. I'll probably go for 2, unless someone has opinions. -- Robin Sheat Catalyst IT Ltd. ✆ +64 4 803 2204 GPG: 5957 6D23 8B16 EFAB FEF8 7175 14D3 6485 A99C EB6D
2011/4/1 Robin Sheat <robin@catalyst.net.nz>:
Ian Walls schreef op do 31-03-2011 om 10:01 [-0400]:
One comment: $type should be user-definable, not hardcoded values. More extensible that way, since libraries are always good at coming up with new ways of doing things. This would allow for statistical reports on Accounts to a library-specific level of granularity; I just know if we do it hard-coded, someone is going to complain that they can't differentiate fine types at all get lumped into 'M'.
There's no logic behind the type, except perhaps 'F' to indicate fines that should be forgiven/removed when in dropbox mode. What I'll do for now is make sure I don't put anything in that's restricting them (except perhaps 'F',) and this will allow a future enhancement that could, say, pull the possible options from an authorised value or some such.
On that note, what would people suggest for handling fines that should be cancelled in an accounting-sorta way? So far in my design, the only way to reduce/remove a charge is to attach a payment to it. The two options I can see are: 1) create an offset that doesn't link to a payment that cancels out the charge (this seems like it'll make things unbalanced) 2) add a 'cancelled' flag on the transaction.
I'll probably go for 2, unless someone has opinions.
There definitely needs to be an easy way to write off a charge. It happens a lot, The way it is done now is with a writeoff payment. But as long as however it is done allows the same functionality I'm happy Chris
2011/3/31 Ian Walls <ian.walls@bywatersolutions.com>:
One comment: $type should be user-definable, not hardcoded values. More extensible that way, since libraries are always good at coming up with new ways of doing things. This would allow for statistical reports on Accounts to a library-specific level of granularity; I just know if we do it hard-coded, someone is going to complain that they can't differentiate fine types at all get lumped into 'M'.
Otherwise, looks good. If I have any more comments after re-reading, I'll post. Thanks, Robin!
-Ian
+1 for user definable $type. I get that all the time. Regards, Koustubha Kale Anant Corporation Contact Details : Address : 103, Armaan Residency, R. W Sawant Road, Nr. Golden Dyes Naka, Thane (w), Maharashtra, India, Pin : 400601. TeleFax : +91-22-21720108, +91-22-21720109 Mobile : +919820715876 Website : http://www.anantcorp.com Blog : http://www.anantcorp.com/blog/?author=2
participants (4)
-
Chris Cormack -
Ian Walls -
Koustubha Kale -
Robin Sheat