Do not modify the unit price in the sales invoice

Please help set restrictions on changing the unit price in the sales invoice. I hope there is an option in adding the item that prevents changing the selling price in the sales invoice.

1 Like

I agree, and one more way, use magic spells for it.
:grinning:

@ahmed11
We have to use obsolete features for this. if you understand Extensions, you can try this

function disableSecondTextInputInRow(rowElement) {
    const inputGroupDivs = rowElement.querySelectorAll('td > div.input-group');

    if (inputGroupDivs.length >= 2) {
        const secondInputGroup = inputGroupDivs[1];
        const inputElement = secondInputGroup.querySelector('input[type="text"]');

        if (inputElement) {
            inputElement.disabled = true;
        }
    }
}

function disableSecondTextInputsInExistingRows() {
    const rows = document.querySelectorAll('div.form-group > table > tbody > tr');

    rows.forEach(row => {
        disableSecondTextInputInRow(row);
    });
}

function handleMutations(mutationsList, observer) {
    mutationsList.forEach(mutation => {
        if (mutation.type === 'childList') {
            mutation.addedNodes.forEach(node => {
                if (node.nodeType === 1 && node.tagName.toLowerCase() === 'tr') {
                    disableSecondTextInputInRow(node);
                }
            });
        }
    });
}

function initialize() {
    disableSecondTextInputsInExistingRows();

    const targetNode = document.querySelector('div.form-group > table > tbody');
    if (targetNode) {
        const observer = new MutationObserver(handleMutations);
        observer.observe(targetNode, { childList: true });
    }
}

initialize();

2 Likes

Its would be better if extensions were reintroduced (currently not working when country is selected) if its not possible to meet users demands like this. So atleast there is way to achieve what they want not an easy way but possibilty should be there.

The method worked for me, but when I change the country to Saudi Arabia or choose another country, it does not work. Please help with this and also specify restrictions on the selling price on the sales invoice and not on all unit prices on the purchase invoice or elsewhere.

Thank you Mabaega,
Here is the modified code. I have added the option to exempt the administrator user from the function, and you can add more user IDs if you want.

// Get the user link element using XPath
const userLink = document.evaluate('/html/body/div[1]/div/div[2]/a[1]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

// Define an array of allowed users who can modify the field
const allowedUsers = ["Administrator"];

// Get the current user name from the user link element
const userName = userLink.textContent.trim();
currnetUser = userName;

// Function to disable the second text input in a row
function disableSecondTextInputInRow(rowElement) {
  // Get all input group divs in the row
  const inputGroupDivs = rowElement.querySelectorAll('td > div.input-group');

  // Check if there are at least 2 input group divs
  if (inputGroupDivs.length >= 2) {
    // Get the second input group div
    const secondInputGroup = inputGroupDivs[1];
    // Get the text input element in the second input group div
    const inputElement = secondInputGroup.querySelector('input[type="text"]');

    // Check if the input element exists
    if (inputElement) {
      // Check if the current user is in the allowed users array
      if (allowedUsers.includes(currnetUser)) {
        // Allow modification if the user is in the allowed users array
        inputElement.disabled = false;
      } else {
        // Disable modification if the user is not in the allowed users array
        inputElement.disabled = true;
      }
    }
  }
}

// Function to disable the second text input in all existing rows
function disableSecondTextInputsInExistingRows() {
  // Get all rows in the table
  const rows = document.querySelectorAll('div.form-group > table > tbody > tr');

  // Loop through each row and disable the second text input
  rows.forEach(row => {
    disableSecondTextInputInRow(row);
  });
}

// Function to handle mutations (e.g. when new rows are added to the table)
function handleMutations(mutationsList, observer) {
  // Loop through each mutation
  mutationsList.forEach(mutation => {
    // Check if the mutation is a child list mutation (e.g. a new row was added)
    if (mutation.type === 'childList') {
      // Loop through each added node (e.g. each new row)
      mutation.addedNodes.forEach(node => {
        // Check if the node is a table row element
        if (node.nodeType === 1 && node.tagName.toLowerCase() === 'tr') {
          // Disable the second text input in the new row
          disableSecondTextInputInRow(node);
        }
      });
    }
  });
}

// Function to initialize the script
function initialize() {
  // Disable the second text input in all existing rows
  disableSecondTextInputsInExistingRows();

  // Get the table body element
  const targetNode = document.querySelector('div.form-group > table > tbody');

  // Check if the table body element exists
  if (targetNode) {
    // Create a new MutationObserver instance
    const observer = new MutationObserver(handleMutations);

    // Observe the table body element for child list mutations
    observer.observe(targetNode, { childList: true });
  }
}

// Initialize the script
initialize();
1 Like

I will attempt an alternative method because the Manager does not execute the extensions when changing the country from automatic to another as I know.

@Noiemany
A nice addition, we can add this script to the CustomField Description.

1 Like

@Mabaega I am pleased to inform you that the solution is functioning flawlessly. Your proficiency is truly commendable.

Does this signify that we can employ this methodology to execute any script?

I’ve noticed that you’ve applied this approach in two earlier scripts (“Date Created” and “Created By”), albeit it appears to be ineffective with the “Previous Balance on Invoice” script.

Looking forward to your guidance.
add this script to the CustomField Description and the placement is sales invoice

<script>
document.addEventListener('DOMContentLoaded', () => {
  // Get the user link element using XPath
  const userLink = document.evaluate('/html/body/div[1]/div/div[2]/a[1]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

  // Define an array of allowed users who can modify the field
  const allowedUsers = ["Administrator"];

  // Get the current user name from the user link element
  const userName = userLink.textContent.trim();
  const currentUser = userName;

  // Function to disable the second text input in a row
  function disableSecondTextInputInRow(rowElement) {
    // Get all input group divs in the row
    const inputGroupDivs = rowElement.querySelectorAll('td > div.input-group');

    // Check if there are at least 2 input group divs
    if (inputGroupDivs.length >= 2) {
      // Get the second input group div
      const secondInputGroup = inputGroupDivs[1];
      // Get the text input element in the second input group div
      const inputElement = secondInputGroup.querySelector('input[type="text"]');

      // Check if the input element exists
      if (inputElement) {
        // Check if the current user is in the allowed users array
        if (allowedUsers.includes(currentUser)) {
          // Allow modification if the user is in the allowed users array
          inputElement.disabled = false;
        } else {
          // Disable modification if the user is not in the allowed users array
          inputElement.disabled = true;
        }
      }
    }
  }

  // Function to disable the second text input in all existing rows
  function disableSecondTextInputsInExistingRows() {
    // Get all rows in the table
    const rows = document.querySelectorAll('div.form-group > table > tbody > tr');

    // Loop through each row and disable the second text input
    rows.forEach(row => {
      disableSecondTextInputInRow(row);
    });
  }

  // Function to handle mutations (e.g. when new rows are added to the table)
  function handleMutations(mutationsList, observer) {
    mutationsList.forEach(mutation => {
      if (mutation.type === 'childList') {
        mutation.addedNodes.forEach(node => {
          if (node.nodeType === 1 && node.tagName.toLowerCase() === 'tr') {
            disableSecondTextInputInRow(node);
          }
        });
        // Add code to handle removed nodes if necessary
      }
    });
  }

  const labelText = '0';
  const vModelForm = document.getElementById('v-model-form');

  if (vModelForm) {
    const labels = vModelForm.querySelectorAll('label');

    labels.forEach(label => {
      if (label.textContent.trim() === labelText) {
        const formGroup = label.closest('.form-group');

        if (formGroup) {
          formGroup.style.display = 'none';
        }
      }
    });
  } else {
    console.error('Error: Element with id "v-model-form" not found.');
  }

  // Function to initialize the script
  function initialize() {
    // Disable the second text input in all existing rows
    disableSecondTextInputsInExistingRows();

    // Get the table body element
    const targetNode = document.querySelector('div.form-group > table > tbody');

    if (targetNode) {
      // Create a new MutationObserver instance
      const observer = new MutationObserver(handleMutations);

      // Observe the table body element for child list mutations
      observer.observe(targetNode, { childList: true });
    } else {
      console.error('Error: Table body element not found.');
    }
  }

  // Initialize the script
  initialize();
});
</script>

the code will hide the CustomField

1 Like

@Noiemany Same result as yours

<script>
	document.addEventListener('DOMContentLoaded', () => {
		const labelText = 'Disable Prices InputText';
		const allowedUsers = ['Administrator', 'Mabaega'];
		
		const vModelForm = document.getElementById('v-model-form');
		if (vModelForm) {
			const labels = vModelForm.querySelectorAll('label');
			labels.forEach(label => {
				if (label.textContent.trim() === labelText) {
					const formGroup = label.closest('.form-group');
					if (formGroup) {
						formGroup.style.display = 'none';
						formGroup.style.readonly = true;
					}
				}
			});
		}

		const userLink = document.querySelector('a[href="/change-password-form"]');
		let currentUser = '';
		if (userLink) {
			const userName = userLink.textContent.trim();
			const startIndex = userName.indexOf(' ') + 1;
			currentUser = userName.substring(startIndex);
		}

		function disableSecondTextInputInRow(rowElement) {
			const inputGroupDivs = rowElement.querySelectorAll('td > div.input-group');

			if (inputGroupDivs.length >= 2) {
				const secondInputGroup = inputGroupDivs[1];
				const inputElement = secondInputGroup.querySelector('input[type="text"]');

				if (inputElement) {
					// Check if the current user is in the allowed users list
					const isUserAllowed = allowedUsers.includes(currentUser);
					inputElement.disabled = !isUserAllowed; // Disable if user is not allowed
				}
			}
		}

		function disableSecondTextInputsInExistingRows() {
			const rows = document.querySelectorAll('div.form-group > table > tbody > tr');

			rows.forEach(row => {
				disableSecondTextInputInRow(row);
			});
		}

		function handleMutations(mutationsList, observer) {
			mutationsList.forEach(mutation => {
				if (mutation.type === 'childList') {
					mutation.addedNodes.forEach(node => {
						if (node.nodeType === 1 && node.tagName.toLowerCase() === 'tr') {
							disableSecondTextInputInRow(node);
						}
					});
				}
			});
		}

		disableSecondTextInputsInExistingRows();

		const targetNode = document.querySelector('div.form-group > table > tbody');
		if (targetNode) {
			const observer = new MutationObserver(handleMutations);
			observer.observe(targetNode, { childList: true });
		}
	});
</script>

@ahmed11 we got solutions.

2 Likes

It really worked. Thank you for your wonderful efforts, but it is of no use if the country is changed to Saudi Arabia. I hope there is a solution. I can’t keep making the country automatic without selecting. You must select a country for electronic billing controls.

This code works fine, even if you use localization, you just need to adjust this part

const labelText = 'Disable Prices InputText';
const allowedUsers = ['Administrator', 'Mabaega'];

if you are in Desktop Edition, change
const allowedUsers = ['Administrator', 'Mabaega'];
to
const allowedUsers = [''];

1 Like

The problem is not in the translation
But in selecting the country in the manager
When the country is set automatically, the method works, and the sales invoice cannot modify the price, while the purchase invoice can, and this is what I want
But when I change the country to my country, Saudi Arabia, for electronic billing purposes, I must change the country to Saudi Arabia.
But the method fails and it becomes possible to modify the price in the sales invoice
I hope you understand me and I appreciate your efforts. I hope there is a solution and that you will help me close the amendment to the selling price if I choose Saudi Arabia.



I want to change the country to Saudi Arabia
This remains the same when the country is selected automatically and the selling price cannot be modified in the sales invoice
Because the country must be chosen for other purposes

Where you put the script code?
We use CustomField, not Extension.

@Mabaega your code does not work with sales quote, sales order form etc. It disables discount input field instead in these forms.
It seems ‘Unit price’ Input is not under form-group in these above mentioned forms.

Its working in sales invoice form without any issue.

@lubos why is this html structure difference between sales invoice form & other sales form w.rt ‘Unit price’ input?

Why would you expect that? The topic is about Sales Invoice only and @Mabeaga provided the code to put in a Custom Field to be assigned to Sales Invoices only for those that seem to want this feature.

If you want to have such feature in other than the Sales Invoice you can try to develop the code yourself and/or create a new topic related to these other edit screens.

I have already developed my own code that works with other edit screens. But still i thought its worth mentioning because standard practice now as recommended by lubos is creating sales order first & then sales invoice from it.
So, if anyone is following that process for creating invoice, disabling unit price only on invoice is worthless as user can edit unit price in sales order, & same price will be copied to invoice as well.

As you quote @Lubos and also following general accounting principles, the Sales Invoice is the basis for the customer payment and not the quote nor the order. As you MUST issue an Invoice after delivery of goods (that is why you used the Sales Order) it is paramount as in this topic requested to at least prevent this in the Sales Invoice for those that want to.

I advised to create another topic because Sales Quote and sales Order are not part of this topic.