Merge pull request #40263 from nabinhait/pi-optimization

perf: Performance optmization for Purchase Invoice submission
This commit is contained in:
Nabin Hait
2024-03-13 18:17:41 +05:30
committed by GitHub
14 changed files with 252 additions and 248 deletions

View File

@@ -236,14 +236,16 @@ def get_accounting_dimensions(as_list=True, filters=None):
def get_checks_for_pl_and_bs_accounts(): def get_checks_for_pl_and_bs_accounts():
dimensions = frappe.db.sql( if frappe.flags.accounting_dimensions_details is None:
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs # nosemgrep
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c frappe.flags.accounting_dimensions_details = frappe.db.sql(
WHERE p.name = c.parent""", """SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
as_dict=1, FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
) WHERE p.name = c.parent""",
as_dict=1,
)
return dimensions return frappe.flags.accounting_dimensions_details
def get_dimension_with_children(doctype, dimensions): def get_dimension_with_children(doctype, dimensions):

View File

@@ -78,6 +78,8 @@ class TestAccountingDimension(unittest.TestCase):
def tearDown(self): def tearDown(self):
disable_dimension() disable_dimension()
frappe.flags.accounting_dimensions_details = None
frappe.flags.dimension_filter_map = None
def create_dimension(): def create_dimension():

View File

@@ -38,37 +38,41 @@ class AccountingDimensionFilter(Document):
def get_dimension_filter_map(): def get_dimension_filter_map():
filters = frappe.db.sql( if not frappe.flags.get("dimension_filter_map"):
""" # nosemgrep
SELECT filters = frappe.db.sql(
a.applicable_on_account, d.dimension_value, p.accounting_dimension, """
p.allow_or_restrict, a.is_mandatory SELECT
FROM a.applicable_on_account, d.dimension_value, p.accounting_dimension,
`tabApplicable On Account` a, `tabAllowed Dimension` d, p.allow_or_restrict, a.is_mandatory
`tabAccounting Dimension Filter` p FROM
WHERE `tabApplicable On Account` a, `tabAllowed Dimension` d,
p.name = a.parent `tabAccounting Dimension Filter` p
AND p.disabled = 0 WHERE
AND p.name = d.parent p.name = a.parent
""", AND p.disabled = 0
as_dict=1, AND p.name = d.parent
) """,
as_dict=1,
dimension_filter_map = {}
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
) )
return dimension_filter_map dimension_filter_map = {}
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
)
frappe.flags.dimension_filter_map = dimension_filter_map
return frappe.flags.dimension_filter_map
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory): def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):

View File

@@ -47,6 +47,8 @@ class TestAccountingDimensionFilter(unittest.TestCase):
def tearDown(self): def tearDown(self):
disable_dimension_filter() disable_dimension_filter()
disable_dimension() disable_dimension()
frappe.flags.accounting_dimensions_details = None
frappe.flags.dimension_filter_map = None
for si in self.invoice_list: for si in self.invoice_list:
si.load_from_db() si.load_from_db()

View File

@@ -109,6 +109,8 @@ class Budget(Document):
def validate_expense_against_budget(args, expense_amount=0): def validate_expense_against_budget(args, expense_amount=0):
args = frappe._dict(args) args = frappe._dict(args)
if not frappe.get_all("Budget", limit=1):
return
if args.get("company") and not args.fiscal_year: if args.get("company") and not args.fiscal_year:
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0] args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
@@ -116,6 +118,11 @@ def validate_expense_against_budget(args, expense_amount=0):
"Company", args.get("company"), "exception_budget_approver_role" "Company", args.get("company"), "exception_budget_approver_role"
) )
if not frappe.get_cached_value(
"Budget", {"fiscal_year": args.fiscal_year, "company": args.company}
): # nosec
return
if not args.account: if not args.account:
args.account = args.get("expense_account") args.account = args.get("expense_account")
@@ -142,13 +149,13 @@ def validate_expense_against_budget(args, expense_amount=0):
if ( if (
args.get(budget_against) args.get(budget_against)
and args.account and args.account
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"}) and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense")
): ):
doctype = dimension.get("document_type") doctype = dimension.get("document_type")
if frappe.get_cached_value("DocType", doctype, "is_tree"): if frappe.get_cached_value("DocType", doctype, "is_tree"):
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"]) lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"])
condition = """and exists(select name from `tab%s` condition = """and exists(select name from `tab%s`
where lft<=%s and rgt>=%s and name=b.%s)""" % ( where lft<=%s and rgt>=%s and name=b.%s)""" % (
doctype, doctype,

View File

@@ -1,94 +1,42 @@
{ {
"allow_copy": 0, "actions": [],
"allow_import": 0, "creation": "2016-05-16 11:54:09.286135",
"allow_rename": 0, "doctype": "DocType",
"beta": 0, "editable_grid": 1,
"creation": "2016-05-16 11:54:09.286135", "engine": "InnoDB",
"custom": 0, "field_order": [
"docstatus": 0, "account",
"doctype": "DocType", "budget_amount"
"document_type": "", ],
"editable_grid": 1,
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "fieldname": "account",
"bold": 0, "fieldtype": "Link",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "Account",
"fieldname": "account", "options": "Account",
"fieldtype": "Link", "reqd": 1,
"hidden": 0, "search_index": 1
"ignore_user_permissions": 0, },
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "fieldname": "budget_amount",
"bold": 0, "fieldtype": "Currency",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "Budget Amount",
"fieldname": "budget_amount", "options": "Company:company:default_currency",
"fieldtype": "Currency", "reqd": 1
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Budget Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"hide_heading": 0, "istable": 1,
"hide_toolbar": 0, "links": [],
"idx": 0, "modified": "2024-03-04 15:43:27.016947",
"image_view": 0, "modified_by": "Administrator",
"in_create": 0, "module": "Accounts",
"name": "Budget Account",
"is_submittable": 0, "owner": "Administrator",
"issingle": 0, "permissions": [],
"istable": 1, "quick_entry": 1,
"max_attachments": 0, "sort_field": "modified",
"modified": "2017-01-02 17:02:53.339420", "sort_order": "DESC",
"modified_by": "Administrator", "states": []
"module": "Accounts",
"name": "Budget Account",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
} }

View File

@@ -127,7 +127,7 @@ class GLEntry(Document):
frappe.throw(msg, title=_("Missing Cost Center")) frappe.throw(msg, title=_("Missing Cost Center"))
def validate_dimensions_for_pl_and_bs(self): def validate_dimensions_for_pl_and_bs(self):
account_type = frappe.db.get_value("Account", self.account, "report_type") account_type = frappe.get_cached_value("Account", self.account, "report_type")
for dimension in get_checks_for_pl_and_bs_accounts(): for dimension in get_checks_for_pl_and_bs_accounts():
if ( if (
@@ -159,7 +159,7 @@ class GLEntry(Document):
def check_pl_account(self): def check_pl_account(self):
if ( if (
self.is_opening == "Yes" self.is_opening == "Yes"
and frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss" and frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss"
and not self.is_cancelled and not self.is_cancelled
): ):
frappe.throw( frappe.throw(
@@ -252,7 +252,7 @@ class GLEntry(Document):
def validate_balance_type(account, adv_adj=False): def validate_balance_type(account, adv_adj=False):
if not adv_adj and account: if not adv_adj and account:
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be") balance_must_be = frappe.get_cached_value("Account", account, "balance_must_be")
if balance_must_be: if balance_must_be:
balance = frappe.db.sql( balance = frappe.db.sql(
"""select sum(debit) - sum(credit) """select sum(debit) - sum(credit)
@@ -279,7 +279,7 @@ def update_outstanding_amt(
party_condition = "" party_condition = ""
if against_voucher_type == "Sales Invoice": if against_voucher_type == "Sales Invoice":
party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to") party_account = frappe.get_cached_value(against_voucher_type, against_voucher, "debit_to")
account_condition = "and account in ({0}, {1})".format( account_condition = "and account in ({0}, {1})".format(
frappe.db.escape(account), frappe.db.escape(party_account) frappe.db.escape(account), frappe.db.escape(party_account)
) )
@@ -347,7 +347,7 @@ def update_outstanding_amt(
def validate_frozen_account(account, adv_adj=None): def validate_frozen_account(account, adv_adj=None):
frozen_account = frappe.get_cached_value("Account", account, "freeze_account") frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
if frozen_account == "Yes" and not adv_adj: if frozen_account == "Yes" and not adv_adj:
frozen_accounts_modifier = frappe.db.get_value( frozen_accounts_modifier = frappe.get_cached_value(
"Accounts Settings", None, "frozen_accounts_modifier" "Accounts Settings", None, "frozen_accounts_modifier"
) )

View File

@@ -132,11 +132,12 @@ class PaymentLedgerEntry(Document):
def on_update(self): def on_update(self):
adv_adj = self.flags.adv_adj adv_adj = self.flags.adv_adj
if not self.flags.from_repost: if not self.flags.from_repost:
self.validate_account_details()
self.validate_dimensions_for_pl_and_bs()
self.validate_allowed_dimensions()
validate_balance_type(self.account, adv_adj)
validate_frozen_account(self.account, adv_adj) validate_frozen_account(self.account, adv_adj)
if not self.delinked:
self.validate_account_details()
self.validate_dimensions_for_pl_and_bs()
self.validate_allowed_dimensions()
validate_balance_type(self.account, adv_adj)
# update outstanding amount # update outstanding amount
if ( if (

View File

@@ -3,7 +3,7 @@
import frappe import frappe
from frappe import _, throw from frappe import _, qb, throw
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum from frappe.query_builder.functions import Sum
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
@@ -575,13 +575,12 @@ class PurchaseInvoice(BuyingController):
self.db_set("repost_required", self.needs_repost) self.db_set("repost_required", self.needs_repost)
def make_gl_entries(self, gl_entries=None, from_repost=False): def make_gl_entries(self, gl_entries=None, from_repost=False):
if not gl_entries: update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
gl_entries = self.get_gl_entries() if self.docstatus == 1:
if not gl_entries:
gl_entries = self.get_gl_entries()
if gl_entries: if gl_entries:
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
if self.docstatus == 1:
make_gl_entries( make_gl_entries(
gl_entries, gl_entries,
update_outstanding=update_outstanding, update_outstanding=update_outstanding,
@@ -589,29 +588,43 @@ class PurchaseInvoice(BuyingController):
from_repost=from_repost, from_repost=from_repost,
) )
self.make_exchange_gain_loss_journal() self.make_exchange_gain_loss_journal()
elif self.docstatus == 2: elif self.docstatus == 2:
provisional_entries = [a for a in gl_entries if a.voucher_type == "Purchase Receipt"]
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if provisional_entries:
for entry in provisional_entries:
frappe.db.set_value(
"GL Entry",
{"voucher_type": "Purchase Receipt", "voucher_detail_no": entry.voucher_detail_no},
"is_cancelled",
1,
)
if update_outstanding == "No":
update_outstanding_amt(
self.credit_to,
"Supplier",
self.supplier,
self.doctype,
self.return_against if cint(self.is_return) and self.return_against else self.name,
)
elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
self.cancel_provisional_entries()
self.update_supplier_outstanding(update_outstanding)
def cancel_provisional_entries(self):
rows = set()
purchase_receipts = set()
for d in self.items:
if d.purchase_receipt:
purchase_receipts.add(d.purchase_receipt)
rows.add(d.name)
if rows:
# cancel gl entries
gle = qb.DocType("GL Entry")
gle_update_query = (
qb.update(gle)
.set(gle.is_cancelled, 1)
.where(
(gle.voucher_type == "Purchase Receipt")
& (gle.voucher_no.isin(purchase_receipts))
& (gle.voucher_detail_no.isin(rows))
)
)
gle_update_query.run()
def update_supplier_outstanding(self, update_outstanding):
if update_outstanding == "No":
update_outstanding_amt(
self.credit_to,
"Supplier",
self.supplier,
self.doctype,
self.return_against if cint(self.is_return) and self.return_against else self.name,
)
def get_gl_entries(self, warehouse_account=None): def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
@@ -724,8 +737,9 @@ class PurchaseInvoice(BuyingController):
"Company", self.company, "enable_provisional_accounting_for_non_stock_items" "Company", self.company, "enable_provisional_accounting_for_non_stock_items"
) )
) )
self.provisional_enpenses_booked_in_pr = False
purchase_receipt_doc_map = {} if provisional_accounting_for_non_stock_items:
self.get_provisional_accounts()
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):
@@ -862,44 +876,7 @@ class PurchaseInvoice(BuyingController):
dummy, amount = self.get_amount_and_base_amount(item, None) dummy, amount = self.get_amount_and_base_amount(item, None)
if provisional_accounting_for_non_stock_items: if provisional_accounting_for_non_stock_items:
if item.purchase_receipt: self.make_provisional_gl_entry(gl_entries, item)
provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value(
"Purchase Receipt Item",
item.pr_detail,
["provisional_expense_account", "qty", "base_rate"],
)
provisional_account = provisional_account or self.get_company_default(
"default_provisional_account"
)
purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt)
if not purchase_receipt_doc:
purchase_receipt_doc = frappe.get_doc("Purchase Receipt", item.purchase_receipt)
purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc
# 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": provisional_account,
},
"name",
)
if expense_booked_in_pr:
# Intentionally passing purchase invoice item to handle partial billing
purchase_receipt_doc.add_provisional_gl_entry(
item,
gl_entries,
self.posting_date,
provisional_account,
reverse=1,
item_amount=(min(item.qty, pr_qty) * pr_base_rate),
)
if not self.is_internal_transfer(): if not self.is_internal_transfer():
gl_entries.append( gl_entries.append(
@@ -996,6 +973,58 @@ class PurchaseInvoice(BuyingController):
if item.is_fixed_asset and item.landed_cost_voucher_amount: if item.is_fixed_asset and item.landed_cost_voucher_amount:
self.update_gross_purchase_amount_for_linked_assets(item) self.update_gross_purchase_amount_for_linked_assets(item)
def get_provisional_accounts(self):
self.provisional_accounts = frappe._dict()
linked_purchase_receipts = set([d.purchase_receipt for d in self.items if d.purchase_receipt])
pr_items = frappe.get_all(
"Purchase Receipt Item",
filters={"parent": ("in", linked_purchase_receipts)},
fields=["name", "provisional_expense_account", "qty", "base_rate"],
)
default_provisional_account = self.get_company_default("default_provisional_account")
for item in pr_items:
self.provisional_accounts[item.name] = {
"provisional_account": item.provisional_expense_account or default_provisional_account,
"qty": item.qty,
"base_rate": item.base_rate,
}
def make_provisional_gl_entry(self, gl_entries, item):
if item.purchase_receipt:
if not self.provisional_enpenses_booked_in_pr:
pr_item = self.provisional_accounts.get(item.pr_detail, {})
provisional_account = pr_item.get("provisional_account")
pr_qty = pr_item.get("qty")
pr_base_rate = pr_item.get("base_rate")
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
provision_gle_against_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": provisional_account,
},
["name"],
)
if provision_gle_against_pr:
self.provisional_enpenses_booked_in_pr = True
if self.provisional_enpenses_booked_in_pr:
purchase_receipt_doc = frappe.get_cached_doc("Purchase Receipt", item.purchase_receipt)
# Intentionally passing purchase invoice item to handle partial billing
purchase_receipt_doc.add_provisional_gl_entry(
item,
gl_entries,
self.posting_date,
provisional_account,
reverse=1,
item_amount=(min(item.qty, pr_qty) * pr_base_rate),
)
def update_gross_purchase_amount_for_linked_assets(self, item): def update_gross_purchase_amount_for_linked_assets(self, item):
assets = frappe.db.get_all( assets = frappe.db.get_all(
"Asset", "Asset",

View File

@@ -7,7 +7,7 @@ import copy
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
from frappe.utils import cint, cstr, flt, formatdate, getdate, now from frappe.utils import cint, flt, formatdate, getdate, now
import erpnext import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -234,11 +234,13 @@ def get_cost_center_allocation_data(company, posting_date):
def merge_similar_entries(gl_map, precision=None): def merge_similar_entries(gl_map, precision=None):
merged_gl_map = [] merged_gl_map = []
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions()
merge_properties = get_merge_properties(accounting_dimensions)
for entry in gl_map: for entry in gl_map:
entry.merge_key = get_merge_key(entry, merge_properties)
# if there is already an entry in this account then just add it # if there is already an entry in this account then just add it
# to that entry # to that entry
same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions) same_head = check_if_in_list(entry, merged_gl_map)
if same_head: if same_head:
same_head.debit = flt(same_head.debit) + flt(entry.debit) same_head.debit = flt(same_head.debit) + flt(entry.debit)
same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt( same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
@@ -273,33 +275,34 @@ def merge_similar_entries(gl_map, precision=None):
return merged_gl_map return merged_gl_map
def check_if_in_list(gle, gl_map, dimensions=None): def get_merge_properties(dimensions=None):
account_head_fieldnames = [ merge_properties = [
"voucher_detail_no", "account",
"party",
"against_voucher",
"cost_center", "cost_center",
"against_voucher_type", "party",
"party_type", "party_type",
"voucher_detail_no",
"against_voucher",
"against_voucher_type",
"project", "project",
"finance_book", "finance_book",
] ]
if dimensions: if dimensions:
account_head_fieldnames = account_head_fieldnames + dimensions merge_properties.extend(dimensions)
return merge_properties
def get_merge_key(entry, merge_properties):
merge_key = []
for fieldname in merge_properties:
merge_key.append(entry.get(fieldname, ""))
return tuple(merge_key)
def check_if_in_list(gle, gl_map):
for e in gl_map: for e in gl_map:
same_head = True if e.merge_key == gle.merge_key:
if e.account != gle.account:
same_head = False
continue
for fieldname in account_head_fieldnames:
if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
same_head = False
break
if same_head:
return e return e

View File

@@ -1606,8 +1606,8 @@ class AccountsController(TransactionBase):
item_allowance = {} item_allowance = {}
global_qty_allowance, global_amount_allowance = None, None global_qty_allowance, global_amount_allowance = None, None
role_allowed_to_over_bill = frappe.db.get_single_value( role_allowed_to_over_bill = frappe.get_cached_value(
"Accounts Settings", "role_allowed_to_over_bill" "Accounts Settings", None, "role_allowed_to_over_bill"
) )
user_roles = frappe.get_roles() user_roles = frappe.get_roles()

View File

@@ -573,6 +573,7 @@ class StatusUpdater(Document):
ref_doc.set_status(update=True) ref_doc.set_status(update=True)
@frappe.request_cache
def get_allowance_for( def get_allowance_for(
item_code, item_code,
item_allowance=None, item_allowance=None,
@@ -602,20 +603,20 @@ def get_allowance_for(
global_amount_allowance, global_amount_allowance,
) )
qty_allowance, over_billing_allowance = frappe.db.get_value( qty_allowance, over_billing_allowance = frappe.get_cached_value(
"Item", item_code, ["over_delivery_receipt_allowance", "over_billing_allowance"] "Item", item_code, ["over_delivery_receipt_allowance", "over_billing_allowance"]
) )
if qty_or_amount == "qty" and not qty_allowance: if qty_or_amount == "qty" and not qty_allowance:
if global_qty_allowance == None: if global_qty_allowance == None:
global_qty_allowance = flt( global_qty_allowance = flt(
frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance") frappe.get_cached_value("Stock Settings", None, "over_delivery_receipt_allowance")
) )
qty_allowance = global_qty_allowance qty_allowance = global_qty_allowance
elif qty_or_amount == "amount" and not over_billing_allowance: elif qty_or_amount == "amount" and not over_billing_allowance:
if global_amount_allowance == None: if global_amount_allowance == None:
global_amount_allowance = flt( global_amount_allowance = flt(
frappe.db.get_single_value("Accounts Settings", "over_billing_allowance") frappe.get_cached_value("Accounts Settings", None, "over_billing_allowance")
) )
over_billing_allowance = global_amount_allowance over_billing_allowance = global_amount_allowance

View File

@@ -104,7 +104,8 @@
"in_standard_filter": 1, "in_standard_filter": 1,
"label": "Price List", "label": "Price List",
"options": "Price List", "options": "Price List",
"reqd": 1 "reqd": 1,
"search_index": 1
}, },
{ {
"bold": 1, "bold": 1,
@@ -220,7 +221,7 @@
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2022-11-15 08:26:04.041861", "modified": "2024-03-13 12:23:39.630290",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item Price", "name": "Item Price",

View File

@@ -104,22 +104,8 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
if args.customer and cint(args.is_pos): if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args, update_data=True)) out.update(get_pos_profile_item_details(args.company, args, update_data=True))
if ( if item.is_stock_item:
args.get("doctype") == "Material Request" update_bin_details(args, out, doc)
and args.get("material_request_type") == "Material Transfer"
):
out.update(get_bin_details(args.item_code, args.get("from_warehouse")))
elif out.get("warehouse"):
if doc and doc.get("doctype") == "Purchase Order":
# calculate company_total_stock only for po
bin_details = get_bin_details(
args.item_code, out.warehouse, args.company, include_child_warehouses=True
)
else:
bin_details = get_bin_details(args.item_code, out.warehouse, include_child_warehouses=True)
out.update(bin_details)
# update args with out, if key or value not exists # update args with out, if key or value not exists
for key, value in out.items(): for key, value in out.items():
@@ -202,6 +188,24 @@ def set_valuation_rate(out, args):
out.update(get_valuation_rate(args.item_code, args.company, out.get("warehouse"))) out.update(get_valuation_rate(args.item_code, args.company, out.get("warehouse")))
def update_bin_details(args, out, doc):
if (
args.get("doctype") == "Material Request"
and args.get("material_request_type") == "Material Transfer"
):
out.update(get_bin_details(args.item_code, args.get("from_warehouse")))
elif out.get("warehouse"):
company = args.company if (doc and doc.get("doctype") == "Purchase Order") else None
# calculate company_total_stock only for po
bin_details = get_bin_details(
args.item_code, out.warehouse, company, include_child_warehouses=True
)
out.update(bin_details)
def process_args(args): def process_args(args):
if isinstance(args, str): if isinstance(args, str):
args = json.loads(args) args = json.loads(args)