webstaff: ad hoc issuances (no scap) and general cleanup
authorMike Rylander <mrylander@gmail.com>
Mon, 22 May 2017 14:02:11 +0000 (10:02 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Tue, 30 May 2017 16:06:48 +0000 (12:06 -0400)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Open-ILS/src/templates/staff/serials/t_holding_code_dialog.tt2
Open-ILS/src/templates/staff/serials/t_view_items_grid.tt2
Open-ILS/web/js/ui/default/staff/serials/directives/view-items-grid.js
Open-ILS/web/js/ui/default/staff/serials/services/core.js

index d69b735..60bd636 100644 (file)
       <select
         class="form-control"
           ng-model="type"
-          ng-init='types=[{n:"basic",l:"[%l('Basic')%]"},{n:"supplement",l:"[%l('Supplement')%]"},{n:"index",l:"[%l('Index')%]"}]'
+          ng-init='types=[{n:"basic",l:"[%l('Basic')%]"},{n:"supplement",l:"[%l('Supplement')%]"},{n:"index",l:"[%l('Index')%]"},{n:"adhoc",l:"[%l('Ad Hoc')%]"}]'
           ng-options='t.n as t.l for t in types'>
       </select>
     </div>
   </div>
 
+  <div ng-show="type=='adhoc'">
+  <div class="pad-vert row">
+    <div class="col-md-3">
+      <b>[% l('Issuance Label') %]</b>
+    </div>
+    <div class="col-md-9">
+      <input class="form-control" type="text" ng-model="label"/>
+    </div>
+  </div>
+  </div>
+
+  <div ng-hide="type=='adhoc'">
   <div class="row container" ng-if="args.enums.length">
     <hr/>
     <h2>[% l('Enumeration labels') %]</h2>
@@ -59,6 +71,8 @@
       {{ c.pattern }}
     </div>
   </div>
+  </div>
+
 </div>
 
 <div class="modal-footer">
index d669287..b05b078 100644 (file)
     <eg-grid-menu-item handler="add_issuances" standalone="true"
         label="[% l('Predict New Issues') %]"></eg-grid-menu-item>
 
+
     <eg-grid-menu-item handler="receive_barcode_checkbox_handler"
       label="[% l('Barcode on receive') %]"
       checkbox="receive_and_barcode"
-      checked="receive_and_barcode"
-      standalone="true"/>
+      checked="receive_and_barcode"/>
+
 
+<!-- Hiding this for now ... seems unnecessary?
     <eg-grid-menu-item handler="receive_barcode_checkbox_handler"
       label="[% l('Bind on receive') %]"
       checkbox="receive_and_bind"
-      checked="receive_and_bind"
-      standalone="true"/>
+      checked="receive_and_bind"/>
+-->
 
 
     <eg-grid-action handler="receive_selected"
       label="[% l('Receive selected') %]"></eg-grid-action>
+
+    <eg-grid-action handler="bind_selected"
+      disabled="need_one_selected"
+      label="[% l('Barcode selected') %]"></eg-grid-action>
+
     <eg-grid-action handler="bind_selected"
+      disabled="need_many_selected"
       label="[% l('Bind selected') %]"></eg-grid-action>
-    <eg-grid-action handler="following_issuance" disabled="need_one_selected"
+
+    <eg-grid-action handler="following_issuance"
+      disabled="need_one_selected"
       label="[% l('Add following issue') %]"></eg-grid-action>
+
     <eg-grid-action handler="edit_issuance_holding_code"
       label="[% l('Edit issue holding codes') %]"></eg-grid-action>
+
     <eg-grid-action handler="delete_items"
       label="[% l('Delete items') %]"></eg-grid-action>
 
     <eg-grid-field label="[% l('Distribution Library') %]" path="stream.distribution.holding_lib.name" visible></eg-grid-field>
     <eg-grid-field label="[% l('Issuance') %]" path="issuance.label" visible></eg-grid-field>
+    <eg-grid-field label="[% l('Barcode') %]" path="unit.barcode" visible></eg-grid-field>
     <eg-grid-field label="[% l('Publication Date') %]" path="issuance.date_published" visible>{{item.issuance.date_published|date:'shortDate'}}</eg-grid-field>
     <eg-grid-field label="[% l('Status') %]" path="status" visible></eg-grid-field>
     <eg-grid-field label="[% l('Date Expected') %]" path="date_expected" visible>{{item.date_expected|date:'shortDate'}}</eg-grid-field>
index 7210974..d883f86 100644 (file)
@@ -11,9 +11,9 @@ angular.module('egSerialsAppDep')
         templateUrl: './serials/t_view_items_grid',
         controller:
        ['$scope','$q','egSerialsCoreSvc','egCore','egGridDataProvider',
-        '$uibModal','ngToast','egConfirmDialog','egPromptDialog',
+        '$uibModal','ngToast','egConfirmDialog','egPromptDialog','$timeout',
 function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
-         $uibModal , ngToast , egConfirmDialog , egPromptDialog) {
+         $uibModal , ngToast , egConfirmDialog , egPromptDialog , $timeout) {
 
     $scope.svc = egSerialsCoreSvc;
 
@@ -91,43 +91,53 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
     $scope.edit_issuance_holding_code = function (items) {
         var promises = [];
         var edits = [];
-        angular.forEach(items, function (item) {
+        angular.forEach(items.reverse(), function (item) {
             promises.push( egSerialsCoreSvc.new_holding_code({
                     title    : egCore.strings.SERIALS_EDIT_SISS_HC,
-                    curr_iss : egCore.idl.fromHash('siss',item.issuance)
+                    curr_iss : egCore.idl.fromHash('siss',item.issuance),
+                    label    : item.issuance.label,
+                    type     : item.issuance.type ? item.issuance.type : 'adhoc'
                 }).then(function(result) {
-                    item.issuance.holding_code = JSON.stringify(result.holding_code);
-                    item.issuance.holding_type = result.type;
+                    if (result.type != 'adhoc') {
+                        item.issuance.holding_code = JSON.stringify(result.holding_code);
+                        item.issuance.holding_type = result.type;
+                    } else {
+                        item.issuance.label = result.label;
+                    }
+
                     item.issuance.date_published = result.date.toISOString();
                     item.issuance.editor = egCore.auth.user();
                     item.issuance.edit_date = 'now';
 
-                    return $q.when(egCore.idl.fromHash('siss',item.issuance));
-                }).then(function(iss) {
-                    return egCore.net.request(
-                        'open-ils.serial',
-                        'open-ils.serial.make_prediction_values',
-                        egCore.auth.token(),
-                        { ssub_id : $scope.ssubId,
-                          num_to_predict : 0,
-                          include_base_issuance : 1,
-                          base_issuance : iss
-                        }
-                    ).then( function(resp) {
-                        var evt = egCore.evt.parse(resp);
-                        if (evt) {
-                            ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
-                        } else {
-                            iss.label(resp[0].label);
-                            edits.push(iss);
-                        }
-                    });
+                    var iss = egCore.idl.fromHash('siss',item.issuance);
+                    if (iss.holding_type()) { // not an ad hoc issuance, get predicted label
+                        return egCore.net.request(
+                            'open-ils.serial',
+                            'open-ils.serial.make_prediction_values',
+                            egCore.auth.token(),
+                            { ssub_id : $scope.ssubId,
+                              num_to_predict : 0,
+                              include_base_issuance : 1,
+                              base_issuance : iss
+                            }
+                        ).then( function(resp) {
+                            var evt = egCore.evt.parse(resp);
+                            if (evt) {
+                                ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
+                            } else {
+                                iss.label(resp[0].label);
+                                edits.push(iss);
+                            }
+                        });
+                    }
+
+                    return $q.when(edits.push(iss));
                 })
             );
         });
         return $q.all(promises)
             .finally(function() {
-                return update_issuances(edits);
+                if (edits.length) return update_issuances(edits);
             });
     }
 
@@ -160,8 +170,36 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
     $scope.following_issuance = function (items) {
         return egSerialsCoreSvc.new_holding_code({
             title : egCore.strings.SERIALS_ISSUANCE_ADD,
+            type  : 'adhoc',
             prev_iss : egCore.idl.fromHash('siss',items[0].issuance)
         }).then(function(hc) {
+            if (hc.type == 'adhoc') {
+                var new_iss = new egCore.idl.siss();
+                new_iss.creator( egCore.auth.user().id() );
+                new_iss.editor( egCore.auth.user().id() );
+                new_iss.date_published( hc.date.toISOString() );
+                new_iss.subscription( $scope.ssubId );
+                new_iss.label( hc.label );
+
+                return egCore.pcrud.create(new_iss).then(function(issuance) {
+                    var new_item = new egCore.idl.sitem();
+                    new_item.creator( egCore.auth.user().id() );
+                    new_item.editor( egCore.auth.user().id() );
+                    new_item.issuance( issuance.id() );
+                    new_item.stream( items[0].stream.id );
+                    new_item.date_expected( hc.date.toISOString() ); // XXX do we have interval math?
+
+                    return egCore.pcrud.create(new_item).then(function() {
+                        ngToast.success(egCore.strings.SERIALS_ISSUANCE_SUCCESS_SAVE);
+                        return reload($scope.ssubId,_paging_filter);
+                    },function (error) {
+                        ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
+                    });
+                },function (error) {
+                    ngToast.danger(egCore.strings.SERIALS_ISSUANCE_FAIL_SAVE);
+                });
+            }
+
             return egCore.net.request(
                 'open-ils.serial',
                 'open-ils.serial.make_predictions',
@@ -188,8 +226,6 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
     }
 
     $scope.receive_and_barcode = false;
-    $scope.receive_and_bind = false;
-
     $scope.receive_barcode_checkbox_handler = function(item) {
         $scope[item.checkbox] = item.checked;
     }
@@ -201,7 +237,7 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
             if (next_per_stream[item.stream().id()]) return;
             if (item.status() == 'Expected') {
                 next_per_stream[item.stream().id()] = item;
-                list.push(item);
+                list.push(egCore.idl.Clone(item));
             }
         });
 
@@ -210,18 +246,17 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
 
     $scope.receive_selected = function (list) {
         return process_next('receive', list.map(function(item) {
-            return egSerialsCoreSvc.itemMap[item.id];
+            return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
         }));
     }
 
     $scope.bind_selected = function (list) {
-        $scope.receive_and_bind = true;
         return process_next('bind', list.map(function(item) {
-            return egSerialsCoreSvc.itemMap[item.id];
-        }));
+            return egCore.idl.Clone(egSerialsCoreSvc.itemMap[item.id]);
+        }), true);
     }
 
-    function process_next (mode, list) {
+    function process_next (mode, list, receive_and_bind) {
 
         if (!list.length) return $q.reject();
 
@@ -229,7 +264,7 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
         angular.forEach(list, function (item) {
             if (item.unit()) donor_unit_ids[item.unit().id()] = 1;
             if ($scope.receive_and_barcode) item.unit(-1);
-            if ($scope.receive_and_bind) item.unit(-2);
+            if (receive_and_bind) item.unit(-2);
         });
 
         var method; var success_label;
@@ -246,110 +281,113 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
         var call_numbers = {};
         var call_numbers_by_siss_and_sdist = {};
 
-        $scope.r_and_b_barcode = '';
+        var r_and_b_barcode = '';
+        var r_and_b_callnumber = '';
 
-        var prompt_promises = [];
-        if ($scope.receive_and_barcode || $scope.receive_and_bind) { // supplying a barcode (unit) to new items
+        var deferred = $q.defer();
+        var current_promise = deferred.promise;
+        var last_promise;
+        if ($scope.receive_and_barcode || receive_and_bind) { // supplying a barcode (unit) to new items
 
             angular.forEach(list, function (item) {
 
+                if (last_promise) current_promise = last_promise;
+
                 // TODO: I18N
                 var prompt_text = 'for '+
                     item.issuance().label()+
                     ' from Distribution: '+item.stream().distribution().label()+
                     '/'+item.stream().id()+':';
 
-                if ($scope.receive_and_bind) {
+                if (receive_and_bind) {
                     prompt_text = 'for the new unit:';
                 }
 
-                prompt_promises.push(
-                    $q(function() {
-                        if ($scope.receive_and_bind && $scope.r_and_b_barcode) {
-                            barcodes[item.id()] = $scope.r_and_b_barcode;
-                            return $q.when();
-                        }
+                last_promise = current_promise.then(function() {
+                    if (receive_and_bind && r_and_b_barcode) {
+                        barcodes[item.id()] = r_and_b_barcode;
+                        return $q.when();
+                    }
 
-                        return egPromptDialog.open(
-                            'Please enter a barcode ' + prompt_text, '@@AUTO',
-                            {ok : function(barcode) {
-                                if (barcode) {
-                                    barcode = String( barcode ).replace(/\s/g,'');
-                                    if (!barcode || barcode == 'null') {
-                                        barcode = '@@AUTO';
-    /*
-     *                                } else {
-     *                                    // XXX test for barcode in use
-     *                                    // disable alarm sound temporarily
-     *                                    var sound_setting = obj.data.no_sound;
-     *                                    if (!sound_setting) { // undefined or false
-     *                                         obj.data.no_sound = true; obj.data.stash('no_sound');
-     *                                    }
-     *                                    var test = obj.network.simple_request('FM_ACP_RETRIEVE_VIA_BARCODE',[ barcode ]);
-     *                                    if (typeof test.ilsevent == 'undefined') {
-     *                                         alert('Another copy has barcode "' + barcode + '", defaulting to system-generated.');
-     *                                         barcode = '@@AUTO';
-     *                                    }
-     *                                    if (!sound_setting) {
-     *                                         obj.data.no_sound = sound_setting; obj.data.stash('no_sound');
-     *                                    }
-     */
-                                    }
-                                    barcodes[item.id()] = barcode;
-                                    if ($scope.receive_and_bind && $scope.r_and_b_barcode)
-                                        $scope.r_and_b_barcode = barcode;
-                                } else {
-                                    barcodes[item.id()] = '@@AUTO';
+                    return egPromptDialog.open(
+                        'Please enter a barcode ' + prompt_text, '@@AUTO',
+                        {ok : function(barcode) {
+                            if (barcode) {
+                                barcode = String( barcode ).replace(/\s/g,'');
+                                if (!barcode || barcode == 'null') {
+                                    barcode = '@@AUTO';
+/*
+ *                                } else {
+ *                                    // XXX test for barcode in use
+ *                                    // disable alarm sound temporarily
+ *                                    var sound_setting = obj.data.no_sound;
+ *                                    if (!sound_setting) { // undefined or false
+ *                                         obj.data.no_sound = true; obj.data.stash('no_sound');
+ *                                    }
+ *                                    var test = obj.network.simple_request('FM_ACP_RETRIEVE_VIA_BARCODE',[ barcode ]);
+ *                                    if (typeof test.ilsevent == 'undefined') {
+ *                                         alert('Another copy has barcode "' + barcode + '", defaulting to system-generated.');
+ *                                         barcode = '@@AUTO';
+ *                                    }
+ *                                    if (!sound_setting) {
+ *                                         obj.data.no_sound = sound_setting; obj.data.stash('no_sound');
+ *                                    }
+ */
                                 }
-                            }}
-                        ).result;
-                    })
-                );
-
-                // now call numbers
-                prompt_promises.push(
-                    $q(function() {
-                        if ($scope.receive_and_bind && $scope.r_and_b_callnumber) {
-                            call_numbers[item.id()] = $scope.r_and_b_callnumber;
-                            return $q.when();
-                        }
-
-                        if (typeof call_numbers_by_siss_and_sdist[item.issuance().id() + '@' + item.stream().distribution().id()] == 'undefined') {
-                            var default_cn = 'DEFAULT';
-                            // if they defined a *_call_number, honor it as the default
-                            var preset_cn = item.stream().distribution()[mode + '_call_number']();
-                            if (preset_cn) {
-                                default_cn = preset_cn.label();
+                                barcodes[item.id()] = barcode;
+                                if (receive_and_bind && !r_and_b_barcode)
+                                    r_and_b_barcode = barcode;
+                            } else {
+                                barcodes[item.id()] = '@@AUTO';
                             }
-                        } else {
-                            // we have already seen this same issuance and distribution combo, so use the same call number
-                            call_numbers[item.id()] = call_numbers_by_siss_and_sdist[item.issuance().id() + '@' + item.stream().distribution().id()];
+                        }}
+                    ).result;
+                }).then(function() {
+                    if (receive_and_bind && r_and_b_callnumber) {
+                        call_numbers[item.id()] = r_and_b_callnumber;
+                        return $q.when();
+                    }
+
+                    if (typeof call_numbers_by_siss_and_sdist[item.issuance().id() + '@' + item.stream().distribution().id()] == 'undefined') {
+                        var default_cn = 'DEFAULT';
+                        // if they defined a *_call_number, honor it as the default
+                        var preset_cn = item.stream().distribution()[mode + '_call_number']();
+                        if (preset_cn) {
+                            default_cn = preset_cn.label();
                         }
+                    } else {
+                        // we have already seen this same issuance and distribution combo, so use the same call number
+                        call_numbers[item.id()] = call_numbers_by_siss_and_sdist[item.issuance().id() + '@' + item.stream().distribution().id()];
+                    }
 
-                        return egPromptDialog.open('Please enter/adjust a call number'+prompt_text, default_cn,
-                            {ok : function(call_number) {
-                                if (call_number) {
-                                    call_number = String( call_number ).replace(/\s/g,'');
-                                    if (!call_number || call_number == 'null')
-                                        call_number = 'DEFAULT';
-                                    call_numbers[item.id()] = call_number;
-                                    call_numbers_by_siss_and_sdist[item.issuance().id() + '@' + item.stream().distribution().id()] = call_number;
-                                    if ($scope.receive_and_bind && $scope.r_and_b_callnumber)
-                                        $scope.r_and_b_callnumber = callnumber;
-                                } else {
-                                    call_numbers[item.id()] = 'DEFAULT';
-                                }
-                            }}
-                        ).result;
-                    })
-                );
+                    return egPromptDialog.open('Please enter/adjust a call number'+prompt_text, default_cn,
+                        {ok : function(call_number) {
+                            if (call_number) {
+                                call_number = String( call_number ).replace(/\s/g,'');
+                                if (!call_number || call_number == 'null')
+                                    call_number = 'DEFAULT';
+                                call_numbers[item.id()] = call_number;
+                                call_numbers_by_siss_and_sdist[item.issuance().id() + '@' + item.stream().distribution().id()] = call_number;
+                                if (receive_and_bind && !r_and_b_callnumber)
+                                    r_and_b_callnumber = call_number;
+                            } else {
+                                call_numbers[item.id()] = 'DEFAULT';
+                            }
+                        }}
+                    ).result;
+                });
 
             });
-        } else {
-            prompt_promises.push($q.when());
         }
 
-        return $q.all(prompt_promises).finally(function () {
+        if (last_promise) current_promise = last_promise;
+
+        current_promise.then(function () {
+            console.log(list);
+            console.log(barcodes);
+            console.log(call_numbers);
+            console.log(donor_unit_ids);
+
             return egCore.net.request(
                 'open-ils.serial', method,
                 egCore.auth.token(), list, barcodes, call_numbers, donor_unit_ids
@@ -368,6 +406,8 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
                 }
             );
         });
+
+        return deferred.resolve();
     }
 
     $scope.add_issuances = function () {
@@ -428,6 +468,12 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
         return true;
     };
 
+    $scope.need_many_selected = function() {
+        var items = $scope.itemGridControls.selectedItems();
+        if (items.length > 1) return false;
+        return true;
+    };
+
 }]
 
     }
index f44f8ec..ec60fce 100644 (file)
@@ -229,7 +229,7 @@ function(egCore , orderByFilter , $q , $filter , $uibModal) {
         var link = '1.1';
 
         var sub = service.get_ssub(service.subId);
-        if (!sub) return {};
+        if (!sub) return args;
 
         var scap;
         if (prev_iss) { // we're predicting
@@ -263,6 +263,8 @@ function(egCore , orderByFilter , $q , $filter , $uibModal) {
             date = new Date(curr_iss.date_published());
         }
 
+        args.date = date;
+
         if (!scap) {
             var tmp = sub.scaps().filter(function (s) {
                 return (s.type() == type && s.active() == 't');
@@ -270,7 +272,7 @@ function(egCore , orderByFilter , $q , $filter , $uibModal) {
             if (angular.isArray(tmp) && tmp[0]) scap = tmp[0];
         }
 
-        if (!scap) return {};
+        if (!scap) return args;
 
         var others = [], enums = [], chrons = [], freq = '';
         var pat = JSON.parse(scap.pattern_code()).slice(4); // just the part we care about
@@ -349,6 +351,7 @@ function(egCore , orderByFilter , $q , $filter , $uibModal) {
     service.new_holding_code = function (options) {
         if (options === undefined) options = {};
         options.count = options.count || 1;
+        options.label = options.label || '';
 
         return $uibModal.open({
             templateUrl: './serials/t_holding_code_dialog',
@@ -360,7 +363,8 @@ function(egCore , orderByFilter , $q , $filter , $uibModal) {
                 $scope.focusMe = true;
                 $scope.title = options.title;
                 $scope.request_count = options.request_count;
-                $scope.count = 1;
+                $scope.count = options.count;
+                $scope.label = options.label;
                 $scope.save_label = options.save_label;
                 $scope.pubdate = options.date || new Date();
                 $scope.type = options.type || 'basic';
@@ -385,7 +389,8 @@ function(egCore , orderByFilter , $q , $filter , $uibModal) {
                     }
                 }
 
-                $scope.$watch('count',function (n,o) { options.count = n });
+                $scope.$watch('count',function (n) {options.count = n});
+                $scope.$watch('label',function (n) {options.label = n});
                 $scope.$watch('type',refresh);
                 $scope.$watch('pubdate',refresh);
 
@@ -395,14 +400,17 @@ function(egCore , orderByFilter , $q , $filter , $uibModal) {
                 refresh(1,2); // force data loading
             }]
         }).result.then(function (args) {
-            angular.forEach(
-                args.enums.concat(args.chrons),
-                function (e) {
-                    args.holding_code.push(e.subfield);
-                    args.holding_code.push(e.value);
-                }
-            );
+            if (args.enums && args.chrons) {
+                angular.forEach(
+                    args.enums.concat(args.chrons),
+                    function (e) {
+                        args.holding_code.push(e.subfield);
+                        args.holding_code.push(e.value);
+                    }
+                );
+            }
             args.count = options.count;
+            args.label = options.label;
             return $q.when(args);
         });
     }