mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-24 15:39:20 +00:00
Merge pull request #47757 from frappe/version-14-hotfix
chore: release v14
This commit is contained in:
@@ -622,6 +622,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
|
|||||||
conditions.append(ple.party.isin(parties))
|
conditions.append(ple.party.isin(parties))
|
||||||
conditions.append(ple.voucher_no == ple.against_voucher_no)
|
conditions.append(ple.voucher_no == ple.against_voucher_no)
|
||||||
conditions.append(ple.company == inv.company)
|
conditions.append(ple.company == inv.company)
|
||||||
|
conditions.append(ple.posting_date[tax_details.from_date : tax_details.to_date])
|
||||||
|
|
||||||
advance_amt = (
|
advance_amt = (
|
||||||
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0
|
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0
|
||||||
|
|||||||
@@ -243,17 +243,18 @@ class TestTaxWithholdingCategory(FrappeTestCase):
|
|||||||
frappe.db.set_value(
|
frappe.db.set_value(
|
||||||
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
|
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
|
||||||
)
|
)
|
||||||
|
fiscal_year = get_fiscal_year(today(), company="_Test Company")
|
||||||
|
|
||||||
vouchers = []
|
vouchers = []
|
||||||
|
|
||||||
# create advance payment
|
# create advance payment
|
||||||
pe = create_payment_entry(
|
pe1 = create_payment_entry(
|
||||||
payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=20000
|
payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=20000
|
||||||
)
|
)
|
||||||
pe.paid_from = "Debtors - _TC"
|
pe1.paid_from = "Debtors - _TC"
|
||||||
pe.paid_to = "Cash - _TC"
|
pe1.paid_to = "Cash - _TC"
|
||||||
pe.submit()
|
pe1.submit()
|
||||||
vouchers.append(pe)
|
vouchers.append(pe1)
|
||||||
|
|
||||||
# create invoice
|
# create invoice
|
||||||
si1 = create_sales_invoice(customer="Test TCS Customer", rate=5000)
|
si1 = create_sales_invoice(customer="Test TCS Customer", rate=5000)
|
||||||
@@ -275,6 +276,17 @@ class TestTaxWithholdingCategory(FrappeTestCase):
|
|||||||
# make another invoice
|
# make another invoice
|
||||||
# sum of unallocated amount from payment entry and this sales invoice will breach cumulative threashold
|
# sum of unallocated amount from payment entry and this sales invoice will breach cumulative threashold
|
||||||
# TDS should be calculated
|
# TDS should be calculated
|
||||||
|
|
||||||
|
# this payment should not be considered for TCS calculation as it is outside of fiscal year
|
||||||
|
pe2 = create_payment_entry(
|
||||||
|
payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=10000
|
||||||
|
)
|
||||||
|
pe2.paid_from = "Debtors - _TC"
|
||||||
|
pe2.paid_to = "Cash - _TC"
|
||||||
|
pe2.posting_date = add_days(fiscal_year[1], -10)
|
||||||
|
pe2.submit()
|
||||||
|
vouchers.append(pe2)
|
||||||
|
|
||||||
si2 = create_sales_invoice(customer="Test TCS Customer", rate=15000)
|
si2 = create_sales_invoice(customer="Test TCS Customer", rate=15000)
|
||||||
si2.submit()
|
si2.submit()
|
||||||
vouchers.append(si2)
|
vouchers.append(si2)
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
|||||||
args: {
|
args: {
|
||||||
item_code: item.item_code,
|
item_code: item.item_code,
|
||||||
warehouse: cstr(item.warehouse),
|
warehouse: cstr(item.warehouse),
|
||||||
qty: flt(item.stock_qty),
|
qty: -1 * flt(item.stock_qty),
|
||||||
serial_no: item.serial_no,
|
serial_no: item.serial_no,
|
||||||
posting_date: me.frm.doc.posting_date,
|
posting_date: me.frm.doc.posting_date,
|
||||||
posting_time: me.frm.doc.posting_time,
|
posting_time: me.frm.doc.posting_time,
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ def update_last_purchase_rate(doc, is_submit) -> None:
|
|||||||
this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
|
this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
|
||||||
|
|
||||||
for d in doc.get("items"):
|
for d in doc.get("items"):
|
||||||
|
if d.get("is_free_item"):
|
||||||
|
continue
|
||||||
|
|
||||||
# get last purchase details
|
# get last purchase details
|
||||||
last_purchase_details = get_last_purchase_details(d.item_code, doc.name)
|
last_purchase_details = get_last_purchase_details(d.item_code, doc.name)
|
||||||
|
|
||||||
|
|||||||
@@ -29,4 +29,10 @@ frappe.ui.form.on("Contract", {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
party_name: function (frm) {
|
||||||
|
let field = frm.doc.party_type.toLowerCase() + "_name";
|
||||||
|
frappe.db.get_value(frm.doc.party_type, frm.doc.party_name, field, (r) => {
|
||||||
|
frm.set_value("party_full_name", r[field]);
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"party_user",
|
"party_user",
|
||||||
"status",
|
"status",
|
||||||
"fulfilment_status",
|
"fulfilment_status",
|
||||||
|
"party_full_name",
|
||||||
"sb_terms",
|
"sb_terms",
|
||||||
"start_date",
|
"start_date",
|
||||||
"cb_date",
|
"cb_date",
|
||||||
@@ -244,11 +245,18 @@
|
|||||||
"fieldname": "authorised_by_section",
|
"fieldname": "authorised_by_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Authorised By"
|
"label": "Authorised By"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "party_full_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Party Full Name",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"grid_page_length": 50,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-12-07 11:15:58.385521",
|
"modified": "2025-05-23 13:54:03.346537",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Contract",
|
"name": "Contract",
|
||||||
@@ -315,9 +323,10 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"row_format": "Dynamic",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1,
|
||||||
"track_seen": 1
|
"track_seen": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,17 @@ class Contract(Document):
|
|||||||
self.name = _(name)
|
self.name = _(name)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.set_missing_values()
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.update_contract_status()
|
self.update_contract_status()
|
||||||
self.update_fulfilment_status()
|
self.update_fulfilment_status()
|
||||||
|
|
||||||
|
def set_missing_values(self):
|
||||||
|
if not self.party_full_name:
|
||||||
|
field = self.party_type.lower() + "_name"
|
||||||
|
if res := frappe.db.get_value(self.party_type, self.party_name, field):
|
||||||
|
self.party_full_name = res
|
||||||
|
|
||||||
def before_submit(self):
|
def before_submit(self):
|
||||||
self.signed_by_company = frappe.session.user
|
self.signed_by_company = frappe.session.user
|
||||||
|
|
||||||
|
|||||||
@@ -375,3 +375,5 @@ erpnext.stock.doctype.stock_ledger_entry.patches.ensure_sle_indexes
|
|||||||
erpnext.patches.v14_0.rename_group_by_to_categorize_by
|
erpnext.patches.v14_0.rename_group_by_to_categorize_by
|
||||||
execute:frappe.db.set_single_value("Accounts Settings", "receivable_payable_fetch_method", "Buffered Cursor")
|
execute:frappe.db.set_single_value("Accounts Settings", "receivable_payable_fetch_method", "Buffered Cursor")
|
||||||
erpnext.patches.v14_0.set_update_price_list_based_on
|
erpnext.patches.v14_0.set_update_price_list_based_on
|
||||||
|
erpnext.patches.v14_0.rename_group_by_to_categorize_by_in_custom_reports
|
||||||
|
erpnext.patches.v14_0.update_full_name_in_contract
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
custom_reports = frappe.get_all(
|
||||||
|
"Report",
|
||||||
|
filters={
|
||||||
|
"report_type": "Custom Report",
|
||||||
|
"reference_report": ["in", ["General Ledger", "Supplier Quotation Comparison"]],
|
||||||
|
},
|
||||||
|
fields=["name", "json"],
|
||||||
|
)
|
||||||
|
|
||||||
|
for report in custom_reports:
|
||||||
|
report_json = json.loads(report.json)
|
||||||
|
|
||||||
|
if "filters" in report_json and "group_by" in report_json["filters"]:
|
||||||
|
report_json["filters"]["categorize_by"] = (
|
||||||
|
report_json["filters"].pop("group_by").replace("Group", "Categorize")
|
||||||
|
)
|
||||||
|
|
||||||
|
frappe.db.set_value("Report", report.name, "json", json.dumps(report_json))
|
||||||
15
erpnext/patches/v14_0/update_full_name_in_contract.py
Normal file
15
erpnext/patches/v14_0/update_full_name_in_contract.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe import qb
|
||||||
|
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
con = qb.DocType("Contract")
|
||||||
|
for c in (
|
||||||
|
qb.from_(con)
|
||||||
|
.select(con.name, con.party_type, con.party_name)
|
||||||
|
.where(con.party_full_name.isnull())
|
||||||
|
.run(as_dict=True)
|
||||||
|
):
|
||||||
|
field = c.party_type.lower() + "_name"
|
||||||
|
if res := frappe.db.get_value(c.party_type, c.party_name, field):
|
||||||
|
frappe.db.set_value("Contract", c.name, "party_full_name", res)
|
||||||
@@ -26,7 +26,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
|||||||
item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
|
item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.discount_amount) {
|
if (item.discount_amount > 0) {
|
||||||
item_rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
|
item_rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
|
||||||
item.discount_percentage = 100 * flt(item.discount_amount) / flt(item.rate_with_margin);
|
item.discount_percentage = 100 * flt(item.discount_amount) / flt(item.rate_with_margin);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_guest_to_view": 1,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:item_code",
|
"autoname": "field:item_code",
|
||||||
@@ -897,10 +896,9 @@
|
|||||||
"icon": "fa fa-tag",
|
"icon": "fa fa-tag",
|
||||||
"idx": 2,
|
"idx": 2,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"index_web_pages_for_search": 1,
|
|
||||||
"links": [],
|
"links": [],
|
||||||
"make_attachments_public": 1,
|
"make_attachments_public": 1,
|
||||||
"modified": "2024-01-08 18:09:30.225085",
|
"modified": "2025-02-03 23:43:57.253667",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
|||||||
@@ -178,13 +178,13 @@ class SerialNo(StockController):
|
|||||||
entries = {}
|
entries = {}
|
||||||
sle_dict = self.get_stock_ledger_entries(serial_no)
|
sle_dict = self.get_stock_ledger_entries(serial_no)
|
||||||
if sle_dict:
|
if sle_dict:
|
||||||
|
last_sle = sle_dict.get("last_sle") or {}
|
||||||
|
entries["last_sle"] = last_sle
|
||||||
|
|
||||||
if sle_dict.get("incoming", []):
|
if sle_dict.get("incoming", []):
|
||||||
entries["purchase_sle"] = sle_dict["incoming"][-1]
|
entries["purchase_sle"] = sle_dict["incoming"][-1]
|
||||||
|
|
||||||
if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0:
|
if last_sle.get("actual_qty") < 0 and sle_dict.get("outgoing", []):
|
||||||
entries["last_sle"] = sle_dict["incoming"][0]
|
|
||||||
else:
|
|
||||||
entries["last_sle"] = sle_dict["outgoing"][0]
|
|
||||||
entries["delivery_sle"] = sle_dict["outgoing"][0]
|
entries["delivery_sle"] = sle_dict["outgoing"][0]
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
@@ -221,6 +221,9 @@ class SerialNo(StockController):
|
|||||||
as_dict=1,
|
as_dict=1,
|
||||||
):
|
):
|
||||||
if serial_no.upper() in get_serial_nos(sle.serial_no):
|
if serial_no.upper() in get_serial_nos(sle.serial_no):
|
||||||
|
if "last_sle" not in sle_dict:
|
||||||
|
sle_dict["last_sle"] = sle
|
||||||
|
|
||||||
if cint(sle.actual_qty) > 0:
|
if cint(sle.actual_qty) > 0:
|
||||||
sle_dict.setdefault("incoming", []).append(sle)
|
sle_dict.setdefault("incoming", []).append(sle)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -458,17 +458,19 @@ class StockEntry(StockController):
|
|||||||
if acc_details.account_type == "Stock":
|
if acc_details.account_type == "Stock":
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"At row {0}: the Difference Account must not be a Stock type account, please change the Account Type for the account {1} or select a different account"
|
"At row #{0}: the Difference Account must not be a Stock type account, please change the Account Type for the account {1} or select a different account"
|
||||||
).format(d.idx, get_link_to_form("Account", d.expense_account)),
|
).format(d.idx, get_link_to_form("Account", d.expense_account)),
|
||||||
OpeningEntryAccountError,
|
title=_("Difference Account in Items Table"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.purpose != "Material Issue" and acc_details.account_type == "Cost of Goods Sold":
|
if self.purpose != "Material Issue" and acc_details.account_type == "Cost of Goods Sold":
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
_(
|
_(
|
||||||
"At row {0}: You have selected the Difference Account {1}, which is a Cost of Goods Sold type account. Please select a different account"
|
"At row #{0}: you have selected the Difference Account {1}, which is a Cost of Goods Sold type account. Please select a different account"
|
||||||
).format(d.idx, bold(get_link_to_form("Account", d.expense_account))),
|
).format(d.idx, bold(get_link_to_form("Account", d.expense_account))),
|
||||||
title=_("Warning : Cost of Goods Sold Account"),
|
title=_("Cost of Goods Sold Account in Items Table"),
|
||||||
|
indicator="orange",
|
||||||
|
alert=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate_warehouse(self):
|
def validate_warehouse(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user