Revision fe13d7f1
Von Tamino Steinert vor 9 Monaten hinzugefügt
SL/DB/PeriodicInvoicesConfig.pm | ||
---|---|---|
use SL::DB::MetaSetup::PeriodicInvoicesConfig;
|
||
use SL::DB::Manager::PeriodicInvoicesConfig;
|
||
|
||
use Params::Validate qw(:all);
|
||
use List::Util qw(max min);
|
||
|
||
use SL::Helper::DateTime;
|
||
|
||
__PACKAGE__->meta->initialize;
|
||
|
||
our %PERIOD_LENGTHS = ( o => 0, m => 1, q => 3, b => 6, y => 12 );
|
||
... | ... | |
our @PERIODICITIES = keys %PERIOD_LENGTHS;
|
||
our @ORDER_VALUE_PERIODICITIES = keys %ORDER_VALUE_PERIOD_LENGTHS;
|
||
|
||
sub calculate_invoice_dates {
|
||
my $self = shift;
|
||
|
||
my %params = validate(@_, {
|
||
start_date => {
|
||
callbacks => { is_date => \&_is_date, },
|
||
default => $self->start_date,
|
||
},
|
||
end_date => {
|
||
callbacks => { is_date => \&_is_date, },
|
||
default => DateTime->today_local,
|
||
},
|
||
});
|
||
|
||
my $start_date = DateTime->from_ymd($params{start_date});
|
||
my $end_date = DateTime->from_ymd($params{end_date});
|
||
|
||
if ($self->end_date
|
||
&& ($self->terminated || !$self->extend_automatically_by) ) {
|
||
$end_date = min($end_date, $self->end_date);
|
||
}
|
||
|
||
my $last_created_on_date = $self->get_previous_billed_period_start_date;
|
||
|
||
my @start_dates;
|
||
my $first_period_start_date = $self->first_billing_date || $self->start_date;
|
||
if ($self->periodicity ne 'o') {
|
||
my $billing_period_length = $self->get_billing_period_length;
|
||
my $months_first_period =
|
||
$first_period_start_date->year * 12 + $first_period_start_date->month;
|
||
|
||
my $month_to_start = $start_date->year * 12 + $start_date->month - $months_first_period;
|
||
$month_to_start += 1
|
||
if add_months($first_period_start_date, $month_to_start) < $start_date;
|
||
|
||
my $month_after_last_created = 0;
|
||
if ($last_created_on_date) {
|
||
$month_after_last_created =
|
||
$last_created_on_date->year * 12 + $last_created_on_date->month - $months_first_period;
|
||
$month_after_last_created += 1
|
||
if add_months($first_period_start_date, $month_after_last_created) <= $last_created_on_date;
|
||
}
|
||
|
||
my $months_from_period_start = max(
|
||
$month_to_start,
|
||
$month_after_last_created,
|
||
0);
|
||
|
||
my $period_count = int($months_from_period_start / $billing_period_length); # floor
|
||
$period_count += $months_from_period_start % $billing_period_length != 0 ? 1 : 0; # ceil
|
||
|
||
my $next_period_start_date = add_months($first_period_start_date, $period_count * $billing_period_length);
|
||
while ($next_period_start_date <= $end_date) {
|
||
push @start_dates, $next_period_start_date;
|
||
$period_count++;
|
||
$next_period_start_date = add_months($first_period_start_date, $period_count * $billing_period_length);
|
||
}
|
||
} else { # single
|
||
push @start_dates, $first_period_start_date
|
||
unless $last_created_on_date
|
||
|| $first_period_start_date < $start_date
|
||
|| $first_period_start_date > $end_date;
|
||
}
|
||
|
||
return @start_dates;
|
||
}
|
||
|
||
sub get_billing_period_length {
|
||
my $self = shift;
|
||
return $PERIOD_LENGTHS{ $self->periodicity } || 1;
|
||
... | ... | |
return $ORDER_VALUE_PERIOD_LENGTHS{ $self->order_value_periodicity } || 1;
|
||
}
|
||
|
||
sub add_months {
|
||
my ($date, $months) = @_;
|
||
|
||
my $start_months_of_date = $date->month;
|
||
$date = $date->clone();
|
||
my $new_date = $date->clone();
|
||
$new_date->add(months => $months);
|
||
# stay in month: 31.01 + 1 month should be 28.02 or 29.02 (not 03.03. or 02.03)
|
||
while (($start_months_of_date + $months) % 12 != $new_date->month % 12) {
|
||
$new_date->add(days => -1);
|
||
}
|
||
|
||
# if date was at end of month -> move new date also to end of month
|
||
if ($date->is_last_day_of_month()) {
|
||
return DateTime->last_day_of_month(year => $new_date->year, month => $new_date->month)
|
||
}
|
||
|
||
return $new_date
|
||
};
|
||
|
||
sub _is_date {
|
||
return !!DateTime->from_ymd($_[0]); # can also be a DateTime object
|
||
}
|
||
|
||
sub _log_msg {
|
||
$::lxdebug->message(LXDebug->DEBUG1(), join('', 'SL::DB::PeriodicInvoicesConfig: ', @_));
|
||
}
|
||
... | ... | |
|
||
# Add the automatic extension period to the new end date as long as
|
||
# the new end date is in the past. Then save it and get out.
|
||
$end_date->add(months => $self->extend_automatically_by) while $today > $end_date;
|
||
$end_date = add_months($end_date, $self->extend_automatically_by) while $today > $end_date;
|
||
_log_msg("new end date $end_date\n");
|
||
|
||
$self->end_date($end_date);
|
||
... | ... | |
return ref $date ? $date : $self->db->parse_date($date);
|
||
}
|
||
|
||
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)->clone;
|
||
my $end_date = $self->terminated || !$self->extend_automatically_by ? $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->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};
|
||
|
||
if ($self->periodicity eq 'o') {
|
||
return ($cur_date >= $start_date) && ($cur_date <= $end_date) ? ($cur_date) : ();
|
||
}
|
||
|
||
my @dates;
|
||
|
||
while ($cur_date <= $end_date) {
|
||
push @dates, $cur_date->clone if $cur_date >= $start_date;
|
||
|
||
$cur_date->add(months => $period_len);
|
||
}
|
||
|
||
return @dates;
|
||
}
|
||
|
||
sub is_last_bill_date_in_order_value_cycle {
|
||
my ($self, %params) = @_;
|
||
my $self = shift;
|
||
|
||
my %params = validate(@_, {
|
||
date => { callbacks => { is_date => \&_is_date, } },
|
||
});
|
||
|
||
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));
|
||
my $billing_date = DateTime->from_ymd($params{date});
|
||
my $first_date = $self->first_billing_date || $self->start_date;
|
||
|
||
return $date_itr == $next_billing_date;
|
||
return (12 * ($billing_date->year - $first_date->year) + $billing_date->month + $months_billing) % $months_order_value
|
||
== $first_date->month % $months_order_value;
|
||
}
|
||
|
||
sub disable_one_time_config {
|
Auch abrufbar als: Unified diff
S:D:PeriodicInvoicesConfig: Monatsgrenzen beachten