LP#1350371 PO name on create w/ dupe detect
authorBill Erickson <berick@esilibrary.com>
Wed, 30 Jul 2014 19:02:22 +0000 (15:02 -0400)
committerMike Rylander <mrylander@gmail.com>
Thu, 19 Feb 2015 13:38:16 +0000 (08:38 -0500)
Adds support for entering a PO name at PO creation time, both from the
picklist page and from the PO create page.  If a name is entered which
matches the name of an existing PO at or below the ordering agency, a
link to the matching PO is shown and the user is prevented from creating
the PO until the name is changed or cleared.

Signed-off-by: Bill Erickson <berick@esilibrary.com>
Signed-off-by: Kathy Lussier <klussier@masslnc.org>
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/Acq/Order.pm
Open-ILS/src/templates/acq/common/li_table.tt2
Open-ILS/web/js/dojo/openils/acq/nls/acq.js
Open-ILS/web/js/dojo/openils/widget/EditPane.js
Open-ILS/web/js/dojo/openils/widget/FilteringTreeSelect.js
Open-ILS/web/js/ui/default/acq/common/li_table.js
Open-ILS/web/js/ui/default/acq/po/create.js

index 4dc88af..12b8def 100644 (file)
@@ -1803,6 +1803,7 @@ sub create_purchase_order_api {
     $pargs{provider}            = $po->provider            if $po->provider;
     $pargs{ordering_agency}     = $po->ordering_agency     if $po->ordering_agency;
     $pargs{prepayment_required} = $po->prepayment_required if $po->prepayment_required;
+    $pargs{name}                = $po->name                 if $po->name;
     my $vandelay = $args->{vandelay};
         
     $po = create_purchase_order($mgr, %pargs) or return $e->die_event;
index 533a2ca..2239689 100644 (file)
                     <td><div name='ordering_agency' id='acq-lit-po-agency'></div></td>
                 </tr>
                 <tr>
+                    <td>[% l('Name (optional)') %]</td>
+                    <td><div name='name' id='acq-lit-po-name'></div></td>
+                </tr>
+                <tr id='acq-dupe-po-name' class='hidden'>
+                    <td>[% l('This name is used by another PO') %]</td>
+                    <td><a href='javascript:;' id='acq-dupe-po-link'>[% l('View PO') %]</a>
+                </tr>
+                <tr>
                     <td>[% l('Provider') %]</td>
                     <td><div name='provider' id='acq-lit-po-provider'></div></td>
                 </tr>
                     <td><input name='create_assets' dojoType='dijit.form.CheckBox'/></td>
                 </tr>
                 <tr>
-                    <td colspan='2'>
+                    <td>
                         <div dojoType='dijit.form.Button' type='submit' jsId='acqLitCreatePoSubmit'>[% l('Submit') %]</div>
                     </td>
+                    <td>
+                        <div dojoType='dijit.form.Button' jsId='acqLitCreatePoCancel'>[% l('Cancel') %]</div>
+                    </td>
                 </tr>
             </table>
         </div>
index 674840c..990260a 100644 (file)
@@ -99,5 +99,8 @@
     "ACQ_SEARCH_CLASS_ABBR_acqinv": "I",
     "ACQ_SEARCH_CLASS_ABBR_acqlid": "LID",
     "ACQ_SEARCH_CLASS_ABBR_acqlia": "LIA",
-    "NO_LI_GENERAL" : "You have not selected any (suitable) line items."
+    "NO_LI_GENERAL" : "You have not selected any (suitable) line items.",
+    "DUPE_PO_NAME_MSG" : "This name is already in use by another PO",
+    "DUPE_PO_NAME_LINK" : "View PO",
+    "PO_NAME_OPTIONAL" : "${0} (optional)"
 }
index a3d36c0..0e14144 100644 (file)
@@ -199,7 +199,7 @@ if(!dojo._hasResource['openils.widget.EditPane']) {
 
                 if(this.hideSaveButton) return;
 
-                new dijit.form.Button({
+                this.saveButton = new dijit.form.Button({
                     label:this.nls.SAVE,
                     onClick: function() {self.performAutoEditAction();}
                 }, applySpan);
index 7791369..6495781 100644 (file)
@@ -28,6 +28,12 @@ if(!dojo._hasResource["openils.widget.FilteringTreeSelect"]){
             disableQuery : null,
             tree : null,
 
+            construct : function(args) {
+                if (args && args.dijitArgs && args.dijitArgs.onChange) {
+                    dojo.connect(this, 'onChange', args.dijitArgs.onChange);
+                }
+            },
+
             startup : function() {
                 this.tree = (typeof this.tree == 'string') ? 
                         dojox.jsonPath.query(window, '$.' + this.tree, {evalType:"RESULT"}) : this.tree;
index af9e29a..2a2a2ff 100644 (file)
@@ -122,6 +122,9 @@ function AcqLiTable() {
         this.selectedIndex = 0;
     };
 
+    acqLitCreatePoCancel.onClick = function() {
+        acqLitPoCreateDialog.hide();
+    }
     acqLitCreatePoSubmit.onClick = function() {
         if (!self.createPoProviderSelector.attr("value") ||
                 !self.createPoAgencySelector.attr("value")) {
@@ -3270,6 +3273,8 @@ function AcqLiTable() {
         po.provider(this.createPoProviderSelector.attr("value"));
         po.ordering_agency(this.createPoAgencySelector.attr("value"));
         po.prepayment_required(fields.prepayment_required[0] ? true : false);
+        var name = this.createPoNameInput.attr('value'); 
+        if (name) po.name(name); // avoid name=""
 
         // if we're creating assets, delay the asset creation 
         // until after the PO is created.  This will allow us to 
@@ -3522,6 +3527,67 @@ function AcqLiTable() {
             widget.build(function(w) { self.createPoProviderSelector = w; });
         }
 
+        this.createPoCheckDupes = function() {
+            var org = self.createPoAgencySelector.attr('value');
+            var name = self.createPoNameInput.attr('value');
+            openils.Util.hide('acq-dupe-po-name');
+
+            if (!name) {
+                acqLitCreatePoSubmit.attr('disabled', false);
+                return;
+            }
+
+            acqLitCreatePoSubmit.attr('disabled', true);
+            var orgs = fieldmapper.aou.descendantNodeList(org, true);
+            new openils.PermaCrud().search('acqpo', 
+                {name : name, ordering_agency : orgs},
+                {async : true, oncomplete : function(r) {
+                    var po = openils.Util.readResponse(r);
+
+                    if (po && (po = po[0])) {
+
+                        var link = dojo.byId('acq-dupe-po-link');
+                        openils.Util.show('acq-dupe-po-name', 'table-row');
+                        var dupe_path = '/acq/po/view/' + po.id();
+
+                        if (window.xulG) {
+
+                            if (window.IAMBROWSER) {
+                                // TODO: integration
+
+                            } else {
+                                // XUL client
+                                link.onclick = function() {
+
+                                    var loc = xulG.url_prefix('XUL_BROWSER?url=') + 
+                                        window.encodeURIComponent( 
+                                        xulG.url_prefix('EG_WEB_BASE' + dupe_path)
+                                    );
+
+                                    xulG.new_tab(loc, 
+                                        {tab_name: '', browser:false},
+                                        {
+                                            no_xulG : false, 
+                                            show_nav_buttons : true, 
+                                            show_print_button : true, 
+                                        }
+                                    );
+                                }
+                            }
+
+                        } else {
+                            link.onclick = function() {
+                                window.open(oilsBasePath + dupe_path, '_blank').focus();
+                            }
+                        }
+
+                    } else {
+                        acqLitCreatePoSubmit.attr('disabled', false);
+                    }
+                }}
+            );
+        }
+
         if (!this.createPoAgencySelector) {
             var widget = new openils.widget.AutoFieldWidget({
                 "fmField": "ordering_agency",
@@ -3529,7 +3595,23 @@ function AcqLiTable() {
                 "parentNode": dojo.byId("acq-lit-po-agency"),
                 "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
             });
-            widget.build(function(w) { self.createPoAgencySelector = w; });
+            widget.build(function(w) { 
+                self.createPoAgencySelector = w; 
+                dojo.connect(w, 'onChange', self.createPoCheckDupes);
+            });
+        }
+
+        if (!this.createPoNameInput) {
+            var widget = new openils.widget.AutoFieldWidget({
+                "fmField": "name",
+                "fmClass": "acqpo",
+                "parentNode": dojo.byId("acq-lit-po-name"),
+                "orgLimitPerms": ["CREATE_PURCHASE_ORDER"],
+            });
+            widget.build(function(w) { 
+                self.createPoNameInput = w; 
+                dojo.connect(w, 'onChange', self.createPoCheckDupes);
+            });
         }
     };
 
index 181fc11..723603e 100644 (file)
@@ -1,7 +1,13 @@
 dojo.require("openils.widget.EditDialog");
 dojo.require("openils.widget.EditPane");
+dojo.require("openils.PermaCrud");
+dojo.require("openils.User");
+dojo.require("fieldmapper.OrgUtils");
 
-var editDialog;
+dojo.requireLocalization('openils.acq', 'acq');
+var localeStrings = dojo.i18n.getLocalization('openils.acq', 'acq');
+
+var editDialog, selectedAgency, currentName;
 
 function toPoListing() {
     location.href = oilsBasePath + "/acq/search/unified?ca=po";
@@ -13,6 +19,111 @@ function toOnePo(id) {
 
 openils.Util.addOnLoad(
     function() {
+
+        // apply here in case the selector never changes
+        // (i.e. no onchange fires).
+        selectedAgency = openils.User.user.ws_ou();
+
+        // called each time the PO name changes
+        function name_changed(new_name) {
+            currentName = new_name;
+
+            console.debug('checking for PO name collision + "' 
+                + currentName + '" at ' + selectedAgency);
+
+            if (!new_name) { // name cleared
+                editDialog.editPane.saveButton.attr('disabled', false);
+                return;
+            }
+
+            // disable Save option pending confirmation of uniqueness
+            editDialog.editPane.saveButton.attr('disabled', true);
+
+            var orgs = fieldmapper.aou.descendantNodeList(selectedAgency, true);
+            new openils.PermaCrud().search('acqpo', 
+                {name : new_name, ordering_agency : orgs},
+                {async : true, oncomplete : function(r) {
+                    var po = openils.Util.readResponse(r);
+                    var tbody = editDialog.editPane.table.getElementsByTagName('tbody')[0];
+
+                    // remove any previous dupe warning row
+                    dojo.forEach(tbody.getElementsByTagName('tr'), function(row) {
+                        if (row) { // sometimes row is undefined??
+                            if (row.getAttribute('dupe-po-row'))
+                                tbody.removeChild(row);
+                        }
+                    });
+
+                    if (po && (po = po[0])) {
+                        // duplicate found.  add info row to create dialog
+
+                        var parent_row;
+                        dojo.forEach(tbody.getElementsByTagName('tr'), function(row) {
+                            if (row.getAttribute('fmfield') == 'name')
+                                parent_row = row;
+                        });
+
+                        var new_tr = dojo.create('tr', {'dupe-po-row' : 1});
+                        var link = dojo.create('a', {
+                            href : 'javascript:;', 
+                            innerHTML : localeStrings.DUPE_PO_NAME_LINK
+                        });
+
+                        var dupe_path = '/acq/po/view/' + po.id();
+
+                        if (window.xulG) {
+
+                            if (window.IAMBROWSER) {
+                                // TODO: integration
+
+                            } else {
+                                // XUL client
+                                link.onclick = function() {
+
+                                    var loc = xulG.url_prefix('XUL_BROWSER?url=') + 
+                                        window.encodeURIComponent( 
+                                        xulG.url_prefix('EG_WEB_BASE' + dupe_path)
+                                    );
+
+                                    xulG.new_tab(loc, 
+                                        {tab_name: '', browser:false},
+                                        {
+                                            no_xulG : false, 
+                                            show_nav_buttons : true, 
+                                            show_print_button : true, 
+                                        }
+                                    );
+                                }
+                            }
+
+                        } else {
+                            link.onclick = function() {
+                                window.open(oilsBasePath + dupe_path, '_blank').focus();
+                            }
+                        }
+
+                        new_tr.appendChild(dojo.create('td', 
+                            {innerHTML : localeStrings.DUPE_PO_NAME_MSG}));
+                        var link_td = dojo.create('td');
+                        link_td.appendChild(link);
+                        new_tr.appendChild(link_td);
+                        tbody.insertBefore(new_tr, parent_row.nextSibling);
+
+                    } else {
+                        editDialog.editPane.saveButton.attr('disabled', false);
+                    }
+                }}
+            );
+        }
+
+        function agency_changed(val) {
+            selectedAgency = val;
+            if (currentName) {
+                // if the ordering agency changes, re-run the dupe name check.
+                name_changed(currentName);
+            }
+        }
+
         editDialog = new openils.widget.EditDialog({
             "editPane": new openils.widget.EditPane({
                 "fmObject": new acqpo(),
@@ -22,13 +133,17 @@ openils.Util.addOnLoad(
                  * much advantage over a hardcoded interface. */
                 "suppressFields": [
                     "create_time", "edit_time", "editor", "order_date",
-                    "owner", "cancel_reason", "creator", "state", "name"
+                    "owner", "cancel_reason", "creator", "state"
                 ],
-                "fieldOrder": ["ordering_agency", "provider"],
+                "fieldOrder": ["ordering_agency", "name", "provider"],
                 "mode": "create",
                 overrideWidgetArgs : {
                     provider : { dijitArgs : { store_options : { base_filter : { active :"t" } } } },
-                    ordering_agency : { orgDefaultsToWs : true }
+                    ordering_agency : { 
+                        orgDefaultsToWs : true,
+                        dijitArgs : {onChange : agency_changed}
+                    },
+                    name : {dijitArgs : {onChange : name_changed}}
                 },
                 "onSubmit": function(po) {
                     fieldmapper.standardRequest(
@@ -54,5 +169,13 @@ openils.Util.addOnLoad(
         });
         editDialog.startup();
         editDialog.show();
+
+        // modify the label of the 'name' field to make it more clear it's optional
+        var row = dojo.query('[fmfield=name]', editDialog.editPane.table)[0];
+        var name_td = row.getElementsByTagName('td')[0];
+        name_td.innerHTML = dojo.string.substitute(
+            localeStrings.PO_NAME_OPTIONAL,
+            [name_td.innerHTML]
+        );
     }
 );