4 |
4 |
|
5 |
5 |
use parent qw(Exporter);
|
6 |
6 |
our @EXPORT = qw(pay_invoice);
|
7 |
|
our @EXPORT_OK = qw(skonto_date amount_less_skonto within_skonto_period percent_skonto reference_account open_amount skonto_amount valid_skonto_amount validate_payment_type get_payment_select_options_for_bank_transaction forex _skonto_charts_and_tax_correction get_exchangerate_for_bank_transaction get_exchangerate _add_bank_fx_fees);
|
|
7 |
our @EXPORT_OK = qw(skonto_date amount_less_skonto within_skonto_period percent_skonto reference_account open_amount skonto_amount valid_skonto_amount validate_payment_type get_payment_select_options_for_bank_transaction forex _skonto_charts_and_tax_correction get_exchangerate_for_bank_transaction get_exchangerate _add_bank_fx_fees open_amount_fx);
|
8 |
8 |
our %EXPORT_TAGS = (
|
9 |
9 |
"ALL" => [@EXPORT, @EXPORT_OK],
|
10 |
10 |
);
|
... | ... | |
70 |
70 |
croak "payment type with_skonto_pt can't be used if payments have already been made" if $self->paid != 0;
|
71 |
71 |
}
|
72 |
72 |
|
73 |
|
|
74 |
73 |
my $transdate_obj;
|
75 |
74 |
if (ref($params{transdate}) eq 'DateTime') {
|
76 |
75 |
$transdate_obj = $params{transdate};
|
... | ... | |
92 |
91 |
|
93 |
92 |
# currency has to be passed and caller has to be sure to assign it for a forex invoice
|
94 |
93 |
# dies if called for a invoice with the default currency (TODO: Params::Validate before)
|
95 |
|
my ($exchangerate, $currency, $fx_gain_loss_amount, $return_bank_amount);
|
|
94 |
my ($exchangerate, $currency, $return_bank_amount , $fx_gain_loss_amount);
|
96 |
95 |
$return_bank_amount = 0;
|
97 |
96 |
if ($params{currency} || $params{currency_id} && $self->forex) { # currency was specified
|
98 |
97 |
$currency = SL::DB::Manager::Currency->find_by(name => $params{currency}) || SL::DB::Manager::Currency->find_by(id => $params{currency_id});
|
99 |
|
# set exchangerate - no fallback
|
100 |
|
# die "No exchange rate" unless $params{exchangerate} > 0;
|
|
98 |
|
|
99 |
die "No currency found" unless ref $currency eq 'SL::DB::Currency';
|
|
100 |
die "No exchange rate" unless $params{exchangerate} > 0;
|
|
101 |
|
101 |
102 |
$exchangerate = $params{exchangerate};
|
102 |
|
# hook for gl_bookings $book_fx_bank_fees;
|
103 |
|
# and calculus fidibus total fx
|
104 |
|
# self->amount - paid / self->exchangerate * banktransaction.exchangerate = total new amount EUR
|
|
103 |
|
105 |
104 |
my $new_open_amount = ( $self->open_amount / $self->get_exchangerate ) * $exchangerate;
|
106 |
105 |
# VORHER
|
107 |
106 |
# my $gain_loss_amount = _round($amount * ($exchangerate - $self->get_exchangerate ) * -1,2);
|
108 |
|
$fx_gain_loss_amount = _round( $self->open_amount - $new_open_amount);
|
|
107 |
# $fx_gain_loss_amount = _round( $self->open_amount - $new_open_amount);
|
|
108 |
# $fx_gain_loss_amount = _round($self->open_amount / $self->get_exchangerate - $new_open_amount / $exchangerate);
|
109 |
109 |
# works for ap, but change sign for ar (todo credit notes and negative ap transactions
|
110 |
|
$fx_gain_loss_amount *= -1 if $self->is_sales;
|
111 |
|
$main::lxdebug->message(0, 'h 1 ' . $new_open_amount . ' h 3 ' . $params{amount} . ' und fx ' . $fx_gain_loss_amount );
|
112 |
|
if ($new_open_amount < $params{amount}) {
|
113 |
|
# if new open amount for payment booking is smaller than original amount use this
|
114 |
|
# assume that the rest are fees, if the user selected this
|
115 |
|
if($params{fx_book}) {
|
116 |
|
die "Bank Fees can only be added for AP transactions" if $self->is_sales;
|
117 |
|
$self->_add_bank_fx_fees(fee => _round($params{amount} - $new_open_amount),
|
118 |
|
bt_id => $params{bt_id},
|
119 |
|
bank_chart_id => $params{chart_id},
|
120 |
|
memo => $params{memo},
|
121 |
|
source => $params{source},
|
122 |
|
transdate_obj => $transdate_obj );
|
123 |
|
# invoice_amount add gl booking
|
124 |
|
$return_bank_amount += _round($params{amount} - $new_open_amount);
|
125 |
|
} else {
|
126 |
|
# invoice_amount without gl booking
|
127 |
|
# $return_bank_amount = $new_open_amount;
|
128 |
|
}
|
129 |
|
# with or without fees simply assign the new open amount for bank (fx_gain follows later)
|
130 |
|
$params{amount} = $new_open_amount;
|
|
110 |
# $fx_gain_loss_amount *= -1 if $self->is_sales;
|
|
111 |
$main::lxdebug->message(0, 'h 1 ' . $new_open_amount . ' h 3 ' . $params{amount});
|
|
112 |
# if new open amount for payment booking is smaller than original amount use this
|
|
113 |
# assume that the rest are fees, if the user selected this
|
|
114 |
if ($params{fx_book} && $params{fx_fee_amount} > 0) {
|
|
115 |
die "Bank Fees can only be added for AP transactions or Sales Credit Notes"
|
|
116 |
unless $self->invoice_type =~ m/purchase_invoice|ap_transaction|credit_note/;
|
|
117 |
|
|
118 |
$self->_add_bank_fx_fees(fee => _round($params{fx_fee_amount}),
|
|
119 |
bt_id => $params{bt_id},
|
|
120 |
bank_chart_id => $params{chart_id},
|
|
121 |
memo => $params{memo},
|
|
122 |
source => $params{source},
|
|
123 |
transdate_obj => $transdate_obj );
|
|
124 |
# invoice_amount add gl booking
|
|
125 |
$main::lxdebug->message(0, 'fee ' . $params{fx_fee_amount});
|
|
126 |
$return_bank_amount += _round($params{fx_fee_amount}); # invoice_type needs negative bank_amount
|
|
127 |
$main::lxdebug->message(0, 'bank_amount' . $return_bank_amount);
|
|
128 |
#$fx_gain_loss_amount = _round($params{amount} - ($params{amount} / $self->get_exchangerate * $exchangerate) );
|
131 |
129 |
}
|
132 |
|
$main::lxdebug->message(0, 'return 1gl booking ' . $return_bank_amount); # stimmt f
|
133 |
|
# $paid_amount = $new_open_amount;
|
134 |
130 |
} elsif (!$self->forex) { # invoices uses default currency. no exchangerate
|
135 |
131 |
$exchangerate = 1;
|
136 |
|
# $return_bank_amount = _round($params{amount}); # no forex
|
137 |
132 |
} else {
|
138 |
133 |
die "Cannot calculate exchange rate, if invoices uses the default currency";
|
139 |
134 |
}
|
... | ... | |
178 |
173 |
$paid_amount += $pay_amount;
|
179 |
174 |
|
180 |
175 |
my $amount = (-1 * $pay_amount) * $mult;
|
181 |
|
|
|
176 |
$main::lxdebug->message(0, 'bank pay amount:' . $pay_amount);
|
|
177 |
$main::lxdebug->message(0, 'paidamount:' . $paid_amount);
|
182 |
178 |
|
183 |
179 |
# total amount against bank, do we already know this by now?
|
184 |
180 |
# Yes, method requires this
|
... | ... | |
193 |
189 |
taxkey => 0,
|
194 |
190 |
tax_id => SL::DB::Manager::Tax->find_by(taxkey => 0)->id);
|
195 |
191 |
$new_acc_trans->save;
|
196 |
|
$return_bank_amount += abs($amount); # add sign
|
197 |
|
$main::lxdebug->message(0, 'return 5 ' . $return_bank_amount);
|
|
192 |
$return_bank_amount += $amount;
|
|
193 |
$main::lxdebug->message(0, 'return 5 :' . $return_bank_amount);
|
|
194 |
$main::lxdebug->message(0, 'paid amount hier 1 :' . $paid_amount);
|
198 |
195 |
push @new_acc_ids, $new_acc_trans->acc_trans_id;
|
199 |
196 |
# deal with fxtransaction ...
|
200 |
197 |
# if invoice exchangerate differs from exchangerate of payment
|
201 |
198 |
# add fxloss or fxgain
|
202 |
|
if ($fx_gain_loss_amount && $exchangerate != 1 && $self->get_exchangerate and $self->get_exchangerate != 1 and $self->get_exchangerate != $exchangerate) {
|
203 |
|
# (self->amount - self->paid) / $self->exchangerate
|
|
199 |
if ($exchangerate != 1 && $self->get_exchangerate and $self->get_exchangerate != 1 and $self->get_exchangerate != $exchangerate) {
|
204 |
200 |
my $fxgain_chart = SL::DB::Manager::Chart->find_by(id => $::instance_conf->get_fxgain_accno_id) || die "Can't determine fxgain chart";
|
205 |
201 |
my $fxloss_chart = SL::DB::Manager::Chart->find_by(id => $::instance_conf->get_fxloss_accno_id) || die "Can't determine fxloss chart";
|
206 |
|
$main::lxdebug->message(0, 'was sagt gain loss' . $fx_gain_loss_amount);
|
|
202 |
# AMOUNT == EUR / fx rate pay * (fx rate invoice - fx rate pa)
|
|
203 |
# rate invoice = 2, fx rate paid = 1.75
|
|
204 |
# partial payment of 15000 EUR invtotal 20000
|
|
205 |
# 15000/1.75 * (2 - 1.75) = 2142. EUR gain if invoice is purchase # sql ledger (with fx amount):
|
|
206 |
# AR.pm
|
|
207 |
# $amount = $form->round_amount($form->{"paid_$i"} * ($form->{exchangerate} - $form->{"exchangerate_$i"}) * -1, 2);
|
|
208 |
# AP.pm
|
|
209 |
# exchangerate gain/loss
|
|
210 |
# $amount = $form->round_amount($form->{"paid_$i"} * ($form->{exchangerate} - $form->{"exchangerate_$i"}), 2);
|
|
211 |
# ==>
|
|
212 |
# my $fx_gain_loss_sign = $is_sales ? -1 : 1; # multiplier for getting the right sign depending on ar/ap
|
|
213 |
|
|
214 |
#my $fx_gain_loss_sign = $self->invoice_type =~ m/purchase_invoice|ap_transaction|credit_note/ ? 1
|
|
215 |
# : $self->invoice_type =~ m/invoice|ar_transaction|purchase_credit_note|invoice_for_advance_payment/ ? -1
|
|
216 |
# : die "invalid state";
|
|
217 |
|
|
218 |
$fx_gain_loss_amount = _round($amount / $exchangerate * ( $self->get_exchangerate - $exchangerate)); # * $fx_gain_loss_sign;
|
|
219 |
|
|
220 |
$main::lxdebug->message(0, 'was sagt gain loss 2 ' . $fx_gain_loss_amount);
|
|
221 |
# die "huchz" . $fx_gain_loss_amount;
|
207 |
222 |
my $gain_loss_chart = $fx_gain_loss_amount > 0 ? $fxgain_chart : $fxloss_chart;
|
208 |
223 |
# $paid_amount += abs($fx_gain_loss_amount); # if $fx_gain_loss_amount < 0; # only add if we have fx_loss
|
209 |
|
$paid_amount += abs($fx_gain_loss_amount) if $fx_gain_loss_amount < 0; # only add if we have fx_loss
|
210 |
|
$paid_amount -= abs($fx_gain_loss_amount) if $fx_gain_loss_amount > 0; # but extract if we have gain to match original invoice amount (ar)
|
|
224 |
$main::lxdebug->message(0, 'paid hier 1 ' . $paid_amount);
|
|
225 |
# for sales add loss to ar.paid and subtract gain from ar.paid
|
|
226 |
# for purchase add gain to ap.paid and subtract loss from ap.paid
|
|
227 |
$paid_amount += abs($fx_gain_loss_amount) if $fx_gain_loss_amount < 0 && $self->is_sales; # extract if we have fx_loss
|
|
228 |
$paid_amount -= abs($fx_gain_loss_amount) if $fx_gain_loss_amount > 0 && $self->is_sales; # but add if to match original invoice amount (arap)
|
|
229 |
$paid_amount += abs($fx_gain_loss_amount) if $fx_gain_loss_amount > 0 && !$self->is_sales; # but add if to match original invoice amount (arap)
|
|
230 |
$paid_amount -= abs($fx_gain_loss_amount) if $fx_gain_loss_amount < 0 && !$self->is_sales; # extract if we have fx_loss
|
|
231 |
# (self->amount - self->paid) / $self->exchangerate
|
|
232 |
$main::lxdebug->message(0, 'paid dort 2 ' . $paid_amount);
|
|
233 |
|
211 |
234 |
$main::lxdebug->message(0, 'return 1 ' . $return_bank_amount);
|
212 |
235 |
$main::lxdebug->message(0, 'paid amount hier 2 ' . $paid_amount);
|
213 |
236 |
# $return_bank_amount += $fx_gain_loss_amount if $fx_gain_loss_amount < 0; # only add if we have fx_loss
|
... | ... | |
397 |
420 |
1;
|
398 |
421 |
|
399 |
422 |
}) || die t8('error while paying invoice #1 : ', $self->invnumber) . $db->error . "\n";
|
400 |
|
return wantarray ? (abs($return_bank_amount), @new_acc_ids) : 1;
|
|
423 |
|
|
424 |
$return_bank_amount *= -1; # negative booking is positive bank transaction
|
|
425 |
# positive booking is negative bank transaction
|
|
426 |
return wantarray ? ( { return_bank_amount => $return_bank_amount }, @new_acc_ids) : 1;
|
401 |
427 |
}
|
402 |
428 |
|
403 |
429 |
sub skonto_date {
|
... | ... | |
442 |
468 |
return ($self->amount // 0) - ($self->paid // 0);
|
443 |
469 |
}
|
444 |
470 |
|
|
471 |
sub open_amount_fx {
|
|
472 |
# validate shift == $self
|
|
473 |
validate_pos(
|
|
474 |
@_,
|
|
475 |
{ can => [ qw(forex get_exchangerate) ],
|
|
476 |
callbacks => { 'has forex' => sub { return $_[0]->forex } } },
|
|
477 |
{ callbacks => {
|
|
478 |
'is a positive real' => sub { return $_[0] =~ m/^[+]?\d+(\.\d+)?$/ }, },
|
|
479 |
}
|
|
480 |
);
|
|
481 |
|
|
482 |
my ($self, $fx_rate) = @_;
|
|
483 |
|
|
484 |
return ( $self->open_amount / $self->get_exchangerate ) * $fx_rate;
|
|
485 |
|
|
486 |
}
|
|
487 |
|
|
488 |
sub amount_less_skonto_fx {
|
|
489 |
# validate shift == $self
|
|
490 |
validate_pos(
|
|
491 |
@_,
|
|
492 |
{ can => [ qw(forex get_exchangerate percent_skonto) ],
|
|
493 |
callbacks => { 'has forex' => sub { return $_[0]->forex } } },
|
|
494 |
{ callbacks => {
|
|
495 |
'is a positive real' => sub { return $_[0] =~ m/^[+]?\d+(\.\d+)?$/ }, },
|
|
496 |
}
|
|
497 |
);
|
|
498 |
|
|
499 |
my ($self, $fx_rate) = @_;
|
|
500 |
|
|
501 |
return ( $self->amount_less_skonto / $self->get_exchangerate ) * $fx_rate;
|
|
502 |
}
|
|
503 |
|
|
504 |
|
|
505 |
|
445 |
506 |
sub skonto_amount {
|
446 |
507 |
my $self = shift;
|
447 |
508 |
|
... | ... | |
534 |
595 |
# linked gl booking will appear in tab linked records
|
535 |
596 |
# this is just a link for convenience
|
536 |
597 |
%props_rl = (
|
537 |
|
#from_table => $is_sales ? 'ar' : 'ap',
|
538 |
|
from_table => 'ap',
|
|
598 |
from_table => $self->is_sales ? 'ar' : 'ap', # yep sales credit notes
|
|
599 |
#from_table => 'ap',
|
539 |
600 |
from_id => $self->id,
|
540 |
601 |
to_table => 'gl',
|
541 |
602 |
to_id => $current_transaction->id,
|
Wechselkurs beim Bankauszug verbuchen.
> Testfälle i.O. (bank_transaction.t)
> manuelle Prüfung fast i.O. (sechs Fälle) Rundungsdifferenzen> Debugs n.i.O.
> automatisierte Testfälle n.i.O.