fix cpu/mem sucking when service labels are very long, RT#11452
[freeside.git] / FS / FS / Setup.pm
1 package FS::Setup;
2
3 use strict;
4 use vars qw( @ISA @EXPORT_OK );
5 use Exporter;
6 #use Tie::DxHash;
7 use Tie::IxHash;
8 use FS::UID qw( dbh driver_name );
9 use FS::Record;
10
11 use FS::svc_domain;
12 $FS::svc_domain::whois_hack = 1;
13 $FS::svc_domain::whois_hack = 1;
14
15 @ISA = qw( Exporter );
16 @EXPORT_OK = qw( create_initial_data );
17
18 =head1 NAME
19
20 FS::Setup - Database setup
21
22 =head1 SYNOPSIS
23
24   use FS::Setup;
25
26 =head1 DESCRIPTION
27
28 Currently this module simply provides a place to store common subroutines for
29 database setup.
30
31 =head1 SUBROUTINES
32
33 =over 4
34
35 =item
36
37 =cut
38
39 sub create_initial_data {
40   my %opt = @_;
41
42   my $oldAutoCommit = $FS::UID::AutoCommit;
43   local $FS::UID::AutoCommit = 0;
44   $FS::UID::AutoCommit = 0;
45
46   populate_locales();
47
48   populate_duplock();
49
50   #initial_data data
51   populate_initial_data(%opt);
52
53   populate_access();
54
55   populate_msgcat();
56   
57   if ( $oldAutoCommit ) {
58     dbh->commit or die dbh->errstr;
59   }
60
61 }
62
63 sub populate_locales {
64
65   use Locale::Country;
66   use FS::cust_main_county;
67
68   #cust_main_county
69   foreach my $country ( sort map uc($_), all_country_codes ) {
70     _add_country($country);
71   }
72
73 }
74
75 sub populate_addl_locales {
76
77   my %addl = (
78     'US' => {
79       'FM' => 'Federated States of Micronesia',
80       'MH' => 'Marshall Islands',
81       'PW' => 'Palau',
82       'AA' => "Armed Forces Americas (except Canada)",
83       'AE' => "Armed Forces Europe / Canada / Middle East / Africa",
84       'AP' => "Armed Forces Pacific",
85     },
86   );
87
88   foreach my $country ( keys %addl ) {
89     foreach my $state ( keys %{ $addl{$country} } ) {
90       # $longname = $addl{$country}{$state};
91       _add_locale( 'country'=>$country, 'state'=>$state);
92     }
93   }
94
95 }
96
97 sub _add_country {
98
99   use Locale::SubCountry;
100
101   my( $country ) = shift;
102
103   my $subcountry = eval { new Locale::SubCountry($country) };
104   my @states = $subcountry ? $subcountry->all_codes : undef;
105   
106   if ( !scalar(@states) || ( scalar(@states)==1 && !defined($states[0]) ) ) {
107
108     _add_locale( 'country'=>$country );
109   
110   } else {
111   
112     if ( $states[0] =~ /^(\d+|\w)$/ ) {
113       @states = map $subcountry->full_name($_), @states
114     }
115   
116     foreach my $state ( @states ) {
117       _add_locale( 'country'=>$country, 'state'=>$state);
118     }
119     
120   }
121
122 }
123
124 sub _add_locale {
125   my $cust_main_county = new FS::cust_main_county( { 'tax'=>0, @_ });  
126   my $error = $cust_main_county->insert;
127   die $error if $error;
128 }
129
130 sub populate_duplock {
131
132   return unless driver_name =~ /^mysql/i;
133
134   my $sth = dbh->prepare(
135     "INSERT INTO duplicate_lock ( lockname ) VALUES ( 'svc_acct' )"
136   ) or die dbh->errstr;
137
138   $sth->execute or die $sth->errstr;
139
140 }
141
142 sub populate_initial_data {
143   my %opt = @_;
144
145   my $data = initial_data(%opt);
146
147   foreach my $table ( keys %$data ) {
148
149     #warn "popuilating $table\n";
150
151     my $class = "FS::$table";
152     eval "use $class;";
153     die $@ if $@;
154
155     $class->_populate_initial_data(%opt)
156       if $class->can('_populate_initial_data');
157
158     my @records = @{ $data->{$table} };
159
160     foreach my $record ( @records ) {
161
162       my $args = delete($record->{'_insert_args'}) || [];
163       my $object = $class->new( $record );
164       my $error = $object->insert( @$args );
165       die "error inserting record into $table: $error\n"
166         if $error;
167
168       #my $pkey = $object->primary_key;
169       #my $pkeyvalue = $object->$pkey();
170       #warn "  inserted $pkeyvalue\n";
171
172     }
173
174   }
175
176 }
177
178 sub initial_data {
179   my %opt = @_;
180
181   #tie my %hash, 'Tie::DxHash', 
182   tie my %hash, 'Tie::IxHash', 
183
184     #bootstrap user
185     'access_user' => [
186       { 'username'  => 'fs_bootstrap',
187         '_password' => 'changeme', #will trigger warning if you try to enable
188         'last'      => 'User',
189         'first'     => 'Bootstrap',
190         'disabled'  => 'Y',
191       },
192     ],
193
194     #superuser group
195     'access_group' => [
196       { 'groupname' => 'Superuser' },
197     ],
198
199     #reason types
200     'reason_type' => [],
201
202 #XXX need default new-style billing events
203 #    #billing events
204 #    'part_bill_event' => [
205 #      { 'payby'     => 'CARD',
206 #        'event'     => 'Batch card',
207 #        'seconds'   => 0,
208 #        'eventcode' => '$cust_bill->batch_card(%options);',
209 #        'weight'    => 40,
210 #        'plan'      => 'batch-card',
211 #      },
212 #      { 'payby'     => 'BILL',
213 #        'event'     => 'Send invoice',
214 #        'seconds'   => 0,
215 #        'eventcode' => '$cust_bill->send();',
216 #        'weight'    => 50,
217 #        'plan'      => 'send',
218 #      },
219 #      { 'payby'     => 'DCRD',
220 #        'event'     => 'Send invoice',
221 #        'seconds'   => 0,
222 #        'eventcode' => '$cust_bill->send();',
223 #        'weight'    => 50,
224 #        'plan'      => 'send',
225 #      },
226 #      { 'payby'     => 'DCHK',
227 #        'event'     => 'Send invoice',
228 #        'seconds'   => 0,
229 #        'eventcode' => '$cust_bill->send();',
230 #        'weight'    => 50,
231 #        'plan'      => 'send',
232 #      },
233 #      { 'payby'     => 'DCLN',
234 #        'event'     => 'Suspend',
235 #        'seconds'   => 0,
236 #        'eventcode' => '$cust_bill->suspend();',
237 #        'weight'    => 40,
238 #        'plan'      => 'suspend',
239 #      },
240 #      #{ 'payby'     => 'DCLN',
241 #      #  'event'     => 'Retriable',
242 #      #  'seconds'   => 0,
243 #      #  'eventcode' => '$cust_bill_event->retriable();',
244 #      #  'weight'    => 60,
245 #      #  'plan'      => 'retriable',
246 #      #},
247 #    ],
248     
249     #you must create a service definition. An example of a service definition
250     #would be a dial-up account or a domain. First, it is necessary to create a
251     #domain definition. Click on View/Edit service definitions and Add a new
252     #service definition with Table svc_domain (and no modifiers).
253     'part_svc' => [
254       { 'svc'   => 'Domain',
255         'svcdb' => 'svc_domain',
256       }
257     ],
258
259     #Now that you have created your first service, you must create a package
260     #including this service which you can sell to customers. Zero, one, or many
261     #services are bundled into a package. Click on View/Edit package
262     #definitions and Add a new package definition which includes quantity 1 of
263     #the svc_domain service you created above.
264     'part_pkg' => [
265       { 'pkg'     => 'System Domain',
266         'comment' => '(NOT FOR CUSTOMERS)',
267         'freq'    => '0',
268         'plan'    => 'flat',
269         '_insert_args' => [
270           'pkg_svc'     => { 1 => 1 }, # XXX
271           'primary_svc' => 1, #XXX
272           'options'     => {
273             'setup_fee' => '0',
274             'recur_fee' => '0',
275           },
276         ],
277       },
278     ],
279
280     #After you create your first package, then you must define who is able to
281     #sell that package by creating an agent type. An example of an agent type
282     #would be an internal sales representitive which sells regular and
283     #promotional packages, as opposed to an external sales representitive
284     #which would only sell regular packages of services. Click on View/Edit
285     #agent types and Add a new agent type.
286     'agent_type' => [
287       { 'atype' => 'Internal' },
288     ],
289
290     #Allow this agent type to sell the package you created above.
291     'type_pkgs' => [
292       { 'typenum' => 1, #XXX
293         'pkgpart' => 1, #XXX
294       },
295     ],
296
297     #After creating a new agent type, you must create an agent. Click on
298     #View/Edit agents and Add a new agent.
299     'agent' => [
300       { 'agent'   => 'Internal',
301         'typenum' => 1, # XXX
302       },
303     ],
304
305     #Set up at least one Advertising source. Advertising sources will help you
306     #keep track of how effective your advertising is, tracking where customers
307     #heard of your service offerings. You must create at least one advertising
308     #source. If you do not wish to use the referral functionality, simply
309     #create a single advertising source only. Click on View/Edit advertising
310     #sources and Add a new advertising source.
311     'part_referral' => [
312       { 'referral' => 'Internal', },
313     ],
314     
315     #Click on New Customer and create a new customer for your system accounts
316     #with billing type Complimentary. Leave the First package dropdown set to
317     #(none).
318     'cust_main' => [
319       { 'agentnum'  => 1, #XXX
320         'refnum'    => 1, #XXX
321         'first'     => 'System',
322         'last'      => 'Accounts',
323         'address1'  => '1234 System Lane',
324         'city'      => 'Systemtown',
325         'state'     => 'CA',
326         'zip'       => '54321',
327         'country'   => 'US',
328         'payby'     => 'COMP',
329         'payinfo'   => 'system', #or something
330         'paydate'   => '1/2037',
331       },
332     ],
333
334     #From the Customer View screen of the newly created customer, order the
335     #package you defined above.
336     'cust_pkg' => [
337       { 'custnum' => 1, #XXX
338         'pkgpart' => 1, #XXX
339       },
340     ],
341
342     #From the Package View screen of the newly created package, choose
343     #(Provision) to add the customer's service for this new package.
344     #Add your own domain.
345     'svc_domain' => [
346       { 'domain'  => $opt{'domain'},
347         'pkgnum'  => 1, #XXX
348         'svcpart' => 1, #XXX
349         'action'  => 'N', #pseudo-field
350       },
351     ],
352
353     #Go back to View/Edit service definitions on the main menu, and Add a new
354     #service definition with Table svc_acct. Select your domain in the domsvc
355     #Modifier. Set Fixed to define a service locked-in to this domain, or
356     #Default to define a service which may select from among this domain and
357     #the customer's domains.
358
359     #not yet....
360
361     #usage classes
362     'usage_class' => [],
363
364     #phone types
365     'phone_type' => [],
366
367   ;
368
369   \%hash;
370
371 }
372
373 sub populate_access {
374
375   use FS::AccessRight;
376   use FS::access_right;
377
378   foreach my $rightname ( FS::AccessRight->default_superuser_rights ) {
379     my $access_right = new FS::access_right {
380       'righttype'   => 'FS::access_group',
381       'rightobjnum' => 1, #$supergroup->groupnum,
382       'rightname'   => $rightname,
383     };
384     my $ar_error = $access_right->insert;
385     die $ar_error if $ar_error;
386   }
387
388   #foreach my $agent ( qsearch('agent', {} ) ) {
389     my $access_groupagent = new FS::access_groupagent {
390       'groupnum' => 1, #$supergroup->groupnum,
391       'agentnum' => 1, #$agent->agentnum,
392     };
393     my $aga_error = $access_groupagent->insert;
394     die $aga_error if $aga_error;
395   #}
396
397 }
398
399 sub populate_msgcat {
400
401   use FS::Record qw(qsearch);
402   use FS::msgcat;
403
404   foreach my $del_msgcat ( qsearch('msgcat', {}) ) {
405     my $error = $del_msgcat->delete;
406     die $error if $error;
407   }
408
409   my %messages = msgcat_messages();
410
411   foreach my $msgcode ( keys %messages ) {
412     foreach my $locale ( keys %{$messages{$msgcode}} ) {
413       my $msgcat = new FS::msgcat( {
414         'msgcode' => $msgcode,
415         'locale'  => $locale,
416         'msg'     => $messages{$msgcode}{$locale},
417       });
418       my $error = $msgcat->insert;
419       die $error if $error;
420     }
421   }
422
423 }
424
425 sub msgcat_messages {
426
427   #  'msgcode' => {
428   #    'en_US' => 'Message',
429   #  },
430
431   (
432
433     'passwords_dont_match' => {
434       'en_US' => "Passwords don't match",
435     },
436
437     'invalid_card' => {
438       'en_US' => 'Invalid credit card number',
439     },
440
441     'unknown_card_type' => {
442       'en_US' => 'Unknown card type',
443     },
444
445     'not_a' => {
446       'en_US' => 'Not a ',
447     },
448
449     'empty_password' => {
450       'en_US' => 'Empty password',
451     },
452
453     'no_access_number_selected' => {
454       'en_US' => 'No access number selected',
455     },
456
457     'illegal_text' => {
458       'en_US' => 'Illegal (text)',
459       #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in field',
460     },
461
462     'illegal_or_empty_text' => {
463       'en_US' => 'Illegal or empty (text)',
464       #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in required field',
465     },
466
467     'illegal_username' => {
468       'en_US' => 'Illegal username',
469     },
470
471     'illegal_password' => {
472       'en_US' => 'Illegal password (',
473     },
474
475     'illegal_password_characters' => {
476       'en_US' => ' characters)',
477     },
478
479     'username_in_use' => {
480       'en_US' => 'Username in use',
481     },
482
483     'phonenum_in_use' => {
484       'en_US' => 'Phone number in use',
485     },
486
487     'illegal_email_invoice_address' => {
488       'en_US' => 'Illegal email invoice address',
489     },
490
491     'illegal_name' => {
492       'en_US' => 'Illegal (name)',
493       #'en_US' => 'Only letters, numbers, spaces and the following punctuation symbols are permitted: , . - \' in field',
494     },
495
496     'illegal_phone' => {
497       'en_US' => 'Illegal (phone)',
498       #'en_US' => '',
499     },
500
501     'illegal_zip' => {
502       'en_US' => 'Illegal (zip)',
503       #'en_US' => '',
504     },
505
506     'expired_card' => {
507       'en_US' => 'Expired card',
508     },
509
510     'daytime' => {
511       'en_US' => 'Day Phone',
512     },
513
514     'night' => {
515       'en_US' => 'Night Phone',
516     },
517
518     'svc_external-id' => {
519       'en_US' => 'External ID',
520     },
521
522     'svc_external-title' => {
523       'en_US' => 'Title',
524     },
525
526     'stateid' => {
527       'en_US' => 'Driver\'s License',
528     },
529
530     'stateid_state' => {
531       'en_US' => 'Driver\'s License State',
532     },
533
534     'invalid_domain' => {
535       'en_US' => 'Invalid domain',
536     },
537
538   );
539 }
540
541 =back
542
543 =head1 BUGS
544
545 Sure.
546
547 =head1 SEE ALSO
548
549 =cut
550
551 1;
552