File Coverage

blib/lib/OpenERP/XMLRPC/Client.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package OpenERP::XMLRPC::Client;
2             # ABSTRACT: XMLRPC Client tweaked for OpenERP interaction.
3              
4             our $VERSION = '0.14';
5              
6 1     1   23216 use 5.010;
  1         4  
  1         31  
7 1     1   676 use Moose;
  0            
  0            
8             use MIME::Base64;
9              
10              
11             has 'username' => ( is => 'ro', isa => 'Str', default => 'admin');
12             has 'password' => ( is => 'ro', isa => 'Str', default => 'admin');
13             has 'dbname' => ( is => 'ro', isa => 'Str', default => 'terp');
14             has 'host' => ( is => 'ro', isa => 'Str', default => '127.0.0.1');
15             has 'port' => ( is => 'ro', isa => 'Int', default => 8069);
16             has 'proto' => ( is => 'ro', isa => 'Str', default => 'http');
17              
18             has '_report_report_uri' => ( is => 'ro', isa => 'Str', default => 'xmlrpc/report' );
19             has '_object_execute_uri' => ( is => 'ro', isa => 'Str', default => 'xmlrpc/object' );
20             has '_object_execute_kw_uri' => ( is => 'ro', isa => 'Str', default => 'xmlrpc/object' );
21             has '_object_exec_workflow_uri' => ( is => 'ro', isa => 'Str', default => 'xmlrpc/object' );
22              
23             has 'openerp_uid' => ( is => 'rw', isa => 'Int' );
24             has 'base_rpc_uri' => ( is => 'rw', isa => 'Str', default => 'xmlrpc/common');
25              
26              
27             with 'MooseX::Role::XMLRPC::Client' =>
28             {
29             name => 'openerp',
30             login_info => 1,
31             };
32              
33             sub _build_openerp_userid { shift->username }
34             sub _build_openerp_passwd { shift->password }
35             sub _build_openerp_uri
36             {
37             my $self = shift;
38             return $self->proto . '://' . $self->host . ':' . $self->port . '/' . $self->base_rpc_uri;
39             }
40              
41             sub openerp_login
42             {
43             my $self = shift;
44              
45             # call 'login' method to get the uid..
46             my $res = $self->openerp_rpc->send_request('login', $self->dbname, $self->username, $self->password );
47              
48             if ( ! defined $res || ! ref $res )
49             {
50             die "Failed to log into OpenERP XML RPC service";
51             }
52              
53             # set the uid we have just had returned from logging in..
54             $self->openerp_uid( ${ $res } );
55             # NOTE: OpenERP seems to be filling in faultCode not faultString these days
56             # (6.1.1) so we need to check for that and display it instead.
57             $self->openerp_rpc->fault_handler(sub {
58             confess $_[0]->{faultCode} ? $_[0]->{faultCode}->value : $_[0]->string
59             });
60             }
61              
62             sub openerp_logout
63             {
64             my $self = shift;
65             # do nothing on logout...nothing is required..
66             }
67              
68              
69              
70             sub BUILD
71             {
72             my $self = shift;
73             $RPC::XML::ENCODING = 'utf-8';
74             $self->openerp_login;
75             }
76              
77              
78             sub change_uri
79             {
80             my $self = shift;
81             my $base_uri = shift;
82              
83             my $exsting_base_uri = $self->base_rpc_uri;
84              
85             return $exsting_base_uri if $base_uri eq $exsting_base_uri;
86              
87             $self->base_rpc_uri( $base_uri ); # change the base path.
88             $self->openerp_rpc->uri( $self->_build_openerp_uri ); # rebuild and set the new uri.
89             return $exsting_base_uri; # return the old uri.
90             }
91              
92             sub object_execute
93             {
94             my $self = shift;
95              
96             my $method = shift; # eg. 'search'
97             my $relation = shift; # eg. 'res.partner'
98             my @args = @_; # All other args we just pass on.
99              
100             # change the uri to base uri we are going to query..
101             $self->change_uri( $self->_object_execute_uri );
102              
103             $self->simple_request
104             (
105             'execute',
106             $self->dbname,
107             $self->openerp_uid,
108             $self->password,
109             $relation,
110             $method,
111             @args
112             );
113              
114             }
115              
116             sub object_execute_kw
117             {
118             my $self = shift;
119              
120             my $method = shift; # eg. 'search'
121             my $relation = shift; # eg. 'res.partner'
122             my @args = @_; # All other args we just pass on.
123              
124             # change the uri to base uri we are going to query..
125             $self->change_uri( $self->_object_execute_kw_uri );
126              
127             $self->simple_request
128             (
129             'execute_kw',
130             $self->dbname,
131             $self->openerp_uid,
132             $self->password,
133             $relation,
134             $method,
135             @args,
136             );
137              
138             }
139              
140             sub object_exec_workflow
141             {
142             my $self = shift;
143              
144             my $method = shift; # eg. 'search'
145             my $relation = shift; # eg. 'res.partner'
146             my @args = @_; # All other args we just pass on.
147              
148             # change the uri to base uri we are going to query..
149             $self->change_uri( $self->_object_exec_workflow_uri );
150              
151             $self->simple_request
152             (
153             'exec_workflow',
154             $self->dbname,
155             $self->openerp_uid,
156             $self->password,
157             $relation,
158             $method,
159             @args
160             );
161              
162             }
163              
164             sub report_report
165             {
166             my $self = shift;
167              
168             my $report_id = shift; # eg. 'purchase.quotation'
169             my $object_id = shift;
170             my $parameters = shift; # eg. model, id and report_type
171              
172             # change the uri to base uri we are going to query..
173             $self->change_uri( $self->_report_report_uri );
174              
175             return $self->simple_request
176             (
177             'report',
178             $self->dbname,
179             $self->openerp_uid,
180             $self->password,
181             $report_id,
182             [$object_id],
183             $parameters,
184             );
185             }
186              
187             sub report_report_get
188             {
189             my $self = shift;
190              
191             my $report_id = shift; # eg. 123
192              
193             # change the uri to base uri we are going to query..
194             $self->change_uri( $self->_report_report_uri );
195              
196             my $object = $self->simple_request
197             (
198             'report_get',
199             $self->dbname,
200             $self->openerp_uid,
201             $self->password,
202             $report_id,
203             );
204              
205             if($object->{state})
206             {
207             my $data = $object->{result};
208             return decode_base64($data);
209             }
210              
211             return;
212             }
213              
214             sub simple_request
215             {
216             my $self = shift;
217              
218             local *RPC::XML::boolean::value = sub {
219             my $self = shift;
220             # this fudges the false so it's not 0
221             # which means if it was used to indicate null is probably going to work better.
222             # the downside is that we presumably lose some precision when it comes to bools
223             # and nulls.
224             return undef unless ${$self};
225             return 1;
226             };
227              
228             return $self->openerp_rpc->simple_request(@_);
229             }
230              
231             sub create
232             {
233             return shift->_three_arg_execute('create', @_);
234             }
235              
236             sub read
237             {
238             my ($self, $object, $ids, $context, $fields) = @_;
239            
240             $ids = [ $ids ] unless ( ref $ids eq 'ARRAY' );
241            
242             if ($context) {
243             return $self->object_execute('read', $object, $ids, $fields, $context);
244             } else {
245             return $self->object_execute('read', $object, $ids);
246             }
247             }
248              
249             sub search
250             {
251             my ($self, $object, $args, $context, $offset, $limit) = @_;
252            
253             if ($context) {
254             return $self->object_execute('search', $object, $args, $offset // 0, $limit, undef, $context);
255             } else {
256             return $self->object_execute('search', $object, $args);
257             }
258             }
259              
260             sub field_info
261             {
262             return shift->_three_arg_execute('fields_view_get', @_);
263             }
264              
265             sub model_fields
266             {
267             return shift->_three_arg_execute('fields_get', @_);
268             }
269              
270             sub update
271             {
272             return shift->_array_execute('write', @_);
273             }
274              
275             sub get_defaults
276             {
277             return shift->_array_execute('default_get', @_);
278             }
279              
280             sub delete
281             {
282             return shift->_array_execute('unlink', @_);
283             }
284              
285             sub _three_arg_execute
286             {
287             my $self = shift;
288             my $verb = shift;
289             my $object = shift;
290             my $args = shift;
291             return $self->object_execute($verb, $object, $args, @_ );
292             }
293              
294             sub _array_execute
295             {
296             my $self = shift;
297             my $verb = shift;
298             my $object = shift;
299             my $ids = shift;
300             my $args = shift;
301              
302             # ensure we pass an array of IDs to the RPC..
303             $ids = [ $ids ] unless ( ref $ids eq 'ARRAY' );
304              
305             return $self->object_execute($verb, $object, $ids, $args );
306             }
307              
308             sub search_detail
309             {
310             my ($self, $object, $args, $context, $offset, $limit) = @_;
311              
312             # search and get ids..
313             my $ids = $self->search( $object, $args, $context, $offset, $limit );
314             return unless ( defined $ids && ref $ids eq 'ARRAY' && scalar @$ids >= 1 );
315              
316             # read data from all the ids..
317             # FIXME: I'm fairly sure context is in the wrong place.
318             return $self->read( $object, $ids, $context );
319             }
320              
321             sub read_single
322             {
323             my $res = shift->read( @_ );
324             return unless ( defined $res && ref $res eq 'ARRAY' && scalar @$res >= 1 );
325             return $res->[0];
326             }
327              
328              
329              
330             1;
331              
332             __END__
333             =pod
334              
335             =head1 NAME
336              
337             OpenERP::XMLRPC::Client - XMLRPC Client tweaked for OpenERP interaction.
338              
339             =head1 SYNOPSIS
340              
341             my $erp = OpenERP::XMLRPC::Client->new( dbname => 'terp', username => 'admin', password => 'admin', host => '127.0.0.1', port => '8069' )
342             my $partner_ids = $erp->object_execute( 'res.partner', 'search', [ 'name', 'ilke', 'abc' ] );
343              
344             # READ a res.partner object
345             my $partner = $erp->read( 'res.partner', $id );
346              
347             print "You Found Partner:" . $partner->{name} . "\n";
348              
349             =head1 DESCRIPTION
350              
351             I have tried to make this extendable so made use of moose roles to structure the calls to the
352             different methods available from the openerp rpc.
353              
354             This makes use of the L<MooseX::Role::XMLRPC::Client> to communicate via rpc.
355              
356             This module was built to be used by another L<OpenERP::XMLRPC::Simple> and handles
357             openerp specific rpc interactions. It could be used by something else to access
358             openerp rpc services.
359              
360             =head1 NAME
361              
362             OpenERP::XMLRPC::Client - XML RPC Client for OpenERP
363              
364             =head1 Parameters
365              
366             username - string - openerp username (default: 'admin')
367             password - string - openerp password (default: 'admin')
368             dbname - string - openerp database name (default: 'terp')
369             host - string - openerp rpc server host (default: '127.0.0.1' )
370             port - string - openerp rpc server port (default: 8069)
371             proto - string - openerp protocol (default: http) .. untested anything else.
372              
373             =head1 Attributes
374              
375             openerp_uid - int - filled when the connection is logged in.
376             base_rpc_uri - string - used to hold uri the rpc is currently pointing to.
377             openerp_rpc - L<RPC::XML::Client> - Provided by L<MooseX::Role::XMLRPC::Client>
378              
379             =head1 METHODS
380              
381             These methods re-present the OpenERP XML RPC but in a slightly more user friendly way.
382              
383             The methods have been tested using the 'res.partner' object name and the demo database
384             provided when you install OpenERP.
385              
386             =head2 BUILD
387              
388             When the object is instanciated, this method is run. This calls openerp_login.
389              
390             =head2 openerp_login
391              
392             Logs the client in. Called automatically when the object is created.
393              
394             =head2 openerp_logout
395              
396             Basically a no-op.
397              
398             =head2 object_execute
399              
400             Low level method for making a call to the Open ERP server. Normally called by a
401             wrapper function like L<create> or L<read>.
402              
403             =head2 object_exec_workflow
404              
405             Makes an 'exec_workflow' call to Open ERP.
406              
407             =head2 report_report
408              
409             Sends a 'report' call to Open ERP.
410              
411             =head2 report_report_get
412              
413             Sends a 'report_get' call to Open ERP.
414              
415             =head2 change_uri
416              
417             OpenERP makes methods available via different URI's, this method is used to change which
418             URI the rpc client is pointing at.
419              
420             Arguments:
421             $_[0] - object ref. ($self)
422             $_[1] - string (e.g. "xmlrpc/object") base uri path.
423              
424             Returns:
425             string - the old uri - the one this new one replaced.
426              
427             =head2 read ( OBJECTNAME, [IDS] )
428              
429             Can pass this a sinlge ID or an ARRAYREF of ID's, it will return an ARRAYREF of
430             OBJECT records (HASHREF's).
431              
432             Example:
433             $partner = $erp->read('res.partner', 1 );
434             print "This is the returned record name:" . $partner->[0]->{name} . "\n";
435              
436             $partners = $erp->read('res.partner', [1,2] );
437             print "This is the returned record 1:" . $partners->[0]->{name} . "\n";
438             print "This is the returned record 2:" . $partners->[1]->{name} . "\n";
439              
440             Returns: ArrayRef of HashRef's - All the objects with IDs passed.
441              
442             =head2 search ( OBJECTNAME, [ [ COLNAME, COMPARATOR, VALUE ] ] )
443              
444             Used to search and return IDs of objects matching the searcgh.
445              
446             Returns: ArrayRef of ID's - All the objects ID's matching the search.
447              
448             Example:
449             $results = $erp->search('res.partner', [ [ 'name', 'ilke', 'abc' ] ] );
450             print "This is the 1st ID found:" . $results->[0] . "\n";
451              
452             =head2 create ( OBJECTNAME, { COLNAME => COLVALUE } )
453              
454             Returns: ID - the ID of the object created.
455              
456             Example:
457             $new_id = $erp->create('res.partner', { 'name' => 'new company name' } );
458              
459             =head2 update ( OBJECTNAME, ID, { COLNAME => COLVALUE } )
460              
461             Returns: boolean - updated or not.
462              
463             Example:
464             $success = $erp->update('res.partner', 1, { 'name' => 'changed company name' } );
465              
466             =head2 delete ( OBJECTNAME, ID )
467              
468             Returns: boolean - deleted or not.
469              
470             Example:
471             $success = $erp->delete('res.partner', 1 );
472              
473             =head2 field_info ( OBJECTNAME )
474              
475             Returns: hash containing all field info, this contains field names and field types.
476              
477             =head2 model_fields ( OBJECTNAME )
478              
479             Returns: hash containing all the models fields.
480              
481             =head2 get_defaults ( OBJECTNAME, [ FIELDS ] )
482              
483             Returns: hash containing the default values for those fields.
484              
485             =head2 search_detail ( OBJECTNAME, [ [ COLNAME, COMPARATOR, VALUE ] ], CONTEXT )
486              
487             Used to search and read details on a perticular OBJECT. This uses 'search' to find IDs,
488             then calls 'read' to get details on each ID returned.
489              
490             Returns: ArrayRef of HashRef's - All the objects found with all their details.
491              
492             Example:
493             $results = $erp->search_detail('res.partner', [ [ 'name', 'ilke', 'abc' ] ] );
494             print "This is the 1st found record name:" . $results->[0]->{name} . "\n";
495              
496             The C<CONTEXT> argument is optional. This allows a hasref containing the current
497             search context to be provided, e.g.
498              
499             my $results = $erp->search_detail(
500             'stock.location',
501             [
502             ['usage' => '=' => 'internal']
503             ],
504             {
505             active_id => $self->id,
506             active_ids => [$self->id],
507             active_model => 'product.product',
508             full => 1,
509             product_id => $self->id,
510             search_default_in_location => 1,
511             section_id => undef,
512             tz => undef,
513             }
514             )
515              
516             =head2 read_single ( OBJECTNAME, ID )
517              
518             Pass this a sinlge ID and get a single OBJECT record (HASHREF).
519              
520             Example:
521             $partner = $erp->read_single('res.partner', 1 );
522             print "This name of partner with ID 1:" . $partner->{name} . "\n";
523              
524             Returns: HashRef - The objects data
525              
526             =head1 SEE ALSO
527              
528             L<RPC::XML::Client>
529              
530             =head1 AUTHOR
531              
532             Benjamin Martin <ben@madeofpaper.co.uk>
533             Colin Newell <colin@opusvl.com>
534             Jon Allen (JJ) <jj@opusvl.com>
535              
536             =head1 COPYRIGHT AND LICENSE
537              
538             Copyright (C) 2010-2012 OpusVL
539              
540             This library is free software; you can redistribute it and/or modify it under
541             the same terms as Perl itself.
542              
543             =cut
544