mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-25 21:05:19 +00:00
Merge remote-tracking branch 'upstream/develop' into erpnext-refactoring
# Conflicts: # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py # erpnext/accounts/doctype/sales_invoice/sales_invoice.py # erpnext/buying/doctype/purchase_order/purchase_order.py # erpnext/buying/doctype/request_for_quotation/request_for_quotation.py # erpnext/controllers/accounts_controller.py # erpnext/selling/doctype/sales_order/sales_order.py # erpnext/selling/doctype/sales_order/test_sales_order.py # erpnext/stock/doctype/delivery_note/delivery_note.py # erpnext/stock/doctype/purchase_receipt/purchase_receipt.py # erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
This commit is contained in:
@@ -156,7 +156,7 @@ class AccountsController(TransactionBase):
|
||||
if not get_meta(self.doctype).has_field("outstanding_amount"):
|
||||
return
|
||||
|
||||
if self.get("is_return") and self.return_against and not self.get("is_pos"):
|
||||
if self.get("is_return") and self.return_against and not (self.get("is_pos") or self.get("is_paid")):
|
||||
against_voucher_outstanding = frappe.get_value(
|
||||
self.doctype, self.return_against, "outstanding_amount"
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ from frappe.utils.data import nowtime
|
||||
import erpnext
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
|
||||
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||
from erpnext.accounts.party import get_party_details
|
||||
from erpnext.accounts.party import _get_party_details
|
||||
from erpnext.buying.utils import update_last_purchase_rate, validate_for_items
|
||||
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
||||
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
|
||||
@@ -213,7 +213,7 @@ class BuyingController(SubcontractingController):
|
||||
# set contact and address details for supplier, if they are not mentioned
|
||||
if getattr(self, "supplier", None):
|
||||
self.update_if_missing(
|
||||
get_party_details(
|
||||
_get_party_details(
|
||||
self.supplier,
|
||||
party_type="Supplier",
|
||||
doctype=self.doctype,
|
||||
@@ -683,19 +683,6 @@ class BuyingController(SubcontractingController):
|
||||
)
|
||||
)
|
||||
|
||||
def check_for_on_hold_or_closed_status(self, ref_doctype, ref_fieldname):
|
||||
for d in self.get("items"):
|
||||
if d.get(ref_fieldname):
|
||||
status = frappe.db.get_value(ref_doctype, d.get(ref_fieldname), "status")
|
||||
if status in ("Closed", "On Hold"):
|
||||
frappe.throw(
|
||||
_("{ref_doctype} {ref_name} is {status}.").format(
|
||||
ref_doctype=frappe.bold(_(ref_doctype)),
|
||||
ref_name=frappe.bold(d.get(ref_fieldname)),
|
||||
status=frappe.bold(_(status)),
|
||||
)
|
||||
)
|
||||
|
||||
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||
self.update_ordered_and_reserved_qty()
|
||||
|
||||
|
||||
@@ -469,11 +469,9 @@ class SellingController(StockController):
|
||||
return so_qty, so_warehouse
|
||||
|
||||
def check_sales_order_on_hold_or_close(self, ref_fieldname):
|
||||
for d in self.get("items"):
|
||||
if d.get(ref_fieldname):
|
||||
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
|
||||
if status in ("Closed", "On Hold") and not self.is_return:
|
||||
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
|
||||
if self.is_return:
|
||||
return
|
||||
self.check_for_on_hold_or_closed_status("Sales Order", ref_fieldname)
|
||||
|
||||
def update_reserved_qty(self):
|
||||
so_map = {}
|
||||
|
||||
@@ -524,9 +524,9 @@ class StatusUpdater(Document):
|
||||
for args in self.status_updater:
|
||||
# condition to include current record (if submit or no if cancel)
|
||||
if self.docstatus == 1:
|
||||
args["cond"] = " or parent='%s'" % self.name.replace('"', '"')
|
||||
args["cond"] = " or parent=%s" % frappe.db.escape(self.name)
|
||||
else:
|
||||
args["cond"] = " and parent!='%s'" % self.name.replace('"', '"')
|
||||
args["cond"] = " and parent!=%s" % frappe.db.escape(self.name)
|
||||
|
||||
self._update_children(args, update_modified)
|
||||
|
||||
@@ -556,9 +556,10 @@ class StatusUpdater(Document):
|
||||
args["second_source_condition"] = frappe.db.sql(
|
||||
""" select ifnull((select sum({second_source_field})
|
||||
from `tab{second_source_dt}`
|
||||
where `{second_join_field}`='{detail_id}'
|
||||
where `{second_join_field}`=%(detail_id)s
|
||||
and (`tab{second_source_dt}`.docstatus=1)
|
||||
{second_source_extra_cond}), 0) """.format(**args)
|
||||
{second_source_extra_cond}), 0) """.format(**args),
|
||||
{"detail_id": args["detail_id"]},
|
||||
)[0][0]
|
||||
|
||||
if args["detail_id"]:
|
||||
@@ -569,9 +570,10 @@ class StatusUpdater(Document):
|
||||
frappe.db.sql(
|
||||
"""
|
||||
(select ifnull(sum({source_field}), 0)
|
||||
from `tab{source_dt}` where `{join_field}`='{detail_id}'
|
||||
from `tab{source_dt}` where `{join_field}`=%(detail_id)s
|
||||
and (docstatus=1 {cond}) {extra_cond})
|
||||
""".format(**args)
|
||||
""".format(**args),
|
||||
{"detail_id": args["detail_id"]},
|
||||
)[0][0]
|
||||
or 0.0
|
||||
)
|
||||
@@ -582,7 +584,8 @@ class StatusUpdater(Document):
|
||||
frappe.db.sql(
|
||||
"""update `tab{target_dt}`
|
||||
set {target_field} = {source_dt_value} {update_modified}
|
||||
where name='{detail_id}'""".format(**args)
|
||||
where name=%(detail_id)s""".format(**args),
|
||||
{"detail_id": args["detail_id"]},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1803,6 +1803,43 @@ class StockController(AccountsController):
|
||||
|
||||
qty -= working_qty
|
||||
|
||||
def check_for_on_hold_or_closed_status(
|
||||
self, ref_doctype: str, ref_fieldname: str, exclude_if_field: str | None = None
|
||||
) -> None:
|
||||
def _include(d):
|
||||
return d.get(ref_fieldname) and not (exclude_if_field and d.get(exclude_if_field))
|
||||
|
||||
included = [(d, d.get(ref_fieldname)) for d in self.get("items") if _include(d)]
|
||||
if not included:
|
||||
return
|
||||
|
||||
status_map = {
|
||||
r.name: r.status
|
||||
for r in frappe.get_all(
|
||||
ref_doctype,
|
||||
filters={"name": ["in", {name for _, name in included}]},
|
||||
fields=["name", "status"],
|
||||
)
|
||||
}
|
||||
|
||||
errors = []
|
||||
seen = set()
|
||||
for _d, ref_name in included:
|
||||
if ref_name in seen:
|
||||
continue
|
||||
seen.add(ref_name)
|
||||
if (status := status_map.get(ref_name)) in ("Closed", "On Hold"):
|
||||
errors.append(
|
||||
_("{ref_doctype} {ref_name} status is {status}.").format(
|
||||
ref_doctype=frappe.bold(_(ref_doctype)),
|
||||
ref_name=frappe.bold(ref_name),
|
||||
status=frappe.bold(_(status)),
|
||||
)
|
||||
)
|
||||
|
||||
if errors:
|
||||
frappe.throw("<br>".join(errors), frappe.InvalidStatusError)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def show_accounting_ledger_preview(company: str, doctype: str, docname: str):
|
||||
|
||||
@@ -304,6 +304,7 @@ class calculate_taxes_and_totals:
|
||||
return
|
||||
|
||||
for item in self.doc.items:
|
||||
item._unrounded_net_amount = None
|
||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||
cumulated_tax_fraction = 0
|
||||
total_inclusive_tax_amount_per_qty = 0
|
||||
@@ -331,7 +332,8 @@ class calculate_taxes_and_totals:
|
||||
):
|
||||
amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
|
||||
|
||||
item.net_amount = flt(amount / (1 + cumulated_tax_fraction), item.precision("net_amount"))
|
||||
item._unrounded_net_amount = amount / (1 + cumulated_tax_fraction)
|
||||
item.net_amount = flt(item._unrounded_net_amount, item.precision("net_amount"))
|
||||
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
|
||||
item.discount_percentage = flt(
|
||||
item.discount_percentage, item.precision("discount_percentage")
|
||||
@@ -541,7 +543,9 @@ class calculate_taxes_and_totals:
|
||||
actual_breakup = tax._total_tax_breakup
|
||||
diff = flt(expected_amount - actual_breakup, 5)
|
||||
|
||||
if abs(diff) <= 0.5:
|
||||
# TODO: fix rounding difference issues
|
||||
# Allow up to 1 for zero-precision currencies (e.g. JPY, KRW)
|
||||
if abs(diff) <= (1 if tax.precision("tax_amount") == 0 else 0.5):
|
||||
detail_row = self.doc._item_wise_tax_details[last_idx]
|
||||
detail_row["amount"] = flt(detail_row["amount"] + diff, 5)
|
||||
|
||||
@@ -600,7 +604,16 @@ class calculate_taxes_and_totals:
|
||||
elif tax.charge_type == "On Net Total":
|
||||
if tax.account_head in item_tax_map:
|
||||
current_net_amount = item.net_amount
|
||||
current_tax_amount = (tax_rate / 100.0) * item.net_amount
|
||||
|
||||
# Use unrounded net for inclusive taxes to avoid double rounding
|
||||
if (
|
||||
cint(tax.included_in_print_rate)
|
||||
and not self.discount_amount_applied
|
||||
and item._unrounded_net_amount is not None
|
||||
):
|
||||
current_tax_amount = (tax_rate / 100.0) * item._unrounded_net_amount
|
||||
else:
|
||||
current_tax_amount = (tax_rate / 100.0) * item.net_amount
|
||||
elif tax.charge_type == "On Previous Row Amount":
|
||||
current_net_amount = self.doc.get("taxes")[cint(tax.row_id) - 1].tax_amount_for_current_item
|
||||
current_tax_amount = (tax_rate / 100.0) * current_net_amount
|
||||
|
||||
@@ -7,7 +7,7 @@ import json
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.modules.utils import get_module_app
|
||||
from frappe.utils import flt, has_common
|
||||
from frappe.utils import cint, flt, has_common
|
||||
from frappe.utils.user import is_website_user
|
||||
|
||||
|
||||
@@ -179,10 +179,13 @@ def get_list_for_transactions(
|
||||
|
||||
def rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length):
|
||||
data = frappe.db.sql(
|
||||
"""select distinct parent as name, supplier from `tab{doctype}`
|
||||
where supplier = '{supplier}' and docstatus=1 order by creation desc limit {start}, {len}""".format(
|
||||
doctype=parties_doctype, supplier=parties[0], start=limit_start, len=limit_page_length
|
||||
),
|
||||
f"""select distinct parent as name, supplier from `tab{parties_doctype}`
|
||||
where supplier = %(supplier)s and docstatus=1 order by creation desc limit %(start)s, %(len)s""",
|
||||
{
|
||||
"supplier": parties[0],
|
||||
"start": cint(limit_start),
|
||||
"len": cint(limit_page_length),
|
||||
},
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user