mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-15 09:53:09 +00:00
Merge pull request #56630 from mihir-kandoi/pg-convergence-fixes
fix: Postgres transaction-abort savepoints + div0/tiebreaker convergence fixes
This commit is contained in:
@@ -582,6 +582,7 @@ def make_gl_entries(
|
|||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if frappe.in_test:
|
if frappe.in_test:
|
||||||
|
frappe.db.rollback()
|
||||||
doc.log_error(f"Error while processing deferred accounting for Invoice {doc.name}")
|
doc.log_error(f"Error while processing deferred accounting for Invoice {doc.name}")
|
||||||
raise e
|
raise e
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ def create_bank_entries(columns: str, data: str | list, bank_account: str):
|
|||||||
for key, value in header_map.items():
|
for key, value in header_map.items():
|
||||||
fields.update({key: d[int(value) - 1]})
|
fields.update({key: d[int(value) - 1]})
|
||||||
|
|
||||||
|
frappe.db.savepoint("bank_entry")
|
||||||
try:
|
try:
|
||||||
bank_transaction = frappe.get_doc({"doctype": "Bank Transaction"})
|
bank_transaction = frappe.get_doc({"doctype": "Bank Transaction"})
|
||||||
bank_transaction.update(fields)
|
bank_transaction.update(fields)
|
||||||
@@ -56,7 +57,8 @@ def create_bank_entries(columns: str, data: str | list, bank_account: str):
|
|||||||
bank_transaction.submit()
|
bank_transaction.submit()
|
||||||
success += 1
|
success += 1
|
||||||
except Exception:
|
except Exception:
|
||||||
bank_transaction.log_error("Bank entry creation failed")
|
frappe.db.rollback(save_point="bank_entry")
|
||||||
|
frappe.log_error(title="Bank entry creation failed")
|
||||||
errors += 1
|
errors += 1
|
||||||
|
|
||||||
return {"success": success, "errors": errors}
|
return {"success": success, "errors": errors}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ def start_merge(docname):
|
|||||||
total = len(ledger_merge.merge_accounts)
|
total = len(ledger_merge.merge_accounts)
|
||||||
for row in ledger_merge.merge_accounts:
|
for row in ledger_merge.merge_accounts:
|
||||||
if not row.merged:
|
if not row.merged:
|
||||||
|
frappe.db.savepoint("ledger_merge_row")
|
||||||
try:
|
try:
|
||||||
merge_account(
|
merge_account(
|
||||||
row.account,
|
row.account,
|
||||||
@@ -79,8 +80,7 @@ def start_merge(docname):
|
|||||||
{"ledger_merge": ledger_merge.name, "current": successful_merges, "total": total},
|
{"ledger_merge": ledger_merge.name, "current": successful_merges, "total": total},
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
if not frappe.in_test:
|
frappe.db.rollback(save_point="ledger_merge_row")
|
||||||
frappe.db.rollback()
|
|
||||||
ledger_merge.log_error("Ledger merge failed")
|
ledger_merge.log_error("Ledger merge failed")
|
||||||
finally:
|
finally:
|
||||||
if successful_merges == total:
|
if successful_merges == total:
|
||||||
|
|||||||
@@ -514,10 +514,12 @@ class PaymentEntry(AccountsController):
|
|||||||
invoice_names.add((ref.reference_doctype, ref.reference_name))
|
invoice_names.add((ref.reference_doctype, ref.reference_name))
|
||||||
|
|
||||||
for doctype, name in invoice_names:
|
for doctype, name in invoice_names:
|
||||||
|
frappe.db.savepoint("subscription_update")
|
||||||
try:
|
try:
|
||||||
doc = frappe.get_doc(doctype, name)
|
doc = frappe.get_doc(doctype, name)
|
||||||
doc.refresh_subscription_status()
|
doc.refresh_subscription_status()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback(save_point="subscription_update")
|
||||||
frappe.log_error(_("Failed to update subscription status for {0} {1}").format(doctype, name))
|
frappe.log_error(_("Failed to update subscription status for {0} {1}").format(doctype, name))
|
||||||
|
|
||||||
def set_missing_values(self):
|
def set_missing_values(self):
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ def make_depreciation_entry(
|
|||||||
for d in depr_schedule_doc.get("depreciation_schedule")[
|
for d in depr_schedule_doc.get("depreciation_schedule")[
|
||||||
(sch_start_idx or 0) : (sch_end_idx or len(depr_schedule_doc.get("depreciation_schedule")))
|
(sch_start_idx or 0) : (sch_end_idx or len(depr_schedule_doc.get("depreciation_schedule")))
|
||||||
]:
|
]:
|
||||||
|
frappe.db.savepoint("depr_entry")
|
||||||
try:
|
try:
|
||||||
_make_journal_entry_for_depreciation(
|
_make_journal_entry_for_depreciation(
|
||||||
depr_schedule_doc,
|
depr_schedule_doc,
|
||||||
@@ -202,6 +203,7 @@ def make_depreciation_entry(
|
|||||||
accounting_dimensions,
|
accounting_dimensions,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
frappe.db.rollback(save_point="depr_entry")
|
||||||
depr_posting_error = e
|
depr_posting_error = e
|
||||||
|
|
||||||
asset.reload()
|
asset.reload()
|
||||||
|
|||||||
@@ -405,16 +405,10 @@ def get_default_scorecard_standing():
|
|||||||
def make_default_records():
|
def make_default_records():
|
||||||
install_variable_docs = get_default_scorecard_variables()
|
install_variable_docs = get_default_scorecard_variables()
|
||||||
for d in install_variable_docs:
|
for d in install_variable_docs:
|
||||||
try:
|
d["doctype"] = "Supplier Scorecard Variable"
|
||||||
d["doctype"] = "Supplier Scorecard Variable"
|
frappe.get_doc(d).insert(ignore_if_duplicate=True)
|
||||||
frappe.get_doc(d).insert()
|
|
||||||
except frappe.NameError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
install_standing_docs = get_default_scorecard_standing()
|
install_standing_docs = get_default_scorecard_standing()
|
||||||
for d in install_standing_docs:
|
for d in install_standing_docs:
|
||||||
try:
|
d["doctype"] = "Supplier Scorecard Standing"
|
||||||
d["doctype"] = "Supplier Scorecard Standing"
|
frappe.get_doc(d).insert(ignore_if_duplicate=True)
|
||||||
frappe.get_doc(d).insert()
|
|
||||||
except frappe.NameError:
|
|
||||||
pass
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import frappe
|
|||||||
from frappe import _, bold
|
from frappe import _, bold
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
from frappe.query_builder import DocType
|
from frappe.query_builder import DocType
|
||||||
from frappe.query_builder.functions import Abs, Sum
|
from frappe.query_builder.functions import Abs, NullIf, Sum
|
||||||
from frappe.utils import cint, flt, format_datetime, get_datetime
|
from frappe.utils import cint, flt, format_datetime, get_datetime
|
||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
@@ -766,7 +766,7 @@ def get_rate_for_return(
|
|||||||
select_field = "incoming_rate"
|
select_field = "incoming_rate"
|
||||||
else:
|
else:
|
||||||
StockLedgerEntry = frappe.qb.DocType("Stock Ledger Entry")
|
StockLedgerEntry = frappe.qb.DocType("Stock Ledger Entry")
|
||||||
select_field = Abs(StockLedgerEntry.stock_value_difference / StockLedgerEntry.actual_qty)
|
select_field = Abs(StockLedgerEntry.stock_value_difference / NullIf(StockLedgerEntry.actual_qty, 0))
|
||||||
|
|
||||||
item_details = frappe.get_cached_value("Item", item_code, ["has_batch_no", "has_expiry_date"], as_dict=1)
|
item_details = frappe.get_cached_value("Item", item_code, ["has_batch_no", "has_expiry_date"], as_dict=1)
|
||||||
set_zero_rate_for_expired_batch = frappe.db.get_single_value(
|
set_zero_rate_for_expired_batch = frappe.db.get_single_value(
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ def send_mail(entry, email_campaign):
|
|||||||
subject = frappe.render_template(email_template.get("subject"), context)
|
subject = frappe.render_template(email_template.get("subject"), context)
|
||||||
content = frappe.render_template(email_template.response_, context)
|
content = frappe.render_template(email_template.response_, context)
|
||||||
|
|
||||||
|
frappe.db.savepoint("email_campaign_send")
|
||||||
try:
|
try:
|
||||||
comm = make(
|
comm = make(
|
||||||
doctype="Email Campaign",
|
doctype="Email Campaign",
|
||||||
@@ -197,6 +198,7 @@ def send_mail(entry, email_campaign):
|
|||||||
queue_separately=True,
|
queue_separately=True,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback(save_point="email_campaign_send")
|
||||||
frappe.log_error(title="Email Campaign Failed.")
|
frappe.log_error(title="Email Campaign Failed.")
|
||||||
|
|
||||||
return comm
|
return comm
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ def create_prospect_against_crm_deal():
|
|||||||
prospect.insert()
|
prospect.insert()
|
||||||
prospect_name = prospect.name
|
prospect_name = prospect.name
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback()
|
||||||
frappe.log_error(
|
frappe.log_error(
|
||||||
frappe.get_traceback(),
|
frappe.get_traceback(),
|
||||||
f"Error while creating prospect against CRM Deal: {frappe.form_dict.get('crm_deal_id')}",
|
f"Error while creating prospect against CRM Deal: {frappe.form_dict.get('crm_deal_id')}",
|
||||||
@@ -70,6 +71,7 @@ def create_address(doctype, docname, address):
|
|||||||
if not address:
|
if not address:
|
||||||
return
|
return
|
||||||
address = frappe.parse_json(address)
|
address = frappe.parse_json(address)
|
||||||
|
frappe.db.savepoint("crm_create_address")
|
||||||
try:
|
try:
|
||||||
_address = frappe.db.exists("Address", address.get("name"))
|
_address = frappe.db.exists("Address", address.get("name"))
|
||||||
if not _address:
|
if not _address:
|
||||||
@@ -97,6 +99,7 @@ def create_address(doctype, docname, address):
|
|||||||
address.save(ignore_permissions=True)
|
address.save(ignore_permissions=True)
|
||||||
return address.name
|
return address.name
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback(save_point="crm_create_address")
|
||||||
frappe.log_error(frappe.get_traceback(), f"Error while creating address for {docname}")
|
frappe.log_error(frappe.get_traceback(), f"Error while creating address for {docname}")
|
||||||
|
|
||||||
|
|
||||||
@@ -157,6 +160,7 @@ def create_customer(customer_data: dict | None = None):
|
|||||||
create_address("Customer", customer_name, customer_data.get("address"))
|
create_address("Customer", customer_name, customer_data.get("address"))
|
||||||
return customer_name
|
return customer_name
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback()
|
||||||
frappe.log_error(frappe.get_traceback(), "Error while creating customer against Frappe CRM Deal")
|
frappe.log_error(frappe.get_traceback(), "Error while creating customer against Frappe CRM Deal")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ def add_institution(token: str, response: str | dict):
|
|||||||
)
|
)
|
||||||
bank.insert()
|
bank.insert()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback()
|
||||||
frappe.log_error("Plaid Link Error")
|
frappe.log_error("Plaid Link Error")
|
||||||
else:
|
else:
|
||||||
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
||||||
@@ -154,6 +155,7 @@ def add_bank_accounts(response: str | dict, bank: str | dict, company: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
frappe.db.savepoint("plaid_update_account")
|
||||||
try:
|
try:
|
||||||
existing_account = frappe.get_doc("Bank Account", existing_bank_account)
|
existing_account = frappe.get_doc("Bank Account", existing_bank_account)
|
||||||
existing_account.update(
|
existing_account.update(
|
||||||
@@ -169,6 +171,7 @@ def add_bank_accounts(response: str | dict, bank: str | dict, company: str):
|
|||||||
existing_account.save()
|
existing_account.save()
|
||||||
result.append(existing_bank_account)
|
result.append(existing_bank_account)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback(save_point="plaid_update_account")
|
||||||
frappe.log_error("Plaid Link Error")
|
frappe.log_error("Plaid Link Error")
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("There was an error updating Bank Account {0} while linking with Plaid.").format(
|
_("There was an error updating Bank Account {0} while linking with Plaid.").format(
|
||||||
|
|||||||
@@ -313,6 +313,7 @@ class BOMCreator(Document):
|
|||||||
|
|
||||||
frappe.msgprint(_("BOMs created successfully"))
|
frappe.msgprint(_("BOMs created successfully"))
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback()
|
||||||
traceback = frappe.get_traceback(with_context=True)
|
traceback = frappe.get_traceback(with_context=True)
|
||||||
self.db_set(
|
self.db_set(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -856,6 +856,7 @@ def install_country_fixtures(company, country):
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback()
|
||||||
frappe.log_error("Unable to set country fixtures")
|
frappe.log_error("Unable to set country fixtures")
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Failed to setup defaults for country {0}. Please contact support.").format(
|
_("Failed to setup defaults for country {0}. Please contact support.").format(
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ def from_detailed_data(company_name, data):
|
|||||||
def update_regional_tax_settings(country, company):
|
def update_regional_tax_settings(country, company):
|
||||||
path = frappe.get_app_path("erpnext", "regional", frappe.scrub(country))
|
path = frappe.get_app_path("erpnext", "regional", frappe.scrub(country))
|
||||||
if os.path.exists(path.encode("utf-8")):
|
if os.path.exists(path.encode("utf-8")):
|
||||||
|
frappe.db.savepoint("regional_tax_settings")
|
||||||
try:
|
try:
|
||||||
module_name = f"erpnext.regional.{frappe.scrub(country)}.setup.update_regional_tax_settings"
|
module_name = f"erpnext.regional.{frappe.scrub(country)}.setup.update_regional_tax_settings"
|
||||||
frappe.get_attr(module_name)(country, company)
|
frappe.get_attr(module_name)(country, company)
|
||||||
@@ -127,6 +128,7 @@ def update_regional_tax_settings(country, company):
|
|||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
# Log error and ignore if failed to setup regional tax settings
|
# Log error and ignore if failed to setup regional tax settings
|
||||||
|
frappe.db.rollback(save_point="regional_tax_settings")
|
||||||
frappe.log_error("Unable to setup regional tax settings")
|
frappe.log_error("Unable to setup regional tax settings")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,11 @@ def get_exchange_rate(
|
|||||||
|
|
||||||
# cksgb 19/09/2016: get last entry in Currency Exchange with from_currency and to_currency.
|
# cksgb 19/09/2016: get last entry in Currency Exchange with from_currency and to_currency.
|
||||||
entries = frappe.get_all(
|
entries = frappe.get_all(
|
||||||
"Currency Exchange", fields=["exchange_rate"], filters=filters, order_by="date desc", limit=1
|
"Currency Exchange",
|
||||||
|
fields=["exchange_rate"],
|
||||||
|
filters=filters,
|
||||||
|
order_by="date desc, name desc",
|
||||||
|
limit=1,
|
||||||
)
|
)
|
||||||
if entries:
|
if entries:
|
||||||
return flt(entries[0].exchange_rate)
|
return flt(entries[0].exchange_rate)
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ def prepare_closing_stock_balance(name):
|
|||||||
doc.create_stock_closing_balance_entries()
|
doc.create_stock_closing_balance_entries()
|
||||||
doc.db_set("status", "Completed")
|
doc.db_set("status", "Completed")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback()
|
||||||
doc.db_set("status", "Failed")
|
doc.db_set("status", "Failed")
|
||||||
doc.log_error(title="Stock Closing Entry Failed")
|
doc.log_error(title="Stock Closing Entry Failed")
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ def create_material_request(material_requests):
|
|||||||
company_wise_mr = frappe._dict({})
|
company_wise_mr = frappe._dict({})
|
||||||
for request_type in material_requests:
|
for request_type in material_requests:
|
||||||
for company in material_requests[request_type]:
|
for company in material_requests[request_type]:
|
||||||
|
frappe.db.savepoint("reorder_mr")
|
||||||
try:
|
try:
|
||||||
items = material_requests[request_type][company]
|
items = material_requests[request_type][company]
|
||||||
if not items:
|
if not items:
|
||||||
@@ -287,8 +288,9 @@ def create_material_request(material_requests):
|
|||||||
company_wise_mr.setdefault(company, []).append(mr)
|
company_wise_mr.setdefault(company, []).append(mr)
|
||||||
|
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
|
frappe.db.rollback(save_point="reorder_mr")
|
||||||
exceptions_list.append(exception)
|
exceptions_list.append(exception)
|
||||||
mr.log_error("Unable to create material request")
|
frappe.log_error(title="Unable to create material request")
|
||||||
|
|
||||||
if company_wise_mr:
|
if company_wise_mr:
|
||||||
if getattr(frappe.local, "reorder_email_notify", None) is None:
|
if getattr(frappe.local, "reorder_email_notify", None) is None:
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ def create_reposting_entries(rows: str | list, company: str):
|
|||||||
|
|
||||||
for key, sle in item_wh.items():
|
for key, sle in item_wh.items():
|
||||||
item_code, warehouse = key
|
item_code, warehouse = key
|
||||||
|
frappe.db.savepoint("repost_value_comparison")
|
||||||
try:
|
try:
|
||||||
doc = frappe.get_doc(
|
doc = frappe.get_doc(
|
||||||
{
|
{
|
||||||
@@ -219,7 +220,7 @@ def create_reposting_entries(rows: str | list, company: str):
|
|||||||
|
|
||||||
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
|
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
frappe.db.rollback(save_point="repost_value_comparison")
|
||||||
|
|
||||||
if entries:
|
if entries:
|
||||||
entries = ", ".join(entries)
|
entries = ", ".join(entries)
|
||||||
|
|||||||
@@ -304,6 +304,7 @@ def create_reposting_entries(rows: str | list, item_code: str | None = None, war
|
|||||||
for row in rows:
|
for row in rows:
|
||||||
row = frappe._dict(row)
|
row = frappe._dict(row)
|
||||||
|
|
||||||
|
frappe.db.savepoint("repost_invariant_check")
|
||||||
try:
|
try:
|
||||||
doc = frappe.get_doc(
|
doc = frappe.get_doc(
|
||||||
{
|
{
|
||||||
@@ -320,6 +321,7 @@ def create_reposting_entries(rows: str | list, item_code: str | None = None, war
|
|||||||
|
|
||||||
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
|
entries.append(get_link_to_form("Repost Item Valuation", doc.name))
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
|
frappe.db.rollback(save_point="repost_invariant_check")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if entries:
|
if entries:
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ def link_existing_conversations(doc, state):
|
|||||||
return
|
return
|
||||||
if doc.doctype != "Contact":
|
if doc.doctype != "Contact":
|
||||||
return
|
return
|
||||||
|
frappe.db.savepoint("link_call_logs")
|
||||||
try:
|
try:
|
||||||
numbers = [d.phone for d in doc.phone_nos]
|
numbers = [d.phone for d in doc.phone_nos]
|
||||||
|
|
||||||
@@ -196,6 +197,7 @@ def link_existing_conversations(doc, state):
|
|||||||
if not frappe.in_test:
|
if not frappe.in_test:
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
frappe.db.rollback(save_point="link_call_logs")
|
||||||
frappe.log_error(title=_("Error during caller information update"))
|
frappe.log_error(title=_("Error during caller information update"))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user