mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-04 20:59:11 +00:00
Merge remote-tracking branch 'upstream/develop' into feat/so-po-advance-payment-status
This commit is contained in:
@@ -71,6 +71,10 @@ class AccountMissingError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidQtyError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
force_item_fields = (
|
||||
"item_group",
|
||||
"brand",
|
||||
@@ -239,7 +243,7 @@ class AccountsController(TransactionBase):
|
||||
references_map.setdefault(x.parent, []).append(x.name)
|
||||
|
||||
for doc, rows in references_map.items():
|
||||
unreconcile_doc = frappe.get_doc("Unreconcile Payments", doc)
|
||||
unreconcile_doc = frappe.get_doc("Unreconcile Payment", doc)
|
||||
for row in rows:
|
||||
unreconcile_doc.remove(unreconcile_doc.get("allocations", {"name": row})[0])
|
||||
|
||||
@@ -248,9 +252,9 @@ class AccountsController(TransactionBase):
|
||||
unreconcile_doc.save(ignore_permissions=True)
|
||||
|
||||
# delete docs upon parent doc deletion
|
||||
unreconcile_docs = frappe.db.get_all("Unreconcile Payments", filters={"voucher_no": self.name})
|
||||
unreconcile_docs = frappe.db.get_all("Unreconcile Payment", filters={"voucher_no": self.name})
|
||||
for x in unreconcile_docs:
|
||||
_doc = frappe.get_doc("Unreconcile Payments", x.name)
|
||||
_doc = frappe.get_doc("Unreconcile Payment", x.name)
|
||||
if _doc.docstatus == 1:
|
||||
_doc.cancel()
|
||||
_doc.delete()
|
||||
@@ -625,6 +629,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
args["doctype"] = self.doctype
|
||||
args["name"] = self.name
|
||||
args["child_doctype"] = item.doctype
|
||||
args["child_docname"] = item.name
|
||||
args["ignore_pricing_rule"] = (
|
||||
self.ignore_pricing_rule if hasattr(self, "ignore_pricing_rule") else 0
|
||||
@@ -910,10 +915,16 @@ class AccountsController(TransactionBase):
|
||||
return flt(args.get(field, 0) / self.get("conversion_rate", 1))
|
||||
|
||||
def validate_qty_is_not_zero(self):
|
||||
if self.doctype != "Purchase Receipt":
|
||||
for item in self.items:
|
||||
if not item.qty:
|
||||
frappe.throw(_("Item quantity can not be zero"))
|
||||
if self.doctype == "Purchase Receipt":
|
||||
return
|
||||
|
||||
for item in self.items:
|
||||
if not flt(item.qty):
|
||||
frappe.throw(
|
||||
msg=_("Row #{0}: Item quantity cannot be zero").format(item.idx),
|
||||
title=_("Invalid Quantity"),
|
||||
exc=InvalidQtyError,
|
||||
)
|
||||
|
||||
def validate_account_currency(self, account, account_currency=None):
|
||||
valid_currency = [self.company_currency]
|
||||
@@ -2953,6 +2964,9 @@ def validate_and_delete_children(parent, data) -> bool:
|
||||
d.cancel()
|
||||
d.delete()
|
||||
|
||||
if parent.doctype == "Purchase Order":
|
||||
parent.update_ordered_qty_in_so_for_removed_items(deleted_children)
|
||||
|
||||
# need to update ordered qty in Material Request first
|
||||
# bin uses Material Request Items to recalculate & update
|
||||
parent.update_prevdoc_status()
|
||||
@@ -3147,16 +3161,19 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
conv_fac_precision = child_item.precision("conversion_factor") or 2
|
||||
qty_precision = child_item.precision("qty") or 2
|
||||
|
||||
if flt(child_item.billed_amt, rate_precision) > flt(
|
||||
flt(d.get("rate"), rate_precision) * flt(d.get("qty"), qty_precision), rate_precision
|
||||
):
|
||||
# Amount cannot be lesser than billed amount, except for negative amounts
|
||||
row_rate = flt(d.get("rate"), rate_precision)
|
||||
amount_below_billed_amt = flt(child_item.billed_amt, rate_precision) > flt(
|
||||
row_rate * flt(d.get("qty"), qty_precision), rate_precision
|
||||
)
|
||||
if amount_below_billed_amt and row_rate > 0.0:
|
||||
frappe.throw(
|
||||
_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.").format(
|
||||
child_item.idx, child_item.item_code
|
||||
)
|
||||
)
|
||||
else:
|
||||
child_item.rate = flt(d.get("rate"), rate_precision)
|
||||
child_item.rate = row_rate
|
||||
|
||||
if d.get("conversion_factor"):
|
||||
if child_item.stock_uom == child_item.uom:
|
||||
@@ -3240,7 +3257,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
|
||||
if parent_doctype == "Purchase Order":
|
||||
update_last_purchase_rate(parent, is_submit=1)
|
||||
parent.update_prevdoc_status()
|
||||
|
||||
if any_qty_changed or items_added_or_removed or any_conversion_factor_changed:
|
||||
parent.update_prevdoc_status()
|
||||
|
||||
parent.update_requested_qty()
|
||||
parent.update_ordered_qty()
|
||||
parent.update_ordered_and_reserved_qty()
|
||||
|
||||
@@ -118,6 +118,7 @@ class BuyingController(SubcontractingController):
|
||||
"company": self.company,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"voucher_detail_no": row.name,
|
||||
},
|
||||
raise_error_if_no_rate=False,
|
||||
)
|
||||
@@ -365,7 +366,7 @@ class BuyingController(SubcontractingController):
|
||||
{
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.get("from_warehouse"),
|
||||
"posting_date": self.get("posting_date") or self.get("transation_date"),
|
||||
"posting_date": self.get("posting_date") or self.get("transaction_date"),
|
||||
"posting_time": posting_time,
|
||||
"qty": -1 * flt(d.get("stock_qty")),
|
||||
"serial_and_batch_bundle": d.get("serial_and_batch_bundle"),
|
||||
@@ -373,6 +374,7 @@ class BuyingController(SubcontractingController):
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"allow_zero_valuation": d.get("allow_zero_valuation"),
|
||||
"voucher_detail_no": d.name,
|
||||
},
|
||||
raise_error_if_no_rate=False,
|
||||
)
|
||||
@@ -440,7 +442,7 @@ class BuyingController(SubcontractingController):
|
||||
|
||||
if allow_to_edit_stock_qty:
|
||||
d.stock_qty = flt(d.stock_qty, d.precision("stock_qty"))
|
||||
if d.get("received_stock_qty"):
|
||||
if d.get("received_stock_qty") and d.meta.get_field("received_stock_qty"):
|
||||
d.received_stock_qty = flt(d.received_stock_qty, d.precision("received_stock_qty"))
|
||||
|
||||
def validate_purchase_return(self):
|
||||
@@ -758,7 +760,7 @@ class BuyingController(SubcontractingController):
|
||||
"calculate_depreciation": 0,
|
||||
"purchase_receipt_amount": purchase_amount,
|
||||
"gross_purchase_amount": purchase_amount,
|
||||
"asset_quantity": row.qty if is_grouped_asset else 0,
|
||||
"asset_quantity": row.qty if is_grouped_asset else 1,
|
||||
"purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None,
|
||||
"purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None,
|
||||
}
|
||||
|
||||
@@ -356,6 +356,7 @@ def make_return_doc(
|
||||
if doc.doctype == "Sales Invoice" or doc.doctype == "POS Invoice":
|
||||
doc.consolidated_invoice = ""
|
||||
doc.set("payments", [])
|
||||
doc.update_billed_amount_in_delivery_note = True
|
||||
for data in source.payments:
|
||||
paid_amount = 0.00
|
||||
base_paid_amount = 0.00
|
||||
@@ -390,7 +391,10 @@ def make_return_doc(
|
||||
if doc.get("discount_amount"):
|
||||
doc.discount_amount = -1 * source.discount_amount
|
||||
|
||||
if doctype != "Subcontracting Receipt":
|
||||
if doctype == "Subcontracting Receipt":
|
||||
doc.set_warehouse = source.set_warehouse
|
||||
doc.supplier_warehouse = source.supplier_warehouse
|
||||
else:
|
||||
doc.run_method("calculate_taxes_and_totals")
|
||||
|
||||
def update_item(source_doc, target_doc, source_parent):
|
||||
@@ -582,8 +586,6 @@ def make_return_doc(
|
||||
set_missing_values,
|
||||
)
|
||||
|
||||
doclist.set_onload("ignore_price_list", True)
|
||||
|
||||
return doclist
|
||||
|
||||
|
||||
|
||||
@@ -350,11 +350,12 @@ class SellingController(StockController):
|
||||
return il
|
||||
|
||||
def has_product_bundle(self, item_code):
|
||||
return frappe.db.sql(
|
||||
"""select name from `tabProduct Bundle`
|
||||
where new_item_code=%s and docstatus != 2""",
|
||||
item_code,
|
||||
)
|
||||
product_bundle = frappe.qb.DocType("Product Bundle")
|
||||
return (
|
||||
frappe.qb.from_(product_bundle)
|
||||
.select(product_bundle.name)
|
||||
.where((product_bundle.new_item_code == item_code) & (product_bundle.disabled == 0))
|
||||
).run()
|
||||
|
||||
def get_already_delivered_qty(self, current_docname, so, so_detail):
|
||||
delivered_via_dn = frappe.db.sql(
|
||||
@@ -443,6 +444,7 @@ class SellingController(StockController):
|
||||
"company": self.company,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"voucher_detail_no": d.name,
|
||||
"allow_zero_valuation": d.get("allow_zero_valuation"),
|
||||
},
|
||||
raise_error_if_no_rate=False,
|
||||
|
||||
@@ -626,6 +626,18 @@ class SubcontractingController(StockController):
|
||||
(row.item_code, row.get(self.subcontract_data.order_field))
|
||||
] -= row.qty
|
||||
|
||||
def __set_rate_for_serial_and_batch_bundle(self):
|
||||
if self.doctype != "Subcontracting Receipt":
|
||||
return
|
||||
|
||||
for row in self.get(self.raw_material_table):
|
||||
if not row.get("serial_and_batch_bundle"):
|
||||
continue
|
||||
|
||||
row.rate = frappe.get_cached_value(
|
||||
"Serial and Batch Bundle", row.serial_and_batch_bundle, "avg_rate"
|
||||
)
|
||||
|
||||
def __modify_serial_and_batch_bundle(self):
|
||||
if self.is_new():
|
||||
return
|
||||
@@ -681,6 +693,7 @@ class SubcontractingController(StockController):
|
||||
self.__remove_changed_rows()
|
||||
self.__set_supplied_items()
|
||||
self.__modify_serial_and_batch_bundle()
|
||||
self.__set_rate_for_serial_and_batch_bundle()
|
||||
|
||||
def __validate_batch_no(self, row, key):
|
||||
if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get(
|
||||
@@ -867,6 +880,7 @@ class SubcontractingController(StockController):
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * item.consumed_qty,
|
||||
"voucher_detail_no": item.name,
|
||||
"serial_and_batch_bundle": item.serial_and_batch_bundle,
|
||||
}
|
||||
)
|
||||
@@ -939,6 +953,23 @@ class SubcontractingController(StockController):
|
||||
|
||||
return self._sub_contracted_items
|
||||
|
||||
def update_requested_qty(self):
|
||||
material_request_map = {}
|
||||
for d in self.get("items"):
|
||||
if d.material_request_item:
|
||||
material_request_map.setdefault(d.material_request, []).append(d.material_request_item)
|
||||
|
||||
for mr, mr_item_rows in material_request_map.items():
|
||||
if mr and mr_item_rows:
|
||||
mr_obj = frappe.get_doc("Material Request", mr)
|
||||
|
||||
if mr_obj.status in ["Stopped", "Cancelled"]:
|
||||
frappe.throw(
|
||||
_("Material Request {0} is cancelled or stopped").format(mr), frappe.InvalidStatusError
|
||||
)
|
||||
|
||||
mr_obj.update_requested_qty(mr_item_rows)
|
||||
|
||||
|
||||
def get_item_details(items):
|
||||
item = frappe.qb.DocType("Item")
|
||||
|
||||
@@ -54,6 +54,7 @@ class calculate_taxes_and_totals(object):
|
||||
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
|
||||
self.doc.grand_total -= self.doc.discount_amount
|
||||
self.doc.base_grand_total -= self.doc.base_discount_amount
|
||||
self.doc.rounding_adjustment = self.doc.base_rounding_adjustment = 0.0
|
||||
self.set_rounded_total()
|
||||
|
||||
self.calculate_shipping_charges()
|
||||
|
||||
@@ -1001,6 +1001,7 @@ def make_subcontracted_items():
|
||||
"Subcontracted Item SA5": {},
|
||||
"Subcontracted Item SA6": {},
|
||||
"Subcontracted Item SA7": {},
|
||||
"Subcontracted Item SA8": {},
|
||||
}
|
||||
|
||||
for item, properties in sub_contracted_items.items():
|
||||
@@ -1020,6 +1021,7 @@ def make_raw_materials():
|
||||
},
|
||||
"Subcontracted SRM Item 4": {"has_serial_no": 1, "serial_no_series": "SRII.####"},
|
||||
"Subcontracted SRM Item 5": {"has_serial_no": 1, "serial_no_series": "SRIID.####"},
|
||||
"Subcontracted SRM Item 8": {},
|
||||
}
|
||||
|
||||
for item, properties in raw_materials.items():
|
||||
@@ -1043,6 +1045,7 @@ def make_service_items():
|
||||
"Subcontracted Service Item 5": {},
|
||||
"Subcontracted Service Item 6": {},
|
||||
"Subcontracted Service Item 7": {},
|
||||
"Subcontracted Service Item 8": {},
|
||||
}
|
||||
|
||||
for item, properties in service_items.items():
|
||||
@@ -1066,6 +1069,7 @@ def make_bom_for_subcontracted_items():
|
||||
"Subcontracted Item SA5": ["Subcontracted SRM Item 5"],
|
||||
"Subcontracted Item SA6": ["Subcontracted SRM Item 3"],
|
||||
"Subcontracted Item SA7": ["Subcontracted SRM Item 1"],
|
||||
"Subcontracted Item SA8": ["Subcontracted SRM Item 8"],
|
||||
}
|
||||
|
||||
for item_code, raw_materials in boms.items():
|
||||
|
||||
Reference in New Issue
Block a user