|
1 |
package SL::Controller::PhoneNumber;
|
|
2 |
|
|
3 |
use utf8;
|
|
4 |
use strict;
|
|
5 |
use parent qw(SL::Controller::Base);
|
|
6 |
|
|
7 |
use List::MoreUtils qw(any);
|
|
8 |
use List::Util qw(first);
|
|
9 |
|
|
10 |
use SL::JSON;
|
|
11 |
use SL::DBUtils;
|
|
12 |
use SL::Locale::String;
|
|
13 |
use SL::CTI;
|
|
14 |
|
|
15 |
use SL::DB::Contact;
|
|
16 |
use SL::DB::Customer;
|
|
17 |
use SL::DB::Vendor;
|
|
18 |
|
|
19 |
use Data::Dumper;
|
|
20 |
|
|
21 |
sub action_look_up {
|
|
22 |
my ($self) = @_;
|
|
23 |
|
|
24 |
my $number = $self->normalize_number($::form->{number} // '');
|
|
25 |
|
|
26 |
return $self->render(\to_json({}), { type => 'json', process => 0 }) if ($number eq '');
|
|
27 |
|
|
28 |
my $result = $self->find_contact_for_number($number)
|
|
29 |
// $self->find_customer_vendor_for_number($number)
|
|
30 |
// {};
|
|
31 |
|
|
32 |
$self->render(\to_json($result), { type => 'json', process => 0 });
|
|
33 |
}
|
|
34 |
|
|
35 |
sub find_contact_for_number {
|
|
36 |
my ($self, $number) = @_;
|
|
37 |
|
|
38 |
my @number_fields = qw(cp_phone1 cp_phone2 cp_mobile1 cp_mobile2 cp_fax);
|
|
39 |
|
|
40 |
my $contacts = SL::DB::Manager::Contact->get_all(
|
|
41 |
inject_results => 1,
|
|
42 |
where => [ map({ (or => [ "!$_" => undef, "!$_" => "" ],) } @number_fields) ],
|
|
43 |
);
|
|
44 |
|
|
45 |
my @hits;
|
|
46 |
|
|
47 |
foreach my $contact (@{ $contacts }) {
|
|
48 |
foreach my $field (@number_fields) {
|
|
49 |
next if $self->normalize_number($contact->$field) ne $number;
|
|
50 |
|
|
51 |
push @hits, $contact;
|
|
52 |
last;
|
|
53 |
}
|
|
54 |
}
|
|
55 |
|
|
56 |
return if !@hits;
|
|
57 |
|
|
58 |
my @cv_ids = grep { $_ } map { $_->cp_cv_id } @hits;
|
|
59 |
|
|
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) },
|
|
64 |
);
|
|
65 |
|
|
66 |
my $chosen = first {
|
|
67 |
$_->cp_cv_id
|
|
68 |
&& $customers_vendors{$_->cp_cv_id}
|
|
69 |
&& !$customers_vendors{$_->cp_cv_id}->obsolete
|
|
70 |
&& ($_->cp_name !~ m{ungültig}i)
|
|
71 |
} @hits;
|
|
72 |
|
|
73 |
$chosen //= $hits[0];
|
|
74 |
|
|
75 |
return {
|
|
76 |
full_name => join(' ', grep { $_ ne '' } map { $_ // '' } ($chosen->cp_title, $chosen->cp_givenname, $chosen->cp_name)),
|
|
77 |
map({ my $method = "cp_$_"; ($_ => $chosen->$method // '') } qw(title givenname name phone1 phone2 mobile1 mobile2 fax)),
|
|
78 |
};
|
|
79 |
}
|
|
80 |
|
|
81 |
sub find_customer_vendor_for_number {
|
|
82 |
my ($self, $number) = @_;
|
|
83 |
|
|
84 |
my @number_fields = qw(phone fax);
|
|
85 |
|
|
86 |
my @customers_vendors = map {
|
|
87 |
my $class = "SL::DB::Manager::$_";
|
|
88 |
@{ $class->get_all(
|
|
89 |
inject_results => 1,
|
|
90 |
where => [ map({ (or => [ "!$_" => undef, "!$_" => "" ],) } @number_fields) ],
|
|
91 |
) }
|
|
92 |
} qw(Customer Vendor);
|
|
93 |
|
|
94 |
my @hits;
|
|
95 |
|
|
96 |
foreach my $customer_vendor (@customers_vendors) {
|
|
97 |
foreach my $field (@number_fields) {
|
|
98 |
next if $self->normalize_number($customer_vendor->$field) ne $number;
|
|
99 |
|
|
100 |
push @hits, $customer_vendor;
|
|
101 |
last;
|
|
102 |
}
|
|
103 |
}
|
|
104 |
|
|
105 |
return if !@hits;
|
|
106 |
|
|
107 |
my $chosen = first { !$_->obsolete } @hits;
|
|
108 |
$chosen //= $hits[0];
|
|
109 |
|
|
110 |
return {
|
|
111 |
full_name => $chosen->name // '',
|
|
112 |
phone1 => $chosen->phone // '',
|
|
113 |
fax => $chosen->fax // '',
|
|
114 |
map({ ($_ => '') } qw(title givenname name phone2 mobile1 mobile2)),
|
|
115 |
};
|
|
116 |
}
|
|
117 |
|
|
118 |
sub normalize_number {
|
|
119 |
my ($self, $number) = @_;
|
|
120 |
|
|
121 |
return '' if ($number // '') eq '';
|
|
122 |
|
|
123 |
my $config = $::lx_office_conf{cti} || {};
|
|
124 |
my $idp = $config->{international_dialing_prefix} // '00';
|
|
125 |
my $country_code = $config->{our_country_code} // '49';
|
|
126 |
|
|
127 |
$number = SL::CTI->sanitize_number(number => $number);
|
|
128 |
|
|
129 |
return $number if $number =~ m{^$idp};
|
|
130 |
|
|
131 |
$number =~ s{^0+}{};
|
|
132 |
|
|
133 |
return $idp . $country_code . $number;
|
|
134 |
}
|
|
135 |
|
|
136 |
1;
|
|
137 |
|
|
138 |
__END__
|
|
139 |
|
|
140 |
=pod
|
|
141 |
|
|
142 |
=encoding utf8
|
|
143 |
|
|
144 |
=head1 NAME
|
|
145 |
|
|
146 |
SL::Controller::Contact - Looking up information on contacts/customers/vendors based on a phone number
|
|
147 |
|
|
148 |
=head1 FUNCTIONS
|
|
149 |
|
|
150 |
=over 4
|
|
151 |
|
|
152 |
=item C<action_look_up>
|
|
153 |
|
|
154 |
This action can be used by external systems such as PBXes in order to
|
|
155 |
match a calling number to a name. Requires one form parameter to be
|
|
156 |
passed, C<number>.
|
|
157 |
|
|
158 |
The number will then be normalized. This requires that the
|
|
159 |
international dialing prefix and the server's own country code be set
|
|
160 |
up in C<kivitendo.conf>, section C<[cti]>. They default to C<00> and
|
|
161 |
C<49> (Germany) respectively.
|
|
162 |
|
|
163 |
Next the function will look up a contact whose normalized numbers
|
|
164 |
equals the requested number. The fields C<phone1>, C<phone2>,
|
|
165 |
C<mobile1>, C<mobile2> and C<fax> are considered. Active contacts are
|
|
166 |
given preference over inactive ones (inactive meaning that they don't
|
|
167 |
belong to a customer/vendor anymore or that the customer/vendor itself
|
|
168 |
is flagged as obsolete).
|
|
169 |
|
|
170 |
If no contact is found, customers & vendors are searched. Their fields
|
|
171 |
C<phone> and C<fax> are considered. The first customer/vendor who
|
|
172 |
isn't flagged as being obsolete is chosen; if there's none, the first
|
|
173 |
obsolete one is.
|
|
174 |
|
|
175 |
The function always sends one JSON-encoded object. If there's no hit
|
|
176 |
for the number, an empty object is returned. Otherwise the following
|
|
177 |
fields are present:
|
|
178 |
|
|
179 |
=over 4
|
|
180 |
|
|
181 |
=item C<full_name> — for contacts this is the concatenation of the
|
|
182 |
title, given name and family name; for customers/vendors it's the
|
|
183 |
company name
|
|
184 |
|
|
185 |
=item C<title> — title (empty for customers/vendors)
|
|
186 |
|
|
187 |
=item C<givenname> — first/given name (empty for customers/vendors)
|
|
188 |
|
|
189 |
=item C<name> — last/family name (empty for customers/vendors)
|
|
190 |
|
|
191 |
=item C<phone1> — first phone number (for all object types)
|
|
192 |
|
|
193 |
=item C<phone2> — second phone number (empty for customers/vendors)
|
|
194 |
|
|
195 |
=item C<mobile1> — first mobile number (empty for customers/vendors)
|
|
196 |
|
|
197 |
=item C<mobile2> — second mobile number (empty for customers/vendors)
|
|
198 |
|
|
199 |
=item C<fax> — fax number (for all object types)
|
|
200 |
|
|
201 |
=back
|
|
202 |
|
|
203 |
Here's an example how querying the API via C<curl> might look:
|
|
204 |
|
|
205 |
curl --user user_name:password 'https://…/kivitendo/controller.pl?action=PhoneNumber/look_up&number=0049987655443321'
|
|
206 |
|
|
207 |
Note that the request must be authenticated via a valid Kivitendo
|
|
208 |
login. However, the user doesn't need any special permissions within
|
|
209 |
Kivitendo; any valid Kivitendo user will do.
|
|
210 |
|
|
211 |
=back
|
|
212 |
|
|
213 |
=head1 BUGS
|
|
214 |
|
|
215 |
Nothing here yet.
|
|
216 |
|
|
217 |
=head1 AUTHOR
|
|
218 |
|
|
219 |
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
|
|
220 |
|
|
221 |
=cut
|
API für Telefonnummernrückwärtssuche