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

(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/pos/registers.tt (-17 / +177 lines)
Lines 112-128 Link Here
112
                            </td>
112
                            </td>
113
                            [% IF CAN_user_cash_management_cashup %]
113
                            [% IF CAN_user_cash_management_cashup %]
114
                                <td>
114
                                <td>
115
                                    <button
115
                                    [% IF register.cashup_in_progress %]
116
                                        type="button"
116
                                        <button
117
                                        class="cashup_individual btn btn-xs btn-default"
117
                                            type="button"
118
                                        data-bs-toggle="modal"
118
                                            class="btn btn-xs btn-primary pos_complete_cashup"
119
                                        data-bs-target="#confirmCashupModal"
119
                                            data-bs-toggle="modal"
120
                                        data-register="[% register.description | html %]"
120
                                            data-bs-target="#confirmCashupModal"
121
                                        data-bankable="[% rbankable | $Price %]"
121
                                            data-register="[% register.description | html %]"
122
                                        data-float="[% register.starting_float | $Price %]"
122
                                            data-bankable="[% rbankable | $Price %]"
123
                                        data-registerid="[% register.id | html %]"
123
                                            data-float="[% register.starting_float | $Price %]"
124
                                        ><i class="fa-solid fa-money-bill-1"></i> Record cashup</button
124
                                            data-registerid="[% register.id | html %]"
125
                                    >
125
                                            ><i class="fa-solid fa-check"></i> Complete cashup</button
126
                                        >
127
                                    [% ELSE %]
128
                                        <button
129
                                            type="button"
130
                                            class="cashup_individual btn btn-xs btn-default"
131
                                            data-bs-toggle="modal"
132
                                            data-bs-target="#triggerCashupModalRegister"
133
                                            data-register="[% register.description | html %]"
134
                                            data-bankable="[% rbankable | $Price %]"
135
                                            data-float="[% register.starting_float | $Price %]"
136
                                            data-registerid="[% register.id | html %]"
137
                                            ><i class="fa-solid fa-money-bill-1"></i> Record cashup</button
138
                                        >
139
                                    [% END %]
126
                                </td>
140
                                </td>
127
                            [% END %]
141
                            [% END %]
128
                        </tr>
142
                        </tr>
Lines 147-170 Link Here
147
    [% END %]
161
    [% END %]
148
[% END %]
162
[% END %]
149
163
164
<!-- Trigger cashup modal for individual registers -->
165
<div class="modal" id="triggerCashupModalRegister" tabindex="-1" role="dialog" aria-labelledby="triggerCashupLabelRegister">
166
    <form method="post" class="validated">
167
        [% INCLUDE 'csrf-token.inc' %]
168
        <div class="modal-dialog">
169
            <div class="modal-content">
170
                <div class="modal-header">
171
                    <h1 class="modal-title" id="triggerCashupLabelRegister">
172
                        Cashup for <em><span id="register_desc"></span></em>
173
                    </h1>
174
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
175
                </div>
176
                <div class="modal-body">
177
                    <p><strong>Choose how to proceed with the cashup:</strong></p>
178
                    <p><strong>Start cashup</strong></p>
179
                    <ul>
180
                        <li>Remove cash from the register for counting</li>
181
                        <li>The register can continue operating during counting</li>
182
                        <li>Complete the cashup once counted</li>
183
                    </ul>
184
                    <p><strong>Quick cashup</strong></p>
185
                    <ul>
186
                        <li>Confirm you have removed <span id="expected_amount_display"></span> cash from the register to bank immediately</li>
187
                    </ul>
188
                    <p
189
                        >Remember to leave the float amount of <strong><span id="float_amount_display"></span></strong> in the register.</p
190
                    >
191
                </div>
192
                <div class="modal-footer">
193
                    <input type="hidden" name="registerid" id="register_id_field" value="" />
194
                    <input type="hidden" name="op" value="cud-cashup_start" />
195
                    <input type="hidden" name="amount" value="" />
196
                    <button type="submit" class="btn btn-primary">Start cashup</button>
197
                    <button type="button" class="btn btn-success" id="quick_cashup_btn">Quick cashup</button>
198
                    <button type="button" class="btn btn-default" data-bs-dismiss="modal">Cancel</button>
199
                </div>
200
            </div>
201
        </div>
202
    </form>
203
</div>
204
<!-- /#triggerCashupModalRegister -->
205
150
<!-- Confirm cashup modal -->
206
<!-- Confirm cashup modal -->
151
<div class="modal" id="confirmCashupModal" tabindex="-1" role="dialog" aria-labelledby="confirmCashupLabel">
207
<div class="modal" id="confirmCashupModal" tabindex="-1" role="dialog" aria-labelledby="confirmCashupLabel">
152
    <form id="cashup_individual_form" method="post" enctype="multipart/form-data">
208
    <form id="cashup_individual_form" method="post" enctype="multipart/form-data" class="validated">
153
        [% INCLUDE 'csrf-token.inc' %]
209
        [% INCLUDE 'csrf-token.inc' %]
154
        <div class="modal-dialog">
210
        <div class="modal-dialog">
155
            <div class="modal-content">
211
            <div class="modal-content">
156
                <div class="modal-header">
212
                <div class="modal-header">
157
                    <h1 class="modal-title" id="confirmCashupLabel"
213
                    <h1 class="modal-title" id="confirmCashupLabel">
158
                        >Confirm cashup of <em><span id="registerc"></span></em
214
                        Confirm cashup of <em><span id="registerc"></span></em>
159
                    ></h1>
215
                    </h1>
160
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
216
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
161
                </div>
217
                </div>
162
                <div class="modal-body"> Please confirm that you have removed <span id="cashc"></span> from the cash register and left a float of <span id="floatc"></span>. </div>
218
                <div class="modal-body">
219
                    <fieldset class="rows">
220
                        <ol>
221
                            <li>
222
                                <span class="label"> Expected cashup amount: </span>
223
                                <span id="cashc" class="expected-amount"></span>
224
                            </li>
225
                            <li>
226
                                <label class="required" for="amount"> Actual cashup amount counted: </label>
227
                                <input type="text" inputmode="decimal" pattern="^\d+(\.\d{2})?$" id="amount" name="amount" required="required" />
228
                                <span class="required">Required</span>
229
                            </li>
230
                            <li id="reconciliation_display" style="display: none;">
231
                                <span class="label">Reconciliation:</span>
232
                                <span id="reconciliation_text"></span>
233
                            </li>
234
                            <li id="reconciliation_note_field" style="display: none;">
235
                                <label for="reconciliation_note">Note (optional):</label>
236
                                <textarea id="reconciliation_note" name="reconciliation_note" rows="3" cols="40" maxlength="1000" placeholder="Enter a note explaining the surplus or deficit..."></textarea>
237
                                <div class="hint">Maximum 1000 characters</div>
238
                            </li>
239
                        </ol>
240
                    </fieldset>
241
                </div>
163
                <!-- /.modal-body -->
242
                <!-- /.modal-body -->
164
                <div class="modal-footer">
243
                <div class="modal-footer">
165
                    <input type="hidden" name="registerid" id="cashup_registerid" value="" />
244
                    <input type="hidden" name="registerid" id="cashup_registerid" value="" />
166
                    <input type="hidden" name="op" value="cud-cashup" />
245
                    <input type="hidden" name="op" value="cud-cashup" />
167
                    <button type="submit" class="btn btn-primary" id="cashup_confirm">Confirm</button>
246
                    <button type="submit" class="btn btn-primary" id="cashup_confirm"> Complete cashup</button>
168
                    <button type="button" class="btn btn-default" data-bs-dismiss="modal">Cancel</button>
247
                    <button type="button" class="btn btn-default" data-bs-dismiss="modal">Cancel</button>
169
                </div>
248
                </div>
170
                <!-- /.modal-footer -->
249
                <!-- /.modal-footer -->
Lines 229-234 Link Here
229
                paginationType: "full",
308
                paginationType: "full",
230
            });
309
            });
231
310
311
            // Real-time reconciliation calculation for cashup modal
312
            $("#amount").on("input", function() {
313
                var actualAmount = parseFloat($(this).val()) || 0;
314
                var expectedText = $("#expected_amount").text().replace(/[£$,]/g, '');
315
                var expectedAmount = parseFloat(expectedText) || 0;
316
                var difference = actualAmount - expectedAmount;
317
318
                if ($(this).val() && !isNaN(actualAmount)) {
319
                    var reconciliationText = "";
320
                    var reconciliationClass = "";
321
                    var hasDiscrepancy = false;
322
323
                    if (difference > 0) {
324
                        reconciliationText = "Surplus: " + difference.format_price();
325
                        reconciliationClass = "success";
326
                        hasDiscrepancy = true;
327
                    } else if (difference < 0) {
328
                        reconciliationText = "Deficit: " + Math.abs(difference).format_price();
329
                        reconciliationClass = "warning";
330
                        hasDiscrepancy = true;
331
                    } else {
332
                        reconciliationText = "Balanced - no surplus or deficit";
333
                        reconciliationClass = "success";
334
                        hasDiscrepancy = false;
335
                    }
336
337
                    $("#reconciliation_text").text(reconciliationText)
338
                        .removeClass("success warning")
339
                        .addClass(reconciliationClass);
340
                    $("#reconciliation_display").show();
341
342
                    // Show/hide note field based on whether there's a discrepancy
343
                    if (hasDiscrepancy) {
344
                        $("#reconciliation_note_field").show();
345
                    } else {
346
                        $("#reconciliation_note_field").hide();
347
                        $("#reconciliation_note").val(''); // Clear note when balanced
348
                    }
349
                } else {
350
                    $("#reconciliation_display").hide();
351
                    $("#reconciliation_note_field").hide();
352
                }
353
            });
354
355
            // Reset modal when opened
232
            $("#confirmCashupModal").on("shown.bs.modal", function(e){
356
            $("#confirmCashupModal").on("shown.bs.modal", function(e){
233
               var button = $(e.relatedTarget);
357
               var button = $(e.relatedTarget);
234
               var register = button.data('register');
358
               var register = button.data('register');
Lines 239-244 Link Here
239
               $('#floatc').text(rfloat);
363
               $('#floatc').text(rfloat);
240
               var rid = button.data('registerid');
364
               var rid = button.data('registerid');
241
               $('#cashup_registerid').val(rid);
365
               $('#cashup_registerid').val(rid);
366
               $("#amount").val('').focus();
367
               $("#reconciliation_display").hide();
368
               $("#reconciliation_note_field").hide();
369
               $("#reconciliation_note").val('');
370
            });
371
372
            // Handle the new trigger cashup modal for individual registers
373
            $("#triggerCashupModalRegister").on("shown.bs.modal", function(e){
374
               var button = $(e.relatedTarget);
375
               var register = button.data('register');
376
               $("#register_desc").text(register);
377
               var bankable = button.data('bankable');
378
               $("#expected_amount_display").text(bankable);
379
               var rfloat = button.data('float');
380
               $('#float_amount_display').text(rfloat);
381
               var rid = button.data('registerid');
382
               $('#register_id_field').val(rid);
383
384
               // Store bankable amount for quick cashup
385
               $('#triggerCashupModalRegister').data('bankable-amount', bankable.replace(/[^0-9.-]/g, ''));
386
            });
387
388
            // Handle Quick cashup button click
389
            $("#quick_cashup_btn").on("click", function(e){
390
               e.preventDefault();
391
               var form = $(this).closest('form');
392
               var bankableAmount = $('#triggerCashupModalRegister').data('bankable-amount');
393
394
               // Change operation to cud-cashup (quick cashup)
395
               form.find('input[name="op"]').val('cud-cashup');
396
397
               // Set the amount to the expected bankable amount
398
               form.find('input[name="amount"]').val(bankableAmount);
399
400
               // Submit the form
401
               form.submit();
242
            });
402
            });
243
        });
403
        });
244
    </script>
404
    </script>
(-)a/pos/registers.pl (-2 / +32 lines)
Lines 54-60 if ( !$registers->count ) { Link Here
54
}
54
}
55
55
56
my $op = $input->param('op') // '';
56
my $op = $input->param('op') // '';
57
if ( $op eq 'cud-cashup' ) {
57
if ( $op eq 'cud-cashup_start' ) {
58
    if ( $logged_in_user->has_permission( { cash_management => 'cashup' } ) ) {
59
        my $registerid = $input->param('registerid');
60
        if ($registerid) {
61
            my $register = Koha::Cash::Registers->find( { id => $registerid } );
62
63
            eval {
64
                $register->start_cashup(
65
                    {
66
                        manager_id => $logged_in_user->id,
67
                    }
68
                );
69
            };
70
            if ($@) {
71
                if ( $@->isa('Koha::Exceptions::Object::DuplicateID') ) {
72
                    $template->param( error_cashup_in_progress => 1 );
73
                } else {
74
                    $template->param( error_cashup_start => 1 );
75
                }
76
            } else {
77
78
                # Redirect to prevent duplicate submissions (POST/REDIRECT/GET pattern)
79
                print $input->redirect("/cgi-bin/koha/pos/registers.pl");
80
                exit;
81
            }
82
        } else {
83
            $template->param( error_missing_register_start => 1 );
84
        }
85
    } else {
86
        $template->param( error_cashup_permission => 1 );
87
    }
88
} elsif ( $op eq 'cud-cashup' ) {
58
    if ( $logged_in_user->has_permission( { cash_management => 'cashup' } ) ) {
89
    if ( $logged_in_user->has_permission( { cash_management => 'cashup' } ) ) {
59
        my $registerid = $input->param('registerid');
90
        my $registerid = $input->param('registerid');
60
        if ($registerid) {
91
        if ($registerid) {
61
- 

Return to bug 40445