Revision 4f43ec85
Von Kivitendo Admin vor fast 9 Jahren hinzugefügt
SL/DB/Invoice.pm | ||
---|---|---|
3 | 3 |
use strict; |
4 | 4 |
|
5 | 5 |
use Carp; |
6 |
use List::Util qw(first); |
|
6 |
use List::Util qw(first sum);
|
|
7 | 7 |
|
8 |
use Rose::DB::Object::Helpers ();
|
|
8 |
use Rose::DB::Object::Helpers qw(has_loaded_related);
|
|
9 | 9 |
use SL::DB::MetaSetup::Invoice; |
10 | 10 |
use SL::DB::Manager::Invoice; |
11 | 11 |
use SL::DB::Helper::Payment qw(:ALL); |
... | ... | |
282 | 282 |
} |
283 | 283 |
} |
284 | 284 |
|
285 |
sub add_ar_amount_row { |
|
286 |
my ($self, %params ) = @_; |
|
287 |
|
|
288 |
# only allow this method for ar invoices (Debitorenbuchung) |
|
289 |
die "not an ar invoice" if $self->invoice and not $self->customer_id; |
|
290 |
|
|
291 |
die "add_ar_amount_row needs a chart object as chart param" unless $params{chart} && $params{chart}->isa('SL::DB::Chart'); |
|
292 |
die unless $params{chart}->link =~ /AR_amount/; |
|
293 |
|
|
294 |
my $acc_trans = []; |
|
295 |
|
|
296 |
my $roundplaces = 2; |
|
297 |
my ($netamount,$taxamount); |
|
298 |
|
|
299 |
$netamount = $params{amount} * 1; |
|
300 |
my $tax = SL::DB::Manager::Tax->find_by(id => $params{tax_id}) || die "Can't find tax with id " . $params{tax_id}; |
|
301 |
|
|
302 |
if ( $tax and $tax->rate != 0 ) { |
|
303 |
($netamount, $taxamount) = Form->calculate_tax($params{amount}, $tax->rate, $self->taxincluded, $roundplaces); |
|
304 |
}; |
|
305 |
next unless $netamount; # netamount mustn't be zero |
|
306 |
|
|
307 |
my $sign = $self->customer_id ? 1 : -1; |
|
308 |
my $acc = SL::DB::AccTransaction->new( |
|
309 |
amount => $netamount * $sign, |
|
310 |
chart_id => $params{chart}->id, |
|
311 |
chart_link => $params{chart}->link, |
|
312 |
transdate => $self->transdate, |
|
313 |
taxkey => $tax->taxkey, |
|
314 |
tax_id => $tax->id, |
|
315 |
project_id => $params{project_id}, |
|
316 |
); |
|
317 |
|
|
318 |
$self->add_transactions( $acc ); |
|
319 |
push( @$acc_trans, $acc ); |
|
320 |
|
|
321 |
if ( $taxamount ) { |
|
322 |
my $acc = SL::DB::AccTransaction->new( |
|
323 |
amount => $taxamount * $sign, |
|
324 |
chart_id => $tax->chart_id, |
|
325 |
chart_link => $tax->chart->link, |
|
326 |
transdate => $self->transdate, |
|
327 |
taxkey => $tax->taxkey, |
|
328 |
tax_id => $tax->id, |
|
329 |
); |
|
330 |
$self->add_transactions( $acc ); |
|
331 |
push( @$acc_trans, $acc ); |
|
332 |
}; |
|
333 |
return $acc_trans; |
|
334 |
}; |
|
335 |
|
|
336 |
sub create_ar_row { |
|
337 |
my ($self, %params) = @_; |
|
338 |
# to be called after adding all AR_amount rows, adds an AR row |
|
339 |
|
|
340 |
# only allow this method for ar invoices (Debitorenbuchung) |
|
341 |
die if $self->invoice and not $self->customer_id; |
|
342 |
die "create_ar_row needs a chart object as a parameter" unless $params{chart} and ref($params{chart}) eq 'SL::DB::Chart'; |
|
343 |
|
|
344 |
my @transactions = @{$self->transactions}; |
|
345 |
# die "invoice has no acc_transactions" unless scalar @transactions > 0; |
|
346 |
return 0 unless scalar @transactions > 0; |
|
347 |
|
|
348 |
my $chart = $params{chart} || SL::DB::Manager::Chart->find_by(id => $::instance_conf->get_ar_chart_id); |
|
349 |
die "illegal chart in create_ar_row" unless $chart; |
|
350 |
|
|
351 |
die "receivables chart must have link 'AR'" unless $chart->link eq 'AR'; |
|
352 |
|
|
353 |
my $acc_trans = []; |
|
354 |
|
|
355 |
# hardcoded entry for no tax: tax_id and taxkey should be 0 |
|
356 |
my $tax = SL::DB::Manager::Tax->find_by(id => 0, taxkey => 0) || die "Can't find tax with id 0 and taxkey 0"; |
|
357 |
|
|
358 |
my $sign = $self->customer_id ? -1 : 1; |
|
359 |
my $acc = SL::DB::AccTransaction->new( |
|
360 |
amount => $self->amount * $sign, |
|
361 |
chart_id => $params{chart}->id, |
|
362 |
chart_link => $params{chart}->link, |
|
363 |
transdate => $self->transdate, |
|
364 |
taxkey => $tax->taxkey, |
|
365 |
tax_id => $tax->id, |
|
366 |
); |
|
367 |
$self->add_transactions( $acc ); |
|
368 |
push( @$acc_trans, $acc ); |
|
369 |
return $acc_trans; |
|
370 |
}; |
|
371 |
|
|
372 |
sub validate_acc_trans { |
|
373 |
my ($self, %params) = @_; |
|
374 |
# should be able to check unsaved invoice objects with several acc_trans lines |
|
375 |
|
|
376 |
die "validate_acc_trans can't check invoice object with empty transactions" unless $self->transactions; |
|
377 |
|
|
378 |
my @transactions = @{$self->transactions}; |
|
379 |
# die "invoice has no acc_transactions" unless scalar @transactions > 0; |
|
380 |
return 0 unless scalar @transactions > 0; |
|
381 |
return 0 unless $self->has_loaded_related('transactions'); |
|
382 |
if ( $params{debug} ) { |
|
383 |
printf("starting validatation of invoice %s with trans_id %s and taxincluded %s\n", $self->invnumber, $self->id, $self->taxincluded); |
|
384 |
foreach my $acc ( @transactions ) { |
|
385 |
printf("chart: %s amount: %s tax_id: %s link: %s\n", $acc->chart->accno, $acc->amount, $acc->tax_id, $acc->chart->link); |
|
386 |
}; |
|
387 |
}; |
|
388 |
|
|
389 |
my $acc_trans_sum = sum map { $_->amount } @transactions; |
|
390 |
|
|
391 |
unless ( $::form->round_amount($acc_trans_sum, 10) == 0 ) { |
|
392 |
my $string = "sum of acc_transactions isn't 0: $acc_trans_sum\n"; |
|
393 |
|
|
394 |
if ( $params{debug} ) { |
|
395 |
foreach my $trans ( @transactions ) { |
|
396 |
$string .= sprintf(" %s %s %s\n", $trans->chart->accno, $trans->taxkey, $trans->amount); |
|
397 |
}; |
|
398 |
}; |
|
399 |
return 0; |
|
400 |
}; |
|
401 |
|
|
402 |
# only use the first AR entry, so it also works for paid invoices |
|
403 |
my @ar_transactions = map { $_->amount } grep { $_->chart_link eq 'AR' } @transactions; |
|
404 |
my $ar_sum = $ar_transactions[0]; |
|
405 |
# my $ar_sum = sum map { $_->amount } grep { $_->chart_link eq 'AR' } @transactions; |
|
406 |
|
|
407 |
unless ( $::form->round_amount($ar_sum * -1,2) == $::form->round_amount($self->amount,2) ) { |
|
408 |
if ( $params{debug} ) { |
|
409 |
printf("debug: (ar_sum) %s = %s (amount)\n", $::form->round_amount($ar_sum * -1,2) , $::form->round_amount($self->amount, 2) ); |
|
410 |
foreach my $trans ( @transactions ) { |
|
411 |
printf(" %s %s %s %s\n", $trans->chart->accno, $trans->taxkey, $trans->amount, $trans->chart->link); |
|
412 |
}; |
|
413 |
}; |
|
414 |
die sprintf("sum of ar (%s) isn't equal to invoice amount (%s)", $::form->round_amount($ar_sum * -1,2), $::form->round_amount($self->amount,2)); |
|
415 |
}; |
|
416 |
|
|
417 |
return 1; |
|
418 |
}; |
|
419 |
|
|
420 |
sub recalculate_amounts { |
|
421 |
my ($self, %params) = @_; |
|
422 |
# calculate and set amount and netamount from acc_trans objects |
|
423 |
|
|
424 |
croak ("Can only recalculate amounts for ar transactions") if $self->invoice; |
|
425 |
|
|
426 |
return undef unless $self->has_loaded_related('transactions'); |
|
427 |
|
|
428 |
my ($netamount, $taxamount); |
|
429 |
|
|
430 |
my @transactions = @{$self->transactions}; |
|
431 |
|
|
432 |
foreach my $acc ( @transactions ) { |
|
433 |
$netamount += $acc->amount if $acc->chart->link =~ /AR_amount/; |
|
434 |
$taxamount += $acc->amount if $acc->chart->link =~ /AR_tax/; |
|
435 |
}; |
|
436 |
|
|
437 |
$self->amount($netamount+$taxamount); |
|
438 |
$self->netamount($netamount); |
|
439 |
}; |
|
440 |
|
|
441 |
|
|
285 | 442 |
sub _post_create_assemblyitem_entries { |
286 | 443 |
my ($self, $assembly_entries) = @_; |
287 | 444 |
|
... | ... | |
502 | 659 |
|
503 | 660 |
See L<SL::DB::Object::basic_info>. |
504 | 661 |
|
662 |
=item C<recalculate_amounts %params> |
|
663 |
|
|
664 |
Calculate and set amount and netamount from acc_trans objects by summing up the |
|
665 |
values of acc_trans objects with AR_amount and AR_tax link charts. |
|
666 |
amount and netamount are set to the calculated values. |
|
667 |
|
|
668 |
=item C<validate_acc_trans> |
|
669 |
|
|
670 |
Checks if the sum of all associated acc_trans objects is 0 and checks whether |
|
671 |
the amount of the AR acc_transaction matches the AR amount. Only the first AR |
|
672 |
line is checked, because the sum of all AR lines is 0 for paid invoices. |
|
673 |
|
|
674 |
Returns 0 or 1. |
|
675 |
|
|
676 |
Can be called with a debug parameter which writes debug info to STDOUT, which is |
|
677 |
useful in console mode or while writing tests. |
|
678 |
|
|
679 |
my $ar = SL::DB::Manager::Invoice->get_first(); |
|
680 |
$ar->validate_acc_trans(debug => 1); |
|
681 |
|
|
682 |
=item C<create_ar_row %params> |
|
683 |
|
|
684 |
Creates a new acc_trans entry for the receivable (AR) entry of an existing AR |
|
685 |
invoice object, which already has some income and tax acc_trans entries. |
|
686 |
|
|
687 |
The acc_trans entry is also returned inside an array ref. |
|
688 |
|
|
689 |
Mandatory params are |
|
690 |
|
|
691 |
=over 2 |
|
692 |
|
|
693 |
=item * chart as an RDBO object, e.g. for bank. Must be a 'paid' chart. |
|
694 |
|
|
695 |
=back |
|
696 |
|
|
697 |
Currently the amount of the invoice object is used for the acc_trans amount. |
|
698 |
Use C<recalculate_amounts> before calling this mehtod if amount it isn't known |
|
699 |
yet or you didn't set it manually. |
|
700 |
|
|
701 |
=item C<add_ar_amount_row %params> |
|
702 |
|
|
703 |
Add a new entry for an existing AR invoice object. Creates an acc_trans entry, |
|
704 |
and also adds an acc_trans tax entry, if the tax has an associated tax chart. |
|
705 |
Also all acc_trans entries that were created are returned inside an array ref. |
|
706 |
|
|
707 |
Mandatory params are |
|
708 |
|
|
709 |
=over 2 |
|
710 |
|
|
711 |
=item * chart as an RDBO object, should be an income chart (link = AR_amount) |
|
712 |
|
|
713 |
=item * tax_id |
|
714 |
|
|
715 |
=item * amount |
|
716 |
|
|
717 |
=back |
|
718 |
|
|
505 | 719 |
=back |
506 | 720 |
|
507 | 721 |
=head1 TODO |
Auch abrufbar als: Unified diff
Neue Methoden um Debitorenbuchungen zu erstellen
Vorbereitung für Debitorenbuchungsimport, neue Methoden für SL::DB::Invoice
Objekte:
add_ar_amount_row - Erlösbuchungen hinzufügen, mit Steuerschlüssel
create_ar_row - acc-trans für Forderung hinzufügen
validate_acc_trans - Prüfen ob alle acc_trans-Einträge aufgehen
recalculate_amount - anhand acc_trans-Zeilen amount und netamount berechnen