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

chore: release v14
This commit is contained in:
ruthra kumar
2025-02-12 17:36:33 +05:30
committed by GitHub
17 changed files with 144 additions and 78 deletions

View File

@@ -25,6 +25,7 @@ class AccountingDimension(Document):
"Accounting Dimension Detail", "Accounting Dimension Detail",
"Company", "Company",
"Account", "Account",
"Finance Book",
): ):
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type) msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg) frappe.throw(msg)

View File

@@ -73,6 +73,7 @@
"reports_tab", "reports_tab",
"remarks_section", "remarks_section",
"general_ledger_remarks_length", "general_ledger_remarks_length",
"ignore_is_opening_check_for_reporting",
"column_break_lvjk", "column_break_lvjk",
"receivable_payable_remarks_length" "receivable_payable_remarks_length"
], ],
@@ -471,6 +472,13 @@
"fieldtype": "Select", "fieldtype": "Select",
"label": "Posting Date Inheritance for Exchange Gain / Loss", "label": "Posting Date Inheritance for Exchange Gain / Loss",
"options": "Invoice\nPayment\nReconciliation Date" "options": "Invoice\nPayment\nReconciliation Date"
},
{
"default": "0",
"description": "Ignores legacy Is Opening field in GL Entry that allows adding opening balance post the system is in use while generating reports",
"fieldname": "ignore_is_opening_check_for_reporting",
"fieldtype": "Check",
"label": "Ignore Is Opening check for reporting"
} }
], ],
"icon": "icon-cog", "icon": "icon-cog",

View File

@@ -83,7 +83,7 @@ class GLEntry(Document):
if not self.get(k): if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k)))) frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
if not (self.party_type and self.party): if not self.is_cancelled and not (self.party_type and self.party):
account_type = frappe.get_cached_value("Account", self.account, "account_type") account_type = frappe.get_cached_value("Account", self.account, "account_type")
if account_type == "Receivable": if account_type == "Receivable":
frappe.throw( frappe.throw(

View File

@@ -512,12 +512,16 @@ def get_accounting_entries(
.where(gl_entry.company == filters.company) .where(gl_entry.company == filters.company)
) )
ignore_is_opening = frappe.db.get_single_value(
"Accounts Settings", "ignore_is_opening_check_for_reporting"
)
if doctype == "GL Entry": if doctype == "GL Entry":
query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year) query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year)
query = query.where(gl_entry.is_cancelled == 0) query = query.where(gl_entry.is_cancelled == 0)
query = query.where(gl_entry.posting_date <= to_date) query = query.where(gl_entry.posting_date <= to_date)
if ignore_opening_entries: if ignore_opening_entries and not ignore_is_opening:
query = query.where(gl_entry.is_opening == "No") query = query.where(gl_entry.is_opening == "No")
else: else:
query = query.select(gl_entry.closing_date.as_("posting_date")) query = query.select(gl_entry.closing_date.as_("posting_date"))

View File

@@ -209,6 +209,10 @@ def get_gl_entries(filters, accounting_dimensions):
def get_conditions(filters): def get_conditions(filters):
conditions = [] conditions = []
ignore_is_opening = frappe.db.get_single_value(
"Accounts Settings", "ignore_is_opening_check_for_reporting"
)
if filters.get("account"): if filters.get("account"):
filters.account = get_accounts_with_children(filters.account) filters.account = get_accounts_with_children(filters.account)
if filters.account: if filters.account:
@@ -268,9 +272,15 @@ def get_conditions(filters):
or filters.get("party") or filters.get("party")
or filters.get("group_by") in ["Group by Account", "Group by Party"] or filters.get("group_by") in ["Group by Account", "Group by Party"]
): ):
conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')") if not ignore_is_opening:
conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')")
else:
conditions.append("posting_date >=%(from_date)s")
conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')") if not ignore_is_opening:
conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
else:
conditions.append("posting_date <=%(to_date)s")
if filters.get("project"): if filters.get("project"):
conditions.append("project in %(project)s") conditions.append("project in %(project)s")

View File

@@ -207,15 +207,34 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_
def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data): def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data):
for src in gross_profit_data.grouped_data: total_base_amount = 0
row = [] total_buying_amount = 0
for col in group_wise_columns.get(scrub(filters.group_by)):
row.append(src.get(col))
row.append(filters.currency) group_columns = group_wise_columns.get(scrub(filters.group_by))
for src in gross_profit_data.grouped_data:
total_base_amount += src.base_amount or 0.00
total_buying_amount += src.buying_amount or 0.00
row = [src.get(col) for col in group_columns] + [filters.currency]
data.append(row) data.append(row)
total_gross_profit = total_base_amount - total_buying_amount
currency_precision = cint(frappe.db.get_default("currency_precision")) or 3
gross_profit_percent = (total_gross_profit / total_base_amount * 100.0) if total_base_amount else 0
total_row = {
group_columns[0]: "Total",
"base_amount": total_base_amount,
"buying_amount": total_buying_amount,
"gross_profit": total_gross_profit,
"gross_profit_percent": flt(gross_profit_percent, currency_precision),
}
total_row = [total_row.get(col, None) for col in [*group_columns, "currency"]]
data.append(total_row)
def get_columns(group_wise_columns, filters): def get_columns(group_wise_columns, filters):
columns = [] columns = []

View File

@@ -14,14 +14,14 @@
"owner": "Administrator", "owner": "Administrator",
"ref_doctype": "GL Entry", "ref_doctype": "GL Entry",
"report_name": "Trial Balance", "report_name": "Trial Balance",
"report_type": "Script Report", "report_type": "Script Report",
"roles": [ "roles": [
{ {
"role": "Accounts User" "role": "Accounts User"
}, },
{ {
"role": "Accounts Manager" "role": "Accounts Manager"
}, },
{ {
"role": "Auditor" "role": "Auditor"
} }

View File

@@ -89,6 +89,10 @@ def get_data(filters):
) )
company_currency = filters.presentation_currency or erpnext.get_company_currency(filters.company) company_currency = filters.presentation_currency or erpnext.get_company_currency(filters.company)
ignore_is_opening = frappe.db.get_single_value(
"Accounts Settings", "ignore_is_opening_check_for_reporting"
)
if not accounts: if not accounts:
return None return None
@@ -102,7 +106,7 @@ def get_data(filters):
gl_entries_by_account = {} gl_entries_by_account = {}
opening_balances = get_opening_balances(filters) opening_balances = get_opening_balances(filters, ignore_is_opening)
# add filter inside list so that the query in financial_statements.py doesn't break # add filter inside list so that the query in financial_statements.py doesn't break
if filters.project: if filters.project:
@@ -120,7 +124,13 @@ def get_data(filters):
ignore_opening_entries=True, ignore_opening_entries=True,
) )
calculate_values(accounts, gl_entries_by_account, opening_balances, filters.get("show_net_values")) calculate_values(
accounts,
gl_entries_by_account,
opening_balances,
filters.get("show_net_values"),
ignore_is_opening=ignore_is_opening,
)
accumulate_values_into_parents(accounts, accounts_by_name) accumulate_values_into_parents(accounts, accounts_by_name)
data = prepare_data(accounts, filters, parent_children_map, company_currency) data = prepare_data(accounts, filters, parent_children_map, company_currency)
@@ -131,15 +141,15 @@ def get_data(filters):
return data return data
def get_opening_balances(filters): def get_opening_balances(filters, ignore_is_opening):
balance_sheet_opening = get_rootwise_opening_balances(filters, "Balance Sheet") balance_sheet_opening = get_rootwise_opening_balances(filters, "Balance Sheet", ignore_is_opening)
pl_opening = get_rootwise_opening_balances(filters, "Profit and Loss") pl_opening = get_rootwise_opening_balances(filters, "Profit and Loss", ignore_is_opening)
balance_sheet_opening.update(pl_opening) balance_sheet_opening.update(pl_opening)
return balance_sheet_opening return balance_sheet_opening
def get_rootwise_opening_balances(filters, report_type): def get_rootwise_opening_balances(filters, report_type, ignore_is_opening):
gle = [] gle = []
last_period_closing_voucher = "" last_period_closing_voucher = ""
@@ -165,16 +175,24 @@ def get_rootwise_opening_balances(filters, report_type):
report_type, report_type,
accounting_dimensions, accounting_dimensions,
period_closing_voucher=last_period_closing_voucher[0].name, period_closing_voucher=last_period_closing_voucher[0].name,
ignore_is_opening=ignore_is_opening,
) )
# Report getting generate from the mid of a fiscal year # Report getting generate from the mid of a fiscal year
if getdate(last_period_closing_voucher[0].posting_date) < getdate(add_days(filters.from_date, -1)): if getdate(last_period_closing_voucher[0].posting_date) < getdate(add_days(filters.from_date, -1)):
start_date = add_days(last_period_closing_voucher[0].posting_date, 1) start_date = add_days(last_period_closing_voucher[0].posting_date, 1)
gle += get_opening_balance( gle += get_opening_balance(
"GL Entry", filters, report_type, accounting_dimensions, start_date=start_date "GL Entry",
filters,
report_type,
accounting_dimensions,
start_date=start_date,
ignore_is_opening=ignore_is_opening,
) )
else: else:
gle = get_opening_balance("GL Entry", filters, report_type, accounting_dimensions) gle = get_opening_balance(
"GL Entry", filters, report_type, accounting_dimensions, ignore_is_opening=ignore_is_opening
)
opening = frappe._dict() opening = frappe._dict()
for d in gle: for d in gle:
@@ -193,7 +211,13 @@ def get_rootwise_opening_balances(filters, report_type):
def get_opening_balance( def get_opening_balance(
doctype, filters, report_type, accounting_dimensions, period_closing_voucher=None, start_date=None doctype,
filters,
report_type,
accounting_dimensions,
period_closing_voucher=None,
start_date=None,
ignore_is_opening=0,
): ):
closing_balance = frappe.qb.DocType(doctype) closing_balance = frappe.qb.DocType(doctype)
account = frappe.qb.DocType("Account") account = frappe.qb.DocType("Account")
@@ -229,11 +253,16 @@ def get_opening_balance(
(closing_balance.posting_date >= start_date) (closing_balance.posting_date >= start_date)
& (closing_balance.posting_date < filters.from_date) & (closing_balance.posting_date < filters.from_date)
) )
opening_balance = opening_balance.where(closing_balance.is_opening == "No")
if not ignore_is_opening:
opening_balance = opening_balance.where(closing_balance.is_opening == "No")
else: else:
opening_balance = opening_balance.where( if not ignore_is_opening:
(closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes") opening_balance = opening_balance.where(
) (closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes")
)
else:
opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date)
if doctype == "GL Entry": if doctype == "GL Entry":
opening_balance = opening_balance.where(closing_balance.is_cancelled == 0) opening_balance = opening_balance.where(closing_balance.is_cancelled == 0)
@@ -304,7 +333,7 @@ def get_opening_balance(
return gle return gle
def calculate_values(accounts, gl_entries_by_account, opening_balances, show_net_values): def calculate_values(accounts, gl_entries_by_account, opening_balances, show_net_values, ignore_is_opening=0):
init = { init = {
"opening_debit": 0.0, "opening_debit": 0.0,
"opening_credit": 0.0, "opening_credit": 0.0,
@@ -322,7 +351,7 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, show_net
d["opening_credit"] = opening_balances.get(d.name, {}).get("opening_credit", 0) d["opening_credit"] = opening_balances.get(d.name, {}).get("opening_credit", 0)
for entry in gl_entries_by_account.get(d.name, []): for entry in gl_entries_by_account.get(d.name, []):
if cstr(entry.is_opening) != "Yes": if cstr(entry.is_opening) != "Yes" or ignore_is_opening:
d["debit"] += flt(entry.debit) d["debit"] += flt(entry.debit)
d["credit"] += flt(entry.credit) d["credit"] += flt(entry.credit)

View File

@@ -330,7 +330,11 @@ def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=
}, },
"Request for Quotation Item": { "Request for Quotation Item": {
"doctype": "Supplier Quotation Item", "doctype": "Supplier Quotation Item",
"field_map": {"name": "request_for_quotation_item", "parent": "request_for_quotation"}, "field_map": {
"name": "request_for_quotation_item",
"parent": "request_for_quotation",
"project_name": "project",
},
}, },
}, },
target_doc, target_doc,

View File

@@ -1728,22 +1728,22 @@ class AccountsController(TransactionBase):
continue continue
ref_amt = flt(reference_details.get(item.get(item_ref_dn)), self.precision(based_on, item)) ref_amt = flt(reference_details.get(item.get(item_ref_dn)), self.precision(based_on, item))
based_on_amt = flt(item.get(based_on))
if not ref_amt: if not ref_amt:
frappe.msgprint( if based_on_amt: # Skip warning for free items
_("System will not check over billing since amount for Item {0} in {1} is zero").format( frappe.msgprint(
item.item_code, ref_dt _(
), "System will not check over billing since amount for Item {0} in {1} is zero"
title=_("Warning"), ).format(item.item_code, ref_dt),
indicator="orange", title=_("Warning"),
) indicator="orange",
)
continue continue
already_billed = self.get_billed_amount_for_item(item, item_ref_dn, based_on) already_billed = self.get_billed_amount_for_item(item, item_ref_dn, based_on)
total_billed_amt = flt( total_billed_amt = flt(flt(already_billed) + based_on_amt, self.precision(based_on, item))
flt(already_billed) + flt(item.get(based_on)), self.precision(based_on, item)
)
allowance, item_allowance, global_qty_allowance, global_amount_allowance = get_allowance_for( allowance, item_allowance, global_qty_allowance, global_amount_allowance = get_allowance_for(
item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount" item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount"

View File

@@ -331,22 +331,19 @@ def sales_invoice_on_submit(doc, method):
]: ]:
return return
if not len(doc.payment_schedule): for schedule in doc.payment_schedule:
frappe.throw(_("Please set the Payment Schedule"), title=_("E-Invoicing Information Missing")) if not schedule.mode_of_payment:
else: frappe.throw(
for schedule in doc.payment_schedule: _("Row {0}: Please set the Mode of Payment in Payment Schedule").format(schedule.idx),
if not schedule.mode_of_payment: title=_("E-Invoicing Information Missing"),
frappe.throw( )
_("Row {0}: Please set the Mode of Payment in Payment Schedule").format(schedule.idx), elif not frappe.db.get_value("Mode of Payment", schedule.mode_of_payment, "mode_of_payment_code"):
title=_("E-Invoicing Information Missing"), frappe.throw(
) _("Row {0}: Please set the correct code on Mode of Payment {1}").format(
elif not frappe.db.get_value("Mode of Payment", schedule.mode_of_payment, "mode_of_payment_code"): schedule.idx, schedule.mode_of_payment
frappe.throw( ),
_("Row {0}: Please set the correct code on Mode of Payment {1}").format( title=_("E-Invoicing Information Missing"),
schedule.idx, schedule.mode_of_payment )
),
title=_("E-Invoicing Information Missing"),
)
prepare_and_attach_invoice(doc) prepare_and_attach_invoice(doc)

View File

@@ -733,6 +733,7 @@ erpnext.PointOfSale.ItemCart = class {
frappe.utils.play_sound("error"); frappe.utils.play_sound("error");
return; return;
} }
this.highlight_numpad_btn($btn, current_action);
if (first_click_event || field_to_edit_changed) { if (first_click_event || field_to_edit_changed) {
this.prev_action = current_action; this.prev_action = current_action;
@@ -778,7 +779,6 @@ erpnext.PointOfSale.ItemCart = class {
this.numpad_value = current_action; this.numpad_value = current_action;
} }
this.highlight_numpad_btn($btn, current_action);
this.events.numpad_event(this.numpad_value, this.prev_action); this.events.numpad_event(this.numpad_value, this.prev_action);
} }

View File

@@ -182,8 +182,6 @@
"read_only": 1 "read_only": 1
}, },
{ {
"fetch_from": "user_id.user_image",
"fetch_if_empty": 1,
"fieldname": "image", "fieldname": "image",
"fieldtype": "Attach Image", "fieldtype": "Attach Image",
"hidden": 1, "hidden": 1,
@@ -824,7 +822,7 @@
"image_field": "image", "image_field": "image",
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2024-01-03 17:36:20.984421", "modified": "2025-02-07 13:54:40.122345",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Employee", "name": "Employee",
@@ -873,4 +871,4 @@
"states": [], "states": [],
"title_field": "employee_name", "title_field": "employee_name",
"track_changes": 1 "track_changes": 1
} }

View File

@@ -65,14 +65,12 @@ class Employee(NestedSet):
def validate_user_details(self): def validate_user_details(self):
if self.user_id: if self.user_id:
data = frappe.db.get_value("User", self.user_id, ["enabled", "user_image"], as_dict=1) data = frappe.db.get_value("User", self.user_id, ["enabled"], as_dict=1)
if not data: if not data:
self.user_id = None self.user_id = None
return return
if data.get("user_image") and self.image == "":
self.image = data.get("user_image")
self.validate_for_enabled_user_id(data.get("enabled", 0)) self.validate_for_enabled_user_id(data.get("enabled", 0))
self.validate_duplicate_user_id() self.validate_duplicate_user_id()

View File

@@ -430,7 +430,7 @@ def make_request_for_quotation(source_name, target_doc=None):
"field_map": [ "field_map": [
["name", "material_request_item"], ["name", "material_request_item"],
["parent", "material_request"], ["parent", "material_request"],
["uom", "uom"], ["project", "project_name"],
], ],
}, },
}, },

View File

@@ -1165,26 +1165,25 @@ def get_item_account_wise_additional_cost(purchase_document):
for item in landed_cost_voucher_doc.items: for item in landed_cost_voucher_doc.items:
if item.receipt_document == purchase_document: if item.receipt_document == purchase_document:
for account in landed_cost_voucher_doc.taxes: for account in landed_cost_voucher_doc.taxes:
exchange_rate = account.exchange_rate or 1
item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {}) item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {})
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault( item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(
account.expense_account, {"amount": 0.0, "base_amount": 0.0} account.expense_account, {"amount": 0.0, "base_amount": 0.0}
) )
if total_item_cost > 0: item_row = item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][ account.expense_account
account.expense_account ]
]["amount"] += account.amount * item.get(based_on_field) / total_item_cost
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][ if total_item_cost > 0:
account.expense_account item_row["amount"] += account.amount * item.get(based_on_field) / total_item_cost
]["base_amount"] += account.base_amount * item.get(based_on_field) / total_item_cost
item_row["base_amount"] += (
account.base_amount * item.get(based_on_field) / total_item_cost
)
else: else:
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][ item_row["amount"] += item.applicable_charges / exchange_rate
account.expense_account item_row["base_amount"] += item.applicable_charges
]["amount"] += item.applicable_charges
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
account.expense_account
]["base_amount"] += item.applicable_charges
return item_account_wise_cost return item_account_wise_cost

View File

@@ -72,8 +72,7 @@
</span> </span>
</div> </div>
<div class="text-right col-2"> <div class="text-right col-2">
{%- set party_name = doc.supplier_name if doc.doctype in ['Supplier Quotation', 'Purchase Invoice', 'Purchase {%- set party_name = doc.supplier_name if doc.doctype in ['Supplier Quotation', 'Purchase Invoice', 'Purchase Order'] else doc.customer_name %}
Order'] else doc.customer_name %}
<b>{{ party_name }}</b> <b>{{ party_name }}</b>
{% if doc.contact_display and doc.contact_display != party_name %} {% if doc.contact_display and doc.contact_display != party_name %}