4 use vars qw( @ISA @EXPORT_OK );
8 use FS::UID qw( dbh driver_name );
12 $FS::svc_domain::whois_hack = 1;
13 $FS::svc_domain::whois_hack = 1;
15 @ISA = qw( Exporter );
16 @EXPORT_OK = qw( create_initial_data );
20 FS::Setup - Database setup
28 Currently this module simply provides a place to store common subroutines for
39 sub create_initial_data {
42 my $oldAutoCommit = $FS::UID::AutoCommit;
43 local $FS::UID::AutoCommit = 0;
44 $FS::UID::AutoCommit = 0;
51 populate_initial_data(%opt);
59 if ( $oldAutoCommit ) {
60 dbh->commit or die dbh->errstr;
65 sub populate_numbering {
66 eval "use FS::lata_Data;"; # this automatically populates the lata table, if unpopulated
67 eval "use FS::msa_Data;"; # this automatically populates the msa table, if unpopulated
70 sub populate_locales {
73 use FS::cust_main_county;
76 foreach my $country ( sort map uc($_), all_country_codes ) {
77 _add_country($country);
82 sub populate_addl_locales {
86 'FM' => 'Federated States of Micronesia',
87 'MH' => 'Marshall Islands',
89 'AA' => "Armed Forces Americas (except Canada)",
90 'AE' => "Armed Forces Europe / Canada / Middle East / Africa",
91 'AP' => "Armed Forces Pacific",
95 foreach my $country ( keys %addl ) {
96 foreach my $state ( keys %{ $addl{$country} } ) {
97 # $longname = $addl{$country}{$state};
98 _add_locale( 'country'=>$country, 'state'=>$state);
106 use Locale::SubCountry;
108 my( $country ) = shift;
110 my $subcountry = eval { new Locale::SubCountry($country) };
111 my @states = $subcountry ? $subcountry->all_codes : undef;
113 if ( !scalar(@states) || ( scalar(@states)==1 && !defined($states[0]) ) ) {
115 _add_locale( 'country'=>$country );
119 if ( $states[0] =~ /^(\d+|\w)$/ ) {
120 @states = map $subcountry->full_name($_), @states
123 foreach my $state ( @states ) {
124 _add_locale( 'country'=>$country, 'state'=>$state);
132 my $cust_main_county = new FS::cust_main_county( { 'tax'=>0, @_ });
133 my $error = $cust_main_county->insert;
134 die $error if $error;
137 sub populate_duplock {
139 return unless driver_name =~ /^mysql/i;
141 my $sth = dbh->prepare(
142 "INSERT INTO duplicate_lock ( lockname ) VALUES ( 'svc_acct' )"
143 ) or die dbh->errstr;
145 $sth->execute or die $sth->errstr;
149 sub populate_initial_data {
152 my $data = initial_data(%opt);
154 foreach my $table ( keys %$data ) {
156 #warn "popuilating $table\n";
158 my $class = "FS::$table";
162 $class->_populate_initial_data(%opt)
163 if $class->can('_populate_initial_data');
165 my @records = @{ $data->{$table} };
167 foreach my $record ( @records ) {
169 my $args = delete($record->{'_insert_args'}) || [];
170 my $object = $class->new( $record );
171 my $error = $object->insert( @$args );
172 die "error inserting record into $table: $error\n"
175 #my $pkey = $object->primary_key;
176 #my $pkeyvalue = $object->$pkey();
177 #warn " inserted $pkeyvalue\n";
188 #tie my %hash, 'Tie::DxHash',
189 tie my %hash, 'Tie::IxHash',
193 { 'username' => 'fs_bootstrap',
194 '_password' => 'changeme', #will trigger warning if you try to enable
196 'first' => 'Bootstrap',
203 { 'groupname' => 'Superuser' },
209 #XXX need default new-style billing events
211 # 'part_bill_event' => [
212 # { 'payby' => 'CARD',
213 # 'event' => 'Batch card',
215 # 'eventcode' => '$cust_bill->batch_card(%options);',
217 # 'plan' => 'batch-card',
219 # { 'payby' => 'BILL',
220 # 'event' => 'Send invoice',
222 # 'eventcode' => '$cust_bill->send();',
226 # { 'payby' => 'DCRD',
227 # 'event' => 'Send invoice',
229 # 'eventcode' => '$cust_bill->send();',
233 # { 'payby' => 'DCHK',
234 # 'event' => 'Send invoice',
236 # 'eventcode' => '$cust_bill->send();',
240 # { 'payby' => 'DCLN',
241 # 'event' => 'Suspend',
243 # 'eventcode' => '$cust_bill->suspend();',
245 # 'plan' => 'suspend',
247 # #{ 'payby' => 'DCLN',
248 # # 'event' => 'Retriable',
250 # # 'eventcode' => '$cust_bill_event->retriable();',
252 # # 'plan' => 'retriable',
256 #you must create a service definition. An example of a service definition
257 #would be a dial-up account or a domain. First, it is necessary to create a
258 #domain definition. Click on View/Edit service definitions and Add a new
259 #service definition with Table svc_domain (and no modifiers).
262 'svcdb' => 'svc_domain',
266 #Now that you have created your first service, you must create a package
267 #including this service which you can sell to customers. Zero, one, or many
268 #services are bundled into a package. Click on View/Edit package
269 #definitions and Add a new package definition which includes quantity 1 of
270 #the svc_domain service you created above.
272 { 'pkg' => 'System Domain',
273 'comment' => '(NOT FOR CUSTOMERS)',
277 'pkg_svc' => { 1 => 1 }, # XXX
278 'primary_svc' => 1, #XXX
287 #After you create your first package, then you must define who is able to
288 #sell that package by creating an agent type. An example of an agent type
289 #would be an internal sales representitive which sells regular and
290 #promotional packages, as opposed to an external sales representitive
291 #which would only sell regular packages of services. Click on View/Edit
292 #agent types and Add a new agent type.
294 { 'atype' => 'Internal' },
297 #Allow this agent type to sell the package you created above.
299 { 'typenum' => 1, #XXX
304 #After creating a new agent type, you must create an agent. Click on
305 #View/Edit agents and Add a new agent.
307 { 'agent' => 'Internal',
308 'typenum' => 1, # XXX
312 #Set up at least one Advertising source. Advertising sources will help you
313 #keep track of how effective your advertising is, tracking where customers
314 #heard of your service offerings. You must create at least one advertising
315 #source. If you do not wish to use the referral functionality, simply
316 #create a single advertising source only. Click on View/Edit advertising
317 #sources and Add a new advertising source.
319 { 'referral' => 'Internal', },
322 #Click on New Customer and create a new customer for your system accounts
323 #with billing type Complimentary. Leave the First package dropdown set to
326 { 'agentnum' => 1, #XXX
329 'last' => 'Accounts',
330 'address1' => '1234 System Lane',
331 'city' => 'Systemtown',
336 'payinfo' => 'system', #or something
337 'paydate' => '1/2037',
341 #From the Customer View screen of the newly created customer, order the
342 #package you defined above.
344 { 'custnum' => 1, #XXX
349 #From the Package View screen of the newly created package, choose
350 #(Provision) to add the customer's service for this new package.
351 #Add your own domain.
353 { 'domain' => $opt{'domain'},
356 'action' => 'N', #pseudo-field
360 #Go back to View/Edit service definitions on the main menu, and Add a new
361 #service definition with Table svc_acct. Select your domain in the domsvc
362 #Modifier. Set Fixed to define a service locked-in to this domain, or
363 #Default to define a service which may select from among this domain and
364 #the customer's domains.
380 sub populate_access {
383 use FS::access_right;
385 foreach my $rightname ( FS::AccessRight->default_superuser_rights ) {
386 my $access_right = new FS::access_right {
387 'righttype' => 'FS::access_group',
388 'rightobjnum' => 1, #$supergroup->groupnum,
389 'rightname' => $rightname,
391 my $ar_error = $access_right->insert;
392 die $ar_error if $ar_error;
395 #foreach my $agent ( qsearch('agent', {} ) ) {
396 my $access_groupagent = new FS::access_groupagent {
397 'groupnum' => 1, #$supergroup->groupnum,
398 'agentnum' => 1, #$agent->agentnum,
400 my $aga_error = $access_groupagent->insert;
401 die $aga_error if $aga_error;
406 sub populate_msgcat {
408 use FS::Record qw(qsearch);
411 foreach my $del_msgcat ( qsearch('msgcat', {}) ) {
412 my $error = $del_msgcat->delete;
413 die $error if $error;
416 my %messages = msgcat_messages();
418 foreach my $msgcode ( keys %messages ) {
419 foreach my $locale ( keys %{$messages{$msgcode}} ) {
420 my $msgcat = new FS::msgcat( {
421 'msgcode' => $msgcode,
423 'msg' => $messages{$msgcode}{$locale},
425 my $error = $msgcat->insert;
426 die $error if $error;
432 sub msgcat_messages {
435 # 'en_US' => 'Message',
440 'passwords_dont_match' => {
441 'en_US' => "Passwords don't match",
445 'en_US' => 'Invalid credit card number',
448 'unknown_card_type' => {
449 'en_US' => 'Unknown card type',
456 'empty_password' => {
457 'en_US' => 'Empty password',
460 'no_access_number_selected' => {
461 'en_US' => 'No access number selected',
465 'en_US' => 'Illegal (text)',
466 #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in field',
469 'illegal_or_empty_text' => {
470 'en_US' => 'Illegal or empty (text)',
471 #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in required field',
474 'illegal_username' => {
475 'en_US' => 'Illegal username',
478 'illegal_password' => {
479 'en_US' => 'Illegal password (',
482 'illegal_password_characters' => {
483 'en_US' => ' characters)',
486 'username_in_use' => {
487 'en_US' => 'Username in use',
490 'phonenum_in_use' => {
491 'en_US' => 'Phone number in use',
494 'illegal_email_invoice_address' => {
495 'en_US' => 'Illegal email invoice address',
499 'en_US' => 'Illegal (name)',
500 #'en_US' => 'Only letters, numbers, spaces and the following punctuation symbols are permitted: , . - \' in field',
504 'en_US' => 'Illegal (phone)',
509 'en_US' => 'Illegal (zip)',
514 'en_US' => 'Expired card',
518 'en_US' => 'Day Phone',
522 'en_US' => 'Night Phone',
525 'svc_external-id' => {
526 'en_US' => 'External ID',
529 'svc_external-title' => {
534 'en_US' => 'Driver\'s License',
538 'en_US' => 'Driver\'s License State',
541 'invalid_domain' => {
542 'en_US' => 'Invalid domain',