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>