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