Bug 12958: Set a fund owner to a fund hierarchy
[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         my $results = $sth->fetchall_arrayref({});
524         my @res     = @$results;
525         my $i = 0;
526         while (1) {
527                 my $depth_cnt = 0;
528                 foreach my $r (@res) {
529                         my @child;
530                         # look for children
531                         $r->{depth} = '0' if !defined $r->{budget_parent_id};
532                         foreach my $r2 (@res) {
533                                 if (defined $r2->{budget_parent_id}
534                                         && $r2->{budget_parent_id} == $r->{budget_id}) {
535                                         push @child, $r2->{budget_id};
536                                         $r2->{depth} = ($r->{depth} + 1) if defined $r->{depth};
537                                 }
538                         }
539                         $r->{child} = \@child if scalar @child > 0;    # add the child
540                         $depth_cnt++ if !defined $r->{'depth'};
541                 }
542                 last if ($depth_cnt == 0 || $i == 100);
543                 $i++;
544         }
545
546         # look for top parents 1st
547         my (@sort, $depth_count);
548         ($i, $depth_count) = 0;
549         while (1) {
550                 my $children = 0;
551                 foreach my $r (@res) {
552                         if ($r->{depth} == $depth_count) {
553                                 $children++ if (ref $r->{child} eq 'ARRAY');
554
555                                 # find the parent id element_id and insert it after
556                                 my $i2 = 0;
557                                 my $parent;
558                                 if ($depth_count > 0) {
559
560                                         # add indent
561                                         my $depth = $r->{depth} * 2;
562                                         $r->{budget_code_indent} = $r->{budget_code};
563                                         $r->{budget_name_indent} = $r->{budget_name};
564                                         foreach my $r3 (@sort) {
565                                                 if ($r3->{budget_id} == $r->{budget_parent_id}) {
566                                                         $parent = $i2;
567                                                         last;
568                                                 }
569                                                 $i2++;
570                                         }
571                                 } else {
572                                         $r->{budget_code_indent} = $r->{budget_code};
573                                         $r->{budget_name_indent} = $r->{budget_name};
574                                 }
575                 
576                                 if (defined $parent) {
577                                         splice @sort, ($parent + 1), 0, $r;
578                                 } else {
579                                         push @sort, $r;
580                                 }
581                         }
582
583                         $i++;
584                 }    # --------------foreach
585                 $depth_count++;
586                 last if $children == 0;
587         }
588
589
590     foreach my $budget (@sort) {
591         $budget->{budget_spent}   = GetBudgetSpent( $budget->{budget_id} );
592         $budget->{budget_ordered} = GetBudgetOrdered( $budget->{budget_id} );
593         $budget->{total_spent} = GetBudgetHierarchySpent( $budget->{budget_id} );
594         $budget->{total_ordered} = GetBudgetHierarchyOrdered( $budget->{budget_id} );
595     }
596     return \@sort;
597 }
598
599 # -------------------------------------------------------------------
600
601 sub AddBudget {
602     my ($budget) = @_;
603     return unless ($budget);
604
605     my $resultset = Koha::Database->new()->schema->resultset('Aqbudget');
606     return $resultset->create($budget)->id;
607 }
608
609 # -------------------------------------------------------------------
610 sub ModBudget {
611     my ($budget) = @_;
612     my $result = Koha::Database->new()->schema->resultset('Aqbudget')->find($budget);
613     return unless($result);
614
615     $result = $result->update($budget);
616     return $result->in_storage;
617 }
618
619 # -------------------------------------------------------------------
620 sub DelBudget {
621         my ($budget_id) = @_;
622         my $dbh         = C4::Context->dbh;
623         my $sth         = $dbh->prepare("delete from aqbudgets where budget_id=?");
624         my $rc          = $sth->execute($budget_id);
625         return $rc;
626 }
627
628
629 =head2 GetBudget
630
631   &GetBudget($budget_id);
632
633 get a specific budget
634
635 =cut
636
637 # -------------------------------------------------------------------
638 sub GetBudget {
639     my ( $budget_id ) = @_;
640     my $dbh = C4::Context->dbh;
641     my $query = "
642         SELECT *
643         FROM   aqbudgets
644         WHERE  budget_id=?
645         ";
646     my $sth = $dbh->prepare($query);
647     $sth->execute( $budget_id );
648     my $result = $sth->fetchrow_hashref;
649     return $result;
650 }
651
652 =head2 GetBudgetByOrderNumber
653
654   &GetBudgetByOrderNumber($ordernumber);
655
656 get a specific budget by order number
657
658 =cut
659
660 # -------------------------------------------------------------------
661 sub GetBudgetByOrderNumber {
662     my ( $ordernumber ) = @_;
663     my $dbh = C4::Context->dbh;
664     my $query = "
665         SELECT aqbudgets.*
666         FROM   aqbudgets, aqorders
667         WHERE  ordernumber=?
668         AND    aqorders.budget_id = aqbudgets.budget_id
669         ";
670     my $sth = $dbh->prepare($query);
671     $sth->execute( $ordernumber );
672     my $result = $sth->fetchrow_hashref;
673     return $result;
674 }
675
676 =head2 GetBudgetByCode
677
678     my $budget = &GetBudgetByCode($budget_code);
679
680 Retrieve all aqbudgets fields as a hashref for the budget that has
681 given budget_code
682
683 =cut
684
685 sub GetBudgetByCode {
686     my ( $budget_code ) = @_;
687
688     my $dbh = C4::Context->dbh;
689     my $query = qq{
690         SELECT *
691         FROM aqbudgets
692         WHERE budget_code = ?
693         ORDER BY budget_id DESC
694         LIMIT 1
695     };
696     my $sth = $dbh->prepare( $query );
697     $sth->execute( $budget_code );
698     return $sth->fetchrow_hashref;
699 }
700
701 =head2 GetBudgetHierarchySpent
702
703   my $spent = GetBudgetHierarchySpent( $budget_id );
704
705 Gets the total spent of the level and sublevels of $budget_id
706
707 =cut
708
709 sub GetBudgetHierarchySpent {
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_spent = GetBudgetSpent( $budget_id );
719     for my $child_id ( @$children_ids ) {
720         $total_spent += GetBudgetHierarchySpent( $child_id );
721     }
722     return $total_spent;
723 }
724
725 =head2 GetBudgetHierarchyOrdered
726
727   my $ordered = GetBudgetHierarchyOrdered( $budget_id );
728
729 Gets the total ordered of the level and sublevels of $budget_id
730
731 =cut
732
733 sub GetBudgetHierarchyOrdered {
734     my ( $budget_id ) = @_;
735     my $dbh = C4::Context->dbh;
736     my $children_ids = $dbh->selectcol_arrayref(q|
737         SELECT budget_id
738         FROM   aqbudgets
739         WHERE  budget_parent_id = ?
740     |, {}, $budget_id );
741
742     my $total_ordered = GetBudgetOrdered( $budget_id );
743     for my $child_id ( @$children_ids ) {
744         $total_ordered += GetBudgetHierarchyOrdered( $child_id );
745     }
746     return $total_ordered;
747 }
748
749 =head2 GetBudgets
750
751   &GetBudgets($filter, $order_by);
752
753 gets all budgets
754
755 =cut
756
757 # -------------------------------------------------------------------
758 sub GetBudgets {
759     my ($filters, $orderby) = @_;
760     $orderby = 'budget_name' unless($orderby);
761
762     my $rs = Koha::Database->new()->schema->resultset('Aqbudget');
763     $rs = $rs->search( $filters, { order_by => $orderby } );
764     $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
765     return [ $rs->all  ];
766 }
767
768 =head2 GetBudgetUsers
769
770     my @borrowernumbers = &GetBudgetUsers($budget_id);
771
772 Return the list of borrowernumbers linked to a budget
773
774 =cut
775
776 sub GetBudgetUsers {
777     my ($budget_id) = @_;
778
779     my $dbh = C4::Context->dbh;
780     my $query = qq{
781         SELECT borrowernumber
782         FROM aqbudgetborrowers
783         WHERE budget_id = ?
784     };
785     my $sth = $dbh->prepare($query);
786     $sth->execute($budget_id);
787
788     my @borrowernumbers;
789     while (my ($borrowernumber) = $sth->fetchrow_array) {
790         push @borrowernumbers, $borrowernumber
791     }
792
793     return @borrowernumbers;
794 }
795
796 =head2 ModBudgetUsers
797
798     &ModBudgetUsers($budget_id, @borrowernumbers);
799
800 Modify the list of borrowernumbers linked to a budget
801
802 =cut
803
804 sub ModBudgetUsers {
805     my ($budget_id, @budget_users_id) = @_;
806
807     return unless $budget_id;
808
809     my $dbh = C4::Context->dbh;
810     my $query = "DELETE FROM aqbudgetborrowers WHERE budget_id = ?";
811     my $sth = $dbh->prepare($query);
812     $sth->execute($budget_id);
813
814     $query = qq{
815         INSERT INTO aqbudgetborrowers (budget_id, borrowernumber)
816         VALUES (?,?)
817     };
818     $sth = $dbh->prepare($query);
819     foreach my $borrowernumber (@budget_users_id) {
820         next unless $borrowernumber;
821         $sth->execute($budget_id, $borrowernumber);
822     }
823 }
824
825 sub CanUserUseBudget {
826     my ($borrower, $budget, $userflags) = @_;
827
828     if (not ref $borrower) {
829         $borrower = C4::Members::GetMember(borrowernumber => $borrower);
830     }
831     if (not ref $budget) {
832         $budget = GetBudget($budget);
833     }
834
835     return 0 unless ($borrower and $budget);
836
837     if (not defined $userflags) {
838         $userflags = C4::Auth::getuserflags($borrower->{flags},
839             $borrower->{userid});
840     }
841
842     unless ($userflags->{superlibrarian}
843     || (ref $userflags->{acquisition}
844         && $userflags->{acquisition}->{budget_manage_all})
845     || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
846     {
847         if (not exists $userflags->{acquisition}) {
848             return 0;
849         }
850
851         if (!ref $userflags->{acquisition} && !$userflags->{acquisition}) {
852             return 0;
853         }
854
855         # Budget restricted to owner
856         if ( $budget->{budget_permission} == 1 ) {
857             if (    $budget->{budget_owner_id}
858                 and $budget->{budget_owner_id} != $borrower->{borrowernumber} )
859             {
860                 return 0;
861             }
862         }
863
864         # Budget restricted to owner, users and library
865         elsif ( $budget->{budget_permission} == 2 ) {
866             my @budget_users = GetBudgetUsers( $budget->{budget_id} );
867
868             if (
869                 (
870                         $budget->{budget_owner_id}
871                     and $budget->{budget_owner_id} !=
872                     $borrower->{borrowernumber}
873                     or not $budget->{budget_owner_id}
874                 )
875                 and ( 0 == grep { $borrower->{borrowernumber} == $_ }
876                     @budget_users )
877                 and defined $budget->{budget_branchcode}
878                 and $budget->{budget_branchcode} ne
879                 C4::Context->userenv->{branch}
880               )
881             {
882                 return 0;
883             }
884         }
885
886         # Budget restricted to owner and users
887         elsif ( $budget->{budget_permission} == 3 ) {
888             my @budget_users = GetBudgetUsers( $budget->{budget_id} );
889             if (
890                 (
891                         $budget->{budget_owner_id}
892                     and $budget->{budget_owner_id} !=
893                     $borrower->{borrowernumber}
894                     or not $budget->{budget_owner_id}
895                 )
896                 and ( 0 == grep { $borrower->{borrowernumber} == $_ }
897                     @budget_users )
898               )
899             {
900                 return 0;
901             }
902         }
903     }
904
905     return 1;
906 }
907
908 sub CanUserModifyBudget {
909     my ($borrower, $budget, $userflags) = @_;
910
911     if (not ref $borrower) {
912         $borrower = C4::Members::GetMember(borrowernumber => $borrower);
913     }
914     if (not ref $budget) {
915         $budget = GetBudget($budget);
916     }
917
918     return 0 unless ($borrower and $budget);
919
920     if (not defined $userflags) {
921         $userflags = C4::Auth::getuserflags($borrower->{flags},
922             $borrower->{userid});
923     }
924
925     unless ($userflags->{superlibrarian}
926     || (ref $userflags->{acquisition}
927         && $userflags->{acquisition}->{budget_manage_all})
928     || (!ref $userflags->{acquisition} && $userflags->{acquisition}))
929     {
930         if (!CanUserUseBudget($borrower, $budget, $userflags)) {
931             return 0;
932         }
933
934         if (ref $userflags->{acquisition}
935         && !$userflags->{acquisition}->{budget_modify}) {
936             return 0;
937         }
938     }
939
940     return 1;
941 }
942
943 # -------------------------------------------------------------------
944
945 =head2 GetCurrencies
946
947   @currencies = &GetCurrencies;
948
949 Returns the list of all known currencies.
950
951 C<$currencies> is a array; its elements are references-to-hash, whose
952 keys are the fields from the currency table in the Koha database.
953
954 =cut
955
956 sub GetCurrencies {
957     my $dbh   = C4::Context->dbh;
958     my $query = "
959         SELECT *
960         FROM   currency
961     ";
962     my $sth = $dbh->prepare($query);
963     $sth->execute;
964     my @results = ();
965     while ( my $data = $sth->fetchrow_hashref ) {
966         push( @results, $data );
967     }
968     return @results;
969 }
970
971 # -------------------------------------------------------------------
972
973 sub GetCurrency {
974     my $dbh   = C4::Context->dbh;
975     my $query = "
976         SELECT * FROM currency where active = '1'    ";
977     my $sth = $dbh->prepare($query);
978     $sth->execute;
979     my $r = $sth->fetchrow_hashref;
980     return $r;
981 }
982
983 =head2 ModCurrencies
984
985 &ModCurrencies($currency, $newrate);
986
987 Sets the exchange rate for C<$currency> to be C<$newrate>.
988
989 =cut
990
991 sub ModCurrencies {
992     my ( $currency, $rate ) = @_;
993     my $dbh   = C4::Context->dbh;
994     my $query = qq|
995         UPDATE currency
996         SET    rate=?
997         WHERE  currency=? |;
998     my $sth = $dbh->prepare($query);
999     $sth->execute( $rate, $currency );
1000 }
1001
1002 # -------------------------------------------------------------------
1003
1004 =head2 ConvertCurrency
1005
1006   $foreignprice = &ConvertCurrency($currency, $localprice);
1007
1008 Converts the price C<$localprice> to foreign currency C<$currency> by
1009 dividing by the exchange rate, and returns the result.
1010
1011 If no exchange rate is found, e is one to one.
1012
1013 =cut
1014
1015 sub ConvertCurrency {
1016     my ( $currency, $price ) = @_;
1017     my $dbh   = C4::Context->dbh;
1018     my $query = "
1019         SELECT rate
1020         FROM   currency
1021         WHERE  currency=?
1022     ";
1023     my $sth = $dbh->prepare($query);
1024     $sth->execute($currency);
1025     my $cur = ( $sth->fetchrow_array() )[0];
1026     unless ($cur) {
1027         $cur = 1;
1028     }
1029     return ( $price / $cur );
1030 }
1031
1032
1033 =head2 CloneBudgetPeriod
1034
1035   my $new_budget_period_id = CloneBudgetPeriod({
1036     budget_period_id => $budget_period_id,
1037     budget_period_startdate => $budget_period_startdate,
1038     budget_period_enddate   => $budget_period_enddate,
1039     mark_original_budget_as_inactive => 1n
1040     reset_all_budgets => 1,
1041   });
1042
1043 Clone a budget period with all budgets.
1044 If the mark_origin_budget_as_inactive is set (0 by default),
1045 the original budget will be marked as inactive.
1046
1047 If the reset_all_budgets is set (0 by default), all budget (fund)
1048 amounts will be reset.
1049
1050 =cut
1051
1052 sub CloneBudgetPeriod {
1053     my ($params)                  = @_;
1054     my $budget_period_id          = $params->{budget_period_id};
1055     my $budget_period_startdate   = $params->{budget_period_startdate};
1056     my $budget_period_enddate     = $params->{budget_period_enddate};
1057     my $budget_period_description = $params->{budget_period_description};
1058     my $mark_original_budget_as_inactive =
1059       $params->{mark_original_budget_as_inactive} || 0;
1060     my $reset_all_budgets = $params->{reset_all_budgets} || 0;
1061
1062     my $budget_period = GetBudgetPeriod($budget_period_id);
1063
1064     $budget_period->{budget_period_startdate}   = $budget_period_startdate;
1065     $budget_period->{budget_period_enddate}     = $budget_period_enddate;
1066     $budget_period->{budget_period_description} = $budget_period_description;
1067     # The new budget (budget_period) should be active by default
1068     $budget_period->{budget_period_active}    = 1;
1069     my $original_budget_period_id = $budget_period->{budget_period_id};
1070     delete $budget_period->{budget_period_id};
1071     my $new_budget_period_id = AddBudgetPeriod( $budget_period );
1072
1073     my $budgets = GetBudgetHierarchy($budget_period_id);
1074     CloneBudgetHierarchy(
1075         {
1076             budgets              => $budgets,
1077             new_budget_period_id => $new_budget_period_id
1078         }
1079     );
1080
1081     if ($mark_original_budget_as_inactive) {
1082         ModBudgetPeriod(
1083             {
1084                 budget_period_id     => $budget_period_id,
1085                 budget_period_active => 0,
1086             }
1087         );
1088     }
1089
1090     if ( $reset_all_budgets ) {
1091         my $budgets = GetBudgets({ budget_period_id => $new_budget_period_id });
1092         for my $budget ( @$budgets ) {
1093             $budget->{budget_amount} = 0;
1094             ModBudget( $budget );
1095         }
1096     }
1097
1098     return $new_budget_period_id;
1099 }
1100
1101 =head2 CloneBudgetHierarchy
1102
1103   CloneBudgetHierarchy({
1104     budgets => $budgets,
1105     new_budget_period_id => $new_budget_period_id;
1106   });
1107
1108 Clone a budget hierarchy.
1109
1110 =cut
1111
1112 sub CloneBudgetHierarchy {
1113     my ($params)             = @_;
1114     my $budgets              = $params->{budgets};
1115     my $new_budget_period_id = $params->{new_budget_period_id};
1116     next unless @$budgets or $new_budget_period_id;
1117
1118     my $children_of   = $params->{children_of};
1119     my $new_parent_id = $params->{new_parent_id};
1120
1121     my @first_level_budgets =
1122       ( not defined $children_of )
1123       ? map { ( not $_->{budget_parent_id} )             ? $_ : () } @$budgets
1124       : map { ( $_->{budget_parent_id} == $children_of ) ? $_ : () } @$budgets;
1125
1126     # get only the columns of aqbudgets
1127     my @columns = Koha::Database->new()->schema->source('Aqbudget')->columns;
1128
1129     for my $budget ( sort { $a->{budget_id} <=> $b->{budget_id} }
1130         @first_level_budgets )
1131     {
1132
1133         my $tidy_budget =
1134           { map { join( ' ', @columns ) =~ /$_/ ? ( $_ => $budget->{$_} ) : () }
1135               keys %$budget };
1136         my $new_budget_id = AddBudget(
1137             {
1138                 %$tidy_budget,
1139                 budget_id        => undef,
1140                 budget_parent_id => $new_parent_id,
1141                 budget_period_id => $new_budget_period_id
1142             }
1143         );
1144         CloneBudgetHierarchy(
1145             {
1146                 budgets              => $budgets,
1147                 new_budget_period_id => $new_budget_period_id,
1148                 children_of          => $budget->{budget_id},
1149                 new_parent_id        => $new_budget_id
1150             }
1151         );
1152     }
1153 }
1154
1155 =head2 MoveOrders
1156
1157   my $report = MoveOrders({
1158     from_budget_period_id => $from_budget_period_id,
1159     to_budget_period_id   => $to_budget_period_id,
1160   });
1161
1162 Move orders from one budget period to another.
1163
1164 =cut
1165
1166 sub MoveOrders {
1167     my ($params)              = @_;
1168     my $from_budget_period_id = $params->{from_budget_period_id};
1169     my $to_budget_period_id   = $params->{to_budget_period_id};
1170     my $move_remaining_unspent = $params->{move_remaining_unspent};
1171     return
1172       if not $from_budget_period_id
1173           or not $to_budget_period_id
1174           or $from_budget_period_id == $to_budget_period_id;
1175
1176     # Can't move orders to an inactive budget (budgetperiod)
1177     my $budget_period = GetBudgetPeriod($to_budget_period_id);
1178     return unless $budget_period->{budget_period_active};
1179
1180     my @report;
1181     my $dbh     = C4::Context->dbh;
1182     my $sth_update_aqorders = $dbh->prepare(
1183         q|
1184             UPDATE aqorders
1185             SET budget_id = ?
1186             WHERE ordernumber = ?
1187         |
1188     );
1189     my $sth_update_budget_amount = $dbh->prepare(
1190         q|
1191             UPDATE aqbudgets
1192             SET budget_amount = ?
1193             WHERE budget_id = ?
1194         |
1195     );
1196     my $from_budgets = GetBudgetHierarchy($from_budget_period_id);
1197     for my $from_budget (@$from_budgets) {
1198         my $new_budget_id = $dbh->selectcol_arrayref(
1199             q|
1200                 SELECT budget_id
1201                 FROM aqbudgets
1202                 WHERE budget_period_id = ?
1203                     AND budget_code = ?
1204             |, {}, $to_budget_period_id, $from_budget->{budget_code}
1205         );
1206         $new_budget_id = $new_budget_id->[0];
1207         my $new_budget = GetBudget( $new_budget_id );
1208         unless ( $new_budget ) {
1209             push @report,
1210               {
1211                 moved       => 0,
1212                 budget      => $from_budget,
1213                 error       => 'budget_code_not_exists',
1214               };
1215             next;
1216         }
1217         my $orders_to_move = C4::Acquisition::SearchOrders(
1218             {
1219                 budget_id => $from_budget->{budget_id},
1220                 pending   => 1,
1221             }
1222         );
1223
1224         my @orders_moved;
1225         for my $order (@$orders_to_move) {
1226             $sth_update_aqorders->execute( $new_budget->{budget_id}, $order->{ordernumber} );
1227             push @orders_moved, $order;
1228         }
1229
1230         my $unspent_moved = 0;
1231         if ($move_remaining_unspent) {
1232             my $spent   = GetBudgetHierarchySpent( $from_budget->{budget_id} );
1233             my $unspent = $from_budget->{budget_amount} - $spent;
1234             my $new_budget_amount = $new_budget->{budget_amount};
1235             if ( $unspent > 0 ) {
1236                 $new_budget_amount += $unspent;
1237                 $unspent_moved = $unspent;
1238             }
1239             $new_budget->{budget_amount} = $new_budget_amount;
1240             $sth_update_budget_amount->execute( $new_budget_amount,
1241                 $new_budget->{budget_id} );
1242         }
1243
1244         push @report,
1245           {
1246             budget        => $new_budget,
1247             orders_moved  => \@orders_moved,
1248             moved         => 1,
1249             unspent_moved => $unspent_moved,
1250           };
1251     }
1252     return \@report;
1253 }
1254
1255 END { }    # module clean-up code here (global destructor)
1256
1257 1;
1258 __END__
1259
1260 =head1 AUTHOR
1261
1262 Koha Development Team <http://koha-community.org/>
1263
1264 =cut