From: ivan Date: Thu, 14 Oct 2010 01:14:27 +0000 (+0000) Subject: more contact work and preliminary business card upload X-Git-Tag: TORRUS_1_0_9~219 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=7bbe939d139be7edd77378cfc9a2c3ec6287749d more contact work and preliminary business card upload --- diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index 2282bc58c..550ea1a45 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -126,7 +126,7 @@ if ( -e $addl_handler_use_file ) { use FS::UI::Web::small_custview qw(small_custview); use FS::UI::bytecount; use FS::Msgcat qw(gettext geterror); - use FS::Misc qw( send_email send_fax + use FS::Misc qw( send_email send_fax ocr_image states_hash counties cities state_label ); use FS::Misc::eps2png qw( eps2png ); diff --git a/FS/FS/Misc.pm b/FS/FS/Misc.pm index 0e8d92bb3..fe8ac6082 100644 --- a/FS/FS/Misc.pm +++ b/FS/FS/Misc.pm @@ -20,6 +20,7 @@ use Tie::IxHash; pkg_freqs generate_ps generate_pdf do_print csv_from_fixed + ocr_image ); $DEBUG = 0; @@ -850,6 +851,41 @@ sub csv_from_fixed { ''; } +=item ocr_image IMAGE_SCALAR + +Runs OCR on the provided image data and returns a list of text lines. + +=cut + +sub ocr_image { + my $logo_data = shift; + + #XXX use conf dir location from Makefile + my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc; + my $fh = new File::Temp( + TEMPLATE => 'bizcard.XXXXXXXX', + SUFFIX => '.png', #XXX assuming, but should handle jpg, gif, etc. too + DIR => $dir, + UNLINK => 0, + ) or die "can't open temp file: $!\n"; + + my $filename = $fh->filename; + + print $fh $logo_data; + close $fh; + + run( [qw(ocroscript recognize), $filename], '>'=>"$filename.hocr" ) + or die "ocroscript recognize failed\n"; + + run( [qw(ocroscript hocr-to-text), "$filename.hocr"], '>pipe'=>\*OUT ) + or die "ocroscript hocr-to-text failed\n"; + + my @lines = split(/\n/, ); + + foreach (@lines) { s/\.c0m\s*$/.com/; } + + @lines; +} =back diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 6a987ceb1..a4c12aaff 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -831,8 +831,8 @@ sub tables_hashref { 'last', 'varchar', '', $char_d, '', '', # 'middle', 'varchar', 'NULL', $char_d, '', '', 'first', 'varchar', '', $char_d, '', '', - 'title', 'varchar', '', $char_d, '', '', #eg Head Bottle Washer - 'comment', 'varchar', '', $char_d, '', '', + 'title', 'varchar', 'NULL', $char_d, '', '', #eg Head Bottle Washer + 'comment', 'varchar', 'NULL', $char_d, '', '', 'disabled', 'char', 'NULL', 1, '', '', ], 'primary_key' => 'contactnum', @@ -844,13 +844,13 @@ sub tables_hashref { 'contact_phone' => { 'columns' => [ - 'contactphonenum', 'serial', '', '', '', '', - 'contactnum', 'int', '', '', '', '', - 'phonetypenum', 'int', '', '', '', '', - 'countrycode', 'varchar', '', 3, '', '', - 'phonenum', 'varchar', '', 14, '', '', - 'extension', 'varchar', '', 7, '', '', - #?#'comment', 'varchar', '', $char_d, '', '', + 'contactphonenum', 'serial', '', '', '', '', + 'contactnum', 'int', '', '', '', '', + 'phonetypenum', 'int', '', '', '', '', + 'countrycode', 'varchar', '', 3, '', '', + 'phonenum', 'varchar', '', 14, '', '', + 'extension', 'varchar', 'NULL', 7, '', '', + #?#'comment', 'varchar', '', $char_d, '', '', ], 'primary_key' => 'contactphonenum', 'unique' => [], diff --git a/FS/FS/contact.pm b/FS/FS/contact.pm index d3ab4118a..774aed088 100644 --- a/FS/FS/contact.pm +++ b/FS/FS/contact.pm @@ -2,10 +2,12 @@ package FS::contact; use strict; use base qw( FS::Record ); -use FS::Record qw( qsearch qsearchs ); +use FS::Record qw( qsearch qsearchs dbh ); use FS::prospect_main; use FS::cust_main; use FS::cust_location; +use FS::contact_phone; +use FS::contact_email; =head1 NAME @@ -96,7 +98,59 @@ otherwise returns false. =cut -# the insert method can be inherited from FS::Record +sub insert { + my $self = shift; + + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + my $error = $self->SUPER::insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + foreach my $pf ( grep { /^phonetypenum(\d+)$/ && $self->get($_) =~ /\S/ } + keys %{ $self->hashref } ) { + $pf =~ /^phonetypenum(\d+)$/ or die "wtf (daily, the)"; + my $phonetypenum = $1; + + my $contact_phone = new FS::contact_phone { + 'contactnum' => $self->contactnum, + 'phonetypenum' => $phonetypenum, + _parse_phonestring( $self->get($pf) ), + }; + $error = $contact_phone->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + if ( $self->get('emailaddress') =~ /\S/ ) { + my $contact_email = new FS::contact_email { + 'contactnum' => $self->contactnum, + 'emailaddress' => $self->get('emailaddress'), + }; + $error = $contact_email->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + + ''; + +} =item delete @@ -106,6 +160,8 @@ Delete this record from the database. # the delete method can be inherited from FS::Record +# XXX delete contact_phone, contact_email + =item replace OLD_RECORD Replaces the OLD_RECORD with this one in the database. If there is an error, @@ -113,7 +169,76 @@ returns the error, otherwise returns false. =cut -# the replace method can be inherited from FS::Record +sub replace { + my $self = shift; + + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + my $error = $self->SUPER::replace(@_); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + foreach my $pf ( grep { /^phonetypenum(\d+)$/ && $self->get($_) } + keys %{ $self->hashref } ) { + $pf =~ /^phonetypenum(\d+)$/ or die "wtf (daily, the)"; + my $phonetypenum = $1; + + my %cp = ( 'contactnum' => $self->contactnum, + 'phonetypenum' => $phonetypenum, + ); + my $contact_phone = qsearchs('contact_phone', \%cp) + || new FS::contact_phone \%cp; + + my %cpd = _parse_phonestring( $self->get($pf) ); + $contact_phone->set( $_ => $cpd{$_} ) foreach keys %cpd; + + my $method = $contact_phone->contactphonenum ? 'replace' : 'insert'; + + $error = $contact_phone->$method; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + + ''; + +} + +#i probably belong in contact_phone.pm +sub _parse_phonestring { + my $value = shift; + + my($countrycode, $extension) = ('1', ''); + + #countrycode + if ( $value =~ s/^\s*\+\s*(\d+)// ) { + $countrycode = $1; + } else { + $value =~ s/^\s*1//; + } + #extension + if ( $value =~ s/\s*(ext|x)\s*(\d+)\s*$//i ) { + $extension = $2; + } + + ( 'countrycode' => $countrycode, + 'phonenum' => $value, + 'extension' => $extension, + ); +} =item check @@ -165,8 +290,6 @@ sub line { =head1 BUGS -The author forgot to customize this manpage. - =head1 SEE ALSO L, schema.html from the base documentation. diff --git a/FS/FS/contact_phone.pm b/FS/FS/contact_phone.pm index bb9cf034d..ad8e8f737 100644 --- a/FS/FS/contact_phone.pm +++ b/FS/FS/contact_phone.pm @@ -120,7 +120,7 @@ sub check { || $self->ut_number('phonetypenum') || $self->ut_text('countrycode') || $self->ut_text('phonenum') - || $self->ut_text('extension') + || $self->ut_textn('extension') ; return $error if $error; diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 104a0c49c..4e42f8de0 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -26,7 +26,7 @@ use Tie::IxHash; use Digest::MD5 qw(md5_base64); use Date::Format; #use Date::Manip; -use File::Temp qw( tempfile ); +use File::Temp; #qw( tempfile ); use Business::CreditCard 0.28; use Locale::Country; use FS::UID qw( getotaker dbh driver_name ); diff --git a/httemplate/edit/process/prospect_main.html b/httemplate/edit/process/prospect_main.html index 34d26421b..ca4dfabfe 100644 --- a/httemplate/edit/process/prospect_main.html +++ b/httemplate/edit/process/prospect_main.html @@ -4,7 +4,7 @@ 'agent_virt' => 1, 'process_o2m' => { 'table' => 'contact', - 'fields' => [qw( first last title comment )], + 'fields' => \@contact_fields, }, 'redirect' => popurl(3). 'view/prospect_main.html?', ) @@ -31,4 +31,9 @@ my $args_callback = sub { }; +my @contact_fields = qw( first last title comment emailaddress ); +foreach my $phone_type ( qsearch({table=>'phone_type', order_by=>'weight'}) ) { + push @contact_fields, 'phonetypenum'.$phone_type->phonetypenum; +} + diff --git a/httemplate/edit/prospect_main-ocr.html b/httemplate/edit/prospect_main-ocr.html new file mode 100644 index 000000000..41fc4c105 --- /dev/null +++ b/httemplate/edit/prospect_main-ocr.html @@ -0,0 +1,86 @@ +<% include("/elements/header.html", 'Upload business card' ) %> + +% if ( $error ) { + Error: <% $error %> +

+% } else { + +
+ + + + +% my $num = 0; +% foreach my $line ( @lines ) { + + + + +% unless ( $num++) { + + + +% } + + +% } + +
+ + + <% $line %>
+ +
+ + +% } +<% include('/elements/footer.html') %> +<%init> + +my $fh = $cgi->upload('card'); + +my $error = ''; +my @lines = (); +my $session = ''; +if ( defined $fh ) { + + local $/; + my $logo_data = <$fh>; + + $session = int(rand(4294967296)); #XXX + my $pref = new FS::access_user_pref({ + 'usernum' => $FS::CurrentUser::CurrentUser->usernum, + 'prefname' => "bizcard$session", + 'prefvalue' => encode_base64($logo_data), + 'expiration' => time + 3600, #1h? 1m? + }); + my $pref_error = $pref->insert; + if ( $pref_error ) { + die "FATAL: couldn't set preview cookie: $pref_error\n"; + } + + @lines = eval { ocr_image($logo_data); }; + $error = $@ if $error; + +} else { + + $error = 'No file uploaded'; + +} + + diff --git a/httemplate/edit/prospect_main-upload.html b/httemplate/edit/prospect_main-upload.html new file mode 100644 index 000000000..24b1caa4c --- /dev/null +++ b/httemplate/edit/prospect_main-upload.html @@ -0,0 +1,7 @@ +<% include("/elements/header.html", 'Upload business card' ) %> + + + +
+ +<% include('/elements/footer.html') %> diff --git a/httemplate/edit/prospect_main.html b/httemplate/edit/prospect_main.html index e867907ed..c260eb8e2 100644 --- a/httemplate/edit/prospect_main.html +++ b/httemplate/edit/prospect_main.html @@ -5,6 +5,7 @@ 'agentnum' => 'Agent', 'company' => 'Company', 'contactnum' => 'Contact', + 'locationnum' => ' ', }, 'fields' => [ { 'field' => 'agentnum', @@ -34,6 +35,7 @@ 'empty_label' => 'No address', }, ], + 'new_callback' => $new_callback, 'edit_callback' => $edit_callback, 'error_callbacck' => $error_callback, 'agent_virt' => 1, @@ -62,6 +64,48 @@ if ( $cgi->param('error') ) { } +my $new_callback = sub { + my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_; + + if ( $cgi->param('session') =~ /^(\w+)$/ ) { + my $session = $1; + + #add a link to the image.cgi for this card + $opt_hashref->{'html_bottom'} .= + qq(

'; + + #fill in the incoming params: name, address1/address2, city_state_zip + foreach my $param ( grep /^sel\d+$/, $cgi->param ) { + $param =~ /^sel(\d+)$/ or die 'again, wtf (daily)'; + my $num = $1; + my $field = $cgi->param($param); + my $value = $cgi->param("val$num"); + $cgi->param($field => $value); + } + + if ( $cgi->param('company') ) { + $prospect_main->company( $cgi->param('company') ); + } + + if ( $cgi->param('name') =~ /^(.*\S+)\s+(\w+)\s*$/ ) { + $cgi->param('contactnum0_first' => $1); + $cgi->param('contactnum0_last' => $2); + } + + if ( grep $cgi->param($_), qw( address1 address2 city_state_zip ) ) { + $cgi->param('locationnum', -1); + if ( $cgi->param('city_state_zip') =~ /^(\s*)([\w\s]+)[\., ]+(\w{2})[, ]+(\d{5}(-\d{4})?)/ ) { + $cgi->param('city' => $2); + $cgi->param('state' => $3); + $cgi->param('zip' => $4); + } + } + + } + +}; + my $edit_callback = sub { #my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_; my( $cgi, $prospect_main ) = @_; diff --git a/httemplate/elements/contact.html b/httemplate/elements/contact.html index a7a33b1f0..eea3694e3 100644 --- a/httemplate/elements/contact.html +++ b/httemplate/elements/contact.html @@ -5,12 +5,38 @@ % foreach my $field ( @fields ) { +% +% my $value = ''; +% if ( $field =~ /^phonetypenum(\d+)$/ ) { +% my $contact_phone = qsearchs('contact_phone', { +% 'contactnum' => $curr_value, +% 'phonetypenum' => $1, +% }); +% if ( $contact_phone ) { +% $value = $contact_phone->phonenum; +% $value .= 'x'.$contact_phone->extension +% if $contact_phone->extension; +% $value = '+'. $contact_phone->countrycode. " $value" +% if $contact_phone->countrycode +% && $contact_phone->countrycode ne '1'; +% } +% } elsif ( $field eq 'emailaddress' ) { +% #XXX multiple not yet supported +% my $contact_email = qsearchs('contact_email', { +% 'contactnum' => $curr_value, +% }); +% $value = $contact_email->emailaddress if $contact_email; +% } else { +% $value = $contact->get($field); +% } +
- get($field) |h %>" + || $value |h %>" <% $onchange %> >
<% $label{$field} %> @@ -45,12 +71,25 @@ if ( $curr_value ) { $contact = new FS::contact {}; } +my %size = ( 'title' => 12 ); + tie my %label, 'Tie::IxHash', - 'first' => 'First name', - 'last' => 'Last name', - 'title' => 'Title/Position', - 'comment' => 'Comment', + 'first' => 'First name', + 'last' => 'Last name', + 'title' => 'Title/Position', + 'emailaddress' => 'Email', ; + +my $first = 0; +foreach my $phone_type ( qsearch({table=>'phone_type', order_by=>'weight'}) ) { + next if $phone_type->typename eq 'Home'; + my $f = 'phonetypenum'.$phone_type->phonetypenum; + $label{$f} = $phone_type->typename. ' phone'; + $size{$f} = $first++ ? 11 : 15; +} + +$label{'comment'} = 'Comment'; + my @fields = keys %label; diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 09b8e7405..cc52aaef8 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -364,11 +364,13 @@ $tools_menu{'Process payment batches'} = [ $fsurl.'search/pay_batch.cgi?magic=_d if ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') ) && $curuser->access_right('Process batches'); $tools_menu{'Process invoice batches'} = [ $fsurl.'search/bill_batch.cgi' ] - if ( $conf->exists('invoice_print_pdf') ); + if $conf->exists('invoice_print_pdf'); $tools_menu{'Job Queue'} = [ $fsurl.'search/queue.html', 'View pending job queue' ] if $curuser->access_right('Job queue'); $tools_menu{'Ticketing'} = [ \%tools_ticketing, 'Ticketing tools' ] if $conf->config('ticket_system'); +$tools_menu{'Business card scan'} = [ $fsurl.'edit/prospect_main-upload.html' ] + if $curuser->access_right('New prospect'); $tools_menu{'Time Queue'} = [ $fsurl.'search/report_timeworked.html', 'View pending support time' ] if $curuser->access_right('Time queue'); $tools_menu{'Attachments'} = [ $fsurl.'browse/cust_attachment.html', 'View customer attachments' ] diff --git a/httemplate/view/image.cgi b/httemplate/view/image.cgi new file mode 100644 index 000000000..153ec858e --- /dev/null +++ b/httemplate/view/image.cgi @@ -0,0 +1,31 @@ +<% $data %>\ +<%init> + +#die "access denied" +# unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; + +my $type; +if ( $cgi->param('type') eq 'png' ) { + $type = 'png'; +} elsif ( $cgi->param('type') eq 'eps' ) { + $type = 'eps'; +} else { + die "unknown image type ". $cgi->param('type'); +} + +my $data; +if ( $cgi->param('prefname') =~ /^(\w+)$/ ) { + + my $prefname = $1; + my $curuser = $FS::CurrentUser::CurrentUser; + $data = decode_base64( $curuser->option("$prefname") ); + +} else { + die "no preview_session specified"; +} + +http_header('Content-Type' => 'image/png' ); + +