Bugzilla – Attachment 116142 Details for
Bug 26274
Expose cash register cashup summaries via an API route.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 26274: Add cashups api routes
Bug-26274-Add-cashups-api-routes.patch (text/plain), 46.21 KB, created by
David Nind
on 2021-02-01 09:56:05 UTC
(
hide
)
Description:
Bug 26274: Add cashups api routes
Filename:
MIME Type:
Creator:
David Nind
Created:
2021-02-01 09:56:05 UTC
Size:
46.21 KB
patch
obsolete
>From 1fc2352f9ab40caee40317797b44b278330b722b Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@ptfs-europe.com> >Date: Wed, 19 Aug 2020 08:34:35 +0100 >Subject: [PATCH] Bug 26274: Add cashups api routes > >This patch creates a Koha::Cash::Register::Cashup(s) class pair which >subclass Koha::Cash::Register::Action(s) and moves the cashup specific >code into these new classes to improve code separation. > >We then introduce API routes based on these classes to allow fetching >a list of cashups associated to a cash register and a full cashup with >emeddable summary for individual cashups. > >Test plan >1/ Run the updated unit tests. > t/db_dependent/Koha/Cash/Register/Action.t > t/db_dependent/Koha/Cash/Register/Cashup.t >2/ Run the incuded api tests. > t/ > >Signed-off-by: David Nind <david@davidnind.com> >--- > Koha/Cash/Register.pm | 9 +- > Koha/Cash/Register/Action.pm | 97 -------- > Koha/Cash/Register/Cashup.pm | 208 ++++++++++++++++ > Koha/Cash/Register/Cashups.pm | 64 +++++ > Koha/REST/V1/CashRegisters/Cashups.pm | 79 ++++++ > api/v1/swagger/definitions.json | 3 + > api/v1/swagger/definitions/cashup.json | 30 +++ > api/v1/swagger/parameters.json | 6 + > api/v1/swagger/parameters/cash_register.json | 9 + > api/v1/swagger/parameters/cashup.json | 9 + > api/v1/swagger/paths.json | 6 + > api/v1/swagger/paths/cash_registers.json | 104 ++++++++ > t/db_dependent/Koha/Cash/Register/Action.t | 269 +------------------- > t/db_dependent/Koha/Cash/Register/Cashup.t | 352 +++++++++++++++++++++++++++ > 14 files changed, 876 insertions(+), 369 deletions(-) > create mode 100644 Koha/Cash/Register/Cashup.pm > create mode 100644 Koha/Cash/Register/Cashups.pm > create mode 100644 Koha/REST/V1/CashRegisters/Cashups.pm > create mode 100644 api/v1/swagger/definitions/cashup.json > create mode 100644 api/v1/swagger/parameters/cash_register.json > create mode 100644 api/v1/swagger/parameters/cashup.json > create mode 100644 api/v1/swagger/paths/cash_registers.json > create mode 100644 t/db_dependent/Koha/Cash/Register/Cashup.t > >diff --git a/Koha/Cash/Register.pm b/Koha/Cash/Register.pm >index e54ce5f228..2daa52ed41 100644 >--- a/Koha/Cash/Register.pm >+++ b/Koha/Cash/Register.pm >@@ -22,6 +22,7 @@ use Carp; > use Koha::Account::Lines; > use Koha::Account::Offsets; > use Koha::Cash::Register::Actions; >+use Koha::Cash::Register::Cashups; > use Koha::Database; > > use base qw(Koha::Object); >@@ -66,7 +67,7 @@ sub cashups { > $self->_result->search_related( 'cash_register_actions', > $merged_conditions, $attrs ); > >- return Koha::Cash::Register::Actions->_new_from_dbic($rs); >+ return Koha::Cash::Register::Cashups->_new_from_dbic($rs); > } > > =head3 last_cashup >@@ -85,7 +86,7 @@ sub last_cashup { > )->single; > > return unless $rs; >- return Koha::Cash::Register::Action->_new_from_dbic($rs); >+ return Koha::Cash::Register::Cashup->_new_from_dbic($rs); > } > > =head3 accountlines >@@ -208,7 +209,7 @@ sub drop_default { > > =head3 add_cashup > >- my $action = $cash_register->add_cashup( >+ my $cashup = $cash_register->add_cashup( > { > manager_id => $logged_in_user->id, > amount => $cash_register->outstanding_accountlines->total >@@ -230,7 +231,7 @@ sub add_cashup { > } > )->discard_changes; > >- return Koha::Cash::Register::Action->_new_from_dbic($rs); >+ return Koha::Cash::Register::Cashup->_new_from_dbic($rs); > } > > =head2 Internal methods >diff --git a/Koha/Cash/Register/Action.pm b/Koha/Cash/Register/Action.pm >index 759bd98ccc..ede413c21d 100644 >--- a/Koha/Cash/Register/Action.pm >+++ b/Koha/Cash/Register/Action.pm >@@ -60,103 +60,6 @@ sub register { > return Koha::Cash::Register->_new_from_dbic($rs); > } > >-=head3 cashup_summary >- >- my $cashup_summary = $action->cashup_summary; >- >-Return a hashref containing a summary of transactions that make up this cashup action. >- >-=cut >- >-sub cashup_summary { >- my ($self) = @_; >- my $summary; >- my $prior_cashup = Koha::Cash::Register::Actions->search( >- { >- 'code' => 'CASHUP', >- 'timestamp' => { '<' => $self->timestamp }, >- register_id => $self->register_id >- }, >- { >- order_by => { '-desc' => [ 'timestamp', 'id' ] }, >- rows => 1 >- } >- )->single; >- >- my $conditions = >- $prior_cashup >- ? { >- 'date' => { >- '-between' => >- [ $prior_cashup->timestamp, $self->timestamp ] >- } >- } >- : { 'date' => { '<' => $self->timestamp } }; >- >- my $outgoing_transactions = $self->register->accountlines->search( >- { %{$conditions}, credit_type_code => undef }, >- ); >- my $income_transactions = $self->register->accountlines->search( >- { %{$conditions}, debit_type_code => undef }, >- ); >- >- my $income_summary = Koha::Account::Offsets->search( >- { >- 'me.credit_id' => >- { '-in' => [ $income_transactions->get_column('accountlines_id') ] }, >- 'me.debit_id' => { '!=' => undef } >- }, >- { >- join => { 'debit' => 'debit_type_code' }, >- group_by => [ 'debit.debit_type_code', 'debit_type_code.description' ], >- order_by => { '-asc' => 'debit.debit_type_code' }, >- 'select' => [ { sum => 'me.amount' }, 'debit.debit_type_code', 'debit_type_code.description' ], >- 'as' => [ 'total', 'debit_type_code', 'debit_description' ], >- } >- ); >- >- my $outgoing_summary = Koha::Account::Offsets->search( >- { >- 'me.debit_id' => >- { '-in' => [ $outgoing_transactions->get_column('accountlines_id') ] }, >- 'me.credit_id' => { '!=' => undef } >- }, >- { >- join => { 'credit' => 'credit_type_code' }, >- group_by => [ 'credit.credit_type_code', 'credit_type_code.description' ], >- order_by => { '-asc' => 'credit.credit_type_code' }, >- 'select' => [ { sum => 'me.amount' }, 'credit.credit_type_code', 'credit_type_code.description' ], >- 'as' => [ 'total', 'credit_type_code', 'credit_description' ], >- } >- ); >- >- my @income = map { >- { >- total => $_->get_column('total'), >- debit_type_code => $_->get_column('debit_type_code'), >- debit_type => { description => $_->get_column('debit_description') } >- } >- } $income_summary->as_list; >- my @outgoing = map { >- { >- total => $_->get_column('total'), >- credit_type_code => $_->get_column('credit_type_code'), >- credit_type => { description => $_->get_column('credit_description') } >- } >- } $outgoing_summary->as_list; >- >- $summary = { >- from_date => $prior_cashup? $prior_cashup->timestamp : undef, >- to_date => $self->timestamp, >- income => \@income, >- outgoing => \@outgoing, >- income_transactions => $income_transactions, >- outgoing_transactions => $outgoing_transactions, >- }; >- >- return $summary; >-} >- > =head2 Internal methods > > =cut >diff --git a/Koha/Cash/Register/Cashup.pm b/Koha/Cash/Register/Cashup.pm >new file mode 100644 >index 0000000000..a7e071afae >--- /dev/null >+++ b/Koha/Cash/Register/Cashup.pm >@@ -0,0 +1,208 @@ >+package Koha::Cash::Register::Cashup; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Carp; >+ >+use Koha::Database; >+ >+use base qw(Koha::Cash::Register::Action); >+ >+=head1 NAME >+ >+Koha::Cash::Register::Actions - Koha Cash Register Action Object set class >+ >+=head1 API >+ >+=head2 Class methods >+ >+=head3 search >+ >+ my $cashup = Koha::Cash::Register::Actions::Cashup->search( $where, $attr ); >+ >+Returns a list of cash register cashup. >+ >+=cut >+ >+sub search { >+ my ( $self, $where, $attr ) = @_; >+ >+ $where->{code} = 'CASHUP'; >+ >+ unless ( exists $attr->{order_by} ) { >+ $attr->{order_by} = >+ [ { '-asc' => 'register_id' }, { '-desc' => 'timestamp' } ]; >+ } >+ >+ return $self->SUPER::search( $where, $attr ); >+} >+ >+=head3 summary >+ >+ my $summary = $cashup->summary; >+ >+Return a hashref containing a summary of transactions that make up this cashup. >+ >+=cut >+ >+sub summary { >+ my ($self) = @_; >+ my $summary; >+ my $prior_cashup = Koha::Cash::Register::Cashups->search( >+ { >+ 'timestamp' => { '<' => $self->timestamp }, >+ register_id => $self->register_id >+ }, >+ { >+ order_by => { '-desc' => [ 'timestamp', 'id' ] }, >+ rows => 1 >+ } >+ ); >+ >+ my $previous = $prior_cashup->single; >+ >+ my $conditions = >+ $previous >+ ? { >+ 'date' => { >+ '-between' => >+ [ $previous->_result->get_column('timestamp'), $self->timestamp ] >+ } >+ } >+ : { 'date' => { '<' => $self->timestamp } }; >+ >+ my $payout_transactions = $self->register->accountlines->search( >+ { %{$conditions}, credit_type_code => undef }, >+ ); >+ my $income_transactions = $self->register->accountlines->search( >+ { %{$conditions}, debit_type_code => undef }, >+ ); >+ >+ my $income_summary = Koha::Account::Offsets->search( >+ { >+ 'me.credit_id' => { >+ '-in' => $income_transactions->_resultset->get_column( >+ 'accountlines_id')->as_query >+ }, >+ 'me.debit_id' => { '!=' => undef } >+ }, >+ { >+ join => { 'debit' => 'debit_type_code' }, >+ group_by => >+ [ 'debit.debit_type_code', 'debit_type_code.description' ], >+ 'select' => [ >+ { sum => 'me.amount' }, 'debit.debit_type_code', >+ 'debit_type_code.description' >+ ], >+ 'as' => [ 'total', 'debit_type_code', 'debit_description' ], >+ } >+ ); >+ >+ my $payout_summary = Koha::Account::Offsets->search( >+ { >+ 'me.debit_id' => { >+ '-in' => $payout_transactions->_resultset->get_column( >+ 'accountlines_id')->as_query >+ }, >+ 'me.credit_id' => { '!=' => undef } >+ }, >+ { >+ join => { 'credit' => 'credit_type_code' }, >+ group_by => >+ [ 'credit.credit_type_code', 'credit_type_code.description' ], >+ 'select' => [ >+ { sum => 'me.amount' }, 'credit.credit_type_code', >+ 'credit_type_code.description' >+ ], >+ 'as' => [ 'total', 'credit_type_code', 'credit_description' ], >+ } >+ ); >+ >+ my @income = map { >+ { >+ total => $_->get_column('total') * -1, >+ debit_type_code => $_->get_column('debit_type_code'), >+ debit_type => { description => $_->get_column('debit_description') } >+ } >+ } $income_summary->as_list; >+ my @payout = map { >+ { >+ total => $_->get_column('total') * -1, >+ credit_type_code => $_->get_column('credit_type_code'), >+ credit_type => >+ { description => $_->get_column('credit_description') } >+ } >+ } $payout_summary->as_list; >+ >+ my $income_total = $income_transactions->total; >+ my $payout_total = $payout_transactions->total; >+ my $total = ( $income_total + $payout_total ); >+ >+ my $payment_types = Koha::AuthorisedValues->search( >+ { >+ category => 'PAYMENT_TYPE' >+ }, >+ { >+ order_by => ['lib'], >+ } >+ ); >+ >+ my @total_grouped; >+ for my $type ( $payment_types->as_list ) { >+ my $typed_income = $income_transactions->total( { payment_type => $type->authorised_value } ); >+ my $typed_payout = $payout_transactions->total( { payment_type => $type->authorised_value } ); >+ my $typed_total = ( $typed_income + $typed_payout ); >+ push @total_grouped, { payment_type => $type->lib, total => $typed_total }; >+ } >+ >+ $summary = { >+ from_date => $previous ? $previous->timestamp : undef, >+ to_date => $self->timestamp, >+ income_grouped => \@income, >+ income_total => abs($income_total), >+ payout_grouped => \@payout, >+ payout_total => abs($payout_total), >+ total => $total * -1, >+ total_grouped => \@total_grouped >+ }; >+ >+ return $summary; >+} >+ >+=head3 to_api_mapping >+ >+This method returns the mapping for representing a Koha::Cash::Regiser::Cashup object >+on the API. >+ >+=cut >+ >+sub to_api_mapping { >+ return { >+ id => 'cashup_id', >+ register_id => 'cash_register_id', >+ code => undef >+ }; >+} >+ >+1; >+ >+=head1 AUTHORS >+ >+Martin Renvoize <martin.renvoize@ptfs-europe.com> >+ >+=cut >diff --git a/Koha/Cash/Register/Cashups.pm b/Koha/Cash/Register/Cashups.pm >new file mode 100644 >index 0000000000..9100f38266 >--- /dev/null >+++ b/Koha/Cash/Register/Cashups.pm >@@ -0,0 +1,64 @@ >+package Koha::Cash::Register::Cashups; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Carp; >+ >+use Koha::Database; >+use Koha::Cash::Register::Cashup; >+ >+use base qw(Koha::Cash::Register::Actions); >+ >+=head1 NAME >+ >+Koha::Cash::Register::Actions - Koha Cash Register Action Object set class >+ >+=head1 API >+ >+=head2 Class methods >+ >+=head3 search >+ >+ my $cashups = Koha::Cash::Register::Cashups->search( $where, $attr ); >+ >+Returns a list of cash register cashups. >+ >+=cut >+ >+sub search { >+ my ( $self, $where, $attr ) = @_; >+ >+ $where->{code} = 'CASHUP'; >+ >+ unless ( exists $attr->{order_by} ) { >+ $attr->{order_by} = >+ [ { '-asc' => 'register_id' }, { '-desc' => 'timestamp' } ]; >+ } >+ >+ return $self->SUPER::search( $where, $attr ); >+} >+ >+=head3 object_class >+ >+=cut >+ >+sub object_class { >+ return 'Koha::Cash::Register::Cashup'; >+} >+ >+1; >diff --git a/Koha/REST/V1/CashRegisters/Cashups.pm b/Koha/REST/V1/CashRegisters/Cashups.pm >new file mode 100644 >index 0000000000..647a1921c0 >--- /dev/null >+++ b/Koha/REST/V1/CashRegisters/Cashups.pm >@@ -0,0 +1,79 @@ >+package Koha::REST::V1::CashRegisters::Cashups; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use Scalar::Util qw(blessed); >+use Try::Tiny; >+ >+use Koha::Cash::Register::Cashups; >+ >+=head1 NAME >+ >+Koha::REST::V1::CashRegisters::Cashups >+ >+=head1 API >+ >+=head2 Methods >+ >+=head3 list >+ >+=cut >+ >+sub list { >+ my $c = shift->openapi->valid_input or return; >+ >+ return try { >+ my $cashups_set = Koha::Cash::Register::Cashups->new; >+ my $cashups = $c->objects->search( $cashups_set ); >+ return $c->render( status => 200, openapi => $cashups ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ >+=head3 get >+ >+Controller function that handles retrieving a cash register cashup >+ >+=cut >+ >+sub get { >+ my $c = shift->openapi->valid_input or return; >+ >+ return try { >+ my $cashup = Koha::Cash::Register::Cashups->find( $c->validation->param('cashup_id') ); >+ unless ($cashup) { >+ return $c->render( status => 404, >+ openapi => { error => "Cashup not found" } ); >+ } >+ >+ my $embed = $c->stash('koha.embed'); >+ return $c->render( >+ status => 200, >+ openapi => $cashup->to_api( { embed => $embed } ) >+ ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ } >+} >+ >+1; >diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json >index bc844bdc8f..fdd44d9535 100644 >--- a/api/v1/swagger/definitions.json >+++ b/api/v1/swagger/definitions.json >@@ -5,6 +5,9 @@ > "basket": { > "$ref": "definitions/basket.json" > }, >+ "cashup": { >+ "$ref": "definitions/cashup.json" >+ }, > "circ-rule-kind": { > "$ref": "definitions/circ-rule-kind.json" > }, >diff --git a/api/v1/swagger/definitions/cashup.json b/api/v1/swagger/definitions/cashup.json >new file mode 100644 >index 0000000000..e643a9c493 >--- /dev/null >+++ b/api/v1/swagger/definitions/cashup.json >@@ -0,0 +1,30 @@ >+{ >+ "type": "object", >+ "properties": { >+ "cashup_id": { >+ "type": "integer", >+ "description": "Internal cashup identifier" >+ }, >+ "cash_register_id": { >+ "type": "integer", >+ "description": "Internal identifier for the register the cashup belongs to" >+ }, >+ "manager_id": { >+ "type": "integer", >+ "description": "Internal identifier for the manager the cashup was performed by" >+ }, >+ "amount": { >+ "type": "number", >+ "description": "Account line amount" >+ }, >+ "timestamp": { >+ "type": "string", >+ "format": "date-time", >+ "description": "Timestamp for the latest line update" >+ }, >+ "summary": { >+ "type": "object", >+ "description": "A summary of the cashup action" >+ } >+ } >+} >diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json >index 8fc892a6af..92653a41aa 100644 >--- a/api/v1/swagger/parameters.json >+++ b/api/v1/swagger/parameters.json >@@ -47,6 +47,12 @@ > "seen_pp": { > "$ref": "parameters/checkout.json#/seen_pp" > }, >+ "cash_register_id_pp": { >+ "$ref": "parameters/cash_register.json#/cash_register_id_pp" >+ }, >+ "cashup_id_pp": { >+ "$ref": "parameters/cashup.json#/cashup_id_pp" >+ }, > "match": { > "name": "_match", > "in": "query", >diff --git a/api/v1/swagger/parameters/cash_register.json b/api/v1/swagger/parameters/cash_register.json >new file mode 100644 >index 0000000000..5d46cc94a1 >--- /dev/null >+++ b/api/v1/swagger/parameters/cash_register.json >@@ -0,0 +1,9 @@ >+{ >+ "cash_register_id_pp": { >+ "name": "cash_register_id", >+ "in": "path", >+ "description": "Cash register internal identifier", >+ "required": true, >+ "type": "integer" >+ } >+} >diff --git a/api/v1/swagger/parameters/cashup.json b/api/v1/swagger/parameters/cashup.json >new file mode 100644 >index 0000000000..c0c3883959 >--- /dev/null >+++ b/api/v1/swagger/parameters/cashup.json >@@ -0,0 +1,9 @@ >+{ >+ "cashup_id_pp": { >+ "name": "cashup_id", >+ "in": "path", >+ "description": "Cashup internal identifier", >+ "required": true, >+ "type": "integer" >+ } >+} >diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json >index df30f36459..0462a70b56 100644 >--- a/api/v1/swagger/paths.json >+++ b/api/v1/swagger/paths.json >@@ -23,6 +23,12 @@ > "/biblios/{biblio_id}/items": { > "$ref": "paths/biblios.json#/~1biblios~1{biblio_id}~1items" > }, >+ "/cash_registers/{cash_register_id}/cashups": { >+ "$ref": "paths/cash_registers.json#/~1cash_registers~1{cash_register_id}~1cashups" >+ }, >+ "/cashups/{cashup_id}": { >+ "$ref": "paths/cash_registers.json#/~1cashups~1{cashup_id}" >+ }, > "/checkouts": { > "$ref": "paths/checkouts.json#/~1checkouts" > }, >diff --git a/api/v1/swagger/paths/cash_registers.json b/api/v1/swagger/paths/cash_registers.json >new file mode 100644 >index 0000000000..837f1ad35b >--- /dev/null >+++ b/api/v1/swagger/paths/cash_registers.json >@@ -0,0 +1,104 @@ >+{ >+ "/cash_registers/{cash_register_id}/cashups": { >+ "get": { >+ "x-mojo-to": "CashRegisters::Cashups#list", >+ "operationId": "listCashups", >+ "tags": ["cash_registers", "cashups"], >+ "produces": ["application/json"], >+ "parameters": [{ >+ "$ref": "../parameters.json#/cash_register_id_pp" >+ }, >+ { >+ "$ref": "../parameters.json#/match" >+ }, >+ { >+ "$ref": "../parameters.json#/order_by" >+ }, >+ { >+ "$ref": "../parameters.json#/page" >+ }, >+ { >+ "$ref": "../parameters.json#/per_page" >+ }, >+ { >+ "$ref": "../parameters.json#/q_param" >+ }, >+ { >+ "$ref": "../parameters.json#/q_body" >+ }, >+ { >+ "$ref": "../parameters.json#/q_header" >+ } >+ ], >+ "responses": { >+ "200": { >+ "description": "Cashups performed on this register", >+ "schema": { >+ "type": "array", >+ "items": { >+ "$ref": "../definitions.json#/cashup" >+ } >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Register not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "cash_management": "cashup" >+ } >+ } >+ } >+ }, >+ "/cashups/{cashup_id}": { >+ "get": { >+ "x-mojo-to": "CashRegisters::Cashups#get", >+ "operationId": "getCashup", >+ "tags": ["cash_registers", "cashups"], >+ "parameters": [{ >+ "$ref": "../parameters.json#/cashup_id_pp" >+ }], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A cashup", >+ "schema": { >+ "$ref": "../definitions.json#/cashup" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Patron not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "cash_management": "cashup" >+ } >+ }, >+ "x-koha-embed": [ >+ "summary" >+ ] >+ } >+ } >+} >diff --git a/t/db_dependent/Koha/Cash/Register/Action.t b/t/db_dependent/Koha/Cash/Register/Action.t >index 0d9c70e76c..233c1a7590 100755 >--- a/t/db_dependent/Koha/Cash/Register/Action.t >+++ b/t/db_dependent/Koha/Cash/Register/Action.t >@@ -18,7 +18,7 @@ > # along with Koha; if not, see <http://www.gnu.org/licenses>. > > use Modern::Perl; >-use Test::More tests => 3; >+use Test::More tests => 2; > > use Koha::Database; > >@@ -80,271 +80,4 @@ subtest 'register' => sub { > > }; > >-subtest 'cashup_summary' => sub { >- plan tests => 14; >- >- $schema->storage->txn_begin; >- >- my $register = >- $builder->build_object( { class => 'Koha::Cash::Registers' } ); >- my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >- my $manager = $builder->build_object( { class => 'Koha::Patrons' } ); >- >- # Transaction 1 >- my $debt1 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => undef, >- amount => '1.00', >- amountoutstanding => '0.00', >- credit_type_code => undef, >- debit_type_code => 'OVERDUE', >- date => \'NOW() - INTERVAL 10 MINUTE' >- }, >- } >- ); >- my $income1 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => $register->id, >- amount => '-1.00', >- amountoutstanding => '0.00', >- credit_type_code => 'PAYMENT', >- debit_type_code => undef, >- date => \'NOW() - INTERVAL 5 MINUTE' >- }, >- } >- ); >- $builder->build_object( >- { >- class => 'Koha::Account::Offsets', >- value => { >- credit_id => $income1->accountlines_id, >- debit_id => $debt1->accountlines_id, >- amount => '1.00', >- type => 'Payment' >- }, >- } >- ); >- >- # Transaction 2 >- my $debt2 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => undef, >- amount => '1.00', >- amountoutstanding => '0.00', >- credit_type_code => undef, >- debit_type_code => 'ACCOUNT', >- date => \'NOW() - INTERVAL 3 MINUTE' >- }, >- } >- ); >- my $debt3 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => undef, >- amount => '0.50', >- amountoutstanding => '0.00', >- credit_type_code => undef, >- debit_type_code => 'LOST', >- date => \'NOW() - INTERVAL 3 MINUTE' >- }, >- } >- ); >- my $income2 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => $register->id, >- amount => '-1.50', >- amountoutstanding => '0.00', >- credit_type_code => 'PAYMENT', >- debit_type_code => undef, >- date => \'NOW() - INTERVAL 3 MINUTE' >- }, >- } >- ); >- $builder->build_object( >- { >- class => 'Koha::Account::Offsets', >- value => { >- credit_id => $income2->accountlines_id, >- debit_id => $debt2->accountlines_id, >- amount => '1.00', >- type => 'Payment' >- }, >- } >- ); >- $builder->build_object( >- { >- class => 'Koha::Account::Offsets', >- value => { >- credit_id => $income2->accountlines_id, >- debit_id => $debt3->accountlines_id, >- amount => '0.50', >- type => 'Payment' >- }, >- } >- ); >- my $expected_income = [ >- { >- debit_type_code => 'ACCOUNT', >- total => '1.000000', >- debit_type => { 'description' => 'Account creation fee' } >- }, >- { >- debit_type_code => 'LOST', >- total => '0.500000', >- debit_type => { description => 'Lost item' } >- }, >- { >- debit_type_code => 'OVERDUE', >- total => '1.000000', >- debit_type => { 'description' => 'Overdue fine' } >- } >- ]; >- >- # Transaction 3 >- my $refund1 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => undef, >- amount => '-0.50', >- amountoutstanding => '0.00', >- credit_type_code => 'REFUND', >- debit_type_code => undef, >- date => \'NOW() - INTERVAL 3 MINUTE' >- }, >- } >- ); >- my $outgoing1 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => $register->id, >- amount => '0.50', >- amountoutstanding => '0.00', >- credit_type_code => undef, >- debit_type_code => 'PAYOUT', >- date => \'NOW() - INTERVAL 3 MINUTE' >- }, >- } >- ); >- $builder->build_object( >- { >- class => 'Koha::Account::Offsets', >- value => { >- credit_id => $refund1->accountlines_id, >- debit_id => $outgoing1->accountlines_id, >- amount => '0.50', >- type => 'Refund' >- }, >- } >- ); >- my $expected_outgoing = [ >- { >- 'total' => '0.500000', >- 'credit_type' => { >- 'description' => 'A refund applied to a patrons fine' >- }, >- 'credit_type_code' => 'REFUND' >- } >- ]; >- >- my $cashup1 = >- $register->add_cashup( { manager_id => $manager->id, amount => '2.00' } ); >- >- my $summary = $cashup1->cashup_summary; >- >- is( $summary->{from_date}, undef, >- "from_date is undefined if there is only one recorded" ); >- is( $summary->{to_date}, $cashup1->timestamp, >- "to_date equals cashup timestamp" ); >- is( ref( $summary->{income_transactions} ), >- 'Koha::Account::Lines', >- "income_transactions contains Koha::Account::Lines" ); >- is( $summary->{income_transactions}->count, >- 2, "income_transactions contains 2 transactions" ); >- is( ref( $summary->{outgoing_transactions} ), >- 'Koha::Account::Lines', >- "outgoing_transactions contains Koha::Account::Lines" ); >- is( $summary->{outgoing_transactions}->count, >- 1, "outgoing_transactions contains 1 transaction" ); >- is_deeply( $summary->{income}, $expected_income, >- "income arrayref is correct" ); >- is_deeply( $summary->{outgoing}, $expected_outgoing, >- "outgoing arrayref is correct" ); >- >- # Backdate cashup1 so we can add a new cashup to check 'previous' >- $cashup1->timestamp(\'NOW() - INTERVAL 2 MINUTE')->store(); >- $cashup1->discard_changes; >- >- # Transaction 4 >- my $debt4 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => undef, >- amount => '2.75', >- amountoutstanding => '0.00', >- credit_type_code => undef, >- debit_type_code => 'OVERDUE', >- date => \'NOW() - INTERVAL 1 MINUTE' >- }, >- } >- ); >- my $income3 = $builder->build_object( >- { >- class => 'Koha::Account::Lines', >- value => { >- register_id => $register->id, >- amount => '-2.75', >- amountoutstanding => '0.00', >- credit_type_code => 'PAYMENT', >- debit_type_code => undef, >- date => \'NOW() - INTERVAL 1 MINUTE' >- }, >- } >- ); >- $builder->build_object( >- { >- class => 'Koha::Account::Offsets', >- value => { >- credit_id => $income3->accountlines_id, >- debit_id => $debt4->accountlines_id, >- amount => '2.75', >- type => 'Payment' >- }, >- } >- ); >- >- my $cashup2 = >- $register->add_cashup( { manager_id => $manager->id, amount => '2.75' } ); >- >- $summary = $cashup2->cashup_summary; >- >- is( $summary->{from_date}, $cashup1->timestamp, >- "from_date returns the timestamp of the previous cashup action" ); >- is( $summary->{to_date}, $cashup2->timestamp, >- "to_date equals cashup timestamp" ); >- is( ref( $summary->{income_transactions} ), >- 'Koha::Account::Lines', >- "income_transactions contains Koha::Account::Lines" ); >- is( $summary->{income_transactions}->count, >- 1, "income_transactions contains 2 transactions" ); >- is( ref( $summary->{outgoing_transactions} ), >- 'Koha::Account::Lines', >- "outgoing_transactions contains Koha::Account::Lines" ); >- is( $summary->{outgoing_transactions}->count, >- 0, "outgoing_transactions contains 1 transaction" ); >- >- $schema->storage->txn_rollback; >-}; >- > 1; >diff --git a/t/db_dependent/Koha/Cash/Register/Cashup.t b/t/db_dependent/Koha/Cash/Register/Cashup.t >new file mode 100644 >index 0000000000..cc21a9ce05 >--- /dev/null >+++ b/t/db_dependent/Koha/Cash/Register/Cashup.t >@@ -0,0 +1,352 @@ >+#!/usr/bin/perl >+ >+# Copyright 2020 Koha Development team >+# >+# This file is part of Koha >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+use Test::More tests => 3; >+ >+use Koha::Database; >+ >+use t::lib::TestBuilder; >+ >+my $builder = t::lib::TestBuilder->new; >+my $schema = Koha::Database->new->schema; >+ >+subtest 'manager' => sub { >+ plan tests => 2; >+ >+ $schema->storage->txn_begin; >+ >+ my $manager = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $cashup = $builder->build_object( >+ { >+ class => 'Koha::Cash::Register::Cashups', >+ value => { manager_id => $manager->borrowernumber }, >+ } >+ ); >+ >+ is( ref( $cashup->manager ), >+ 'Koha::Patron', >+ 'Koha::Cash::Register::Cashup->manager should return a Koha::Patron' ); >+ >+ is( $cashup->manager->id, $manager->id, >+ 'Koha::Cash::Register::Cashup->manager returns the correct Koha::Patron' >+ ); >+ >+ $schema->storage->txn_rollback; >+ >+}; >+ >+subtest 'register' => sub { >+ plan tests => 2; >+ >+ $schema->storage->txn_begin; >+ >+ my $register = >+ $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $cashup = $builder->build_object( >+ { >+ class => 'Koha::Cash::Register::Cashups', >+ value => { register_id => $register->id }, >+ } >+ ); >+ >+ is( >+ ref( $cashup->register ), >+ 'Koha::Cash::Register', >+'Koha::Cash::Register::Cashup->register should return a Koha::Cash::Register' >+ ); >+ >+ is( $cashup->register->id, $register->id, >+'Koha::Cash::Register::Cashup->register returns the correct Koha::Cash::Register' >+ ); >+ >+ $schema->storage->txn_rollback; >+ >+}; >+ >+subtest 'summary' => sub { >+ plan tests => 29; >+ >+ $schema->storage->txn_begin; >+ >+ my $register = >+ $builder->build_object( { class => 'Koha::Cash::Registers' } ); >+ my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $manager = $builder->build_object( { class => 'Koha::Patrons' } ); >+ my $account = $patron->account; >+ my $expected_total = 0; >+ my $expected_income_total = 0; >+ my $expected_income_grouped = []; >+ my $expected_payout_total = 0; >+ my $expected_payout_grouped = []; >+ >+ # Transaction 1 (Fine (1.00) + Payment (-1.00)) >+ my $fine1 = $account->add_debit( >+ { >+ amount => '1.00', >+ type => 'OVERDUE', >+ interface => 'cron' >+ } >+ ); >+ $fine1->date( \'NOW() - INTERVAL 20 MINUTE' )->store; >+ >+ my $payment1 = $account->pay( >+ { >+ cash_register => $register->id, >+ amount => '1.00', >+ credit_type => 'PAYMENT', >+ lines => [$fine1] >+ } >+ ); >+ $payment1 = Koha::Account::Lines->find( $payment1->{payment_id} ); >+ $payment1->date( \'NOW() - INTERVAL 15 MINUTE' )->store; >+ $expected_income_total += '1.00'; >+ >+ # Overdue of 1.0 fully paid >+ unshift @{$expected_income_grouped}, >+ { >+ debit_type_code => 'OVERDUE', >+ total => '1', >+ debit_type => { description => 'Overdue fine' } >+ }; >+ >+ # Transaction 2 (Account (1.00) + Lost (0.50) + Payment (-1.50)) >+ my $account1 = $account->add_debit( >+ { >+ amount => '1.00', >+ type => 'ACCOUNT', >+ interface => 'cron' >+ } >+ ); >+ $account1->date( \'NOW() - INTERVAL 13 MINUTE' )->store; >+ my $lost1 = $account->add_debit( >+ { >+ amount => '0.50', >+ type => 'LOST', >+ interface => 'cron' >+ } >+ ); >+ $lost1->date( \'NOW() - INTERVAL 13 MINUTE' )->store; >+ my $payment2 = $account->pay( >+ { >+ cash_register => $register->id, >+ amount => '1.50', >+ credit_type => 'PAYMENT', >+ lines => [ $account1, $lost1 ] >+ } >+ ); >+ $payment2 = Koha::Account::Lines->find( $payment2->{payment_id} ); >+ $payment2->date( \'NOW() - INTERVAL 13 MINUTE' )->store; >+ $expected_income_total += '1.5'; >+ >+ # Lost charge of 0.5 fully paid >+ unshift @{$expected_income_grouped}, >+ { >+ debit_type_code => 'LOST', >+ total => '0.5', >+ debit_type => { description => 'Lost item' } >+ }; >+ >+ # Account fee of 1.0 fully paid >+ unshift @{$expected_income_grouped}, >+ { >+ debit_type_code => 'ACCOUNT', >+ total => '1', >+ debit_type => { description => 'Account creation fee' } >+ }; >+ >+ # Transaction 3 (Refund (-0.50) + Payout (0.50)) >+ $lost1->discard_changes; >+ my $refund1 = $lost1->reduce( >+ { >+ amount => '0.50', >+ reduction_type => 'REFUND', >+ interface => 'cron' >+ } >+ ); >+ $refund1->date( \'NOW() - INTERVAL 13 MINUTE' )->store; >+ >+ my $payout1 = $refund1->payout( >+ { >+ cash_register => $register->id, >+ amount => '0.50', >+ payout_type => 'CASH', >+ interface => 'intranet', >+ staff_id => $manager->borrowernumber, >+ branch => $manager->branchcode >+ } >+ ); >+ $payout1->date( \'NOW() - INTERVAL 13 MINUTE' )->store; >+ $expected_payout_total += '0.5'; >+ >+ # Lost fee of 0.50 fully refunded >+ unshift @{$expected_payout_grouped}, >+ { >+ 'total' => '0.5', >+ 'credit_type' => { >+ 'description' => 'A refund applied to a patrons fine' >+ }, >+ 'credit_type_code' => 'REFUND' >+ }; >+ >+ $expected_total += $expected_income_total; >+ $expected_total -= $expected_payout_total; >+ >+ diag("Cashup 1"); >+ my $cashup1 = >+ $register->add_cashup( { manager_id => $manager->id, amount => '2.00' } ); >+ >+ my $summary = $cashup1->summary; >+ >+ is( $summary->{from_date}, undef, "from_date is undefined if there is only one recorded" ); >+ is( $summary->{to_date}, $cashup1->timestamp, "to_date equals cashup timestamp" ); >+ is( ref( $summary->{income_grouped} ), 'ARRAY', "income_grouped contains an arrayref" ); >+ is( scalar @{ $summary->{income_grouped} }, 3, "income_grouped contains 3 transactions" ); >+ is_deeply( $summary->{income_grouped}, $expected_income_grouped, "income_grouped arrayref is correct" ); >+ is( $summary->{income_total}, $expected_income_total, "income_total is correct" ); >+ >+ is( ref( $summary->{payout_grouped} ), 'ARRAY', "payout_grouped contains an arrayref" ); >+ is( scalar @{ $summary->{payout_grouped} }, 1, "payout_grouped contains 1 transaction" ); >+ is_deeply( $summary->{payout_grouped}, $expected_payout_grouped, "payout_grouped arrayref is correct" ); >+ is( $summary->{payout_total}, $expected_payout_total, "payout_total is correct" ); >+ is( $summary->{total}, $expected_total,"total equals expected_total" ); >+ >+ # Backdate cashup1 so we can add a new cashup to check 'previous' >+ $cashup1->timestamp( \'NOW() - INTERVAL 12 MINUTE' )->store(); >+ $cashup1->discard_changes; >+ $expected_total = 0; >+ $expected_income_total = 0; >+ $expected_income_grouped = []; >+ $expected_payout_total = 0; >+ $expected_payout_grouped = []; >+ >+ # Transaction 4 ( Fine (2.75) + Partial payment (-2.00) ) >+ my $fine2 = $account->add_debit( >+ { >+ amount => '2.75', >+ type => 'OVERDUE', >+ interface => 'cron' >+ } >+ ); >+ $fine2->date( \'NOW() - INTERVAL 10 MINUTE' )->store; >+ >+ my $payment3 = $account->pay( >+ { >+ cash_register => $register->id, >+ amount => '2.00', >+ credit_type => 'PAYMENT', >+ lines => [$fine2] >+ } >+ ); >+ $payment3 = Koha::Account::Lines->find( $payment3->{payment_id} ); >+ $payment3->date( \'NOW() - INTERVAL 10 MINUTE' )->store; >+ $expected_income_total += '2.00'; >+ >+ unshift @{$expected_income_grouped}, >+ { >+ debit_type_code => 'OVERDUE', >+ total => '-2.000000' * -1, >+ debit_type => { 'description' => 'Overdue fine' } >+ }; >+ >+ $expected_total += $expected_income_total; >+ $expected_total -= $expected_payout_total; >+ >+ diag("Cashup 2"); >+ my $cashup2 = >+ $register->add_cashup( { manager_id => $manager->id, amount => '2.00' } ); >+ >+ $summary = $cashup2->summary; >+ >+ is( $summary->{from_date}, $cashup1->timestamp, "from_date returns the timestamp of the previous cashup cashup" ); >+ is( $summary->{to_date}, $cashup2->timestamp, "to_date equals cashup timestamp" ); >+ is( ref( $summary->{income_grouped} ), 'ARRAY', "income_grouped contains Koha::Account::Lines" ); >+ is( scalar @{ $summary->{income_grouped} }, 1, "income_grouped contains 1 transaction" ); >+ is_deeply( $summary->{income_grouped}, $expected_income_grouped, "income_grouped arrayref is correct for partial payment" ); >+ is( ref( $summary->{payout_grouped} ), 'ARRAY', "payout_grouped contains Koha::Account::Lines" ); >+ is( scalar @{ $summary->{payout_grouped} }, 0, "payout_grouped contains 0 transactions" ); >+ is_deeply( $summary->{payout_grouped}, $expected_payout_grouped, "payout_grouped arrayref is correct" ); >+ is( $summary->{total}, $expected_total, "total equals expected_total" ); >+ >+ # Backdate cashup2 so we can add a new cashup to check >+ $cashup2->timestamp( \'NOW() - INTERVAL 6 MINUTE' )->store(); >+ $cashup2->discard_changes; >+ $expected_total = 0; >+ $expected_income_total = 0; >+ $expected_income_grouped = []; >+ $expected_payout_total = 0; >+ $expected_payout_grouped = []; >+ >+ # Transaction 5 (Refund (-1) + Payout (1)) >+ $account1->discard_changes; >+ my $refund2 = $account1->reduce( >+ { >+ amount => '1.00', >+ reduction_type => 'REFUND', >+ interface => 'cron' >+ } >+ ); >+ $refund2->date( \'NOW() - INTERVAL 3 MINUTE' )->store; >+ >+ my $payout2 = $refund2->payout( >+ { >+ cash_register => $register->id, >+ amount => '1.00', >+ payout_type => 'CASH', >+ interface => 'intranet', >+ staff_id => $manager->borrowernumber, >+ branch => $manager->branchcode >+ } >+ ); >+ $payout2->date( \'NOW() - INTERVAL 3 MINUTE' )->store; >+ $expected_payout_total += '1.00'; >+ >+ # Account fee of 1.00 fully refunded (Accross cashup boundary) >+ unshift @{$expected_payout_grouped}, >+ { >+ 'total' => '1', >+ 'credit_type' => { >+ 'description' => 'A refund applied to a patrons fine' >+ }, >+ 'credit_type_code' => 'REFUND' >+ }; >+ >+ $expected_total += $expected_income_total; >+ $expected_total -= $expected_payout_total; >+ >+ diag("Cashup 3"); >+ my $cashup3 = >+ $register->add_cashup( { manager_id => $manager->id, amount => '2.00' } ); >+ >+ $summary = $cashup3->summary; >+ >+ is( $summary->{from_date}, $cashup2->timestamp, "from_date returns the timestamp of the previous cashup cashup" ); >+ is( $summary->{to_date}, $cashup3->timestamp, "to_date equals cashup timestamp" ); >+ is( ref( $summary->{income_grouped} ), 'ARRAY', "income_grouped contains Koha::Account::Lines" ); >+ is( scalar @{ $summary->{income_grouped} }, 0, "income_grouped contains 1 transaction" ); >+ is_deeply( $summary->{income_grouped}, $expected_income_grouped, "income_grouped arrayref is correct for partial payment" ); >+ is( ref( $summary->{payout_grouped} ), 'ARRAY', "payout_grouped contains Koha::Account::Lines" ); >+ is( scalar @{ $summary->{payout_grouped} }, 1, "payout_grouped contains 0 transactions" ); >+ is_deeply( $summary->{payout_grouped}, $expected_payout_grouped, "payout_grouped arrayref is correct" ); >+ is( $summary->{total}, $expected_total, "total equals expected_total" ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+1; >-- >2.11.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 26274
:
115803
|
115804
|
115991
|
115992
|
116136
|
116137
|
116142
|
116143
|
116145
|
116146
|
116174
|
116175
|
116324
|
116325
|
116326
|
116327
|
116939