Automatically Create Invoice and Payment while creating order

Hi All,

I run a store on Shopify where payment is made right away while placing an order. In ERP Next this is broken down into three steps
1- Create Order
2- Create Invoice
3- Create Payment

Is there a way to automatically create the invoice and payment while making the order entry in ERPnext?

1 Like

It is not available out of the box but you maybe able to achieve it using some server scripts

Thank you Pawan.

@Pawan Can you maybe give some hints how this can be achieved? That would be very helpful
(or @farrukhmalik: If you already found a solution, would be great if you could share with the community)

1 Like

I haven’t tried it yet. Will post solution here when I am done. Right now, I am studying that how server scripts work :slight_smile:

2 Likes

Awesome. Please don’t forget to post your findings here when you found a solution so others can benefit as well (:
Cheers

1 Like

Check this. This might be helpful. :slight_smile: https://python.hotexamples.com/examples/erpnext.selling.doctype.sales_order.sales_order/-/make_sales_invoice/python-make_sales_invoice-function-examples.html

So I created a Server Script as suggested by @Pawan.

Script Type : DocType Event
Reference Document Type : Sales Order
DocType Event : Before Save (Submitted Document)

and wrote the following code as per examples give in the previous post.

from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice

current_date = doc.transaction_date;
so = doc;

if so.docstatus==1 and not so.per_billed:
		si = make_sales_invoice(so)
		si.doc.posting_date = current_date
		si.insert()
		si.submit()

Unfortunately, I get the following error when a Sales Order is updated.

ImportError: __import__ not found

It seems that I am very close to the solution. I would be obliged if anyone (especially @Pawan) can help to fix that probelm. :pray:

1 Like

Hi @farrukhmalik, I continued trying to solve this issue and realized where your error message probably comes from. According to Server Script you are not allowed to call any function from the erpnext library. There is only a list of selected functions that are available to be called from a server script. As you try to import something from erpnext, python throws this error message (see RestrictedPython · PyPI, under “Problematic code example”).

@Pawan : You mentioned that this is available using server scripts. How would one do it then, if the erpnext functions are not available. Sales Order for example uses from frappe.model.mapper import get_mapped_doc to create a sales invoice (inmake_sales_invoice()). From what I can see, this function can not be called from a server script.

You can try to write your own function using frappe.get_doc and then use the insert method or if you have access to the server code, you can try to whitelist the function.

@bluesky kindly do not tag people as this is a voluntary forum and we do not have any obligation to answer your queries

Thanks Pawan. I will try.
(Concerning the tagging: Not sure what the purpose of the tags are if not to address specific people? Feel free to not answer if tagged (as you said, it’s a voluntary forum). If tagging should not be used, this feature should be disabled in my opinion or a clearer guideline should be provided in FAQ - ERPNext Forum. Any clarification in this regard is appreciated. Maybe I just didn’t understand so far what the purpose of the tags are…)

1 Like

Did somebody succeed automatically creating Sales Invoice and Payment entry after Sales Order is submitted?

Thank you!

you can do it easly by get the order data and insert it

the same like first qoute

the same like first qoute

@farrukhmalik see th last answer

Dear @bluesky and @farrukhmalik

Were you able to find a solution pls?

Yes. We only came up with a “dirty” method, hard-coding the automatic creation. Our server script looks like this:

    sales_order = doc

    #Sales Invoice
    sales_invoice = frappe.get_doc({
        "doctype": "Sales Invoice",
        #"title": sales_order.title if sales_order.title else "{customer_name}",
        "customer": sales_order.customer,
        "customer_name": sales_order.customer.customer_name,
        #"amended_from": sales_order.amended_from,
        "company": sales_order.company,
        "po_no": sales_order.po_no,
        "po_date": sales_order.po_date,
        "tax_id": sales_order.customer.tax_id,
        "customer_address": sales_order.customer_address,
        "address_display": sales_order.address_display,
        "contact_person": sales_order.contact_person,
        "contact_display": sales_order.contact_display,
        "contact_mobile": sales_order.contact_mobile,
        "contact_email": sales_order.contact_email,
        "company_address": sales_order.company_address,
        "company_address_display": sales_order.company_address_display,
        "shipping_address_name": sales_order.shipping_address_name,
        "shipping_address": sales_order.shipping_address,
        "dispatch_address_name": sales_order.dispatch_address_name,
        "dispatch_address": sales_order.dispatch_address,
        "customer_group": sales_order.customer_group,
        "territory": sales_order.territory,
        "currency": sales_order.currency,
        "conversion_rate": sales_order.conversion_rate,
        "selling_price_list": sales_order.selling_price_list,
        "price_list_currency": sales_order.price_list_currency,
        "plc_conversion_rate": sales_order.plc_conversion_rate,
        "ignore_pricing_rule": sales_order.ignore_pricing_rule if sales_order.ignore_pricing_rule else "0",
        "set_warehouse": sales_order.set_warehouse,
        "scan_barcode": sales_order.scan_barcode,
        "posa_notes": sales_order.posa_notes,
        "total_qty": sales_order.total_qty,
        "base_total": sales_order.base_total,
        "base_net_total": sales_order.base_net_total,
        "total_net_weight": sales_order.total_net_weight,
        "total": sales_order.total,
        "net_total": sales_order.net_total,
        "tax_category": sales_order.tax_category,
        "shipping_rule": sales_order.shipping_rule,
        "taxes_and_charges": sales_order.taxes_and_charges,
        "other_charges_calculation": sales_order.other_charges_calculation,
        "base_total_taxes_and_charges": sales_order.base_total_taxes_and_charges,
        "total_taxes_and_charges": sales_order.total_taxes_and_charges,
        "loyalty_points": sales_order.loyalty_points,
        "loyalty_amount": sales_order.loyalty_amount,
        "apply_discount_on": sales_order.apply_discount_on if sales_order.apply_discount_on else "Grand Total",
        "base_discount_amount": sales_order.base_discount_amount,
        "additional_discount_percentage": sales_order.additional_discount_percentage,
        "discount_amount": sales_order.discount_amount,
        "base_grand_total": sales_order.base_grand_total,
        "base_rounding_adjustment": sales_order.base_rounding_adjustment,
        "base_rounded_total": sales_order.base_rounded_total,
        "base_in_words": sales_order.base_in_words,
        "grand_total": sales_order.grand_total,
        "rounding_adjustment": sales_order.rounding_adjustment,
        "rounded_total": sales_order.rounded_total,
        "in_words": sales_order.in_words,
        "disable_rounded_total": sales_order.disable_rounded_total if sales_order.disable_rounded_total else "0",
        "payment_terms_template": sales_order.payment_terms_template,
        "tc_name": sales_order.tc_name,
        "terms": sales_order.terms,
        "is_internal_customer": sales_order.customer.is_internal_customer,
        "represents_company": sales_order.customer.represents_company,
        "project": sales_order.project,
        "party_account_currency": sales_order.party_account_currency,
        "source": sales_order.source,
        "campaign": sales_order.campaign,
        "language": sales_order.language,
        "letter_head": sales_order.letter_head,
        "select_print_heading": sales_order.select_print_heading,
        "group_same_items": sales_order.group_same_items if sales_order.group_same_items else "0",
        "sales_partner": sales_order.sales_partner,
        "commission_rate": sales_order.commission_rate,
        "total_commission": sales_order.total_commission,
        "from_date": sales_order.from_date,
        "to_date": sales_order.to_date,
        "auto_repeat": sales_order.auto_repeat,
        "status": "Paid"
    })

    #Sales Invoice Item
    for item in sales_order.items:
        sales_invoice.append("items", {
            "item_code": item.item_code,
            "customer_item_code": item.customer_item_code,
            "posa_notes": item.posa_notes,
            "item_name": item.item_name,
            "description": item.description,
            "item_group": item.item_group,
            "brand": item.brand,
            "image": item.image,
            "image_view": item.image_view,
            "qty": item.qty,
            "stock_uom": item.stock_uom,
            "uom": item.uom,
            "conversion_factor": item.conversion_factor,
            "stock_qty": item.stock_qty,
            "price_list_rate": item.price_list_rate,
            "base_price_list_rate": item.base_price_list_rate,
            "margin_type": item.margin_type,
            "margin_rate_or_amount": item.margin_rate_or_amount,
            "rate_with_margin": item.rate_with_margin,
            "discount_percentage": item.discount_percentage,
            "discount_amount": item.discount_amount,
            "base_rate_with_margin": item.base_rate_with_margin,
            "rate": item.rate,
            "amount": item.amount,
            "item_tax_template": item.item_tax_template,
            "base_rate": item.base_rate,
            "base_amount": item.base_amount,
            "pricing_rules": item.pricing_rules,
            "stock_uom_rate": item.stock_uom_rate,
            "is_free_item": item.is_free_item if item.is_free_item else "0",
            "net_rate": item.net_rate,
            "net_amount": item.net_amount,
            "base_net_rate": item.base_net_rate,
            "base_net_amount": item.base_net_amount,
            "delivered_by_supplier": item.delivered_by_supplier if item.delivered_by_supplier else "0",
            "weight_per_unit": item.weight_per_unit,
            "total_weight": item.total_weight,
            "weight_uom": item.weight_uom,
            "warehouse": item.warehouse,
            "target_warehouse": item.target_warehouse,
            "actual_qty": item.actual_qty,
            "delivered_qty": item.delivered_qty,
            #"page_break": item.page_break if item.page_break else "0",
            "item_tax_rate": item.item_tax_rate,
            #Links
            "sales_order": sales_order.name,
            "so_detail": item.name
        })
    
    #Sales Invoice PRD
    for detail in sales_order.pricing_rules:
        sales_invoice.append("pricing_rules", {
            "pricing_rule": detail.pricing_rule,
            "item_code": detail.item_code,
            "margin_type": detail.margin_type,
            "rate_or_discount": detail.rate_or_discount,
            #"child_docname": detail.child_docname,
            "rule_applied": detail.rule_applied if detail.rule_applied else "1"
        })

    #Sales Invoice Packed Items
    for item in sales_order.packed_items:
        sales_invoice.append("packed_items", {
            "parent_item": item.parent_item,
            "item_code": item.item_code,
            "item_name": item.item_name,
            "description": item.description,
            "warehouse": item.warehouse,
            "target_warehouse": item.target_warehouse,
            "conversion_factor": item.conversion_factor,
            "qty": item.qty,
            "uom": item.uom,
            "serial_no": item.serial_no,
            "batch_no": item.batch_no,
            "actual_batch_qty": item.actual_batch_qty,
            "actual_qty": item.actual_qty,
            "projected_qty": item.projected_qty,
            "incoming_rate": item.incoming_rate,
            #"page_break": item.page_break if item.page_break else "0",
            "prevdoc_doctype": item.prevdoc_doctype,
            "parent_detail_docname": item.parent_detail_docname
        })

    #Sales Invoice Taxes and Charges
    for detail in sales_order.taxes:
        sales_invoice.append("taxes", {
            "category": detail.category if detail.category else "Total",
            "add_deduct_tax": detail.add_deduct_tax if detail.add_deduct_tax else "Add",
            "charge_type": detail.charge_type if detail.charge_type else "On Net Total",
            "row_id": detail.row_id,
            "included_in_print_rate": detail.included_in_print_rate if detail.included_in_print_rate else "0",
            "included_in_paid_amount": detail.included_in_paid_amount if detail.included_in_paid_amount else "0",
            "account_head": detail.account_head,
            "description": detail.description,
            "rate": detail.rate,
            "cost_center": detail.cost_center if detail.cost_center else ":Company",
            "currency": detail.account_head.account_currency,
            "tax_amount": detail.tax_amount,
            "tax_amount_after_discount_amount": detail.tax_amount_after_discount_amount,
            "total": detail.total,
            "base_tax_amount": detail.base_tax_amount,
            "base_total": detail.base_total,
            "base_tax_amount_after_discount_amount": detail.base_tax_amount_after_discount_amount,
            "item_wise_tax_detail": detail.item_wise_tax_detail
        })

    #Sales Invoice Payment Schedule
    for detail in sales_order.payment_schedule:
        sales_invoice.append("payment_schedule", {
            "payment_term": detail.payment_term,
            "description": detail.payment_term.description,
            "due_date": detail.due_date,
            "mode_of_payment": detail.mode_of_payment,
            "invoice_portion": detail.invoice_portion,
            "discount_type": detail.payment_term.discount_type,
            "discount_date": detail.discount_date,
            "discount": detail.payment_term.discount,
            "payment_amount": detail.payment_amount,
            "outstanding": detail.payment_amount,
            "paid_amount": detail.paid_amount,
            "discounted_amount": detail.discounted_amount if detail.discounted_amount else "0",
            "base_payment_amount": detail.base_payment_amount
        })
    
    #Sales Invoice Sales Team
    for detail in sales_order.sales_team:
        sales_invoice.append("sales_team", {
            "sales_person": detail.sales_person,
            "contact_no": detail.contact_no,
            "allocated_percentage": detail.allocated_percentage,
            "allocated_amount": detail.allocated_amount,
            "commission_rate": detail.commission_rate,
            "incentives": detail.incentives
        })
    
    sales_invoice.insert()
    
    frappe.msgprint("Created Sales Invoice.")

I think our approach might actually lead to problems down the road when future updates to ERPnext brings changes to any of the used doctypes. Originally, we wanted to use the function get_mapped_doc to create the invoice/payment entries but it was not possible due to the security settings at that time. I think that the function get_mapped_doc was whitelisted in the meanwhile (feat(safe_exec): Allow more APIs by gavindsouza · Pull Request #16704 · frappe/frappe · GitHub). I haven’t had time to check how a better server script would look like using the get_mapped_doc. If anyone or you @Sherif_Ramzy was successful with the whitelisted get_mapped_doc, I’d appreciate to see your solution and improve our script.