diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1f670f2bec3..56bcb22c9bb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -69,12 +69,14 @@ repos: rev: v0.2.0 hooks: - id: ruff - name: "Run ruff linter and apply fixes" - args: ["--fix"] + name: "Run ruff import sorter" + args: ["--select=I", "--fix"] + + - id: ruff + name: "Run ruff linter" - id: ruff-format - name: "Format Python code" - + name: "Run ruff formatter" ci: autoupdate_schedule: weekly diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json index ff4ac50850f..496dd44ad86 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json @@ -82,7 +82,7 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2020-11-05 12:16:53.081573", + "modified": "2024-05-27 17:29:55.560840", "modified_by": "Administrator", "module": "Accounts", "name": "Fiscal Year", @@ -126,6 +126,10 @@ { "read": 1, "role": "Stock Manager" + }, + { + "read": 1, + "role": "Auditor" } ], "show_name_in_global_search": 1, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index cda0adc7257..f3785800de0 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -171,7 +171,7 @@ frappe.ui.form.on("Journal Entry", { !(frm.doc.accounts || []).length || ((frm.doc.accounts || []).length === 1 && !frm.doc.accounts[0].account) ) { - if (in_list(["Bank Entry", "Cash Entry"], frm.doc.voucher_type)) { + if (["Bank Entry", "Cash Entry"].includes(frm.doc.voucher_type)) { return frappe.call({ type: "GET", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account", @@ -283,7 +283,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro filters: [[jvd.reference_type, "docstatus", "=", 1]], }; - if (in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) { + if (["Sales Invoice", "Purchase Invoice"].includes(jvd.reference_type)) { out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]); // Filter by cost center if (jvd.cost_center) { @@ -295,7 +295,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]); } - if (in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) { + if (["Sales Order", "Purchase Order"].includes(jvd.reference_type)) { // party_type and party mandatory frappe.model.validate_missing(jvd, "party_type"); frappe.model.validate_missing(jvd, "party"); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index c0716ff19ae..ad1d200ec53 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -21,7 +21,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_from", function() { frm.events.validate_company(frm); - var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ? + var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { @@ -75,7 +75,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); - var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? + var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { @@ -121,7 +121,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query('payment_term', 'references', function(frm, cdt, cdn) { const child = locals[cdt][cdn]; - if (in_list(['Purchase Invoice', 'Sales Invoice'], child.reference_doctype) && child.reference_name) { + if (['Purchase Invoice', 'Sales Invoice'].includes(child.reference_doctype) && child.reference_name) { return { query: "erpnext.controllers.queries.get_payment_terms_for_references", filters: { @@ -485,7 +485,7 @@ frappe.ui.form.on('Payment Entry', { if (frm.doc.paid_from_account_currency == company_currency) { frm.set_value("source_exchange_rate", 1); } else if (frm.doc.paid_from){ - if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) { + if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) { let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", @@ -853,7 +853,7 @@ frappe.ui.form.on('Payment Entry', { } var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding; - } else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) { + } else if (["Customer", "Supplier"].includes(frm.doc.party_type)) { total_negative_outstanding = flt(total_negative_outstanding, precision("outstanding_amount")) if(paid_amount > total_negative_outstanding) { if(total_negative_outstanding == 0) { @@ -988,7 +988,7 @@ frappe.ui.form.on('Payment Entry', { } if(frm.doc.party_type=="Customer" && - !in_list(["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"], row.reference_doctype) + !["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"].includes(row.reference_doctype) ) { frappe.model.set_value(row.doctype, row.name, "reference_doctype", null); frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning", [row.idx])); @@ -996,7 +996,7 @@ frappe.ui.form.on('Payment Entry', { } if(frm.doc.party_type=="Supplier" && - !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.reference_doctype) + !["Purchase Order", "Purchase Invoice", "Journal Entry"].includes(row.reference_doctype) ) { frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null); frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx])); @@ -1080,7 +1080,7 @@ frappe.ui.form.on('Payment Entry', { bank_account: function(frm) { const field = frm.doc.payment_type == "Pay" ? "paid_from":"paid_to"; - if (frm.doc.bank_account && in_list(['Pay', 'Receive'], frm.doc.payment_type)) { + if (frm.doc.bank_account && ['Pay', 'Receive'].includes(frm.doc.payment_type)) { frappe.call({ method: "erpnext.accounts.doctype.bank_account.bank_account.get_bank_account_details", args: { @@ -1395,8 +1395,9 @@ frappe.ui.form.on('Payment Entry Reference', { args: { reference_doctype: row.reference_doctype, reference_name: row.reference_name, - party_account_currency: frm.doc.payment_type=="Receive" ? - frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency + party_account_currency: frm.doc.payment_type == "Receive" ? frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency, + party_type: frm.doc.party_type, + party: frm.doc.party, }, callback: function(r, rt) { if(r.message) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 370e1deaa40..fa5aabe1268 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -9,8 +9,7 @@ import frappe from frappe import ValidationError, _, qb, scrub, throw from frappe.utils import cint, comma_or, flt, getdate, nowdate from frappe.utils.data import comma_and, fmt_money -from pypika import Case -from pypika.functions import Coalesce, Sum +from pypika.functions import Sum import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions @@ -348,7 +347,11 @@ class PaymentEntry(AccountsController): continue ref_details = get_reference_details( - d.reference_doctype, d.reference_name, self.party_account_currency + d.reference_doctype, + d.reference_name, + self.party_account_currency, + self.party_type, + self.party, ) # Only update exchange rate when the reference is Journal Entry @@ -1883,33 +1886,42 @@ def get_company_defaults(company): return frappe.get_cached_value("Company", company, fields, as_dict=1) -def get_outstanding_on_journal_entry(name): - gl = frappe.qb.DocType("GL Entry") - res = ( - frappe.qb.from_(gl) - .select( - Case() - .when( - gl.party_type == "Customer", - Coalesce(Sum(gl.debit_in_account_currency - gl.credit_in_account_currency), 0), - ) - .else_(Coalesce(Sum(gl.credit_in_account_currency - gl.debit_in_account_currency), 0)) - .as_("outstanding_amount") - ) +def get_outstanding_on_journal_entry(voucher_no, party_type, party): + ple = frappe.qb.DocType("Payment Ledger Entry") + + outstanding = ( + frappe.qb.from_(ple) + .select(Sum(ple.amount_in_account_currency)) .where( - (Coalesce(gl.party_type, "") != "") - & (gl.is_cancelled == 0) - & ((gl.voucher_no == name) | (gl.against_voucher == name)) + (ple.against_voucher_no == voucher_no) + & (ple.party_type == party_type) + & (ple.party == party) + & (ple.delinked == 0) ) - ).run(as_dict=True) + ).run() - outstanding_amount = res[0].get("outstanding_amount", 0) if res else 0 + outstanding_amount = outstanding[0][0] if outstanding else 0 - return outstanding_amount + total = ( + frappe.qb.from_(ple) + .select(Sum(ple.amount_in_account_currency)) + .where( + (ple.voucher_no == voucher_no) + & (ple.party_type == party_type) + & (ple.party == party) + & (ple.delinked == 0) + ) + ).run() + + total_amount = total[0][0] if total else 0 + + return outstanding_amount, total_amount @frappe.whitelist() -def get_reference_details(reference_doctype, reference_name, party_account_currency): +def get_reference_details( + reference_doctype, reference_name, party_account_currency, party_type=None, party=None +): total_amount = outstanding_amount = exchange_rate = None ref_doc = frappe.get_doc(reference_doctype, reference_name) @@ -1920,12 +1932,13 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre exchange_rate = 1 elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1: - total_amount = ref_doc.get("total_amount") if ref_doc.multi_currency: exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) else: exchange_rate = 1 - outstanding_amount = get_outstanding_on_journal_entry(reference_name) + outstanding_amount, total_amount = get_outstanding_on_journal_entry( + reference_name, party_type, party + ) elif reference_doctype != "Journal Entry": if not total_amount: diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index c600198999e..28dfae29966 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1087,7 +1087,9 @@ class TestPaymentEntry(FrappeTestCase): pe.source_exchange_rate = 50 pe.save() - ref_details = get_reference_details(so.doctype, so.name, pe.paid_from_account_currency) + ref_details = get_reference_details( + so.doctype, so.name, pe.paid_from_account_currency, "Customer", so.customer + ) expected_response = { "total_amount": 5000.0, "outstanding_amount": 5000.0, diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e5a6040c735..e45aa512fe8 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -28,7 +28,7 @@ frappe.ui.form.on("Payment Request", "refresh", function (frm) { if ( frm.doc.payment_request_type == "Inward" && frm.doc.payment_channel !== "Phone" && - !in_list(["Initiated", "Paid"], frm.doc.status) && + !["Initiated", "Paid"].includes(frm.doc.status) && !frm.doc.__islocal && frm.doc.docstatus == 1 ) { diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 583735b1cc6..0fa2e7835eb 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -596,7 +596,11 @@ def update_payment_req_status(doc, method): if payment_request_name: ref_details = get_reference_details( - ref.reference_doctype, ref.reference_name, doc.party_account_currency + ref.reference_doctype, + ref.reference_name, + doc.party_account_currency, + doc.party_type, + doc.party, ) pay_req_doc = frappe.get_doc("Payment Request", payment_request_name) status = pay_req_doc.status diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py index f61bfdb58ab..4e4a7492a00 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py @@ -33,7 +33,7 @@ class POSClosingEntry(StatusUpdater): for key, value in pos_occurences.items(): if len(value) > 1: error_list.append( - _(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}") + _("{0} is added multiple times on rows: {1}").format(frappe.bold(key), frappe.bold(value)) ) if error_list: diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 1302a0c386c..a183a5ee6aa 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -29,7 +29,7 @@ class POSInvoiceMergeLog(Document): for key, value in pos_occurences.items(): if len(value) > 1: error_list.append( - _(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}") + _("{0} is added multiple times on rows: {1}").format(frappe.bold(key), frappe.bold(value)) ) if error_list: @@ -441,7 +441,7 @@ def create_merge_logs(invoice_by_customer, closing_entry=None): if closing_entry: closing_entry.set_status(update=True, status="Failed") if isinstance(error_message, list): - error_message = frappe.json.dumps(error_message) + error_message = json.dumps(error_message) closing_entry.db_set("error_message", error_message) raise diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index e8e80449292..6f191c106c9 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -74,15 +74,21 @@ "discount_amount", "discount_percentage", "for_price_list", - "section_break_13", - "threshold_percentage", - "priority", + "dynamic_condition_tab", "condition", - "column_break_66", + "section_break_13", "apply_multiple_pricing_rules", "apply_discount_on_rate", + "column_break_66", + "threshold_percentage", + "validate_pricing_rule_section", "validate_applied_rule", + "column_break_texp", "rule_description", + "priority_section", + "has_priority", + "column_break_sayg", + "priority", "help_section", "pricing_rule_help", "reference_section", @@ -477,7 +483,7 @@ { "collapsible": 1, "fieldname": "section_break_13", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Advanced Settings" }, { @@ -487,6 +493,7 @@ "label": "Threshold for Suggestion (In Percentage)" }, { + "depends_on": "has_priority", "description": "Higher the number, higher the priority", "fieldname": "priority", "fieldtype": "Select", @@ -513,6 +520,7 @@ { "default": "0", "depends_on": "eval:doc.price_or_product_discount == 'Price'", + "description": "If enabled, then system will only validate the pricing rule and not apply automatically. User has to manually set the discount percentage / margin / free items to validate the pricing rule", "fieldname": "validate_applied_rule", "fieldtype": "Check", "label": "Validate Applied Rule" @@ -525,7 +533,8 @@ }, { "fieldname": "help_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", + "label": "Help Article", "options": "Simple" }, { @@ -603,12 +612,42 @@ "fieldname": "apply_recursion_over", "fieldtype": "Float", "label": "Apply Recursion Over (As Per Transaction UOM)" + }, + { + "fieldname": "priority_section", + "fieldtype": "Section Break", + "label": "Priority" + }, + { + "fieldname": "dynamic_condition_tab", + "fieldtype": "Tab Break", + "label": "Dynamic Condition" + }, + { + "fieldname": "validate_pricing_rule_section", + "fieldtype": "Section Break", + "label": "Validate Pricing Rule" + }, + { + "fieldname": "column_break_texp", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_sayg", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "Enable this checkbox even if you want to set the zero priority", + "fieldname": "has_priority", + "fieldtype": "Check", + "label": "Has Priority" } ], "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2023-02-14 04:53:34.887358", + "modified": "2024-05-17 13:16:34.496704", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index bc30118a0c6..30dbb14f84c 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -47,6 +47,12 @@ class PricingRule(Document): frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on)) def validate_mandatory(self): + if self.has_priority and not self.priority: + throw(_("Priority is mandatory"), frappe.MandatoryError, _("Please Set Priority")) + + if self.priority and not self.has_priority: + self.has_priority = 1 + for apply_on, field in apply_on_dict.items(): if self.apply_on == apply_on and len(self.get(field) or []) < 1: throw(_("{0} is not added in the table").format(apply_on), frappe.MandatoryError) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 18658e6e4a6..6f1cee61637 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -1031,6 +1031,62 @@ class TestPricingRule(unittest.TestCase): frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + def test_priority_of_multiple_pricing_rules(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule 1", + "name": "_Test Pricing Rule 1", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "price_or_product_discount": "Price", + "rate_or_discount": "Discount Percentage", + "discount_percentage": 10, + "has_priority": 1, + "priority": 1, + "company": "_Test Company", + } + + frappe.get_doc(test_record.copy()).insert() + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule 2", + "name": "_Test Pricing Rule 2", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "price_or_product_discount": "Price", + "rate_or_discount": "Discount Percentage", + "discount_percentage": 20, + "has_priority": 1, + "priority": 3, + "company": "_Test Company", + } + + frappe.get_doc(test_record.copy()).insert() + + so = make_sales_order(item_code="_Test Item", qty=1, price_list_rate=1000, do_not_submit=True) + self.assertEqual(so.items[0].discount_percentage, 20) + self.assertEqual(so.items[0].rate, 800) + + frappe.delete_doc_if_exists("Sales Order", so.name) + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1") + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2") + test_dependencies = ["Campaign"] @@ -1059,6 +1115,7 @@ def make_pricing_rule(**args): "priority": args.priority or 1, "discount_amount": args.discount_amount or 0.0, "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0, + "has_priority": args.has_priority or 0, } ) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 2a78bebd103..ab9894a52a8 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -33,6 +33,9 @@ def get_pricing_rules(args, doc=None): for apply_on in ["Item Code", "Item Group", "Brand"]: pricing_rules.extend(_get_pricing_rules(apply_on, args, values)) + if pricing_rules and pricing_rules[0].has_priority: + continue + if pricing_rules and not apply_multiple_pricing_rules(pricing_rules): break diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py index c7535e76d7e..b1419020f0f 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py @@ -1,6 +1,8 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import json + import frappe from frappe import _, qb from frappe.model.document import Document @@ -479,7 +481,7 @@ def is_any_doc_running(for_filter: str | dict | None = None) -> str | None: running_doc = None if for_filter: if isinstance(for_filter, str): - for_filter = frappe.json.loads(for_filter) + for_filter = json.loads(for_filter) running_doc = frappe.db.get_value( "Process Payment Reconciliation", diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 0c266ce7947..a602e0d7df3 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -104,7 +104,7 @@ def set_ageing(doc, entry): ageing_filters = frappe._dict( { "company": doc.company, - "report_date": doc.to_date, + "report_date": doc.posting_date, "ageing_based_on": doc.ageing_based_on, "range1": 30, "range2": 60, diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html index 647600a9fea..bf8de073853 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts_accounts_receivable.html @@ -340,10 +340,11 @@ - - - - + + + + + @@ -352,6 +353,7 @@ +
30 Days60 Days90 Days120 Days0 - 30 Days30 - 60 Days60 - 90 Days90 - 120 DaysAbove 120 Days
{{ frappe.utils.fmt_money(ageing.range2, currency=data[0]["currency"]) }} {{ frappe.utils.fmt_money(ageing.range3, currency=data[0]["currency"]) }} {{ frappe.utils.fmt_money(ageing.range4, currency=data[0]["currency"]) }}{{ frappe.utils.fmt_money(ageing.range5, currency=filters.presentation_currency) }}
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 665fc6edcc9..c29ec5fd12f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -440,8 +440,12 @@ function hide_fields(doc) { var item_fields_stock = ['warehouse_section', 'received_qty', 'rejected_qty']; - cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock, - (cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false)); + if (cur_frm.fields_dict["items"]) { + cur_frm.fields_dict["items"].grid.set_column_disp( + item_fields_stock, + cint(doc.update_stock) == 1 || cint(doc.is_return) == 1 ? true : false + ); + } cur_frm.refresh_fields(); } diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b53627a682f..e0debba6fd5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -284,7 +284,7 @@ class PurchaseInvoice(BuyingController): stock_not_billed_account = self.get_company_default("stock_received_but_not_billed") stock_items = self.get_stock_items() - asset_received_but_not_billed = None + self.asset_received_but_not_billed = None if self.update_stock: self.validate_item_code() @@ -367,26 +367,45 @@ class PurchaseInvoice(BuyingController): frappe.msgprint(msg, title=_("Expense Head Changed")) item.expense_account = stock_not_billed_account - elif item.is_fixed_asset and item.pr_detail: - if not asset_received_but_not_billed: - asset_received_but_not_billed = self.get_company_default("asset_received_but_not_billed") - item.expense_account = asset_received_but_not_billed elif item.is_fixed_asset: - account_type = ( - "capital_work_in_progress_account" - if is_cwip_accounting_enabled(item.asset_category) - else "fixed_asset_account" - ) - asset_category_account = get_asset_category_account( - account_type, item=item.item_code, company=self.company - ) - if not asset_category_account: - form_link = get_link_to_form("Asset Category", item.asset_category) - throw( - _("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company), - title=_("Missing Account"), + account = None + if item.pr_detail: + if not self.asset_received_but_not_billed: + self.asset_received_but_not_billed = self.get_company_default( + "asset_received_but_not_billed" + ) + + # check if 'Asset Received But Not Billed' account is credited in Purchase receipt or not + arbnb_booked_in_pr = frappe.db.get_value( + "GL Entry", + { + "voucher_type": "Purchase Receipt", + "voucher_no": item.purchase_receipt, + "account": self.asset_received_but_not_billed, + }, + "name", ) - item.expense_account = asset_category_account + if arbnb_booked_in_pr: + account = self.asset_received_but_not_billed + + if not account: + account_type = ( + "capital_work_in_progress_account" + if is_cwip_accounting_enabled(item.asset_category) + else "fixed_asset_account" + ) + account = get_asset_category_account( + account_type, item=item.item_code, company=self.company + ) + if not account: + form_link = get_link_to_form("Asset Category", item.asset_category) + throw( + _("Please set Fixed Asset Account in {} against {}.").format( + form_link, self.company + ), + title=_("Missing Account"), + ) + item.expense_account = account elif not item.expense_account and for_validate: throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name)) @@ -982,7 +1001,7 @@ class PurchaseInvoice(BuyingController): pr_items = frappe.get_all( "Purchase Receipt Item", filters={"parent": ("in", linked_purchase_receipts)}, - fields=["name", "provisional_expense_account", "qty", "base_rate"], + fields=["name", "provisional_expense_account", "qty", "base_rate", "rate"], ) default_provisional_account = self.get_company_default("default_provisional_account") provisional_accounts = set( @@ -1010,6 +1029,7 @@ class PurchaseInvoice(BuyingController): "provisional_account": item.provisional_expense_account or default_provisional_account, "qty": item.qty, "base_rate": item.base_rate, + "rate": item.rate, "has_provisional_entry": item.name in rows_with_provisional_entries, } @@ -1026,7 +1046,10 @@ class PurchaseInvoice(BuyingController): self.posting_date, pr_item.get("provisional_account"), reverse=1, - item_amount=(min(item.qty, pr_item.get("qty")) * pr_item.get("base_rate")), + item_amount=( + (min(item.qty, pr_item.get("qty")) * pr_item.get("rate")) + * purchase_receipt_doc.get("conversion_rate") + ), ) def update_gross_purchase_amount_for_linked_assets(self, item): diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json index 5b7cd2b0b20..0d74b2150f7 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.json @@ -1,7 +1,5 @@ { "actions": [], - "allow_rename": 1, - "autoname": "format:ACC-REPOST-{#####}", "creation": "2023-07-04 13:07:32.923675", "default_view": "List", "doctype": "DocType", @@ -55,14 +53,15 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-09-26 14:21:27.362567", + "modified": "2024-06-03 17:30:37.012593", "modified_by": "Administrator", "module": "Accounts", "name": "Repost Accounting Ledger", - "naming_rule": "Expression", "owner": "Administrator", "permissions": [ { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -71,7 +70,9 @@ "read": 1, "report": 1, "role": "System Manager", + "select": 1, "share": 1, + "submit": 1, "write": 1 } ], diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json index 8aa0a840c7e..dd314ca6601 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json @@ -17,7 +17,7 @@ "in_create": 1, "issingle": 1, "links": [], - "modified": "2023-11-07 14:24:13.321522", + "modified": "2024-06-06 13:56:37.908879", "modified_by": "Administrator", "module": "Accounts", "name": "Repost Accounting Ledger Settings", @@ -30,13 +30,17 @@ "print": 1, "read": 1, "role": "Administrator", + "select": 1, "share": 1, "write": 1 }, { + "create": 1, + "delete": 1, "read": 1, "role": "System Manager", - "select": 1 + "select": 1, + "write": 1 } ], "sort_field": "modified", diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.json b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.json index ed8d395a0ec..2b4efead7f3 100644 --- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.json +++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_rename": 1, "creation": "2022-10-19 21:59:33.553852", "doctype": "DocType", "editable_grid": 1, @@ -99,13 +98,15 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-09-26 14:21:35.719727", + "modified": "2024-06-03 17:31:04.472279", "modified_by": "Administrator", "module": "Accounts", "name": "Repost Payment Ledger", "owner": "Administrator", "permissions": [ { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -114,7 +115,9 @@ "read": 1, "report": 1, "role": "System Manager", + "select": 1, "share": 1, + "submit": 1, "write": 1 }, { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index b2672424af9..ad857840147 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -200,7 +200,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e if(cur_frm.meta._default_print_format) { cur_frm.meta.default_print_format = cur_frm.meta._default_print_format; cur_frm.meta._default_print_format = null; - } else if(in_list([cur_frm.pos_print_format, cur_frm.return_print_format], cur_frm.meta.default_print_format)) { + } else if([cur_frm.pos_print_format, cur_frm.return_print_format].includes(cur_frm.meta.default_print_format)) { cur_frm.meta.default_print_format = null; cur_frm.meta._default_print_format = null; } diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 87d297f4aa1..f22218fcd33 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt import copy +import json import frappe from frappe.model.dynamic_links import get_dynamic_link_map @@ -2978,10 +2979,8 @@ class TestSalesInvoice(FrappeTestCase): ["2021-06-30", 20000.0, 21366.12, True], ["2022-06-30", 20000.0, 41366.12, False], ["2023-06-30", 20000.0, 61366.12, False], - ["2024-06-30", 20000.0, 81366.12, False], - ["2025-06-06", 18633.88, 100000.0, False], + ["2024-06-06", 38633.88, 100000.0, False], ] - for i, schedule in enumerate(asset.schedules): self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) self.assertEqual(expected_values[i][1], schedule.depreciation_amount) @@ -3479,9 +3478,9 @@ class TestSalesInvoice(FrappeTestCase): map_docs( method="erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice", - source_names=frappe.json.dumps([dn1.name, dn2.name]), + source_names=json.dumps([dn1.name, dn2.name]), target_doc=si, - args=frappe.json.dumps({"customer": dn1.customer, "merge_taxes": 1, "filtered_children": []}), + args=json.dumps({"customer": dn1.customer, "merge_taxes": 1, "filtered_children": []}), ) si.save().submit() diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 87cf1ae80d6..5e7a75b8a3f 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -867,7 +867,8 @@ "label": "Purchase Order", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "column_break_92", @@ -892,7 +893,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:34:10.479329", + "modified": "2024-05-23 16:36:18.970862", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py index 091bccf5099..6ba1de35238 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py @@ -1,6 +1,8 @@ # Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import json + import frappe from frappe import _, qb from frappe.model.document import Document @@ -142,7 +144,7 @@ def get_linked_payments_for_doc( @frappe.whitelist() def create_unreconcile_doc_for_selection(selections=None): if selections: - selections = frappe.json.loads(selections) + selections = json.loads(selections) # assuming each row is a unique voucher for row in selections: unrecon = frappe.new_doc("Unreconcile Payment") diff --git a/erpnext/accounts/report/account_balance/account_balance.py b/erpnext/accounts/report/account_balance/account_balance.py index 6f9f7ebcb8d..f385c71a9f4 100644 --- a/erpnext/accounts/report/account_balance/account_balance.py +++ b/erpnext/accounts/report/account_balance/account_balance.py @@ -49,7 +49,6 @@ def get_conditions(filters): if filters.account_type: conditions["account_type"] = filters.account_type - return conditions if filters.company: conditions["company"] = filters.company diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 25143e555d3..be42da8423f 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -1028,20 +1028,6 @@ class ReceivablePayableReport: fieldtype="Link", options="Contact", ) - if self.filters.party_type == "Customer": - self.add_column( - _("Customer Name"), - fieldname="customer_name", - fieldtype="Link", - options="Customer", - ) - elif self.filters.party_type == "Supplier": - self.add_column( - _("Supplier Name"), - fieldname="supplier_name", - fieldtype="Link", - options="Supplier", - ) self.add_column(label=_("Cost Center"), fieldname="cost_center", fieldtype="Data") self.add_column(label=_("Voucher Type"), fieldname="voucher_type", fieldtype="Data") diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js index 215d431e786..88ec0881585 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js @@ -15,14 +15,14 @@ frappe.query_reports["Asset Depreciations and Balances"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, { diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js index c15d43012db..e8ae2a1dabe 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js @@ -7,7 +7,7 @@ frappe.query_reports["Bank Clearance Summary"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], width: "80", }, { diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js index 27c0c660f01..149b258248c 100644 --- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js +++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js @@ -38,14 +38,14 @@ frappe.require("assets/erpnext/js/financial_statements.js", function () { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, { diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index f70aec743d2..0c4e1eca3f9 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -211,7 +211,8 @@ def get_conditions(filters): if filters.get("account"): filters.account = get_accounts_with_children(filters.account) - conditions.append("account in %(account)s") + if filters.account: + conditions.append("account in %(account)s") if filters.get("cost_center"): filters.cost_center = get_cost_centers_with_children(filters.cost_center) @@ -316,7 +317,7 @@ def get_accounts_with_children(accounts): else: frappe.throw(_("Account: {0} does not exist").format(d)) - return list(set(all_accounts)) + return list(set(all_accounts)) if all_accounts else None def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries): diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index 890befe8596..64a9838f54b 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -15,14 +15,14 @@ frappe.query_reports["Gross Profit"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, { diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 2f87b5be836..06afb4c0e8f 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -5,14 +5,15 @@ import frappe from frappe import _ from frappe.utils import flt +from pypika import Order import erpnext from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import ( add_sub_total_row, add_total_row, + apply_group_by_conditions, get_grand_total, get_group_by_and_display_fields, - get_group_by_conditions, get_tax_accounts, ) from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns @@ -29,7 +30,7 @@ def _execute(filters=None, additional_table_columns=None): company_currency = erpnext.get_company_currency(filters.company) - item_list = get_items(filters, get_query_columns(additional_table_columns)) + item_list = get_items(filters, additional_table_columns) aii_account_map = get_aii_accounts() if item_list: itemised_tax, tax_columns = get_tax_accounts( @@ -287,59 +288,87 @@ def get_columns(additional_table_columns, filters): return columns -def get_conditions(filters): - conditions = "" +def apply_conditions(query, pi, pii, filters): + for opts in ("company", "supplier", "item_code", "mode_of_payment"): + if filters.get(opts): + query = query.where(pi[opts] == filters[opts]) - for opts in ( - ("company", " and `tabPurchase Invoice`.company=%(company)s"), - ("supplier", " and `tabPurchase Invoice`.supplier = %(supplier)s"), - ("item_code", " and `tabPurchase Invoice Item`.item_code = %(item_code)s"), - ("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"), - ("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s"), - ("mode_of_payment", " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"), - ): - if filters.get(opts[0]): - conditions += opts[1] + if filters.get("from_date"): + query = query.where(pi.posting_date >= filters.get("from_date")) + + if filters.get("to_date"): + query = query.where(pi.posting_date <= filters.get("to_date")) + + if filters.get("item_group"): + query = query.where(pii.item_group == filters.get("item_group")) if not filters.get("group_by"): - conditions += ( - "ORDER BY `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc" - ) + query = query.orderby(pi.posting_date, order=Order.desc) + query = query.orderby(pii.item_group, order=Order.desc) else: - conditions += get_group_by_conditions(filters, "Purchase Invoice") + query = apply_group_by_conditions(filters, "Purchase Invoice") - return conditions + return query -def get_items(filters, additional_query_columns): - conditions = get_conditions(filters) - if additional_query_columns: - additional_query_columns = "," + ",".join(additional_query_columns) - return frappe.db.sql( - """ - select - `tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`, - `tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company, - `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, - `tabPurchase Invoice`.unrealized_profit_loss_account, - `tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description, - `tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` - ,`tabPurchase Invoice Item`.`item_group` as pi_item_group, - `tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group, - `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, - `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, - `tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`, - `tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`, - `tabPurchase Invoice`.`supplier_name`, `tabPurchase Invoice`.`mode_of_payment` {} - from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem` - where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and - `tabItem`.name = `tabPurchase Invoice Item`.`item_code` and - `tabPurchase Invoice`.docstatus = 1 {} - """.format(additional_query_columns, conditions), - filters, - as_dict=1, +def get_items(filters, additional_table_columns): + pi = frappe.qb.DocType("Purchase Invoice") + pii = frappe.qb.DocType("Purchase Invoice Item") + Item = frappe.qb.DocType("Item") + query = ( + frappe.qb.from_(pi) + .join(pii) + .on(pi.name == pii.parent) + .left_join(Item) + .on(pii.item_code == Item.name) + .select( + pii.name, + pii.parent, + pi.posting_date, + pi.credit_to, + pi.company, + pi.supplier, + pi.remarks, + pi.base_net_total, + pi.unrealized_profit_loss_account, + pii.item_code, + pii.description, + pii.item_group, + pii.item_name.as_("pi_item_name"), + pii.item_group.as_("pi_item_group"), + Item.item_name.as_("i_item_name"), + Item.item_group.as_("i_item_group"), + pii.project, + pii.purchase_order, + pii.purchase_receipt, + pii.po_detail, + pii.expense_account, + pii.stock_qty, + pii.stock_uom, + pii.base_net_amount, + pi.supplier_name, + pi.mode_of_payment, + ) + .where(pi.docstatus == 1) ) + if filters.get("supplier"): + query = query.where(pi.supplier == filters["supplier"]) + if filters.get("company"): + query = query.where(pi.company == filters["company"]) + + if additional_table_columns: + for column in additional_table_columns: + if column.get("_doctype"): + table = frappe.qb.DocType(column.get("_doctype")) + query = query.select(table[column.get("fieldname")]) + else: + query = query.select(pi[column.get("fieldname")]) + + query = apply_conditions(query, pi, pii, filters) + + return query.run(as_dict=True) + def get_aii_accounts(): return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany")) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 7ed2ebd89ab..1f155de63a0 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -41,6 +41,12 @@ frappe.query_reports["Item-wise Sales Register"] = { label: __("Warehouse"), fieldtype: "Link", options: "Warehouse", + get_query: function () { + const company = frappe.query_report.get_filter_value("company"); + return { + filters: { company: company }, + }; + }, }, { fieldname: "brand", diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index c7819731930..cd50b118715 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -7,6 +7,7 @@ from frappe import _ from frappe.model.meta import get_field_precision from frappe.utils import cstr, flt from frappe.utils.xlsxutils import handle_html +from pypika import Order from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns @@ -26,7 +27,7 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions= company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency") - item_list = get_items(filters, get_query_columns(additional_table_columns), additional_conditions) + item_list = get_items(filters, additional_table_columns, additional_conditions) if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) @@ -83,9 +84,7 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions= "company": d.company, "sales_order": d.sales_order, "delivery_note": d.delivery_note, - "income_account": d.unrealized_profit_loss_account - if d.is_internal_customer == 1 - else d.income_account, + "income_account": get_income_account(d), "cost_center": d.cost_center, "stock_qty": d.stock_qty, "stock_uom": d.stock_uom, @@ -150,6 +149,15 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions= return columns, data, None, None, None, skip_total_row +def get_income_account(row): + if row.enable_deferred_revenue: + return row.deferred_revenue_account + elif row.is_internal_customer == 1: + return row.unrealized_profit_loss_account + else: + return row.income_account + + def get_columns(additional_table_columns, filters): columns = [] @@ -333,93 +341,140 @@ def get_columns(additional_table_columns, filters): return columns -def get_conditions(filters, additional_conditions=None): - conditions = "" +def apply_conditions(query, si, sii, filters, additional_conditions=None): + for opts in ("company", "customer", "item_code"): + if filters.get(opts): + query = query.where(si[opts] == filters[opts]) - for opts in ( - ("company", " and `tabSales Invoice`.company=%(company)s"), - ("customer", " and `tabSales Invoice`.customer = %(customer)s"), - ("item_code", " and `tabSales Invoice Item`.item_code = %(item_code)s"), - ("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"), - ("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s"), - ): - if filters.get(opts[0]): - conditions += opts[1] + if filters.get("from_date"): + query = query.where(si.posting_date >= filters.get("from_date")) - if additional_conditions: - conditions += additional_conditions + if filters.get("to_date"): + query = query.where(si.posting_date <= filters.get("to_date")) if filters.get("mode_of_payment"): - conditions += """ and exists(select name from `tabSales Invoice Payment` - where parent=`tabSales Invoice`.name - and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)""" + sales_invoice = frappe.db.get_all( + "Sales Invoice Payment", {"mode_of_payment": filters.get("mode_of_payment")}, pluck="parent" + ) + query = query.where(si.name.isin(sales_invoice)) if filters.get("warehouse"): if frappe.db.get_value("Warehouse", filters.get("warehouse"), "is_group"): lft, rgt = frappe.db.get_all( "Warehouse", filters={"name": filters.get("warehouse")}, fields=["lft", "rgt"], as_list=True )[0] - conditions += f"and ifnull(`tabSales Invoice Item`.warehouse, '') in (select name from `tabWarehouse` where lft > {lft} and rgt < {rgt}) " + warehouses = frappe.db.get_all("Warehouse", {"lft": (">", lft), "rgt": ("<", rgt)}, pluck="name") + query = query.where(sii.warehouse.isin(warehouses)) else: - conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s""" + query = query.where(sii.warehouse == filters.get("warehouse")) if filters.get("brand"): - conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s""" + query = query.where(sii.brand == filters.get("brand")) if filters.get("item_group"): - conditions += """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s""" + query = query.where(sii.item_group == filters.get("item_group")) + + if filters.get("income_account"): + query = query.where( + (sii.income_account == filters.get("income_account")) + | (sii.deferred_revenue_account == filters.get("income_account")) + | (si.unrealized_profit_loss_account == filters.get("income_account")) + ) if not filters.get("group_by"): - conditions += "ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc" + query = query.orderby(si.posting_date, order=Order.desc) + query = query.orderby(sii.item_group, order=Order.desc) else: - conditions += get_group_by_conditions(filters, "Sales Invoice") + query = apply_group_by_conditions(query, si, sii, filters) - return conditions + for key, value in (additional_conditions or {}).items(): + query = query.where(si[key] == value) + + return query -def get_group_by_conditions(filters, doctype): +def apply_group_by_conditions(query, si, ii, filters): if filters.get("group_by") == "Invoice": - return f"ORDER BY `tab{doctype} Item`.parent desc" + query = query.orderby(ii.parent, order=Order.desc) elif filters.get("group_by") == "Item": - return f"ORDER BY `tab{doctype} Item`.`item_code`" + query = query.orderby(ii.item_code) elif filters.get("group_by") == "Item Group": - return "ORDER BY `tab{} Item`.{}".format(doctype, frappe.scrub(filters.get("group_by"))) + query = query.orderby(ii.item_group) elif filters.get("group_by") in ("Customer", "Customer Group", "Territory", "Supplier"): - return "ORDER BY `tab{}`.{}".format(doctype, frappe.scrub(filters.get("group_by"))) + query = query.orderby(si[frappe.scrub(filters.get("group_by"))]) + + return query def get_items(filters, additional_query_columns, additional_conditions=None): - conditions = get_conditions(filters, additional_conditions) + si = frappe.qb.DocType("Sales Invoice") + sii = frappe.qb.DocType("Sales Invoice Item") + item = frappe.qb.DocType("Item") + + query = ( + frappe.qb.from_(si) + .join(sii) + .on(si.name == sii.parent) + .left_join(item) + .on(sii.item_code == item.name) + .select( + sii.name, + sii.parent, + si.posting_date, + si.debit_to, + si.unrealized_profit_loss_account, + si.is_internal_customer, + si.customer, + si.remarks, + si.territory, + si.company, + si.base_net_total, + sii.project, + sii.item_code, + sii.description, + sii.item_name, + sii.item_group, + sii.item_name.as_("si_item_name"), + sii.item_group.as_("si_item_group"), + item.item_name.as_("i_item_name"), + item.item_group.as_("i_item_group"), + sii.sales_order, + sii.delivery_note, + sii.income_account, + sii.cost_center, + sii.enable_deferred_revenue, + sii.deferred_revenue_account, + sii.stock_qty, + sii.stock_uom, + sii.base_net_rate, + sii.base_net_amount, + si.customer_name, + si.customer_group, + sii.so_detail, + si.update_stock, + sii.uom, + sii.qty, + ) + .where(si.docstatus == 1) + ) + if additional_query_columns: - additional_query_columns = "," + ",".join(additional_query_columns) - return frappe.db.sql( - """ - select - `tabSales Invoice Item`.name, `tabSales Invoice Item`.parent, - `tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to, - `tabSales Invoice`.unrealized_profit_loss_account, - `tabSales Invoice`.is_internal_customer, - `tabSales Invoice`.customer, `tabSales Invoice`.remarks, - `tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total, - `tabSales Invoice Item`.project, - `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, - `tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`, - `tabSales Invoice Item`.`item_name` as si_item_name, `tabSales Invoice Item`.`item_group` as si_item_group, - `tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group, - `tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note, - `tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center, - `tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom, - `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, - `tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail, - `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {} - from `tabSales Invoice`, `tabSales Invoice Item`, `tabItem` - where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and - `tabItem`.name = `tabSales Invoice Item`.`item_code` and - `tabSales Invoice`.docstatus = 1 {} - """.format(additional_query_columns, conditions), - filters, - as_dict=1, - ) # nosec + for column in additional_query_columns: + if column.get("_doctype"): + table = frappe.qb.DocType(column.get("_doctype")) + query = query.select(table[column.get("fieldname")]) + else: + query = query.select(si[column.get("fieldname")]) + + if filters.get("customer"): + query = query.where(si.customer == filters["customer"]) + + if filters.get("customer_group"): + query = query.where(si.customer_group == filters["customer_group"]) + + query = apply_conditions(query, si, sii, filters, additional_conditions) + + return query.run(as_dict=True) def get_delivery_notes_against_sales_order(item_list): @@ -427,16 +482,14 @@ def get_delivery_notes_against_sales_order(item_list): so_item_rows = list(set([d.so_detail for d in item_list])) if so_item_rows: - delivery_notes = frappe.db.sql( - """ - select parent, so_detail - from `tabDelivery Note Item` - where docstatus=1 and so_detail in (%s) - group by so_detail, parent - """ - % (", ".join(["%s"] * len(so_item_rows))), - tuple(so_item_rows), - as_dict=1, + dn_item = frappe.qb.DocType("Delivery Note Item") + delivery_notes = ( + frappe.qb.from_(dn_item) + .select(dn_item.parent, dn_item.so_detail) + .where(dn_item.docstatus == 1) + .where(dn_item.so_detail.isin(so_item_rows)) + .groupby(dn_item.so_detail, dn_item.parent) + .run(as_dict=True) ) for dn in delivery_notes: @@ -446,15 +499,16 @@ def get_delivery_notes_against_sales_order(item_list): def get_grand_total(filters, doctype): - return frappe.db.sql( - f""" SELECT - SUM(`tab{doctype}`.base_grand_total) - FROM `tab{doctype}` - WHERE `tab{doctype}`.docstatus = 1 - and posting_date between %s and %s - """, - (filters.get("from_date"), filters.get("to_date")), - )[0][0] # nosec + return flt( + frappe.db.get_value( + doctype, + { + "docstatus": 1, + "posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]), + }, + "sum(base_grand_total)", + ) + ) def get_tax_accounts( diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js index 8625c57d46b..528246cd370 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js @@ -15,7 +15,7 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 834eb5f519c..f3f30d38a04 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -3,7 +3,9 @@ import frappe -from frappe import _ +from frappe import _, qb +from frappe.query_builder import Criterion +from frappe.query_builder.functions import Abs from frappe.utils import flt, getdate from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport @@ -21,16 +23,12 @@ def execute(filters=None): data = [] for d in entries: - invoice = invoice_details.get(d.against_voucher) or frappe._dict() - - if d.reference_type == "Purchase Invoice": - payment_amount = flt(d.debit) or -1 * flt(d.credit) - else: - payment_amount = flt(d.credit) or -1 * flt(d.debit) + invoice = invoice_details.get(d.against_voucher_no) or frappe._dict() + payment_amount = d.amount d.update({"range1": 0, "range2": 0, "range3": 0, "range4": 0, "outstanding": payment_amount}) - if d.against_voucher: + if d.against_voucher_no: ReceivablePayableReport(filters).get_ageing_data(invoice.posting_date, d) row = [ @@ -39,11 +37,10 @@ def execute(filters=None): d.party_type, d.party, d.posting_date, - d.against_voucher, + d.against_voucher_no, invoice.posting_date, invoice.due_date, - d.debit, - d.credit, + d.amount, d.remarks, d.age, d.range1, @@ -111,8 +108,7 @@ def get_columns(filters): "width": 100, }, {"fieldname": "due_date", "label": _("Payment Due Date"), "fieldtype": "Date", "width": 100}, - {"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 140}, - {"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 140}, + {"fieldname": "amount", "label": _("Amount"), "fieldtype": "Currency", "width": 140}, {"fieldname": "remarks", "label": _("Remarks"), "fieldtype": "Data", "width": 200}, {"fieldname": "age", "label": _("Age"), "fieldtype": "Int", "width": 50}, {"fieldname": "range1", "label": _("0-30"), "fieldtype": "Currency", "width": 140}, @@ -129,51 +125,68 @@ def get_columns(filters): def get_conditions(filters): + ple = qb.DocType("Payment Ledger Entry") conditions = [] - if not filters.party_type: - if filters.payment_type == _("Outgoing"): - filters.party_type = "Supplier" - else: - filters.party_type = "Customer" - - if filters.party_type: - conditions.append("party_type=%(party_type)s") + conditions.append(ple.delinked.eq(0)) + if filters.payment_type == _("Outgoing"): + conditions.append(ple.party_type.eq("Supplier")) + conditions.append(ple.against_voucher_type.eq("Purchase Invoice")) + else: + conditions.append(ple.party_type.eq("Customer")) + conditions.append(ple.against_voucher_type.eq("Sales Invoice")) if filters.party: - conditions.append("party=%(party)s") - - if filters.party_type: - conditions.append("against_voucher_type=%(reference_type)s") - filters["reference_type"] = ( - "Sales Invoice" if filters.party_type == "Customer" else "Purchase Invoice" - ) + conditions.append(ple.party.eq(filters.party)) if filters.get("from_date"): - conditions.append("posting_date >= %(from_date)s") + conditions.append(ple.posting_date.gte(filters.get("from_date"))) if filters.get("to_date"): - conditions.append("posting_date <= %(to_date)s") + conditions.append(ple.posting_date.lte(filters.get("to_date"))) - return "and " + " and ".join(conditions) if conditions else "" + if filters.get("company"): + conditions.append(ple.company.eq(filters.get("company"))) + + return conditions def get_entries(filters): - return frappe.db.sql( - """select - voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher - from `tabGL Entry` - where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') and is_cancelled = 0 {} - """.format(get_conditions(filters)), - filters, - as_dict=1, + ple = qb.DocType("Payment Ledger Entry") + conditions = get_conditions(filters) + + query = ( + qb.from_(ple) + .select( + ple.voucher_type, + ple.voucher_no, + ple.party_type, + ple.party, + ple.posting_date, + Abs(ple.amount).as_("amount"), + ple.remarks, + ple.against_voucher_no, + ) + .where(Criterion.all(conditions)) ) + res = query.run(as_dict=True) + return res def get_invoice_posting_date_map(filters): invoice_details = {} - dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice" - for t in frappe.db.sql(f"select name, posting_date, due_date from `tab{dt}`", as_dict=1): + dt = ( + qb.DocType("Sales Invoice") + if filters.get("payment_type") == _("Incoming") + else qb.DocType("Purchase Invoice") + ) + res = ( + qb.from_(dt) + .select(dt.name, dt.posting_date, dt.due_date) + .where((dt.docstatus.eq(1)) & (dt.company.eq(filters.get("company")))) + .run(as_dict=1) + ) + for t in res: invoice_details[t.name] = t return invoice_details diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index dcecf7bb47f..816adfaf916 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -59,13 +59,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function () { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, { fieldname: "show_zero_values", diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 59b366a3dca..7337fd477e7 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -37,13 +37,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function () { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, { fieldname: "cost_center", diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js index ab1508bebd0..50578d314e3 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js @@ -36,13 +36,13 @@ frappe.query_reports["Trial Balance for Party"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, { fieldname: "party_type", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 8fc47b0b679..7e67085f5dd 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -55,6 +55,9 @@ GL_REPOSTING_CHUNK = 100 def get_fiscal_year( date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False, boolean=False ): + if isinstance(boolean, str): + boolean = loads(boolean) + fiscal_years = get_fiscal_years( date, fiscal_year, label, verbose, company, as_dict=as_dict, boolean=boolean ) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 49c0b24e4cb..b6afe11e4ff 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -78,7 +78,7 @@ frappe.ui.form.on("Asset", { frm.events.make_schedules_editable(frm); if (frm.doc.docstatus == 1) { - if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) { + if (["Submitted", "Partially Depreciated", "Fully Depreciated"].includes(frm.doc.status)) { frm.add_custom_button( __("Transfer Asset"), function () { @@ -280,7 +280,7 @@ frappe.ui.form.on("Asset", { if (v.journal_entry) { asset_values.push(asset_value); } else { - if (in_list(["Scrapped", "Sold"], frm.doc.status)) { + if (["Scrapped", "Sold"].includes(frm.doc.status)) { asset_values.push(null); } else { asset_values.push(asset_value); @@ -312,7 +312,7 @@ frappe.ui.form.on("Asset", { }); } - if (in_list(["Scrapped", "Sold"], frm.doc.status)) { + if (["Scrapped", "Sold"].includes(frm.doc.status)) { x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: "Date" })); asset_values.push(0); } diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index fd00a495030..405eda8d09f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -12,6 +12,7 @@ from frappe.utils import ( add_months, add_years, cint, + cstr, date_diff, flt, get_datetime, @@ -361,9 +362,11 @@ class Asset(AccountsController): final_number_of_depreciations = cint(finance_book.total_number_of_depreciations) - cint( self.number_of_depreciations_booked ) - has_pro_rata = self.check_is_pro_rata(finance_book) - if has_pro_rata: + depr_already_booked = any( + [d.journal_entry for d in self.get("schedules") if d.finance_book == finance_book.finance_book] + ) + if has_pro_rata and not depr_already_booked: final_number_of_depreciations += 1 has_wdv_or_dd_non_yearly_pro_rata = False @@ -543,7 +546,7 @@ class Asset(AccountsController): "depreciation_amount": depreciation_amount, "depreciation_method": finance_book.depreciation_method, "finance_book": finance_book.finance_book, - "finance_book_id": finance_book.idx, + "finance_book_id": cstr(finance_book.idx), "shift": shift, }, ) @@ -749,7 +752,6 @@ class Asset(AccountsController): ): straight_line_idx = [] finance_books = [] - for i, d in enumerate(self.get("schedules")): if ignore_booked_entry and d.journal_entry: continue @@ -771,7 +773,10 @@ class Asset(AccountsController): finance_books.append(int(d.finance_book_id)) depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount")) - value_after_depreciation -= flt(depreciation_amount) + if not d.journal_entry: + value_after_depreciation = flt( + flt(value_after_depreciation) - depreciation_amount, d.precision("depreciation_amount") + ) # for the last row, if depreciation method = Straight Line if ( @@ -783,10 +788,13 @@ class Asset(AccountsController): book = self.get("finance_books")[cint(d.finance_book_id) - 1] if not book.shift_based: - depreciation_amount += flt( + adjustment_amount = flt( value_after_depreciation - flt(book.expected_value_after_useful_life), d.precision("depreciation_amount"), ) + depreciation_amount = flt( + depreciation_amount + adjustment_amount, d.precision("depreciation_amount") + ) d.depreciation_amount = depreciation_amount accumulated_depreciation += d.depreciation_amount @@ -1433,7 +1441,7 @@ def get_straight_line_or_manual_depr_amount(asset, row, schedule_idx, number_of_ # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value elif asset.flags.increase_in_asset_value_due_to_repair: return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / flt( - row.total_number_of_depreciations + number_of_pending_depreciations ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 00a53aa80d2..dc58222008a 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1355,9 +1355,9 @@ class TestDepreciationBasics(AssetSetup): for schedule in asset.schedules: if schedule.idx <= 3: - self.assertEqual(schedule.finance_book_id, 1) + self.assertEqual(schedule.finance_book_id, "1") else: - self.assertEqual(schedule.finance_book_id, 2) + self.assertEqual(schedule.finance_book_id, "2") def test_depreciation_entry_cancellation(self): asset = create_asset( diff --git a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py index 7661e70fd17..f3583b0768d 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py @@ -103,12 +103,11 @@ class TestAssetValueAdjustment(unittest.TestCase): ["2023-05-31", 9983.33, 45408.05], ["2023-06-30", 9983.33, 55391.38], ["2023-07-31", 9983.33, 65374.71], - ["2023-08-31", 8300.0, 73674.71], - ["2023-09-30", 8300.0, 81974.71], - ["2023-10-31", 8300.0, 90274.71], - ["2023-11-30", 8300.0, 98574.71], - ["2023-12-31", 8300.0, 106874.71], - ["2024-01-15", 8300.0, 115174.71], + ["2023-08-31", 9960.0, 75334.71], + ["2023-09-30", 9960.0, 85294.71], + ["2023-10-31", 9960.0, 95254.71], + ["2023-11-30", 9960.0, 105214.71], + ["2023-12-15", 9960.0, 115174.71], ] schedules = [ diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 52a0f4ac2a2..d6fd53dc778 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -6,8 +6,8 @@ frappe.provide("erpnext.accounts.dimensions"); {% include 'erpnext/public/js/controllers/buying.js' %}; frappe.ui.form.on("Purchase Order", { - setup: function(frm) { - + setup: function (frm) { + frm.ignore_doctypes_on_cancel_all = ["Unreconcile Payment", "Unreconcile Payment Entries"]; if (frm.doc.is_old_subcontracting_flow) { frm.set_query("reserve_warehouse", "supplied_items", function() { return { @@ -180,7 +180,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e this.frm.fields_dict.items_section.wrapper.removeClass("hide-border"); } - if(!in_list(["Closed", "Delivered"], doc.status)) { + if(!["Closed", "Delivered"].includes(doc.status)) { if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) { // Don't add Update Items button if the PO is following the new subcontracting flow. if (!(this.frm.doc.is_subcontracted && !this.frm.doc.is_old_subcontracting_flow)) { @@ -211,7 +211,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e this.frm.page.set_inner_btn_group_as_primary(__("Status")); } - } else if(in_list(["Closed", "Delivered"], doc.status)) { + } else if(["Closed", "Delivered"].includes(doc.status)) { if (this.frm.has_perm("submit")) { this.frm.add_custom_button(__('Re-open'), () => this.unclose_purchase_order(), __("Status")); } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 58d7440211c..705e8fd6dd8 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -345,7 +345,13 @@ class PurchaseOrder(BuyingController): update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) def on_cancel(self): - self.ignore_linked_doctypes = ("GL Entry", "Payment Ledger Entry") + self.ignore_linked_doctypes = ( + "GL Entry", + "Payment Ledger Entry", + "Unreconcile Payment", + "Unreconcile Payment Entries", + ) + super().on_cancel() if self.is_against_so(): diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.js b/erpnext/buying/report/procurement_tracker/procurement_tracker.js index 7127c60b286..df8dc9c6902 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.js +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.js @@ -27,13 +27,13 @@ frappe.query_reports["Procurement Tracker"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, ], }; diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js index 91958d520a7..7de965a4586 100644 --- a/erpnext/buying/report/purchase_analytics/purchase_analytics.js +++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js @@ -35,14 +35,14 @@ frappe.query_reports["Purchase Analytics"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, { diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 89226453b7f..364aadc8eae 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -8,6 +8,7 @@ from frappe.contacts.doctype.address.address import render_address from frappe.utils import cint, cstr, flt, getdate from frappe.utils.data import nowtime +import erpnext from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.accounts.party import get_party_details from erpnext.buying.utils import update_last_purchase_rate, validate_for_items @@ -305,6 +306,8 @@ class BuyingController(SubcontractingController): else: item.valuation_rate = 0.0 + update_regional_item_valuation_rate(self) + def set_incoming_rate(self): if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"): return @@ -894,3 +897,8 @@ def validate_item_type(doc, fieldname, message): ).format(items, message) frappe.throw(error_message) + + +@erpnext.allow_regional +def update_regional_item_valuation_rate(doc): + pass diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 41085181720..a4d8e3efee1 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -51,7 +51,7 @@ class StockController(AccountsController): self.validate_internal_transfer() self.validate_putaway_capacity() - def make_gl_entries(self, gl_entries=None, from_repost=False): + def make_gl_entries(self, gl_entries=None, from_repost=False, via_landed_cost_voucher=False): if self.docstatus == 2: make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) @@ -72,7 +72,11 @@ class StockController(AccountsController): if self.docstatus == 1: if not gl_entries: - gl_entries = self.get_gl_entries(warehouse_account) + gl_entries = ( + self.get_gl_entries(warehouse_account, via_landed_cost_voucher) + if self.doctype == "Purchase Receipt" + else self.get_gl_entries(warehouse_account) + ) make_gl_entries(gl_entries, from_repost=from_repost) def validate_serialized_batch(self): diff --git a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js index d2c52938bde..a1e51f0654d 100644 --- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js +++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js @@ -6,13 +6,13 @@ frappe.query_reports["Campaign Efficiency"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, ], }; diff --git a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js index c140567fed5..bd64641dcc2 100644 --- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js +++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js @@ -6,13 +6,13 @@ frappe.query_reports["Lead Owner Efficiency"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, ], }; diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index b7ab6ef3872..bf8cb05d8ce 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -400,7 +400,7 @@ frappe.ui.form.on("BOM", { }, rm_cost_as_per(frm) { - if (in_list(["Valuation Rate", "Last Purchase Rate"], frm.doc.rm_cost_as_per)) { + if (["Valuation Rate", "Last Purchase Rate"].includes(frm.doc.rm_cost_as_per)) { frm.set_value("plc_conversion_rate", 1.0); } }, diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 54d1414c814..6db901c71a4 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -129,7 +129,7 @@ frappe.ui.form.on("Production Plan", { if ( frm.doc.mr_items && frm.doc.mr_items.length && - !in_list(["Material Requested", "Closed"], frm.doc.status) + !["Material Requested", "Closed"].includes(frm.doc.status) ) { frm.add_custom_button( __("Material Request"), diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 5e386035514..ea86d0a939c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -194,7 +194,7 @@ frappe.ui.form.on("Work Order", { }, add_custom_button_to_return_components: function (frm) { - if (frm.doc.docstatus === 1 && in_list(["Closed", "Completed"], frm.doc.status)) { + if (frm.doc.docstatus === 1 && ["Closed", "Completed"].includes(frm.doc.status)) { let non_consumed_items = frm.doc.required_items.filter((d) => { return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty); }); @@ -594,7 +594,7 @@ erpnext.work_order = { ); } - if (doc.docstatus === 1 && !in_list(["Closed", "Completed"], doc.status)) { + if (doc.docstatus === 1 && !["Closed", "Completed"].includes(doc.status)) { if (doc.status != "Stopped" && doc.status != "Completed") { frm.add_custom_button( __("Stop"), diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py index 2aa31be0f0e..97c85502c98 100644 --- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py +++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py @@ -21,7 +21,8 @@ def get_exploded_items(bom, data, indent=0, qty=1): exploded_items = frappe.get_all( "BOM Item", filters={"parent": bom}, - fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom"], + fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom", "idx"], + order_by="idx ASC", ) for item in exploded_items: diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index 0d6a52f4b8e..aac687c1413 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -37,14 +37,14 @@ frappe.query_reports["Job Card Summary"] = { label: __("From Posting Date"), fieldname: "from_date", fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { label: __("To Posting Date"), fieldname: "to_date", fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, { diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.js b/erpnext/manufacturing/report/production_analytics/production_analytics.js index 6f2c38295e9..da8e4926a45 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.js +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.js @@ -16,14 +16,14 @@ frappe.query_reports["Production Analytics"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, { diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 078aa5e0a0f..25f038173d6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -362,4 +362,5 @@ erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2 erpnext.patches.v14_0.set_maintain_stock_for_bom_item execute:frappe.db.set_single_value('E Commerce Settings', 'show_actual_qty', 1) erpnext.patches.v14_0.delete_orphaned_asset_movement_item_records -erpnext.patches.v14_0.remove_cancelled_asset_capitalization_from_asset \ No newline at end of file +erpnext.patches.v14_0.remove_cancelled_asset_capitalization_from_asset +erpnext.patches.v14_0.enable_set_priority_for_pricing_rules #1 diff --git a/erpnext/patches/v14_0/enable_set_priority_for_pricing_rules.py b/erpnext/patches/v14_0/enable_set_priority_for_pricing_rules.py new file mode 100644 index 00000000000..af87eeb2727 --- /dev/null +++ b/erpnext/patches/v14_0/enable_set_priority_for_pricing_rules.py @@ -0,0 +1,10 @@ +import frappe + + +def execute(): + pr_table = frappe.qb.DocType("Pricing Rule") + ( + frappe.qb.update(pr_table) + .set(pr_table.has_priority, 1) + .where((pr_table.priority.isnotnull()) & (pr_table.priority != "")) + ).run() diff --git a/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py b/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py index ec72527552c..d740a5b9c8e 100644 --- a/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py +++ b/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py @@ -9,15 +9,13 @@ def execute(): dt = frappe.qb.DocType(doctype) records = ( - frappe.qb.from_(dt) - .select(dt.name, dt.notes, dt.modified_by, dt.modified) - .where(dt.notes.isnotnull() & dt.notes != "") + frappe.qb.from_(dt).select(dt.name, dt.notes).where(dt.notes.isnotnull() & dt.notes != "") ).run(as_dict=True) for d in records: if strip_html(cstr(d.notes)).strip(): doc = frappe.get_doc(doctype, d.name) - doc.append("notes", {"note": d.notes, "added_by": d.modified_by, "added_on": d.modified}) + doc.append("notes", {"note": d.notes}) doc.update_child_table("notes") frappe.db.sql_ddl(f"alter table `tab{doctype}` drop column `notes`") diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 3ea8189feec..6ac6d1f88a6 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -57,6 +57,14 @@ frappe.ui.form.on("Project", { filters: filters, }; }); + + frm.set_query("cost_center", () => { + return { + filters: { + company: frm.doc.company, + }, + }; + }); }, refresh: function (frm) { diff --git a/erpnext/public/js/account_tree_grid.js b/erpnext/public/js/account_tree_grid.js index 6b4cdf1177a..1d4596ba1a4 100644 --- a/erpnext/public/js/account_tree_grid.js +++ b/erpnext/public/js/account_tree_grid.js @@ -240,7 +240,7 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep flt(account.closing_dr) - flt(account.closing_cr); me.set_debit_or_credit(parent_account, "closing", bal); - } else if (in_list(["debit", "credit"], col.field)) { + } else if (["debit", "credit"].includes(col.field)) { parent_account[col.field] = flt(parent_account[col.field]) + flt(account[col.field]); } diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js index d9187f8b678..c8905e14af2 100644 --- a/erpnext/public/js/communication.js +++ b/erpnext/public/js/communication.js @@ -20,7 +20,7 @@ frappe.ui.form.on("Communication", { ); } - if (!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) { + if (!["Lead", "Opportunity"].includes(frm.doc.reference_doctype)) { frm.add_custom_button( __("Lead"), () => { diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 72845b20742..1ba941ab6f7 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -9,7 +9,7 @@ frappe.ui.form.on(cur_frm.doctype, { setup: function(frm) { // set conditional display for rate column in taxes $(frm.wrapper).on('grid-row-render', function(e, grid_row) { - if(in_list(['Sales Taxes and Charges', 'Purchase Taxes and Charges'], grid_row.doc.doctype)) { + if(['Sales Taxes and Charges', 'Purchase Taxes and Charges'].includes(grid_row.doc.doctype)) { erpnext.taxes.set_conditional_mandatory_rate_or_amount(grid_row); } }); diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index b0e08cc6f26..aa7dfbd90d6 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -136,7 +136,7 @@ erpnext.buying.BuyingController = class BuyingController extends erpnext.Transac } toggle_subcontracting_fields() { - if (in_list(['Purchase Receipt', 'Purchase Invoice'], this.frm.doc.doctype)) { + if (['Purchase Receipt', 'Purchase Invoice'].includes(this.frm.doc.doctype)) { this.frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', 'read_only', this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === 'BOM'); diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 186c342a75e..670cf35bb11 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -9,7 +9,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { apply_pricing_rule_on_item(item) { let effective_item_rate = item.price_list_rate; let item_rate = item.rate; - if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) { + if (["Sales Order", "Quotation"].includes(item.parenttype) && item.blanket_order_rate) { effective_item_rate = item.blanket_order_rate; } if (item.margin_type == "Percentage") { @@ -52,7 +52,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { // Advance calculation applicable to Sales/Purchase Invoice if ( - in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype) + ["Sales Invoice", "POS Invoice", "Purchase Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.docstatus < 2 && !this.frm.doc.is_return ) { @@ -60,7 +60,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } if ( - in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) + ["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.is_pos && this.frm.doc.is_return ) { @@ -69,7 +69,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } // Sales person's commission - if (in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"], this.frm.doc.doctype)) { + if (["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"].includes(this.frm.doc.doctype)) { this.calculate_commission(); this.calculate_contribution(); } @@ -547,7 +547,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { ? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.rounding_adjustment) : this.frm.doc.net_total); - if(in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"], this.frm.doc.doctype)) { + if(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype)) { this.frm.doc.base_grand_total = (this.frm.doc.total_taxes_and_charges) ? flt(this.frm.doc.grand_total * this.frm.doc.conversion_rate) : this.frm.doc.base_net_total; } else { @@ -555,7 +555,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this.frm.doc.taxes_and_charges_added = this.frm.doc.taxes_and_charges_deducted = 0.0; if(tax_count) { $.each(this.frm.doc["taxes"] || [], function(i, tax) { - if (in_list(["Valuation and Total", "Total"], tax.category)) { + if (["Valuation and Total", "Total"].includes(tax.category)) { if(tax.add_deduct_tax == "Add") { me.frm.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount); } else { @@ -702,7 +702,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { var actual_taxes_dict = {}; $.each(this.frm.doc["taxes"] || [], function(i, tax) { - if (in_list(["Actual", "On Item Quantity"], tax.charge_type)) { + if (["Actual", "On Item Quantity"].includes(tax.charge_type)) { var tax_amount = (tax.category == "Valuation") ? 0.0 : tax.tax_amount; tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; actual_taxes_dict[tax.idx] = tax_amount; @@ -747,7 +747,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { // NOTE: // paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice // total_advance is only for non POS Invoice - if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) && this.frm.doc.is_return){ + if(["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.is_return){ this.calculate_paid_amount(); } @@ -755,7 +755,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); - if(in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype)) { + if(["Sales Invoice", "POS Invoice", "Purchase Invoice"].includes(this.frm.doc.doctype)) { let grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; let base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total; @@ -778,7 +778,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this.frm.refresh_field("base_paid_amount"); } - if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype)) { + if(["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype)) { let total_amount_for_payment = (this.frm.doc.redeem_loyalty_points && this.frm.doc.loyalty_amount) ? flt(total_amount_to_pay - this.frm.doc.loyalty_amount, precision("base_grand_total")) : total_amount_to_pay; @@ -882,7 +882,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { calculate_change_amount(){ this.frm.doc.change_amount = 0.0; this.frm.doc.base_change_amount = 0.0; - if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) + if(["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) { var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type; }); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d6c5885891e..7291a82719c 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -260,7 +260,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } setup_quality_inspection() { - if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) { + if(!["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype)) { return; } @@ -272,7 +272,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.frm.page.set_inner_btn_group_as_primary(__('Create')); } - const inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype) + const inspection_type = ["Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype) ? "Incoming" : "Outgoing"; let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection"); @@ -304,7 +304,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe make_payment_request() { let me = this; - const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype)) + const payment_request_type = (['Sales Order', 'Sales Invoice'].includes(this.frm.doc.doctype)) ? "Inward" : "Outward"; frappe.call({ @@ -417,7 +417,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe setup_sms() { var me = this; let blacklist = ['Purchase Invoice', 'BOM']; - if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status) + if(this.frm.doc.docstatus===1 && !["Lost", "Stopped", "Closed"].includes(this.frm.doc.status) && !blacklist.includes(this.frm.doctype)) { this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); }); } @@ -780,7 +780,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } var set_party_account = function(set_pricing) { - if (in_list(["Sales Invoice", "Purchase Invoice"], me.frm.doc.doctype)) { + if (["Sales Invoice", "Purchase Invoice"].includes(me.frm.doc.doctype)) { if(me.frm.doc.doctype=="Sales Invoice") { var party_type = "Customer"; var party_account_field = 'debit_to'; @@ -815,7 +815,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") && - in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) { + ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'].includes(this.frm.doctype)) { erpnext.utils.get_shipping_address(this.frm, function() { set_party_account(set_pricing); }); @@ -1482,7 +1482,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "doctype": me.frm.doc.doctype, "name": me.frm.doc.name, "is_return": cint(me.frm.doc.is_return), - "update_stock": in_list(['Sales Invoice', 'Purchase Invoice'], me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0, + "update_stock": ['Sales Invoice', 'Purchase Invoice'].includes(me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0, "conversion_factor": me.frm.doc.conversion_factor, "pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '', "coupon_code": me.frm.doc.coupon_code @@ -2126,7 +2126,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe get_method_for_payment() { var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){ - if(in_list(['Sales Invoice', 'Purchase Invoice'], cur_frm.doc.doctype)){ + if(['Sales Invoice', 'Purchase Invoice'].includes( cur_frm.doc.doctype)){ method = "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice"; }else { method= "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order"; diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index 0e584205396..c91bb046a52 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -218,7 +218,7 @@ erpnext.payments = class payments extends erpnext.stock.StockController { update_paid_amount(update_write_off) { var me = this; - if (in_list(["change_amount", "write_off_amount"], this.idx)) { + if (["change_amount", "write_off_amount"].includes(this.idx)) { var value = me.selected_mode.val(); if (me.idx == "change_amount") { me.change_amount(value); diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index d3147bb4600..63833da5af3 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -28,11 +28,11 @@ erpnext.SMSManager = function SMSManager(doc) { "Purchase Receipt": "Items has been received against purchase receipt: " + doc.name, }; - if (in_list(["Sales Order", "Delivery Note", "Sales Invoice"], doc.doctype)) + if (["Sales Order", "Delivery Note", "Sales Invoice"].includes(doc.doctype)) this.show(doc.contact_person, "Customer", doc.customer, "", default_msg[doc.doctype]); else if (doc.doctype === "Quotation") this.show(doc.contact_person, "Customer", doc.party_name, "", default_msg[doc.doctype]); - else if (in_list(["Purchase Order", "Purchase Receipt"], doc.doctype)) + else if (["Purchase Order", "Purchase Receipt"].includes(doc.doctype)) this.show(doc.contact_person, "Supplier", doc.supplier, "", default_msg[doc.doctype]); else if (doc.doctype == "Lead") this.show("", "", "", doc.mobile_no, default_msg[doc.doctype]); else if (doc.doctype == "Opportunity") diff --git a/erpnext/public/js/templates/crm_notes.html b/erpnext/public/js/templates/crm_notes.html index 53df9330784..a20e6c2723c 100644 --- a/erpnext/public/js/templates/crm_notes.html +++ b/erpnext/public/js/templates/crm_notes.html @@ -12,6 +12,7 @@ {% for(var i=0, l=notes.length; i
+ {% if (notes[i].added_by && notes[i].added_on) %}
{{ frappe.avatar(notes[i].added_by) }} @@ -25,6 +26,7 @@
+ {% } %}
{{ notes[i].note }} diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index f6ba4ae9fab..4d886d47f4e 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -410,11 +410,13 @@ $.extend(erpnext.utils, { method: "erpnext.accounts.utils.get_fiscal_year", args: { date: date, + boolean: boolean, }, async: false, callback: function (r) { if (r.message) { - fiscal_year = r.message[0]; + if (with_dates) fiscal_year = r.message; + else fiscal_year = r.message[0]; } }, }); diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 801376b2ed7..623941755d1 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -14,10 +14,10 @@ erpnext.utils.get_party_details = function (frm, method, args, callback) { if (!args) { if ( (frm.doctype != "Purchase Order" && frm.doc.customer) || - (frm.doc.party_name && in_list(["Quotation", "Opportunity"], frm.doc.doctype)) + (frm.doc.party_name && ["Quotation", "Opportunity"].includes(frm.doc.doctype)) ) { let party_type = "Customer"; - if (frm.doc.quotation_to && in_list(["Lead", "Prospect"], frm.doc.quotation_to)) { + if (frm.doc.quotation_to && ["Lead", "Prospect"].includes(frm.doc.quotation_to)) { party_type = frm.doc.quotation_to; } diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index 35c1fb64e36..a0d90f53f3c 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -446,7 +446,7 @@ body.product-page { width: 30%; @media (max-width: 992px) { - width: 40%; + width: 50%; } } } diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js index 5fbb5cb7e01..7aa8012f0b6 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js @@ -34,7 +34,7 @@ frappe.ui.form.on("Import Supplier Invoice", { }, toggle_read_only_fields: function (frm) { - if (in_list(["File Import Completed", "Processing File Data"], frm.doc.status)) { + if (["File Import Completed", "Processing File Data"].includes(frm.doc.status)) { cur_frm.set_read_only(); cur_frm.refresh_fields(); frm.set_df_property("import_invoices", "hidden", 1); diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 61e5d5e457c..23052299330 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -2,6 +2,8 @@ # License: GNU General Public License v3. See license.txt +import json + import frappe from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.test_runner import make_test_records @@ -322,7 +324,7 @@ class TestCustomer(FrappeTestCase): frappe.ValidationError, update_child_qty_rate, so.doctype, - frappe.json.dumps([modified_item]), + json.dumps([modified_item]), so.name, ) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 29ef186c42a..bfd5d6aadee 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -241,7 +241,11 @@ frappe.ui.form.on("Sales Order", { frm.set_value("advance_paid", 0) } - frm.ignore_doctypes_on_cancel_all = ['Purchase Order']; + frm.ignore_doctypes_on_cancel_all = [ + "Purchase Order", + "Unreconcile Payment", + "Unreconcile Payment Entries", + ]; }, delivery_date: function(frm) { diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 09b73878aa2..43e8e41f7f3 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1138,7 +1138,8 @@ "hide_seconds": 1, "label": "Inter Company Order Reference", "options": "Purchase Order", - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "project", @@ -1631,7 +1632,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:04:43.627183", + "modified": "2024-05-23 16:35:54.905804", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", @@ -1710,4 +1711,4 @@ "title_field": "customer_name", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 68fa67e92f1..5b4597125fb 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -269,7 +269,13 @@ class SalesOrder(SellingController): update_coupon_code_count(self.coupon_code, "used") def on_cancel(self): - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") + self.ignore_linked_doctypes = ( + "GL Entry", + "Stock Ledger Entry", + "Payment Ledger Entry", + "Unreconcile Payment", + "Unreconcile Payment Entries", + ) super().on_cancel() # Cannot cancel closed SO @@ -576,6 +582,11 @@ def get_requested_item_qty(sales_order): def make_material_request(source_name, target_doc=None): requested_item_qty = get_requested_item_qty(source_name) + def postprocess(source, target): + if source.tc_name and frappe.db.get_value("Terms and Conditions", source.tc_name, "buying") != 1: + target.tc_name = None + target.terms = None + def get_remaining_qty(so_item): return flt( flt(so_item.qty) @@ -631,6 +642,7 @@ def make_material_request(source_name, target_doc=None): }, }, target_doc, + postprocess, ) return doc @@ -932,11 +944,19 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t target.discount_amount = 0.0 target.inter_company_order_reference = "" target.shipping_rule = "" + target.tc_name = "" + target.terms = "" + target.payment_terms_template = "" + target.payment_schedule = [] default_price_list = frappe.get_value("Supplier", supplier, "default_price_list") if default_price_list: target.buying_price_list = default_price_list + default_payment_terms = frappe.get_value("Supplier", supplier, "payment_terms") + if default_payment_terms: + target.payment_terms_template = default_payment_terms + if any(item.delivered_by_supplier == 1 for item in source.items): if source.shipping_address_name: target.shipping_address = source.shipping_address_name @@ -988,7 +1008,6 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "contact_person", "taxes_and_charges", "shipping_address", - "terms", ], "validation": {"docstatus": ["=", 1]}, }, @@ -1056,6 +1075,10 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): target.discount_amount = 0.0 target.inter_company_order_reference = "" target.shipping_rule = "" + target.tc_name = "" + target.terms = "" + target.payment_terms_template = "" + target.payment_schedule = [] if is_drop_ship_order(target): target.customer = source.customer @@ -1091,7 +1114,6 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): "contact_person", "taxes_and_charges", "shipping_address", - "terms", ], "validation": {"docstatus": ["=", 1]}, }, diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 96b2c051e72..b0b64e64747 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -73,7 +73,7 @@ erpnext.PointOfSale.PastOrderSummary = class { const { status } = doc; let indicator_color = ""; - in_list(["Paid", "Consolidated"], status) && (indicator_color = "green"); + ["Paid", "Consolidated"].includes(status) && (indicator_color = "green"); status === "Draft" && (indicator_color = "red"); status === "Return" && (indicator_color = "grey"); diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js index 04891301d7b..745550b57c6 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js @@ -23,14 +23,14 @@ frappe.query_reports["Customer Acquisition and Loyalty"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, ], diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index 9274586c3e3..0f87923949f 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -43,14 +43,14 @@ frappe.query_reports["Sales Analytics"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], reqd: 1, }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], reqd: 1, }, { diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js index ebbf9cf2dd2..7944453a1b0 100644 --- a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js +++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js @@ -21,7 +21,7 @@ frappe.query_reports["Sales Person Commission Summary"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js index a02d0711128..828e9e74f23 100644 --- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js +++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js @@ -20,7 +20,7 @@ frappe.query_reports["Sales Person-wise Transaction Summary"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index b2a64a5d461..ad2f1e5f282 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -219,7 +219,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran serial_no: item.serial_no || "", }, callback:function(r){ - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if (['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; if (has_batch_no) { me.set_batch_number(cdt, cdn); @@ -332,7 +332,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran if ((doc.packed_items || []).length) { $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true); - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if (['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { var help_msg = "
" + __("For 'Product Bundle' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Product Bundle' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+ "
"; @@ -340,7 +340,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } } else { $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false); - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if (['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = ''; } } @@ -367,7 +367,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) { super.conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate); if(frappe.meta.get_docfield(cdt, "stock_qty", cdn) && - in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + ['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; this.set_batch_number(cdt, cdn); } @@ -376,7 +376,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran qty(doc, cdt, cdn) { super.qty(doc, cdt, cdn); - if(in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if(['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; this.set_batch_number(cdt, cdn); } @@ -440,7 +440,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran }; frappe.ui.form.on(cur_frm.doctype,"project", function(frm) { - if(in_list(["Delivery Note", "Sales Invoice"], frm.doc.doctype)) { + if(["Delivery Note", "Sales Invoice"].includes(frm.doc.doctype)) { if(frm.doc.project) { frappe.call({ method:'erpnext.projects.doctype.project.project.get_cost_center_name' , diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 6c237d787bb..ff4e186d6ce 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -12,10 +12,11 @@ frappe.ui.form.on("Company", { } }); } - - frm.call("check_if_transactions_exist").then((r) => { - frm.toggle_enable("default_currency", !r.message); - }); + if (!frm.doc.__islocal) { + frm.call("check_if_transactions_exist").then((r) => { + frm.toggle_enable("default_currency", !r.message); + }); + } }, setup: function (frm) { frm.__rename_queue = "long"; diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 349c328c06c..70a0872a2c4 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -712,7 +712,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-10-23 10:19:24.322898", + "modified": "2024-05-27 17:32:49.057386", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -768,6 +768,10 @@ "role": "Accounts Manager", "share": 1, "write": 1 + }, + { + "role": "Auditor", + "select": 1 } ], "show_name_in_global_search": 1, diff --git a/erpnext/setup/doctype/department/department.json b/erpnext/setup/doctype/department/department.json index 99deca5c19d..fa6b9ad4a55 100644 --- a/erpnext/setup/doctype/department/department.json +++ b/erpnext/setup/doctype/department/department.json @@ -90,7 +90,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2023-08-28 17:26:46.826501", + "modified": "2024-06-12 16:10:31.451257", "modified_by": "Administrator", "module": "Setup", "name": "Department", @@ -132,6 +132,10 @@ "role": "HR Manager", "share": 1, "write": 1 + }, + { + "role": "Employee", + "select": 1 } ], "show_name_in_global_search": 1, diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js index 0f0221fa562..aec752aec77 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js @@ -8,7 +8,7 @@ frappe.ui.form.on("Closing Stock Balance", { }, generate_closing_balance(frm) { - if (in_list(["Queued", "Failed"], frm.doc.status)) { + if (["Queued", "Failed"].includes(frm.doc.status)) { frm.add_custom_button(__("Generate Closing Stock Balance"), () => { frm.call({ method: "enqueue_job", diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js index 230107caadb..65a1be33224 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js @@ -1,9 +1,9 @@ frappe.listview_settings["Delivery Trip"] = { add_fields: ["status"], get_indicator: function (doc) { - if (in_list(["Cancelled", "Draft"], doc.status)) { + if (["Cancelled", "Draft"].includes(doc.status)) { return [__(doc.status), "red", "status,=," + doc.status]; - } else if (in_list(["In Transit", "Scheduled"], doc.status)) { + } else if (["In Transit", "Scheduled"].includes(doc.status)) { return [__(doc.status), "orange", "status,=," + doc.status]; } else if (doc.status === "Completed") { return [__(doc.status), "green", "status,=," + doc.status]; diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index b2b06e05871..3543e42e217 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -5,7 +5,7 @@ import copy import json import frappe -from frappe import _ +from frappe import _, bold from frappe.model.document import Document from frappe.utils import ( cint, @@ -397,6 +397,13 @@ class Item(Document): def validate_warehouse_for_reorder(self): """Validate Reorder level table for duplicate and conditional mandatory""" warehouse_material_request_type: list[tuple[str, str]] = [] + + _warehouse_before_save = frappe._dict() + if not self.is_new() and self._doc_before_save: + _warehouse_before_save = { + d.name: d.warehouse for d in self._doc_before_save.get("reorder_levels") or [] + } + for d in self.get("reorder_levels"): if not d.warehouse_group: d.warehouse_group = d.warehouse @@ -413,6 +420,19 @@ class Item(Document): if d.warehouse_reorder_level and not d.warehouse_reorder_qty: frappe.throw(_("Row #{0}: Please set reorder quantity").format(d.idx)) + if d.warehouse_group and d.warehouse: + if _warehouse_before_save.get(d.name) == d.warehouse: + continue + + child_warehouses = get_child_warehouses(d.warehouse_group) + if d.warehouse not in child_warehouses: + frappe.throw( + _( + "Row #{0}: The warehouse {1} is not a child warehouse of a group warehouse {2}" + ).format(d.idx, bold(d.warehouse), bold(d.warehouse_group)), + title=_("Incorrect Check in (group) Warehouse for Reorder"), + ) + def stock_ledger_created(self): if not hasattr(self, "_stock_ledger_created"): self._stock_ledger_created = len( @@ -1318,3 +1338,10 @@ def get_asset_naming_series(): from erpnext.assets.doctype.asset.asset import get_asset_naming_series return get_asset_naming_series() + + +@frappe.request_cache +def get_child_warehouses(warehouse): + from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses + + return get_child_warehouses(warehouse) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index d486e972771..827261d6540 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -848,6 +848,27 @@ class TestItem(FrappeTestCase): self.assertEqual(data[0].description, item.description) self.assertTrue("description" in data[0]) + def test_group_warehouse_for_reorder_item(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + item_doc = make_item("_Test Group Warehouse For Reorder Item", {"is_stock_item": 1}) + warehouse = create_warehouse("_Test Warehouse - _TC") + warehouse_doc = frappe.get_doc("Warehouse", warehouse) + warehouse_doc.db_set("parent_warehouse", "") + + item_doc.append( + "reorder_levels", + { + "warehouse": warehouse, + "warehouse_reorder_level": 10, + "warehouse_reorder_qty": 100, + "material_request_type": "Purchase", + "warehouse_group": "_Test Warehouse Group - _TC", + }, + ) + + self.assertRaises(frappe.ValidationError, item_doc.save) + def set_item_variant_settings(fields): doc = frappe.get_doc("Item Variant Settings") diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index f6b7e0d8435..81dea899f39 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -224,7 +224,10 @@ class LandedCostVoucher(Document): # update stock & gl entries for submit state of PR doc.docstatus = 1 doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) - doc.make_gl_entries() + if d.receipt_document_type == "Purchase Receipt": + doc.make_gl_entries(via_landed_cost_voucher=True) + else: + doc.make_gl_entries() doc.repost_future_sle_and_gle() def validate_asset_qty_and_status(self, receipt_document_type, receipt_document): diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 948011cce69..b4aadbd7987 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -18,6 +18,7 @@ "parent_warehouse", "consider_rejected_warehouses", "get_item_locations", + "pick_manually", "section_break_6", "scan_barcode", "column_break_13", @@ -192,11 +193,18 @@ "fieldname": "consider_rejected_warehouses", "fieldtype": "Check", "label": "Consider Rejected Warehouses" + }, + { + "default": "0", + "description": "If enabled then system won't override the picked qty / batches / serial numbers.", + "fieldname": "pick_manually", + "fieldtype": "Check", + "label": "Pick Manually" } ], "is_submittable": 1, "links": [], - "modified": "2024-01-24 17:05:20.317180", + "modified": "2024-03-27 22:49:16.954637", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", @@ -268,4 +276,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index ef7dc710b13..99858835fed 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -29,7 +29,8 @@ class PickList(Document): def before_save(self): self.update_status() - self.set_item_locations() + if not self.pick_manually: + self.set_item_locations() if self.get("locations"): self.validate_sales_order_percentage() diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index a6fd929ea34..7bbe7b9ff75 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -307,13 +307,13 @@ class PurchaseReceipt(BuyingController): self.delete_auto_created_batches() self.set_consumed_qty_in_subcontract_order() - def get_gl_entries(self, warehouse_account=None): + def get_gl_entries(self, warehouse_account=None, via_landed_cost_voucher=False): from erpnext.accounts.general_ledger import process_gl_map gl_entries = [] self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) - self.make_tax_gl_entries(gl_entries) + self.make_tax_gl_entries(gl_entries, via_landed_cost_voucher) update_regional_gl_entries(gl_entries, self) return process_gl_map(gl_entries) @@ -661,7 +661,7 @@ class PurchaseReceipt(BuyingController): posting_date=posting_date, ) - def make_tax_gl_entries(self, gl_entries): + def make_tax_gl_entries(self, gl_entries, via_landed_cost_voucher=False): negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")]) is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) # Cost center-wise amount breakup for other charges included for valuation @@ -696,18 +696,17 @@ class PurchaseReceipt(BuyingController): i = 1 for tax in self.get("taxes"): if valuation_tax.get(tax.name): - negative_expense_booked_in_pi = frappe.db.sql( - """select name from `tabPurchase Invoice Item` pi - where docstatus = 1 and purchase_receipt=%s - and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' - and voucher_no=pi.parent and account=%s)""", - (self.name, tax.account_head), - ) - - if negative_expense_booked_in_pi: - account = stock_rbnb - else: + if via_landed_cost_voucher: account = tax.account_head + else: + negative_expense_booked_in_pi = frappe.db.sql( + """select name from `tabPurchase Invoice Item` pi + where docstatus = 1 and purchase_receipt=%s + and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' + and voucher_no=pi.parent and account=%s)""", + (self.name, tax.account_head), + ) + account = stock_rbnb if negative_expense_booked_in_pi else tax.account_head if i == len(valuation_tax): applicable_amount = amount_including_divisional_loss diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index b185fd471f9..52dd85767f8 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -9,6 +9,7 @@ from pypika import functions as fn import erpnext from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.controllers.buying_controller import QtyMismatchError +from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.item.test_item import create_item, make_item from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -1640,7 +1641,6 @@ class TestPurchaseReceipt(FrappeTestCase): frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 0) def test_internal_pr_gl_entries(self): - from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry @@ -2301,6 +2301,54 @@ class TestPurchaseReceipt(FrappeTestCase): for index, d in enumerate(data): self.assertEqual(d.qty_after_transaction, 11 + index) + def test_valuation_taxes_lcv_repost_after_billing(self): + from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import ( + make_landed_cost_voucher, + ) + + old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled("_Test Company") + frappe.local.enable_perpetual_inventory["_Test Company"] = 1 + frappe.db.set_value( + "Company", + "_Test Company", + "stock_received_but_not_billed", + "Stock Received But Not Billed - _TC", + ) + + pr = make_purchase_receipt(qty=10, rate=1000, do_not_submit=1) + pr.append( + "taxes", + { + "category": "Valuation and Total", + "charge_type": "Actual", + "account_head": "Freight and Forwarding Charges - _TC", + "tax_amount": 2000, + "description": "Test", + }, + ) + pr.submit() + pi = make_purchase_invoice(pr.name) + pi.submit() + make_landed_cost_voucher( + company=pr.company, + receipt_document_type="Purchase Receipt", + receipt_document=pr.name, + charges=2000, + distribute_charges_based_on="Qty", + expense_account="Expenses Included In Valuation - _TC", + ) + + gl_entries = get_gl_entries("Purchase Receipt", pr.name, skip_cancelled=True, as_dict=False) + warehouse_account = get_warehouse_account_map("_Test Company") + expected_gle = ( + ("Stock Received But Not Billed - _TC", 0, 10000, "Main - _TC"), + ("Freight and Forwarding Charges - _TC", 0, 2000, "Main - _TC"), + ("Expenses Included In Valuation - _TC", 0, 2000, "Main - _TC"), + (warehouse_account[pr.items[0].warehouse]["account"], 14000, 0, "Main - _TC"), + ) + self.assertSequenceEqual(expected_gle, gl_entries) + frappe.local.enable_perpetual_inventory["_Test Company"] = old_perpetual_inventory + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier @@ -2347,14 +2395,24 @@ def get_sl_entries(voucher_type, voucher_no): ) -def get_gl_entries(voucher_type, voucher_no): - return frappe.db.sql( - """select account, debit, credit, cost_center, is_cancelled - from `tabGL Entry` where voucher_type=%s and voucher_no=%s - order by account desc""", - (voucher_type, voucher_no), - as_dict=1, +def get_gl_entries(voucher_type, voucher_no, skip_cancelled=False, as_dict=True): + gl = frappe.qb.DocType("GL Entry") + gl_query = ( + frappe.qb.from_(gl) + .select( + gl.account, + gl.debit, + gl.credit, + gl.cost_center, + ) + .where((gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no)) + .orderby(gl.account, order=frappe.qb.desc) ) + if skip_cancelled: + gl_query = gl_query.where(gl.is_cancelled == 0) + else: + gl_query = gl_query.select(gl.is_cancelled) + return gl_query.run(as_dict=as_dict) def get_taxes(**args): diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index e47c1f8aba7..60a7707228d 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -45,7 +45,7 @@ class RepostItemValuation(Document): def validate_period_closing_voucher(self): # Period Closing Voucher - year_end_date = self.get_max_year_end_date(self.company) + year_end_date = self.get_max_period_closing_date(self.company) if year_end_date and getdate(self.posting_date) <= getdate(year_end_date): date = frappe.format(year_end_date, "Date") msg = f"Due to period closing, you cannot repost item valuation before {date}" @@ -88,24 +88,16 @@ class RepostItemValuation(Document): return frappe.get_all("Closing Stock Balance", fields=["name", "to_date"], filters=filters) @staticmethod - def get_max_year_end_date(company): - data = frappe.get_all( - "Period Closing Voucher", fields=["fiscal_year"], filters={"docstatus": 1, "company": company} - ) - - if not data: - return - - fiscal_years = [d.fiscal_year for d in data] - table = frappe.qb.DocType("Fiscal Year") + def get_max_period_closing_date(company): + table = frappe.qb.DocType("Period Closing Voucher") query = ( frappe.qb.from_(table) - .select(Max(table.year_end_date)) - .where((table.name.isin(fiscal_years)) & (table.disabled == 0)) + .select(Max(table.posting_date)) + .where((table.company == company) & (table.docstatus == 1)) ).run() - return query[0][0] if query else None + return query[0][0] if query and query[0][0] else None def validate_accounts_freeze(self): acc_settings = frappe.db.get_value( diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index b6459ba1e28..d58361b2d3b 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -77,7 +77,7 @@ frappe.ui.form.on('Stock Entry', { if(!item.item_code) { frappe.throw(__("Please enter Item Code to get Batch Number")); } else { - if (in_list(["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"], doc.purpose)) { + if (["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"].includes(doc.purpose)) { var filters = { 'item_code': item.item_code, 'posting_date': frm.doc.posting_date || frappe.datetime.nowdate() diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js index 4d0472ceaa8..cde502ad738 100644 --- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js +++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js @@ -22,14 +22,14 @@ frappe.query_reports["Incorrect Serial No Valuation"] = { fieldtype: "Date", fieldname: "from_date", reqd: 1, - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { label: __("To Date"), fieldtype: "Date", fieldname: "to_date", reqd: 1, - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, ], }; diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 4e624c9acb0..0cd2ecdaaf5 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1196,7 +1196,11 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc order by posting_datetime desc, creation desc limit 1 for update""", - args, + { + "item_code": args.get("item_code"), + "warehouse": args.get("warehouse"), + "posting_datetime": args.get("posting_datetime"), + }, as_dict=1, ) diff --git a/erpnext/templates/form_grid/item_grid.html b/erpnext/templates/form_grid/item_grid.html index c596890aa32..449edd14e15 100644 --- a/erpnext/templates/form_grid/item_grid.html +++ b/erpnext/templates/form_grid/item_grid.html @@ -18,7 +18,7 @@ actual_qty = (frm.doc.doctype==="Sales Order" ? doc.projected_qty : doc.actual_qty); if(flt(frm.doc.per_delivered) < 100 - && in_list(["Sales Order Item", "Delivery Note Item"], doc.doctype)) { + && ["Sales Order Item", "Delivery Note Item"].includes(doc.doctype)) { if(actual_qty != undefined) { if(actual_qty >= doc.qty) { var color = "green"; diff --git a/erpnext/templates/print_formats/includes/total.html b/erpnext/templates/print_formats/includes/total.html index 879203bbf25..f964047bd08 100644 --- a/erpnext/templates/print_formats/includes/total.html +++ b/erpnext/templates/print_formats/includes/total.html @@ -1,14 +1,14 @@ -
+
{% if doc.flags.show_inclusive_tax_in_print %}
-
+
{{ doc.get_formatted("net_total", doc) }}
{% else %}
-
+
{{ doc.get_formatted("total", doc) }}
{% endif %}