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