html template based print support; print bills supported
authorBill Erickson <berick@esilibrary.com>
Thu, 29 May 2014 20:53:44 +0000 (16:53 -0400)
committerBill Erickson <berick@esilibrary.com>
Thu, 29 May 2014 20:53:44 +0000 (16:53 -0400)
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/templates/staff/admin/workstation/t_print_config.tt2
Open-ILS/src/templates/staff/admin/workstation/t_print_templates.tt2
Open-ILS/src/templates/staff/css/style.css.tt2
Open-ILS/src/templates/staff/share/print_templates/t_bills_current.tt2
Open-ILS/src/templates/staff/share/print_templates/t_bills_historical.tt2 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/admin/workstation/app.js
Open-ILS/web/js/ui/default/staff/circ/patron/bills.js
Open-ILS/web/js/ui/default/staff/services/hatch.js

index f90fc01..06067ac 100644 (file)
@@ -157,7 +157,7 @@ ng-init="htmlPrintContent='
   <style>p { color: blue }</style>
   <h2>[% l('Test HTML Print') %]</h2>
   <br/>
-  <img src=\'http://[% ctx.hostname %]/opac/images/main_logo.png\' width=\'140\' height=\'24\'/>
+  <img src=\'https://[% ctx.hostname %]/opac/images/main_logo.png\' width=\'140\' height=\'24\'/>
   <p>[% l('Welcome, Stranger!') %]</p>
   <p>{{value1}}</p>
   <p>{{value2}}</p>
index cda10b2..911c7d5 100644 (file)
@@ -30,7 +30,9 @@
 <div class="row">
   <div class="col-md-5">
     <h3>[% l('Preview') %]</h3>
-    <div eg-print-template-output content="print.template_content"></div>
+    <div eg-print-template-output 
+      content="print.template_content" 
+      context="preview_scope"></div>
   </div>
   <div class="col-md-7">
     <h3>[% l('Template') %]</h3>
index c30215b..8cf4d15 100644 (file)
@@ -113,7 +113,7 @@ table.list tr.selected td {
 .pad-horiz {padding : 0px 10px 0px 10px; }
 .pad-vert {padding : 20px 0px 10px 0px;}
 
-#print-div { display: none; }
+/*#print-div { display: none; }*/
 
 /* by default, give all tab panes some top padding */
 .tab-pane { padding-top: 20px; }
index 71e6bf2..c99cb4d 100644 (file)
@@ -1,10 +1,7 @@
 Welcome to {{current_location.name}}!<br/>
 You have the following bills:
 <hr/>
-
-<b ng-repeat="xact in transactions">{{xact.id}}</b>
 <dl>
-
   <div ng-repeat="xact in transactions">
     <dt><b>Bill #{{xact.id}}</b></dt>
     <dd>
diff --git a/Open-ILS/src/templates/staff/share/print_templates/t_bills_historical.tt2 b/Open-ILS/src/templates/staff/share/print_templates/t_bills_historical.tt2
new file mode 100644 (file)
index 0000000..c99cb4d
--- /dev/null
@@ -0,0 +1,49 @@
+Welcome to {{current_location.name}}!<br/>
+You have the following bills:
+<hr/>
+<dl>
+  <div ng-repeat="xact in transactions">
+    <dt><b>Bill #{{xact.id}}</b></dt>
+    <dd>
+    <table> 
+      <tr valign="top">
+        <td>[% l('Date:') %]</td>
+        <td>{{xact.xact_start | date:'short'}}</td>
+      </tr> 
+      <tr valign="top">
+        <td>[% l('Type') %]:</td>
+        <td>{{xact.summary.xact_type}}</td>
+      </tr> 
+      <tr valign="top">
+        <td>[% l('Last Billing') %]:</td>
+        <td>{{xact.summary.last_billing_type}}<br/>
+            {{xact.summary.last_billing_note}}
+        </td>
+      </tr> 
+      <tr valign="top">
+        <td>[% l('Total Billed') %]:</td>
+        <td>{{xact.summary.total_owed | currency}}</td>
+      </tr> 
+      <tr valign="top">
+        <td>[% l('Last Payment') %]:</td>
+        <td>{{xact.summary.last_payment_type}}<br/>
+            {{xact.summary.last_payment_note}}
+        </td>
+      </tr> 
+      <tr valign="top">
+        <td>[% l('Total Paid') %]:</td>
+        <td>{{xact.summary.total_paid | currency}}</td>
+      </tr> 
+      <tr valign="top">
+        <td><b>[% l('Balance') %]:</b></td>
+        <td><b>{{xact.summary.balance_owed | currency}}</b></td>
+      </tr> 
+    </table>
+    </dd>
+    <br/>
+  </div><!-- ng-repeat -->
+</dl>
+<hr/>
+{{current_location.shortname}} {{today | date:'short'}}
+<br/><br/>
+
index 9e3a104..645d780 100644 (file)
@@ -342,6 +342,7 @@ function($scope , $q , egCore) {
         egCore.hatch.getPrintTemplate($scope.print.template_name)
         .then(function(html) { 
             $scope.print.template_content = html;
+            console.log('set template content');
         });
     }
 
@@ -355,7 +356,7 @@ function($scope , $q , egCore) {
     $scope.template_changed(); // load the default
 }])
 
-// TODO: move compilation into egHatch!
+// 
 .directive('egPrintTemplateOutput', ['$compile',function($compile) {
     return function(scope, element, attrs) {
         scope.$watch(
@@ -363,11 +364,13 @@ function($scope , $q , egCore) {
                 return scope.$eval(attrs.content);
             },
             function(value) {
+                // create an isolate scope and copy the print context
+                // data into the new scope.
+                // TODO: see also print security concerns in egHatch
                 var result = element.html(value);
+                var context = scope.$eval(attrs.context);
                 var print_scope = scope.$new(true);
-                // copy only the print preview scope data 
-                // into the isolate scope for compilation
-                angular.forEach(scope.preview_scope, function(val, key) {
+                angular.forEach(context, function(val, key) {
                     print_scope[key] = val;
                 })
                 $compile(element.contents())(print_scope);
index b0bf85a..323179e 100644 (file)
@@ -405,8 +405,32 @@ function($scope , $q , $routeParams , egCore , egConfirmDialog , $location,
         // TODO: select grid items where refunds are due
     }
 
-    $scope.printBills = function() {
-        // TODO: print selected bills using the bills print template
+    $scope.printBills = function(selected) {
+        console.log('HEREEE');
+
+        // bills print receipt assumes nested hashes, but our grid
+        // stores flattener data.  Fetch the selected xacts as
+        // fleshed pcrud objects.  (Consider an alternate approach..)
+        var ids = selected.map(function(t){ return t.id });
+        var xacts = [];
+        egCore.pcrud.search('mbt', 
+            {id : ids},
+            {flesh : 1, flesh_fields : {'mbt' : ['summary']}},
+            {authoritative : true}
+        ).then(
+            function() {
+                egCore.hatch.printFromTemplate('receipt', 'bills_current', 
+                    {   transactions : xacts,
+                        current_location : egCore.idl.toHash(
+                            egCore.org.get(egCore.auth.user().ws_ou()))
+                    }
+                );
+            }, 
+            null, 
+            function(xact) {
+                xacts.push(egCore.idl.toHash(xact));
+            }
+        );
     }
 
     $scope.applyPayment = function() {
index edd6907..e673b5b 100644 (file)
@@ -201,39 +201,51 @@ angular.module('egCoreMod')
         }
     }
 
-    service.interpolateHtmlTemplate = function(template, printScope) {
-        // TODO: for print template security, we must scrub
-        // the scope object and remove any references to 
-        // functions or objects.  Otherwise, print templates
-        // would have the power to modify data via the scope
-        var subScope = $rootScope.$new();
-        angular.forEach(printScope, function(val, key) {subScope[key] = val});
-        var html = $interpolate(template)(subScope); 
-        subScope.$destroy();
-        return html;
-    }
+    service.printFromTemplate = function(context, templateName, printScope) {
 
+        // populate some common print scope values...
+        printScope.today = new Date();
+        return service.getPrintTemplate(templateName)
+        .then(function(template) {
+            service.print(context, 'text/html', template, printScope);
+        });
+    }
 
-    // supported values for contentType are 'text/html' and 'text/plain'
     service.print = function(
         context, contentType, content, printScope, withDialog) {
 
-        // generate our HTML content if necessary
+        var promise;
         if (contentType == 'text/html') {
-            content = service.interpolateHtmlTemplate(content, printScope);
+            // all HTML content is assumed to require compilation, regardless
+            // of the print destination
+            promise = service.ingestPrintContent(
+                contentType, content, printScope);
+        } else {
+            // text content does not require compilation for remote printing
+            promise = $q.when(content);
         }
 
-        return service.remotePrint(
-            context, contentType, content, printScope, withDialog)['catch'](
-            function(msg) {
-                // remote print not available; print locally
-                return service.browserPrint(msg);
-            }
-        );
+        return promise.then(function(html) {
+            return service.remotePrint(
+                context, contentType, html, withDialog)['catch'](
+
+                function(msg) {
+                    // remote print not available; 
+                    if (contentType != 'text/html') {
+                        // text content does require compilation 
+                        // (absorption) for browser printing
+                        service.ingestPrintContent(contentType, content, {})
+                        .then(function() { $window.print() });
+                    } else {
+                        $window.print();
+                    }
+                }
+            );
+        });
     }
 
     service.remotePrint = function(
-        context, contentType, content, printScope, withDialog) {
+        context, contentType, content, withDialog) {
 
         return service.getPrintConfig().then(
             function(conf) {
@@ -249,11 +261,6 @@ angular.module('egCoreMod')
         );
     }
 
-    // print locally via the browser
-    service.browserPrint = function(msg) {
-        service.onBrowserPrint(msg.contentType, msg.content);
-    }
-
     // -------------
     // print configuration is always stored as remote items,
     // since there is no concept of a local printer
@@ -508,44 +515,48 @@ angular.module('egCoreMod')
 // option will always result in empty pages.  Move the print CSS
 // out of the standalone CSS file and put it into a template file
 // for this directive.
-.directive('egPrintContainer', function() {
+.directive('egPrintContainer', ['$compile', function($compile) {
     return {
         restrict : 'AE',
-        template : '<div id="eg-print-container-for-html"></div>',
+        scope : {}, // isolate our scope
+        link : function(scope, element, attrs) {
+            scope.elm = element;
+        },
         controller : 
-                   ['$scope','$window','$timeout','egHatch', 
-            function($scope , $window , $timeout , egHatch) {
-
-                egHatch.onBrowserPrint = function(contentType, content) {
-                    switch(contentType) {
-                        case 'text/csv':
-                        case 'text/plain':
-                            // preserve newlines, spaces, etc.
-                            content = '<pre>' + content + '</pre>';
-                        case 'text/html':
-                            // TODO: make this angular-y
-                            var div = document.getElementById('eg-print-container-for-html');
-                            while (div.childNodes[0])
-                                div.removeChild(div.childNodes[0]);
-                            div.innerHTML = content;
+                   ['$scope','$q','$window','$timeout','egHatch', 
+            function($scope , $q , $window , $timeout , egHatch) {
+
+                egHatch.ingestPrintContent = function(type, content, printScope) {
+
+                    if (type == 'text/csv' || type == 'text/plain') {
+                        // preserve newlines, spaces, etc.
+                        content = '<pre>' + content + '</pre>';
                     }
 
-                    // force the template to absorb the data before printing
-                    // if an apply/digest loop is not already in progress
-                    //if (!$scope.$$phase) $scope.$apply();
-                    //
-                    // TODO: apply() may no longer be necessary
-
-                    $timeout(
-                        function() {
-                            $scope.$apply();
-                            $window.print();
-                        }
-                    );
+                    $scope.elm.html(content);
+
+                    var sub_scope = $scope.$new(true);
+                    angular.forEach(printScope, function(val, key) {
+                        sub_scope[key] = val;
+                    })
+
+                    var resp = $compile($scope.elm.contents())(sub_scope);
+
+                    var deferred = $q.defer();
+                    $timeout(function(){
+                        // give the $digest a chance to complete then
+                        // resolve with the compiled HTML from our
+                        // print container
+
+                        deferred.resolve(
+                            resp.contents()[0].parentNode.innerHTML
+                        );
+                    });
+
+                    return deferred.promise;
                 }
             }
         ]
     }
-});
-
+}])