Jump to content

Calculations, Interest, Diff between two days


aarontbarksdale

Recommended Posts

usort($payments, 'function_to_compare_dates'); foreach ($payments as $item){  if (isset($item['payment_due'])) // this is a payment due line  {    // add to balance due, calculate interest, etc    ...  }  else // this is a payment line  {    // apply payment to interest/principal, etc    ...  }}

The 'function_to_compare_dates' would be the $dayslate = $monthEnd - $monthStart;?Also, in the IF...ELSE statement is where the lines for all calculations for what goes into the table...and where are the table outputs...within the IF...ELSE statement as well?

Edited by aarontbarksdale
Link to comment
Share on other sites

Yeah, you would print each line in the table there. The comparison function would probably just be a function that takes 2 arguments (e.g., $a and $B), and returns $a['pmt_date'] - $b['pmt_date'] to figure out which one should be sorted first. Using a comparison function with usort is explained here: http://www.php.net/manual/en/function.usort.php If the comparison function returns a value less than 0 then the first item should come before the second, or if it's greater than 0 then the second item should come before the first, or 0 if they are equal. A custom sort function is necessary because you're not just sorting a basic array of numbers, so the function needs to compare the payment dates.

Link to comment
Share on other sites

And should all of the code snippet you provided go in the <head> tag or within the <body>? I'm assuming <head> and then the output for the table within the <body> Are my assumptions correct?

Link to comment
Share on other sites

Alright, I'm thoroughly confused about the "usort" function to compare dates. I'm sorry, I've tried, I think my brain is full and cannot allow for more information. Please help me understand it. Thanks

Link to comment
Share on other sites

Sorry I haven't sent the quote yet, I've been a little busy. usort is a way to sort an array by any arbitrary criteria. You have an array of arrays, where each item in the array is another array (from the database, or the other loop). You can't just use sort to sort an array of arrays because it doesn't know how you want to sort it. So you use usort with a custom comparison function that will compare the pmt_date items in each array.

function cmp_pmt_date($a, ${  return strtotime($a['pmt_date']) - strtotime($b['pmt_date']);} usort($payments, 'cmp_pmt_date');

That's all it needs to be. The comparison function needs to return a value that is less than, greater than, or equal to zero to determine which item should be sorted first. That subtraction will do that, it just subtracts the 2 dates and returns the difference, which will be less than, greater than, or equal to zero. That will tell the sort function how to sort the arrays with pmt_date items.

  • Like 1
Link to comment
Share on other sites

I'm getting an error:

Fatal error: Allowed memory size of 67108864 bytes exhausted (tried to allocate 47 bytes) in /home/content/w/e/b/webmasterbark/html/csac/login/report.phpon line 68
Line 68 is:
[/b]while($monthStart <= $today) {  $payments[] = array(   'payment_due' => true, //LINE 68'amount' => $payments['pmt_amt'],'pmt_date' => $payments['pmt_date']); }

Link to comment
Share on other sites

If I add:

  $monthStart = strtotime('+1 month', $monthStart);  $monthEnd = strtotime('+1 month', $monthStart);

That's outside the main while loop, will that mess up my numbers inside the main loop?

Link to comment
Share on other sites

Okay, so I did that...and the output went haywire.

$payments = array();while ($row2 = mysql_fetch_assoc($data2)) {         $payments[] = $row2;}$pmtdate = $payments['pmt_date'];$monthStart = strtotime($row['datestart']);$today = strtotime('now'); while($monthStart <= $today) {     $payments[] = array(           'payment_due' => true,            'amount' => $payments['pmt_amt'],           'pmt_date' => $payments['pmt_date']);      $monthStart = strtotime('+1 month', $monthStart);     $monthEnd = strtotime('+1 month', $monthStart);} function cmp_pmt_date($a, ${     return strtotime($a['pmt_date']) - strtotime($b['pmt_date']);} usort($payments, 'cmp_pmt_date'); foreach ($payments as $item){      if (isset($item['pmt_amt'])) // this is a payment due line      {     // add to balance due, calculate interest, etc            echo '<tr><td>'. date("m.d.Y", $monthStart) . ' </td>';            echo '<td>'. "$". number_format($row['supportamount'], 2, '.',',') .' </td>';            echo '<td>$0.00 </td>';              $totint = $int + $newint;           echo '<td>'. "$". number_format($totint, 2, '.',',') . ' </td>';           echo '<td>'. "$". number_format($appint, 2, '.',',') . ' </td>';           echo '<td>'. "$". number_format($appprinc, 2, '.',',') . ' </td>';            $bal = $bal - $payments['pmt_amt'];           echo '<td>'. "$". number_format($bal, 2, '.',',') . ' </td></tr>';           $bal = $lastbal;           $newint = $totint;      } else { // this is a payment line           echo '<tr><td>'. date("m.d.Y", strtotime($payment['pmt_date'])) .'</td>';           echo '<td>$0.00</td>';           echo '<td>'. "$". number_format($payment['pmt_amt'], 2, '.',',') . '</td>';            echo '<td>'. "$". number_format($intacc , 2, '.',',') . ' </td>';           echo '<td>'. "$". number_format($appint , 2, '.',',') . ' </td>';           echo '<td>'. "$". number_format($appprinc, 2, '.',',') . ' </td>';           $bal = $bal - $payments['pmt_amt'];           echo '<td>'. "$". number_format($bal, 2, '.',',') . ' </td></td>';           $dayslateP = ($monthEnd - strtotime($payment['pmt_date']))/(24*60*60);      }     $newint = $totint;     $monthStart = strtotime('+1 month', $monthStart);     $monthEnd = strtotime('+1 month', $monthStart);}

And here's the output:screenshot.jpg

Link to comment
Share on other sites

I'm not sure what you're asking, you can always reset those to a different value. You need to have the while loop stop at some point though, it should only add 1 item to the array per month of the date range. After that you can reset those variables if you need to. I was thinking you only need them once though, after you populate the array then it should contain everything you need, you can just loop through it to print each row.

Link to comment
Share on other sites

I had a while loop stop, it's $today = strtotime('now'); which kept it from passing today's date. Now, it doesn't increase the payment due lines and then inserts the payments after it runs through several times (without increasing the date).

Link to comment
Share on other sites

What I don't understand is how does comparing the payment dates determine's whether the payment date comes before the current $monthStart...

Link to comment
Share on other sites

I'm not sure I understand the problem, post the code you're using now. Sorting doesn't have anything to do with the start of the month, sorting just puts the array of line items in chronological order. The array will end up with each payment from the database, plus one line for each payment due date. When you're looping through each item in the array then you can check the pmt_date item to figure out what date it's for, but they will be in order after the sort.

Link to comment
Share on other sites

Why does it launch the dates well past today's date? Current Code:

while ($row2 = mysql_fetch_assoc($data2)) {         $payments[] = $row2;}$pmtdate = $payments['pmt_date'];$monthStart = strtotime($row['datestart']);$today = strtotime('now');while($monthStart <= $today) {  $payments[] = array(   'payment_due' => true, 'amount' => $payments['pmt_amt'],'pmt_date' => $payments['pmt_date']);  $monthStart = strtotime('+1 month', $monthStart);} function cmp_pmt_date($a, ${if ($a >= $ {return strtotime($a['pmt_date']) - strtotime($b['pmt_date']);} else {return strtotime($b['pmt_date']) - strtotime($b['pmt_date']);}} usort($payments, 'cmp_pmt_date'); foreach ($payments as $item) {  if (isset($item['pmt_amt'])) { // this is a payment due line     // add to balance due, calculate interest, etcif (strtotime($item['pmt_date']) < $monthStart) {  // this is a payment line   echo '<tr><td>'. date("m.d.Y", $monthStart) . ' </td>';echo '<td>'. "$". number_format($row['supportamount'], 2, '.',',') .' </td>';echo '<td>$0.00 </td>';  $totint = $int + $newint;echo '<td>'. "$". number_format($totint, 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($appint, 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($appprinc, 2, '.',',') . ' </td>'; $bal = $bal - $item['pmt_amt'];echo '<td>'. "$". number_format($bal, 2, '.',',') . ' </td>';echo '<td>'. $dayslate . '</td></tr>'; $bal = $lastbal;$newint = $totint;} else {echo '<tr><td>'. date("m.d.Y", strtotime($item['pmt_date'])) .'</td>';echo '<td>$0.00</td>';echo '<td>'. "$". number_format($item['pmt_amt'], 2, '.',',') . '</td>';  echo '<td>'. "$". number_format($intacc , 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($appint , 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($appprinc, 2, '.',',') . ' </td>';$bal = $bal - $item['pmt_amt'];echo '<td>'. "$". number_format($bal, 2, '.',',') . ' </td>';echo '<td>'. $dayslate . '</td></tr>';$dayslateP = ($monthStart - strtotime($item['pmt_date']))/(24*60*60);  }   }  $newint = $totint;  $monthStart = strtotime("+1 month", $monthStart);}

Output:screenshot3.jpgI specified that IF $monthStart <= $today...that should start it from the first due date and then NOT exceed today's date...which allows for the flexibility of an increasing date. I want to limit the entry into the database as much as possible, that way I can have everything created and calculated dynamically, and should things change...I want the changes quickly reflected.

Link to comment
Share on other sites

There are a couple things I'm confused about. First, this part:

while($monthStart <= $today) {$payments[] = array('payment_due' => true,'amount' => $payments['pmt_amt'],'pmt_date' => $payments['pmt_date']);$monthStart = strtotime('+1 month', $monthStart);}

Why are you setting all of the pmt_dates to the same value? The pmt_date should be the date that the payment is due. If you have one payment due per month, then that should be the date that month that the payment is due, not the same date for every payment line. I don't think you should be using the $payments array at all to set those values. You're adding lines to the $payments array, not using that array to calculate something. Maybe $payments is not a good name for that, $lines or something. The array should be composed of the lines that will actually appear in the report, with the correct dates and everything. You shouldn't be re-calculating the dates for each line in the loop where you print. Maybe I didn't explain what I was suggesting clearly enough. Run this code and see what it prints. Note that the code doesn't run the database query, I started my code where you started what you pasted. You'll need to add the code to run the query.

<?php$report_lines = array();$date_due = 10; // payments are due on the 10th of each month.  you can set this variable however you need to$amt_due = 500; // 500 is due each month$date_format = 'M j, Y'; // see PHP's date function http://www.php.net/manual/en/function.date.php while ($row2 = mysql_fetch_assoc($data2)) {  // convert the date to a timestamp  $row2['pmt_date'] = strtotime($row2['pmt_date']);   echo 'found a payment paid on ' . date($date_format, $row2['pmt_date']) . '<br>';  $report_lines[] = $row2;} $monthStart = strtotime($row['datestart']);$today = strtotime('now'); while($monthStart <= $today) {  $date_info = getdate($monthStart); // http://www.php.net/manual/en/function.getdate.php  $due = mktime(0, 0, 0, $date_info['mon'], $date_due, $date_info['year']); // http://www.php.net/manual/en/function.mktime.php   echo 'adding payment due on ' . date($date_format, $due) . ' for ' . $amt_due . '<br>';   $report_lines[] = array(	'payment_due' => true, // this is a payment due line, not a payment line	'amount' => $amt_due, // same amount due each month	'pmt_date' => $due // due date for current month  );   $monthStart = strtotime('+1 month', $monthStart);} // sort the report lines by datefunction cmp_pmt_date($x, $y){  return $x['pmt_date'] - $y['pmt_date'];}usort($report_lines, 'cmp_pmt_date'); $interest_owed = 0;$balance_due = 0; foreach ($report_lines as $line){  if (isset($line['payment_due']))  {	// payment due line, add to balance due and calculate interest	// in this section we only add to interest and balance due	$interest = $balance_due * .08; // interest for this month, on the previous balance	$interest_owed += $interest; // add interest for this month to total interest owed	$balance_due += ($line['amount'] + $interest); // add interest for this month and this month's amount due to total balance due	echo 'found a payment due on ' . date($date_format, $line['pmt_date']) . ' for ' . $line['amount'] . ', need to calculate interest and balance<br>';	echo 'interest for this month is ' . $interest . ', total balance due is ' . $balance_due . '<br>';  }  else  {	// payment line, apply payment amount to interest and principal	// in this section we only subtract from interest and balance due	if ($line['pmt_amt'] > $interest_owed) // they paid more than the interest owed	{	  // they paid off interest	  $amt = $line['pmt_amt'] - $interest_owed; // subtract the interest from what they paid; $amt can then be applied to the principal	  $interest_owed = 0; // they don't owe interest any more	}	else // they owe more interest than what they paid	{	  $interest_owed -= $line['pmt_amt']; // reduce what they paid from the interest owed	  $amt = 0; // nothing gets reduced from principal	}	$balance_due -= $line['pmt_amt']; // reduce the balance due by what they paid 	echo 'found a payment made on ' . date($date_format, $line['pmt_date']) . ' for ' . $line['pmt_amt'] . '. total interest owed is now ' . $interest_owed . ', balance due is now ' . $balance_due . '<br>';  }}

Notice inside the foreach loop that there's no reason to use $monthStart or the other date stuff, the date is in the array (pmt_date). Those calculations are probably over-simplified, if you need to calculate interest based on how many days late they are then you'll need to do some additional stuff there, but hopefully that gives you an idea about what I mean. This code is split up into sections. The first section gets all of the data, through the database and then adding the dynamic payment due lines. Then it sorts the data, then it prints the data.

Link to comment
Share on other sites

Okay...so there's only ONE thing missing from everything...Days Late. The $dayslate variable is IMPORTANT because the interest is calculated on a daily basis. The interest in this case is 8%. So, I added to the interest line you have (.08/365)...however I need to add "* $dayslate" to the rest of that calculation for accurate reporting. Here's the output:screenshot10.gifHere's the code (with my tweaks):

 $payments = array();$report_lines = array();$date_due = 15; // payments are due on the 10th of each month.  you can set this variable however you need to$amt_due = $row['supportamount']; // 500 is due each month$date_format = 'm.j.Y'; // see PHP's date function http://www.php.net/manual/en/function.date.php while ($row2 = mysql_fetch_assoc($data2)) {  // convert the date to a timestamp  $row2['pmt_date'] = strtotime($row2['pmt_date']);   //echo 'found a payment paid on ' . date($date_format, $row2['pmt_date']) . '<br>';  $report_lines[] = $row2;} $monthStart = strtotime($row['datestart']);$today = strtotime('now'); while($monthStart <= $today) {  $date_info = getdate($monthStart); // http://www.php.net/manual/en/function.getdate.php  $due = mktime(0, 0, 0, $date_info['mon'], $date_due, $date_info['year']); // http://www.php.net/manual/en/function.mktime.php   //echo 'adding payment due on ' . date($date_format, $due) . ' for ' . $amt_due . '<br>';   $report_lines[] = array(		'payment_due' => true, // this is a payment due line, not a payment line		'amount' => $amt_due, // same amount due each month		'pmt_date' => $due // due date for current month  );   $monthStart = strtotime('+1 month', $monthStart);} // sort the report lines by datefunction cmp_pmt_date($x, $y){  return $x['pmt_date'] - $y['pmt_date'];}usort($report_lines, 'cmp_pmt_date'); $interest_owed = 0;$balance_due = 0; foreach ($report_lines as $line){  if (isset($line['payment_due']))  {		// payment due line, add to balance due and calculate interest		// in this section we only add to interest and balance due		$interest = $balance_due * (.08/365); // * $dayslate; // interest for this month, on the previous balance		$interest_owed += $interest; // add interest for this month to total interest owed		$balance_due += $line['amount']; // add interest for this month and this month's amount due to total balance dueecho '<tr><td>'. date($date_format, $line['pmt_date']) . ' </td>';echo '<td>'. "$". number_format($line['amount'], 2, '.',',') .' </td>';echo '<td>$0.00 </td>';  echo '<td>'. "$". number_format($interest, 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($appint, 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($appprinc, 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($balance_due, 2, '.',',') . ' </td></tr>';		//echo 'found a payment due on ' . date($date_format, $line['pmt_date']) . ' for ' . $line['amount'] . ', need to calculate interest and balance<br>';		//echo 'interest for this month is ' . $interest . ', total balance due is ' . $balance_due . '<br>';  }  else  {		// payment line, apply payment amount to interest and principal		// in this section we only subtract from interest and balance due		if ($line['pmt_amt'] > $interest_owed) // they paid more than the interest owed		{		  // they paid off interest		  $amt = $line['pmt_amt'] - $interest_owed; // subtract the interest from what they paid; $amt can then be applied to the principal		  $interest_owed = 0; // they don't owe interest any more		}		else // they owe more interest than what they paid		{		  $interest_owed -= $line['pmt_amt']; // reduce what they paid from the interest owed		  $amt = 0; // nothing gets reduced from principal		}		$balance_due -= $line['pmt_amt']; // reduce the balance due by what they paid  echo '<tr><td>'. date($date_format, $line['pmt_date']) .'</td>';echo '<td>$0.00</td>';echo '<td>'. "$". number_format($line['pmt_amt'], 2, '.',',') . '</td>';  echo '<td>'. "$". number_format($interest_owed , 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($interest , 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($amt, 2, '.',',') . ' </td>';echo '<td>'. "$". number_format($balance_due, 2, '.',',') . ' </td></tr>';		//echo 'found a payment made on ' . date($date_format, $line['pmt_date']) . ' for ' . $line['pmt_amt'] . '. total interest owed is now ' . $interest_owed . ', balance due is now ' . $balance_due . '<br>';  }}

Edited by aarontbarksdale
Link to comment
Share on other sites

If you notice, for some reason, the payment made on 5.02.2010 is on there twice...only time that happens...any idea. UPDATE:It's not a code issue, there's actually 2 payments in the DB for that date...never mind THAT "error"

Edited by aarontbarksdale
Link to comment
Share on other sites

How exactly should days late be calculated? When they make a payment, do we look up when the previous payment was due and charge interest based on the difference? Or another way? What if several due dates go by before they make any payment?

Link to comment
Share on other sites

Yes, interest begins accruing the DAY after they're due up until they pay. So if a person is due on the 1st and pay on the 10th, there will be 9 days of interest calculated, then that gets applied to the interest line

$p = 0.08;$interest = $balance_due * ($p/365) * $days late; // 9 days late, balance_due is 500 should = $0.99

So, I just need to figure out a $dayslate calculation, I've tried a few things, but they didn't work.

Link to comment
Share on other sites

And the days late gets calculated when they pay, correct? If that's the case, then you probably need to keep track of the last time something was due, and use that when you see a payment line. That's not going to cover the case where they miss multiple payments though, is that an issue? If so, the last payment date also needs to be kept track of.

$interest_owed = 0;$balance_due = 0; $last_due = 0;$dayslate = 0; foreach ($report_lines as $line){  if (isset($line['payment_due']))  {	// payment due line, add to balance due and calculate interest	// in this section we only add to interest and balance due	$interest = $balance_due * (.08/365); // * $dayslate; // interest for this month, on the previous balance	$interest_owed += $interest; // add interest for this month to total interest owed	$balance_due += $line['amount']; // add interest for this month and this month's amount due to total balance due	echo '<tr><td>'. date($date_format, $line['pmt_date']) . ' </td>';	echo '<td>'. "$". number_format($line['amount'], 2, '.',',') .' </td>';	echo '<td>$0.00 </td>';	echo '<td>'. "$". number_format($interest, 2, '.',',') . ' </td>';	echo '<td>'. "$". number_format($appint, 2, '.',',') . ' </td>';	echo '<td>'. "$". number_format($appprinc, 2, '.',',') . ' </td>';	echo '<td>'. "$". number_format($balance_due, 2, '.',',') . ' </td></tr>';  	$last_due = $line['pmt_date'];  	//echo 'found a payment due on ' . date($date_format, $line['pmt_date']) . ' for ' . $line['amount'] . ', need to calculate interest and balance<br>';	//echo 'interest for this month is ' . $interest . ', total balance due is ' . $balance_due . '<br>';  }  else  {	// payment line, apply payment amount to interest and principal	// in this section we only subtract from interest and balance due  	$dayslate = ceil(($line['pmt_date'] - $last_due) / 86400); 	if ($line['pmt_amt'] > $interest_owed) // they paid more than the interest owed	{	  // they paid off interest	  $amt = $line['pmt_amt'] - $interest_owed; // subtract the interest from what they paid; $amt can then be applied to the principal	  $interest_owed = 0; // they don't owe interest any more	}	else // they owe more interest than what they paid	{	  $interest_owed -= $line['pmt_amt']; // reduce what they paid from the interest owed	  $amt = 0; // nothing gets reduced from principal	}	$balance_due -= $line['pmt_amt']; // reduce the balance due by what they paid	echo '<tr><td>'. date($date_format, $line['pmt_date']) .'</td>';	echo '<td>$0.00</td>';	echo '<td>'. "$". number_format($line['pmt_amt'], 2, '.',',') . '</td>';	echo '<td>'. "$". number_format($interest_owed , 2, '.',',') . ' </td>';	echo '<td>'. "$". number_format($interest , 2, '.',',') . ' </td>';	echo '<td>'. "$". number_format($amt, 2, '.',',') . ' </td>';	echo '<td>'. "$". number_format($balance_due, 2, '.',',') . ' </td></tr>';	//echo 'found a payment made on ' . date($date_format, $line['pmt_date']) . ' for ' . $line['pmt_amt'] . '. total interest owed is now ' . $interest_owed . ', balance due is now ' . $balance_due . '<br>';  }}

Since the dates are given as integer timestamps (in seconds), in order to find the difference between 2 dates you can just subtract. Dividing the result by 86400 gives you days, because there are 86400 seconds in a day. Using the ceil function there makes it round up. If you want to round down you can use floor instead of ceil. It sounds like that might not work though, it sounds like it should be more complicated. If you charge them 500, and they pay 400, then they owe 100. So interest runs on that 100 until the next due date. At the next due date they get another 500, so interest starts running on 600 until they pay again. So now you have 100 that is maybe 3 weeks late, and 500 that is maybe a week late. Do you have to keep track of how late each amount is, or are you running interest on the entire 600 for the full time since they've had the 100 running?

Link to comment
Share on other sites

After many tweaks to get certain calculations to work out right, I almost have it...however, when trying to remove the hard coding for the original start date...I ran into a problem. Here's the code:

$monthStart = $row['datestart']; //get RAW database information $today = strtotime('now'); $due_date = getdate($monthStart); $date_due = $monthStart['mday'];  //extracts JUST the day of the month from RAW db info $monthStart = strtotime($monthStart); //converts db info to unix timestamp while($monthStart <= $today) { ....//Code continues, just truncating it

What it's doing is setting EVERYTHING to start on the 2nd of the month. There is NO hard-coding anymore, I removed where you had $date_due = 15; because it needs to be dynamic. However, the $monthStart is defaulted to the 2nd of each month. Any suggestions?

Link to comment
Share on other sites

Fixed using:

$time  = strtotime($monthStart);$date_due = date('d',$time);

I probably could shorten it by one line, remove the $time one and just put $date_due after I call the $monthStart variable that is set to UTC...but for right now, it's working and I don't want to screw it up.

Edited by aarontbarksdale
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...