feat: book Expenses Added To Stock GL entries for stock vouchers (configurable) (#57190)

* feat: book Expenses Added To Stock GL entries for Stock Entry, Stock Reconciliation and LCV

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: make stock expense GL booking configurable via Accounts Settings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: skip stock expense booking for unconfigured companies, check flag once per compose

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
rohitwaghchaure
2026-07-16 15:34:45 +05:30
committed by GitHub
parent a25acee43f
commit 7b517a4e64
19 changed files with 440 additions and 24 deletions

View File

@@ -22,6 +22,8 @@
"allow_multi_currency_invoices_against_single_party_account",
"confirm_before_resetting_posting_date",
"preview_mode",
"stock_expense_section",
"book_stock_expense_gl_entries",
"analytics_section",
"enable_discounts_and_margin",
"enable_accounting_dimensions",
@@ -757,6 +759,18 @@
"description": "Changing the account in any transaction of the DocTypes listed below will trigger a repost. To prevent reposting, remove the relevant DocType from the list.",
"fieldname": "column_break_mfor",
"fieldtype": "Column Break"
},
{
"fieldname": "stock_expense_section",
"fieldtype": "Section Break",
"label": "Stock Expense Accounting"
},
{
"default": "0",
"description": "Books Purchase Expense and Expenses Added To Stock account pairs against stock value. On enabling this, the accounts become mandatory in Company or Item Defaults for Purchase Receipt, Purchase Invoice, Stock Entry, Stock Reconciliation and Landed Cost Voucher",
"fieldname": "book_stock_expense_gl_entries",
"fieldtype": "Check",
"label": "Book Stock Expense GL Entries"
}
],
"grid_page_length": 50,
@@ -765,7 +779,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2026-06-24 12:59:41.868865",
"modified": "2026-07-15 17:00:00.000000",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -62,6 +62,7 @@ class AccountsSettings(Document):
book_asset_depreciation_entry_automatically: DF.Check
book_deferred_entries_based_on: DF.Literal["Days", "Months"]
book_deferred_entries_via_journal_entry: DF.Check
book_stock_expense_gl_entries: DF.Check
book_tax_discount_loss: DF.Check
calculate_depr_using_total_days: DF.Check
check_supplier_invoice_uniqueness: DF.Check

View File

@@ -330,30 +330,38 @@ class BuyingController(SubcontractingController):
address_display_field, render_address(self.get(address_field), check_permissions=False)
)
def get_validated_purchase_expense_details(self, item_code):
fields = ("purchase_expense_account", "purchase_expense_contra_account")
details = get_purchase_expense_account(item_code, self.company)
for field in fields:
if not details.get(field):
details[field] = frappe.get_cached_value("Company", self.company, field)
if not any(details.get(field) for field in fields):
return None
for field in fields:
if not details.get(field):
frappe.throw(
_("Please set {0} in Company {1} or in the Item Defaults of Item {2}").format(
frappe.bold(_(frappe.unscrub(field))), self.company, item_code
)
)
return details
def set_gl_entry_for_purchase_expense(self, gl_entries):
if not cint(frappe.db.get_single_value("Accounts Settings", "book_stock_expense_gl_entries")):
return
if self.doctype == "Purchase Invoice" and not self.update_stock:
return
for row in self.items:
details = get_purchase_expense_account(row.item_code, self.company)
if not details.purchase_expense_account:
details.purchase_expense_account = frappe.get_cached_value(
"Company", self.company, "purchase_expense_account"
)
if not details.purchase_expense_account:
return
if not details.purchase_expense_contra_account:
details.purchase_expense_contra_account = frappe.get_cached_value(
"Company", self.company, "purchase_expense_contra_account"
)
if not details.purchase_expense_contra_account:
frappe.throw(
_("Please set Purchase Expense Contra Account in Company {0}").format(self.company)
)
details = self.get_validated_purchase_expense_details(row.item_code)
if not details:
continue
amount = flt(row.valuation_rate * row.stock_qty, row.precision("base_amount"))
self.add_gl_entry(

View File

@@ -499,3 +499,4 @@ erpnext.patches.v15_0.backfill_sla_link_filters_on_custom_field
erpnext.patches.v15_0.backfill_sla_link_filters_on_docfield
erpnext.patches.v16_0.crm_settings_handle_allowed_users_for_frappe_crm
erpnext.patches.v16_0.access_control_for_project_users
erpnext.patches.v16_0.enable_book_stock_expense_gl_entries

View File

@@ -0,0 +1,10 @@
import frappe
def execute():
has_expense_accounts = frappe.db.exists(
"Company", {"purchase_expense_account": ("is", "set")}
) or frappe.db.exists("Item Default", {"purchase_expense_account": ("is", "set")})
if has_expense_accounts:
frappe.db.set_single_value("Accounts Settings", "book_stock_expense_gl_entries", 1)

View File

@@ -323,6 +323,8 @@ erpnext.company.setup_queries = function (frm) {
["default_advance_received_account", { root_type: "Liability", account_type: "Receivable" }],
["default_advance_paid_account", { root_type: "Asset", account_type: "Payable" }],
["service_expense_account", { root_type: "Expense" }],
["expenses_added_to_stock_account", { root_type: "Expense" }],
["expenses_added_to_stock_contra_account", { root_type: "Expense" }],
],
function (i, v) {
erpnext.company.set_custom_query(frm, v);

View File

@@ -137,6 +137,10 @@
"disable_sdbnb_in_sr",
"default_provisional_account",
"default_in_transit_warehouse",
"stock_expense_section",
"expenses_added_to_stock_account",
"column_break_gthb",
"expenses_added_to_stock_contra_account",
"manufacturing_section",
"default_operating_cost_account",
"column_break_9prc",
@@ -962,6 +966,18 @@
"label": "Service Expense Account",
"options": "Account"
},
{
"fieldname": "expenses_added_to_stock_account",
"fieldtype": "Link",
"label": "Expenses Added To Stock Account",
"options": "Account"
},
{
"fieldname": "expenses_added_to_stock_contra_account",
"fieldtype": "Link",
"label": "Expenses Added To Stock Contra Account",
"options": "Account"
},
{
"default": "0",
"description": "If enabled, the system will use the inventory account set in the Item Master or Item Group or Brand. Otherwise, it will use the inventory account set in the Warehouse.",
@@ -1071,6 +1087,15 @@
"fieldname": "enable_stock_delivered_but_not_billed",
"fieldtype": "Check",
"label": "Enable Stock Delivered But Not Billed"
},
{
"fieldname": "stock_expense_section",
"fieldtype": "Section Break",
"label": "Stock Expense"
},
{
"fieldname": "column_break_gthb",
"fieldtype": "Column Break"
}
],
"grid_page_length": 50,
@@ -1079,7 +1104,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
"modified": "2026-07-02 07:21:21.794533",
"modified": "2026-07-15 15:38:29.214020",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",

View File

@@ -104,6 +104,8 @@ class Company(NestedSet):
exception_budget_approver_role: DF.Link | None
exchange_gain_loss_account: DF.Link | None
existing_company: DF.Link | None
expenses_added_to_stock_account: DF.Link | None
expenses_added_to_stock_contra_account: DF.Link | None
fax: DF.Data | None
is_group: DF.Check
lft: DF.Int

View File

@@ -75,6 +75,23 @@ frappe.ui.form.on("Item Group", {
},
};
};
["expenses_added_to_stock_account", "expenses_added_to_stock_contra_account"].forEach((field) => {
frm.fields_dict["item_group_defaults"].grid.get_field(field).get_query = function (
doc,
cdt,
cdn
) {
const row = locals[cdt][cdn];
return {
filters: {
root_type: "Expense",
company: row.company,
is_group: 0,
},
};
};
});
},
refresh: function (frm) {
@@ -174,6 +191,8 @@ const COMPANY_DEFAULTS_TO_VF = {
default_discount_account: "vf_default_discount_account",
default_supplier: "vf_default_supplier",
purchase_expense_contra_account: "vf_purchase_expense_contra_account",
expenses_added_to_stock_account: "vf_expenses_added_to_stock_account",
expenses_added_to_stock_contra_account: "vf_expenses_added_to_stock_contra_account",
};
const FIELD_DEFAULT_SOURCE = {
@@ -192,6 +211,8 @@ const FIELD_DEFAULT_SOURCE = {
default_discount_account: "Company",
default_supplier: null,
purchase_expense_contra_account: "Company",
expenses_added_to_stock_account: "Company",
expenses_added_to_stock_contra_account: "Company",
};
function populate_item_group_company_defaults(frm, cdt, cdn, row) {

View File

@@ -126,6 +126,8 @@ def get_company_resolved_defaults(company: str) -> dict:
"deferred_revenue_account": company_doc.get("default_deferred_revenue_account"),
"default_discount_account": company_doc.get("default_discount_account"),
"purchase_expense_contra_account": company_doc.get("purchase_expense_contra_account"),
"expenses_added_to_stock_account": company_doc.get("expenses_added_to_stock_account"),
"expenses_added_to_stock_contra_account": company_doc.get("expenses_added_to_stock_contra_account"),
"default_price_list": "",
"default_supplier": "",
}

View File

@@ -17,6 +17,8 @@ const virtual_field_map = {
default_provisional_account: "vf_default_provisional_account",
purchase_expense_account: "vf_purchase_expense_account",
purchase_expense_contra_account: "vf_purchase_expense_contra_account",
expenses_added_to_stock_account: "vf_expenses_added_to_stock_account",
expenses_added_to_stock_contra_account: "vf_expenses_added_to_stock_contra_account",
selling_cost_center: "vf_selling_cost_center",
income_account: "vf_income_account",
default_cogs_account: "vf_default_cogs_account",
@@ -787,7 +789,13 @@ $.extend(erpnext.item, {
};
});
let fields = ["purchase_expense_account", "purchase_expense_contra_account", "default_cogs_account"];
let fields = [
"purchase_expense_account",
"purchase_expense_contra_account",
"default_cogs_account",
"expenses_added_to_stock_account",
"expenses_added_to_stock_contra_account",
];
fields.forEach((field) => {
frm.set_query(field, "item_defaults", (doc, cdt, cdn) => {

View File

@@ -27,6 +27,8 @@
"vf_default_provisional_account",
"vf_purchase_expense_account",
"vf_purchase_expense_contra_account",
"vf_expenses_added_to_stock_account",
"vf_expenses_added_to_stock_contra_account",
"column_break_ghzl",
"buying_cost_center",
"default_supplier",
@@ -34,6 +36,8 @@
"default_provisional_account",
"purchase_expense_account",
"purchase_expense_contra_account",
"expenses_added_to_stock_account",
"expenses_added_to_stock_contra_account",
"purchase_price_variance_account",
"manufacturing_variance_account",
"selling_defaults",
@@ -191,6 +195,22 @@
"options": "Account",
"show_description_on_click": 1
},
{
"description": "Account to track value added to stock via Stock Entry, Stock Reconciliation or Landed Cost Voucher",
"fieldname": "expenses_added_to_stock_account",
"fieldtype": "Link",
"label": "Expenses Added To Stock Account",
"options": "Account",
"show_description_on_click": 1
},
{
"description": "Used to balance the books when recording expenses added to stock",
"fieldname": "expenses_added_to_stock_contra_account",
"fieldtype": "Link",
"label": "Expenses Added To Stock Contra Account",
"options": "Account",
"show_description_on_click": 1
},
{
"description": "For Standard Cost items: the purchase price vs standard rate difference is booked here. Falls back to the Company's Default Purchase Price Variance Account.",
"fieldname": "purchase_price_variance_account",
@@ -247,6 +267,18 @@
"is_virtual": 1,
"label": "Purchase Expense Contra Account"
},
{
"fieldname": "vf_expenses_added_to_stock_account",
"fieldtype": "Read Only",
"is_virtual": 1,
"label": "Expenses Added To Stock Account"
},
{
"fieldname": "vf_expenses_added_to_stock_contra_account",
"fieldtype": "Read Only",
"is_virtual": 1,
"label": "Expenses Added To Stock Contra Account"
},
{
"fieldname": "selling_defaults",
"fieldtype": "Section Break",
@@ -374,7 +406,7 @@
],
"istable": 1,
"links": [],
"modified": "2026-07-01 11:48:07.853494",
"modified": "2026-07-15 10:00:00.000000",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item Default",

View File

@@ -26,6 +26,8 @@ class ItemDefault(Document):
deferred_expense_account: DF.Link | None
deferred_revenue_account: DF.Link | None
expense_account: DF.Link | None
expenses_added_to_stock_account: DF.Link | None
expenses_added_to_stock_contra_account: DF.Link | None
income_account: DF.Link | None
inventory_account_currency: DF.Link | None
manufacturing_variance_account: DF.Link | None

View File

@@ -191,6 +191,14 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
item=item,
)
def make_expenses_added_to_stock_entries(item):
if not self.book_stock_expense_enabled():
return
amount = flt(item.landed_cost_voucher_amount, item.precision("base_net_amount"))
if amount and not item.is_fixed_asset:
self.append_expenses_added_to_stock_pair(gl_entries, item.item_code, amount, item)
def make_amount_difference_entry(item):
if item.amount_difference_with_purchase_invoice and stock_asset_rbnb:
account_currency = get_account_currency(stock_asset_rbnb)
@@ -321,6 +329,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
make_item_asset_inward_gl_entry(d, stock_value_diff, stock_asset_account_name)
outgoing_amount = make_stock_received_but_not_billed_entry(d)
make_landed_cost_gl_entries(d)
make_expenses_added_to_stock_entries(d)
make_amount_difference_entry(d)
make_sub_contracting_gl_entries(d)
make_divisional_loss_gl_entry(d, outgoing_amount)

View File

@@ -5054,6 +5054,7 @@ class TestPurchaseReceipt(ERPNextTestSuite):
self.assertEqual(srbnb_cost, 1000)
def test_purchase_expense_account(self):
frappe.db.set_single_value("Accounts Settings", "book_stock_expense_gl_entries", 1)
item = "Test Item with Purchase Expense Account"
make_item(item, {"is_stock_item": 1})
company = "_Test Company with perpetual inventory"

View File

@@ -20,6 +20,7 @@ class StockEntryGLComposer(BaseStockGLComposer):
"""
enforce_pl_expense_account = False
book_expenses_added_to_stock = True
def compose(self, inventory_account_map: dict | None = None) -> list:
doc = self.doc

View File

@@ -18,6 +18,7 @@ class StockReconciliationGLComposer(BaseStockGLComposer):
"""
enforce_pl_expense_account = False
book_expenses_added_to_stock = True
def compose(self, inventory_account_map: dict | None = None) -> list:
doc = self.doc

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _
from frappe.utils import flt
from frappe.utils import cint, flt
from erpnext.accounts.general_ledger import process_gl_map
from erpnext.accounts.services.base_gl_composer import BaseGLComposer
@@ -22,6 +22,8 @@ class BaseStockGLComposer(BaseGLComposer):
#: account (stock transfers, deliveries, reconciliations) set this to False.
enforce_pl_expense_account = True
book_expenses_added_to_stock = False
def compose(
self,
inventory_account_map: dict | None = None,
@@ -154,6 +156,9 @@ class BaseStockGLComposer(BaseGLComposer):
).format(wh, doc.company)
)
if self.book_expenses_added_to_stock:
self.append_expenses_added_to_stock_entries(gl_list, voucher_details, sle_map)
return process_gl_map(
gl_list, precision=precision, from_repost=frappe.flags.through_repost_item_valuation
)
@@ -164,6 +169,81 @@ class BaseStockGLComposer(BaseGLComposer):
return frappe.flags.debit_field_precision
def book_stock_expense_enabled(self):
if not hasattr(self, "_book_stock_expense_enabled"):
self._book_stock_expense_enabled = cint(
frappe.db.get_single_value("Accounts Settings", "book_stock_expense_gl_entries")
)
return self._book_stock_expense_enabled
def append_expenses_added_to_stock_entries(self, gl_list, voucher_details, sle_map):
if not self.book_stock_expense_enabled():
return
precision = self.get_debit_field_precision()
for item_row in voucher_details:
sle_list = sle_map.get(item_row.name)
if not sle_list:
continue
amount = flt(sum(flt(sle.stock_value_difference) for sle in sle_list), precision)
if not amount:
continue
item_code = item_row.get("item_code") or sle_list[0].item_code
self.append_expenses_added_to_stock_pair(gl_list, item_code, amount, item_row)
def append_expenses_added_to_stock_pair(self, gl_list, item_code, amount, item_row):
doc = self.doc
fields = ("expenses_added_to_stock_account", "expenses_added_to_stock_contra_account")
details = get_expenses_added_to_stock_accounts(item_code, doc.company)
if not any(details.get(field) for field in fields):
return
for field in fields:
if not details.get(field):
frappe.throw(
_("Please set {0} in Company {1} or in the Item Defaults of Item {2}").format(
frappe.bold(_(frappe.unscrub(field))), doc.company, item_code
)
)
cost_center = item_row.get("cost_center") or frappe.get_cached_value(
"Company", doc.company, "cost_center"
)
remarks = _("Expenses Added To Stock for Item {0}").format(item_code)
common_args = {
"cost_center": cost_center,
"project": item_row.get("project") or doc.get("project"),
"remarks": remarks,
}
gl_list.append(
self.get_gl_dict(
{
"account": details.expenses_added_to_stock_account,
"against": details.expenses_added_to_stock_contra_account,
"debit": amount,
**common_args,
},
item=item_row,
)
)
gl_list.append(
self.get_gl_dict(
{
"account": details.expenses_added_to_stock_contra_account,
"against": details.expenses_added_to_stock_account,
"debit": -1 * amount,
**common_args,
},
item=item_row,
)
)
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
details = self.doc.get("items")
@@ -203,3 +283,29 @@ class BaseStockGLComposer(BaseGLComposer):
_(self.doc.doctype), self.doc.name, item.get("item_code")
)
)
@frappe.request_cache
def get_expenses_added_to_stock_accounts(item_code, company):
from erpnext.stock.doctype.item.item import get_item_defaults
fields = ["expenses_added_to_stock_account", "expenses_added_to_stock_contra_account"]
defaults = get_item_defaults(item_code, company)
details = frappe._dict({field: defaults.get(field) for field in fields})
if not details.expenses_added_to_stock_account:
details = frappe.db.get_value(
"Item Default", {"parent": defaults.item_group, "company": company}, fields, as_dict=1
) or frappe._dict({})
if not details.expenses_added_to_stock_account and defaults.get("brand"):
details = frappe.db.get_value(
"Item Default", {"parent": defaults.brand, "company": company}, fields, as_dict=1
) or frappe._dict({})
for field in fields:
if not details.get(field):
details[field] = frappe.get_cached_value("Company", company, field)
return details

View File

@@ -0,0 +1,170 @@
# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.tests.utils import ERPNextTestSuite
COMPANY = "_Test Company with perpetual inventory"
WAREHOUSE = "Stores - TCP1"
class TestExpensesAddedToStock(ERPNextTestSuite):
def setUp(self):
self.eats_account = create_account(
account_name="Expenses Added To Stock",
parent_account="Expenses - TCP1",
company=COMPANY,
)
self.eats_contra_account = create_account(
account_name="Expenses Added To Stock Contra",
parent_account="Expenses - TCP1",
company=COMPANY,
)
self.purchase_expense_account = create_account(
account_name="Test Purchase Expense EATS",
parent_account="Expenses - TCP1",
company=COMPANY,
)
self.purchase_expense_contra_account = create_account(
account_name="Test Purchase Expense Contra EATS",
parent_account="Expenses - TCP1",
company=COMPANY,
)
frappe.db.set_value(
"Company",
COMPANY,
{
"expenses_added_to_stock_account": self.eats_account,
"expenses_added_to_stock_contra_account": self.eats_contra_account,
"purchase_expense_account": self.purchase_expense_account,
"purchase_expense_contra_account": self.purchase_expense_contra_account,
},
)
frappe.db.set_single_value("Accounts Settings", "book_stock_expense_gl_entries", 1)
self.item = make_item(properties={"is_stock_item": 1, "valuation_method": "FIFO"}).name
def get_gl_balances(self, voucher_type, voucher_no):
entries = frappe.get_all(
"GL Entry",
filters={
"voucher_type": voucher_type,
"voucher_no": voucher_no,
"is_cancelled": 0,
"account": ("in", [self.eats_account, self.eats_contra_account]),
},
fields=["account", "debit", "credit"],
)
balances = frappe._dict({self.eats_account: 0.0, self.eats_contra_account: 0.0})
debits = frappe._dict({self.eats_account: 0.0, self.eats_contra_account: 0.0})
credits = frappe._dict({self.eats_account: 0.0, self.eats_contra_account: 0.0})
for entry in entries:
balances[entry.account] += entry.debit - entry.credit
debits[entry.account] += entry.debit
credits[entry.account] += entry.credit
return balances, debits, credits
def test_material_receipt_books_expenses_added_to_stock(self):
se = make_stock_entry(item_code=self.item, to_warehouse=WAREHOUSE, qty=10, rate=100, company=COMPANY)
_balances, debits, credits = self.get_gl_balances("Stock Entry", se.name)
self.assertEqual(debits[self.eats_account], 1000)
self.assertEqual(credits[self.eats_contra_account], 1000)
def test_material_issue_books_reverse_pair(self):
make_stock_entry(item_code=self.item, to_warehouse=WAREHOUSE, qty=10, rate=100, company=COMPANY)
se = make_stock_entry(item_code=self.item, from_warehouse=WAREHOUSE, qty=5, company=COMPANY)
_balances, debits, credits = self.get_gl_balances("Stock Entry", se.name)
self.assertEqual(credits[self.eats_account], 500)
self.assertEqual(debits[self.eats_contra_account], 500)
def test_material_transfer_books_nothing(self):
make_stock_entry(item_code=self.item, to_warehouse=WAREHOUSE, qty=10, rate=100, company=COMPANY)
se = make_stock_entry(
item_code=self.item,
from_warehouse=WAREHOUSE,
to_warehouse="Finished Goods - TCP1",
qty=5,
company=COMPANY,
)
_balances, debits, credits = self.get_gl_balances("Stock Entry", se.name)
self.assertEqual(debits[self.eats_account], 0)
self.assertEqual(credits[self.eats_account], 0)
def test_stock_reconciliation_books_pair(self):
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
)
make_stock_entry(item_code=self.item, to_warehouse=WAREHOUSE, qty=10, rate=100, company=COMPANY)
sr = create_stock_reconciliation(
item_code=self.item, warehouse=WAREHOUSE, qty=15, rate=100, company=COMPANY
)
_balances, debits, credits = self.get_gl_balances("Stock Reconciliation", sr.name)
self.assertEqual(debits[self.eats_account], 500)
self.assertEqual(credits[self.eats_contra_account], 500)
def test_landed_cost_voucher_books_pair(self):
from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import (
create_landed_cost_voucher,
)
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
pr = make_purchase_receipt(
company=COMPANY, warehouse=WAREHOUSE, item_code=self.item, qty=10, rate=100
)
_balances, debits, credits = self.get_gl_balances("Purchase Receipt", pr.name)
self.assertEqual(debits[self.eats_account], 0)
create_landed_cost_voucher("Purchase Receipt", pr.name, COMPANY, charges=200)
_balances, debits, credits = self.get_gl_balances("Purchase Receipt", pr.name)
self.assertEqual(debits[self.eats_account], 200)
self.assertEqual(credits[self.eats_contra_account], 200)
def test_no_entries_when_feature_disabled(self):
frappe.db.set_single_value("Accounts Settings", "book_stock_expense_gl_entries", 0)
se = make_stock_entry(item_code=self.item, to_warehouse=WAREHOUSE, qty=10, rate=100, company=COMPANY)
_balances, debits, credits = self.get_gl_balances("Stock Entry", se.name)
self.assertEqual(debits[self.eats_account], 0)
self.assertEqual(credits[self.eats_contra_account], 0)
def test_unconfigured_company_skips_booking(self):
frappe.db.set_value(
"Company",
COMPANY,
{
"expenses_added_to_stock_account": None,
"expenses_added_to_stock_contra_account": None,
},
)
se = make_stock_entry(item_code=self.item, to_warehouse=WAREHOUSE, qty=10, rate=100, company=COMPANY)
_balances, debits, credits = self.get_gl_balances("Stock Entry", se.name)
self.assertEqual(debits[self.eats_account], 0)
self.assertEqual(credits[self.eats_contra_account], 0)
def test_missing_contra_account_raises_when_feature_enabled(self):
frappe.db.set_value("Company", COMPANY, "expenses_added_to_stock_contra_account", None)
self.assertRaises(
frappe.ValidationError,
make_stock_entry,
item_code=self.item,
to_warehouse=WAREHOUSE,
qty=10,
rate=100,
company=COMPANY,
)