X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/blobdiff_plain/59f6b67c2936ef72e9c5ee3629fe49876bd9f484..07e01856fbef2494905b7ce82a32c02ddf8128a9:/SL/IS.pm diff --git a/SL/IS.pm b/SL/IS.pm index 39bb7936c..8b79376d2 100644 --- a/SL/IS.pm +++ b/SL/IS.pm @@ -36,6 +36,7 @@ package IS; use List::Util qw(max sum0); +use List::MoreUtils qw(any); use Carp; use SL::AM; @@ -578,21 +579,31 @@ sub invoice_details { $form->{$_} = $form->format_amount($myconfig, $form->{$_}, 2) for @separate_totals; foreach my $invoice_for_advance_payment (@{$self->_get_invoices_for_advance_payment($form->{convert_from_ar_ids} || $form->{id})}) { + # Collect VAT of invoices for advance payment. + # Set sellprices to fxsellprices for items, because + # the PriceTaxCalculator sets fxsellprice from sellprice before calculating. + $_->sellprice($_->fxsellprice) for @{$invoice_for_advance_payment->items}; my %pat = $invoice_for_advance_payment->calculate_prices_and_taxes; my $taxamount = sum0 values %{ $pat{taxes_by_tax_id} }; - push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_$_"} }, $invoice_for_advance_payment->$_) for qw(invnumber transdate); - push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_amount_nofmt"} }, $invoice_for_advance_payment->amount); - push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_amount"} }, $invoice_for_advance_payment->amount_as_number); - push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_taxamount_nofmt"} }, $taxamount); - push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_taxamount"} }, $form->format_amount($myconfig, $taxamount, 2)); + push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_$_"} }, $invoice_for_advance_payment->$_) for qw(invnumber transdate); + push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_amount_nofmt"} }, $invoice_for_advance_payment->amount); + push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_amount"} }, $invoice_for_advance_payment->amount_as_number); + push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_taxamount_nofmt"} }, $taxamount); + push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_taxamount"} }, $form->format_amount($myconfig, $taxamount, 2)); - $form->{iap_amount_nofmt} += $invoice_for_advance_payment->amount; - $form->{iap_taxamount_nofmt} += $taxamount; - $form->{iap_existing} = 1; + my $open_amount = $form->round_amount($invoice_for_advance_payment->open_amount, 2); + push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_open_amount_nofmt"} }, $open_amount); + push(@{ $form->{TEMPLATE_ARRAYS}->{"iap_open_amount"} }, $form->format_amount($myconfig, $open_amount, 2)); + + $form->{iap_amount_nofmt} += $invoice_for_advance_payment->amount; + $form->{iap_taxamount_nofmt} += $taxamount; + $form->{iap_open_amount_nofmt} += $open_amount; + $form->{iap_existing} = 1; } - $form->{iap_amount} = $form->format_amount($myconfig, $form->{iap_amount_nofmt}, 2); - $form->{iap_taxamount} = $form->format_amount($myconfig, $form->{iap_taxamount_nofmt}, 2); + $form->{iap_amount} = $form->format_amount($myconfig, $form->{iap_amount_nofmt}, 2); + $form->{iap_taxamount} = $form->format_amount($myconfig, $form->{iap_taxamount_nofmt}, 2); + $form->{iap_open_amount} = $form->format_amount($myconfig, $form->{iap_open_amount_nofmt}, 2); $main::lxdebug->leave_sub(); } @@ -734,6 +745,8 @@ sub _post_invoice { my $all_units = AM->retrieve_units($myconfig, $form); + my $already_booked = !!$form->{id}; + if (!$payments_only) { if ($form->{storno}) { _delete_transfers($dbh, $form, $form->{storno_id}); @@ -1059,51 +1072,83 @@ SQL # entsprechend auch beim Bestimmen des Steuerschlüssels in Taxkey.pm berücksichtigen my $taxdate = $form->{tax_point} ||$form->{deliverydate} || $form->{invdate}; - # better type? maybe define Invoice->invoice_type - if ($form->{type} ne 'invoice_for_advance_payment') { + # Sanity checks for invoices for advance payment and final invoices + my $advance_payment_clearing_chart; + if (any { $_ eq $form->{type} } qw(invoice_for_advance_payment final_invoice)) { + $advance_payment_clearing_chart = SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_clearing_chart_id)->load; + die "No Clearing Chart for Advance Payment" unless ref $advance_payment_clearing_chart eq 'SL::DB::Chart'; + + my @current_taxaccounts = (split(/ /, $form->{taxaccounts})); + die 'Wrong call: Cannot post invoice for advance payment or final invoice with more than one tax' if (scalar @current_taxaccounts > 1); + + my @trans_ids = keys %{ $form->{amount} }; + if (scalar @trans_ids > 1) { + require Data::Dumper; + die "Invalid state for advance payment more than one trans_id " . Dumper($form->{amount}); + } + } + + my $iap_amounts; + if ($form->{type} eq 'final_invoice') { my $invoices_for_advance_payment = $self->_get_invoices_for_advance_payment($form->{convert_from_ar_ids} || $form->{id}); if (scalar @$invoices_for_advance_payment > 0) { # reverse booking for invoices for advance payment - my $clearing_chart = SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_clearing_chart_id)->load; - die "No Clearing Chart for Advance Payment" unless ref $clearing_chart eq 'SL::DB::Chart'; foreach my $invoice_for_advance_payment (@$invoices_for_advance_payment) { - # delete ? post twice case ? + # delete ? + # --> is implemented below (bookings are marked in memo field) + # # TODO: helper table acc_trans_advance_payment # trans_id for final invoice connects to acc_trans_id here # my $booking = SL::DB::AccTrans->new( ...) + # --> helper table not nessessary because of mark in memo field + # # TODO: If final_invoice change (delete storno) delete all connectin acc_trans entries, if # period is not closed + # --> no problem because gldate of reverse booking is date of final invoice + # if deletion of final invoice is allowed, reverting bookings in invoices + # for advance payment are allowed, too. # $booking->id, $self->id in helper table - $form->{amount}->{$invoice_for_advance_payment->id}->{$clearing_chart->accno} = -1 * $invoice_for_advance_payment->netamount; - $form->{memo} ->{$invoice_for_advance_payment->id}->{$clearing_chart->accno} = 'reverse booking by final invoice'; - # AR - $form->{amount}->{$invoice_for_advance_payment->id}->{$form->{AR}} = $invoice_for_advance_payment->netamount; - $form->{memo} ->{$invoice_for_advance_payment->id}->{$form->{AR}} = 'reverse booking by final invoice'; - } - } - } - if ($form->{type} eq 'invoice_for_advance_payment') { - # sanity and decomplex, allow only one tax rate - my $clearing_chart = SL::DB::Chart->new(id => $::instance_conf->get_advance_payment_clearing_chart_id)->load; - die "No Clearing Chart for Advance Payment" unless ref $clearing_chart eq 'SL::DB::Chart'; + if (!$already_booked) { + $form->{amount}->{$invoice_for_advance_payment->id}->{$advance_payment_clearing_chart->accno} = -1 * $invoice_for_advance_payment->netamount; + $form->{memo} ->{$invoice_for_advance_payment->id}->{$advance_payment_clearing_chart->accno} = 'reverse booking by final invoice'; + # AR + $form->{amount}->{$invoice_for_advance_payment->id}->{$form->{AR}} = $invoice_for_advance_payment->netamount; + $form->{memo} ->{$invoice_for_advance_payment->id}->{$form->{AR}} = 'reverse booking by final invoice'; + } - my @current_taxaccounts = (split(/ /, $form->{taxaccounts})); - die 'Wrong Call, cannot post invoice for advance payment with more than one tax' if (scalar @current_taxaccounts > 1); + # VAT for invoices for advance payment is booked on payment of these. So do not book this VAT for final invoice. + # And book the amount of the invoices for advance payment with taxkey 0 (see below). + # Collect amounts and VAT of invoices for advance payment. - my @trans_ids = keys %{ $form->{amount} }; - if (scalar @trans_ids > 1) { - require Data::Dumper; - die "Invalid state for advance payment more than one trans_id " . Dumper($form->{amount}); + # Set sellprices to fxsellprices for items, because + # the PriceTaxCalculator sets fxsellprice from sellprice before calculating. + $_->sellprice($_->fxsellprice) for @{$invoice_for_advance_payment->items}; + my %pat = $invoice_for_advance_payment->calculate_prices_and_taxes; + + foreach my $tax_chart_id (keys %{ $pat{taxes_by_chart_id} }) { + my $tax_accno = SL::DB::Chart->load_cached($tax_chart_id)->accno; + $form->{amount}{ $form->{id} }{$tax_accno} -= $pat{taxes_by_chart_id}->{$tax_chart_id}; + $form->{amount}{ $form->{id} }{$form->{AR}} += $pat{taxes_by_chart_id}->{$tax_chart_id}; + } + + foreach my $amount_chart_id (keys %{ $pat{amounts} }) { + my $amount_accno = SL::DB::Chart->load_cached($amount_chart_id)->accno; + $iap_amounts->{$amount_accno} += $pat{amounts}->{$amount_chart_id}->{amount}; + $form->{amount}{ $form->{id} }{$amount_accno} -= $pat{amounts}->{$amount_chart_id}->{amount}; + } + } } + } + if ($form->{type} eq 'invoice_for_advance_payment') { # get gross and move to clearing chart - delete everything else # 1. gross - my $gross = $form->{amount}{$trans_ids[0]}{$form->{AR}}; + my $gross = $form->{amount}{ $form->{id} }{$form->{AR}}; # 2. destroy - undef $form->{amount}{$trans_ids[0]}; + undef $form->{amount}{ $form->{id} }; # 3. rebuild - $form->{amount}{$trans_ids[0]}{$form->{AR}} = $gross; - $form->{amount}{$trans_ids[0]}{$clearing_chart->accno} = $gross * -1; + $form->{amount}{ $form->{id} }{$form->{AR}} = $gross; + $form->{amount}{ $form->{id} }{$advance_payment_clearing_chart->accno} = $gross * -1; # 4. no cogs, hopefully not commonly used at all undef $form->{amount_cogs}; } @@ -1208,6 +1253,17 @@ SQL } } + # Book the amount of the invoices for advance payment with taxkey 0 (see below). + if ($form->{type} eq 'final_invoice' && $iap_amounts) { + foreach my $accno (keys %$iap_amounts) { + $query = + qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, tax_id, taxkey, project_id, chart_link) + VALUES (?, (SELECT id FROM chart WHERE accno = ?), ?, ?, (SELECT id FROM tax WHERE taxkey=0), 0, ?, (SELECT link FROM chart WHERE accno = ?))|; + @values = (conv_i($form->{id}), $accno, $iap_amounts->{$accno}, conv_date($form->{invdate}), conv_i($project_id), $accno); + do_query($form, $dbh, $query, @values); + } + } + # deduct payment differences from diff for my $i (1 .. $form->{paidaccounts}) { if ($form->{"paid_$i"} != 0) { @@ -2054,6 +2110,9 @@ sub _delete_invoice { # if we delete a final invoice, the reverse bookings for the clearing account in the invoice for advance payment # must be deleted as well my $invoices_for_advance_payment = $self->_get_invoices_for_advance_payment($form->{convert_from_ar_ids} || $form->{id}); + + # Todo: allow only if invoice for advance payment is not paid. + # die if any { $_->paid } for @$invoices_for_advance_payment; my @trans_ids_to_consider = map { $_->id } @$invoices_for_advance_payment; if (scalar @trans_ids_to_consider) { my $query = sprintf 'DELETE FROM acc_trans WHERE memo LIKE ? AND trans_id IN (%s)', join ', ', ("?") x scalar @trans_ids_to_consider;