Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: release v14 #42254

Merged
merged 31 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2cf8256
refactor: remove obsolete function call (#42162)
Mutantpenguin Jul 3, 2024
701dd9e
fix: multiple free items on same Item Group
ruthra-kumar Jul 3, 2024
454e147
fix: manual pick allow to pick more than available stock (backport #4…
mergify[bot] Jul 3, 2024
0f1f5b6
Merge pull request #42169 from frappe/mergify/bp/version-14-hotfix/pr…
ruthra-kumar Jul 3, 2024
49fb6be
refactor: validation to prevent recursion with mixed conditions
ruthra-kumar Jul 2, 2024
9fde733
fix: use standard method to get `_doc_before_save`
ruthra-kumar Jul 3, 2024
9931776
test: validation on mixed condition with recursion
ruthra-kumar Jul 3, 2024
71cbebd
test: validation on mixed condition and recursion on pricing rule
ruthra-kumar Jul 3, 2024
d5fa968
chore: resolve conflicts
ruthra-kumar Jul 3, 2024
4d6a71a
fix: fetch expence account from asset category
khushi8112 Jul 3, 2024
d92a042
Merge pull request #42172 from frappe/mergify/bp/version-14-hotfix/pr…
ruthra-kumar Jul 4, 2024
45899b3
Merge pull request #42174 from khushi8112/fetch-expence-account-from-…
khushi8112 Jul 4, 2024
62ad466
fix: group by in item-wise purchase register
Nihantra-Patel Jun 21, 2024
64f8498
Merge pull request #42180 from frappe/mergify/bp/version-14-hotfix/pr…
ruthra-kumar Jul 4, 2024
13895fa
fix: empty item-wise sales/purchase register reports on initial load
ruthra-kumar Jul 4, 2024
e9357c1
Merge pull request #42184 from frappe/mergify/bp/version-14-hotfix/pr…
ruthra-kumar Jul 4, 2024
e2f8e02
fix: stock qty validation in SCR (backport #42124) (#42224)
mergify[bot] Jul 8, 2024
d5c1c62
fix: add missing german translations
barredterra Jul 8, 2024
f2d5a69
Merge pull request #42237 from frappe/mergify/bp/version-14-hotfix/pr…
barredterra Jul 8, 2024
fcf6500
fix(Holiday List): sort holidays on save to avoid disorienting the us…
mergify[bot] Jul 9, 2024
f2f1f32
Merge pull request #42163 from frappe/mergify/bp/version-14-hotfix/pr…
ruthra-kumar Jul 10, 2024
49e5066
fix: updated logic for calculating tax_withholding_net_total in payme…
ljain112 Jul 2, 2024
5000c09
Merge pull request #42261 from frappe/mergify/bp/version-14-hotfix/pr…
sagarvora Jul 10, 2024
51cbbee
fix(tds): use doctype reference when mapping keys across multiple doc…
ljain112 Jul 10, 2024
fdb8e5b
Merge pull request #42262 from ljain112/fix-tds-backport
vorasmit Jul 10, 2024
4195c50
fix: removed max discount validation for sales return
ljain112 Jul 1, 2024
00e8b86
Merge pull request #42264 from frappe/mergify/bp/version-14-hotfix/pr…
vorasmit Jul 10, 2024
106c154
fix: tax on stock_rbnb on repost of Purchase Receipt
ruthra-kumar Jul 9, 2024
fdf1dfe
test: tax account heads on PR report without LCV
ruthra-kumar Jul 9, 2024
115a012
chore: resolve conflict
ruthra-kumar Jul 10, 2024
5c75bb8
Merge pull request #42271 from frappe/mergify/bp/version-14-hotfix/pr…
ruthra-kumar Jul 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions erpnext/accounts/doctype/payment_entry/payment_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def validate(self):
self.set_exchange_rate()
self.validate_mandatory()
self.validate_reference_documents()
self.set_tax_withholding()
self.set_amounts()
self.validate_amounts()
self.apply_taxes()
Expand All @@ -89,6 +88,7 @@ def validate(self):
self.validate_allocated_amount()
self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked()
self.set_tax_withholding()
self.set_status()

def on_submit(self):
Expand Down Expand Up @@ -674,9 +674,7 @@ def set_tax_withholding(self):
if not self.apply_tax_withholding_amount:
return

order_amount = self.get_order_net_total()

net_total = flt(order_amount) + flt(self.unallocated_amount)
net_total = self.calculate_tax_withholding_net_total()

# Adding args as purchase invoice to get TDS amount
args = frappe._dict(
Expand Down Expand Up @@ -720,20 +718,42 @@ def set_tax_withholding(self):
for d in to_remove:
self.remove(d)

def get_order_net_total(self):
def calculate_tax_withholding_net_total(self):
net_total = 0
order_details = self.get_order_wise_tax_withholding_net_total()

for d in self.references:
tax_withholding_net_total = order_details.get(d.reference_name)
if not tax_withholding_net_total:
continue

net_taxable_outstanding = max(
0, d.outstanding_amount - (d.total_amount - tax_withholding_net_total)
)

net_total += min(net_taxable_outstanding, d.allocated_amount)

net_total += self.unallocated_amount

return net_total

def get_order_wise_tax_withholding_net_total(self):
if self.party_type == "Supplier":
doctype = "Purchase Order"
else:
doctype = "Sales Order"

docnames = [d.reference_name for d in self.references if d.reference_doctype == doctype]

tax_withholding_net_total = frappe.db.get_value(
doctype, {"name": ["in", docnames]}, ["sum(base_tax_withholding_net_total)"]
return frappe._dict(
frappe.db.get_all(
doctype,
filters={"name": ["in", docnames]},
fields=["name", "base_tax_withholding_net_total"],
as_list=True,
)
)

return tax_withholding_net_total

def apply_taxes(self):
self.initialize_taxes()
self.determine_exclusive_rate()
Expand Down
5 changes: 5 additions & 0 deletions erpnext/accounts/doctype/pricing_rule/pricing_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def validate(self):
self.validate_price_list_with_currency()
self.validate_dates()
self.validate_condition()
self.validate_mixed_with_recursion()

if not self.margin_type:
self.margin_rate_or_amount = 0.0
Expand Down Expand Up @@ -201,6 +202,10 @@ def validate_condition(self):
):
frappe.throw(_("Invalid condition expression"))

def validate_mixed_with_recursion(self):
if self.mixed_conditions and self.is_recursive:
frappe.throw(_("Recursive Discounts with Mixed condition is not supported by the system"))


# --------------------------------------------------------------------------------

Expand Down
12 changes: 12 additions & 0 deletions erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,18 @@ 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")

def test_validation_on_mixed_condition_with_recursion(self):
pricing_rule = make_pricing_rule(
discount_percentage=10,
selling=1,
priority=2,
min_qty=4,
title="_Test Pricing Rule with Min Qty - 2",
)
pricing_rule.mixed_conditions = True
pricing_rule.is_recursive = True
self.assertRaises(frappe.ValidationError, pricing_rule.save)


test_dependencies = ["Campaign"]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def validate(self):

self.validate_applicable_for()
self.validate_pricing_rules()
self.validate_mixed_with_recursion()

def validate_applicable_for(self):
if self.applicable_for:
Expand All @@ -94,7 +95,7 @@ def validate_pricing_rules(self):
docnames = []

# If user has changed applicable for
if self._doc_before_save.applicable_for == self.applicable_for:
if self.get_doc_before_save() and self.get_doc_before_save().applicable_for == self.applicable_for:
return

docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
Expand All @@ -108,6 +109,7 @@ def validate_pricing_rules(self):
frappe.delete_doc("Pricing Rule", docname.name)

def on_update(self):
self.validate()
pricing_rules = (
frappe.get_all(
"Pricing Rule",
Expand All @@ -119,6 +121,15 @@ def on_update(self):
)
self.update_pricing_rules(pricing_rules)

def validate_mixed_with_recursion(self):
if self.mixed_conditions:
if self.product_discount_slabs:
for slab in self.product_discount_slabs:
if slab.is_recursive:
frappe.throw(
_("Recursive Discounts with Mixed condition is not supported by the system")
)

def update_pricing_rules(self, pricing_rules):
rules = {}
count = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,25 @@ def test_min_max_amount_configuration(self):
price_rules = frappe.get_all("Pricing Rule", filters={"promotional_scheme": ps.name})
self.assertEqual(price_rules, [])

def test_validation_on_recurse_with_mixed_condition(self):
ps = make_promotional_scheme()
ps.set("price_discount_slabs", [])
ps.set(
"product_discount_slabs",
[
{
"rule_description": "12+1",
"min_qty": 12,
"free_item": "_Test Item 2",
"free_qty": 1,
"is_recursive": 1,
"recurse_for": 12,
}
],
)
ps.mixed_conditions = True
self.assertRaises(frappe.ValidationError, ps.save)


def make_promotional_scheme(**args):
args = frappe._dict(args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ frappe.query_reports["Item-wise Purchase Register"] = {
label: __("Group By"),
fieldname: "group_by",
fieldtype: "Select",
options: ["Supplier", "Item Group", "Item", "Invoice"],
options: ["", "Supplier", "Item Group", "Item", "Invoice"],
},
],
formatter: function (value, row, column, data, default_formatter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def apply_conditions(query, pi, pii, filters):
query = query.orderby(pi.posting_date, order=Order.desc)
query = query.orderby(pii.item_group, order=Order.desc)
else:
query = apply_group_by_conditions(filters, "Purchase Invoice")
query = apply_group_by_conditions(query, pi, pii, filters)

return query

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ frappe.query_reports["Item-wise Sales Register"] = {
label: __("Group By"),
fieldname: "group_by",
fieldtype: "Select",
options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"],
options: ["", "Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"],
},
],
formatter: function (value, row, column, data, default_formatter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def execute(filters=None):
else:
party_naming_by = frappe.db.get_single_value("Buying Settings", "supp_master_name")

filters.update({"naming_series": party_naming_by})
filters["naming_series"] = party_naming_by

validate_filters(filters)
(
Expand Down Expand Up @@ -63,21 +63,23 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
tax_withholding_category = tds_accounts.get(entry.account)
# or else the consolidated value from the voucher document
if not tax_withholding_category:
tax_withholding_category = tax_category_map.get(name)
tax_withholding_category = tax_category_map.get((voucher_type, name))
# or else from the party default
if not tax_withholding_category:
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")

rate = tax_rate_map.get(tax_withholding_category)
if net_total_map.get(name):
if net_total_map.get((voucher_type, name)):
if voucher_type == "Journal Entry" and tax_amount and rate:
# back calcalute total amount from rate and tax_amount
if rate:
total_amount = grand_total = base_total = tax_amount / (rate / 100)
elif voucher_type == "Purchase Invoice":
total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(name)
total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(
(voucher_type, name)
)
else:
total_amount, grand_total, base_total = net_total_map.get(name)
total_amount, grand_total, base_total = net_total_map.get((voucher_type, name))
else:
total_amount += entry.credit

Expand All @@ -97,7 +99,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
}

if filters.naming_series == "Naming Series":
row.update({"party_name": party_map.get(party, {}).get(party_name)})
row["party_name"] = party_map.get(party, {}).get(party_name)

row.update(
{
Expand Down Expand Up @@ -279,7 +281,6 @@ def get_tds_docs(filters):
journal_entries = []
tax_category_map = frappe._dict()
net_total_map = frappe._dict()
frappe._dict()
journal_entry_party_map = frappe._dict()
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")

Expand Down Expand Up @@ -412,7 +413,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
)

for entry in entries:
tax_category_map.update({entry.name: entry.tax_withholding_category})
tax_category_map[(doctype, entry.name)] = entry.tax_withholding_category
if doctype == "Purchase Invoice":
value = [
entry.base_tax_withholding_net_total,
Expand All @@ -427,7 +428,8 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
value = [entry.paid_amount, entry.paid_amount_after_tax, entry.base_paid_amount]
else:
value = [entry.total_amount] * 3
net_total_map.update({entry.name: value})

net_total_map[(doctype, entry.name)] = value


def get_tax_rate_map(filters):
Expand Down
4 changes: 2 additions & 2 deletions erpnext/assets/doctype/asset/test_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -1689,12 +1689,12 @@ def create_asset(**args):
return asset


def create_asset_category():
def create_asset_category(enable_cwip=1):
asset_category = frappe.new_doc("Asset Category")
asset_category.asset_category_name = "Computers"
asset_category.total_number_of_depreciations = 3
asset_category.frequency_of_depreciation = 3
asset_category.enable_cwip_accounting = 1
asset_category.enable_cwip_accounting = enable_cwip
asset_category.append(
"accounts",
{
Expand Down
3 changes: 3 additions & 0 deletions erpnext/controllers/accounts_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,9 @@ def set_missing_item_details(self, for_validate=False):
# reset pricing rule fields if pricing_rule_removed
item.set(fieldname, value)

elif fieldname == "expense_account" and not item.get("expense_account"):
item.expense_account = value

if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field(
"is_fixed_asset"
):
Expand Down
2 changes: 1 addition & 1 deletion erpnext/controllers/selling_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def onload(self):
def validate(self):
super().validate()
self.validate_items()
if not self.get("is_debit_note"):
if not (self.get("is_debit_note") or self.get("is_return")):
self.validate_max_discount()
self.validate_selling_price()
self.set_qty_as_per_stock_uom()
Expand Down
7 changes: 5 additions & 2 deletions erpnext/public/js/controllers/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -1614,12 +1614,15 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
apply_product_discount(args) {
const items = this.frm.doc.items.filter(d => (d.is_free_item)) || [];

const exist_items = items.map(row => (row.item_code, row.pricing_rules));
const exist_items = items.map(row => { return {item_code: row.item_code, pricing_rules: row.pricing_rules};});

args.free_item_data.forEach(pr_row => {
let row_to_modify = {};
if (!items || !in_list(exist_items, (pr_row.item_code, pr_row.pricing_rules))) {

// If there are no free items, or if the current free item doesn't exist in the table, add it
if (!items || !exist_items.filter(e_row => {
return e_row.item_code == pr_row.item_code && e_row.pricing_rules == pr_row.pricing_rules;
}).length) {
row_to_modify = frappe.model.add_child(this.frm.doc,
this.frm.doc.doctype + ' Item', 'items');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ frappe.ui.form.on("Import Supplier Invoice", {
toggle_read_only_fields: function (frm) {
if (["File Import Completed", "Processing File Data"].includes(frm.doc.status)) {
cur_frm.set_read_only();
cur_frm.refresh_fields();
frm.set_df_property("import_invoices", "hidden", 1);
} else {
frm.set_df_property("import_invoices", "hidden", 0);
Expand Down
5 changes: 1 addition & 4 deletions erpnext/setup/doctype/holiday_list/holiday_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class HolidayList(Document):
def validate(self):
self.validate_days()
self.total_holidays = len(self.holidays)
self.sort_holidays()

@frappe.whitelist()
def get_weekly_off_dates(self):
Expand All @@ -33,8 +34,6 @@ def get_weekly_off_dates(self):

self.append("holidays", {"description": _(self.weekly_off), "holiday_date": d, "weekly_off": 1})

self.sort_holidays()

@frappe.whitelist()
def get_supported_countries(self):
from holidays.utils import list_supported_countries
Expand Down Expand Up @@ -76,8 +75,6 @@ def get_local_holidays(self):
"holidays", {"description": holiday_name, "holiday_date": holiday_date, "weekly_off": 0}
)

self.sort_holidays()

def sort_holidays(self):
self.holidays.sort(key=lambda x: getdate(x.holiday_date))
for i in range(len(self.holidays)):
Expand Down
27 changes: 27 additions & 0 deletions erpnext/stock/doctype/item/test_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,33 @@ def test_get_item_details(self):
for key, value in to_check.items():
self.assertEqual(value, details.get(key), key)

def test_get_asset_item_details(self):
from erpnext.assets.doctype.asset.test_asset import create_asset_category, create_fixed_asset_item

create_asset_category(0)
create_fixed_asset_item()

details = get_item_details(
{
"item_code": "Macbook Pro",
"company": "_Test Company",
"currency": "INR",
"doctype": "Purchase Receipt",
}
)
self.assertEqual(details.get("expense_account"), "_Test Fixed Asset - _TC")

frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", "1")
details = get_item_details(
{
"item_code": "Macbook Pro",
"company": "_Test Company",
"currency": "INR",
"doctype": "Purchase Receipt",
}
)
self.assertEqual(details.get("expense_account"), "CWIP Account - _TC")

def test_item_tax_template(self):
expected_item_tax_template = [
{
Expand Down
Loading
Loading