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