mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-03 12:19:12 +00:00
feat(stock): add Stock Delivered But Not Billed GL entries on Delivery Note and Sales Invoice
This commit is contained in:
committed by
kavin-114
parent
8596d98ac4
commit
3364ee9274
@@ -1586,6 +1586,12 @@ class SalesInvoice(SellingController):
|
|||||||
self.make_internal_transfer_gl_entries(gl_entries)
|
self.make_internal_transfer_gl_entries(gl_entries)
|
||||||
|
|
||||||
self.make_item_gl_entries(gl_entries)
|
self.make_item_gl_entries(gl_entries)
|
||||||
|
|
||||||
|
disable_sdbnb_in_sr = frappe.get_cached_value("Company", self.company, "disable_sdbnb_in_sr")
|
||||||
|
|
||||||
|
if not (self.is_return and disable_sdbnb_in_sr):
|
||||||
|
self.stock_delivered_but_not_billed_gl_entries(gl_entries)
|
||||||
|
|
||||||
self.make_precision_loss_gl_entry(gl_entries)
|
self.make_precision_loss_gl_entry(gl_entries)
|
||||||
self.make_discount_gl_entries(gl_entries)
|
self.make_discount_gl_entries(gl_entries)
|
||||||
|
|
||||||
@@ -1603,6 +1609,81 @@ class SalesInvoice(SellingController):
|
|||||||
self.set_transaction_currency_and_rate_in_gl_map(gl_entries)
|
self.set_transaction_currency_and_rate_in_gl_map(gl_entries)
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
|
def stock_delivered_but_not_billed_gl_entries(self, gl_entries):
|
||||||
|
if self.update_stock or not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
||||||
|
return
|
||||||
|
|
||||||
|
for item in self.get("items"):
|
||||||
|
if not item.delivery_note and not item.dn_detail:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not frappe.get_cached_value("Item", item.item_code, "is_stock_item"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
dn_expense_account = frappe.get_cached_value(
|
||||||
|
"Delivery Note Item", item.dn_detail, "expense_account"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
not dn_expense_account
|
||||||
|
or frappe.get_cached_value("Account", dn_expense_account, "account_type")
|
||||||
|
!= "Stock Delivered But Not Billed"
|
||||||
|
or not item.expense_account
|
||||||
|
or dn_expense_account == item.expense_account
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
delivery_note = item.delivery_note or frappe.get_cached_value(
|
||||||
|
"Delivery Note Item", item.dn_detail, "parent"
|
||||||
|
)
|
||||||
|
if not delivery_note:
|
||||||
|
continue
|
||||||
|
|
||||||
|
item_g = frappe.get_cached_value(
|
||||||
|
"Stock Ledger Entry",
|
||||||
|
{
|
||||||
|
"voucher_no": delivery_note,
|
||||||
|
"voucher_detail_no": item.dn_detail,
|
||||||
|
"item_code": item.item_code,
|
||||||
|
"is_cancelled": 0,
|
||||||
|
},
|
||||||
|
["stock_value_difference", "actual_qty"],
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not item_g or not flt(item_g.actual_qty):
|
||||||
|
continue
|
||||||
|
valuation_rate = flt(item_g.stock_value_difference) / flt(item_g.actual_qty)
|
||||||
|
valuation_amount = valuation_rate * item.stock_qty
|
||||||
|
dn_account_currency = get_account_currency(dn_expense_account)
|
||||||
|
item_account_currency = get_account_currency(item.expense_account)
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict(
|
||||||
|
{
|
||||||
|
"account": dn_expense_account,
|
||||||
|
"against": item.expense_account,
|
||||||
|
"credit": flt(valuation_amount),
|
||||||
|
"credit_in_account_currency": flt(valuation_amount),
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
},
|
||||||
|
dn_account_currency,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict(
|
||||||
|
{
|
||||||
|
"account": item.expense_account,
|
||||||
|
"against": dn_expense_account,
|
||||||
|
"debit": flt(valuation_amount),
|
||||||
|
"debit_in_account_currency": flt(valuation_amount),
|
||||||
|
"cost_center": item.cost_center,
|
||||||
|
},
|
||||||
|
item_account_currency,
|
||||||
|
item=item,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def make_customer_gl_entry(self, gl_entries):
|
def make_customer_gl_entry(self, gl_entries):
|
||||||
# Checked both rounding_adjustment and rounded_total
|
# Checked both rounding_adjustment and rounded_total
|
||||||
# because rounded_total had value even before introduction of posting GLE based on rounded total
|
# because rounded_total had value even before introduction of posting GLE based on rounded total
|
||||||
|
|||||||
@@ -940,6 +940,7 @@ class StockController(AccountsController):
|
|||||||
"Stock Reconciliation",
|
"Stock Reconciliation",
|
||||||
"Stock Entry",
|
"Stock Entry",
|
||||||
"Subcontracting Receipt",
|
"Subcontracting Receipt",
|
||||||
|
"Delivery Note",
|
||||||
)
|
)
|
||||||
and not is_expense_account
|
and not is_expense_account
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ class DeliveryNote(SellingController):
|
|||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
super().validate()
|
super().validate()
|
||||||
self.validate_references()
|
self.validate_references()
|
||||||
|
self.validate_expense_account()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.so_required()
|
self.so_required()
|
||||||
self.validate_proj_cust()
|
self.validate_proj_cust()
|
||||||
@@ -461,6 +462,42 @@ class DeliveryNote(SellingController):
|
|||||||
d.actual_qty = flt(bin_qty.actual_qty)
|
d.actual_qty = flt(bin_qty.actual_qty)
|
||||||
d.projected_qty = flt(bin_qty.projected_qty)
|
d.projected_qty = flt(bin_qty.projected_qty)
|
||||||
|
|
||||||
|
def validate_expense_account(self):
|
||||||
|
company_values = frappe.get_cached_value(
|
||||||
|
"Company",
|
||||||
|
self.company,
|
||||||
|
[
|
||||||
|
"stock_delivered_but_not_billed",
|
||||||
|
"disable_sdbnb_in_sr",
|
||||||
|
"default_expense_account",
|
||||||
|
],
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
sdbnb_account = company_values.stock_delivered_but_not_billed
|
||||||
|
disable_sdbnb_in_sr = company_values.disable_sdbnb_in_sr
|
||||||
|
default_expense_account = company_values.default_expense_account
|
||||||
|
|
||||||
|
for item in self.items:
|
||||||
|
if item.get("against_sales_invoice"):
|
||||||
|
continue
|
||||||
|
is_stock_item = frappe.get_cached_value("Item", item.item_code, "is_stock_item")
|
||||||
|
# Only stock items
|
||||||
|
if not is_stock_item or item.get("is_fixed_asset") or item.get("is_subcontracted"):
|
||||||
|
continue
|
||||||
|
# Sales Return handling
|
||||||
|
if self.is_return and disable_sdbnb_in_sr:
|
||||||
|
if default_expense_account and (
|
||||||
|
not item.expense_account or item.expense_account == sdbnb_account
|
||||||
|
):
|
||||||
|
item.expense_account = default_expense_account
|
||||||
|
continue
|
||||||
|
|
||||||
|
if sdbnb_account:
|
||||||
|
item.expense_account = sdbnb_account
|
||||||
|
elif not item.expense_account and default_expense_account:
|
||||||
|
item.expense_account = default_expense_account
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.validate_packed_qty()
|
self.validate_packed_qty()
|
||||||
self.update_pick_list_status()
|
self.update_pick_list_status()
|
||||||
|
|||||||
@@ -436,6 +436,27 @@ def get_basic_details(ctx: ItemDetailsCtx, item, overwrite_warehouse=True) -> It
|
|||||||
fieldname="fixed_asset_account", item=ctx.item_code, company=ctx.company
|
fieldname="fixed_asset_account", item=ctx.item_code, company=ctx.company
|
||||||
)
|
)
|
||||||
|
|
||||||
|
company_values = frappe.get_cached_value(
|
||||||
|
"Company",
|
||||||
|
ctx.company,
|
||||||
|
[
|
||||||
|
"stock_delivered_but_not_billed",
|
||||||
|
"disable_sdbnb_in_sr",
|
||||||
|
],
|
||||||
|
as_dict=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
ctx.doctype == "Delivery Note"
|
||||||
|
and ctx.is_stock_item
|
||||||
|
and company_values
|
||||||
|
and company_values.stock_delivered_but_not_billed
|
||||||
|
and not ctx.get("is_fixed_asset")
|
||||||
|
and not ctx.get("is_subcontracted")
|
||||||
|
):
|
||||||
|
if not (ctx.get("is_return") and company_values.disable_sdbnb_in_sr):
|
||||||
|
expense_account = company_values.stock_delivered_but_not_billed
|
||||||
|
|
||||||
# Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
|
# Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
|
||||||
if not ctx.uom:
|
if not ctx.uom:
|
||||||
if ctx.doctype in sales_doctypes:
|
if ctx.doctype in sales_doctypes:
|
||||||
|
|||||||
Reference in New Issue
Block a user