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