1 package SL::Controller::PhoneNumber;
 
   5 use parent qw(SL::Controller::Base);
 
   7 use List::MoreUtils qw(any);
 
   8 use List::Util qw(first);
 
  12 use SL::Locale::String;
 
  24   my $number = $self->normalize_number($::form->{number} // '');
 
  26   return $self->render(\to_json({}), { type => 'json', process => 0 }) if ($number eq '');
 
  28   my $result = $self->find_contact_for_number($number)
 
  29     //         $self->find_customer_vendor_for_number($number)
 
  32   $self->render(\to_json($result), { type => 'json', process => 0 });
 
  35 sub find_contact_for_number {
 
  36   my ($self, $number) = @_;
 
  38   my @number_fields = qw(cp_phone1 cp_phone2 cp_mobile1 cp_mobile2 cp_fax);
 
  40   my $contacts = SL::DB::Manager::Contact->get_all(
 
  42     where          => [ map({ (or => [ "!$_" => undef, "!$_"  => "" ],) } @number_fields) ],
 
  47   foreach my $contact (@{ $contacts }) {
 
  48     foreach my $field (@number_fields) {
 
  49       next if $self->normalize_number($contact->$field) ne $number;
 
  58   my @cv_ids = grep { $_ } map { $_->cp_cv_id } @hits;
 
  60   my %customers_vendors =
 
  61     map { ($_->id => $_) } (
 
  62       @{ SL::DB::Manager::Customer->get_all(where => [ id => \@cv_ids ], inject_results => 1) },
 
  63       @{ SL::DB::Manager::Vendor  ->get_all(where => [ id => \@cv_ids ], inject_results => 1) },
 
  68     &&  $customers_vendors{$_->cp_cv_id}
 
  69     && !$customers_vendors{$_->cp_cv_id}->obsolete
 
  70     && ($_->cp_name !~ m{ungültig}i)
 
  76     full_name => join(' ', grep { $_ ne '' } map { $_ // '' } ($chosen->cp_title, $chosen->cp_givenname, $chosen->cp_name)),
 
  79     map({ my $method = "cp_$_"; ($_ => $chosen->$method // '') } qw(title givenname name phone1 phone2 mobile1 mobile2 fax)),
 
  83 sub find_customer_vendor_for_number {
 
  84   my ($self, $number) = @_;
 
  86   my @number_fields = qw(phone fax);
 
  88   my @customers_vendors = map {
 
  89       my $class = "SL::DB::Manager::$_";
 
  92            where          => [ map({ (or => [ "!$_" => undef, "!$_"  => "" ],) } @number_fields) ],
 
  94     } qw(Customer Vendor);
 
  98   foreach my $customer_vendor (@customers_vendors) {
 
  99     foreach my $field (@number_fields) {
 
 100       next if $self->normalize_number($customer_vendor->$field) ne $number;
 
 102       push @hits, $customer_vendor;
 
 109   my $chosen = first { !$_->obsolete } @hits;
 
 110   $chosen  //= $hits[0];
 
 113     full_name => $chosen->name  // '',
 
 114     phone1    => $chosen->phone // '',
 
 115     fax       => $chosen->fax   // '',
 
 117     type      => ref($chosen) eq 'SL::DB::Customer' ? 'customer' : 'vendor',
 
 118     map({ ($_ => '') } qw(title givenname name phone2 mobile1 mobile2)),
 
 122 sub normalize_number {
 
 123   my ($self, $number) = @_;
 
 125   return '' if ($number // '') eq '';
 
 127   my $config       = $::lx_office_conf{cti} || {};
 
 128   my $idp          = $config->{international_dialing_prefix} // '00';
 
 129   my $country_code = $config->{our_country_code}             // '49';
 
 131   $number          = SL::CTI->sanitize_number(number => $number);
 
 133   return $number if $number =~ m{^$idp};
 
 137   return $idp . $country_code . $number;
 
 150 SL::Controller::Contact - Looking up information on contacts/customers/vendors based on a phone number
 
 156 =item C<action_look_up>
 
 158 This action can be used by external systems such as PBXes in order to
 
 159 match a calling number to a name. Requires one form parameter to be
 
 162 The number will then be normalized. This requires that the
 
 163 international dialing prefix and the server's own country code be set
 
 164 up in C<kivitendo.conf>, section C<[cti]>. They default to C<00> and
 
 165 C<49> (Germany) respectively.
 
 167 Next the function will look up a contact whose normalized numbers
 
 168 equals the requested number. The fields C<phone1>, C<phone2>,
 
 169 C<mobile1>, C<mobile2> and C<fax> are considered. Active contacts are
 
 170 given preference over inactive ones (inactive meaning that they don't
 
 171 belong to a customer/vendor anymore or that the customer/vendor itself
 
 172 is flagged as obsolete).
 
 174 If no contact is found, customers & vendors are searched. Their fields
 
 175 C<phone> and C<fax> are considered. The first customer/vendor who
 
 176 isn't flagged as being obsolete is chosen; if there's none, the first
 
 179 The function always sends one JSON-encoded object. If there's no hit
 
 180 for the number, an empty object is returned. Otherwise the following
 
 185 =item C<id> — the database ID of the corresponding record
 
 187 =item C<type> — describes the type of record returned; can be either
 
 188 C<contact>, C<customer> or C<vendor>
 
 190 =item C<full_name> — for contacts this is the concatenation of the
 
 191 title, given name and family name; for customers/vendors it's the
 
 194 =item C<title> — title (empty for customers/vendors)
 
 196 =item C<givenname> — first/given name (empty for customers/vendors)
 
 198 =item C<name> — last/family name (empty for customers/vendors)
 
 200 =item C<phone1> — first phone number (for all object types)
 
 202 =item C<phone2> — second phone number (empty for customers/vendors)
 
 204 =item C<mobile1> — first mobile number (empty for customers/vendors)
 
 206 =item C<mobile2> — second mobile number (empty for customers/vendors)
 
 208 =item C<fax> — fax number (for all object types)
 
 212 Here's an example how querying the API via C<curl> might look:
 
 214     curl --user user_name:password 'https://…/kivitendo/controller.pl?action=PhoneNumber/look_up&number=0049987655443321'
 
 216 Note that the request must be authenticated via a valid Kivitendo
 
 217 login. However, the user doesn't need any special permissions within
 
 218 Kivitendo; any valid Kivitendo user will do.
 
 228 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>