Lp 1803734: Only push on-order purchase orders in edi_order_pusher.pl.
[Evergreen.git] / Open-ILS / src / support-scripts / edi_order_pusher.pl
1 #!/usr/bin/perl
2 # ---------------------------------------------------------------
3 # Copyright (C) 2016 King County Library System
4 # Author: Bill Erickson <berickxx@gmail.com>
5 #
6 # Copied heavily from edi_pusher.pl
7 #
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 # ---------------------------------------------------------------
18 use strict;
19 use warnings;
20 use Getopt::Long;
21 use OpenSRF::Utils::Logger q/$logger/;
22 use OpenILS::Utils::Fieldmapper;
23 use OpenILS::Application::Acq::EDI;
24 use OpenILS::Utils::EDIWriter;
25
26 my $osrf_config = '/openils/conf/opensrf_core.xml';
27 my $po_id;
28 my $test_mode;
29 my $verbose;
30 my $help;
31
32 my $ops = GetOptions(
33     'osrf-config=s' => \$osrf_config,
34     'test-mode'     => \$test_mode,
35     'po-id=i'       => \$po_id,
36     'verbose'       => \$verbose,
37     'help'          => \$help
38 );
39
40 sub help {
41     print <<HELP;
42
43     Synopsis:
44
45         Generate and deliver 'ORDERS' EDI for purchase orders.  Unless a
46         specific PO is provided (via --po-id), EDI messages will be 
47         generated for all PO's that meet the following conditions:
48         
49         1. PO must be activated.
50         2. PO provider must be active.
51         3. PO must use a provider that supports EDI delivery (via edi_default)
52         4. EDI account linked to provider must have 'use_attrs' set to true.
53         5. PO must have no EDI ORDERS messages attached or, if it does, 
54            the message has a status of "retry".
55
56     Usage:
57
58         $0
59
60         --osrf-config [/openils/conf/opensrf_core.xml]
61
62         --test-mode
63             Prints EDI that would be sent to STDOUT.  No files are sent
64             and no edi_message's are created.
65
66         --po-id <po-id-value>
67             Process a specific PO instead of processing all available PO's
68
69         --verbose
70             Log debug info to STDOUT.  This script logs various information
71             via \$logger regardless of this option.
72
73         --help
74             Show this message.
75 HELP
76     exit 0;
77 }
78
79 help() if $help or !$ops;
80
81 # $lvl should match a $logger logging function.  E.g. 'info', 'error', etc.
82 sub announce {
83     my $lvl = shift;
84         my $msg = shift;
85     $logger->$lvl($msg);
86
87     # always announce errors and warnings
88     return unless $verbose || $lvl =~ /error|warn/;
89
90     my $date_str = DateTime->now(time_zone => 'local')->strftime('%F %T');
91     print "$date_str $msg\n";
92 }
93
94 # connect to osrf...
95 OpenSRF::System->bootstrap_client(config_file => $osrf_config);
96 Fieldmapper->import(IDL => 
97     OpenSRF::Utils::SettingsClient->new->config_value("IDL"));
98 OpenILS::Utils::CStoreEditor::init();
99 my $e = OpenILS::Utils::CStoreEditor->new;
100
101 announce('debug', "FTP_PASSIVE is ".($ENV{FTP_PASSIVE} ? "ON" : "OFF"));
102
103 my $po_ids;
104
105 if ($po_id) {
106     # Caller provided a specific PO to process.
107     $po_ids = [$po_id];
108
109 } else {
110     # Find PO's that have an order date set (i.e. are activated) and are 
111     # linked to active providers that support EDI orders, but has no 
112     # successful "ORDERS" edi_message attached.
113     
114     my $ids = $e->json_query({
115         select => {acqpo => ['id']},
116         from => {
117             acqpo => {
118                 acqedim => {
119                     type => 'left',
120                     filter => {message_type => 'ORDERS'}
121                 },
122                 acqpro => {
123                     join => {
124                         acqedi => {
125                         }
126                     }
127                 }
128             }
129         },
130         where => {
131             '+acqpo' => {
132                 state => 'on-order', # on-order only
133                 order_date => {'!=' => undef} # activated
134             },
135             '+acqpro' => {
136                 active => 't', 
137                 edi_default => {'!=' => undef}
138             },
139             '+acqedi' => {
140                 use_attrs => 't'
141             },
142             '+acqedim' => {
143                 '-or' => [
144                     {id => undef}, # no ORDERS message exists
145                     {status => 'retry'} # ORDERS needs re-sending
146                 ]
147             }
148         }
149     });
150
151     $po_ids = [map {$_->{id}} @$ids];
152 }
153
154 if (!@$po_ids) {
155     announce('info', "No purchase orders to process");
156     exit 0;
157 }
158
159 for $po_id (@$po_ids) {
160
161     my $edi = OpenILS::Utils::EDIWriter->new->write($po_id);
162
163     if (!$edi) {
164         announce('error', "Unable to generate EDI for PO $po_id");
165         next;
166     }
167
168     if ($test_mode) {
169         # Caller just wants the EDI printed to STDOUT
170         print "EDI for PO $po_id:\n$edi\n";
171         next;
172     }
173
174     # this may be a retry attempt.  if so, reuse the original edi_message
175     my $message = $e->search_acq_edi_message({
176         purchase_order => $po_id,
177         message_type => 'ORDERS', 
178         status => 'retry'
179     })->[0];
180
181     if (!$message) {
182         $message = Fieldmapper::acq::edi_message->new;
183         $message->create_time('NOW');
184         $message->purchase_order($po_id);
185         $message->message_type('ORDERS');
186         $message->isnew(1);
187
188         my $po = $e->retrieve_acq_purchase_order([$po_id, {
189             flesh => 2,
190             flesh_fields => {
191                 acqpo  => ['provider'],
192                 acqpro => ['edi_default'],
193             }
194         }]);
195
196         if (!$po->provider->edi_default) {
197             announce('error', "Provider for PO $po_id has no default EDI ".
198                 "account, EDI message will not be sent.");
199             next;
200         }
201
202         $message->account($po->provider->edi_default->id);
203     }
204
205     $message->edi($edi);
206
207     $e->xact_begin;
208     if ($message->isnew) {
209         unless($e->create_acq_edi_message($message)) {
210             announce('error', 
211                 "Error creating acq.edi_message for PO $po_id: ".$e->die_event);
212             next;
213         }
214     } else {
215         unless($e->update_acq_edi_message($message)) {
216             announce('error', 
217                 "Error updating acq.edi_message for PO $po_id: ".$e->die_event);
218             next;
219         }
220     }
221     $e->xact_commit;
222
223     my $po = $e->retrieve_acq_purchase_order([
224         $po_id, {
225             flesh => 2,
226             flesh_fields => {
227                 acqpo  => ['provider'],
228                 acqpro => ['edi_default'],
229             }
230         }
231     ]);
232
233     if (!$po->provider->edi_default) {
234         # Caller has provided a PO ID for a provider that does not
235         # support EDI.  
236         announce('error', "Cannot send EDI for PO $po_id, because the ".
237             "provider (".$po->provider->id.") is not configured to use EDI");
238         next;
239     }
240
241     my $res = OpenILS::Application::Acq::EDI->send_core(
242         $po->provider->edi_default, [$message->id]);
243
244     if (my $out = $res->[0]) {
245         announce('info', "message ".$message->id." status: ".$out->status);
246     } else {
247         announce('error', "send_core failed for message ".$message->id);
248     }
249 }
250
251