Bug 7794: follow-up DBRev number + removing UNIQUE index that is now useless
[koha.git] / C4 / Budgets.pm
blob7c867e0aabdea5a5a3170845bfd3b4635e1c71d2
1 package C4::Budgets;
3 # Copyright 2000-2002 Katipo Communications
5 # This file is part of Koha.
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.
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.
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.
20 use strict;
21 #use warnings; FIXME - Bug 2505
22 use C4::Context;
23 use C4::Dates qw(format_date format_date_in_iso);
24 use C4::SQLHelper qw<:all>;
25 use C4::Debug;
27 use vars qw($VERSION @ISA @EXPORT);
29 BEGIN {
30 # set the version for version checking
31 $VERSION = 3.01;
32 require Exporter;
33 @ISA = qw(Exporter);
34 @EXPORT = qw(
36 &GetBudget
37 &GetBudgets
38 &GetBudgetHierarchy
39 &AddBudget
40 &ModBudget
41 &DelBudget
42 &GetBudgetSpent
43 &GetBudgetOrdered
44 &GetPeriodsCount
45 &GetChildBudgetsSpent
47 &GetBudgetPeriod
48 &GetBudgetPeriods
49 &ModBudgetPeriod
50 &AddBudgetPeriod
51 &DelBudgetPeriod
53 &GetAuthvalueDropbox
55 &ModBudgetPlan
57 &GetCurrency
58 &GetCurrencies
59 &ModCurrencies
60 &ConvertCurrency
62 &GetBudgetsPlanCell
63 &AddBudgetPlanValue
64 &GetBudgetAuthCats
65 &BudgetHasChildren
66 &CheckBudgetParent
67 &CheckBudgetParentPerm
69 &HideCols
70 &GetCols
74 # ----------------------------BUDGETS.PM-----------------------------";
77 =head1 FUNCTIONS ABOUT BUDGETS
79 =cut
81 sub HideCols {
82 my ( $authcat, @hide_cols ) = @_;
83 my $dbh = C4::Context->dbh;
85 my $sth1 = $dbh->prepare(
86 qq|
87 UPDATE aqbudgets_planning SET display = 0
88 WHERE authcat = ?
89 AND authvalue = ? |
91 foreach my $authvalue (@hide_cols) {
92 # $sth1->{TraceLevel} = 3;
93 $sth1->execute( $authcat, $authvalue );
97 sub GetCols {
98 my ( $authcat, $authvalue ) = @_;
100 my $dbh = C4::Context->dbh;
101 my $sth = $dbh->prepare(
103 SELECT count(display) as cnt from aqbudgets_planning
104 WHERE authcat = ?
105 AND authvalue = ? and display = 0 |
108 # $sth->{TraceLevel} = 3;
109 $sth->execute( $authcat, $authvalue );
110 my $res = $sth->fetchrow_hashref;
112 return $res->{cnt} > 0 ? 0: 1
116 sub CheckBudgetParentPerm {
117 my ( $budget, $borrower_id ) = @_;
118 my $depth = $budget->{depth};
119 my $parent_id = $budget->{budget_parent_id};
120 while ($depth) {
121 my $parent = GetBudget($parent_id);
122 $parent_id = $parent->{budget_parent_id};
123 if ( $parent->{budget_owner_id} == $borrower_id ) {
124 return 1;
126 $depth--
128 return 0;
131 sub AddBudgetPeriod {
132 my ($budgetperiod) = @_;
133 return InsertInTable("aqbudgetperiods",$budgetperiod);
135 # -------------------------------------------------------------------
136 sub GetPeriodsCount {
137 my $dbh = C4::Context->dbh;
138 my $sth = $dbh->prepare("
139 SELECT COUNT(*) AS sum FROM aqbudgetperiods ");
140 $sth->execute();
141 my $res = $sth->fetchrow_hashref;
142 return $res->{'sum'};
145 # -------------------------------------------------------------------
146 sub CheckBudgetParent {
147 my ( $new_parent, $budget ) = @_;
148 my $new_parent_id = $new_parent->{'budget_id'};
149 my $budget_id = $budget->{'budget_id'};
150 my $dbh = C4::Context->dbh;
151 my $parent_id_tmp = $new_parent_id;
153 # check new-parent is not a child (or a child's child ;)
154 my $sth = $dbh->prepare(qq|
155 SELECT budget_parent_id FROM
156 aqbudgets where budget_id = ? | );
157 while (1) {
158 $sth->execute($parent_id_tmp);
159 my $res = $sth->fetchrow_hashref;
160 if ( $res->{'budget_parent_id'} == $budget_id ) {
161 return 1;
163 if ( not defined $res->{'budget_parent_id'} ) {
164 return 0;
166 $parent_id_tmp = $res->{'budget_parent_id'};
170 # -------------------------------------------------------------------
171 sub BudgetHasChildren {
172 my ( $budget_id ) = @_;
173 my $dbh = C4::Context->dbh;
174 my $sth = $dbh->prepare(qq|
175 SELECT count(*) as sum FROM aqbudgets
176 WHERE budget_parent_id = ? | );
177 $sth->execute( $budget_id );
178 my $sum = $sth->fetchrow_hashref;
179 return $sum->{'sum'};
182 # -------------------------------------------------------------------
183 sub GetBudgetsPlanCell {
184 my ( $cell, $period, $budget ) = @_;
185 my ($actual, $sth);
186 my $dbh = C4::Context->dbh;
187 if ( $cell->{'authcat'} eq 'MONTHS' ) {
188 # get the actual amount
189 $sth = $dbh->prepare( qq|
191 SELECT SUM(ecost) AS actual FROM aqorders
192 WHERE budget_id = ? AND
193 entrydate like "$cell->{'authvalue'}%" |
195 $sth->execute( $cell->{'budget_id'} );
196 } elsif ( $cell->{'authcat'} eq 'BRANCHES' ) {
197 # get the actual amount
198 $sth = $dbh->prepare( qq|
200 SELECT SUM(ecost) FROM aqorders
201 LEFT JOIN aqorders_items
202 ON (aqorders.ordernumber = aqorders_items.ordernumber)
203 LEFT JOIN items
204 ON (aqorders_items.itemnumber = items.itemnumber)
205 WHERE budget_id = ? AND homebranch = ? | );
207 $sth->execute( $cell->{'budget_id'}, $cell->{'authvalue'} );
208 } elsif ( $cell->{'authcat'} eq 'ITEMTYPES' ) {
209 # get the actual amount
210 $sth = $dbh->prepare( qq|
212 SELECT SUM( ecost * quantity) AS actual
213 FROM aqorders JOIN biblioitems
214 ON (biblioitems.biblionumber = aqorders.biblionumber )
215 WHERE aqorders.budget_id = ? and itemtype = ? |
217 $sth->execute( $cell->{'budget_id'},
218 $cell->{'authvalue'} );
220 # ELSE GENERIC ORDERS SORT1/SORT2 STAT COUNT.
221 else {
222 # get the actual amount
223 $sth = $dbh->prepare( qq|
225 SELECT SUM(ecost * quantity) AS actual
226 FROM aqorders
227 JOIN aqbudgets ON (aqbudgets.budget_id = aqorders.budget_id )
228 WHERE aqorders.budget_id = ? AND
229 ((aqbudgets.sort1_authcat = ? AND sort1 =?) OR
230 (aqbudgets.sort2_authcat = ? AND sort2 =?)) |
232 $sth->execute( $cell->{'budget_id'},
233 $budget->{'sort1_authcat'},
234 $cell->{'authvalue'},
235 $budget->{'sort2_authcat'},
236 $cell->{'authvalue'}
239 $actual = $sth->fetchrow_array;
241 # get the estimated amount
242 $sth = $dbh->prepare( qq|
244 SELECT estimated_amount AS estimated, display FROM aqbudgets_planning
245 WHERE budget_period_id = ? AND
246 budget_id = ? AND
247 authvalue = ? AND
248 authcat = ? |
250 $sth->execute( $cell->{'budget_period_id'},
251 $cell->{'budget_id'},
252 $cell->{'authvalue'},
253 $cell->{'authcat'},
257 my $res = $sth->fetchrow_hashref;
258 # my $display = $res->{'display'};
259 my $estimated = $res->{'estimated'};
262 return $actual, $estimated;
265 # -------------------------------------------------------------------
266 sub ModBudgetPlan {
267 my ( $budget_plan, $budget_period_id, $authcat ) = @_;
268 my $dbh = C4::Context->dbh;
269 foreach my $buds (@$budget_plan) {
270 my $lines = $buds->{lines};
271 my $sth = $dbh->prepare( qq|
272 DELETE FROM aqbudgets_planning
273 WHERE budget_period_id = ? AND
274 budget_id = ? AND
275 authcat = ? |
277 #delete a aqplan line of cells, then insert new cells,
278 # these could be UPDATES rather than DEL/INSERTS...
279 $sth->execute( $budget_period_id, $lines->[0]{budget_id} , $authcat );
281 foreach my $cell (@$lines) {
282 my $sth = $dbh->prepare( qq|
284 INSERT INTO aqbudgets_planning
285 SET budget_id = ?,
286 budget_period_id = ?,
287 authcat = ?,
288 estimated_amount = ?,
289 authvalue = ? |
291 $sth->execute(
292 $cell->{'budget_id'},
293 $cell->{'budget_period_id'},
294 $cell->{'authcat'},
295 $cell->{'estimated_amount'},
296 $cell->{'authvalue'},
302 # -------------------------------------------------------------------
303 sub GetBudgetSpent {
304 my ($budget_id) = @_;
305 my $dbh = C4::Context->dbh;
306 my $sth = $dbh->prepare(qq|
307 SELECT SUM( COALESCE(unitprice, ecost) * quantity ) AS sum FROM aqorders
308 WHERE budget_id = ? AND
309 quantityreceived > 0 AND
310 datecancellationprinted IS NULL
313 $sth->execute($budget_id);
314 my $sum = $sth->fetchrow_array;
315 return $sum;
318 # -------------------------------------------------------------------
319 sub GetBudgetOrdered {
320 my ($budget_id) = @_;
321 my $dbh = C4::Context->dbh;
322 my $sth = $dbh->prepare(qq|
323 SELECT SUM(ecost * quantity) AS sum FROM aqorders
324 WHERE budget_id = ? AND
325 quantityreceived = 0 AND
326 datecancellationprinted IS NULL
329 $sth->execute($budget_id);
330 my $sum = $sth->fetchrow_array;
331 return $sum;
334 # -------------------------------------------------------------------
335 sub GetBudgetAuthCats {
336 my ($budget_period_id) = shift;
337 # now, populate the auth_cats_loop used in the budget planning button
338 # we must retrieve all auth values used by at least one budget
339 my $dbh = C4::Context->dbh;
340 my $sth=$dbh->prepare("SELECT sort1_authcat,sort2_authcat FROM aqbudgets WHERE budget_period_id=?");
341 $sth->execute($budget_period_id);
342 my %authcats;
343 while (my ($sort1_authcat,$sort2_authcat) = $sth->fetchrow) {
344 $authcats{$sort1_authcat}=1;
345 $authcats{$sort2_authcat}=1;
347 my @auth_cats_loop;
348 foreach (sort keys %authcats) {
349 push @auth_cats_loop,{ authcat => $_ };
351 return \@auth_cats_loop;
354 # -------------------------------------------------------------------
355 sub GetAuthvalueDropbox {
356 my ( $authcat, $default ) = @_;
357 my $dbh = C4::Context->dbh;
358 my $sth = $dbh->prepare(
359 'SELECT authorised_value,lib FROM authorised_values
360 WHERE category = ? ORDER BY lib'
362 $sth->execute( $authcat );
363 my $option_list = [];
364 my @authorised_values = ( q{} );
365 while (my ($value, $lib) = $sth->fetchrow_array) {
366 push @{$option_list}, {
367 value => $value,
368 label => $lib,
369 default => ($default eq $value),
373 if ( @{$option_list} ) {
374 return $option_list;
376 return;
379 # -------------------------------------------------------------------
380 sub GetBudgetPeriods {
381 my ($filters,$orderby) = @_;
382 return SearchInTable("aqbudgetperiods",$filters, $orderby, undef,undef, undef, "wide");
384 # -------------------------------------------------------------------
385 sub GetBudgetPeriod {
386 my ($budget_period_id) = @_;
387 my $dbh = C4::Context->dbh;
388 ## $total = number of records linked to the record that must be deleted
389 my $total = 0;
390 ## get information about the record that will be deleted
391 my $sth;
392 if ($budget_period_id) {
393 $sth = $dbh->prepare( qq|
394 SELECT *
395 FROM aqbudgetperiods
396 WHERE budget_period_id=? |
398 $sth->execute($budget_period_id);
399 } else { # ACTIVE BUDGET
400 $sth = $dbh->prepare(qq|
401 SELECT *
402 FROM aqbudgetperiods
403 WHERE budget_period_active=1 |
405 $sth->execute();
407 my $data = $sth->fetchrow_hashref;
408 return $data;
411 # -------------------------------------------------------------------
412 sub DelBudgetPeriod{
413 my ($budget_period_id) = @_;
414 my $dbh = C4::Context->dbh;
415 ; ## $total = number of records linked to the record that must be deleted
416 my $total = 0;
418 ## get information about the record that will be deleted
419 my $sth = $dbh->prepare(qq|
420 DELETE
421 FROM aqbudgetperiods
422 WHERE budget_period_id=? |
424 return $sth->execute($budget_period_id);
427 # -------------------------------------------------------------------
428 sub ModBudgetPeriod {
429 my ($budget_period_information) = @_;
430 return UpdateInTable("aqbudgetperiods",$budget_period_information);
433 # -------------------------------------------------------------------
434 sub GetBudgetHierarchy {
435 my ( $budget_period_id, $branchcode, $owner ) = @_;
436 my @bind_params;
437 my $dbh = C4::Context->dbh;
438 my $query = qq|
439 SELECT aqbudgets.*, aqbudgetperiods.budget_period_active
440 FROM aqbudgets
441 JOIN aqbudgetperiods USING (budget_period_id)|;
443 my @where_strings;
444 # show only period X if requested
445 if ($budget_period_id) {
446 push @where_strings," aqbudgets.budget_period_id = ?";
447 push @bind_params, $budget_period_id;
449 # show only budgets owned by me, my branch or everyone
450 if ($owner) {
451 if ($branchcode) {
452 push @where_strings,
453 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="")))};
454 push @bind_params, ( $owner, $branchcode );
455 } else {
456 push @where_strings, ' (budget_owner_id = ? OR budget_owner_id IS NULL or budget_owner_id ="") ';
457 push @bind_params, $owner;
459 } else {
460 if ($branchcode) {
461 push @where_strings," (budget_branchcode =? or budget_branchcode is NULL)";
462 push @bind_params, $branchcode;
465 $query.=" WHERE ".join(' AND ', @where_strings) if @where_strings;
466 $debug && warn $query,join(",",@bind_params);
467 my $sth = $dbh->prepare($query);
468 $sth->execute(@bind_params);
469 my $results = $sth->fetchall_arrayref({});
470 my @res = @$results;
471 my $i = 0;
472 while (1) {
473 my $depth_cnt = 0;
474 foreach my $r (@res) {
475 my @child;
476 # look for children
477 $r->{depth} = '0' if !defined $r->{budget_parent_id};
478 foreach my $r2 (@res) {
479 if (defined $r2->{budget_parent_id}
480 && $r2->{budget_parent_id} == $r->{budget_id}) {
481 push @child, $r2->{budget_id};
482 $r2->{depth} = ($r->{depth} + 1) if defined $r->{depth};
485 $r->{child} = \@child if scalar @child > 0; # add the child
486 $depth_cnt++ if !defined $r->{'depth'};
488 last if ($depth_cnt == 0 || $i == 100);
489 $i++;
492 # look for top parents 1st
493 my (@sort, $depth_count);
494 ($i, $depth_count) = 0;
495 while (1) {
496 my $children = 0;
497 foreach my $r (@res) {
498 if ($r->{depth} == $depth_count) {
499 $children++ if (ref $r->{child} eq 'ARRAY');
501 # find the parent id element_id and insert it after
502 my $i2 = 0;
503 my $parent;
504 if ($depth_count > 0) {
506 # add indent
507 my $depth = $r->{depth} * 2;
508 $r->{budget_code_indent} = $r->{budget_code};
509 $r->{budget_name_indent} = $r->{budget_name};
510 foreach my $r3 (@sort) {
511 if ($r3->{budget_id} == $r->{budget_parent_id}) {
512 $parent = $i2;
513 last;
515 $i2++;
517 } else {
518 $r->{budget_code_indent} = $r->{budget_code};
519 $r->{budget_name_indent} = $r->{budget_name};
522 if (defined $parent) {
523 splice @sort, ($parent + 1), 0, $r;
524 } else {
525 push @sort, $r;
529 $i++;
530 } # --------------foreach
531 $depth_count++;
532 last if $children == 0;
535 # add budget-percent and allocation, and flags for html-template
536 foreach my $r (@sort) {
537 my $subs_href = $r->{'child'};
538 my @subs_arr = ();
539 if ( defined $subs_href ) {
540 @subs_arr = @{$subs_href};
543 my $moo = $r->{'budget_code_indent'};
544 $moo =~ s/\ /\&nbsp\;/g;
545 $r->{'budget_code_indent'} = $moo;
547 $moo = $r->{'budget_name_indent'};
548 $moo =~ s/\ /\&nbsp\;/g;
549 $r->{'budget_name_indent'} = $moo;
551 $r->{'budget_spent'} = GetBudgetSpent( $r->{'budget_id'} );
553 $r->{'budget_amount_total'} = $r->{'budget_amount'};
555 # foreach sub-levels
556 my $unalloc_count ;
558 foreach my $sub (@subs_arr) {
559 my $sub_budget = GetBudget($sub);
561 $r->{budget_spent_sublevel} += GetBudgetSpent( $sub_budget->{'budget_id'} );
562 $unalloc_count += $sub_budget->{'budget_amount'};
565 return \@sort;
568 # -------------------------------------------------------------------
570 sub AddBudget {
571 my ($budget) = @_;
572 return InsertInTable("aqbudgets",$budget);
575 # -------------------------------------------------------------------
576 sub ModBudget {
577 my ($budget) = @_;
578 return UpdateInTable("aqbudgets",$budget);
581 # -------------------------------------------------------------------
582 sub DelBudget {
583 my ($budget_id) = @_;
584 my $dbh = C4::Context->dbh;
585 my $sth = $dbh->prepare("delete from aqbudgets where budget_id=?");
586 my $rc = $sth->execute($budget_id);
587 return $rc;
591 =head2 GetBudget
593 &GetBudget($budget_id);
595 get a specific budget
597 =cut
599 # -------------------------------------------------------------------
600 sub GetBudget {
601 my ( $budget_id ) = @_;
602 my $dbh = C4::Context->dbh;
603 my $query = "
604 SELECT *
605 FROM aqbudgets
606 WHERE budget_id=?
608 my $sth = $dbh->prepare($query);
609 $sth->execute( $budget_id );
610 my $result = $sth->fetchrow_hashref;
611 return $result;
614 =head2 GetChildBudgetsSpent
616 &GetChildBudgetsSpent($budget-id);
618 gets the total spent of the level and sublevels of $budget_id
620 =cut
622 # -------------------------------------------------------------------
623 sub GetChildBudgetsSpent {
624 my ( $budget_id ) = @_;
625 my $dbh = C4::Context->dbh;
626 my $query = "
627 SELECT *
628 FROM aqbudgets
629 WHERE budget_parent_id=?
631 my $sth = $dbh->prepare($query);
632 $sth->execute( $budget_id );
633 my $result = $sth->fetchall_arrayref({});
634 my $total_spent = GetBudgetSpent($budget_id);
635 if ($result){
636 $total_spent += GetChildBudgetsSpent($_->{"budget_id"}) foreach @$result;
638 return $total_spent;
641 =head2 GetBudgets
643 &GetBudgets($filter, $order_by);
645 gets all budgets
647 =cut
649 # -------------------------------------------------------------------
650 sub GetBudgets {
651 my ($filters,$orderby) = @_;
652 return SearchInTable("aqbudgets",$filters, $orderby, undef,undef, undef, "wide");
655 # -------------------------------------------------------------------
657 =head2 GetCurrencies
659 @currencies = &GetCurrencies;
661 Returns the list of all known currencies.
663 C<$currencies> is a array; its elements are references-to-hash, whose
664 keys are the fields from the currency table in the Koha database.
666 =cut
668 sub GetCurrencies {
669 my $dbh = C4::Context->dbh;
670 my $query = "
671 SELECT *
672 FROM currency
674 my $sth = $dbh->prepare($query);
675 $sth->execute;
676 my @results = ();
677 while ( my $data = $sth->fetchrow_hashref ) {
678 push( @results, $data );
680 return @results;
683 # -------------------------------------------------------------------
685 sub GetCurrency {
686 my $dbh = C4::Context->dbh;
687 my $query = "
688 SELECT * FROM currency where active = '1' ";
689 my $sth = $dbh->prepare($query);
690 $sth->execute;
691 my $r = $sth->fetchrow_hashref;
692 return $r;
695 =head2 ModCurrencies
697 &ModCurrencies($currency, $newrate);
699 Sets the exchange rate for C<$currency> to be C<$newrate>.
701 =cut
703 sub ModCurrencies {
704 my ( $currency, $rate ) = @_;
705 my $dbh = C4::Context->dbh;
706 my $query = qq|
707 UPDATE currency
708 SET rate=?
709 WHERE currency=? |;
710 my $sth = $dbh->prepare($query);
711 $sth->execute( $rate, $currency );
714 # -------------------------------------------------------------------
716 =head2 ConvertCurrency
718 $foreignprice = &ConvertCurrency($currency, $localprice);
720 Converts the price C<$localprice> to foreign currency C<$currency> by
721 dividing by the exchange rate, and returns the result.
723 If no exchange rate is found, e is one to one.
725 =cut
727 sub ConvertCurrency {
728 my ( $currency, $price ) = @_;
729 my $dbh = C4::Context->dbh;
730 my $query = "
731 SELECT rate
732 FROM currency
733 WHERE currency=?
735 my $sth = $dbh->prepare($query);
736 $sth->execute($currency);
737 my $cur = ( $sth->fetchrow_array() )[0];
738 unless ($cur) {
739 $cur = 1;
741 return ( $price / $cur );
744 =head2 _columns
746 returns an array containing fieldname followed by PRI as value if PRIMARY Key
748 =cut
750 sub _columns(;$) {
751 my $tablename=shift||"aqbudgets";
752 return @{C4::Context->dbh->selectcol_arrayref("SHOW columns from $tablename",{Columns=>[1,4]})};
755 sub _filter_fields{
756 my $budget=shift;
757 my $tablename=shift;
758 my @keys;
759 my @values;
760 my %columns= _columns($tablename);
761 #Filter Primary Keys of table
762 my $elements=join "|",grep {$columns{$_} ne "PRI"} keys %columns;
763 foreach my $field (grep {/\b($elements)\b/} keys %$budget){
764 $$budget{$field}=format_date_in_iso($$budget{$field}) if ($field=~/date/ && $$budget{$field} !~C4::Dates->regexp("iso"));
765 my $strkeys= " $field = ? ";
766 if ($field=~/branch/){
767 $strkeys="( $strkeys OR $field='' OR $field IS NULL) ";
769 push @values, $$budget{$field};
770 push @keys, $strkeys;
772 return (\@keys,\@values);
775 END { } # module clean-up code here (global destructor)
778 __END__
780 =head1 AUTHOR
782 Koha Development Team <http://koha-community.org/>
784 =cut