Bug 11201: Display overdues in red
[koha.git] / C4 / Budgets.pm
1 package C4::Budgets;
2
3 # Copyright 2000-2002 Katipo Communications
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
10 # version.
11 #
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 use strict;
21 #use warnings; FIXME - Bug 2505
22 use C4::Context;
23 use Koha::Database;
24 use C4::Debug;
25 use vars qw($VERSION @ISA @EXPORT);
26
27 BEGIN {
28         # set the version for version checking
29     $VERSION = 3.07.00.049;
30         require Exporter;
31         @ISA    = qw(Exporter);
32         @EXPORT = qw(
33
34         &GetBudget
35         &GetBudgetByOrderNumber
36         &GetBudgetByCode
37         &GetBudgets
38         &GetBudgetHierarchy
39             &AddBudget
40         &ModBudget
41         &DelBudget
42         &GetBudgetSpent
43         &GetBudgetOrdered
44         &GetBudgetName
45         &GetPeriodsCount
46         GetBudgetHierarchySpent
47         GetBudgetHierarchyOrdered
48
49         &GetBudgetUsers
50         &ModBudgetUsers
51         &CanUserUseBudget
52         &CanUserModifyBudget
53
54             &GetBudgetPeriod
55         &GetBudgetPeriods
56         &ModBudgetPeriod
57         &AddBudgetPeriod
58             &DelBudgetPeriod
59
60         &ModBudgetPlan
61
62         &GetCurrency
63         &GetCurrencies
64         &ModCurrencies
65         &ConvertCurrency
66         
67                 &GetBudgetsPlanCell
68         &AddBudgetPlanValue
69         &GetBudgetAuthCats
70         &BudgetHasChildren
71         &CheckBudgetParent
72         &CheckBudgetParentPerm
73
74         &HideCols
75         &GetCols
76         );
77 }
78
79 # ----------------------------BUDGETS.PM-----------------------------";
80
81
82 =head1 FUNCTIONS ABOUT BUDGETS
83
84 =cut
85
86 sub HideCols {
87     my ( $authcat, @hide_cols ) = @_;
88     my $dbh = C4::Context->dbh;
89
90     my $sth1 = $dbh->prepare(
91         qq|
92         UPDATE aqbudgets_planning SET display = 0 
93         WHERE authcat = ? 
94         AND  authvalue = ? |
95     );
96     foreach my $authvalue (@hide_cols) {
97 #        $sth1->{TraceLevel} = 3;
98         $sth1->execute(  $authcat, $authvalue );
99     }
100 }
101
102 sub GetCols {
103     my ( $authcat, $authvalue ) = @_;
104
105     my $dbh = C4::Context->dbh;
106     my $sth = $dbh->prepare(
107         qq|
108         SELECT count(display) as cnt from aqbudgets_planning
109         WHERE  authcat = ? 
110         AND authvalue = ? and display  = 0   |
111     );
112
113 #    $sth->{TraceLevel} = 3;
114     $sth->execute( $authcat, $authvalue );
115     my $res  = $sth->fetchrow_hashref;
116
117     return  $res->{cnt} > 0 ? 0: 1
118
119 }
120
121 sub CheckBudgetParentPerm {
122     my ( $budget, $borrower_id ) = @_;
123     my $depth = $budget->{depth};
124     my $parent_id = $budget->{budget_parent_id};
125     while ($depth) {
126         my $parent = GetBudget($parent_id);
127         $parent_id = $parent->{budget_parent_id};
128         if ( $parent->{budget_owner_id} == $borrower_id ) {
129             return 1;
130         }
131         $depth--
132     }
133     return 0;
134 }
135
136 sub AddBudgetPeriod {
137     my ($budgetperiod) = @_;
138     return unless($budgetperiod->{budget_period_startdate} && $budgetperiod->{budget_period_enddate});
139
140     my $resultset = Koha::Database->new()->schema->resultset('Aqbudgetperiod');
141     return $resultset->create($budgetperiod)->id;
142 }
143 # -------------------------------------------------------------------
144 sub GetPeriodsCount {
145     my $dbh = C4::Context->dbh;
146     my $sth = $dbh->prepare("
147         SELECT COUNT(*) AS sum FROM aqbudgetperiods ");
148     $sth->execute();
149     my $res = $sth->fetchrow_hashref;
150     return $res->{'sum'};
151 }
152
153 # -------------------------------------------------------------------
154 sub CheckBudgetParent {
155     my ( $new_parent, $budget ) = @_;
156     my $new_parent_id = $new_parent->{'budget_id'};
157     my $budget_id     = $budget->{'budget_id'};
158     my $dbh           = C4::Context->dbh;
159     my $parent_id_tmp = $new_parent_id;
160
161     # check new-parent is not a child (or a child's child ;)
162     my $sth = $dbh->prepare(qq|
163         SELECT budget_parent_id FROM
164             aqbudgets where budget_id = ? | );
165     while (1) {
166         $sth->execute($parent_id_tmp);
167         my $res = $sth->fetchrow_hashref;
168         if ( $res->{'budget_parent_id'} == $budget_id ) {
169             return 1;
170         }
171         if ( not defined $res->{'budget_parent_id'} ) {
172             return 0;
173         }
174         $parent_id_tmp = $res->{'budget_parent_id'};
175     }
176 }
177
178 # -------------------------------------------------------------------
179 sub BudgetHasChildren {
180     my ( $budget_id  ) = @_;
181     my $dbh = C4::Context->dbh;
182     my $sth = $dbh->prepare(qq|
183        SELECT count(*) as sum FROM  aqbudgets
184         WHERE budget_parent_id = ?   | );
185     $sth->execute( $budget_id );
186     my $sum = $sth->fetchrow_hashref;
187     return $sum->{'sum'};
188 }
189
190 sub GetBudgetChildren {
191     my ( $budget_id ) = @_;
192     my $dbh = C4::Context->dbh;
193     return $dbh->selectall_arrayref(q|
194        SELECT  * FROM  aqbudgets
195         WHERE budget_parent_id = ?
196     |, { Slice => {} }, $budget_id );
197 }
198
199 sub SetOwnerToFundHierarchy {
200     my ( $budget_id, $borrowernumber ) = @_;
201
202     my $budget = GetBudget( $budget_id );
203     $budget->{budget_owner_id} = $borrowernumber;
204     ModBudget( $budget );
205     my $children = GetBudgetChildren( $budget_id );
206     for my $child ( @$children ) {
207         SetOwnerToFundHierarchy( $child->{budget_id}, $borrowernumber );
208     }
209 }
210
211 # -------------------------------------------------------------------
212 sub GetBudgetsPlanCell {
213     my ( $cell, $period, $budget ) = @_;
214     my ($actual, $sth);
215     my $dbh = C4::Context->dbh;
216     if ( $cell->{'authcat'} eq 'MONTHS' ) {
217         # get the actual amount
218         $sth = $dbh->prepare( qq|
219
220             SELECT SUM(ecost) AS actual FROM aqorders
221                 WHERE    budget_id = ? AND
222                 entrydate like "$cell->{'authvalue'}%"  |
223         );
224         $sth->execute( $cell->{'budget_id'} );
225     } elsif ( $cell->{'authcat'} eq 'BRANCHES' ) {
226         # get the actual amount
227         $sth = $dbh->prepare( qq|
228
229             SELECT SUM(ecost) FROM aqorders
230                 LEFT JOIN aqorders_items
231                 ON (aqorders.ordernumber = aqorders_items.ordernumber)
232                 LEFT JOIN items
233                 ON (aqorders_items.itemnumber = items.itemnumber)
234                 WHERE budget_id = ? AND homebranch = ? |          );
235
236         $sth->execute( $cell->{'budget_id'}, $cell->{'authvalue'} );
237     } elsif ( $cell->{'authcat'} eq 'ITEMTYPES' ) {
238         # get the actual amount
239         $sth = $dbh->prepare(  qq|
240
241             SELECT SUM( ecost *  quantity) AS actual
242                 FROM aqorders JOIN biblioitems
243                 ON (biblioitems.biblionumber = aqorders.biblionumber )
244                 WHERE aqorders.budget_id = ? and itemtype  = ? |
245         );
246         $sth->execute(  $cell->{'budget_id'},
247                         $cell->{'authvalue'} );
248     }
249     # ELSE GENERIC ORDERS SORT1/SORT2 STAT COUNT.
250     else {
251         # get the actual amount
252         $sth = $dbh->prepare( qq|
253
254         SELECT  SUM(ecost * quantity) AS actual
255             FROM aqorders
256             JOIN aqbudgets ON (aqbudgets.budget_id = aqorders.budget_id )
257             WHERE  aqorders.budget_id = ? AND
258                 ((aqbudgets.sort1_authcat = ? AND sort1 =?) OR
259                 (aqbudgets.sort2_authcat = ? AND sort2 =?))    |
260         );
261         $sth->execute(  $cell->{'budget_id'},
262                         $budget->{'sort1_authcat'},
263                         $cell->{'authvalue'},
264                         $budget->{'sort2_authcat'},
265                         $cell->{'authvalue'}
266         );
267     }
268     $actual = $sth->fetchrow_array;
269
270     # get the estimated amount
271     $sth = $dbh->prepare( qq|
272
273         SELECT estimated_amount AS estimated, display FROM aqbudgets_planning
274             WHERE budget_period_id = ? AND
275                 budget_id = ? AND
276                 authvalue = ? AND
277                 authcat = ?         |
278     );
279     $sth->execute(  $cell->{'budget_period_id'},
280                     $cell->{'budget_id'},
281                     $cell->{'authvalue'},
282                     $cell->{'authcat'},
283     );
284
285
286     my $res  = $sth->fetchrow_hashref;
287   #  my $display = $res->{'display'};
288     my $estimated = $res->{'estimated'};
289
290
291     return $actual, $estimated;
292 }
293
294 # -------------------------------------------------------------------
295 sub ModBudgetPlan {
296     my ( $budget_plan, $budget_period_id, $authcat ) = @_;
297     my $dbh = C4::Context->dbh;
298     foreach my $buds (@$budget_plan) {
299         my $lines = $buds->{lines};
300         my $sth = $dbh->prepare( qq|
301                 DELETE FROM aqbudgets_planning
302                     WHERE   budget_period_id   = ? AND
303                             budget_id   = ? AND
304                             authcat            = ? |
305         );
306     #delete a aqplan line of cells, then insert new cells, 
307     # these could be UPDATES rather than DEL/INSERTS...
308         $sth->execute( $budget_period_id,  $lines->[0]{budget_id}   , $authcat );
309
310         foreach my $cell (@$lines) {
311             my $sth = $dbh->prepare( qq|
312
313                 INSERT INTO aqbudgets_planning
314                      SET   budget_id     = ?,
315                      budget_period_id  = ?,
316                      authcat          = ?,
317                      estimated_amount  = ?,
318                      authvalue       = ?  |
319             );
320             $sth->execute(
321                             $cell->{'budget_id'},
322                             $cell->{'budget_period_id'},
323                             $cell->{'authcat'},
324                             $cell->{'estimated_amount'},
325                             $cell->{'authvalue'},
326             );
327         }
328     }
329 }
330
331 # -------------------------------------------------------------------
332 sub GetBudgetSpent {
333         my ($budget_id) = @_;
334         my $dbh = C4::Context->dbh;
335         my $sth = $dbh->prepare(qq|
336         SELECT SUM( COALESCE(unitprice, ecost) * quantity ) AS sum FROM aqorders
337             WHERE budget_id = ? AND
338             quantityreceived > 0 AND
339             datecancellationprinted IS NULL
340     |);
341         $sth->execute($budget_id);
342         my $sum =  $sth->fetchrow_array;
343
344     $sth = $dbh->prepare(qq|
345         SELECT SUM(shipmentcost) AS sum
346         FROM aqinvoices
347         WHERE shipmentcost_budgetid = ?
348           AND closedate IS NOT NULL
349     |);
350     $sth->execute($budget_id);
351     my ($shipmentcost_sum) = $sth->fetchrow_array;
352     $sum += $shipmentcost_sum;
353
354         return $sum;
355 }
356
357 # -------------------------------------------------------------------
358 sub GetBudgetOrdered {
359         my ($budget_id) = @_;
360         my $dbh = C4::Context->dbh;
361         my $sth = $dbh->prepare(qq|
362         SELECT SUM(ecost *  quantity) AS sum FROM aqorders
363             WHERE budget_id = ? AND
364             quantityreceived = 0 AND
365             datecancellationprinted IS NULL
366     |);
367         $sth->execute($budget_id);
368         my $sum =  $sth->fetchrow_array;
369
370     $sth = $dbh->prepare(qq|
371         SELECT SUM(shipmentcost) AS sum
372         FROM aqinvoices
373         WHERE shipmentcost_budgetid = ?
374           AND closedate IS NULL
375     |);
376     $sth->execute($budget_id);
377     my ($shipmentcost_sum) = $sth->fetchrow_array;
378     $sum += $shipmentcost_sum;
379
380         return $sum;
381 }
382
383 =head2 GetBudgetName
384
385   my $budget_name = &GetBudgetName($budget_id);
386
387 get the budget_name for a given budget_id
388
389 =cut
390
391 sub GetBudgetName {
392     my ( $budget_id ) = @_;
393     my $dbh         = C4::Context->dbh;
394     my $sth         = $dbh->prepare(
395         qq|
396         SELECT budget_name
397         FROM aqbudgets
398         WHERE budget_id = ?
399     |);
400
401     $sth->execute($budget_id);
402     return $sth->fetchrow_array;
403 }
404
405 # -------------------------------------------------------------------
406 sub GetBudgetAuthCats  {
407     my ($budget_period_id) = shift;
408     # now, populate the auth_cats_loop used in the budget planning button
409     # we must retrieve all auth values used by at least one budget
410     my $dbh = C4::Context->dbh;
411     my $sth=$dbh->prepare("SELECT sort1_authcat,sort2_authcat FROM aqbudgets WHERE budget_period_id=?");
412     $sth->execute($budget_period_id);
413     my %authcats;
414     while (my ($sort1_authcat,$sort2_authcat) = $sth->fetchrow) {
415         $authcats{$sort1_authcat}=1;
416         $authcats{$sort2_authcat}=1;
417     }
418     my @auth_cats_loop;
419     foreach (sort keys %authcats) {
420         push @auth_cats_loop,{ authcat => $_ };
421     }
422     return \@auth_cats_loop;
423 }
424
425 # -------------------------------------------------------------------
426 sub GetBudgetPeriods {
427         my ($filters,$orderby) = @_;
428
429     my $rs = Koha::Database->new()->schema->resultset('Aqbudgetperiod');
430     $rs = $rs->search( $filters, { order_by => $orderby } );
431     $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
432     return [ $rs->all ];
433 }
434 # -------------------------------------------------------------------
435 sub GetBudgetPeriod {
436         my ($budget_period_id) = @_;
437         my $dbh = C4::Context->dbh;
438         ## $total = number of records linked to the record that must be deleted
439         my $total = 0;
440         ## get information about the record that will be deleted
441         my $sth;
442         if ($budget_period_id) {
443                 $sth = $dbh->prepare( qq|
444               SELECT      *
445                 FROM aqbudgetperiods
446                 WHERE budget_period_id=? |
447                 );
448                 $sth->execute($budget_period_id);
449         } else {         # ACTIVE BUDGET
450                 $sth = $dbh->prepare(qq|
451                           SELECT      *
452                 FROM aqbudgetperiods
453                 WHERE budget_period_active=1 |
454                 );
455                 $sth->execute();
456         }
457         my $data = $sth->fetchrow_hashref;
458         return $data;
459 }
460
461 # -------------------------------------------------------------------
462 sub DelBudgetPeriod{
463         my ($budget_period_id) = @_;
464         my $dbh = C4::Context->dbh;
465           ; ## $total = number of records linked to the record that must be deleted
466     my $total = 0;
467
468         ## get information about the record that will be deleted
469         my $sth = $dbh->prepare(qq|
470                 DELETE 
471          FROM aqbudgetperiods
472          WHERE budget_period_id=? |
473         );
474         return $sth->execute($budget_period_id);
475 }
476
477 # -------------------------------------------------------------------
478 sub ModBudgetPeriod {
479     my ($budget_period) = @_;
480     my $result = Koha::Database->new()->schema->resultset('Aqbudgetperiod')->find($budget_period);
481     return unless($result);
482
483     $result = $result->update($budget_period);
484     return $result->in_storage;
485 }
486
487 # -------------------------------------------------------------------
488 sub GetBudgetHierarchy {
489     my ( $budget_period_id, $branchcode, $owner ) = @_;
490     my @bind_params;
491     my $dbh   = C4::Context->dbh;
492     my $query = qq|
493                     SELECT aqbudgets.*, aqbudgetperiods.budget_period_active, aqbudgetperiods.budget_period_description
494                     FROM aqbudgets 
495                     JOIN aqbudgetperiods USING (budget_period_id)|;
496                         
497         my @where_strings;
498     # show only period X if requested
499     if ($budget_period_id) {
500         push @where_strings," aqbudgets.budget_period_id = ?";
501         push @bind_params, $budget_period_id;
502     }
503         # show only budgets owned by me, my branch or everyone
504     if ($owner) {
505         if ($branchcode) {
506             push @where_strings,
507             qq{ (budget_owner_id = ? OR budget_branchcode = ? OR ((budget_branchcode IS NULL or budget_branchcode="") AND (budget_owner_id IS NULL OR budget_owner_id="")))};
508             push @bind_params, ( $owner, $branchcode );
509         } else {
510             push @where_strings, ' (budget_owner_id = ? OR budget_owner_id IS NULL or budget_owner_id ="") ';
511             push @bind_params, $owner;
512         }
513     } else {
514         if ($branchcode) {
515             push @where_strings," (budget_branchcode =? or budget_branchcode is NULL)";
516             push @bind_params, $branchcode;
517         }
518     }
519         $query.=" WHERE ".join(' AND ', @where_strings) if @where_strings;
520         $debug && warn $query,join(",",@bind_params);
521         my $sth = $dbh->prepare($query);
522         $sth->execute(@bind_params);
523
524     my %links;
525     # create hash with budget_id has key
526     while ( my $data = $sth->fetchrow_hashref ) {
527         $links{ $data->{'budget_id'} } = $data;
528     }
529
530     # link child to parent
531     my @first_parents;
532     foreach ( sort keys %links ) {
533         my $child = $links{$_};
534         if ( $child->{'budget_parent_id'} ) {
535             my $parent = $links{ $child->{'budget_parent_id'} };
536             if ($parent) {
537                 unless ( $parent->{'children'} ) {
538                     # init child arrayref
539                     $parent->{'children'} = [];
540                 }
541                 # add as child
542                 push @{ $parent->{'children'} }, $child;
543             }
544         } else {
545             push @first_parents, $child;
546         }
547     }
548
549     my @sort = ();
550     foreach my $first_parent (@first_parents) {
551         _add_budget_children(\@sort, $first_parent);
552     }
553
554     foreach my $budget (@sort) {
555         $budget->{budget_spent}   = GetBudgetSpent( $budget->{budget_id} );
556         $budget->{budget_ordered} = GetBudgetOrdered( $budget->{budget_id} );
557         $budget->{total_spent} = GetBudgetHierarchySpent( $budget->{budget_id} );
558         $budget->{total_ordered} = GetBudgetHierarchyOrdered( $budget->{budget_id} );
559     }
560     return \@sort;
561 }
562
563 # Recursive method to add a budget and its chidren to an array
564 sub _add_budget_children {
565     my $res = shift;
566     my $budget = shift;
567     push @$res, $budget;
568     my $children = $budget->{'children'} || [];
569     return unless @$children; # break recursivity
570     foreach my $child (@$children) {
571         _add_budget_children($res, $child);
572     }
573 }
574
575 # -------------------------------------------------------------------
576
577 sub AddBudget {
578     my ($budget) = @_;
579     return unless ($budget);
580
581     my $resultset = Koha::Database->new()->schema->resultset('Aqbudget');
582     return $resultset->create($budget)->id;
583 }
584
585 # -------------------------------------------------------------------
586 sub ModBudget {
587     my ($budget) = @_;
588     my $result = Koha::Database->new()->schema->resultset('Aqbudget')->find($budget);
589     return unless($result);
590
591     $result = $result->update($budget);
592     return $result->in_storage;
593 }
594
595 # -------------------------------------------------------------------
596 sub DelBudget {
597         my ($budget_id) = @_;
598         my $dbh         = C4::Context->dbh;
599         my $sth         = $dbh->prepare("delete from aqbudgets where budget_id=?");
600         my $rc          = $sth->execute($budget_id);
601         return $rc;
602 }
603
604
605 =head2 GetBudget
606
607   &GetBudget($budget_id);
608
609 get a specific budget
610
611 =cut
612
613 # -------------------------------------------------------------------
614 sub GetBudget {
615     my ( $budget_id ) = @_;
616     my $dbh = C4::Context->dbh;
617     my $query = "
618         SELECT *
619         FROM   aqbudgets
620         WHERE  budget_id=?
621         ";
622     my $sth = $dbh->prepare($query);
623     $sth->execute( $budget_id );
624     my $result = $sth->fetchrow_hashref;
625     return $result;
626 }
627
628 =head2 GetBudgetByOrderNumber
629
630   &GetBudgetByOrderNumber($ordernumber);
631
632 get a specific budget by order number
633
634 =cut
635
636 # -------------------------------------------------------------------
637 sub GetBudgetByOrderNumber {
638     my ( $ordernumber ) = @_;
639     my $dbh = C4::Context->dbh;
640     my $query = "
641         SELECT aqbudgets.*
642         FROM   aqbudgets, aqorders
643         WHERE  ordernumber=?
644         AND    aqorders.budget_id = aqbudgets.budget_id
645         ";
646     my $sth = $dbh->prepare($query);
647     $sth->execute( $ordernumber );
648     my $result = $sth->fetchrow_hashref;
649     return $result;
650 }
651
652 =head2 GetBudgetByCode
653
654     my $budget = &GetBudgetByCode($budget_code);
655
656 Retrieve all aqbudgets fields as a hashref for the budget that has
657 given budget_code
658
659 =cut
660
661 sub GetBudgetByCode {
662     my ( $budget_code ) = @_;
663
664     my $dbh = C4::Context->dbh;
665     my $query = qq{
666         SELECT *
667         FROM aqbudgets
668         WHERE budget_code = ?
669         ORDER BY budget_id DESC
670         LIMIT 1
671     };
672     my $sth = $dbh->prepare( $query );
673     $sth->execute( $budget_code );
674     return $sth->fetchrow_hashref;
675 }
676
677 =head2 GetBudgetHierarchySpent
678
679   my $spent = GetBudgetHierarchySpent( $budget_id );
680
681 Gets the total spent of the level and sublevels of $budget_id
682
683 =cut
684
685 sub GetBudgetHierarchySpent {
686     my ( $budget_id ) = @_;
687     my $dbh = C4::Context->dbh;
688     my $children_ids = $dbh->selectcol_arrayref(q|
689         SELECT budget_id
690         FROM   aqbudgets
691         WHERE  budget_parent_id = ?
692     |, {}, $budget_id );
693
694     my $total_spent = GetBudgetSpent( $budget_id );
695     for my $child_id ( @$children_ids ) {
696         $total_spent += GetBudgetHierarchySpent( $child_id );
697     }
698     return $total_spent;
699 }
700
701 =head2 GetBudgetHierarchyOrdered
702
703   my $ordered = GetBudgetHierarchyOrdered( $budget_id );
704
705 Gets the total ordered of the level and sublevels of $budget_id
706
707 =cut
708
709 sub GetBudgetHierarchyOrdered {
710     my ( $budget_id ) = @_;
711     my $dbh = C4::Context->dbh;
712     my $children_ids = $dbh->selectcol_arrayref(q|
713         SELECT budget_id
714         FROM   aqbudgets
715         WHERE  budget_parent_id = ?
716     |, {}, $budget_id );
717
718     my $total_ordered = GetBudgetOrdered( $budget_id );
719     for my $child_id ( @$children_ids ) {
720         $total_ordered += GetBudgetHierarchyOrdered( $child_id );
721     }
722     return $total_ordered;
723 }
724
725 =head2 GetBudgets
726
727   &GetBudgets($filter, $order_by);
728
729 gets all budgets
730
731 =cut
732
733 # -------------------------------------------------------------------
734 sub GetBudgets {
735     my ($filters, $orderby) = @_;
736     $orderby = 'budget_name' unless($orderby);
737
738     my $rs = Koha::Database->new()->schema->resultset('Aqbudget');
739     $rs = $rs->search( $filters, { order_by => $orderby } );
740     $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
741     return [ $rs->all  ];
742 }
743
744 =head2 GetBudgetUsers
745
746     my @borrowernumbers = &GetBudgetUsers($budget_id);
747
748 Return the list of borrowernumbers linked to a budget
749
750 =cut
751
752 sub GetBudgetUsers {
753     my ($budget_id) = @_;
754
755     my $dbh = C4::Context->dbh;
756     my $query = qq{
757         SELECT borrowernumber
758         FROM aqbudgetborrowers
759         WHERE budget_id = ?
760     };
761     my $sth = $dbh->prepare($query);
762     $sth->execute($budget_id);
763
764     my @borrowernumbers;
765     while (my ($borrowernumber) = $sth->fetchrow_array) {
766         push @borrowernumbers, $borrowernumber
767     }
768
769     return @borrowernumbers;
770 }
771
772 =head2 ModBudgetUsers
773
774     &ModBudgetUsers($budget_id, @borrowernumbers);
775
776 Modify the list of borrowernumbers linked to a budget
777
778 =cut
779
780 sub ModBudgetUsers {
781     my ($budget_id, @budget_users_id) = @_;
782
783     return unless $budget_id;
784
785     my $dbh = C4::Context->dbh;
786     my $query = "DELETE FROM aqbudgetborrowers WHERE budget_id = ?";
787     my $sth = $dbh->prepare($query);
788     $sth->execute($budget_id);
789
790     $query = qq{
791         INSERT INTO aqbudgetborrowers (budget_id, borrowernumber)
792         VALUES (?,?)
793     };
794     $sth = $dbh->prepare($query);
795     foreach my $borrowernumber (@budget_users_id) {
796         next unless $borrowernumber;
797         $sth->execute($budget_id, $borrowernumber);
798     }
799 }
800
801 sub CanUserUseBudget {
802     my ($borrower, $budget, $userflags) = @_;
803
804     if (not ref $borrower) {
805         $borrower = C4::Members::GetMember(borrowernumber => $borrower);
806     }
807     if (not ref $budget) {
808         $budget = GetBudget($budget);
809     }
810
811     return 0 unless ($borrower and $budget);
812
813     if (not defined $userflags) {
814         $userflags = C4::Auth::getuserflags($borrower->{flags},
815             $borrower->{userid});
816     }
817
818     unless ($userflags->{superlibrarian}
819     || (ref $userflags->{acquisition}
820         && $userflags->{acquisition}->{budget_manage_all})
821     || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
822     {
823         if (not exists $userflags->{acquisition}) {
824             return 0;
825         }
826
827         if (!ref $userflags->{acquisition} && !$userflags->{acquisition}) {
828             return 0;
829         }
830
831         # Budget restricted to owner
832         if ( $budget->{budget_permission} == 1 ) {
833             if (    $budget->{budget_owner_id}
834                 and $budget->{budget_owner_id} != $borrower->{borrowernumber} )
835             {
836                 return 0;
837             }
838         }
839
840         # Budget restricted to owner, users and library
841         elsif ( $budget->{budget_permission} == 2 ) {
842             my @budget_users = GetBudgetUsers( $budget->{budget_id} );
843
844             if (
845                 (
846                         $budget->{budget_owner_id}
847                     and $budget->{budget_owner_id} !=
848                     $borrower->{borrowernumber}
849                     or not $budget->{budget_owner_id}
850                 )
851                 and ( 0 == grep { $borrower->{borrowernumber} == $_ }
852                     @budget_users )
853                 and defined $budget->{budget_branchcode}
854                 and $budget->{budget_branchcode} ne
855                 C4::Context->userenv->{branch}
856               )
857             {
858                 return 0;
859             }
860         }
861
862         # Budget restricted to owner and users
863         elsif ( $budget->{budget_permission} == 3 ) {
864             my @budget_users = GetBudgetUsers( $budget->{budget_id} );
865             if (
866                 (
867                         $budget->{budget_owner_id}
868                     and $budget->{budget_owner_id} !=
869                     $borrower->{borrowernumber}
870                     or not $budget->{budget_owner_id}
871                 )
872                 and ( 0 == grep { $borrower->{borrowernumber} == $_ }
873                     @budget_users )
874               )
875             {
876                 return 0;
877             }
878         }
879     }
880
881     return 1;
882 }
883
884 sub CanUserModifyBudget {
885     my ($borrower, $budget, $userflags) = @_;
886
887     if (not ref $borrower) {
888         $borrower = C4::Members::GetMember(borrowernumber => $borrower);
889     }
890     if (not ref $budget) {
891         $budget = GetBudget($budget);
892     }
893
894     return 0 unless ($borrower and $budget);
895
896     if (not defined $userflags) {
897         $userflags = C4::Auth::getuserflags($borrower->{flags},
898             $borrower->{userid});
899     }
900
901     unless ($userflags->{superlibrarian}
902     || (ref $userflags->{acquisition}
903         && $userflags->{acquisition}->{budget_manage_all})
904     || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
905     {
906         if (!CanUserUseBudget($borrower, $budget, $userflags)) {
907             return 0;
908         }
909
910         if (ref $userflags->{acquisition}
911         && !$userflags->{acquisition}->{budget_modify}) {
912             return 0;
913         }
914     }
915
916     return 1;
917 }
918
919 # -------------------------------------------------------------------
920
921 =head2 GetCurrencies
922
923   @currencies = &GetCurrencies;
924
925 Returns the list of all known currencies.
926
927 C<$currencies> is a array; its elements are references-to-hash, whose
928 keys are the fields from the currency table in the Koha database.
929
930 =cut
931
932 sub GetCurrencies {
933     my $dbh   = C4::Context->dbh;
934     my $query = "
935         SELECT *
936         FROM   currency
937     ";
938     my $sth = $dbh->prepare($query);
939     $sth->execute;
940     my @results = ();
941     while ( my $data = $sth->fetchrow_hashref ) {
942         push( @results, $data );
943     }
944     return @results;
945 }
946
947 # -------------------------------------------------------------------
948
949 sub GetCurrency {
950     my $dbh   = C4::Context->dbh;
951     my $query = "
952         SELECT * FROM currency where active = '1'    ";
953     my $sth = $dbh->prepare($query);
954     $sth->execute;
955     my $r = $sth->fetchrow_hashref;
956     return $r;
957 }
958
959 =head2 ModCurrencies
960
961 &ModCurrencies($currency, $newrate);
962
963 Sets the exchange rate for C<$currency> to be C<$newrate>.
964
965 =cut
966
967 sub ModCurrencies {
968     my ( $currency, $rate ) = @_;
969     my $dbh   = C4::Context->dbh;
970     my $query = qq|
971         UPDATE currency
972         SET    rate=?
973         WHERE  currency=? |;
974     my $sth = $dbh->prepare($query);
975     $sth->execute( $rate, $currency );
976 }
977
978 # -------------------------------------------------------------------
979
980 =head2 ConvertCurrency
981
982   $foreignprice = &ConvertCurrency($currency, $localprice);
983
984 Converts the price C<$localprice> to foreign currency C<$currency> by
985 dividing by the exchange rate, and returns the result.
986
987 If no exchange rate is found, e is one to one.
988
989 =cut
990
991 sub ConvertCurrency {
992     my ( $currency, $price ) = @_;
993     my $dbh   = C4::Context->dbh;
994     my $query = "
995         SELECT rate
996         FROM   currency
997         WHERE  currency=?
998     ";
999     my $sth = $dbh->prepare($query);
1000     $sth->execute($currency);
1001     my $cur = ( $sth->fetchrow_array() )[0];
1002     unless ($cur) {
1003         $cur = 1;
1004     }
1005     return ( $price / $cur );
1006 }
1007
1008
1009 =head2 CloneBudgetPeriod
1010
1011   my $new_budget_period_id = CloneBudgetPeriod({
1012     budget_period_id => $budget_period_id,
1013     budget_period_startdate => $budget_period_startdate,
1014     budget_period_enddate   => $budget_period_enddate,
1015     mark_original_budget_as_inactive => 1n
1016     reset_all_budgets => 1,
1017   });
1018
1019 Clone a budget period with all budgets.
1020 If the mark_origin_budget_as_inactive is set (0 by default),
1021 the original budget will be marked as inactive.
1022
1023 If the reset_all_budgets is set (0 by default), all budget (fund)
1024 amounts will be reset.
1025
1026 =cut
1027
1028 sub CloneBudgetPeriod {
1029     my ($params)                  = @_;
1030     my $budget_period_id          = $params->{budget_period_id};
1031     my $budget_period_startdate   = $params->{budget_period_startdate};
1032     my $budget_period_enddate     = $params->{budget_period_enddate};
1033     my $budget_period_description = $params->{budget_period_description};
1034     my $mark_original_budget_as_inactive =
1035       $params->{mark_original_budget_as_inactive} || 0;
1036     my $reset_all_budgets = $params->{reset_all_budgets} || 0;
1037
1038     my $budget_period = GetBudgetPeriod($budget_period_id);
1039
1040     $budget_period->{budget_period_startdate}   = $budget_period_startdate;
1041     $budget_period->{budget_period_enddate}     = $budget_period_enddate;
1042     $budget_period->{budget_period_description} = $budget_period_description;
1043     # The new budget (budget_period) should be active by default
1044     $budget_period->{budget_period_active}    = 1;
1045     my $original_budget_period_id = $budget_period->{budget_period_id};
1046     delete $budget_period->{budget_period_id};
1047     my $new_budget_period_id = AddBudgetPeriod( $budget_period );
1048
1049     my $budgets = GetBudgetHierarchy($budget_period_id);
1050     CloneBudgetHierarchy(
1051         {
1052             budgets              => $budgets,
1053             new_budget_period_id => $new_budget_period_id
1054         }
1055     );
1056
1057     if ($mark_original_budget_as_inactive) {
1058         ModBudgetPeriod(
1059             {
1060                 budget_period_id     => $budget_period_id,
1061                 budget_period_active => 0,
1062             }
1063         );
1064     }
1065
1066     if ( $reset_all_budgets ) {
1067         my $budgets = GetBudgets({ budget_period_id => $new_budget_period_id });
1068         for my $budget ( @$budgets ) {
1069             $budget->{budget_amount} = 0;
1070             ModBudget( $budget );
1071         }
1072     }
1073
1074     return $new_budget_period_id;
1075 }
1076
1077 =head2 CloneBudgetHierarchy
1078
1079   CloneBudgetHierarchy({
1080     budgets => $budgets,
1081     new_budget_period_id => $new_budget_period_id;
1082   });
1083
1084 Clone a budget hierarchy.
1085
1086 =cut
1087
1088 sub CloneBudgetHierarchy {
1089     my ($params)             = @_;
1090     my $budgets              = $params->{budgets};
1091     my $new_budget_period_id = $params->{new_budget_period_id};
1092     next unless @$budgets or $new_budget_period_id;
1093
1094     my $children_of   = $params->{children_of};
1095     my $new_parent_id = $params->{new_parent_id};
1096
1097     my @first_level_budgets =
1098       ( not defined $children_of )
1099       ? map { ( not $_->{budget_parent_id} )             ? $_ : () } @$budgets
1100       : map { ( $_->{budget_parent_id} == $children_of ) ? $_ : () } @$budgets;
1101
1102     # get only the columns of aqbudgets
1103     my @columns = Koha::Database->new()->schema->source('Aqbudget')->columns;
1104
1105     for my $budget ( sort { $a->{budget_id} <=> $b->{budget_id} }
1106         @first_level_budgets )
1107     {
1108
1109         my $tidy_budget =
1110           { map { join( ' ', @columns ) =~ /$_/ ? ( $_ => $budget->{$_} ) : () }
1111               keys %$budget };
1112         my $new_budget_id = AddBudget(
1113             {
1114                 %$tidy_budget,
1115                 budget_id        => undef,
1116                 budget_parent_id => $new_parent_id,
1117                 budget_period_id => $new_budget_period_id
1118             }
1119         );
1120         CloneBudgetHierarchy(
1121             {
1122                 budgets              => $budgets,
1123                 new_budget_period_id => $new_budget_period_id,
1124                 children_of          => $budget->{budget_id},
1125                 new_parent_id        => $new_budget_id
1126             }
1127         );
1128     }
1129 }
1130
1131 =head2 MoveOrders
1132
1133   my $report = MoveOrders({
1134     from_budget_period_id => $from_budget_period_id,
1135     to_budget_period_id   => $to_budget_period_id,
1136   });
1137
1138 Move orders from one budget period to another.
1139
1140 =cut
1141
1142 sub MoveOrders {
1143     my ($params)              = @_;
1144     my $from_budget_period_id = $params->{from_budget_period_id};
1145     my $to_budget_period_id   = $params->{to_budget_period_id};
1146     my $move_remaining_unspent = $params->{move_remaining_unspent};
1147     return
1148       if not $from_budget_period_id
1149           or not $to_budget_period_id
1150           or $from_budget_period_id == $to_budget_period_id;
1151
1152     # Can't move orders to an inactive budget (budgetperiod)
1153     my $budget_period = GetBudgetPeriod($to_budget_period_id);
1154     return unless $budget_period->{budget_period_active};
1155
1156     my @report;
1157     my $dbh     = C4::Context->dbh;
1158     my $sth_update_aqorders = $dbh->prepare(
1159         q|
1160             UPDATE aqorders
1161             SET budget_id = ?
1162             WHERE ordernumber = ?
1163         |
1164     );
1165     my $sth_update_budget_amount = $dbh->prepare(
1166         q|
1167             UPDATE aqbudgets
1168             SET budget_amount = ?
1169             WHERE budget_id = ?
1170         |
1171     );
1172     my $from_budgets = GetBudgetHierarchy($from_budget_period_id);
1173     for my $from_budget (@$from_budgets) {
1174         my $new_budget_id = $dbh->selectcol_arrayref(
1175             q|
1176                 SELECT budget_id
1177                 FROM aqbudgets
1178                 WHERE budget_period_id = ?
1179                     AND budget_code = ?
1180             |, {}, $to_budget_period_id, $from_budget->{budget_code}
1181         );
1182         $new_budget_id = $new_budget_id->[0];
1183         my $new_budget = GetBudget( $new_budget_id );
1184         unless ( $new_budget ) {
1185             push @report,
1186               {
1187                 moved       => 0,
1188                 budget      => $from_budget,
1189                 error       => 'budget_code_not_exists',
1190               };
1191             next;
1192         }
1193         my $orders_to_move = C4::Acquisition::SearchOrders(
1194             {
1195                 budget_id => $from_budget->{budget_id},
1196                 pending   => 1,
1197             }
1198         );
1199
1200         my @orders_moved;
1201         for my $order (@$orders_to_move) {
1202             $sth_update_aqorders->execute( $new_budget->{budget_id}, $order->{ordernumber} );
1203             push @orders_moved, $order;
1204         }
1205
1206         my $unspent_moved = 0;
1207         if ($move_remaining_unspent) {
1208             my $spent   = GetBudgetHierarchySpent( $from_budget->{budget_id} );
1209             my $unspent = $from_budget->{budget_amount} - $spent;
1210             my $new_budget_amount = $new_budget->{budget_amount};
1211             if ( $unspent > 0 ) {
1212                 $new_budget_amount += $unspent;
1213                 $unspent_moved = $unspent;
1214             }
1215             $new_budget->{budget_amount} = $new_budget_amount;
1216             $sth_update_budget_amount->execute( $new_budget_amount,
1217                 $new_budget->{budget_id} );
1218         }
1219
1220         push @report,
1221           {
1222             budget        => $new_budget,
1223             orders_moved  => \@orders_moved,
1224             moved         => 1,
1225             unspent_moved => $unspent_moved,
1226           };
1227     }
1228     return \@report;
1229 }
1230
1231 END { }    # module clean-up code here (global destructor)
1232
1233 1;
1234 __END__
1235
1236 =head1 AUTHOR
1237
1238 Koha Development Team <http://koha-community.org/>
1239
1240 =cut