diff options
-rw-r--r-- | LedgerSMB/Form.pm | 2 | ||||
-rw-r--r-- | LedgerSMB/Sysconfig.pm | 1 | ||||
-rw-r--r-- | UI/payments/payment1.html | 14 | ||||
-rw-r--r-- | UI/payments/payment2.html | 26 | ||||
-rw-r--r-- | scripts/payment.pl | 81 | ||||
-rw-r--r-- | sql/modules/Payment.sql | 28 |
6 files changed, 117 insertions, 35 deletions
diff --git a/LedgerSMB/Form.pm b/LedgerSMB/Form.pm index b8cd8f70..1d61ac09 100644 --- a/LedgerSMB/Form.pm +++ b/LedgerSMB/Form.pm @@ -89,7 +89,7 @@ sub new { my $argstr = shift; - if ($ENV{CONTENT_LENGTH} > $LedgerSMB::Sysconfig::max_post_size; ) { + if ($ENV{CONTENT_LENGTH} > $LedgerSMB::Sysconfig::max_post_size) { print "Status: 413\n Request entity too large\n\n"; die "Error: Request entity too large\n"; } diff --git a/LedgerSMB/Sysconfig.pm b/LedgerSMB/Sysconfig.pm index 58267be8..1d93ce8f 100644 --- a/LedgerSMB/Sysconfig.pm +++ b/LedgerSMB/Sysconfig.pm @@ -7,7 +7,6 @@ package LedgerSMB::Sysconfig; use LedgerSMB::Form; use Config::Std; use DBI qw(:sql_types); - binmode STDOUT, ':utf8'; binmode STDERR, ':utf8'; diff --git a/UI/payments/payment1.html b/UI/payments/payment1.html index 1df90592..d928dd33 100644 --- a/UI/payments/payment1.html +++ b/UI/payments/payment1.html @@ -17,11 +17,15 @@ <form name="search" method="post" action="payment.pl"> <?lsmb PROCESS elements.html # Include form elements helper. -?> -<?lsmb login.type = 'hidden' ; PROCESS input element_data=login -?> -<?lsmb accountclass.type = 'hidden'; PROCESS input element_data=accountclass -?> -<?lsmb type.type = "hidden"; - PROCESS input element_data=type; - -?> +<?lsmb login.type = 'hidden' ; INCLUDE input element_data=login -?> +<?lsmb accountclass.type = 'hidden'; INCLUDE input element_data=accountclass -?> +<?lsmb # The first_load field is required on payment2.html to initialize discounts the first time, on each subsequent update it wont exist-?> +<?lsmb INCLUDE input element_data={ + name => 'first_load', + id => 'first_load', + type => 'hidden', + value => 'on'} ?> +<?lsmb type.type = "hidden"; INCLUDE input element_data=type; -?> <table width="100%"> <tr id="top-bar" class="listtop"> <th id="top-bar-header" class="listtop"><label id="top-bar-header-label"><?lsmb text('Receipts') ?></th> diff --git a/UI/payments/payment2.html b/UI/payments/payment2.html index e2824349..58a997d6 100644 --- a/UI/payments/payment2.html +++ b/UI/payments/payment2.html @@ -157,12 +157,19 @@ onLoad="maximize_minimize_on_load('div_topay_state', 'UI/payments/img/down.gif', <td><?lsmb row.invoice_date ?></td> <td><?lsmb row.amount ?></td> <td><?lsmb row.paid ?></td> +<<<<<<< .mine <td><?lsmb row.discount ?></td> + <td align="center"><input name="<?lsmb "optional_discount_$row.invoice.id" -?>" id="<?lsmb + "optional_discount_$row.invoice.id" -?>" type="checkbox" class="checkbox"<?lsmb IF + row.optional_discount OR first_load -?> checked <?lsmb END -?> ></td> +======= + <td><?lsmb row.discount ?></td> +>>>>>>> .r2199 <td><?lsmb row.due ?></td> <?lsmb IF defaultcurrency.text != curr.value ?> <td><?lsmb row.exchange_rate ?></td> - <td><?lsmb row.due_fx ?></td> - <td><div id="<?lsmb "div_topay_invoice_$i" ?>"><?lsmb row.topay ?></div></td> + <td><?lsmb row.due ?></td> + <td><div id="<?lsmb "div_topay_invoice_$i" ?>"><?lsmb row.due_fx ?></div></td> <?lsmb END ?> <?lsmb #This should be computed and updated to the div using ?> <td><?lsmb row.topay_fx.id = row.topay_fx.name ;INCLUDE input element_data=row.topay_fx; @@ -290,11 +297,24 @@ onLoad="maximize_minimize_on_load('div_topay_state', 'UI/payments/img/down.gif', <option value="<?lsmb item -?>"><?lsmb item -?></option> <?lsmb END -?> </select> - <input name="overpayment_source2_<?lsmb overpayment_item ?>" id="overpayment_source2_<?lsmb overpayment_item ?>" /> + <input name="overpayment_source2_<?lsmb overpayment_item -?>" id="overpayment_source2_<?lsmb overpayment_item -?>"/> <input type="hidden" name="overpayment_qty" id="overpayment_qty" value="<?lsmb overpayment_item ?>" /> </td> +<<<<<<< .mine + <td align="center"><input name="overpayment_memo_<?lsmb overpayment_item -?>" id="overpayment_memo_<?lsmboverpayment_item ?>" /></td> + <td align="center"> + <input + name="overpayment_topay_<?lsmb overpayment_item -?>" + id="overpayment_topay_<?lsmboverpayment_item ?>" + value="<?lsmb IF unhandled_overpayment.value > 0 -?> + <?lsmb unhandled_overpayment.value -?> + <?lsmb END -?>" + /> + </td> +======= <td align="center"><input name="overpayment_memo_<?lsmb overpayment_item -?>" id="overpayment_memo_<?lsmboverpayment_item ?>" /></td> <td align="center"><input name="overpayment_topay_<?lsmb overpayment_item -?>" id="overpayment_topay_<?lsmboverpayment_item ?>" /></td> +>>>>>>> .r2199 <td align="center"><input type="checkbox" name="overpayment_checkbox_<?lsmb overpayment_item -?>"/></td> </tr> <tr class="listsubtotal"> diff --git a/scripts/payment.pl b/scripts/payment.pl index 986cd764..dd3a309d 100644 --- a/scripts/payment.pl +++ b/scripts/payment.pl @@ -555,6 +555,7 @@ my @array_options; my @currency_options; my $exchangerate; # LETS GET THE CUSTOMER/VENDOR INFORMATION + ($Payment->{entity_credit_id}, $Payment->{company_name}) = split /--/ , $request->{'vendor-customer'}; # WE NEED TO RETRIEVE A BILLING LOCATION, THIS IS HARDCODED FOR NOW... Should we change it? @@ -579,7 +580,8 @@ my @account_options = $Payment->list_accounting(); # LETS GET THE POSSIBLE SOURCES my @sources_options = $Payment->get_sources(\%$locale); # WE MUST PREPARE THE ENTITY INFORMATION -@array_options = $Payment->get_vc_info(); +#@array_options = $Payment->get_vc_info();# IS THIS WORKING? + # LETS BUILD THE CURRENCIES INFORMATION # FIRST, WE NEED TO KNOW THE DEFAULT CURRENCY my $default_currency = $Payment->get_default_currency(); @@ -595,6 +597,7 @@ my @column_headers = ({text => $locale->text('Invoice')}, {text => $locale->text('Total').$default_currency_text}, {text => $locale->text('Paid').$default_currency_text}, {text => $locale->text('Discount').$default_currency_text}, + {text => $locale->text('Apply Disc')}, {text => $locale->text('Amount Due').$default_currency_text}, {text => $locale->text('To pay').$default_currency_text} ); @@ -642,27 +645,46 @@ my @column_headers = ({text => $locale->text('Invoice')}, my @invoice_data; my @topay_state; # WE WILL USE THIS TO HELP UI TO DETERMINE WHAT IS VISIBLE @array_options = $Payment->get_open_invoices(); - +my $unhandled_overpayment; for my $ref (0 .. $#array_options) { if ( !$request->{"checkbox_$array_options[$ref]->{invoice_id}"}) { # SHOULD I APPLY DISCCOUNTS? - + $request->{"optional_discount_$array_options[$ref]->{invoice_id}"} = $request->{first_load}? "on": $request->{"optional_discount_$array_options[$ref]->{invoice_id}"}; + # LETS SET THE EXCHANGERATE VALUES my $due_fx; my $topay_fx_value; if ("$exchangerate") { $topay_fx_value = $due_fx = "$array_options[$ref]->{due}"/"$exchangerate" - "$array_options[$ref]->{discount}"/"$exchangerate"; + if ($request->{"optional_discount_$array_options[$ref]->{invoice_id}"}) { + $topay_fx_value = $due_fx = $due_fx - "$array_options[$ref]->{discount}"/"$exchangerate"; + } } else { $topay_fx_value = $due_fx = "N/A"; } +# We need to check for unhandled overpayment, see the post function for details +# First we will see if the discount should apply? + my $temporary_discount = 0; + if (($request->{"optional_discount_$array_options[$ref]->{invoice_id}"})&&($due_fx <= $request->{"topay_fx_$array_options[$ref]->{invoice_id}"} + $array_options[$ref]->{discount}/"$exchangerate")) { + $temporary_discount = "$array_options[$ref]->{discount}"/"$exchangerate"; + + } +# We need to compute the unhandled_overpayment, notice that all the values inside the if already have +# the exchangerate applied + if ( $due_fx < $request->{"topay_fx_$array_options[$ref]->{invoice_id}"}) { + # We need to store all the overpayments so we can use it on the screen + $unhandled_overpayment = $unhandled_overpayment + $request->{"topay_fx_$array_options[$ref]->{invoice_id}"} - $due_fx ; + $request->{"topay_fx_$array_options[$ref]->{invoice_id}"} = $due_fx; + } push @invoice_data, { invoice => { number => $array_options[$ref]->{invnumber}, id => $array_options[$ref]->{invoice_id}, href => 'ar.pl?id='."$array_options[$ref]->{invoice_id}" }, invoice_date => "$array_options[$ref]->{invoice_date}", amount => "$array_options[$ref]->{amount}", - due => "$array_options[$ref]->{due}" - "$array_options[$ref]->{discount}", + due => $request->{"optional_discount_$array_options[$ref]->{invoice_id}"}? "$array_options[$ref]->{due}" - "$array_options[$ref]->{discount}": "$array_options[$ref]->{due}", paid => "$array_options[$ref]->{amount}" - "$array_options[$ref]->{due}", - discount => "$array_options[$ref]->{discount}", + discount => $request->{"optional_discount_$array_options[$ref]->{invoice_id}"} ? "$array_options[$ref]->{discount}" : 0 , + optional_discount => $request->{"optional_discount_$array_options[$ref]->{invoice_id}"}, exchange_rate => "$exchangerate", due_fx => $due_fx, # This was set at the begining of the for statement topay => "$array_options[$ref]->{due}" - "$array_options[$ref]->{discount}", @@ -738,7 +760,10 @@ if (${LedgerSMB::Sysconfig::latex}) { push @format_options, {value => 2, text => "PDF" }, {value => 3, text => "POSTSCRIPT" }; } # LETS BUILD THE SELECTION FOR THE UI +# Notice that the first data inside this selection is the firs_load, this +# will help payment2.html to know wether it is beeing called for the first time my $select = { + first_load => $request->{first_load}, stylesheet => $request->{_user}->{stylesheet}, header => { text => $request->{type} eq 'receipt' ? $locale->text('Receipt') : $locale->text('Payment') }, type => { name => 'type', @@ -774,7 +799,7 @@ my $select = { vendorcustomer => { name => 'vendor-customer', value => $request->{'vendor-customer'} }, - + unhandled_overpayment => { name => 'unhandledoverpayment', value => $unhandled_overpayment } , vc => { name => $Payment->{company_name}, # We will assume that the first Billing Information as default address => [ {text => $vc_options[0]->{'line_one'}}, {text => $vc_options[0]->{'line_two'}}, @@ -808,6 +833,7 @@ eval {$template->render($select) }; } + =pod =item post_payment @@ -824,7 +850,7 @@ my ($request) = @_; my $locale = $request->{_locale}; my $Payment = LedgerSMB::DBObject::Payment->new({'base' => $request}); # LETS GET THE CUSTOMER/VENDOR INFORMATION -($Payment->{entity_id}, $Payment->{company_name}) = split /--/ , $request->{'vendor-customer'}; +($Payment->{entity_credit_id}, $Payment->{company_name}) = split /--/ , $request->{'vendor-customer'}; # LETS GET THE DEPARTMENT INFO # WE HAVE TO SET $dbPayment->{department_id} in order to process if ($request->{department}) { @@ -849,10 +875,11 @@ $Payment->{approved} = 'true'; # Variable definition # # We use the prefix op to refer to the overpayment variables. -my $overpayment; # This variable might be fuzzy, we are using it to handle invalid data - # i.e. a user set an overpayment qty inside an invoice. +my $unhandled_overpayment = 0; # This variable might be fuzzy, we are using it to handle invalid data + # i.e. a user set an overpayment qty inside an invoice. my @array_options; my @amount; +my @discount; my @cash_account_id; my @source; my @transaction_id; @@ -865,33 +892,53 @@ my @op_account_id; # We need the invoices in order to process the income data, this is done this way # since the data we have isn't indexed in any way. # - +# Ok, we want to use the disccount information in order to do some accounting movements, +# we will process it with the same logic for a regular payment, and see where does this leave us. +@array_options = $Payment->get_entity_credit_account();# We need to know the disccount account +my $discount_account_id = $array_options[0]->{discount}; @array_options = $Payment->get_open_invoices(); for my $ref (0 .. $#array_options) { if ( !$request->{"checkbox_$array_options[$ref]->{invoice_id}"}) { + # First i have to determine if discounts will apply + # we will assume that a discount should apply only + # if this is the last payment of an invoice + my $temporary_discount = 0; + if (($request->{"optional_discount_$array_options[$ref]->{invoice_id}"})&&("$array_options[$ref]->{due}"/"$request->{exrate}" <= $request->{"topay_fx_$array_options[$ref]->{invoice_id}"} + $array_options[$ref]->{discount})) { + $temporary_discount = $array_options[$ref]->{discount}; + } # # The prefix cash is to set the movements of the cash accounts, # same names are used for ap/ar accounts w/o the cash prefix. # - # Maybe i should move this to another sub, so i can call it from payment2 as well :). D.M. - if ($array_options[$ref]->{amount} < $request->{"topay_$array_options[$ref]->{invoice_id}"} ) { - # THERE IS AN OVERPAYMENT!, we should store it and see if we can use it on the UI - $overpayment = $overpayment + $request->{"topay_$array_options[$ref]->{invoice_id}"} - $array_options[$ref]->{amount}; - $request->{"topay_$array_options[$ref]->{invoice_id}"} = $request->{"topay_$array_options[$ref]->{invoice_id}"}; + if ( "$array_options[$ref]->{due}"/"$request->{exrate}" < $request->{"topay_fx_$array_options[$ref]->{invoice_id}"} + $temporary_discount ) { + # We need to store all the overpayments so we can use it on a new payment2 screen + $unhandled_overpayment = $unhandled_overpayment + $request->{"topay_fx_$array_options[$ref]->{invoice_id}"} + $temporary_discount - $array_options[$ref]->{amount} ; + } + if ($request->{"optional_discount_$array_options[$ref]->{invoice_id}"}) { + push @amount, $array_options[$ref]->{discount}; + push @cash_account_id, $discount_account_id; + push @source, $locale->text('Applied discount'); + push @transaction_id, $array_options[$ref]->{invoice_id}; + } push @amount, $request->{"topay_fx_$array_options[$ref]->{invoice_id}"}; # We'll use this for both cash and ap/ar accounts push @cash_account_id, $request->{"optional_pay_$array_options[$ref]->{invoice_id}"} ? $request->{"account_$array_options[$ref]->{invoice_id}"} : $request->{account}; push @source, $request->{"source1_$array_options[$ref]->{invoice_id}"}.' '.$request->{"source2_$array_options[$ref]->{invoice_id}"}; # We'll use this for both source and ap/ar accounts push @transaction_id, $array_options[$ref]->{invoice_id}; } } +# Check if there is an unhandled overpayment and run payment2 as needed + +if ($unhandled_overpayment) { +&payment2($request); +return 0; +} # # Now we need the overpayment information. # # We will use the prefix op to indicate it is an overpayment information. # # note: I love the for's C-like syntax. - for (my $i=1 ; $i <= $request->{overpayment_qty}; $i++) { if (!$request->{"overpayment_checkbox_$i"}) { # Is overpayment marked as deleted ? if ( $request->{"overpayment_topay_$i"} ) { # Is this overpayment an used field? @@ -909,7 +956,6 @@ for (my $i=1 ; $i <= $request->{overpayment_qty}; $i++) { } } } - # Finally we store all the data inside the LedgerSMB::DBObject::Payment object. $Payment->{cash_account_id} = $Payment->_db_array_scalars(@cash_account_id); $Payment->{amount} = $Payment->_db_array_scalars(@amount); @@ -935,5 +981,6 @@ for (my $i=1 ; $i <= $request->{overpayment_qty}; $i++) { } + eval { do "scripts/custom/payment.pl"}; 1; diff --git a/sql/modules/Payment.sql b/sql/modules/Payment.sql index 8f0317e5..6e935b81 100644 --- a/sql/modules/Payment.sql +++ b/sql/modules/Payment.sql @@ -1,13 +1,24 @@ + +CREATE TYPE payment_vc_info AS ( + id int, + name text, + entity_class int, + discount int +); + + CREATE OR REPLACE FUNCTION payment_get_entity_accounts (in_account_class int, in_vc_name text, in_vc_idn int) - returns SETOF entity AS + returns SETOF payment_vc_info AS $$ - DECLARE out_entity entity%ROWTYPE; + DECLARE out_entity payment_vc_info; + + BEGIN FOR out_entity IN - SELECT ec.id, cp.legal_name as name, e.entity_class, e.created + SELECT ec.id, cp.legal_name as name, e.entity_class, ec.discount_account_id FROM entity e JOIN entity_credit_account ec ON (ec.entity_id = e.id) JOIN company cp ON (cp.entity_id = e.id) @@ -110,6 +121,7 @@ BEGIN SELECT id, invnumber, transdate, amount, entity_id, 2 AS invoice_class, paid, curr, entity_credit_account, department_id + FROM ar ) a JOIN (SELECT trans_id, chart_id, sum(CASE WHEN in_account_class = 1 THEN amount @@ -137,7 +149,8 @@ BEGIN AND (a.amount <= in_amountto OR in_amountto IS NULL) AND (a.department_id = in_department_id - OR in_department_id IS NULL) + OR in_department_id IS NULL) + AND due <> 0 GROUP BY a.invnumber, a.transdate, a.amount, discount, ac.due, a.id, c.discount_terms LOOP RETURN NEXT payment_inv; @@ -554,7 +567,7 @@ BEGIN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES (in_cash_account_id[out_count], - CASE WHEN in_account_class = 2 THEN in_amount[out_count] + CASE WHEN in_account_class = 1 THEN in_amount[out_count] ELSE in_amount[out_count]* - 1 END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), @@ -576,7 +589,7 @@ BEGIN INSERT INTO acc_trans (chart_id, amount, trans_id, transdate, approved, source) VALUES (var_account_id, - CASE WHEN in_account_class = 2 THEN in_amount[out_count] * -1 + CASE WHEN in_account_class = 1 THEN in_amount[out_count] * -1 ELSE in_amount[out_count] END, in_transaction_id[out_count], in_datepaid, coalesce(in_approved, true), @@ -740,7 +753,6 @@ CREATE TYPE payment_location_result AS ( class text ); - -- -- payment_get_vc_info has the same arch as company__list_locations, except for the filtering capabilities -- This should be unified on the API when we get things working - David Mora @@ -748,7 +760,7 @@ CREATE TYPE payment_location_result AS ( CREATE OR REPLACE FUNCTION payment_get_vc_info(in_entity_credit_id int, in_location_class_id int) RETURNS SETOF payment_location_result AS $$ -DECLARE out_row RECORD; +DECLARE out_row payment_location_result; BEGIN FOR out_row IN SELECT l.id, l.line_one, l.line_two, l.line_three, l.city, |