ERPNext Expenses Doctype

Expense Claim (erpnext.com)

does this help?

Thanks. It’s a different solution (I think the naming is slightly misleading). I also thought it was the feature but it’s something different (from the docs) :
“Expense Claim is made when employees make expenses out of their pocket on behalf of the company.
For example, if they take a customer out for lunch, they can make a request for reimbursement via the Expense Claim form.”

@adam26d 's solution is to track company’s expenses (instead of using the more complicated way using a journal entry).

Quoting @rahy who described it above well:
" The Expense Claim is for reimbursement to the employee
(employee -> 3rd party , then company -> employee)

The Expense Voucher is for direct payment to third-party
(company -> 3rd party or company -> employee -> 3rd party)"

1 Like

A feature like this, to my opinion, is unnecessary. Booking non-item expenses can already be achieved with Purchase Invoice or Payment Entry.

Thanks for your feedback @dannyfoo. I might be wrong but it seems you are not having the same understanding of the Expense Claim. Purchase incoice/payment entry are for buying something. You need to have an item registered for this. What is if you have expenses such as a flight ticket, workshop costs or electricity bill. You don’t want to create an additional item (esp. for rarer expenses) in order to book an invoice. Although I agree that both are very similar, it’s a slightly different use case in my opinion. It might not be so relevant for your application but in our case we (and based on the other comments above also many other users) would benefit from having a simpler form to input expenses than using a journal entry or purchase invoice/payment entry as you are suggesting.

You can create a purchase invoice and tick the checkbox for “Is Paid”. In the Item section, click Edit to expand into the grid view. Skip Item Code and just key in the name for your expense, i.e. flight ticket, workshop etc. in Item Name, select quantity, uom, rate and finally scroll down to Accouting section to select the expense head to credit.
Also, because the “Is Paid” checkbox is ticked, you now have an additional “Payments” section. Just key in the payment info (i.e. account to credit) here.
Hope this helps!

@bluesky @dannyfoo I think the key here is use case. The fact is different organisations might decide to use different methods to settle expenses. Some would prefer payment entry, others would prefer journal entry. In my opinion, no solution is a waste as someone somewhere would like and want to use it.

I modified the cash advance and expense claim doctypes to manage both Employee and general expenses as my use case demanded that an Employee be responsible for all expenses not paid directly to recognised suppliers/vendors using invoicing (cash and open market expenses like flight tickets, prepaid bills payment, e.t.c). So while some might argue this is not right, I have justification for using it and it helps my financial reporting so it works for me. It may not work for you but it works for me and that’s the beauty of ERPNext… you can bend it to your will if you need to.

So kudos to @bluesky for not only coming up with a solution to fit his use case, he shared it with the community so whoever shares his use case can benefit.

In our app POS Bahrain we have made a simple payment entry method wherein user can just mention the amount and ledger. No need to make purchase invoice or item. Tax also can be added so it shows up in the custom built tax report for our country. The method was copied from SAP. It was during our ERPNext installation work where KPMG was the software consultant that KPMG showed us this feature of SAP. The code is below. It’s part of an app so someone can extract the code into another app.

3 Likes

Kudos should go to @adam26d! :slight_smile:

1 Like

Actually, this is not unnecessary. It is a must-have feature!
Not all the expenses are paid right away. However, they should be “accrued” at the time of the obligation according to all accepted accounting conventions. And recording it as a “purchase invoice” or a “journal entry” is not a good practice.

There are many weak points in simple things in the Accounting module of ERPNext that hinder its power as a fully-fledged ERP such as the incorrect format of profit and loss account, Incorrect gross profit reports etc. Which I will hope improve in future versions.

Anyways kudos to @adam26d for bringing this in!

1 Like

@bluesky oh right… tagged you wrongly. @adam26d Kudos to you!!!

I don’t think it is impractical. I think the opposite. Use of purchase invoice to record general invoices is the last thing you want in an organized environment.

1 Like

Yes. It is different. Not all the expenses in a business are employee-related. There are general expenses like rent, electricity etc. These need to be accounted for separately and most importantly accrued at the period ends.

As of now ERP NEXT allows tax to item.

Is there any chance to add tax to Indirect expenses.

Then we can create purchases for rent, office expenses, etc

And create payment againist the purchase. Then only it should reflected to tax report like GSTR

Can you detailed on this, how to do it?

  1. Create a default item in Purchase Taxes and Charges Template (e.g. “DEFAULT VAT + EWT”), charges should be 0 rate.

  2. On Expense Claim
    -Add a link to “Purchase Taxes and Charges Template” renamed to “Expense Taxes and Charges Template” where it will prefill the “Expense Taxes and Charges” table base on the template.

  3. On Expense Claim Detail, add the following columns:
    -Gross Amount
    -BIR Company (link)
    -Item Tax Template (link)

  4. Add a client script to apply calculations. Sanction amount should be less to the taxes and charges as the employee will not compute for it and so is the Taxes and Charges.

frm.add_custom_button('Calculate Expense Taxes And Charges', () => {
	        if (frm.doc.expense_taxes_and_charges_template) {
                var taxes = frm.doc.taxes;
                var newTaxes = [];
                frm.doc.expenses.forEach((expense) => {
                    var expense_rate = 0;
                    var tax_template = frm.doc.expense_taxes_and_charges_template;
                    if (expense.item_tax_template) {
                        tax_template = expense.item_tax_template;
                    }
                    frappe.call({
                        method: 'frappe.client.get_list',
                        async: false,
                        args: {
                            'doctype': 'Purchase Taxes and Charges',
                            'parent': 'Purchase Taxes and Charges Template',
                            'filters': {
                                'parent': tax_template,
                                'parenttype': 'Purchase Taxes and Charges Template'
                            },
                            'fields': [
                                'add_deduct_tax',
                                'included_in_print_rate',
                                'rate',
                                'account_head',
                                'description'
                            ]
                        },
                        callback: function(r) {
                            if (!r.exc) {
                                r.message.forEach((obj) => {
                                    if (obj.included_in_print_rate === 1) {
                                        if (obj.rate!==0) {
                                            if (obj.add_deduct_tax==="Add") {
                                                expense_rate = expense_rate+obj.rate;
                                            } else {
                                                expense_rate = expense_rate-obj.rate;
                                            }
                                        }
                                    } else {
                                        var tax_amount = 0;
                                        if (obj.rate!==0) {
                                            if (obj.add_deduct_tax==="Add") {
                                                tax_amount=(expense.gross_amount*obj.rate)/100;
                                            } else {
                                                tax_amount=-(expense.gross_amount*obj.rate)/100;
                                            }
                                        }
                                        newTaxes.push({
                                            account_head: obj.account_head,
                                            add_deduct_tax: obj.add_deduct_tax,
                                            san_amount: expense.gross_amount,
                                            tax_amount: tax_amount
                                        });
                                    }
                                });
                                expense.amount = expense.gross_amount;
                                expense.sanctioned_amount = expense.gross_amount;
                                if (expense_rate!==0) {
                                    var san_amount = expense.gross_amount/(1+(expense_rate/100));
                                    expense.amount = san_amount;
                                    expense.sanctioned_amount = san_amount;
                                    frm.refresh_fields("expenses");
                                    r.message.forEach((obj) => {
                                        var tax_amount = 0;
                                        if (obj.rate!==0) {
                                            if (obj.add_deduct_tax==="Add") {
                                                tax_amount=(san_amount*obj.rate)/100;
                                            } else {
                                                tax_amount=-(san_amount*obj.rate)/100;
                                            }
                                        }
                                        newTaxes.push({
                                            account_head: obj.account_head,
                                            add_deduct_tax: obj.add_deduct_tax,
                                            san_amount: san_amount,
                                            tax_amount: tax_amount
                                        });
                                    });
                                }
                                var total_sanctioned_amount = 0;
                                frm.doc.expenses.forEach((obj)=> {
                                    total_sanctioned_amount = total_sanctioned_amount*1 + obj.sanctioned_amount*1;
                                });
                                if (total_sanctioned_amount!==0) {
                                    frm.doc.total_sanctioned_amount = total_sanctioned_amount*1;
                                }
                                taxes = taxes.map((obj)=> {
                                    obj.tax_amount=0;
                                    return obj;
                                });
                                newTaxes.forEach((obj)=>{
                                    if (taxes && taxes.length) {
                                        taxes.forEach((tax) => {
                                            if (tax.account_head===obj.account_head) {
                                                tax.tax_amount=tax.tax_amount+obj.tax_amount;
                                                tax.total=frm.doc.total_sanctioned_amount*1+tax.tax_amount*1;
                                            }
                                        });
                                    } 
                                });
                                var total_taxes_and_charges = 0;
                                taxes.forEach((tax) => { 
                                    total_taxes_and_charges = total_taxes_and_charges+tax.tax_amount;
                                });
                                frm.doc.total_taxes_and_charges = total_taxes_and_charges;
                                frm.doc.total_claimed_amount = frm.doc.total_sanctioned_amount + total_taxes_and_charges;
                                if (frm.doc.total_advance_amount) {
                                    frm.doc.grand_total = frm.doc.total_sanctioned_amount*1 + total_taxes_and_charges*1;
                                } else {
                                    frm.doc.grand_total = frm.doc.total_sanctioned_amount*1 + total_taxes_and_charges*1 - frm.doc.total_advance_amount*1;
                                }
                                frm.refresh_fields("taxes");
                                frappe.msgprint("Calculate Expense Taxes And Charges: Done!");
                            }
                        }
                    });
                });
	        } else {
	            frm.doc.expenses.forEach((expense) => {
    	            expense.amount = expense.gross_amount;
                    expense.sanctioned_amount = expense.gross_amount;
    	        });
    	        frm.refresh_fields("expenses");
    	        frappe.msgprint("Calculate Expense Taxes And Charges: Done!");
	        }
        });
  1. Create a report showing the new doctype per line item. “Expense VAT and EWT”
1 Like

Thank you so much for reserving an hour out of your busy schedule to detailed and customize the doctype. you’re so generous!

1 Like

Hi @adam26d, could you add the app to Frappe Cloud Market Place? This way users on Frappe Cloud can also benefit from the app.

(The instructions how to do this, can be found here:
Publishing an App to Marketplace)

1 Like

how to update the app
what’s the commanded

The easiest way is to go into the app directory and do a git pull

cd <bench>/apps/expense_request
git pull

Will be doing this in december and refining the app. Thanks for the suggestion

1 Like