Error with Sales Invoice inserted via frappe.new_doc

Hi,
I was using v14 and migrating to v15 didn’t affect this (both in development mode).
When clicking in the custom button I have for create sales invoice, I get an error related to the sales_invocie.insert() :
po_or_so = self.get(“items”)[0].get(“sales_order”) | IndexError: list index out of range

This is what I have in Selling Settings:

This is my code (that prints the information correctly on console)

import frappe
from frappe.model.document import Document
import erpnext



class PayersFeeCategoryPE(Document):
	@frappe.whitelist()
	def get_inv_data_pe(self):
		print("Method called")
		today = frappe.utils.today()
		company = frappe.defaults.get_defaults().company
		currency = erpnext.get_company_currency(company)
		receivable_account = frappe.db.get_single_value('Education Settings', 'receivable_account')
		income_account = frappe.db.get_single_value('Education Settings', 'income_account')
		company = frappe.db.get_single_value('Education Settings', 'company')
		cost_center = frappe.db.get_single_value('Education Settings', 'cost_center') or None
		inv_data = []
		inv_data = frappe.db.sql("""select pfc.pf_student as student, pep.fee_category, pep.payer as Customer, pfc.pf_custgroup, pep.pay_percent, pep.payterm_payer, pep.pep_event, fc.feecategory_type, fc.is_credit, fc.item, cg.default_price_list, ip.price_list_rate 
		from `tabpgm_enroll_payers` pep, `tabPayers Fee Category PE` pfc, `tabFee Category` fc, `tabCustomer Group` cg, `tabItem Price` ip 
		where pep.parent = pfc.name and
		pep.fee_category = fc.category_name and
		pep.fee_category = fc.name and
		cg.default_price_list = ip.price_list and
		ip.item_code = fc.item and
		pfc.pf_custgroup = cg.customer_group_name and
		pep.pep_event = 'Program Enrollment' and
		pfc.pf_student = %s""", self.pf_student, as_list=1)
		rows = frappe.db.sql("""select count(pep.fee_category)
		from `tabpgm_enroll_payers` pep, `tabPayers Fee Category PE` pfc, `tabFee Category` fc, `tabCustomer Group` cg 
		where pep.parent = pfc.name and
		pep.fee_category = fc.category_name and
		pep.fee_category = fc.name and
		pfc.pf_custgroup = cg.customer_group_name and
		pep.pep_event = 'Program Enrollment' and
		pfc.pf_student = %s""", self.pf_student) [0] [0]
		
		
		i =0
		while i < rows:
			print("Creating Invoice - " + str(i) + " of " + str(rows) + " rows")
			print(company, currency, receivable_account, income_account, cost_center, inv_data[i][11])
						
			sales_invoice = frappe.new_doc("Sales Invoice")
			sales_invoice.naming_series = "ACC-SINV-.YYYY.-"
			sales_invoice.posting_date = today
			
			sales_invoice.company = company
			sales_invoice.currency = currency
			sales_invoice.debit_to = receivable_account
			sales_invoice.income_account = income_account
			sales_invoice.conversion_rate = 1
			sales_invoice.customer = inv_data[i][2]
			sales_invoice.selling_price_list = inv_data[i][10]
			sales_invoice.grand_total = inv_data[i][11]
			sales_invoice.base_grand_total = inv_data[i][11]
			sales_invoice.payment_terms_template = inv_data[i][5]
			
			sales_invoice.insert()
			sii = frappe.new_doc("Sales Invoice Item")
			sii.parent = sales_invoice.name
			sii.parentfield = "items"
			sii.parenttype = "Sales Invoice"
			sii.item_name = inv_data[i][9]
			sii.qty = inv_data[i][4]/100
			sii.rate = 0
			sii.amount = 0
			sii.cost_center = cost_center
			sii.income_account = income_account
			sii.conversion_rate = 1
			sii.currency = currency
			sii.price_list_rate = 0
			sii.description = "Fee for " + inv_data[i][1]
			sii.item_group = inv_data[i][7]
			sii.price_list_rate = inv_data[i][11]
			sii.insert()
			sales_invoice.save()
			i += 1
			print("Invoice Created")

		

I cannot figure out how to avoid being checked for sales order. Your help is much appreciated!

Hi @Murilo_Melo:

Maybe is not related to sales config, just would be caused by sales invoice creation method …

Try this approach instead, it will be easier …

customer = "Customer 01"
due_date = frappe.utils.today()
posting_date = due_date
currency = "INR"
# ... more data

items_to_sale = frappe.get_list("Item", fields=["item_name", "description"]) # or any other query to get items data

items = []

for item_to_sale in items_to_sale:
    items.append({
        "doctype": "Sales Invoice Item",
        "item_code": item_to_sale.item_name,
        "description": item_to_sale.description,
        "qty": 1, 
        "rate": 100
    })

doc = frappe.get_doc(
    {
        "doctype": "Sales Invoice",
        "customer": cliente,
        "due_date": due_date,
        "posting_date": posting_date,
        "items": items
    })

doc.insert()
doc.submit()

Hope this helps.

1 Like

Thanks @avc!

The logic of my query is that on my custom app I can have more than one Sales Invoice per event, as The student may have a third-party (company, others) pay for part of the tuition and I want to create one Sales Invoice per Customer (the query also allows for variety of other payment situations). But the query itself is working.
I will try shifting things around as you suggested, particularly how you specified the items and their relation to the main Sales Invoice doc.
Since I am new to ERPNext, I was afraid I was missing some setting or initial declaration to skip that check, as it seemed a problem with ERPNext validating my sales invoice against a sales order (the error message traces to such validation on “apps/erpnext/erpnext/controllers/accounts_controller.py”, line 2182).
Once again, thanks! I will let you know how things progress so others may benefit as well.

1 Like

IMHO your error is not related to this setting on Sales Settings, probably about how you are inserting Sales Invoice Item data.

Welcome here, and let us know how it doing :slight_smile:

1 Like

Hey @avc,
Thanks for your help! You nailed it!
Just to document for others, after I followed your super helpful insight, I faced another error “frappe.exceptions.ValidationError: Row 1: Income Account None does not belong to Company”
I had to include income_account in the items childtable as well.

1 Like

Hi @Murilo_Melo:

Glad to be helpful.

As a trick, add doc.run_method("set_missing_values") before doc.insert
It will help with some required data.

1 Like