summaryrefslogtreecommitdiff
path: root/LedgerSMB/AA.pm
blob: 295b89a631890287c2cf8b8edd3fc8241cc42aa4 (plain)
  1. #=====================================================================
  2. # LedgerSMB
  3. # Small Medium Business Accounting software
  4. # http://www.ledgersmb.org/
  5. # Copyright (C) 2006
  6. # This work contains copyrighted information from a number of sources all used
  7. # with permission.
  8. #
  9. # This file contains source code included with or based on SQL-Ledger which
  10. # is Copyright Dieter Simader and DWS Systems Inc. 2000-2005 and licensed
  11. # under the GNU General Public License version 2 or, at your option, any later
  12. # version. For a full list including contact information of contributors,
  13. # maintainers, and copyright holders, see the CONTRIBUTORS file.
  14. #
  15. # Original Copyright Notice from SQL-Ledger 2.6.17 (before the fork):
  16. # Copyright (C) 2006
  17. #
  18. # Author: DWS Systems Inc.
  19. # Web: http://www.sql-ledger.org
  20. #
  21. # Contributors:
  22. #
  23. #
  24. # See COPYRIGHT file for copyright information
  25. #======================================================================
  26. #
  27. # This file has undergone whitespace cleanup.
  28. #
  29. #======================================================================
  30. #
  31. # AR/AP backend routines
  32. # common routines
  33. #
  34. #======================================================================
  35. package AA;
  36. use LedgerSMB::Sysconfig;
  37. =pod
  38. =head1 post_transaction()
  39. Post transaction uses the following variables in the $form variable:
  40. * dbh - the database connection handle
  41. * currency - The current users' currency
  42. * defaultcurrency - The "normal" currency
  43. * department - Unknown
  44. * department_id - ID for the department
  45. * exchangerate - Conversion between currency and defaultcurrency
  46. * invnumber - invoice number
  47. * reverse - ?
  48. * rowcount - Number of rows in the invoice
  49. * taxaccounts - Apply taxes?
  50. * taxincluded - ?
  51. * transdate - Date of the transaction
  52. * vc - Vendor or customer - determines transaction type
  53. =cut
  54. sub post_transaction {
  55. use strict;
  56. my ( $self, $myconfig, $form ) = @_;
  57. my $exchangerate;
  58. my $batch_class;
  59. my %paid;
  60. my $paidamount;
  61. my @queries;
  62. if ($form->{separate_duties}){
  63. $form->{approved} = '0';
  64. }
  65. for (1 .. $form->{rowcount}){
  66. $form->{"amount_$_"} = $form->parse_amount(
  67. $myconfig, $form->{"amount_$_"}
  68. );
  69. $form->{"amount_$_"} *= -1 if $form->{reverse};
  70. }
  71. # connect to database
  72. my $dbh = $form->{dbh};
  73. my $query;
  74. my $sth;
  75. my $null;
  76. ( $null, $form->{department_id} ) = split( /--/, $form->{department} );
  77. $form->{department_id} *= 1;
  78. my $ml = 1;
  79. my $table = 'ar';
  80. my $buysell = 'buy';
  81. my $ARAP = 'AR';
  82. my $invnumber = "sinumber";
  83. my $keepcleared;
  84. if ( $form->{vc} eq 'vendor' ) {
  85. $table = 'ap';
  86. $buysell = 'sell';
  87. $ARAP = 'AP';
  88. $ml = -1;
  89. $invnumber = "vinumber";
  90. }
  91. $form->{invnumber} = $form->update_defaults( $myconfig, $invnumber )
  92. unless $form->{invnumber};
  93. if ( $form->{currency} eq $form->{defaultcurrency} ) {
  94. $form->{exchangerate} = 1;
  95. }
  96. else {
  97. $exchangerate =
  98. $form->check_exchangerate( $myconfig, $form->{currency},
  99. $form->{transdate}, $buysell );
  100. $form->{exchangerate} =
  101. ($exchangerate)
  102. ? $exchangerate
  103. : $form->parse_amount( $myconfig, $form->{exchangerate} );
  104. }
  105. my @taxaccounts = split / /, $form->{taxaccounts};
  106. my $tax = 0;
  107. my $fxtax = 0;
  108. my $amount;
  109. my $diff;
  110. my %tax = ();
  111. my $accno;
  112. # add taxes
  113. foreach $accno (@taxaccounts) {
  114. $form->{"tax_$accno"} *= -1 if $form->{reverse};
  115. $fxtax += $tax{fxamount}{$accno} = $form->{"tax_$accno"};
  116. $tax += $tax{fxamount}{$accno};
  117. push @{ $form->{acc_trans}{taxes} },
  118. {
  119. accno => $accno,
  120. amount => $tax{fxamount}{$accno},
  121. project_id => undef,
  122. fx_transaction => 0
  123. };
  124. $amount = $tax{fxamount}{$accno} * $form->{exchangerate};
  125. $tax{amount}{$accno} = $form->round_amount( $amount - $diff, 2 );
  126. $diff = $tax{amount}{$accno} - ( $amount - $diff );
  127. $amount = $tax{amount}{$accno} - $tax{fxamount}{$accno};
  128. $tax += $amount;
  129. if ( $form->{currency} ne $form->{defaultcurrency} ) {
  130. push @{ $form->{acc_trans}{taxes} },
  131. {
  132. accno => $accno,
  133. amount => $amount,
  134. project_id => undef,
  135. fx_transaction => 1
  136. };
  137. }
  138. }
  139. my %amount = ();
  140. my $fxinvamount = 0;
  141. for ( 1 .. $form->{rowcount} ) {
  142. $fxinvamount += $amount{fxamount}{$_} = $form->{"amount_$_"};
  143. }
  144. $form->{taxincluded} *= 1;
  145. my $i;
  146. my $project_id;
  147. my $cleared = 0;
  148. $diff = 0;
  149. # deduct tax from amounts if tax included
  150. for $i ( 1 .. $form->{rowcount} ) {
  151. if ( $amount{fxamount}{$i} ) {
  152. if ( $form->{taxincluded} ) {
  153. $amount =
  154. ($fxinvamount)
  155. ? $fxtax * $amount{fxamount}{$i} / $fxinvamount
  156. : 0;
  157. $amount{fxamount}{$i} -= $amount;
  158. }
  159. # multiply by exchangerate
  160. $amount = $amount{fxamount}{$i} * $form->{exchangerate};
  161. $amount{amount}{$i} = $form->round_amount( $amount - $diff, 2 );
  162. $diff = $amount{amount}{$i} - ( $amount - $diff );
  163. ( $null, $project_id ) = split /--/, $form->{"projectnumber_$i"};
  164. $project_id ||= undef;
  165. ($accno) = split /--/, $form->{"${ARAP}_amount_$i"};
  166. if ($keepcleared) {
  167. $cleared = ( $form->{"cleared_$i"} ) ? 1 : 0;
  168. }
  169. push @{ $form->{acc_trans}{lineitems} },
  170. {
  171. accno => $accno,
  172. amount => $amount{fxamount}{$i},
  173. project_id => $project_id,
  174. description => $form->{"description_$i"},
  175. cleared => $cleared,
  176. fx_transaction => 0
  177. };
  178. if ( $form->{currency} ne $form->{defaultcurrency} ) {
  179. $amount = $amount{amount}{$i} - $amount{fxamount}{$i};
  180. push @{ $form->{acc_trans}{lineitems} },
  181. {
  182. accno => $accno,
  183. amount => $amount,
  184. project_id => $project_id,
  185. description => $form->{"description_$i"},
  186. cleared => $cleared,
  187. fx_transaction => 1
  188. };
  189. }
  190. }
  191. }
  192. my $invnetamount = 0;
  193. for ( @{ $form->{acc_trans}{lineitems} } ) { $invnetamount += $_->{amount} }
  194. my $invamount = $invnetamount + $tax;
  195. # adjust paidaccounts if there is no date in the last row
  196. $form->{paidaccounts}--
  197. unless ( $form->{"datepaid_$form->{paidaccounts}"} );
  198. if ( $form->{vc} ne "customer" ) {
  199. $form->{vc} = "vendor";
  200. }
  201. my $paid = 0;
  202. my $fxamount;
  203. $diff = 0;
  204. # add payments
  205. for $i ( 1 .. $form->{paidaccounts} ) {
  206. $form->{"paid_$i"} = $form->parse_amount(
  207. $myconfig, $form->{"paid_$i"}
  208. );
  209. $form->{"paid_$i"} *= -1 if $form->{reverse};
  210. $fxamount = $form->{"paid_$i"};
  211. if ($fxamount) {
  212. $paid += $fxamount;
  213. $paidamount = $fxamount * $form->{exchangerate};
  214. $amount = $form->round_amount( $paidamount - $diff, 2 );
  215. $diff = $amount - ( $paidamount - $diff );
  216. $form->{datepaid} = $form->{"datepaid_$i"};
  217. $paid{fxamount}{$i} = $fxamount;
  218. $paid{amount}{$i} = $amount;
  219. }
  220. }
  221. $fxinvamount += $fxtax unless $form->{taxincluded};
  222. $fxinvamount = $form->round_amount( $fxinvamount, 2 );
  223. $invamount = $form->round_amount( $invamount, 2 );
  224. $paid = $form->round_amount( $paid, 2 );
  225. $paid =
  226. ( $fxinvamount == $paid )
  227. ? $invamount
  228. : $form->round_amount( $paid * $form->{exchangerate}, 2 );
  229. $query = q|
  230. SELECT (SELECT value FROM defaults
  231. WHERE setting_key = 'fxgain_accno_id'),
  232. (SELECT value FROM defaults
  233. WHERE setting_key = 'fxloss_accno_id')|;
  234. my ( $fxgain_accno_id, $fxloss_accno_id ) = $dbh->selectrow_array($query);
  235. ( $null, $form->{employee_id} ) = split /--/, $form->{employee};
  236. unless ( $form->{employee_id} ) {
  237. ( $form->{employee}, $form->{employee_id} ) = $form->get_employee($dbh);
  238. }
  239. # check if id really exists
  240. if ( $form->{id} ) {
  241. my $id = $dbh->quote( $form->{id} );
  242. $keepcleared = 1;
  243. $query = qq|
  244. SELECT id
  245. FROM $table
  246. WHERE id = $id|;
  247. if ( $dbh->selectrow_array($query) ) {
  248. # delete detail records
  249. $query = qq|
  250. DELETE FROM acc_trans
  251. WHERE trans_id = $id|;
  252. $dbh->do($query) || $form->dberror($query);
  253. }
  254. }
  255. else {
  256. my $uid = localtime;
  257. $uid .= "$$";
  258. # The query is done like this as the login name maps to the users table
  259. # which maps to the user conf table, which links to an entity, to which
  260. # a person is also attached. This is done in this fashion because we
  261. # are using the current username as the "person" inserting the new
  262. # AR/AP Transaction.
  263. # ~A
  264. $query = qq|
  265. INSERT INTO $table (invnumber, person_id,
  266. entity_credit_account)
  267. VALUES (?, (select e.id from person p, entity e, users u
  268. where u.username = ?
  269. AND e.id = u.entity_id
  270. AND p.entity_id = e.id ), ?)|;
  271. # the second param is undef, as the DBI api expects a hashref of
  272. # attributes to pass to $dbh->prepare. This is not used here.
  273. # ~A
  274. $dbh->do($query,undef,$uid,$form->{login}, $form->{"$form->{vc}_id"}) || $form->dberror($query);
  275. $query = qq|
  276. SELECT id FROM $table
  277. WHERE invnumber = ?|;
  278. ( $form->{id} ) = $dbh->selectrow_array($query,undef,$uid);
  279. }
  280. # record last payment date in ar/ap table
  281. $form->{datepaid} = $form->{transdate} unless $form->{datepaid};
  282. my $datepaid = ($paid) ? qq|'$form->{datepaid}'| : undef;
  283. $query = qq|
  284. UPDATE $table
  285. SET invnumber = ?,
  286. ordnumber = ?,
  287. transdate = ?,
  288. taxincluded = ?,
  289. amount = ?,
  290. duedate = ?,
  291. paid = ?,
  292. datepaid = ?,
  293. netamount = ?,
  294. curr = ?,
  295. notes = ?,
  296. department_id = ?,
  297. ponumber = ?
  298. WHERE id = ?
  299. |;
  300. my @queryargs = (
  301. $form->{invnumber}, $form->{ordnumber},
  302. $form->{transdate},
  303. $form->{taxincluded}, $invamount,
  304. $form->{duedate}, $paid,
  305. $datepaid, $invnetamount,
  306. $form->{currency}, $form->{notes},
  307. $form->{department_id},
  308. $form->{ponumber}, $form->{id}
  309. );
  310. $dbh->prepare($query)->execute(@queryargs) || $form->dberror($query);
  311. if (defined $form->{approved}) {
  312. $query = qq| UPDATE $table SET approved = ? WHERE id = ?|;
  313. $dbh->prepare($query)->execute($form->{approved}, $form->{id}) ||
  314. $form->dberror($query);
  315. if (!$form->{approved} && $form->{batch_id}){
  316. if ($form->{arap} eq 'ar'){
  317. $batch_class = 'ar';
  318. } else {
  319. $batch_class = 'ap';
  320. }
  321. $query = qq|
  322. INSERT INTO voucher (batch_id, trans_id, batch_class)
  323. VALUES (?, ?, (select id from batch_class where class = ?))|;
  324. $dbh->prepare($query)->execute($form->{batch_id}, $form->{id},
  325. $batch_class) || $form->dberror($query);
  326. }
  327. }
  328. @queries = $form->run_custom_queries( $table, 'INSERT' );
  329. # update exchangerate
  330. my $buy = $form->{exchangerate};
  331. my $sell = 0;
  332. if ( $form->{vc} eq 'vendor' ) {
  333. $buy = 0;
  334. $sell = $form->{exchangerate};
  335. }
  336. if ( ( $form->{currency} ne $form->{defaultcurrency} ) && !$exchangerate ) {
  337. $form->update_exchangerate( $dbh, $form->{currency}, $form->{transdate},
  338. $buy, $sell );
  339. }
  340. my $ref;
  341. # add individual transactions
  342. foreach $ref ( @{ $form->{acc_trans}{lineitems} } ) {
  343. # insert detail records in acc_trans
  344. if ( $ref->{amount} ) {
  345. $query = qq|
  346. INSERT INTO acc_trans
  347. (trans_id, chart_id, amount,
  348. transdate, project_id, memo,
  349. fx_transaction, cleared)
  350. VALUES (?, (SELECT id FROM chart
  351. WHERE accno = ?),
  352. ?, ?, ?, ?, ?, ?)|;
  353. @queryargs = (
  354. $form->{id}, $ref->{accno},
  355. $ref->{amount} * $ml, $form->{transdate},
  356. $ref->{project_id}, $ref->{description},
  357. $ref->{fx_transaction}, $ref->{cleared}
  358. );
  359. $dbh->prepare($query)->execute(@queryargs)
  360. || $form->dberror($query);
  361. }
  362. }
  363. # save taxes
  364. foreach $ref ( @{ $form->{acc_trans}{taxes} } ) {
  365. if ( $ref->{amount} ) {
  366. $query = qq|
  367. INSERT INTO acc_trans
  368. (trans_id, chart_id, amount,
  369. transdate, fx_transaction)
  370. VALUES (?, (SELECT id FROM chart
  371. WHERE accno = ?),
  372. ?, ?, ?)|;
  373. @queryargs = (
  374. $form->{id}, $ref->{accno}, $ref->{amount} * $ml,
  375. $form->{transdate}, $ref->{fx_transaction}
  376. );
  377. $dbh->prepare($query)->execute(@queryargs)
  378. || $form->dberror($query);
  379. }
  380. }
  381. my $arap;
  382. # record ar/ap
  383. if ( ( $arap = $invamount ) ) {
  384. ($accno) = split /--/, $form->{$ARAP};
  385. $query = qq|
  386. INSERT INTO acc_trans
  387. (trans_id, chart_id, amount, transdate)
  388. VALUES (?, (SELECT id FROM chart
  389. WHERE accno = ?),
  390. ?, ?)|;
  391. @queryargs =
  392. ( $form->{id}, $accno, $invamount * -1 * $ml, $form->{transdate} );
  393. $dbh->prepare($query)->execute(@queryargs)
  394. || $form->dberror($query);
  395. }
  396. # if there is no amount force ar/ap
  397. if ( $fxinvamount == 0 ) {
  398. $arap = 1;
  399. }
  400. my $exchangerate;
  401. # add paid transactions
  402. for $i ( 1 .. $form->{paidaccounts} ) {
  403. if ( $paid{fxamount}{$i} ) {
  404. ($accno) = split( /--/, $form->{"${ARAP}_paid_$i"} );
  405. $form->{"datepaid_$i"} = $form->{transdate}
  406. unless ( $form->{"datepaid_$i"} );
  407. $exchangerate = 0;
  408. if ( $form->{currency} eq $form->{defaultcurrency} ) {
  409. $form->{"exchangerate_$i"} = 1;
  410. }
  411. else {
  412. $exchangerate =
  413. $form->check_exchangerate( $myconfig, $form->{currency},
  414. $form->{"datepaid_$i"}, $buysell );
  415. $form->{"exchangerate_$i"} =
  416. ($exchangerate)
  417. ? $exchangerate
  418. : $form->parse_amount( $myconfig,
  419. $form->{"exchangerate_$i"} );
  420. }
  421. # if there is no amount
  422. if ( $fxinvamount == 0 ) {
  423. $form->{exchangerate} = $form->{"exchangerate_$i"};
  424. }
  425. # ar/ap amount
  426. if ($arap) {
  427. ($accno) = split /--/, $form->{$ARAP};
  428. # add ar/ap
  429. $query = qq|
  430. INSERT INTO acc_trans
  431. (trans_id, chart_id,
  432. amount,transdate)
  433. VALUES (?, (SELECT id FROM chart
  434. WHERE accno = ?),
  435. ?, ?)|;
  436. @queryargs = (
  437. $form->{id}, $accno,
  438. $paid{amount}{$i} * $ml,
  439. $form->{"datepaid_$i"}
  440. );
  441. $dbh->prepare($query)->execute(@queryargs)
  442. || $form->dberror($query);
  443. }
  444. $arap = $paid{amount}{$i};
  445. # add payment
  446. if ( $paid{fxamount}{$i} ) {
  447. ($accno) = split /--/, $form->{"${ARAP}_paid_$i"};
  448. my $cleared = ( $form->{"cleared_$i"} ) ? 1 : 0;
  449. $amount = $paid{fxamount}{$i};
  450. $query = qq|
  451. INSERT INTO acc_trans
  452. (trans_id, chart_id, amount,
  453. transdate, source, memo,
  454. cleared)
  455. VALUES (?, (SELECT id FROM chart
  456. WHERE accno = ?),
  457. ?, ?, ?, ?, ?)|;
  458. @queryargs = (
  459. $form->{id}, $accno,
  460. $amount * -1 * $ml, $form->{"datepaid_$i"},
  461. $form->{"source_$i"}, $form->{"memo_$i"},
  462. $cleared
  463. );
  464. $dbh->prepare($query)->execute(@queryargs)
  465. || $form->dberror($query);
  466. if ( $form->{currency} ne $form->{defaultcurrency} ) {
  467. # exchangerate gain/loss
  468. $amount = (
  469. $form->round_amount(
  470. $paid{fxamount}{$i} * $form->{exchangerate}, 2 ) -
  471. $form->round_amount(
  472. $paid{fxamount}{$i} * $form->{"exchangerate_$i"}, 2
  473. )
  474. ) * -1;
  475. if ($amount) {
  476. my $accno_id =
  477. ( ( $amount * $ml ) > 0 )
  478. ? $fxgain_accno_id
  479. : $fxloss_accno_id;
  480. $query = qq|
  481. INSERT INTO acc_trans
  482. (trans_id,
  483. chart_id,
  484. amount,
  485. transdate,
  486. fx_transaction,
  487. cleared)
  488. VALUES (?, ?,
  489. ?,
  490. ?, '1', ?)|;
  491. @queryargs = (
  492. $form->{id}, $accno_id,
  493. $amount * $ml,
  494. $form->{"datepaid_$i"}, $cleared
  495. );
  496. $sth = $dbh->prepare($query);
  497. $sth->execute(@queryargs)
  498. || $form->dberror($query);
  499. }
  500. # exchangerate difference
  501. $amount = $paid{amount}{$i} - $paid{fxamount}{$i} + $amount;
  502. $query = qq|
  503. INSERT INTO acc_trans
  504. (trans_id, chart_id,
  505. amount,
  506. transdate,
  507. fx_transaction,
  508. cleared, source)
  509. VALUES (?, (SELECT id
  510. FROM chart
  511. WHERE accno
  512. = ?),
  513. ?, ?,
  514. '1', ?, ?)|;
  515. @queryargs = (
  516. $form->{id}, $accno,
  517. $amount * -1 * $ml,
  518. $form->{"datepaid_$i"},
  519. $cleared, $form->{"source_$i"}
  520. );
  521. $sth = $dbh->prepare($query);
  522. $sth->execute(@queryargs)
  523. || $form->dberror($query);
  524. }
  525. # update exchangerate record
  526. $buy = $form->{"exchangerate_$i"};
  527. $sell = 0;
  528. if ( $form->{vc} eq 'vendor' ) {
  529. $buy = 0;
  530. $sell = $form->{"exchangerate_$i"};
  531. }
  532. if ( ( $form->{currency} ne $form->{defaultcurrency} )
  533. && !$exchangerate )
  534. {
  535. $form->update_exchangerate( $dbh, $form->{currency},
  536. $form->{"datepaid_$i"},
  537. $buy, $sell );
  538. }
  539. }
  540. }
  541. }
  542. # save printed and queued
  543. $form->save_status($dbh);
  544. my %audittrail = (
  545. tablename => $table,
  546. reference => $form->{invnumber},
  547. formname => 'transaction',
  548. action => 'posted',
  549. id => $form->{id}
  550. );
  551. $form->audittrail( $dbh, "", \%audittrail );
  552. $form->save_recurring( $dbh, $myconfig );
  553. my $rc = $dbh->commit;
  554. $rc;
  555. }
  556. sub delete_transaction {
  557. my ( $self, $myconfig, $form ) = @_;
  558. # connect to database, turn AutoCommit off
  559. my $dbh = $form->{dbh};
  560. my $table = ( $form->{vc} eq 'customer' ) ? 'ar' : 'ap';
  561. my %audittrail = (
  562. tablename => $table,
  563. reference => $form->{invnumber},
  564. formname => 'transaction',
  565. action => 'deleted',
  566. id => $form->{id}
  567. );
  568. $form->audittrail( $dbh, "", \%audittrail );
  569. my $query = qq|DELETE FROM $table WHERE id = $form->{id}|;
  570. $dbh->do($query) || $form->dberror($query);
  571. $query = qq|DELETE FROM acc_trans WHERE trans_id = ?|;
  572. $dbh->prepare($query)->execute( $form->{id} ) || $form->dberror($query);
  573. # get spool files
  574. $query = qq|SELECT spoolfile
  575. FROM status
  576. WHERE trans_id = ?
  577. AND spoolfile IS NOT NULL|;
  578. my $sth = $dbh->prepare($query);
  579. $sth->execute( $form->{id} ) || $form->dberror($query);
  580. my $spoolfile;
  581. my @spoolfiles = ();
  582. while ( ($spoolfile) = $sth->fetchrow_array ) {
  583. push @spoolfiles, $spoolfile;
  584. }
  585. $sth->finish;
  586. $query = qq|DELETE FROM status WHERE trans_id = ?|;
  587. $dbh->prepare($query)->execute( $form->{id} ) || $form->dberror($query);
  588. # commit
  589. my $rc = $dbh->commit;
  590. if ($rc) {
  591. foreach $spoolfile (@spoolfiles) {
  592. unlink "${LedgerSMB::Sysconfig::spool}/$spoolfile" if $spoolfile;
  593. }
  594. }
  595. $rc;
  596. }
  597. # This is going to get a little awkward because it involves delving into the
  598. # acc_trans table in order to avoid catching unapproved payment vouchers.
  599. sub transactions {
  600. my ( $self, $myconfig, $form ) = @_;
  601. # connect to database
  602. my $dbh = $form->{dbh};
  603. my $null;
  604. my $var;
  605. my $paid = "a.paid";
  606. my $ml = 1;
  607. my $ARAP = 'AR';
  608. my $table = 'ar';
  609. my $buysell = 'buy';
  610. my $acc_trans_join;
  611. my $acc_trans_flds;
  612. my $approved = ($form->{approved}) ? 'TRUE' : 'FALSE';
  613. if ( $form->{vc} eq 'vendor' ) {
  614. $ml = -1;
  615. $ARAP = 'AP';
  616. $table = 'ap';
  617. $buysell = 'sell';
  618. }
  619. $form->{db_dateformat} = $myconfig->{dateformat};
  620. ( $form->{transdatefrom}, $form->{transdateto} ) =
  621. $form->from_to( $form->{year}, $form->{month}, $form->{interval} )
  622. if (($form->{year} && $form->{month}) &&
  623. (!$form->{transdatefrom} && !$form->{transdateto}));
  624. my @paidargs = ();
  625. if ( $form->{outstanding} ) {
  626. $paid = qq|
  627. SELECT SUM(ac.amount) * -1 * $ml
  628. FROM acc_trans ac
  629. JOIN chart c ON (c.id = ac.chart_id)
  630. WHERE ac.trans_id = a.id
  631. AND ($approved OR ac.approved)
  632. AND (c.link LIKE '%${ARAP}_paid%'
  633. OR c.link = '')|;
  634. if ( $form->{transdateto} ) {
  635. $paid .= qq|
  636. AND ac.transdate <= ?|;
  637. # push @paidargs, $form->{transdateto};
  638. }
  639. $form->{summary} = 1;
  640. }
  641. if ( !$form->{summary} ) {
  642. $acc_trans_flds = qq|
  643. , c.accno, ac.source,
  644. pr.projectnumber, ac.memo AS description,
  645. ac.amount AS linetotal,
  646. i.description AS linedescription|;
  647. $acc_trans_join = qq|
  648. JOIN acc_trans ac ON (a.id = ac.trans_id)
  649. JOIN chart c ON (c.id = ac.chart_id)
  650. LEFT JOIN project pr ON (pr.id = ac.project_id)
  651. LEFT JOIN invoice i ON (i.id = ac.invoice_id)|;
  652. }
  653. my $query;
  654. if ($form->{outstanding}){
  655. # $form->{ARAP} is safe since it is set in calling scripts and not passed from the UA
  656. if ($form->{transdateto} eq ''){
  657. delete $form->{transdateto};
  658. }
  659. $query = qq|
  660. SELECT a.id, a.invnumber, a.ordnumber, a.transdate,
  661. a.duedate, a.netamount, a.amount, a.amount - sum(acs.amount) AS paid,
  662. a.invoice, a.datepaid, a.terms, a.notes,
  663. a.shipvia, a.shippingpoint,
  664. vce.name, vc.meta_number,
  665. a.entity_id, a.till,
  666. ex.$buysell AS exchangerate,
  667. d.description AS department,
  668. a.ponumber $acc_trans_fields
  669. FROM $table a
  670. JOIN entity_credit_account vc ON (a.entity_credit_account = vc.id)
  671. JOIN acc_trans acs ON (acs.trans_id = a.id)
  672. JOIN entity vce ON (vc.entity_id = vce.id)
  673. JOIN chart c ON (acs.chart_id = c.id)
  674. LEFT JOIN exchangerate ex ON (ex.curr = a.curr
  675. AND ex.transdate = a.transdate)
  676. LEFT JOIN department d ON (a.department_id = d.id)
  677. $acc_trans_join
  678. WHERE c.link = '$form->{ARAP}' AND
  679. (|.$dbh->quote($form->{transdateto}) . qq| IS NULL OR
  680. |.$dbh->quote($form->{transdateto}) . qq| >= acs.transdate)
  681. AND a.approved IS TRUE AND acs.approved IS TRUE
  682. AND a.force_closed IS NOT TRUE
  683. GROUP BY a.id, a.invnumber, a.ordnumber, a.transdate, a.duedate, a.netamount,
  684. a.amount, a.terms, a.notes, a.shipvia, a.shippingpoint, vce.name,
  685. vc.meta_number, a.entity_id, a.till, ex.$buysell, d.description,
  686. a.ponumber, a.invoice, a.datepaid $acc_trans_fields
  687. HAVING abs(a.amount - (a.amount - sum(acs.amount))) > 0.005 |;
  688. } else {
  689. $query = qq|
  690. SELECT a.id, a.invnumber, a.ordnumber, a.transdate,
  691. a.duedate, a.netamount, a.amount, ($paid) AS paid,
  692. a.invoice, a.datepaid, a.terms, a.notes,
  693. a.shipvia, a.shippingpoint, ee.name AS employee,
  694. vce.name, vc.meta_number,
  695. vc.entity_id, a.till, me.name AS manager, a.curr,
  696. ex.$buysell AS exchangerate,
  697. d.description AS department,
  698. a.ponumber $acc_trans_flds
  699. FROM $table a
  700. JOIN entity_credit_account vc ON (a.entity_credit_account = vc.id)
  701. LEFT JOIN employee e ON (a.person_id = e.entity_id)
  702. LEFT JOIN employee m ON (e.manager_id = m.entity_id)
  703. LEFT JOIN entity ee ON (e.entity_id = ee.id)
  704. LEFT JOIN entity me ON (m.entity_id = me.id)
  705. JOIN entity vce ON (vc.entity_id = vce.id)
  706. LEFT JOIN exchangerate ex ON (ex.curr = a.curr
  707. AND ex.transdate = a.transdate)
  708. LEFT JOIN department d ON (a.department_id = d.id)
  709. $acc_trans_join|;
  710. }
  711. my %ordinal = (
  712. id => 1,
  713. invnumber => 2,
  714. ordnumber => 3,
  715. transdate => 4,
  716. duedate => 5,
  717. datepaid => 10,
  718. shipvia => 13,
  719. shippingpoint => 14,
  720. employee => 15,
  721. name => 16,
  722. manager => 20,
  723. curr => 21,
  724. department => 23,
  725. ponumber => 24,
  726. accno => 25,
  727. source => 26,
  728. project => 27,
  729. description => 28
  730. );
  731. my @a = ( transdate, invnumber, name );
  732. push @a, "employee" if $form->{l_employee};
  733. push @a, "manager" if $form->{l_manager};
  734. my $sortorder = $form->sort_order( \@a, \%ordinal );
  735. my $where = "";
  736. if (!$form->{outstanding}){
  737. $where = "1 = 1";
  738. }
  739. if ($form->{"meta_number"}){
  740. $where .= " AND vc.meta_number = " . $dbh->quote($form->{meta_number});
  741. }
  742. if ( $form->{"$form->{vc}_id"} ) {
  743. $form->{entity_id} = $form->{$form->{vc}."_id"};
  744. $where .= qq| AND a.entity_id = $form->{entity_id}|;
  745. }
  746. else {
  747. if ( $form->{ $form->{vc} } ) {
  748. $var = $dbh->quote( $form->like( lc $form->{ $form->{vc} } ) );
  749. $where .= " AND lower(vce.name) LIKE $var";
  750. }
  751. }
  752. for (qw(department employee)) {
  753. if ( $form->{$_} ) {
  754. ( $null, $var ) = split /--/, $form->{$_};
  755. $var = $dbh->quote($var);
  756. $where .= " AND a.${_}_id = $var";
  757. }
  758. }
  759. for (qw(invnumber ordnumber)) {
  760. if ( $form->{$_} ) {
  761. $var = $dbh->quote( $form->like( lc $form->{$_} ) );
  762. $where .= " AND lower(a.$_) LIKE $var";
  763. $form->{open} = $form->{closed} = 0;
  764. }
  765. }
  766. if ( $form->{partsid} ) {
  767. my $partsid = $dbh->quote( $form->{partsid} );
  768. $where .= " AND a.id IN (select trans_id FROM invoice
  769. WHERE parts_id = $partsid)";
  770. }
  771. for (qw(ponumber shipvia notes)) {
  772. if ( $form->{$_} ) {
  773. $var = $dbh->quote( $form->like( lc $form->{$_} ) );
  774. $where .= " AND lower(a.$_) LIKE $var";
  775. }
  776. }
  777. if ( $form->{description} ) {
  778. if ($acc_trans_flds) {
  779. $var = $dbh->quote( $form->like( lc $form->{description} ) );
  780. $where .= " AND lower(ac.memo) LIKE $var
  781. OR lower(i.description) LIKE $var";
  782. }
  783. else {
  784. $where .= " AND a.id = 0";
  785. }
  786. }
  787. if ( $form->{source} ) {
  788. if ($acc_trans_flds) {
  789. $var = $dbh->quote( $form->like( lc $form->{source} ) );
  790. $where .= " AND lower(ac.source) LIKE $var";
  791. }
  792. else {
  793. $where .= " AND a.id = 0";
  794. }
  795. }
  796. my $transdatefrom = $dbh->quote( $form->{transdatefrom} );
  797. $where .= " AND a.transdate >= $transdatefrom"
  798. if $form->{transdatefrom};
  799. my $transdateto = $dbh->quote( $form->{transdateto} );
  800. $where .= " AND a.transdate <= $transdateto" if $form->{transdateto};
  801. if ( $form->{open} || $form->{closed} ) {
  802. unless ( $form->{open} && $form->{closed} ) {
  803. $where .= " AND a.amount != a.paid" if ( $form->{open} );
  804. $where .= " AND a.amount = a.paid" if ( $form->{closed} );
  805. }
  806. }
  807. if ( $form->{till} ne "" ) {
  808. $form->{till} = $dbh->quote($form->{till});
  809. $where .= " AND a.invoice = '1'
  810. AND a.till = $form->{till}";
  811. if ( $myconfig->{role} eq 'user' ) {
  812. my $login = $dbh->quote( $form->{login} );
  813. $where .= " AND e.entity_id = (select entity_id from users where username = $login";
  814. }
  815. }
  816. if ( $form->{$ARAP} ) {
  817. my ($accno) = split /--/, $form->{$ARAP};
  818. $accno = $dbh->quote($accno);
  819. $where .= qq|
  820. AND a.id IN (SELECT ac.trans_id
  821. FROM acc_trans ac
  822. JOIN chart c ON (c.id = ac.chart_id)
  823. WHERE a.id = ac.trans_id
  824. AND c.accno = $accno)|;
  825. }
  826. if ( $form->{description} ) {
  827. $var = $dbh->quote( $form->like( lc $form->{description} ) );
  828. $where .= qq|
  829. AND (a.id IN (SELECT DISTINCT trans_id
  830. FROM acc_trans
  831. WHERE lower(memo) LIKE $var)
  832. OR a.id IN
  833. (SELECT DISTINCT trans_id
  834. FROM invoice
  835. WHERE lower(description)
  836. LIKE $var))|;
  837. }
  838. if ($form->{invoice_type}) {
  839. if ( $form->{invoice_type} == 2 ) {
  840. $where .= qq|
  841. AND a.on_hold = 'f'
  842. |;
  843. }
  844. if ($form->{invoice_type} == 3) {
  845. $where .= qq|
  846. AND a.on_hold = 't'
  847. |;
  848. }
  849. }
  850. # the third state, all invoices, sets no explicit toggles. It just selects them all, as normal.
  851. # $approved is safe as it is set to either "TRUE" or "FALSE"
  852. if ($form->{outstanding}){
  853. if ($where ne ""){
  854. $query =~ s/GROUP BY / $where \n GROUP BY /;
  855. }
  856. $query .= "\n ORDER BY $sortorder";
  857. } else {
  858. $query .= "WHERE ($approved OR a.approved) AND $where
  859. ORDER BY $sortorder";
  860. }
  861. my $sth = $dbh->prepare($query);
  862. $sth->execute(@paidargs) || $form->dberror($query);
  863. while ( my $ref = $sth->fetchrow_hashref(NAME_lc) ) {
  864. $form->db_parse_numeric(sth => $sth, hashref => $ref);
  865. $ref->{exchangerate} = 1 unless $ref->{exchangerate};
  866. if ( $ref->{linetotal} <= 0 ) {
  867. $ref->{debit} = $ref->{linetotal} * -1;
  868. $ref->{credit} = 0;
  869. }
  870. else {
  871. $ref->{debit} = 0;
  872. $ref->{credit} = $ref->{linetotal};
  873. }
  874. if ( $ref->{invoice} ) {
  875. $ref->{description} ||= $ref->{linedescription};
  876. }
  877. push @{ $form->{transactions} }, $ref;
  878. }
  879. $sth->finish;
  880. $dbh->commit;
  881. }
  882. # this is used in IS, IR to retrieve the name
  883. sub get_name {
  884. my ( $self, $myconfig, $form ) = @_;
  885. # sanitize $form->{vc}
  886. if ( $form->{vc} ne 'customer' ) {
  887. $form->{vc} = 'vendor';
  888. }
  889. else {
  890. $form->{vc} = 'customer';
  891. }
  892. # connect to database
  893. my $dbh = $form->{dbh};
  894. my $dateformat = $myconfig->{dateformat};
  895. if ( $myconfig->{dateformat} !~ /^y/ ) {
  896. my @a = split /\W/, $form->{transdate};
  897. $dateformat .= "yy" if ( length $a[2] > 2 );
  898. }
  899. if ( $form->{transdate} !~ /\W/ ) {
  900. $dateformat = 'yyyymmdd';
  901. }
  902. my $duedate;
  903. $dateformat = $dbh->quote($dateformat);
  904. my $tdate = $dbh->quote( $form->{transdate} );
  905. $duedate = ( $form->{transdate} )
  906. ? "to_date($tdate, $dateformat)
  907. + c.terms"
  908. : "current_date + c.terms";
  909. $form->{"$form->{vc}_id"} *= 1;
  910. # get customer/vendor
  911. my $query = qq|
  912. SELECT entity.name AS $form->{vc}, c.discount,
  913. c.creditlimit,
  914. c.terms, c.taxincluded,
  915. c.curr AS currency,
  916. c.language_code, $duedate AS duedate,
  917. b.discount AS tradediscount,
  918. b.description AS business
  919. FROM entity_credit_account c
  920. JOIN entity ON (entity.id = c.entity_id)
  921. LEFT JOIN business b ON (b.id = c.business_id)
  922. WHERE c.id = ?|;
  923. # TODO: Add location join
  924. @queryargs = ( $form->{"$form->{vc}_id"} );
  925. my $sth = $dbh->prepare($query);
  926. $sth->execute(@queryargs) || $form->dberror($query);
  927. $ref = $sth->fetchrow_hashref(NAME_lc);
  928. $form->db_parse_numeric(sth => $sth, hashref => $ref);
  929. if ( $form->{id} ) {
  930. for (qw(currency employee employee_id intnotes)) {
  931. delete $ref->{$_};
  932. }
  933. }
  934. for ( keys %$ref ) { $form->{$_} = $ref->{$_} }
  935. $sth->finish;
  936. # TODO: Retrieve contact records
  937. my $buysell = ( $form->{vc} eq 'customer' ) ? "buy" : "sell";
  938. # if no currency use defaultcurrency
  939. $form->{currency} =
  940. ( $form->{currency} )
  941. ? $form->{currency}
  942. : $form->{defaultcurrency};
  943. $form->{exchangerate} = 0
  944. if $form->{currency} eq $form->{defaultcurrency};
  945. if ( $form->{transdate}
  946. && ( $form->{currency} ne $form->{defaultcurrency} ) )
  947. {
  948. $form->{exchangerate} =
  949. $form->get_exchangerate( $dbh, $form->{currency}, $form->{transdate},
  950. $buysell );
  951. }
  952. $form->{forex} = $form->{exchangerate};
  953. # if no employee, default to login
  954. ( $form->{employee}, $form->{employee_id} ) = $form->get_employee($dbh)
  955. unless $form->{employee_id};
  956. my $arap = ( $form->{vc} eq 'customer' ) ? 'ar' : 'ap';
  957. my $ARAP = uc $arap;
  958. $form->{creditremaining} = $form->{creditlimit};
  959. $query = qq|
  960. SELECT SUM(amount - paid)
  961. FROM $arap
  962. WHERE id = ?|;
  963. $sth = $dbh->prepare($query);
  964. $sth->execute( $form->{"$form->{vc}_id"} )
  965. || $form->dberror($query);
  966. ( $form->{creditremaining} ) -= $sth->fetchrow_array;
  967. $sth->finish;
  968. if ( $form->{vc} ne "customer" ) {
  969. $form->{vc} = 'vendor';
  970. }
  971. $query = qq|
  972. SELECT o.amount, (SELECT e.$buysell FROM exchangerate e
  973. WHERE e.curr = o.curr
  974. AND e.transdate = o.transdate)
  975. FROM oe o
  976. WHERE o.entity_id =
  977. (select entity_id from $form->{vc} WHERE id = ?)
  978. AND o.quotation = '0' AND o.closed = '0'|;
  979. $sth = $dbh->prepare($query);
  980. $sth->execute( $form->{"$form->{vc}_id"} ) || $form->dberror($query);
  981. while ( my @ref = $sth->fetchrow_array ) {
  982. $form->db_parse_numeric(sth => $sth, arrayref => \@ref);
  983. my ($amount, $exch) = @ref;
  984. $exch = 1 unless $exch;
  985. $form->{creditremaining} -= $amount * $exch;
  986. }
  987. $sth->finish;
  988. # get shipto if we did not converted an order or invoice
  989. if ( !$form->{shipto} ) {
  990. for (
  991. qw(shiptoname shiptoaddress1 shiptoaddress2
  992. shiptocity shiptostate shiptozipcode
  993. shiptocountry shiptocontact shiptophone
  994. shiptofax shiptoemail)
  995. )
  996. {
  997. delete $form->{$_};
  998. }
  999. ## needs fixing (SELECT *)
  1000. $query = qq|
  1001. SELECT *
  1002. FROM shipto
  1003. WHERE trans_id = $form->{"$form->{vc}_id"}|;
  1004. $sth = $dbh->prepare($query);
  1005. $sth->execute || $form->dberror($query);
  1006. $ref = $sth->fetchrow_hashref(NAME_lc);
  1007. for ( keys %$ref ) { $form->{$_} = $ref->{$_} }
  1008. $sth->finish;
  1009. }
  1010. # get taxes
  1011. $query = qq|
  1012. SELECT c.accno
  1013. FROM chart c
  1014. JOIN $form->{vc}tax ct ON (ct.chart_id = c.id)
  1015. WHERE ct.$form->{vc}_id = ?|;
  1016. $sth = $dbh->prepare($query);
  1017. $sth->execute( $form->{"$form->{vc}_id"} ) || $form->dberror($query);
  1018. my %tax;
  1019. while ( $ref = $sth->fetchrow_hashref(NAME_lc) ) {
  1020. $tax{ $ref->{accno} } = 1;
  1021. }
  1022. $sth->finish;
  1023. $transdate = $dbh->quote( $form->{transdate} );
  1024. my $where = qq|AND (t.validto >= $transdate OR t.validto IS NULL)|
  1025. if $form->{transdate};
  1026. # get tax rates and description
  1027. $query = qq|
  1028. SELECT c.accno, c.description, t.rate, t.taxnumber
  1029. FROM chart c
  1030. JOIN tax t ON (c.id = t.chart_id)
  1031. WHERE c.link LIKE '%${ARAP}_tax%'
  1032. $where
  1033. ORDER BY accno, validto|;
  1034. $sth = $dbh->prepare($query);
  1035. $sth->execute || $form->dberror($query);
  1036. $form->{taxaccounts} = "";
  1037. my %a = ();
  1038. while ( $ref = $sth->fetchrow_hashref(NAME_lc) ) {
  1039. $form->db_parse_numeric(sth => $sth, hashref => $hashref);
  1040. if ( $tax{ $ref->{accno} } ) {
  1041. if ( not exists $a{ $ref->{accno} } ) {
  1042. for (qw(rate description taxnumber)) {
  1043. $form->{"$ref->{accno}_$_"} = $ref->{$_};
  1044. }
  1045. $form->{taxaccounts} .= "$ref->{accno} ";
  1046. $a{ $ref->{accno} } = 1;
  1047. }
  1048. }
  1049. }
  1050. $sth->finish;
  1051. chop $form->{taxaccounts};
  1052. # setup last accounts used for this customer/vendor
  1053. if ( !$form->{id} && $form->{type} !~ /_(order|quotation)/ ) {
  1054. $query = qq|
  1055. SELECT c.accno, c.description, c.link,
  1056. c.category,
  1057. ac.project_id,
  1058. a.department_id
  1059. FROM chart c
  1060. JOIN acc_trans ac ON (ac.chart_id = c.id)
  1061. JOIN $arap a ON (a.id = ac.trans_id)
  1062. WHERE a.entity_id = ?
  1063. AND a.id = (SELECT max(id)
  1064. FROM $arap
  1065. WHERE entity_id =
  1066. ?)
  1067. |;
  1068. $sth = $dbh->prepare($query);
  1069. $sth->execute( $form->{"$form->{vc}_id"}, $form->{"$form->{vc}_id"} )
  1070. || $form->dberror($query);
  1071. my $i = 0;
  1072. while ( $ref = $sth->fetchrow_hashref(NAME_lc) ) {
  1073. $form->{department_id} = $ref->{department_id};
  1074. if ( $ref->{link} =~ /_amount/ ) {
  1075. $i++;
  1076. $form->{"$form->{ARAP}_amount_$i"} =
  1077. "$ref->{accno}--$ref->{description}"
  1078. if $ref->{accno};
  1079. $form->{"projectnumber_$i"} =
  1080. "$ref->{projectnumber}--" . "$ref->{project_id}"
  1081. if $ref->{project_id};
  1082. }
  1083. if ( $ref->{link} eq $form->{ARAP} ) {
  1084. $form->{ $form->{ARAP} } = $form->{"$form->{ARAP}_1"} =
  1085. "$ref->{accno}--" . "$ref->{description}"
  1086. if $ref->{accno};
  1087. }
  1088. }
  1089. $sth->finish;
  1090. $query = "select description from department where id = ?";
  1091. $sth = $dbh->prepare($query);
  1092. $sth->execute($form->{department_id});
  1093. ($form->{department}) = $sth->fetchrow_array;
  1094. $form->{rowcount} = $i if ( $i && !$form->{type} );
  1095. }
  1096. $dbh->commit;
  1097. }
  1098. 1;