X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;ds=sidebyside;f=bin%2Fmozilla%2Fgl.pl;h=faca8ce0d256b84c0fa535427d870a6a64e3dc31;hb=2a0cbd885790174fa0f212e6661b30362650a42c;hp=f88a0eb7a11da5d4625208020d2f9b8b1c22ca14;hpb=4dbb09950c9f5596646537c12d991c99086fe7c1;p=kivitendo-erp.git diff --git a/bin/mozilla/gl.pl b/bin/mozilla/gl.pl index f88a0eb7a..faca8ce0d 100644 --- a/bin/mozilla/gl.pl +++ b/bin/mozilla/gl.pl @@ -24,24 +24,38 @@ # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1335, USA. #====================================================================== # # Genereal Ledger # #====================================================================== +use utf8; +use strict; -use SL::GL; -use SL::PE; - -use Data::Dumper; - -require "$form->{path}/arap.pl"; - -1; -# end of main +use POSIX qw(strftime); +use List::Util qw(first sum); +use SL::DB::ApGl; +use SL::DB::RecordTemplate; +use SL::DB::ReconciliationLink; +use SL::DB::BankTransactionAccTrans; +use SL::DB::Tax; +use SL::FU; +use SL::GL; +use SL::Helper::Flash qw(flash flash_later); +use SL::IS; +use SL::ReportGenerator; +use SL::DBUtils qw(selectrow_query selectall_hashref_query); +use SL::Webdav; +use SL::Locale::String qw(t8); +use SL::Helper::GlAttachments qw(count_gl_attachments); +use SL::Presenter::Tag; +use SL::Presenter::Chart; +require "bin/mozilla/common.pl"; +require "bin/mozilla/reportgenerator.pl"; # this is for our long dates # $locale->text('January') @@ -71,1554 +85,1467 @@ require "$form->{path}/arap.pl"; # $locale->text('Nov') # $locale->text('Dec') +sub load_record_template { + $::auth->assert('gl_transactions'); + + # Load existing template and verify that its one for this module. + my $template = SL::DB::RecordTemplate + ->new(id => $::form->{id}) + ->load( + with_object => [ qw(customer payment currency record_items record_items.chart) ], + ); + + die "invalid template type" unless $template->template_type eq 'gl_transaction'; + + $template->substitute_variables; + my $payment_suggestion = $::form->{form_defaults}->{amount_1}; + + # Clean the current $::form before rebuilding it from the template. + my $form_defaults = delete $::form->{form_defaults}; + delete @{ $::form }{ grep { !m{^(?:script|login)$}i } keys %{ $::form } }; + + my $dummy_form = {}; + GL->transaction(\%::myconfig, $dummy_form); + + # Fill $::form from the template. + my $today = DateTime->today_local; + $::form->{title} = "Add"; + $::form->{transdate} = $today->to_kivitendo; + $::form->{duedate} = $today->to_kivitendo; + $::form->{rowcount} = @{ $template->items }; + $::form->{paidaccounts} = 1; + $::form->{$_} = $template->$_ for qw(department_id taxincluded ob_transaction cb_transaction reference description show_details transaction_description); + $::form->{$_} = $dummy_form->{$_} for qw(closedto revtrans previous_id previous_gldate); + + my $row = 0; + foreach my $item (@{ $template->items }) { + $row++; + + my $active_taxkey = $item->chart->get_active_taxkey; + my $taxes = SL::DB::Manager::Tax->get_all( + where => [ chart_categories => { like => '%' . $item->chart->category . '%' }], + sort_by => 'taxkey, rate', + ); + + my $tax = first { $item->tax_id == $_->id } @{ $taxes }; + $tax //= first { $active_taxkey->tax_id == $_->id } @{ $taxes }; + $tax //= $taxes->[0]; + + if (!$tax) { + $row--; + next; + } + + $::form->{"accno_id_${row}"} = $item->chart_id; + $::form->{"previous_accno_id_${row}"} = $item->chart_id; + $::form->{"debit_${row}"} = $::form->format_amount(\%::myconfig, ($payment_suggestion ? $payment_suggestion : $item->amount1), 2) if $item->amount1 * 1; + $::form->{"credit_${row}"} = $::form->format_amount(\%::myconfig, ($payment_suggestion ? $payment_suggestion : $item->amount2), 2) if $item->amount2 * 1; + $::form->{"taxchart_${row}"} = $item->tax_id . '--' . $tax->rate; + $::form->{"${_}_${row}"} = $item->$_ for qw(source memo project_id); + } + + $::form->{$_} = $form_defaults->{$_} for keys %{ $form_defaults // {} }; + + flash('info', $::locale->text("The record template '#1' has been loaded.", $template->template_name)); + + update( + keep_rows_without_amount => 1, + dont_add_new_row => 1, + ); +} + +sub save_record_template { + $::auth->assert('gl_transactions'); + + my $template = $::form->{record_template_id} ? SL::DB::RecordTemplate->new(id => $::form->{record_template_id})->load : SL::DB::RecordTemplate->new; + my $js = SL::ClientJS->new(controller => SL::Controller::Base->new); + my $new_name = $template->template_name_to_use($::form->{record_template_new_template_name}); + $js->dialog->close('#record_template_dialog'); + + + # bank transactions need amounts for assignment + my $can_save = 0; + $can_save = 1 if ($::form->{credit_1} > 0 && $::form->{debit_2} > 0 && $::form->{credit_2} == 0 && $::form->{debit_1} == 0); + $can_save = 1 if ($::form->{credit_2} > 0 && $::form->{debit_1} > 0 && $::form->{credit_1} == 0 && $::form->{debit_2} == 0); + return $js->flash('error', t8('Can only save template if amounts,i.e. 1 for debit and credit are set.'))->render unless $can_save; + + my @items = grep { + $_->{chart_id} && (($_->{tax_id} // '') ne '') + } map { + +{ chart_id => $::form->{"accno_id_${_}"}, + amount1 => $::form->parse_amount(\%::myconfig, $::form->{"debit_${_}"}), + amount2 => $::form->parse_amount(\%::myconfig, $::form->{"credit_${_}"}), + tax_id => (split m{--}, $::form->{"taxchart_${_}"})[0], + project_id => $::form->{"project_id_${_}"} || undef, + source => $::form->{"source_${_}"}, + memo => $::form->{"memo_${_}"}, + } + } (1..($::form->{rowcount} || 1)); + + $template->assign_attributes( + template_type => 'gl_transaction', + template_name => $new_name, + + currency_id => $::instance_conf->get_currency_id, + department_id => $::form->{department_id} || undef, + project_id => $::form->{globalproject_id} || undef, + taxincluded => $::form->{taxincluded} ? 1 : 0, + ob_transaction => $::form->{ob_transaction} ? 1 : 0, + cb_transaction => $::form->{cb_transaction} ? 1 : 0, + reference => $::form->{reference}, + description => $::form->{description}, + show_details => $::form->{show_details}, + transaction_description => $::form->{transaction_description}, + + items => \@items, + ); + + eval { + $template->save; + 1; + } or do { + return $js + ->flash('error', $::locale->text("Saving the record template '#1' failed.", $new_name)) + ->render; + }; + + return $js + ->flash('info', $::locale->text("The record template '#1' has been saved.", $new_name)) + ->render; +} sub add { - $lxdebug->enter_sub(); + $main::lxdebug->enter_sub(); + + $main::auth->assert('gl_transactions'); + + my $form = $main::form; + my %myconfig = %main::myconfig; $form->{title} = "Add"; - - $form->{callback} = "$form->{script}?action=add&path=$form->{path}&login=$form->{login}&password=$form->{password}" unless $form->{callback}; + + $form->{callback} = "gl.pl?action=add" unless $form->{callback}; # we use this only to set a default date + # yep. aber er holt hier auch schon ALL_CHARTS. Aufwand / Nutzen? jb GL->transaction(\%myconfig, \%$form); - map { $chart .= "" } @{ $form->{chart} }; - map { $tax .= qq|" } @{ $form->{chart} }; - $form->{chart} = $chart; - map { $tax .= qq|"; - $form->{debitchart} = ""; - } else { - $form->{creditchart} = $form->{chart}; - $form->{creditchart} =~ s/value=\"$form->{creditchartselected}\"/value=\"$form->{creditchartselected}\" selected/; - - $form->{debitchart} = $form->{chart}; - $form->{debitchart} =~ s/value=\"$form->{debitchartselected}\"/value=\"$form->{debitchartselected}\" selected/; + my @flds = + qw(accno_id debit credit projectnumber fx_transaction source memo tax taxchart); + + for my $i (1 .. $form->{rowcount}) { + $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"}) for qw(debit credit tax); + + next if !$form->{"debit_$i"} && !$form->{"credit_$i"} && !$params{keep_rows_without_amount}; + + push @a, {}; + $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1"; + if ($debitcredit) { + $debitcount++; + } else { + $creditcount++; + } + + if (($debitcount >= 2) && ($creditcount == 2)) { + $form->{"credit_$i"} = 0; + $form->{"tax_$i"} = 0; + $creditcount--; + $form->{creditlock} = 1; + } + if (($creditcount >= 2) && ($debitcount == 2)) { + $form->{"debit_$i"} = 0; + $form->{"tax_$i"} = 0; + $debitcount--; + $form->{debitlock} = 1; + } + if (($creditcount == 1) && ($debitcount == 2)) { + $form->{creditlock} = 1; + } + if (($creditcount == 2) && ($debitcount == 1)) { + $form->{debitlock} = 1; + } + if ($debitcredit && $credittax) { + $form->{"taxchart_$i"} = "$notax_id--0.00000"; + } + if (!$debitcredit && $debittax) { + $form->{"taxchart_$i"} = "$notax_id--0.00000"; + } + $amount = + ($form->{"debit_$i"} == 0) + ? $form->{"credit_$i"} + : $form->{"debit_$i"}; + my $j = $#a; + if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) { + $form->{"taxchart_$i"} = "$notax_id--0.00000"; + $form->{"tax_$i"} = 0; + } + my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"}); + my $iswithouttax = grep { $_->{id} == $taxkey } @{ $zerotaxes }; + if (!$iswithouttax) { + if ($debitcredit) { + $debittax = 1; + } else { + $credittax = 1; + } + }; + my ($tmpnetamount,$tmpdiff); + ($tmpnetamount,$form->{"tax_$i"},$tmpdiff) = $form->calculate_tax($amount,$rate,$form->{taxincluded} *= 1,2); + + for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} } + $count++; } - ($debitaccno, $debittaxkey) = split(/--/, $form->{debitchartselected}); - ($creditaccno, $credittaxkey) = split(/--/, $form->{creditchartselected}); - if ($debittaxkey >0) { - $form->{taxchart} = $form->unescape($form->{taxchart}); - $form->{taxchart} =~ s/selected//ig; - $form->{taxchart} =~ s/\"$debittaxkey--([^\"]*)\"/\"$debittaxkey--$1\" selected/; - - $rate = $1; - - if ($form->{taxincluded}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1); - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1) * $rate; - } else { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * ($rate + 1); - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) * $rate; - } - } else { - $form->{taxchart} = $form->unescape($form->{taxchart}); - $form->{taxchart} =~ s/selected//ig; - $form->{taxchart} =~ s/\"$credittaxkey--([^\"]*)\"/\"$credittaxkey--$1\" selected/; - $rate = $1; - - if ($form->{taxincluded}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1); - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1) * $rate; - } else { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * ($rate + 1); - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) * $rate; - } - } - if ($form->{credit_splited}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{credit} = $credit_save; - $form->{tax} = 0; - } elsif ($form->{debit_splited}) { - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{debit} = $debit_save; - $form->{tax} = 0; + + for my $i (1 .. $count) { + my $j = $i - 1; + for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} } } - &check_project; - &display_form; - - $lxdebug->leave_sub(); -} + for my $i ($count + 1 .. $form->{rowcount}) { + for (@flds) { delete $form->{"${_}_$i"} } + } + + $form->{rowcount} = $count + ($params{dont_add_new_row} ? 0 : 1); + display_form(); + $main::lxdebug->leave_sub(); + +} sub display_form { - $lxdebug->enter_sub(); + my ($init) = @_; + $main::lxdebug->enter_sub(); + + $main::auth->assert('gl_transactions'); + my $form = $main::form; + my %myconfig = %main::myconfig; - &form_header; -# for $i (1 .. $form->{rowcount}) { -# $form->{totaldebit} += $form->parse_amount(\%myconfig, $form->{"debit_$i"}); -# $form->{totalcredit} += $form->parse_amount(\%myconfig, $form->{"credit_$i"}); -# -# &form_row($i); -# } + &form_header($init); + # for $i (1 .. $form->{rowcount}) { + # $form->{totaldebit} += $form->parse_amount(\%myconfig, $form->{"debit_$i"}); + # $form->{totalcredit} += $form->parse_amount(\%myconfig, $form->{"credit_$i"}); + # + # &form_row($i); + # } + &display_rows($init); &form_footer; + $main::lxdebug->leave_sub(); - $lxdebug->leave_sub(); } +sub display_rows { + my ($init) = @_; + $main::lxdebug->enter_sub(); + $main::auth->assert('gl_transactions'); + my $form = $main::form; + my %myconfig = %main::myconfig; + my $cgi = $::request->{cgi}; -sub form_header { - $lxdebug->enter_sub(); - - $title = $form->{title}; - $form->{title} = $locale->text("$title General Ledger Transaction"); - $readonly = ($form->{id}) ? "readonly" : ""; - - $form->{urldebit} = "$form->{script}?action=split_debit&path=$form->{path}&login=$form->{login}&password=$form->{password}"; - $form->{urlcredit} = "$form->{script}?action=split_credit&path=$form->{path}&login=$form->{login}&password=$form->{password}"; -# $locale->text('Add General Ledger Transaction') -# $locale->text('Edit General Ledger Transaction') - map { $form->{$_} =~ s/\"/"/g } qw(reference description chart); - - $form->{selectdepartment} =~ s/ selected//; - $form->{taxchart} =~ s/ selected//; - $form->{selectdepartment} =~ s/option>\Q$form->{department}\E/option selected>$form->{department}/; - - if (($rows = $form->numtextrows($form->{description}, 50)) > 1) { - $description = qq||; - } else { - $description = qq||; - } - - $taxincluded = ($form->{taxincluded}) ? "checked" : ""; - - if (!$form->{id}) { - $taxincluded = "checked"; - } - - $amount = qq||; - - - $department = qq| - - |.$locale->text('Department').qq| - - - -| if $form->{selectdepartment}; - - $form->{fokus} = "gl.reference"; - $form->{remote} = 1; - - # use JavaScript Calendar or not - $form->{jsscript} = $jscalendar; - $jsscript = ""; - if ($form->{jsscript}) - { - # with JavaScript Calendar - $button1 = qq| - {transdate} tabindex="2" $readonly> - text('button').qq|> - |; - #write Trigger - $jsscript = Form->write_trigger(\%myconfig,"1","transdate","BL","trigger1","","",""); - } - else - { - # without JavaScript Calendar - $button1 = qq|{transdate} tabindex="2" $readonly>|; + my %balances = GL->get_chart_balances(map { $_->{id} } @{ $form->{ALL_CHARTS} }); + + $form->{debit_1} = 0 if !$form->{"debit_1"}; + $form->{totaldebit} = 0; + $form->{totalcredit} = 0; + + my %charts_by_id = map { ($_->{id} => $_) } @{ $::form->{ALL_CHARTS} }; + my $default_chart = $::form->{ALL_CHARTS}[0]; + my $transdate = $::form->{transdate} ? DateTime->from_kivitendo($::form->{transdate}) : DateTime->today_local; + my $deliverydate = $::form->{deliverydate} ? DateTime->from_kivitendo($::form->{deliverydate}) : undef; + + my ($source, $memo, $source_hidden, $memo_hidden); + for my $i (1 .. $form->{rowcount}) { + if ($form->{show_details}) { + $source = qq| + |; + $memo = qq| + |; + } else { + $source_hidden = qq| + |; + $memo_hidden = qq| + |; } - - $form->header; - - print qq| - + my %taxchart_labels = (); + my @taxchart_values = (); -
{script}> + my $accno_id = $::form->{"accno_id_$i"}; + my $chart = $charts_by_id{$accno_id} // $default_chart; + $accno_id = $chart->{id}; + my ($first_taxchart, $default_taxchart, $taxchart_to_use); + + my $used_tax_id; + if ( $form->{"taxchart_$i"} ) { + ($used_tax_id) = split(/--/, $form->{"taxchart_$i"}); + } -{id}> + my $taxdate = $deliverydate ? $deliverydate : $transdate; + foreach my $item ( GL->get_active_taxes_for_chart($accno_id, $taxdate, $used_tax_id) ) { + my $key = $item->id . "--" . $item->rate; + $first_taxchart //= $item; + $default_taxchart = $item if $item->{is_default}; + $taxchart_to_use = $item if $key eq $form->{"taxchart_$i"}; -{closedto}> -{locked}> - -escape($form->{taxchart}).qq|> - - - + push(@taxchart_values, $key); + $taxchart_labels{$key} = $item->taxkey . " - " . $item->taxdescription . " " . $item->rate * 100 . ' %'; + } + $taxchart_to_use //= $default_taxchart // $first_taxchart; + my $selected_taxchart = $taxchart_to_use->id . '--' . $taxchart_to_use->rate; + + my $accno = qq|| . + SL::Presenter::Chart::picker("accno_id_$i", $accno_id, style => "width: 300px") . + SL::Presenter::Tag::hidden_tag("previous_accno_id_$i", $accno_id) + . qq||; + my $tax_ddbox = qq|| . + NTI($cgi->popup_menu('-name' => "taxchart_$i", + '-id' => "taxchart_$i", + '-style' => 'width:200px', + '-values' => \@taxchart_values, + '-labels' => \%taxchart_labels, + '-default' => $selected_taxchart)) + . qq||; + + my ($fx_transaction, $checked); + if ($init) { + if ($form->{transfer}) { + $fx_transaction = qq| + + |; + } - - - - - - - - - - - -$jsscript -|; + } else { + $form->{totalcredit} += $form->{"credit_$i"}; + if (!$form->{taxincluded}) { + $form->{totalcredit} += $form->{"tax_$i"}; + } + } - $lxdebug->leave_sub(); -} + for (qw(debit credit tax)) { + $form->{"${_}_$i"} = + ($form->{"${_}_$i"}) + ? $form->format_amount(\%myconfig, $form->{"${_}_$i"}, 2) + : ""; + } + if ($i < $form->{rowcount}) { + if ($form->{transfer}) { + $checked = ($form->{"fx_transaction_$i"}) ? "1" : ""; + my $x = ($checked) ? "x" : ""; + $fx_transaction = qq| + + |; + } + $form->hide_form("accno_$i"); -sub form_footer { - $lxdebug->enter_sub(); - ($dec) = ($form->{totaldebit} =~ /\.(\d+)/); - $dec = length $dec; - $decimalplaces = ($dec > 2) ? $dec : 2; - $taxrowcount = ($form->{creditrowcount} > $form->{debitrowcount}) ? $form->{creditrowcount} : $form->{debitrowcount}; - $radieren = ($form->current_date(\%myconfig) eq $form->{gldate})? 1 : 0; - - map { $form->{$_} = $form->format_amount(\%myconfig, $form->{$_}, $decimalplaces, " ") } qw(totaldebit totalcredit); - - print qq| -
$form->{title}
- - - - - - |; -if ($form->{id}) { - print qq| - - - - - |; - } - print qq| - $department|; -if ($form->{id}) { - print qq| - - - - - |; - } else { - print qq| - - - - |; - } - print qq| - - - - - |; - if ($form->{debit_splited}) { - print qq| - - - - - - {debit_splited}> - - - - - - {credit_splited}> - - |; - } elsif ($form->{credit_splited}) { - print qq| - - - - - {debit_splited}> - - - - - - - {credit_splited}> - |; - } else { - print qq| - - - - - - {debit_splited}> - - - - - - - {credit_splited}> - - - - - - - |; + } else { + if ($form->{"debit_$i"} != 0) { + $form->{totaldebit} += $form->{"debit_$i"}; + if (!$form->{taxincluded}) { + $form->{totaldebit} += $form->{"tax_$i"}; } - print qq| -
|.$locale->text('Reference').qq| - - - - $button1 - -
|.$locale->text('Date').qq|
-
|.$locale->text('Belegnummer').qq| - - - - - -
|.$locale->text('Buchungsdatum').qq|{gldate} $readonly>
-
|.$locale->text('Description').qq|$description - - - - - -
|.$locale->text('Mitarbeiter').qq|{employee} $readonly>
-
|.$locale->text('Description').qq|$description
|.$locale->text('Betrag').qq|$amount - - - - - -
|.$locale->text('MwSt. inkl.').qq|
-
|.$locale->text('Debit').qq|Betrag gesplittet EUR
|.$locale->text('Credit').qq| EUR
|.$locale->text('Debit').qq| EUR
|.$locale->text('Credit').qq|Betrag gesplittet EUR
|.$locale->text('Debit').qq| EUR
|.$locale->text('Credit').qq| EUR
|.$locale->text('Tax').qq| EUR
-

$x
-|; - for $i (2 .. $form->{creditrowcount}) { - print qq| - - + } else { + if ($form->{transfer}) { + $fx_transaction = qq| + |; - } - for $i (2 .. $form->{debitrowcount}) { + } + } + } + my $debitreadonly = ""; + my $creditreadonly = ""; + if ($i == $form->{rowcount}) { + if ($form->{debitlock}) { + $debitreadonly = "readonly"; + } elsif ($form->{creditlock}) { + $creditreadonly = "readonly"; + } + } + + my $projectnumber = SL::Presenter::Project::picker("project_id_$i", $form->{"project_id_$i"}); + my $projectnumber_hidden = SL::Presenter::Tag::hidden_tag("project_id_$i", $form->{"project_id_$i"}); + + my $copy2credit = $i == 1 ? 'onkeyup="copy_debit_to_credit()"' : ''; + my $balance = $form->format_amount(\%::myconfig, $balances{$accno_id} // 0, 2, 'DRCR'); + + # if we have a bt_chart_id we disallow changing the amount of the bank account + if ($form->{bt_chart_id}) { + $debitreadonly = $creditreadonly = "readonly" if ($form->{"accno_id_$i"} eq $form->{bt_chart_id}); + $copy2credit = '' if $i == 1; # and disallow copy2credit + } + + print qq| + $accno + ${balance} + $fx_transaction + + + $form->{"tax_$i"} + $tax_ddbox|; + + if ($form->{show_details}) { + print qq| + $source + $memo + $projectnumber +|; + } else { print qq| - - + $source_hidden + $memo_hidden + $projectnumber_hidden |; - } - if ($taxrowcount > 1) { - for $i (2 .. $taxrowcount) { - print qq| - - - |; } - } -print qq| -{path}> -{login}> -{password}> - - -
+ print qq| + |; + } - $transdate = $form->datetonum($form->{transdate}, \%myconfig); - $closedto = $form->datetonum($form->{closedto}, \%myconfig); + $form->hide_form(qw(rowcount selectaccno)); + $main::lxdebug->leave_sub(); + +} + +sub _get_radieren { + return ($::instance_conf->get_gl_changeable == 2) ? ($::form->current_date(\%::myconfig) eq $::form->{gldate}) : ($::instance_conf->get_gl_changeable == 1); +} + +sub setup_gl_action_bar { + my %params = @_; + my $form = $::form; + my $change_never = $::instance_conf->get_gl_changeable == 0; + my $change_on_same_day_only = $::instance_conf->get_gl_changeable == 2 && ($form->current_date(\%::myconfig) ne $form->{gldate}); + my ($is_linked_bank_transaction, $is_linked_ap_transaction, $is_reconciled_bank_transaction); + + if ($form->{id} && SL::DB::Manager::BankTransactionAccTrans->find_by(gl_id => $form->{id})) { + $is_linked_bank_transaction = 1; + } + if ($form->{id} && SL::DB::Manager::ApGl->find_by(gl_id => $form->{id})) { + $is_linked_ap_transaction = 1; + } + # dont edit reconcilated bookings! if ($form->{id}) { - - print qq||; - -# Löschen und ändern von Buchungen nicht mehr möglich (GoB) nur am selben Tag möglich - - - - if (!$form->{locked} && $radieren) { - print qq| - - |; - } - - -# if ($transdate > $closedto) { -# print qq| -# |; -# } - } else { - if ($transdate > $closedto) { - print qq| - |; - } - } - - if ($form->{menubar}) { - require "$form->{path}/menu.pl"; - &menubar; + my @acc_trans = map { $_->acc_trans_id } @{ SL::DB::Manager::AccTransaction->get_all( where => [ trans_id => $form->{id} ] ) }; + if (scalar @acc_trans && scalar @{ SL::DB::Manager::ReconciliationLink->get_all(where => [ acc_trans_id => [ @acc_trans ] ]) }) { + $is_reconciled_bank_transaction = 1; + } + } + my $create_post_action = sub { + # $_[0]: description + # $_[1]: after_action + action => [ + $_[0], + submit => [ '#form', { action => 'post', after_action => $_[1] } ], + disabled => $form->{locked} ? t8('The billing period has already been locked.') + : $form->{storno} ? t8('A canceled general ledger transaction cannot be posted.') + : ($form->{id} && $change_never) ? t8('Changing general ledger transaction has been disabled in the configuration.') + : ($form->{id} && $change_on_same_day_only) ? t8('General ledger transactions can only be changed on the day they are posted.') + : $is_linked_bank_transaction ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.') + : $is_linked_ap_transaction ? t8('This transaction is linked with a AP transaction. Please undo and redo the AP transaction booking if needed.') + : $is_reconciled_bank_transaction ? t8('This transaction is reconciled with a bank transaction. Please undo the reconciliation if needed.') + : undef, + ], + }; + + my %post_entry; + if ($::instance_conf->get_gl_add_doc && $::instance_conf->get_doc_storage) { + %post_entry = (combobox => [ $create_post_action->(t8('Post'), 'doc-tab'), + $create_post_action->(t8('Post and new booking')) ]); + } elsif ($::instance_conf->get_doc_storage) { + %post_entry = (combobox => [ $create_post_action->(t8('Post')), + $create_post_action->(t8('Post and upload document'), 'doc-tab') ]); + } else { + %post_entry = $create_post_action->(t8('Post')); + } + + for my $bar ($::request->layout->get('actionbar')) { + $bar->add( + action => [ + t8('Update'), + submit => [ '#form', { action => 'update' } ], + id => 'update_button', + accesskey => 'enter', + ], + %post_entry, + combobox => [ + action => [ t8('Storno'), + submit => [ '#form', { action => 'storno' } ], + confirm => t8('Do you really want to cancel this general ledger transaction?'), + disabled => !$form->{id} ? t8('This general ledger transaction has not been posted yet.') + : $form->{storno} ? t8('A canceled general ledger transaction cannot be canceled again.') + : $is_linked_bank_transaction ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.') + : $is_linked_ap_transaction ? t8('This transaction is linked with a AP transaction. Please undo and redo the AP transaction booking if needed.') + : $is_reconciled_bank_transaction ? t8('This transaction is reconciled with a bank transaction. Please undo the reconciliation if needed.') + : undef, + ], + action => [ t8('Delete'), + submit => [ '#form', { action => 'delete' } ], + confirm => t8('Do you really want to delete this object?'), + disabled => !$form->{id} ? t8('This invoice has not been posted yet.') + : $form->{locked} ? t8('The billing period has already been locked.') + : $change_never ? t8('Changing invoices has been disabled in the configuration.') + : $change_on_same_day_only ? t8('Invoices can only be changed on the day they are posted.') + : $is_linked_bank_transaction ? t8('This transaction is linked with a bank transaction. Please undo and redo the bank transaction booking if needed.') + : $is_linked_ap_transaction ? t8('This transaction is linked with a AP transaction. Please undo and redo the AP transaction booking if needed.') + : $is_reconciled_bank_transaction ? t8('This transaction is reconciled with a bank transaction. Please undo the reconciliation if needed.') + : $form->{storno} ? t8('A canceled general ledger transaction cannot be deleted.') + : undef, + ], + ], # end of combobox "Storno" + + combobox => [ + action => [ t8('more') ], + action => [ + t8('History'), + call => [ 'set_history_window', $form->{id} * 1, 'glid' ], + disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef, + ], + action => [ + t8('Follow-Up'), + call => [ 'follow_up_window' ], + disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef, + ], + action => [ + t8('Record templates'), + call => [ 'kivi.RecordTemplate.popup', 'gl_transaction' ], + ], + action => [ + t8('Drafts'), + call => [ 'kivi.Draft.popup', 'gl', 'unknown', $form->{draft_id}, $form->{draft_description} ], + disabled => $form->{id} ? t8('This invoice has already been posted.') + : $form->{locked} ? t8('The billing period has already been locked.') + : undef, + ], + ], # end of combobox "more" + ); } - - print " -
- - - -"; - - $lxdebug->leave_sub(); } +sub setup_gl_search_action_bar { + my %params = @_; + + for my $bar ($::request->layout->get('actionbar')) { + $bar->add( + action => [ + t8('Search'), + submit => [ '#form', { action => 'continue', nextsub => 'generate_report' } ], + accesskey => 'enter', + ], + ); + } +} -sub delete { - $lxdebug->enter_sub(); +sub setup_gl_transactions_action_bar { + my %params = @_; + + for my $bar ($::request->layout->get('actionbar')) { + $bar->add( + combobox => [ + action => [ $::locale->text('Create new') ], + action => [ + $::locale->text('GL Transaction'), + submit => [ '#create_new_form', { action => 'gl_transaction' } ], + ], + action => [ + $::locale->text('AR Transaction'), + submit => [ '#create_new_form', { action => 'ar_transaction' } ], + ], + action => [ + $::locale->text('AP Transaction'), + submit => [ '#create_new_form', { action => 'ap_transaction' } ], + ], + action => [ + $::locale->text('Sales Invoice'), + submit => [ '#create_new_form', { action => 'sales_invoice' } ], + ], + action => [ + $::locale->text('Vendor Invoice'), + submit => [ '#create_new_form', { action => 'vendor_invoice' } ], + ], + ], # end of combobox "Create new" + ); + } +} - $form->header; +sub form_header { + $::lxdebug->enter_sub; + $::auth->assert('gl_transactions'); - print qq| - + my ($init) = @_; -
{script}> -|; + $::request->layout->add_javascripts("autocomplete_chart.js", "autocomplete_project.js", "kivi.File.js", "kivi.GL.js", "kivi.RecordTemplate.js", "kivi.Validator.js", "show_history.js"); + + my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount}; + my @conditions = @old_project_ids ? (id => \@old_project_ids) : (); + $::form->{ALL_PROJECTS} = SL::DB::Manager::Project->get_all_sorted(query => [ or => [ active => 1, @conditions ]]); + + $::form->get_lists( + "charts" => { "key" => "ALL_CHARTS", "transdate" => $::form->{transdate} }, + ); - map { $form->{$_} =~ s/\"/"/g } qw(reference description chart); + # we cannot book on charttype header + @{ $::form->{ALL_CHARTS} } = grep { $_->{charttype} ne 'H' } @{ $::form->{ALL_CHARTS} }; + $::form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted; - delete $form->{header}; + my $title = $::form->{title}; + $::form->{title} = $::locale->text("$title General Ledger Transaction"); + # $locale->text('Add General Ledger Transaction') + # $locale->text('Edit General Ledger Transaction') - foreach $key (keys %$form) { - print qq|\n|; + map { $::form->{$_} =~ s/\"/"/g } + qw(chart taxchart); + + if ($init) { + $::request->{layout}->focus("#reference"); + $::form->{taxincluded} = "1"; + } else { + $::request->{layout}->focus("#accno_id_$::form->{rowcount}_name"); } - print qq| -

|.$locale->text('Confirm!').qq|

+ $::form->{previous_id} ||= "--"; + $::form->{previous_gldate} ||= "--"; -

|.$locale->text('Are you sure you want to delete Transaction').qq| $form->{reference}

+ setup_gl_action_bar(); - -
-|; + $::form->header; + print $::form->parse_html_template('gl/form_header', { + hide_title => $title, + readonly => $::form->{id} && ($::form->{locked} || !_get_radieren()), + }); + + $::lxdebug->leave_sub; - $lxdebug->leave_sub(); } +sub form_footer { + $::lxdebug->enter_sub; + $::auth->assert('gl_transactions'); -sub yes { - $lxdebug->enter_sub(); + my ($follow_ups, $follow_ups_due); - $form->redirect($locale->text('Transaction deleted!')) if (GL->delete_transaction(\%myconfig, \%$form)); + if ($::form->{id}) { + $follow_ups = FU->follow_ups('trans_id' => $::form->{id}, 'not_done' => 1); + $follow_ups_due = sum map { $_->{due} * 1 } @{ $follow_ups || [] }; + } + + print $::form->parse_html_template('gl/form_footer', { + radieren => _get_radieren(), + follow_ups => $follow_ups, + follow_ups_due => $follow_ups_due, + }); + + $::lxdebug->leave_sub; +} + +sub delete { + $main::lxdebug->enter_sub(); + + my $form = $main::form; + my %myconfig = %main::myconfig; + my $locale = $main::locale; + + if (GL->delete_transaction(\%myconfig, \%$form)){ + # saving the history + if(!exists $form->{addition} && $form->{id} ne "") { + $form->{snumbers} = qq|gltransaction_| . $form->{id}; + $form->{addition} = "DELETED"; + $form->{what_done} = "gl_transaction"; + $form->save_history; + } + # /saving the history + $form->redirect($locale->text('Transaction deleted!')) + } $form->error($locale->text('Cannot delete transaction!')); - - $lxdebug->leave_sub(); + $main::lxdebug->leave_sub(); + } +sub post_transaction { + $main::lxdebug->enter_sub(); + + my $form = $main::form; + my %myconfig = %main::myconfig; + my $locale = $main::locale; -sub post { - $lxdebug->enter_sub(); # check if there is something in reference and date - $form->isblank("reference", $locale->text('Reference missing!')); - $form->isblank("transdate", $locale->text('Transaction Date missing!')); - $form->isblank("description", $locale->text('Description missing!')); - - $transdate = $form->datetonum($form->{transdate}, \%myconfig); - $closedto = $form->datetonum($form->{closedto}, \%myconfig); - - ($debitaccno, $debittaxkey) = split(/--/, $form->{debitchartselected}); - ($creditaccno, $credittaxkey) = split(/--/, $form->{creditchartselected}); - - $credit_save = $form->{credit}; - $debit_save = $form->{debit}; - - # check project - &check_project; - ($taxkey, $taxrate) = split(/--/, $form->{taxchartselected}); - - if ($debittaxkey >0) { - $form->{taxchart} = $form->unescape($form->{taxchart}); - $form->{taxchart} =~ s/\"$debittaxkey--([^\"]*)\"/\"$debittaxkey--$1\"/; - - $rate = ($form->{taxchart} =~ /selected/) ? $taxrate : $1; - $form->{taxkey} = ($form->{taxchart} =~ /selected/) ? $taxkey : $debittaxkey; - - if ($form->{storno}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{debit}); - $form->{credit} = $form->parse_amount(\%myconfig, $form->{credit}); - $form->{tax} = $form->parse_amount(\%myconfig, $form->{tax}); - } else { - if ($form->{taxincluded}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1); - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1) * $rate; - } else { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * ($rate + 1); - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) * $rate; - } - } - $form->{debittaxkey}=1; - - } else { - $form->{taxchart} = $form->unescape($form->{taxchart}); - $form->{taxchart} =~ s/\"$credittaxkey--([^\"]*)\"/\"$credittaxkey--$1\"/; - - - $rate = ($form->{taxchart} =~ /selected/) ? $taxrate : $1; - $form->{taxkey} = ($form->{taxchart} =~ /selected/) ? $taxkey : $credittaxkey; - - if ($form->{storno}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{debit}); - $form->{credit} = $form->parse_amount(\%myconfig, $form->{credit}); - $form->{tax} = $form->parse_amount(\%myconfig, $form->{tax}); - } else { - if ($form->{taxincluded}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1); - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) / ($rate + 1) * $rate; - } else { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * ($rate + 1); - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{tax} = $form->parse_amount(\%myconfig, $form->{amount}) * $rate; - } - } - $form->{debittaxkey}=0; - - } - if ($form->{credit_splited}) { - $form->{debit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{credit} = $credit_save; - $form->{tax} = 0; - } elsif ($form->{debit_splited}) { - $form->{credit} = $form->parse_amount(\%myconfig, $form->{amount}) * 1; - $form->{debit} = $debit_save; - $form->{tax} = 0; + $form->isblank("reference", $locale->text('Reference missing!')); + $form->isblank("transdate", $locale->text('Transaction Date missing!')); + $form->isblank("description", $locale->text('Description missing!')); + $form->isblank("transaction_description", $locale->text('A transaction description is required.')) if $::instance_conf->get_require_transaction_description_ps; + + my $transdate = $form->datetonum($form->{transdate}, \%myconfig); + my $closedto = $form->datetonum($form->{closedto}, \%myconfig); + + my @a = (); + my $count = 0; + my $debittax = 0; + my $credittax = 0; + my $debitcount = 0; + my $creditcount = 0; + my $debitcredit; + my %split_safety = (); + + my $dbh = SL::DB->client->dbh; + my ($notax_id) = selectrow_query($form, $dbh, "SELECT id FROM tax WHERE taxkey = 0 LIMIT 1", ); + my $zerotaxes = selectall_hashref_query($form, $dbh, "SELECT id FROM tax WHERE rate = 0", ); + + my @flds = qw(accno_id debit credit projectnumber fx_transaction source memo tax taxchart); + + for my $i (1 .. $form->{rowcount}) { + next if $form->{"debit_$i"} eq "" && $form->{"credit_$i"} eq ""; + + for (qw(debit credit tax)) { + $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"}); + } + + push @a, {}; + $debitcredit = ($form->{"debit_$i"} == 0) ? "0" : "1"; + + $split_safety{ $form->{"debit_$i"} <=> 0 }++; + $split_safety{ - $form->{"credit_$i"} <=> 0 }++; + + if ($debitcredit) { + $debitcount++; + } else { + $creditcount++; + } + + if (($debitcount >= 2) && ($creditcount == 2)) { + $form->{"credit_$i"} = 0; + $form->{"tax_$i"} = 0; + $creditcount--; + $form->{creditlock} = 1; + } + if (($creditcount >= 2) && ($debitcount == 2)) { + $form->{"debit_$i"} = 0; + $form->{"tax_$i"} = 0; + $debitcount--; + $form->{debitlock} = 1; + } + if (($creditcount == 1) && ($debitcount == 2)) { + $form->{creditlock} = 1; + } + if (($creditcount == 2) && ($debitcount == 1)) { + $form->{debitlock} = 1; + } + if ($debitcredit && $credittax) { + $form->{"taxchart_$i"} = "$notax_id--0.00000"; + } + if (!$debitcredit && $debittax) { + $form->{"taxchart_$i"} = "$notax_id--0.00000"; + } + my $amount = ($form->{"debit_$i"} == 0) + ? $form->{"credit_$i"} + : $form->{"debit_$i"}; + my $j = $#a; + if (($debitcredit && $credittax) || (!$debitcredit && $debittax)) { + $form->{"taxchart_$i"} = "$notax_id--0.00000"; + $form->{"tax_$i"} = 0; + } + my ($taxkey, $rate) = split(/--/, $form->{"taxchart_$i"}); + my $iswithouttax = grep { $_->{id} == $taxkey } @{ $zerotaxes }; + if (!$iswithouttax) { + if ($debitcredit) { + $debittax = 1; + } else { + $credittax = 1; + } + + my ($tmpnetamount,$tmpdiff); + ($tmpnetamount,$form->{"tax_$i"},$tmpdiff) = $form->calculate_tax($amount,$rate,$form->{taxincluded} *= 1,2); + if ($debitcredit) { + $form->{"debit_$i"} = $tmpnetamount; + } else { + $form->{"credit_$i"} = $tmpnetamount; + } + + } else { + $form->{"tax_$i"} = 0; + } + + for (@flds) { $a[$j]->{$_} = $form->{"${_}_$i"} } + $count++; + } + + if ($split_safety{-1} > 1 && $split_safety{1} > 1) { + $::form->error($::locale->text("Split entry detected. The values you have entered will result in an entry with more than one position on both debit and credit. " . + "Due to known problems involving accounting software kivitendo does not allow these.")); + } + + for my $i (1 .. $count) { + my $j = $i - 1; + for (@flds) { $form->{"${_}_$i"} = $a[$j]->{$_} } } - + + for my $i ($count + 1 .. $form->{rowcount}) { + for (@flds) { delete $form->{"${_}_$i"} } + } + + my ($debit, $credit, $taxtotal); + for my $i (1 .. $form->{rowcount}) { + my $dr = $form->{"debit_$i"}; + my $cr = $form->{"credit_$i"}; + my $tax = $form->{"tax_$i"}; + if ($dr && $cr) { + $form->error($locale->text('Cannot post transaction with a debit and credit entry for the same account!')); + } + $debit += $dr + $tax if $dr; + $credit += $cr + $tax if $cr; + $taxtotal += $tax if $form->{taxincluded} + } + + $form->{taxincluded} = 0 if !$taxtotal; # this is just for the wise guys - $form->error($locale->text('Cannot post transaction for a closed period!')) if ($transdate <= $closedto); - $form->error($locale->text('Soll- und Habenkonto sind gleich!')) if ($debitaccno eq $creditaccno); - $form->error($locale->text('Keine Steuerautomatik möglich!')) if ($debittaxkey && $credittaxkey && !($taxkey == 0)); - - if (($errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) { - $errno *= -1; - $err[1] = $locale->text('Cannot have a value in both Debit and Credit!'); - $err[2] = $locale->text('Debit and credit out of balance!'); - $err[3] = $locale->text('Cannot post a transaction without a value!'); - - $form->error($err[$errno]); + + $form->error($locale->text('Cannot post transaction above the maximum future booking date!')) + if ($form->date_max_future($form->{"transdate"}, \%myconfig)); + $form->error($locale->text('Cannot post transaction for a closed period!')) + if ($form->date_closed($form->{"transdate"}, \%myconfig)); + if ($form->round_amount($debit, 2) != $form->round_amount($credit, 2)) { + $form->error($locale->text('Out of balance transaction!')); } - undef($form->{callback}); - $form->redirect("Buchung gespeichert. Buchungsnummer = ".$form->{id}); - - $lxdebug->leave_sub(); -} + if ($form->round_amount($debit, 2) + $form->round_amount($credit, 2) == 0) { + $form->error($locale->text('Empty transaction!')); + } -sub post_as_new { - $lxdebug->enter_sub(); - $form->{id} = 0; - &add; + # start transaction (post + history + (optional) banktrans) + SL::DB->client->with_transaction(sub { - $lxdebug->leave_sub(); -} + if ((my $errno = GL->post_transaction(\%myconfig, \%$form)) <= -1) { + $errno *= -1; + my @err; + $err[1] = $locale->text('Cannot have a value in both Debit and Credit!'); + $err[2] = $locale->text('Debit and credit out of balance!'); + $err[3] = $locale->text('Cannot post a transaction without a value!'); -sub storno { - $lxdebug->enter_sub(); + die $err[$errno]; + } + # saving the history + if(!exists $form->{addition} && $form->{id} ne "") { + $form->{snumbers} = qq|gltransaction_| . $form->{id}; + $form->{addition} = "POSTED"; + $form->{what_done} = "gl transaction"; + $form->save_history; + } - $form->{id} = 0; - $form->{storno} =1; - &post; + # Case BankTransaction: update RecordLink and BankTransaction + if ($form->{callback} =~ /BankTransaction/ && $form->{bt_id}) { + # set invoice_amount - we only rely on bt_id in form, do all other stuff ui independent + # die if we have a unlogic or NYI case and abort the whole transaction + my ($bt, $chart_id, $payment); + require SL::DB::Manager::BankTransaction; + + $bt = SL::DB::Manager::BankTransaction->find_by(id => $::form->{bt_id}); + die "No bank transaction found" unless $bt; + + $chart_id = SL::DB::Manager::BankAccount->find_by(id => $bt->local_bank_account_id)->chart_id; + die "no chart id" unless $chart_id; + + $payment = SL::DB::Manager::AccTransaction->get_all(where => [ trans_id => $::form->{id}, + chart_link => { like => '%_paid%' }, + chart_id => $chart_id ]); + die "guru meditation error: Can only assign amount to one bank account booking" if scalar @{ $payment } > 1; + + # credit/debit * -1 matches the sign for bt.amount and bt.invoice_amount + + die "Can only assign the full (partial) bank amount to a single general ledger booking: " . $bt->not_assigned_amount . " " . ($payment->[0]->amount * -1) + unless (abs($bt->not_assigned_amount - ($payment->[0]->amount * -1)) < 0.001); + + $bt->update_attributes(invoice_amount => $bt->invoice_amount + ($payment->[0]->amount * -1)); + + # create record_link + my %props = ( + from_table => 'bank_transactions', + from_id => $::form->{bt_id}, + to_table => 'gl', + to_id => $::form->{id}, + ); + SL::DB::RecordLink->new(%props)->save; + # and tighten holy acc_trans_id for this bank_transaction + my %props_acc = ( + acc_trans_id => $payment->[0]->acc_trans_id, + bank_transaction_id => $bt->id, + gl_id => $payment->[0]->trans_id, + ); + my $bta = SL::DB::BankTransactionAccTrans->new(%props_acc); + $bta->save; - $lxdebug->leave_sub(); + } + 1; + }) or do { die SL::DB->client->error }; + + $main::lxdebug->leave_sub(); } -sub split_debit { - $lxdebug->enter_sub(); - # change callback - $form->{old_callback} = $form->escape($form->{callback},1); - $form->{callback} = $form->escape("$form->{script}?action=update",1); - # delete action - delete $form->{action}; - # save all other form variables in a previousform variable - foreach $key (keys %$form) { - # escape ampersands - $form->{$key} =~ s/&/%26/g; - $previousform .= qq|$key=$form->{$key}&|; +sub post { + $main::lxdebug->enter_sub(); + + $main::auth->assert('gl_transactions'); + + my $form = $main::form; + my $locale = $main::locale; + + if ($::myconfig{mandatory_departments} && !$form->{department_id}) { + $form->error($locale->text('You have to specify a department.')); } - chop $previousform; - $previousform = $form->escape($previousform, 1); - if (!$form->{debitpost}) { - $form->{debitpost} = $form->{amount}; - } - $form->{previousform} = $previousform; - &display_split_debit(); + $form->{title} = $locale->text("$form->{title} General Ledger Transaction"); + $form->{storno} = 0; - $lxdebug->leave_sub(); -} + post_transaction(); + if ($::instance_conf->get_webdav) { + SL::Webdav->new(type => 'general_ledger', + number => $form->{id}, + )->webdav_path; + } -sub split_credit { - $lxdebug->enter_sub(); - # change callback - $form->{old_callback} = $form->escape($form->{callback},1); - $form->{callback} = $form->escape("$form->{script}?action=update",1); - # delete action - delete $form->{action}; - # save all other form variables in a previousform variable - foreach $key (keys %$form) { - # escape ampersands - $form->{$key} =~ s/&/%26/g; - $previousform .= qq|$key=$form->{$key}&|; + my $msg = $::locale->text("General ledger transaction '#1' posted (ID: #2)", $form->{reference}, $form->{id}); + if ($form->{callback} =~ /BankTransaction/ && $form->{bt_id}) { + $form->redirect($msg); + + } elsif ('doc-tab' eq $form->{after_action}) { + # Redirect with callback containing a fragment does not work (by now) + # because the callback info is stored in the session an parsing the + # callback parameters does not support fragments (see SL::Form::redirect). + # So use flash_later for the message and redirect_headers for redirecting. + my $add_doc_url = build_std_url("script=gl.pl", 'action=edit', 'id=' . E($form->{id}), 'fragment=ui-tabs-docs'); + SL::Helper::Flash::flash_later('info', $msg); + print $form->redirect_header($add_doc_url); + $::dispatcher->end_request; + + } else { + $form->{callback} = build_std_url("action=add", "show_details"); + $form->redirect($msg); } - chop $previousform; - $previousform = $form->escape($previousform, 1); - if (!$form->{creditpost}) { - $form->{creditpost} = $form->{amount}; - } - $form->{previousform} = $previousform; - &display_split_credit(); - $lxdebug->leave_sub(); + + $main::lxdebug->leave_sub(); } -sub display_split_credit { - $lxdebug->enter_sub(); - $form->{taxchart} = $form->unescape($form->{taxchart}); - - $form->header; - - print qq| - -
-|.$locale->text('Credit').qq|: |.$form->format_amount(\%myconfig, $form->{amount},2).qq| EUR
-|.$locale->text('Still to post').qq|: EUR
- - -|; -for $i (2 .. $form->{creditrowcount}) { - $form->{"creditchart_$i"} = $form->{chart}; - $form->{"creditchart_$i"} =~ s/value=\"$form->{"creditchartselected_$i"}\"/value=\"$form->{"creditchartselected_$i"}\" selected/; - - $form->{"taxchart_$i"} = $form->{taxchart}; - $form->{"taxchart_$i"} =~ s/value=\"$form->{"taxchartselected_$i"}\"/value=\"$form->{"taxchartselected_$i"}\" selected/; - $position = $i -1; -print qq| - - - - - - - - - - - - - - - - - - - - +sub post_as_new { + $main::lxdebug->enter_sub(); -|; -} -print qq| -
Position $position|.$locale->text('Amount').qq| $1 EUR
|.$locale->text('Tax').qq| EUR Steuerkorrektur

- - -{path}> -{login}> -{password}> -{callback}> -{old_callback}> -{amount}> - - -escape($form->{chart}).qq|> -escape($form->{taxchart}).qq|> -

- - -

- - - -|; + $main::auth->assert('gl_transactions'); - $lxdebug->leave_sub(); -} + my $form = $main::form; -sub display_split_debit { - $lxdebug->enter_sub(); - $form->{taxchart} = $form->unescape($form->{taxchart}); - - $form->header; - - print qq| - -
-|.$locale->text('Debit').qq|: |.$form->format_amount(\%myconfig, $form->{amount},2).qq| EUR
-|.$locale->text('Still to post').qq|: EUR
- - -|; -for $i (2 .. $form->{debitrowcount}) { - $form->{"debitchart_$i"} = $form->{chart}; - $form->{"debitchart_$i"} =~ s/value=\"$form->{"debitchartselected_$i"}\"/value=\"$form->{"debitchartselected_$i"}\" selected/; - - $form->{"taxchart_$i"} = $form->{taxchart}; - $form->{"taxchart_$i"} =~ s/value=\"$form->{"taxchartselected_$i"}\"/value=\"$form->{"taxchartselected_$i"}\" selected/; - $position = $i -1; -print qq| - - - - - - - - - - - - - - - - - - - - + $form->{id} = 0; + &add; + $main::lxdebug->leave_sub(); -|; } -print qq| -
Position $position|.$locale->text('Amount').qq| $1 EUR
|.$locale->text('Tax').qq| EUR Steuerkorrektur

- - -{path}> -{login}> -{password}> -{callback}> -{old_callback}> -{amount}> - - -escape($form->{chart}).qq|> -escape($form->{taxchart}).qq|> -

- - -

- - - -|; - $lxdebug->leave_sub(); -} +sub storno { + $main::lxdebug->enter_sub(); -sub new_account { - $lxdebug->enter_sub(); - - $form->{chart} = $form->unescape($form->{chart}); - - if ($form->{credit_splited}) { - $form->{creditpost} = $form->{amount}; - - for $i (2 .. $form->{creditrowcount}) { - $form->{"credit_$i"} = $form->parse_amount(\%myconfig, $form->{"credit_$i"}); - $form->{creditpost} -= $form->{"credit_$i"}; - ($taxkey, $taxrate) = split(/--/, $form->{"taxchartselected_$i"}); - if ($form->{"tax_manual_$i"}) { - $form->{"tax_$i"} = $form->parse_amount(\%myconfig, $form->{"tax_$i"}); - } else { - $form->{"tax_$i"} = $form->{"credit_$i"} * $taxrate; - } - $form->{creditpost} -= $form->{"tax_$i"}; + $main::auth->assert('gl_transactions'); - } - if ($form->{"credit_$form->{creditrowcount}"}) { - $form->{creditrowcount}++; - } - - &display_split_credit; + my $form = $main::form; + my %myconfig = %main::myconfig; + my $locale = $main::locale; + + # don't cancel cancelled transactions + if (IS->has_storno(\%myconfig, $form, 'gl')) { + $form->{title} = $locale->text("Cancel Accounts Receivables Transaction"); + $form->error($locale->text("Transaction has already been cancelled!")); } - if ($form->{debit_splited}) { - $form->{debitpost} = $form->{amount}; - - for $i (2 .. $form->{debitrowcount}) { - $form->{"debit_$i"} = $form->parse_amount(\%myconfig, $form->{"debit_$i"}); - $form->{debitpost} -= $form->{"debit_$i"}; - ($taxkey, $taxrate) = split(/--/, $form->{"taxchartselected_$i"}); - if ($form->{"tax_manual_$i"}) { - $form->{"tax_$i"} = $form->parse_amount(\%myconfig, $form->{"tax_$i"}); - } else { - $form->{"tax_$i"} = $form->{"debit_$i"} * $taxrate; - } - $form->{debitpost} -= $form->{"tax_$i"}; - } - if ($form->{"debit_$form->{debitrowcount}"}) { - $form->{debitrowcount}++; - } - - &display_split_debit; - } - $lxdebug->leave_sub(); -} + GL->storno($form, \%myconfig, $form->{id}); -sub close { - $lxdebug->enter_sub(); + # saving the history + if(!exists $form->{addition} && $form->{id} ne "") { + $form->{snumbers} = qq|gltransaction_| . $form->{id}; + $form->{addition} = "STORNO"; + $form->{what_done} = "gl_transaction"; + $form->save_history; + } + # /saving the history - # save the new form variables before splitting previousform - map { $newform{$_} = $form->{$_} } keys %$form; + $form->redirect(sprintf $locale->text("Transaction %d cancelled."), $form->{storno_id}); - $previousform = $form->unescape($form->{previousform}); + $main::lxdebug->leave_sub(); +} - # don't trample on previous variables - map { delete $form->{$_} } keys %newform; +sub continue { + call_sub($main::form->{nextsub}); +} - # now take it apart and restore original values - foreach $item (split /&/, $previousform) { - ($key, $value) = split /=/, $item, 2; - $value =~ s/%26/&/g; - $form->{$key} = $value; - } - if ($newform{credit_splited}) { - $form->{credit} = 0; - $form->{credit_splited} = $newform{credit_splited}; - $form->{creditrowcount} = $newform{creditrowcount}; - for $i (2 .. $form->{creditrowcount}) { - $form->{"creditchartselected_$i"} =$newform{"creditchartselected_$i"}; - $form->{"credit_$i"} =$newform{"credit_$i"}; - $form->{"credit_$i"} = $form->parse_amount(\%myconfig, $form->{"credit_$i"}); - $form->{"taxchartselected_$i"} =$newform{"taxchartselected_$i"}; - ($taxkey, $taxrate) = split(/--/, $form->{"taxchartselected_$i"}); - if ($newform{"tax_manual_$i"}) { - $form->{"tax_$i"} = $form->parse_amount(\%myconfig, $newform{"tax_$i"}); - } else { - $form->{"tax_$i"} = $form->{"credit_$i"} * $taxrate; - } - $form->{credit} += $form->round_amount($form->{"credit_$i"},2); - $form->{credit} += $form->round_amount($form->{"tax_$i"},2);; - - } - } else { - $form->{debit} = 0; - $form->{debit_splited} = $newform{debit_splited}; - $form->{debitrowcount} = $newform{debitrowcount}; - for $i (2 .. $form->{debitrowcount}) { - $form->{"debitchartselected_$i"} =$newform{"debitchartselected_$i"}; - $form->{"debit_$i"} =$newform{"debit_$i"}; - $form->{"debit_$i"} = $form->parse_amount(\%myconfig, $form->{"debit_$i"}); - $form->{"taxchartselected_$i"} =$newform{"taxchartselected_$i"}; - ($taxkey, $taxrate) = split(/--/, $form->{"taxchartselected_$i"}); - if ($newform{"tax_manual_$i"}) { - $form->{"tax_$i"} = $form->parse_amount(\%myconfig, $newform{"tax_$i"}); - } else { - $form->{"tax_$i"} = $form->{"debit_$i"} * $taxrate; - } - $form->{debit} += $form->round_amount($form->{"debit_$i"},2); - $form->{debit} += $form->round_amount($form->{"tax_$i"},2);; - } - } - delete $form->{action}; - $callback = $form->unescape($form->{callback}); - $form->{callback} = $form->unescape($form->{old_callback}); - delete $form->{old_callback}; - - # put callback together - foreach $key (keys %$form) { - # do single escape for Apache 2.0 - $value = $form->escape($form->{$key}, 1); - $callback .= qq|&$key=$value|; - } - $form->{callback} = $callback; - +sub get_tax_dropdown { + my $transdate = $::form->{transdate} ? DateTime->from_kivitendo($::form->{transdate}) : DateTime->today_local; + my $deliverydate = $::form->{deliverydate} ? DateTime->from_kivitendo($::form->{deliverydate}) : undef; + my @tax_accounts = GL->get_active_taxes_for_chart($::form->{accno_id}, $deliverydate // $transdate); + my $html = $::form->parse_html_template("gl/update_tax_accounts", { TAX_ACCOUNTS => \@tax_accounts }); - # redirect - $form->redirect; - $lxdebug->leave_sub(); + print $::form->ajax_response_header, $html; } + +sub get_chart_balance { + my %balances = GL->get_chart_balances($::form->{accno_id}); + my $balance = $::form->format_amount(\%::myconfig, $balances{ $::form->{accno_id} }, 2, 'DRCR'); + + print $::form->ajax_response_header, $balance; +} + +1;