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