webstaff: service addition for generating holding codes, seeded or unseeded
authorMike Rylander <mrylander@gmail.com>
Fri, 12 May 2017 20:34:33 +0000 (16:34 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Tue, 30 May 2017 16:06:44 +0000 (12:06 -0400)
Signed-off-by: Mike Rylander <mrylander@gmail.com>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/templates/staff/serials/t_holding_code_dialog.tt2 [new file with mode: 0644]
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

diff --git a/Open-ILS/src/templates/staff/serials/t_holding_code_dialog.tt2 b/Open-ILS/src/templates/staff/serials/t_holding_code_dialog.tt2
new file mode 100644 (file)
index 0000000..a093f0d
--- /dev/null
@@ -0,0 +1,68 @@
+<form ng-submit="ok(args)" role="form">
+<div class="modal-header">
+    <button type="button" class="close" ng-click="cancel()" 
+        aria-hidden="true">&times;</button>
+    <h4 class="modal-title">{{ title || "[% l('Construct new holding code') %]" }}</h4>
+</div>
+
+<div class="modal-body">
+  <div class="row">
+    <div class="col-md-3">
+      <b>[% l('Publication date') %]</b>
+    </div>
+    <div class="col-md-4">
+      <eg-date-input ng-model="pubdate"></eg-date-input>
+    </div>
+    <div class="col-md-2">
+      <b>[% l('Type') %]</b>
+    </div>
+    <div class="col-md-3">
+      <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-options='t.n as t.l for t in types'>
+      </select>
+    </div>
+  </div>
+
+  <div class="row container" ng-if="args.enums.length">
+    <hr/>
+    <h2>[% l('Enumeration labels') %]</h2>
+  </div>
+
+  <div class="row" ng-repeat="e in args.enums">
+    <div class="col-md-4">
+      [% l('Enumeration level [_1]','{{ $index + 1}}') %]
+    </div>
+    <div class="col-md-4">
+      <input class="form-control" ng-model="e.value" type="text"/>
+    </div>
+    <div class="col-md-4">
+      {{ e.pattern }}
+    </div>
+  </div>
+
+  <div class="row container" ng-if="args.chrons.length">
+    <hr/>
+    <h2>[% l('Chronology labels') %]</h2>
+  </div>
+
+  <div class="row" ng-repeat="c in chrons">
+    <div class="col-md-4">
+      [% l('Chronology level [_1]','{{ $index + 1}}') %]
+    </div>
+    <div class="col-md-4">
+      <input class="form-control" ng-model="c.value" type="text"/>
+    </div>
+    <div class="col-md-4">
+      {{ c.pattern }}
+    </div>
+  </div>
+</div>
+
+<div class="modal-footer">
+    <input type="submit" class="btn btn-primary" value='{{ save_label || "[% l('Save') %]" }}'></input>
+    <button class="btn btn-warning" ng-click="cancel()">[% l('Cancel') %]</button>
+</div>
+</form>
index 24228ec..3371201 100644 (file)
@@ -6,6 +6,8 @@
     grid-controls="itemGridControls"
     persist-key="serials.view_item_grid">
 
+    <eg-grid-action handler="add_issuance"
+      label="[% l('following issuance') %]"></eg-grid-action>
     <eg-grid-action handler="edit_items"
       label="[% l('Edit Items') %]"></eg-grid-action>
     <eg-grid-action handler="delete_items"
index 1ce60b4..2591055 100644 (file)
@@ -37,6 +37,12 @@ function($scope , $q , egSerialsCoreSvc , egCore , egGridDataProvider ,
         }
     });
 
+    $scope.add_issuance = function (items) {
+        egSerialsCoreSvc.new_holding_code({
+            prev_iss : egCore.idl.fromHash('siss',items[0].issuance)
+        }).then(function(hc) { console.log(hc) });
+    }
+
     $scope.need_one_selected = function() {
         var items = $scope.itemGridControls.selectedItems();
         if (items.length == 1) return false;
index f3fc525..62d7659 100644 (file)
@@ -1,7 +1,8 @@
 angular.module('egSerialsMod', ['egCoreMod'])
 .factory('egSerialsCoreSvc',
-       ['egCore','orderByFilter','$q',
-function(egCore , orderByFilter , $q) {
+       ['egCore','orderByFilter','$q','$filter','$uibModal',
+function(egCore , orderByFilter , $q , $filter , $uibModal) {
+    var DAY = 86400000;
     var service = {
         bibId : null,
         subId : null,
@@ -9,9 +10,76 @@ function(egCore , orderByFilter , $q) {
         subList : [],
         sptList : [],
         itemTree : [],
-        itemList : []
+        itemList : [],
+        freq_offset : {
+            a : 365 * DAY,
+            b : 62 * DAY,
+            c : 4 * DAY,
+            d : DAY,
+            e : 14 * DAY,
+            f : 186 * DAY,
+            g : 2 * 365 * DAY,
+            h : 3 * 365 * DAY,
+            i : 2 * DAY,
+            j : 10 * DAY,
+            k : DAY,
+            m : 31 * DAY,
+            q : 93 * DAY,
+            s : 14 * DAY,
+            t : 124 * DAY,
+            w : 7 * DAY,
+            x : 0
+        },
+        freq_chrons : {
+            a : ['year'],
+            b : ['year','month'],
+            c : ['year','month'],
+            d : ['year','month','day'],
+            e : ['year','month','day'],
+            f : ['year','month'],
+            g : ['year'],
+            h : ['year','month'],
+            i : ['year','month','day'],
+            j : ['year','month','day'],
+            k : ['year','month','day'],
+            m : ['year','month'],
+            q : ['year','season'],
+            s : ['year','month'],
+            t : ['year','month','day'],
+            w : ['year','month','day'],
+            x : ['year','month','day']
+        },
+        get_chron_part : {
+            year  : function(d) { return d.getFullYear() },
+            season: function(d) { return _loose_season(d) },
+            month : function(d) { return ('00' + (d.getMonth() + 1)).slice(-2) },
+            week  : function(d) { return $filter('date')(d, 'ww') },
+            day   : function(d) { return ('00'+d.getDate()).slice(-2) },
+            hour  : function(d) { return ('00'+d.getHours()).slice(-2) }
+        }
     };
 
+    function _loose_season(D) {
+        var m = D.getMonth() + 1;
+        var d = D.getDate();
+
+        if (
+            (m == 1 || m == 2) || (m == 12 && d >= 21) || (m == 3 && d < 20)
+        ) {
+            return 24;  /* MFHD winter */
+        } else if (
+            (m == 4 || m == 5) || (m == 3 && d >= 20) || (m == 6 && d < 21)
+        ) {
+            return 21;  /* spring */
+        } else if (
+            (m == 7 || m == 8) || (m == 6 && d >= 21) || (m == 9 && d < 22)
+        ) {
+            return 22;  /* summer */
+        } else {
+            return 23;  /* autumn */
+        }
+    }
+
     // fetch subscription, distributions, streams, captions,
     // and notes associated with the indicated bib
     service.fetch = function(bibId, contextOrg) {
@@ -96,6 +164,150 @@ function(egCore , orderByFilter , $q) {
         });
     }
 
+    service.prep_new_holding_code = function (args) {
+
+        var type = args.type;
+        var date = args.date;
+        var prev_iss = args.prev_iss;
+
+        var sub = service.get_ssub(service.subId);
+        if (!sub) return $q.reject();
+
+        var scap;
+        var tmp = sub.scaps().filter(function (scap) {
+            return (scap.type() == type && scap.active() == 't');
+        });
+        if (angular.isArray(tmp) && tmp[0]) scap = tmp[0];
+
+        if (!scap) return $q.reject();
+
+        var others = [], enums = [], chrons = [], freq = '';
+        var pat = JSON.parse(scap.pattern_code()).slice(4); // just the part we care about
+
+        var freq_index = pat.indexOf('w');
+        if (freq_index > -1) {
+            freq = pat[freq_index + 1];
+        }
+
+        var link = '1.1';
+        if (prev_iss) {
+            var old_link_parts = JSON.parse(prev_iss.holding_code())[3].split('.');
+            var olink = old_link_parts[0];
+            var oseq = parseInt(old_link_parts[1]) + 1;
+            link = [olink,oseq].join('.');
+
+            date = new Date(
+                new Date(prev_iss.date_published()).getTime() + service.freq_offset[freq]
+            );
+        }
+        
+        if (!date) date = new Date();
+
+        for (var i = 0; i < pat.length; i++) {
+            sf = pat[i]; i++;
+            val = pat[i];
+
+            if (sf != 'w') {
+                var pat_part = {
+                    subfield : sf,
+                    pattern  : val
+                };
+
+                var chron_part = val.replace(/[)(]+/g,'');
+                try {
+                    pat_part.value = service.get_chron_part[chron_part](date);
+                } catch (e) {
+                    // not a chron part
+                    pat_part.value = '';
+                }
+
+                if (sf.match(/[a-f]/)) {
+                    enums.push(pat_part);
+                } else if (sf.match(/[i-l]/)) {
+                    chrons.push(pat_part);
+                } else {
+                    others.push(pat_part);
+                }
+            }
+        }
+
+        if (enums.length == 0 && chrons.length == 0) {
+            var parts = service.freq_chrons[freq];
+            if (parts.length) {
+                angular.forEach(parts, function(p, ind) {
+                    var sf = !ind ? 'i' : !--ind ? 'j' : 'k';
+                    chrons.push({
+                        subfield : sf,
+                        value    : service.get_chron_part.year(date)
+                    });
+                });
+            } else { 
+                chrons = [
+                    { subfield : 'i', value : service.get_chron_part.year(date)  },
+                    { subfield : 'j', value : service.get_chron_part.month(date) },
+                    { subfield : 'k', value : service.get_chron_part.day(date)  }
+                ];
+            }
+        }
+
+        return {
+            holding_code : ["4","1","8",link],
+            date         : date,
+            enums        : enums,
+            chrons       : chrons,
+            others       : others,
+            freq         : freq
+        };
+    }
+
+    service.new_holding_code = function (options) {
+        if (options === undefined) options = {};
+
+        return $uibModal.open({
+            templateUrl: './serials/t_holding_code_dialog',
+            //size: 'lg',
+            //windowClass: 'eg-wide-modal',
+            backdrop: 'static',
+            controller:
+                ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
+                $scope.focusMe = true;
+                $scope.title = options.title;
+                $scope.pubdate = options.date || new Date();
+                $scope.type = options.type || 'basic';
+                $scope.args = {};
+
+                function refresh (n,o) {
+                    if (n && o && n !== o) {
+                        $scope.args = service.prep_new_holding_code({
+                            type : $scope.type,
+                            date : $scope.pubdate,
+                            prev_iss : options.prev_iss
+                        });
+                        $scope.pubdate = $scope.args.date;
+                    }
+                }
+
+                $scope.$watch('type',refresh);
+                $scope.$watch('pubdate',refresh);
+
+                $scope.ok = function(args) { $uibModalInstance.close(args) }
+                $scope.cancel = function () { $uibModalInstance.dismiss() }
+
+                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);
+                }
+            );
+
+            return $q.when(args.holding_code);
+        });
+    }
+
     // create/update a flat version of the subscription/distribution/stream
     // tree for feeding to the distribution and stream grid
     function update_flat_sdist_sstr_list() {