hopefully fix tax report for taxclass & named tax edge cases
[freeside.git] / FS / bin / freeside-setup
1 #!/usr/bin/perl -Tw
2
3 #to delay loading dbdef until we're ready
4 BEGIN { $FS::Record::setup_hack = 1; }
5
6 use strict;
7 use vars qw($opt_s);
8 use Getopt::Std;
9 use Locale::Country;
10 use Locale::SubCountry;
11 use DBI;
12 use DBIx::DBSchema 0.20;
13 use DBIx::DBSchema::Table;
14 use DBIx::DBSchema::Column;
15 use DBIx::DBSchema::ColGroup::Unique;
16 use DBIx::DBSchema::ColGroup::Index;
17 use FS::UID qw(adminsuidsetup datasrc checkeuid getsecrets);
18 use FS::Record;
19 use FS::cust_main_county;
20 use FS::raddb;
21 use FS::part_bill_event;
22
23 die "Not running uid freeside!" unless checkeuid();
24
25 my %attrib2db =
26   map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib;
27
28 getopts("s");
29 my $user = shift or die &usage;
30 getsecrets($user);
31
32 #needs to match FS::Record
33 my($dbdef_file) = "/usr/local/etc/freeside/dbdef.". datasrc;
34
35 ###
36
37 #print "\nEnter the maximum username length: ";
38 #my($username_len)=&getvalue;
39 my $username_len = 32; #usernamemax config file
40
41 #print "\n\n", <<END, ":";
42 #Freeside tracks the RADIUS User-Name, check attribute Password and
43 #reply attribute Framed-IP-Address for each user.  You can specify additional
44 #check and reply attributes (or you can add them later with the
45 #fs-radius-add-check and fs-radius-add-reply programs).
46 #
47 #First enter any additional RADIUS check attributes you need to track for each 
48 #user, separated by whitespace.
49 #END
50 #my @check_attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
51 #                         split(" ",&getvalue);
52 #
53 #print "\n\n", <<END, ":";
54 #Now enter any additional reply attributes you need to track for each user,
55 #separated by whitespace.
56 #END
57 #my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
58 #                   split(" ",&getvalue);
59 #
60 #print "\n\n", <<END, ":";
61 #Do you wish to enable the tracking of a second, separate shipping/service
62 #address?
63 #END
64 #my $ship = &_yesno;
65 #
66 #sub getvalue {
67 #  my($x)=scalar(<STDIN>);
68 #  chop $x;
69 #  $x;
70 #}
71 #
72 #sub _yesno {
73 #  print " [y/N]:";
74 #  my $x = scalar(<STDIN>);
75 #  $x =~ /^y/i;
76 #}
77
78 my @check_attributes = (); #add later
79 my @attributes = (); #add later
80 my $ship = $opt_s;
81
82 ###
83
84 my($char_d) = 80; #default maxlength for text fields
85
86 #my(@date_type)  = ( 'timestamp', '', ''     );
87 my(@date_type)  = ( 'int', 'NULL', ''     );
88 my(@perl_type) = ( 'text', 'NULL', ''  ); 
89 my @money_type = ( 'decimal',   '', '10,2' );
90
91 ###
92 # create a dbdef object from the old data structure
93 ###
94
95 my(%tables)=&tables_hash_hack;
96
97 #turn it into objects
98 my($dbdef) = new DBIx::DBSchema ( map {  
99   my(@columns);
100   while (@{$tables{$_}{'columns'}}) {
101     my($name,$type,$null,$length)=splice @{$tables{$_}{'columns'}}, 0, 4;
102     push @columns, new DBIx::DBSchema::Column ( $name,$type,$null,$length );
103   }
104   DBIx::DBSchema::Table->new(
105     $_,
106     $tables{$_}{'primary_key'},
107     DBIx::DBSchema::ColGroup::Unique->new($tables{$_}{'unique'}),
108     DBIx::DBSchema::ColGroup::Index->new($tables{$_}{'index'}),
109     @columns,
110   );
111 } (keys %tables) );
112
113 my $cust_main = $dbdef->table('cust_main');
114 unless ($ship) { #remove ship_ from cust_main
115   $cust_main->delcolumn($_) foreach ( grep /^ship_/, $cust_main->columns );
116 } else { #add indices on ship_last and ship_company
117   push @{$cust_main->index->lol_ref}, ( ['ship_last'], ['ship_company'] ) 
118 }
119
120 #add radius attributes to svc_acct
121
122 my($svc_acct)=$dbdef->table('svc_acct');
123
124 my($attribute);
125 foreach $attribute (@attributes) {
126   $svc_acct->addcolumn ( new DBIx::DBSchema::Column (
127     'radius_'. $attribute,
128     'varchar',
129     'NULL',
130     $char_d,
131   ));
132 }
133
134 foreach $attribute (@check_attributes) {
135   $svc_acct->addcolumn( new DBIx::DBSchema::Column (
136     'rc_'. $attribute,
137     'varchar',
138     'NULL',
139     $char_d,
140   ));
141 }
142
143 ##make part_svc table (but now as object)
144 #
145 #my($part_svc)=$dbdef->table('part_svc');
146 #
147 ##because of svc_acct_pop
148 ##foreach (grep /^svc_/, $dbdef->tables) { 
149 ##foreach (qw(svc_acct svc_acct_sm svc_charge svc_domain svc_wo)) {
150 #foreach (qw(svc_acct svc_domain svc_forward svc_www)) {
151 #  my($table)=$dbdef->table($_);
152 #  my($col);
153 #  foreach $col ( $table->columns ) {
154 #    next if $col =~ /^svcnum$/;
155 #    $part_svc->addcolumn( new DBIx::DBSchema::Column (
156 #      $table->name. '__' . $table->column($col)->name,
157 #      'varchar', #$table->column($col)->type, 
158 #      'NULL',
159 #      $char_d, #$table->column($col)->length,
160 #    ));
161 #    $part_svc->addcolumn ( new DBIx::DBSchema::Column (
162 #      $table->name. '__'. $table->column($col)->name . "_flag",
163 #      'char',
164 #      'NULL',
165 #      1,
166 #    ));
167 #  }
168 #}
169
170 #create history tables (false laziness w/create-history-tables)
171 foreach my $table ( grep { ! /^h_/ } $dbdef->tables ) {
172   my $tableobj = $dbdef->table($table)
173     or die "unknown table $table";
174
175   die "unique->lol_ref undefined for $table"
176     unless defined $tableobj->unique->lol_ref;
177   die "index->lol_ref undefined for $table"
178     unless defined $tableobj->index->lol_ref;
179
180   my $h_tableobj = DBIx::DBSchema::Table->new( {
181     name        => "h_$table",
182     primary_key => 'historynum',
183     unique      => DBIx::DBSchema::ColGroup::Unique->new( [] ),
184     'index'     => DBIx::DBSchema::ColGroup::Index->new( [
185                      @{$tableobj->unique->lol_ref},
186                      @{$tableobj->index->lol_ref}
187                    ] ),
188     columns     => [
189                      DBIx::DBSchema::Column->new( {
190                        'name'    => 'historynum',
191                        'type'    => 'serial',
192                        'null'    => 'NOT NULL',
193                        'length'  => '',
194                        'default' => '',
195                        'local'   => '',
196                      } ),
197                      DBIx::DBSchema::Column->new( {
198                        'name'    => 'history_date',
199                        'type'    => 'int',
200                        'null'    => 'NULL',
201                        'length'  => '',
202                        'default' => '',
203                        'local'   => '',
204                      } ),
205                      DBIx::DBSchema::Column->new( {
206                        'name'    => 'history_user',
207                        'type'    => 'varchar',
208                        'null'    => 'NOT NULL',
209                        'length'  => '80',
210                        'default' => '',
211                        'local'   => '',
212                      } ),
213                      DBIx::DBSchema::Column->new( {
214                        'name'    => 'history_action',
215                        'type'    => 'varchar',
216                        'null'    => 'NOT NULL',
217                        'length'  => '80',
218                        'default' => '',
219                        'local'   => '',
220                      } ),
221                      map { $tableobj->column($_) } $tableobj->columns
222                    ],
223   } );
224   $dbdef->addtable($h_tableobj);
225 }
226
227 #important
228 $dbdef->save($dbdef_file);
229 &FS::Record::reload_dbdef($dbdef_file);
230
231 ###
232 # create 'em
233 ###
234
235 my($dbh)=adminsuidsetup $user;
236
237 #create tables
238 $|=1;
239
240 foreach my $statement ( $dbdef->sql($dbh) ) {
241   $dbh->do( $statement )
242     or die "CREATE error: ". $dbh->errstr. "\ndoing statement: $statement";
243 }
244
245 #cust_main_county
246 foreach my $country ( sort map uc($_), all_country_codes ) {
247
248   my $subcountry = eval { new Locale::SubCountry($country) };
249   my @states = $subcountry ? $subcountry->all_codes : undef;
250
251   if ( !scalar(@states) || ( scalar(@states) == 1 && !defined($states[0]) ) ) {
252
253     my $cust_main_county = new FS::cust_main_county({
254       'tax'   => 0,
255       'country' => $country,
256     });  
257     my $error = $cust_main_county->insert;
258     die $error if $error;
259
260   } else {
261
262     if ( $states[0] =~ /^(\d+|\w)$/ ) {
263       @states = map $subcountry->full_name($_), @states
264     }
265
266     foreach my $state ( @states ) {
267
268       my $cust_main_county = new FS::cust_main_county({
269         'state' => $state,
270         'tax'   => 0,
271         'country' => $country,
272       });  
273       my $error = $cust_main_county->insert;
274       die $error if $error;
275
276     }
277   
278   }
279 }
280
281 #billing events
282 foreach my $aref ( 
283   [ 'COMP', 'Comp invoice', '$cust_bill->comp();', 30, 'comp' ],
284   [ 'CARD', 'Batch card', '$cust_bill->batch_card();', 40, 'batch-card' ],
285   [ 'BILL', 'Send invoice', '$cust_bill->send();', 50, 'send' ],
286 ) {
287
288   my $part_bill_event = new FS::part_bill_event({
289     'payby' => $aref->[0],
290     'event' => $aref->[1],
291     'eventcode' => $aref->[2],
292     'seconds' => 0,
293     'weight' => $aref->[3],
294     'plan' => $aref->[4],
295   });
296   my($error);
297   $error=$part_bill_event->insert;
298   die $error if $error;
299
300 }
301
302 $dbh->commit or die $dbh->errstr;
303 $dbh->disconnect or die $dbh->errstr;
304
305 #print "Freeside database initialized sucessfully\n";
306
307 sub usage {
308   die "Usage:\n  freeside-setup [ -s ] user\n"; 
309 }
310
311 ###
312 # Now it becomes an object.  much better.
313 ###
314 sub tables_hash_hack {
315
316   #note that s/(date|change)/_$1/; to avoid keyword conflict.
317   #put a kludge in FS::Record to catch this or? (pry need some date-handling
318   #stuff anyway also)
319
320   my(%tables)=( #yech.}
321
322     'agent' => {
323       'columns' => [
324         'agentnum', 'int',            '',     '',
325         'agent',    'varchar',           '',     $char_d,
326         'typenum',  'int',            '',     '',
327         'freq',     'int',       'NULL', '',
328         'prog',     @perl_type,
329       ],
330       'primary_key' => 'agentnum',
331       'unique' => [],
332       'index' => [ ['typenum'] ],
333     },
334
335     'agent_type' => {
336       'columns' => [
337         'typenum',   'int',  '', '',
338         'atype',     'varchar', '', $char_d,
339       ],
340       'primary_key' => 'typenum',
341       'unique' => [],
342       'index' => [],
343     },
344
345     'type_pkgs' => {
346       'columns' => [
347         'typenum',   'int',  '', '',
348         'pkgpart',   'int',  '', '',
349       ],
350       'primary_key' => '',
351       'unique' => [ ['typenum', 'pkgpart'] ],
352       'index' => [ ['typenum'] ],
353     },
354
355     'cust_bill' => {
356       'columns' => [
357         'invnum',    'int',  '', '',
358         'custnum',   'int',  '', '',
359         '_date',     @date_type,
360         'charged',   @money_type,
361         'printed',   'int',  '', '',
362         'closed',    'char', 'NULL', 1,
363       ],
364       'primary_key' => 'invnum',
365       'unique' => [],
366       'index' => [ ['custnum'], ['_date'] ],
367     },
368
369     'cust_bill_event' => {
370       'columns' => [
371         'eventnum',    'int',  '', '',
372         'invnum',   'int',  '', '',
373         'eventpart',   'int',  '', '',
374         '_date',     @date_type,
375         'status', 'varchar', '', $char_d,
376         'statustext', 'text', 'NULL', '',
377       ],
378       'primary_key' => 'eventnum',
379       #no... there are retries now #'unique' => [ [ 'eventpart', 'invnum' ] ],
380       'unique' => [],
381       'index' => [ ['invnum'], ['status'] ],
382     },
383
384     'part_bill_event' => {
385       'columns' => [
386         'eventpart',    'int',  '', '',
387         'payby',       'char',  '', 4,
388         'event',       'varchar',           '',     $char_d,
389         'eventcode',    @perl_type,
390         'seconds',     'int', 'NULL', '',
391         'weight',      'int', '', '',
392         'plan',       'varchar', 'NULL', $char_d,
393         'plandata',   'text', 'NULL', '',
394         'disabled',     'char', 'NULL', 1,
395       ],
396       'primary_key' => 'eventpart',
397       'unique' => [],
398       'index' => [ ['payby'] ],
399     },
400
401     'cust_bill_pkg' => {
402       'columns' => [
403         'pkgnum',  'int', '', '',
404         'invnum',  'int', '', '',
405         'setup',   @money_type,
406         'recur',   @money_type,
407         'sdate',   @date_type,
408         'edate',   @date_type,
409       ],
410       'primary_key' => '',
411       'unique' => [ ['pkgnum', 'invnum'] ],
412       'index' => [ ['invnum'] ],
413     },
414
415     'cust_credit' => {
416       'columns' => [
417         'crednum',  'int', '', '',
418         'custnum',  'int', '', '',
419         '_date',    @date_type,
420         'amount',   @money_type,
421         'otaker',   'varchar', '', 32,
422         'reason',   'text', 'NULL', '',
423         'closed',    'char', 'NULL', 1,
424       ],
425       'primary_key' => 'crednum',
426       'unique' => [],
427       'index' => [ ['custnum'] ],
428     },
429
430     'cust_credit_bill' => {
431       'columns' => [
432         'creditbillnum', 'int', '', '',
433         'crednum',  'int', '', '',
434         'invnum',  'int', '', '',
435         '_date',    @date_type,
436         'amount',   @money_type,
437       ],
438       'primary_key' => 'creditbillnum',
439       'unique' => [],
440       'index' => [ ['crednum'], ['invnum'] ],
441     },
442
443     'cust_main' => {
444       'columns' => [
445         'custnum',  'int',  '',     '',
446         'agentnum', 'int',  '',     '',
447 #        'titlenum', 'int',  'NULL',   '',
448         'last',     'varchar', '',     $char_d,
449 #        'middle',   'varchar', 'NULL', $char_d,
450         'first',    'varchar', '',     $char_d,
451         'ss',       'varchar', 'NULL', 11,
452         'company',  'varchar', 'NULL', $char_d,
453         'address1', 'varchar', '',     $char_d,
454         'address2', 'varchar', 'NULL', $char_d,
455         'city',     'varchar', '',     $char_d,
456         'county',   'varchar', 'NULL', $char_d,
457         'state',    'varchar', 'NULL', $char_d,
458         'zip',      'varchar', '',     10,
459         'country',  'char', '',     2,
460         'daytime',  'varchar', 'NULL', 20,
461         'night',    'varchar', 'NULL', 20,
462         'fax',      'varchar', 'NULL', 12,
463         'ship_last',     'varchar', 'NULL', $char_d,
464 #        'ship_middle',   'varchar', 'NULL', $char_d,
465         'ship_first',    'varchar', 'NULL', $char_d,
466         'ship_company',  'varchar', 'NULL', $char_d,
467         'ship_address1', 'varchar', 'NULL', $char_d,
468         'ship_address2', 'varchar', 'NULL', $char_d,
469         'ship_city',     'varchar', 'NULL', $char_d,
470         'ship_county',   'varchar', 'NULL', $char_d,
471         'ship_state',    'varchar', 'NULL', $char_d,
472         'ship_zip',      'varchar', 'NULL', 10,
473         'ship_country',  'char', 'NULL', 2,
474         'ship_daytime',  'varchar', 'NULL', 20,
475         'ship_night',    'varchar', 'NULL', 20,
476         'ship_fax',      'varchar', 'NULL', 12,
477         'payby',    'char', '',     4,
478         'payinfo',  'varchar', 'NULL', $char_d,
479         #'paydate',  @date_type,
480         'paydate',  'varchar', 'NULL', 10,
481         'payname',  'varchar', 'NULL', $char_d,
482         'tax',      'char', 'NULL', 1,
483         'otaker',   'varchar', '',     32,
484         'refnum',   'int',  '',     '',
485         'referral_custnum', 'int',  'NULL', '',
486         'comments', 'text', 'NULL', '',
487       ],
488       'primary_key' => 'custnum',
489       'unique' => [],
490       #'index' => [ ['last'], ['company'] ],
491       'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ] ],
492     },
493
494     'cust_main_invoice' => {
495       'columns' => [
496         'destnum',  'int',  '',     '',
497         'custnum',  'int',  '',     '',
498         'dest',     'varchar', '',  $char_d,
499       ],
500       'primary_key' => 'destnum',
501       'unique' => [],
502       'index' => [ ['custnum'], ],
503     },
504
505     'cust_main_county' => { #county+state+country are checked off the
506                             #cust_main_county for validation and to provide
507                             # a tax rate.
508       'columns' => [
509         'taxnum',   'int',   '',    '',
510         'state',    'varchar',  'NULL',    $char_d,
511         'county',   'varchar',  'NULL',    $char_d,
512         'country',  'char',  '', 2, 
513         'taxclass',   'varchar', 'NULL', $char_d,
514         'exempt_amount', @money_type,
515         'tax',      'real',  '',    '', #tax %
516       ],
517       'primary_key' => 'taxnum',
518       'unique' => [],
519   #    'unique' => [ ['taxnum'], ['state', 'county'] ],
520       'index' => [],
521     },
522
523     'cust_pay' => {
524       'columns' => [
525         'paynum',   'int',    '',   '',
526         #now cust_bill_pay #'invnum',   'int',    '',   '',
527         'custnum',  'int',    '',   '',
528         'paid',     @money_type,
529         '_date',    @date_type,
530         'payby',    'char',   '',     4, # CARD/BILL/COMP, should be index into
531                                          # payment type table.
532         'payinfo',  'varchar',   'NULL', $char_d,  #see cust_main above
533         'paybatch', 'varchar',   'NULL', $char_d, #for auditing purposes.
534         'closed',    'char', 'NULL', 1,
535       ],
536       'primary_key' => 'paynum',
537       'unique' => [],
538       'index' => [ [ 'custnum' ], [ 'paybatch' ] ],
539     },
540
541     'cust_bill_pay' => {
542       'columns' => [
543         'billpaynum', 'int',     '',   '',
544         'invnum',  'int',     '',   '',
545         'paynum',  'int',     '',   '',
546         'amount',  @money_type,
547         '_date',   @date_type
548       ],
549       'primary_key' => 'billpaynum',
550       'unique' => [],
551       'index' => [ [ 'paynum' ], [ 'invnum' ] ],
552     },
553
554     'cust_pay_batch' => { #what's this used for again?  list of customers
555                           #in current CARD batch? (necessarily CARD?)
556       'columns' => [
557         'paybatchnum',   'int',    '',   '',
558         'invnum',   'int',    '',   '',
559         'custnum',   'int',    '',   '',
560         'last',     'varchar', '',     $char_d,
561         'first',    'varchar', '',     $char_d,
562         'address1', 'varchar', '',     $char_d,
563         'address2', 'varchar', 'NULL', $char_d,
564         'city',     'varchar', '',     $char_d,
565         'state',    'varchar', 'NULL', $char_d,
566         'zip',      'varchar', '',     10,
567         'country',  'char', '',     2,
568 #        'trancode', 'int', '', '',
569         'cardnum',  'varchar', '',     16,
570         #'exp',      @date_type,
571         'exp',      'varchar', '',     11,
572         'payname',  'varchar', 'NULL', $char_d,
573         'amount',   @money_type,
574       ],
575       'primary_key' => 'paybatchnum',
576       'unique' => [],
577       'index' => [ ['invnum'], ['custnum'] ],
578     },
579
580     'cust_pkg' => {
581       'columns' => [
582         'pkgnum',    'int',    '',   '',
583         'custnum',   'int',    '',   '',
584         'pkgpart',   'int',    '',   '',
585         'otaker',    'varchar', '', 32,
586         'setup',     @date_type,
587         'bill',      @date_type,
588         'susp',      @date_type,
589         'cancel',    @date_type,
590         'expire',    @date_type,
591         'manual_flag', 'char', 'NULL', 1,
592       ],
593       'primary_key' => 'pkgnum',
594       'unique' => [],
595       'index' => [ ['custnum'] ],
596     },
597
598     'cust_refund' => {
599       'columns' => [
600         'refundnum',    'int',    '',   '',
601         #now cust_credit_refund #'crednum',      'int',    '',   '',
602         'custnum',  'int',    '',   '',
603         '_date',        @date_type,
604         'refund',       @money_type,
605         'otaker',       'varchar',   '',   32,
606         'reason',       'varchar',   '',   $char_d,
607         'payby',        'char',   '',     4, # CARD/BILL/COMP, should be index
608                                              # into payment type table.
609         'payinfo',      'varchar',   'NULL', $char_d,  #see cust_main above
610         'paybatch',     'varchar',   'NULL', $char_d,
611         'closed',    'char', 'NULL', 1,
612       ],
613       'primary_key' => 'refundnum',
614       'unique' => [],
615       'index' => [],
616     },
617
618     'cust_credit_refund' => {
619       'columns' => [
620         'creditrefundnum', 'int',     '',   '',
621         'crednum',  'int',     '',   '',
622         'refundnum',  'int',     '',   '',
623         'amount',  @money_type,
624         '_date',   @date_type
625       ],
626       'primary_key' => 'creditrefundnum',
627       'unique' => [],
628       'index' => [ [ 'crednum', 'refundnum' ] ],
629     },
630
631
632     'cust_svc' => {
633       'columns' => [
634         'svcnum',    'int',    '',   '',
635         'pkgnum',    'int',    'NULL',   '',
636         'svcpart',   'int',    '',   '',
637       ],
638       'primary_key' => 'svcnum',
639       'unique' => [],
640       'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
641     },
642
643     'part_pkg' => {
644       'columns' => [
645         'pkgpart',    'int',    '',   '',
646         'pkg',        'varchar',   '',   $char_d,
647         'comment',    'varchar',   '',   $char_d,
648         'setup',      @perl_type,
649         'freq',       'int', '', '',  #billing frequency (months)
650         'recur',      @perl_type,
651         'setuptax',  'char', 'NULL', 1,
652         'recurtax',  'char', 'NULL', 1,
653         'plan',       'varchar', 'NULL', $char_d,
654         'plandata',   'text', 'NULL', '',
655         'disabled',   'char', 'NULL', 1,
656         'taxclass',   'varchar', 'NULL', $char_d,
657       ],
658       'primary_key' => 'pkgpart',
659       'unique' => [],
660       'index' => [ [ 'disabled' ], ],
661     },
662
663 #    'part_title' => {
664 #      'columns' => [
665 #        'titlenum',   'int',    '',   '',
666 #        'title',      'varchar',   '',   $char_d,
667 #      ],
668 #      'primary_key' => 'titlenum',
669 #      'unique' => [ [] ],
670 #      'index' => [ [] ],
671 #    },
672
673     'pkg_svc' => {
674       'columns' => [
675         'pkgpart',    'int',    '',   '',
676         'svcpart',    'int',    '',   '',
677         'quantity',   'int',    '',   '',
678       ],
679       'primary_key' => '',
680       'unique' => [ ['pkgpart', 'svcpart'] ],
681       'index' => [ ['pkgpart'] ],
682     },
683
684     'part_referral' => {
685       'columns' => [
686         'refnum',   'int',    '',   '',
687         'referral', 'varchar',   '',   $char_d,
688       ],
689       'primary_key' => 'refnum',
690       'unique' => [],
691       'index' => [],
692     },
693
694     'part_svc' => {
695       'columns' => [
696         'svcpart',    'int',    '',   '',
697         'svc',        'varchar',   '',   $char_d,
698         'svcdb',      'varchar',   '',   $char_d,
699         'disabled',   'char',  'NULL',   1,
700       ],
701       'primary_key' => 'svcpart',
702       'unique' => [],
703       'index' => [ [ 'disabled' ] ],
704     },
705
706     'part_svc_column' => {
707       'columns' => [
708         'columnnum',   'int',         '', '',
709         'svcpart',     'int',         '', '',
710         'columnname',  'varchar',     '', 64,
711         'columnvalue', 'varchar', 'NULL', $char_d,
712         'columnflag',  'char',    'NULL', 1, 
713       ],
714       'primary_key' => 'columnnum',
715       'unique' => [ [ 'svcpart', 'columnname' ] ],
716       'index' => [ [ 'svcpart' ] ],
717     },
718
719     #(this should be renamed to part_pop)
720     'svc_acct_pop' => {
721       'columns' => [
722         'popnum',    'int',    '',   '',
723         'city',      'varchar',   '',   $char_d,
724         'state',     'varchar',   '',   $char_d,
725         'ac',        'char',   '',   3,
726         'exch',      'char',   '',   3,
727         'loc',       'char',   'NULL',   4, #NULL for legacy purposes
728       ],
729       'primary_key' => 'popnum',
730       'unique' => [],
731       'index' => [ [ 'state' ] ],
732     },
733
734     'part_pop_local' => {
735       'columns' => [
736         'localnum',  'int',     '',     '',
737         'popnum',    'int',     '',     '',
738         'city',      'varchar', 'NULL', $char_d,
739         'state',     'char',    'NULL', 2,
740         'npa',       'char',    '',     3,
741         'nxx',       'char',    '',     3,
742       ],
743       'primary_key' => 'localnum',
744       'unique' => [],
745       'index' => [ [ 'npa', 'nxx' ], [ 'popnum' ] ],
746     },
747
748     'svc_acct' => {
749       'columns' => [
750         'svcnum',    'int',    '',   '',
751         'username',  'varchar',   '',   $username_len, #unique (& remove dup code)
752         '_password', 'varchar',   '',   72, #13 for encryped pw's plus ' *SUSPENDED* (md5 passwords can be 34, blowfish 60)
753         'sec_phrase', 'varchar',  'NULL',   $char_d,
754         'popnum',    'int',    'NULL',   '',
755         'uid',       'int', 'NULL',   '',
756         'gid',       'int', 'NULL',   '',
757         'finger',    'varchar',   'NULL',   $char_d,
758         'dir',       'varchar',   'NULL',   $char_d,
759         'shell',     'varchar',   'NULL',   $char_d,
760         'quota',     'varchar',   'NULL',   $char_d,
761         'slipip',    'varchar',   'NULL',   15, #four TINYINTs, bah.
762         'seconds',   'int', 'NULL',   '', #uhhhh
763         'domsvc',    'int', '',   '',
764       ],
765       'primary_key' => 'svcnum',
766       #'unique' => [ [ 'username', 'domsvc' ] ],
767       'unique' => [],
768       'index' => [ ['username'], ['domsvc'] ],
769     },
770
771 #    'svc_acct_sm' => {
772 #      'columns' => [
773 #        'svcnum',    'int',    '',   '',
774 #        'domsvc',    'int',    '',   '',
775 #        'domuid',    'int', '',   '',
776 #        'domuser',   'varchar',   '',   $char_d,
777 #      ],
778 #      'primary_key' => 'svcnum',
779 #      'unique' => [ [] ],
780 #      'index' => [ ['domsvc'], ['domuid'] ], 
781 #    },
782
783     #'svc_charge' => {
784     #  'columns' => [
785     #    'svcnum',    'int',    '',   '',
786     #    'amount',    @money_type,
787     #  ],
788     #  'primary_key' => 'svcnum',
789     #  'unique' => [ [] ],
790     #  'index' => [ [] ],
791     #},
792
793     'svc_domain' => {
794       'columns' => [
795         'svcnum',    'int',    '',   '',
796         'domain',    'varchar',    '',   $char_d,
797         'catchall',  'int', 'NULL',    '',
798       ],
799       'primary_key' => 'svcnum',
800       'unique' => [ ['domain'] ],
801       'index' => [],
802     },
803
804     'domain_record' => {
805       'columns' => [
806         'recnum',    'int',     '',  '',
807         'svcnum',    'int',     '',  '',
808         'reczone',   'varchar', '',  255,
809         'recaf',     'char',    '',  2,
810         'rectype',   'varchar', '',  5,
811         'recdata',   'varchar', '',  255,
812       ],
813       'primary_key' => 'recnum',
814       'unique'      => [],
815       'index'       => [ ['svcnum'] ],
816     },
817
818     'svc_forward' => {
819       'columns' => [
820         'svcnum',   'int',    '',  '',
821         'srcsvc',   'int',    '',  '',
822         'dstsvc',   'int',    '',  '',
823         'dst',      'varchar',    'NULL',  $char_d,
824       ],
825       'primary_key' => 'svcnum',
826       'unique'      => [],
827       'index'       => [ ['srcsvc'], ['dstsvc'] ],
828     },
829
830     'svc_www' => {
831       'columns' => [
832         'svcnum',   'int',    '',  '',
833         'recnum',   'int',    '',  '',
834         'usersvc',  'int',    '',  '',
835       ],
836       'primary_key' => 'svcnum',
837       'unique'      => [],
838       'index'       => [],
839     },
840
841     #'svc_wo' => {
842     #  'columns' => [
843     #    'svcnum',    'int',    '',   '',
844     #    'svcnum',    'int',    '',   '',
845     #    'svcnum',    'int',    '',   '',
846     #    'worker',    'varchar',   '',   $char_d,
847     #    '_date',     @date_type,
848     #  ],
849     #  'primary_key' => 'svcnum',
850     #  'unique' => [ [] ],
851     #  'index' => [ [] ],
852     #},
853
854     'prepay_credit' => {
855       'columns' => [
856         'prepaynum',   'int',     '',   '',
857         'identifier',  'varchar', '', $char_d,
858         'amount',      @money_type,
859         'seconds',     'int',     'NULL', '',
860       ],
861       'primary_key' => 'prepaynum',
862       'unique'      => [ ['identifier'] ],
863       'index'       => [],
864     },
865
866     'port' => {
867       'columns' => [
868         'portnum',  'int',     '',   '',
869         'ip',       'varchar', 'NULL', 15,
870         'nasport',  'int',     'NULL', '',
871         'nasnum',   'int',     '',   '',
872       ],
873       'primary_key' => 'portnum',
874       'unique'      => [],
875       'index'       => [],
876     },
877
878     'nas' => {
879       'columns' => [
880         'nasnum',   'int',     '',    '',
881         'nas',      'varchar', '',    $char_d,
882         'nasip',    'varchar', '',    15,
883         'nasfqdn',  'varchar', '',    $char_d,
884         'last',     'int',     '',    '',
885       ],
886       'primary_key' => 'nasnum',
887       'unique'      => [ [ 'nas' ], [ 'nasip' ] ],
888       'index'       => [ [ 'last' ] ],
889     },
890
891     'session' => {
892       'columns' => [
893         'sessionnum', 'int',       '',   '',
894         'portnum',    'int',       '',   '',
895         'svcnum',     'int',       '',   '',
896         'login',      @date_type,
897         'logout',     @date_type,
898       ],
899       'primary_key' => 'sessionnum',
900       'unique'      => [],
901       'index'       => [ [ 'portnum' ] ],
902     },
903
904     'queue' => {
905       'columns' => [
906         'jobnum', 'int', '', '',
907         'job', 'text', '', '',
908         '_date', 'int', '', '',
909         'status', 'varchar', '', $char_d,
910         'statustext', 'text', 'NULL', '',
911         'svcnum', 'int', 'NULL', '',
912       ],
913       'primary_key' => 'jobnum',
914       'unique'      => [],
915       'index'       => [ [ 'svcnum' ], [ 'status' ] ],
916     },
917
918     'queue_arg' => {
919       'columns' => [
920         'argnum', 'int', '', '',
921         'jobnum', 'int', '', '',
922         'arg', 'text', 'NULL', '',
923       ],
924       'primary_key' => 'argnum',
925       'unique'      => [],
926       'index'       => [ [ 'jobnum' ] ],
927     },
928
929     'queue_depend' => {
930       'columns' => [
931         'dependnum', 'int', '', '',
932         'jobnum', 'int', '', '',
933         'depend_jobnum', 'int', '', '',
934       ],
935       'primary_key' => 'dependnum',
936       'unique'      => [],
937       'index'       => [ [ 'jobnum' ], [ 'depend_jobnum' ] ],
938     },
939
940     'export_svc' => {
941       'columns' => [
942         'exportsvcnum' => 'int', '', '',
943         'exportnum'    => 'int', '', '',
944         'svcpart'      => 'int', '', '',
945       ],
946       'primary_key' => 'exportsvcnum',
947       'unique'      => [ [ 'exportnum', 'svcpart' ] ],
948       'index'       => [ [ 'exportnum' ], [ 'svcpart' ] ],
949     },
950
951     'part_export' => {
952       'columns' => [
953         'exportnum', 'int', '', '',
954         #'svcpart',   'int', '', '',
955         'machine', 'varchar', '', $char_d,
956         'exporttype', 'varchar', '', $char_d,
957         'nodomain',     'char', 'NULL', 1,
958       ],
959       'primary_key' => 'exportnum',
960       'unique'      => [],
961       'index'       => [ [ 'machine' ], [ 'exporttype' ] ],
962     },
963
964     'part_export_option' => {
965       'columns' => [
966         'optionnum', 'int', '', '',
967         'exportnum', 'int', '', '',
968         'optionname', 'varchar', '', $char_d,
969         'optionvalue', 'text', 'NULL', '',
970       ],
971       'primary_key' => 'optionnum',
972       'unique'      => [],
973       'index'       => [ [ 'exportnum' ], [ 'optionname' ] ],
974     },
975
976     'radius_usergroup' => {
977       'columns' => [
978         'usergroupnum', 'int', '', '',
979         'svcnum',       'int', '', '',
980         'groupname',    'varchar', '', $char_d,
981       ],
982       'primary_key' => 'usergroupnum',
983       'unique'      => [],
984       'index'       => [ [ 'svcnum' ], [ 'groupname' ] ],
985     },
986
987     'msgcat' => {
988       'columns' => [
989         'msgnum', 'int', '', '',
990         'msgcode', 'varchar', '', $char_d,
991         'locale', 'varchar', '', 16,
992         'msg', 'text', '', '',
993       ],
994       'primary_key' => 'msgnum',
995       'unique'      => [ [ 'msgcode', 'locale' ] ],
996       'index'       => [],
997     },
998
999     'cust_tax_exempt' => {
1000       'columns' => [
1001         'exemptnum', 'int', '', '',
1002         'custnum',   'int', '', '',
1003         'taxnum',    'int', '', '',
1004         'year',      'int', '', '',
1005         'month',     'int', '', '',
1006         'amount',   @money_type,
1007       ],
1008       'primary_key' => 'exemptnum',
1009       'unique'      => [ [ 'custnum', 'taxnum', 'year', 'month' ] ],
1010       'index'       => [],
1011     },
1012
1013
1014
1015   );
1016
1017   %tables;
1018
1019 }
1020