CORE-POS/IS4C

View on GitHub
fannie/mem/PaymentPlanEditor.php

Summary

Maintainability
C
1 day
Test Coverage
A
94%
<?php
/*******************************************************************************

    Copyright 2015 Whole Foods Co-op

    This file is part of CORE-POS.

    IT CORE is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    IT CORE is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    in the file license.txt along with IT CORE; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*********************************************************************************/
// A page to search the member base.
include(dirname(__FILE__) . '/../config.php');
if (!class_exists('FannieAPI')) {
    include_once(__DIR__ . '/../classlib2.0/FannieAPI.php');
}

class PaymentPlanEditor extends FannieRESTfulPage 
{
    protected $title = 'Payment Plan Editor';
    protected $header = 'Payment Plan Editor';
    public $description = '[Payment Plan Editor] defines equity payment schedules.';
    protected $must_authenticate = true;
    protected $auth_classes = array('editmembers');

    public function preprocess()
    {
        $this->addRoute('post<id><name><final><initial><recurring><cycle><basis><overdue><reason>');

        return parent::preprocess();
    }

    public function post_id_name_final_initial_recurring_cycle_basis_overdue_reason_handler()
    {
        $dbc = $this->connection;
        $dbc->selectDB($this->config->get('OP_DB'));
        $plan = new EquityPaymentPlansModel($dbc);
        $plan->equityPaymentPlanID($this->id);
        $plan->name($this->name);
        $plan->finalBalance($this->final);
        $plan->initialPayment($this->initial);
        $plan->recurringPayment($this->recurring);
        $plan->billingCycle($this->cycle);
        $plan->dueDateBasis($this->basis);
        $plan->overDueLimit($this->overdue);
        $plan->reasonMask($this->reason);
        $plan->save();

        return filter_input(INPUT_SERVER, 'PHP_SELF') . '?id=' . $this->id;
    }

    public function put_handler()
    {
        $dbc = $this->connection;
        $dbc->selectDB($this->config->get('OP_DB'));
        $plan = new EquityPaymentPlansModel($dbc);
        $plan->name('NEW PLAN');
        $plan->initialPayment(20);
        $plan->recurringPayment(80);
        $plan->finalBalance(100);
        $planID = $plan->save();
        if ($planID !== false) {
            return filter_input(INPUT_SERVER, 'PHP_SELF') . '?id=' . $planID;
        }

        return true;
    }

    public function put_view()
    {
        return '<div class="alert alert-danger">Error: failed to create new plan</div>'
            . $this->get_view();
    }

    public function get_id_view()
    {
        $dbc = FannieDB::getReadOnly($this->config->get('OP_DB'));
        $codes = new ReasoncodesModel($dbc);
        $plan = new EquityPaymentPlansModel($dbc);
        $plan->equityPaymentPlanID($this->id);
        if (!$plan->load()) {
            return '<div class="alert alert-danger">Error: plan does not exist</div>';
        }
        
        $ret = '<script type="text/javascript">
        function validateBillingCycle()
        {
            var val = $(\'input[name=cycle]\').val();
            if (val.match(/^\s*\d+[dDwWmMyY]\s*$/)) {
                $(\'#billing-cycle-valid\').addClass(\'collapse\');
                return true;
            } else {
                $(\'#billing-cycle-valid\').removeClass(\'collapse\');
                $(\'#billing-cycle-valid\').html(\'<div class="alert alert-danger">Invalid Entry</div>\');
                $(\'input[name=cycle]\').focus();
                return false;
            }
        }
        </script>';
        $ret .= '<form method="post" onsubmit="return validateBillingCycle();">';

        $ret .= sprintf('
            <div class="form-group">
                <label>Name</label>
                <input class="form-control" type="text" name="name" value="%s" />
            </div>', $plan->name());

        $ret .= sprintf('
            <div class="form-group">
                <label>Total Equity Required</label>
                <div class="input-group">
                    <span class="input-group-addon">$</span>
                    <input type="text" class="form-control" name="final" value="%.2f" />
                </div>
            </div>', $plan->finalBalance());

        $ret .= sprintf('
            <div class="form-group">
                <label>Initial Payment</label>
                <div class="input-group">
                    <span class="input-group-addon">$</span>
                    <input type="text" class="form-control" name="initial" value="%.2f" />
                </div>
            </div>', $plan->initialPayment());

        $ret .= sprintf('
            <div class="form-group">
                <label>Recurring Payment</label>
                <div class="input-group">
                    <span class="input-group-addon">$</span>
                    <input type="text" class="form-control" name="recurring" value="%.2f" />
                </div>
            </div>', 
            $plan->recurringPayment()
        );

        $ret .= sprintf('
            <div class="form-group">
                <div id="billing-cycle-valid" class="collapse"></div>
                <label>Billing Cycle %s</label>
                <input class="form-control" type="text" name="cycle" value="%s" onchange="validateBillingCycle();" />
            </div>', 
            \COREPOS\Fannie\API\lib\FannieHelp::toolTip('Enter a number followed by D, W, M, or Y for days, weeks, months, or years'),
            $plan->billingCycle()
        );

        $ret .= sprintf('<div class="form-group">
            <label>Calculate Next Due Date based on</label>
            <select name="basis" class="form-control">
                <option value="0" %s>Join Date</option>
                <option value="1" %s>Date of Last Equity Payment</option>
            </select>
            </div>',
            ($plan->dueDateBasis() == 0 ? 'selected' : ''),
            ($plan->dueDateBasis() == 1 ? 'selected' : '')
        );

        $ret .= sprintf('
            <div class="form-group">
                <label>Overdue Limit (in days)</label>
                <input class="form-control" type="text" name="overdue" value="%d" />
            </div>', 
            $plan->overDueLimit()
        );

        $ret .= '<div class="form-group">
            <label>Reason Label for Overdue Accounts</label>
            <select class="form-control" name="reason">';
        $ret .= $codes->toOptions($plan->reasonMask());
        $ret .= '</select></div>';

        $ret .= sprintf('<input type="hidden" name="id" value="%d" />', $plan->equityPaymentPlanID());

        $ret .= '<p><button type="submit" class="btn btn-default">Save Plan</button></p>
            </form>';

        return $ret;
    }

    public function get_view()
    {
        $dbc = FannieDB::getReadOnly($this->config->get('OP_DB'));
        $plans = new EquityPaymentPlansModel($dbc);
        $ret = '<table class="table table-bordered">
            <tr>
                <th>Name</th>
                <th>Payment Amount</th>
                <th>Frequency</th>
            </tr>';
        foreach ($plans->find('name') as $plan) {
            $ret .= sprintf('
                <tr>
                    <td>%s</td>
                    <td>%.2f</td>
                    <td>%s</td>
                    <td><a class="btn btn-default btn-xs" href="%s?id=%d">%s</a></td>
                </tr>',
                $plan->name(),
                $plan->recurringPayment(),
                $plan->billingCycle(),
                filter_input(INPUT_SERVER, 'PHP_SELF'), $plan->equityPaymentPlanID(),
                \COREPOS\Fannie\API\lib\FannieUI::editIcon()
            );
        }
        $ret .= '</table>';
        $ret .= '<p>
            <a href="?_method=put" class="btn btn-default">Create New Plan</a>
            </p>';

        return $ret;
    }

    public function unitTest($phpunit)
    {
        $values = new \COREPOS\common\mvc\ValueContainer();
        $values->_method = 'get';
        $this->setForm($values);
        $this->readRoutes();
        
        $page = $this->get_view();
        $phpunit->assertNotEquals(0, strlen($page));

        $values->_method = 'put';
        $this->setForm($values);
        $this->readRoutes();
        $this->put_handler();

        $values->_method = 'get';
        $this->setForm($values);
        $this->readRoutes();

        $newpage = $this->get_view();
        $phpunit->assertNotEquals($newpage, $page);

        $model = new EquityPaymentPlansModel($this->connection);
        $model->equityPaymentPlanID(1);
        $phpunit->assertEquals(true, $model->load());

        $values->_method = 'get';
        $values->id = 1;
        $this->setForm($values);
        $this->readRoutes();
        $page = $this->get_id_view();
        $phpunit->assertEquals(false, strstr($page, 'plan does not exist'));

        $values->_method = 'post';
        $values->id = 1;
        $values->name = 'Test';
        $values->final = '123.00';
        $values->initial = '12.00';
        $values->recurring = '23.00';
        $values->cycle = '5W';
        $values->basis = 1;
        $values->overdue = 90;
        $values->reason = 4;
        $this->setForm($values);
        $this->readRoutes();
        $this->post_id_name_final_initial_recurring_cycle_basis_overdue_reason_handler();

        $model->reset();
        $model->equityPaymentPlanID(1);
        $model->load();
        $phpunit->assertEquals($values->name, $model->name());
        $phpunit->assertEquals($values->final, $model->finalBalance());
        $phpunit->assertEquals($values->initial, $model->initialPayment());
        $phpunit->assertEquals($values->recurring, $model->recurringPayment());
        $phpunit->assertEquals($values->cycle, $model->billingCycle());
        $phpunit->assertEquals($values->basis, $model->dueDateBasis());
        $phpunit->assertEquals($values->overdue, $model->overDueLimit());
        $phpunit->assertEquals($values->reason, $model->reasonMask());
    }

    public function helpContent()
    {
        return '
            <p>
            A payment plan can be assigned to a member to
            define how much equity they need to pay in total
            and how their payments to reach the total
            should be scheduled.
            </p> 
            <p>
            Elements of a payment plan are:
            <ul>
            <li>Name - this is for organizational purposes if
            more than one plan exists.</li>
            <li>The three equity payment options fit together to
            define the total investment required, the minimum payment
            to get started (the voting share in many jurisdictions),
            and the incremental payments required to reach the total
            investment requirement. Example:
                <ul>
                <li>Total Equity Required is $100.</li>
                <li>Inital Payment is $20.</li>
                <li>Recurring Payment is $10.</li>
                <li>The member makes a $20 payment when opening their 
                account then will make eight additional $10 payments
                to reach $100 in total equity.</li>
                </ul>
            </li>
            <li>Billing Cycle defines how frequently the recurring payment
            is required. This is a number of days, weeks, months, or years.
            Examples:
                <ul>
                <li>1Y => 1 payment per year</li>
                <li>6M => 1 payment every six months</li>
                <li>90D => 1 payment every 90 days</li>
                <li>13W => 1 payment every 13 weeks</li>
                </ul>
            </li>
            <li>Calculate next due date changes how the billing cycle moves.
            For simplicity, assume an annual billing cycle. Using join date
            as the basis for next payment, if a member joins on January 1, 2000
            their next payment will be due on January 1, 2001 then January 1, 2002
            etc. If the basis is instead based on the last equity payment, making
            a payment on December 30, 2000 would move the next due date to December
            30, 2001.
            </li>
            <li>The overdue limit allows accounts to automatically change to an
            inactive status when a payment is missed. Setting a limit of zero
            means accounts will never be automatically disabled. A limit of one or
            more will disable the account after that many days have passed since
            the due date.
            </li>
            <li>Reason label is applied to accounts when deactivated for an overdue
            payment. If accounts are not automatically disabled this setting does
            not do anything.</li>
            </ul>
            </p>';
    }
}

FannieDispatch::conditionalExec();