How can I automatically submit Draft invoices based on a condition?

Continuing the discussion from What's the best known restaurant POS integration?:

I’m integrating SambaPOS with ERPNext directly from MS SQL database to Frappe API through Myddleware. I have successfully received Sales Invoices into ERPNext but they are in Draft mode.

I think a script running every 5 minutes would do this, it needs to a condition so that it’s not attempting to send the wrong Invoices, e.g. Checks only draft Sales Invoices with from_sambapos=1

I think a second condition is necessary. In case of an increasing number of invoices not submitting due to some error. Only Draft invoices with auto_submit_attempted=0 should also be a condition and should be set to 1 by the script after first attempt.

How would such a script look? I’m quite new to ERPNext scripting. What kind of script would work without the user taking any action?

Below is my attempt. But I keep getting console issues like base_amount… and Frappe call… @

frappe.ui.form.on('Sales Invoice', {
   refresh(frm) {
    
   	if (frm.doc.docstatus===0 && frm.doc.auto_submit_attempted === 0){
	    
    	frm.doc.docstatus = 1;
        frm.save();
        
        frm.doc.auto_submit_attempted = 1;
	}
  }
})

Hi
you can write your script on py side and can use doc.submit() to submit your documents.

1 Like

@lokesh

I think that’s the better version. But I can only find database examples for server side scripting.

Could you kindly guide me on how to do it, with a sample to start from if possible?

Thank you for your time.

Hi
Please explain me the whole scenario, when you want to submit the document? when user open the draft mode document or when user saves the new document in draft mode and then immediately it should get submitted.

Hello @lokesh

Thank you for your interest in helping. Please note that we have already developed a script to do this. The only remaining step is to reduce the interval to 5minutes from 1 hour

1 Like

This is how I implemented a feature to auto-submit Sales Invoices upon saving the documents. The following example code demonstrates how we can enable/disable auto submit of Sales Invoices based on custom user configuration:

import frappe
from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice as BaseSalesInvoice

class SalesInvoice(BaseSalesInvoice):

    def before_save(self):
        auto_submit = frappe.db.get_single_value('MyApp Settings',
            'should_auto_submit_sales_invoice'
        )

        if auto_submit and self.docstatus == 0:
            __islocal = self.get('__islocal', default = False)
            self.set("__islocal", False)
            self.docstatus = 1
            self.set_status()
            self.set('__islocal', __islocal)
            self._action = 'submit'

        super(SalesInvoice, self).before_save()

Let’s say you extended the SalesInvoice class from my_app/orverrides/sales_invoce.py file you must include this in your app’s hooks.py file also :

override_doctype_class = {
    'Sales Invoice': 'my_app.overrides.sales_invoice.SalesInvoice',
}

Hi @lekhnath can you please teach me onchange function, button function and inherit concept in ERPNext.

I have a doubt how to convert from Odoo to ERPNext.

Thanks In Advance,
S.Kavitha

Hello @Kavitha

  1. onchange function
    There is no @api.onchange('some_field') equivalent in ERPNext. You have to do this from Front-End (ie. JavaScript files). The following example demonstrates how you would capture onchange on naming_series field of Purchase Invoice doctype:

    frappe.ui.form.on('Purchase Invoice', {
        'naming_series': function (frm) {
            // your logic here
            
            // Example
            if ((frm.doc?.naming_series || '').startsWith('PUR-IMP') {
                frm.set_value({ 'is_imported_purchase': True }); //Please note that `is_imported_purchase` field does not exists in ERPNext this is used for demo only
                refresh_fields('is_imported_purchase');
            }
            
    
            
        },
    })
    

    Create a file with above JS code and save it into my_app/my_app/public/js/PurchaseInvoiceForm.js. Include this file in hooks.py file as:

    doctype_js = {
        'Purchase Invoice' : 'public/js/PurchaseInvoiceForm.js',
    }
    
  2. button function
    ERPNext has no xml based views so you have to do this also from the JavaScript. If you would like to provide a new button in some DocType (say Sales Invoioce) form then you can use the following code snippet:

    frappe.ui.form.on('Sales Invoice', {
    
       refresh(frm) {
           frm.add_custom_button('Button Label Here, function() {
                  const rec = frm.doc;
                   console.log(rec);
           });
       }
    
    });
    

    Create above js file inside my_app/my_app/public/js/SalesInvoiceForm.js and then include this file in your hooks.py file as below:

    doctype_js = {
        'Purchase Invoice' : 'my_app/my_app/public/js/PurchaseInvoiceForm.js',
        'Sales Invoice': 'public/js/SalesInvoiceForm.js',
    }
    
  3. Inheritance Concept
    In Odoo you could simply extend any model by using the _inherit keyword. However, in ERPNext it is a bit different. And also it not model but actually a controller that we extend in Frappe/Erpnext. Please follow all the steps mentioned below:

  • Create a new file inside my_app/my_app/overrides/purchase_invoice_controller.py (NOTE: You can put any name you want instead of overrides and purchase_invoice_controller.py)

  • Import the base Document/Controller in this file from which you want to extend from

  • Extend the base class as you would normally do in python

    import frappe
    from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import PurchaseInvoice as BasePurchaseInvoice
    
    class PurchaseInvoice(BasePurchaseInvoice):
        def validate(self):
            super(PurchaseInvoice, self).validate()
    
            frappe.msgprint(
                msg = '%s has been validated' % self.name,
                title = 'My message',
            )
    
  • Include this extension in hooks.py :

    override_doctype_class = {
        'Purchase Invoice': 'my_app.overrides.purchase_invoice_controller.PurchaseInvoice'
    }
    

Here is a list of methods that you can hook into. I hope it will help you to move forward and build awesome apps in ERPNext.

1 Like

Thanks for your replay @lekhnath , Its very useful for me.

1 Like

Hi @lekhnath this is my button function in odoo13, May I know how to do this function in frappe.js
please let me know!
def generate_customerwise_results(self):

    customers = self.env['res.partner'].search([('customer', '=', 'True')])

    annual_point_schemes = self.env['annual.point.scheme'].search([('state', '=', 'done')])
    annual_point_scheme_results_unlink = self.env['annual.point.scheme.results'].search([])
    [i.annual_point_scheme_results_lines.unlink() for i in annual_point_scheme_results_unlink]
    for customer in customers:
        annual_point_scheme_results_lines = []

        for annual_point_scheme in annual_point_schemes:
            invoices = self.env['account.move'].search([('state', '=', 'posted'),
                                                        ('partner_id', '=', customer.id),
                                                        ('invoice_date', '>=', annual_point_scheme.start_date),
                                                        ('invoice_date', '<=', annual_point_scheme.end_date),
                                                        ('type', '=', 'out_invoice')])
            annual_point_scheme_results = self.env['annual.point.scheme.results'].search(
                [('annual_point_scheme_id', '=', annual_point_scheme.id)])
        if len(invoices) == 0:
            annual_point_scheme_results_lines.append((0, 0,
                                                      {'partner_id': customer.id,
                                                       'red_points_earned': 0.00,
                                                       'black_points_earned': 0.00,
                                                       'grey_points_earned': 0.00,
                                                       'lost_points_count': 0.00,
                                                       }))
            if len(annual_point_scheme_results) == 0:
                annual_point_scheme_results.create({
                    'annual_point_scheme_id': annual_point_scheme.id,
                    'annual_point_scheme_results_lines': annual_point_scheme_results_lines

                })

            else:
                annual_point_scheme_results.write({
                    'annual_point_scheme_id': annual_point_scheme.id,
                    'annual_point_scheme_results_lines': annual_point_scheme_results_lines})
        else:
            red = 0
            for invoice in invoices.invoice_line_ids:
                red += invoice.scheme_point

            annual_point_scheme_results_lines.append((0, 0,
                                                      {'partner_id': customer.id,
                                                       'red_points_earned': red,
                                                       'black_points_earned': 0.00,
                                                       'grey_points_earned': 0.00,
                                                       'lost_points_count': 0.00,

                                                       }))

            if len(annual_point_scheme_results) == 0:
                annual_point_scheme_results.create({
                    'annual_point_scheme_id': annual_point_scheme.id,
                    'annual_point_scheme_results_lines': annual_point_scheme_results_lines

                })
            else:
                # [i.annual_point_scheme_results_lines.unlink() for i in annual_point_scheme_results]
                annual_point_scheme_results.write({
                    'annual_point_scheme_id': annual_point_scheme.id,
                    'annual_point_scheme_results_lines': annual_point_scheme_results_lines})

Please gave me idea how to do this button function in ERPNext.
Becuase I’m new to this functionality.

def track_results(self):

    for monthly_scheme in self:

        monthly_scheme_results = self.env['monthly.scheme.results'].search([('monthly_schemes_id', '=', monthly_scheme.id)])
        monthly_scheme_results_lines = self.env['monthly.scheme.results.line'].search([('monthly_scheme_results_id', 'in',
                                                                                            monthly_scheme_results)])

        if monthly_scheme_results_lines:
            monthly_scheme_results_details = self.env['monthly.scheme.results.detail'].search([('monthly_scheme_results_line_id','in',monthly_scheme_results_lines)])

            if monthly_scheme_results_details:
                monthly_scheme_results_details.unlink()
                monthly_scheme_results_lines.unlink()

        category_lines_obj = self.env['category.lines']
        sub_category_lines_obj = self.env['sub.category.lines']
        product_lines_obj = self.env['product.lines']

        print(('date_invoice', '<=', monthly_scheme.end_date),('date_invoice', '>=', monthly_scheme.start_date),'::::::::')
        invoices = self.env['account.invoice'].search([('state', 'in', ['open', 'paid']),('date_invoice', '<=', monthly_scheme.end_date),
                                                                     ('date_invoice', '>=', monthly_scheme.start_date)]).ids
        print(invoices,'invoicesinvoices')

        category_lines = category_lines_obj.search([('monthly_schemes_id', '=', monthly_scheme.id)]).ids
        sub_category_lines = sub_category_lines_obj.search([('category_lines_id', 'in', category_lines)]).ids
        product_lines = product_lines_obj.search([('sub_category_lines_id', 'in', sub_category_lines)]).ids

        closed_scheme_category_lines = category_lines_obj.search([('id', 'in', category_lines),
                                                                           ('closed_scheme', '=', True)]).ids
        closed_scheme_sub_category_lines = sub_category_lines_obj.search([('id', 'in', sub_category_lines),
                                                                                   ('closed_scheme', '=', True)]).ids
        closed_scheme_product_lines = product_lines_obj.search([('id', 'in', product_lines),
                                                                         ('closed_scheme', '=', True)]).ids

        customers_in_this_period = []
        for invoice in self.env['account.invoice'].browse(invoices):
            if not invoice.partner_id.id in customers_in_this_period:
                customers_in_this_period.append(invoice.partner_id.id)

        monthly_scheme_results_lines = []


        for customer in self.env['res.partner'].browse(customers_in_this_period):
            credit_note_total = 0.0
            monthly_scheme_results_details = []
            monthly_scheme_gifts_details = []

            products_accounted = []

            for product_line in product_lines_obj.browse(closed_scheme_product_lines):
                products_accounted.append(product_line.product_id.id)
                self.env.cr.execute('WITH product_sales_query AS \
                                                (SELECT product_id, sum(quantity) AS quantity, sum(actual_quantity*quantity) as actual_quantity, \
                                                sum(round(price_subtotal)) AS price_subtotal, sum(price_total) AS price_total, \
                                                sum(price_discount) AS price_discount \
                                                FROM account_invoice_line AS ail \
                                                JOIN account_invoice ai ON (ail.invoice_id = ai.id) \
                                                WHERE \
                                                ai.state IN %s AND \
                                                ai.type = %s AND \
                                                ai.partner_id = %s AND \
                                                ail.product_id = %s AND \
                                                ai.date_invoice >= %s AND \
                                                ai.date_invoice <= %s GROUP BY product_id) \
                                                SELECT prod.id AS product_id, sum(pss.actual_quantity) AS actual_quantity, sum(pss.price_total) AS price_total, sum(pss.price_subtotal) AS price_subtotal \
                                                FROM product_sales_query pss \
                                                JOIN product_product prod ON (pss.product_id = prod.id) GROUP BY prod.id',
                           (('open', 'paid'), 'out_invoice', customer.id, product_line.product_id.id,
                            monthly_scheme.start_date, monthly_scheme.end_date))
                result = self.env.cr.dictfetchall()
                if result and result[0]['actual_quantity']:
                    volume = result[0]['actual_quantity']
                    print(volume,product_line.id,'gghhfggddjjjj')
                    closed_scheme_rebates = self.env['closed.scheme.rebate'].search([('vol', '<=', volume), ('product_lines_id', '=', product_line.id)], order='vol desc').ids
                    print(closed_scheme_rebates, 'closed_scheme_rebatesclosed_scheme_rebatesclosed_scheme_rebates')
                    if closed_scheme_rebates:
                        closed_scheme_rebate = self.env['closed.scheme.rebate'].browse(closed_scheme_rebates)[0]
                        print(closed_scheme_rebate,'closed_scheme_rebateclosed_scheme_rebateclosed_scheme_rebate')
                        if not closed_scheme_rebate.ignore:

                            rebate = closed_scheme_rebate.rebate
                            credit_note = volume * rebate
                            credit_note_total += credit_note

                            monthly_scheme_results_details.append((0, 0, {
                                'name': product_line.product_id.variant_name,
                                'credit_note': credit_note,
                                'customer_volume': volume,
                                'rebate': rebate,
                                'rebate_volume': closed_scheme_rebate.vol
                            }))
                        else:
                            monthly_scheme_gifts_details.append((0, 0, {
                                'name': product_line.product_id.variant_name,
                                'customer_volume': volume,
                                'gift_name': closed_scheme_rebate.gift_name,
                                'rebate_volume': closed_scheme_rebate.vol
                            }))
                            monthly_scheme_results_lines.append((0, 0, {
                                'partner_id': customer.id,
                                'gift_name': closed_scheme_rebate.gift_name,
                                'state': 'draft',
                                'ignore': 'True',
                                'monthly_scheme_results_details': monthly_scheme_gifts_details
                            }))

            categ_accounted = []

            for sub_category_line in sub_category_lines_obj.browse(closed_scheme_sub_category_lines):
                print(sub_category_line.category_id.id,'testing')
                categ_accounted.append(sub_category_line.category_id.id)
                products = self.env['product.product'].search([('id', 'not in', products_accounted), ('categ_id', '=', sub_category_line.category_id.id)]).ids
                (list(products))
                if products:
                    self.env.cr.execute('WITH product_sales_query AS \
                    (SELECT product_id, sum(quantity) AS quantity, sum(actual_quantity*quantity) as actual_quantity, \
                                                                                                sum(round(price_subtotal)) AS price_subtotal, sum(price_total) AS price_total, \
                                                                                                sum(price_discount) AS price_discount \
                                                                                                FROM account_invoice_line AS ail \
                                                                                                JOIN account_invoice ai ON (ail.invoice_id = ai.id) \
                                                                                                WHERE \
                                                                                                ai.state IN %s AND \
                                                                                                ai.type = %s AND \
                                                                                                ai.partner_id = %s AND \
                                                                                                ail.product_id IN %s AND \
                                                                                                ai.date_invoice >= %s AND \
                                                                                                ai.date_invoice <= %s GROUP BY product_id) \
                                                                                                SELECT pc.id AS categ_id, sum(pss.actual_quantity) AS actual_quantity, sum(pss.price_total) AS price_total, sum(pss.price_subtotal) AS price_subtotal \
                                                                                                FROM product_sales_query pss \
                                                                                                JOIN product_product prod ON (pss.product_id = prod.id) \
                                                                                                JOIN product_template pt ON (prod.product_tmpl_id = pt.id) \
                                                                                                JOIN product_category pc ON (pt.categ_id = pc.id) \
                                                                                                GROUP BY pc.id',
                                        (('open', 'paid'), 'out_invoice', customer.id, tuple(products),
                                         monthly_scheme.start_date, monthly_scheme.end_date))
                    result = self.env.cr.dictfetchall()
                    if result and result[0]['actual_quantity']:
                        volume = result[0]['actual_quantity']
                        print(volume,'subcategory')
                        closed_scheme_rebates = self.env['closed.scheme.rebate'].search([('vol', '<=', volume), ('sub_category_lines_id', '=', sub_category_line.id)],  order='vol desc').ids
                        print(closed_scheme_rebates,'closed_scheme_rebatesclosed_scheme_rebatesclosed_scheme_rebates')
                        if closed_scheme_rebates:
                            closed_scheme_rebate = self.env['closed.scheme.rebate'].browse(closed_scheme_rebates)[0]
                            if not closed_scheme_rebate.ignore:
                                rebate = closed_scheme_rebate.rebate

                                credit_note = volume * rebate
                                credit_note_total += credit_note

                                monthly_scheme_results_details.append((0, 0, {
                                    'name': sub_category_line.category_id.parent_id.name + ' / ' + sub_category_line.category_id.name,
                                    'credit_note': credit_note,
                                    'customer_volume': volume,
                                    'rebate': rebate,
                                    'rebate_volume': closed_scheme_rebate.vol
                                }))
                            else:
                                monthly_scheme_gifts_details.append((0, 0, {
                                    'name': sub_category_line.category_id.parent_id.name + ' / ' + sub_category_line.category_id.name,
                                    'credit_note': credit_note,
                                    'customer_volume': volume,
                                    'gift_name': closed_scheme_rebate.gift_name,
                                    'rebate_volume': closed_scheme_rebate.vol
                                }))
                                monthly_scheme_results_lines.append((0, 0, {
                                    'partner_id': customer.id,
                                    'gift_name': closed_scheme_rebate.gift_name,
                                    'state': 'draft',
                                    'ignore': 'True',
                                    'monthly_scheme_results_details': monthly_scheme_gifts_details
                                }))
            for category_line in category_lines_obj.browse(closed_scheme_category_lines):
                print(categ_accounted,'categ_accountedcateg_accountedcateg_accountedcateg_accounted')
                sub_categories_sr = self.env['product.category'].search([('id', 'not in', categ_accounted), ('parent_id', '=', category_line.category_id.id)]).ids
                products = self.env['product.product'].search([('id', 'not in', products_accounted), ('categ_id', 'in', sub_categories_sr)]).ids
                if products:
                    self.env.cr.execute('WITH product_sales_query AS \
                                                        (SELECT product_id, sum(quantity) AS quantity, sum(actual_quantity*quantity) as actual_quantity, \
                                                        sum(round(price_subtotal)) AS price_subtotal, sum(price_total) AS price_total, \
                                                        sum(price_discount) AS price_discount \
                                                        FROM account_invoice_line AS ail \
                                                        JOIN account_invoice ai ON (ail.invoice_id = ai.id) \
                                                        WHERE \
                                                        ai.state IN %s AND \
                                                        ai.type = %s AND \
                                                        ai.partner_id = %s AND \
                                                        ail.product_id IN %s AND \
                                                        ai.date_invoice >= %s AND \
                                                        ai.date_invoice <= %s GROUP BY product_id) \
                                                        SELECT parent.id AS parent_id, sum(pss.actual_quantity) AS actual_quantity, sum(pss.price_total) AS price_total, sum(pss.price_subtotal) AS price_subtotal \
                                                        FROM product_sales_query pss \
                                                        JOIN product_product prod ON (pss.product_id = prod.id) \
                                                        JOIN product_template pt ON (prod.product_tmpl_id = pt.id) \
                                                        JOIN product_category pc ON (pt.categ_id = pc.id) \
                                                        JOIN product_category parent ON (pc.parent_id = parent.id) \
                                                        GROUP BY parent.id',
                               (('open', 'paid'), 'out_invoice', customer.id, tuple(products),
                                monthly_scheme.start_date, monthly_scheme.end_date))

                    result = self.env.cr.dictfetchall()
                    if result and result[0]['actual_quantity']:

                        volume = result[0]['actual_quantity']
                        closed_scheme_rebates = self.env['closed.scheme.rebate'].search([('vol', '<=', volume), ('category_lines_id', '=', category_line.id)], order='vol desc')

                        if closed_scheme_rebates:

                            closed_scheme_rebate = self.env['closed.scheme.rebate'].browse(closed_scheme_rebates)[0]
                            if not closed_scheme_rebate.ignore:
                                rebate = closed_scheme_rebate.rebate

                                credit_note = volume * rebate
                                credit_note_total += credit_note

                                monthly_scheme_results_details.append((0, 0, {
                                    'name': category_line.category_id.name,
                                    'credit_note': credit_note,
                                    'customer_volume': volume,
                                    'rebate': rebate,
                                    'rebate_volume': closed_scheme_rebate.vol
                                }))

                            else:
                                gift_name = closed_scheme_rebate.gift_name
                                monthly_scheme_gifts_details.append((0, 0, {
                                    'name': category_line.category_id.name,
                                    'customer_volume': volume,
                                    'gift_name': closed_scheme_rebate.gift_name,
                                    'rebate_volume': closed_scheme_rebate.vol
                                }))
                                monthly_scheme_results_lines.append((0, 0, {
                                    'partner_id': customer.id,
                                    'gift_name': closed_scheme_rebate.gift_name,
                                    'state': 'draft',
                                    'ignore': 'True',
                                    'monthly_scheme_results_details': monthly_scheme_gifts_details
                                }))

            if credit_note_total:
                monthly_scheme_results_lines.append((0, 0, {
                    'partner_id': customer.id,
                    'credit_note': credit_note_total,
                    'state': 'draft',
                    'monthly_scheme_results_details': monthly_scheme_results_details
                }))
                print(monthly_scheme_results_lines,'monthly_scheme_results_linesmonthly_scheme_results_linesmonthly_scheme_results_lines')

    if not monthly_scheme_results:
        self.env['monthly.scheme.results'].create({
                    'monthly_schemes_id': monthly_scheme.id,
                    'monthly_scheme_results_lines': monthly_scheme_results_lines,
                    'state': 'draft',
                })
    else:
        monthly_scheme_results.write({'monthly_scheme_results_lines': monthly_scheme_results_lines})

    return True