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