hesco has asked for the wisdom of the Perl Monks concerning the following question:

Hello all:

I'm working on a simple event registration form. I was hoping to avoid having to recreate all that business acocunting logic which folks like Dieter Simander and the LedgerSMB team have already figured out and automate the process of keeping the books up to snuff as registrations were made across my client's webform.

I have found that I can perform all sorts of useful interactions with the SL/LS database using either direct connections to the database backend (for data searches) or using WWW::Mechanize (so I can count on th SL / LS code to handle data integrity issues on a data insert operation. My $reg_form->create_sl_customer_mech() method works just fine and even prevents exact duplicates.

But the one method which would be the clincher for me in terms of delivering the functionality required by my client on this project, is the ability to create an invoice. On this piece I have been unable to achieve success.

sub create_sl_invoice_mech { my $self = shift; my ($customer, $event_id) = @_; my ($inv_id,$inv_pdf_path,$inv_html_path); $inv_pdf_path = '/path/to/pdf/file'; $inv_html_path = '/path/to/html/file'; print STDERR "Now creating new invoice for $customer->{'name'}.\n"; my $base_url = $self->{'Cfg'}->{'default'}->{'base_url'}; my $user = $self->{'Cfg'}->{'sl_event_registration'}->{'user'}; my $pw = $self->{'Cfg'}->{'sl_event_registration'}->{'pw'}; # 'is.pl?path=bin/mozilla&action=add&level=AR--Sales%20Invoice&login +=test-user&js=1&type=invoice'; my $arg = 'path=bin/mozilla'; $arg .= '&login=' .$user; $arg .= '&password=' . $pw; $arg .= '&action=Login'; $arg .= '&action=add'; $arg .= '&level=AR--Sales%20Invoice'; $arg .= '&login=' .$user; $arg .= '&js=1'; $arg .= '&type=invoice'; my $url = $base_url . 'is.pl?' . $arg; print STDERR "\$url is: $url \n"; my $agent = $self->{'mech'}; $agent->get($url); if($agent->success()){ $agent->field('customer', $customer->{'customer_id'}); print STDERR "customer_id is: $customer->{'customer_id'}\n"; $agent->field('employee', $self->{'Cfg'}->{'default'}->{'employee' +}); print STDERR "Employee is: $self->{'Cfg'}->{'default'}->{'employee +'}\n"; # 'Finding Your Center Event Registration--10064'); $agent->field('partnumber_1', $event_id ); print STDERR "As partnumber_1 we will use $event_id\n"; $agent->click_button( name => 'action', value => 'Update' ); if($agent->success()){ print "\$agent reports that invoice was successfully updated\n"; } $agent->click_button( name => 'action', value => 'Post' ); if($agent->success()){ print '\$agent reports that invoice was successfully posted',"\n +"; } # sleep 15; # $agent->click_button( name => 'action', value => 'Post' ); # $agent->click_button( name => 'action', value => 'Post' ); # sleep 15; # $agent->click_button( name => 'action', value => 'Post' ); # $agent->click_button( name => 'action', value => 'Post' ); # sleep 15; # $agent->click_button( name => 'action', value => 'Post' ); } else { croak "Unable to get invoice creation form.\n"; } # print STDERR $agent->content(); open('INVOICE','>','/home/hesco/sb/fyc_registration/invoice.htm +l'); print INVOICE $agent->content(); close(INVOICE); print STDERR "We're about to search_sl_invoice_mech for $customer->{ +'customer_id'} at this point . . . \n"; my @invoice_ids = $self->search_sl_invoice_mech($customer->{'custome +r_id'}); print STDERR "We have search_sl_invoice_mech and found |@invoice_ids +| . . . \n"; # print Dumper($customer); # print STDERR "We're about to retrieve_sl_invoice_mech at this poin +t . . . "; my $inv = $self->retrieve_sl_invoice_mech($customer,$invoice_ids[0]) +; # print STDERR 'We have retrieve_sl_invoice_mech at this point . . + . '; $inv_id = $inv->{'id'} || $invoice_ids[0] || 0; my $amount = $inv->{'amount'} || 0; my $msg = sprintf ("Have now grabbed id: %u for \$%.2f as the new in +voice.\n", $inv_id, $amount); print STDERR $msg; my %invoice = ( id => $inv_id, amount => $amount, pdf_path => "$inv_pdf_path", html_path => "$inv_html_path", ); $self->{'Cfg'}->{'invoices'}->{'latest_invoice'} = \%invoice; return \%invoice; }
My test results are looking like this:

ok 7 - Correctly identified that no invoice id number exists for new c +ustomer. ok 8 - ->search_sl_customer_by_name_mech() found id: 10089 for newly c +reated customer. Now creating new invoice for Testy D Tester. $url is: http://192.168.1.103/sl-test/is.pl?path=bin/mozilla&login=tes +t_user&password=secret&action=Login&action=add&level=AR--Sales%20Invo +ice&login=test_user&js=1&type=invoice customer_id is: Testy D Tester--10089 Employee is: Finding Your Center Event Registration--10064 As partnumber_1 we will use G0002 $agent reports that invoice was successfully updated \$agent reports that invoice was successfully posted We're about to search_sl_invoice_mech for Testy D Tester--10089 at thi +s point . . . We have search_sl_invoice_mech and found || . . . $VAR1 = { 'pdf_path' => '', 'amount' => undef, 'customer' => 'Testy D Tester', 'id' => 0, 'event' => '', 'html_path' => '' }; Have now grabbed id: 0 for $0.00 as the new invoice. <h1>Software error:</h1> <pre>Delete this croak statement to run rest of test script.
The readmore tags hide the subroutine I'm working on now and the output of the test script I'm using to exercise it.

My apologies for not distilling this to something which can run in a local environment. Unless you have SL or LS installed locally, this is not code which can be easily tested. But I'm hoping that someone familiar with either WWW::Mechanize or with the SL / LS code base or both might be able to recognize from this routine something obvious to them which I have overlooked.

If I take the invoice.html file written out by my debugging code, adjust the action tag to give a fully qualified url and then invoke it with lynx to 'Post' it, a new invoice shows up in my accounting backend. But no matter how many times I try to post it using WWW::Mechanize, or sleep between different invocations, nothing seems to work inside the ->create_sl_invoice_mech() method that would look like success.

I'm guessing that I should not have to edit $agent->content() before posting the create invoice form, but perhaps I don't understand enough about how this works.

Any ideas about how to proceed would be geratly appreciated.

-- Hugh

if( $lal && $lol ) { $life++; }

Replies are listed 'Best First'.
Re: Integration with SQL-Ledger / LedgerSMB
by Anonymous Monk on Jun 06, 2007 at 04:27 UTC
    Realize, too, that you can use a module such as HTTP::Recorder or WWW::Mechanize::Shell to record one (or many) successful manual form submission(s). The output of HTTP::Recorder, for instance, can be "dropped" right into your WWW::Mechanize scripts. This makes the development of such scripts trivially easy.