I have worked intensely on Inventory and Costing with several commercial ERPs. And have worked lightly with a dozen others. ERPNext is the only system I’ve seen with such a crazy interpretation (and implementation) of “Perpetual Inventory”.
Here’s how I’ve seen it everywhere else:
- When you configure your ERP, you create 1 (or more) balance sheet accounts in the General Ledger. These represent the total cost value of your inventory.
- Whenever inventory is added to your company (Purchase Order, output of Manufacturing, RMA), then inventory is debited immediately in the GL accounts.
- Whenever inventory is removed from your company (Sales Order, material consumption, PO return), then inventory is credited in the GL accounts.
- As needed, quantity and value adjustments are made. Some happen automatically (late-arriving freight charges on a PO). Others manually (cycle counts, corrections). Either way, the GL is posted to immediately
-
Periodically , the finance department runs reports. To compare the value of the inventory according to the inventory sub-system, versus what is in the General Ledger accounts.
- If everything is configured properly, and no one has made manual Journal Entries, everything should tie out.
- Otherwise, if there are discrepancies, Finance reconciles and adjusts.
What ERPNext is doing is trying to -automate- Step #5. Whenever anything happens to physical inventory, it immediately runs a ton of calculations. Trying to confirm that the amounts in the General Ledger (per account, per warehouse, by date), precisely match the inventory sub-ledger. And if the comparison fails? It fails to post.
This is fundamentally wrong (in my opinion) for the following 2 reasons:
1. It prevents companies from conducting business.
Assume I have a customer trying to make a purchase. ERPNext attempts to post the invoice. However, it cannot. It throws an error, because it believes the GL and inventory balances are out-of-sync.
Do I tell my customer: “I am sorry, you cannot make this purchase, my computer says there is a math error in my ledger. I’ll ask Accounting to fix. Please come back tomorrow.” ?
That conversation obviously cannot happen.
2. The functions being called introduce a devastating performance hit:
../apps/erpnext/erpnext/accounts/utils.py
def check_if_stock_and_account_balance_synced(posting_date, company, voucher_type=None, voucher_no=None):
if not cint(erpnext.is_perpetual_inventory_enabled(company)):
return
accounts = get_stock_accounts(company, voucher_type, voucher_no)
stock_adjustment_account = frappe.db.get_value("Company", company, "stock_adjustment_account")
for account in accounts:
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account,
posting_date, company)
if abs(account_bal - stock_bal) > 0.1:
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
currency=frappe.get_cached_value('Company', company, "default_currency"))
diff = flt(stock_bal - account_bal, precision)
error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses as on {3}.").format(
stock_bal, account_bal, frappe.bold(account), posting_date)
error_resolution = _("Please create an adjustment Journal Entry for amount {0} on {1}")\
.format(frappe.bold(diff), frappe.bold(posting_date))
frappe.msgprint(
msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
raise_exception=StockValueAndAccountBalanceOutOfSync,
title=_('Values Out Of Sync'),
primary_action={
'label': _('Make Journal Entry'),
'client_action': 'erpnext.route_to_adjustment_jv',
'args': get_journal_entry(account, stock_adjustment_account, diff)
})
A process that normally takes a few seconds (posting an invoice), can now take minutes in an ERPNext with a lot of history.
Equally unacceptable.
For my own clients, I’m going to have to tear apart the Perpetual Inventory code, and disable the “real-time reconcilation” components. Otherwise v13 is entirely unusable for us.