refactor(accounts): extract get_gl_dict and add_gl_entry into gl_entry_builder.py

Move the get_gl_dict/add_gl_entry logic from AccountsController/StockController
into free functions in accounts/services/gl_entry_builder.py with doc as first arg.
BaseGLComposer gains get_gl_dict and add_gl_entry methods that delegate to the free
functions — GL composers now call self.get_gl_dict/self.add_gl_entry directly
without going through the doc. AccountsController and StockController keep thin
shims for backward compatibility with unrefactored callers.

Also move update_gl_dict_with_regional_fields and update_gl_dict_with_app_based_fields
to gl_entry_builder.py, re-exporting them from accounts_controller.py to avoid a
circular import.
This commit is contained in:
Nabin Hait
2026-05-27 21:46:16 +05:30
parent 29261c5fc2
commit cba6a31497
14 changed files with 378 additions and 252 deletions

View File

@@ -14,7 +14,7 @@ class JournalEntryGLComposer(BaseGLComposer):
A Journal Entry already carries its ledger rows in the ``accounts`` child
table, so composing is a straight projection of those rows into GL dicts
via ``self.doc.get_gl_dict``. The transaction currency/rate are resolved
via ``self.get_gl_dict``. The transaction currency/rate are resolved
from the first foreign-currency row (mirroring the former build_gl_map).
"""
@@ -95,7 +95,7 @@ class JournalEntryGLComposer(BaseGLComposer):
frappe.flags.party_not_required = True
gl_map.append(
doc.get_gl_dict(
self.get_gl_dict(
row,
item=d,
)

View File

@@ -49,7 +49,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
party_account_type = frappe.db.get_value("Party Type", doc.party_type, "account_type")
party_gl_dict = doc.get_gl_dict(
party_gl_dict = self.get_gl_dict(
{
"account": doc.party_account,
"party_type": doc.party_type,
@@ -84,7 +84,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
gle.update(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.party_account,
"party_type": doc.party_type,
@@ -137,7 +137,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
gle = party_gl_dict.copy()
gle.update(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.party_account,
"party_type": doc.party_type,
@@ -167,7 +167,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
doc = self.doc
if doc.payment_type in ("Pay", "Internal Transfer"):
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.paid_from,
"account_currency": doc.paid_from_account_currency,
@@ -185,7 +185,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
)
if doc.payment_type in ("Receive", "Internal Transfer"):
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.paid_to,
"account_currency": doc.paid_to_account_currency,
@@ -222,7 +222,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
base_tax_amount = d.base_tax_amount
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": d.account_head,
"against": against,
@@ -249,7 +249,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
base_tax_amount = flt((tax_amount / exchange_rate), doc.precision("paid_amount"))
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": payment_account,
"against": against,
@@ -278,7 +278,7 @@ class PaymentEntryGLComposer(BaseGLComposer):
frappe.throw(_("Currency for {0} must be {1}").format(d.account, doc.company_currency))
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": d.account,
"account_currency": account_currency,

View File

@@ -94,7 +94,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
}
if remarks:
gl["remarks"] = remarks
gl_entries.append(doc.get_gl_dict(gl, doc.party_account_currency, item=doc))
gl_entries.append(self.get_gl_dict(gl, doc.party_account_currency, item=doc))
def make_item_gl_entries(self, gl_entries):
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import (
@@ -163,7 +163,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": _inv_dict["account"],
"against": _inv_dict_from_warehouse["account"],
@@ -184,7 +184,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": _inv_dict_from_warehouse["account"],
"against": _inv_dict["account"],
@@ -201,7 +201,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
if not doc.is_internal_transfer():
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": item.expense_account,
"against": doc.supplier,
@@ -219,7 +219,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
else:
if not doc.is_internal_transfer():
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": item.expense_account,
"against": doc.supplier,
@@ -244,7 +244,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
(item.item_code, item.name)
].items():
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": account,
"against": item.expense_account,
@@ -270,7 +270,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
_("Please set account in Warehouse {0}").format(doc.supplier_warehouse)
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": supplier_inventory_account,
"against": item.expense_account,
@@ -299,7 +299,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
if not doc.is_internal_transfer():
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": expense_account,
"against": doc.supplier,
@@ -330,7 +330,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
) * (exchange_rate_map[item.purchase_receipt] - doc.conversion_rate)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": expense_account,
"against": doc.supplier,
@@ -343,7 +343,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
)
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.get_company_default("exchange_gain_loss_account"),
"against": doc.supplier,
@@ -378,7 +378,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
if not negative_expense_booked_in_pr:
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.stock_received_but_not_billed,
"against": doc.supplier,
@@ -505,7 +505,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
stock_adjustment_amt = stock_amount - warehouse_debit_amount
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": cost_of_goods_sold_account,
"against": item.expense_account,
@@ -531,7 +531,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": cost_of_goods_sold_account,
"against": item.expense_account,
@@ -560,7 +560,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
account_currency = get_account_currency(tax.account_head)
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": tax.account_head,
"against": doc.supplier,
@@ -606,7 +606,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
amount_including_divisional_loss -= applicable_amount
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": tax.account_head,
"cost_center": tax.cost_center,
@@ -627,7 +627,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
for tax in doc.get("taxes"):
if valuation_tax.get(tax.name):
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": tax.account_head,
"cost_center": tax.cost_center,
@@ -648,7 +648,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
if doc.is_internal_transfer() and flt(doc.base_total_taxes_and_charges):
account_currency = get_account_currency(doc.unrealized_profit_loss_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.unrealized_profit_loss_account,
"against": doc.supplier,
@@ -691,7 +691,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
bank_account_currency = get_account_currency(doc.cash_bank_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.credit_to,
"party_type": "Supplier",
@@ -715,7 +715,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.cash_bank_account,
"against": doc.supplier,
@@ -737,7 +737,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
write_off_account_currency = get_account_currency(doc.write_off_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.credit_to,
"party_type": "Supplier",
@@ -760,7 +760,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
)
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.write_off_account,
"against": doc.supplier,
@@ -802,7 +802,7 @@ class PurchaseInvoiceGLComposer(BaseGLComposer):
round_off_account = round_off_for_opening
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": round_off_account,
"against": doc.supplier,

View File

@@ -110,7 +110,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
item_account_currency = get_account_currency(item.expense_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": dn_expense_account,
"against": item.expense_account,
@@ -123,7 +123,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
)
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": item.expense_account,
"against": dn_expense_account,
@@ -157,7 +157,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
# Did not use base_grand_total to book rounding loss gle
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.debit_to,
"party_type": "Customer",
@@ -191,7 +191,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
if flt(tax.base_tax_amount_after_discount_amount):
account_currency = get_account_currency(tax.account_head)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": tax.account_head,
"against": doc.customer,
@@ -216,7 +216,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
if doc.is_internal_transfer() and flt(doc.base_total_taxes_and_charges):
account_currency = get_account_currency(doc.unrealized_profit_loss_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.unrealized_profit_loss_account,
"against": doc.customer,
@@ -262,7 +262,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
account_currency = get_account_currency(income_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": income_account,
"against": doc.customer,
@@ -310,13 +310,13 @@ class SalesInvoiceGLComposer(BaseGLComposer):
for gle in fixed_asset_gl_entries:
gle["against"] = doc.customer
gl_entries.append(doc.get_gl_dict(gle, item=item))
gl_entries.append(self.get_gl_dict(gle, item=item))
def make_loyalty_point_redemption_gle(self, gl_entries):
doc = self.doc
if cint(doc.redeem_loyalty_points and doc.loyalty_points and not doc.is_consolidated):
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.debit_to,
"party_type": "Customer",
@@ -334,7 +334,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
)
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.loyalty_redemption_account,
"cost_center": doc.cost_center or doc.loyalty_redemption_cost_center,
@@ -365,7 +365,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
if payment_mode.base_amount:
# POS, make payment entries
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.debit_to,
"party_type": "Customer",
@@ -387,7 +387,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
payment_mode_account_currency = get_account_currency(payment_mode.account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": payment_mode.account,
"against": doc.customer,
@@ -415,7 +415,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
frappe.throw(_("Please set Account for Change Amount"), title=_("Mandatory Field"))
return [
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.debit_to,
"party_type": "Customer",
@@ -436,7 +436,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
doc.party_account_currency,
item=doc,
),
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.account_for_change_amount,
"against": doc.customer,
@@ -460,7 +460,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
default_cost_center = frappe.get_cached_value("Company", doc.company, "cost_center")
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.debit_to,
"party_type": "Customer",
@@ -485,7 +485,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
)
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": doc.write_off_account,
"against": doc.customer,
@@ -536,7 +536,7 @@ class SalesInvoiceGLComposer(BaseGLComposer):
round_off_account = round_off_for_opening
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": round_off_account,
"against": doc.customer,

View File

@@ -10,6 +10,8 @@ modelled as a class holding the document being composed. Subclasses implement
``compose`` to return the voucher-specific list of GL entries.
"""
from erpnext.accounts.services.gl_entry_builder import add_gl_entry, get_gl_dict
class BaseGLComposer:
def __init__(self, doc):
@@ -17,3 +19,41 @@ class BaseGLComposer:
def compose(self):
raise NotImplementedError
def get_gl_dict(self, args: dict, account_currency: str | None = None, item=None) -> dict:
return get_gl_dict(self.doc, args, account_currency, item)
def add_gl_entry(
self,
gl_entries: list,
account: str,
cost_center: str,
debit: float,
credit: float,
remarks: str,
against_account: str,
debit_in_account_currency: float | None = None,
credit_in_account_currency: float | None = None,
account_currency: str | None = None,
project: str | None = None,
voucher_detail_no: str | None = None,
item=None,
posting_date=None,
) -> None:
add_gl_entry(
self.doc,
gl_entries,
account,
cost_center,
debit,
credit,
remarks,
against_account,
debit_in_account_currency,
credit_in_account_currency,
account_currency,
project,
voucher_detail_no,
item,
posting_date,
)

View File

@@ -0,0 +1,223 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
"""Free functions for building GL entry dicts.
These are the implementations behind ``AccountsController.get_gl_dict`` and
``StockController.add_gl_entry``. Extracting them as free functions (with
``doc`` as the first argument) allows ``BaseGLComposer`` to delegate to them
directly — without requiring every composing doctype to inherit from
``AccountsController``.
``AccountsController`` and ``StockController`` keep thin shims that call these
functions so that existing code continues to work unchanged.
"""
import frappe
from frappe import _
from frappe.utils import flt, formatdate
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from erpnext.accounts.services.taxes import set_balance_in_account_currency
from erpnext.accounts.utils import get_account_currency, get_fiscal_years
from erpnext.utilities.regional import temporary_flag
def get_gl_dict(doc, args: dict, account_currency: str | None = None, item=None) -> dict:
"""Build a GL entry dict populated with doc-level fields."""
posting_date = args.get("posting_date") or doc.get("posting_date")
fiscal_years = get_fiscal_years(posting_date, company=doc.company)
if len(fiscal_years) > 1:
frappe.throw(
_("Multiple fiscal years exist for the date {0}. Please set company in Fiscal Year").format(
formatdate(posting_date)
)
)
else:
fiscal_year = fiscal_years[0][0]
gl_dict = frappe._dict(
{
"company": doc.company,
"posting_date": posting_date,
"fiscal_year": fiscal_year,
"voucher_type": doc.doctype,
"voucher_no": doc.name,
"remarks": doc.get("remarks") or doc.get("remark"),
"debit": 0,
"credit": 0,
"debit_in_account_currency": 0,
"credit_in_account_currency": 0,
"is_opening": doc.get("is_opening") or "No",
"party_type": None,
"party": None,
"project": doc.get("project"),
"post_net_value": args.get("post_net_value"),
"voucher_detail_no": args.get("voucher_detail_no"),
"voucher_subtype": get_voucher_subtype(doc),
}
)
with temporary_flag("company", doc.company):
update_gl_dict_with_regional_fields(doc, gl_dict)
update_gl_dict_with_app_based_fields(doc, gl_dict)
accounting_dimensions = get_accounting_dimensions()
dimension_dict = frappe._dict()
for dimension in accounting_dimensions:
dimension_dict[dimension] = doc.get(dimension)
if item and item.get(dimension):
dimension_dict[dimension] = item.get(dimension)
gl_dict.update(dimension_dict)
gl_dict.update(args)
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
if gl_dict.account and doc.doctype not in [
"Journal Entry",
"Period Closing Voucher",
"Payment Entry",
"Purchase Receipt",
"Purchase Invoice",
"Stock Entry",
]:
validate_account_currency(doc, gl_dict.account, account_currency)
if gl_dict.account and doc.doctype not in [
"Journal Entry",
"Period Closing Voucher",
"Payment Entry",
]:
set_balance_in_account_currency(
gl_dict,
account_currency,
args.get("transaction_exchange_rate") or doc.get("conversion_rate"),
doc.company_currency,
)
if doc.doctype not in ["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"]:
gl_dict.update(
{
"transaction_currency": doc.get("currency") or doc.company_currency,
"transaction_exchange_rate": args.get("transaction_exchange_rate")
or doc.get("conversion_rate", 1),
"debit_in_transaction_currency": get_value_in_transaction_currency(
doc, account_currency, gl_dict, "debit"
),
"credit_in_transaction_currency": get_value_in_transaction_currency(
doc, account_currency, gl_dict, "credit"
),
}
)
if not args.get("against_voucher_type") and doc.get("against_voucher_type"):
gl_dict.update({"against_voucher_type": doc.get("against_voucher_type")})
if not args.get("against_voucher") and doc.get("against_voucher"):
gl_dict.update({"against_voucher": doc.get("against_voucher")})
return gl_dict
def add_gl_entry(
doc,
gl_entries: list,
account: str,
cost_center: str,
debit: float,
credit: float,
remarks: str,
against_account: str,
debit_in_account_currency: float | None = None,
credit_in_account_currency: float | None = None,
account_currency: str | None = None,
project: str | None = None,
voucher_detail_no: str | None = None,
item=None,
posting_date=None,
) -> None:
"""Build a GL entry via get_gl_dict and append it to gl_entries."""
gl_entry = {
"account": account,
"cost_center": cost_center,
"debit": debit,
"credit": credit,
"against": against_account,
"remarks": remarks,
}
if voucher_detail_no:
gl_entry["voucher_detail_no"] = voucher_detail_no
if debit_in_account_currency:
gl_entry["debit_in_account_currency"] = debit_in_account_currency
if credit_in_account_currency:
gl_entry["credit_in_account_currency"] = credit_in_account_currency
if posting_date:
gl_entry["posting_date"] = posting_date
gl_entries.append(get_gl_dict(doc, gl_entry, account_currency, item=item))
def get_voucher_subtype(doc) -> str:
voucher_subtypes = {
"Journal Entry": "voucher_type",
"Payment Entry": "payment_type",
"Stock Entry": "stock_entry_type",
"Asset Capitalization": "entry_type",
}
for method_name in frappe.get_hooks("voucher_subtypes"):
voucher_subtype = frappe.get_attr(method_name)(doc)
if voucher_subtype:
return voucher_subtype
if doc.doctype in voucher_subtypes:
return doc.get(voucher_subtypes[doc.doctype])
elif doc.doctype == "Purchase Receipt" and doc.is_return:
return "Purchase Return"
elif doc.doctype == "Delivery Note" and doc.is_return:
return "Sales Return"
elif doc.doctype == "Sales Invoice" and doc.is_return:
return "Credit Note"
elif doc.doctype == "Sales Invoice" and doc.is_debit_note:
return "Debit Note"
elif doc.doctype == "Purchase Invoice" and doc.is_return:
return "Debit Note"
return doc.doctype
def get_value_in_transaction_currency(doc, account_currency: str, gl_dict: dict, field: str) -> float:
if account_currency == doc.get("currency"):
return gl_dict.get(field + "_in_account_currency")
return flt(gl_dict.get(field, 0) / doc.get("conversion_rate", 1))
def validate_account_currency(doc, account: str, account_currency: str | None = None) -> None:
valid_currency = [doc.company_currency]
if doc.get("currency") and doc.currency != doc.company_currency:
valid_currency.append(doc.currency)
if account_currency not in valid_currency:
frappe.throw(
_("Account {0} is invalid. Account Currency must be {1}").format(
account, (" " + _("or") + " ").join(valid_currency)
)
)
@erpnext.allow_regional
def update_gl_dict_with_regional_fields(doc, gl_dict):
pass
def update_gl_dict_with_app_based_fields(doc, gl_dict):
for method in frappe.get_hooks("update_gl_dict_with_app_based_fields", default=[]):
frappe.get_attr(method)(doc, gl_dict)

View File

@@ -65,7 +65,7 @@ class AssetCapitalizationGLComposer(BaseStockGLComposer):
target_against.add(account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": account,
"against": target_account,
@@ -108,7 +108,7 @@ class AssetCapitalizationGLComposer(BaseStockGLComposer):
for gle in fixed_asset_gl_entries:
gle["against"] = target_account
gl_entries.append(doc.get_gl_dict(gle, item=item))
gl_entries.append(self.get_gl_dict(gle, item=item))
target_against.add(gle["account"])
asset.db_set("disposal_date", doc.posting_date)
@@ -123,7 +123,7 @@ class AssetCapitalizationGLComposer(BaseStockGLComposer):
target_against.add(item_row.expense_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": item_row.expense_account,
"against": target_account,
@@ -147,7 +147,7 @@ class AssetCapitalizationGLComposer(BaseStockGLComposer):
total_value = flt(doc.total_value - composite_component_value, self.precision)
if total_value:
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": target_account,
"against": ", ".join(target_against),

View File

@@ -37,7 +37,7 @@ class AssetRepairGLComposer(BaseGLComposer):
for pi in doc.invoices:
debit_against_account.add(pi.expense_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": pi.expense_account,
"credit": pi.repair_cost,
@@ -55,7 +55,7 @@ class AssetRepairGLComposer(BaseGLComposer):
debit_against_account_str = ", ".join(debit_against_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": fixed_asset_account,
"debit": doc.repair_cost,
@@ -94,7 +94,7 @@ class AssetRepairGLComposer(BaseGLComposer):
for item in stock_entry_items:
if flt(item.amount) > 0:
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": item.expense_account or default_expense_account,
"credit": item.amount,
@@ -111,7 +111,7 @@ class AssetRepairGLComposer(BaseGLComposer):
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": fixed_asset_account,
"debit": item.amount,

View File

@@ -19,7 +19,6 @@ from frappe.utils import (
comma_and,
flt,
fmt_money,
formatdate,
get_last_day,
get_link_to_form,
getdate,
@@ -51,7 +50,6 @@ from erpnext.accounts.utils import (
create_gain_loss_journal,
get_account_currency,
get_currency_precision,
get_fiscal_years,
validate_fiscal_year,
)
from erpnext.accounts.utils import (
@@ -1293,140 +1291,19 @@ class AccountsController(TransactionBase):
)
def get_gl_dict(self, args, account_currency=None, item=None):
"""this method populates the common properties of a gl entry record"""
from erpnext.accounts.services.gl_entry_builder import get_gl_dict
posting_date = args.get("posting_date") or self.get("posting_date")
fiscal_years = get_fiscal_years(posting_date, company=self.company)
if len(fiscal_years) > 1:
frappe.throw(
_("Multiple fiscal years exist for the date {0}. Please set company in Fiscal Year").format(
formatdate(posting_date)
)
)
else:
fiscal_year = fiscal_years[0][0]
gl_dict = frappe._dict(
{
"company": self.company,
"posting_date": posting_date,
"fiscal_year": fiscal_year,
"voucher_type": self.doctype,
"voucher_no": self.name,
"remarks": self.get("remarks") or self.get("remark"),
"debit": 0,
"credit": 0,
"debit_in_account_currency": 0,
"credit_in_account_currency": 0,
"is_opening": self.get("is_opening") or "No",
"party_type": None,
"party": None,
"project": self.get("project"),
"post_net_value": args.get("post_net_value"),
"voucher_detail_no": args.get("voucher_detail_no"),
"voucher_subtype": self.get_voucher_subtype(),
}
)
with temporary_flag("company", self.company):
update_gl_dict_with_regional_fields(self, gl_dict)
update_gl_dict_with_app_based_fields(self, gl_dict)
accounting_dimensions = get_accounting_dimensions()
dimension_dict = frappe._dict()
for dimension in accounting_dimensions:
dimension_dict[dimension] = self.get(dimension)
if item and item.get(dimension):
dimension_dict[dimension] = item.get(dimension)
gl_dict.update(dimension_dict)
gl_dict.update(args)
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
if gl_dict.account and self.doctype not in [
"Journal Entry",
"Period Closing Voucher",
"Payment Entry",
"Purchase Receipt",
"Purchase Invoice",
"Stock Entry",
]:
self.validate_account_currency(gl_dict.account, account_currency)
if gl_dict.account and self.doctype not in [
"Journal Entry",
"Period Closing Voucher",
"Payment Entry",
]:
set_balance_in_account_currency(
gl_dict,
account_currency,
args.get("transaction_exchange_rate") or self.get("conversion_rate"),
self.company_currency,
)
# Update details in transaction currency
if self.doctype not in ["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"]:
gl_dict.update(
{
"transaction_currency": self.get("currency") or self.company_currency,
"transaction_exchange_rate": args.get("transaction_exchange_rate")
or self.get("conversion_rate", 1),
"debit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "debit"
),
"credit_in_transaction_currency": self.get_value_in_transaction_currency(
account_currency, gl_dict, "credit"
),
}
)
if not args.get("against_voucher_type") and self.get("against_voucher_type"):
gl_dict.update({"against_voucher_type": self.get("against_voucher_type")})
if not args.get("against_voucher") and self.get("against_voucher"):
gl_dict.update({"against_voucher": self.get("against_voucher")})
return gl_dict
return get_gl_dict(self, args, account_currency, item)
def get_voucher_subtype(self):
voucher_subtypes = {
"Journal Entry": "voucher_type",
"Payment Entry": "payment_type",
"Stock Entry": "stock_entry_type",
"Asset Capitalization": "entry_type",
}
from erpnext.accounts.services.gl_entry_builder import get_voucher_subtype
for method_name in frappe.get_hooks("voucher_subtypes"):
voucher_subtype = frappe.get_attr(method_name)(self)
if voucher_subtype:
return voucher_subtype
if self.doctype in voucher_subtypes:
return self.get(voucher_subtypes[self.doctype])
elif self.doctype == "Purchase Receipt" and self.is_return:
return "Purchase Return"
elif self.doctype == "Delivery Note" and self.is_return:
return "Sales Return"
elif self.doctype == "Sales Invoice" and self.is_return:
return "Credit Note"
elif self.doctype == "Sales Invoice" and self.is_debit_note:
return "Debit Note"
elif self.doctype == "Purchase Invoice" and self.is_return:
return "Debit Note"
return self.doctype
return get_voucher_subtype(self)
def get_value_in_transaction_currency(self, account_currency, gl_dict, field):
if account_currency == self.get("currency"):
return gl_dict.get(field + "_in_account_currency")
else:
return flt(gl_dict.get(field, 0) / self.get("conversion_rate", 1))
from erpnext.accounts.services.gl_entry_builder import get_value_in_transaction_currency
return get_value_in_transaction_currency(self, account_currency, gl_dict, field)
def validate_zero_qty_for_return_invoices_with_stock(self):
rows = []
@@ -1458,16 +1335,9 @@ class AccountsController(TransactionBase):
)
def validate_account_currency(self, account, account_currency=None):
valid_currency = [self.company_currency]
if self.get("currency") and self.currency != self.company_currency:
valid_currency.append(self.currency)
from erpnext.accounts.services.gl_entry_builder import validate_account_currency
if account_currency not in valid_currency:
frappe.throw(
_("Account {0} is invalid. Account Currency must be {1}").format(
account, (" " + _("or") + " ").join(valid_currency)
)
)
return validate_account_currency(self, account, account_currency)
def clear_unallocated_advances(self, childtype, parentfield):
self.set(parentfield, self.get(parentfield, {"allocated_amount": ["not in", [0, None, ""]]}))
@@ -3622,14 +3492,10 @@ def validate_einvoice_fields(doc):
pass
@erpnext.allow_regional
def update_gl_dict_with_regional_fields(doc, gl_dict):
pass
def update_gl_dict_with_app_based_fields(doc, gl_dict):
for method in frappe.get_hooks("update_gl_dict_with_app_based_fields", default=[]):
frappe.get_attr(method)(doc, gl_dict)
from erpnext.accounts.services.gl_entry_builder import (
update_gl_dict_with_app_based_fields,
update_gl_dict_with_regional_fields,
)
@frappe.whitelist()

View File

@@ -1661,28 +1661,25 @@ class StockController(AccountsController):
item=None,
posting_date=None,
):
gl_entry = {
"account": account,
"cost_center": cost_center,
"debit": debit,
"credit": credit,
"against": against_account,
"remarks": remarks,
}
from erpnext.accounts.services.gl_entry_builder import add_gl_entry
if voucher_detail_no:
gl_entry.update({"voucher_detail_no": voucher_detail_no})
if debit_in_account_currency:
gl_entry.update({"debit_in_account_currency": debit_in_account_currency})
if credit_in_account_currency:
gl_entry.update({"credit_in_account_currency": credit_in_account_currency})
if posting_date:
gl_entry.update({"posting_date": posting_date})
gl_entries.append(self.get_gl_dict(gl_entry, item=item))
add_gl_entry(
self,
gl_entries,
account,
cost_center,
debit,
credit,
remarks,
against_account,
debit_in_account_currency,
credit_in_account_currency,
account_currency,
project,
voucher_detail_no,
item,
posting_date,
)
def update_stock_reservation_entries(self):
def get_sre_list():

View File

@@ -58,7 +58,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
account_currency = get_account_currency(stock_asset_account_name)
if not stock_asset_account_name:
validate_account("Asset or warehouse account")
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=stock_asset_account_name,
cost_center=d.cost_center,
@@ -108,7 +108,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
if not account:
validate_account("Stock or Asset Received But Not Billed")
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=item.cost_center,
@@ -131,7 +131,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
exchange_rate_map[item.purchase_invoice] - doc.conversion_rate
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=item.cost_center,
@@ -144,7 +144,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
item=item,
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=doc.get_company_default("exchange_gain_loss_account"),
cost_center=d.cost_center,
@@ -173,7 +173,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
if not account:
validate_account("Landed Cost Account")
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=item.cost_center,
@@ -190,7 +190,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
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)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=stock_asset_rbnb,
cost_center=item.cost_center,
@@ -205,7 +205,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
def make_sub_contracting_gl_entries(item):
if flt(item.rm_supp_cost) and supplier_warehouse_account:
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=supplier_warehouse_account,
cost_center=item.cost_center,
@@ -252,7 +252,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
"Company", doc.company, "cost_center"
)
account_currency = get_account_currency(loss_account)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=loss_account,
cost_center=cost_center,
@@ -394,7 +394,7 @@ class PurchaseReceiptGLComposer(BaseStockGLComposer):
)
amount_including_divisional_loss -= applicable_amount
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=tax.cost_center,

View File

@@ -75,7 +75,7 @@ class StockEntryGLComposer(BaseStockGLComposer):
continue
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": account,
"against": d.expense_account,
@@ -89,7 +89,7 @@ class StockEntryGLComposer(BaseStockGLComposer):
)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": d.expense_account,
"against": account,
@@ -122,7 +122,7 @@ class StockEntryGLComposer(BaseStockGLComposer):
_inv_dict = doc.get_inventory_account_dict(item, inventory_account_map, "t_warehouse")
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": account,
"against": _inv_dict["account"],
@@ -140,7 +140,7 @@ class StockEntryGLComposer(BaseStockGLComposer):
account_currency = get_account_currency(item.expense_account)
gl_entries.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": item.expense_account,
"against": _inv_dict["account"],

View File

@@ -56,7 +56,7 @@ class BaseStockGLComposer(BaseGLComposer):
expense_account = item_row.expense_account
gl_list.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": _inv_dict["account"],
"against": expense_account,
@@ -72,7 +72,7 @@ class BaseStockGLComposer(BaseGLComposer):
)
gl_list.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": expense_account,
"against": _inv_dict["account"],
@@ -110,7 +110,7 @@ class BaseStockGLComposer(BaseGLComposer):
)
gl_list.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": expense_account,
"against": warehouse_asset_account,
@@ -126,7 +126,7 @@ class BaseStockGLComposer(BaseGLComposer):
)
gl_list.append(
doc.get_gl_dict(
self.get_gl_dict(
{
"account": warehouse_asset_account,
"against": expense_account,

View File

@@ -66,7 +66,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
remarks = doc.get("remarks") or _("Accounting Entry for Stock")
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=_inv_dict["account"],
cost_center=item.cost_center,
@@ -83,7 +83,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
item.service_cost_per_qty, item.precision("service_cost_per_qty")
) * flt(item.qty, item.precision("qty"))
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=item.expense_account,
cost_center=item.cost_center,
@@ -97,7 +97,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
)
service_account = item.service_expense_account or item.expense_account
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=service_account,
cost_center=item.cost_center,
@@ -116,7 +116,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
rm_item, inventory_account_map, "supplier_warehouse"
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=_inv_dict.get("account"),
cost_center=rm_item.cost_center or item.cost_center,
@@ -128,7 +128,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
project=item.project,
item=item,
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=rm_item.expense_account or item.expense_account,
cost_center=rm_item.cost_center or item.cost_center,
@@ -142,7 +142,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
)
if item.additional_cost_per_qty:
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=item.expense_account,
cost_center=doc.cost_center or doc.get_company_default("cost_center"),
@@ -158,7 +158,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
"stock_adjustment_account", ignore_validation=True
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=loss_account,
cost_center=item.cost_center,
@@ -170,7 +170,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
project=item.project,
item=item,
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=item.expense_account,
cost_center=item.cost_center,
@@ -195,7 +195,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
else flt(row.amount)
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=row.expense_account,
cost_center=doc.cost_center or doc.get_company_default("cost_center"),
@@ -234,7 +234,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
else flt(amount["amount"])
)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=account,
cost_center=item.cost_center,
@@ -250,7 +250,7 @@ class SubcontractingReceiptGLComposer(BaseStockGLComposer):
account_currency = get_account_currency(item.expense_account)
doc.add_gl_entry(
self.add_gl_entry(
gl_entries=gl_entries,
account=item.expense_account,
cost_center=item.cost_center,