Merge branch 'develop' of https://github.com/frappe/erpnext into party_account_currency_check

This commit is contained in:
Deepesh Garg
2021-12-12 21:09:11 +05:30
3296 changed files with 36091 additions and 61405 deletions

View File

@@ -1,28 +1,60 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe, erpnext
import json
import frappe
from frappe import _, throw
from frappe.utils import (today, flt, cint, fmt_money, formatdate,
getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied, WorkflowPermissionError
from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
from erpnext.setup.utils import get_exchange_rate
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
from erpnext.utilities.transaction_base import TransactionBase
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
from frappe.utils import (
add_days,
add_months,
cint,
flt,
fmt_money,
formatdate,
get_last_day,
get_link_to_form,
getdate,
nowdate,
today,
)
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.doctype.pricing_rule.utils import (
apply_pricing_rule_for_free_items,
apply_pricing_rule_on_transaction,
get_applied_pricing_rules,
)
from erpnext.accounts.party import (
get_party_account,
get_party_account_currency,
get_party_gle_currency,
validate_party_frozen_disabled,
)
from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year
from erpnext.buying.utils import update_last_purchase_rate
from erpnext.controllers.print_settings import (
set_print_templates_for_item_table,
set_print_templates_for_taxes,
)
from erpnext.controllers.sales_and_purchase_return import validate_return
from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled, get_party_gle_currency
from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction,
apply_pricing_rule_for_free_items, get_applied_pricing_rules)
from erpnext.exceptions import InvalidCurrency
from six import text_type
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
from erpnext.setup.utils import get_exchange_rate
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
from erpnext.controllers.print_settings import set_print_templates_for_item_table, set_print_templates_for_taxes
from erpnext.stock.get_item_details import (
_get_item_tax_template,
get_conversion_factor,
get_item_details,
get_item_tax_map,
get_item_warehouse,
)
from erpnext.utilities.transaction_base import TransactionBase
class AccountMissingError(frappe.ValidationError): pass
@@ -115,11 +147,6 @@ class AccountsController(TransactionBase):
self.validate_currency()
self.validate_party_account_currency()
if self.doctype == 'Purchase Invoice':
self.calculate_paid_amount()
# apply tax withholding only if checked and applicable
self.set_tax_withholding()
if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
@@ -134,6 +161,11 @@ class AccountsController(TransactionBase):
self.set_inter_company_account()
if self.doctype == 'Purchase Invoice':
self.calculate_paid_amount()
# apply tax withholding only if checked and applicable
self.set_tax_withholding()
validate_regional(self)
if self.doctype != 'Material Request':
@@ -220,7 +252,12 @@ class AccountsController(TransactionBase):
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
calculate_taxes_and_totals(self)
if self.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
if self.doctype in (
'Sales Order',
'Delivery Note',
'Sales Invoice',
'POS Invoice',
):
self.calculate_commission()
self.calculate_contribution()
@@ -494,7 +531,8 @@ class AccountsController(TransactionBase):
'is_opening': self.get("is_opening") or "No",
'party_type': None,
'party': None,
'project': self.get("project")
'project': self.get("project"),
'post_net_value': args.get('post_net_value')
})
accounting_dimensions = get_accounting_dimensions()
@@ -653,13 +691,17 @@ class AccountsController(TransactionBase):
.format(d.reference_name, d.against_order))
def set_advance_gain_or_loss(self):
if not self.get("advances"):
if self.get('conversion_rate') == 1 or not self.get("advances"):
return
is_purchase_invoice = self.doctype == 'Purchase Invoice'
party_account = self.credit_to if is_purchase_invoice else self.debit_to
if get_account_currency(party_account) != self.currency:
return
for d in self.get("advances"):
advance_exchange_rate = d.ref_exchange_rate
if (d.allocated_amount and self.conversion_rate != 1
and self.conversion_rate != advance_exchange_rate):
if (d.allocated_amount and self.conversion_rate != advance_exchange_rate):
base_allocated_amount_in_ref_rate = advance_exchange_rate * d.allocated_amount
base_allocated_amount_in_inv_rate = self.conversion_rate * d.allocated_amount
@@ -678,7 +720,7 @@ class AccountsController(TransactionBase):
gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account')
if not gain_loss_account:
frappe.throw(_("Please set Default Exchange Gain/Loss Account in Company {}")
frappe.throw(_("Please set default Exchange Gain/Loss Account in Company {}")
.format(self.get('company')))
account_currency = get_account_currency(gain_loss_account)
if account_currency != self.company_currency:
@@ -697,7 +739,7 @@ class AccountsController(TransactionBase):
"against": party,
dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss),
dr_or_cr: abs(d.exchange_gain_loss),
"cost_center": self.cost_center,
"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
"project": self.project
}, item=d)
)
@@ -771,7 +813,6 @@ class AccountsController(TransactionBase):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
self.update_allocated_advance_taxes_on_cancel()
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
@@ -779,6 +820,38 @@ class AccountsController(TransactionBase):
if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
unlink_ref_doc_from_payment_entries(self)
if self.doctype == "Sales Order":
self.unlink_ref_doc_from_po()
def unlink_ref_doc_from_po(self):
so_items = []
for item in self.items:
so_items.append(item.name)
linked_po = list(set(frappe.get_all(
'Purchase Order Item',
filters = {
'sales_order': self.name,
'sales_order_item': ['in', so_items],
'docstatus': ['<', 2]
},
pluck='parent'
)))
if linked_po:
frappe.db.set_value(
'Purchase Order Item', {
'sales_order': self.name,
'sales_order_item': ['in', so_items],
'docstatus': ['<', 2]
},{
'sales_order': None,
'sales_order_item': None
}
)
frappe.msgprint(_("Purchase Orders {0} are un-linked").format("\n".join(linked_po)))
def get_tax_map(self):
tax_map = {}
for tax in self.get('taxes'):
@@ -787,29 +860,6 @@ class AccountsController(TransactionBase):
return tax_map
def update_allocated_advance_taxes_on_cancel(self):
if self.get('advances'):
tax_accounts = [d.account_head for d in self.get('taxes')]
allocated_tax_map = frappe._dict(frappe.get_all('GL Entry', fields=['account', 'sum(credit - debit)'],
filters={'voucher_no': self.name, 'account': ('in', tax_accounts)},
group_by='account', as_list=1))
tax_map = self.get_tax_map()
for pe in self.get('advances'):
if pe.reference_type == 'Payment Entry':
pe = frappe.get_doc('Payment Entry', pe.reference_name)
for tax in pe.get('taxes'):
allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head)
if allocated_amount > tax.tax_amount:
allocated_amount = tax.tax_amount
if allocated_amount:
frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount',
tax.allocated_amount - allocated_amount)
tax_map[tax.account_head] -= allocated_amount
allocated_tax_map[tax.account_head] -= allocated_amount
def get_amount_and_base_amount(self, item, enable_discount_accounting):
amount = item.net_amount
base_amount = item.base_net_amount
@@ -893,97 +943,94 @@ class AccountsController(TransactionBase):
}, item=self)
)
def allocate_advance_taxes(self, gl_entries):
tax_map = self.get_tax_map()
for pe in self.get("advances"):
if pe.reference_type == "Payment Entry" and \
frappe.db.get_value('Payment Entry', pe.reference_name, 'advance_tax_account'):
pe = frappe.get_doc("Payment Entry", pe.reference_name)
for tax in pe.get("taxes"):
account_currency = get_account_currency(tax.account_head)
if self.doctype == "Purchase Invoice":
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
else:
dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
party = self.supplier if self.doctype == "Purchase Invoice" else self.customer
unallocated_amount = tax.tax_amount - tax.allocated_amount
if tax_map.get(tax.account_head):
amount = tax_map.get(tax.account_head)
if amount < unallocated_amount:
unallocated_amount = amount
gl_entries.append(
self.get_gl_dict({
"account": tax.account_head,
"against": party,
dr_or_cr: unallocated_amount,
dr_or_cr + "_in_account_currency": unallocated_amount
if account_currency==self.company_currency
else unallocated_amount,
"cost_center": tax.cost_center
}, account_currency, item=tax))
gl_entries.append(
self.get_gl_dict({
"account": pe.advance_tax_account,
"against": party,
rev_dr_cr: unallocated_amount,
rev_dr_cr + "_in_account_currency": unallocated_amount
if account_currency==self.company_currency
else unallocated_amount,
"cost_center": tax.cost_center
}, account_currency, item=tax))
frappe.db.set_value("Advance Taxes and Charges", tax.name, "allocated_amount",
tax.allocated_amount + unallocated_amount)
tax_map[tax.account_head] -= unallocated_amount
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_allowance_for
item_allowance = {}
global_qty_allowance, global_amount_allowance = None, None
role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
user_roles = frappe.get_roles()
total_overbilled_amt = 0.0
for item in self.get("items"):
if item.get(item_ref_dn):
ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
item.get(item_ref_dn), based_on), self.precision(based_on, item))
if not ref_amt:
frappe.msgprint(
_("Warning: System will not check overbilling since amount for Item {0} in {1} is zero")
.format(item.item_code, ref_dt))
else:
already_billed = frappe.db.sql("""
select sum(%s)
from `tab%s`
where %s=%s and docstatus=1 and parent != %s
""" % (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'),
(item.get(item_ref_dn), self.name))[0][0]
if not item.get(item_ref_dn):
continue
total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
self.precision(based_on, item))
ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
item.get(item_ref_dn), based_on), self.precision(based_on, item))
if not ref_amt:
frappe.msgprint(
_("System will not check overbilling since amount for Item {0} in {1} is zero")
.format(item.item_code, ref_dt), title=_("Warning"), indicator="orange")
continue
allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
already_billed = self.get_billed_amount_for_item(item, item_ref_dn, based_on)
max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
self.precision(based_on, item))
if total_billed_amt < 0 and max_allowed_amt < 0:
# while making debit note against purchase return entry(purchase receipt) getting overbill error
total_billed_amt = abs(total_billed_amt)
max_allowed_amt = abs(max_allowed_amt)
allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
if total_billed_amt - max_allowed_amt > 0.01 and role_allowed_to_over_bill not in frappe.get_roles():
if self.doctype != "Purchase Invoice":
self.throw_overbill_exception(item, max_allowed_amt)
elif not cint(frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice")):
self.throw_overbill_exception(item, max_allowed_amt)
if total_billed_amt < 0 and max_allowed_amt < 0:
# while making debit note against purchase return entry(purchase receipt) getting overbill error
total_billed_amt = abs(total_billed_amt)
max_allowed_amt = abs(max_allowed_amt)
overbill_amt = total_billed_amt - max_allowed_amt
total_overbilled_amt += overbill_amt
if overbill_amt > 0.01 and role_allowed_to_over_bill not in user_roles:
if self.doctype != "Purchase Invoice":
self.throw_overbill_exception(item, max_allowed_amt)
elif not cint(frappe.db.get_single_value("Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice")):
self.throw_overbill_exception(item, max_allowed_amt)
if role_allowed_to_over_bill in user_roles and total_overbilled_amt > 0.1:
frappe.msgprint(_("Overbilling of {} ignored because you have {} role.")
.format(total_overbilled_amt, role_allowed_to_over_bill), indicator="orange", alert=True)
def get_billed_amount_for_item(self, item, item_ref_dn, based_on):
'''
Returns Sum of Amount of
Sales/Purchase Invoice Items
that are linked to `item_ref_dn` (`dn_detail` / `pr_detail`)
that are submitted OR not submitted but are under current invoice
'''
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Sum
item_doctype = frappe.qb.DocType(item.doctype)
based_on_field = frappe.qb.Field(based_on)
join_field = frappe.qb.Field(item_ref_dn)
result = (
frappe.qb.from_(item_doctype)
.select(Sum(based_on_field))
.where(
join_field == item.get(item_ref_dn)
).where(
Criterion.any([ # select all items from other invoices OR current invoices
Criterion.all([ # for selecting items from other invoices
item_doctype.docstatus == 1,
item_doctype.parent != self.name
]),
Criterion.all([ # for selecting items from current invoice, that are linked to same reference
item_doctype.docstatus == 0,
item_doctype.parent == self.name,
item_doctype.name != item.name
])
])
)
).run()
return result[0][0] if result else 0
def throw_overbill_exception(self, item, max_allowed_amt):
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
@@ -1000,7 +1047,7 @@ class AccountsController(TransactionBase):
stock_items = [r[0] for r in frappe.db.sql("""
select name from `tabItem`
where name in (%s) and is_stock_item=1
""" % (", ".join((["%s"] * len(item_codes))),), item_codes)]
""" % (", ".join(["%s"] * len(item_codes)),), item_codes)]
return stock_items
@@ -1223,7 +1270,7 @@ class AccountsController(TransactionBase):
d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
d.outstanding = d.payment_amount
elif not d.invoice_portion:
d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount'))
d.base_payment_amount = flt(d.payment_amount * self.get("conversion_rate"), d.precision('base_payment_amount'))
def get_order_details(self):
@@ -1321,8 +1368,8 @@ class AccountsController(TransactionBase):
total = 0
base_total = 0
for d in self.get("payment_schedule"):
total += flt(d.payment_amount)
base_total += flt(d.base_payment_amount)
total += flt(d.payment_amount, d.precision("payment_amount"))
base_total += flt(d.base_payment_amount, d.precision("base_payment_amount"))
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
grand_total = self.get("rounded_total") or self.grand_total
@@ -1338,8 +1385,9 @@ class AccountsController(TransactionBase):
else:
grand_total -= self.get("total_advance")
base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
if total != flt(grand_total, self.precision("grand_total")) or \
base_total != flt(base_grand_total, self.precision("base_grand_total")):
if flt(total, self.precision("grand_total")) - flt(grand_total, self.precision("grand_total")) > 0.1 or \
flt(base_total, self.precision("base_grand_total")) - flt(base_grand_total, self.precision("base_grand_total")) > 0.1:
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
def is_rounded_total_disabled(self):
@@ -1380,6 +1428,67 @@ class AccountsController(TransactionBase):
return False
def process_common_party_accounting(self):
is_invoice = self.doctype in ['Sales Invoice', 'Purchase Invoice']
if not is_invoice:
return
if frappe.db.get_single_value('Accounts Settings', 'enable_common_party_accounting'):
party_link = self.get_common_party_link()
if party_link and self.outstanding_amount:
self.create_advance_and_reconcile(party_link)
def get_common_party_link(self):
party_type, party = self.get_party()
return frappe.db.get_value(
doctype='Party Link',
filters={'secondary_role': party_type, 'secondary_party': party},
fieldname=['primary_role', 'primary_party'],
as_dict=True
)
def create_advance_and_reconcile(self, party_link):
secondary_party_type, secondary_party = self.get_party()
primary_party_type, primary_party = party_link.primary_role, party_link.primary_party
primary_account = get_party_account(primary_party_type, primary_party, self.company)
secondary_account = get_party_account(secondary_party_type, secondary_party, self.company)
jv = frappe.new_doc('Journal Entry')
jv.voucher_type = 'Journal Entry'
jv.posting_date = self.posting_date
jv.company = self.company
jv.remark = 'Adjustment for {} {}'.format(self.doctype, self.name)
reconcilation_entry = frappe._dict()
advance_entry = frappe._dict()
reconcilation_entry.account = secondary_account
reconcilation_entry.party_type = secondary_party_type
reconcilation_entry.party = secondary_party
reconcilation_entry.reference_type = self.doctype
reconcilation_entry.reference_name = self.name
reconcilation_entry.cost_center = self.cost_center
advance_entry.account = primary_account
advance_entry.party_type = primary_party_type
advance_entry.party = primary_party
advance_entry.cost_center = self.cost_center
advance_entry.is_advance = 'Yes'
if self.doctype == 'Sales Invoice':
reconcilation_entry.credit_in_account_currency = self.outstanding_amount
advance_entry.debit_in_account_currency = self.outstanding_amount
else:
advance_entry.credit_in_account_currency = self.outstanding_amount
reconcilation_entry.debit_in_account_currency = self.outstanding_amount
jv.append('accounts', reconcilation_entry)
jv.append('accounts', advance_entry)
jv.save()
jv.submit()
@frappe.whitelist()
def get_tax_rate(account_head):
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
@@ -1543,7 +1652,7 @@ def get_advance_journal_entries(party_type, party, party_account, amount_field,
def get_advance_payment_entries(party_type, party, party_account, order_doctype,
order_list=None, include_unallocated=True, against_all_orders=False, limit=None):
order_list=None, include_unallocated=True, against_all_orders=False, limit=None, condition=None):
party_account_field = "paid_from" if party_type == "Customer" else "paid_to"
currency_field = "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency"
payment_type = "Receive" if party_type == "Customer" else "Pay"
@@ -1578,27 +1687,72 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype,
if include_unallocated:
unallocated_payment_entries = frappe.db.sql("""
select "Payment Entry" as reference_type, name as reference_name,
remarks, unallocated_amount as amount, {2} as exchange_rate
select "Payment Entry" as reference_type, name as reference_name, posting_date,
remarks, unallocated_amount as amount, {2} as exchange_rate, {3} as currency
from `tabPayment Entry`
where
{0} = %s and party_type = %s and party = %s and payment_type = %s
and docstatus = 1 and unallocated_amount > 0
and docstatus = 1 and unallocated_amount > 0 {condition}
order by posting_date {1}
""".format(party_account_field, limit_cond, exchange_rate_field),
""".format(party_account_field, limit_cond, exchange_rate_field, currency_field, condition=condition or ""),
(party_account, party_type, party, payment_type), as_dict=1)
return list(payment_entries_against_order) + list(unallocated_payment_entries)
def update_invoice_status():
# Daily update the status of the invoices
frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue'
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
"""Updates status as Overdue for applicable invoices. Runs daily."""
today = getdate()
for doctype in ("Sales Invoice", "Purchase Invoice"):
frappe.db.sql("""
UPDATE `tab{doctype}` invoice SET invoice.status = 'Overdue'
WHERE invoice.docstatus = 1
AND invoice.status REGEXP '^Unpaid|^Partly Paid'
AND invoice.outstanding_amount > 0
AND (
{or_condition}
(
(
CASE
WHEN invoice.party_account_currency = invoice.currency
THEN (
CASE
WHEN invoice.disable_rounded_total
THEN invoice.grand_total
ELSE invoice.rounded_total
END
)
ELSE (
CASE
WHEN invoice.disable_rounded_total
THEN invoice.base_grand_total
ELSE invoice.base_rounded_total
END
)
END
) - invoice.outstanding_amount
) < (
SELECT SUM(
CASE
WHEN invoice.party_account_currency = invoice.currency
THEN ps.payment_amount
ELSE ps.base_payment_amount
END
)
FROM `tabPayment Schedule` ps
WHERE ps.parent = invoice.name
AND ps.due_date < %(today)s
)
)
""".format(
doctype=doctype,
or_condition=(
"invoice.is_pos AND invoice.due_date < %(today)s OR"
if doctype == "Sales Invoice"
else ""
)
), {"today": today}
)
@frappe.whitelist()
def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
@@ -1618,7 +1772,7 @@ def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_
@frappe.whitelist()
def get_payment_term_details(term, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
term_details = frappe._dict()
if isinstance(term, text_type):
if isinstance(term, str):
term = frappe.get_doc("Payment Term", term)
else:
term_details.payment_term = term.payment_term
@@ -1767,7 +1921,12 @@ def validate_child_on_delete(row, parent):
def update_bin_on_delete(row, doctype):
"""Update bin for deleted item (row)."""
from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty, get_ordered_qty, get_indented_qty
from erpnext.stock.stock_balance import (
get_indented_qty,
get_ordered_qty,
get_reserved_qty,
update_bin_qty,
)
qty_dict = {}
if doctype == "Sales Order":