/**
  * 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')));
 }
 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');
     }
 }