Bug 18736: Unit tests for rounding
[srvgit] / t / db_dependent / Budgets.t
index a583d9b..816d97a 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 use Modern::Perl;
-use Test::More tests => 144;
+use Test::More tests => 146;
 
 BEGIN {
     use_ok('C4::Budgets')
@@ -11,12 +11,19 @@ use C4::Acquisition;
 
 use Koha::Acquisition::Booksellers;
 use Koha::Acquisition::Orders;
+use Koha::Acquisition::Funds;
 use Koha::Patrons;
+use Koha::Number::Price;
 
 use t::lib::TestBuilder;
+use t::lib::Mocks;
+use Koha::DateUtils;
 
 use YAML;
 
+use t::lib::Mocks;
+t::lib::Mocks::mock_preference('OrderPriceRounding','');
+
 my $schema  = Koha::Database->new->schema;
 $schema->storage->txn_begin;
 my $builder = t::lib::TestBuilder->new;
@@ -28,11 +35,13 @@ my $library = $builder->build({
     source => 'Branch',
 });
 
-# Mock userenv
-local $SIG{__WARN__} = sub { warn $_[0] unless $_[0] =~ /redefined/ };
-my $userenv;
-*C4::Context::userenv = \&Mock_userenv;
-$userenv = { flags => 1, id => 'my_userid', branch => $library->{branchcode} };
+t::lib::Mocks::mock_userenv(
+    {
+        flags => 1,
+        userid => 'my_userid',
+        branch => $library->{branchcode},
+    }
+);
 
 #
 # Budget Periods :
@@ -57,6 +66,7 @@ my $my_budgetperiod = {
     budget_period_enddate     => '2008-12-31',
     budget_period_description => 'MAPERI',
     budget_period_active      => 0,
+    budget_period_id          => '', # Bug 21604
 };
 $bpid = AddBudgetPeriod($my_budgetperiod);
 isnt( $bpid, undef, 'AddBugetPeriod does not returns undef' );
@@ -125,6 +135,7 @@ my $my_budget = {
     budget_name      => 'Periodiques',
     budget_notes     => 'This is a note',
     budget_period_id => $bpid,
+    budget_encumb    => '', # Bug 21604
 };
 my $budget_id = AddBudget($my_budget);
 isnt( $budget_id, undef, 'AddBudget does not returns undef' );
@@ -470,12 +481,19 @@ ModReceiveOrder({
    invoice           => $test_invoice,
    received_items    => [],
 } );
+t::lib::Mocks::mock_preference('OrderPriceRounding','');
 
 is ( GetBudgetSpent( $fund ), 6, "total shipping cost is 6");
-is ( GetBudgetOrdered( $fund ), '20.000000', "total ordered price is 20");
+is ( GetBudgetOrdered( $fund ), '20', "total ordered price is 20");
 
 
 # CloneBudgetPeriod
+# Let's make sure our timestamp is old
+my @orig_funds = Koha::Acquisition::Funds->search({ budget_period_id => $budget_period_id });
+foreach my $fund (@orig_funds){
+    $fund->timestamp('1999-12-31 23:59:59')->store;
+}
+
 my $budget_period_id_cloned = C4::Budgets::CloneBudgetPeriod(
     {
         budget_period_id        => $budget_period_id,
@@ -488,6 +506,13 @@ my $budget_period_id_cloned = C4::Budgets::CloneBudgetPeriod(
 my $budget_period_cloned = C4::Budgets::GetBudgetPeriod($budget_period_id_cloned);
 is($budget_period_cloned->{budget_period_description}, 'Budget Period Cloned', 'Cloned budget\'s description is updated.');
 
+my $budget_cloned = C4::Budgets::GetBudgets({ budget_period_id => $budget_period_id_cloned });
+my $budget_time =  $budget_cloned->[0]->{timestamp};
+
+isnt($budget_time, '1999-12-31 23:59:59', "New budget has an updated timestamp");
+
+
+
 my $budget_hierarchy        = GetBudgetHierarchy($budget_period_id);
 my $budget_hierarchy_cloned = GetBudgetHierarchy($budget_period_id_cloned);
 
@@ -798,6 +823,236 @@ is( scalar @{$authCat}, 0, "GetBudgetAuthCats returns only non-empty sorting cat
 
 # /Test GetBudgetAuthCats
 
+subtest 'GetBudgetSpent and GetBudgetOrdered' => sub {
+    plan tests => 20;
+
+    my $budget_period = $builder->build({
+        source => 'Aqbudgetperiod',
+        value  => {
+            budget_period_active => 1,
+            budget_total => 10000,
+        }
+    });
+    my $budget = $builder->build({
+        source => 'Aqbudget',
+        value  => {
+            budget_amount => 1000,
+            budget_encumb => undef,
+            budget_expend => undef,
+            budget_period_id => $budget_period->{budget_period_id},
+            budget_parent_id => undef,
+        }
+    });
+    my $invoice = $builder->build({
+        source => 'Aqinvoice',
+        value  => {
+            closedate => undef,
+        }
+    });
+
+    my $spent     = GetBudgetSpent( $budget->{budget_id} );
+    my $ordered   = GetBudgetOrdered( $budget->{budget_id} );
+    my $hierarchy = GetBudgetHierarchy($budget_period->{budget_period_id} );
+
+    is( $spent, 0, "New budget, no orders/invoices, should be nothing spent");
+    is( $ordered, 0, "New budget, no orders/invoices, should be nothing ordered");
+    is( @$hierarchy[0]->{total_spent},0,"New budgets, no orders/invoices, budget hierarchy shows 0 spent");
+    is( @$hierarchy[0]->{total_ordered},0,"New budgets, no orders/invoices, budget hierarchy shows 0 ordered");
+
+    my $inv_adj_1 = $builder->build({
+        source => 'AqinvoiceAdjustment',
+        value  => {
+            invoiceid     => $invoice->{invoiceid},
+            adjustment    => 3,
+            encumber_open => 0,
+            budget_id     => $budget->{budget_id},
+        }
+    });
+
+    $spent = GetBudgetSpent( $budget->{budget_id} );
+    $ordered = GetBudgetOrdered( $budget->{budget_id} );
+    $hierarchy = GetBudgetHierarchy($budget_period->{budget_period_id} );
+    is( $spent, 0, "After adding invoice adjustment on open invoice, should be nothing spent");
+    is( $ordered, 0, "After adding invoice adjustment on open invoice not encumbered, should be nothing ordered");
+    is( @$hierarchy[0]->{total_spent},0,"After adding invoice adjustment on open invoice, budget hierarchy shows 0 spent");
+    is( @$hierarchy[0]->{total_ordered},0,"After adding invoice adjustment on open invoice, budget hierarchy shows 0 ordered");
+
+    my $inv_adj_2 = $builder->build({
+        source => 'AqinvoiceAdjustment',
+        value  => {
+            invoiceid     => $invoice->{invoiceid},
+            adjustment    => 3,
+            encumber_open => 1,
+            budget_id     => $budget->{budget_id},
+        }
+    });
+
+    $spent = GetBudgetSpent( $budget->{budget_id} );
+    $ordered = GetBudgetOrdered( $budget->{budget_id} );
+    $hierarchy = GetBudgetHierarchy($budget_period->{budget_period_id} );
+    is( $spent, 0, "After adding invoice adjustment on open invoice, should be nothing spent");
+    is( $ordered, 3, "After adding invoice adjustment on open invoice encumbered, should be 3 ordered");
+    is( @$hierarchy[0]->{total_spent},0,"After adding invoice adjustment on open invoice encumbered, budget hierarchy shows 0 spent");
+    is( @$hierarchy[0]->{total_ordered},3,"After adding invoice adjustment on open invoice encumbered, budget hierarchy shows 3 ordered");
+
+    my $invoice_2 = $builder->build({
+        source => 'Aqinvoice',
+        value  => {
+            closedate => '2017-07-01',
+        }
+    });
+    my $inv_adj_3 = $builder->build({
+        source => 'AqinvoiceAdjustment',
+        value  => {
+            invoiceid     => $invoice_2->{invoiceid},
+            adjustment    => 3,
+            encumber_open => 0,
+            budget_id     => $budget->{budget_id},
+        }
+    });
+    my $inv_adj_4 = $builder->build({
+        source => 'AqinvoiceAdjustment',
+        value  => {
+            invoiceid     => $invoice_2->{invoiceid},
+            adjustment    => 3,
+            encumber_open => 1,
+            budget_id     => $budget->{budget_id},
+        }
+    });
+
+    $spent = GetBudgetSpent( $budget->{budget_id} );
+    $ordered = GetBudgetOrdered( $budget->{budget_id} );
+    $hierarchy = GetBudgetHierarchy($budget_period->{budget_period_id} );
+    is( $spent, 6, "After adding invoice adjustment on closed invoice, should be 6 spent, encumber has no affect once closed");
+    is( $ordered, 3, "After adding invoice adjustment on closed invoice, should still be 3 ordered");
+    is( @$hierarchy[0]->{total_spent},6,"After adding invoice adjustment on closed invoice, budget hierarchy shows 6 spent");
+    is( @$hierarchy[0]->{total_ordered},3,"After adding invoice adjustment on closed invoice, budget hierarchy still shows 3 ordered");
+
+    my $budget0 = $builder->build({
+        source => 'Aqbudget',
+        value  => {
+            budget_amount => 1000,
+            budget_encumb => undef,
+            budget_expend => undef,
+            budget_period_id => $budget_period->{budget_period_id},
+            budget_parent_id => $budget->{budget_id},
+        }
+    });
+    my $inv_adj_5 = $builder->build({
+        source => 'AqinvoiceAdjustment',
+        value  => {
+            invoiceid     => $invoice->{invoiceid},
+            adjustment    => 3,
+            encumber_open => 1,
+            budget_id     => $budget0->{budget_id},
+        }
+    });
+    my $inv_adj_6 = $builder->build({
+        source => 'AqinvoiceAdjustment',
+        value  => {
+            invoiceid     => $invoice_2->{invoiceid},
+            adjustment    => 3,
+            encumber_open => 1,
+            budget_id     => $budget0->{budget_id},
+        }
+    });
+
+    $spent = GetBudgetSpent( $budget->{budget_id} );
+    $ordered = GetBudgetOrdered( $budget->{budget_id} );
+    $hierarchy = GetBudgetHierarchy($budget_period->{budget_period_id} );
+    is( $spent, 6, "After adding invoice adjustment on a child budget should be 6 spent/budget unaffected");
+    is( $ordered, 3, "After adding invoice adjustment on a child budget, should still be 3 ordered/budget unaffected");
+    is( @$hierarchy[0]->{total_spent},9,"After adding invoice adjustment on child budget, budget hierarchy shows 9 spent");
+    is( @$hierarchy[0]->{total_ordered},6,"After adding invoice adjustment on child budget, budget hierarchy shows 6 ordered");
+};
+
+subtest 'OrderPriceRounding GetBudgetSpent GetBudgetOrdered tests' => sub {
+
+    plan tests => 8;
+
+#Let's build an order, we need a couple things though
+
+    my $spent_biblio = $builder->build({ source => 'Biblio' });
+    my $spent_basket = $builder->build({ source => 'Aqbasket', value => { is_standing => 0 } });
+    my $spent_invoice = $builder->build({ source => 'Aqinvoice'});
+    my $spent_currency = $builder->build({ source => 'Currency', value => { active => 1, archived => 0, symbol => 'F', rate => 2, isocode => undef, currency => 'FOO' }  });
+    my $spent_vendor = $builder->build({ source => 'Aqbookseller',value => { listincgst => 0, listprice => $spent_currency->{currency}, invoiceprice => $spent_currency->{currency} } });
+    my $spent_orderinfo = {
+        basketno => $spent_basket->{basketno},
+        booksellerid => $spent_vendor->{id},
+        rrp => 16.99,
+        discount => .42,
+        ecost => 16.91,
+        biblionumber => $spent_biblio->{biblionumber},
+        currency => $spent_currency->{currency},
+        tax_rate_on_ordering => 0,
+        tax_value_on_ordering => 0,
+        tax_rate_on_receiving => 0,
+        tax_value_on_receiving => 0,
+        quantity => 8,
+        quantityreceived => 0,
+        datecancellationprinted => undef,
+        datereceived => undef,
+    };
+
+#Okay we have basically what the user would enter, now we do some maths
+
+    $spent_orderinfo = C4::Acquisition::populate_order_with_prices({
+            order        => $spent_orderinfo,
+            booksellerid => $spent_orderinfo->{booksellerid},
+            ordering     => 1,
+    });
+
+#And let's place the order
+
+    my $spent_order = $builder->build({ source => 'Aqorder', value => $spent_orderinfo });
+    t::lib::Mocks::mock_preference('OrderPriceRounding','');
+    my $spent_ordered = GetBudgetOrdered( $spent_order->{budget_id} );
+
+    is($spent_orderinfo->{ecost_tax_excluded}, 9.854200,'We store extra precision in price calculation');
+    is( Koha::Number::Price->new($spent_orderinfo->{ecost_tax_excluded})->format(), 9.85,'But the price as formatted is two digits');
+    is($spent_ordered,'78.8336',"We expect the ordered amount to be equal to the estimated price times quantity with full precision");
+
+    t::lib::Mocks::mock_preference('OrderPriceRounding','nearest_cent');
+    $spent_ordered = GetBudgetOrdered( $spent_order->{budget_id} );
+    is($spent_ordered,'78.8',"We expect the ordered amount to be equal to the estimated price rounded times quantity");
+
+#Okay, now we can receive the order, giving the price as the user would
+
+    $spent_orderinfo->{unitprice} = 9.85; #we are paying what we expected
+
+#Do our maths
+
+    $spent_orderinfo = C4::Acquisition::populate_order_with_prices({
+            order        => $spent_orderinfo,
+            booksellerid => $spent_orderinfo->{booksellerid},
+            receiving    => 1,
+    });
+    my $received_order = $builder->build({ source => 'Aqorder', value => $spent_orderinfo });
+
+#And receive
+
+    ModReceiveOrder({
+            biblionumber => $spent_order->{biblionumber},
+            order => $received_order,
+            invoice => $spent_invoice,
+            quantityreceived => $spent_order->{quantity},
+            budget_id => $spent_order->{budget_id},
+            received_items => [],
+    });
+
+    t::lib::Mocks::mock_preference('OrderPriceRounding','');
+    my $spent_spent = GetBudgetSpent( $spent_order->{budget_id} );
+    is($spent_orderinfo->{unitprice_tax_excluded}, 9.854200,'We store extra precision in price calculation');
+    is( Koha::Number::Price->new($spent_orderinfo->{unitprice_tax_excluded})->format(), 9.85,'But the price as formatted is two digits');
+    is($spent_spent,'78.8336',"We expect the spent amount to be equal to the estimated price times quantity with full precision");
+
+    t::lib::Mocks::mock_preference('OrderPriceRounding','nearest_cent');
+    $spent_spent = GetBudgetSpent( $spent_order->{budget_id} );
+    is($spent_spent,'78.8',"We expect the spent amount to be equal to the estimated price rounded times quantity");
+
+};
+
 sub _get_dependencies {
     my ($budget_hierarchy) = @_;
     my $graph;
@@ -822,8 +1077,3 @@ sub _get_budgetname_by_id {
       @$budgets;
     return $budget_name;
 }
-
-# C4::Context->userenv
-sub Mock_userenv {
-    return $userenv;
-}