View | Details | Raw Unified | Return to bug 40445
Collapse All | Expand All

(-)a/Koha/Cash/Register.pm (-5 / +5 lines)
Lines 289-298 sub start_cashup { Link Here
289
        Koha::Exceptions::Object::DuplicateID->throw( error => "A cashup is already in progress for this register" );
289
        Koha::Exceptions::Object::DuplicateID->throw( error => "A cashup is already in progress for this register" );
290
    }
290
    }
291
291
292
    my $expected_amount = abs( $self->outstanding_accountlines->total( { payment_type => [ 'CASH', 'SIP00' ] } ) );
292
    my $expected_amount = $self->outstanding_accountlines->total( { payment_type => [ 'CASH', 'SIP00' ] } ) * -1;
293
293
294
    # Prevent starting a cashup when there are no cash transactions
294
    # Prevent starting a cashup when there are no cash transactions
295
    unless ( $expected_amount > 0 ) {
295
    unless ( $expected_amount != 0 ) {
296
        Koha::Exceptions::Object::BadValue->throw(
296
        Koha::Exceptions::Object::BadValue->throw(
297
            error => "Cannot start cashup with zero cash transactions",
297
            error => "Cannot start cashup with zero cash transactions",
298
            type  => 'amount',
298
            type  => 'amount',
Lines 345-354 sub add_cashup { Link Here
345
    }
345
    }
346
    my $manager_id = $params->{manager_id};
346
    my $manager_id = $params->{manager_id};
347
347
348
    # Validate amount should always be a positive value
348
    # Validate amount is a non-zero number
349
    my $amount = $params->{amount};
349
    my $amount = $params->{amount};
350
    unless ( looks_like_number($amount) && $amount > 0 ) {
350
    unless ( looks_like_number($amount) && $amount != 0 ) {
351
        Koha::Exceptions::Account::AmountNotPositive->throw( error => 'Cashup amount passed is not positive number' );
351
        Koha::Exceptions::Account::AmountNotPositive->throw( error => 'Cashup amount must be a non-zero number' );
352
    }
352
    }
353
353
354
    # Sanitize reconciliation note - treat empty/whitespace-only as undef
354
    # Sanitize reconciliation note - treat empty/whitespace-only as undef
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/pos/register.tt (-13 / +81 lines)
Lines 447-453 Link Here
447
                            <li>
447
                            <li>
448
                                <span class="label">
448
                                <span class="label">
449
                                    [% IF cashup_in_progress %]
449
                                    [% IF cashup_in_progress %]
450
                                        Expected cashup amount:
450
                                        [% IF cashup_in_progress.amount < 0 %]
451
                                            Expected amount to add:
452
                                        [% ELSE %]
453
                                            Expected cashup amount:
454
                                        [% END %]
451
                                    [% ELSE %]
455
                                    [% ELSE %]
452
                                        Expected amount to remove:
456
                                        Expected amount to remove:
453
                                    [% END %]
457
                                    [% END %]
Lines 457-468 Link Here
457
                            <li>
461
                            <li>
458
                                <label class="required" for="amount">
462
                                <label class="required" for="amount">
459
                                    [% IF cashup_in_progress %]
463
                                    [% IF cashup_in_progress %]
460
                                        Actual cashup amount counted:
464
                                        [% IF cashup_in_progress.amount < 0 %]
465
                                            Actual amount added to register:
466
                                        [% ELSE %]
467
                                            Actual cashup amount counted:
468
                                        [% END %]
461
                                    [% ELSE %]
469
                                    [% ELSE %]
462
                                        Actual amount removed from register:
470
                                        Actual amount removed from register:
463
                                    [% END %]
471
                                    [% END %]
464
                                </label>
472
                                </label>
465
                                <input type="text" inputmode="decimal" pattern="^\d+(\.\d{2})?$" id="amount" name="amount" required="required" />
473
                                <input type="text" inputmode="decimal" pattern="^-?\d+(\.\d{2})?$" id="amount" name="amount" required="required" />
466
                                <span class="required">Required</span>
474
                                <span class="required">Required</span>
467
                            </li>
475
                            </li>
468
                            <li id="reconciliation_display" style="display: none;">
476
                            <li id="reconciliation_display" style="display: none;">
Lines 564-588 Link Here
564
                <div class="modal-body">
572
                <div class="modal-body">
565
                    <p><strong>Choose how to proceed with the cashup:</strong></p>
573
                    <p><strong>Choose how to proceed with the cashup:</strong></p>
566
                    <p><strong>Start cashup</strong></p>
574
                    <p><strong>Start cashup</strong></p>
567
                    <ul>
575
                    <ul id="start_cashup_instructions_individual">
568
                        <li>Remove cash from the register for counting</li>
576
                        <!-- JavaScript will populate this based on positive/negative amount -->
569
                        <li>The register can continue operating during counting</li>
570
                        <li>Complete the cashup once counted</li>
571
                    </ul>
577
                    </ul>
572
                    <p><strong>Quick cashup</strong></p>
578
                    <p><strong>Quick cashup</strong></p>
573
                    <ul>
579
                    <ul id="quick_cashup_instructions_individual">
574
                        <li>Confirm you have removed [% accountlines.total( payment_type => [ 'CASH', 'SIP00' ]) * -1 | $Price %] cash from the register to bank immediately</li>
580
                        <!-- JavaScript will populate this based on positive/negative amount -->
575
                    </ul>
581
                    </ul>
576
                    <p>Remember to leave the float amount of <strong>[% register.starting_float | $Price %]</strong> in the register.</p>
582
                    <p id="float_reminder_text_individual">
583
                        <!-- JavaScript will populate this based on positive/negative amount -->
584
                    </p>
577
                </div>
585
                </div>
578
                <div class="modal-footer">
586
                <div class="modal-footer">
579
                    <input type="hidden" name="registerid" value="[% register.id | html %]" />
587
                    <input type="hidden" name="registerid" value="[% register.id | html %]" />
580
                    <input type="hidden" name="op" value="cud-cashup_start" />
588
                    <input type="hidden" name="op" value="cud-cashup_start" />
581
                    <input type="hidden" name="amount" value="" />
589
                    <input type="hidden" name="amount" value="" />
590
                    <input type="hidden" id="register_bankable_amount" value="[% accountlines.total( payment_type => [ 'CASH', 'SIP00' ]) * -1 | html %]" />
582
                    <button type="submit" class="btn btn-primary">Start cashup</button>
591
                    <button type="submit" class="btn btn-primary">Start cashup</button>
583
                    <button type="button" class="btn btn-success" onclick="this.form.op.value='cud-cashup'; this.form.amount.value='[% accountlines.total( payment_type => [ 'CASH', 'SIP00' ]) * -1 | html %]'; this.form.submit();"
592
                    <button type="button" class="btn btn-success" id="quick_cashup_btn_individual">Quick cashup</button>
584
                        >Quick cashup</button
585
                    >
586
                    <button type="button" class="btn btn-default" data-bs-dismiss="modal">Cancel</button>
593
                    <button type="button" class="btn btn-default" data-bs-dismiss="modal">Cancel</button>
587
                </div>
594
                </div>
588
            </div>
595
            </div>
Lines 801-806 Link Here
801
            $("#reconciliation_note_field").hide();
808
            $("#reconciliation_note_field").hide();
802
            $("#reconciliation_note").val('');
809
            $("#reconciliation_note").val('');
803
        });
810
        });
811
812
        // Handle triggerCashupModal for individual register page
813
        $("#triggerCashupModal").on("shown.bs.modal", function(e){
814
           var bankableAmount = parseFloat($("#register_bankable_amount").val());
815
           var isNegative = bankableAmount < 0;
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
        });
804
    </script>
872
    </script>
805
[% END %]
873
[% END %]
806
874
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/pos/registers.tt (-17 / +77 lines)
Lines 272-289 Link Here
272
                <div class="modal-body">
272
                <div class="modal-body">
273
                    <p><strong>Choose how to proceed with the cashup:</strong></p>
273
                    <p><strong>Choose how to proceed with the cashup:</strong></p>
274
                    <p><strong>Start cashup</strong></p>
274
                    <p><strong>Start cashup</strong></p>
275
                    <ul>
275
                    <ul id="start_cashup_instructions">
276
                        <li>Remove cash from the register for counting</li>
276
                        <!-- JavaScript will populate this based on positive/negative amount -->
277
                        <li>The register can continue operating during counting</li>
278
                        <li>Complete the cashup once counted</li>
279
                    </ul>
277
                    </ul>
280
                    <p><strong>Quick cashup</strong></p>
278
                    <p><strong>Quick cashup</strong></p>
281
                    <ul>
279
                    <ul id="quick_cashup_instructions">
282
                        <li>Confirm you have removed <span id="expected_amount_display"></span> cash from the register to bank immediately</li>
280
                        <!-- JavaScript will populate this based on positive/negative amount -->
283
                    </ul>
281
                    </ul>
284
                    <p
282
                    <p id="float_reminder_text">
285
                        >Remember to leave the float amount of <strong><span id="float_amount_display"></span></strong> in the register.</p
283
                        <!-- JavaScript will populate this based on positive/negative amount -->
286
                    >
284
                    </p>
287
                </div>
285
                </div>
288
                <div class="modal-footer">
286
                <div class="modal-footer">
289
                    <input type="hidden" name="registerid" id="register_id_field" value="" />
287
                    <input type="hidden" name="registerid" id="register_id_field" value="" />
Lines 377-388 Link Here
377
                    <fieldset class="rows">
375
                    <fieldset class="rows">
378
                        <ol>
376
                        <ol>
379
                            <li>
377
                            <li>
380
                                <span class="label"> Expected cashup amount: </span>
378
                                <span class="label" id="expected_amount_label">Expected cashup amount:</span>
381
                                <span id="cashc" class="expected-amount"></span>
379
                                <span id="cashc" class="expected-amount"></span>
382
                            </li>
380
                            </li>
383
                            <li>
381
                            <li>
384
                                <label class="required" for="amount"> Actual cashup amount counted: </label>
382
                                <label class="required" for="amount" id="actual_amount_label">Actual cashup amount counted:</label>
385
                                <input type="text" inputmode="decimal" pattern="^\d+(\.\d{2})?$" id="amount" name="amount" required="required" />
383
                                <input type="text" inputmode="decimal" pattern="^-?\d+(\.\d{2})?$" id="amount" name="amount" required="required" />
386
                                <span class="required">Required</span>
384
                                <span class="required">Required</span>
387
                            </li>
385
                            </li>
388
                            <li id="reconciliation_display" style="display: none;">
386
                            <li id="reconciliation_display" style="display: none;">
Lines 518-523 Link Here
518
               $("#registerc").text(register);
516
               $("#registerc").text(register);
519
               var expected = button.data('expected');
517
               var expected = button.data('expected');
520
               $("#cashc").text(expected);
518
               $("#cashc").text(expected);
519
520
               // Parse expected amount to check if negative
521
               var expectedAmount = expected.replace(/[^0-9.-]/g, '');
522
               var isNegative = parseFloat(expectedAmount) < 0;
523
524
               // Update labels based on sign
525
               if (isNegative) {
526
                   $("#expected_amount_label").text(_("Expected amount to add:"));
527
                   $("#actual_amount_label").text(_("Actual amount added to register:"));
528
               } else {
529
                   $("#expected_amount_label").text(_("Expected cashup amount:"));
530
                   $("#actual_amount_label").text(_("Actual cashup amount counted:"));
531
               }
532
521
               var rfloat = button.data('float');
533
               var rfloat = button.data('float');
522
               $('#floatc').text(rfloat);
534
               $('#floatc').text(rfloat);
523
               var rid = button.data('registerid');
535
               var rid = button.data('registerid');
Lines 534-547 Link Here
534
               var register = button.data('register');
546
               var register = button.data('register');
535
               $("#register_desc").text(register);
547
               $("#register_desc").text(register);
536
               var bankable = button.data('bankable');
548
               var bankable = button.data('bankable');
537
               $("#expected_amount_display").text(bankable);
538
               var rfloat = button.data('float');
549
               var rfloat = button.data('float');
539
               $('#float_amount_display').text(rfloat);
540
               var rid = button.data('registerid');
550
               var rid = button.data('registerid');
541
               $('#register_id_field').val(rid);
551
               $('#register_id_field').val(rid);
542
552
543
               // Store bankable amount for quick cashup
553
               // Guard against undefined/null bankable value
544
               $('#triggerCashupModalRegister').data('bankable-amount', bankable.replace(/[^0-9.-]/g, ''));
554
               if (!bankable) {
555
                   console.error("Bankable amount is undefined");
556
                   return;
557
               }
558
559
               // Parse bankable amount (remove currency formatting, keep minus sign)
560
               var bankableAmount = String(bankable).replace(/[^0-9.-]/g, '');
561
               var numericAmount = parseFloat(bankableAmount);
562
               var isNegative = numericAmount < 0;
563
564
               // Format the absolute amount for display
565
               var absAmountFormatted = Math.abs(numericAmount).format_price();
566
567
               // Update Start cashup instructions
568
               if (isNegative) {
569
                   $("#start_cashup_instructions").html(
570
                       '<li>' + _("Count cash in the register") + '</li>' +
571
                       '<li>' + _("The register can continue operating during counting") + '</li>' +
572
                       '<li>' + _("Complete the cashup by adding cash to restore the float") + '</li>'
573
                   );
574
               } else {
575
                   $("#start_cashup_instructions").html(
576
                       '<li>' + _("Remove cash from the register for counting") + '</li>' +
577
                       '<li>' + _("The register can continue operating during counting") + '</li>' +
578
                       '<li>' + _("Complete the cashup once counted") + '</li>'
579
                   );
580
               }
581
582
               // Update Quick cashup instructions
583
               if (isNegative) {
584
                   $("#quick_cashup_instructions").html(
585
                       '<li>' + _("Top up the register with %s to restore the float").format(absAmountFormatted) + '</li>'
586
                   );
587
               } else {
588
                   $("#quick_cashup_instructions").html(
589
                       '<li>' + _("Confirm you have removed %s cash from the register to bank immediately").format(absAmountFormatted) + '</li>'
590
                   );
591
               }
592
593
               // Update float reminder
594
               if (isNegative) {
595
                   $("#float_reminder_text").html(
596
                       _("This will bring the register back to the expected float of <strong>%s</strong>").format(rfloat)
597
                   );
598
               } else {
599
                   $("#float_reminder_text").html(
600
                       _("Remember to leave the float amount of <strong>%s</strong> in the register.").format(rfloat)
601
                   );
602
               }
603
604
               // Store bankable amount for quick cashup (with sign)
605
               $('#triggerCashupModalRegister').data('bankable-amount', bankableAmount);
545
            });
606
            });
546
607
547
            // Handle Quick cashup button click
608
            // Handle Quick cashup button click
548
- 

Return to bug 40445