4 use Test::More tests => 18;
 
  10 use Support::TestSetup;
 
  12 use SL::DBUtils qw(selectall_hashref_query);
 
  14 use SL::DB::BankAccount;
 
  17 use SL::DB::PurchaseInvoice;
 
  19 use SL::Dev::Record qw(create_ar_transaction create_ap_transaction create_gl_transaction);
 
  21 use SL::Controller::YearEndTransactions;
 
  23 Support::TestSetup::login();
 
  29 # * in the default test client the tax accounts are configured as I/E rather than A/L
 
  30 # * also the default test client has the accounting method "cash" rather than "accrual"
 
  31 #   (Ist-versteuerung, rather than Soll-versteuerung)
 
  33 # use 2019 instead of 2020 because of tax changes in Germany (19/16 and 7/5) because we check for account sums
 
  35 my $start_of_year = DateTime->new(year => $year, month => 01, day => 01);
 
  36 my $booking_date  = DateTime->new(year => $year, month => 12, day => 22);
 
  38 note('configuring accounts');
 
  39 my $bank_account = SL::DB::BankAccount->new(
 
  40   account_number  => '123',
 
  45   chart_id        => SL::DB::Manager::Chart->find_by(description => 'Bank')->id,
 
  46   name            => SL::DB::Manager::Chart->find_by(description => 'Bank')->description,
 
  49 my $profit_account = SL::DB::Manager::Chart->find_by(accno => '0890') //
 
  52                        description    => 'Gewinnvortrag vor Verwendung',
 
  57                        datevautomatik => 'f',
 
  60 my $loss_account = SL::DB::Manager::Chart->find_by(accno => '0868') //
 
  63                      description    => 'Verlustvortrag vor Verwendung',
 
  68                      datevautomatik => 'f',
 
  71 my $carry_over_chart = SL::DB::Manager::Chart->find_by(accno => 9000); 
 
  72 my $income_chart     = SL::DB::Manager::Chart->find_by(accno => '8400'); # income 19%, taxkey 3
 
  73 my $bank             = SL::DB::Manager::Chart->find_by(description => 'Bank');
 
  74 my $cash             = SL::DB::Manager::Chart->find_by(description => 'Kasse');
 
  75 my $privateinlagen   = SL::DB::Manager::Chart->find_by(description => 'Privateinlagen');
 
  76 my $betriebsbedarf   = SL::DB::Manager::Chart->find_by(description => 'Betriebsbedarf'); 
 
  78 my $dbh = SL::DB->client->dbh;
 
  79 $dbh->do('UPDATE defaults SET carry_over_account_chart_id     = ' . $carry_over_chart->id);
 
  80 $dbh->do('UPDATE defaults SET profit_carried_forward_chart_id = ' . $profit_account->id);
 
  81 $dbh->do('UPDATE defaults SET loss_carried_forward_chart_id   = ' . $loss_account->id);
 
  84 note('creating transactions');
 
  85 my $ar_transaction = create_ar_transaction(
 
  87   transdate   => $booking_date,
 
  90                      chart  => $income_chart, # income 19%, taxkey 3
 
  96 $ar_transaction->pay_invoice(
 
  97                               chart_id     => $bank_account->chart_id,
 
  98                               amount       => $ar_transaction->amount,
 
  99                               transdate    => $booking_date,
 
 100                               payment_type => 'without_skonto',
 
 103 my $ar_transaction2 = create_ar_transaction(
 
 105   transdate   => $booking_date,
 
 108                      chart  => $income_chart, # income 19%, taxkey 3
 
 114 my $ap_transaction = create_ap_transaction(
 
 116   transdate   => $booking_date,
 
 119                      chart  => SL::DB::Manager::Chart->find_by( accno => '3400' ), # Wareneingang 19%, taxkey 9
 
 125 gl_booking(40, $start_of_year, 'foo', 'bar', $bank, $privateinlagen, 1, 0);
 
 127 is(SL::DB::Manager::AccTransaction->get_all_count(                                ), 13, 'acc_trans transactions created ok');
 
 128 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ ob_transaction => 1 ]),  2, 'acc_trans ob_transactions created ok');
 
 129 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ cb_transaction => 1 ]),  0, 'no cb_transactions created ok');
 
 131 is_deeply( &get_account_balances, 
 
 135                'account_type' => 'asset_account',
 
 136                'sum'          => '-206.60000'
 
 140                'account_type' => 'asset_account',
 
 141                'sum'          => '-166.60000'
 
 145                'account_type' => 'asset_account',
 
 150                'account_type' => 'asset_account',
 
 155                'account_type' => 'profit_loss_account',
 
 160                'account_type' => 'profit_loss_account',
 
 165                'account_type' => 'profit_loss_account',
 
 166                'sum'          => '-100.00000'
 
 170                'account_type' => 'profit_loss_account',
 
 174            'account balances before year_end bookings ok',
 
 177 #  accno |    account_type     |    sum     
 
 178 # -------+---------------------+------------
 
 179 #  1200  | asset_account       | -206.60000
 
 180 #  1400  | asset_account       | -166.60000
 
 181 #  1600  | asset_account       |  119.00000
 
 182 #  1890  | asset_account       |   40.00000
 
 183 #  1576  | profit_loss_account |  -19.00000
 
 184 #  1776  | profit_loss_account |   53.20000
 
 185 #  3400  | profit_loss_account | -100.00000
 
 186 #  8400  | profit_loss_account |  280.00000
 
 189 note('running year-end transactions');
 
 190 my $start_date = DateTime->new(year => $year, month => 1,  day => 1);  
 
 191 my $cb_date    = DateTime->new(year => $year, month => 12, day => 31);
 
 192 my $ob_date    = $cb_date->clone->add(days => 1);
 
 194 SL::Controller::YearEndTransactions::_year_end_bookings( start_date => $start_date,
 
 198 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ cb_transaction => 1 ]), 14, 'acc_trans cb_transactions created ok');
 
 199 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ ob_transaction => 1 ]), 10, 'acc_trans ob_transactions created ok');
 
 200 is(SL::DB::Manager::GLTransaction->get_all_count( where => [ cb_transaction => 1 ]),  5, 'GL cb_transactions created ok');
 
 201 is(SL::DB::Manager::GLTransaction->get_all_count( where => [ ob_transaction => 1 ]),  4, 'GL ob_transactions created ok');
 
 203 my $final_account_balances = [
 
 207                                  'amount_with_cb' => '0.00000',
 
 209                                  'cb_amount' => '0.00000',
 
 210                                  'ob_amount' => undef,
 
 211                                  'ob_next_year' => '214.20000',
 
 213                                  'year_end_amount' => undef
 
 217                                  'amount' => '-166.60000',
 
 218                                  'amount_with_cb' => '0.00000',
 
 220                                  'cb_amount' => '-206.60000',
 
 221                                  'ob_amount' => '-40.00000',
 
 222                                  'ob_next_year' => '-206.60000',
 
 224                                  'year_end_amount' => '-206.60000'
 
 228                                  'amount' => '-166.60000',
 
 229                                  'amount_with_cb' => '0.00000',
 
 231                                  'cb_amount' => '-166.60000',
 
 232                                  'ob_amount' => undef,
 
 233                                  'ob_next_year' => '-166.60000',
 
 235                                  'year_end_amount' => '-166.60000'
 
 239                                  'amount' => '119.00000',
 
 240                                  'amount_with_cb' => '0.00000',
 
 242                                  'cb_amount' => '119.00000',
 
 243                                  'ob_amount' => undef,
 
 244                                  'ob_next_year' => '119.00000',
 
 246                                  'year_end_amount' => '119.00000'
 
 251                                  'amount_with_cb' => '0.00000',
 
 253                                  'cb_amount' => '40.00000',
 
 254                                  'ob_amount' => '40.00000',
 
 255                                  'ob_next_year' => '40.00000',
 
 257                                  'year_end_amount' => '40.00000'
 
 262                                  'amount_with_cb' => '0.00000',
 
 264                                  'cb_amount' => '0.00000',
 
 265                                  'ob_amount' => undef,
 
 266                                  'ob_next_year' => '0.00000',
 
 268                                  'year_end_amount' => undef
 
 272                                  'amount' => '-19.00000',
 
 273                                  'amount_with_cb' => '0.00000',
 
 275                                  'cb_amount' => '-19.00000',
 
 276                                  'ob_amount' => undef,
 
 277                                  'ob_next_year' => undef,
 
 279                                  'year_end_amount' => '-19.00000'
 
 283                                  'amount' => '53.20000',
 
 284                                  'amount_with_cb' => '0.00000',
 
 286                                  'cb_amount' => '53.20000',
 
 287                                  'ob_amount' => undef,
 
 288                                  'ob_next_year' => undef,
 
 290                                  'year_end_amount' => '53.20000'
 
 294                                  'amount' => '-100.00000',
 
 295                                  'amount_with_cb' => '0.00000',
 
 297                                  'cb_amount' => '-100.00000',
 
 298                                  'ob_amount' => undef,
 
 299                                  'ob_next_year' => undef,
 
 301                                  'year_end_amount' => '-100.00000'
 
 305                                  'amount' => '280.00000',
 
 306                                  'amount_with_cb' => '0.00000',
 
 308                                  'cb_amount' => '280.00000',
 
 309                                  'ob_amount' => undef,
 
 310                                  'ob_next_year' => undef,
 
 312                                  'year_end_amount' => '280.00000'
 
 316 # running _year_end_bookings several times shouldn't change the anything, the
 
 317 # second and third run should be no-ops, at least while no further bookings where
 
 320 SL::Controller::YearEndTransactions::_year_end_bookings( start_date => $start_date,
 
 324 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ cb_transaction => 1 ]), 14, 'acc_trans cb_transactions created ok');
 
 325 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ ob_transaction => 1 ]), 10, 'acc_trans ob_transactions created ok');
 
 326 is(SL::DB::Manager::GLTransaction->get_all_count( where => [ cb_transaction => 1 ]),  5, 'GL cb_transactions created ok');
 
 327 is(SL::DB::Manager::GLTransaction->get_all_count( where => [ ob_transaction => 1 ]),  4, 'GL ob_transactions created ok');
 
 330 # all asset accounts should be the same, except 0890, which should be the sum of p/l-accounts
 
 331 # all p/l account should be 0
 
 333 #  accno |    account_type     |    sum     
 
 334 # -------+---------------------+------------
 
 335 #  0890  | asset_account       |  214.20000
 
 336 #  1200  | asset_account       | -206.60000
 
 337 #  1400  | asset_account       | -166.60000
 
 338 #  1600  | asset_account       |  119.00000
 
 339 #  1890  | asset_account       |   40.00000
 
 340 #  9000  | asset_account       |    0.00000
 
 341 #  1576  | profit_loss_account |    0.00000
 
 342 #  1776  | profit_loss_account |    0.00000
 
 343 #  3400  | profit_loss_account |    0.00000
 
 344 #  8400  | profit_loss_account |    0.00000
 
 347 is_deeply( &get_final_balances, 
 
 348            $final_account_balances,
 
 349            'balances after second year_end ok (nothing changed)');
 
 355 #        sum(a.amount     ) filter (where ob_transaction is true                              and a.transdate  < '2020-01-01') as ob_amount,
 
 356 #        sum(a.amount     ) filter (where cb_transaction is false and ob_transaction is false and a.transdate  < '2020-01-01') as amount,
 
 357 #        sum(a.amount     ) filter (where cb_transaction is false                             and a.transdate  < '2020-01-01') as year_end_amount,
 
 358 #        sum(a.amount     ) filter (where                                                         a.transdate  < '2020-01-01') as amount_with_cb,
 
 359 #        sum(a.amount * -1) filter (where cb_transaction is true                              and a.transdate  < '2020-01-01') as cb_amount,
 
 360 #        sum(a.amount     ) filter (where ob_transaction is true                              and a.transdate >= '2020-01-01') as ob_next_year,
 
 361 #        case when c.category = ANY( '{I,E}'     ) then 'pl'
 
 362 #             when c.category = ANY( '{A,C,L,Q}' ) then 'asset'
 
 366 #        inner join chart c on (c.id = a.chart_id)
 
 367 #  where     a.transdate >= '2019-01-01'
 
 368 #        and a.transdate <= '2020-01-01'
 
 369 #  group by c.id, c.accno, c.category
 
 370 #  order by type, c.accno;
 
 371 #  accno |             description             | cat | ob_amount |   amount   | year_end_amount | amount_with_cb | cb_amount  | ob_next_year | type  
 
 372 # -------+-------------------------------------+-----+-----------+------------+-----------------+----------------+------------+--------------+-------
 
 373 #  0890  | Gewinnvortrag vor Verwendung        | Q   |           |            |                 |        0.00000 |    0.00000 |    214.20000 | asset
 
 374 #  1200  | Bank                                | A   | -40.00000 | -166.60000 |      -206.60000 |        0.00000 | -206.60000 |   -206.60000 | asset
 
 375 #  1400  | Ford. a.Lieferungen und Leistungen  | A   |           | -166.60000 |      -166.60000 |        0.00000 | -166.60000 |   -166.60000 | asset
 
 376 #  1600  | Verbindlichkeiten aus Lief.u.Leist. | L   |           |  119.00000 |       119.00000 |        0.00000 |  119.00000 |    119.00000 | asset
 
 377 #  1890  | Privateinlagen                      | Q   |  40.00000 |            |        40.00000 |        0.00000 |   40.00000 |     40.00000 | asset
 
 378 #  9000  | Saldenvorträge,Sachkonten           | A   |           |            |                 |        0.00000 |    0.00000 |      0.00000 | asset
 
 379 #  1576  | Abziehbare Vorsteuer 19 %           | E   |           |  -19.00000 |       -19.00000 |        0.00000 |  -19.00000 |              | pl
 
 380 #  1776  | Umsatzsteuer 19 %                   | I   |           |   53.20000 |        53.20000 |        0.00000 |   53.20000 |              | pl
 
 381 #  3400  | Wareneingang 16%/19% Vorsteuer      | E   |           | -100.00000 |      -100.00000 |        0.00000 | -100.00000 |              | pl
 
 382 #  8400  | Erlöse 16%/19% USt.                 | I   |           |  280.00000 |       280.00000 |        0.00000 |  280.00000 |              | pl
 
 385 # ob_amount + amount = year_end_amount
 
 386 # amount_with_cb should be 0 after year-end transactions
 
 387 # year_end_amount and cb_amount should be the same (will be true with amount_with_cb = 0)
 
 388 # cb_amount should match ob_next_year for asset accounts, except for profit-carried-forward
 
 389 # ob_next_year should be empty for profit-loss-accounts
 
 391 # Oops, we forgot some bookings, lets quickly add them and run
 
 392 #_year_end_bookings again.
 
 394 # Just these new bookings by themselves will lead to a loss, so the loss account
 
 395 # will be booked rather than the profit account.
 
 396 # It would probably be better to check the total profit/loss so far, and
 
 397 # adjust that profit-loss-carry-over # chart, rather than creating a new entry
 
 400 gl_booking(10, $booking_date, 'foo', 'bar', $cash, $bank, 0, 0);
 
 401 gl_booking(5,  $booking_date, 'foo', 'bar', $betriebsbedarf, $cash, 0, 0);
 
 403 SL::Controller::YearEndTransactions::_year_end_bookings( start_date => $start_date,
 
 407 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ cb_transaction => 1 ]), 23, 'acc_trans cb_transactions created ok');
 
 408 is(SL::DB::Manager::AccTransaction->get_all_count(where => [ ob_transaction => 1 ]), 16, 'acc_trans ob_transactions created ok');
 
 409 is(SL::DB::Manager::GLTransaction->get_all_count( where => [ cb_transaction => 1 ]),  9, 'GL cb_transactions created ok');
 
 410 is(SL::DB::Manager::GLTransaction->get_all_count( where => [ ob_transaction => 1 ]),  7, 'GL ob_transactions created ok');
 
 412 is_deeply( &get_final_balances, 
 
 417                'amount_with_cb' => '0.00000',
 
 419                'cb_amount' => '0.00000',
 
 420                'ob_amount' => undef,
 
 421                'ob_next_year' => '-5.00000',
 
 423                'year_end_amount' => undef
 
 428                'amount_with_cb' => '0.00000',
 
 430                'cb_amount' => '0.00000',
 
 431                'ob_amount' => undef,
 
 432                'ob_next_year' => '214.20000',
 
 434                'year_end_amount' => undef
 
 438                'amount' => '-5.00000',
 
 439                'amount_with_cb' => '0.00000',
 
 441                'cb_amount' => '-5.00000',
 
 442                'ob_amount' => undef,
 
 443                'ob_next_year' => '-5.00000',
 
 445                'year_end_amount' => '-5.00000'
 
 449                'amount' => '-156.60000',
 
 450                'amount_with_cb' => '0.00000',
 
 452                'cb_amount' => '-196.60000',
 
 453                'ob_amount' => '-40.00000',
 
 454                'ob_next_year' => '-196.60000',
 
 456                'year_end_amount' => '-196.60000'
 
 460                'amount' => '-166.60000',
 
 461                'amount_with_cb' => '0.00000',
 
 463                'cb_amount' => '-166.60000',
 
 464                'ob_amount' => undef,
 
 465                'ob_next_year' => '-166.60000',
 
 467                'year_end_amount' => '-166.60000'
 
 471                'amount' => '119.00000',
 
 472                'amount_with_cb' => '0.00000',
 
 474                'cb_amount' => '119.00000',
 
 475                'ob_amount' => undef,
 
 476                'ob_next_year' => '119.00000',
 
 478                'year_end_amount' => '119.00000'
 
 483                'amount_with_cb' => '0.00000',
 
 485                'cb_amount' => '40.00000',
 
 486                'ob_amount' => '40.00000',
 
 487                'ob_next_year' => '40.00000',
 
 489                'year_end_amount' => '40.00000'
 
 494                'amount_with_cb' => '0.00000',
 
 496                'cb_amount' => '0.00000',
 
 497                'ob_amount' => undef,
 
 498                'ob_next_year' => '0.00000',
 
 500                'year_end_amount' => undef
 
 504                'amount' => '-19.80000',
 
 505                'amount_with_cb' => '0.00000',
 
 507                'cb_amount' => '-19.80000',
 
 508                'ob_amount' => undef,
 
 509                'ob_next_year' => undef,
 
 511                'year_end_amount' => '-19.80000'
 
 515                'amount' => '53.20000',
 
 516                'amount_with_cb' => '0.00000',
 
 518                'cb_amount' => '53.20000',
 
 519                'ob_amount' => undef,
 
 520                'ob_next_year' => undef,
 
 522                'year_end_amount' => '53.20000'
 
 526                'amount' => '-100.00000',
 
 527                'amount_with_cb' => '0.00000',
 
 529                'cb_amount' => '-100.00000',
 
 530                'ob_amount' => undef,
 
 531                'ob_next_year' => undef,
 
 533                'year_end_amount' => '-100.00000'
 
 537                'amount' => '-4.20000',
 
 538                'amount_with_cb' => '0.00000',
 
 540                'cb_amount' => '-4.20000',
 
 541                'ob_amount' => undef,
 
 542                'ob_next_year' => undef,
 
 544                'year_end_amount' => '-4.20000'
 
 548                'amount' => '280.00000',
 
 549                'amount_with_cb' => '0.00000',
 
 551                'cb_amount' => '280.00000',
 
 552                'ob_amount' => undef,
 
 553                'ob_next_year' => undef,
 
 555                'year_end_amount' => '280.00000'
 
 558            'balances after third year_end ok');
 
 566   foreach (qw(BankAccount
 
 576     "SL::DB::Manager::${_}"->delete_all(all => 1);
 
 580 sub get_account_balances {
 
 583          case when c.category = ANY( '{I,E}'   )   then 'profit_loss_account'
 
 584               when c.category = ANY( '{A,C,L,Q}' ) then 'asset_account'
 
 589          left join chart c on (c.id = a.chart_id)
 
 590 group by c.accno, account_type
 
 591 order by account_type, c.accno;
 
 594   my $result = selectall_hashref_query($::form, $dbh, $query);
 
 598 sub get_final_balances {
 
 602         sum(a.amount     ) filter (where ob_transaction is true                              and a.transdate  < ?) as ob_amount,
 
 603         sum(a.amount     ) filter (where cb_transaction is false and ob_transaction is false and a.transdate  < ?) as amount,
 
 604         sum(a.amount     ) filter (where cb_transaction is false                             and a.transdate  < ?) as year_end_amount,
 
 605         sum(a.amount     ) filter (where                                                         a.transdate  < ?) as amount_with_cb,
 
 606         sum(a.amount * -1) filter (where cb_transaction is true                              and a.transdate  < ?) as cb_amount,
 
 607         sum(a.amount     ) filter (where ob_transaction is true                              and a.transdate  = ?) as ob_next_year,
 
 608         case when c.category = ANY( '{I,E}'     ) then 'pl'
 
 609              when c.category = ANY( '{A,C,L,Q}' ) then 'asset'
 
 613         inner join chart c on (c.id = a.chart_id)
 
 614   where     a.transdate >= ?
 
 616   group by c.id, c.accno, c.category
 
 617   order by type, c.accno
 
 620   my $result = selectall_hashref_query($::form, $dbh, $query, $ob_date, $ob_date, $ob_date, $ob_date, $ob_date, $ob_date, $start_date, $ob_date);
 
 625   # wrapper around SL::Dev::Record::create_gl_transaction for quickly creating transactions
 
 626   my ($amount, $date, $reference, $description, $gegenkonto, $konto, $ob, $cb) = @_;
 
 628   # my $transdate = $::locale->parse_date_to_object($date);
 
 630   return create_gl_transaction(
 
 631     ob_transaction => $ob,
 
 632     cb_transaction => $cb,
 
 634     reference      => $reference,
 
 635     description    => $description,
 
 642                           chart => $gegenkonto,