From 430216b97bf4096f8770626125127168ed63c5a0 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Fri, 6 Feb 2015 11:26:26 +0100 Subject: [PATCH] =?utf8?q?Wiederkehrende=20Rechnungen:=20Berechnung=20f?= =?utf8?q?=C3=BCr=20Auftragswertperiodizit=C3=A4t=20angepasst?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- SL/BackgroundJob/CreatePeriodicInvoices.pm | 40 ++++++++++++++++- SL/DB/PeriodicInvoicesConfig.pm | 51 ++++++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/SL/BackgroundJob/CreatePeriodicInvoices.pm b/SL/BackgroundJob/CreatePeriodicInvoices.pm index e782d821b..651552a5f 100644 --- a/SL/BackgroundJob/CreatePeriodicInvoices.pm +++ b/SL/BackgroundJob/CreatePeriodicInvoices.pm @@ -66,7 +66,7 @@ sub run { } sub _log_msg { - my $message = join('', @_); + my $message = join('', 'SL::BackgroundJob::CreatePeriodicInvoices: ', @_); $message .= "\n" unless $message =~ m/\n$/; $::lxdebug->message(LXDebug::DEBUG1(), $message); } @@ -142,6 +142,40 @@ sub _replace_vars { $params{object}->$sub($str); } +sub _adjust_sellprices_for_period_lengths { + my (%params) = @_; + + my $billing_len = $params{config}->get_billing_period_length; + my $order_value_len = $params{config}->get_order_value_period_length; + + return if $billing_len == $order_value_len; + + my $is_last_invoice_in_cycle = $params{config}->is_last_bill_date_in_order_value_cycle(date => $params{period_start_date}); + + _log_msg("_adjust_sellprices_for_period_lengths: period_start_date $params{period_start_date} is_last_invoice_in_cycle $is_last_invoice_in_cycle billing_len $billing_len order_value_len $order_value_len"); + + if ($order_value_len < $billing_len) { + my $num_orders_per_invoice = $billing_len / $order_value_len; + + $_->sellprice($_->sellprice * $num_orders_per_invoice) for @{ $params{invoice}->items }; + + return; + } + + my $num_invoices_in_cycle = $order_value_len / $billing_len; + + foreach my $item (@{ $params{invoice}->items }) { + my $sellprice_one_invoice = $::form->round_amount($item->sellprice * $billing_len / $order_value_len, 2); + + if ($is_last_invoice_in_cycle) { + $item->sellprice($item->sellprice - ($num_invoices_in_cycle - 1) * $sellprice_one_invoice); + + } else { + $item->sellprice($sellprice_one_invoice); + } + } +} + sub _create_periodic_invoice { $main::lxdebug->enter_sub(); @@ -174,6 +208,8 @@ sub _create_periodic_invoice { _replace_vars(object => $item, vars => $time_period_vars, attribute => $_, attribute_format => ($_ eq 'longdescription' ? 'html' : 'text')) for qw(description longdescription); } + _adjust_sellprices_for_period_lengths(invoice => $invoice, config => $config, period_start_date => $period_start_date); + $invoice->post(ar_id => $config->ar_chart_id) || die; # like $form->add_shipto, but we don't need to check for a manual exception, @@ -210,6 +246,8 @@ sub _create_periodic_invoice { period_start_date => $period_start_date) ->save; + _log_msg("_create_invoice created for period start date $period_start_date id " . $invoice->id . " number " . $invoice->invnumber . " netamount " . $invoice->netamount . " amount " . $invoice->amount); + # die $invoice->transaction_description; })) { $::lxdebug->message(LXDebug->WARN(), "_create_invoice failed: " . join("\n", (split(/\n/, $self->{db_obj}->db->error))[0..2])); diff --git a/SL/DB/PeriodicInvoicesConfig.pm b/SL/DB/PeriodicInvoicesConfig.pm index 53ac1c679..0d6608565 100644 --- a/SL/DB/PeriodicInvoicesConfig.pm +++ b/SL/DB/PeriodicInvoicesConfig.pm @@ -28,7 +28,7 @@ sub get_order_value_period_length { } sub _log_msg { - $::lxdebug->message(LXDebug->DEBUG1(), join('', @_)); + $::lxdebug->message(LXDebug->DEBUG1(), join('', 'SL::DB::PeriodicInvoicesConfig: ', @_)); } sub handle_automatic_extension { @@ -87,11 +87,11 @@ sub calculate_invoice_dates { my ($self, %params) = @_; my $period_len = $self->get_billing_period_length; - my $cur_date = $self->first_billing_date || $self->start_date; + my $cur_date = ($self->first_billing_date || $self->start_date)->clone; my $end_date = $self->terminated ? $self->end_date : undef; $end_date //= DateTime->today_local->add(years => 100); - my $start_date = $params{past_dates} ? undef : $self->get_previous_billed_period_start_date; - $start_date = $start_date ? $start_date->add(days => 1) : $cur_date->clone; + my $start_date = $params{past_dates} ? undef : $self->get_previous_billed_period_start_date; + $start_date = $start_date ? $start_date->clone->add(days => 1) : $cur_date->clone; $start_date = max($start_date, $params{start_date}) if $params{start_date}; $end_date = min($end_date, $params{end_date}) if $params{end_date}; @@ -107,6 +107,27 @@ sub calculate_invoice_dates { return @dates; } +sub is_last_bill_date_in_order_value_cycle { + my ($self, %params) = @_; + + my $months_billing = $self->get_billing_period_length; + my $months_order_value = $self->get_order_value_period_length; + + return 1 if $months_billing >= $months_order_value; + + my $next_billing_date = $params{date}->clone->add(months => $months_billing); + my $date_itr = max($self->start_date, $self->first_billing_date || $self->start_date)->clone; + + _log_msg("is_last_billing_date_in_order_value_cycle start: id " . $self->id . " date_itr $date_itr start " . $self->start_date); + + $date_itr->add(months => $months_order_value) while $date_itr < $next_billing_date; + + _log_msg("is_last_billing_date_in_order_value_cycle end: refdate $params{date} next_billing_date $next_billing_date date_itr $date_itr months_billing $months_billing months_order_value $months_order_value result " + . ($date_itr == $next_billing_date)); + + return $date_itr == $next_billing_date; +} + 1; __END__ @@ -192,6 +213,28 @@ Otherwise (if C is 0) the property C will be set to 1, and the configuration will be saved. In this case C will be returned. +=item C + +Determines whether or not the mandatory parameter C, an instance +of L, is the last billing date within the cycle given by the +order value periodicity. Returns a truish value if this is the case +and a falsish value otherwise. + +This check is always true if the billing periodicity is longer than or +equal to the order value periodicity. For example, if you have an +order whose value is given for three months and you bill every six +months and you have twice the order value on each invoice, meaning +each invoice is itself the last invoice for not only one but two order +value cycles. + +Otherwise (if the order value periodicity is longer than the billing +periodicity) this function iterates over all eligible dates starting +with C (or C if C +is unset) and adding the order value length with each step. If the +date given by the C parameter plus the billing period length +equals one of those dates then the given date is indeed the date of +the last invoice in that particular order value cycle. + =back =head1 BUGS -- 2.20.1