public function onReceive(PaymentEvent $event) { $payment = $event->getPayment(); $cpRepo = $this->em->getRepository('TSK\\PaymentBundle\\Entity\\ChargePayment'); $chargePayments = $cpRepo->findBy(array('payment' => $payment)); foreach ($chargePayments as $cp) { if ($cp->getCharge()->getAccount()->getName() == 'Inc Fm Students') { $results = array(); $contracts = $cp->getCharge()->getContracts(); foreach ($contracts as $contract) { $inits = $this->getMonthlyPrepayments($contract); $today = new \DateTime(); $terms = $contract->getPaymentTerms(); $obj = json_decode($terms['paymentsData']); $d = new Deferral($obj->principal, $contract->getDeferralRate(), $contract->getDeferralDurationMonths(), $inits, $contract->getContractStartDate()); $numFirsts = $this->countFirstOfMonthsSince($contract->getContractStartDate()); $deferrals = $d->distributePaymentMax($cp->getAmount(), $contract->getDeferralDurationMonths() - $numFirsts); $Deferrals = $d->datestampPayments($deferrals); $debitAccount = $payment->getPaymentMethod()->getAccount(); $chargeDeferralAccount = $cp->getCharge()->getDeferralAccount(); $chargeAccount = $cp->getCharge()->getAccount(); foreach ($Deferrals as $DeferralDate => $DeferralAmount) { $DD = new \DateTime($DeferralDate); if ($DeferralAmount) { if ($DD <= $today) { $results[] = array('date' => $DeferralDate, 'creditAccount' => $chargeAccount, 'debitAccount' => $debitAccount, 'debitAmount' => $DeferralAmount, 'creditAmount' => $DeferralAmount, 'chargePayment' => $cp, 'description' => $cp->getCharge()->getDescription()); if ($cp->getAmount() > $DeferralAmount) { $results[] = array('date' => $DeferralDate, 'creditAccount' => $chargeDeferralAccount, 'debitAccount' => $debitAccount, 'debitAmount' => $cp->getAmount() - $DeferralAmount, 'creditAmount' => $cp->getAmount() - $DeferralAmount, 'chargePayment' => $cp, 'description' => $cp->getCharge()->getDescription()); } } else { $results[] = array('date' => $DeferralDate, 'creditAccount' => $chargeAccount, 'debitAccount' => $chargeDeferralAccount, 'debitAmount' => $DeferralAmount, 'creditAmount' => $DeferralAmount, 'chargePayment' => $cp, 'description' => $cp->getCharge()->getDescription()); } } } } // Here is where we actually insert into journal ... if (count($results)) { foreach ($results as $result) { $journal = $this->generateJournal($result); $this->em->persist($journal); } $this->em->flush(); } } } }
/** * Graph/Simulate Payment Deferrals * * @Route("/deferral/graph", name="deferral_graph", options={"expose"=true}) * @Template() */ public function graphAction(Request $request) { if ($request->getMethod() == 'POST') { $principal = (int) $request->request->get('principal'); $rate = (double) $request->request->get('rate'); $recognitionRate = (double) $request->request->get('recognitionRate'); $recognitionCap = (double) $request->request->get('recognitionCap'); $periods = (int) $request->request->get('periods'); $payments = $request->request->get('payments'); $strategy = $request->request->get('strategy'); } else { $principal = (int) $request->query->get('principal'); $rate = (double) $request->query->get('rate'); $recognitionRate = (double) $request->query->get('recognitionRate'); $recognitionCap = (double) $request->query->get('recognitionCap'); $periods = (int) $request->query->get('periods'); $payments = $request->query->get('payments'); $strategy = $request->query->get('strategy'); } if (!$principal) { $principal = 1000; } if (!$rate) { $rate = 0.75; } if (!$recognitionRate) { $recognitionRate = 1 - $rate; } if (!$recognitionCap) { $recognitionCap = $principal; } if (!$periods) { $periods = 10; } if (!$payments) { $payments = array(); } if (!$strategy) { $strategy = 'max'; } $function = $strategy == 'even' ? 'distributePaymentEvenly' : 'distributePaymentMax'; $d = new Deferral($principal, $rate, $periods); $schedule = $d->getSchedule(); $runningTotal = 0; $totals = array(); foreach ($schedule as $s) { $runningTotal += $s; $totals[] = $runningTotal; } $inits = array_fill(0, $periods, 0); $idx = 0; // Sort the $payments array by order key usort($payments, function ($a, $b) { return $a['period'] - $b['period']; }); $paymentsTotal = 0; $paymentsTotal = array_reduce($payments, function ($result, $item) { $result += $item['amount']; return $result; }); foreach ($payments as $payment) { $d = new Deferral($principal, $rate, $periods, $inits, null, $recognitionRate, $recognitionCap); $deferrals = $d->{$function}($payment['amount'], $periods - $payment['period']); $chartArrays[] = array('name' => "Payment {$idx} realized", 'type' => 'column', 'data' => $deferrals); $inits = $this->sumArraySlots($inits, $deferrals); $idx++; } // Chart $series = array(array("name" => "Monthly Payment Realized", "type" => 'column', "data" => $schedule), array("name" => "Deferral Schedule", "type" => 'spline', "data" => $totals)); $runningPaymentTotal = 0; $paymentTotals = array_fill(0, $periods, 0); if (!empty($chartArrays)) { foreach ($chartArrays as $ca) { array_push($series, $ca); $paymentTotals = $this->sumArraySlots($paymentTotals, $ca['data']); } $tally = 0; foreach ($paymentTotals as $idx => $pt) { $tally += $pt; $myTotals[] = $tally; } // Push payment totals onto series array_push($series, array('name' => 'Cumulative Payment Realized', 'type' => 'spline', 'data' => $myTotals)); } $ob = new Highchart(); $ob->chart->renderTo('linechart'); // The #id of the div where to render the chart $ob->title->text('Deferral Schedule'); $ob->xAxis->title(array('text' => "Payment Periods (months)")); $ob->yAxis->title(array('text' => "Payment Realized (dollars)")); $ob->series($series); return $this->render('TSKPaymentBundle:Deferral:graph.html.twig', array('chart' => $ob, 'principal' => $principal, 'rate' => $rate, 'recognitionRate' => $recognitionRate, 'recognitionCap' => $recognitionCap, 'periods' => $periods, 'payments' => $payments, 'paymentsTotal' => $paymentsTotal, 'strategy' => $strategy, 'evenSelected' => $strategy == 'even' ? 'selected' : '', 'maxSelected' => $strategy == 'max' ? 'selected' : '', 'admin_pool' => $this->get('sonata.admin.pool'))); }
/** * @Route("/defer/{id}/{amount}/{contractStartDate}") * @Template() * @Method({"GET"}) */ public function testAction(Payment $payment, $amount, \DateTime $contractStartDate = null) { $em = $this->getDoctrine()->getManager(); $cpRepo = $em->getRepository('TSK\\PaymentBundle\\Entity\\ChargePayment'); $chargePayments = $cpRepo->findBy(array('payment' => $payment)); foreach ($chargePayments as $cp) { if ($cp->getCharge()->getAccount()->getName() == 'Inc Fm Students') { $contracts = $cp->getCharge()->getContracts(); foreach ($contracts as $contract) { $inits = $this->getMonthlyPrepayments($contract, $contractStartDate); print '<pre>inits'; print_r($inits) . '</pre>'; $today = new \DateTime(); $terms = $contract->getPaymentTerms(); $obj = json_decode($terms['paymentsData']); $d = new Deferral($obj->principal, $contract->getDeferralRate(), $contract->getDeferralDurationMonths(), $inits, $contractStartDate); $numFirsts = $this->countFirstOfMonthsSince($contractStartDate); // $numFirsts = $this->countFirstOfMonthsSince($contract->getContractStartDate); // $deferrals = $d->distributePaymentMax($cp->getAmount(), 9); $deferrals = $d->distributePaymentMax($amount, $contract->getDeferralDurationMonths() - $numFirsts); $Deferrals = $d->datestampPayments($deferrals); $debitAccountName = $payment->getPaymentMethod()->getAccount()->getName(); $chargeDeferralAccountName = $cp->getCharge()->getDeferralAccount()->getName(); $chargeAccountName = $cp->getCharge()->getAccount()->getName(); foreach ($Deferrals as $DeferralDate => $DeferralAmount) { print "{$DeferralDate} - {$DeferralAmount}<br>"; $DD = new \DateTime($DeferralDate); if ($DeferralAmount) { if ($DD <= $today) { $results[] = array('date' => $DeferralDate, 'creditAccount' => $chargeAccountName, 'debitAccount' => $debitAccountName, 'debitAmount' => $DeferralAmount, 'creditAmount' => $DeferralAmount); if ($amount > $DeferralAmount) { $results[] = array('date' => $DeferralDate, 'creditAccount' => $chargeDeferralAccountName, 'debitAccount' => $debitAccountName, 'debitAmount' => $amount - $DeferralAmount, 'creditAmount' => $amount - $DeferralAmount); } } else { $results[] = array('date' => $DeferralDate, 'creditAccount' => $chargeAccountName, 'debitAccount' => $chargeDeferralAccountName, 'debitAmount' => $DeferralAmount, 'creditAmount' => $DeferralAmount); } } } function sorter($key) { return function ($a, $b) use($key) { $ad = new \DateTime($a[$key]); $bd = new \DateTime($b[$key]); return $ad > $bd; }; } usort($results, sorter('date')); } } } return array('today' => $today, 'results' => $results, 'foo' => '$1200 contract, 10 payments @ $120, starting on ' . $contractStartDate->format('Y-m-d') . ', deferral rate of 0.75, deferred over 10 months'); // return array('foo' => '$1200 contract, 10 payments @ $120, starting on '.$contract->getContractStartDate()->format('Y-m-d').', deferral rate of 0.75, deferred over 10 months'); }
protected function execute(InputInterface $input, OutputInterface $output) { try { $since = $input->getOption('since'); $dry_run = $input->getOption('dry-run'); $paymentID = $input->getOption('payment-id'); $max = (int) $input->getOption('max'); $MAX = 20; if ($max > 0 && $max < $MAX) { // nada } else { $max = $MAX; } $clauses[] = 'p.deferralTimestamp IS NULL'; $params = array(); if ($since) { $clauses[] = 'p.payment_date > :since'; $params[':since'] = $since; } if ($paymentID) { $clauses[] = 'p.id = :id'; $params[':id'] = $paymentID; } $em = $this->getContainer()->get('doctrine')->getManager(); $query = $em->createQuery('SELECT p from TSK\\PaymentBundle\\Entity\\Payment p WHERE ' . join(' AND ', $clauses)); $query->setParameters($params); $query->setFirstResult(0); $query->setMaxResults($max); $payments = $query->getResult(); if (count($payments)) { foreach ($payments as $payment) { $appliedCharges = $payment->getChargePayments(); if ($appliedCharges->count()) { foreach ($appliedCharges as $chargePayment) { $charge = $chargePayment->getCharge(); // Is this charge of type tuition? We only defer // the portion of the payment applied to tuition if ($charge->getAccount()->getName() == 'Inc Fm Students') { // get contract details $contracts = $charge->getContracts(); $contract = $contracts[0]; $contractAmount = $contract->getAmount(); $contractStartDate = $contract->getCreatedDate()->format('YYYY-mm-dd'); $deferralDuration = $contract->getDeferralDurationMonths(); $deferralDistributionStrategy = $contract->getDeferralDistributionStrategy(); $deferralDistributionStrategy = 'accelerated'; if (!in_array($deferralDistributionStrategy, array('straight', 'accelerated'))) { throw new \Exception('Unrecognized deferral distribution strategy ' . $deferralDistributionStrategy); } $deferralRate = $contract->getDeferralRate(); // Sum any REALIZED payments for this contract and group by year-month ... // Sum any pre-existing deferral payments for this contract and group by year-month // Could have done this in mysql w/ the following query, but Doctrine doesn't support it. // select year(date_realized), month(date_realized), sum(amount) from tsk_payments_deferred where fk_contract_id=6 group by year(date_realized), month(date_realized) order by year(date_realized) asc, month(date_realized) asc $query = $em->createQuery('SELECT p from TSK\\PaymentBundle\\Entity\\PaymentsDeferred p WHERE p.contract=:contract ORDER BY p.dateRealized'); $query->setParameters(array(':contract' => $contract)); $deferments = $query->getResult(); $initialDeferrals = array(); foreach ($deferments as $deferment) { $key = $deferment->getDateRealized()->format('Y-m'); if (empty($initialDeferrals[$key])) { $initialDeferrals[$key] = $deferment->getAmount(); } else { $initialDeferrals[$key] += $deferment->getAmount(); } } $inits = array_values($initialDeferrals); if (!$inits) { $inits = array_fill(0, $contract->getDeferralDurationMonths(), 0); } // Set deferral schedule $d = new Deferral($contract->getAmount(), $contract->getDeferralRate(), $contract->getDeferralDurationMonths(), $inits, $contract->getContractStartDate()); // RUN THE DEFERRALS!! $deferralMethod = $deferralDistributionStrategy == 'straight' ? 'distributePaymentEvenly' : 'distributePaymentMax'; $deferrals = $d->{$deferralMethod}($chargePayment->getAmount(), $contract->getRemainingDeferralPeriods()); $Deferrals = $d->datestampPayments($deferrals); if ($dry_run) { print "schedule =\n"; ladybug_dump($d->getSchedule()); print "initial deferrals\n"; ladybug_dump($inits); print "deferrals\n"; $summary = sprintf('<options=bold>$%5.2f</options=bold> Deferred at <options=bold>%3.2f</options=bold> over <options=bold>%d</options=bold> months using <options=bold>"%s"</options=bold> Strategy starting on <options=bold>%s</options=bold>', $chargePayment->getAmount(), $contract->getDeferralRate(), $contract->getRemainingDeferralPeriods(), $contract->getDeferralDistributionStrategy(), $contract->getContractStartDate()->format('Y-m-d')); $formatter = $this->getHelperSet()->get('formatter'); $formattedLine = $formatter->formatSection('Deferral Summary', $summary); $output->writeln($formattedLine); ladybug_dump($Deferrals); print count($Deferrals) . " payments totalling "; print array_sum(array_values($Deferrals)) . "\n"; } else { foreach ($Deferrals as $DeferralDate => $DeferralAmount) { if ($DeferralAmount) { $paymentDeferred = new PaymentsDeferred(); $paymentDeferred->setPayment($payment); $paymentDeferred->setAmount($DeferralAmount); $paymentDeferred->setDateRealized(new \DateTime($DeferralDate)); $paymentDeferred->setContract($contract); $em->persist($paymentDeferred); } // we actually save the deferred payment each time ... $em->flush(); } } } } if (!$dry_run) { // Update payment deferralTimestamp $payment->setDeferralTimestamp(new \DateTime()); $em->persist($payment); $em->flush(); } } else { throw new \Exception('No charges applied to payment. Surely, this cannot be true'); } } } else { $dialog = $this->getDialogHelper(); $dialog->writeSection($output, "No deferrable payments match your criteria, nothing to defer.", 'bg=red;fg=white'); } } catch (\PDOException $e) { } catch (\Exception $e) { $dialog = $this->getDialogHelper(); $dialog->writeSection($output, $e->getMessage(), 'bg=red;fg=white'); } if (!empty($results['errors'])) { $dialog = $this->getDialogHelper(); $dialog->writeSection($output, $results['errors'][0], 'bg=yellow;fg=white'); } }