fix: GL entry fixes and validation for intercompany account

This commit is contained in:
Deepesh Garg
2020-11-30 20:52:21 +05:30
parent 65fb0729d2
commit 57d79a8149
3 changed files with 178 additions and 168 deletions

View File

@@ -375,12 +375,17 @@ class PurchaseInvoice(BuyingController):
def set_inter_company_account(self): def set_inter_company_account(self):
""" """
Set intercompany account for inter warehouse transactions Set intercompany account for inter warehouse transactions
This account will be used in case billing company and internal supplier's This account will be used in case billing company and internal customer's
representation company is same representation company is same
""" """
if self.is_internal_transfer() and not self.inter_company_account: if self.is_internal_transfer() and not self.inter_company_account:
self.inter_company_account = frappe.get_cached_value('Company', self.company, 'default_inter_company_account') inter_company_account = frappe.get_cached_value('Company', self.company, 'default_inter_company_account')
if not inter_company_account:
msg = _("Please select inter-company account or add default inter-company account for company {0}").format(
frappe.bold(self.company))
frappe.throw(msg)
def is_internal_transfer(self): def is_internal_transfer(self):
""" """
@@ -474,7 +479,6 @@ class PurchaseInvoice(BuyingController):
self.make_payment_gl_entries(gl_entries) self.make_payment_gl_entries(gl_entries)
self.make_write_off_gl_entry(gl_entries) self.make_write_off_gl_entry(gl_entries)
self.make_gle_for_rounding_adjustment(gl_entries) self.make_gle_for_rounding_adjustment(gl_entries)
return gl_entries return gl_entries
def check_asset_cwip_enabled(self): def check_asset_cwip_enabled(self):
@@ -514,55 +518,55 @@ class PurchaseInvoice(BuyingController):
def make_item_gl_entries(self, gl_entries): def make_item_gl_entries(self, gl_entries):
# item gl entries # item gl entries
if not self.is_internal_transfer(): stock_items = self.get_stock_items()
stock_items = self.get_stock_items() if self.update_stock and self.auto_accounting_for_stock:
if self.update_stock and self.auto_accounting_for_stock: warehouse_account = get_warehouse_account_map(self.company)
warehouse_account = get_warehouse_account_map(self.company)
landed_cost_entries = get_item_account_wise_additional_cost(self.name) landed_cost_entries = get_item_account_wise_additional_cost(self.name)
voucher_wise_stock_value = {} voucher_wise_stock_value = {}
if self.update_stock: if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry', for d in frappe.get_all('Stock Ledger Entry',
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}): fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference) voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
valuation_tax_accounts = [d.account_head for d in self.get("taxes") valuation_tax_accounts = [d.account_head for d in self.get("taxes")
if d.category in ('Valuation', 'Total and Valuation') if d.category in ('Valuation', 'Total and Valuation')
and flt(d.base_tax_amount_after_discount_amount)] and flt(d.base_tax_amount_after_discount_amount)]
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account) account_currency = get_account_currency(item.expense_account)
if item.item_code: if item.item_code:
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items: if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
# warehouse account # warehouse account
warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries, warehouse_debit_amount = self.make_stock_adjustment_entry(gl_entries,
item, voucher_wise_stock_value, account_currency) item, voucher_wise_stock_value, account_currency)
if item.from_warehouse: if item.from_warehouse:
gl_entries.append(self.get_gl_dict({
"account": warehouse_account[item.warehouse]['account'],
"against": warehouse_account[item.from_warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": warehouse_debit_amount,
}, warehouse_account[item.warehouse]["account_currency"], item=item))
gl_entries.append(self.get_gl_dict({ # Intentionally passed negative debit amount to avoid incorrect GL Entry validation
"account": warehouse_account[item.warehouse]['account'], gl_entries.append(self.get_gl_dict({
"against": warehouse_account[item.from_warehouse]["account"], "account": warehouse_account[item.from_warehouse]['account'],
"cost_center": item.cost_center, "against": warehouse_account[item.warehouse]["account"],
"project": item.project or self.project, "cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "project": item.project or self.project,
"debit": warehouse_debit_amount, "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
}, warehouse_account[item.warehouse]["account_currency"], item=item)) "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
}, warehouse_account[item.from_warehouse]["account_currency"], item=item))
# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
gl_entries.append(self.get_gl_dict({
"account": warehouse_account[item.from_warehouse]['account'],
"against": warehouse_account[item.warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")),
}, warehouse_account[item.from_warehouse]["account_currency"], item=item))
# Do not book expense for transfer within same company transfer
if not self.is_internal_transfer():
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.expense_account, "account": item.expense_account,
@@ -574,128 +578,128 @@ class PurchaseInvoice(BuyingController):
}, account_currency, item=item) }, account_currency, item=item)
) )
else: else:
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.expense_account, "account": item.expense_account,
"against": self.supplier, "against": self.supplier,
"debit": warehouse_debit_amount, "debit": warehouse_debit_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project or self.project "project": item.project or self.project
}, account_currency, item=item) }, account_currency, item=item)
) )
# Amount added through landed-cost-voucher # Amount added through landed-cost-voucher
if landed_cost_entries: if landed_cost_entries:
for account, amount in iteritems(landed_cost_entries[(item.item_code, item.name)]): for account, amount in iteritems(landed_cost_entries[(item.item_code, item.name)]):
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(amount),
"project": item.project or self.project
}, item=item))
# sub-contracting warehouse
if flt(item.rm_supp_cost):
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
if not supplier_warehouse_account:
frappe.throw(_("Please set account in Warehouse {0}")
.format(self.supplier_warehouse))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": supplier_warehouse_account, "account": account,
"against": item.expense_account, "against": item.expense_account,
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.rm_supp_cost) "credit": flt(amount),
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) "project": item.project or self.project
}, item=item))
elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)): # sub-contracting warehouse
expense_account = (item.expense_account if flt(item.rm_supp_cost):
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
if not supplier_warehouse_account:
frappe.throw(_("Please set account in Warehouse {0}")
.format(self.supplier_warehouse))
gl_entries.append(self.get_gl_dict({
"account": supplier_warehouse_account,
"against": item.expense_account,
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.rm_supp_cost)
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
if not item.is_fixed_asset: elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)):
amount = flt(item.base_net_amount, item.precision("base_net_amount")) expense_account = (item.expense_account
else: if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items')) if not item.is_fixed_asset:
amount = flt(item.base_net_amount, item.precision("base_net_amount"))
else:
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
if auto_accounting_for_non_stock_items: auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items'))
service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
if item.purchase_receipt: if auto_accounting_for_non_stock_items:
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0,
'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail,
'account':service_received_but_not_billed_account}, ['name'])
if expense_booked_in_pr: if item.purchase_receipt:
expense_account = service_received_but_not_billed_account # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0,
'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail,
'account':service_received_but_not_billed_account}, ['name'])
if expense_booked_in_pr:
expense_account = service_received_but_not_billed_account
gl_entries.append(self.get_gl_dict({
"account": expense_account,
"against": self.supplier,
"debit": amount,
"cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item))
# If asset is bought through this document and not linked to PR
if self.update_stock and item.landed_cost_voucher_amount:
expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
# Amount added through landed-cost-voucher
gl_entries.append(self.get_gl_dict({
"account": expenses_included_in_asset_valuation,
"against": expense_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.landed_cost_voucher_amount),
"project": item.project or self.project
}, item=item))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": expense_account, "account": expense_account,
"against": self.supplier, "against": expenses_included_in_asset_valuation,
"debit": amount, "cost_center": item.cost_center,
"cost_center": item.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"project": item.project or self.project "debit": flt(item.landed_cost_voucher_amount),
}, account_currency, item=item)) "project": item.project or self.project
}, item=item))
# If asset is bought through this document and not linked to PR # update gross amount of asset bought through this document
if self.update_stock and item.landed_cost_voucher_amount: assets = frappe.db.get_all('Asset',
expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation") filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
# Amount added through landed-cost-voucher )
gl_entries.append(self.get_gl_dict({ for asset in assets:
"account": expenses_included_in_asset_valuation, frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
"against": expense_account, frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.landed_cost_voucher_amount),
"project": item.project or self.project
}, item=item))
gl_entries.append(self.get_gl_dict({ if self.auto_accounting_for_stock and self.is_opening == "No" and \
"account": expense_account, item.item_code in stock_items and item.item_tax_amount:
"against": expenses_included_in_asset_valuation, # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
"cost_center": item.cost_center, if item.purchase_receipt and valuation_tax_accounts:
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
"debit": flt(item.landed_cost_voucher_amount), where voucher_type='Purchase Receipt' and voucher_no=%s and account in %s""",
"project": item.project or self.project (item.purchase_receipt, valuation_tax_accounts))
}, item=item))
# update gross amount of asset bought through this document if not negative_expense_booked_in_pr:
assets = frappe.db.get_all('Asset', gl_entries.append(
filters={ 'purchase_invoice': self.name, 'item_code': item.item_code } self.get_gl_dict({
"account": self.stock_received_but_not_billed,
"against": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
"remarks": self.remarks or "Accounting Entry for Stock",
"cost_center": self.cost_center,
"project": item.project or self.project
}, item=item)
) )
for asset in assets:
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
if self.auto_accounting_for_stock and self.is_opening == "No" and \ self.negative_expense_to_be_booked += flt(item.item_tax_amount, \
item.item_code in stock_items and item.item_tax_amount: item.precision("item_tax_amount"))
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
if item.purchase_receipt and valuation_tax_accounts:
negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Purchase Receipt' and voucher_no=%s and account in %s""",
(item.purchase_receipt, valuation_tax_accounts))
if not negative_expense_booked_in_pr:
gl_entries.append(
self.get_gl_dict({
"account": self.stock_received_but_not_billed,
"against": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
"remarks": self.remarks or "Accounting Entry for Stock",
"cost_center": self.cost_center,
"project": item.project or self.project
}, item=item)
)
self.negative_expense_to_be_booked += flt(item.item_tax_amount, \
item.precision("item_tax_amount"))
def get_asset_gl_entry(self, gl_entries): def get_asset_gl_entry(self, gl_entries):
arbnb_account = self.get_company_default("asset_received_but_not_billed") arbnb_account = self.get_company_default("asset_received_but_not_billed")

View File

@@ -579,7 +579,12 @@ class SalesInvoice(SellingController):
""" """
if self.is_internal_transfer() and not self.inter_company_account: if self.is_internal_transfer() and not self.inter_company_account:
self.inter_company_account = frappe.get_cached_value('Company', self.company, 'default_inter_company_account') inter_company_account = frappe.get_cached_value('Company', self.company, 'default_inter_company_account')
if not inter_company_account:
msg = _("Please select inter-company account or add default inter-company account for company {0}").format(
frappe.bold(self.company))
frappe.throw(msg)
def is_internal_transfer(self): def is_internal_transfer(self):
""" """
@@ -845,27 +850,28 @@ class SalesInvoice(SellingController):
def make_item_gl_entries(self, gl_entries): def make_item_gl_entries(self, gl_entries):
# income account gl entries # income account gl entries
if not self.is_internal_transfer(): for item in self.get("items"):
for item in self.get("items"): if flt(item.base_net_amount, item.precision("base_net_amount")):
if flt(item.base_net_amount, item.precision("base_net_amount")): if item.is_fixed_asset:
if item.is_fixed_asset: asset = frappe.get_doc("Asset", item.asset)
asset = frappe.get_doc("Asset", item.asset)
if (len(asset.finance_books) > 1 and not item.finance_book if (len(asset.finance_books) > 1 and not item.finance_book
and asset.finance_books[0].finance_book): and asset.finance_books[0].finance_book):
frappe.throw(_("Select finance book for the item {0} at row {1}") frappe.throw(_("Select finance book for the item {0} at row {1}")
.format(item.item_code, item.idx)) .format(item.item_code, item.idx))
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
item.base_net_amount, item.finance_book) item.base_net_amount, item.finance_book)
for gle in fixed_asset_gl_entries: for gle in fixed_asset_gl_entries:
gle["against"] = self.customer gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item)) gl_entries.append(self.get_gl_dict(gle, item=item))
asset.db_set("disposal_date", self.posting_date) asset.db_set("disposal_date", self.posting_date)
asset.set_status("Sold" if self.docstatus==1 else None) asset.set_status("Sold" if self.docstatus==1 else None)
else: else:
# Do not book income for transfer within same company
if not self.is_internal_transfer():
income_account = (item.income_account income_account = (item.income_account
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)

View File

@@ -103,8 +103,8 @@ class StockController(AccountsController):
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row)) }, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
# expense account/ target_warehouse / source_warehouse # expense account/ target_warehouse / source_warehouse
if item_row.get('target_warehouse') or item_row.get('from_warehouse'): if item_row.get('target_warehouse'):
warehouse = item_row.get('target_warehouse') or item_row.get('from_warehouse') warehouse = item_row.get('target_warehouse')
expense_account = warehouse_account[warehouse]["account"] expense_account = warehouse_account[warehouse]["account"]
else: else:
expense_account = item_row.expense_account expense_account = item_row.expense_account