summaryrefslogtreecommitdiff
path: root/LedgerSMB/CP.pm
blob: f5c651bb080843892d93abd0a63cd1edacae5210 (plain)
  1. #=====================================================================
  2. # LedgerSMB
  3. # Small Medium Business Accounting software
  4. #
  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) 2003
  17. #
  18. # Author: DWS Systems Inc.
  19. # Web: http://www.sql-ledger.org
  20. #
  21. # Contributors:
  22. #
  23. #
  24. #======================================================================
  25. #
  26. # This file has undergone whitespace cleanup.
  27. #
  28. #======================================================================
  29. #
  30. # Check and receipt printing payment module backend routines
  31. # Number to text conversion routines are in
  32. # locale/{countrycode}/Num2text
  33. #
  34. #======================================================================
  35. package CP;
  36. sub new {
  37. my ($type, $countrycode) = @_;
  38. $self = {};
  39. if ($countrycode) {
  40. if (-f "locale/$countrycode/Num2text") {
  41. require "locale/$countrycode/Num2text";
  42. } else {
  43. use LedgerSMB::Num2text;
  44. }
  45. } else {
  46. use LedgerSMB::Num2text;
  47. }
  48. bless $self, $type;
  49. }
  50. sub paymentaccounts {
  51. my ($self, $myconfig, $form) = @_;
  52. # connect to database
  53. my $dbh = $form->dbconnect($myconfig);
  54. my $query = qq|SELECT accno, description, link
  55. FROM chart
  56. WHERE link LIKE '%$form->{ARAP}%'
  57. ORDER BY accno|;
  58. my $sth = $dbh->prepare($query);
  59. $sth->execute || $form->dberror($query);
  60. $form->{PR}{$form->{ARAP}} = ();
  61. $form->{PR}{"$form->{ARAP}_paid"} = ();
  62. while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
  63. foreach my $item (split /:/, $ref->{link}) {
  64. if ($item eq $form->{ARAP}) {
  65. push @{ $form->{PR}{$form->{ARAP}} }, $ref;
  66. }
  67. if ($item eq "$form->{ARAP}_paid") {
  68. push @{ $form->{PR}{"$form->{ARAP}_paid"} }, $ref;
  69. }
  70. }
  71. }
  72. $sth->finish;
  73. # get currencies and closedto
  74. $query = qq|SELECT curr, closedto, current_date
  75. FROM defaults|;
  76. ($form->{currencies}, $form->{closedto}, $form->{datepaid}) = $dbh->selectrow_array($query);
  77. if ($form->{payment} eq 'payments') {
  78. # get language codes
  79. $query = qq|SELECT *
  80. FROM language
  81. ORDER BY 2|;
  82. $sth = $dbh->prepare($query);
  83. $sth->execute || $self->dberror($query);
  84. $form->{all_language} = ();
  85. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  86. push @{ $form->{all_language} }, $ref;
  87. }
  88. $sth->finish;
  89. $form->all_departments($myconfig, $dbh, $form->{vc});
  90. }
  91. $dbh->disconnect;
  92. }
  93. sub get_openvc {
  94. my ($self, $myconfig, $form) = @_;
  95. my $dbh = $form->dbconnect($myconfig);
  96. my $arap = ($form->{vc} eq 'customer') ? 'ar' : 'ap';
  97. my $query = qq|SELECT count(*)
  98. FROM $form->{vc} ct, $arap a
  99. WHERE a.$form->{vc}_id = ct.id
  100. AND a.amount != a.paid|;
  101. my ($count) = $dbh->selectrow_array($query);
  102. my $sth;
  103. my $ref;
  104. my $i = 0;
  105. my $where = qq|WHERE a.$form->{vc}_id = ct.id
  106. AND a.amount != a.paid|;
  107. if ($form->{$form->{vc}}) {
  108. my $var = $form->like(lc $form->{$form->{vc}});
  109. $where .= " AND lower(name) LIKE '$var'";
  110. }
  111. # build selection list
  112. $query = qq|SELECT DISTINCT ct.*
  113. FROM $form->{vc} ct, $arap a
  114. $where
  115. ORDER BY name|;
  116. $sth = $dbh->prepare($query);
  117. $sth->execute || $form->dberror($query);
  118. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  119. $i++;
  120. push @{ $form->{name_list} }, $ref;
  121. }
  122. $sth->finish;
  123. $form->all_departments($myconfig, $dbh, $form->{vc});
  124. # get language codes
  125. $query = qq|SELECT *
  126. FROM language
  127. ORDER BY 2|;
  128. $sth = $dbh->prepare($query);
  129. $sth->execute || $self->dberror($query);
  130. $form->{all_language} = ();
  131. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  132. push @{ $form->{all_language} }, $ref;
  133. }
  134. $sth->finish;
  135. # get currency for first name
  136. if (@{ $form->{name_list} }) {
  137. $query = qq|SELECT curr
  138. FROM $form->{vc}
  139. WHERE id = $form->{name_list}->[0]->{id}|;
  140. ($form->{currency}) = $dbh->selectrow_array($query);
  141. $form->{currency} ||= $form->{defaultcurrency};
  142. }
  143. $dbh->disconnect;
  144. $i;
  145. }
  146. sub get_openinvoices {
  147. my ($self, $myconfig, $form) = @_;
  148. my $null;
  149. my $department_id;
  150. # connect to database
  151. my $dbh = $form->dbconnect($myconfig);
  152. my $where = qq|WHERE a.$form->{vc}_id = $form->{"$form->{vc}_id"}
  153. AND a.amount != a.paid|;
  154. $where .= qq| AND a.curr = '$form->{currency}'| if $form->{currency};
  155. my $sortorder = "transdate, invnumber";
  156. my ($buysell);
  157. if ($form->{vc} eq 'customer') {
  158. $buysell = "buy";
  159. } else {
  160. $buysell = "sell";
  161. }
  162. if ($form->{payment} eq 'payments') {
  163. $where = qq|WHERE a.amount != a.paid|;
  164. $where .= qq| AND a.curr = '$form->{currency}'| if $form->{currency};
  165. if ($form->{duedatefrom}) {
  166. $where .= qq| AND a.duedate >= '$form->{duedatefrom}'|;
  167. }
  168. if ($form->{duedateto}) {
  169. $where .= qq| AND a.duedate <= '$form->{duedateto}'|;
  170. }
  171. $sortorder = "name, transdate";
  172. }
  173. ($null, $department_id) = split /--/, $form->{department};
  174. if ($department_id) {
  175. $where .= qq| AND a.department_id = $department_id|;
  176. }
  177. my $query = qq|SELECT a.id, a.invnumber, a.transdate, a.amount, a.paid,
  178. a.curr, c.name, a.$form->{vc}_id, c.language_code
  179. FROM $form->{arap} a
  180. JOIN $form->{vc} c ON (c.id = a.$form->{vc}_id)
  181. $where
  182. ORDER BY $sortorder|;
  183. my $sth = $dbh->prepare($query);
  184. $sth->execute || $form->dberror($query);
  185. $query = qq|SELECT s.spoolfile
  186. FROM status s
  187. WHERE s.formname = '$form->{formname}'
  188. AND s.trans_id = ?|;
  189. my $vth = $dbh->prepare($query);
  190. my $spoolfile;
  191. while ($ref = $sth->fetchrow_hashref(NAME_lc)) {
  192. # if this is a foreign currency transaction get exchangerate
  193. $ref->{exchangerate} = $form->get_exchangerate($dbh, $ref->{curr}, $ref->{transdate}, $buysell) if ($form->{currency} ne $form->{defaultcurrency});
  194. $vth->execute($ref->{id});
  195. $ref->{queue} = "";
  196. while (($spoolfile) = $vth->fetchrow_array) {
  197. $ref->{queued} .= "$form->{formname} $spoolfile ";
  198. }
  199. $vth->finish;
  200. $ref->{queued} =~ s/ +$//g;
  201. push @{ $form->{PR} }, $ref;
  202. }
  203. $sth->finish;
  204. $dbh->disconnect;
  205. }
  206. sub post_payment {
  207. my ($self, $myconfig, $form) = @_;
  208. # connect to database, turn AutoCommit off
  209. my $dbh = $form->dbconnect_noauto($myconfig);
  210. my $sth;
  211. my ($paymentaccno) = split /--/, $form->{account};
  212. # if currency ne defaultcurrency update exchangerate
  213. if ($form->{currency} ne $form->{defaultcurrency}) {
  214. $form->{exchangerate} = $form->parse_amount($myconfig, $form->{exchangerate});
  215. if ($form->{vc} eq 'customer') {
  216. $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, $form->{exchangerate}, 0);
  217. } else {
  218. $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, 0, $form->{exchangerate});
  219. }
  220. } else {
  221. $form->{exchangerate} = 1;
  222. }
  223. my $query = qq|SELECT fxgain_accno_id, fxloss_accno_id
  224. FROM defaults|;
  225. my ($fxgain_accno_id, $fxloss_accno_id) = $dbh->selectrow_array($query);
  226. my ($buysell);
  227. if ($form->{vc} eq 'customer') {
  228. $buysell = "buy";
  229. } else {
  230. $buysell = "sell";
  231. }
  232. my $ml;
  233. my $where;
  234. if ($form->{ARAP} eq 'AR') {
  235. $ml = 1;
  236. $where = qq| (c.link = 'AR' OR c.link LIKE 'AR:%') |;
  237. } else {
  238. $ml = -1;
  239. $where = qq| (c.link = 'AP' OR c.link LIKE '%:AP' OR c.link LIKE '%:AP:%') |;
  240. }
  241. my $paymentamount = $form->parse_amount($myconfig, $form->{amount});
  242. # query to retrieve paid amount
  243. $query = qq|SELECT paid
  244. FROM $form->{arap}
  245. WHERE id = ?
  246. FOR UPDATE|;
  247. my $pth = $dbh->prepare($query) || $form->dberror($query);
  248. my %audittrail;
  249. # go through line by line
  250. for my $i (1 .. $form->{rowcount}) {
  251. $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
  252. $form->{"due_$i"} = $form->parse_amount($myconfig, $form->{"due_$i"});
  253. if ($form->{"checked_$i"} && $form->{"paid_$i"}) {
  254. $paymentamount -= $form->{"paid_$i"};
  255. # get exchangerate for original
  256. $query = qq|SELECT $buysell
  257. FROM exchangerate e
  258. JOIN $form->{arap} a ON (a.transdate = e.transdate)
  259. WHERE e.curr = '$form->{currency}'
  260. AND a.id = $form->{"id_$i"}|;
  261. my ($exchangerate) = $dbh->selectrow_array($query);
  262. $exchangerate = 1 unless $exchangerate;
  263. $query = qq|SELECT c.id
  264. FROM chart c
  265. JOIN acc_trans a ON (a.chart_id = c.id)
  266. WHERE $where
  267. AND a.trans_id = $form->{"id_$i"}|;
  268. my ($id) = $dbh->selectrow_array($query);
  269. $amount = $form->round_amount($form->{"paid_$i"} * $exchangerate, 2);
  270. # add AR/AP
  271. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate, amount)
  272. VALUES ($form->{"id_$i"}, $id, '$form->{datepaid}', $amount * $ml)|;
  273. $dbh->do($query) || $form->dberror($query);
  274. # add payment
  275. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
  276. amount, source, memo)
  277. VALUES ($form->{"id_$i"}, (SELECT id
  278. FROM chart
  279. WHERE accno = '$paymentaccno'),
  280. '$form->{datepaid}', $form->{"paid_$i"} * $ml * -1, |
  281. .$dbh->quote($form->{source}).qq|, |
  282. .$dbh->quote($form->{memo}).qq|)|;
  283. $dbh->do($query) || $form->dberror($query);
  284. # add exchangerate difference if currency ne defaultcurrency
  285. $amount = $form->round_amount($form->{"paid_$i"} * ($form->{exchangerate} - 1), 2);
  286. if ($amount) {
  287. # exchangerate difference
  288. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
  289. amount, cleared, fx_transaction, source)
  290. VALUES ($form->{"id_$i"}, (SELECT id
  291. FROM chart
  292. WHERE accno = '$paymentaccno'),
  293. '$form->{datepaid}', $amount * $ml * -1, '0', '1', |
  294. .$dbh->quote($form->{source}).qq|)|;
  295. $dbh->do($query) || $form->dberror($query);
  296. # gain/loss
  297. $amount = ($form->round_amount($form->{"paid_$i"} * $exchangerate,2) - $form->round_amount($form->{"paid_$i"} * $form->{exchangerate},2)) * $ml * -1;
  298. if ($amount) {
  299. my $accno_id = ($amount > 0) ? $fxgain_accno_id : $fxloss_accno_id;
  300. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
  301. amount, cleared, fx_transaction)
  302. VALUES ($form->{"id_$i"}, $accno_id,
  303. '$form->{datepaid}', $amount, '0', '1')|;
  304. $dbh->do($query) || $form->dberror($query);
  305. }
  306. }
  307. $form->{"paid_$i"} = $form->round_amount($form->{"paid_$i"} * $exchangerate, 2);
  308. $pth->execute($form->{"id_$i"}) || $form->dberror;
  309. ($amount) = $pth->fetchrow_array;
  310. $pth->finish;
  311. $amount += $form->{"paid_$i"};
  312. # update AR/AP transaction
  313. $query = qq|UPDATE $form->{arap}
  314. SET paid = $amount,
  315. datepaid = '$form->{datepaid}'
  316. WHERE id = $form->{"id_$i"}|;
  317. $dbh->do($query) || $form->dberror($query);
  318. %audittrail = ( tablename => $form->{arap},
  319. reference => $form->{source},
  320. formname => $form->{formname},
  321. action => 'posted',
  322. id => $form->{"id_$i"} );
  323. $form->audittrail($dbh, "", \%audittrail);
  324. }
  325. }
  326. # record a AR/AP with a payment
  327. if ($form->round_amount($paymentamount, 2)) {
  328. $form->{invnumber} = "";
  329. OP::overpayment("", $myconfig, $form, $dbh, $paymentamount, $ml, 1);
  330. }
  331. my $rc = $dbh->commit;
  332. $dbh->disconnect;
  333. $rc;
  334. }
  335. sub post_payments {
  336. my ($self, $myconfig, $form) = @_;
  337. # connect to database, turn AutoCommit off
  338. my $dbh = $form->dbconnect_noauto($myconfig);
  339. my $sth;
  340. my ($paymentaccno) = split /--/, $form->{account};
  341. # if currency ne defaultcurrency update exchangerate
  342. if ($form->{currency} ne $form->{defaultcurrency}) {
  343. $form->{exchangerate} = $form->parse_amount($myconfig, $form->{exchangerate});
  344. if ($form->{vc} eq 'customer') {
  345. $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, $form->{exchangerate}, 0);
  346. } else {
  347. $form->update_exchangerate($dbh, $form->{currency}, $form->{datepaid}, 0, $form->{exchangerate});
  348. }
  349. } else {
  350. $form->{exchangerate} = 1;
  351. }
  352. my $query = qq|SELECT fxgain_accno_id, fxloss_accno_id
  353. FROM defaults|;
  354. my ($fxgain_accno_id, $fxloss_accno_id) = $dbh->selectrow_array($query);
  355. my ($buysell);
  356. if ($form->{vc} eq 'customer') {
  357. $buysell = "buy";
  358. } else {
  359. $buysell = "sell";
  360. }
  361. my $ml;
  362. my $where;
  363. if ($form->{ARAP} eq 'AR') {
  364. $ml = 1;
  365. $where = qq| (c.link = 'AR' OR c.link LIKE 'AR:%') |;
  366. } else {
  367. $ml = -1;
  368. $where = qq| (c.link = 'AP' OR c.link LIKE '%:AP' OR c.link LIKE '%:AP:%') |;
  369. }
  370. # get AR/AP account
  371. $query = qq|SELECT c.accno
  372. FROM chart c
  373. JOIN acc_trans ac ON (ac.chart_id = c.id)
  374. WHERE trans_id = ?
  375. AND $where|;
  376. my $ath = $dbh->prepare($query) || $form->dberror($query);
  377. # query to retrieve paid amount
  378. $query = qq|SELECT paid
  379. FROM $form->{arap}
  380. WHERE id = ?
  381. FOR UPDATE|;
  382. my $pth = $dbh->prepare($query) || $form->dberror($query);
  383. my %audittrail;
  384. my $overpayment = 0;
  385. my $accno_id;
  386. # go through line by line
  387. for my $i (1 .. $form->{rowcount}) {
  388. $ath->execute($form->{"id_$i"});
  389. ($form->{$form->{ARAP}}) = $ath->fetchrow_array;
  390. $ath->finish;
  391. $form->{"paid_$i"} = $form->parse_amount($myconfig, $form->{"paid_$i"});
  392. $form->{"due_$i"} = $form->parse_amount($myconfig, $form->{"due_$i"});
  393. if ($form->{"$form->{vc}_id_$i"} ne $sameid) {
  394. # record a AR/AP with a payment
  395. if ($overpayment > 0 && $form->{$form->{ARAP}}) {
  396. $form->{invnumber} = "";
  397. OP::overpayment("", $myconfig, $form, $dbh, $overpayment, $ml, 1);
  398. }
  399. $overpayment = 0;
  400. $form->{"$form->{vc}_id"} = $form->{"$form->{vc}_id_$i"};
  401. for (qw(source memo)) { $form->{$_} = $form->{"${_}_$i"} }
  402. }
  403. if ($form->{"checked_$i"} && $form->{"paid_$i"}) {
  404. $overpayment += ($form->{"paid_$i"} - $form->{"due_$i"});
  405. # get exchangerate for original
  406. $query = qq|SELECT $buysell
  407. FROM exchangerate e
  408. JOIN $form->{arap} a ON (a.transdate = e.transdate)
  409. WHERE e.curr = '$form->{currency}'
  410. AND a.id = $form->{"id_$i"}|;
  411. my ($exchangerate) = $dbh->selectrow_array($query);
  412. $exchangerate ||= 1;
  413. $query = qq|SELECT c.id
  414. FROM chart c
  415. JOIN acc_trans a ON (a.chart_id = c.id)
  416. WHERE $where
  417. AND a.trans_id = $form->{"id_$i"}|;
  418. my ($id) = $dbh->selectrow_array($query);
  419. $paid = ($form->{"paid_$i"} > $form->{"due_$i"}) ? $form->{"due_$i"} : $form->{"paid_$i"};
  420. $amount = $form->round_amount($paid * $exchangerate, 2);
  421. # add AR/AP
  422. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate, amount)
  423. VALUES ($form->{"id_$i"}, $id, '$form->{datepaid}',
  424. $amount * $ml)|;
  425. $dbh->do($query) || $form->dberror($query);
  426. $query = qq|SELECT id
  427. FROM chart
  428. WHERE accno = '$paymentaccno'|;
  429. ($accno_id) = $dbh->selectrow_array($query);
  430. # add payment
  431. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
  432. amount, source, memo)
  433. VALUES ($form->{"id_$i"}, $accno_id, '$form->{datepaid}',
  434. $paid * $ml * -1, |
  435. .$dbh->quote($form->{source}).qq|, |
  436. .$dbh->quote($form->{memo}).qq|)|;
  437. $dbh->do($query) || $form->dberror($query);
  438. # add exchangerate difference if currency ne defaultcurrency
  439. $amount = $form->round_amount($paid * ($form->{exchangerate} - 1) * $ml * -1, 2);
  440. if ($amount) {
  441. # exchangerate difference
  442. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
  443. amount, source)
  444. VALUES ($form->{"id_$i"}, $accno_id, '$form->{datepaid}',
  445. $amount, |
  446. .$dbh->quote($form->{source}).qq|)|;
  447. $dbh->do($query) || $form->dberror($query);
  448. # gain/loss
  449. $amount = ($form->round_amount($paid * $exchangerate,2) - $form->round_amount($paid * $form->{exchangerate},2)) * $ml * -1;
  450. if ($amount) {
  451. $accno_id = ($amount > 0) ? $fxgain_accno_id : $fxloss_accno_id;
  452. $query = qq|INSERT INTO acc_trans (trans_id, chart_id, transdate,
  453. amount, fx_transaction)
  454. VALUES ($form->{"id_$i"}, $accno_id,
  455. '$form->{datepaid}', $amount, '1')|;
  456. $dbh->do($query) || $form->dberror($query);
  457. }
  458. }
  459. $paid = $form->round_amount($paid * $exchangerate, 2);
  460. $pth->execute($form->{"id_$i"}) || $form->dberror;
  461. ($amount) = $pth->fetchrow_array;
  462. $pth->finish;
  463. $amount += $paid;
  464. # update AR/AP transaction
  465. $query = qq|UPDATE $form->{arap}
  466. SET paid = $amount,
  467. datepaid = '$form->{datepaid}'
  468. WHERE id = $form->{"id_$i"}|;
  469. $dbh->do($query) || $form->dberror($query);
  470. %audittrail = ( tablename => $form->{arap},
  471. reference => $form->{source},
  472. formname => $form->{formname},
  473. action => 'posted',
  474. id => $form->{"id_$i"} );
  475. $form->audittrail($dbh, "", \%audittrail);
  476. }
  477. $sameid = $form->{"$form->{vc}_id_$i"};
  478. }
  479. # record a AR/AP with a payment
  480. if ($overpayment > 0 && $form->{$form->{ARAP}}) {
  481. $form->{invnumber} = "";
  482. OP::overpayment("", $myconfig, $form, $dbh, $overpayment, $ml, 1);
  483. }
  484. my $rc = $dbh->commit;
  485. $dbh->disconnect;
  486. $rc;
  487. }
  488. 1;