From 0411eb7e3d7b49cc1845bb50a263691c2c68b424 Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Fri, 19 Sep 2014 17:09:15 +0200 Subject: [PATCH] [PASSED QA] Bug 12969: Add a subroutine to calculate VAT and prices This patch adds a new subroutine populate_order_with_prices in the C4::Acquisition module. Its goal is to refactore the VAT and prices calculation into Koha. All scripts will use this subroutine. Test plan: Verify that the prices in t/Prices.t are consistent with the values listed in the file "Prices and VAT calculation - before" submit on bug 12964. Verify that prove t/Prices.t returns green Signed-off-by: Paola Rossi Signed-off-by: Katrin Fischer http://bugs.koha-community.org/show_bug.cgi?id=12896 --- C4/Acquisition.pm | 72 +++++++++ Koha/Number/Price.pm | 9 ++ t/Prices.t | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 484 insertions(+) create mode 100644 t/Prices.t diff --git a/C4/Acquisition.pm b/C4/Acquisition.pm index 10faff5..22934e9 100644 --- a/C4/Acquisition.pm +++ b/C4/Acquisition.pm @@ -32,6 +32,7 @@ use C4::Templates qw(gettemplate); use Koha::DateUtils qw( dt_from_string output_pref ); use Koha::Acquisition::Order; use Koha::Acquisition::Bookseller; +use Koha::Number::Price; use Time::localtime; use HTML::Entities; @@ -2888,6 +2889,77 @@ sub GetBiblioCountByBasketno { return $sth->fetchrow; } +# This is *not* the good way to calcul prices +# But it's how it works at the moment into Koha +# This will be fixed later. +# Note this subroutine should be moved to Koha::Acquisition::Order +# Will do when a DBIC decision will be taken. +sub populate_order_with_prices { + my ($params) = @_; + + my $order = $params->{order}; + my $booksellerid = $params->{booksellerid}; + return unless $booksellerid; + + my $bookseller = Koha::Acquisition::Bookseller->fetch({ id => $booksellerid }); + + my $receiving = $params->{receiving}; + my $ordering = $params->{ordering}; + my $discount = $order->{discount}; + $discount /= 100 if $discount > 1; + + $order->{rrp} = Koha::Number::Price->new( $order->{rrp} )->round; + $order->{ecost} = Koha::Number::Price->new( $order->{ecost} )->round; + if ($ordering) { + if ( $bookseller->{listincgst} ) { + $order->{rrpgsti} = $order->{rrp}; + $order->{rrpgste} = Koha::Number::Price->new( + $order->{rrpgsti} / ( 1 + $order->{gstrate} ) )->round; + $order->{ecostgsti} = $order->{ecost}; + $order->{ecostgste} = Koha::Number::Price->new( + $order->{ecost} / ( 1 + $order->{gstrate} ) )->round; + $order->{gstvalue} = Koha::Number::Price->new( + ( $order->{ecostgsti} - $order->{ecostgste} ) * + $order->{quantity} )->round; + $order->{totalgste} = $order->{ecostgste} * $order->{quantity}; + $order->{totalgsti} = $order->{ecostgsti} * $order->{quantity}; + } + else { + $order->{rrpgste} = $order->{rrp}; + $order->{rrpgsti} = Koha::Number::Price->new( + $order->{rrp} * ( 1 + $order->{gstrate} ) )->round; + $order->{ecostgste} = $order->{ecost}; + $order->{ecostgsti} = Koha::Number::Price->new( + $order->{ecost} * ( 1 + $order->{gstrate} ) )->round; + $order->{gstvalue} = Koha::Number::Price->new( + ( $order->{ecostgsti} - $order->{ecostgste} ) * + $order->{quantity} )->round; + $order->{totalgste} = $order->{ecostgste} * $order->{quantity}; + $order->{totalgsti} = $order->{ecostgsti} * $order->{quantity}; + } + } + + # Not used yet + #if ($receiving) { + # if ( $bookseller->{invoiceincgst} ) { + # $order->{unitpricegsti} = $order->{unitprice}; + # $order->{unitpricegste} = + # $order->{unitpricegsti} / ( 1 + $order->{gstrate} ); + # } + # else { + # $order->{unitpricegste} = $order->{unitprice}; + # $order->{unitpricegsti} = + # $order->{unitpricegste} * ( 1 + $order->{gstrate} ); + # } + # $order->{gstvalue} = + # $order->{quantityreceived} * + # $order->{unitpricegste} * + # $order->{gstrate}; + #} + + return $order; +} + 1; __END__ diff --git a/Koha/Number/Price.pm b/Koha/Number/Price.pm index 80854ab..6380bbc 100644 --- a/Koha/Number/Price.pm +++ b/Koha/Number/Price.pm @@ -53,6 +53,15 @@ sub unformat { return Number::Format->new(%$format_params)->unformat_number($self->value); } +sub round { + my ( $self ) = @_; + return unless defined $self->value; + + my $format_params = $self->_format_params; + + return Number::Format->new(%$format_params)->round($self->value); +} + sub _format_params { my ( $self, $params ) = @_; my $with_symbol = $params->{with_symbol} || 0; diff --git a/t/Prices.t b/t/Prices.t new file mode 100644 index 0000000..1a9278e --- /dev/null +++ b/t/Prices.t @@ -0,0 +1,403 @@ +use Modern::Perl; +use Test::More tests => 8; +use Test::MockModule; + +use C4::Acquisition; +use C4::Bookseller; +use C4::Context; + +use Koha::Number::Price; + +use t::lib::Mocks; + +t::lib::Mocks::mock_preference( 'gist', '0.02|0.05|0.196' ); + +my $bookseller_module = Test::MockModule->new('Koha::Acquisition::Bookseller'); + +my ( $basketno_0_0, $basketno_1_1, $basketno_1_0, $basketno_0_1 ); +my ( $invoiceid_0_0, $invoiceid_1_1, $invoiceid_1_0, $invoiceid_0_1 ); +my $today; + +for my $currency_format ( qw( US FR ) ) { + t::lib::Mocks::mock_preference( 'CurrencyFormat', $currency_format ); + subtest 'Configuration 1: 0 0' => sub { + plan tests => 7; + $bookseller_module->mock( + 'fetch', + sub { + return { listincgst => 0, invoiceincgst => 0 }; + } + ); + + my $biblionumber_0_0 = 42; + + my $order_0_0 = { + biblionumber => $biblionumber_0_0, + quantity => 2, + listprice => 82.000000, + unitprice => 73.80000, + quantityreceived => 2, + basketno => $basketno_0_0, + invoiceid => $invoiceid_0_0, + rrp => 82.00, + ecost => 73.80, + gstrate => 0.0500, + discount => 10.0000, + datereceived => $today + }; + $order_0_0 = C4::Acquisition::populate_order_with_prices( + { + order => $order_0_0, + booksellerid => 'just_something', + ordering => 1, + } + ); + + # Note that this configuration is correct \o/ + compare( + { + got => $order_0_0->{rrpgsti}, + expected => 86.10, + conf => '0 0', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_0_0->{rrpgste}, + expected => 82.00, + conf => '0 0', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_0_0->{ecostgsti}, + expected => 77.49, + conf => '0 0', + field => 'ecostgsti' + } + ); + compare( + { + got => $order_0_0->{ecostgste}, + expected => 73.80, + conf => '0 0', + field => 'ecostgste' + } + ); + compare( + { + got => $order_0_0->{gstvalue}, + expected => 7.38, + conf => '0 0', + field => 'gstvalue' + } + ); + compare( + { + got => $order_0_0->{totalgsti}, + expected => 154.98, + conf => '0 0', + field => 'totalgsti' + } + ); + compare( + { + got => $order_0_0->{totalgste}, + expected => 147.60, + conf => '0 0', + field => 'totalgste' + } + ); + }; + + subtest 'Configuration 1: 1 1' => sub { + plan tests => 7; + $bookseller_module->mock( + 'fetch', + sub { + return { listincgst => 1, invoiceincgst => 1 }; + } + ); + + my $biblionumber_1_1 = 43; + my $order_1_1 = { + biblionumber => $biblionumber_1_1, + quantity => 2, + listprice => 82.000000, + unitprice => 73.800000, + quantityreceived => 2, + basketno => $basketno_1_1, + invoiceid => $invoiceid_1_1, + rrp => 82.00, + ecost => 73.80, + gstrate => 0.0500, + discount => 10.0000, + datereceived => $today + }; + + $order_1_1 = C4::Acquisition::populate_order_with_prices( + { + order => $order_1_1, + booksellerid => 'just_something', + ordering => 1, + } + ); + + # Note that this configuration is *not* correct + # gstvalue should be 7.03 instead of 7.02 + compare( + { + got => $order_1_1->{rrpgsti}, + expected => 82.00, + conf => '1 1', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_1_1->{rrpgste}, + expected => 78.10, + conf => '1 1', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_1_1->{ecostgsti}, + expected => 73.80, + conf => '1 1', + field => 'ecostgsti' + } + ); + compare( + { + got => $order_1_1->{ecostgste}, + expected => 70.29, + conf => '1 1', + field => 'ecostgste' + } + ); + compare( + { + got => $order_1_1->{gstvalue}, + expected => 7.02, + conf => '1 1', + field => 'gstvalue' + } + ); + compare( + { + got => $order_1_1->{totalgsti}, + expected => 147.60, + conf => '1 1', + field => 'totalgsti' + } + ); + compare( + { + got => $order_1_1->{totalgste}, + expected => 140.58, + conf => '1 1', + field => 'totalgste' + } + ); + }; + + subtest 'Configuration 1: 1 0' => sub { + plan tests => 7; + $bookseller_module->mock( + 'fetch', + sub { + return { listincgst => 1, invoiceincgst => 0 }; + } + ); + + my $biblionumber_1_0 = 44; + my $order_1_0 = { + biblionumber => $biblionumber_1_0, + quantity => 2, + listprice => 82.000000, + unitprice => 73.804500, + quantityreceived => 2, + basketno => $basketno_1_1, + invoiceid => $invoiceid_1_1, + rrp => 82.01, + ecost => 73.80, + gstrate => 0.0500, + discount => 10.0000, + datereceived => $today + }; + + $order_1_0 = C4::Acquisition::populate_order_with_prices( + { + order => $order_1_0, + booksellerid => 'just_something', + ordering => 1, + } + ); + + # Note that this configuration is *not* correct! + # rrp gsti should be 82 (what we inserted!) + # gstvalue should be 7.03 instead of 7.02 + + compare( + { + got => $order_1_0->{rrpgsti}, + expected => 82.01, + conf => '1 0', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_1_0->{rrpgste}, + expected => 78.10, + conf => '1 0', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_1_0->{ecostgsti}, + expected => 73.80, + conf => '1 0', + field => 'ecostgsti' + } + ); + compare( + { + got => $order_1_0->{ecostgste}, + expected => 70.29, + conf => '1 0', + field => 'ecostgste' + } + ); + compare( + { + got => $order_1_0->{gstvalue}, + expected => 7.02, + conf => '1 0', + field => 'gstvalue' + } + ); + compare( + { + got => $order_1_0->{totalgsti}, + expected => 147.60, + conf => '1 0', + field => 'totalgsti' + } + ); + compare( + { + got => $order_1_0->{totalgste}, + expected => 140.58, + conf => '1 0', + field => 'totalgste' + } + ); + }; + + subtest 'Configuration 1: 0 1' => sub { + plan tests => 7; + $bookseller_module->mock( + 'fetch', + sub { + return { listincgst => 0, invoiceincgst => 1 }; + } + ); + + my $biblionumber_0_1 = 45; + my $order_0_1 = { + biblionumber => $biblionumber_0_1, + quantity => 2, + listprice => 82.000000, + unitprice => 73.800000, + quantityreceived => 2, + basketno => $basketno_1_1, + invoiceid => $invoiceid_1_1, + rrp => 82.00, + ecost => 73.80, + gstrate => 0.0500, + discount => 10.0000, + datereceived => $today + }; + + $order_0_1 = C4::Acquisition::populate_order_with_prices( + { + order => $order_0_1, + booksellerid => 'just_something', + ordering => 1, + } + ); + + # Note that this configuration is correct \o/ + compare( + { + got => $order_0_1->{rrpgsti}, + expected => 86.10, + conf => '1 0', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_0_1->{rrpgste}, + expected => 82.00, + conf => '1 0', + field => 'rrpgsti' + } + ); + compare( + { + got => $order_0_1->{ecostgsti}, + expected => 77.49, + conf => '1 0', + field => 'ecostgsti' + } + ); + compare( + { + got => $order_0_1->{ecostgste}, + expected => 73.80, + conf => '1 0', + field => 'ecostgste' + } + ); + compare( + { + got => $order_0_1->{gstvalue}, + expected => 7.38, + conf => '1 0', + field => 'gstvalue' + } + ); + compare( + { + got => $order_0_1->{totalgsti}, + expected => 154.98, + conf => '1 0', + field => 'totalgsti' + } + ); + compare( + { + got => $order_0_1->{totalgste}, + expected => 147.60, + conf => '1 0', + field => 'totalgste' + } + ); + }; +} + +sub compare { + my ($params) = @_; + is( + Koha::Number::Price->new( $params->{got} )->format, + Koha::Number::Price->new( $params->{expected} )->format, +"configuration $params->{conf}: $params->{field} should be correctly calculated" + ); +} -- 1.9.1