From 919a822ace77563d03ca92c73c596b1a7492e97e Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Wed, 21 Aug 2019 16:16:16 +0100 Subject: [PATCH] Bug 23442: Add a refund option to POS --- Koha/Account/Line.pm | 152 ++++++++++++++++++ Koha/Cash/Register.pm | 94 ++++++++++- installer/data/mysql/account_offset_types.sql | 3 + .../data/mysql/atomicupdate/bug_23442.perl | 22 +++ .../prog/en/includes/pos-menu.inc | 2 +- .../prog/en/modules/pos/register.tt | 96 ++++++++++- pos/register.pl | 23 ++- pos/registers.pl | 4 +- 8 files changed, 388 insertions(+), 8 deletions(-) create mode 100644 installer/data/mysql/atomicupdate/bug_23442.perl diff --git a/Koha/Account/Line.pm b/Koha/Account/Line.pm index 824b050d05..a956f8b33c 100644 --- a/Koha/Account/Line.pm +++ b/Koha/Account/Line.pm @@ -187,6 +187,158 @@ sub void { } +=head3 reduce + + $charge_accountline->reduce({ + reduction_type => $reduction_type + }); + +Used to 'reduce' a charge/debit by adding a credit to offset against the amount outstanding. + +May be used to apply a discount whilst retaining the original debit amounts or to apply a full or partial refund for example when a lost item is found and returned. + +It will immediately be applied to the given debit unless the debit has already been paid, in which case a 'zero' offset will be added to maintain a link to the debit but the outstanding credit will be left so it may be applied to other debts. + +Reduction type may be one of: + +* Discount +* Refund + +Returns the reduction accountline (which will be a credit) + +=cut + +sub reduce { + my ( $self, $params ) = @_; + + # Make sure it is a charge we are reducing + return unless $self->amount > 0; + + my $reduction; + $self->_result->result_source->schema->txn_do( + sub { + + # A 'reduction' is a 'credit' + $reduction = Koha::Account::Line->new( + { + date => \'NOW()', + amount => 0 - $params->{amount}, + accounttype => $params->{reduction_type}, + payment_type => $params->{payment_type}, + amountoutstanding => 0 - $params->{amount}, + manager_id => $params->{staff_id}, + borrowernumber => $params->{patron_id}, + interface => 'intranet', + branchcode => $self->branch, + } + )->store(); + + my $create_reduction_offset = Koha::Account::Offset->new( + { + credit_id => $reduction->accountlines_id, + type => uc( $params->{reduction_type} ), + amount => $params->{amount} + } + )->store(); + + # Link reduction to debit (and apply as required) + my $debit_outstanding = $self->amountoutstanding; + if ( $debit_outstanding >= $params->{amount} ) { + + my $credit_outstanding = $reduction->apply( + { debits => [$self], offset_type => uc($params->{reduction_type}) } ); + } + else { + + # Zero amount offset used to link original 'debit' to reduction 'credit' + my $link_reduction_offset = Koha::Account::Offset->new( + { + credit_id => $reduction->accountlines_id, + debit_id => $self->accountlines_id, + type => uc($params->{reduction_type}), + amount => 0 + } + )->store(); + } + } + ); + + return $reduction; +} + +=head3 payout + + $credit_accountline->payout( + { + payout_type => $payout_type, + register_id => $register_id, + amount => $amount + } + ); + +Used to 'pay out' a credit to a user. + +Payout type may be one of any existing payment types + +=cut + +sub payout { + my ( $self, $params ) = @_; + + # Make sure it is a credit we are paying out + return unless $self->amount < 0; + + # Make sure there is outstanding credit to pay out + my $amount = $params->{amount} ? $params->{amount} : $self->amountoutstanding; + return unless $self->amountoutstanding >= $amount; + + # Make sure we record the cash register for cash transactions + return if ( $params->{payout_type} eq 'CASH' && !defined($params->{cash_register})); + + my $payout; + $self->_result->result_source->schema->txn_do( + sub { + + # A 'payout' is a 'debit' + $payout = Koha::Account::Line->new( + { + date => \'NOW()', + amount => 0 - $amount, + accounttype => 'Payout', + payment_type => $params->{payout_type}, + amountoutstanding => 0, + manager_id => $params->{staff_id}, + borrowernumber => $params->{patron_id}, + interface => 'intranet', + branchcode => $self->branch, + register_id => $params->{cash_register}, + note => $params->{quantity} + } + )->store(); + + my $create_payout_offset = Koha::Account::Offset->new( + { + debit_id => $payout->accountlines_id, + type => 'PAYOUT', + amount => 0 - $amount + } + )->store(); + + my $payout_reduction_offset = Koha::Account::Offset->new( + { + debit_id => $payout->accountlines_id, + credit_id => $self->accountlines_id, + type => 'PAYOUT', + amount => 0 - $amount + } + )->store(); + } + ); + + return $payout; +} + + =head3 apply my $debits = $account->outstanding_debits; diff --git a/Koha/Cash/Register.pm b/Koha/Cash/Register.pm index 8c87d0cfc3..ad9ca653fa 100644 --- a/Koha/Cash/Register.pm +++ b/Koha/Cash/Register.pm @@ -220,7 +220,7 @@ sub add_cashup { my $rs = $self->_result->add_to_cash_register_actions( { code => 'CASHUP', - manager_id => $params->{user_id}, + manager_id => $params->{staff_id}, amount => $params->{amount} } )->discard_changes; @@ -228,6 +228,98 @@ sub add_cashup { return Koha::Cash::Register::Action->_new_from_dbic($rs); } +=head3 issue_refund + +Add a refund to the till, returns the added 'payout' accountline. + +NOTE: How do VOID and REFUND interact? + +NOTE: Should this really live inside Koha::Account::Line (and only be applicable to debit lines) +NOTE: This should also probably check for corresponding payment lines before allowing a 'payout' or be split into two routines. + +=cut + +sub issue_refund { + my ( $self, $params ) = @_; + + my $schema = Koha::Database->new->schema; + + my $payout; + $schema->txn_do( + sub { + + # A 'refund' is a 'credit' + my $refund = Koha::Account::Line->new( + { + date => \'NOW()', + amount => 0 - $params->{amount}, + accounttype => 'Refund', + payment_type => $params->{payment_type}, + amountoutstanding => 0, + manager_id => $params->{staff_id}, + borrowernumber => $params->{patron_id}, + interface => 'intranet', + branchcode => $self->branch, + } + )->store(); + + my $create_refund_offset = Koha::Account::Offset->new( + { + credit_id => $refund->accountlines_id, + type => 'REFUND', + amount => $params->{amount} + } + )->store(); + + # Zero amount offset used to link original 'debit' to refund 'credit' + my $link_refund_offset = Koha::Account::Offset->new( + { + credit_id => $refund->accountlines_id, + debit_id => $params->{accountline}->accountlines_id, + type => 'LINK', + amount => 0 + } + )->store(); + + # A 'payout' is a 'debit' + $payout = Koha::Account::Line->new( + { + date => \'NOW()', + amount => $params->{amount}, + accounttype => 'Payout', + payment_type => $params->{payment_type}, + amountoutstanding => 0, + manager_id => $params->{staff_id}, + borrowernumber => $params->{patron_id}, + interface => 'intranet', + branchcode => $self->branch, + register_id => $self->id, + note => $params->{quantity} + } + )->store(); + + my $create_payout_offset = Koha::Account::Offset->new( + { + debit_id => $payout->accountlines_id, + type => 'PAYOUT', + amount => $params->{amount} + } + )->store(); + + my $payout_refund_offset = Koha::Account::Offset->new( + { + debit_id => $payout->accountlines_id, + credit_id => $refund->accountlines_id, + type => 'PAYOUT', + amount => 0 - $params->{amount} + } + )->store(); + } + ); + + return $payout; +} + =head2 Internal methods =cut diff --git a/installer/data/mysql/account_offset_types.sql b/installer/data/mysql/account_offset_types.sql index 78f3a3ba53..bf0c236a21 100644 --- a/installer/data/mysql/account_offset_types.sql +++ b/installer/data/mysql/account_offset_types.sql @@ -6,6 +6,9 @@ INSERT INTO account_offset_types ( type ) VALUES ('Manual Credit'), ('Manual Debit'), ('Reverse Payment'), +('REFUND'), +('LINK'), +('PAYOUT'), ('Forgiven'), ('Dropbox'), ('Account Fee'), diff --git a/installer/data/mysql/atomicupdate/bug_23442.perl b/installer/data/mysql/atomicupdate/bug_23442.perl new file mode 100644 index 0000000000..c0f8d3eb76 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_23442.perl @@ -0,0 +1,22 @@ +$DBversion = 'XXX'; # will be replaced by the RM +if ( CheckVersion($DBversion) ) { + + $dbh->do(qq{ + INSERT IGNORE permissions (module_bit, code, description) + VALUES + (25, 'refund_cash_registers', 'Perform refund actions from cash registers') + }); + + $dbh->do(q{ + INSERT IGNORE INTO account_offset_types ( type ) VALUES ( 'REFUND' ); + }); + $dbh->do(q{ + INSERT IGNORE INTO account_offset_types ( type ) VALUES ( 'LINK' ); + }); + $dbh->do(q{ + INSERT IGNORE INTO account_offset_types ( type ) VALUES ( 'PAYOUT' ); + }); + + SetVersion($DBversion); + print "Upgrade to $DBversion done (Bug 23442 - Add a refund option to the point of sale system)\n"; +} diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/pos-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/pos-menu.inc index d309164a68..526b6dfd7a 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/pos-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/pos-menu.inc @@ -1,6 +1,6 @@ + + + [% MACRO jsinclude BLOCK %] [% INCLUDE 'datatables.inc' %] [% Asset.js("lib/jquery/plugins/rowGroup/dataTables.rowGroup.min.js") | $raw %] @@ -119,7 +198,7 @@ startRender: function ( rows, group ) { var details = JSON.parse(rows.data().pluck(1).pop()); return $('') - .append( ''+group+' '+details.description+'' ) + .append( ''+group+' '+details.description+'' ) .append( ''+details.amount+'' ) .append( ''); }, @@ -127,6 +206,19 @@ } })); + $("#issueRefundModal").on("shown.bs.modal", function(e){ + var button = $(e.relatedTarget); + var item = button.data('item'); + $("#item + span").replaceWith(item); + var accountline = button.data('accountline'); + $('#refundline').val(accountline); + var amount = button.data('amount'); + $("#amount").val(amount); + var quantity = button.data('quantity'); + $("#quantity").val(quantity); + $("#amount, #quantity, #transaction_type").focus(); + }); + $(".printReceipt").click(function() { var accountlines_id = $(this).data('accountline'); var win = window.open('/cgi-bin/koha/pos/printreceipt.pl?action=print&accountlines_id=' + accountlines_id, '_blank'); diff --git a/pos/register.pl b/pos/register.pl index 9a32fdf934..67864bbb36 100755 --- a/pos/register.pl +++ b/pos/register.pl @@ -24,6 +24,7 @@ use C4::Auth; use C4::Output; use C4::Context; +use Koha::Account::Lines; use Koha::Cash::Registers; use Koha::Database; @@ -74,8 +75,26 @@ my $op = $q->param('op') // ''; if ( $op eq 'cashup' ) { $cash_register->add_cashup( { - user_id => $logged_in_user->id, - amount => $cash_register->outstanding_accountlines->total + staff_id => $logged_in_user->id, + amount => $cash_register->outstanding_accountlines->total + } + ); +} +elsif ( $op eq 'refund' ) { + my $amount = $q->param('amount'); + my $quantity = $q->param('quantity'); + my $accountline_id = $q->param('accountline'); + my $transaction_type = $q->param('transaction_type'); + + my $accountline = Koha::Account::Lines->find($accountline_id); + $cash_register->issue_refund( + { + staff_id => $logged_in_user->id, + patron_id => $accountline->borrowernumber, + amount => $amount, + payment_type => $transaction_type, + quantity => $quantity, + accountline => $accountline } ); } diff --git a/pos/registers.pl b/pos/registers.pl index bf74f9f7af..2a5dd604dc 100755 --- a/pos/registers.pl +++ b/pos/registers.pl @@ -59,8 +59,8 @@ if ( $op eq 'cashup' ) { for my $register ( $registers->as_list ) { $register->add_cashup( { - user_id => $logged_in_user->id, - amount => $register->outstanding_accountlines->total + staff_id => $logged_in_user->id, + amount => $register->outstanding_accountlines->total } ); } -- 2.20.1