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 @@
- | 30 Days |
- 60 Days |
- 90 Days |
- 120 Days |
+ 0 - 30 Days |
+ 30 - 60 Days |
+ 60 - 90 Days |
+ 90 - 120 Days |
+ Above 120 Days |
@@ -352,6 +353,7 @@
{{ 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