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