4 |
4 |
|
5 |
5 |
use parent qw(Exporter);
|
6 |
6 |
our @EXPORT = qw(pay_invoice);
|
7 |
|
our @EXPORT_OK = qw(skonto_date skonto_charts amount_less_skonto within_skonto_period percent_skonto reference_account reference_amount open_amount open_percent remaining_skonto_days skonto_amount check_skonto_configuration valid_skonto_amount get_payment_suggestions validate_payment_type open_sepa_transfer_amount get_payment_select_options_for_bank_transaction exchangerate forex);
|
|
7 |
our @EXPORT_OK = qw(skonto_date skonto_charts amount_less_skonto within_skonto_period percent_skonto reference_account reference_amount open_amount open_percent remaining_skonto_days skonto_amount check_skonto_configuration valid_skonto_amount get_payment_suggestions validate_payment_type open_sepa_transfer_amount get_payment_select_options_for_bank_transaction exchangerate forex _skonto_charts_and_tax_correction);
|
8 |
8 |
our %EXPORT_TAGS = (
|
9 |
9 |
"ALL" => [@EXPORT, @EXPORT_OK],
|
10 |
10 |
);
|
... | ... | |
39 |
39 |
|
40 |
40 |
# check for required parameters and optional params depending on payment_type
|
41 |
41 |
Common::check_params(\%params, qw(chart_id transdate));
|
|
42 |
Common::check_params(\%params, qw(bt_id)) unless $params{payment_type} eq 'without_skonto';
|
42 |
43 |
if ( $params{'payment_type'} eq 'without_skonto' && abs($params{'amount'}) < 0) {
|
43 |
44 |
croak "invalid amount for payment_type 'without_skonto': $params{'amount'}\n";
|
44 |
45 |
}
|
... | ... | |
220 |
221 |
if ( $params{payment_type} eq 'with_skonto_pt' ) {
|
221 |
222 |
$total_skonto_amount = $self->skonto_amount;
|
222 |
223 |
} elsif ( $params{payment_type} eq 'difference_as_skonto' ) {
|
|
224 |
# only used for tests. no real code calls this payment_type!
|
223 |
225 |
$total_skonto_amount = $self->open_amount;
|
224 |
226 |
} elsif ( $params{payment_type} eq 'free_skonto') {
|
225 |
227 |
$total_skonto_amount = $params{skonto_amount};
|
226 |
228 |
}
|
227 |
|
my @skonto_bookings = $self->skonto_charts($total_skonto_amount);
|
228 |
|
|
|
229 |
my @skonto_bookings = $self->_skonto_charts_and_tax_correction(amount => $total_skonto_amount, bt_id => $params{bt_id},
|
|
230 |
transdate_obj => $transdate_obj, memo => $params{memo},
|
|
231 |
source => $params{source});
|
229 |
232 |
# error checking:
|
230 |
233 |
if ( $params{payment_type} eq 'difference_as_skonto' ) {
|
231 |
234 |
my $calculated_skonto_sum = sum map { $_->{skonto_amount} } @skonto_bookings;
|
... | ... | |
235 |
238 |
my $reference_amount = $total_skonto_amount;
|
236 |
239 |
|
237 |
240 |
# create an acc_trans entry for each result of $self->skonto_charts
|
|
241 |
# TODO create internal sub _skonto_bookings
|
238 |
242 |
foreach my $skonto_booking ( @skonto_bookings ) {
|
239 |
243 |
next unless $skonto_booking->{'chart_id'};
|
240 |
244 |
next unless $skonto_booking->{'skonto_amount'} != 0;
|
... | ... | |
566 |
570 |
|
567 |
571 |
return $open_sepa_amount || 0;
|
568 |
572 |
|
569 |
|
};
|
|
573 |
}
|
|
574 |
|
|
575 |
sub _skonto_charts_and_tax_correction {
|
|
576 |
my ($self, %params) = @_;
|
|
577 |
my $amount = $params{amount} || $self->skonto_amount;
|
|
578 |
|
|
579 |
croak "no amount passed to skonto_charts" unless abs(_round($amount)) >= 0.01;
|
|
580 |
croak "no banktransaction.id passed to skonto_charts" unless $params{bt_id};
|
|
581 |
croak "no banktransaction.transdate passed to skonto_charts" unless ref $params{transdate_obj} eq 'DateTime';
|
|
582 |
#$main::lxdebug->message(0, 'id der transaktion' . $params{bt_id});
|
|
583 |
#$main::lxdebug->message(0, 'wert des skontos:' . $amount);
|
|
584 |
my $is_sales = $self->is_sales;
|
|
585 |
my (@skonto_charts, $inv_calc, $total_skonto_rounded);
|
|
586 |
$inv_calc = $self->get_tax_and_amount_by_tax_chart_id();
|
|
587 |
#$main::lxdebug->message(0, 'lulu' . Dumper($inv_calc));
|
|
588 |
while (my ($tax_chart_id, $entry) = each %{ $inv_calc } ) { # foreach tax key = tax.id
|
|
589 |
#$main::lxdebug->message(0, 'was hier:' . $tax_chart_id);
|
|
590 |
my $tax = SL::DB::Manager::Tax->find_by(id => $entry->{tax_id}) || die "Can't find tax with id " . $tax_chart_id;
|
|
591 |
die t8('no skonto_chart configured for taxkey #1 : #2 : #3', $tax->taxkey, $tax->taxdescription , $tax->rate * 100)
|
|
592 |
unless $is_sales ? ref $tax->skonto_sales_chart : ref $tax->skonto_purchase_chart;
|
|
593 |
#$main::lxdebug->message(0, 'was dort:' . $tax->id);
|
|
594 |
my $transaction_net_skonto_percent = abs($entry->{netamount} / $self->amount);
|
|
595 |
my $skonto_netamount_unrounded = abs($amount * $transaction_net_skonto_percent);
|
|
596 |
#$main::lxdebug->message(0, 'ungerundet netto:' . $skonto_netamount_unrounded);
|
|
597 |
# divide for tax
|
|
598 |
my $transaction_tax_skonto_percent = abs($entry->{tax} / $self->amount);
|
|
599 |
my $skonto_taxamount_unrounded = abs($amount * $transaction_tax_skonto_percent);
|
|
600 |
#$main::lxdebug->message(0, 'ungerundet steuer:' . $skonto_taxamount_unrounded);
|
|
601 |
my $skonto_taxamount_rounded = _round($skonto_taxamount_unrounded);
|
|
602 |
my $skonto_netamount_rounded = _round($skonto_netamount_unrounded);
|
|
603 |
my $chart_id = $is_sales ? $tax->skonto_sales_chart->id : $tax->skonto_purchase_chart->id;
|
|
604 |
|
|
605 |
my $rec_net = {
|
|
606 |
chart_id => $chart_id,
|
|
607 |
skonto_amount => _round($skonto_netamount_unrounded + $skonto_taxamount_unrounded),
|
|
608 |
# skonto_amount => _round($skonto_netamount_unrounded) + _round($skonto_taxamount_unrounded),
|
|
609 |
};
|
|
610 |
push @skonto_charts, $rec_net;
|
|
611 |
$total_skonto_rounded += $rec_net->{skonto_amount};
|
|
612 |
|
|
613 |
# add-on: correct tax with one linked gl booking
|
|
614 |
|
|
615 |
# no skonto tax correction for dual tax (reverse charge) or rate = 0
|
|
616 |
next if ($tax->rate == 0 || $tax->reverse_charge_chart_id);
|
|
617 |
|
|
618 |
my ($credit, $debit);
|
|
619 |
$credit = SL::DB::Manager::Chart->find_by(id => $chart_id);
|
|
620 |
$debit = SL::DB::Manager::Chart->find_by(id => $tax_chart_id);
|
|
621 |
croak("No such Chart ID") unless ref $credit eq 'SL::DB::Chart' && ref $debit eq 'SL::DB::Chart';
|
|
622 |
|
|
623 |
my $current_transaction = SL::DB::GLTransaction->new(
|
|
624 |
employee_id => $self->employee_id,
|
|
625 |
transdate => $params{transdate_obj},
|
|
626 |
notes => $params{source} . ' ' . $params{memo},
|
|
627 |
description => $self->notes || $self->invnumber,
|
|
628 |
reference => t8('Skonto Tax Correction for') . " " . $tax->rate * 100 . '% ' . $self->invnumber,
|
|
629 |
department_id => $self->department_id ? $self->department_id : undef,
|
|
630 |
imported => 0, # not imported
|
|
631 |
taxincluded => 0,
|
|
632 |
)->add_chart_booking(
|
|
633 |
chart => $is_sales ? $debit : $credit,
|
|
634 |
debit => abs($skonto_taxamount_rounded),
|
|
635 |
source => t8('Skonto Tax Correction for') . " " . $self->invnumber,
|
|
636 |
memo => $params{memo},
|
|
637 |
tax_id => 0,
|
|
638 |
)->add_chart_booking(
|
|
639 |
chart => $is_sales ? $credit : $debit,
|
|
640 |
credit => abs($skonto_taxamount_rounded),
|
|
641 |
source => t8('Skonto Tax Correction for') . " " . $self->invnumber,
|
|
642 |
memo => $params{memo},
|
|
643 |
tax_id => 0,
|
|
644 |
)->post;
|
|
645 |
|
|
646 |
## add a stable link from ap to gl
|
|
647 |
# not needed, BankTransactionAccTrans is already stable
|
|
648 |
# furthermore the origin of the booking is the bank_transaction
|
|
649 |
#my $arap = $self->is_sales ? 'ar' : 'ap';
|
|
650 |
#my %props_gl = (
|
|
651 |
# $arap . _id => $self->id,
|
|
652 |
# gl_id => $current_transaction->id,
|
|
653 |
# datev_export => 1,
|
|
654 |
#);
|
|
655 |
#if ($arap eq 'ap') {
|
|
656 |
# require SL::DB::ApGl;
|
|
657 |
# SL::DB::ApGl->new(%props_gl)->save;
|
|
658 |
#} elsif ($arap eq 'ar') {
|
|
659 |
# require SL::DB::ArGl;
|
|
660 |
# SL::DB::ArGl->new(%props_gl)->save;
|
|
661 |
#} else { die "Invalid state"; }
|
|
662 |
#push @new_acc_ids, map { $_->acc_trans_id } @{ $current_transaction->transactions };
|
|
663 |
|
|
664 |
foreach my $transaction (@{ $current_transaction->transactions }) {
|
|
665 |
my %props_acc = (
|
|
666 |
acc_trans_id => $transaction->acc_trans_id,
|
|
667 |
bank_transaction_id => $params{bt_id},
|
|
668 |
gl => $current_transaction->id,
|
|
669 |
);
|
|
670 |
SL::DB::BankTransactionAccTrans->new(%props_acc)->save;
|
|
671 |
}
|
|
672 |
# Record a record link from banktransactions to gl
|
|
673 |
# caller has to assign param bt_id
|
|
674 |
my %props_rl = (
|
|
675 |
from_table => 'bank_transactions',
|
|
676 |
from_id => $params{bt_id},
|
|
677 |
to_table => 'gl',
|
|
678 |
to_id => $current_transaction->id,
|
|
679 |
);
|
|
680 |
SL::DB::RecordLink->new(%props_rl)->save;
|
|
681 |
# Record a record link from arap to gl
|
|
682 |
# linked gl booking will appear in tab linked records
|
|
683 |
# this is just a link for convenience
|
|
684 |
%props_rl = (
|
|
685 |
from_table => $is_sales ? 'ar' : 'ap',
|
|
686 |
from_id => $self->id,
|
|
687 |
to_table => 'gl',
|
|
688 |
to_id => $current_transaction->id,
|
|
689 |
);
|
|
690 |
SL::DB::RecordLink->new(%props_rl)->save;
|
570 |
691 |
|
|
692 |
}
|
|
693 |
# check for rounding errors, at least for the payment chart
|
|
694 |
# we ignore tax rounding errors as long as the user or calculated
|
|
695 |
# amount of skonto is fully assigned
|
|
696 |
# we simply alter one cent for the first skonto booking entry
|
|
697 |
# should be correct for most of the cases (no invoices with mixed taxes)
|
|
698 |
if ($total_skonto_rounded - $amount > 0.01) {
|
|
699 |
# add one cent
|
|
700 |
$main::lxdebug->message(0, 'Una mas!' . $total_skonto_rounded);
|
|
701 |
$skonto_charts[0]->{skonto_amount} -= 0.01;
|
|
702 |
} elsif ($amount - $total_skonto_rounded > 0.01) {
|
|
703 |
# subtract one cent
|
|
704 |
$main::lxdebug->message(0, 'Una menos!' . $total_skonto_rounded);
|
|
705 |
$skonto_charts[0]->{skonto_amount} += 0.01;
|
|
706 |
} else { $main::lxdebug->message(0, 'No rounding error'); }
|
|
707 |
|
|
708 |
# return same array of skonto charts as sub skonto_charts
|
|
709 |
return @skonto_charts;
|
|
710 |
}
|
571 |
711 |
|
572 |
712 |
sub skonto_charts {
|
573 |
713 |
my $self = shift;
|
Payment-Helper Skonto verbuchen mit Steuerkorrektur
tax_and_amount_by_tax_id ausgelagert für ar und ap in SalesPurchaseInvoice.
pay_invoice mit skonto erwartet die banktransaction.id
Invoice und PurchaseInvoice bindet den SPI Helper ein
Alte Methode skonto_charts noch im Payment-Helper drin.
Ferner auskommentierte Debug-Statements und auskommtiert ArGl, ApGl
stabile Anbindung an ARAP (nicht notwendig, da mit BankTransationAccTrans
verknüpft).