2 use base qw( Exporter );
5 use vars qw( @EXPORT_OK );
8 use Crypt::OpenSSL::RSA;
9 use FS::UID qw( dbh driver_name );
12 $FS::svc_domain::whois_hack = 1;
13 $FS::svc_domain::whois_hack = 1;
17 use Locale::SubCountry 1.42;
18 use FS::cust_main_county;
23 use FS::access_groupagent;
26 use FS::Record qw(qsearch);
29 @EXPORT_OK = qw( create_initial_data enable_encryption enable_banned_pay_pad );
33 FS::Setup - Database setup
41 Currently this module simply provides a place to store common subroutines for
52 sub create_initial_data {
55 my $oldAutoCommit = $FS::UID::AutoCommit;
56 local $FS::UID::AutoCommit = 0;
57 $FS::UID::AutoCommit = 0;
64 populate_initial_data(%opt);
74 enable_banned_pay_pad();
76 if ( $oldAutoCommit ) {
77 dbh->commit or die dbh->errstr;
82 sub enable_encryption {
87 my $conf = new FS::Conf;
89 die "encryption key(s) already in place"
90 if $conf->exists('encryptionpublickey')
91 || $conf->exists('encryptionprivatekey');
94 my $rsa = Crypt::OpenSSL::RSA->generate_key($length);
96 $conf->set('encryption', 1);
97 $conf->set('encryptionmodule', 'Crypt::OpenSSL::RSA');
98 $conf->set('encryptionpublickey', $rsa->get_public_key_string );
99 $conf->set('encryptionprivatekey', $rsa->get_private_key_string );
101 # reload Record globals, false laziness with FS::Record
102 $FS::Record::conf_encryption = $conf->exists('encryption');
103 $FS::Record::conf_encryptionmodule = $conf->config('encryptionmodule');
104 $FS::Record::conf_encryptionpublickey = join("\n",$conf->config('encryptionpublickey'));
105 $FS::Record::conf_encryptionprivatekey = join("\n",$conf->config('encryptionprivatekey'));
109 sub enable_banned_pay_pad {
114 my $conf = new FS::Conf;
116 die "banned_pay-pad already in place"
117 if length( $conf->config('banned_pay-pad') );
119 #arbitrary but good enough... all we need is *some* per-site random padding
120 my @pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '.', ',' );
122 $conf->set('banned_pay-pad',
123 join('', map($pw_set[ int(rand($#pw_set)) ], (0..15) ) )
128 sub populate_numbering {
129 eval "use FS::lata_Data;"; # this automatically populates the lata table, if unpopulated
130 eval "use FS::msa_Data;"; # this automatically populates the msa table, if unpopulated
133 sub populate_locales {
136 foreach my $country ( sort map uc($_), all_country_codes ) {
137 _add_country($country);
142 sub populate_addl_locales {
146 'FM' => 'Federated States of Micronesia',
147 'MH' => 'Marshall Islands',
149 'AA' => "Armed Forces Americas (except Canada)",
150 'AE' => "Armed Forces Europe / Canada / Middle East / Africa",
151 'AP' => "Armed Forces Pacific",
155 foreach my $country ( keys %addl ) {
156 foreach my $state ( keys %{ $addl{$country} } ) {
157 # $longname = $addl{$country}{$state};
158 _add_locale( 'country'=>$country, 'state'=>$state);
166 my( $country ) = shift;
168 my $subcountry = eval { new Locale::SubCountry($country) };
169 my @states = $subcountry ? $subcountry->all_codes : undef;
171 if ( !scalar(@states) || ( scalar(@states)==1 && !defined($states[0]) ) ) {
173 _add_locale( 'country'=>$country );
177 if ( $states[0] =~ /^(\d+|\w)$/ ) {
178 @states = map $subcountry->full_name($_), @states
181 foreach my $state ( @states ) {
182 _add_locale( 'country'=>$country, 'state'=>$state);
190 my $cust_main_county = new FS::cust_main_county( { 'tax'=>0, @_ });
191 my $error = $cust_main_county->insert;
192 die $error if $error;
195 sub populate_duplock {
197 return unless driver_name =~ /^mysql/i;
199 my $sth = dbh->prepare(
200 "INSERT INTO duplicate_lock ( lockname ) VALUES ( 'svc_acct' )"
201 ) or die dbh->errstr;
203 $sth->execute or die $sth->errstr;
207 sub populate_initial_data {
210 my $data = initial_data(%opt);
212 foreach my $table ( keys %$data ) {
214 #warn "popuilating $table\n";
216 my $class = "FS::$table";
220 $class->_populate_initial_data(%opt)
221 if $class->can('_populate_initial_data');
223 my @records = @{ $data->{$table} };
225 foreach my $record ( @records ) {
227 my $args = delete($record->{'_insert_args'}) || [];
228 my $object = $class->new( $record );
229 my $error = $object->insert( @$args );
230 die "error inserting record into $table: $error\n"
233 #my $pkey = $object->primary_key;
234 #my $pkeyvalue = $object->$pkey();
235 #warn " inserted $pkeyvalue\n";
246 my $cust_location = FS::cust_location->new({
247 'address1' => '1234 System Lane',
248 'city' => 'Systemtown',
254 #tie my %hash, 'Tie::DxHash',
255 tie my %hash, 'Tie::IxHash',
259 { 'username' => 'fs_bootstrap',
260 '_password' => 'changeme', #will trigger warning if you try to enable
262 'first' => 'Bootstrap',
269 { 'groupname' => 'Superuser' },
275 #XXX need default new-style billing events
277 # 'part_bill_event' => [
278 # { 'payby' => 'CARD',
279 # 'event' => 'Batch card',
281 # 'eventcode' => '$cust_bill->batch_card(%options);',
283 # 'plan' => 'batch-card',
285 # { 'payby' => 'BILL',
286 # 'event' => 'Send invoice',
288 # 'eventcode' => '$cust_bill->send();',
292 # { 'payby' => 'DCRD',
293 # 'event' => 'Send invoice',
295 # 'eventcode' => '$cust_bill->send();',
299 # { 'payby' => 'DCHK',
300 # 'event' => 'Send invoice',
302 # 'eventcode' => '$cust_bill->send();',
306 # { 'payby' => 'DCLN',
307 # 'event' => 'Suspend',
309 # 'eventcode' => '$cust_bill->suspend();',
311 # 'plan' => 'suspend',
313 # #{ 'payby' => 'DCLN',
314 # # 'event' => 'Retriable',
316 # # 'eventcode' => '$cust_bill_event->retriable();',
318 # # 'plan' => 'retriable',
322 #you must create a service definition. An example of a service definition
323 #would be a dial-up account or a domain. First, it is necessary to create a
324 #domain definition. Click on View/Edit service definitions and Add a new
325 #service definition with Table svc_domain (and no modifiers).
328 'svcdb' => 'svc_domain',
332 #Now that you have created your first service, you must create a package
333 #including this service which you can sell to customers. Zero, one, or many
334 #services are bundled into a package. Click on View/Edit package
335 #definitions and Add a new package definition which includes quantity 1 of
336 #the svc_domain service you created above.
338 { 'pkg' => 'System Domain',
339 'comment' => '(NOT FOR CUSTOMERS)',
343 'pkg_svc' => { 1 => 1 }, # XXX
344 'primary_svc' => 1, #XXX
353 #After you create your first package, then you must define who is able to
354 #sell that package by creating an agent type. An example of an agent type
355 #would be an internal sales representitive which sells regular and
356 #promotional packages, as opposed to an external sales representitive
357 #which would only sell regular packages of services. Click on View/Edit
358 #agent types and Add a new agent type.
360 { 'atype' => 'Internal' },
363 #Allow this agent type to sell the package you created above.
365 { 'typenum' => 1, #XXX
370 #After creating a new agent type, you must create an agent. Click on
371 #View/Edit agents and Add a new agent.
373 { 'agent' => 'Internal',
374 'typenum' => 1, # XXX
378 #Set up at least one Advertising source. Advertising sources will help you
379 #keep track of how effective your advertising is, tracking where customers
380 #heard of your service offerings. You must create at least one advertising
381 #source. If you do not wish to use the referral functionality, simply
382 #create a single advertising source only. Click on View/Edit advertising
383 #sources and Add a new advertising source.
385 { 'referral' => 'Internal', },
388 #Click on New Customer and create a new customer for your system accounts
389 #with billing type Complimentary. Leave the First package dropdown set to
392 { 'agentnum' => 1, #XXX
395 'last' => 'Accounts',
396 'complimentary' => 'Y',
397 'bill_location' => $cust_location,
398 'ship_location' => $cust_location,
402 #From the Customer View screen of the newly created customer, order the
403 #package you defined above.
405 { 'custnum' => 1, #XXX
410 #From the Package View screen of the newly created package, choose
411 #(Provision) to add the customer's service for this new package.
412 #Add your own domain.
414 { 'domain' => $opt{'domain'},
417 'action' => 'N', #pseudo-field
421 #Go back to View/Edit service definitions on the main menu, and Add a new
422 #service definition with Table svc_acct. Select your domain in the domsvc
423 #Modifier. Set Fixed to define a service locked-in to this domain, or
424 #Default to define a service which may select from among this domain and
425 #the customer's domains.
436 'msg_template' => [],
444 sub populate_access {
446 foreach my $rightname ( FS::AccessRight->default_superuser_rights ) {
447 my $access_right = new FS::access_right {
448 'righttype' => 'FS::access_group',
449 'rightobjnum' => 1, #$supergroup->groupnum,
450 'rightname' => $rightname,
452 my $ar_error = $access_right->insert;
453 die $ar_error if $ar_error;
456 #foreach my $agent ( qsearch('agent', {} ) ) {
457 my $access_groupagent = new FS::access_groupagent {
458 'groupnum' => 1, #$supergroup->groupnum,
459 'agentnum' => 1, #$agent->agentnum,
461 my $aga_error = $access_groupagent->insert;
462 die $aga_error if $aga_error;
467 sub populate_msgcat {
469 foreach my $del_msgcat ( qsearch('msgcat', {}) ) {
470 my $error = $del_msgcat->delete;
471 die $error if $error;
474 my %messages = FS::msgcat::_legacy_messages();
476 foreach my $msgcode ( keys %messages ) {
477 foreach my $locale ( keys %{$messages{$msgcode}} ) {
478 my $msgcat = new FS::msgcat( {
479 'msgcode' => $msgcode,
481 'msg' => $messages{$msgcode}{$locale},
483 my $error = $msgcat->insert;
484 die $error if $error;