]> wagnertech.de Git - kivitendo-erp.git/blob - SL/AM.pm
Konten speichern - Refaktoring von ->{link}
[kivitendo-erp.git] / SL / AM.pm
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger Accounting
9 # Copyright (C) 2001
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #  Contributors:
16 #
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
21 #
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #======================================================================
30 #
31 # Administration module
32 #    Chart of Accounts
33 #    template routines
34 #    preferences
35 #
36 #======================================================================
37
38 package AM;
39
40 use Carp;
41 use Data::Dumper;
42 use Encode;
43 use List::MoreUtils qw(any);
44 use SL::DBUtils;
45 use SL::DB::AuthUser;
46 use SL::DB::Default;
47 use SL::DB::Employee;
48 use SL::DB::Chart;
49 use SL::GenericTranslations;
50
51 use strict;
52
53 sub get_account {
54   $main::lxdebug->enter_sub();
55
56   # fetch chart-related data and set form fields
57   # get_account is called by add_account in am.pl
58   # always sets $form->{TAXKEY} and default_accounts
59   # loads chart data when $form->{id} is passed
60
61   my ($self, $myconfig, $form) = @_;
62
63   # get default accounts
64   map { $form->{$_} = $::instance_conf->{$_} } qw(inventory_accno_id income_accno_id expense_accno_id);
65
66   require SL::DB::Tax;
67   my $taxes = SL::DB::Manager::Tax->get_all( with_objects => ['chart'] , sort_by => 'taxkey' );
68   $form->{TAXKEY} = [];
69   foreach my $tk ( @{$taxes} ) {
70     push @{ $form->{TAXKEY} },  { id          => $tk->id,
71                                   chart_accno => $tk->chart_id ? $tk->chart->accno : undef,
72                                   taxkey      => $tk->taxkey,
73                                   tax         => $tk->id . '--' . $tk->taxkey,
74                                   rate        => $tk->rate
75                                 };
76   };
77
78   if ($form->{id}) {
79
80     my $chart_obj = SL::DB::Manager::Chart->find_by(id => $form->{id}) || die "Can't open chart";
81
82     my @chart_fields = qw(accno description charttype category link pos_bilanz
83                           pos_eur pos_er new_chart_id valid_from pos_bwa datevautomatik);
84     foreach my $cf ( @chart_fields ) {
85       $form->{"$cf"} = $chart_obj->$cf;
86     }
87
88     my $active_taxkey = $chart_obj->get_active_taxkey;
89     $form->{$_}  = $active_taxkey->$_ foreach qw(taxkey_id pos_ustva tax_id startdate);
90     $form->{tax} = $active_taxkey->tax_id . '--' . $active_taxkey->taxkey_id;
91
92     # check if there are any transactions for this chart
93     $form->{orphaned} = $chart_obj->has_transaction ? 0 : 1;
94
95     # check if new account is active
96     # The old sql query was broken since at least 2006 and always returned 0
97     $form->{new_chart_valid} = $chart_obj->new_chart_valid;
98
99     # get the taxkeys of the account
100     $form->{ACCOUNT_TAXKEYS} = [];
101     foreach my $taxkey ( @{ $chart_obj->taxkeys } ) {
102       push @{ $form->{ACCOUNT_TAXKEYS} }, { id             => $taxkey->id,
103                                             chart_id       => $taxkey->chart_id,
104                                             tax_id         => $taxkey->tax_id,
105                                             taxkey_id      => $taxkey->taxkey_id,
106                                             pos_ustva      => $taxkey->pos_ustva,
107                                             startdate      => $taxkey->startdate->to_kivitendo,
108                                             taxdescription => $taxkey->tax->taxdescription,
109                                             rate           => $taxkey->tax->rate,
110                                             accno          => defined $taxkey->tax->chart_id ? $taxkey->tax->chart->accno : undef,
111                                           };
112     }
113
114     # get new accounts (Folgekonto). Find all charts with the same link
115     $form->{NEWACCOUNT} = $chart_obj->db->dbh->selectall_arrayref('select id, accno,description from chart where link = ? order by accno', {Slice => {}}, $chart_obj->link);
116
117   } else { # set to orphaned for new charts, so chart_type can be changed (needed by $AccountIsPosted)
118     $form->{orphaned} = 1;
119   };
120
121   $main::lxdebug->leave_sub();
122 }
123
124 sub save_account {
125   $main::lxdebug->enter_sub();
126
127   # TODO: it should be forbidden to change an account to a heading if there
128   # have been bookings to this account in the past
129
130   my ($self, $myconfig, $form) = @_;
131
132   # connect to database, turn off AutoCommit
133   my $dbh = $form->dbconnect_noauto($myconfig);
134
135   for (qw(AR_include_in_dropdown AP_include_in_dropdown summary_account)) {
136     $form->{$form->{$_}} = $form->{$_} if $form->{$_};
137   }
138
139   # sanity check, can't have AR with AR_...
140   if ($form->{AR} || $form->{AP} || $form->{IC}) {
141     if (any { $form->{$_} } qw(AR_amount AR_tax AR_paid AP_amount AP_tax AP_paid IC_sale IC_cogs IC_taxpart IC_income IC_expense IC_taxservice)) {
142       $form->error($::locale->text('It is not allowed that a summary account occurs in a drop-down menu!'));
143     }
144   }
145
146   my @link_order = qw(AR AR_amount AR_tax AR_paid AP AP_amount AP_tax AP_paid IC IC_sale IC_cogs IC_taxpart IC_income IC_expense IC_taxservice);
147   $form->{link} = join ':', grep $_, map $form->{$_}, @link_order;
148
149   # strip blanks from accno
150   map { $form->{$_} =~ s/ //g; } qw(accno);
151
152   my ($query, $sth);
153
154   if ($form->{id} eq "NULL") {
155     $form->{id} = "";
156   }
157
158   $query = '
159     SELECT accno
160     FROM chart
161     WHERE accno = ?';
162
163   my @values = ($form->{accno});
164
165   if ( $form->{id} ) {
166     $query .= ' AND NOT id = ?';
167     push(@values, $form->{id});
168   }
169
170   my ($accno) = selectrow_query($form, $dbh, $query, @values);
171
172   if ($accno) {
173     $form->error($::locale->text('Account number not unique!'));
174   }
175
176
177   if (!$form->{id} || $form->{id} eq "") {
178     $query = qq|SELECT nextval('id')|;
179     ($form->{"id"}) = selectrow_query($form, $dbh, $query);
180     $query = qq|INSERT INTO chart (id, accno, link) VALUES (?, ?, ?)|;
181     do_query($form, $dbh, $query, $form->{"id"}, $form->{"accno"}, '');
182   }
183
184   @values = ();
185
186
187   if ($form->{id}) {
188
189     # if charttype is heading make sure certain values are empty
190     # specifically, if charttype is changed from an existing account, empty the
191     # fields unnecessary for headings, so that e.g. heading doesn't appear in
192     # drop-down menues due to still having a valid "link" entry
193
194     if ( $form->{charttype} eq 'H' ) {
195       $form->{link} = '';
196       $form->{pos_bwa} = '';
197       $form->{pos_bilanz} = '';
198       $form->{pos_eur} = '';
199       $form->{new_chart_id} = '';
200       $form->{valid_from} = '';
201     };
202
203     $query = qq|UPDATE chart SET
204                   accno = ?,
205                   description = ?,
206                   charttype = ?,
207                   category = ?,
208                   link = ?,
209                   pos_bwa   = ?,
210                   pos_bilanz = ?,
211                   pos_eur = ?,
212                   pos_er = ?,
213                   new_chart_id = ?,
214                   valid_from = ?,
215                   datevautomatik = ?
216                 WHERE id = ?|;
217
218     @values = (
219                   $form->{accno},
220                   $form->{description},
221                   $form->{charttype},
222                   $form->{category},
223                   $form->{link},
224                   conv_i($form->{pos_bwa}),
225                   conv_i($form->{pos_bilanz}),
226                   conv_i($form->{pos_eur}),
227                   conv_i($form->{pos_er}),
228                   conv_i($form->{new_chart_id}),
229                   conv_date($form->{valid_from}),
230                   ($form->{datevautomatik} eq 'T') ? 'true':'false',
231                 $form->{id},
232     );
233
234
235   }
236
237   do_query($form, $dbh, $query, @values);
238
239   #Save Taxkeys
240
241   my @taxkeys = ();
242
243   my $MAX_TRIES = 10; # Maximum count of taxkeys in form
244   my $tk_count;
245
246   READTAXKEYS:
247   for $tk_count (0 .. $MAX_TRIES) {
248
249     # Loop control
250
251     # Check if the account already exists, else cancel
252
253     print(STDERR "Keine Taxkeys weil ID =: $form->{id}\n");
254
255     last READTAXKEYS if ( $form->{'id'} == 0);
256
257     # check if there is a startdate
258     if ( $form->{"taxkey_startdate_$tk_count"} eq '' ) {
259       $tk_count++;
260       next READTAXKEYS;
261     }
262
263     # Add valid taxkeys into the array
264     push @taxkeys ,
265       {
266         id        => ($form->{"taxkey_id_$tk_count"} eq 'NEW') ? conv_i('') : conv_i($form->{"taxkey_id_$tk_count"}),
267         tax_id    => conv_i($form->{"taxkey_tax_$tk_count"}),
268         startdate => conv_date($form->{"taxkey_startdate_$tk_count"}),
269         chart_id  => conv_i($form->{"id"}),
270         pos_ustva => conv_i($form->{"taxkey_pos_ustva_$tk_count"}),
271         delete    => ( $form->{"taxkey_del_$tk_count"} eq 'delete' ) ? '1' : '',
272       };
273
274     $tk_count++;
275   }
276
277   TAXKEY:
278   for my $j (0 .. $#taxkeys){
279     if ( defined $taxkeys[$j]{'id'} ){
280       # delete Taxkey?
281
282       if ($taxkeys[$j]{'delete'}){
283         $query = qq{
284           DELETE FROM taxkeys WHERE id = ?
285         };
286
287         @values = ($taxkeys[$j]{'id'});
288
289         do_query($form, $dbh, $query, @values);
290
291         next TAXKEY;
292       }
293
294       # UPDATE Taxkey
295
296       $query = qq{
297         UPDATE taxkeys
298         SET taxkey_id = (SELECT taxkey FROM tax WHERE tax.id = ?),
299             chart_id  = ?,
300             tax_id    = ?,
301             pos_ustva = ?,
302             startdate = ?
303         WHERE id = ?
304       };
305       @values = (
306         $taxkeys[$j]{'tax_id'},
307         $taxkeys[$j]{'chart_id'},
308         $taxkeys[$j]{'tax_id'},
309         $taxkeys[$j]{'pos_ustva'},
310         $taxkeys[$j]{'startdate'},
311         $taxkeys[$j]{'id'},
312       );
313       do_query($form, $dbh, $query, @values);
314     }
315     else {
316       # INSERT Taxkey
317
318       $query = qq{
319         INSERT INTO taxkeys (
320           taxkey_id,
321           chart_id,
322           tax_id,
323           pos_ustva,
324           startdate
325         )
326         VALUES ((SELECT taxkey FROM tax WHERE tax.id = ?), ?, ?, ?, ?)
327       };
328       @values = (
329         $taxkeys[$j]{'tax_id'},
330         $taxkeys[$j]{'chart_id'},
331         $taxkeys[$j]{'tax_id'},
332         $taxkeys[$j]{'pos_ustva'},
333         $taxkeys[$j]{'startdate'},
334       );
335
336       do_query($form, $dbh, $query, @values);
337     }
338
339   }
340
341   # Update chart.taxkey_id to the latest from taxkeys for this chart.
342   $query = <<SQL;
343     UPDATE chart
344     SET taxkey_id = (
345       SELECT taxkey_id
346       FROM taxkeys
347       WHERE taxkeys.chart_id = chart.id
348       ORDER BY startdate DESC
349       LIMIT 1
350     )
351     WHERE id = ?
352 SQL
353
354   do_query($form, $dbh, $query, $form->{id});
355
356   # commit
357   my $rc = $dbh->commit;
358   $dbh->disconnect;
359
360   $main::lxdebug->leave_sub();
361
362   return $rc;
363 }
364
365 sub delete_account {
366   $main::lxdebug->enter_sub();
367
368   my ($self, $myconfig, $form) = @_;
369
370   # connect to database, turn off AutoCommit
371   my $dbh = $form->dbconnect_noauto($myconfig);
372
373   my $query = qq|SELECT count(*) FROM acc_trans a
374                  WHERE a.chart_id = ?|;
375   my ($count) = selectrow_query($form, $dbh, $query, $form->{id});
376
377   if ($count) {
378     $dbh->disconnect;
379     $main::lxdebug->leave_sub();
380     return;
381   }
382
383   # set inventory_accno_id, income_accno_id, expense_accno_id to defaults
384   foreach my $type (qw(inventory income expense)) {
385     $query =
386       qq|UPDATE parts | .
387       qq|SET ${type}_accno_id = (SELECT ${type}_accno_id FROM defaults) | .
388       qq|WHERE ${type}_accno_id = ?|;
389     do_query($form, $dbh, $query, $form->{id});
390   }
391
392   $query = qq|DELETE FROM tax
393               WHERE chart_id = ?|;
394   do_query($form, $dbh, $query, $form->{id});
395
396   # delete account taxkeys
397   $query = qq|DELETE FROM taxkeys
398               WHERE chart_id = ?|;
399   do_query($form, $dbh, $query, $form->{id});
400
401   # delete chart of account record
402   # last step delete chart, because we have a constraint
403   # to taxkeys
404   $query = qq|DELETE FROM chart
405               WHERE id = ?|;
406   do_query($form, $dbh, $query, $form->{id});
407
408   # commit and redirect
409   my $rc = $dbh->commit;
410   $dbh->disconnect;
411
412   $main::lxdebug->leave_sub();
413
414   return $rc;
415 }
416
417 sub lead {
418   $main::lxdebug->enter_sub();
419
420   my ($self, $myconfig, $form) = @_;
421
422   # connect to database
423   my $dbh = $form->dbconnect($myconfig);
424
425   my $query = qq|SELECT id, lead
426                  FROM leads
427                  ORDER BY 2|;
428
429   my $sth = $dbh->prepare($query);
430   $sth->execute || $form->dberror($query);
431
432   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
433     push @{ $form->{ALL} }, $ref;
434   }
435
436   $sth->finish;
437   $dbh->disconnect;
438
439   $main::lxdebug->leave_sub();
440 }
441
442 sub get_lead {
443   $main::lxdebug->enter_sub();
444
445   my ($self, $myconfig, $form) = @_;
446
447   # connect to database
448   my $dbh = $form->dbconnect($myconfig);
449
450   my $query =
451     qq|SELECT l.id, l.lead | .
452     qq|FROM leads l | .
453     qq|WHERE l.id = ?|;
454   my $sth = $dbh->prepare($query);
455   $sth->execute($form->{id}) || $form->dberror($query . " ($form->{id})");
456
457   my $ref = $sth->fetchrow_hashref("NAME_lc");
458
459   map { $form->{$_} = $ref->{$_} } keys %$ref;
460
461   $sth->finish;
462
463   $dbh->disconnect;
464
465   $main::lxdebug->leave_sub();
466 }
467
468 sub save_lead {
469   $main::lxdebug->enter_sub();
470
471   my ($self, $myconfig, $form) = @_;
472   my ($query);
473
474   # connect to database
475   my $dbh = $form->dbconnect($myconfig);
476
477   my @values = ($form->{description});
478   # id is the old record
479   if ($form->{id}) {
480     $query = qq|UPDATE leads SET
481                 lead = ?
482                 WHERE id = ?|;
483     push(@values, $form->{id});
484   } else {
485     $query = qq|INSERT INTO leads
486                 (lead)
487                 VALUES (?)|;
488   }
489   do_query($form, $dbh, $query, @values);
490
491   $dbh->disconnect;
492
493   $main::lxdebug->leave_sub();
494 }
495
496 sub delete_lead {
497   $main::lxdebug->enter_sub();
498
499   my ($self, $myconfig, $form) = @_;
500   my ($query);
501
502   # connect to database
503   my $dbh = $form->dbconnect($myconfig);
504
505   $query = qq|DELETE FROM leads
506               WHERE id = ?|;
507   do_query($form, $dbh, $query, $form->{id});
508
509   $dbh->disconnect;
510
511   $main::lxdebug->leave_sub();
512 }
513
514 sub language {
515   $main::lxdebug->enter_sub();
516
517   my ($self, $myconfig, $form, $return_list) = @_;
518
519   # connect to database
520   my $dbh = $form->dbconnect($myconfig);
521
522   my $query =
523     "SELECT id, description, template_code, article_code, " .
524     "  output_numberformat, output_dateformat, output_longdates " .
525     "FROM language ORDER BY description";
526
527   my $sth = $dbh->prepare($query);
528   $sth->execute || $form->dberror($query);
529
530   my $ary = [];
531
532   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
533     push(@{ $ary }, $ref);
534   }
535
536   $sth->finish;
537   $dbh->disconnect;
538
539   $main::lxdebug->leave_sub();
540
541   if ($return_list) {
542     return @{$ary};
543   } else {
544     $form->{ALL} = $ary;
545   }
546 }
547
548 sub get_language {
549   $main::lxdebug->enter_sub();
550
551   my ($self, $myconfig, $form) = @_;
552
553   # connect to database
554   my $dbh = $form->dbconnect($myconfig);
555
556   my $query =
557     "SELECT description, template_code, article_code, " .
558     "  output_numberformat, output_dateformat, output_longdates " .
559     "FROM language WHERE id = ?";
560   my $sth = $dbh->prepare($query);
561   $sth->execute($form->{"id"}) || $form->dberror($query . " ($form->{id})");
562
563   my $ref = $sth->fetchrow_hashref("NAME_lc");
564
565   map { $form->{$_} = $ref->{$_} } keys %$ref;
566
567   $sth->finish;
568
569   $dbh->disconnect;
570
571   $main::lxdebug->leave_sub();
572 }
573
574 sub get_language_details {
575   $main::lxdebug->enter_sub();
576
577   my ($self, $myconfig, $form, $id) = @_;
578
579   # connect to database
580   my $dbh = $form->dbconnect($myconfig);
581
582   my $query =
583     "SELECT template_code, " .
584     "  output_numberformat, output_dateformat, output_longdates " .
585     "FROM language WHERE id = ?";
586   my @res = selectrow_query($form, $dbh, $query, $id);
587   $dbh->disconnect;
588
589   $main::lxdebug->leave_sub();
590
591   return @res;
592 }
593
594 sub save_language {
595   $main::lxdebug->enter_sub();
596
597   my ($self, $myconfig, $form) = @_;
598
599   # connect to database
600   my $dbh = $form->dbconnect($myconfig);
601   my (@values, $query);
602
603   map({ push(@values, $form->{$_}); }
604       qw(description template_code article_code
605          output_numberformat output_dateformat output_longdates));
606
607   # id is the old record
608   if ($form->{id}) {
609     $query =
610       "UPDATE language SET " .
611       "  description = ?, template_code = ?, article_code = ?, " .
612       "  output_numberformat = ?, output_dateformat = ?, " .
613       "  output_longdates = ? " .
614       "WHERE id = ?";
615     push(@values, $form->{id});
616   } else {
617     $query =
618       "INSERT INTO language (" .
619       "  description, template_code, article_code, " .
620       "  output_numberformat, output_dateformat, output_longdates" .
621       ") VALUES (?, ?, ?, ?, ?, ?)";
622   }
623   do_query($form, $dbh, $query, @values);
624
625   $dbh->disconnect;
626
627   $main::lxdebug->leave_sub();
628 }
629
630 sub delete_language {
631   $main::lxdebug->enter_sub();
632
633   my ($self, $myconfig, $form) = @_;
634   my $query;
635
636   # connect to database
637   my $dbh = $form->dbconnect_noauto($myconfig);
638
639   foreach my $table (qw(generic_translations units_language)) {
640     $query = qq|DELETE FROM $table WHERE language_id = ?|;
641     do_query($form, $dbh, $query, $form->{"id"});
642   }
643
644   $query = "DELETE FROM language WHERE id = ?";
645   do_query($form, $dbh, $query, $form->{"id"});
646
647   $dbh->commit();
648   $dbh->disconnect;
649
650   $main::lxdebug->leave_sub();
651 }
652
653 sub prepare_template_filename {
654   $main::lxdebug->enter_sub();
655
656   my ($self, $myconfig, $form) = @_;
657
658   my ($filename, $display_filename);
659
660   if ($form->{type} eq "stylesheet") {
661     $filename = "css/$myconfig->{stylesheet}";
662     $display_filename = $myconfig->{stylesheet};
663
664   } else {
665     $filename = $form->{formname};
666
667     if ($form->{language}) {
668       my ($id, $template_code) = split(/--/, $form->{language});
669       $filename .= "_${template_code}";
670     }
671
672     if ($form->{printer}) {
673       my ($id, $template_code) = split(/--/, $form->{printer});
674       $filename .= "_${template_code}";
675     }
676
677     $filename .= "." . ($form->{format} eq "html" ? "html" : "tex");
678     if ($form->{"formname"} =~ m|\.\.| || $form->{"formname"} =~ m|^/|) {
679       $filename =~ s|.*/||;
680     }
681     $display_filename = $filename;
682     $filename = SL::DB::Default->get->templates . "/$filename";
683   }
684
685   $main::lxdebug->leave_sub();
686
687   return ($filename, $display_filename);
688 }
689
690
691 sub load_template {
692   $main::lxdebug->enter_sub();
693
694   my ($self, $filename) = @_;
695
696   my ($content, $lines) = ("", 0);
697
698   local *TEMPLATE;
699
700   if (open(TEMPLATE, $filename)) {
701     while (<TEMPLATE>) {
702       $content .= $_;
703       $lines++;
704     }
705     close(TEMPLATE);
706   }
707
708   $content = Encode::decode('utf-8-strict', $content);
709
710   $main::lxdebug->leave_sub();
711
712   return ($content, $lines);
713 }
714
715 sub save_template {
716   $main::lxdebug->enter_sub();
717
718   my ($self, $filename, $content) = @_;
719
720   local *TEMPLATE;
721
722   my $error = "";
723
724   if (open(TEMPLATE, ">", $filename)) {
725     $content = Encode::encode('utf-8-strict', $content);
726     $content =~ s/\r\n/\n/g;
727     print(TEMPLATE $content);
728     close(TEMPLATE);
729   } else {
730     $error = $!;
731   }
732
733   $main::lxdebug->leave_sub();
734
735   return $error;
736 }
737
738 sub save_preferences {
739   $main::lxdebug->enter_sub();
740
741   my ($self, $form) = @_;
742
743   my $employee = SL::DB::Manager::Employee->find_by(login => $::myconfig{login});
744   $employee->update_attributes(name => $form->{name});
745
746   my $user = SL::DB::Manager::AuthUser->find_by(login => $::myconfig{login});
747   $user->update_attributes(
748     config_values => {
749       %{ $user->config_values },
750       map { ($_ => $form->{$_}) } SL::DB::AuthUser::CONFIG_VARS(),
751     });
752
753   $main::lxdebug->leave_sub();
754
755   return 1;
756 }
757
758 sub get_defaults {
759   $main::lxdebug->enter_sub();
760
761   my $self     = shift;
762   my %params   = @_;
763
764   my $myconfig = \%main::myconfig;
765   my $form     = $main::form;
766
767   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
768
769   my $defaults = selectfirst_hashref_query($form, $dbh, qq|SELECT * FROM defaults|) || {};
770
771   $defaults->{weightunit} ||= 'kg';
772
773   $main::lxdebug->leave_sub();
774
775   return $defaults;
776 }
777
778 sub closedto {
779   $main::lxdebug->enter_sub();
780
781   my ($self, $myconfig, $form) = @_;
782
783   my $dbh = $form->dbconnect($myconfig);
784
785   my $query = qq|SELECT closedto, max_future_booking_interval, revtrans FROM defaults|;
786   my $sth   = $dbh->prepare($query);
787   $sth->execute || $form->dberror($query);
788
789   ($form->{closedto}, $form->{max_future_booking_interval}, $form->{revtrans}) = $sth->fetchrow_array;
790
791   $sth->finish;
792
793   $dbh->disconnect;
794
795   $main::lxdebug->leave_sub();
796 }
797
798 sub closebooks {
799   $main::lxdebug->enter_sub();
800
801   my ($self, $myconfig, $form) = @_;
802
803   my $dbh = $form->dbconnect($myconfig);
804
805   my ($query, @values);
806
807   # is currently NEVER trueish (no more hidden revtrans in $form)
808   # if ($form->{revtrans}) {
809   #   $query = qq|UPDATE defaults SET closedto = NULL, revtrans = '1'|;
810   # -> therefore you can only set this to false (which is already the default)
811   # and this flag is currently only checked in gl.pl. TOOD Can probably be removed
812
813     $query = qq|UPDATE defaults SET closedto = ?, max_future_booking_interval = ?, revtrans = '0'|;
814     @values = (conv_date($form->{closedto}), conv_i($form->{max_future_booking_interval}));
815
816   # set close in defaults
817   do_query($form, $dbh, $query, @values);
818
819   $dbh->disconnect;
820
821   $main::lxdebug->leave_sub();
822 }
823
824 sub get_base_unit {
825   my ($self, $units, $unit_name, $factor) = @_;
826
827   $factor = 1 unless ($factor);
828
829   my $unit = $units->{$unit_name};
830
831   if (!defined($unit) || !$unit->{"base_unit"} ||
832       ($unit_name eq $unit->{"base_unit"})) {
833     return ($unit_name, $factor);
834   }
835
836   return AM->get_base_unit($units, $unit->{"base_unit"}, $factor * $unit->{"factor"});
837 }
838
839 sub retrieve_units {
840   $main::lxdebug->enter_sub();
841
842   my ($self, $myconfig, $form, $prefix) = @_;
843   $prefix ||= '';
844
845   my $dbh = $form->get_standard_dbh;
846
847   my $query = "SELECT *, base_unit AS original_base_unit FROM units";
848
849   my $sth = prepare_execute_query($form, $dbh, $query);
850
851   my $units = {};
852   while (my $ref = $sth->fetchrow_hashref()) {
853     $units->{$ref->{"name"}} = $ref;
854   }
855   $sth->finish();
856
857   my $query_lang = "SELECT id, template_code FROM language ORDER BY description";
858   $sth = $dbh->prepare($query_lang);
859   $sth->execute() || $form->dberror($query_lang);
860   my @languages;
861   while (my $ref = $sth->fetchrow_hashref()) {
862     push(@languages, $ref);
863   }
864   $sth->finish();
865
866   $query_lang = "SELECT ul.localized, ul.localized_plural, l.id, l.template_code " .
867     "FROM units_language ul " .
868     "LEFT JOIN language l ON ul.language_id = l.id " .
869     "WHERE ul.unit = ?";
870   $sth = $dbh->prepare($query_lang);
871
872   foreach my $unit (values(%{$units})) {
873     ($unit->{"${prefix}base_unit"}, $unit->{"${prefix}factor"}) = AM->get_base_unit($units, $unit->{"name"});
874
875     $unit->{"LANGUAGES"} = {};
876     foreach my $lang (@languages) {
877       $unit->{"LANGUAGES"}->{$lang->{"template_code"}} = { "template_code" => $lang->{"template_code"} };
878     }
879
880     $sth->execute($unit->{"name"}) || $form->dberror($query_lang . " (" . $unit->{"name"} . ")");
881     while (my $ref = $sth->fetchrow_hashref()) {
882       map({ $unit->{"LANGUAGES"}->{$ref->{"template_code"}}->{$_} = $ref->{$_} } keys(%{$ref}));
883     }
884   }
885   $sth->finish;
886
887   $main::lxdebug->leave_sub();
888
889   return $units;
890 }
891
892 sub retrieve_all_units {
893   $main::lxdebug->enter_sub();
894
895   my $self = shift;
896
897   if (!$::request->{cache}{all_units}) {
898     $::request->{cache}{all_units} = $self->retrieve_units(\%main::myconfig, $main::form);
899   }
900
901   $main::lxdebug->leave_sub();
902
903   return $::request->{cache}{all_units};
904 }
905
906
907 sub translate_units {
908   $main::lxdebug->enter_sub();
909
910   my ($self, $form, $template_code, $unit, $amount) = @_;
911
912   my $units = $self->retrieve_units(\%main::myconfig, $form);
913
914   my $h = $units->{$unit}->{"LANGUAGES"}->{$template_code};
915   my $new_unit = $unit;
916   if ($h) {
917     if (($amount != 1) && $h->{"localized_plural"}) {
918       $new_unit = $h->{"localized_plural"};
919     } elsif ($h->{"localized"}) {
920       $new_unit = $h->{"localized"};
921     }
922   }
923
924   $main::lxdebug->leave_sub();
925
926   return $new_unit;
927 }
928
929 sub units_in_use {
930   $main::lxdebug->enter_sub();
931
932   my ($self, $myconfig, $form, $units) = @_;
933
934   my $dbh = $form->dbconnect($myconfig);
935
936   map({ $_->{"in_use"} = 0; } values(%{$units}));
937
938   foreach my $unit (values(%{$units})) {
939     my $base_unit = $unit->{"original_base_unit"};
940     while ($base_unit) {
941       $units->{$base_unit}->{"in_use"} = 1;
942       $units->{$base_unit}->{"DEPENDING_UNITS"} = [] unless ($units->{$base_unit}->{"DEPENDING_UNITS"});
943       push(@{$units->{$base_unit}->{"DEPENDING_UNITS"}}, $unit->{"name"});
944       $base_unit = $units->{$base_unit}->{"original_base_unit"};
945     }
946   }
947
948   foreach my $unit (values(%{$units})) {
949     map({ $_ = $dbh->quote($_); } @{$unit->{"DEPENDING_UNITS"}});
950
951     foreach my $table (qw(parts invoice orderitems)) {
952       my $query = "SELECT COUNT(*) FROM $table WHERE unit ";
953
954       if (0 == scalar(@{$unit->{"DEPENDING_UNITS"}})) {
955         $query .= "= " . $dbh->quote($unit->{"name"});
956       } else {
957         $query .= "IN (" . $dbh->quote($unit->{"name"}) . "," .
958           join(",", map({ $dbh->quote($_) } @{$unit->{"DEPENDING_UNITS"}})) . ")";
959       }
960
961       my ($count) = $dbh->selectrow_array($query);
962       $form->dberror($query) if ($dbh->err);
963
964       if ($count) {
965         $unit->{"in_use"} = 1;
966         last;
967       }
968     }
969   }
970
971   $dbh->disconnect();
972
973   $main::lxdebug->leave_sub();
974 }
975
976 sub convertible_units {
977   $main::lxdebug->enter_sub();
978
979   my $self        = shift;
980   my $units       = shift;
981   my $filter_unit = shift;
982   my $not_smaller = shift;
983
984   my $conv_units = [];
985
986   $filter_unit = $units->{$filter_unit};
987
988   foreach my $name (sort { lc $a cmp lc $b } keys %{ $units }) {
989     my $unit = $units->{$name};
990
991     if (($unit->{base_unit} eq $filter_unit->{base_unit}) &&
992         (!$not_smaller || ($unit->{factor} >= $filter_unit->{factor}))) {
993       push @{$conv_units}, $unit;
994     }
995   }
996
997   my @sorted = sort { $b->{factor} <=> $a->{factor} } @{ $conv_units };
998
999   $main::lxdebug->leave_sub();
1000
1001   return \@sorted;
1002 }
1003
1004 # if $a is translatable to $b, return the factor between them.
1005 # else return 1
1006 sub convert_unit {
1007   $main::lxdebug->enter_sub(2);
1008   my ($this, $a, $b, $all_units) = @_;
1009
1010   if (!$all_units) {
1011     $all_units = $this->retrieve_all_units;
1012   }
1013
1014   $main::lxdebug->leave_sub(2) and return 0 unless $a && $b;
1015   $main::lxdebug->leave_sub(2) and return 0 unless $all_units->{$a} && $all_units->{$b};
1016   $main::lxdebug->leave_sub(2) and return 0 unless $all_units->{$a}{base_unit} eq $all_units->{$b}{base_unit};
1017   $main::lxdebug->leave_sub(2) and return $all_units->{$a}{factor} / $all_units->{$b}{factor};
1018 }
1019
1020 sub unit_select_data {
1021   $main::lxdebug->enter_sub();
1022
1023   my ($self, $units, $selected, $empty_entry, $convertible_into) = @_;
1024
1025   my $select = [];
1026
1027   if ($empty_entry) {
1028     push(@{$select}, { "name" => "", "base_unit" => "", "factor" => "", "selected" => "" });
1029   }
1030
1031   foreach my $unit (sort({ $units->{$a}->{"sortkey"} <=> $units->{$b}->{"sortkey"} } keys(%{$units}))) {
1032     if (!$convertible_into ||
1033         ($units->{$convertible_into} &&
1034          ($units->{$convertible_into}->{base_unit} eq $units->{$unit}->{base_unit}))) {
1035       push @{$select}, { "name"      => $unit,
1036                          "base_unit" => $units->{$unit}->{"base_unit"},
1037                          "factor"    => $units->{$unit}->{"factor"},
1038                          "selected"  => ($unit eq $selected) ? "selected" : "" };
1039     }
1040   }
1041
1042   $main::lxdebug->leave_sub();
1043
1044   return $select;
1045 }
1046
1047 sub unit_select_html {
1048   $main::lxdebug->enter_sub();
1049
1050   my ($self, $units, $name, $selected, $convertible_into) = @_;
1051
1052   my $select = "<select name=${name}>";
1053
1054   foreach my $unit (sort({ $units->{$a}->{"sortkey"} <=> $units->{$b}->{"sortkey"} } keys(%{$units}))) {
1055     if (!$convertible_into ||
1056         ($units->{$convertible_into} &&
1057          ($units->{$convertible_into}->{"base_unit"} eq $units->{$unit}->{"base_unit"}))) {
1058       $select .= "<option" . (($unit eq $selected) ? " selected" : "") . ">${unit}</option>";
1059     }
1060   }
1061   $select .= "</select>";
1062
1063   $main::lxdebug->leave_sub();
1064
1065   return $select;
1066 }
1067
1068 sub sum_with_unit {
1069   $main::lxdebug->enter_sub();
1070
1071   my $self  = shift;
1072
1073   my $units = $self->retrieve_all_units();
1074
1075   my $sum   = 0;
1076   my $base_unit;
1077
1078   while (2 <= scalar(@_)) {
1079     my $qty  = shift(@_);
1080     my $unit = $units->{shift(@_)};
1081
1082     croak "No unit defined with name $unit" if (!defined $unit);
1083
1084     if (!$base_unit) {
1085       $base_unit = $unit->{base_unit};
1086     } elsif ($base_unit ne $unit->{base_unit}) {
1087       croak "Adding values with incompatible base units $base_unit/$unit->{base_unit}";
1088     }
1089
1090     $sum += $qty * $unit->{factor};
1091   }
1092
1093   $main::lxdebug->leave_sub();
1094
1095   return $sum;
1096 }
1097
1098 sub add_unit {
1099   $main::lxdebug->enter_sub();
1100
1101   my ($self, $myconfig, $form, $name, $base_unit, $factor, $languages) = @_;
1102
1103   my $dbh = $form->dbconnect_noauto($myconfig);
1104
1105   my $query = qq|SELECT COALESCE(MAX(sortkey), 0) + 1 FROM units|;
1106   my ($sortkey) = selectrow_query($form, $dbh, $query);
1107
1108   $query = "INSERT INTO units (name, base_unit, factor, sortkey) " .
1109     "VALUES (?, ?, ?, ?)";
1110   do_query($form, $dbh, $query, $name, $base_unit, $factor, $sortkey);
1111
1112   if ($languages) {
1113     $query = "INSERT INTO units_language (unit, language_id, localized, localized_plural) VALUES (?, ?, ?, ?)";
1114     my $sth = $dbh->prepare($query);
1115     foreach my $lang (@{$languages}) {
1116       my @values = ($name, $lang->{"id"}, $lang->{"localized"}, $lang->{"localized_plural"});
1117       $sth->execute(@values) || $form->dberror($query . " (" . join(", ", @values) . ")");
1118     }
1119     $sth->finish();
1120   }
1121
1122   $dbh->commit();
1123   $dbh->disconnect();
1124
1125   $main::lxdebug->leave_sub();
1126 }
1127
1128 sub save_units {
1129   $main::lxdebug->enter_sub();
1130
1131   my ($self, $myconfig, $form, $units, $delete_units) = @_;
1132
1133   my $dbh = $form->dbconnect_noauto($myconfig);
1134
1135   my ($base_unit, $unit, $sth, $query);
1136
1137   $query = "DELETE FROM units_language";
1138   $dbh->do($query) || $form->dberror($query);
1139
1140   if ($delete_units && (0 != scalar(@{$delete_units}))) {
1141     $query = "DELETE FROM units WHERE name IN (";
1142     map({ $query .= "?," } @{$delete_units});
1143     substr($query, -1, 1) = ")";
1144     $dbh->do($query, undef, @{$delete_units}) ||
1145       $form->dberror($query . " (" . join(", ", @{$delete_units}) . ")");
1146   }
1147
1148   $query = "UPDATE units SET name = ?, base_unit = ?, factor = ? WHERE name = ?";
1149   $sth = $dbh->prepare($query);
1150
1151   my $query_lang = "INSERT INTO units_language (unit, language_id, localized, localized_plural) VALUES (?, ?, ?, ?)";
1152   my $sth_lang = $dbh->prepare($query_lang);
1153
1154   foreach $unit (values(%{$units})) {
1155     $unit->{"depth"} = 0;
1156     my $base_unit = $unit;
1157     while ($base_unit->{"base_unit"}) {
1158       $unit->{"depth"}++;
1159       $base_unit = $units->{$base_unit->{"base_unit"}};
1160     }
1161   }
1162
1163   foreach $unit (sort({ $a->{"depth"} <=> $b->{"depth"} } values(%{$units}))) {
1164     if ($unit->{"LANGUAGES"}) {
1165       foreach my $lang (@{$unit->{"LANGUAGES"}}) {
1166         next unless ($lang->{"id"} && $lang->{"localized"});
1167         my @values = ($unit->{"name"}, $lang->{"id"}, $lang->{"localized"}, $lang->{"localized_plural"});
1168         $sth_lang->execute(@values) || $form->dberror($query_lang . " (" . join(", ", @values) . ")");
1169       }
1170     }
1171
1172     next if ($unit->{"unchanged_unit"});
1173
1174     my @values = ($unit->{"name"}, $unit->{"base_unit"}, $unit->{"factor"}, $unit->{"old_name"});
1175     $sth->execute(@values) || $form->dberror($query . " (" . join(", ", @values) . ")");
1176   }
1177
1178   $sth->finish();
1179   $sth_lang->finish();
1180   $dbh->commit();
1181   $dbh->disconnect();
1182
1183   $main::lxdebug->leave_sub();
1184 }
1185
1186 sub taxes {
1187   $main::lxdebug->enter_sub();
1188
1189   my ($self, $myconfig, $form) = @_;
1190
1191   # connect to database
1192   my $dbh = $form->dbconnect($myconfig);
1193
1194   my $query = qq|SELECT
1195                    t.id,
1196                    t.taxkey,
1197                    t.taxdescription,
1198                    round(t.rate * 100, 2) AS rate,
1199                    (SELECT accno FROM chart WHERE id = chart_id) AS taxnumber,
1200                    (SELECT description FROM chart WHERE id = chart_id) AS account_description,
1201                    (SELECT accno FROM chart WHERE id = skonto_sales_chart_id) AS skonto_chart_accno,
1202                    (SELECT description FROM chart WHERE id = skonto_sales_chart_id) AS skonto_chart_description,
1203                    (SELECT accno FROM chart WHERE id = skonto_purchase_chart_id) AS skonto_chart_purchase_accno,
1204                    (SELECT description FROM chart WHERE id = skonto_purchase_chart_id) AS skonto_chart_purchase_description
1205                  FROM tax t
1206                  ORDER BY taxkey, rate|;
1207
1208   my $sth = $dbh->prepare($query);
1209   $sth->execute || $form->dberror($query);
1210
1211   $form->{TAX} = [];
1212   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
1213     push @{ $form->{TAX} }, $ref;
1214   }
1215
1216   $sth->finish;
1217   $dbh->disconnect;
1218
1219   $main::lxdebug->leave_sub();
1220 }
1221
1222 sub get_tax_accounts {
1223   $main::lxdebug->enter_sub();
1224
1225   my ($self, $myconfig, $form) = @_;
1226
1227   my $dbh = $form->dbconnect($myconfig);
1228
1229   # get Accounts from chart
1230   my $query = qq{ SELECT
1231                  id,
1232                  accno || ' - ' || description AS taxaccount
1233                FROM chart
1234                WHERE link LIKE '%_tax%'
1235                ORDER BY accno
1236              };
1237
1238   my $sth = $dbh->prepare($query);
1239   $sth->execute || $form->dberror($query);
1240
1241   $form->{ACCOUNTS} = [];
1242   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
1243     push @{ $form->{ACCOUNTS} }, $ref;
1244   }
1245
1246   $form->{AR_PAID} = SL::DB::Manager::Chart->get_all(where => [ link => { like => '%AR_paid%' } ], sort_by => 'accno ASC');
1247   $form->{AP_PAID} = SL::DB::Manager::Chart->get_all(where => [ link => { like => '%AP_paid%' } ], sort_by => 'accno ASC');
1248
1249   $form->{skontochart_value_title_sub} = sub {
1250     my $item = shift;
1251     return [
1252       $item->{id},
1253       $item->{accno} .' '. $item->{description},
1254     ];
1255   };
1256
1257   $sth->finish;
1258
1259   $dbh->disconnect;
1260
1261   $main::lxdebug->leave_sub();
1262 }
1263
1264 sub get_tax {
1265   $main::lxdebug->enter_sub();
1266
1267   my ($self, $myconfig, $form) = @_;
1268
1269   # connect to database
1270   my $dbh = $form->dbconnect($myconfig);
1271
1272   my $query = qq|SELECT
1273                    taxkey,
1274                    taxdescription,
1275                    round(rate * 100, 2) AS rate,
1276                    chart_id,
1277                    chart_categories,
1278                    (id IN (SELECT tax_id
1279                            FROM acc_trans)) AS tax_already_used,
1280                    skonto_sales_chart_id,
1281                    skonto_purchase_chart_id
1282                  FROM tax
1283                  WHERE id = ? |;
1284
1285   my $sth = $dbh->prepare($query);
1286   $sth->execute($form->{id}) || $form->dberror($query . " ($form->{id})");
1287
1288   my $ref = $sth->fetchrow_hashref("NAME_lc");
1289
1290   map { $form->{$_} = $ref->{$_} } keys %$ref;
1291
1292   $sth->finish;
1293
1294   # see if it is used by a taxkey
1295   $query = qq|SELECT count(*) FROM taxkeys
1296               WHERE tax_id = ? AND chart_id >0|;
1297
1298   ($form->{orphaned}) = selectrow_query($form, $dbh, $query, $form->{id});
1299
1300   $form->{orphaned} = !$form->{orphaned};
1301   $sth->finish;
1302
1303   if (!$form->{orphaned} ) {
1304     $query = qq|SELECT DISTINCT c.id, c.accno
1305                 FROM taxkeys tk
1306                 JOIN   tax t ON (t.id = tk.tax_id)
1307                 JOIN chart c ON (c.id = tk.chart_id)
1308                 WHERE tk.tax_id = ?|;
1309
1310     $sth = $dbh->prepare($query);
1311     $sth->execute($form->{id}) || $form->dberror($query . " ($form->{id})");
1312
1313     $form->{TAXINUSE} = [];
1314     while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
1315       push @{ $form->{TAXINUSE} }, $ref;
1316     }
1317
1318     $sth->finish;
1319   }
1320
1321   $dbh->disconnect;
1322
1323   $main::lxdebug->leave_sub();
1324 }
1325
1326 sub save_tax {
1327   $main::lxdebug->enter_sub();
1328
1329   my ($self, $myconfig, $form) = @_;
1330   my $query;
1331
1332   # connect to database
1333   my $dbh = $form->get_standard_dbh($myconfig);
1334
1335   $form->{rate} = $form->{rate} / 100;
1336
1337   my $chart_categories = '';
1338   $chart_categories .= 'A' if $form->{asset};
1339   $chart_categories .= 'L' if $form->{liability};
1340   $chart_categories .= 'Q' if $form->{equity};
1341   $chart_categories .= 'I' if $form->{revenue};
1342   $chart_categories .= 'E' if $form->{expense};
1343   $chart_categories .= 'C' if $form->{costs};
1344
1345   my @values = ($form->{taxkey}, $form->{taxdescription}, $form->{rate}, conv_i($form->{chart_id}), conv_i($form->{chart_id}), conv_i($form->{skonto_sales_chart_id}), conv_i($form->{skonto_purchase_chart_id}), $chart_categories);
1346   if ($form->{id} ne "") {
1347     $query = qq|UPDATE tax SET
1348                   taxkey                   = ?,
1349                   taxdescription           = ?,
1350                   rate                     = ?,
1351                   chart_id                 = ?,
1352                   taxnumber                = (SELECT accno FROM chart WHERE id = ? ),
1353                   skonto_sales_chart_id    = ?,
1354                   skonto_purchase_chart_id = ?,
1355                   chart_categories         = ?
1356                 WHERE id = ?|;
1357
1358   } else {
1359     #ok
1360     ($form->{id}) = selectfirst_array_query($form, $dbh, qq|SELECT nextval('id')|);
1361     $query = qq|INSERT INTO tax (
1362                   taxkey,
1363                   taxdescription,
1364                   rate,
1365                   chart_id,
1366                   taxnumber,
1367                   skonto_sales_chart_id,
1368                   skonto_purchase_chart_id,
1369                   chart_categories,
1370                   id
1371                 )
1372                 VALUES (?, ?, ?, ?, (SELECT accno FROM chart WHERE id = ?), ?, ?,  ?, ?)|;
1373   }
1374   push(@values, $form->{id});
1375   do_query($form, $dbh, $query, @values);
1376
1377   foreach my $language_id (keys %{ $form->{translations} }) {
1378     GenericTranslations->save('dbh'              => $dbh,
1379                               'translation_type' => 'SL::DB::Tax/taxdescription',
1380                               'translation_id'   => $form->{id},
1381                               'language_id'      => $language_id,
1382                               'translation'      => $form->{translations}->{$language_id});
1383   }
1384
1385   $dbh->commit();
1386
1387   $main::lxdebug->leave_sub();
1388 }
1389
1390 sub delete_tax {
1391   $main::lxdebug->enter_sub();
1392
1393   my ($self, $myconfig, $form) = @_;
1394   my $query;
1395
1396   # connect to database
1397   my $dbh = $form->get_standard_dbh($myconfig);
1398
1399   $query = qq|DELETE FROM tax
1400               WHERE id = ?|;
1401   do_query($form, $dbh, $query, $form->{id});
1402
1403   $dbh->commit();
1404
1405   $main::lxdebug->leave_sub();
1406 }
1407
1408 sub save_price_factor {
1409   $main::lxdebug->enter_sub();
1410
1411   my ($self, $myconfig, $form) = @_;
1412
1413   # connect to database
1414   my $dbh = $form->get_standard_dbh($myconfig);
1415
1416   my $query;
1417   my @values = ($form->{description}, conv_i($form->{factor}));
1418
1419   if ($form->{id}) {
1420     $query = qq|UPDATE price_factors SET description = ?, factor = ? WHERE id = ?|;
1421     push @values, conv_i($form->{id});
1422
1423   } else {
1424     $query = qq|INSERT INTO price_factors (description, factor, sortkey) VALUES (?, ?, (SELECT COALESCE(MAX(sortkey), 0) + 1 FROM price_factors))|;
1425   }
1426
1427   do_query($form, $dbh, $query, @values);
1428
1429   $dbh->commit();
1430
1431   $main::lxdebug->leave_sub();
1432 }
1433
1434 sub get_all_price_factors {
1435   $main::lxdebug->enter_sub();
1436
1437   my ($self, $myconfig, $form) = @_;
1438
1439   # connect to database
1440   my $dbh = $form->get_standard_dbh($myconfig);
1441
1442   $form->{PRICE_FACTORS} = selectall_hashref_query($form, $dbh, qq|SELECT * FROM price_factors ORDER BY sortkey|);
1443
1444   $main::lxdebug->leave_sub();
1445 }
1446
1447 sub get_price_factor {
1448   $main::lxdebug->enter_sub();
1449
1450   my ($self, $myconfig, $form) = @_;
1451
1452   # connect to database
1453   my $dbh = $form->get_standard_dbh($myconfig);
1454
1455   my $query = qq|SELECT description, factor,
1456                    ((SELECT COUNT(*) FROM parts      WHERE price_factor_id = ?) +
1457                     (SELECT COUNT(*) FROM invoice    WHERE price_factor_id = ?) +
1458                     (SELECT COUNT(*) FROM orderitems WHERE price_factor_id = ?)) = 0 AS orphaned
1459                  FROM price_factors WHERE id = ?|;
1460
1461   ($form->{description}, $form->{factor}, $form->{orphaned}) = selectrow_query($form, $dbh, $query, (conv_i($form->{id})) x 4);
1462
1463   $main::lxdebug->leave_sub();
1464 }
1465
1466 sub delete_price_factor {
1467   $main::lxdebug->enter_sub();
1468
1469   my ($self, $myconfig, $form) = @_;
1470
1471   # connect to database
1472   my $dbh = $form->get_standard_dbh($myconfig);
1473
1474   do_query($form, $dbh, qq|DELETE FROM price_factors WHERE id = ?|, conv_i($form->{id}));
1475   $dbh->commit();
1476
1477   $main::lxdebug->leave_sub();
1478 }
1479
1480 sub save_warehouse {
1481   $main::lxdebug->enter_sub();
1482
1483   my ($self, $myconfig, $form) = @_;
1484
1485   # connect to database
1486   my $dbh = $form->get_standard_dbh($myconfig);
1487
1488   my ($query, @values, $sth);
1489
1490   if (!$form->{id}) {
1491     $query        = qq|SELECT nextval('id')|;
1492     ($form->{id}) = selectrow_query($form, $dbh, $query);
1493
1494     $query        = qq|INSERT INTO warehouse (id, sortkey) VALUES (?, (SELECT COALESCE(MAX(sortkey), 0) + 1 FROM warehouse))|;
1495     do_query($form, $dbh, $query, $form->{id});
1496   }
1497
1498   do_query($form, $dbh, qq|UPDATE warehouse SET description = ?, invalid = ? WHERE id = ?|,
1499            $form->{description}, $form->{invalid} ? 't' : 'f', conv_i($form->{id}));
1500
1501   if (0 < $form->{number_of_new_bins}) {
1502     my ($num_existing_bins) = selectfirst_array_query($form, $dbh, qq|SELECT COUNT(*) FROM bin WHERE warehouse_id = ?|, $form->{id});
1503     $query = qq|INSERT INTO bin (warehouse_id, description) VALUES (?, ?)|;
1504     $sth   = prepare_query($form, $dbh, $query);
1505
1506     foreach my $i (1..$form->{number_of_new_bins}) {
1507       do_statement($form, $sth, $query, conv_i($form->{id}), "$form->{prefix}" . ($i + $num_existing_bins));
1508     }
1509
1510     $sth->finish();
1511   }
1512
1513   $dbh->commit();
1514
1515   $main::lxdebug->leave_sub();
1516 }
1517
1518 sub save_bins {
1519   $main::lxdebug->enter_sub();
1520
1521   my ($self, $myconfig, $form) = @_;
1522
1523   # connect to database
1524   my $dbh = $form->get_standard_dbh($myconfig);
1525
1526   my ($query, @values, $commit_necessary, $sth);
1527
1528   @values = map { $form->{"id_${_}"} } grep { $form->{"delete_${_}"} } (1..$form->{rowcount});
1529
1530   if (@values) {
1531     $query = qq|DELETE FROM bin WHERE id IN (| . join(', ', ('?') x scalar(@values)) . qq|)|;
1532     do_query($form, $dbh, $query, @values);
1533
1534     $commit_necessary = 1;
1535   }
1536
1537   $query = qq|UPDATE bin SET description = ? WHERE id = ?|;
1538   $sth   = prepare_query($form, $dbh, $query);
1539
1540   foreach my $row (1..$form->{rowcount}) {
1541     next if ($form->{"delete_${row}"});
1542
1543     do_statement($form, $sth, $query, $form->{"description_${row}"}, conv_i($form->{"id_${row}"}));
1544
1545     $commit_necessary = 1;
1546   }
1547
1548   $sth->finish();
1549
1550   $dbh->commit() if ($commit_necessary);
1551
1552   $main::lxdebug->leave_sub();
1553 }
1554
1555 sub delete_warehouse {
1556   $main::lxdebug->enter_sub();
1557
1558   my ($self, $myconfig, $form) = @_;
1559
1560   # connect to database
1561   my $dbh = $form->get_standard_dbh($myconfig);
1562
1563   my $id      = conv_i($form->{id});
1564   my $query   = qq|SELECT i.bin_id FROM inventory i WHERE i.bin_id IN (SELECT b.id FROM bin b WHERE b.warehouse_id = ?) LIMIT 1|;
1565   my ($count) = selectrow_query($form, $dbh, $query, $id);
1566
1567   if ($count) {
1568     $main::lxdebug->leave_sub();
1569     return 0;
1570   }
1571
1572   do_query($form, $dbh, qq|DELETE FROM bin       WHERE warehouse_id = ?|, conv_i($form->{id}));
1573   do_query($form, $dbh, qq|DELETE FROM warehouse WHERE id           = ?|, conv_i($form->{id}));
1574
1575   $dbh->commit();
1576
1577   $main::lxdebug->leave_sub();
1578
1579   return 1;
1580 }
1581
1582 sub get_all_warehouses {
1583   $main::lxdebug->enter_sub();
1584
1585   my ($self, $myconfig, $form) = @_;
1586
1587   # connect to database
1588   my $dbh = $form->get_standard_dbh($myconfig);
1589
1590   my $query = qq|SELECT w.id, w.description, w.invalid,
1591                    (SELECT COUNT(b.description) FROM bin b WHERE b.warehouse_id = w.id) AS number_of_bins
1592                  FROM warehouse w
1593                  ORDER BY w.sortkey|;
1594
1595   $form->{WAREHOUSES} = selectall_hashref_query($form, $dbh, $query);
1596
1597   $main::lxdebug->leave_sub();
1598 }
1599
1600 sub get_warehouse {
1601   $main::lxdebug->enter_sub();
1602
1603   my ($self, $myconfig, $form) = @_;
1604
1605   # connect to database
1606   my $dbh = $form->get_standard_dbh($myconfig);
1607
1608   my $id    = conv_i($form->{id});
1609   my $query = qq|SELECT w.description, w.invalid
1610                  FROM warehouse w
1611                  WHERE w.id = ?|;
1612
1613   my $ref   = selectfirst_hashref_query($form, $dbh, $query, $id);
1614
1615   map { $form->{$_} = $ref->{$_} } keys %{ $ref };
1616
1617   $query = <<SQL;
1618     SELECT b.*,
1619       (   EXISTS(SELECT i.bin_id FROM inventory i WHERE i.bin_id = b.id LIMIT 1)
1620        OR EXISTS(SELECT p.bin_id FROM parts     p WHERE p.bin_id = b.id LIMIT 1))
1621       AS in_use
1622     FROM bin b
1623     WHERE b.warehouse_id = ?
1624 SQL
1625
1626   $form->{BINS} = selectall_hashref_query($form, $dbh, $query, conv_i($form->{id}));
1627
1628   $main::lxdebug->leave_sub();
1629 }
1630
1631 1;