Merge pull request #43025 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
ruthra kumar
2024-09-04 20:28:07 +05:30
committed by GitHub
18 changed files with 150 additions and 46 deletions

View File

@@ -36,8 +36,12 @@ frappe.ui.form.on("Bank Clearance", {
refresh: function (frm) { refresh: function (frm) {
frm.disable_save(); frm.disable_save();
frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries")); frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries"));
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
frm.change_custom_button_type("Get Payment Entries", null, "primary"); if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () => frm.trigger("update_clearance_date"));
frm.change_custom_button_type(__("Get Payment Entries"), null, "default");
frm.change_custom_button_type(__("Update Clearance Date"), null, "primary");
}
}, },
update_clearance_date: function (frm) { update_clearance_date: function (frm) {
@@ -45,13 +49,7 @@ frappe.ui.form.on("Bank Clearance", {
method: "update_clearance_date", method: "update_clearance_date",
doc: frm.doc, doc: frm.doc,
callback: function (r, rt) { callback: function (r, rt) {
frm.refresh_field("payment_entries"); frm.refresh();
frm.refresh_fields();
if (!frm.doc.payment_entries.length) {
frm.change_custom_button_type("Get Payment Entries", null, "primary");
frm.change_custom_button_type("Update Clearance Date", null, "default");
}
}, },
}); });
}, },
@@ -60,17 +58,8 @@ frappe.ui.form.on("Bank Clearance", {
return frappe.call({ return frappe.call({
method: "get_payment_entries", method: "get_payment_entries",
doc: frm.doc, doc: frm.doc,
callback: function (r, rt) { callback: function () {
frm.refresh_field("payment_entries"); frm.refresh();
if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () =>
frm.trigger("update_clearance_date")
);
frm.change_custom_button_type("Get Payment Entries", null, "default");
frm.change_custom_button_type("Update Clearance Date", null, "primary");
}
}, },
}); });
}, },

View File

@@ -250,7 +250,7 @@ frappe.ui.form.on('Payment Entry', {
}, },
set_dynamic_labels: function(frm) { set_dynamic_labels: function(frm) {
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: ""; var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company)?.default_currency: "";
frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount", frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax", "difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax",
@@ -517,7 +517,7 @@ frappe.ui.form.on('Payment Entry', {
frm.set_value("source_exchange_rate", 1); frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from){ } else if (frm.doc.paid_from){
if (["Internal Transfer", "Pay"].includes(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; let company_currency = frappe.get_doc(":Company", frm.doc.company)?.default_currency;
frappe.call({ frappe.call({
method: "erpnext.setup.utils.get_exchange_rate", method: "erpnext.setup.utils.get_exchange_rate",
args: { args: {

View File

@@ -6,7 +6,9 @@ import unittest
import frappe import frappe
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import get_item_details
@@ -1099,6 +1101,69 @@ class TestPricingRule(unittest.TestCase):
pricing_rule.is_recursive = True pricing_rule.is_recursive = True
self.assertRaises(frappe.ValidationError, pricing_rule.save) self.assertRaises(frappe.ValidationError, pricing_rule.save)
def test_ignore_pricing_rule_for_credit_note(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
pricing_rule = make_pricing_rule(
discount_percentage=20,
selling=1,
buying=1,
priority=1,
title="_Test Pricing Rule",
)
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
item = si.items[0]
si.submit()
self.assertEqual(item.discount_percentage, 20)
self.assertEqual(item.rate, 80)
# change discount on pricing rule
pricing_rule.discount_percentage = 30
pricing_rule.save()
credit_note = make_return_doc(si.doctype, si.name)
credit_note.save()
self.assertEqual(credit_note.ignore_pricing_rule, 1)
self.assertEqual(credit_note.pricing_rules, [])
self.assertEqual(credit_note.items[0].discount_percentage, 20)
self.assertEqual(credit_note.items[0].rate, 80)
self.assertEqual(credit_note.items[0].pricing_rules, None)
credit_note.delete()
si.cancel()
def test_ignore_pricing_rule_for_debit_note(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
pricing_rule = make_pricing_rule(
discount_percentage=20,
buying=1,
priority=1,
title="_Test Pricing Rule",
)
pi = make_purchase_invoice(do_not_submit=True, supplier="_Test Supplier 1", qty=1)
item = pi.items[0]
pi.submit()
self.assertEqual(item.discount_percentage, 20)
self.assertEqual(item.rate, 40)
# change discount on pricing rule
pricing_rule.discount_percentage = 30
pricing_rule.save()
# create debit note from purchase invoice
debit_note = make_return_doc(pi.doctype, pi.name)
debit_note.save()
self.assertEqual(debit_note.ignore_pricing_rule, 1)
self.assertEqual(debit_note.pricing_rules, [])
self.assertEqual(debit_note.items[0].discount_percentage, 20)
self.assertEqual(debit_note.items[0].rate, 40)
self.assertEqual(debit_note.items[0].pricing_rules, None)
debit_note.delete()
pi.cancel()
test_dependencies = ["Campaign"] test_dependencies = ["Campaign"]

View File

@@ -369,6 +369,21 @@ class PurchaseInvoice(BuyingController):
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
elif item.is_fixed_asset: elif item.is_fixed_asset:
account = None account = None
if not item.pr_detail and item.po_detail:
receipt_item = frappe.get_cached_value(
"Purchase Receipt Item",
{
"purchase_order": item.purchase_order,
"purchase_order_item": item.po_detail,
"docstatus": 1,
},
["name", "parent"],
as_dict=1,
)
if receipt_item:
item.pr_detail = receipt_item.name
item.purchase_receipt = receipt_item.parent
if item.pr_detail: if item.pr_detail:
if not self.asset_received_but_not_billed: if not self.asset_received_but_not_billed:
self.asset_received_but_not_billed = self.get_company_default( self.asset_received_but_not_billed = self.get_company_default(

View File

@@ -10,7 +10,10 @@ import erpnext
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice as make_pi_from_po
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
create_purchase_order,
)
from erpnext.buying.doctype.supplier.test_supplier import create_supplier from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.controllers.buying_controller import QtyMismatchError from erpnext.controllers.buying_controller import QtyMismatchError

View File

@@ -46,4 +46,20 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
fieldtype: "Check", fieldtype: "Check",
}, },
], ],
formatter: function (value, row, column, data, default_formatter, filter) {
if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") {
column.link_onclick =
"frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()";
}
return default_formatter(value, row, column, data);
},
open_utility_report: function () {
frappe.route_options = {
company: frappe.query_report.get_filter_value("company"),
account: frappe.query_report.get_filter_value("account"),
report_date: frappe.query_report.get_filter_value("report_date"),
};
frappe.open_in_new_tab = true;
frappe.set_route("query-report", "Cheques and Deposits Incorrectly cleared");
},
}; };

View File

@@ -43,7 +43,7 @@ function get_filters() {
label: __("From Fiscal Year"), label: __("From Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year, default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1, reqd: 1,
}, },
{ {
@@ -51,7 +51,7 @@ function get_filters() {
label: __("To Fiscal Year"), label: __("To Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year, default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1, reqd: 1,
}, },
{ {

View File

@@ -11,6 +11,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
onload() { onload() {
this.setup_queries(); this.setup_queries();
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
} }
refresh() { refresh() {

View File

@@ -84,7 +84,6 @@ force_item_fields = (
"brand", "brand",
"stock_uom", "stock_uom",
"is_fixed_asset", "is_fixed_asset",
"item_tax_rate",
"pricing_rules", "pricing_rules",
"weight_per_unit", "weight_per_unit",
"weight_uom", "weight_uom",
@@ -707,7 +706,6 @@ class AccountsController(TransactionBase):
args["is_subcontracted"] = self.is_subcontracted args["is_subcontracted"] = self.is_subcontracted
ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False) ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
for fieldname, value in ret.items(): for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and value is not None: if item.meta.get_field(fieldname) and value is not None:
if item.get(fieldname) is None or fieldname in force_item_fields: if item.get(fieldname) is None or fieldname in force_item_fields:
@@ -717,7 +715,10 @@ class AccountsController(TransactionBase):
fieldname fieldname
): ):
item.set(fieldname, value) item.set(fieldname, value)
elif fieldname == "item_tax_rate" and not (
self.get("is_return") and self.get("return_against")
):
item.set(fieldname, value)
elif fieldname == "serial_no": elif fieldname == "serial_no":
# Ensure that serial numbers are matched against Stock UOM # Ensure that serial numbers are matched against Stock UOM
item_conversion_factor = item.get("conversion_factor") or 1.0 item_conversion_factor = item.get("conversion_factor") or 1.0

View File

@@ -334,6 +334,8 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
def set_missing_values(source, target): def set_missing_values(source, target):
doc = frappe.get_doc(target) doc = frappe.get_doc(target)
doc.is_return = 1 doc.is_return = 1
doc.ignore_pricing_rule = 1
doc.pricing_rules = []
doc.return_against = source.name doc.return_against = source.name
doc.set_warehouse = "" doc.set_warehouse = ""
if doctype == "Sales Invoice" or doctype == "POS Invoice": if doctype == "Sales Invoice" or doctype == "POS Invoice":
@@ -397,6 +399,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
def update_item(source_doc, target_doc, source_parent): def update_item(source_doc, target_doc, source_parent):
target_doc.qty = -1 * source_doc.qty target_doc.qty = -1 * source_doc.qty
target_doc.pricing_rules = None
if source_doc.serial_no: if source_doc.serial_no:
returned_serial_nos = get_returned_serial_nos(source_doc, source_parent) returned_serial_nos = get_returned_serial_nos(source_doc, source_parent)

View File

@@ -90,6 +90,9 @@ class calculate_taxes_and_totals:
self.doc.base_tax_withholding_net_total = sum_base_net_amount self.doc.base_tax_withholding_net_total = sum_base_net_amount
def validate_item_tax_template(self): def validate_item_tax_template(self):
if self.doc.get("is_return") and self.doc.get("return_against"):
return
for item in self._items: for item in self._items:
if item.item_code and item.get("item_tax_template"): if item.item_code and item.get("item_tax_template"):
item_doc = frappe.get_cached_doc("Item", item.item_code) item_doc = frappe.get_cached_doc("Item", item.item_code)
@@ -238,7 +241,6 @@ class calculate_taxes_and_totals:
"tax_fraction_for_current_item", "tax_fraction_for_current_item",
"grand_total_fraction_for_current_item", "grand_total_fraction_for_current_item",
] ]
if tax.charge_type != "Actual" and not ( if tax.charge_type != "Actual" and not (
self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total" self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
): ):

View File

@@ -1817,6 +1817,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
let item_rates = {}; let item_rates = {};
let item_tax_templates = {}; let item_tax_templates = {};
if (me.frm.doc.is_return && me.frm.doc.return_against) return;
$.each(this.frm.doc.items || [], function(i, item) { $.each(this.frm.doc.items || [], function(i, item) {
if (item.item_code) { if (item.item_code) {
// Use combination of name and item code in case same item is added multiple times // Use combination of name and item code in case same item is added multiple times

View File

@@ -115,14 +115,17 @@ erpnext.financial_statements = {
erpnext.financial_statements.filters = get_filters(); erpnext.financial_statements.filters = get_filters();
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
var filters = report.get_values();
frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) { if (!filters.period_start_date || !filters.period_end_date) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); frappe.model.with_doc("Fiscal Year", fiscal_year, function (r) {
frappe.query_report.set_filter_value({ var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
period_start_date: fy.year_start_date, frappe.query_report.set_filter_value({
period_end_date: fy.year_end_date, period_start_date: fy.year_start_date,
period_end_date: fy.year_end_date,
});
}); });
}); }
const views_menu = report.page.add_custom_button_group(__("Financial Statements")); const views_menu = report.page.add_custom_button_group(__("Financial Statements"));

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Sales Partner Target Variance based on Item Group"] = {
label: __("Fiscal Year"), label: __("Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year, default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
}, },
{ {
fieldname: "doctype", fieldname: "doctype",

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Sales Person Target Variance Based On Item Group"] = {
label: __("Fiscal Year"), label: __("Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year, default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
}, },
{ {
fieldname: "doctype", fieldname: "doctype",

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Territory Target Variance Based On Item Group"] = {
label: __("Fiscal Year"), label: __("Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year, default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
}, },
{ {
fieldname: "doctype", fieldname: "doctype",

View File

@@ -546,7 +546,7 @@ class PurchaseReceipt(BuyingController):
if not ( if not (
(erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items) (erpnext.is_perpetual_inventory_enabled(self.company) and d.item_code in stock_items)
or d.is_fixed_asset or (d.is_fixed_asset and not d.purchase_invoice)
): ):
continue continue

View File

@@ -855,14 +855,10 @@ def get_price_list_rate(args, item_doc, out=None):
if price_list_rate is None or frappe.db.get_single_value( if price_list_rate is None or frappe.db.get_single_value(
"Stock Settings", "update_existing_price_list_rate" "Stock Settings", "update_existing_price_list_rate"
): ):
if args.get("is_internal_supplier") or args.get("is_internal_customer"): insert_item_price(args)
return out
if args.price_list and args.rate: if price_list_rate is None:
insert_item_price(args) return out
if not price_list_rate:
return out
out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate) out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate)
@@ -883,6 +879,14 @@ def get_price_list_rate(args, item_doc, out=None):
def insert_item_price(args): def insert_item_price(args):
"""Insert Item Price if Price List and Price List Rate are specified and currency is the same""" """Insert Item Price if Price List and Price List Rate are specified and currency is the same"""
if (
not args.price_list
or not args.rate
or args.get("is_internal_supplier")
or args.get("is_internal_customer")
):
return
if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency and cint( if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency and cint(
frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing") frappe.db.get_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing")
): ):