Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision fa589161

Von Tamino Steinert vor 3 Monaten hinzugefügt

  • ID fa589161d4d6a6a43150b0c5221c834b5b0ecc83
  • Vorgänger 5d90b614
  • Nachfolger fcc8a4a3

S:D:PeriodicInvoicesConfig: sep. Funk. für Start/End-Datum von Positionen

Unterschiede anzeigen:

SL/DB/PeriodicInvoicesConfig.pm
32 32
    },
33 33
  });
34 34

  
35
  my @start_dates = $self->calculate_invoice_dates(%params);
36
  return [] unless scalar @start_dates;
35
  my @invoice_dates = $self->calculate_invoice_dates(%params);
36
  return [] unless scalar @invoice_dates;
37 37

  
38 38
  my $orig_order = $self->order;
39 39

  
40 40
  my @orders;
41
  foreach my $period_start_date (@start_dates) {
41
  foreach my $invoice_date (@invoice_dates) {
42 42
    my $new_order = clone($orig_order);
43
    $new_order->reqdate($period_start_date);
43
    $new_order->reqdate($invoice_date);
44 44
    $new_order->tax_point(
45
      $self->add_months(
46
        $period_start_date, $self->get_billing_period_length || $self->get_order_value_period_length || 1
45
      $self->add_months($invoice_date,
46
        $self->get_billing_period_length || $self->get_order_value_period_length || 1
47 47
      )->subtract(days => 1)
48 48
    );
49 49
    my @items;
50 50
    for my $item ($orig_order->items) {
51

  
52 51
      my $new_item = $self->_create_item_for_period(
53 52
        order_item => $item,
54
        period_start_date => $period_start_date,
53
        invoice_date => $invoice_date,
55 54
      );
56

  
57 55
      push @items, $new_item if $new_item;
58 56
    }
59 57
    if (scalar @items) { # don't return empty orders
......
69 67
  my $self = shift;
70 68

  
71 69
  my %params = validate(@_, {
72
    period_start_date => { callbacks => { is_date => \&_is_date, } },
70
    invoice_date => { callbacks => { is_date => \&_is_date, } },
73 71
    order_item => { isa => 'SL::DB::OrderItem' },
74 72
  });
75 73

  
76
  my $item = $params{order_item};
77
  my $period_start_date = $params{period_start_date};
74
  my $item         = $params{order_item};
75
  my $invoice_date = DateTime->from_ymd($params{invoice_date});
78 76

  
79 77
  my $new_item = clone($item);
80 78

  
81
  my $item_config = $item->periodic_invoice_items_config;
82
  if ($item_config) {
83

  
84
    return if $item_config->start_date && $item_config->start_date > $period_start_date;
79
  my $item_count_and_date = $self->item_count_and_dates_in_period(
80
    invoice_date => $invoice_date,
81
    item => $new_item,
82
  );
85 83

  
86
    my $i_period = $item_config->get_item_period_length;
87
    my $b_period = $self->get_billing_period_length;
84
  my $count = $item_count_and_date->{count};
85
  return if $count == 0;
86
  my $item_start_date = $item_count_and_date->{start_date};
88 87

  
89
    return if $item_config->periodicity eq 'n';
90
    if ($item_config->periodicity eq 'o') {
91
      return if $item_config->once_invoice_id;
92
      my $start_date = $item_config->start_date
93
        || $self->get_previous_billed_period_start_date
94
        || $self->first_billing_date || $self->start_date;
95
      my @dates = $self->calculate_invoice_dates(
96
        start_date => $start_date,
97
        end_date => $self->add_months($start_date, $b_period),
98
      );
99
      my $once_date = scalar @dates ? $dates[0] : undef;
100
      return if $period_start_date != $once_date;
101
      return $new_item;
102
    } elsif ($i_period > $b_period) {
103
      if ($item_config->terminated || !$item_config->extend_automatically_by) {
104
        return if $item_config->end_date && $item_config->end_date < $period_start_date;
105
      }
106
      my $start_date = $item_config->start_date
107
        || $self->first_billing_date || $self->start_date;
108
      my $months_from_start_date =
109
          ($period_start_date->year  - $start_date->year) * 12
110
        + ($period_start_date->month - $start_date->month);
111
      $months_from_start_date-- if $start_date->day > $period_start_date->day;
112
      my $first_in_sub_period = $months_from_start_date % ($i_period / $b_period) == 0 ? 1 : 0;
113
      return if !$first_in_sub_period;
114
    } elsif ($i_period < $b_period) { # calc items period in last billing period
115
      my $max_periods = $b_period / $i_period;
116
      my $periods = $max_periods;
117
      if ($item_config->start_date) {
118
        $periods-- while $periods > 0
119
          && $self->add_months($period_start_date, -1 * ($periods - 1) * $i_period) < $item_config->start_date;
120
      }
121
      if ($item_config->end_date && ($item_config->terminated || !$item_config->extend_automatically_by)) {
122
        my $periods_from_end = 0;
123
        $periods_from_end++ while $periods_from_end < $periods
124
          && $self->add_months($period_start_date, -1 * ($periods_from_end)) > $item_config->end_date;
125
        $periods -= $periods_from_end;
126
      }
127
      return if $periods == 0;
128
      $new_item->qty($new_item->qty * $periods);
129
    } elsif ($i_period == $b_period) {
130
      return if $item_config->start_date && $item_config->start_date > $period_start_date;
131
      if ($item_config->terminated || !$item_config->extend_automatically_by) {
132
        return if $item_config->end_date && $item_config->end_date < $period_start_date;
133
      }
134
    }
135
  }
88
  $new_item->qty($new_item->qty * $count);
89
  $new_item->reqdate($item_start_date) if $new_item->reqdate;
136 90

  
137 91
  $new_item = $self->_adjust_sellprices_for_period(
138 92
      order_item => $new_item,
139
      period_start_date => $period_start_date,
93
      invoice_date => $invoice_date,
140 94
  );
141 95
  return $new_item
142 96
}
143 97

  
98
sub item_count_and_dates_in_period {
99
  my $self = shift;
100

  
101
  my %params = validate(@_, {
102
    invoice_date => { callbacks => { is_date => \&_is_date, } },
103
    item => { isa => 'SL::DB::OrderItem' },
104
  });
105

  
106
  my $period_length = $self->get_billing_period_length;
107

  
108
  my $invoice_date  = DateTime->from_ymd($params{invoice_date});
109
  my $item_config   = $params{item}->periodic_invoice_items_config
110
      or return {
111
      count => 1,
112
      start_date => $invoice_date,
113
      end_date => $self->add_months(
114
        $invoice_date, $period_length
115
      )->subtract(days => 1),
116
    };
117

  
118
  my %empty_return = (count => 0);
119

  
120
  return \%empty_return if $item_config->periodicity eq 'n';
121

  
122
  my $item_start_date = $item_config->start_date;
123
  if (!$item_start_date && $self->first_billing_date) {
124
    my $item_start_date = $self->first_billing_date;
125
    $item_start_date = $self->add_months(
126
      $item_start_date, $period_length
127
    ) while $item_start_date < $self->start_date;
128
  }
129
  $item_start_date ||= $self->start_date;
130

  
131
  return \%empty_return if $item_start_date > $invoice_date;
132

  
133
  if ($item_config->periodicity eq 'o') {
134
    return \%empty_return if $item_config->once_invoice_id;
135

  
136
    my $first_possible_date = max(
137
      $item_start_date, $self->get_previous_billed_period_start_date
138
    );
139
    $first_possible_date ||= $item_start_date;
140

  
141
    my @dates = $self->calculate_invoice_dates(
142
      start_date => $first_possible_date,
143
      end_date => $self->add_months($first_possible_date, $period_length),
144
    );
145
    my $once_date = scalar @dates ? $dates[0] : undef;
146
    return \%empty_return if $invoice_date != $once_date;
147
    return {
148
      count => 1,
149
      start_date => $item_start_date,
150
      end_date   => undef             # end_date don't affect once items
151
    };
152
  }
153

  
154
  my $period_start_date =
155
    $self->sub_months($invoice_date, $period_length)->add(days => 1);
156
  my $i_period_length = $item_config->get_item_period_length;
157
  my $item_start_date_in_period;
158
  if ($period_start_date > $item_start_date) {
159
    my $months_from_item_start_date =
160
          ($period_start_date->year  - $item_start_date->year) * 12
161
        + ($period_start_date->month - $item_start_date->month);
162
    $months_from_item_start_date++
163
      if $self->add_months($item_start_date, $months_from_item_start_date) < $period_start_date;
164
    my $months_offset_to_item_start_date_in_period =
165
      $months_from_item_start_date % $i_period_length ?
166
          $i_period_length - ($months_from_item_start_date % $i_period_length)
167
        : 0;
168
    $item_start_date_in_period = $self->add_months($item_start_date,
169
      $months_from_item_start_date + $months_offset_to_item_start_date_in_period
170
    );
171
  } else {
172
    $item_start_date_in_period = $item_start_date;
173
  }
174

  
175
  return \%empty_return if $item_start_date_in_period > $invoice_date;
176

  
177
  my $item_end_date;
178
  if ($item_config->terminated || !$item_config->extend_automatically_by) {
179
    $item_end_date = $item_config->end_date;
180
  } elsif ($self->terminated || !$self->extend_automatically_by) {
181
    $item_end_date = $self->end_date;
182
  }
183
  return \%empty_return if $item_end_date && $item_end_date < $period_start_date;
184

  
185
  if ($i_period_length < $period_length) { # calc items periods in last billing period
186
    my $max_periods = $period_length / $i_period_length;
187
    my $periods = $max_periods;
188

  
189
    my $periods_to_start = 0;
190
    $periods_to_start++ while $periods > $periods_to_start
191
      && $self->add_months(
192
        $period_start_date,
193
        ($periods_to_start + 1) * $i_period_length
194
      ) < $item_start_date_in_period;
195
    $periods -= $periods_to_start;
196

  
197
    my $periods_from_end = 0;
198
    if ($item_end_date) {
199
      $periods_from_end++ while $periods > $periods_from_end
200
        && $self->sub_months($invoice_date, $periods_from_end) > $item_end_date;
201
      $periods -= $periods_from_end;
202
    }
203

  
204
    return \%empty_return if $periods == 0;
205
    return {
206
      count => $periods,
207
      start_date => $item_start_date_in_period,
208
      end_date => $self->add_months(
209
        $item_start_date_in_period, $i_period_length * $periods
210
      )->subtract(days => 1),
211
    };
212
  } else {
213
    return {
214
      count => 1,
215
      start_date => $item_start_date_in_period,
216
      end_date => $self->add_months(
217
        $item_start_date_in_period, $i_period_length
218
      )->subtract(days => 1),
219
    };
220
  }
221
}
222

  
144 223
sub _adjust_sellprices_for_period {
145 224
  my $self = shift;
146 225

  
147 226
  my %params = validate(@_, {
148
    period_start_date => { callbacks => { is_date => \&_is_date, } },
227
    invoice_date => { callbacks => { is_date => \&_is_date, } },
149 228
    order_item => { isa => 'SL::DB::OrderItem' },
150 229
  });
151 230
  my $item = $params{order_item};
......
158 237
  return $item if $billing_len == $order_value_len;
159 238
  return $item if $billing_len == 0;
160 239

  
161
  my $is_last_invoice_in_cycle = $config->is_last_bill_date_in_order_value_cycle(date => $params{period_start_date});
240
  my $is_last_invoice_in_cycle = $config->is_last_invoice_date_in_order_value_cycle(date => $params{invoice_date});
162 241

  
163 242
  my $multiplier_per_invoice = $billing_len / $order_value_len;
164 243
  my $sellprice_one_invoice = $::form->round_amount($item->sellprice * $multiplier_per_invoice, 2);
......
283 362
}
284 363

  
285 364
sub add_months {
365
  validate_pos(@_,
366
    1,
367
    { callbacks => { is_date => \&_is_date, } },
368
    { type => SCALAR },
369
  );
286 370
  my ($self, $date, $months) = @_;
371
  $date = DateTime->from_ymd($date);
372

  
373
  return $date unless $months;
287 374

  
288 375
  my $start_months_of_date = $date->month;
289 376
  $date = $date->clone();
......
302 389
  return $new_date
303 390
};
304 391

  
392
sub sub_months {
393
  my ($self, $date, $months) = @_;
394
  return $self->add_months($date, -1 * $months);
395
}
396

  
305 397
sub _is_date {
306 398
   return !!DateTime->from_ymd($_[0]); # can also be a DateTime object
307 399
}
......
369 461
  return ref $date ? $date : $self->db->parse_date($date);
370 462
}
371 463

  
372
sub is_last_bill_date_in_order_value_cycle {
464
sub is_last_invoice_date_in_order_value_cycle {
373 465
  my $self    = shift;
374 466

  
375 467
  my %params = validate(@_, {
......
381 473

  
382 474
  return 1 if $months_billing >= $months_order_value;
383 475

  
384
  my $billing_date = DateTime->from_ymd($params{date});
476
  my $invoice_date = DateTime->from_ymd($params{date});
385 477
  my $first_date   = $self->first_billing_date || $self->start_date;
386 478

  
387
  return (12 * ($billing_date->year - $first_date->year) + $billing_date->month + $months_billing) % $months_order_value
479
  return (12 * ($invoice_date->year - $first_date->year) + $invoice_date->month + $months_billing) % $months_order_value
388 480
    == $first_date->month % $months_order_value;
389 481
}
390 482

  
......
489 581

  
490 582
=back
491 583

  
584
=item C<item_count_and_dates_in_period %params>
585

  
586
Return a hash reference with the C<count> and the C<start_date> and C<end_date>
587
of the period for a item which would be on a invoice created on specific date.
588
If C<count> is 0 C<start_date> and C<end_date> are not set. For items with a
589
'once' periodicity C<end_date> is not set.
590

  
591
=over 2
592

  
593
=item C<item>  a item of the order corresponding to this configuration
594

  
595
=item C<invoice_date> the date on which the invoice would be created
596

  
597
=back
598

  
492 599
=item C<get_billing_period_length>
493 600

  
494 601
Returns the number of months corresponding to the billing

Auch abrufbar als: Unified diff