diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index f24b28917237..47cb3fd812f0 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -538,7 +538,7 @@ def make_payment_request(**args): ) party_type = args.get("party_type") or "Customer" - party_account_currency = ref_doc.party_account_currency + party_account_currency = ref_doc.get("party_account_currency") if not party_account_currency: party_account = get_party_account(party_type, ref_doc.get(party_type.lower()), ref_doc.company) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 6109185f0bd4..59ffd656689b 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -40,7 +40,7 @@ def on_cancel(self): ) if gle_count > 5000: frappe.enqueue( - make_reverse_gl_entries, + process_cancellation, voucher_type="Period Closing Voucher", voucher_no=self.name, queue="long", @@ -51,9 +51,7 @@ def on_cancel(self): alert=True, ) else: - make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name) - - self.delete_closing_entries() + process_cancellation(voucher_type="Period Closing Voucher", voucher_no=self.name) def validate_future_closing_vouchers(self): if frappe.db.exists( @@ -66,12 +64,6 @@ def validate_future_closing_vouchers(self): ) ) - def delete_closing_entries(self): - closing_balance = frappe.qb.DocType("Account Closing Balance") - frappe.qb.from_(closing_balance).delete().where( - closing_balance.period_closing_voucher == self.name - ).run() - def validate_account_head(self): closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type") @@ -136,14 +128,7 @@ def make_gl_entries(self, get_opening_entries=False): closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries) if len(gl_entries + closing_entries) > 3000: frappe.enqueue( - process_gl_entries, - gl_entries=gl_entries, - voucher_name=self.name, - timeout=3000, - ) - - frappe.enqueue( - process_closing_entries, + process_gl_and_closing_entries, gl_entries=gl_entries, closing_entries=closing_entries, voucher_name=self.name, @@ -157,8 +142,9 @@ def make_gl_entries(self, get_opening_entries=False): alert=True, ) else: - process_gl_entries(gl_entries, self.name) - process_closing_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date) + process_gl_and_closing_entries( + gl_entries, closing_entries, self.name, self.company, self.posting_date + ) def get_grouped_gl_entries(self, get_opening_entries=False): closing_entries = [] @@ -323,9 +309,10 @@ def get_balances_based_on_dimensions( if get_opening_entries: query = query.where( - gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date) - | gl_entry.is_opening - == "Yes" + ( # noqa: UP034 + (gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)) + | (gl_entry.is_opening == "Yes") + ) ) else: query = query.where( @@ -343,12 +330,16 @@ def get_balances_based_on_dimensions( return query.run(as_dict=1) -def process_gl_entries(gl_entries, voucher_name): +def process_gl_and_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date): + from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( + make_closing_entries, + ) from erpnext.accounts.general_ledger import make_gl_entries try: if gl_entries: make_gl_entries(gl_entries, merge_entries=False) + make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed") except Exception as e: frappe.db.rollback() @@ -356,25 +347,21 @@ def process_gl_entries(gl_entries, voucher_name): frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed") -def process_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date): - from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( - make_closing_entries, - ) - - try: - make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) - except Exception as e: - frappe.db.rollback() - frappe.log_error(e) - - -def make_reverse_gl_entries(voucher_type, voucher_no): +def process_cancellation(voucher_type, voucher_no): from erpnext.accounts.general_ledger import make_reverse_gl_entries try: make_reverse_gl_entries(voucher_type=voucher_type, voucher_no=voucher_no) + delete_closing_entries(voucher_no) frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Completed") except Exception as e: frappe.db.rollback() frappe.log_error(e) frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Failed") + + +def delete_closing_entries(voucher_no): + closing_balance = frappe.qb.DocType("Account Closing Balance") + frappe.qb.from_(closing_balance).delete().where( + closing_balance.period_closing_voucher == voucher_no + ).run() diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 93a91584ecdb..90e606d7861d 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -981,6 +981,12 @@ def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self): self.assertEqual(so.items[1].item_code, "_Test Item") self.assertEqual(so.items[1].qty, 3) + so = make_sales_order(item_code="_Test Item", qty=5, do_not_submit=1) + so.items[0].qty = 1 + del so.items[-1] + so.save() + self.assertEqual(len(so.items), 1) + def test_apply_multiple_pricing_rules_for_discount_percentage_and_amount(self): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index b5d4af1ab90b..0032e539e446 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -644,6 +644,9 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): if pricing_rule.round_free_qty: qty = math.floor(qty) + if not qty: + return + free_item_data_args = { "item_code": free_item, "qty": qty, diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 857ee0e71879..c9762282a7dc 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -5,6 +5,8 @@ import frappe from frappe import _ from frappe.model.document import Document +from frappe.query_builder import Criterion +from frappe.query_builder.functions import IfNull pricing_rule_fields = [ "apply_on", @@ -91,22 +93,50 @@ def validate_pricing_rules(self): if self.is_new(): return - transaction_exists = False - docnames = [] + invalid_pricing_rule = self.get_invalid_pricing_rules() - # If user has changed applicable for - if self.get_doc_before_save() and self.get_doc_before_save().applicable_for == self.applicable_for: + if not invalid_pricing_rule: return - docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name}) + if frappe.db.exists( + "Pricing Rule Detail", + { + "pricing_rule": ["in", invalid_pricing_rule], + "docstatus": ["<", 2], + }, + ): + raise_for_transaction_exists(self.name) + + for doc in invalid_pricing_rule: + frappe.delete_doc("Pricing Rule", doc) + + frappe.msgprint( + _("The following invalid Pricing Rules are deleted:") + + "

" + ) + + def get_invalid_pricing_rules(self): + pr = frappe.qb.DocType("Pricing Rule") + conditions = [] + conditions.append(pr.promotional_scheme == self.name) + + if self.applicable_for: + applicable_for = frappe.scrub(self.applicable_for) + applicable_for_list = [d.get(applicable_for) for d in self.get(applicable_for)] - for docname in docnames: - if frappe.db.exists("Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}): - raise_for_transaction_exists(self.name) + conditions.append( + (IfNull(pr.applicable_for, "") != self.applicable_for) + | ( + (IfNull(pr.applicable_for, "") == self.applicable_for) + & IfNull(pr[applicable_for], "").notin(applicable_for_list) + ) + ) + else: + conditions.append(IfNull(pr.applicable_for, "") != "") - if docnames and not transaction_exists: - for docname in docnames: - frappe.delete_doc("Pricing Rule", docname.name) + return frappe.qb.from_(pr).select(pr.name).where(Criterion.all(conditions)).run(pluck=True) def on_update(self): self.validate() diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py index 0d08fd98139b..c4a5fcf4b0a3 100644 --- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py @@ -90,6 +90,31 @@ def test_change_applicable_for_in_promotional_scheme(self): price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name}) self.assertEqual(price_rules, []) + def test_change_applicable_for_values_in_promotional_scheme(self): + ps = make_promotional_scheme(applicable_for="Customer", customer="_Test Customer") + ps.append("customer", {"customer": "_Test Customer 2"}) + ps.save() + + price_rules = frappe.get_all( + "Pricing Rule", filters={"promotional_scheme": ps.name, "applicable_for": "Customer"} + ) + self.assertTrue(len(price_rules), 2) + + ps.set("customer", []) + ps.append("customer", {"customer": "_Test Customer 2"}) + ps.save() + + price_rules = frappe.get_all( + "Pricing Rule", + filters={ + "promotional_scheme": ps.name, + "applicable_for": "Customer", + "customer": "_Test Customer", + }, + ) + self.assertEqual(price_rules, []) + frappe.delete_doc("Promotional Scheme", ps.name) + def test_min_max_amount_configuration(self): ps = make_promotional_scheme() ps.price_discount_slabs[0].min_amount = 10 diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 8b0a2557d9b0..d1bb24617956 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -578,8 +578,6 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): conditions.append(ple.voucher_no == ple.against_voucher_no) conditions.append(ple.company == inv.company) - (qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1)) - advance_amt = ( qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0 ) diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json index f906dc6cec6a..f2c2f380fe90 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.json @@ -1,7 +1,5 @@ { "actions": [], - "allow_rename": 1, - "autoname": "format:UNREC-{#####}", "creation": "2023-08-22 10:26:34.421423", "default_view": "List", "doctype": "DocType", @@ -58,11 +56,10 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-08-28 17:42:50.261377", + "modified": "2024-10-10 12:03:50.022444", "modified_by": "Administrator", "module": "Accounts", "name": "Unreconcile Payment", - "naming_rule": "Expression", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js index 9335a8cd65a8..2684c87a22ab 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -47,7 +47,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = { }, ], formatter: function (value, row, column, data, default_formatter, filter) { - if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") { + if (column.fieldname == "payment_entry" && value == __("Cheques and Deposits incorrectly cleared")) { column.link_onclick = "frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()"; } diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index a809edd33cb9..388b7d8fe86b 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -474,10 +474,13 @@ def update_parent_account_names(accounts): for d in accounts: if d.account_number: - account_name = d.account_number + " - " + d.account_name + account_key = d.account_number + " - " + d.account_name else: - account_name = d.account_name - name_to_account_map[d.name] = account_name + account_key = d.account_name + + d.account_key = account_key + + name_to_account_map[d.name] = account_key for account in accounts: if account.parent_account: @@ -510,33 +513,26 @@ def get_subsidiary_companies(company): def get_accounts(root_type, companies): accounts = [] - added_accounts = [] for company in companies: - for account in frappe.get_all( - "Account", - fields=[ - "name", - "is_group", - "company", - "parent_account", - "lft", - "rgt", - "root_type", - "report_type", - "account_name", - "account_number", - ], - filters={"company": company, "root_type": root_type}, - ): - if account.account_number: - account_key = account.account_number + "-" + account.account_name - else: - account_key = account.account_name - - if account_key not in added_accounts: - accounts.append(account) - added_accounts.append(account_key) + accounts.extend( + frappe.get_all( + "Account", + fields=[ + "name", + "is_group", + "company", + "parent_account", + "lft", + "rgt", + "root_type", + "report_type", + "account_name", + "account_number", + ], + filters={"company": company, "root_type": root_type}, + ) + ) return accounts @@ -775,15 +771,17 @@ def add_total_row(out, root_type, balance_must_be, companies, company_currency): def filter_accounts(accounts, depth=10): parent_children_map = {} accounts_by_name = {} + added_accounts = [] + for d in accounts: - if d.account_number: - account_name = d.account_number + " - " + d.account_name - else: - account_name = d.account_name + if d.account_key in added_accounts: + continue + + added_accounts.append(d.account_key) d["company_wise_opening_bal"] = defaultdict(float) - accounts_by_name[account_name] = d + accounts_by_name[d.account_key] = d - parent_children_map.setdefault(d.parent_account or None, []).append(d) + parent_children_map.setdefault(d.parent_account_name or None, []).append(d) filtered_accounts = [] @@ -795,7 +793,7 @@ def add_to_list(parent, level): for child in children: child.indent = level filtered_accounts.append(child) - add_to_list(child.name, level + 1) + add_to_list(child.account_key, level + 1) add_to_list(None, 0) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 73882d39cec9..a65fe30f050f 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -392,6 +392,7 @@ def create_rfq_items(sq_doc, supplier, data): "material_request", "material_request_item", "stock_qty", + "uom", ]: args[field] = data.get(field) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 827f204e40e9..3b4d32b7305a 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -456,6 +456,16 @@ def set_incoming_rate(self): raise_error_if_no_rate=False, ) + if ( + not d.incoming_rate + and self.get("return_against") + and self.get("is_return") + and get_valuation_method(d.item_code) == "Moving Average" + ): + d.incoming_rate = get_rate_for_return( + self.doctype, self.name, d.item_code, self.return_against, item_row=d + ) + # For internal transfers use incoming rate as the valuation rate if self.is_internal_transfer(): if self.doctype == "Delivery Note" or self.get("update_stock"): diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a4d8e3efee16..b37658b77842 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -50,6 +50,21 @@ def validate(self): self.set_rate_of_stock_uom() self.validate_internal_transfer() self.validate_putaway_capacity() + self.reset_conversion_factor() + + def reset_conversion_factor(self): + for row in self.get("items"): + if row.uom != row.stock_uom: + continue + + if row.conversion_factor != 1.0: + row.conversion_factor = 1.0 + frappe.msgprint( + _( + "Conversion factor for item {0} has been reset to 1.0 as the uom {1} is same as stock uom {2}." + ).format(bold(row.item_code), bold(row.uom), bold(row.stock_uom)), + alert=True, + ) def make_gl_entries(self, gl_entries=None, from_repost=False, via_landed_cost_voucher=False): if self.docstatus == 2: diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index f201228c2b1c..293066cde388 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -30,7 +30,7 @@ from erpnext.selling.doctype.customer.customer import check_credit_limit from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.stock.doctype.item.item import get_item_defaults -from erpnext.stock.get_item_details import get_default_bom, get_price_list_rate +from erpnext.stock.get_item_details import get_bin_details, get_default_bom, get_price_list_rate from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty form_grid_templates = {"items": "templates/form_grid/item_grid.html"} @@ -607,6 +607,9 @@ def update_item(source, target, source_parent): target.project = source_parent.project target.qty = get_remaining_qty(source) target.stock_qty = flt(target.qty) * flt(target.conversion_factor) + target.actual_qty = get_bin_details( + target.item_code, target.warehouse, source_parent.company, True + ).get("actual_qty", 0) args = target.as_dict().copy() args.update( diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 74f781d89828..b1207ee97a19 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -30,6 +30,7 @@ ) from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.get_item_details import get_bin_details class TestSalesOrder(AccountsTestMixin, FrappeTestCase): @@ -99,6 +100,12 @@ def test_make_material_request(self): self.assertEqual(mr.material_request_type, "Purchase") self.assertEqual(len(mr.get("items")), len(so.get("items"))) + for item in mr.get("items"): + actual_qty = get_bin_details(item.item_code, item.warehouse, mr.company, True).get( + "actual_qty", 0 + ) + self.assertEqual(flt(item.actual_qty), actual_qty) + def test_make_delivery_note(self): so = make_sales_order(do_not_submit=True) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index e9b07e76b2de..85fbda5a28e4 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1449,6 +1449,46 @@ def test_internal_transfer_for_non_stock_item(self): self.assertEqual(so.items[0].rate, rate) self.assertEqual(dn.items[0].rate, so.items[0].rate) + def test_batch_return_dn(self): + from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return + + item_code = make_item( + "Test Batch Return DN Item 1", + properties={ + "has_batch_no": 1, + "valuation_method": "Moving Average", + "create_new_batch": 1, + "batch_number_series": "TBRDN1-.#####", + "is_stock_item": 1, + }, + ).name + + se = make_stock_entry(item_code=item_code, target="_Test Warehouse - _TC", qty=5, basic_rate=100) + + batch_no = se.items[0].batch_no + dn = create_delivery_note( + item_code=item_code, + qty=5, + rate=500, + batch_no=batch_no, + ) + + dn_return = make_sales_return(dn.name) + dn_return.save().submit() + + self.assertEqual(dn_return.items[0].qty, 5 * -1) + + returned_batch_no = dn_return.items[0].batch_no + self.assertEqual(batch_no, returned_batch_no) + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_no": dn_return.name, "voucher_type": "Delivery Note"}, + "stock_value_difference", + ) + + self.assertEqual(stock_value_difference, 100.0 * 5) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c87699f5f8c1..1c4feedbcfb0 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2668,6 +2668,21 @@ def test_sles_with_same_posting_datetime_and_creation(self): self.assertEqual(data[0].get("bal_qty"), 50.0) + def test_same_stock_and_transaction_uom_conversion_factor(self): + item_code = "Test Item for Same Stock and Transaction UOM Conversion Factor" + create_item(item_code) + + pr = make_purchase_receipt( + item_code=item_code, + qty=10, + rate=100, + stock_uom="Nos", + transaction_uom="Nos", + conversion_factor=10, + ) + + self.assertEqual(pr.items[0].conversion_factor, 1.0) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 8ebc2aa9c048..a81b57168de0 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -775,6 +775,15 @@ def get_incoming_outgoing_rate_from_transaction(self, sle): } ) + if not rate and sle.voucher_type in ["Delivery Note", "Sales Invoice"]: + rate = get_rate_for_return( + sle.voucher_type, + sle.voucher_no, + sle.item_code, + voucher_detail_no=sle.voucher_detail_no, + sle=sle, + ) + else: rate = get_rate_for_return( sle.voucher_type,