PageRenderTime 35ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/LedgerSMB/Scripts/recon.pm

https://gitlab.com/Datse-Multimedia-Productions/LedgerSMB
Perl | 494 lines | 353 code | 107 blank | 34 comment | 40 complexity | 9b55dab6773f88b5d8ee25364c539be1 MD5 | raw file
  1. =pod
  2. =head1 NAME
  3. LedgerSMB::Scripts::recon - web entry points for reconciliation workflow
  4. =head1 SYOPSIS
  5. This module acts as the UI controller class for Reconciliation. It controls
  6. interfacing with the Core Logic and database layers.
  7. =head1 METHODS
  8. =cut
  9. # NOTE: This is a first draft modification to use the current parameter type.
  10. # It will certainly need some fine tuning on my part. Chris
  11. package LedgerSMB::Scripts::recon;
  12. use LedgerSMB::Template;
  13. use LedgerSMB::DBObject::Reconciliation;
  14. use LedgerSMB::Setting;
  15. use LedgerSMB::Scripts::reports;
  16. use LedgerSMB::Report::Reconciliation::Summary;
  17. use strict;
  18. use warnings;
  19. =over
  20. =item display_report($self, $request, $user)
  21. Renders out the selected report given by the incoming variable report_id.
  22. Returns HTML, or raises an error from being unable to find the selected
  23. report_id.
  24. =cut
  25. sub display_report {
  26. my ($request) = @_;
  27. my $recon = LedgerSMB::DBObject::Reconciliation->new({base => $request, copy => 'all'});
  28. _display_report($recon, $request);
  29. }
  30. =item search($self, $request, $user)
  31. Renders out a list of reports based on the search criteria passed to the
  32. search function.
  33. Meta-reports are report_id, date_range, and likely errors.
  34. Search criteria accepted are
  35. =over
  36. =item date_begin
  37. =item date_end
  38. =item account
  39. =item status
  40. =back
  41. =item update_recon_set
  42. Updates the reconciliation set, checks for new transactions to be included,
  43. and re-renders the reconciliation screen.
  44. =cut
  45. sub update_recon_set {
  46. my ($request) = shift;
  47. my $recon = LedgerSMB::DBObject::Reconciliation->new({base => $request});
  48. $recon->{their_total} = LedgerSMB::PGNumber->from_input($recon->{their_total}) if defined $recon->{their_total};
  49. $recon->save() if !$recon->{submitted};
  50. $recon->update();
  51. _display_report($recon, $request);
  52. }
  53. =item select_all_recons
  54. Checks off all reconciliation items and updates recon set
  55. =cut
  56. sub select_all_recons {
  57. my ($request) = @_;
  58. my $i = 1;
  59. while (my $id = $request->{"id_$i"}){
  60. $request->{"cleared_$id"} = $id;
  61. ++ $i;
  62. }
  63. update_recon_set($request);
  64. }
  65. =item reject
  66. Rejects the recon set and returns it to non-submitted state
  67. =cut
  68. sub reject {
  69. my ($request) = @_;
  70. my $recon = LedgerSMB::DBObject::Reconciliation->new({base => $request});
  71. $recon->reject;
  72. search($request);
  73. }
  74. =item submit_recon_set
  75. Submits the recon set to be approved.
  76. =cut
  77. sub submit_recon_set {
  78. my ($request) = shift;
  79. my $recon = LedgerSMB::DBObject::Reconciliation->new({base => $request});
  80. $recon->submit();
  81. my $template = LedgerSMB::Template->new(
  82. user => $request->{_user},
  83. template => 'reconciliation/submitted',
  84. locale => $request->{_locale},
  85. format => 'HTML',
  86. path=>"UI");
  87. return $template->render($recon);
  88. }
  89. =item save_recon_set
  90. Saves the reconciliation set for later use.
  91. =cut
  92. sub save_recon_set {
  93. my ($request) = @_;
  94. my $recon = LedgerSMB::DBObject::Reconciliation->new({base => $request});
  95. if ($request->close_form){
  96. $recon->save();
  97. return search($request);
  98. } else {
  99. $recon->{notice} = $recon->{_locale}->text('Data not saved. Please update again.');
  100. return _display_report($recon, $request);
  101. }
  102. }
  103. =item get_results
  104. Displays the search results
  105. =cut
  106. sub get_results {
  107. my ($request) = @_;
  108. my $report = LedgerSMB::Report::Reconciliation::Summary->new(%$request);
  109. $report->render($request);
  110. }
  111. =item search
  112. Displays search criteria screen
  113. =cut
  114. sub search {
  115. my ($request) = @_;
  116. my $recon = LedgerSMB::DBObject::Reconciliation->new({base=>$request, copy=>'all'});
  117. if (!$recon->{hide_status}){
  118. $recon->{show_approved} = 1;
  119. $recon->{show_submitted} = 1;
  120. }
  121. @{$recon->{recon_accounts}} = $recon->get_accounts();
  122. unshift @{$recon->{recon_accounts}}, {id => '', name => '' };
  123. $recon->{report_name} = 'reconciliation_search';
  124. LedgerSMB::Scripts::reports::start_report($recon);
  125. }
  126. =item new_report ($self, $request, $user)
  127. Creates a new report, from a selectable set of bank statements that have been
  128. received (or can be received from, depending on implementation)
  129. Allows for an optional selection key, which will return the new report after
  130. it has been created.
  131. =cut
  132. sub _display_report {
  133. my ($recon, $request) = @_;
  134. $recon->get();
  135. my $setting_handle = LedgerSMB::Setting->new({base => $recon});
  136. $recon->{reverse} = $setting_handle->get('reverse_bank_recs');
  137. delete $recon->{reverse} unless $recon->{account_info}->{category}
  138. eq 'A';
  139. $request->close_form;
  140. $request->open_form;
  141. $recon->unapproved_checks;
  142. $recon->add_entries($recon->import_file('csv_file')) if !$recon->{submitted};
  143. $recon->{can_approve} = $request->is_allowed_role({allowed_roles => ['reconciliation_approve']});
  144. $recon->get();
  145. $recon->{form_id} = $request->{form_id};
  146. my $template = LedgerSMB::Template->new(
  147. user=> $recon->{_user},
  148. template => 'reconciliation/report',
  149. locale => $recon->{_locale},
  150. format=>'HTML',
  151. path=>"UI"
  152. );
  153. $recon->{sort_options} = [
  154. {id => 'clear_time', label => $recon->{_locale}->text('Clear date')},
  155. {id => 'scn', label => $recon->{_locale}->text('Source')},
  156. {id => 'post_date', label => $recon->{_locale}->text('Post Date')},
  157. {id => 'our_balance', label => $recon->{_locale}->text('Our Balance')},
  158. {id => 'their_balance', label => $recon->{_locale}->text('Their Balance')},
  159. ];
  160. if (!$recon->{line_order}){
  161. $recon->{line_order} = 'scn';
  162. }
  163. for my $field (qw/ total_cleared_credits total_cleared_debits total_uncleared_credits total_uncleared_debits /) {
  164. $recon->{"$field"} = LedgerSMB::PGNumber->from_input(0);
  165. }
  166. my $neg_factor = 1;
  167. if ($recon->{account_info}->{category} =~ /(A|E)/){
  168. $recon->{their_total} *= -1;
  169. $neg_factor = -1;
  170. }
  171. # Credit/Debit separation (useful for some)
  172. for my $l (@{$recon->{report_lines}}){
  173. if ($l->{their_balance} > 0){
  174. $l->{their_debits} = LedgerSMB::PGNumber->from_input(0);
  175. $l->{their_credits} = $l->{their_balance};
  176. }
  177. else {
  178. $l->{their_credits} = LedgerSMB::PGNumber->from_input(0);
  179. $l->{their_debits} = $l->{their_balance}->bneg;
  180. }
  181. if ($l->{our_balance} > 0){
  182. $l->{our_debits} = LedgerSMB::PGNumber->from_input(0);
  183. $l->{our_credits} = $l->{our_balance};
  184. }
  185. else {
  186. $l->{our_credits} = LedgerSMB::PGNumber->from_input(0);
  187. $l->{our_debits} = $l->{our_balance}->bneg;
  188. }
  189. if ($l->{cleared}){
  190. $recon->{total_cleared_credits}->badd($l->{our_credits});
  191. $recon->{total_cleared_debits}->badd($l->{our_debits});
  192. } else {
  193. $recon->{total_uncleared_credits}->badd($l->{our_credits});
  194. $recon->{total_uncleared_debits}->badd($l->{our_debits});
  195. }
  196. for my $amt_name (qw/ our_ their_ /) {
  197. for my $bal_type (qw/ balance credits debits/) {
  198. $l->{"$amt_name$bal_type"} = $l->{"$amt_name$bal_type"}->to_output(money=>1);
  199. }
  200. }
  201. }
  202. $recon->{zero_string} = LedgerSMB::PGNumber->from_input(0)->to_output(money => 1);
  203. $recon->{statement_gl_calc} = $neg_factor *
  204. ($recon->{their_total}
  205. + $recon->{outstanding_total}
  206. + $recon->{mismatch_our_total});
  207. $recon->{out_of_balance} = $recon->{their_total} - $recon->{our_total};
  208. for my $amt_name (qw/ mismatch_our_ mismatch_their_ total_cleared_ total_uncleared_ /) {
  209. for my $bal_type (qw/ credits debits/) {
  210. $recon->{"$amt_name$bal_type"} = $recon->{"$amt_name$bal_type"}->to_output(money=>1);
  211. }
  212. }
  213. $recon->{their_total} = $recon->{their_total} * $neg_factor;
  214. for my $field (qw/ cleared_total outstanding_total statement_gl_calc their_total /) {
  215. $recon->{"$field"} = $recon->{"$field"}->to_output(money=>1);
  216. }
  217. for my $field (qw/ our_total beginning_balance out_of_balance /) {
  218. $recon->{"$field"} ||= LedgerSMB::PGNumber->from_db(0);
  219. $recon->{"$field"} = $recon->{"$field"}->to_output(money => 1);
  220. }
  221. $recon->{submit_allowed} = ( $recon->{their_total} - $recon->{beginning_balance})
  222. - ( $recon->{total_cleared_credits} - $recon->{total_cleared_debits});
  223. $recon->{submit_allowed} = int($recon->{submit_allowed}*100)/100;
  224. return $template->render($recon);
  225. }
  226. =item new_report
  227. Displays the new report screen.
  228. =cut
  229. sub new_report {
  230. my ($request) = @_;
  231. # Trap user error: dates accidentally entered in the amount field
  232. if ($request->{total} && $request->{total} =~ m|\d[/-]|){
  233. $request->error($request->{_locale}->text(
  234. 'Invalid statement balance. Hint: Try entering a number'
  235. ));
  236. }
  237. $request->{total} = LedgerSMB::PGNumber->from_input($request->{total});
  238. my $template;
  239. my $return;
  240. my $recon = LedgerSMB::DBObject::Reconciliation->new({base => $request, copy => 'all'});
  241. # This method detection makes debugging a bit harder.
  242. # Not sure I like it but won't refactor until 1.4..... --CT
  243. #
  244. if ($request->type() eq "POST") {
  245. # We can assume that we're doing something useful with new data.
  246. # We can also assume that we've got a file.
  247. # $self is expected to have both the file handling logic, as well as
  248. # the logic to load the processing module.
  249. # Why isn't this testing for errors?
  250. my ($report_id, $entries) = $recon->new_report($recon->import_file());
  251. if ($recon->{error}) {
  252. #$recon->{error};
  253. $template = LedgerSMB::Template->new(
  254. user=>$recon->{_user},
  255. template=> 'reconciliation/upload',
  256. locale => $recon->{_locale},
  257. format=>'HTML',
  258. path=>"UI"
  259. );
  260. return $template->render($recon);
  261. }
  262. _display_report($recon, $request);
  263. }
  264. else {
  265. # we can assume we're to generate the "Make a happy new report!" page.
  266. @{$recon->{accounts}} = $recon->get_accounts;
  267. $template = LedgerSMB::Template->new(
  268. user => $recon->{_user},
  269. template => 'reconciliation/upload',
  270. locale => $recon->{_locale},
  271. format => 'HTML',
  272. path=>"UI"
  273. );
  274. return $template->render($recon);
  275. }
  276. return undef;
  277. }
  278. =item delete_report($request)
  279. Requires report_id
  280. This deletes a report. Reports may not be deleted if approved (this will throw
  281. a database-level exception). Users may delete their own reports if they have
  282. not yet been submitted for approval. Those who have approval permissions may
  283. delete any non-approved reports.
  284. =cut
  285. sub delete_report {
  286. my ($request) = @_;
  287. my $recon = LedgerSMB::DBObject::Reconciliation->new({
  288. base=>$request,
  289. copy=>'all'
  290. });
  291. my $resp = $recon->delete($request->{report_id});
  292. delete($request->{report_id});
  293. return search($request);
  294. }
  295. =item approve ($self, $request, $user)
  296. Requires report_id
  297. Approves the given report based on id. Generally, the roles should be
  298. configured so as to disallow the same user from approving, as created the report.
  299. Returns a success page on success, returns a new report on failure, showing
  300. the uncorrected entries.
  301. =cut
  302. sub approve {
  303. my ($request) = @_;
  304. if (!$request->close_form){
  305. return get_results($request);
  306. }
  307. # Approve will also display the report in a blurred/opaqued out version,
  308. # with the controls removed/disabled, so that we know that it has in fact
  309. # been cleared. This will also provide for return-home links, auditing,
  310. # etc.
  311. if ($request->type() eq "POST") {
  312. # we need a report_id for this.
  313. my $recon = LedgerSMB::DBObject::Reconciliation->new({base => $request, copy=> 'all'});
  314. my $code = $recon->approve($request->{report_id});
  315. my $template = $code == 0 ? 'reconciliation/approved'
  316. : 'reconciliation/report';
  317. return LedgerSMB::Template->new(
  318. user => $recon->{_user},
  319. template => $template,
  320. locale => $recon->{_locale},
  321. format => 'HTML',
  322. path=>"UI"
  323. )->render($recon);
  324. }
  325. else {
  326. return _display_report($request, $request);
  327. }
  328. }
  329. =item pending ($self, $request, $user)
  330. Requires {date} and {month}, to handle the month-to-month pending transactions
  331. in the database. No mechanism is provided to grab ALL pending transactions
  332. from the acc_trans table.
  333. =cut
  334. sub pending {
  335. my ($request) = @_;
  336. my $recon = LedgerSMB::DBObject::Reconciliation->new({base=>$request, copy=>'all'});
  337. my $template;
  338. $template= LedgerSMB::Template->new(
  339. user => $request->{_user},
  340. template=>'reconciliation/pending',
  341. locale => $request->{_locale},
  342. format=>'HTML',
  343. path=>"UI"
  344. );
  345. if ($request->type() eq "POST") {
  346. return $template->render(
  347. {
  348. pending=>$recon->get_pending($request->{year}."-".$request->{month})
  349. }
  350. );
  351. }
  352. else {
  353. return $template->render();
  354. }
  355. }
  356. sub __default {
  357. my ($request) = @_;
  358. my $recon = LedgerSMB::DBObject::Reconciliation->new({base=>$request, copy=>'all'});
  359. my $template;
  360. $template = LedgerSMB::Template->new(
  361. user => $request->{_user},
  362. template => 'reconciliation/list',
  363. locale => $request->{_locale},
  364. format=>'HTML',
  365. path=>"UI"
  366. );
  367. return $template->render(
  368. {
  369. reports=>$recon->get_report_list()
  370. }
  371. );
  372. }
  373. ###TODO-LOCALIZE-DOLLAR-AT
  374. eval { do "scripts/custom/recon.pl" };
  375. 1;
  376. =back
  377. =head1 Copyright (C) 2007, The LedgerSMB core team.
  378. This file is licensed under the Gnu General Public License version 2, or at your
  379. option any later version. A copy of the license should have been included with
  380. your software.
  381. =cut