Acq: Give users a way to split one PO into many (one PO per lineitem).
authorsenator <senator@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Fri, 5 Feb 2010 21:55:23 +0000 (21:55 +0000)
committersenator <senator@dcc99617-32d9-48b4-a31d-7c20da2025e4>
Fri, 5 Feb 2010 21:55:23 +0000 (21:55 +0000)
After splitting, the staff client user is directed to the PO search
interface and is shown a combined view of all the newly separated POs.

git-svn-id: svn://svn.open-ils.org/ILS/trunk@15467 dcc99617-32d9-48b4-a31d-7c20da2025e4

Open-ILS/examples/fm_IDL.xml
Open-ILS/src/extras/ils_events.xml
Open-ILS/src/perlmods/OpenILS/Application/Acq/Order.pm
Open-ILS/web/js/dojo/openils/acq/nls/acq.js
Open-ILS/web/js/ui/default/acq/po/search.js
Open-ILS/web/js/ui/default/acq/po/view_po.js
Open-ILS/web/templates/default/acq/po/search.tt2
Open-ILS/web/templates/default/acq/po/view.tt2

index e21c101..bdafe10 100644 (file)
@@ -4824,6 +4824,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="Order Date" name="order_date" reporter:datatype="timestamp" />
                        <field reporter:label="Name" name="name" reporter:datatype="text" />
                        <field reporter:label="Line Items" name="lineitems" oils_persist:virtual="true" reporter:datatype="link" />
+                       <field reporter:label="Notes" name="notes" oils_persist:virtual="true" reporter:datatype="link" />
                        <field reporter:label="Line Item Count" name="lineitem_count" oils_persist:virtual="true" reporter:datatype="link" />
                        <field reporter:label="Amount Encumbered" name="amount_encumbered" oils_persist:virtual="true" reporter:datatype="float" />
                        <field reporter:label="Amount Spent" name="amount_spent" oils_persist:virtual="true" reporter:datatype="float" />
@@ -4835,6 +4836,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <link field="default_fund" reltype="has_a" key="id" map="" class="acqf"/>
                        <link field="provider" reltype="has_a" key="id" map="" class="acqpro"/>
                        <link field="lineitems" reltype="has_many" key="purchase_order" map="" class="jub"/>
+                       <link field="notes" reltype="has_many" key="purchase_order" map="" class="acqpon"/>
                        <link field="ordering_agency" reltype="has_a" key="id" map="" class="aou"/>
                </links>
                <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
index b989770..fde2d9e 100644 (file)
        <event code='1859' textcode='ACQ_PURCHASE_ORDER_NOT_FOUND'>
                <desc xml:lang='en-US'>The requested acq.purchase_order was not found</desc>
        </event>
+       <event code='1860' textcode='ACQ_PURCHASE_ORDER_TOO_SHORT'>
+               <desc xml:lang='en-US'>The requested acq.purchase_order cannot be split because it does not have more than one lineitem</desc>
+       </event>
+       <event code='1861' textcode='ACQ_PURCHASE_ORDER_TOO_LATE'>
+               <desc xml:lang='en-US'>The requested acq.purchase_order cannot be split because it has gone beyond the "pending" state</desc>
+       </event>
        <event code='1870' textcode='ACQ_LINEITEM_DETAIL_NOT_FOUND'>
                <desc xml:lang='en-US'>The requested acq.lineitem_detail was not found</desc>
        </event>
index 77a2136..c8d0986 100644 (file)
@@ -1291,8 +1291,9 @@ sub create_purchase_order_api {
     my $mgr = OpenILS::Application::Acq::BatchManager->new(editor => $e, conn => $conn);
 
     # create the PO
-    my %pargs = (ordering_agency => $e->requestor->ws_ou);
+    my %pargs = (ordering_agency => $e->requestor->ws_ou); # default
     $pargs{provider} = $po->provider if $po->provider;
+    $pargs{ordering_agency} = $po->ordering_agency if $po->ordering_agency;
     $po = create_purchase_order($mgr, %pargs) or return $e->die_event;
 
     my $li_ids = $$args{lineitems};
@@ -1827,4 +1828,89 @@ sub activate_purchase_order {
 }
 
 
+__PACKAGE__->register_method(
+       method => 'split_purchase_order_by_lineitems',
+       api_name        => 'open-ils.acq.purchase_order.split_by_lineitems',
+       signature => {
+        desc => q/Splits a PO into many POs, 1 per lineitem.  Only works for
+        POs a) with more than one lineitems, and b) in the "pending" state./,
+        params => [
+            {desc => 'Authentication token', type => 'string'},
+            {desc => 'Purchase order ID', type => 'number'}
+        ],
+        return => {desc => 'list of new PO IDs on success, Event on error'}
+    }
+);
+
+sub split_purchase_order_by_lineitems {
+    my ($self, $conn, $auth, $po_id) = @_;
+
+    my $e = new_editor("xact" => 1, "authtoken" => $auth);
+    return $e->die_event unless $e->checkauth;
+
+    my $po = $e->retrieve_acq_purchase_order([
+        $po_id, {
+            "flesh" => 1,
+            "flesh_fields" => {"acqpo" => [qw/lineitems notes/]}
+        }
+    ]) or return $e->die_event;
+
+    return $e->die_event
+        unless $e->allowed("CREATE_PURCHASE_ORDER", $po->ordering_agency);
+
+    unless ($po->state eq "pending") {
+        $e->rollback;
+        return new OpenILS::Event("ACQ_PURCHASE_ORDER_TOO_LATE");
+    }
+
+    unless (@{$po->lineitems} > 1) {
+        $e->rollback;
+        return new OpenILS::Event("ACQ_PURCHASE_ORDER_TOO_SHORT");
+    }
+
+    # To split an existing PO into many, it seems unwise to just delete the
+    # original PO, so we'll instead detach all of the original POs' lineitems
+    # but the first, then create new POs for each of the remaining LIs, and
+    # then attach the LIs to their new POs.
+
+    my @po_ids = ($po->id);
+    my @moving_li = @{$po->lineitems};
+    shift @moving_li;    # discard first LI
+
+    foreach my $li (@moving_li) {
+        my $new_po = $po->clone;
+        $new_po->clear_id;
+        $new_po->clear_name;
+        $new_po->creator($e->requestor->id);
+        $new_po->editor($e->requestor->id);
+        $new_po->owner($e->requestor->id);
+        $new_po->edit_time("now");
+        $new_po->create_time("now");
+
+        $new_po = $e->create_acq_purchase_order($new_po);
+
+        # Clone any notes attached to the old PO and attach to the new one.
+        foreach my $note (@{$po->notes}) {
+            my $new_note = $note->clone;
+            $new_note->clear_id;
+            $new_note->edit_time("now");
+            $new_note->purchase_order($new_po->id);
+            $e->create_acq_po_note($new_note);
+        }
+
+        $li->edit_time("now");
+        $li->purchase_order($new_po->id);
+        $e->update_acq_lineitem($li);
+
+        push @po_ids, $new_po->id;
+    }
+
+    $po->edit_time("now");
+    $e->update_acq_purchase_order($po);
+
+    return \@po_ids if $e->commit;
+    return $e->die_event;
+}
+
+
 1;
index d6a9d51..693e961 100644 (file)
@@ -4,5 +4,6 @@
     'XUL_RECORD_DETAIL_PAGE' : 'Record Details',
     'DELETE_LI_COPIES_CONFIRM' : 'This will delete the last ${0} copies in the table.  Proceed?',
     'NO_PO_RESULTS': "No results",
-    'PO_HEADING_ERROR' : "Unexpected problem building virtual combined PO"
+    'PO_HEADING_ERROR' : "Unexpected problem building virtual combined PO",
+    'CONFIRM_SPLIT_PO': "Are you sure you want to split this purchase order into\none purchase order for every constituent line item?"
 }
index 30aa6eb..b4185ae 100644 (file)
@@ -32,7 +32,10 @@ function doSearch(fields) {
         delete fields.metapo_view;
     }
 
-    if(isNaN(fields.id)) {
+    if (
+        !(fields.id && fields.id.constructor.name == 'Array') && 
+        isNaN(fields.id)
+    ) {
         delete fields.id;
         for(var k in fields) {
             if(fields[k] == '' || fields[k] == null)
@@ -48,6 +51,7 @@ function doSearch(fields) {
     for(var k in fields) some = true;
     if(!some) fields.id = {'!=' : null};
 
+
     if (metapo_view) {
         openils.Util.hide("holds_po_grid");
         loadMetaPO(fields);
@@ -77,7 +81,11 @@ function loadForm() {
         dijitArgs : {name:'ordering_agency', required:false}
     }).build();
 
-    doSearch({ordering_agency : openils.User.user.ws_ou()});
+    if (poIds && poIds.length > 0) {
+        doSearch({"id": poIds, "metapo_view": [true] /* [sic] */});
+    } else {
+        doSearch({"ordering_agency": openils.User.user.ws_ou()});
+    }
 }
 
 function loadMetaPO(fields) {
index 742d4ef..76294bc 100644 (file)
@@ -24,8 +24,12 @@ function init() {
                 dojo.byId('acq-po-view-total-spent').innerHTML = PO.amount_spent();
                 dojo.byId('acq-po-view-state').innerHTML = PO.state(); // TODO i18n
 
-                if(PO.state() == 'pending') 
+                if(PO.state() == 'pending') {
                     openils.Util.show('acq-po-activate');
+                    if (PO.lineitem_count() > 1) {
+                        openils.Util.show('acq-po-split');
+                    }
+                }
             }
         }
     );
@@ -59,6 +63,32 @@ function activatePo() {
     }
 }
 
+function splitPo() {
+    progressDialog.show(true);
+    try {
+        var list;
+        fieldmapper.standardRequest(
+            ['open-ils.acq', 'open-ils.acq.purchase_order.split_by_lineitems'],
+            {   async: true,
+                params: [openils.User.authtoken, PO.id()],
+                onresponse : function(r) {
+                    list = openils.Util.readResponse(r);
+                },
+                oncomplete : function() {
+                    progressDialog.hide();
+                    if (list) {
+                        location.href = oilsBasePath + '/eg/acq/po/search/' +
+                            list.join(",");
+                    }
+                }
+            }
+        );
+    } catch(E) {
+        progressDialog.hide();
+        alert(E);
+    }
+}
+
 function updatePoName() {
     var value = prompt('Enter new purchase order name:', PO.name()); // TODO i18n
     if(!value || value == PO.name()) return;
index d152efb..07dcfcb 100644 (file)
@@ -1,5 +1,11 @@
 [% WRAPPER default/base.tt2 %]
 [% ctx.page_title = 'Purchase Orders' %]
+<script type="text/javascript">
+    var poIds = function(s) {
+        return s == "" ? undefined :
+            s.split(",").map(function(t) { return Number(t); });
+    }("[% ctx.page_args.0 %]");
+</script>
 <div id='oils-acq-list-header' class='container'>
     <div id='oils-acq-list-header-label'>PO Search</div>
 </div>
index 4b7073a..7b04973 100644 (file)
@@ -14,6 +14,7 @@
                 <tr><td>Total Spent</td><td>$<span id='acq-po-view-total-spent'/></td></tr>
                 <tr><td>Status</td><td><span id='acq-po-view-state'/></td></tr>
                 <tr><td><a class='hidden' id='acq-po-activate' href='javascript:void(0);' onclick='activatePo()'>Activate Order</a></td></tr>
+                <tr><td><a class="hidden" id="acq-po-split" href="javascript:void(0);" onclick="if (confirm(localeStrings.CONFIRM_SPLIT_PO)) splitPo();">Split Order by Lineitems</a></td></tr>
             </table>
         </div>
     </div>