|
Lines 114-120
Link Here
|
| 114 |
[% IF cashup_in_progress %] |
114 |
[% IF cashup_in_progress %] |
| 115 |
<button id="pos_complete_cashup" type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#confirmCashupModal"> <i class="fa-solid fa-check"></i> Complete cashup </button> |
115 |
<button id="pos_complete_cashup" type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#confirmCashupModal"> <i class="fa-solid fa-check"></i> Complete cashup </button> |
| 116 |
[% ELSE %] |
116 |
[% ELSE %] |
| 117 |
<button type="button" class="btn btn-default" data-bs-toggle="modal" data-bs-target="#triggerCashupModal" [% IF total_bankable == 0 %]disabled title="No bankable transactions"[% END %]> <i class="fa fa-money-bill-alt"></i> Record cashup </button> |
117 |
<button type="button" class="btn btn-default" data-bs-toggle="modal" data-bs-target="#triggerCashupModal" [% IF total_bankable == 0 %]disabled title="No bankable transactions"[% END %]> |
|
|
118 |
<i class="fa fa-money-bill-alt"></i> Record cashup |
| 119 |
</button> |
| 118 |
[% END %] |
120 |
[% END %] |
| 119 |
</div> |
121 |
</div> |
| 120 |
[% END %] |
122 |
[% END %] |
|
Lines 425-521
Link Here
|
| 425 |
[% END %] |
427 |
[% END %] |
| 426 |
[% END %] |
428 |
[% END %] |
| 427 |
|
429 |
|
| 428 |
<!-- Confirm cashup modal --> |
|
|
| 429 |
<div class="modal" id="confirmCashupModal" tabindex="-1" role="dialog" aria-labelledby="confirmCashupLabel"> |
| 430 |
<form id="cashup_form" method="post" enctype="multipart/form-data" class="validated"> |
| 431 |
[% INCLUDE 'csrf-token.inc' %] |
| 432 |
<div class="modal-dialog"> |
| 433 |
<div class="modal-content"> |
| 434 |
<div class="modal-header"> |
| 435 |
<h1 class="modal-title" id="confirmCashupLabel"> |
| 436 |
[% IF cashup_in_progress %] |
| 437 |
Complete cashup of <em>[% register.description | html %]</em> |
| 438 |
[% ELSE %] |
| 439 |
Confirm cashup of <em>[% register.description | html %]</em> |
| 440 |
[% END %] |
| 441 |
</h1> |
| 442 |
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
| 443 |
</div> |
| 444 |
<div class="modal-body"> |
| 445 |
<fieldset class="rows"> |
| 446 |
<ol> |
| 447 |
<li> |
| 448 |
<span class="label"> |
| 449 |
[% IF cashup_in_progress %] |
| 450 |
[% IF cashup_in_progress.amount < 0 %] |
| 451 |
Expected amount to add: |
| 452 |
[% ELSE %] |
| 453 |
Expected cashup amount: |
| 454 |
[% END %] |
| 455 |
[% ELSE %] |
| 456 |
Expected amount to remove: |
| 457 |
[% END %] |
| 458 |
</span> |
| 459 |
<span id="expected_amount" class="expected-amount">[% cashup_in_progress.amount | $Price %]</span> |
| 460 |
</li> |
| 461 |
<li> |
| 462 |
<label class="required" for="amount"> |
| 463 |
[% IF cashup_in_progress %] |
| 464 |
[% IF cashup_in_progress.amount < 0 %] |
| 465 |
Actual amount added to register: |
| 466 |
[% ELSE %] |
| 467 |
Actual cashup amount counted: |
| 468 |
[% END %] |
| 469 |
[% ELSE %] |
| 470 |
Actual amount removed from register: |
| 471 |
[% END %] |
| 472 |
</label> |
| 473 |
<input type="text" inputmode="decimal" pattern="^-?\d+(\.\d{2})?$" id="amount" name="amount" required="required" /> |
| 474 |
<span class="required">Required</span> |
| 475 |
</li> |
| 476 |
<li id="reconciliation_display" style="display: none;"> |
| 477 |
<span class="label">Reconciliation:</span> |
| 478 |
<span id="reconciliation_text"></span> |
| 479 |
</li> |
| 480 |
<li id="reconciliation_note_field" style="display: none;"> |
| 481 |
<label for="reconciliation_note" id="reconciliation_note_label">Note:</label> |
| 482 |
[% IF reconciliation_note_avs %] |
| 483 |
<select id="reconciliation_note" name="reconciliation_note"> |
| 484 |
<option value="">-- Select a reason --</option> |
| 485 |
[% FOREACH av IN reconciliation_note_avs %] |
| 486 |
<option value="[% av.authorised_value | html %]">[% av.lib | html %]</option> |
| 487 |
[% END %] |
| 488 |
</select> |
| 489 |
[% IF reconciliation_note_required %] |
| 490 |
<span class="required">Required</span> |
| 491 |
[% END %] |
| 492 |
[% ELSE %] |
| 493 |
<textarea id="reconciliation_note" name="reconciliation_note" rows="3" cols="40" maxlength="1000" placeholder="Enter a note explaining the surplus or deficit..."></textarea> |
| 494 |
[% IF reconciliation_note_required %] |
| 495 |
<span class="required">Required</span> |
| 496 |
[% END %] |
| 497 |
<div class="hint">Maximum 1000 characters</div> |
| 498 |
[% END %] |
| 499 |
</li> |
| 500 |
</ol> |
| 501 |
</fieldset> |
| 502 |
</div> |
| 503 |
<!-- /.modal-body --> |
| 504 |
<div class="modal-footer"> |
| 505 |
<input type="hidden" name="registerid" value="[% register.id | html %]" /> |
| 506 |
<input type="hidden" name="op" value="cud-cashup" /> |
| 507 |
<button type="submit" class="btn btn-primary" id="pos_cashup_confirm"> [% IF cashup_in_progress %]Complete cashup[% ELSE %]Confirm cashup[% END %] </button> |
| 508 |
<button type="button" class="btn btn-default" data-bs-dismiss="modal">Cancel</button> |
| 509 |
</div> |
| 510 |
<!-- /.modal-footer --> |
| 511 |
</div> |
| 512 |
<!-- /.modal-content --> |
| 513 |
</div> |
| 514 |
<!-- /.modal-dialog --> |
| 515 |
</form> |
| 516 |
</div> |
| 517 |
<!-- /#confirmCashupModal --> |
| 518 |
|
| 519 |
<!-- Issue refund modal --> |
430 |
<!-- Issue refund modal --> |
| 520 |
<div class="modal" id="issueRefundModal" tabindex="-1" role="dialog" aria-labelledby="issueRefundLabel"> |
431 |
<div class="modal" id="issueRefundModal" tabindex="-1" role="dialog" aria-labelledby="issueRefundLabel"> |
| 521 |
<form id="refund_form" method="post" enctype="multipart/form-data" class="validated"> |
432 |
<form id="refund_form" method="post" enctype="multipart/form-data" class="validated"> |
|
Lines 559-620
Link Here
|
| 559 |
</div> |
470 |
</div> |
| 560 |
<!-- /#issueRefundModal --> |
471 |
<!-- /#issueRefundModal --> |
| 561 |
|
472 |
|
| 562 |
<!-- Trigger cashup modal --> |
|
|
| 563 |
<div class="modal" id="triggerCashupModal" tabindex="-1" role="dialog" aria-labelledby="triggerCashupLabel"> |
| 564 |
<form method="post" class="validated"> |
| 565 |
[% INCLUDE 'csrf-token.inc' %] |
| 566 |
<div class="modal-dialog"> |
| 567 |
<div class="modal-content"> |
| 568 |
<div class="modal-header"> |
| 569 |
<h1 class="modal-title" id="triggerCashupLabel"> Cashup for <em>[% register.description | html %]</em> </h1> |
| 570 |
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> |
| 571 |
</div> |
| 572 |
<div class="modal-body"> |
| 573 |
<p><strong>Choose how to proceed with the cashup:</strong></p> |
| 574 |
<p><strong>Start cashup</strong></p> |
| 575 |
<ul id="start_cashup_instructions_individual"> |
| 576 |
<!-- JavaScript will populate this based on positive/negative amount --> |
| 577 |
</ul> |
| 578 |
<p><strong>Quick cashup</strong></p> |
| 579 |
<ul id="quick_cashup_instructions_individual"> |
| 580 |
<!-- JavaScript will populate this based on positive/negative amount --> |
| 581 |
</ul> |
| 582 |
<p id="float_reminder_text_individual"> |
| 583 |
<!-- JavaScript will populate this based on positive/negative amount --> |
| 584 |
</p> |
| 585 |
</div> |
| 586 |
<div class="modal-footer"> |
| 587 |
<input type="hidden" name="registerid" value="[% register.id | html %]" /> |
| 588 |
<input type="hidden" name="op" value="cud-cashup_start" /> |
| 589 |
<input type="hidden" name="amount" value="" /> |
| 590 |
<input type="hidden" id="register_bankable_amount" value="[% accountlines.total( payment_type => [ 'CASH', 'SIP00' ]) * -1 | html %]" /> |
| 591 |
<button type="submit" class="btn btn-primary">Start cashup</button> |
| 592 |
<button type="button" class="btn btn-success" id="quick_cashup_btn_individual">Quick cashup</button> |
| 593 |
<button type="button" class="btn btn-default" data-bs-dismiss="modal">Cancel</button> |
| 594 |
</div> |
| 595 |
</div> |
| 596 |
</div> |
| 597 |
</form> |
| 598 |
</div> |
| 599 |
<!-- /#triggerCashupModal --> |
| 600 |
|
| 601 |
[% INCLUDE 'modals/cashup_summary.inc' %] |
473 |
[% INCLUDE 'modals/cashup_summary.inc' %] |
|
|
474 |
[% |
| 475 |
INCLUDE 'modals/trigger_cashup.inc' |
| 476 |
modal_id='triggerCashupModal' |
| 477 |
register_description=register.description |
| 478 |
register_id=register.id |
| 479 |
%] |
| 480 |
[% |
| 481 |
INCLUDE 'modals/confirm_cashup.inc' |
| 482 |
modal_id='confirmCashupModal' |
| 483 |
reconciliation_note_avs=reconciliation_note_avs |
| 484 |
reconciliation_note_required=reconciliation_note_required |
| 485 |
cashup_in_progress=cashup_in_progress |
| 486 |
%] |
| 602 |
|
487 |
|
| 603 |
[% MACRO jsinclude BLOCK %] |
488 |
[% MACRO jsinclude BLOCK %] |
| 604 |
[% INCLUDE 'datatables.inc' %] |
489 |
[% INCLUDE 'datatables.inc' %] |
| 605 |
[% INCLUDE 'format_price.inc' %] |
490 |
[% INCLUDE 'format_price.inc' %] |
| 606 |
[% INCLUDE 'js-date-format.inc' %] |
491 |
[% INCLUDE 'js-date-format.inc' %] |
| 607 |
<script> |
|
|
| 608 |
// Map of authorized value codes to descriptions for reconciliation notes |
| 609 |
var reconciliation_note_avs = { |
| 610 |
[% IF reconciliation_note_avs %] |
| 611 |
[% FOREACH av IN reconciliation_note_avs %] |
| 612 |
"[% av.authorised_value | html %]": "[% av.lib | html %]"[% UNLESS loop.last %],[% END %] |
| 613 |
[% END %] |
| 614 |
[% END %] |
| 615 |
}; |
| 616 |
</script> |
| 617 |
[% Asset.js("js/cashup_modal.js") | $raw %] |
492 |
[% Asset.js("js/cashup_modal.js") | $raw %] |
|
|
493 |
[% Asset.js("js/modals/cashup_modals.js") | $raw %] |
| 618 |
[% Asset.js("js/modal_printer.js") | $raw %] |
494 |
[% Asset.js("js/modal_printer.js") | $raw %] |
| 619 |
[% INCLUDE 'calendar.inc' %] |
495 |
[% INCLUDE 'calendar.inc' %] |
| 620 |
<script> |
496 |
<script> |
|
Lines 746-873
Link Here
|
| 746 |
] |
622 |
] |
| 747 |
}, null, 1); |
623 |
}, null, 1); |
| 748 |
|
624 |
|
| 749 |
// Real-time reconciliation calculation for cashup modal |
625 |
// Initialize cashup modals |
| 750 |
$("#amount").on("input", function() { |
626 |
initTriggerCashupModal('#triggerCashupModal', { |
| 751 |
var actualAmount = parseFloat($(this).val()) || 0; |
627 |
bankableAmount: [% accountlines.total( payment_type => [ 'CASH', 'SIP00' ]) * -1 | html %], |
| 752 |
var expectedText = $("#expected_amount").text().replace(/[£$,]/g, ''); |
628 |
registerFloat: [% register.starting_float | html %] |
| 753 |
var expectedAmount = parseFloat(expectedText) || 0; |
|
|
| 754 |
var difference = actualAmount - expectedAmount; |
| 755 |
|
| 756 |
if ($(this).val() && !isNaN(actualAmount)) { |
| 757 |
var reconciliationText = ""; |
| 758 |
var reconciliationClass = ""; |
| 759 |
var hasDiscrepancy = false; |
| 760 |
|
| 761 |
if (difference > 0) { |
| 762 |
reconciliationText = "Surplus: " + difference.format_price(); |
| 763 |
reconciliationClass = "success"; |
| 764 |
hasDiscrepancy = true; |
| 765 |
} else if (difference < 0) { |
| 766 |
reconciliationText = "Deficit: " + Math.abs(difference).format_price(); |
| 767 |
reconciliationClass = "warning"; |
| 768 |
hasDiscrepancy = true; |
| 769 |
} else { |
| 770 |
reconciliationText = "Balanced - no surplus or deficit"; |
| 771 |
reconciliationClass = "success"; |
| 772 |
hasDiscrepancy = false; |
| 773 |
} |
| 774 |
|
| 775 |
$("#reconciliation_text").text(reconciliationText) |
| 776 |
.removeClass("success warning") |
| 777 |
.addClass(reconciliationClass); |
| 778 |
$("#reconciliation_display").show(); |
| 779 |
|
| 780 |
// Show/hide note field based on whether there's a discrepancy |
| 781 |
if (hasDiscrepancy) { |
| 782 |
$("#reconciliation_note_field").show(); |
| 783 |
// Update required attribute and label based on system preference |
| 784 |
[% IF reconciliation_note_required %] |
| 785 |
$("#reconciliation_note").attr('required', 'required'); |
| 786 |
$("#reconciliation_note_label").addClass("required"); |
| 787 |
[% ELSE %] |
| 788 |
$("#reconciliation_note").removeAttr('required'); |
| 789 |
$("#reconciliation_note_label").removeClass("required"); |
| 790 |
[% END %] |
| 791 |
} else { |
| 792 |
$("#reconciliation_note_field").hide(); |
| 793 |
$("#reconciliation_note").val(''); // Clear note when balanced |
| 794 |
$("#reconciliation_note").removeAttr('required'); |
| 795 |
$("#reconciliation_note_label").removeClass("required"); |
| 796 |
} |
| 797 |
} else { |
| 798 |
$("#reconciliation_display").hide(); |
| 799 |
$("#reconciliation_note_field").hide(); |
| 800 |
} |
| 801 |
}); |
| 802 |
|
| 803 |
// Reset modal when opened |
| 804 |
$("#confirmCashupModal").on("shown.bs.modal", function() { |
| 805 |
// Start with empty actual amount field (user must enter amount) |
| 806 |
$("#amount").val('').focus(); |
| 807 |
$("#reconciliation_display").hide(); |
| 808 |
$("#reconciliation_note_field").hide(); |
| 809 |
$("#reconciliation_note").val(''); |
| 810 |
}); |
629 |
}); |
| 811 |
|
630 |
|
| 812 |
// Handle triggerCashupModal for individual register page |
631 |
initConfirmCashupModal('#confirmCashupModal', { |
| 813 |
$("#triggerCashupModal").on("shown.bs.modal", function(e){ |
632 |
hasAuthorisedValues: [% reconciliation_note_avs ? 'true' : 'false' %], |
| 814 |
var bankableAmount = parseFloat($("#register_bankable_amount").val()); |
633 |
noteRequired: [% reconciliation_note_required ? 'true' : 'false' %], |
| 815 |
var isNegative = bankableAmount < 0; |
634 |
isInProgress: [% cashup_in_progress ? 'true' : 'false' %] |
| 816 |
var formattedAmount = Math.abs(bankableAmount).format_price(); |
|
|
| 817 |
var floatAmount = parseFloat([% register.starting_float | html %]).format_price(); |
| 818 |
|
| 819 |
// Update Start cashup instructions |
| 820 |
if (isNegative) { |
| 821 |
$("#start_cashup_instructions_individual").html( |
| 822 |
'<li>' + _("Count cash in the register") + '</li>' + |
| 823 |
'<li>' + _("The register can continue operating during counting") + '</li>' + |
| 824 |
'<li>' + _("Complete the cashup by adding cash to restore the float") + '</li>' |
| 825 |
); |
| 826 |
} else { |
| 827 |
$("#start_cashup_instructions_individual").html( |
| 828 |
'<li>' + _("Remove cash from the register for counting") + '</li>' + |
| 829 |
'<li>' + _("The register can continue operating during counting") + '</li>' + |
| 830 |
'<li>' + _("Complete the cashup once counted") + '</li>' |
| 831 |
); |
| 832 |
} |
| 833 |
|
| 834 |
// Update Quick cashup instructions |
| 835 |
if (isNegative) { |
| 836 |
$("#quick_cashup_instructions_individual").html( |
| 837 |
'<li>' + _("Top up the register with %s to restore the float").format(formattedAmount) + '</li>' |
| 838 |
); |
| 839 |
} else { |
| 840 |
$("#quick_cashup_instructions_individual").html( |
| 841 |
'<li>' + _("Confirm you have removed %s cash from the register to bank immediately").format(formattedAmount) + '</li>' |
| 842 |
); |
| 843 |
} |
| 844 |
|
| 845 |
// Update float reminder |
| 846 |
if (isNegative) { |
| 847 |
$("#float_reminder_text_individual").html( |
| 848 |
_("This will bring the register back to the expected float of <strong>%s</strong>").format(floatAmount) |
| 849 |
); |
| 850 |
} else { |
| 851 |
$("#float_reminder_text_individual").html( |
| 852 |
_("Remember to leave the float amount of <strong>%s</strong> in the register.").format(floatAmount) |
| 853 |
); |
| 854 |
} |
| 855 |
}); |
| 856 |
|
| 857 |
// Handle Quick cashup button click for individual register |
| 858 |
$("#quick_cashup_btn_individual").on("click", function(e){ |
| 859 |
e.preventDefault(); |
| 860 |
var form = $(this).closest('form'); |
| 861 |
var bankableAmount = $("#register_bankable_amount").val(); |
| 862 |
|
| 863 |
// Change operation to cud-cashup (quick cashup) |
| 864 |
form.find('input[name="op"]').val('cud-cashup'); |
| 865 |
|
| 866 |
// Set the amount to the expected bankable amount |
| 867 |
form.find('input[name="amount"]').val(bankableAmount); |
| 868 |
|
| 869 |
// Submit the form |
| 870 |
form.submit(); |
| 871 |
}); |
635 |
}); |
| 872 |
</script> |
636 |
</script> |
| 873 |
[% END %] |
637 |
[% END %] |