Bug 23354: Add receipting to Pay page
authorMartin Renvoize <martin.renvoize@ptfs-europe.com>
Tue, 17 Sep 2019 11:28:30 +0000 (12:28 +0100)
committerMartin Renvoize <martin.renvoize@ptfs-europe.com>
Mon, 13 Jan 2020 14:04:09 +0000 (14:04 +0000)
This patch adds receipt printing to the new Point of Sale pay page.

Test plan:
1) Apply patch and run database update
2) Enable automatic receipt printing via the `` system preference.
3) Make a payment for an item via the new POS pay page.
4) Note that a receipt printing dialogue is shown automatically after
   payment.
5) Note that a new notice is available under tools where you can alter
   the content of the receipt.
6) Signoff

Sponsored-by: PTFS Europe
Sponsored-by: Cheshire Libraries Shared Services
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Josef Moravec <josef.moravec@gmail.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

installer/data/mysql/atomicupdate/bug_23354.perl
installer/data/mysql/en/mandatory/sample_notices.sql
koha-tmpl/intranet-tmpl/prog/en/modules/pos/pay.tt
koha-tmpl/intranet-tmpl/prog/en/modules/pos/printreceipt.tt [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/tools/letter.tt
pos/pay.pl
pos/printreceipt.pl [new file with mode: 0755]

index 716cb57..edf3525 100644 (file)
@@ -5,6 +5,74 @@ if( CheckVersion( $DBversion ) ) {
         INSERT IGNORE INTO account_offset_types ( type ) VALUES ( 'Purchase' );
     });
 
+    $dbh->do(q{
+INSERT IGNORE INTO `letter` (`module`, `code`, `branchcode`, `name`, `is_html`, `title`, `content`, `message_transport_type`, `lang`) VALUES
+('pos', 'RECEIPT', '', 'Point of sale receipt', 0, 'Receipt', '<table>
+[% IF ( LibraryName ) %]
+ <tr>
+    <th colspan="2" class="centerednames">
+        <h3>[% LibraryName | html %]</h3>
+    </th>
+ </tr>
+[% END %]
+ <tr>
+    <th colspan="2" class="centerednames">
+        <h2>[% Branches.GetName( payment.branchcode ) | html %]</h2>
+    </th>
+ </tr>
+<tr>
+    <th colspan="2" class="centerednames">
+        <h3>[% payment.date | $KohaDates %]</h3>
+</tr>
+<tr>
+  <td>Transaction ID: </td>
+  <td>[% payment.accountlines_id %]</td>
+</tr>
+<tr>
+  <td>Operator ID: </td>
+  <td>[% payment.manager_id %]</td>
+</tr>
+<tr>
+  <td>Payment type: </td>
+  <td>[% payment.payment_type %]</td>
+</tr>
+ <tr></tr>
+ <tr>
+    <th colspan="2" class="centerednames">
+        <h2><u>Fee receipt</u></h2>
+    </th>
+ </tr>
+ <tr></tr>
+ <tr>
+    <th>Description of charges</th>
+    <th>Amount</th>
+  </tr>
+
+  [% FOREACH offset IN offsets %]
+    <tr>
+        <td>[% offset.debit.accounttype %]</td>
+        <td>[% offset.amount * -1 | $Price %]</td>
+    </tr>
+  [% END %]
+
+<tfoot>
+  <tr class="highlight">
+    <td>Total: </td>
+    <td>[% payment.amount * -1| $Price %]</td>
+  </tr>
+  <tr>
+    <td>Tendered: </td>
+    <td>[% collected | $Price %]</td>
+  </tr>
+  <tr>
+    <td>Change: </td>
+    <td>[% change | $Price %]</td>
+    </tr>
+</tfoot>
+</table>', 'print', 'default');
+    });
+
     SetVersion( $DBversion );
     print "Upgrade to $DBversion done (Bug 23354 - Add 'Purchase' account offset type)\n";
+    print "Upgrade to $DBversion done (Bug 23354 - Add 'RECEIPT' notice for Point of Sale)\n";
 }
index 6d676e7..a970969 100644 (file)
@@ -316,3 +316,68 @@ INSERT IGNORE INTO `letter` (`module`, `code`, `branchcode`, `name`, `is_html`,
 
 INSERT INTO `letter` (`module`, `code`, `branchcode`, `name`, `is_html`, `title`, `content`, `message_transport_type`) VALUES
 ('circulation', 'SR_SLIP', '', 'Stock rotation slip', 0, 'Stock rotation report', 'Stock rotation report for [% branch.name %]:\r\n\r\n[% IF branch.items.size %][% branch.items.size %] items to be processed for this branch.\r\n[% ELSE %]No items to be processed for this branch\r\n[% END %][% FOREACH item IN branch.items %][% IF item.reason != \'in-demand\' %]Title: [% item.title %]\r\nAuthor: [% item.author %]\r\nCallnumber: [% item.callnumber %]\r\nLocation: [% item.location %]\r\nBarcode: [% item.barcode %]\r\nOn loan?: [% item.onloan %]\r\nStatus: [% item.reason %]\r\nCurrent library: [% item.branch.branchname %] [% item.branch.branchcode %]\r\n\r\n[% END %][% END %]', 'email');
+
+INSERT IGNORE INTO `letter` (`module`, `code`, `branchcode`, `name`, `is_html`, `title`, `content`, `message_transport_type`, `lang`) VALUES
+('pos', 'RECEIPT', '', 'Point of sale receipt', 0, 'Receipt', '<table>
+[% IF ( LibraryName ) %]
+ <tr>
+    <th colspan="2" class="centerednames">
+        <h3>[% LibraryName | html %]</h3>
+    </th>
+ </tr>
+[% END %]
+ <tr>
+    <th colspan="2" class="centerednames">
+        <h2>[% Branches.GetName( payment.branchcode ) | html %]</h2>
+    </th>
+ </tr>
+<tr>
+    <th colspan="2" class="centerednames">
+        <h3>[% payment.date | $KohaDates %]</h3>
+</tr>
+<tr>
+  <td>Transaction ID: </td>
+  <td>[% payment.accountlines_id %]</td>
+</tr>
+<tr>
+  <td>Operator ID: </td>
+  <td>[% payment.manager_id %]</td>
+</tr>
+<tr>
+  <td>Payment type: </td>
+  <td>[% payment.payment_type %]</td>
+</tr>
+ <tr></tr>
+ <tr>
+    <th colspan="2" class="centerednames">
+        <h2><u>Fee receipt</u></h2>
+    </th>
+ </tr>
+ <tr></tr>
+ <tr>
+    <th>Description of charges</th>
+    <th>Amount</th>
+  </tr>
+
+  [% FOREACH offset IN offsets %]
+    <tr>
+        <td>[% offset.debit.accounttype %]</td>
+        <td>[% offset.amount * -1 | $Price %]</td>
+    </tr>
+  [% END %]
+
+<tfoot>
+  <tr class="highlight">
+    <td>Total: </td>
+    <td>[% payment.amount * -1| $Price %]</td>
+  </tr>
+  <tr>
+    <td>Tendered: </td>
+    <td>[% collected | $Price %]</td>
+  </tr>
+  <tr>
+    <td>Change: </td>
+    <td>[% change | $Price %]</td>
+    </tr>
+</tfoot>
+</table>', 'print', 'default');
index 33e0fb0..1946646 100644 (file)
                         <ol>
                             <li>
                                 <label for="paid">Amount being paid: </label>
-                                <input name="paid" id="paid" value="[% amountoutstanding | $Price on_editing => 1 %]"/>
+                                <input type="number" min="0.00" max="10000.00" step="0.01" name="paid" id="paid" value="[% amountoutstanding | $Price on_editing => 1 %]" readonly/>
                             </li>
                             <li>
                                 <label for="collected">Collected from patron: </label>
-                                <input id="collected" value="[% amountoutstanding | $Price on_editing => 1 %]"/>
+                                <input type="number" min="0.00" max="10000.00" step="0.01" name="collected" id="collected" value=""/>
                             </li>
                             <li>
                                 <label>Change to give: </label>
                                 <span id="change">0.00</span>
+                                <input type="hidden" name="change" value="0.00"/>
                             </li>
 
                             [% SET payment_types = AuthorisedValues.GetAuthValueDropbox('PAYMENT_TYPE') %]
             [% INCLUDE 'pos-menu.inc' %]
         </aside>
     </div>
-
 </div> <!-- /.row -->
 
+<!-- Modal -->
+<div id="confirm_change_form" class="modal" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h3>The amount collected is more than the outstanding charge</h3>
+            </div>
+            <div class="modal-body">
+                <p>The amount collected from the patron is higher than the amount to be paid.</p>
+                <p>The change to give is <b><span id="modal_change">0.00</span></b>.</p>
+                <p>Confirm this payment?</p>
+            </div>
+            <div class="modal-footer">
+                <button class="btn btn-default approve" id="modal_submit" type="button"><i class="fa fa-check"></i> Yes</button>
+                <button class="btn btn-default deny cancel" href="#" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times"></i> No</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+[% IF payment_id && Koha.Preference('FinePaymentAutoPopup') %]
+<!-- Automatic Print Reciept -->
+      <a id="printReciept" style="display: none" href="#"></a>
+[% END %]
+
 [% MACRO jsinclude BLOCK %]
     [% Asset.js("js/admin-menu.js") | $raw %]
     [% INCLUDE 'datatables.inc' %]
         var decFlag   = false;
         var aChar     = "";
 
-        for(i=0; i < newValue.length; i++) {
+        for(var i=0; i < newValue.length; i++) {
             aChar = newValue.substring(i, i+1);
             if (aChar >= "0" && aChar <= "9") {
                 if(decFlag) {
             moneyFormat(change);
             change.innerHTML = change.value;
         }
+
+        $(':input[name="change"]').val(change.value);
         $('#modal_change').html(change.innerHTML);
     }
 
                     return value;
                 },{
                     type    : 'text'
-                })
+                });
             },
             "fnRowCallback": function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
                 var iTotal = aData[1] * aData[2];
                 iTotalPrice = Number.parseFloat(iTotalPrice).toFixed(2);
                 nFoot.getElementsByTagName('td')[1].innerHTML = iTotalPrice;
                 $('#paid').val(iTotalPrice);
+                $('#paid').trigger('change');
             }
         }));
 
                "paginationType": "full",
         }));
 
-        $(".add_button").on("click", function(ev) {
-            ev.preventDefault();
+        $(".add_button").on("click", function(e) {
+            e.preventDefault();
             fnClickAddRow(sale_table, $( this ).data('invoiceTitle'), $( this ).data('invoicePrice') );
             items_table.fnFilter( '' );
         });
 
+        // Change calculation and modal
+        var change = $('#change')[0];
         $("#paid, #collected").on("change",function() {
             moneyFormat( this );
             if (change != undefined) {
             }
         });
 
-        $("#payForm").submit(function(e){
-            var rows = sale_table.fnGetData();
-            rows.forEach(function (row, index) {
-                var sale = {
-                    code: row[0],
-                    price: row[1],
-                    quantity: row[2]
-                };
-                $('<input>').attr({
-                    type: 'hidden',
-                    name: 'sales',
-                    value: JSON.stringify(sale)
-                }).appendTo('#payForm');
-            });
-            return true;
+        var checked = false;
+        $('#modal_submit').click(function() {
+            checked = true;
+            $('#payForm').submit();
         });
+
+        $('#payForm').submit(function(e){
+            if (change != undefined && change.innerHTML > 0.00 && !checked) {
+                e.preventDefault();
+                $("#confirm_change_form").modal("show");
+            } else {
+                var rows = sale_table.fnGetData();
+                rows.forEach(function (row, index) {
+                    var sale = {
+                        code: row[0],
+                        price: row[1],
+                        quantity: row[2]
+                    };
+                    $('<input>').attr({
+                        type: 'hidden',
+                        name: 'sales',
+                        value: JSON.stringify(sale)
+                    }).appendTo('#payForm');
+                });
+                return true;
+            }
+        });
+
+        [% IF payment_id && Koha.Preference('FinePaymentAutoPopup') %]
+            $("#printReciept").click(function() {
+                var win = window.open('/cgi-bin/koha/pos/printreceipt.pl?action=print&accountlines_id=[% payment_id | uri %]&collected=[% collected | uri %]&change=[% change | uri %]', '_blank');
+                win.focus();
+            });
+            $("#printReciept").click();
+        [% END %]
     });
     </script>
 [% END %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/pos/printreceipt.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/pos/printreceipt.tt
new file mode 100644 (file)
index 0000000..4aa936d
--- /dev/null
@@ -0,0 +1,26 @@
+[% USE raw %]
+[% USE Asset %]
+[% USE Koha %]
+[% USE KohaDates %]
+[% USE Branches %]
+[% USE Price %]
+[% SET footerjs = 1 %]
+
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Print receipt</title>
+[% INCLUDE 'doc-head-close.inc' %]
+[% Asset.css("css/printreceiptinvoice.css") | $raw %]
+[% INCLUDE 'blocking_errors.inc' %]
+</head>
+
+<body id="pat_printfeercpt" class="pat">
+
+<div id="receipt">
+    [% letter.content | $raw | evaltt %]
+</div>
+
+[% MACRO jsinclude BLOCK %]
+    [% INCLUDE 'slip-print.inc' #printThenClose %]
+[% END %]
+
+[% INCLUDE 'intranet-bottom.inc' %]
index 0845c45..313e44b 100644 (file)
                                 <li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&amp;module=members">Patrons</a></li>
                                 <li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&amp;module=serial">Serials (new issue)</a></li>
                                 <li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&amp;module=suggestions">Suggestions</a></li>
-
+                                <li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&amp;module=pos">Point of sale</a></li>
                             </ul>
                         </div>
                     </div> <!-- /#toolbar -->
                                                 [% CASE 'members' %]<span>Patrons</span>
                                                 [% CASE 'serial' %]<span>Serials (new issue)</span>
                                                 [% CASE 'suggestions' %]<span>Suggestions</span>
+                                                [% CASE 'pos' %]<span>Point of sale</span>
                                                 [% CASE %]<span>[% lette.module | html %]</span>
                                             [% END %]
                                         </td>
                                     [% ELSE %]
                                         <option value="suggestions">Suggestions</option>
                                     [% END %]
+                                    [% IF ( module == "pos" ) %]
+                                        <option value="pos" selected="selected">Point of sale</option>
+                                    [% ELSE %]
+                                        <option value="pos">Point of sale</option>
+                                    [% END %]
                                 </select>
                             </li>
                             <li>
index 18d968d..9903459 100755 (executable)
@@ -55,7 +55,6 @@ else {
 
 my $total_paid = $q->param('paid');
 if ( $total_paid and $total_paid ne '0.00' ) {
-    warn "total_paid: $total_paid\n";
     my $cash_register = Koha::Cash::Registers->find( { id => $registerid } );
     my $payment_type  = $q->param('payment_type');
     my $sale          = Koha::Charges::Sales->new(
@@ -71,7 +70,13 @@ if ( $total_paid and $total_paid ne '0.00' ) {
         $sale->add_item($item);
     }
 
-    $sale->purchase( { payment_type => $payment_type } );
+    my $payment = $sale->purchase( { payment_type => $payment_type } );
+
+    $template->param(
+        payment_id => $payment->accountlines_id,
+        collected  => scalar $q->param('collected'),
+        change     => scalar $q->param('change')
+    );
 }
 
 output_html_with_http_headers( $q, $cookie, $template->output );
diff --git a/pos/printreceipt.pl b/pos/printreceipt.pl
new file mode 100755 (executable)
index 0000000..ae53136
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/perl
+
+# Copyright 2019 PTFS Europe
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Koha is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Koha; if not, see <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+
+use C4::Auth qw/:DEFAULT get_session/;
+use C4::Output;
+use CGI qw ( -utf8 );
+use C4::Letters;
+use Koha::Account::Lines;
+use Koha::DateUtils;
+
+my $input = CGI->new;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "pos/printreceipt.tt",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+    }
+);
+
+my $action = $input->param('action') || '';
+my $payment_id = $input->param('accountlines_id');
+
+my $logged_in_user = Koha::Patrons->find($loggedinuser) or die "Not logged in";
+output_and_exit_if_error(
+    $input, $cookie,
+    $template,
+    {
+        module         => 'pos',
+        logged_in_user => $logged_in_user,
+    }
+);
+
+my $payment = Koha::Account::Lines->find($payment_id);
+my @offsets = Koha::Account::Offsets->search( { credit_id => $payment_id } );
+
+my $letter =
+  C4::Letters::getletter( 'pos', 'RECEIPT', C4::Context::mybranch, 'print' );
+
+$template->param(
+    letter    => $letter,
+    payment   => $payment,
+    offsets   => \@offsets,
+    collected => scalar $input->param('collected'),
+    change    => scalar $input->param('change')
+);
+
+output_html_with_http_headers $input, $cookie, $template->output;