---
# System prepended metadata

title: Deposit Management - Partial Payment in Invoice Links

---

Deposit Management - Partial Payment in Invoice Links
===

Documentation
---

### Business Rules

* If deposit is unpaid - Can’t add new deposit.
* Only one deposit at the time. If paid it allows to add new deposit.
* If it has deposit: on invoice link show the deposit amount (% of the balance due).
* If it has valid entry of deposit in the order - In Invoice link: Show deposit amount + what the deposit is. For example:
```
$1,500 (10% Deposit)
```
* Paid invoice - hide Deposit option
* Payment Transactions add comment in front of payment method, example:
```
11/09/2021 Stripe (10% Deposit) $50.00
```


Implementation
---

### Database
**Table** `order_deposits`

**Columns**
* id `int, not null, auto_increment`
* order_id `int`
* percent `float`
* amount_deposit `float`
* status `varchar`
* created_at `int`
* updated_at `int`


### System Features
- Deposit Management in View Order page
![](https://i.imgur.com/40YOYYj.png)

- Modal Popup for adding/editing deposit
![](https://i.imgur.com/SuvLedE.png)

- Display deposit in Invoice Link
![](https://i.imgur.com/Q8W0Sya.png)

- Display deposit comment in Payment Transaction
![](https://i.imgur.com/I8TmqF8.png)


### Code
**Migration**
- Adding `order_deposit` table and columns
```php=
class Create_order_deposits_table
{

    function up()
    {
        // fill up migration
        \DBUtil::create_table('order_deposits', array(
            'id' => array('type' => 'int', 'null' => false, 'auto_increment' => true, 'constraint' => '11'),
            'percent' => array('type' => 'float', 'null' => true, 'default' => 0),
            'label' => array('type' => 'varchar', 'null' => true, 'default' => null, 'constraint' => 255),
            'status' => array('type' => 'varchar', 'null' => true, 'default' => null, 'constraint' => 255),
            'created_at' => array('type' => 'int', 'null' => true, 'default' => 0),
            'updated_at' => array('type' => 'int', 'null' => true, 'default' => 0),
        ), array('id'));

        DB::query('ALTER TABLE `order_types`
ENGINE = InnoDB ;
')->execute();

    }

    function down()
    {
        // fill down migration
        \DBUtil::drop_table('order_deposits');
    }
}
```
- Adding `order_id` in `order_deposits` columns
```php=
class Add_order_id_to_order_deposits_table
{

    function up()
    {
        // fill up migration
        \DB::query('ALTER TABLE `order_deposits`
    ADD COLUMN `order_id` INT(11) NULL AFTER `id`,
    ADD INDEX `fk_order_deposits_order_id_idx` (`order_id` ASC);
    ')->execute();

        DB::query('ALTER TABLE `order_deposits`
    ADD CONSTRAINT `fk_order_deposits_order_id`
      FOREIGN KEY (`order_id`)
      REFERENCES `orders` (`id`)
      ON DELETE SET NULL
      ON UPDATE NO ACTION;
            ')->execute();
    }
    function down()
    {
        // fill down migration
        \DB::query('ALTER TABLE `order_deposits`
    DROP FOREIGN KEY `fk_order_deposits_order_id`;
    ')->execute();
        DB::query('ALTER TABLE `order_deposits`
    DROP COLUMN `order_id`,
    DROP INDEX `fk_order_deposits_order_id_idx`;
            ')->execute();
    }
}
```



**Model**

- Order Deposits Model
```php=
/**
 * Class OrderDeposits
 * @property $id
 * @property $order_id
 * @property $payment_id
 * @property $percent
 * @property $amount_deposit
 * @property $status
 * @property $created_at
 * @property $updated_at
 */
class OrderDeposits extends \Model_Base
{
    use LazyloadTraits;

    const DEPOSIT_STATUS_PAID = 'paid';
    const DEPOSIT_STATUS_UNPAID = 'unpaid';

    // Set the table to use
    protected static $_table_name = 'order_deposits';

    // List of all columns that will be used on create/update
    protected static $_properties = array(
        'id',
        'order_id',
        'payment_id',
        'percent',
        'amount_deposit',
        'status',
        'created_at',
        'updated_at',
    );

    protected static $_created_at = 'created_at';
    protected static $_updated_at = 'updated_at';

    protected $present_class = OrderDepositsPresenter::class;


    public function is_deposit_paid()
    {
        return $this->status == self::DEPOSIT_STATUS_PAID;
    }

    /**
     * @return OrderDepositsPresenter
     */
    public function present()
    {
        return parent::present();
    }

}
```

**View**
- Deposit in View Order page
```htmlembedded=
<div class="row">
  <div class="col-md-6">
    <?php if (PaymentSettings::forge()->get_enable_partial_payment()) : ?>
        <div class="js-deposit-items-container ">
        <?php if ($order->payment_status != \Order\Model_Order::PAYMENT_STATUS_PAID):  ?>
            <h4 class="title-legend mb-15px mt-10px"><?php echo _('Partial Payments for the Customer') ?><i rel="tooltip" class="fa fa-info-circle " data-placement="top" title="<?php echo _('This is the payment due amount that will display on the Invoice Link.') ?>"></i></h4>
            <table class="table table-form table-bordered js-deposit-items-table ">
                <thead>
                <tr>
                    <th><?php echo _('Deposit Percentage') ?></th>
                    <th><?php echo _('Amount') ?></th>
                    <th><?php echo _('Status') ?></th>
                    <th><?php echo _('Action') ?></th>
                </tr>
                </thead>
                <tbody>
                  <?php if ($order_deposits): ?>
                      <?php foreach ($order_deposits as $order_deposit):
                        /**
                        * @var $order_deposit \Flex\Models\OrderDeposits
                        */
                        ?>
                  <tr>
                      <td><?php echo $order_deposit->present()->percent_with_symbol_label(); ?></td>
                      <td><?php echo $order_deposit->present()->fix_amount_formatted(); ?></td>
                      <td><span class="label label-success <?php echo $order_deposit->present()->status_label_class(); ?>"><?php echo $order_deposit->present()->status()?></span></td>
                      <td><a href="javascript:void(0)" data-target-url="<?php echo Uri::create(sprintf("admin/order/get_deposit_modal_form/%s/%s", $order->id, $order_deposit->id)); ?>" class="js-update-deposit <?php echo $order_deposit->is_deposit_paid() ? 'disabled' : ''; ?>">Edit</a><a class="text-danger confirmation-pop-up" data-message="<?php echo $order_deposit->present()->delete_confirmation_message(); ?>" href="javascript:void(0)" data-target-url="<?php echo \Uri::create('admin/order/delete_deposit/' . $order_deposit->id); ?>"><?php echo _('Delete'); ?></a></td>
                  </tr>
                  <?php endforeach; ?>
                  <?php else: ?>
                  <tr>
                      <td class="center"><strong><?php echo _('There are no deposits.'); ?></strong></td>             </tr>
                  <?php endif; ?>
                  </tbody>
             </table>
             <div class="clearfix">
                <div class="pull-right">
                     <a href="javascript:void(0)" data-target-url="<?php echo Uri::create(sprintf("admin/order/get_deposit_modal_form/%s", $order->id)); ?>" class="btn btn-default js-update-deposit <?php echo $order->check_if_has_unpaid_deposit() ? 'disabled' : ''; ?>">
                     <i class="fa fa-plus"></i> <?php echo _('Add New Payment Amount'); ?></a>
                </div>
             </div>
        <?php endif; ?>
    </div>
  <?php endif; ?>
</div>
```
- Create/Update deposit Modal
    - Modal Container
    ```htmlembedded=
    <div class="modal fade js-deposit-form-modal" role="dialog">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal">&times;</button>
                    <h4 class="modal-title"><!--Dynamically loaded from ajax--></h4>

                </div>
                <div class="modal-body js-deposit-form-wrapper">
                    <!--Dynamically loaded from ajax-->
                </div>
            </div>
        </div>
    </div>

    ```

    - Modal Form
    ```htmlembedded=
    <?php
    /**
     * @var $order_deposit \Flex\Models\OrderDeposits
     */
    echo \Form::open(array(
        'action' => Uri::create('admin/order/update_deposit'),
        'method' => 'post',
    ));
    ?>

    <?php echo \Form::csrf() ?>
    <div class="row">
        <div class="col-xs-12">
            <div class="panel-body">
                <div class="form-horizontal">
                    <input type="hidden" name="order_id" value="<?php echo e($order->id); ?>">
                    <input class="js-order-grand-total" type="hidden" name="order_grand_total" value="<?php echo e($order->get_remaining_balance()); ?>">
                    <?php if (!$order_deposit->is_new()): ?>
                        <input type="hidden" name="deposit_id" value="<?php echo e($order_deposit->id); ?>">
                    <?php endif; ?>
                    <div class="col-xs-12">
                        <div class="form-group">
                            <label class="col-xs-4 control-label force-right"><?php echo _('Deposit Percentage'); ?></label>
                            <div class="col-xs-8">
                                <input class="form-control js-deposit-percentage" name="deposit_percentage" value="<?php echo $order_deposit ? $order_deposit->present()->percentage() : ''; ?>"
                                       placeholder="<?php echo _('Deposit'); ?>" required>
                            </div>
                        </div>
                    </div>
                    <div class="col-xs-12">
                        <div class="form-group">
                            <label class="col-xs-4 control-label force-right"><?php echo _('Amount'); ?></label>
                            <div class="col-xs-8">
                                <input class="form-control js-fix-amount" value="<?php echo $order_deposit->present()->fix_amount_formatted(); ?>" readonly>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="modal-footer">
        <button type="submit" class="btn btn-primary"><?php echo $order_deposit->is_new() ?  _('Add Deposit') : _('Update Deposit'); ?></button>
    </div>

    <?php echo \Form::close(); ?>
    ```
    
- Ajax to call modal and submit data
```javascript=
var depositManagement = function($container){
    var form = {
        init: function ($form){
            var grandTotalAmount = $form.find('.js-order-grand-total'),
                getFixAmount = $form.find('.js-fix-amount');

            $form.find('.js-deposit-percentage').keyup(function (){
                var depositVal = $(this).val();
                    calculatePercentage = (depositVal / 100 ) * grandTotalAmount.val();

                getFixAmount.val(flex.formatMoney(calculatePercentage));
            });
        },
    };
    var table = {
        init: function (){
            $container.find('.js-update-deposit').on('click', function (e) {
                e.preventDefault();
                var $this = $(this),
                    depositUrl = $this.data('target-url');

                $.ajax({
                    type: 'get',
                    dataType: 'json',
                    url: depositUrl,
                    success: function (data) {
                        var $modal = $('.js-deposit-form-modal');
                        $modal.find('.modal-body').html(data.form_html);
                        $modal.find('.modal-title').html(data.header_title);
                        $modal.modal('show');
                        form.init($modal.find('form'));
                    },
                });
            });
        }
    };

    table.init();
};

// Call variable depositManagement
 $(function () {
        addPaymentForm($('.js-add-payment-form'));
        depositManagement($('.js-deposit-items-container'));
    });
```

- Display `order_deposit` in Invoice Link Page
```php=
// Declare order_deposit
$order_deposit = $order->get_first_unpaid_deposit();
$amount_to_pay = $order_deposit ? $order_deposit>amount_deposit : $order>get_remaining_balance();

// Set hidden data for deposit
<?php echo \Form::hidden('deposit_id', ($order_deposit ? $order_deposit->id : null), ['class' => 'js-deposit-id']); ?>
<?php echo \Form::hidden('deposit_amount', ($order_deposit ? $order_deposit->amount_deposit : null), ['class' => 'js-deposit-amount']); ?>
```
```htmlembedded=
<!-- Display if has deposit -->
<div class="amount">
    <span class="js-amount-to-pay"><?php echo Helper::format_money($amount_to_pay); ?></span>
    <?php if ($order_deposit) : ?>
        <label class="js-percent-info ml-2"><?php echo e($order_deposit->present()->percent_with_deposit_label()); ?></label>
    <?php endif; ?>
</div>
```
- Dynamically display the deposit in js
```javascript=

var paymentDue = data.grand_total  - data.total_payments,
    amountToPay = paymentDue;

// check if has we allowed deposit
var depositId = $wrapper.find('.js-deposit-id').val(),
    depositAmount = $wrapper.find('.js-deposit-amount').val(),
    $depositInfo = $wrapper.find('.js-percent-info');

$depositInfo.hide()
if (depositId && depositAmount < paymentDue) {
    $depositInfo.show();
    amountToPay = depositAmount;
}

$('.js-amount-to-pay').text(flex.formatMoney(amountToPay));

flex.cart.initiateAfterPayFromContainer($wrapper, paymentDue);
```


**Controller**

- Fetch data of deposits by its `order_id`
```php=
public function get_order_deposits()
{
    return OrderDeposits::find_by_order_id($this->id);
}
```
- Fetch unpaid order deposits
```php=
/**
 * @param false $recache
 * @return mixed
 */
public function get_first_unpaid_deposit()
{
    $item = $this;
    return OrderDeposits::find_one_by(function ($query) use ($item) {
        $query->where('order_id', $item->id);
        $query->where('status', OrderDeposits::DEPOSIT_STATUS_UNPAID);
    });
}
```

- Call ajax and submit data
```php=
public function action_get_deposit_modal_form($order_id, $deposit_id = null)
{
    $order = Model_Order::find_one_by_id($order_id);

    if (!empty($deposit_id))
    {
        $order_deposit = OrderDeposits::find_one_by_id($deposit_id);
    }
    else
    {
        $order_deposit = OrderDeposits::forge();
    }

    $form_html = \Theme::instance('admin')->view('views/order/deposit/_modal_form')
        ->set('order_deposit', $order_deposit, false)
        ->set('order', $order, false)
        ->render();

    return $this->jsonResponse([
        'header_title' => $order_deposit->is_new() ?_('Add Deposit') : _('Edit Deposit'),
        'form_html' => $form_html
    ]);
}
```

- Add/Update the deposit process
```php=
public function action_update_deposit()
{
    $post = Input::post();
    $order_id = Arr::get($post, 'order_id');
    $deposit_id = Arr::get($post, 'deposit_id');
    $order = Model_Order::find_one_by_id($order_id);
    $order_deposit = OrderDeposits::find_one_by_id($deposit_id);

    $redirect_back = \Input::post('_redirect', \Uri::create("admin/order/update/{$order_id}"));

    if ($order_id)
    {
        if ($deposit_id) {
            $order_deposit = OrderDeposits::find_one_by_id($deposit_id);
        } else {
            $order_deposit = OrderDeposits::forge();
        }

        $is_new = $order_deposit->is_new();
        $total_amount = $order->get_remaining_balance();
        $deposit_percentage = Arr::get($post, 'deposit_percentage');
        $amount_deposit = \OrderHelper::calculate_percentage_deposit($deposit_percentage, $total_amount);

        if ($amount_deposit > $total_amount) {
            \Messages::error(_('Invalid deposit amount.'));
            \Response::redirect($redirect_back);
        }

        $order_deposit->set([
            'percent' => $deposit_percentage,
            'order_id' => $order_id,
            'amount_deposit' => $amount_deposit,
            'status' => OrderDeposits::DEPOSIT_STATUS_UNPAID,
        ]);

        $order_deposit->save();

        \Messages::success($is_new ? _('Deposit successfully created.') : _('Deposit successfully updated.'));
        Response::redirect($redirect_back);
    }
}
```

- Process payment in `action_paynow`
```php=
 // override amount based on received deposit
 $deposit_id = \Arr::get($post, 'deposit_id');
 $order_deposit = OrderDeposits::find_one_by_id($deposit_id);
 if ($order_deposit && $order_deposit->order_id == $order->id && $order_deposit->amount_deposit <= $totals->get_balance_due())
 {
     $amount = \Helper::format_number_plain($order_deposit->amount_deposit);
 }
 else
 {
     // deposit is invalid, make it empty
     $deposit_id = null;
     $amount = $totals->get_balance_due();
 }

 $data_handler->set_amount($amount);

 $payment
     ->set_data_handler($data_handler)
     ->save_session([
         'amount' => $amount,
         'deposit_id' => $deposit_id,
     ])
     ->set_inputs(\Input::post())
     ->set_return_url(\Uri::create(sprintf('order/complete_payment/%s/%s', $order->uuid, $payment_method)))
     ->set_cancel_url(\Uri::create(sprintf('order/paynow/%s?action=', $order->uuid, 'payment_cancelled')));

 $payment->process();
 $amount = $payment->get_data_handler()->get_amount();
 $this->complete_order($order, $totals, $payment, $amount);
```

- Set comment and deposit status in complete order
```php=
// check if has deposit id included
$session_data = $payment->get_session();
$deposit_id = \Arr::get($session_data, 'deposit_id');
$deposit = OrderDeposits::find_one_by_id($deposit_id);

if ($deposit_id && $deposit) {
    $deposit->set([
        'status' => OrderDeposits::DEPOSIT_STATUS_PAID,
    ]);
    $deposit->save();

    $model_payment = $payment->get_last_payment_created();

    // insert comment to payment.
    if ($model_payment) {
        $model_payment->set([
            'comment' => $deposit->present()->percent_with_deposit_label(),
        ]);
        $model_payment->save();
    }
}

```

- Set `last_payment_created` in PaymentsAbstract
```php=
/**
 * Last model payment created
 *
 * @var Model_Payment
 */
protected $last_payment_created;

/**
 * @inheritdoc
 */

public function set_last_payment_created(Model_Payment $payment)
{
    $this->last_payment_created = $payment;
    return $this;
}

/**
 * @inheritdoc
 */
public function get_last_payment_created()
{
    return $this->last_payment_created;
}
```

- Set `last_payment_created` in PaymentsInterface
```php=
/**
 * @param Model_Payment $payment
 *
 * @return mixed
 */
public function set_last_payment_created(Model_Payment $payment);

/**
 * @return Model_Payment
 */
public function get_last_payment_created();
```
- And set `last_payment_created` in OrderDataHandler
```php=
public function complete($transaction_id, $create_payment = true)
{
    if ($create_payment) {
        $payment = $this->create_payment();
        $payment->set([
            'transaction_id' => $transaction_id,
            'status' => Model_Payment::STATUS_COMPLETED,
            'response' => 'Ok',
        ]);
        $payment->save();

        PaymentTriggers::created($payment);
        $this->payment->set_last_payment_created($payment);
    }

    $order = $this->order;
    $order->re_check_payments_status();
}
```
