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>
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";
}
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');
<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 %]
--- /dev/null
+[% 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' %]
<li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&module=members">Patrons</a></li>
<li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&module=serial">Serials (new issue)</a></li>
<li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&module=suggestions">Suggestions</a></li>
-
+ <li><a href="/cgi-bin/koha/tools/letter.pl?op=add_form&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>
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(
$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 );
--- /dev/null
+#!/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;