Revision fe13d7f1
Von Tamino Steinert vor 3 Monaten hinzugefügt
SL/DB/PeriodicInvoicesConfig.pm | ||
---|---|---|
5 | 5 |
use SL::DB::MetaSetup::PeriodicInvoicesConfig; |
6 | 6 |
use SL::DB::Manager::PeriodicInvoicesConfig; |
7 | 7 |
|
8 |
use Params::Validate qw(:all); |
|
8 | 9 |
use List::Util qw(max min); |
9 | 10 |
|
11 |
use SL::Helper::DateTime; |
|
12 |
|
|
10 | 13 |
__PACKAGE__->meta->initialize; |
11 | 14 |
|
12 | 15 |
our %PERIOD_LENGTHS = ( o => 0, m => 1, q => 3, b => 6, y => 12 ); |
... | ... | |
14 | 17 |
our @PERIODICITIES = keys %PERIOD_LENGTHS; |
15 | 18 |
our @ORDER_VALUE_PERIODICITIES = keys %ORDER_VALUE_PERIOD_LENGTHS; |
16 | 19 |
|
20 |
sub calculate_invoice_dates { |
|
21 |
my $self = shift; |
|
22 |
|
|
23 |
my %params = validate(@_, { |
|
24 |
start_date => { |
|
25 |
callbacks => { is_date => \&_is_date, }, |
|
26 |
default => $self->start_date, |
|
27 |
}, |
|
28 |
end_date => { |
|
29 |
callbacks => { is_date => \&_is_date, }, |
|
30 |
default => DateTime->today_local, |
|
31 |
}, |
|
32 |
}); |
|
33 |
|
|
34 |
my $start_date = DateTime->from_ymd($params{start_date}); |
|
35 |
my $end_date = DateTime->from_ymd($params{end_date}); |
|
36 |
|
|
37 |
if ($self->end_date |
|
38 |
&& ($self->terminated || !$self->extend_automatically_by) ) { |
|
39 |
$end_date = min($end_date, $self->end_date); |
|
40 |
} |
|
41 |
|
|
42 |
my $last_created_on_date = $self->get_previous_billed_period_start_date; |
|
43 |
|
|
44 |
my @start_dates; |
|
45 |
my $first_period_start_date = $self->first_billing_date || $self->start_date; |
|
46 |
if ($self->periodicity ne 'o') { |
|
47 |
my $billing_period_length = $self->get_billing_period_length; |
|
48 |
my $months_first_period = |
|
49 |
$first_period_start_date->year * 12 + $first_period_start_date->month; |
|
50 |
|
|
51 |
my $month_to_start = $start_date->year * 12 + $start_date->month - $months_first_period; |
|
52 |
$month_to_start += 1 |
|
53 |
if add_months($first_period_start_date, $month_to_start) < $start_date; |
|
54 |
|
|
55 |
my $month_after_last_created = 0; |
|
56 |
if ($last_created_on_date) { |
|
57 |
$month_after_last_created = |
|
58 |
$last_created_on_date->year * 12 + $last_created_on_date->month - $months_first_period; |
|
59 |
$month_after_last_created += 1 |
|
60 |
if add_months($first_period_start_date, $month_after_last_created) <= $last_created_on_date; |
|
61 |
} |
|
62 |
|
|
63 |
my $months_from_period_start = max( |
|
64 |
$month_to_start, |
|
65 |
$month_after_last_created, |
|
66 |
0); |
|
67 |
|
|
68 |
my $period_count = int($months_from_period_start / $billing_period_length); # floor |
|
69 |
$period_count += $months_from_period_start % $billing_period_length != 0 ? 1 : 0; # ceil |
|
70 |
|
|
71 |
my $next_period_start_date = add_months($first_period_start_date, $period_count * $billing_period_length); |
|
72 |
while ($next_period_start_date <= $end_date) { |
|
73 |
push @start_dates, $next_period_start_date; |
|
74 |
$period_count++; |
|
75 |
$next_period_start_date = add_months($first_period_start_date, $period_count * $billing_period_length); |
|
76 |
} |
|
77 |
} else { # single |
|
78 |
push @start_dates, $first_period_start_date |
|
79 |
unless $last_created_on_date |
|
80 |
|| $first_period_start_date < $start_date |
|
81 |
|| $first_period_start_date > $end_date; |
|
82 |
} |
|
83 |
|
|
84 |
return @start_dates; |
|
85 |
} |
|
86 |
|
|
17 | 87 |
sub get_billing_period_length { |
18 | 88 |
my $self = shift; |
19 | 89 |
return $PERIOD_LENGTHS{ $self->periodicity } || 1; |
... | ... | |
25 | 95 |
return $ORDER_VALUE_PERIOD_LENGTHS{ $self->order_value_periodicity } || 1; |
26 | 96 |
} |
27 | 97 |
|
98 |
sub add_months { |
|
99 |
my ($date, $months) = @_; |
|
100 |
|
|
101 |
my $start_months_of_date = $date->month; |
|
102 |
$date = $date->clone(); |
|
103 |
my $new_date = $date->clone(); |
|
104 |
$new_date->add(months => $months); |
|
105 |
# stay in month: 31.01 + 1 month should be 28.02 or 29.02 (not 03.03. or 02.03) |
|
106 |
while (($start_months_of_date + $months) % 12 != $new_date->month % 12) { |
|
107 |
$new_date->add(days => -1); |
|
108 |
} |
|
109 |
|
|
110 |
# if date was at end of month -> move new date also to end of month |
|
111 |
if ($date->is_last_day_of_month()) { |
|
112 |
return DateTime->last_day_of_month(year => $new_date->year, month => $new_date->month) |
|
113 |
} |
|
114 |
|
|
115 |
return $new_date |
|
116 |
}; |
|
117 |
|
|
118 |
sub _is_date { |
|
119 |
return !!DateTime->from_ymd($_[0]); # can also be a DateTime object |
|
120 |
} |
|
121 |
|
|
28 | 122 |
sub _log_msg { |
29 | 123 |
$::lxdebug->message(LXDebug->DEBUG1(), join('', 'SL::DB::PeriodicInvoicesConfig: ', @_)); |
30 | 124 |
} |
... | ... | |
57 | 151 |
|
58 | 152 |
# Add the automatic extension period to the new end date as long as |
59 | 153 |
# the new end date is in the past. Then save it and get out. |
60 |
$end_date->add(months => $self->extend_automatically_by) while $today > $end_date;
|
|
154 |
$end_date = add_months($end_date, $self->extend_automatically_by) while $today > $end_date;
|
|
61 | 155 |
_log_msg("new end date $end_date\n"); |
62 | 156 |
|
63 | 157 |
$self->end_date($end_date); |
... | ... | |
81 | 175 |
return ref $date ? $date : $self->db->parse_date($date); |
82 | 176 |
} |
83 | 177 |
|
84 |
sub calculate_invoice_dates { |
|
85 |
my ($self, %params) = @_; |
|
86 |
|
|
87 |
my $period_len = $self->get_billing_period_length; |
|
88 |
my $cur_date = ($self->first_billing_date || $self->start_date)->clone; |
|
89 |
my $end_date = $self->terminated || !$self->extend_automatically_by ? $self->end_date : undef; |
|
90 |
$end_date //= DateTime->today_local->add(years => 100); |
|
91 |
my $start_date = $params{past_dates} ? undef : $self->get_previous_billed_period_start_date; |
|
92 |
$start_date = $start_date ? $start_date->clone->add(days => 1) : $cur_date->clone; |
|
93 |
|
|
94 |
$start_date = max($start_date, $params{start_date}) if $params{start_date}; |
|
95 |
$end_date = min($end_date, $params{end_date}) if $params{end_date}; |
|
96 |
|
|
97 |
if ($self->periodicity eq 'o') { |
|
98 |
return ($cur_date >= $start_date) && ($cur_date <= $end_date) ? ($cur_date) : (); |
|
99 |
} |
|
100 |
|
|
101 |
my @dates; |
|
102 |
|
|
103 |
while ($cur_date <= $end_date) { |
|
104 |
push @dates, $cur_date->clone if $cur_date >= $start_date; |
|
105 |
|
|
106 |
$cur_date->add(months => $period_len); |
|
107 |
} |
|
108 |
|
|
109 |
return @dates; |
|
110 |
} |
|
111 |
|
|
112 | 178 |
sub is_last_bill_date_in_order_value_cycle { |
113 |
my ($self, %params) = @_; |
|
179 |
my $self = shift; |
|
180 |
|
|
181 |
my %params = validate(@_, { |
|
182 |
date => { callbacks => { is_date => \&_is_date, } }, |
|
183 |
}); |
|
114 | 184 |
|
115 | 185 |
my $months_billing = $self->get_billing_period_length; |
116 | 186 |
my $months_order_value = $self->get_order_value_period_length; |
117 | 187 |
|
118 | 188 |
return 1 if $months_billing >= $months_order_value; |
119 | 189 |
|
120 |
my $next_billing_date = $params{date}->clone->add(months => $months_billing); |
|
121 |
my $date_itr = max($self->start_date, $self->first_billing_date || $self->start_date)->clone; |
|
122 |
|
|
123 |
_log_msg("is_last_billing_date_in_order_value_cycle start: id " . $self->id . " date_itr $date_itr start " . $self->start_date); |
|
124 |
|
|
125 |
$date_itr->add(months => $months_order_value) while $date_itr < $next_billing_date; |
|
126 |
|
|
127 |
_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 " |
|
128 |
. ($date_itr == $next_billing_date)); |
|
190 |
my $billing_date = DateTime->from_ymd($params{date}); |
|
191 |
my $first_date = $self->first_billing_date || $self->start_date; |
|
129 | 192 |
|
130 |
return $date_itr == $next_billing_date; |
|
193 |
return (12 * ($billing_date->year - $first_date->year) + $billing_date->month + $months_billing) % $months_order_value |
|
194 |
== $first_date->month % $months_order_value; |
|
131 | 195 |
} |
132 | 196 |
|
133 | 197 |
sub disable_one_time_config { |
Auch abrufbar als: Unified diff
S:D:PeriodicInvoicesConfig: Monatsgrenzen beachten