Merge pull request #58548 from aerele/lcv-taxes-accounting-dimensions-v16

fix(stock): carry accounting dimensions from Landed Cost Voucher char…
This commit is contained in:
Sudharsanan Ashok
2026-09-08 17:59:20 +05:30
committed by GitHub
11 changed files with 597 additions and 135 deletions

View File

@@ -1024,6 +1024,10 @@ class PurchaseInvoice(BuyingController):
gl_entries.append(self.get_gl_dict(gl, self.party_account_currency, item=self))
def make_item_gl_entries(self, gl_entries):
from erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher import (
get_custom_dimension_overrides,
)
# item gl entries
stock_items = self.get_stock_items()
if self.update_stock and self.auto_accounting_for_stock:
@@ -1165,25 +1169,34 @@ class PurchaseInvoice(BuyingController):
# Amount added through landed-cost-voucher
if landed_cost_entries:
if (item.item_code, item.name) in landed_cost_entries:
for account, base_amount in landed_cost_entries[
(item.item_code, item.name)
].items():
gl_entries.append(
self.get_gl_dict(
{
"account": account,
"against": item.expense_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(base_amount["base_amount"]),
"credit_in_account_currency": flt(base_amount["amount"]),
"credit_in_transaction_currency": item.net_amount,
"project": item.project or self.project,
},
item=item,
)
for entry in landed_cost_entries.get((item.item_code, item.name), []):
if not (entry.amount or entry.base_amount):
continue
lcv_account_currency = get_account_currency(entry.expense_account)
credit_in_transaction_currency = (
flt(entry.amount)
if lcv_account_currency == self.currency
else flt(
entry.base_amount / self.conversion_rate, item.precision("net_amount")
)
)
gl_dict = self.get_gl_dict(
{
"account": entry.expense_account,
"against": item.expense_account,
"cost_center": entry.dimensions.cost_center or item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(entry.base_amount),
"credit_in_account_currency": flt(entry.amount),
"credit_in_transaction_currency": credit_in_transaction_currency,
"project": entry.dimensions.project or item.project or self.project,
},
item=item,
)
gl_dict.update(get_custom_dimension_overrides(entry))
gl_entries.append(gl_dict)
# sub-contracting warehouse
if flt(item.rm_supp_cost):

View File

@@ -1246,7 +1246,13 @@ class StockController(AccountsController):
if not landed_cost_vouchers:
return
from erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher import (
get_lcv_dimension_fields,
get_row_dimensions,
)
item_account_wise_cost = {}
dimension_fields = get_lcv_dimension_fields()
row_fieldname = "purchase_receipt_item"
if self.doctype == "Stock Entry":
@@ -1268,28 +1274,36 @@ class StockController(AccountsController):
for item in landed_cost_voucher_doc.items:
if item.receipt_document == self.name:
charges = item_account_wise_cost.setdefault((item.item_code, item.get(row_fieldname)), {})
for account in landed_cost_voucher_doc.taxes:
exchange_rate = account.exchange_rate or 1
item_account_wise_cost.setdefault((item.item_code, item.get(row_fieldname)), {})
item_account_wise_cost[(item.item_code, item.get(row_fieldname))].setdefault(
account.expense_account, {"amount": 0.0, "base_amount": 0.0}
dimensions = get_row_dimensions(account, item, dimension_fields)
group_key = (
account.expense_account,
tuple(dimensions.get(field) for field in dimension_fields),
)
item_row = item_account_wise_cost[(item.item_code, item.get(row_fieldname))][
account.expense_account
]
item_row = charges.get(group_key)
if item_row is None:
item_row = charges[group_key] = frappe._dict(
expense_account=account.expense_account,
amount=0.0,
base_amount=0.0,
dimensions=dimensions,
)
if total_item_cost > 0:
item_row["amount"] += account.amount * item.get(based_on_field) / total_item_cost
item_row.amount += account.amount * item.get(based_on_field) / total_item_cost
item_row["base_amount"] += (
item_row.base_amount += (
account.base_amount * item.get(based_on_field) / total_item_cost
)
else:
item_row["amount"] += item.applicable_charges / exchange_rate
item_row["base_amount"] += item.applicable_charges
item_row.amount += item.applicable_charges / exchange_rate
item_row.base_amount += item.applicable_charges
return item_account_wise_cost
return {key: list(charges.values()) for key, charges in item_account_wise_cost.items()}
def validate_inventory_dimension_mandatory(self):
# Mandatory inventory dimensions are enforced here (instead of via field-level `reqd`)
@@ -1976,6 +1990,7 @@ class StockController(AccountsController):
voucher_detail_no=None,
item=None,
posting_date=None,
dimensions=None,
):
gl_entry = {
"account": account,
@@ -2001,6 +2016,9 @@ class StockController(AccountsController):
if posting_date:
gl_entry.update({"posting_date": posting_date})
if dimensions:
gl_entry.update(dimensions)
gl_entries.append(self.get_gl_dict(gl_entry, item=item))
def update_stock_reservation_entries(self):

View File

@@ -559,6 +559,7 @@ accounting_dimension_doctypes = [
"Purchase Taxes and Charges",
"Shipping Rule",
"Landed Cost Item",
"Landed Cost Taxes and Charges",
"Asset Value Adjustment",
"Asset Repair",
"Asset Capitalization",

View File

@@ -490,6 +490,7 @@ 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.backfill_pick_list_transferred_qty
erpnext.patches.v16_0.create_accounting_dimensions_in_landed_cost_taxes_and_charges
erpnext.patches.v16_0.access_control_for_project_users
erpnext.patches.v16_0.enable_book_stock_expense_gl_entries
erpnext.patches.v16_0.rename_ar_ap_ageing_filter

View File

@@ -0,0 +1,11 @@
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_dimensions,
make_dimension_in_accounting_doctypes,
)
def execute():
dimensions_and_defaults = get_dimensions()
if dimensions_and_defaults:
for dimension in dimensions_and_defaults[0]:
make_dimension_in_accounting_doctypes(dimension, ["Landed Cost Taxes and Charges"])

View File

@@ -17,7 +17,11 @@
"has_operating_cost",
"operation_id",
"qty",
"operating_component"
"operating_component",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project"
],
"fields": [
{
@@ -107,13 +111,34 @@
"label": "Operating Component",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2026-05-19 12:21:07.953801",
"modified": "2026-08-04 10:00:00.000000",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Taxes and Charges",

View File

@@ -88,6 +88,7 @@ class LandedCostVoucher(Document):
self.set_applicable_charges_on_item()
self.set_total_vendor_invoices_cost()
self.validate_mandatory_dimensions()
def set_total_vendor_invoices_cost(self):
self.total_vendor_invoices_cost = 0.0
@@ -196,6 +197,92 @@ class LandedCostVoucher(Document):
exc=IncorrectCompanyValidationError,
)
def validate_mandatory_dimensions(self):
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_checks_for_pl_and_bs_accounts,
)
from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import (
get_dimension_filter_map,
)
if not is_perpetual_inventory_enabled(self.company):
return
company_checks = [
check
for check in get_checks_for_pl_and_bs_accounts()
if check.company == self.company and (check.mandatory_for_pl or check.mandatory_for_bs)
]
dimension_filter_map = get_dimension_filter_map()
if not company_checks and not dimension_filter_map:
return
labels = {d.fieldname: d.label for d in get_accounting_dimensions(as_list=False)}
receipts = {}
for tax in self.get("taxes"):
if not tax.expense_account:
continue
report_type = frappe.get_cached_value("Account", tax.expense_account, "report_type")
mandatory = {}
for check in company_checks:
is_mandatory = (
check.mandatory_for_pl if report_type == "Profit and Loss" else check.mandatory_for_bs
)
if is_mandatory:
mandatory[check.fieldname] = check.label
for (fieldname, account), dimension_filter in dimension_filter_map.items():
if account == tax.expense_account and dimension_filter.get("is_mandatory"):
mandatory.setdefault(fieldname, labels.get(fieldname) or frappe.unscrub(fieldname))
for fieldname, label in mandatory.items():
if tax.get(fieldname):
continue
for item in self.get("items"):
if self.get_receipt_dimension(receipts, item, fieldname):
continue
frappe.throw(
_(
"Row {0}: Accounting Dimension {1} is mandatory for account {2}."
" Set it on this Taxes and Charges row, or on Item Row {3} ({4})."
).format(
tax.idx,
frappe.bold(label),
frappe.bold(tax.expense_account),
item.idx,
frappe.bold(item.item_code),
),
title=_("Missing Accounting Dimension"),
)
def get_receipt_dimension(self, receipts, item, fieldname):
if item.get(fieldname):
return item.get(fieldname)
key = (item.receipt_document_type, item.receipt_document)
if key not in receipts:
receipts[key] = frappe.get_doc(*key) if item.receipt_document else None
receipt = receipts[key]
if not receipt:
return None
row_fieldname = "stock_entry_item" if receipt.doctype == "Stock Entry" else "purchase_receipt_item"
receipt_row_name = item.get(row_fieldname)
for row in receipt.get("items") or []:
if row.name == receipt_row_name and row.get(fieldname):
return row.get(fieldname)
return receipt.get(fieldname)
def set_total_taxes_and_charges(self):
self.total_taxes_and_charges = sum(flt(d.base_amount) for d in self.get("taxes"))
@@ -519,3 +606,25 @@ def get_vendor_invoice_query(filters):
query = query.where(doctype.name == filters.get("name"))
return query
def get_lcv_dimension_fields():
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
return ["cost_center", "project", *get_accounting_dimensions()]
def get_row_dimensions(tax_row, lcv_item, dimension_fields):
return frappe._dict(
{field: (tax_row.get(field) or lcv_item.get(field) or None) for field in dimension_fields}
)
def get_custom_dimension_overrides(entry):
return {
dimension: value
for dimension, value in (entry.dimensions or {}).items()
if value and dimension not in ("cost_center", "project")
}

View File

@@ -1408,3 +1408,265 @@ def distribute_landed_cost_on_items(lcv):
for item in lcv.get("items"):
item.applicable_charges = flt(item.get(based_on)) * flt(lcv.total_taxes_and_charges) / flt(total)
item.applicable_charges = flt(item.applicable_charges, lcv.precision("applicable_charges", item))
def ensure_dimension_fields_on_lcv_charges(dimensions):
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
make_dimension_in_accounting_doctypes,
)
created = False
for name in dimensions:
dimension = frappe.get_doc("Accounting Dimension", name)
if frappe.db.exists(
"Custom Field", {"dt": "Landed Cost Taxes and Charges", "fieldname": dimension.fieldname}
):
continue
make_dimension_in_accounting_doctypes(dimension, ["Landed Cost Taxes and Charges"])
created = True
if created:
frappe.clear_cache(doctype="Landed Cost Taxes and Charges")
def create_branch(branch):
if not frappe.db.exists("Branch", branch):
frappe.get_doc({"doctype": "Branch", "branch": branch}).insert()
return branch
class TestLandedCostVoucherAccountingDimensions(ERPNextTestSuite):
def setUp(self):
self.company = "_Test Company with perpetual inventory"
self.warehouse = "Stores - TCP1"
self.expense_account = get_expense_account(self.company)
ensure_dimension_fields_on_lcv_charges(["Branch"])
self.branch_a = create_branch("_Test LCV Branch A")
self.branch_b = create_branch("_Test LCV Branch B")
def make_lcv(self, pr, charges, do_not_submit=False):
lcv = frappe.new_doc("Landed Cost Voucher")
lcv.company = self.company
lcv.distribute_charges_based_on = "Amount"
lcv.set(
"purchase_receipts",
[
{
"receipt_document_type": "Purchase Receipt",
"receipt_document": pr.name,
"supplier": pr.supplier,
"posting_date": pr.posting_date,
"grand_total": pr.base_grand_total,
}
],
)
for idx, charge in enumerate(charges):
lcv.append(
"taxes",
{
"description": f"_Test Charge {idx + 1}",
"expense_account": charge.pop("expense_account", self.expense_account),
**charge,
},
)
lcv.insert()
if not do_not_submit:
lcv.submit()
return lcv
def get_lcv_gl_entries(self, pr, account=None):
return frappe.get_all(
"GL Entry",
filters={
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"is_cancelled": 0,
**({"account": account} if account else {}),
},
fields=["account", "debit", "credit", "cost_center", "project", "branch"],
order_by="credit desc",
)
def make_dimension_mandatory(self, name, mandatory_for_pl=0, mandatory_for_bs=0):
dimension = frappe.get_doc("Accounting Dimension", name)
row = next((d for d in dimension.dimension_defaults if d.company == self.company), None)
if row:
previous = (row.mandatory_for_pl, row.mandatory_for_bs)
self.addCleanup(self.restore_dimension_default, name, previous)
else:
row = dimension.append(
"dimension_defaults",
{"company": self.company, "reference_document": dimension.document_type},
)
self.addCleanup(self.remove_dimension_default, name)
row.mandatory_for_pl = mandatory_for_pl
row.mandatory_for_bs = mandatory_for_bs
dimension.save()
def restore_dimension_default(self, name, previous):
dimension = frappe.get_doc("Accounting Dimension", name)
for row in dimension.dimension_defaults:
if row.company == self.company:
row.mandatory_for_pl, row.mandatory_for_bs = previous
dimension.save()
def remove_dimension_default(self, name):
dimension = frappe.get_doc("Accounting Dimension", name)
dimension.set(
"dimension_defaults",
[d for d in dimension.dimension_defaults if d.company != self.company],
)
dimension.save()
def test_charge_row_dimension_reaches_gl_entry(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
self.make_lcv(pr, [{"amount": 100, "branch": self.branch_a}])
charge_entries = self.get_lcv_gl_entries(pr, self.expense_account)
self.assertEqual(len(charge_entries), 1)
self.assertEqual(charge_entries[0].credit, 100.0)
self.assertEqual(charge_entries[0].branch, self.branch_a)
stock_account = get_inventory_account(self.company, self.warehouse)
self.assertFalse(self.get_lcv_gl_entries(pr, stock_account)[0].branch)
def test_charge_row_cost_center_and_project_override_receipt_item(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
create_cost_center(
cost_center_name="_Test LCV Cost Center",
company=self.company,
parent_cost_center=f"{self.company} - TCP1",
)
cost_center = "_Test LCV Cost Center - TCP1"
if not frappe.db.exists("Project", {"project_name": "_Test LCV Project"}):
frappe.get_doc(
{"doctype": "Project", "project_name": "_Test LCV Project", "company": self.company}
).insert()
project = frappe.db.get_value("Project", {"project_name": "_Test LCV Project"})
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
item_cost_center = pr.items[0].cost_center
self.make_lcv(pr, [{"amount": 100, "cost_center": cost_center, "project": project}])
charge_entries = self.get_lcv_gl_entries(pr, self.expense_account)
self.assertEqual(len(charge_entries), 1)
self.assertEqual(charge_entries[0].cost_center, cost_center)
self.assertEqual(charge_entries[0].project, project)
stock_account = get_inventory_account(self.company, self.warehouse)
self.assertEqual(self.get_lcv_gl_entries(pr, stock_account)[0].cost_center, item_cost_center)
def test_blank_charge_row_falls_back_to_receipt_item(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
self.make_lcv(pr, [{"amount": 100}])
charge_entries = self.get_lcv_gl_entries(pr, self.expense_account)
self.assertEqual(len(charge_entries), 1)
self.assertEqual(charge_entries[0].cost_center, pr.items[0].cost_center)
self.assertFalse(charge_entries[0].branch)
def test_charge_rows_on_same_account_with_different_dimensions_stay_separate(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
self.make_lcv(
pr,
[
{"amount": 60, "branch": self.branch_a},
{"amount": 40, "branch": self.branch_b},
],
)
charge_entries = self.get_lcv_gl_entries(pr, self.expense_account)
self.assertEqual(len(charge_entries), 2)
self.assertEqual(
{(e.branch, e.credit) for e in charge_entries},
{(self.branch_a, 60.0), (self.branch_b, 40.0)},
)
self.assertEqual(sum(e.credit for e in charge_entries), 100.0)
def test_two_vouchers_on_same_account_with_different_dimensions_stay_separate(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
self.make_lcv(pr, [{"amount": 60, "branch": self.branch_a}])
self.make_lcv(pr, [{"amount": 40, "branch": self.branch_b}])
charge_entries = self.get_lcv_gl_entries(pr, self.expense_account)
self.assertEqual(len(charge_entries), 2)
self.assertEqual(
{(e.branch, e.credit) for e in charge_entries},
{(self.branch_a, 60.0), (self.branch_b, 40.0)},
)
def test_mandatory_pl_dimension_is_satisfied_by_charge_row(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
self.make_dimension_mandatory("Branch", mandatory_for_pl=1)
self.make_lcv(pr, [{"amount": 100, "branch": self.branch_a}])
charge_entries = self.get_lcv_gl_entries(pr, self.expense_account)
self.assertEqual(len(charge_entries), 1)
self.assertEqual(charge_entries[0].branch, self.branch_a)
def test_missing_mandatory_dimension_is_reported_on_the_voucher(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
self.make_dimension_mandatory("Branch", mandatory_for_pl=1)
with self.assertRaises(frappe.ValidationError) as raised:
self.make_lcv(pr, [{"amount": 100}])
message = str(raised.exception)
self.assertIn("Branch", message)
self.assertIn(self.expense_account, message)
def test_dimensions_survive_reposting(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
self.make_lcv(
pr,
[
{"amount": 60, "branch": self.branch_a},
{"amount": 40, "branch": self.branch_b},
],
)
before = {(e.branch, e.credit) for e in self.get_lcv_gl_entries(pr, self.expense_account)}
items, warehouses = pr.get_items_and_warehouses()
update_gl_entries_after(pr.posting_date, pr.posting_time, warehouses, items, company=pr.company)
after = {(e.branch, e.credit) for e in self.get_lcv_gl_entries(pr, self.expense_account)}
self.assertEqual(before, after)
def test_cancelling_the_voucher_nets_each_dimension_to_zero(self):
pr = make_purchase_receipt(company=self.company, warehouse=self.warehouse)
lcv = self.make_lcv(
pr,
[
{"amount": 60, "branch": self.branch_a},
{"amount": 40, "branch": self.branch_b},
],
)
lcv.reload()
lcv.cancel()
balances = {}
for entry in frappe.get_all(
"GL Entry",
filters={"voucher_no": pr.name, "account": self.expense_account},
fields=["branch", "debit", "credit"],
):
balances[entry.branch] = balances.get(entry.branch, 0.0) + entry.debit - entry.credit
for branch, balance in balances.items():
self.assertEqual(flt(balance, 2), 0.0, msg=f"branch {branch} does not net to zero")

View File

@@ -482,6 +482,9 @@ class PurchaseReceipt(BuyingController):
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import (
get_purchase_document_details,
)
from erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher import (
get_custom_dimension_overrides,
)
provisional_accounting_for_non_stock_items = cint(
frappe.db.get_value("Company", self.company, "enable_provisional_accounting_for_non_stock_items")
@@ -608,32 +611,38 @@ class PurchaseReceipt(BuyingController):
def make_landed_cost_gl_entries(item):
# Amount added through landed-cost-voucher
if item.landed_cost_voucher_amount and landed_cost_entries:
if (item.item_code, item.name) in landed_cost_entries:
for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
account_currency = get_account_currency(account)
credit_amount = (
flt(amount["base_amount"])
if (amount["base_amount"] or account_currency != self.company_currency)
else flt(amount["amount"])
)
if not (item.landed_cost_voucher_amount and landed_cost_entries):
return
if not account:
validate_account("Landed Cost Account")
for entry in landed_cost_entries.get((item.item_code, item.name), []):
if not (entry.amount or entry.base_amount):
continue
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=item.cost_center,
debit=0.0,
credit=credit_amount,
remarks=remarks,
against_account=stock_asset_account_name,
credit_in_account_currency=flt(amount["amount"]),
account_currency=account_currency,
project=item.project,
item=item,
)
account = entry.expense_account
if not account:
validate_account("Landed Cost Account")
account_currency = get_account_currency(account)
credit_amount = (
flt(entry.base_amount)
if (entry.base_amount or account_currency != self.company_currency)
else flt(entry.amount)
)
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=entry.dimensions.cost_center or item.cost_center,
debit=0.0,
credit=credit_amount,
remarks=remarks,
against_account=stock_asset_account_name,
credit_in_account_currency=flt(entry.amount),
account_currency=account_currency,
project=entry.dimensions.project or item.project,
item=item,
dimensions=get_custom_dimension_overrides(entry),
)
def make_expenses_added_to_stock_entries(item):
if not self.book_stock_expense_enabled():

View File

@@ -2440,6 +2440,10 @@ class StockEntry(StockController, SubcontractingInwardController):
return process_gl_map(gl_entries, from_repost=frappe.flags.through_repost_item_valuation)
def set_gl_entries_for_landed_cost_voucher(self, gl_entries, inventory_account_map):
from erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher import (
get_custom_dimension_overrides,
)
landed_cost_entries = self.get_item_account_wise_lcv_entries()
if not landed_cost_entries:
return
@@ -2448,52 +2452,53 @@ class StockEntry(StockController, SubcontractingInwardController):
if item.s_warehouse:
continue
if (item.item_code, item.name) in landed_cost_entries:
for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
account_currency = get_account_currency(account)
credit_amount = (
flt(amount["base_amount"])
if (amount["base_amount"] or account_currency != self.company_currency)
else flt(amount["amount"])
)
for entry in landed_cost_entries.get((item.item_code, item.name), []):
if not (entry.amount or entry.base_amount):
continue
_inv_dict = self.get_inventory_account_dict(item, inventory_account_map, "t_warehouse")
gl_entries.append(
self.get_gl_dict(
{
"account": account,
"against": _inv_dict["account"],
"cost_center": item.cost_center,
"debit": 0.0,
"credit": credit_amount,
"remarks": _("Accounting Entry for LCV in Stock Entry {0}").format(self.name),
"credit_in_account_currency": flt(amount["amount"]),
"account_currency": account_currency,
"project": item.project,
},
item=item,
)
)
account_currency = get_account_currency(entry.expense_account)
credit_amount = (
flt(entry.base_amount)
if (entry.base_amount or account_currency != self.company_currency)
else flt(entry.amount)
)
account_currency = get_account_currency(item.expense_account)
_inv_dict = self.get_inventory_account_dict(item, inventory_account_map, "t_warehouse")
gl_dict = self.get_gl_dict(
{
"account": entry.expense_account,
"against": _inv_dict["account"],
"cost_center": entry.dimensions.cost_center or item.cost_center,
"debit": 0.0,
"credit": credit_amount,
"remarks": _("Accounting Entry for LCV in Stock Entry {0}").format(self.name),
"credit_in_account_currency": flt(entry.amount),
"account_currency": account_currency,
"project": entry.dimensions.project or item.project,
},
item=item,
)
gl_dict.update(get_custom_dimension_overrides(entry))
gl_entries.append(gl_dict)
# credit amount in negative to knock off the debit entry
gl_entries.append(
self.get_gl_dict(
{
"account": item.expense_account,
"against": _inv_dict["account"],
"cost_center": item.cost_center,
"debit": 0.0,
"credit": credit_amount * -1,
"remarks": _("Accounting Entry for LCV in Stock Entry {0}").format(self.name),
"debit_in_account_currency": flt(amount["amount"]),
"account_currency": account_currency,
"project": item.project,
},
item=item,
)
account_currency = get_account_currency(item.expense_account)
gl_entries.append(
self.get_gl_dict(
{
"account": item.expense_account,
"against": _inv_dict["account"],
"cost_center": item.cost_center,
"debit": 0.0,
"credit": credit_amount * -1,
"remarks": _("Accounting Entry for LCV in Stock Entry {0}").format(self.name),
"debit_in_account_currency": flt(entry.amount),
"account_currency": account_currency,
"project": item.project,
},
item=item,
)
)
def update_work_order(self):
def _validate_work_order(pro_doc):

View File

@@ -984,55 +984,63 @@ class SubcontractingReceipt(SubcontractingController):
)
def make_item_gl_entries_for_lcv(self, gl_entries, inventory_account_map):
from erpnext.stock.doctype.landed_cost_voucher.landed_cost_voucher import (
get_custom_dimension_overrides,
)
landed_cost_entries = self.get_item_account_wise_lcv_entries()
if not landed_cost_entries:
return
for item in self.items:
if item.landed_cost_voucher_amount and landed_cost_entries:
item_entries = landed_cost_entries.get((item.item_code, item.name), [])
if item.landed_cost_voucher_amount and item_entries:
remarks = _("Accounting Entry for Landed Cost Voucher for SCR {0}").format(self.name)
if (item.item_code, item.name) in landed_cost_entries:
_inv_dict = self.get_inventory_account_dict(item, inventory_account_map)
_inv_dict = self.get_inventory_account_dict(item, inventory_account_map)
for account, amount in landed_cost_entries[(item.item_code, item.name)].items():
account_currency = get_account_currency(account)
credit_amount = (
flt(amount["base_amount"])
if (amount["base_amount"] or account_currency != self.company_currency)
else flt(amount["amount"])
)
for entry in item_entries:
if not (entry.amount or entry.base_amount):
continue
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=item.cost_center,
debit=0.0,
credit=credit_amount,
remarks=remarks,
against_account=_inv_dict["account"],
credit_in_account_currency=flt(amount["amount"]),
account_currency=account_currency,
project=item.project,
item=item,
)
account_currency = get_account_currency(entry.expense_account)
credit_amount = (
flt(entry.base_amount)
if (entry.base_amount or account_currency != self.company_currency)
else flt(entry.amount)
)
account_currency = get_account_currency(item.expense_account)
self.add_gl_entry(
gl_entries=gl_entries,
account=entry.expense_account,
cost_center=entry.dimensions.cost_center or item.cost_center,
debit=0.0,
credit=credit_amount,
remarks=remarks,
against_account=_inv_dict["account"],
credit_in_account_currency=flt(entry.amount),
account_currency=account_currency,
project=entry.dimensions.project or item.project,
item=item,
dimensions=get_custom_dimension_overrides(entry),
)
# credit amount in negative to knock off the debit entry
self.add_gl_entry(
gl_entries=gl_entries,
account=item.expense_account,
cost_center=item.cost_center,
debit=0.0,
credit=credit_amount * -1,
remarks=remarks,
against_account=_inv_dict["account"],
debit_in_account_currency=flt(amount["amount"]),
account_currency=account_currency,
project=item.project,
item=item,
)
account_currency = get_account_currency(item.expense_account)
self.add_gl_entry(
gl_entries=gl_entries,
account=item.expense_account,
cost_center=item.cost_center,
debit=0.0,
credit=credit_amount * -1,
remarks=remarks,
against_account=_inv_dict["account"],
debit_in_account_currency=flt(entry.amount),
account_currency=account_currency,
project=item.project,
item=item,
)
def auto_create_purchase_receipt(self):
if frappe.db.get_single_value("Buying Settings", "auto_create_purchase_receipt"):