improve testing of prorate-sync behavior, #72928, #42108, and #34622
[freeside.git] / FS / t / suite / 05-prorate_sync_same_day.t
1 #!/usr/bin/perl
2
3 =head2 DESCRIPTION
4
5 Tests the effect of ordering and activating two sync_bill_date packages on
6 the same day. Ref RT#42108.
7
8 Formerly correct: If the packages have prorate_round_day = 1 (round
9 nearest), or 3 (round down) then the second package should be prorated one
10 day short. If they have prorate_round_day = 2 (round up), they should be
11 billed for the same amount. In both cases they should have the same next
12 bill date.
13
14 Revised RT#72928: The second package should be prorated one day short only
15 if the rounding mode is 1 (round nearest), as the nearest day is different
16 for the two packages.
17
18 =cut
19
20 use strict;
21 use Test::More tests => 9;
22 use FS::Test;
23 use Date::Parse 'str2time';
24 use Date::Format 'time2str';
25 use Test::MockTime qw(set_fixed_time);
26 use FS::cust_main;
27 use FS::cust_pkg;
28 use FS::Conf;
29 my $FS= FS::Test->new;
30
31 foreach my $prorate_mode (1, 2, 3) {
32   diag("prorate_round_day = $prorate_mode");
33   # Create a package def with the sync_bill_date option.
34   my $error;
35   my $old_part_pkg = $FS->qsearchs('part_pkg', { pkgpart => 5 });
36   my $part_pkg = $old_part_pkg->clone;
37   BAIL_OUT("existing pkgpart 5 is not a flat monthly package")
38     unless $part_pkg->freq eq '1' and $part_pkg->plan eq 'flat';
39   $error = $part_pkg->insert(
40     options => {  $old_part_pkg->options,
41                   'sync_bill_date' => 1,
42                   'prorate_round_day' => $prorate_mode, }
43   );
44
45   BAIL_OUT("can't configure package: $error") if $error;
46
47   my $pkgpart = $part_pkg->pkgpart;
48   # Create a clean customer with no other packages.
49   my $location = FS::cust_location->new({
50       address1  => '123 Example Street',
51       city      => 'Sacramento',
52       state     => 'CA',
53       country   => 'US',
54       zip       => '94901',
55   });
56   my $cust = FS::cust_main->new({
57       agentnum      => 1,
58       refnum        => 1,
59       last          => 'Customer',
60       first         => 'Sync bill date',
61       invoice_email => 'newcustomer@fake.freeside.biz',
62       bill_location => $location,
63       ship_location => $location,
64   });
65   $error = $cust->insert;
66   BAIL_OUT("can't create test customer: $error") if $error;
67
68   my @pkgs;
69   # Create and bill the first package.
70   set_fixed_time(str2time('2016-03-10 08:00'));
71   $pkgs[0] = FS::cust_pkg->new({ pkgpart => $pkgpart });
72   $error = $cust->order_pkg({ 'cust_pkg' => $pkgs[0] });
73   BAIL_OUT("can't order package: $error") if $error;
74   $error = $cust->bill_and_collect;
75   # Check the amount billed.
76   my ($cust_bill_pkg) = $pkgs[0]->cust_bill_pkg;
77   my $recur = $part_pkg->base_recur;
78   ok( $cust_bill_pkg->recur == $recur, "first package recur is $recur" )
79     or diag("first package recur is ".$cust_bill_pkg->recur);
80
81   # Create and bill the second package.
82   set_fixed_time(str2time('2016-03-10 16:00'));
83   $pkgs[1] = FS::cust_pkg->new({ pkgpart => $pkgpart });
84   $error = $cust->order_pkg({ 'cust_pkg' => $pkgs[1] });
85   BAIL_OUT("can't order package: $error") if $error;
86   $error = $cust->bill_and_collect;
87
88   # Check the amount billed.
89   if ( $prorate_mode == 1 ) {
90     # it should be one day short, in March
91     $recur = sprintf('%.2f', $recur * 30/31);
92   }
93   ($cust_bill_pkg) = $pkgs[1]->cust_bill_pkg;
94   ok( $cust_bill_pkg->recur == $recur, "second package recur is $recur" )
95     or diag("second package recur is ".$cust_bill_pkg->recur);
96
97   my @next_bill = map { time2str('%Y-%m-%d', $_->replace_old->get('bill')) } @pkgs;
98
99   ok( $next_bill[0] eq $next_bill[1],
100     "both packages will bill again on $next_bill[0]" )
101     or diag("first package bill date is $next_bill[0], second package is $next_bill[1]");
102 }