doc: return fields for customer_info, RT#84796
[freeside.git] / bin / freeside-migrate-events
1 #!/usr/bin/perl -w
2
3 use strict;
4 #use Getopt::Std;
5 use FS::UID qw( adminsuidsetup dbh );
6 use FS::Record qw( qsearch );
7 use FS::part_bill_event;
8 use FS::part_event;
9 use FS::cust_bill_event;
10 use FS::cust_event;
11
12 #use vars qw( $opt_m );
13 #getopts('m');
14
15 my $user = shift or die &usage;
16 adminsuidsetup($user);
17
18 my %plan2action = (
19   'fee'                    => 'fee',
20   'fee_percent'            => 'NOTYET', #XXX need fee_percent action
21   'suspend'                => 'suspend',
22   'suspend-if-balance'     => 'suspend', #"if balance" becomes the balance cond
23   'suspend-if-pkgpart'     => 'suspend_if_pkgpart',
24   'suspend-unless-pkgpart' => 'suspend_unless_pkgpart',
25   'cancel'                 => 'cancel',
26   'addpost'                => 'addpost',
27   'comp'                   => 'NOTYET', #XXX or N/A or something
28   'credit'                 => 'writeoff',
29   'realtime-card'          => 'cust_bill_realtime_card',
30   'realtime-check'         => 'cust_bill_realtime_check',
31   'realtime-lec'           => 'cust_bill_realtime_lec',
32   'batch-card'             => 'cust_bill_batch',
33   #?'retriable'             =>
34   'send'                   => 'cust_bill_send',
35   'send_email'             => 'cust_bill_email',
36   'send_alternate'         => 'cust_bill_send_alternate',
37   'send_if_newest'         => 'cust_bill_send_if_newest',
38   'send_agent'             => 'cust_bill_send_agent',
39   'send_csv_ftp'           => 'cust_bill_send_csv_ftp',
40   'spool_csv',             => 'cust_bill_spool_csv',
41   'bill'                   => 'bill',
42   'apply'                  => 'apply',
43   'collect'                => 'collect',
44 );
45
46
47 foreach my $part_bill_event (
48   qsearch({
49     'table'   => 'part_bill_event',
50   })
51 ) {
52
53   print $part_bill_event->event;
54
55   my $action = $plan2action{ $part_bill_event->plan };
56
57   if ( $action eq 'NOTYET' ) {
58     warn "not migrating part_bill_event.eventpart ".$part_bill_event->eventpart.
59          "; ". $part_bill_event->plan. " plan not (yet) handled";
60     next;
61   } elsif ( ! $action ) {
62     warn "not migrating part_bill_event.eventpart ".$part_bill_event->eventpart.
63          "; unknown plan ". $part_bill_event->plan;
64     next;
65   }
66
67   my %plandata = map { /^(\w+) (.*)$/; ($1, $2); }
68                      split(/\n/, $part_bill_event->plandata);
69
70   #XXX may need to fudge some other plandata2option names
71
72   my $balanceover = 0;
73   my $honor_dundate = 0;
74
75   if ( $part_bill_event->plan eq 'suspend-if-balance' ) {
76     $balanceover = delete $plandata{'balanceover'};
77     $honor_dundate = ( (delete $plandata{'balance_honor_dundate'}) =~ /1/ );
78   }
79
80   my $part_event = new FS::part_event {
81     'event'      => $part_bill_event->event,
82     'eventtable' => 'cust_bill',
83     'check_freq' => $part_bill_event->freq || '1d',
84     'weight'     => $part_bill_event->weight,
85     'action'     => $action,
86     'disabled'   => $part_bill_event->disabled,
87   };
88
89   my $error = $part_event->insert(\%plandata);
90   die "error inserting part_event: $error\n" if $error;
91
92   print ' '. $part_event->eventpart;
93
94   my $once = new FS::part_event_condition {
95     'eventpart'     => $part_event->eventpart,
96     'conditionname' => 'once'
97   };
98   $error = $once->insert;
99   die $error if $error;
100
101   my $balance = new FS::part_event_condition {
102     'eventpart'     => $part_event->eventpart,
103     'conditionname' => 'balance'
104   };
105   $error = $balance->insert( 'balance' => $balanceover );
106   die $error if $error;
107
108   my $cust_bill_owed = new FS::part_event_condition {
109     'eventpart'     => $part_event->eventpart,
110     'conditionname' => 'cust_bill_owed'
111   };
112   $error = $cust_bill_owed->insert( 'owed' => 0 );
113   die $error if $error;
114
115   my $payby = new FS::part_event_condition {
116     'eventpart'     => $part_event->eventpart,
117     'conditionname' => 'payby'
118   };
119   $error = $payby->insert( 'payby' => { $part_bill_event->payby => 1 } );
120   die $error if $error;
121
122   if ( $part_bill_event->seconds ) {
123
124     my $age = new FS::part_event_condition { 
125       'eventpart'     => $part_event->eventpart,
126       'conditionname' => 'cust_bill_age'
127     };
128     $error = $age->insert( 'age' => ($part_bill_event->seconds/86400 ).'d' );
129     die $error if $error;
130
131   }
132
133   if ( $honor_dundate ) { 
134     my $dundate = new FS::part_event_condition {
135       'eventpart'     => $part_event->eventpart,
136       'conditionname' => 'dundate'
137     };
138     $error = $dundate->insert();
139     die $error if $error;
140   }
141   
142   #my $derror = $part_bill_event->delete;
143   #die "error removing part_bill_event: $derror\n" if $derror;
144
145 #  if ( $opt_m ) {
146
147     my $sth = dbh->prepare('
148       INSERT INTO cust_event ( eventpart, tablenum, _date, status, statustext )
149                        SELECT     ?     ,  invnum , _date, status, statustext
150                          FROM cust_bill_event WHERE eventpart = ?
151     ') or die dbh->errstr;
152
153     $sth->execute( $part_event->eventpart, $part_bill_event->eventpart )
154       or die $sth->errstr;
155
156 #  } else {
157 #
158 #    foreach my $cust_bill_event (
159 #      qsearch({
160 #        'table'     => 'cust_bill_event',
161 #        'hashref'   => { 'eventpart' => $part_bill_event->eventpart, },
162 #      })
163 #    ) {
164 #
165 #      my $cust_event = new FS::cust_event {
166 #        'eventpart'  => $part_event->eventpart,
167 #        'tablenum'   => $cust_bill_event->invnum,
168 #        '_date'      => $cust_bill_event->_date,
169 #        'status'     => $cust_bill_event->status,
170 #        'statustext' => $cust_bill_event->statustext,
171 #      };
172 #
173 #      my $cerror = $cust_event->insert;
174 #      #die "error inserting cust_event: $cerror\n" if $cerror;
175 #      warn "error inserting cust_event: $cerror\n" if $cerror;
176 #    
177 #      #my $dcerror = $cust_bill_event->delete;
178 #      #die "error removing cust_bill_event: $dcerror\n" if $dcerror;
179 #
180 #      print ".";
181 #
182 #    }
183 #
184 #  }
185
186   print "\n";
187
188 }
189
190 sub usage {
191   die "Usage:\n  freeside-migrate-events user\n"; 
192 }
193
194 =head1 NAME
195
196 freeside-migrate-events - Migrates 1.7/1.8-style invoice events to
197                           1.9/2.0-style billing events
198
199 =head1 SYNOPSIS
200
201   freeside-migrate-events
202
203 =head1 DESCRIPTION
204
205 Migrates events from L<FS::part_bill_event> to L<FS::part_event> and friends,
206 and from L<FS::cust_bill_event> records to L<FS::cust_event>
207
208 =head1 BUGS
209
210 Doesn't migrate any action options yet.
211
212 Doesn't translate option names that changed.
213
214 Doesn't migrate reasons.
215
216 Doesn't delete the old events (which is not a big deal, since the new code
217 won't run them...)
218
219 Can take lots of memory for large databases.
220
221 =head1 SEE ALSO
222
223 =cut
224
225 1;
226
227 __END__
228
229 #part_bill_event      part_event
230 #
231 #eventpart            n/a
232 #event                event
233 #freq                 check_freq
234 #payby                part_event_condition.conditionname = payby
235 #eventcode            PARSE_WITH_REGEX (probably can just get from plandata)
236 #seconds              part_event_condition.conditionname = cust_bill_age
237 #plandata             PARSE_WITH_REGEX (along with eventcode, yuck)
238 #reason               part_event_option.optionname = reason
239 #disabled             disabled
240 #
241
242     #these might help parse existing eventcode
243
244     $c =~ /^\s*\$cust_main\->(suspend|cancel|invoicing_list_addpost|bill|collect)\(\);\s*("";)?\s*$/
245
246       or $c =~ /^\s*\$cust_bill\->(comp|realtime_(card|ach|lec)|batch_card|send)\((%options)*\);\s*$/
247
248       or $c =~ /^\s*\$cust_bill\->send(_if_newest)?\(\'[\w\-\s]+\'\s*(,\s*(\d+|\[\s*\d+(,\s*\d+)*\s*\])\s*,\s*'[\w\@\.\-\+]*'\s*)?\);\s*$/
249
250 #      or $c =~ /^\s*\$cust_main\->apply_payments; \$cust_main->apply_credits; "";\s*$/
251       or $c =~ /^\s*\$cust_main\->apply_payments_and_credits; "";\s*$/
252
253       or $c =~ /^\s*\$cust_main\->charge\( \s*\d*\.?\d*\s*,\s*\'[\w \!\@\#\$\%\&\(\)\-\+\;\:\"\,\.\?\/]*\'\s*\);\s*$/
254
255       or $c =~ /^\s*\$cust_main\->suspend_(if|unless)_pkgpart\([\d\,\s]*\);\s*$/
256
257       or $c =~ /^\s*\$cust_bill\->cust_suspend_if_balance_over\([\d\.\s]*\);\s*$/
258
259       or do {
260         #log
261         return "illegal eventcode: $c";
262       };
263
264   }
265
266