$(document).ready(function() { // Function to configure and start observing a table function observeTable(tableId, debugMode = false) { var targetNode = document.querySelector(tableId + ' tbody'); if (!targetNode) { if (debugMode) { console.error('Table with ID ' + tableId + ' not found.'); } return; } $(tableId + ' tbody tr').each(function() { cloneAndModifyElement($(this), debugMode); }); var observer = new MutationObserver(function(mutationsList, observer) { mutationsList.forEach(function(mutation) { if (mutation.type === 'childList' && mutation.target.nodeName.toLowerCase() === 'tbody') { mutation.addedNodes.forEach(function(node) { if ($(node).is('tr') && !$(node).hasClass('is-cloned') && !$(node).hasClass('cloned-element')) { if (debugMode) { console.log('New element detected in ' + tableId + '.'); console.log('Inserted node:', node); } var originalRow = $(node); disconnectObserver(); cloneAndModifyElement($(node), debugMode); connectObserver(); if (debugMode) { console.log('Original row:', originalRow); } } }); } }); }); function disconnectObserver() { observer.disconnect(); } function connectObserver() { observer.observe(targetNode, { childList: true }); } connectObserver(); } //clone function and handle change id, name, class, and select2 intite issues function cloneAndModifyElement(originalElement, debugMode = false) { // Check if the element or any of its similar siblings have already been cloned if (originalElement.hasClass('is-cloned')) { if (debugMode) { console.log('Element already cloned.'); } return; // Exit the function if already cloned } var clonedElement = originalElement.clone(); // Mark the original and cloned elements originalElement.addClass('is-cloned'); clonedElement.addClass('cloned-element'); // Update all IDs, names, and classes within the cloned row clonedElement.find('[id], [name], [for], [class]').each(function() { var elem = $(this); // Prepend 'm_' to IDs, unless the ID starts with 'select2' var elemId = elem.attr('id'); if (elemId) { // Check if elemId is not undefined or null if (!elemId.startsWith('select2')) { elem.attr('id', 'm_' + elemId); } } // Prepend 'm_' to names if (elem.attr('name')) { elem.attr('name', 'm_' + elem.attr('name')); } // Prepend 'm_' to the 'for' attribute in labels if any if (elem.attr('for')) { elem.attr('for', 'm_' + elem.attr('for')); } // Modify specific classes starting with 'row_' if (elem.attr('class')) { var classes = elem.attr('class').split(/\s+/); var modifiedClasses = classes.map(cls => { // Check if the class starts with 'row_' if (cls.startsWith("row_")) { return 'm_' + cls; // Prepend 'm_' to classes starting with 'row_' } // Check if the class matches common Bootstrap or framework patterns if (/^(fa|fas|far|fab|btn|col|container|d-|justify|align|text|border|bg-|table|nav|modal|dropdown|input|form|alert|pagination|badge|form-control|input-sm|input-lg|visible|hidden|active|disabled|carousel|glyph|accordion|panel|list-group|hide|select2|row|pull-right|paid_on).*/.test(cls)) { return cls; // Return the class unchanged } // For all other classes, prepend 'm_' as well return 'm_' + cls; }).join(' '); elem.attr('class', modifiedClasses); } }); // Insert the modified clone after the original and optionally hide the original row originalElement.after(clonedElement); if (debugMode) { console.log('Cloned row:', clonedElement); console.log('Cloned row appended after the original row.'); } // Capture styles from original Select2 and reinitialize Select2 with those styles on the clone originalElement.find('.select2-hidden-accessible').each(function() { var originalSelect = $(this); var originalSelect2Container = originalSelect.data('select2').$container; var originalWidth = originalSelect2Container.width(); var originalStyles = { width: originalWidth, height: originalSelect2Container.css('height'), lineHeight: originalSelect2Container.css('line-height'), borderColor: originalSelect2Container.find('.select2-selection').css('border-color') }; clonedElement.find('.select2-hidden-accessible').each(function() { var $select = $(this); // Safely destroy Select2 only if it's initialized if ($select.data('select2')) { $select.select2('destroy'); } else { if (debugMode) { console.log('Select2 not initialized on this element, skipping destroy.'); } } }).end().find('.select2-container').remove(); // Reinitialize Select2 on cloned element var newSelect = clonedElement.find('#' + 'm_' + originalSelect.attr('id')); newSelect.select2(); // Initialize Select2 // Apply captured styles to the new Select2 container var newSelect2Container = newSelect.data('select2').$container; newSelect2Container.css({ 'width': originalStyles.width + 'px', // Ensure width is the same 'height': originalStyles.height, 'line-height': originalStyles.lineHeight }); newSelect2Container.find('.select2-selection').css({ 'border-color': originalStyles.borderColor }); }); // Reinitialize Select2 for select elements that had the plugin clonedElement.find('.select2-hidden-accessible').on('change', function() { if (debugMode) { console.log('Select2 value changed on cloned element!'); } // Additional logic for handling changes can be implemented here }); handleRow(clonedElement) originalElement.hide(); // Uncomment if you want to hide the original row } //handle multi currency change rate function handleMultiCurrencyChange(){ $('#m_currency_id').on('change', function(){ var currencyId = $(this).val(); var rate = 1; $.ajax({ url: '/exchange-rate/' + currencyId, method: 'POST', data: {id: currencyId}, success: function(response){ if (response.success) { if(response.data.type === 'api'){ // Call fetchApiCurrencyRate with a callback function to handle the response fetchApiCurrencyRate(currencyId).done(function(apiResponse) { if (apiResponse.success) { rate = apiResponse.rate; console.log("Return API rate:", rate); updateCurrencyFields(response.data, rate); updateAllRows(); } }); }else{ rate = response.data.exchange_rate; updateCurrencyFields(response.data, rate); updateAllRows(); } } else { // Handle the scenario when success is false console.error("Failed to fetch data: " + response.message); } }, error: function(xhr, status, error){ // Handle errors if any console.error("Error: " + xhr.responseText); } }); }); } // Function to fetch currency rate from API function fetchApiCurrencyRate(currencyId) { // Create a deferred object var deferred = $.Deferred(); $.ajax({ url: '/api-currency-rate/' + currencyId, method: 'GET', success: function(apiResponse) { if (apiResponse.success) { var rate = apiResponse.rates[apiResponse.code]; if (rate !== undefined) { // Resolve the deferred object with success and the rate value deferred.resolve({ success: true, rate: rate }); } else { console.error("API currency rate fetch failed: Rate for the code is undefined"); // Reject the deferred object with an error message deferred.reject("Rate for the code is undefined"); } } else { console.error("API currency rate fetch failed."); // Reject the deferred object with an error message deferred.reject("API currency rate fetch failed"); } }, error: function(xhr, status, error) { console.error("Error fetching API currency rate: " + xhr.responseText); // Reject the deferred object with an error message deferred.reject("Error fetching API currency rate"); } }); // Return the promise associated with the deferred object return deferred.promise(); } function updateCurrencyFields(response, rate) { $('#m_exchange_rate').val(rate); $('#m_code').val(response.code); $('#m_symbol').val(response.symbol); $('#m_thousand').val(response.thousand_separator); $('#m_decimal').val(response.decimal_separator); } //update all table calculation function updateAllRows(){ $('.m_purchase_quantity').each(function() { row = $(this).closest('tr'); handleRow(row); }); } // Function to handle calculation for a row function handleRow(row) { multiCurrencyUpdateRowPrice(row); multiCurrencyInlineProfitPer(row); multiCurrencyUpdateTabelTotal(); multiCurrencyUpdateGrandTotal(); } //update row price based on multi currency exchange rate function multiCurrencyUpdateRowPrice(row) { var m_exchange_rate = $('input#m_exchange_rate').val(); if (!m_exchange_rate || m_exchange_rate == 1) { return true; } var m_purchase_unit_cost_without_discount = __m_read_number(row.find('.m_purchase_unit_cost_without_discount'), true) / m_exchange_rate; __m_write_number( row.find('.m_purchase_unit_cost_without_discount'), m_purchase_unit_cost_without_discount, true ); var m_purchase_unit_cost = __m_read_number(row.find('.m_purchase_unit_cost'), true) / m_exchange_rate; __m_write_number(row.find('.m_purchase_unit_cost'), m_purchase_unit_cost, true); var m_row_subtotal_before_tax_hidden = __m_read_number(row.find('.m_row_subtotal_before_tax_hidden'), true) / m_exchange_rate; row.find('.m_row_subtotal_before_tax').text( multiCurrencyTransFromEn(m_row_subtotal_before_tax_hidden, false, true) ); __m_write_number( row.find('input.m_row_subtotal_before_tax_hidden'), m_row_subtotal_before_tax_hidden, true ); var m_purchase_product_unit_tax = __m_read_number(row.find('.m_purchase_product_unit_tax'), true) / m_exchange_rate; __m_write_number(row.find('input.m_purchase_product_unit_tax'), m_purchase_product_unit_tax, true); row.find('.m_purchase_product_unit_tax_text').text( multiCurrencyTransFromEn(m_purchase_product_unit_tax, false, true) ); var m_purchase_unit_cost_after_tax = __m_read_number(row.find('.m_purchase_unit_cost_after_tax'), true) / m_exchange_rate; __m_write_number( row.find('input.m_purchase_unit_cost_after_tax'), m_purchase_unit_cost_after_tax, true ); var m_row_subtotal_after_tax_hidden = __m_read_number(row.find('.m_row_subtotal_after_tax_hidden'), true) / m_exchange_rate; __m_write_number( row.find('input.m_row_subtotal_after_tax_hidden'), m_row_subtotal_after_tax_hidden, true ); row.find('.m_row_subtotal_after_tax').text( multiCurrencyTransFromEn(m_row_subtotal_after_tax_hidden, false, true) ); } function multiCurrencyTransFromEn( input, show_symbol = true, use_page_currency = false, precision = __currency_precision, is_quantity = false ) { var m_s = $('#m_symbol').val(); if ($('#m_symbol').val()) { console.log("M symbol presented"); var s = m_s; var thousand = $('#m_thousand').val(); var decimal = $('#m_decimal').val(); } else { console.log("Default symbol presented"); var s = __currency_symbol; var thousand = __currency_thousand_separator; var decimal = __currency_decimal_separator; } symbol = ''; var format = '%s%v'; if (show_symbol) { symbol = s; format = '%s %v'; if (__currency_symbol_placement == 'after') { format = '%v %s'; } } if (is_quantity) { precision = __quantity_precision; } return accounting.formatMoney(input, symbol, precision, thousand, decimal, format); } //Read input and convert it into natural number function __m_read_number(input_element, use_page_currency = false) { return __m_number_uf(input_element.val(), use_page_currency); } function __m_number_uf(input, use_page_currency = false) { if (use_page_currency && __currency_decimal_separator) { var decimal = $('#m_decimal').val(); } else { var decimal = __currency_decimal_separator; } return accounting.unformat(input, decimal); } function __m_write_number( input_element, value, use_page_currency = false, precision = __currency_precision ) { if(input_element.hasClass('input_quantity')) { precision = __quantity_precision; } input_element.val(__m_number_f(value, false, use_page_currency, precision)); } function __m_number_f( input, show_symbol = false, use_page_currency = false, precision = __currency_precision ) { return multiCurrencyTransFromEn(input, show_symbol, use_page_currency, precision); } function multiCurrencyInlineProfitPer(row) { //Update Profit percentage var m_default_sell_price = __m_read_number(row.find('input.m_default_sell_price'), true); var m_exchange_rate = $('input#m_exchange_rate').val(); m_default_sell_price_in_base_currency = m_default_sell_price / parseFloat(m_exchange_rate); var m_purchase_after_tax = __m_read_number(row.find('input.m_purchase_unit_cost_after_tax'), true); var m_profit_percent = __get_rate(m_purchase_after_tax, m_default_sell_price_in_base_currency); __m_write_number(row.find('input.m_profit_percent'), m_profit_percent, true); } function multiCurrencyUpdateTabelTotal() { var m_total_quantity = 0; var m_total_st_before_tax = 0; var m_total_subtotal = 0; $('#purchase_entry_table tbody').find('tr').each(function() { if ($(this).find('.m_purchase_quantity').length > 0) { m_total_quantity += __m_read_number($(this).find('.m_purchase_quantity'), true); m_total_st_before_tax += __m_read_number( $(this).find('.m_row_subtotal_before_tax_hidden'), true ); m_total_subtotal += __m_read_number($(this).find('.m_row_subtotal_after_tax_hidden'), true); } }); $('#m_total_quantity').text(__m_number_f(m_total_quantity, false)); $('#m_total_st_before_tax').text(multiCurrencyTransFromEn(m_total_st_before_tax, true, true)); __m_write_number($('input#m_st_before_tax_input'), m_total_st_before_tax, true); $('#m_total_subtotal').text(multiCurrencyTransFromEn(m_total_subtotal, true, true)); __m_write_number($('input#m_total_subtotal_input'), m_total_subtotal, true); } function multiCurrencyUpdateGrandTotal() { var m_st_before_tax = __m_read_number($('input#m_st_before_tax_input'), true); var m_total_subtotal = __m_read_number($('input#m_total_subtotal_input'), true); //Calculate Discount var m_discount_type = $('select#m_discount_type').val(); m_discount_type && $('select#discount_type').val(m_discount_type).trigger('change'); var m_discount_amount = __m_read_number($('input#m_discount_amount'), true); m_discount_amount && $('input#discount_amount').val(m_discount_amount); var m_discount = __calculate_amount(m_discount_type, m_discount_amount, m_total_subtotal); $('#m_discount_calculated_amount').text(multiCurrencyTransFromEn(m_discount, true, true)); //Calculate Tax var m_tax_rate = parseFloat($('option:selected', $('#m_tax_id')).data('tax_amount')); var m_tax = __calculate_amount('percentage', m_tax_rate, m_total_subtotal - m_discount); __m_write_number($('input#m_tax_amount'), m_tax); $('#m_tax_calculated_amount').text(multiCurrencyTransFromEn(m_tax, true, true)); //Calculate shipping var m_shipping_charges = __m_read_number($('input#m_shipping_charges'), true); $('input#shipping_charges').val(m_shipping_charges); //calculate additional expenses var m_additional_expense_1 = __m_read_number($('input#m_additional_expense_value_1'), true); $('input#additional_expense_value_1').val(m_additional_expense_1); var m_additional_expense_2 = __m_read_number($('input#m_additional_expense_value_2'), true); $('input#additional_expense_value_2').val(m_additional_expense_2); var m_additional_expense_3 = __m_read_number($('input#m_additional_expense_value_3'), true); $('input#additional_expense_value_3').val(m_additional_expense_3); var m_additional_expense_4 = __m_read_number($('input#m_additional_expense_value_4'), true); $('input#additional_expense_value_4').val(m_additional_expense_4); //Calculate Final total m_grand_total = m_total_subtotal - m_discount + m_tax + m_shipping_charges + m_additional_expense_1 + m_additional_expense_2 + m_additional_expense_3 + m_additional_expense_4; __m_write_number($('input#m_grand_total_hidden'), m_grand_total, true); var m_payment = __m_read_number($('input.m_payment-amount'), true); var m_due = m_grand_total - m_payment; // __m_write_number($('input.payment-amount'), grand_total, true); $('#m_grand_total').text(multiCurrencyTransFromEn(m_grand_total, true, true)); $('#m_payment_due').text(multiCurrencyTransFromEn(m_due, true, true)); update_grand_total(); //__currency_convert_recursively($(document)); } function multiCurrencyQuantityChange(){ //On Change of quantity $(document).on('change', '.m_purchase_quantity', function() { var m_row = $(this).closest('tr'); var m_quantity = __m_read_number($(this), true); var m_purchase_before_tax = __m_read_number(m_row.find('input.m_purchase_unit_cost'), true); var m_purchase_after_tax = __m_read_number( m_row.find('input.m_purchase_unit_cost_after_tax'), true ); //Calculate sub totals var m_sub_total_before_tax = m_quantity * m_purchase_before_tax; var m_sub_total_after_tax = m_quantity * m_purchase_after_tax; m_row.find('.m_row_subtotal_before_tax').text( multiCurrencyTransFromEn(m_sub_total_before_tax, false, true) ); __m_write_number( m_row.find('input.m_row_subtotal_before_tax_hidden'), m_sub_total_before_tax, true ); m_row.find('.m_row_subtotal_after_tax').text( multiCurrencyTransFromEn(m_sub_total_after_tax, false, true) ); __m_write_number(m_row.find('input.m_row_subtotal_after_tax_hidden'), m_sub_total_after_tax, true); multiCurrencyUpdateTabelTotal(); multiCurrencyUpdateGrandTotal(); // Find the corresponding original row and trigger a change on its quantity input var originalRow = m_row.prev('tr'); // Assuming direct sequence originalRow.find('.purchase_quantity').val(m_quantity).trigger('change'); }); } //here is the start of the excute code // Call the function for each table with its respective configuration observeTable('#purchase_entry_table', true); // Set debug mode to true for the purchase table observeTable('#pos_table', true); // Set debug mode to true for the pos table //basic cloned sections var totalTableClone = $('#total_subtotal_input').closest('table'); cloneAndModifyElement(totalTableClone); var discountTableClone = $('#discount_amount').closest('table'); cloneAndModifyElement(discountTableClone); var shippingChargesTableClone = $('#shipping_charges').closest('.box-body'); cloneAndModifyElement(shippingChargesTableClone); var paymentDueTableClone = $('input.payment-amount').closest('div').parent(); cloneAndModifyElement(paymentDueTableClone); var paymentDueTableClone = $('#payment_due').closest('div').parent(); cloneAndModifyElement(paymentDueTableClone); //observe Multi currency change handleMultiCurrencyChange(); //oserve quantity change multiCurrencyQuantityChange(); $(document).on('change', '#m_tax_id, #m_discount_type, #m_discount_amount, input#m_shipping_charges, \ #m_additional_expense_value_1, #m_additional_expense_value_2, \ #m_additional_expense_value_3, #m_additional_expense_value_4', function() { multiCurrencyUpdateGrandTotal(); }); $(document).on('change', 'input.m_payment-amount', function() { var m_payment = __m_read_number($(this), true); var m_grand_total = __m_read_number($('input#m_grand_total_hidden'), true); var m_bal = m_grand_total - m_payment; $('#m_payment_due').text(multiCurrencyTransFromEn(m_bal, true, true)); $('input.payment-amount').val(m_payment).trigger('change') }); });