• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ledgersmb / LedgerSMB / 11643259013

02 Nov 2024 01:55PM UTC coverage: 47.606% (-0.07%) from 47.677%
11643259013

Pull #8478

github

web-flow
Merge 21c3e872c into be0f8595a
Pull Request #8478: chore(deps): update all non-major dependencies (1.12)

120 of 141 branches covered (85.11%)

9941 of 20882 relevant lines covered (47.61%)

470.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.11
/lib/LedgerSMB/Scripts/recon.pm
1

2
package LedgerSMB::Scripts::recon;
3

4
=head1 NAME
5

6
LedgerSMB::Scripts::recon - web entry points for reconciliation workflow
7

8
=head1 DESCRIPTION
9

10
This module acts as the UI controller class for Reconciliation. It controls
11
interfacing with the Core Logic and database layers.
12

13
=head1 METHODS
14

15
=cut
16

17
use strict;
47✔
18
use warnings;
47✔
19

20
use HTTP::Status qw( HTTP_BAD_REQUEST );
47✔
21
use Log::Any qw($log);
47✔
22
use Workflow::Context;
47✔
23

24
use LedgerSMB::DBObject::Reconciliation;
47✔
25
use LedgerSMB::File;
47✔
26
use LedgerSMB::Magic qw( FC_RECONCILIATION );
47✔
27
use LedgerSMB::PGNumber;
47✔
28
use LedgerSMB::Report::Reconciliation::Summary;
47✔
29

30
=over
31

32
=item display_report($request)
33

34
Retrieves and displays the specified reconciliation report.
35

36
C<$request> is a L<LedgerSMB> object reference. The following request keys
37
must be set:
38

39
  * dbh
40
  * report_id
41

42
=cut
43

44
sub display_report {
45
    my ($request) = @_;
2✔
46

47
    my $recon_data = {
48
        dbh => $request->{dbh},
49
        report_id => $request->{report_id}
50
    };
2✔
51

52
    my $recon = LedgerSMB::DBObject::Reconciliation->new(%$recon_data);
2✔
53
    return _display_report($recon, $request);
2✔
54
}
55

56
=item update_recon_set
57

58
Updates the reconciliation set, checks for new transactions to be included,
59
and re-renders the reconciliation screen.
60

61
=cut
62

63
sub update_recon_set {
64
    my ($request) = shift;
6✔
65
    my $recon = LedgerSMB::DBObject::Reconciliation->new(%$request);
6✔
66
    $recon->{their_total} = $request->parse_amount(
67
        $recon->{their_total}
68
    ) if defined $recon->{their_total};
6✔
69
    $recon->save() if !$recon->{submitted};
6✔
70
    $recon->update();
6✔
71
    return _display_report($recon, $request);
6✔
72
}
73

74
=item select_all_recons
75

76
Checks off all reconciliation items and updates recon set
77

78
=cut
79

80
sub select_all_recons {
81
    my ($request) = @_;
4✔
82
    my $i = 1;
4✔
83
    while (my $id = $request->{"id_$i"}){
4✔
84
        $request->{"cleared_$id"} = $id;
85
        ++ $i;
86
    }
87
    return update_recon_set($request);
4✔
88
}
89

90
=item reject
91

92
Rejects the recon set and returns it to non-submitted state, by marking
93
it as not submitted. Can only be performed if the recon set has not
94
already been marked as approved.
95

96
C<$request> is a L<LedgerSMB> object reference. The following request keys
97
must be set:
98

99
  * dbh
100
  * report_id
101

102
=cut
103

104
sub reject {
105
    my ($request) = @_;
×
106

107
    my $wf = $request->{_wire}->get('workflows')
108
        ->fetch_workflow( 'reconciliation', $request->{workflow_id} );
×
109
    $wf->execute_action( 'reject' );
×
110

111
    return search($request);
×
112
}
113

114
=item submit_recon_set
115

116
Submits the recon set to be approved.
117

118
=cut
119

120
sub submit_recon_set {
121
    my ($request) = shift;
×
122
    my $wf = $request->{_wire}->get('workflows')
123
        ->fetch_workflow( 'reconciliation', $request->{workflow_id} );
×
124
    $wf->execute_action( 'submit' );
×
125

126
    my $recon = LedgerSMB::DBObject::Reconciliation->new(%$request);
×
127
    my $can_approve = $request->is_allowed_role(
×
128
        {allowed_roles => ['reconciliation_approve']}
129
    );
130
    if ( !$can_approve ) {
×
131
        my $template = $request->{_wire}->get('ui');
132
        return $template->render($request, 'reconciliation/submitted',
133
                                 $recon);
134
    }
135
    return _display_report($recon, $request);
×
136
}
137

138
=item save_recon_set
139

140
Saves the reconciliation set for later use.
141

142
=cut
143

144
sub save_recon_set {
145
    my ($request) = @_;
×
146
    my $recon = LedgerSMB::DBObject::Reconciliation->new(%$request);
×
147
    if ($request->close_form){
×
148
        $recon->save();
149
        return search($request);
150
    } else {
151
        $recon->{notice} = $request->{_locale}->text(
152
            'Data not saved.  Please update again.'
153
        );
154
        return _display_report($recon, $request);
155
    }
156
}
157

158
=item get_results
159

160
Displays the search results
161

162
=cut
163

164
sub get_results {
165
    my ($request) = @_;
10✔
166
    return $request->render_report(
167
        LedgerSMB::Report::Reconciliation::Summary->new(
168
            $request->%{ qw( account_id approved submitted language _locale
169
                             interval from_month from_year comparison_periods
170
                             comparison_type comparisons ) },
171
            formatter_options => $request->formatter_options,
172
            balance_from => $request->parse_amount( $request->{balance_from} ),
173
            balance_to => $request->parse_amount( $request->{balance_to} ),
174
            from_date => $request->parse_date( $request->{from_date} ),
175
            to_date => $request->parse_date( $request->{to_date} ),
10✔
176
        ));
177
}
178

179
=item search($request)
180

181
Displays bank reconciliation report search criteria screen.
182

183
C<$request> is a L<LedgerSMB> object reference. The following request keys
184
must be set:
185

186
  * dbh
187

188
Search criteria accepted are
189

190
  * date_begin
191
  * date_end
192
  * account
193
  * status
194

195
=cut
196

197
sub search {
198
    my ($request) = @_;
32✔
199

200
    my $recon = LedgerSMB::DBObject::Reconciliation->new();
32✔
201
    $recon->set_dbh($request->{dbh});
32✔
202
    $recon->get_accounts();
32✔
203

204
    $log->info('render');
32✔
205
    my $template = $request->{_wire}->get('ui');
32✔
206
    return $template->render(
32✔
207
        $request,
208
        'Reports/filters/reconciliation_search',
209
        $recon
210
    );
211
}
212

213
# _display_report ($recon, $request)
214
#
215
# Private method to display the provided LedgerSMB::DBObject::Reconciliation
216
# object as a report.
217
#
218
# Called after an existing report has been instantiated by display_report(),
219
# or a new report has been created by start_report(), or after updates to
220
# a report have been submitted.
221

222
sub _display_report {
223
    my ($recon, $request) = @_;
12✔
224

225
    $request->close_form;
12✔
226
    $request->open_form;
12✔
227

228
    my $file                 = LedgerSMB::File->new();
12✔
229
    $recon->{upload_formats} = [
230
        map { +{ name => $_->name } }
231
        $request->{_wire}->get('reconciliation_importer')->configurations->@*
12✔
232
        ];
233
    $recon->{files}  =
234
        [ $file->list({ ref_key    => $request->{report_id},
235
                        file_class => FC_RECONCILIATION }) ];
12✔
236
    $recon->{file_links} = [ $file->list_links(
237
        { ref_key    => $request->{report_id},
238
          file_class => FC_RECONCILIATION }) ];
12✔
239

240
    $recon->{form_id} = $request->{form_id};
12✔
241
    $recon->{can_approve} = $request->is_allowed_role(
12✔
242
        {allowed_roles => ['reconciliation_approve']}
243
    );
244
    $recon->{decimal_places} = $request->setting->get('decimal_places');
12✔
245
    _set_sort_options($recon, $request);
12✔
246

247
    _process_upload($recon, $request) unless $recon->{submitted};
12✔
248
    $recon->get;
12✔
249
    $recon->build_totals;
12✔
250

251
    if ($recon->{account_info}->{category} eq 'A') {
12✔
252
        $recon->{reverse} = $request->setting->get('reverse_bank_recs');
253
    }
254

255
    $recon->{submit_enabled} = ($recon->{variance} == 0);
12✔
256
    _highlight_suspect_rows($recon);
12✔
257

258
    for my $amt_name (qw/
12✔
259
        mismatch_our_
260
        mismatch_their_
261
        total_cleared_
262
        total_uncleared_
263
    /) {
264
        for my $bal_type (qw/ credits debits/) {
48✔
265
            $recon->{"$amt_name$bal_type"} = (
266
                $request->format_amount( $recon->{"$amt_name$bal_type"}, money => 1)
96✔
267
            );
268
        }
269
    }
270

271
    for my $line (@{$recon->{report_lines}}){
12✔
272
        for my $element (qw/
20✔
273
            our_balance
274
            our_credits
275
            our_debits
276
            their_balance
277
            their_credits
278
            their_debits
279
        /) {
280
            $line->{$element} = $request->format_amount( $line->{$element}, money => 1);
120✔
281
        }
282
    }
283

284
    for my $field (qw/
12✔
285
        cleared_total
286
        outstanding_total
287
        statement_gl_calc
288
        their_total
289
        variance
290
        our_total
291
        beginning_balance
292
    /) {
293
        $recon->{$field} = $request->format_amount( $recon->{$field}, money => 1);
84✔
294
    }
295

296
    my $template = $request->{_wire}->get('ui');
12✔
297
    return $template->render($request, 'reconciliation/report', $recon);
12✔
298
}
299

300

301
=item new_report($request)
302

303
Displays the Create New Report screen, allowing the user to input parameters
304
for the creation of a new reconcilition report..
305

306
C<$request> is a L<LedgerSMB> object reference. The following request keys
307
must be set:
308

309
  * dbh
310

311
=cut
312

313
sub new_report {
314
    my ($request) = @_;
×
315

316
    my $recon = LedgerSMB::DBObject::Reconciliation->new();
×
317
    $recon->set_dbh($request->{dbh});
×
318
    $recon->get_accounts();
×
319

320
    my $template = $request->{_wire}->get('ui');
×
321
    return $template->render(
×
322
        $request,
323
        'reconciliation/new_report',
324
        $recon
325
    );
326
}
327

328
=item start_report($request)
329

330
Creates a new reconciliation report in the database, using the supplied
331
parameters and displays the blank report.
332

333
C<$request> is a L<LedgerSMB> object reference. The following request keys
334
must be set:
335

336
  * dbh
337
  * chart_id  [the account to be reconciled]
338
  * end_date  [the end date]
339
  * total     [the end balance]
340

341
Optionally the following request key may be set:
342

343
  * recon_fx  [boolean, default false]
344

345
=cut
346

347
sub start_report {
348
    my ($request) = @_;
4✔
349

350
    # Trap user error: dates accidentally entered in the amount field
351
    if ($request->{total} && $request->{total} =~ m|\d[/-]|){
4✔
352
        $request->error($request->{_locale}->text(
353
           'Invalid statement balance.  Hint: Try entering a number'
354
        ));
355
    }
356

357
    my $recon_data = {
358
        account_id     => $request->{chart_id},
359
        end_date       => $request->{end_date},
360
        ending_balance => $request->{total},
361
        recon_fx       => $request->{recon_fx},
362
    };
4✔
363
    my $ctx = Workflow::Context->new(%$recon_data);
4✔
364
    my $wf = $request->{_wire}->get( 'workflows' )->
4✔
365
        create_workflow( 'reconciliation', $ctx );
366

367
    my $recon = LedgerSMB::DBObject::Reconciliation->new(
368
        dbh => $request->{dbh},
369
        report_id => $wf->context->param( 'id' ),
4✔
370
        workflow_id => $wf->id,
371
        );
372
    $recon->refresh_pending_transactions;
4✔
373

374
    # Format ending balance as a PGNumber - required for display
375
    $recon->{their_total} = $request->parse_amount($request->{total});
4✔
376
    delete $recon->{total};
4✔
377

378
    return _display_report($recon, $request);
4✔
379
}
380

381
=item delete_report($request)
382

383
Requires report_id
384

385
This deletes a report.  Reports may not be deleted if approved (this will throw
386
a database-level exception).  Users may delete their own reports if they have
387
not yet been submitted for approval.  Those who have approval permissions may
388
delete any non-approved reports.
389

390
=cut
391

392
sub delete_report {
393
    my ($request) = @_;
2✔
394

395
    my $wf = $request->{_wire}->get('workflows')
396
        ->fetch_workflow( 'reconciliation', $request->{workflow_id} );
2✔
397
    $wf->execute_action( 'delete' );
2✔
398

399
    return search($request);
2✔
400
}
401

402
=item approve ($request)
403

404
Requires report_id
405

406
Approves the given report based on id. Generally, the roles should be
407
configured so as to disallow the same user from approving, as created the report.
408

409
Returns a success page on success, returns a new report on failure, showing
410
the uncorrected entries.
411

412
=cut
413

414
sub approve {
415
    my ($request) = @_;
×
416
    if (!$request->close_form){
×
417
        return get_results($request);
418
    }
419

420
    return [ HTTP_BAD_REQUEST,
421
             [ 'Content-Type' => 'text/plain; charset=utf-8' ],
422
             [ q{'report_id' parameter missing} ]
423
        ] if ! $request->{report_id};
×
424

425
    my $wf = $request->{_wire}->get('workflows')
426
        ->fetch_workflow( 'reconciliation', $request->{workflow_id} );
×
427
    $wf->execute_action( 'approve' );
×
428

429
    my $recon = LedgerSMB::DBObject::Reconciliation->new(%$request);
×
430
    my $template = 'reconciliation/approved';
×
431
    return $request->{_wire}->get('ui')
×
432
        ->render($request, $template, $recon);
433
}
434

435
=item pending ($request)
436

437
Requires {date} and {month}, to handle the month-to-month pending transactions
438
in the database. No mechanism is provided to grab ALL pending transactions
439
from the acc_trans table.
440

441
=cut
442

443

444
sub pending {
445

446
    my ($request) = @_;
×
447

448
    my $recon = LedgerSMB::DBObject::Reconciliation->new(%$request);
×
449

450
    my $template= $request->{_wire}->get('ui');
×
451
    return $template->render($request, 'reconciliation/pending', {});
×
452
}
453

454

455
# _process_upload($recon, $request)
456
#
457
# If the request data includes a csv_file upload, import it and
458
# apply the contents to the current reconciliation report.
459

460
sub _process_upload {
461
    my ($recon, $request) = @_;
12✔
462
    my $handle = eval { $request->upload('csv_file') };
12✔
463

464
    if ($handle) {
12✔
465
        my $cfg = $request->{_wire}->get('reconciliation_importer')
466
            ->get_configuration(name => $request->{'trx_format'});
467
        my $entries = $cfg->process($handle);
468
        $recon->add_entries($entries);
469
    }
470

471
    return;
12✔
472
}
473

474

475
# _set_sort_options($recon, $request)
476
#
477
# Define the available sort options for display on a reconciliation report.
478
# Set a default sort order, if otherwise unspecified.
479

480
sub _set_sort_options {
481
    my ($recon, $request) = @_;
12✔
482

483
    $recon->{sort_options} = [
484
        {
485
            id => 'clear_time',
486
            label => $request->{_locale}->text('Clear date')
487
        },
488
        {
489
            id => 'scn',
490
            label => $request->{_locale}->text('Source')
491
        },
492
        {
493
            id => 'post_date',
494
            label => $request->{_locale}->text('Post Date')
495
        },
496
        {
497
            id => 'our_balance',
498
            label => $request->{_locale}->text('Our Balance')
499
        },
500
        {
501
            id => 'their_balance',
502
            label => $request->{_locale}->text('Their Balance')
12✔
503
        },
504
    ];
505

506
    $recon->{line_order} ||= 'scn';
12✔
507
    return;
12✔
508
}
509

510

511
# _highlight_suspect_rows
512
#
513
# If there is a variance in the report, highlight any rows which
514
# exactly match the variance, to help identify if a single row is
515
# responsible for the mismatch.
516
#
517
# Does nothing if the variance is zero.
518

519
sub _highlight_suspect_rows {
520
    my ($recon) = @_;
12✔
521

522
    if ($recon->{variance} == 0) {
12✔
523
        # No differences to highlight    
524
        return;
525
    }
526

527
    # Check if only one entry could explain the difference
528
    for my $l (@{$recon->{report_lines}}){
10✔
529
        $l->{suspect} = $l->{our_credits} == -$recon->{variance}
530
                     || $l->{our_debits}  ==  $recon->{variance}
531
                     ? 1 : 0;
16✔
532
    }
533

534
    return;
10✔
535
}
536

537

538
=back
539

540
=head1 LICENSE AND COPYRIGHT
541

542
Copyright (C) 2011-2020 The LedgerSMB Core Team
543

544
This file is licensed under the GNU General Public License version 2, or at your
545
option any later version.  A copy of the license should have been included with
546
your software.
547

548
=cut
549

550

551
1;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc