0

I am working on an invoice in yii2. The invoice has items added dynamically using the wbraganca\dynamicform\DynamicFormWidget. These items each has quantity and price inputs, and an amount updated automatically based on the values of quantity and price. At the bottom of the invoice, is the total which is the sum of the amount of each item of the invoice. The total field is also updated automatically when any of the input fields i.e. quantity and price changes. I have tried coming up with a Javascript code that I got online but this only updates the first item amount but the rest are not working. Below is my code for the form...

_form.php

<?php

use yii\helpers\Html;
use kartik\widgets\ActiveForm;
use wbraganca\dynamicform\DynamicFormWidget;
use kartik\widgets\DatePicker;
use yii\helpers\Url;
use yii\helpers\ArrayHelper;
use app\models\Tenant;
use app\models\InvoiceItems;
use app\models\FinancialAccounts;

/* @var $this yii\web\View */
/* @var $model app\models\Invoices */
/* @var $form yii\widgets\ActiveForm */
?>

<?php
/* start getting the totalamount */
$script = <<<EOD
    var getAmount = function() {

        var qnty = $(".qnty").val();
        var price = $(".price").val();
        var amount = 0;

        amount = parseInt(qnty) * parseInt(price);

        //Assign the sum value to the field
        $(".amount").val(amount);
    };

    $(".price").on("change", function() {
        getAmount();
    });
EOD;
$this->registerJs($script);
/*end getting the totalamount */
?>

<div class="invoices-form">

    <?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?>

    <fieldset>

        <legend>Invoice Details</legend>

        <div class="form-group kv-fieldset-inline">
            <div class="col-sm-3">
                <?= $form->field($modelInvoice, 'tenant_id')->dropDownList(ArrayHelper::map(Tenant::find()->select(['tenant_id', 'first_name', 'last_name', 'address'])->all(), 'tenant_id', 'displayName'),
                [
                    'prompt'=>'Select Tenant', 'class' => 'form-control inline-block',
                    'onchange'=>'
                        $.post( "'.Yii::$app->urlManager->createUrl('lease/list-lease?tenant_id=').'"+$(this).val(), function( data ) {
                          $( "select#lease" ).html( data );
                        });
                    '
                ]) ?>
            </div>

            <div class="col-sm-3">
                <?php 
                $dataLease=ArrayHelper::map(\app\models\Lease::find()->asArray()->all(), 'lease_id', 'leaseDetails');
                echo $form->field($modelInvoice, 'lease')
                    ->dropDownList(
                        $dataLease,           
                        ['id'=>'lease']
                    );
            ?>
            </div>

            <div class="col-sm-3">
                <?= $form->field($modelInvoice, 'date')->widget(DatePicker::classname(), ['options' => ['placeholder' => 'Enter Invoice date ...'], 'pluginOptions' => ['autoclose'=>true, 'format' => 'yyyy-mm-dd', 'endDate' => '0d']]) ?>
            </div>

            <div class="col-sm-3">
                <?= $form->field($modelInvoice, 'due_date')->widget(DatePicker::classname(), ['options' => ['placeholder' => 'Enter due date ...'], 'pluginOptions' => ['autoclose'=>true, 'format' => 'yyyy-mm-dd', 'startDate' => '0d']]) ?>
            </div>

            <div class="col-sm-3">
                <?= $form->field($modelInvoice, 'invoice_number')->textInput(['maxlength' => true]) ?>
            </div>
        </div>        

    </fieldset>

    <fieldset>

        <legend>Invoice Line Items</legend>

        <?php DynamicFormWidget::begin([
            'widgetContainer' => 'dynamicform_wrapper',
            'widgetBody' => '.container-items',
            'widgetItem' => '.invoice-item',
            'limit' => 10,
            'min' => 1,
            'insertButton' => '.add-item',
            'deleteButton' => '.remove-item',
            'model' => $modelsInvoiceItem[0],
            'formId' => 'dynamic-form',
            'formFields' => [
                'Items',
            ],
        ]); ?>

        <table class="table table-bordered table-striped">
            <thead>
                <tr>
                    <th>Invoice Item</th>
                    <th>Description</th>
                    <th>Quantity</th>
                    <th>Price (Kshs)</th>
                    <th>Account</th>
                    <th>Amount</th>
                    <th class="text-center" style="width: 5%;">
                        <button type="button" class="add-item btn btn-success btn-xs"><span class="fa fa-plus"></span></button>
                    </th>
                </tr>
            </thead>
            <tbody class="container-items">
            <?php foreach ($modelsInvoiceItem as $indexItem => $modelInvoiceItem): ?>
                <tr class="invoice-item">
                    <td class="vcenter">
                        <?php
                            // necessary for update action.
                            if (! $modelInvoiceItem->isNewRecord) {
                                echo Html::activeHiddenInput($modelInvoiceItem, "[{$indexItem}]id");
                            }
                        ?>
                        <?= $form->field($modelInvoiceItem, "[{$indexItem}]item_id")->label(false)->dropDownList(ArrayHelper::map(InvoiceItems::find()->select(['item_id', 'item'])->all(), 'item_id', 'item'),['class' => 'form-control inline-block', 'prompt'=>'Select Invoice Item']) ?>
                    </td>
                    <td class="vcenter">
                        <?= $form->field($modelInvoiceItem, "[{$indexItem}]desc")->label(false)->textInput(['maxlength' => true]) ?>
                    </td>
                    <td class="vcenter">
                        <?= $form->field($modelInvoiceItem, "[{$indexItem}]qnty")->label(false)->textInput(['class' => 'qnty']) ?>
                    </td>
                    <td class="vcenter">
                        <?= $form->field($modelInvoiceItem, "[{$indexItem}]price")->label(false)->textInput(['class' => 'price']) ?>
                    </td>
                    <td class="vcenter">
                        <?= $form->field($modelInvoiceItem, "[{$indexItem}]account_id")->label(false)->dropDownList(ArrayHelper::map(FinancialAccounts::find()->select(['account_id', 'account_name'])->all(), 'account_id', 'account_name'),['class' => 'form-control inline-block', 'prompt'=>'Select Account']) ?>
                    </td>
                    <td class="vcenter">
                        <?= $form->field($modelInvoiceItem, "[{$indexItem}]amount")->label(false)->textInput(['class'=>'amount','readOnly'=> true]) ?>
                    </td>
                    <td class="text-center vcenter" style="width: 5%; verti">
                        <button type="button" class="remove-item btn btn-danger btn-xs"><span class="fa fa-minus"></span></button>
                    </td>
                </tr>
             <?php endforeach; ?>
            </tbody>
            <tbody>
                <tr>
                     <td></td>
                     <td></td>
                     <td></td>
                     <td></td>
                     <td>Total</td>
                     <td class="total">
                         <?= $form->field($modelInvoice, "total")->label(false)->textInput(['class'=>'total','readOnly'=> true]) ?>
                     </td>
                     <td></td>
                 </tr>
            </tbody>
        </table>

        <?php DynamicFormWidget::end(); ?>

    </fieldset>


    <div class="col-sm-offset-3 col-sm-9">
        <?= Html::submitButton($modelInvoice->isNewRecord ? 'Create' : 'Update', ['class' => $modelInvoice->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
        <?= Html::resetButton('Reset', ['class' => 'btn btn-default']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>

Specifically, this is the Javascript code

<?php
/* start getting the totalamount */
$script = <<<EOD
    var getAmount = function() {

        // var items = $(".item");
        // var qnty = $(elem).find(".qnty").val();
        var qnty = $(".qnty").val();
        var price = $(".price").val();
        // var price = $(elem).find(".price").val();
        var amount = 0;

        amount = parseInt(qnty) * parseInt(price);

        // items.each(function (index, elem) {
        //     var priceValue = $(elem).find(".sumPart").val();
        //     //Check if priceValue is numeric or something like that
        //     sum = parseInt(sum) + parseInt(priceValue);
        // });
        //Assign the sum value to the field
        $(".amount").val(amount);
    };

    //Bind new elements to support the function too
    $(".price").on("change", function() {
        getAmount();
    });
EOD;
$this->registerJs($script);
/*end getting the totalamount */
?>

And here is the screenshot of the form that I am working with: Screen Shot of the Invoice

I am not good with Javascript hence I don't understand most of the code but I get the idea and flow of information. Please assist me on what I can do here. Thanks.

1

2 Answers 2

1

You use $ sign with class selector (".class"). So you get set in return.

jQuery val method:

Get the current value of the first element in the set of matched elements or set the value of every matched element.

You can use row index to make different elements unique.

Try

$(".price").on("change", function(event) {
    getAmount(event);
});

function getAmount(event) {
    var priceElement = event.target;
    // extract row index from priceElement's id
    // use row index to find elements with id selector ("#elementId")
};
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your answer @Sui. Exactly how do I extract the row elements? I am not very good at Javascript, if you could kindly provide a working code, I will appreciate.
I just checked the page source and I find that the id's of the input fields are numbered as invoiceline-0-price, invoiceline-1-price etc. The center value is dynamic depending on the index. How do I do the same in Javascript?
@japheth, you can use substr() method: priceElement.attr('id').substr(12, 1)
Then, you can use string concatenation to get other element's id, like 'invoiceline-' + priceElement.attr('id').substr(12, 1) + '-price'
I managed to solve the problem using the .class element and looping through each item. I will update my solution as an answer below. Thanks for the help.
1

Based on @Sui Dream solution above, and from researching other places on the internet, this is what I was able to do finally. I only updated the script and everything worked fine:

<?php
/* start getting the totalamount */
$script = <<<EOD
    var getAmount = function() {

        var items = $(".invoice-item");
        var amount = 0;
        var total = 0;

        items.each(function (index, elem) {
            var qnty = $(elem).find(".qnty").val();
            var price = $(elem).find(".price").val();
            //Check if qnty and price are numeric or something like that
            amount = parseInt(qnty) * parseInt(price);

            //Assign the amount value to the field
            $(elem).find(".amount").val(amount);

            var amountValue = $(elem).find(".amount").val();

            total = parseInt(total) + parseInt(amountValue);

            $(".total").val(total);
        });
    };

    //Bind new elements to support the function too
    $(".container-items").on("change", function() {
        getAmount();
    });
EOD;

$this->registerJs($script);
?>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.