From 6c44a351d95cf1bcfa2a9f614382b818f581b1f6 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Fri, 15 Mar 2019 14:39:26 +0100 Subject: [PATCH] =?utf8?q?API=20f=C3=BCr=20Telefonnummernr=C3=BCckw=C3=A4r?= =?utf8?q?tssuche?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- SL/Controller/PhoneNumber.pm | 221 ++++++++++++++++++++++++++++++++++ config/kivitendo.conf.default | 2 + 2 files changed, 223 insertions(+) create mode 100644 SL/Controller/PhoneNumber.pm diff --git a/SL/Controller/PhoneNumber.pm b/SL/Controller/PhoneNumber.pm new file mode 100644 index 000000000..4bfbe3ebb --- /dev/null +++ b/SL/Controller/PhoneNumber.pm @@ -0,0 +1,221 @@ +package SL::Controller::PhoneNumber; + +use utf8; +use strict; +use parent qw(SL::Controller::Base); + +use List::MoreUtils qw(any); +use List::Util qw(first); + +use SL::JSON; +use SL::DBUtils; +use SL::Locale::String; +use SL::CTI; + +use SL::DB::Contact; +use SL::DB::Customer; +use SL::DB::Vendor; + +use Data::Dumper; + +sub action_look_up { + my ($self) = @_; + + my $number = $self->normalize_number($::form->{number} // ''); + + return $self->render(\to_json({}), { type => 'json', process => 0 }) if ($number eq ''); + + my $result = $self->find_contact_for_number($number) + // $self->find_customer_vendor_for_number($number) + // {}; + + $self->render(\to_json($result), { type => 'json', process => 0 }); +} + +sub find_contact_for_number { + my ($self, $number) = @_; + + my @number_fields = qw(cp_phone1 cp_phone2 cp_mobile1 cp_mobile2 cp_fax); + + my $contacts = SL::DB::Manager::Contact->get_all( + inject_results => 1, + where => [ map({ (or => [ "!$_" => undef, "!$_" => "" ],) } @number_fields) ], + ); + + my @hits; + + foreach my $contact (@{ $contacts }) { + foreach my $field (@number_fields) { + next if $self->normalize_number($contact->$field) ne $number; + + push @hits, $contact; + last; + } + } + + return if !@hits; + + my @cv_ids = grep { $_ } map { $_->cp_cv_id } @hits; + + my %customers_vendors = + map { ($_->id => $_) } ( + @{ SL::DB::Manager::Customer->get_all(where => [ id => \@cv_ids ], inject_results => 1) }, + @{ SL::DB::Manager::Vendor ->get_all(where => [ id => \@cv_ids ], inject_results => 1) }, + ); + + my $chosen = first { + $_->cp_cv_id + && $customers_vendors{$_->cp_cv_id} + && !$customers_vendors{$_->cp_cv_id}->obsolete + && ($_->cp_name !~ m{ungültig}i) + } @hits; + + $chosen //= $hits[0]; + + return { + full_name => join(' ', grep { $_ ne '' } map { $_ // '' } ($chosen->cp_title, $chosen->cp_givenname, $chosen->cp_name)), + map({ my $method = "cp_$_"; ($_ => $chosen->$method // '') } qw(title givenname name phone1 phone2 mobile1 mobile2 fax)), + }; +} + +sub find_customer_vendor_for_number { + my ($self, $number) = @_; + + my @number_fields = qw(phone fax); + + my @customers_vendors = map { + my $class = "SL::DB::Manager::$_"; + @{ $class->get_all( + inject_results => 1, + where => [ map({ (or => [ "!$_" => undef, "!$_" => "" ],) } @number_fields) ], + ) } + } qw(Customer Vendor); + + my @hits; + + foreach my $customer_vendor (@customers_vendors) { + foreach my $field (@number_fields) { + next if $self->normalize_number($customer_vendor->$field) ne $number; + + push @hits, $customer_vendor; + last; + } + } + + return if !@hits; + + my $chosen = first { !$_->obsolete } @hits; + $chosen //= $hits[0]; + + return { + full_name => $chosen->name // '', + phone1 => $chosen->phone // '', + fax => $chosen->fax // '', + map({ ($_ => '') } qw(title givenname name phone2 mobile1 mobile2)), + }; +} + +sub normalize_number { + my ($self, $number) = @_; + + return '' if ($number // '') eq ''; + + my $config = $::lx_office_conf{cti} || {}; + my $idp = $config->{international_dialing_prefix} // '00'; + my $country_code = $config->{our_country_code} // '49'; + + $number = SL::CTI->sanitize_number(number => $number); + + return $number if $number =~ m{^$idp}; + + $number =~ s{^0+}{}; + + return $idp . $country_code . $number; +} + +1; + +__END__ + +=pod + +=encoding utf8 + +=head1 NAME + +SL::Controller::Contact - Looking up information on contacts/customers/vendors based on a phone number + +=head1 FUNCTIONS + +=over 4 + +=item C + +This action can be used by external systems such as PBXes in order to +match a calling number to a name. Requires one form parameter to be +passed, C. + +The number will then be normalized. This requires that the +international dialing prefix and the server's own country code be set +up in C, section C<[cti]>. They default to C<00> and +C<49> (Germany) respectively. + +Next the function will look up a contact whose normalized numbers +equals the requested number. The fields C, C, +C, C and C are considered. Active contacts are +given preference over inactive ones (inactive meaning that they don't +belong to a customer/vendor anymore or that the customer/vendor itself +is flagged as obsolete). + +If no contact is found, customers & vendors are searched. Their fields +C and C are considered. The first customer/vendor who +isn't flagged as being obsolete is chosen; if there's none, the first +obsolete one is. + +The function always sends one JSON-encoded object. If there's no hit +for the number, an empty object is returned. Otherwise the following +fields are present: + +=over 4 + +=item C — for contacts this is the concatenation of the +title, given name and family name; for customers/vendors it's the +company name + +=item C — title (empty for customers/vendors) + +=item C<givenname> — first/given name (empty for customers/vendors) + +=item C<name> — last/family name (empty for customers/vendors) + +=item C<phone1> — first phone number (for all object types) + +=item C<phone2> — second phone number (empty for customers/vendors) + +=item C<mobile1> — first mobile number (empty for customers/vendors) + +=item C<mobile2> — second mobile number (empty for customers/vendors) + +=item C<fax> — fax number (for all object types) + +=back + +Here's an example how querying the API via C<curl> might look: + + curl --user user_name:password 'https://…/kivitendo/controller.pl?action=PhoneNumber/look_up&number=0049987655443321' + +Note that the request must be authenticated via a valid Kivitendo +login. However, the user doesn't need any special permissions within +Kivitendo; any valid Kivitendo user will do. + +=back + +=head1 BUGS + +Nothing here yet. + +=head1 AUTHOR + +Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt> + +=cut diff --git a/config/kivitendo.conf.default b/config/kivitendo.conf.default index 108ce6b95..219896c4d 100644 --- a/config/kivitendo.conf.default +++ b/config/kivitendo.conf.default @@ -370,3 +370,5 @@ dial_command = external_prefix = 0 # The prefix for international calls (numbers starting with +). international_dialing_prefix = 00 +# Our own country code +our_country_code = 49 -- 2.20.1