summaryrefslogtreecommitdiff
path: root/LedgerSMB/DBObject/Payment.pm
blob: 8bf22ee904d12244302f206f03e964e7daa5f1b1 (plain)
  1. =head1 NAME
  2. LedgerSMB::Payment: Payment Handling Back-end Routines for LedgerSMB
  3. =head1 SYNOPSIS
  4. Provides the functions for generating the data structures payments made in
  5. LedgerSMB. This module currently handles only basic payment logic, and does
  6. handle overpayment logic, though these features will be moved into this module
  7. in the near future.
  8. =head1 COPYRIGHT
  9. Copyright (c) 2007 The LedgerSMB Core Team. Licensed under the GNU General
  10. Public License version 2 or at your option any later version. Please see the
  11. included COPYRIGHT and LICENSE files for more information.
  12. =cut
  13. package LedgerSMB::DBObject::Payment;
  14. use base qw(LedgerSMB::DBObject);
  15. use strict;
  16. use Math::BigFloat lib => 'GMP';
  17. our $VERSION = '0.1.0';
  18. =head1 METHODS
  19. =over
  20. =item LedgerSMB::DBObject::Payment->new()
  21. Inherited from LedgerSMB::DBObject. Please see that documnetation for details.
  22. =item $payment->get_open_accounts()
  23. This function returns a list of open accounts depending on the
  24. $payment->{account_class} property. If this property is 1, it returns a list
  25. of vendor accounts, for 2, a list of customer accounts are returned.
  26. The returned list of hashrefs is stored in the $payment->{accounts} property.
  27. Each hashref has the following keys: id (entity id), name, and entity_class.
  28. An account is considered open if there are outstanding, unpaid invoices
  29. attached to it. Customer/vendor payment threshold is not considered for this
  30. calculation.
  31. =back
  32. =cut
  33. sub __validate__ {
  34. my ($self) = shift @_;
  35. # If the account class is not set, we don't know if it is a payment or a
  36. # receipt. --CT
  37. if (!$self->{account_class}) {
  38. $self->error("account_class must be set")
  39. };
  40. # We should try to re-engineer this so that we don't have to include SQL in
  41. # this file. --CT
  42. ($self->{current_date}) = $self->{dbh}->selectrow_array('select current_date');
  43. }
  44. sub get_metadata {
  45. my ($self) = @_;
  46. $self->list_open_projects();
  47. @{$self->{departments}} = $self->exec_method(funcname => 'department_list');
  48. $self->get_open_currencies();
  49. $self->{currencies} = [];
  50. for my $c (@{$self->{openCurrencies}}){
  51. push @{$self->{currencies}}, $c->{payments_get_open_currencies};
  52. }
  53. @{$self->{businesses}} = $self->exec_method(
  54. funcname => 'business_type__list'
  55. );
  56. @{$self->{debt_accounts}} = $self->exec_method(
  57. funcname => 'chart_get_ar_ap');
  58. }
  59. sub get_open_accounts {
  60. my ($self) = @_;
  61. @{$self->{accounts}} =
  62. $self->exec_method(funcname => 'payment_get_open_accounts');
  63. return @{$self->{accounts}};
  64. }
  65. =over
  66. =item $payment->get_all_accounts()
  67. This function returns a list of open or closed accounts depending on the
  68. $payment->{account_class} property. If this property is 1, it returns a list
  69. of vendor accounts, for 2, a list of customer accounts are returned.
  70. The returned list of hashrefs is stored in the $payment->{accounts} property.
  71. Each hashref has the following keys: id (entity id), name, and entity_class.
  72. =back
  73. =cut
  74. sub get_all_accounts {
  75. my ($self) = @_;
  76. @{$self->{accounts}} =
  77. $self->exec_method(funcname => 'payment_get_all_accounts');
  78. return @{$self->{accounts}};
  79. }
  80. =over
  81. =item $payment->get_open_invoices()
  82. This function returns a list of open invoices depending on the
  83. $payment->{account_class}, $payment->{entity_id}, and $payment->{curr}
  84. properties. Account classes follow the conventions above. This list is hence
  85. specific to a customer or vendor and currency as well.
  86. The returned list of hashrefs is stored in the $payment->{open_invoices}
  87. property. Each hashref has the following keys: id (entity id), name, and
  88. entity_class.
  89. =back
  90. =cut
  91. sub get_open_invoices {
  92. my ($self) = @_;
  93. @{$self->{open_invoices}} =
  94. $self->exec_method(funcname => 'payment_get_open_invoices');
  95. return @{$self->{open_invoices}};
  96. }
  97. =over
  98. =item $oayment->get_all_contact_invoices()
  99. This function returns a list of open accounts depending on the
  100. $payment->{account_class} property. If this property is 1, it returns a list
  101. of vendor accounts, for 2, a list of customer accounts are returned. Attached
  102. to each account is a list of open invoices. The data structure is somewhat
  103. complex.
  104. Each item in the list has the following keys: contact_id, contact_name, \
  105. account_number, total_due, and invoices.
  106. The invoices entry is a reference to an array of hashrefs. Each of these
  107. hashrefs has the following keys: invoice_id, invnumber, invoice_date, amount,
  108. discount, and due.
  109. These are filtered based on the (required) properties:
  110. $payment->{account_class}, $payment->{business_type}, $payment->{date_from},
  111. $payment->{date_to}, and $payment->{ar_ap_accno}.
  112. The $payment->{ar_ap_accno} property is used to filter out by AR or AP account.
  113. The following can also be optionally passed: $payment->{batch_id}. If this is
  114. patched, vouchers in the current batch will be picked up as well.
  115. The returned list of hashrefs is stored in the $payment->{contact} property.
  116. Each hashref has the following keys: id (entity id), name, and entity_class.
  117. =back
  118. =cut
  119. sub get_all_contact_invoices {
  120. my ($self) = @_;
  121. @{$self->{contacts}} =
  122. $self->exec_method(funcname => 'payment_get_all_contact_invoices');
  123. # When arrays of complex types are supported by all versions of Postgres
  124. # that this application supports, we should look at doing type conversions
  125. # in DBObject so this sort of logic is unncessary. -- CT
  126. for my $contact (@{$self->{contacts}}){
  127. my @invoices = $self->parse_array($contact->{invoices});
  128. my $processed_invoices = [];
  129. for my $invoice (@invoices){
  130. my $new_invoice = {};
  131. for (qw(invoice_id invnumber invoice_date amount discount due)){
  132. $new_invoice->{$_} = shift @$invoice;
  133. if ($_ =~ /^(amount|discount|due)$/){
  134. $new_invoice->{$_} =
  135. Math::BigFloat->new($new_invoice->{$_});
  136. }
  137. }
  138. push(@$processed_invoices, $new_invoice);
  139. }
  140. $contact->{invoice} = $processed_invoices;
  141. }
  142. return @{$self->{contacts}};
  143. }
  144. =over
  145. =item list_open_projects
  146. This method gets the current date attribute, and provides a list of open
  147. projects. The list is attached to $self->{projects} and returned.
  148. =back
  149. =cut
  150. sub list_open_projects {
  151. my ($self) = @_;
  152. @{$self->{projects}} = $self->call_procedure(
  153. procname => 'project_list_open', args => [$self->{current_date}]
  154. );
  155. return @{$self->{projects}};
  156. }
  157. =over
  158. =item list_departments
  159. This method gets the type of document as a parameter, and provides a list of departments
  160. of the required type.
  161. The list is attached to $self->{departments} and returned.
  162. =back
  163. =cut
  164. sub list_departments {
  165. my ($self) = shift @_;
  166. my @args = @_;
  167. @{$self->{departments}} = $self->call_procedure(
  168. procname => 'department_list',
  169. args => \@args
  170. );
  171. return @{$self->{departments}};
  172. }
  173. =item list_open_vc
  174. This method gets the type of vc (vendor or customer) as a parameter, and provides a list of departments
  175. of the required type.
  176. The list is attached to $self->{departments} and returned.
  177. =back
  178. =cut
  179. sub list_departments {
  180. my ($self) = shift @_;
  181. my @args = @_;
  182. @{$self->{departments}} = $self->call_procedure(
  183. procname => 'department_list',
  184. args => \@args
  185. );
  186. return @{$self->{departments}};
  187. }
  188. =item get_open_currencies
  189. This method gets a list of the open currencies inside the database, it requires that
  190. $self->{account_class} (must be 1 or 2) exist to work.
  191. =back
  192. =cut
  193. sub get_open_currencies {
  194. my ($self) = shift @_;
  195. @{$self->{openCurrencies}} = $self->exec_method( funcname => 'payments_get_open_currencies');
  196. return @{$self->{openCurrencies}};
  197. }
  198. =item list_accounting
  199. This method lists all accounts that match the role specified in account_class property and
  200. are availible to store the payment or receipts.
  201. =back
  202. =cut
  203. sub list_accounting {
  204. my ($self) = @_;
  205. @{$self->{pay_accounts}} = $self->exec_method( funcname => 'chart_list_cash');
  206. return @{$self->{pay_accounts}};
  207. }
  208. =item get_sources
  209. This method builds all the possible sources of money,
  210. in the future it will look inside the DB.
  211. =back
  212. =cut
  213. sub get_sources {
  214. my ($self, $locale) = @_;
  215. @{$self->{cash_sources}} = ($locale->text('cash'),
  216. $locale->text('check'),
  217. $locale->text('deposit'),
  218. $locale->text('other'));
  219. return @{$self->{cash_sources}};
  220. }
  221. =item get_exchange_rate(currency, date)
  222. This method gets the exchange rate for the specified currency and date
  223. =cut
  224. sub get_exchange_rate {
  225. my ($self) = shift @_;
  226. ($self->{currency}, $self->{date}) = @_;
  227. ($self->{exchangerate}) = $self->exec_method(funcname => 'currency_get_exchangerate');
  228. return $self->{exchangerate}->{currency_get_exchangerate};
  229. }
  230. =item get_default_currency
  231. This method gets the default currency
  232. =back
  233. =cut
  234. sub get_default_currency {
  235. my ($self) = shift @_;
  236. ($self->{default_currency}) = $self->call_procedure(procname => 'defaults_get_defaultcurrency');
  237. return $self->{default_currency}->{defaults_get_defaultcurrency};
  238. }
  239. =item get_current_date
  240. This method returns the system's current date
  241. =cut
  242. sub get_current_date {
  243. my ($self) = shift @_;
  244. return $self->{current_date};
  245. }
  246. =item get_vc_info
  247. This method returns the contact informatino for a customer or vendor according to
  248. $self->{account_class}
  249. =cut
  250. sub get_vc_info {
  251. my ($self) = @_;
  252. #@{$self->{vendor_customer_info}} = $self->call_procedure(procname => 'vendor_customer_info');
  253. #return @{$self->{vendor_customer_info}};
  254. }
  255. =item get_payment_detail_data
  256. This method sets appropriate project, department, etc. fields.
  257. =cut
  258. sub get_payment_detail_data {
  259. my ($self) = @_;
  260. @{$self->{cash_accounts}} = $self->exec_method(
  261. funcname => 'chart_list_cash');
  262. $self->get_metadata();
  263. my $source_inc;
  264. my $source_src;
  265. if (defined ($self->{source_start})){
  266. $self->{source_start} =~ /(\d*)\D*$/;
  267. $source_src = $1;
  268. if ($source_src) {
  269. $source_inc = $source_src;
  270. } else {
  271. $source_inc = $0;
  272. }
  273. }
  274. @{$self->{contact_invoices}} = $self->exec_method(
  275. funcname => 'payment_get_all_contact_invoices');
  276. for my $inv (@{$self->{contact_invoices}}){
  277. if (defined $self->{source_start}){
  278. my $source = $self->{source_start};
  279. $source =~ s/$source_src(\D*)$/$source_inc$1/;
  280. ++ $source_inc;
  281. $inv->{source} = $source;
  282. }
  283. my $tmp_invoices = $inv->{invoices};
  284. $inv->{invoices} = [];
  285. @{$inv->{invoices}} = $self->_parse_array($tmp_invoices);
  286. }
  287. # $self->{dbh}->commit; # Commit locks
  288. }
  289. sub post_bulk {
  290. my ($self) = @_;
  291. my $total_count = 0;
  292. my ($ref) = $self->callproc(
  293. procname => 'setting_get',
  294. args => ['queue_payments'],
  295. );
  296. my $queue_payments = $ref->{setting_get};
  297. $self->{payment_date} = $self->{datepaid};
  298. for my $contact_row (1 .. $self->{contact_count}){
  299. my $contact_id = $self->{"contact_$contact_row"};
  300. next if (!$self->{"id_$contact_id"});
  301. my $invoice_array = "{}"; # Pg Array
  302. for my $invoice_row (1 .. $self->{"invoice_count_$contact_id"}){
  303. my $invoice_id = $self->{"invoice_${contact_id}_${invoice_row}"};
  304. print STDERR "invoice_${contact_id}_${invoice_row}: $invoice_id\n";
  305. my $pay_amount = ($self->{"paid_$contact_id"} eq 'all' )
  306. ? $self->{"net_$invoice_id"}
  307. : $self->{"payment_$invoice_id"};
  308. if (!$pay_amount){
  309. $pay_amount = 0;
  310. }
  311. my $invoice_subarray = "{$invoice_id,$pay_amount}";
  312. if ($invoice_array eq '{}'){ # Omit comma
  313. $invoice_array = "{$invoice_subarray}";
  314. } else {
  315. $invoice_array =~ s/}$/,$invoice_subarray}/;
  316. }
  317. }
  318. $self->{transactions} = $invoice_array;
  319. $self->{source} = $self->{"source_$contact_id"};
  320. if ($queue_payments){
  321. my ($job_ref) = $self->exec_method(
  322. funcname => 'job__create'
  323. );
  324. $self->{job_id} = $job_ref->{job__create};
  325. $self->exec_method(
  326. funcname => 'payment_bulk_queue_entry'
  327. );
  328. } else {
  329. $self->exec_method(funcname => 'payment_bulk_post');
  330. }
  331. }
  332. $self->{queue_payments} = $queue_payments;
  333. return $self->{dbh}->commit;
  334. }
  335. sub check_job {
  336. my ($self) = @_;
  337. ($self->{job}) = $self->exec_method(funcname => 'job__status');
  338. }
  339. 1;