1

I am using CouchCMS. In CouchCMS there is a concept of repeatable regions. This in fact generates tables and displays the repeatable contents in it.

I have the repeatable region defined as:

<cms:repeatable name="item_detail" label="Item Detail" order="10" >
    
    <cms:editable name="product" label="Product" type="dropdown" opt_values="Select =- | <cms:pages masterpage='product/product.php' order='asc' orderby='product_name'><cms:show product_name /><cms:if '<cms:not k_paginated_bottom />'>|</cms:if></cms:pages>" order="1" />
    <cms:editable name="product_hsn" label="HSN" type="text" order="2" />hsn,qty,price,gst,amount
    <cms:editable name="product_qty" label="Quantity" type="text" order="3" />
    <cms:editable name="product_price" label="Price" type="text" order="4" />
    <cms:editable name="product_tax" label="Tax" type="text" order="5" />
    <cms:editable name="product_line_total_amount" label="Amount" type="text" order="6" />

</cms:repeatable>  

Where the editables are the regions where we can fill in the data by bounding them to the respective textboxes/ selects, etc.

Now What I am trying to do is:

  1. Select a value from the dropdown of the editable named "product".
  2. When an option is selected, an AJAX is called. This AJAX in turn returns some data.
  3. I am able to get the data, console log it or display it in a div or a table.

But what I really want to do is:

  1. Since the repeatable region is shown is the form of a table, the exists. I want to be able to just get the AJAX JSON data displayed in the of the existing data.
  2. If you see the editables above, a table exists with each editables' select/textbox in the , there are:
  • product (select, for product name)
  • product_hsn (textbox, with to be created and be filled by AJAX JSON)
  • product_qty (textbox, with to be created but needs to be blank)
  • product_price (textbox, with to be created and be filled by AJAX JSON)
  • product_tax (textbox, with to be created and be filled by AJAX JSON)
  • product_line_total_amount (textbox, with to be created but needs to be blank)

Now the repeatable region creates a structure as follows ( for the above defined repeatable region):

Product HSN Quantity Price Tax Amount Delete
<div>
    <p class="addRow" id="addRow_f_item_detail"><a>Add a Row</a></p>
</div>

I can add new rows also right out of the box when using repeatable regions. If one observes ids and the names have a zero [0] this keeps on incrementing as one would add the new rows. The script helping in this above code generation is:

if ( !window.COUCH ) var COUCH = {};
    $(function(){
        $('table.rr > tbody').sortable({
            axis: "y",
            handle: ".dg-arrange-table-rows-drag-icon",
            helper: function (e, ui) { 
                // https://paulund.co.uk/fixed-width-sortable-tables
                ui.children().each(function() {                
                    $(this).width($(this).width());
                });
            return ui;
        },
        update: function( event, ui ){
            var row = ui.item;
            var tbody = $( row ).closest( 'tbody' );
            tbody.trigger('_reorder');
        },
        start: function( event, ui ){    
            var row = ui.item;
            row.trigger('_reorder_start');
        },
        stop: function( event, ui ){
            var row = ui.item;
            row.trigger('_reorder_stop');
        },
    });
});
COUCH.rrInit = function( field_id, default_row ){
    var $field = $('#'+field_id);
    $field.tableGear({addDefaultRow:default_row, stackLayout:1});
    $field.on('click', '.col-actions .add-row', function(){
        var $this = $(this);
        var row_id = $this.attr('data_mosaic_row');
        var add_btn = $('#addRow_'+field_id+' a');
        add_btn.trigger("click", [row_id]);
    });
}
COUCH.t_confirm_delete_row = "Delete this row?";
COUCH.t_no_data_message = "- No Data -";

Just in case if required this is my AJAX code, using which I am able to add a new but it is in a New while I want the to be appended to the same that contains the existing repeatable regions. AJAX CODE:

$(document).on('change','select',function() {
    var data = "";
    $.ajax({
        type:"GET",
        url : "<cms:show k_site_link />generate/quotation-ajax.php",
        data: 
            "select_id="+$(this).val(),
        async: false
    }).done(function(data) {
        console.log(data);
        var trHTML = '';
        $.each(data.product_details, function (i, item) {
            trHTML += "<tr id='f_item_detail-" + i + "'>" + '<td class="editable k_element_product_hsn"><div style="position:relative;"><input type="bound" name=" f_item_detail[0][product_hsn]" id="f_item_detail-[0]-product_hsn" class="form-control" value="' + item.product_hsn + '"/></div></td>' +
            // '<td style="position:relative;"><input type="bound" name=" f_item_detail[0][product_price]" id="f_item_detail-[0]-product_price" class="form-control" value="' + item.product_price + '"/></td>' +
            // '<td style="position:relative;"><input type="bound" name=" f_item_detail[0][product_tax]" id="f_item_detail-[0]-product_tax" class="form-control" value="' + item.product_tax + '"/></td>' +
            '</tr>';
        });
        $('#f_item_detail').append(trHTML);
    })
});

And my AJAX file has the code:

<?php require_once('../couch/cms.php'); ?>
<cms:set selected_product="<cms:gpc 'select_id' method='get' />" scope="global" />
<cms:content_type 'application/json'/>
<cms:template title="Quotation AJAX" hidden='1' parent="_generate_" />
    {
        "product_details": 
        [
            <cms:pages masterpage='product/product.php' custom_field="product_name=<cms:show selected_product />" >
            {
                "product_hsn": "<cms:addslashes><cms:show product_hsn/></cms:addslashes>",
                "product_price": "<cms:addslashes><cms:show min_selling_cost/></cms:addslashes>",
                "product_tax": "<cms:addslashes><cms:show tax_on_purchase/></cms:addslashes>"   
            }<cms:if "<cms:not k_paginated_bottom/>">,</cms:if>
            </cms:pages>
        ]
    }
<?php COUCH::invoke(); ?>

What I am looking for: Add the AJAX success JSON values to the respective textboxes in the existing , and rather than adding a new or or . I am unable to set the correct jQuery. Any help would be really appreciated.

Thanks in advance. Regards! @Swati: Full HTML in this fiddle (with some changes in the AJAX part, which partially works and outputs what I want to achieve. The value is put into the textbox but for each new row the same textbox value is updated from the first row, if i could update the textbox values row wise it would be great)

EDIT #1 I have used your code (@Swati) as follows and yes it works fine (to an extent).

<script type="text/javascript">
    $(document).ready(function(){
        $("#f_item_detail-0-product").select2();
        $('input#f_item_detail-0-product_hsn').attr('readonly', true).addClass("form-control");
        $('input#f_item_detail-0-product_qty').attr('onchange', 'line_total()');
        $('input#f_item_detail-0-product_price').attr('onchange', 'line_total()');
        $('input#f_item_detail-0-product_tax').attr('readonly', true).addClass("form-control");
        $('input#f_item_detail-0-line_tax_amount').attr('readonly', true).addClass("form-control");
        $('input#f_item_detail-0-product_line_total_amount').attr('readonly', true).addClass("form-control");
    });
    
    var counter = 0;
    $(document).ready(function() {
        $(".addRow").click(function(){
            counter++;
            $("#f_item_detail-" + counter + "-product").select2();
        });
    });

    $(document).on('change','select',function() {
        var data = "";
        var i = 0;
        var indexs = $(this).closest("tr").index();//get index no
        console.log(indexs);

        $.ajax({
            type:"GET",
            url : "<cms:show k_site_link />generate/quotation-ajax.php",
            data: 
                "select_id="+$(this).val(),
            async: false
        }).done(function(data) {
            $('#f_item_detail-' + indexs + '-product_hsn').val(data.product_details[i].product_hsn).attr('readonly', true).addClass("form-control");

            $('#f_item_detail-' + indexs + '-product_qty').attr('onchange', 'line_total()');

            $('#f_item_detail-' + indexs + '-product_price').val(data.product_details[i].product_price).attr('onchange', 'line_total()');

            $('input#f_item_detail-' + indexs + '-product_tax').val(data.product_details[i].product_tax).attr('readonly', true).addClass("form-control");

            $('#f_item_detail-' + indexs + '-line_tax_amount').attr('readonly', true).addClass("form-control");

            $('#f_item_detail-' + indexs + '-product_line_total_amount').val(data.product_details[i].product_line_total_amount).attr('readonly', true).addClass("form-control");
        });
    });
            
    function line_total(){
        var line_qty = $('input#f_item_detail-' + indexs + '-product_qty').val();
        var line_tax = $('input#f_item_detail-' + indexs + '-product_tax').val();
        var line_cost =  $('input#f_item_detail-' + indexs + '-product_price').val();
        var line_tax_amount = parseFloat(((line_cost * line_tax)/100) * line_qty).toFixed(2);
        var result = parseFloat((+line_qty * +line_cost) + +line_tax_amount).toFixed(2);
        $('#f_item_detail-' + indexs + '-line_tax_amount').val(line_tax_amount).attr('hidden',true);
        $('#f_item_detail-' + indexs + '-product_line_total_amount').val(result);
    }

</script>

It is solving the issue of going back and editing the product and hence updating the line item value as you had suggested.

But if you see the function line_total() it breaks. And the total are not calculated. What do you suggest? How can we use the indexs value or something else. Also, I would be greatful if you could also suggest me how can we display the GST Amount total and Amount Total at the end with a Grand Total (GST Amount Total + Amount Total), I would be really greatful.

I am not good with javascript or jQuery at all.

GXCPL.Official
  • 267
  • 3
  • 19
  • Hi, can you show html generated or if that thing is online somewhere then it would easy to test. – Swati May 11 '21 at 11:54
  • @Swati: Please see the fiddle – GXCPL.Official May 11 '21 at 12:22
  • Hi, is there any thing which uniquely identify that row and in that code select-box is this which has option `3Ply Mask...` etc . – Swati May 11 '21 at 14:21
  • @Swati: Yes the code "$(document).on('change','select',function()" does that for each select-box. I have answered the question below. Got it to work. Thanks for your time. If you or someone else could help me in refining the code, I would be grateful. – GXCPL.Official May 11 '21 at 14:33
  • Below answer will only work depending on last counter values . What if counter is `2` and user need to change `1` tr value ? – Swati May 11 '21 at 14:34
  • 1
    @Swati I guess it was too early to celebrate. Yes you have a valid point. I didnot ponder upon it. Could you suggest something? – GXCPL.Official May 11 '21 at 14:36

2 Answers2

1

Whenever your select-box gets change you can simply get closest tr from that select-box then .find() to find required inputs and add value there .

Demo Code :

$(document).on('change', 'select', function() {
  var selector = $(this).closest("tr") //get closest tr
  /* $.ajax({
     type: "GET",
     url: "<cms:show k_site_link />generate/quotation-ajax.php",
     data: "select_id=" + $(this).val(),
     async: false
   }).done(function(data) {*/
  //find your input and add value there
  selector.find('.k_element_product_hsn input').val("ac"); //data.product_details[i].product_hsn
  selector.find('.k_element_product_price input').val(124); //data.product_details[i].product_price
  selector.find('.k_element_product_tax input').val(23); //data.product_details[i].product_tax
  selector.find('.k_element_product_line_total_amount input').val(4356); //data.product_details[i].product_line_total_amount
  selector.find('.k_element_product_qty input').val(2); //data.product_details[i].product_qty


  /*}
  })*/
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>

<table>
  <tbody>
    <tr id="newDataRow_f_item_detail" class="newRow even">
      <td class="dg-arrange-table-rows-drag-icon">&nbsp;</td>
      <td class="editable k_element_product">
        <div style="position:relative;">
          <select name="data[xxx][product]" idx="data-xxx-product" id="data-xxx-product">
            <option value="-">Select</option>
            <option value="3Ply Mask">3Ply Mask</option>
            <option value="Laptop i3 4th Gen">Laptop i3 4th Gen</option>
          </select>
        </div>
      </td>
      <td class="editable k_element_product_hsn">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_hsn" id="data-xxx-product_hsn" name="data[xxx][product_hsn]" value="">
        </div>
      </td>
      <td class="editable k_element_product_qty">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_qty" id="data-xxx-product_qty" name="data[xxx][product_qty]" value="">
        </div>
      </td>
      <td class="editable k_element_product_price">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_price" id="data-xxx-product_price" name="data[xxx][product_price]" value="">
        </div>
      </td>
      <td class="editable k_element_product_tax">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_tax" id="data-xxx-product_tax" name="data[xxx][product_tax]" value="">
        </div>
      </td>
      <td class="editable k_element_product_line_total_amount">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_line_total_amount" id="data-xxx-product_line_total_amount" name="data[xxx][product_line_total_amount]" value="">
        </div>
      </td>
      <td class="delete"><input type="checkbox" name="delete[]" value="" id="deleteNULL_STRING" style="display: none;" /><label for="deleteNULL_STRING">    <img src="http://localhost/CTO/GXCPL-Billing/couch/addons/repeatable/tablegear/delete.gif" alt="Delete Row" /></label></td>
    </tr>
    <tr id="newDataRow_f_item_detail" class="newRow even">
      <td class="dg-arrange-table-rows-drag-icon">&nbsp;</td>
      <td class="editable k_element_product">
        <div style="position:relative;">
          <select name="data[xxx][product]" idx="data-xxx-product" id="data-xxx-product">
            <option value="-">Select</option>
            <option value="3Ply Mask">3Ply Mask</option>
            <option value="Laptop i3 4th Gen">Laptop i3 4th Gen</option>
          </select>
        </div>
      </td>
      <td class="editable k_element_product_hsn">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_hsn" id="data-xxx-product_hsn" name="data[xxx][product_hsn]" value="">
        </div>
      </td>
      <td class="editable k_element_product_qty">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_qty" id="data-xxx-product_qty" name="data[xxx][product_qty]" value="">
        </div>
      </td>
      <td class="editable k_element_product_price">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_price" id="data-xxx-product_price" name="data[xxx][product_price]" value="">
        </div>
      </td>
      <td class="editable k_element_product_tax">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_tax" id="data-xxx-product_tax" name="data[xxx][product_tax]" value="">
        </div>
      </td>
      <td class="editable k_element_product_line_total_amount">
        <div style="position:relative;"><input type="text" idx="data-xxx-product_line_total_amount" id="data-xxx-product_line_total_amount" name="data[xxx][product_line_total_amount]" value="">
        </div>
      </td>
      <td class="delete"><input type="checkbox" name="delete[]" value="" id="deleteNULL_STRING" style="display: none;" /><label for="deleteNULL_STRING">    <img src="http://localhost/CTO/GXCPL-Billing/couch/addons/repeatable/tablegear/delete.gif" alt="Delete Row" /></label></td>
    </tr>
  </tbody>
</table>

Updated 1 :

You can get index of tr which is change then using that index we can update that input values .

Updated Jquery code :

 $(document).on('change', 'select', function() {
  var data = "";
  var i = 0;
  var indexs = $(this).closest("tr").index();//get index no
  console.log(indexs)

  $.ajax({
    type: "GET",
    url: "<cms:show k_site_link />generate/quotation-ajax.php",
    data: "select_id=" + $(this).val(),
    async: false
  }).done(function(data) {

    $('#f_item_detail-' + indexs + '-product_hsn').val(data.product_details[i].product_hsn).attr('readonly', true).addClass("form-control");

    $('#f_item_detail-' + indexs + '-product_qty').attr('onchange', 'add_number()');

    $('#f_item_detail-' + indexs + '-product_price').val(data.product_details[i].product_price).attr('onchange', 'add_number()');

    $('input#f_item_detail-' + indexs + '-product_tax').val(data.product_details[i].product_tax).attr('readonly', true).addClass("form-control");

    $('#f_item_detail-' + indexs + '-line_tax_amount').attr('readonly', true).addClass("form-control");

    $('#f_item_detail-' + indexs + '-product_line_total_amount').val(data.product_details[i].product_line_total_amount).attr('readonly', true).addClass("form-control");
  });

});

Update 2 :

You can pass this as a parameter to your line_total() then use that to get closest tr index and then do calculation according to that .

Updated Jquery code :

$(document).on('change', 'select', function() {
  var indexs = $(this).closest("tr").index();
  var selector = $(this); //save selector
  var i = 0;
   $.ajax({
    type: "GET",
    url: "<cms:show k_site_link />generate/quotation-ajax.php",
    data: "select_id=" + $(this).val(),
    async: false
  }).done(function(data) {
    console.log("de");
    $('#f_item_detail-' + indexs + '-product_hsn').val(data.product_details[i].product_hsn).attr('readonly', true).addClass("form-control");

    $('#f_item_detail-' + indexs + '-product_qty').attr('onchange', 'line_total(this)'); //pass this here ...

    $('#f_item_detail-' + indexs + '-product_price').val(data.product_details[i].product_price).attr('onchange', 'line_total(this)'); //pass this here

    $('input#f_item_detail-' + indexs + '-product_tax').val(data.product_details[i].product_tax).attr('readonly', true).addClass("form-control");

    $('#f_item_detail-' + indexs + '-line_tax_amount').attr('readonly', true).addClass("form-control");

    $('#f_item_detail-' + indexs + '-product_line_total_amount').val(data.product_details[i].product_line_total_amount).attr('readonly', true).addClass("form-control");
    line_total(selector); //call this
  });

});



function line_total(selector) {
  //do same here
  var indexs = $(selector).closest("tr").index()
  var line_qty = $('input#f_item_detail-' + indexs + '-product_qty').val() != "" ? $('input#f_item_detail-' + indexs + '-product_qty').val() : 1;
  var line_tax = $('input#f_item_detail-' + indexs + '-product_tax').val();
  var line_cost = $('input#f_item_detail-' + indexs + '-product_price').val();
  var line_tax_amount = parseFloat(((line_cost * line_tax) / 100) * line_qty).toFixed(2);
  var result = parseFloat((+line_qty * +line_cost) + +line_tax_amount).toFixed(2);
  $('#f_item_detail-' + indexs + '-line_tax_amount').val(line_tax_amount).attr('hidden', true);
  $('#f_item_detail-' + indexs + '-product_line_total_amount').val(result);
  grand_total(); //call this
}

function grand_total() {
  var grand = 0;
  $(".k_element_product_line_total_amount input").each(function() {
    grand += $(this).val() != "" ? parseFloat($(this).val()) : 0
  })
  $("#grand_total").text(grand + 100); //100 is gst change it...according to your need and change id where you need to display grand total
}
GXCPL.Official
  • 267
  • 3
  • 19
Swati
  • 28,069
  • 4
  • 21
  • 41
  • I tried your code and it will need a little tweaking. I will get the things on a development server and share the link and test creds with you. and the code too. Please give me some time. Tumhara point valid hai so me koshish karta hu online dalke share karne ka at the earliest. – GXCPL.Official May 11 '21 at 14:51
  • Okay no problem . I also have one more way to do this let me know .. if this doesn't work.. :) – Swati May 11 '21 at 15:08
  • Hi @Swati. Please use this link: https://drive.google.com/drive/folders/1SUA_roZN3ERvCo_MpHmXi4Pp-yTsCyhQ?usp=sharing | It has the code that we discussed about yesterday with the implementation readme. – GXCPL.Official May 12 '21 at 09:28
  • I tested out already its working fine on my end . I have done minimal changes in jquery code which i will update in my answer here . – Swati May 12 '21 at 13:24
  • Could you please have a look at the Edit #1. I have integrated your suggestions into my code and it works fine just that there are some concerns. – GXCPL.Official May 12 '21 at 16:28
  • 1
    I have made one more update(already tested) see if that works . – Swati May 13 '21 at 04:54
  • 1
    Yup all is working great. I also added a total for the gst amount in a separate function, adding quantity to be displayed as "1" when product is selected rather than keeping it blank, and other few changes, thus completing the code in consideration. I am really thankful to you for all the help! You knowledge is commendable on this subject. Thanks once again! – GXCPL.Official May 14 '21 at 07:25
  • Glad i was able to help :) – Swati May 14 '21 at 11:46
0

So I have been trying to get things on track. Finally, I have been able to do it: The jQuery AJAX code that I was looking for to solve my problem is as below:

var counter = 0;
$(document).ready(function() {
    $(".addRow").click(function(){
        counter++;
    });
});
            
$(document).on('change','select',function() {
    var data = "";
    var i = 0;
    $.ajax({
        type:"GET",
        url : "<cms:show k_site_link />generate/quotation-ajax.php",
        data: 
            "select_id="+$(this).val(),
        async: false
    }).done(function(data) {
        $('#f_item_detail-'+ counter +'-product_hsn').val(data.product_details[i].product_hsn);
        $('#f_item_detail-'+ counter +'-product_qty').val(data.product_details[i].product_qty);
        $('#f_item_detail-'+ counter +'-product_price').val(data.product_details[i].product_price);
        $('#f_item_detail-'+ counter +'-product_tax').val(data.product_details[i].product_tax);
        $('#f_item_detail-'+ counter +'-product_line_total_amount').val(data.product_details[i].product_line_total_amount);
    })
});

I am actually creating a global counter using:

var counter = 0;
$(document).ready(function() {
    $(".addRow").click(function(){
        var globalcounter = parseFloat(counter);
        counter++;
    });
});

And then passing the value of the global counter as "counter" to the ajax(). Everytime the "Add Row" is clicked, the counter value in passed then incremented. This helps me in giving the numbers to the id's of each rows input as:

$('#f_item_detail-'+ counter +'-product_hsn').val(data.product_details[i].product_hsn);

Here i in "data.product_details[i]" remains zero because the AJAX returns details for only one product per line-item.

GXCPL.Official
  • 267
  • 3
  • 19