1.1.4 release, fix postgresql
[freeside.git] / bin / fs-setup
1 #!/usr/bin/perl -Tw
2 #
3 # create database and necessary tables, etc.  DBI version.
4 #
5 # ivan@sisd.com 97-nov-8,9
6 #
7 # agent_type and type_pkgs added.
8 # (index need to be declared, & primary keys shoudln't have mysql syntax)
9 # ivan@sisd.com 97-nov-13
10 #
11 # pulled modified version back out of register.cgi ivan@sisd.com 98-feb-21
12 #
13 # removed extraneous sample data ivan@sisd.com 98-mar-23
14 #
15 # gained the big hash from dbdef.pm, dbdef.pm usage rewrite ivan@sisd.com
16 # 98-apr-19 - 98-may-11 plus
17 #
18 # finished up ivan@sisd.com 98-jun-1
19 #
20 # part_svc fields are all forced NULL, not the opposite
21 # hmm: also are forced varchar($char_d) as fixed '0' for things like
22 # uid is Not Good.  will this break anything else?
23 # ivan@sisd.com 98-jun-29
24 #
25 # ss is 11 chars ivan@sisd.com 98-jul-20
26 #
27 # setup of arbitrary radius fields ivan@sisd.com 98-aug-9
28 #
29 # ouch, removed index on company name that wasn't supposed to be there
30 # ivan@sisd.com 98-sep-4
31 #
32 # fix radius attributes ivan@sisd.com 98-sep-27
33
34 #to delay loading dbdef until we're ready
35 BEGIN { $FS::Record::setup_hack = 1; }
36
37 use strict;
38 use DBI;
39 use FS::dbdef;
40 use FS::UID qw(adminsuidsetup datasrc);
41 use FS::Record;
42 use FS::cust_main_county;
43
44 #needs to match FS::Record
45 my($dbdef_file) = "/var/spool/freeside/dbdef.". datasrc;
46
47 ###
48
49 print "\nEnter the maximum username length: ";
50 my($username_len)=&getvalue;
51
52 print "\n\n", <<END, ":";
53 Freeside tracks the RADIUS attributes User-Name, Password and Framed-IP-Address
54 for each user.  Enter any additional RADIUS attributes you need to track for
55 each user, separated by whitespace.
56 END
57 my @attributes = map { s/\-/_/g; $_; } split(" ",&getvalue);
58
59 sub getvalue {
60   my($x)=scalar(<STDIN>);
61   chop $x;
62   $x;
63 }
64
65 ###
66
67 my($char_d) = 80; #default maxlength for text fields
68
69 #my(@date_type)  = ( 'timestamp', '', ''     );
70 my(@date_type)  = ( 'int', 'NULL', ''     );
71 my(@perl_type) = ( 'varchar', 'NULL', ''   ); 
72 my(@money_type);
73 if (datasrc =~ m/Pg/) { #Pg can't do decimal(10,2)
74   @money_type = ( 'money',   '', '' );
75 } else {
76   @money_type = ( 'decimal',   '', '10,2' );
77 }
78
79 ###
80 # create a dbdef object from the old data structure
81 ###
82
83 my(%tables)=&tables_hash_hack;
84
85 #turn it into objects
86 my($dbdef) = new FS::dbdef ( map {  
87   my(@columns);
88   while (@{$tables{$_}{'columns'}}) {
89     my($name,$type,$null,$length)=splice @{$tables{$_}{'columns'}}, 0, 4;
90     push @columns, new FS::dbdef_column ( $name,$type,$null,$length );
91   }
92   FS::dbdef_table->new(
93     $_,
94     $tables{$_}{'primary_key'},
95     #FS::dbdef_unique->new(@{$tables{$_}{'unique'}}),
96     #FS::dbdef_index->new(@{$tables{$_}{'index'}}),
97     FS::dbdef_unique->new($tables{$_}{'unique'}),
98     FS::dbdef_index->new($tables{$_}{'index'}),
99     @columns,
100   );
101 } (keys %tables) );
102
103 #add radius attributes to svc_acct
104
105 my($svc_acct)=$dbdef->table('svc_acct');
106
107 my($attribute);
108 foreach $attribute (@attributes) {
109   $svc_acct->addcolumn ( new FS::dbdef_column (
110     'radius_'. $attribute,
111     'varchar',
112     'NULL',
113     $char_d,
114   ));
115 }
116
117 #make part_svc table (but now as object)
118
119 my($part_svc)=$dbdef->table('part_svc');
120
121 #because of svc_acct_pop
122 #foreach (grep /^svc_/, $dbdef->tables) { 
123 #foreach (qw(svc_acct svc_acct_sm svc_charge svc_domain svc_wo)) {
124 foreach (qw(svc_acct svc_acct_sm svc_domain)) {
125   my($table)=$dbdef->table($_);
126   my($col);
127   foreach $col ( $table->columns ) {
128     next if $col =~ /^svcnum$/;
129     $part_svc->addcolumn( new FS::dbdef_column (
130       $table->name. '__' . $table->column($col)->name,
131       'varchar', #$table->column($col)->type, 
132       'NULL',
133       $char_d, #$table->column($col)->length,
134     ));
135     $part_svc->addcolumn ( new FS::dbdef_column (
136       $table->name. '__'. $table->column($col)->name . "_flag",
137       'char',
138       'NULL',
139       1,
140     ));
141   }
142 }
143
144 #important
145 $dbdef->save($dbdef_file);
146 FS::Record::reload_dbdef;
147
148 ###
149 # create 'em
150 ###
151
152 my($dbh)=adminsuidsetup;
153
154 #create tables
155 $|=1;
156
157 my($table);
158 foreach  ($dbdef->tables) {
159   my($table)=$dbdef->table($_);
160   print "Creating $_...";
161
162   my($statement);
163
164   #create table
165   foreach $statement ($table->sql_create_table(datasrc)) {
166     #print $statement, "\n"; 
167     $dbh->do( $statement )
168       or die "CREATE error: ",$dbh->errstr, "\ndoing statement: $statement";
169   }
170
171   print "\n";
172 }
173
174 #not really sample data (and shouldn't default to US)
175
176 #cust_main_county
177 foreach ( qw(
178 AL AK AS AZ AR CA CO CT DC DE FM FL GA GU HI ID IL IN IA KS KY LA
179 ME MH MD MA MI MN MS MO MT NC ND NE NH NJ NM NV NY MP OH OK OR PA PW PR RI 
180 SC SD TN TX TT UT VT VI VA WA WV WI WY AE AA AP
181 ) ) {
182   my($cust_main_county)=create FS::cust_main_county({
183     'state' => $_,
184     'tax'   => 0,
185   });  
186   my($error);
187   $error=$cust_main_county->insert;
188   die $error if $error;
189 }
190
191 $dbh->disconnect or die $dbh->errstr;
192
193 ###
194 # Now it becomes an object.  much better.
195 ###
196 sub tables_hash_hack {
197
198   #note that s/(date|change)/_$1/; to avoid keyword conflict.
199   #put a kludge in FS::Record to catch this or? (pry need some date-handling
200   #stuff anyway also)
201
202   my(%tables)=( #yech.}
203
204     'agent' => {
205       'columns' => [
206         'agentnum', 'int',            '',     '',
207         'agent',    'varchar',           '',     $char_d,
208         'typenum',  'int',            '',     '',
209         'freq',     'smallint',       'NULL', '',
210         'prog',     @perl_type,
211       ],
212       'primary_key' => 'agentnum',
213       'unique' => [ [] ],
214       'index' => [ ['typenum'] ],
215     },
216
217     'agent_type' => {
218       'columns' => [
219         'typenum',   'int',  '', '',
220         'atype',     'varchar', '', $char_d,
221       ],
222       'primary_key' => 'typenum',
223       'unique' => [ [] ],
224       'index' => [ [] ],
225     },
226
227     'type_pkgs' => {
228       'columns' => [
229         'typenum',   'int',  '', '',
230         'pkgpart',   'int',  '', '',
231       ],
232       'primary_key' => '',
233       'unique' => [ ['typenum', 'pkgpart'] ],
234       'index' => [ ['typenum'] ],
235     },
236
237     'cust_bill' => {
238       'columns' => [
239         'invnum',    'int',  '', '',
240         'custnum',   'int',  '', '',
241         '_date',     @date_type,
242         'charged',   @money_type,
243         'owed',      @money_type,
244         'printed',   'int',  '', '',
245       ],
246       'primary_key' => 'invnum',
247       'unique' => [ [] ],
248       'index' => [ ['custnum'] ],
249     },
250
251     'cust_bill_pkg' => {
252       'columns' => [
253         'pkgnum',  'int', '', '',
254         'invnum',  'int', '', '',
255         'setup',   @money_type,
256         'recur',   @money_type,
257         'sdate',   @date_type,
258         'edate',   @date_type,
259       ],
260       'primary_key' => '',
261       'unique' => [ ['pkgnum', 'invnum'] ],
262       'index' => [ ['invnum'] ],
263     },
264
265     'cust_credit' => {
266       'columns' => [
267         'crednum',  'int', '', '',
268         'custnum',  'int', '', '',
269         '_date',    @date_type,
270         'amount',   @money_type,
271         'credited', @money_type,
272         'otaker',   'varchar', '', 8,
273         'reason',   'varchar', '', 255,
274       ],
275       'primary_key' => 'crednum',
276       'unique' => [ [] ],
277       'index' => [ ['custnum'] ],
278     },
279
280     'cust_main' => {
281       'columns' => [
282         'custnum',  'int',  '',     '',
283         'agentnum', 'int',  '',     '',
284         'last',     'varchar', '',     $char_d,
285         'first',    'varchar', '',     $char_d,
286         'ss',       'char', 'NULL', 11,
287         'company',  'varchar', 'NULL', $char_d,
288         'address1', 'varchar', '',     $char_d,
289         'address2', 'varchar', 'NULL', $char_d,
290         'city',     'varchar', '',     $char_d,
291         'county',   'varchar', 'NULL', $char_d,
292         'state',    'char', '',     2,
293         'zip',      'varchar', '',     10,
294         'country',  'char', '',     2,
295         'daytime',  'varchar', 'NULL', 20,
296         'night',    'varchar', 'NULL', 20,
297         'fax',      'varchar', 'NULL', 12,
298         'payby',    'char', '',     4,
299         'payinfo',  'varchar', 'NULL', 16,
300         'paydate',  @date_type,
301         'payname',  'varchar', 'NULL', $char_d,
302         'tax',      'char', 'NULL', 1,
303         'otaker',   'varchar', '',     8,
304         'refnum',   'int',  '',     '',
305       ],
306       'primary_key' => 'custnum',
307       'unique' => [ [] ],
308       #'index' => [ ['last'], ['company'] ],
309       'index' => [ ['last'], ],
310     },
311
312     'cust_main_county' => { #county+state are checked off the cust_main_county
313                             #table for validation and to provide a tax rate.
314                             #add country?
315       'columns' => [
316         'taxnum',   'int',   '',    '',
317         'state',    'char',  '',    2,  #two letters max in US... elsewhere?
318         'county',   'varchar',  'NULL',    $char_d,
319         'tax',      'real',  '',    '', #tax %
320       ],
321       'primary_key' => 'taxnum',
322       'unique' => [ [] ],
323   #    'unique' => [ ['taxnum'], ['state', 'county'] ],
324       'index' => [ [] ],
325     },
326
327     'cust_pay' => {
328       'columns' => [
329         'paynum',   'int',    '',   '',
330         'invnum',   'int',    '',   '',
331         'paid',     @money_type,
332         '_date',    @date_type,
333         'payby',    'char',   '',     4, # CARD/BILL/COMP, should be index into
334                                          # payment type table.
335         'payinfo',  'varchar',   'NULL', 16,  #see cust_main above
336         'paybatch', 'varchar',   'NULL', $char_d, #for auditing purposes.
337       ],
338       'primary_key' => 'paynum',
339       'unique' => [ [] ],
340       'index' => [ ['invnum'] ],
341     },
342
343     'cust_pay_batch' => { #what's this used for again?  list of customers
344                           #in current CARD batch? (necessarily CARD?)
345       'columns' => [
346         'invnum',   'int',    '',   '',
347         'custnum',   'int',    '',   '',
348         'last',     'varchar', '',     $char_d,
349         'first',    'varchar', '',     $char_d,
350         'address1', 'varchar', '',     $char_d,
351         'address2', 'varchar', 'NULL', $char_d,
352         'city',     'varchar', '',     $char_d,
353         'state',    'char', '',     2,
354         'zip',      'varchar', '',     10,
355         'country',  'char', '',     2,
356         'trancode', 'int', '', '',
357         'cardnum',  'varchar', '',     16,
358         'exp',      @date_type,
359         'payname',  'varchar', 'NULL', $char_d,
360         'amount',   @money_type,
361       ],
362       'primary_key' => '',
363       'unique' => [ [] ],
364       'index' => [ ['invnum'], ['custnum'] ],
365     },
366
367     'cust_pkg' => {
368       'columns' => [
369         'pkgnum',    'int',    '',   '',
370         'custnum',   'int',    '',   '',
371         'pkgpart',   'int',    '',   '',
372         'otaker',    'varchar', '', 8,
373         'setup',     @date_type,
374         'bill',      @date_type,
375         'susp',      @date_type,
376         'cancel',    @date_type,
377         'expire',    @date_type,
378       ],
379       'primary_key' => 'pkgnum',
380       'unique' => [ [] ],
381       'index' => [ ['custnum'] ],
382     },
383
384     'cust_refund' => {
385       'columns' => [
386         'refundnum',    'int',    '',   '',
387         'crednum',      'int',    '',   '',
388         '_date',        @date_type,
389         'refund',       @money_type,
390         'otaker',       'varchar',   '',   8,
391         'reason',       'varchar',   '',   $char_d,
392         'payby',        'char',   '',     4, # CARD/BILL/COMP, should be index
393                                              # into payment type table.
394         'payinfo',      'varchar',   'NULL', 16,  #see cust_main above
395       ],
396       'primary_key' => 'refundnum',
397       'unique' => [ [] ],
398       'index' => [ ['crednum'] ],
399     },
400
401     'cust_svc' => {
402       'columns' => [
403         'svcnum',    'int',    '',   '',
404         'pkgnum',    'int',    '',   '',
405         'svcpart',   'int',    '',   '',
406       ],
407       'primary_key' => 'svcnum',
408       'unique' => [ [] ],
409       'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
410     },
411
412     'part_pkg' => {
413       'columns' => [
414         'pkgpart',    'int',    '',   '',
415         'pkg',        'varchar',   '',   $char_d,
416         'comment',    'varchar',   '',   $char_d,
417         'setup',      @perl_type,
418         'freq',       'smallint', '', '',  #billing frequency (months)
419         'recur',      @perl_type,
420       ],
421       'primary_key' => 'pkgpart',
422       'unique' => [ [] ],
423       'index' => [ [] ],
424     },
425
426     'pkg_svc' => {
427       'columns' => [
428         'pkgpart',    'int',    '',   '',
429         'svcpart',    'int',    '',   '',
430         'quantity',   'int',    '',   '',
431       ],
432       'primary_key' => '',
433       'unique' => [ ['pkgpart', 'svcpart'] ],
434       'index' => [ ['pkgpart'] ],
435     },
436
437     'part_referral' => {
438       'columns' => [
439         'refnum',   'int',    '',   '',
440         'referral', 'varchar',   '',   $char_d,
441       ],
442       'primary_key' => 'refnum',
443       'unique' => [ [] ],
444       'index' => [ [] ],
445     },
446
447     'part_svc' => {
448       'columns' => [
449         'svcpart',    'int',    '',   '',
450         'svc',        'varchar',   '',   $char_d,
451         'svcdb',      'varchar',   '',   $char_d,
452       ],
453       'primary_key' => 'svcpart',
454       'unique' => [ [] ],
455       'index' => [ [] ],
456     },
457
458     #(this should be renamed to part_pop)
459     'svc_acct_pop' => {
460       'columns' => [
461         'popnum',    'int',    '',   '',
462         'city',      'varchar',   '',   $char_d,
463         'state',     'char',   '',   2,
464         'ac',        'char',   '',   3,
465         'exch',      'char',   '',   3,
466         #rest o' number?
467       ],
468       'primary_key' => 'popnum',
469       'unique' => [ [] ],
470       'index' => [ [] ],
471     },
472
473     'svc_acct' => {
474       'columns' => [
475         'svcnum',    'int',    '',   '',
476         'username',  'varchar',   '',   $username_len, #unique (& remove dup code)
477         '_password', 'varchar',   '',   25, #13 for encryped pw's plus ' *SUSPENDED*
478         'popnum',    'int',    'NULL',   '',
479         'uid',       'int', 'NULL',   '',
480         'gid',       'int', 'NULL',   '',
481         'finger',    'varchar',   'NULL',   $char_d,
482         'dir',       'varchar',   'NULL',   $char_d,
483         'shell',     'varchar',   'NULL',   $char_d,
484         'quota',     'varchar',   'NULL',   $char_d,
485         'slipip',    'varchar',   'NULL',   15, #four TINYINTs, bah.
486       ],
487       'primary_key' => 'svcnum',
488       'unique' => [ [] ],
489       'index' => [ ['username'] ],
490     },
491
492     'svc_acct_sm' => {
493       'columns' => [
494         'svcnum',    'int',    '',   '',
495         'domsvc',    'int',    '',   '',
496         'domuid',    'int', '',   '',
497         'domuser',   'varchar',   '',   $char_d,
498       ],
499       'primary_key' => 'svcnum',
500       'unique' => [ [] ],
501       'index' => [ ['domsvc'], ['domuid'] ], 
502     },
503
504     #'svc_charge' => {
505     #  'columns' => [
506     #    'svcnum',    'int',    '',   '',
507     #    'amount',    @money_type,
508     #  ],
509     #  'primary_key' => 'svcnum',
510     #  'unique' => [ [] ],
511     #  'index' => [ [] ],
512     #},
513
514     'svc_domain' => {
515       'columns' => [
516         'svcnum',    'int',    '',   '',
517         'domain',    'varchar',    '',   $char_d,
518       ],
519       'primary_key' => 'svcnum',
520       'unique' => [ ['domain'] ],
521       'index' => [ [] ],
522     },
523
524     #'svc_wo' => {
525     #  'columns' => [
526     #    'svcnum',    'int',    '',   '',
527     #    'svcnum',    'int',    '',   '',
528     #    'svcnum',    'int',    '',   '',
529     #    'worker',    'varchar',   '',   $char_d,
530     #    '_date',     @date_type,
531     #  ],
532     #  'primary_key' => 'svcnum',
533     #  'unique' => [ [] ],
534     #  'index' => [ [] ],
535     #},
536
537   );
538
539   %tables;
540
541 }
542