LP#1117808: teach record bucket merge about merge profiles
authorGalen Charlton <gmc@equinoxinitiative.org>
Fri, 23 Dec 2016 06:38:27 +0000 (01:38 -0500)
committerGalen Charlton <gmc@equinoxinitiative.org>
Fri, 10 Feb 2017 16:46:33 +0000 (11:46 -0500)
This patch adds a widget to the record bucket merge dialog
to allow the user to select a MARC merge profile to use
during the merge.

After the user has chosen a lead record, the result of the merge
is displayed in the left-hand pane, and is updated whenever the user

- changes the selected merge profile
- swaps in a different lead record
- removes a subordinate record from consideration

As before, the user can choose to edit the lead record, but note that
the version that is edited is the /original/ version of the
lead record, with any changes due to the merge profile
being applied after the edit.

Since there can be more than one subordinate record in play, the
result of the merge is calculated by merging the first subordinate
record into the lead record, then in the second subordinate record,
and so forth.

Merge profiles that have a 'preserve' specification are excluded
from selection, as such profiles have the effect of swapping what
is considered the lead record for the purpose of the MARC merge.

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/templates/staff/cat/bucket/record/t_edit_lead_record.tt2
Open-ILS/src/templates/staff/cat/bucket/record/t_merge_records.tt2
Open-ILS/web/js/ui/default/staff/cat/bucket/record/app.js

index 60f3a09..40c05e1 100644 (file)
@@ -5,8 +5,10 @@
     <h4 class="modal-title">[% l('Edit Lead Record') %]</h4>
   </div>
   <div class="modal-body">
-    <eg-marc-edit-record dirty-flag="dirty_flag" record-id="record_id"
-                         record-type="bre" />
+    <eg-marc-edit-record dirty-flag="dirty_flag" marc-xml="lead.marc_xml"
+                         in-place-mode="true" on-save="on_save"
+                         record-type="bre">
+    </eg-marc-edit-record>
   </div>
   <div class="modal-footer">
     <input type="submit" ng-click="ok()"
index 76e6409..255d85f 100644 (file)
@@ -6,6 +6,12 @@
   </div>
   <div class="modal-body">
       <div class="row">
+        <div class="col-xs-3">
+          <label for="merge_profile_selector">[% l('Choose merge profile') %]</label>
+          <eg-fm-value-selector id="merge_profile_selector" ng-model="merge_profile" idl-class="vmp" ou-setting="cat.default_merge_profile" filter="{'preserve_spec':{'=':null}}" sticky-setting="eg.cat.record_bucket.default_merge_profile"></eg-fm-value-selector>
+        </div>
+      </div>
+      <div class="row">
           <div class="col-xs-6">
             <h4>[% l('Lead record') %]</h4>
             <div ng-if="lead_id">
                  <tab heading="[% l('Bib [_1]', '{{lead_id}}') %]">
                    <button class="btn btn-default btn-sm" ng-class="{disabled : editing_inplace}" ng-click="edit_lead_inplace()">[% l('Edit') %]</button>
                    <button class="btn btn-default btn-sm" ng-class="{disabled : editing_inplace}" ng-click="edit_lead()">[% l('Edit using full editor') %]</button>
-                   <eg-marc-edit-record dirty-flag="dirty_flag" record-id="lead_id"
+                   <eg-marc-edit-record dirty-flag="dirty_flag" marc-xml="lead.marc_xml"
+                             in-place-mode="true"
                              record-type="bre" flat-only="true" embedded="true" 
-                             ng-if="editing_inplace" on-save="post_edit_inplace">
+                             ng-show="editing_inplace" on-save="post_edit_inplace">
                    </eg-marc-edit-record>
-                   <eg-record-breaker record-id="lead_id" ng-if="!editing_inplace"></eg-record-breaker>
+                   <eg-record-breaker record-id="lead_id" marc-xml="lead.marc_xml" ng-show="!editing_inplace"></eg-record-breaker>
                    <eg-volume-list record-id="lead_id" edit-copies="true" edit-volumes="true"></eg-volume-list>
                  </tab>
                </tabset>
index 5c891d0..b6c4661 100644 (file)
@@ -550,6 +550,8 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                 ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
                 $scope.records = [];
                 $scope.lead_id = 0;
+                $scope.merge_profile = null;
+                $scope.lead = { marc_xml : null };
                 $scope.editing_inplace = false;
                 angular.forEach(records, function(rec) {
                     $scope.records.push({ id : rec.id });
@@ -557,16 +559,59 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                 $scope.ok = function() {
                     $uibModalInstance.close({
                         lead_id : $scope.lead_id,
-                        records : $scope.records
+                        records : $scope.records,
+                        merge_profile : $scope.merge_profile,
+                        lead : $scope.lead
                     });
                 }
                 $scope.cancel = function () { $uibModalInstance.dismiss() }
+
+                $scope.merge_marc = function() {
+                    // need lead, at least one sub, and a merge profile
+                    if (!$scope.lead_id) return;
+                    if (!$scope.merge_profile) return;
+
+                    if (!$scope.records.length) {
+                        // if we got here, the last subordinate record
+                        // was likely removed, so let's refresh the
+                        // lead for the sake of a consistent display
+                        egCore.pcrud.retrieve('bre', $scope.lead_id)
+                        .then(function(rec) {
+                            $scope.lead.marc_xml = rec.marc();
+                        });
+                        return;
+                    }
+
+                    var recs = $scope.records.map(function(val) { return val.id; });
+                    recs.unshift($scope.lead_id);
+                    egCore.net.request(
+                        'open-ils.cat',
+                        'open-ils.cat.merge.biblio.per_profile',
+                        egCore.auth.token(),
+                        $scope.merge_profile,
+                        recs
+                    ).then(function(merged) {
+                        if (merged) $scope.lead.marc_xml = merged;
+                    });
+                }
+                $scope.$watch('merge_profile', function(newVal, oldVal) {
+                    if (newVal && newVal !== oldVal) {
+                        $scope.merge_marc();
+                    }
+                });
+
                 $scope.use_as_lead = function(rec) {
                     if ($scope.lead_id) {
                         $scope.records.push({ id : $scope.lead_id });
                     }
                     $scope.lead_id = rec.id;
                     $scope.drop(rec);
+
+                    egCore.pcrud.retrieve('bre', $scope.lead_id)
+                    .then(function(rec) {
+                        $scope.lead.marc_xml = rec.marc();
+                        $scope.merge_marc();
+                    });
                 }
                 $scope.drop = function(rec) {
                     angular.forEach($scope.records, function(val, i) {
@@ -574,6 +619,7 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                             $scope.records.splice(i, 1);
                         }
                     });
+                    $scope.merge_marc();
                 }
                 $scope.post_edit_inplace = function() {
                     $scope.editing_inplace = false;
@@ -582,36 +628,61 @@ function($scope,  $q , $routeParams,  bucketSvc,  egCore,  $window,
                     $scope.editing_inplace = true;
                 }
                 $scope.edit_lead = function() {
-                    var lead_id = $scope.lead_id;
+                    var lead = { marc_xml : $scope.lead.marc_xml };
+
+                    // passing the on-save callback this way is a
+                    // hack - this invocation of the MARC editor doesn't
+                    // need it, but for some reason using this stomps
+                    // over the callback set by the other MARC editor
+                    // instance
+                    var callback = $scope.post_edit_inplace;
+
                     $uibModal.open({
                         templateUrl: './cat/bucket/record/t_edit_lead_record',
                         size: 'lg',
                         controller:
                             ['$scope', '$uibModalInstance', function($scope, $uibModalInstance) {
                             $scope.focusMe = true;
-                            $scope.record_id = lead_id;
+                            $scope.lead = lead;
                             $scope.dirty_flag = false;
                             $scope.ok = function() { $uibModalInstance.close() }
                             $scope.cancel = function () { $uibModalInstance.dismiss() }
+                            $scope.on_save = callback;
                         }]
                     }).result.then(function() {
-                        // TODO: need a way to force a refresh of the egRecordBreaker, as
-                        // the record ID does not change
+                        $scope.lead.marc_xml = lead.marc_xml;
                     });
                 };
             }]
         }).result.then(function (args) {
             if (!args.lead_id) return;
             if (!args.records.length) return;
-            egCore.net.request(
-                'open-ils.cat',
-                'open-ils.cat.biblio.records.merge',
-                egCore.auth.token(),
-                args.lead_id,
-                args.records.map(function(val) { return val.id; })
-            ).then(function() {
-                $window.location.href =
-                    egCore.env.basePath + 'cat/catalog/record/' + args.lead_id;
+
+            function update_bib() {
+                if (args.merge_profile) {
+                    return egCore.pcrud.retrieve('bre', args.lead_id)
+                    .then(function(rec) {
+                        rec.marc(args.lead.marc_xml);
+                        rec.edit_date('now');
+                        rec.editor(egCore.auth.user().id());
+                        return egCore.pcrud.update(rec);
+                    });
+                } else {
+                    return $q.when();
+                }
+            }
+
+            update_bib().then(function() {
+                egCore.net.request(
+                    'open-ils.cat',
+                    'open-ils.cat.biblio.records.merge',
+                    egCore.auth.token(),
+                    args.lead_id,
+                    args.records.map(function(val) { return val.id; })
+                ).then(function() {
+                    $window.location.href =
+                        egCore.env.basePath + 'cat/catalog/record/' + args.lead_id;
+                });
             });
         });
     }