Jahresabschluß - YearEndTransactions neu implementiert
authorG. Richardson <grichardson@kivitec.de>
Wed, 25 Sep 2019 16:51:58 +0000 (18:51 +0200)
committerG. Richardson <grichardson@kivitec.de>
Mon, 30 Sep 2019 10:25:00 +0000 (12:25 +0200)
Der alte Jahresabschluß hatte eine Reihe von Schwächen, z.B. wurde nicht
zwischen Bestands- und Erfolgskonten unterschieden, und es wurde auch
kein Gewinn- oder Verlustvortrag gemacht. Der Anwender mußte selber
entscheiden, welche Konten abgeschlossen werden sollten.

* Saldenvortragskonto, sowie Gewinn- und Verlustvortragskonto müssen in
  der Mandantenkonfiguration unter "Standardkonten" konfiguriert werden

* Es wird nicht mehr für jedes Konto eine Dialogbuchung erstellt,
  sondern es werden alle Soll- und Habensalden zusammengefasst, und
  diese in Summe gegen das Saldenvortragskonto gebucht

* Der Jahresabschluß lässt sich für einen bestimmten Zeitraum mehrmals
  ausführen, falls später Buchungen für den Zeitraum hinzukommen.

12 files changed:
SL/Controller/YearEndTransactions.pm
SL/DB/MetaSetup/Default.pm
doc/changelog
locale/de/all
menus/user/00-erp.yaml
sql/Pg-upgrade2/defaults_year_end_charts.sql [new file with mode: 0644]
templates/webpages/client_config/_default_accounts.html
templates/webpages/gl/yearend_bottom.html [deleted file]
templates/webpages/gl/yearend_filter.html [deleted file]
templates/webpages/gl/yearend_top.html [deleted file]
templates/webpages/yearend/_charts.html [new file with mode: 0644]
templates/webpages/yearend/form.html [new file with mode: 0644]

index 5228038..d015cca 100644 (file)
@@ -4,312 +4,551 @@ use strict;
 
 use parent qw(SL::Controller::Base);
 
+use utf8; # Umlauts in hardcoded German default texts
 use DateTime;
 use SL::Locale::String qw(t8);
-use SL::ReportGenerator;
 use SL::Helper::Flash;
 use SL::DBUtils;
+use Data::Dumper;
+use List::Util qw(sum);
+use SL::ClientJS;
 
 use SL::DB::Chart;
 use SL::DB::GLTransaction;
 use SL::DB::AccTransaction;
-use SL::DB::Helper::AccountingPeriod qw(get_balance_starting_date);
-
-use SL::Presenter::Tag qw(checkbox_tag);
+use SL::DB::Employee;
+use SL::DB::Helper::AccountingPeriod qw(get_balance_starting_date get_balance_startdate_method_options);
 
 use Rose::Object::MakeMethods::Generic (
-  'scalar --get_set_init' => [ qw(charts charts9000 cbob_chart cb_date cb_startdate ob_date cb_reference ob_reference cb_description ob_description) ],
+  'scalar --get_set_init' => [ qw(cb_date cb_startdate ob_date) ],
 );
 
 __PACKAGE__->run_before('check_auth');
 
-sub action_filter {
+sub action_form {
   my ($self) = @_;
-  $self->ob_date(DateTime->today->truncate(to => 'year'))                  if !$self->ob_date;
-  $self->cb_date(DateTime->today->truncate(to => 'year')->add(days => -1)) if !$self->cb_date;
-  $self->ob_reference(t8('OB Transaction'))   if !$self->ob_reference;
-  $self->cb_reference(t8('CB Transaction'))   if !$self->cb_reference;
-  $self->ob_description(t8('OB Transaction')) if !$self->ob_description;
-  $self->cb_description(t8('CB Transaction')) if !$self->cb_description;
-
-  $self->setup_filter_action_bar;
-  $self->render('gl/yearend_filter',
-                title               => t8('CB/OB Transactions'),
-                make_title_of_chart => sub { $_[0]->accno.' '.$_[0]->description }
-               );
 
-}
+  $self->cb_startdate($::locale->parse_date_to_object($self->get_balance_starting_date($self->cb_date)));
+
+  my $defaults         = SL::DB::Default->get;
+  my $carry_over_chart = SL::DB::Manager::Chart->find_by( id => $defaults->carry_over_account_chart_id     );
+  my $profit_chart     = SL::DB::Manager::Chart->find_by( id => $defaults->profit_carried_forward_chart_id );
+  my $loss_chart       = SL::DB::Manager::Chart->find_by( id => $defaults->loss_carried_forward_chart_id   );
+
+  $self->render('yearend/form',
+                title                            => t8('Year-end closing'),
+                carry_over_chart                 => $carry_over_chart,
+                profit_chart                     => $profit_chart,
+                loss_chart                       => $loss_chart,
+                balance_startdate_method_options => get_balance_startdate_method_options(),
+               );
+};
 
-sub action_list {
+sub action_year_end_bookings {
   my ($self) = @_;
-  $main::lxdebug->enter_sub();
 
-  my $report     = SL::ReportGenerator->new(\%::myconfig, $::form);
+  $self->_parse_form;
+
 
-  $self->prepare_report($report);
+  eval {
+    _year_end_bookings( start_date => $self->cb_startdate,
+                        cb_date    => $self->cb_date,
+                      );
+    1;
+  } or do {
+    $self->js->flash('error', t8('Error while applying year-end bookings!') . ' ' . $@);
+    return $self->js->render;
+  };
 
-  $report->set_options(
-    output_format        => 'HTML',
-    raw_top_info_text    => $::form->parse_html_template('gl/yearend_top',    { SELF => $self }),
-    raw_bottom_info_text => $::form->parse_html_template('gl/yearend_bottom', { SELF => $self }),
-    allow_pdf_export     => 0,
-    allow_csv_export     => 0,
-    title                => $::locale->text('CB/OB Transactions'),
-  );
+  my ($report_data, $profit_loss_sum) = _report(
+                                                cb_date    => $self->cb_date,
+                                                start_date => $self->cb_startdate,
+                                               );
 
-  $self->setup_list_action_bar;
-  $report->generate_with_headers();
-  $main::lxdebug->leave_sub();
+  my $html = $self->render('yearend/_charts', { layout  => 0 , process => 1, output => 0 },
+                 charts          => $report_data,
+                 profit_loss_sum => $profit_loss_sum,
+               );
+  return $self->js->flash('info', t8('Year-end bookings were successfully completed!'))
+               ->html('#charts', $html)
+               ->render;
 }
 
-sub action_generate {
+sub action_get_start_date {
   my ($self) = @_;
 
-  if ($self->cb_date > $self->ob_date) {
-    flash ('error', $::locale->text('CB date #1 is higher than OB date #2. Please select again.', $self->cb_date, $self->ob_date));
-  } else {
-    my $cnt = $self->make_booking();
-    flash('info', $::locale->text('#1 CB transactions and #1 OB transactions generated.',$cnt)) if $cnt > 0;
+  my $cb_date = $self->cb_date; # parse from form via init
+  unless ( $self->cb_date ) {
+    return $self->hide('#apply_year_end_bookings_button')
+                ->flash('error', t8('Year-end date missing'))
+                ->render;
   }
-  $self->action_list;
+
+  $self->cb_startdate($::locale->parse_date_to_object($self->get_balance_starting_date($self->cb_date, $::form->{'balance_startdate_method'})));
+
+  # $main::lxdebug->message(0, "found start date: ", $self->cb_startdate->to_kivitendo);
+
+  return $self->js->val('#cb_startdate', $self->cb_startdate->to_kivitendo)
+              ->show('#apply_year_end_bookings_button')
+              ->show('.startdate')
+              ->render;
 }
 
-sub check_auth {
-  $::auth->assert('general_ledger');
+sub action_update_charts {
+  my ($self) = @_;
+
+  $self->_parse_form;
+
+  my ($report_data, $profit_loss_sum) = _report(
+                                                cb_date   => $self->cb_date,
+                                                start_date => $self->cb_startdate,
+                                               );
+
+  $self->render('yearend/_charts', { layout  => 0 , process => 1 },
+                 charts          => $report_data,
+                 profit_loss_sum => $profit_loss_sum,
+               );
 }
 
 #
 # helpers
 #
 
-sub make_booking {
+sub _parse_form {
   my ($self) = @_;
-  $main::lxdebug->enter_sub();
-  my @ids = map { $::form->{"multi_id_$_"} } grep { $::form->{"multi_id_$_"} } (1..$::form->{rowcount});
-  my $cnt = 0;
-  $main::lxdebug->message(LXDebug->DEBUG2(),"generate for ".$::form->{cbob_chart}." # ".scalar(@ids)." charts");
-  if (scalar(@ids) && $::form->{cbob_chart}) {
-    my $carryoverchart = SL::DB::Manager::Chart->get_first(  query => [ id => $::form->{cbob_chart} ] );
-    my $charts = SL::DB::Manager::Chart->get_all(  query => [ id => \@ids ] );
-    foreach my $chart (@{ $charts }) {
-      $main::lxdebug->message(LXDebug->DEBUG2(),"chart_id=".$chart->id." accno=".$chart->accno);
-      my $balance = $self->get_balance($chart);
-      if ( $balance != 0 ) {
-        # SB
-        $self->gl_booking($balance,$self->cb_date,$::form->{cb_reference},$::form->{cb_description},$chart,$carryoverchart,0,1);
-        # EB
-        $self->gl_booking($balance,$self->ob_date,$::form->{ob_reference},$::form->{ob_description},$carryoverchart,$chart,1,0);
-        $cnt++;
-      }
-    }
-  }
-  $main::lxdebug->leave_sub();
-  return $cnt;
+
+  # parse dates
+  $self->cb_startdate($::locale->parse_date_to_object($self->get_balance_starting_date($self->cb_date)));
+
+  die "cb_date must come after start_date" unless $self->cb_date > $self->cb_startdate;
+
 }
 
+sub _year_end_bookings {
+  my (%params) = @_;
 
-sub prepare_report {
-  my ($self,$report) = @_;
-  $main::lxdebug->enter_sub();
-  my $idx = 1;
+  my $start_date = delete $params{start_date};
+  my $cb_date    = delete $params{cb_date};
 
-  my %column_defs = (
-    'ids'         => { raw_header_data => checkbox_tag("", id => "check_all",
-                                                                          checkall => "[data-checkall=1]"), 'align' => 'center' },
-    'chart'       => { text => $::locale->text('Account'), },
-    'description' => { text => $::locale->text('Description'), },
-    'saldo'       => { text => $::locale->text('Saldo'),  'align' => 'right'},
-    'sum_cb'      => { text => $::locale->text('Sum CB Transactions'), 'align' => 'right'},  ##close == Schluss
-    'sum_ob'      => { text => $::locale->text('Sum OB Transactions'), 'align' => 'right'},  ##open  == Eingang
-  );
-  my @columns      = qw(ids chart description saldo sum_cb sum_ob);
-  map { $column_defs{$_}->{visible} = 1 } @columns;
+  my $defaults         = SL::DB::Default->get;
+  my $carry_over_chart = SL::DB::Manager::Chart->find_by( id => $defaults->carry_over_account_chart_id     ) // die t8('No carry-over chart configured!');
+  my $profit_chart     = SL::DB::Manager::Chart->find_by( id => $defaults->profit_carried_forward_chart_id ) // die t8('No profit carried forward chart configured!');
+  my $loss_chart       = SL::DB::Manager::Chart->find_by( id => $defaults->loss_carried_forward_chart_id   ) // die t8('No profit and loss carried forward chart configured!');
 
-  my $ob_next_date = $self->ob_date->clone();
-  $ob_next_date->add(years => 1)->add(days => -1);
+  my ($report_data, $profit_loss_sum) = _report(
+                                                start_date => $start_date,
+                                                cb_date    => $cb_date,
+                                               );
 
-  $self->cb_startdate($::locale->parse_date_to_object($self->get_balance_starting_date($self->cb_date)));
+  my @asset_accounts       = grep { $_->{account_type} eq 'asset_account' }       @{ $report_data };
+  my @profit_loss_accounts = grep { $_->{account_type} eq 'profit_loss_account' } @{ $report_data };
 
-  my @custom_headers = ();
-  # Zeile 1:
-  push @custom_headers, [
-      { 'text' => '   ', 'colspan' => 3 },
-      { 'text' => $::locale->text("Timerange")."<br />".$self->cb_startdate->to_kivitendo." - ".$self->cb_date->to_kivitendo, 'colspan' => 2, 'align' => 'center'},
-      { 'text' => $::locale->text("Timerange")."<br />".$self->ob_date->to_kivitendo." - ".$ob_next_date->to_kivitendo, 'align' => 'center'},
-    ];
-
-  # Zeile 2:
-  my @line_2 = ();
-  map { push @line_2 , $column_defs{$_} } grep { $column_defs{$_}->{visible} } @columns;
-  push @custom_headers, [ @line_2 ];
-
-  $report->set_custom_headers(@custom_headers);
-  $report->set_columns(%column_defs);
-  $report->set_column_order(@columns);
-
-  my $chart9actual = SL::DB::Manager::Chart->get_first( query => [ id => $self->cbob_chart ] );
-  $self->{cbob_chartaccno} = $chart9actual->accno.' '.$chart9actual->description;
-
-  foreach my $chart (@{ $self->charts }) {
-    my $balance = $self->get_balance($chart);
-    if ( $balance != 0 ) {
-      my $chart_id = $chart->id;
-      my $row = { map { $_ => { 'data' => '' } } @columns };
-      $row->{ids}  = {
-        'raw_data' => checkbox_tag("multi_id_${idx}", value => $chart_id, "data-checkall" => 1),
-        'valign'   => 'center',
-        'align'    => 'center',
-      };
-      $row->{chart}->{data}       = $chart->accno;
-      $row->{description}->{data} = $chart->description;
-      if ( $balance > 0 ) {
-        $row->{saldo}->{data} = $::form->format_amount(\%::myconfig, $balance, 2)." H";
-      } elsif ( $balance < 0 )  {
-        $row->{saldo}->{data} = $::form->format_amount(\%::myconfig,-$balance, 2)." S";
-      } else {
-        $row->{saldo}->{data} = $::form->format_amount(\%::myconfig,0, 2)."  ";
-      }
-      my $sum_cb = 0;
-      foreach my $acc ( @{ SL::DB::Manager::AccTransaction->get_all(where => [ chart_id  => $chart->id, cb_transaction => 't',
-                                                                               transdate => { ge => $self->cb_startdate},
-                                                                               transdate => { le => $self->cb_date }
-                                                                             ]) }) {
-        $sum_cb += $acc->amount;
-      }
-      my $sum_ob = 0;
-      foreach my $acc ( @{ SL::DB::Manager::AccTransaction->get_all(where => [ chart_id  => $chart->id, ob_transaction => 't',
-                                                                               transdate => { ge => $self->ob_date},
-                                                                               transdate => { le => $ob_next_date }
-                                                                             ]) }) {
-        $sum_ob += $acc->amount;
-      }
-      if ( $sum_cb > 0 ) {
-        $row->{sum_cb}->{data} = $::form->format_amount(\%::myconfig, $sum_cb, 2)." H";
-      } elsif ( $sum_cb < 0 )  {
-        $row->{sum_cb}->{data} = $::form->format_amount(\%::myconfig,-$sum_cb, 2)." S";
-      } else {
-        $row->{sum_cb}->{data} = $::form->format_amount(\%::myconfig,0, 2)."  ";
-      }
-      if ( $sum_ob > 0 ) {
-        $row->{sum_ob}->{data} = $::form->format_amount(\%::myconfig, $sum_ob, 2)." H";
-      } elsif ( $sum_ob < 0 )  {
-        $row->{sum_ob}->{data} = $::form->format_amount(\%::myconfig,-$sum_ob, 2)." S";
-      } else {
-        $row->{sum_ob}->{data} = $::form->format_amount(\%::myconfig,0, 2)."  ";
-      }
-      $report->add_data($row);
-    }
-    $idx++;
-  }
+  my $ob_date = $cb_date->clone->add(days => 1);
 
-  $self->{row_count} = $idx;
-  $main::lxdebug->leave_sub();
-}
+  my ($credit_sum, $debit_sum) = (0,0);
 
-sub get_balance {
-  $main::lxdebug->enter_sub();
-  my ($self,$chart) = @_;
+  my $employee_id = SL::DB::Manager::Employee->current->id;
 
-  #$main::lxdebug->message(LXDebug->DEBUG2(),"get_balance from=".$self->cb_startdate->to_kivitendo." to=".$self->cb_date->to_kivitendo);
-  my $balance = $chart->get_balance(fromdate => $self->cb_startdate, todate => $self->cb_date);
-  $main::lxdebug->leave_sub();
-  return 0 if !defined $balance || $balance == 0;
-  return $balance;
-}
+       # rather than having one gl transaction for each asset account, we group all
+  # the debit sums and credit sums for cb and ob bookings, so we will have 4 gl
+  # transactions:
 
-sub gl_booking {
-  my ($self, $amount, $transdate, $reference, $description, $konto, $gegenkonto, $ob, $cb) = @_;
-  $::form->get_employee();
-  my $employee_id = $::form->{employee_id};
-  $main::lxdebug->message(LXDebug->DEBUG2(),"employee_id=".$employee_id." ob=".$ob." cb=".$cb);
-  my $gl_entry = SL::DB::GLTransaction->new(
-    employee_id    => $employee_id,
-    transdate      => $transdate,
-    reference      => $reference,
-    description    => $description,
-    ob_transaction => $ob,
-    cb_transaction => $cb,
-  );
-  #$gl_entry->save;
-  my $kto_trans1 = SL::DB::AccTransaction->new(
-    trans_id       => $gl_entry->id,
-    transdate      => $transdate,
-    ob_transaction => $ob,
-    cb_transaction => $cb,
-    chart_id       => $gegenkonto->id,
-    chart_link     => $konto->link,
-    tax_id         => 0,
-    taxkey         => 0,
-    amount         => $amount,
-  );
-  #$kto_trans1->save;
-  my $kto_trans2 = SL::DB::AccTransaction->new(
-    trans_id       => $gl_entry->id,
-    transdate      => $transdate,
-    ob_transaction => $ob,
-    cb_transaction => $cb,
-    chart_id       => $konto->id,
-    chart_link     => $konto->link,
-    tax_id         => 0,
-    taxkey         => 0,
-    amount         => -$amount,
-  );
-  #$kto_trans2->save;
-  $gl_entry->add_transactions($kto_trans1);
-  $gl_entry->add_transactions($kto_trans2);
-  $gl_entry->save;
-}
+  # * cb for credit
+  # * cb for debit
+  # * ob for credit
+  # * ob for debit
 
-sub init_cbob_chart     { $::form->{cbob_chart}                                    }
-sub init_ob_date        { $::locale->parse_date_to_object($::form->{ob_date})      }
-sub init_ob_reference   { $::form->{ob_reference}                                  }
-sub init_ob_description { $::form->{ob_description}                                }
-sub init_cb_startdate   { $::locale->parse_date_to_object($::form->{cb_startdate}) }
-sub init_cb_date        { $::locale->parse_date_to_object($::form->{cb_date})      }
-sub init_cb_reference   { $::form->{cb_reference}                                  }
-sub init_cb_description { $::form->{cb_description}                                }
+  my $db = SL::DB->client;
+  $db->with_transaction(sub {
 
-sub init_charts9000 {
-  SL::DB::Manager::Chart->get_all(  query => [ accno => { like => '9%'}] );
-}
+    ######### asset accounts ########
+    # need cb and ob transactions
 
-sub init_charts {
-  # wie geht 'not like' in rose ?
-  SL::DB::Manager::Chart->get_all(  query => [ \ "accno not like '9%'"], sort_by => 'accno ASC' );
-}
+    my $debit_balance  = 0;
+    my $credit_balance = 0;
 
-sub setup_filter_action_bar {
-  my ($self) = @_;
+    my $asset_cb_debit_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $cb_date,
+      reference      => 'SB ' . $cb_date->year,
+      description    => 'Automatische SB-Buchungen Bestandskonten Soll für ' . $cb_date->year,
+      ob_transaction => 0,
+      cb_transaction => 1,
+    );
+    my $asset_ob_debit_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $ob_date,
+      reference      => 'EB ' . $ob_date->year,
+      description    => 'Automatische EB-Buchungen Bestandskonten Haben für ' . $ob_date->year,
+      ob_transaction => 1,
+      cb_transaction => 0,
+    );
+    my $asset_cb_credit_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $cb_date,
+      reference      => 'SB ' . $cb_date->year,
+      description    => 'Automatische SB-Buchungen Bestandskonten Haben für ' . $cb_date->year,
+      ob_transaction => 0,
+      cb_transaction => 1,
+    );
+    my $asset_ob_credit_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $ob_date,
+      reference      => 'EB ' . $ob_date->year,
+      description    => 'Automatische EB-Buchungen Bestandskonten Soll für ' . $ob_date->year,
+      ob_transaction => 1,
+      cb_transaction => 0,
+    );
+    $asset_cb_debit_entry->transactions([]);
+    $asset_ob_debit_entry->transactions([]);
+    $asset_cb_credit_entry->transactions([]);
+    $asset_ob_credit_entry->transactions([]);
+
+    foreach my $asset_account ( @asset_accounts ) {
+      next if $asset_account->{amount_with_cb} == 0;
+
+      # create cb and ob acc_trans entry here, but decide which gl entry to add it to later
+      my $asset_cb_acc = SL::DB::AccTransaction->new(
+        transdate      => $cb_date,
+        ob_transaction => 0,
+        cb_transaction => 1,
+        chart_id       => $asset_account->{chart_id},
+        chart_link     => $asset_account->{chart_link},
+        tax_id         => 0,
+        taxkey         => 0,
+        amount         => - $asset_account->{amount_with_cb},
+      );
+      my $asset_ob_acc = SL::DB::AccTransaction->new(
+        transdate      => $ob_date,
+        ob_transaction => 1,
+        cb_transaction => 0,
+        chart_id       => $asset_account->{chart_id},
+        chart_link     => $asset_account->{chart_link},
+        tax_id         => 0,
+        taxkey         => 0,
+        amount         => $asset_account->{amount_with_cb},
+      );
+
+      if ( $asset_account->{amount_with_cb} < 0 ) {
+        $debit_balance += $asset_account->{amount_with_cb};
+        # $main::lxdebug->message(0, sprintf("adding accno %s with balance %s to debit", $asset_account->{accno}, $asset_account->{amount_with_cb}));
+
+        $asset_cb_debit_entry->add_transactions($asset_cb_acc);
+        $asset_ob_debit_entry->add_transactions($asset_ob_acc);
+      } else {
+        # $main::lxdebug->message(0, sprintf("adding accno %s with balance %s to credit", $asset_account->{accno}, $asset_account->{amount_with_cb}));
+        $credit_balance += $asset_account->{amount_with_cb};
+        $asset_cb_credit_entry->add_transactions($asset_cb_acc);
+        $asset_ob_credit_entry->add_transactions($asset_ob_acc);
+      };
+    };
+
+    my $debit_cb_acc = SL::DB::AccTransaction->new(
+      transdate      => $cb_date,
+      ob_transaction => 0,
+      cb_transaction => 1,
+      chart_id       => $carry_over_chart->id,
+      chart_link     => $carry_over_chart->link, # maybe leave chart_link empty?
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => $debit_balance,
+    );
+    my $debit_ob_acc = SL::DB::AccTransaction->new(
+      transdate      => $ob_date,
+      ob_transaction => 1,
+      cb_transaction => 0,
+      chart_id       => $carry_over_chart->id,
+      chart_link     => $carry_over_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => - $debit_balance,
+    );
+    my $credit_cb_acc = SL::DB::AccTransaction->new(
+      transdate      => $cb_date,
+      ob_transaction => 0,
+      cb_transaction => 1,
+      chart_id       => $carry_over_chart->id,
+      chart_link     => $carry_over_chart->link, # maybe leave chart_link empty?
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => $credit_balance,
+    );
+    my $credit_ob_acc = SL::DB::AccTransaction->new(
+      transdate      => $ob_date,
+      ob_transaction => 1,
+      cb_transaction => 0,
+      chart_id       => $carry_over_chart->id,
+      chart_link     => $carry_over_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => - $credit_balance,
+    );
+    $asset_cb_debit_entry->add_transactions($debit_cb_acc);
+    $asset_ob_debit_entry->add_transactions($debit_ob_acc);
+    $asset_cb_credit_entry->add_transactions($credit_cb_acc);
+    $asset_ob_credit_entry->add_transactions($credit_ob_acc);
+
+    $asset_cb_debit_entry->save if scalar @{ $asset_cb_debit_entry->transactions } > 1;
+    $asset_ob_debit_entry->save if scalar @{ $asset_ob_debit_entry->transactions } > 1;
+    $asset_cb_credit_entry->save if scalar @{ $asset_cb_credit_entry->transactions } > 1;
+    $asset_ob_credit_entry->save if scalar @{ $asset_ob_credit_entry->transactions } > 1;
+
+    #######  profit-loss accounts #######
+    # these only have a closing balance, the balance is transferred to the profit-loss account
+
+    # need to know if profit or loss first!
+    # use amount_with_cb, so it can be run several times. So sum may be 0 the second time.
+    my $profit_loss_sum = sum map { $_->{amount_with_cb} }
+                              grep { $_->{account_type} eq 'profit_loss_account' }
+                              @{$report_data};
+    my $pl_chart;
+    if ( $profit_loss_sum > 0 ) {
+      $pl_chart = $profit_chart;
+    } else {
+      $pl_chart = $loss_chart;
+    };
+
+    my $pl_debit_balance  = 0;
+    my $pl_credit_balance = 0;
+    # soll = debit, haben = credit
+    my $pl_cb_debit_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $cb_date,
+      reference      => 'SB ' . $cb_date->year,
+      description    => 'Automatische SB-Buchungen Erfolgskonten Soll für ' . $cb_date->year,
+      ob_transaction => 0,
+      cb_transaction => 1,
+    );
+    my $pl_cb_credit_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $cb_date,
+      reference      => 'SB ' . $cb_date->year,
+      description    => 'Automatische SB-Buchungen Erfolgskonten Haben für ' . $cb_date->year,
+      ob_transaction => 0,
+      cb_transaction => 1,
+    );
+    $pl_cb_debit_entry->transactions([]);
+    $pl_cb_credit_entry->transactions([]);
+
+    foreach my $profit_loss_account ( @profit_loss_accounts ) {
+      # $main::lxdebug->message(0, sprintf("found chart %s with balance %s", $profit_loss_account->{accno}, $profit_loss_account->{amount_with_cb}));
+
+      next if $profit_loss_account->{amount_with_cb} == 0;
+
+      my $debit_cb_acc = SL::DB::AccTransaction->new(
+        transdate      => $cb_date,
+        ob_transaction => 0,
+        cb_transaction => 1,
+        chart_id       => $profit_loss_account->{chart_id},
+        chart_link     => $profit_loss_account->{chart_link},
+        tax_id         => 0,
+        taxkey         => 0,
+        amount         => - $profit_loss_account->{amount_with_cb},
+      );
+      my $credit_cb_acc = SL::DB::AccTransaction->new(
+        transdate      => $cb_date,
+        ob_transaction => 0,
+        cb_transaction => 1,
+        chart_id       => $profit_loss_account->{chart_id},
+        chart_link     => $profit_loss_account->{chart_link},
+        tax_id         => 0,
+        taxkey         => 0,
+        amount         => $profit_loss_account->{amount_with_cb},
+      );
+      if ( { $profit_loss_account->{amount_with_cb} < 0 } ) {
+        $pl_debit_balance += $profit_loss_account->{amount_with_cb};
+         $pl_cb_debit_entry->add_transactions($debit_cb_acc);
+      } else {
+        $pl_credit_balance += $profit_loss_account->{amount_with_cb};
+         $pl_cb_credit_entry->add_transactions($credit_cb_acc);
+      };
+    };
+
+    my $debit_cb_acc = SL::DB::AccTransaction->new(
+      transdate      => $cb_date,
+      ob_transaction => 0,
+      cb_transaction => 1,
+      chart_id       => $pl_chart->id,
+      chart_link     => $pl_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => $pl_debit_balance,
+    );
+    my $credit_cb_acc = SL::DB::AccTransaction->new(
+      transdate      => $cb_date,
+      ob_transaction => 0,
+      cb_transaction => 1,
+      chart_id       => $pl_chart->id,
+      chart_link     => $pl_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => - $pl_credit_balance,
+    );
+    $pl_cb_debit_entry->add_transactions($debit_cb_acc);
+    $pl_cb_credit_entry->add_transactions($credit_cb_acc);
+
+    $pl_cb_debit_entry->save  if scalar @{ $pl_cb_debit_entry->transactions }  > 1;
+    $pl_cb_credit_entry->save if scalar @{ $pl_cb_credit_entry->transactions } > 1;
+
+    ######### profit-loss transfer #########
+    # and finally transfer the new balance of the profit-loss account via the carry-over account
+    # we want to use profit_loss_sum with cb!
+
+    my $carry_over_cb_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $cb_date,
+      reference      => 'SB ' . $cb_date->year,
+      description    => sprintf('Automatische SB-Buchung für %s %s',
+                                $profit_loss_sum >= 0 ? 'Gewinnvortrag' : 'Verlustvortrag',
+                                $cb_date->year,
+                               ),
+      ob_transaction => 0,
+      cb_transaction => 1,
+    );
+    my $carry_over_ob_entry = SL::DB::GLTransaction->new(
+      employee_id    => $employee_id,
+      transdate      => $ob_date,
+      reference      => 'EB ' . $ob_date->year,
+      description    => sprintf('Automatische EB-Buchung für %s %s',
+                                $profit_loss_sum >= 0 ? 'Gewinnvortrag' : 'Verlustvortrag',
+                                $ob_date->year,
+                               ),
+      ob_transaction => 1,
+      cb_transaction => 0,
+    );
+    $carry_over_cb_entry->transactions([]);
+    $carry_over_ob_entry->transactions([]);
+
+    my $carry_over_cb_acc_co = SL::DB::AccTransaction->new(
+      transdate      => $cb_date,
+      ob_transaction => 0,
+      cb_transaction => 1,
+      chart_id       => $carry_over_chart->id,
+      chart_link     => $carry_over_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => $profit_loss_sum,
+    );
+    my $carry_over_cb_acc_pl = SL::DB::AccTransaction->new(
+      transdate      => $cb_date,
+      ob_transaction => 0,
+      cb_transaction => 1,
+      chart_id       => $pl_chart->id,
+      chart_link     => $pl_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => - $profit_loss_sum,
+    );
 
-  for my $bar ($::request->layout->get('actionbar')) {
-    $bar->add(
-      action => [
-        t8('Continue'),
-        submit    => [ '#filter_form', { action => 'YearEndTransactions/list' } ],
-        accesskey => 'enter',
-      ],
+    $carry_over_cb_entry->add_transactions($carry_over_cb_acc_co);
+    $carry_over_cb_entry->add_transactions($carry_over_cb_acc_pl);
+    $carry_over_cb_entry->save if $profit_loss_sum != 0;
+
+    my $carry_over_ob_acc_co = SL::DB::AccTransaction->new(
+      transdate      => $ob_date,
+      ob_transaction => 1,
+      cb_transaction => 0,
+      chart_id       => $pl_chart->id,
+      chart_link     => $pl_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => $profit_loss_sum,
     );
-  }
+    my $carry_over_ob_acc_pl = SL::DB::AccTransaction->new(
+      transdate      => $ob_date,
+      ob_transaction => 1,
+      cb_transaction => 0,
+      chart_id       => $carry_over_chart->id,
+      chart_link     => $carry_over_chart->link,
+      tax_id         => 0,
+      taxkey         => 0,
+      amount         => - $profit_loss_sum,
+    );
+
+    $carry_over_ob_entry->add_transactions($carry_over_ob_acc_co);
+    $carry_over_ob_entry->add_transactions($carry_over_ob_acc_pl);
+    $carry_over_ob_entry->save if $profit_loss_sum != 0;
+
+    my $consistency_query = <<SQL;
+select sum(amount)
+  from acc_trans
+ where     (ob_transaction is true or cb_transaction is true)
+       and (transdate = ? or transdate = ?)
+SQL
+     my ($sum) = my ($empty) = selectrow_query($::form, $db->dbh, $consistency_query,
+                                               $cb_date,
+                                               $ob_date
+                                              );
+     die "acc_trans transactions don't add up to zero" unless $sum == 0;
+
+    1;
+  }) or die $db->error;
 }
 
-sub setup_list_action_bar {
-  my ($self) = @_;
+sub _report {
+  my (%params) = @_;
+
+  my $start_date = delete $params{start_date};
+  my $cb_date    = delete $params{cb_date};
+
+  my $defaults = SL::DB::Default->get;
+  die "no carry over account defined"
+    unless defined $defaults->carry_over_account_chart_id
+           and $defaults->carry_over_account_chart_id > 0;
+
+  my $salden_query = <<SQL;
+select c.id as chart_id,
+       c.accno,
+       c.description,
+       c.link as chart_link,
+       c.category,
+       sum(a.amount) filter (where cb_transaction is false and ob_transaction is false) as amount,
+       sum(a.amount) filter (where ob_transaction is true                             ) as ob_amount,
+       sum(a.amount) filter (where cb_transaction is false                            ) as amount_without_cb,
+       sum(a.amount) filter (where cb_transaction is true                             ) as cb_amount,
+       sum(a.amount)                                                                    as amount_with_cb,
+       case when c.category = ANY( '{I,E}'     ) then 'profit_loss_account'
+            when c.category = ANY( '{A,C,L,Q}' ) then 'asset_account'
+                                                 else null
+            end                                                                         as account_type
+  from acc_trans a
+       inner join chart c on (c.id = a.chart_id)
+ where     a.transdate >= ?
+       and a.transdate <= ?
+       and a.chart_id != ?
+ group by c.id, c.accno, c.category
+ order by account_type, c.accno
+SQL
+
+  my $dbh = SL::DB->client->dbh;
+  my $report = selectall_hashref_query($::form, $dbh, $salden_query,
+                                       $start_date,
+                                       $cb_date,
+                                       $defaults->carry_over_account_chart_id,
+                                      );
+  # profit_loss_sum is the actual profit/loss for the year, without cb, use "amount_without_cb")
+  my $profit_loss_sum = sum map { $_->{amount_without_cb} }
+                            grep { $_->{account_type} eq 'profit_loss_account' }
+                            @{$report};
+
+  return ($report, $profit_loss_sum);
+}
 
-  for my $bar ($::request->layout->get('actionbar')) {
-    $bar->add(
-      action => [
-        t8('Post'),
-        submit    => [ '#form', { action => 'YearEndTransactions/generate' } ],
-        tooltip   => t8('generate cb/ob transactions for selected charts'),
-        confirm   => t8('Are you sure to generate cb/ob transactions?'),
-        accesskey => 'enter',
-      ],
-      action => [
-        t8('Back'),
-        call => [ 'kivi.history_back' ],
-      ],
-    );
-  }
+#
+# auth
+#
+
+sub check_auth {
+  $::auth->assert('general_ledger');
 }
 
+
+#
+# inits
+#
+
+sub init_ob_date        { $::locale->parse_date_to_object($::form->{ob_date})      }
+sub init_cb_startdate   { $::locale->parse_date_to_object($::form->{cb_startdate}) }
+sub init_cb_date        { $::locale->parse_date_to_object($::form->{cb_date})      }
+
 1;
index e588b3a..9ad1109 100644 (file)
@@ -30,6 +30,7 @@ __PACKAGE__->meta->columns(
   bin_id                                    => { type => 'integer' },
   bin_id_ignore_onhand                      => { type => 'integer' },
   businessnumber                            => { type => 'text' },
+  carry_over_account_chart_id               => { type => 'integer' },
   closedto                                  => { type => 'date' },
   cnnumber                                  => { type => 'text' },
   co_ustid                                  => { type => 'text' },
@@ -100,6 +101,7 @@ __PACKAGE__->meta->columns(
   itime                                     => { type => 'timestamp', default => 'now()' },
   language_id                               => { type => 'integer' },
   letternumber                              => { type => 'integer' },
+  loss_carried_forward_chart_id             => { type => 'integer' },
   max_future_booking_interval               => { type => 'integer', default => 360 },
   mtime                                     => { type => 'timestamp' },
   normalize_part_descriptions               => { type => 'boolean', default => 'true' },
@@ -114,6 +116,7 @@ __PACKAGE__->meta->columns(
   pdonumber                                 => { type => 'text' },
   ponumber                                  => { type => 'text' },
   precision                                 => { type => 'numeric', default => '0.01', not_null => 1, precision => 15, scale => 5 },
+  profit_carried_forward_chart_id           => { type => 'integer' },
   profit_determination                      => { type => 'text' },
   project_status_id                         => { type => 'integer' },
   project_type_id                           => { type => 'integer' },
@@ -195,11 +198,26 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { bin_id_ignore_onhand => 'id' },
   },
 
+  carry_over_account_chart => {
+    class       => 'SL::DB::Chart',
+    key_columns => { carry_over_account_chart_id => 'id' },
+  },
+
   currency => {
     class       => 'SL::DB::Currency',
     key_columns => { currency_id => 'id' },
   },
 
+  loss_carried_forward_chart => {
+    class       => 'SL::DB::Chart',
+    key_columns => { loss_carried_forward_chart_id => 'id' },
+  },
+
+  profit_carried_forward_chart => {
+    class       => 'SL::DB::Chart',
+    key_columns => { profit_carried_forward_chart_id => 'id' },
+  },
+
   project_status => {
     class       => 'SL::DB::ProjectStatus',
     key_columns => { project_status_id => 'id' },
index f138d21..48d953d 100644 (file)
@@ -25,6 +25,10 @@ Mittelgroße neue Features:
   die aus Workflows aus dem Quellauftrag entstanden sind, gleich dem Betrag
   des Quellauftrags ist.
 
+- Der Jahresabschluß wurde komplett überarbeitet, es wird nun zwischen
+  Bestands- und Erfolgskonten unterschieden und ein Gewinn- bzw. Verlustvortrag
+  übertragen.
+
 Kleinere neue Features und Detailverbesserungen:
 
 - Mahnungen nach Abteilung filtern
index 918a788..564f94a 100755 (executable)
@@ -14,7 +14,6 @@ $self->{texts} = {
   ' bytes, max='                => ' Bytes, Maximum=',
   ' missing!'                   => ' fehlt!',
   '#1 (custom variable)'        => '#1 (benutzerdefinierte Variable)',
-  '#1 CB transactions and #1 OB transactions generated.' => '#1 Schluss- und #1 Eröffnungsbuchungen wurden erstellt.',
   '#1 MD'                       => '#1 PT',
   '#1 additional part(s)'       => '#1 zusätzliche(r) Artikel',
   '#1 bank transaction bookings undone.' => '#1 Bankbewegung(en) rückgängig gemacht',
@@ -307,12 +306,12 @@ $self->{texts} = {
   'Apply to transfers without bin' => 'Bei allen Lagerbewegungen ohne Lagerplatz setzen',
   'Apply to transfers without comment' => 'Bei allen Lagerbewegungen ohne Kommentar setzen',
   'Apply to transfers without warehouse' => 'Bei allen Lagerbewegungen ohne Lager setzen',
+  'Apply year-end bookings'     => 'Jahresabschlußbuchungen durchführen',
   'Applying #1:'                => 'Führe #1 aus:',
   'Approximately #1 prices will be updated.' => 'Ungefähr #1 Preise werden aktualisiert.',
   'Apr'                         => 'Apr',
   'April'                       => 'April',
   'Ar aging on %s'              => 'Offene Forderungen zum %s',
-  'Are you sure to generate cb/ob transactions?' => 'Sollen die EB/SB Buchungen wirklich erzeugt werden?',
   'Are you sure you want to delete Invoice Number' => 'Soll die Rechnung mit folgender Nummer wirklich gelöscht werden:',
   'Are you sure you want to delete this letter?' => 'Sind Sie sicher, dass Sie diesen Brief löschen wollen?',
   'Are you sure you want to remove the marked entries from the queue?' => 'Sind Sie sicher, dass die markierten Einträge von der Warteschlange gelöscht werden sollen?',
@@ -356,7 +355,6 @@ $self->{texts} = {
   'Attachment name'             => 'Name des Anhangs',
   'Attachments'                 => 'Dateianhänge',
   'Attempt to call an undefined sub named \'%s\'' => 'Es wurde versucht, eine nicht definierte Unterfunktion namens \'%s\' aufzurufen.',
-  'Attention: Here will be generated a lot of CB/OB transactions.' => 'Hiermit werden Buchungen für den Schlussbestand (SB-Buchung) und den Eröffnungsbestand (EB-Buchung) für mehrere Konten gleichzeitig gebucht.<br>In diesem Schritt wird das Datum der Buchungen sowie das Saldovortragskonto festgelegt.<br>Das Datum der SB-Buchung wird außerdem verwendet um das Saldo der Konten zu ermitteln, welche im nächsten Schritt (nach "Weiter") angezeigt werden.',
   'Audit Control'               => 'Bücherkontrolle',
   'Aug'                         => 'Aug',
   'August'                      => 'August',
@@ -389,8 +387,10 @@ $self->{texts} = {
   'Background jobs and task server' => 'Hintergrund-Jobs und Task-Server',
   'Balance'                     => 'Bilanz',
   'Balance Sheet'               => 'Bilanz',
+  'Balance accounts'            => 'Bestandskonten',
   'Balance sheet date'          => 'Bilanzstichtag',
   'Balance startdate method'    => 'Methode zur Ermittlung des Startdatums für Bilanz',
+  'Balance with CB'             => 'Saldo mit SB',
   'Balances'                    => 'Salden',
   'Balancing'                   => 'Bilanzierung',
   'Bank'                        => 'Bank',
@@ -495,8 +495,6 @@ $self->{texts} = {
   'CANCELED'                    => 'Storniert',
   'CB Transaction'              => 'SB-Buchung',
   'CB Transactions'             => 'SB-Buchungen',
-  'CB date #1 is higher than OB date #2. Please select again.' => 'SB-Datum #1 ist größer als EB-Datum #2. Bitte sinnvollere Werte auswählen.',
-  'CB/OB Transactions'          => 'SB/EB buchen',
   'CN'                          => 'Kd-Nr.',
   'CR'                          => 'H',
   'CSS style for pictures'      => 'CSS Style für Bilder',
@@ -569,6 +567,7 @@ $self->{texts} = {
   'Cannot transfer negative quantities.' => 'Negative Mengen können nicht ausgelagert werden.',
   'Cannot transfer. <br> Reason:<br>#1' => 'Kann nicht ein-/auslagern. <br>Grund:<br>#1',
   'Cannot unlink payment for a closed period!' => 'Ein oder alle Bankbewegungen befinden sich innerhalb einer geschloßenen Periode. ',
+  'Carry over account for year-end closing' => 'Saldenvortragskonto',
   'Carry over shipping address' => 'Lieferadresse übernehmen',
   'Cash'                        => 'Zahlungsverkehr',
   'Cash accounting'             => 'Ist-Versteuerung',
@@ -639,6 +638,7 @@ $self->{texts} = {
   'Close Window'                => 'Fenster Schließen',
   'Close window'                => 'Fenster schließen',
   'Closed'                      => 'Geschlossen',
+  'Closing Balance'             => 'Abschlußsaldo',
   'Collective Orders only work for orders from one customer!' => 'Sammelaufträge funktionieren nur für Aufträge von einem Kunden!',
   'Column name'                 => 'Spaltenname',
   'Comma'                       => 'Komma',
@@ -1263,6 +1263,7 @@ $self->{texts} = {
   'Error message from the database: #1' => 'Fehlermeldung der Datenbank: #1',
   'Error message from the webshop api:' => 'Fehlermeldung der Webshop Api',
   'Error when saving: #1'       => 'Fehler beim Speichern: #1',
+  'Error while applying year-end bookings!' => 'Fehler beim Durchführen der Abschlußbuchungen!',
   'Error with default taxzone'  => 'Ungültige Standardsteuerzone',
   'Error!'                      => 'Fehler!',
   'Error: #1'                   => 'Fehler: #1',
@@ -1846,6 +1847,8 @@ $self->{texts} = {
   'Long Description (quotations & orders)' => 'Langtext (Angebote & Aufträge)',
   'Long Description for invoices' => 'Langtext für Rechnungen',
   'Long Description for quotations & orders' => 'Langtext für Angebote & Aufträge',
+  'Loss'                        => 'Verlust',
+  'Loss carried forward account' => 'Verlustvortragskonto',
   'Luxembourg'                  => 'Luxemburg',
   'MAILED'                      => 'Gesendet',
   'MD'                          => 'PT',
@@ -1991,6 +1994,7 @@ $self->{texts} = {
   'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => 'Für diesen Kunden wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
   'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => 'Für diesen Lieferanten wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
   'No bins have been added to this warehouse yet.' => 'Es wurden zu diesem Lager noch keine Lagerplätze angelegt.',
+  'No carry-over chart configured!' => 'Kein Saldenvortragskonto konfiguriert!',
   'No changes since previous version.' => 'Keine Änderungen seit der letzten Version.',
   'No clients have been created yet.' => 'Es wurden noch keine Mandanten angelegt.',
   'No contact selected to delete' => 'Keine Ansprechperson zum Löschen ausgewählt',
@@ -2027,11 +2031,12 @@ $self->{texts} = {
   'No print templates have been created for this client yet. Please do so in the client configuration.' => 'Für diesen Mandanten wurden noch keine Druckvorlagen angelegt. Bitte holen Sie dies in der Mandantenkonfiguration nach.',
   'No printers have been created yet.' => 'Es wurden noch keine Drucker angelegt.',
   'No problems were recognized.' => 'Es wurden keine Probleme gefunden.',
+  'No profit and loss carried forward chart configured!' => 'Kein Verlustvortragskonto konfiguriert!',
+  'No profit carried forward chart configured!' => 'Kein Gewinnvortragskonto konfiguriert!',
   'No quotations or orders have been created yet.' => 'Es wurden noch keine Angebote oder Aufträge angelegt.',
   'No report with id #1'        => 'Es gibt keinen Report mit der Id #1',
   'No requirement spec templates have been created yet.' => 'Es wurden noch keine Pflichtenheftvorlagen angelegt.',
   'No results.'                 => 'Keine Artikel',
-  'No revert available.'        => 'Dieser Vorgang kann nicht rückgängig gemacht werden, ggf. falsch erstellte Buchungen müssen einzeln manuell korrigiert werden.',
   'No search results found!'    => 'Keine Suchergebnisse gefunden!',
   'No sections created yet'     => 'Keine Abschnitte erstellt',
   'No sections have been created so far.' => 'Bisher wurden noch keine Abschnitte angelegt.',
@@ -2107,7 +2112,6 @@ $self->{texts} = {
   'Number pages'                => 'Seiten nummerieren',
   'Number variables: \'PRECISION=n\' forces numbers to be shown with exactly n decimal places.' => 'Zahlenvariablen: Mit \'PRECISION=n\' erzwingt man, dass Zahlen mit n Nachkommastellen formatiert werden.',
   'OB Transaction'              => 'EB-Buchung',
-  'OB Transactions'             => 'EB-Buchungen',
   'Objects have been imported.' => 'Objekte wurden importiert.',
   'Obsolete'                    => 'Ungültig',
   'Oct'                         => 'Okt',
@@ -2120,8 +2124,6 @@ $self->{texts} = {
   'On Hand'                     => 'Auf Lager',
   'On Order'                    => 'Ist bestellt',
   'On the next page the type of all variables can be set.' => 'Auf der folgenden Seite können die Typen aller Variablen gesetzt werden.',
-  'One OB-transaction'          => 'Eine EB-Buchung',
-  'One SB-transaction'          => 'Eine SB-Buchung',
   'One of the columns "qty" or "target_qty" must be given. If "target_qty" is given, the quantity to transfer for each transfer will be calculate, so that the quantity for this part, warehouse and bin will result in the given "target_qty" after each transfer.' => 'Eine der Spalten "qty" oder "target_qty" muss angegeben werden. Wird "target_qty" angegeben, so wird die zu bewegende Menge für jede Lagerbewegung so berechnet, dass die Lagermenge für diesen Artikel, Lager und Lagerplatz nach jeder Lagerbewegung der angegebenen Zielmenge entspricht.',
   'One or more Perl modules missing' => 'Ein oder mehr Perl-Module fehlen',
   'Onhand only sets the quantity in master data, not in inventory. This is only a legacy info field and will be overwritten as soon as a inventory transfer happens.' => 'Das Import-Feld Auf Lager setzt nur die Menge in den Stammdaten, nicht im Lagerbereich. Dies ist historisch gewachsen nur ein Informationsfeld was mit dem tatsächlichen Wert überschrieben wird, sobald eine wirkliche Lagerbewegung stattfindet (DB-Trigger).',
@@ -2298,6 +2300,7 @@ $self->{texts} = {
   'Please choose a part.'       => 'Bitte wählen Sie einen Artikel aus.',
   'Please choose for which categories the taxes should be displayed (otherwise remove the ticks):' => 'Bitte wählen Sie für welche Kontoart die Steuer angezeigt werden soll (ansonsten einfach die Häkchen entfernen)',
   'Please choose the action to be processed for your target quantity:' => 'Bitte wählen Sie eine Aktion, die mit Ihrer gezählten Zielmenge durchgeführt werden soll:',
+  'Please configure the carry over and profit and loss accounts for year-end closing in the client configuration!' => 'Bitte konfigurieren Sie in der Mandantenkonfiguration das Saldenvortragskonto, das Gewinnvortragskonto und das Verlustvortragskonto!',
   'Please contact your administrator or a service provider.' => 'Bitte kontaktieren Sie Ihren Administrator oder einen Dienstleister.',
   'Please contact your administrator.' => 'Bitte wenden Sie sich an Ihren Administrator.',
   'Please correct the settings and try again or deactivate that client.' => 'Bitte korrigieren Sie die Einstellungen und versuchen Sie es erneut, oder deaktivieren Sie diesen Mandanten.',
@@ -2443,6 +2446,9 @@ $self->{texts} = {
   'Production'                  => 'Produktion',
   'Production (typeabbreviation)' => 'P',
   'Productivity'                => 'Produktivität',
+  'Profit'                      => 'Gewinn',
+  'Profit and loss accounts'    => 'Erfolgskonten',
+  'Profit carried forward account' => 'Gewinnvortragskonto',
   'Profit determination'        => 'Gewinnermittlung',
   'Proforma Invoice'            => 'Proformarechnung',
   'Program'                     => 'Programm',
@@ -2687,7 +2693,6 @@ $self->{texts} = {
   'SEPA strings'                => 'SEPA-Überweisungen',
   'SQL query'                   => 'SQL-Abfrage',
   'SWIFT MT940 format'          => 'SWIFT-MT940-Format',
-  'Saldo'                       => 'Saldo',
   'Saldo Credit'                => 'Saldo Haben',
   'Saldo Debit'                 => 'Saldo Soll',
   'Saldo neu'                   => 'Saldo neu',
@@ -2790,7 +2795,6 @@ $self->{texts} = {
   'Select Mulit-Item Options'   => 'Multi-Treffer Auswahlliste',
   'Select a Customer'           => 'Endkunde auswählen',
   'Select a period'             => 'Bitte Zeitraum auswählen',
-  'Select charts for which the CB/OB transactions want to be posted.' => 'Wählen Sie Konten aus, zu welchen SB/EB-Buchungen erstellt werden sollen.',
   'Select federal state...'     => 'Bundesland auswählen...',
   'Select file to upload'       => 'Datei zum Hochladen auswählen',
   'Select from one of the items below' => 'Wählen Sie einen der untenstehenden Einträge',
@@ -2996,6 +3000,7 @@ $self->{texts} = {
   'Start of year'               => 'Jahresanfang',
   'Start process'               => 'Prozess starten',
   'Start the correction assistant' => 'Korrekturassistenten starten',
+  'Startdate method'            => 'Methode zur Ermittlung des Startdatums',
   'Startdate_coa'               => 'Gültig ab',
   'Starting Balance'            => 'Eröffnungsbilanzwerte',
   'Starting balance'            => 'Anfangssaldo',
@@ -3050,10 +3055,8 @@ $self->{texts} = {
   'Subtotals per quarter'       => 'Zwischensummen pro Quartal',
   'Such entries cannot be exported into the DATEV format and have to be fixed as well.' => 'Solche Einträge sind aber nicht DATEV-exportiertbar und müssen ebenfalls korrigiert werden.',
   'Suggested invoice'           => 'Rechnungsvorschlag',
-  'Sum CB Transactions'         => 'Summe SB',
   'Sum Credit'                  => 'Summe Haben',
   'Sum Debit'                   => 'Summe Soll',
-  'Sum OB Transactions'         => 'Summe EB',
   'Sum for'                     => 'Summe für',
   'Sum for #1'                  => 'Summe für #1',
   'Sum for section'             => 'Summe für Abschnitt',
@@ -3512,7 +3515,6 @@ $self->{texts} = {
   'There was an error saving the draft' => 'Beim Speichern des Entwurfs ist ein Fehler aufgetretetn',
   'There was an error saving the letter' => 'Ein Fehler ist aufgetreten. Der Brief konnte nicht gespeichert werden.',
   'There was an error saving the letter draft' => 'Ein Fehler ist aufgetreten. Der Briefentwurf konnte nicht gespeichert werden.',
-  'There will be two transactions done:' => 'Zu jedem ausgewählten Konto werden jeweils zwei Buchungen erstellt:',
   'There you can let kivitendo create the basic tables for you, even in an already existing database.' => 'Dort können Sie kivitendo diese grundlegenden Tabellen erstellen lassen, selbst in einer bereits existierenden Datenbank.',
   'Therefore several settings that had to be made for each user in the past have been consolidated into the client configuration.' => 'Dazu wurden gewisse Einstellungen, die vorher bei jedem Benutzer vorgenommen werden mussten, in die Konfiguration eines Mandanten verschoben.',
   'Therefore the definition of "kg" with the base unit "g" and a factor of 1000 is valid while defining "g" with a base unit of "kg" and a factor of "0.001" is not.' => 'So ist die Definition von "kg" mit der Basiseinheit "g" und dem Faktor 1000 zulässig, die Definition von "g" mit der Basiseinheit "kg" und dem Faktor "0,001" hingegen nicht.',
@@ -3598,7 +3600,6 @@ $self->{texts} = {
   'Time estimate'               => 'Zeitschätzung',
   'Time period for the analysis:' => 'Analysezeitraum:',
   'Time/cost estimate actions'  => 'Aktionen für Kosten-/Zeitabschätzung',
-  'Timerange'                   => 'Zeitraum',
   'Timestamp'                   => 'Uhrzeit',
   'Tired of copying always nice phrases for this message? Click here to use the new preset message option!' => 'Müde vom vielen Copy & Paste aus vorherigen Anschreiben? Hier klicken, um E-Mail-Texte vorzudefinieren!',
   'Title'                       => 'Titel',
@@ -3904,6 +3905,10 @@ $self->{texts} = {
   'X'                           => 'X',
   'YYYY'                        => 'JJJJ',
   'Year'                        => 'Jahr',
+  'Year-end bookings were successfully completed!' => 'Die Jahresabschlußbuchungen wurden erfolgreich durchgeführt!',
+  'Year-end closing'            => 'Jahresabschluß',
+  'Year-end date'               => 'Jahresabschlußdatum',
+  'Year-end date missing'       => '',
   'Yearly'                      => 'jährlich',
   'Yearly taxreport not yet implemented' => 'Jährlicher Steuerreport für dieses Ausgabeformat noch nicht implementiert',
   'Yes'                         => 'Ja',
@@ -4005,7 +4010,6 @@ $self->{texts} = {
   'cleared'                     => 'Abgeglichen',
   'click here to edit cvars'    => 'Klicken Sie hier, um nach benutzerdefinierten Variablen zu suchen',
   'close'                       => 'schließen',
-  'close chart'                 => 'Saldovortragskonto',
   'closed'                      => 'geschlossen',
   'companylogo_subtitle'        => 'Lizenziert für',
   'config/kivitendo.conf: Key "DB_config" is missing.' => 'config/kivitendo.conf: Das Schlüsselwort "DB_config" fehlt.',
@@ -4065,7 +4069,6 @@ $self->{texts} = {
   'from \'#1\' imported Files'  => 'Von \'#1\' importierte Dateien',
   'from (time)'                 => 'von',
   'general_ledger_list'         => 'Buchungsjournal',
-  'generate cb/ob transactions for selected charts' => 'Start-/Endbuchungen für ausgewählte Konten erstellen',
   'generated Files'             => 'Erzeugte Dokumente',
   'gobd-#1-#2.zip'              => 'gobd-#1-#2.zip',
   'h'                           => 'h',
index 8c69e4b..d5eada3 100644 (file)
   params:
     action: GoBD/filter
 - parent: general_ledger
-  id: general_ledger_cbob_transactions
-  name: CB/OB Transactions
+  id: year_end_closing
+  name: Year-end closing
   icon: cbob
   order: 470
   access: general_ledger
   params:
-    action: YearEndTransactions/filter
+    action: YearEndTransactions/form
 - parent: general_ledger
   id: general_ledger_reports
   name: Reports
diff --git a/sql/Pg-upgrade2/defaults_year_end_charts.sql b/sql/Pg-upgrade2/defaults_year_end_charts.sql
new file mode 100644 (file)
index 0000000..128c3ff
--- /dev/null
@@ -0,0 +1,7 @@
+-- @tag: defaults_year_end_charts
+-- @description: Standardkonten für Jahresabschluß
+-- @depends: release_3_5_4
+
+ALTER TABLE defaults ADD COLUMN carry_over_account_chart_id     INTEGER REFERENCES chart(id);
+ALTER TABLE defaults ADD COLUMN profit_carried_forward_chart_id INTEGER REFERENCES chart(id);
+ALTER TABLE defaults ADD COLUMN loss_carried_forward_chart_id   INTEGER REFERENCES chart(id);
index 1666678..35a8a3a 100644 (file)
    <td>[% P.chart.picker('defaults.workflow_po_ap_chart_id', SELF.defaults.workflow_po_ap_chart_id, type='AP_amount', choose=1, style=style) %]<td>
   </tr>
 
+  <tr>
+    <th align="right">[% LxERP.t8("Year-end closing") %]</th>
+  </tr>
+  <tr>
+   <td align="right">[% LxERP.t8("Carry over account for year-end closing") %]</td>
+   <td>[% P.chart.picker('defaults.carry_over_account_chart_id', SELF.defaults.carry_over_account_chart_id, category='A', choose=1, style=style) %]<td>
+  </tr>
+
+  <tr>
+   <td align="right">[% LxERP.t8("Profit carried forward account") %]</td>
+   <td>[% P.chart.picker('defaults.profit_carried_forward_chart_id', SELF.defaults.profit_carried_forward_chart_id, category='A', choose=1, style=style) %]<td>
+  </tr>
+
+  <tr>
+   <td align="right">[% LxERP.t8("Loss carried forward account") %]</td>
+   <td>[% P.chart.picker('defaults.loss_carried_forward_chart_id', SELF.defaults.loss_carried_forward_chart_id, category='A', choose=1, style=style) %]<td>
+  </tr>
 </table>
 </div>
diff --git a/templates/webpages/gl/yearend_bottom.html b/templates/webpages/gl/yearend_bottom.html
deleted file mode 100644 (file)
index 1a8fb82..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[%- USE L %]
-[%- USE LxERP %]
-    [%- L.hidden_tag("cb_date",SELF.cb_date.to_kivitendo) %]
-    [%- L.hidden_tag("cb_startdate",SELF.cb_startdate.to_kivitendo) %]
-    [%- L.hidden_tag("cb_reference",SELF.cb_reference) %]
-    [%- L.hidden_tag("cb_description",SELF.cb_description) %]
-    [%- L.hidden_tag("ob_date",SELF.ob_date.to_kivitendo) %]
-    [%- L.hidden_tag("ob_reference",SELF.ob_reference) %]
-    [%- L.hidden_tag("ob_description",SELF.ob_description) %]
-    [%- L.hidden_tag("cbob_chart",SELF.cbob_chart) %]
-    [%- L.hidden_tag("rowcount",SELF.row_count) %]
-</form>
diff --git a/templates/webpages/gl/yearend_filter.html b/templates/webpages/gl/yearend_filter.html
deleted file mode 100644 (file)
index 566454d..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-[%- USE HTML %]
-[%- USE T8 %]
-[%- USE L %]
-[%- USE LxERP %]
-
-<h1>[% title | html %]</h1>
-
-[%- INCLUDE 'common/flash.html' %]
-
-<form id='filter_form'>
-
-<table>
-  <tr>
-    <td width="20px"></td>
-    <td align="left" colspan="5">[% 'Attention: Here will be generated a lot of CB/OB transactions.' | $T8 %]</td>
-  </tr>
-  <tr>
-    <td><p></p></td>
-  </tr>
-  <tr>
-    <th></th>
-    <th width="400px" colspan="2" align="center">[% 'CB Transactions' | $T8 %]</th>
-    <th width="400px"colspan="2" align="center">[% 'OB Transactions' | $T8 %]</th>
-    <th>&nbsp;</th>
-  </tr>
-  <tr>
-    <td></td>
-    <td align="right">[% 'Date' | $T8 %]</td>
-    <td>[% L.date_tag('cb_date', SELF.cb_date) %]</td>
-    <td align="right">[% 'Date' | $T8 %]</td>
-    <td>[% L.date_tag('ob_date', SELF.ob_date) %]</td>
-    <td></td>
-  </tr>
-  <tr>
-    <td></td>
-    <td align="right">[% 'Reference' | $T8 %]</td>
-    <td>[% L.input_tag('cb_reference', SELF.cb_reference) %]</td>
-    <td align="right">[% 'Reference' | $T8 %]</td>
-    <td>[% L.input_tag('ob_reference', SELF.ob_reference) %]</td>
-    <td></td>
-  </tr>
-  <tr>
-    <td></td>
-    <td align="right">[% 'Description' | $T8 %]</td>
-    <td>[% L.input_tag('cb_description', SELF.cb_description) %]</td>
-    <td align="right">[% 'Description' | $T8 %]</td>
-    <td>[% L.input_tag('ob_description', SELF.ob_description) %]</td>
-    <td></td>
-  </tr>
-  <tr>
-    <td><p></p></td>
-  </tr>
-  <tr>
-    <th colspan="2"></th>
-    <th align=right>[% 'close chart' | $T8 %]</th>
-    <td colspan="3">[% L.select_tag('cbob_chart', SELF.charts9000, title_sub=\make_title_of_chart, default=SELF.cbob_chart, style="width: 400px") %]</td>
-  </tr>
-</table>
-</form>
diff --git a/templates/webpages/gl/yearend_top.html b/templates/webpages/gl/yearend_top.html
deleted file mode 100644 (file)
index 59cca0e..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-[%- USE LxERP %]
-<form method="post" action="controller.pl" id="form">
-<table>
-  <tr>
-    <td width="20px"></td>
-    <td colspan="6" align="left">
-      [%- LxERP.t8('Select charts for which the CB/OB transactions want to be posted.') %]<br>
-      [%- LxERP.t8('There will be two transactions done:') %]<br>
-     - [%- LxERP.t8('One SB-transaction') %] ( [% SELF.cb_date.to_kivitendo %], [% SELF.cb_reference %], [% SELF.cb_description %], [% SELF.cbob_chartaccno %] )<br>
-     - [%- LxERP.t8('One OB-transaction') %] ( [% SELF.ob_date.to_kivitendo %], [% SELF.ob_reference %], [% SELF.ob_description %], [% SELF.cbob_chartaccno %] )<br>
-      [%- LxERP.t8('No revert available.') %]
-    </td>
-  </tr>
-  <tr>
-    <td><p></p></td>
-  </tr>
-  <tr>
-    <th></th>
-    <th align="right">[% LxERP.t8('close chart') %]</th>
-    <td>[% SELF.cbob_chartaccno %]</td>
-  </tr>
-</table>
diff --git a/templates/webpages/yearend/_charts.html b/templates/webpages/yearend/_charts.html
new file mode 100644 (file)
index 0000000..20f7621
--- /dev/null
@@ -0,0 +1,79 @@
+[%- USE LxERP -%]
+[%- USE T8    -%]
+[%- USE L     -%]
+[%- USE HTML  -%]
+[%- USE P     -%]
+
+
+[%- SET dec = 2 %]
+
+<h2>[% 'Balance accounts' | $T8 %]</h2>
+<table cellpadding="3px">
+ <tr class="listheading">
+  <th            >[%- 'Account'          | $T8 %]</th>
+  <th            >[%- 'Description'      | $T8 %]</th>
+  <th colspan="2">[%- 'Starting Balance' | $T8 %]</th>
+  <th colspan="2">[%- 'Balance with CB'  | $T8 %]</th>
+  <th colspan="2">[%- 'Closing Balance'  | $T8 %]</th>
+ </tr>
+ <tr class="listheading">
+  <th></th>
+  <th></th>
+  <th>[%- 'Debit'  | $T8 %]</th>
+  <th>[%- 'Credit' | $T8 %]</th>
+  <th>[%- 'Debit'  | $T8 %]</th>
+  <th>[%- 'Credit' | $T8 %]</th>
+  <th>[%- 'Debit'  | $T8 %]</th>
+  <th>[%- 'Credit' | $T8 %]</th>
+ </tr>
+ [% FOREACH chart = charts %]
+   [%- NEXT UNLESS chart.account_type == 'asset_account' -%]
+ <tr id="tr_[% loop.count %]" class="listrow[% loop.count % 2 %]">
+  <td>                 [% chart.accno | html %]</td>
+  <td>                 [% chart.description | html %]</td>
+  <td class="numeric"> [% IF chart.ob_amount < 0      %]  [% LxERP.format_amount(chart.ob_amount * -1, dec)       %] [% END %]</td>
+  <td class="numeric"> [% IF chart.ob_amount > 0      %]  [% LxERP.format_amount(chart.ob_amount, dec)            %] [% END %]</td>
+  <td class="numeric"> [% IF chart.amount_with_cb < 0 %]  [% LxERP.format_amount(chart.amount_with_cb * -1, dec)  %] [% END %]</td>
+  <td class="numeric"> [% IF chart.amount_with_cb > 0 %]  [% LxERP.format_amount(chart.amount_with_cb, dec)       %] [% END %]</td>
+  [% # cb amounts: >/< are switched and cb_amounts are multiplied with -1. The closing balance as calculated by cb_amount negates the actual balance, but when displaying it as the closing balance we want to display it in the same form as the actual balance %]
+  <td class="numeric"> [% IF chart.cb_amount > 0 %]  [% LxERP.format_amount(chart.cb_amount *  1, dec) %] [% END %]</td>
+  <td class="numeric"> [% IF chart.cb_amount < 0 %]  [% LxERP.format_amount(chart.cb_amount * -1, dec) %] [% END %]</td>
+ </tr>
+ [% END %]
+</table>
+
+<h2>[% 'Profit and loss accounts' | $T8 %]</h2>
+
+<p>
+[% IF profit_loss_sum < 0 %] [% THEN %][% 'Loss' | $T8 %] [% ELSE %] [% 'Profit' | $T8 %] [% END %]:   
+[% LxERP.format_amount(profit_loss_sum, dec) %]
+</p>
+
+<table cellpadding="3px">
+ <tr class="listheading">
+  <th          >[%- 'Account'         | $T8 %]</th>
+  <th          >[%- 'Description'     | $T8 %]</th>
+  <th colspan=2>[%- 'Balance with CB' | $T8 %]</th>
+  <th colspan=2>[%- 'Closing Balance' | $T8 %]</th>
+ </tr>
+ <tr class="listheading">
+  <th></th>
+  <th></th>
+  <th>[%- 'Debit'  | $T8 %]</th>
+  <th>[%- 'Credit' | $T8 %]</th>
+  <th>[%- 'Debit'  | $T8 %]</th>
+  <th>[%- 'Credit' | $T8 %]</th>
+ </tr>
+ [% FOREACH chart = charts %]
+   [%- NEXT UNLESS chart.account_type == 'profit_loss_account' -%]
+ <tr id="tr_[% loop.count %]" class="listrow[% loop.count % 2 %]">
+  <td                >[% chart.accno | html %]</td>
+  <td                >[% chart.description | html %]</td>
+  <td class="numeric">[% IF chart.amount_with_cb < 0 %] [% LxERP.format_amount(chart.amount_with_cb * -1, dec) %] [% END %]</td>
+  <td class="numeric">[% IF chart.amount_with_cb > 0 %] [% LxERP.format_amount(chart.amount_with_cb, dec)      %] [% END %]</td>
+  <td class="numeric">[% IF chart.cb_amount > 0 %] [% LxERP.format_amount(chart.cb_amount *  1, dec) %] [% END %]</td>
+  <td class="numeric">[% IF chart.cb_amount < 0 %] [% LxERP.format_amount(chart.cb_amount * -1, dec)      %] [% END %]</td>
+ </tr>
+ [% END %]
+</table>
+[% # L.dump(charts) %]
diff --git a/templates/webpages/yearend/form.html b/templates/webpages/yearend/form.html
new file mode 100644 (file)
index 0000000..d28dfc1
--- /dev/null
@@ -0,0 +1,98 @@
+[%- USE HTML %]
+[%- USE T8 %]
+[%- USE L %]
+[%- USE LxERP %]
+
+<h1>[% title | html %]</h1>
+
+[%- INCLUDE 'common/flash.html' %]
+
+[% IF carry_over_chart AND profit_chart AND loss_chart %] [% THEN %]
+<form id="filter" name="filter" method="post" action="controller.pl">
+<table>
+  <tr>
+    <td align="right">[% 'Year-end date' | $T8 %]</td>
+    <td>[% L.date_tag('cb_date', SELF.cb_date) %]</td>
+  </tr>
+  <tr class="startdate">
+   <td align="right">[% 'Startdate method' | $T8 %]</td>
+   <td>[% L.select_tag('balance_startdate_method', balance_startdate_method_options, value_key = 'value', title_key = 'title') %]</td>
+  </tr>
+  <tr class="startdate">
+    <td align="right">[% 'Start date' | $T8 %]</td>
+    <td>[% L.date_tag('cb_startdate', '', readonly=1) %]</td>
+  </tr>
+  <tr>
+    <td align="right">[% 'Carry over account for year-end closing' | $T8 %]</td>
+    <td>[% carry_over_chart.displayable_name | html %]</td>
+  </tr>
+  <tr>
+    <td align="right">[% 'Profit carried forward account' | $T8 %]</td>
+    <td>[% profit_chart.displayable_name | html %]</td>
+  </tr>
+  <tr>
+    <td align="right">[% 'Loss carried forward account' | $T8 %]</td>
+    <td>[% loss_chart.displayable_name | html %]</td>
+  </tr>
+</table>
+</form>
+[% ELSE %]
+  [% 'Please configure the carry over and profit and loss accounts for year-end closing in the client configuration!' | $T8 %]
+[% END %]
+
+[% # L.button_tag("refresh_charts();", LxERP.t8("Preview")) %]
+[% L.button_tag("year_end_bookings();", LxERP.t8("Apply year-end bookings"), id='apply_year_end_bookings_button', confirm=LxERP.t8("Are you sure?")) %]
+
+<div id="charts" style="padding-top: 20px">
+</div>
+
+<script type="text/javascript">
+
+  function get_startdate() {
+    $.get("controller.pl", {
+      action:                   'YearEndTransactions/get_start_date',
+      cb_date:                  $('#cb_date').val(),
+      balance_startdate_method: $('#balance_startdate_method').val()
+    }, kivi.eval_json_result)
+  }
+
+  function year_end_bookings() {
+    $.post("controller.pl", {
+      action:  'YearEndTransactions/year_end_bookings',
+      cb_date: $('#cb_date').val(),
+    }, kivi.eval_json_result)
+  }
+
+  function refresh_charts() {
+    var filterdata = $('#filter').serialize()
+    var url = './controller.pl?action=YearEndTransactions/update_charts&' + filterdata;
+    $.ajax({
+       url : url,
+       type: 'GET',
+       success: function(data){
+           $('#charts').html(data);
+       }
+    })
+  };
+
+$(function(){
+
+  $('#apply_year_end_bookings_button').hide();
+  $('.startdate').hide();
+
+  $('#balance_startdate_method').change(function(){
+    get_startdate();
+    setTimeout(function() {
+      refresh_charts();
+    }, 200);    
+  });
+
+  $('#cb_date').change(function(){
+    get_startdate();
+    setTimeout(function() {
+      refresh_charts();
+    }, 200);    
+  });
+})
+
+</script>