Skip to content

Commit

Permalink
Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr…
Browse files Browse the repository at this point in the history
…-45240
  • Loading branch information
diptanilsaha authored Jan 19, 2025
2 parents fe886b5 + 011519e commit 88582f0
Show file tree
Hide file tree
Showing 17 changed files with 406 additions and 110 deletions.
6 changes: 3 additions & 3 deletions erpnext/accounts/party.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ def set_taxes(
):
from erpnext.accounts.doctype.tax_rule.tax_rule import get_party_details, get_tax_template

args = {party_type.lower(): party, "company": company}
args = {frappe.scrub(party_type): party, "company": company}

if tax_category:
args["tax_category"] = tax_category
Expand All @@ -696,10 +696,10 @@ def set_taxes(
else:
args.update(get_party_details(party, party_type))

if party_type in ("Customer", "Lead", "Prospect"):
if party_type in ("Customer", "Lead", "Prospect", "CRM Deal"):
args.update({"tax_type": "Sales"})

if party_type in ["Lead", "Prospect"]:
if party_type in ["Lead", "Prospect", "CRM Deal"]:
args["customer"] = None
del args[frappe.scrub(party_type)]
else:
Expand Down
4 changes: 2 additions & 2 deletions erpnext/controllers/sales_and_purchase_return.py
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ def get_serial_and_batch_bundle(field, doctype, reference_ids, is_rejected=False

def filter_serial_batches(parent_doc, data, row, warehouse_field=None, qty_field=None):
if not qty_field:
qty_field = "qty"
qty_field = "stock_qty"

if not warehouse_field:
warehouse_field = "warehouse"
Expand Down Expand Up @@ -1109,7 +1109,7 @@ def make_serial_batch_bundle_for_return(data, child_doc, parent_doc, warehouse_f
warehouse_field = "warehouse"

if not qty_field:
qty_field = "qty"
qty_field = "stock_qty"

warehouse = child_doc.get(warehouse_field)
if parent_doc.get("is_internal_customer"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"capacity_planning_for_days",
"mins_between_operations",
"other_settings_section",
"set_op_cost_and_scrape_from_sub_assemblies",
"set_op_cost_and_scrap_from_sub_assemblies",
"column_break_23",
"make_serial_no_batch_from_work_order"
],
Expand Down Expand Up @@ -202,13 +202,6 @@
"fieldtype": "Check",
"label": "Allow Excess Material Transfer"
},
{
"default": "0",
"description": "In the case of 'Use Multi-Level BOM' in a work order, if the user wishes to add sub-assembly costs to Finished Goods items without using a job card as well the scrap items, then this option needs to be enable.",
"fieldname": "set_op_cost_and_scrape_from_sub_assemblies",
"fieldtype": "Check",
"label": "Set Operating Cost / Scrap Items From Sub-assemblies"
},
{
"default": "0",
"depends_on": "eval: doc.material_consumption",
Expand Down Expand Up @@ -243,13 +236,20 @@
"fieldname": "validate_components_quantities_per_bom",
"fieldtype": "Check",
"label": "Validate Components and Quantities Per BOM"
},
{
"default": "0",
"description": "To include sub-assembly costs and scrap items in Finished Goods on a work order without using a job card, when the 'Use Multi-Level BOM' option is enabled.",
"fieldname": "set_op_cost_and_scrap_from_sub_assemblies",
"fieldtype": "Check",
"label": "Set Operating Cost / Scrap Items From Sub-assemblies"
}
],
"icon": "icon-wrench",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-01-09 16:02:23.326763",
"modified": "2025-01-13 12:07:03.089977",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class ManufacturingSettings(Document):
mins_between_operations: DF.Int
overproduction_percentage_for_sales_order: DF.Percent
overproduction_percentage_for_work_order: DF.Percent
set_op_cost_and_scrape_from_sub_assemblies: DF.Check
set_op_cost_and_scrap_from_sub_assemblies: DF.Check
update_bom_costs_automatically: DF.Check
validate_components_quantities_per_bom: DF.Check
# end: auto-generated types
Expand Down
4 changes: 2 additions & 2 deletions erpnext/manufacturing/doctype/work_order/test_work_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -2091,7 +2091,7 @@ def test_operating_cost_account(self):
def test_op_cost_and_scrap_based_on_sub_assemblies(self):
# Make Sub Assembly BOM 1

frappe.db.set_single_value("Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies", 1)
frappe.db.set_single_value("Manufacturing Settings", "set_op_cost_and_scrap_from_sub_assemblies", 1)

items = {
"Test Final FG Item": 0,
Expand Down Expand Up @@ -2132,7 +2132,7 @@ def test_op_cost_and_scrap_based_on_sub_assemblies(self):
for row in se_doc.additional_costs:
self.assertEqual(row.amount, 3000)

frappe.db.set_single_value("Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies", 0)
frappe.db.set_single_value("Manufacturing Settings", "set_op_cost_and_scrap_from_sub_assemblies", 0)

@change_settings(
"Manufacturing Settings", {"material_consumption": 1, "get_rm_cost_from_consumption_entry": 1}
Expand Down
3 changes: 2 additions & 1 deletion erpnext/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries
erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch
erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
erpnext.patches.v14_0.set_period_start_end_date_in_pcv
erpnext.patches.v14_0.update_closing_balances #08-11-2024
erpnext.patches.v14_0.update_closing_balances #20-12-2024
execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts
erpnext.patches.v14_0.update_subscription_details
Expand Down Expand Up @@ -387,5 +387,6 @@ erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions
erpnext.patches.v15_0.enable_allow_existing_serial_no
erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts
erpnext.patches.v15_0.update_asset_status_to_work_in_progress
erpnext.patches.v15_0.rename_manufacturing_settings_field
erpnext.patches.v15_0.migrate_checkbox_to_select_for_reconciliation_effect
erpnext.patches.v15_0.sync_auto_reconcile_config
140 changes: 83 additions & 57 deletions erpnext/patches/v14_0/update_closing_balances.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE

import itertools

import frappe

Expand All @@ -10,34 +11,91 @@
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.utils import get_fiscal_year


def execute():
# clear balances, they will be recalculated
frappe.db.truncate("Account Closing Balance")

gle_fields = get_gle_fields()
pcv_list = get_period_closing_vouchers()
gl_entries = get_gl_entries(pcv_list)

for _, pcvs in itertools.groupby(pcv_list, key=lambda pcv: (pcv.company, pcv.period_start_date)):
process_grouped_pcvs(list(pcvs), gl_entries)


def process_grouped_pcvs(pcvs, gl_entries):
pl_account_entries = []
closing_account_entries = []
first_pcv = pcvs[0]

for pcv in pcvs:
pcv_entries = gl_entries.get(pcv.name) or []
for entry in pcv_entries:
entry["closing_date"] = first_pcv.period_end_date
entry["period_closing_voucher"] = first_pcv.name
entry["voucher_no"] = first_pcv.name

list_to_update = (
pl_account_entries if entry.account != pcv.closing_account_head else closing_account_entries
)
list_to_update.append(entry)

# hacky!!
if to_cancel := pcvs[1:]:
to_cancel = [pcv.name for pcv in to_cancel]

# update voucher number
gle_to_update = [entry.name for entry in closing_account_entries + pl_account_entries]
frappe.db.set_value(
"GL Entry",
{
"name": ("in", gle_to_update),
"voucher_no": ("in", to_cancel),
},
"voucher_no",
first_pcv.name,
update_modified=False,
)

# mark as cancelled
frappe.db.set_value(
"Period Closing Voucher",
{"name": ("in", to_cancel)},
"docstatus",
2,
update_modified=False,
)

pcv_doc = frappe.get_doc("Period Closing Voucher", first_pcv.name)
pcv_doc.pl_accounts_reverse_gle = pl_account_entries
pcv_doc.closing_account_gle = closing_account_entries
closing_entries = pcv_doc.get_account_closing_balances()
make_closing_entries(closing_entries, pcv_doc.name, pcv_doc.company, pcv_doc.period_end_date)


def get_period_closing_vouchers():
return frappe.db.get_all(
"Period Closing Voucher",
fields=["name", "closing_account_head", "period_start_date", "period_end_date", "company"],
filters={"docstatus": 1},
order_by="period_start_date asc, period_end_date desc",
)


for company in frappe.get_all("Company", pluck="name"):
i = 0
company_wise_order = {}
for pcv in get_period_closing_vouchers(company):
company_wise_order.setdefault(pcv.company, [])
if pcv.period_end_date not in company_wise_order[pcv.company]:
pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name)
pcv_doc.pl_accounts_reverse_gle = get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields)
pcv_doc.closing_account_gle = get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields)
closing_entries = pcv_doc.get_account_closing_balances()
make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date)
def get_gl_entries(pcv_list):
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_no": ("in", [pcv.name for pcv in pcv_list]), "is_cancelled": 0},
fields=get_gle_fields(),
update={"is_period_closing_voucher_entry": 1},
)

company_wise_order[pcv.company].append(pcv.period_end_date)
i += 1
return {k: list(v) for k, v in itertools.groupby(gl_entries, key=lambda gle: gle.voucher_no)}


def get_gle_fields():
default_diemnsion_fields = ["cost_center", "finance_book", "project"]
accounting_dimension_fields = get_accounting_dimensions()
gle_fields = [
return [
"name",
"company",
"posting_date",
Expand All @@ -47,43 +105,11 @@ def get_gle_fields():
"credit",
"debit_in_account_currency",
"credit_in_account_currency",
*default_diemnsion_fields,
*accounting_dimension_fields,
"voucher_no",
# default dimension fields
"cost_center",
"finance_book",
"project",
# accounting dimensions
*get_accounting_dimensions(),
]

return gle_fields


def get_period_closing_vouchers(company):
return frappe.db.get_all(
"Period Closing Voucher",
fields=["name", "closing_account_head", "period_start_date", "period_end_date", "company"],
filters={"docstatus": 1, "company": company},
order_by="period_end_date",
)


def get_pcv_gl_entries_for_pl_accounts(pcv, gle_fields):
return get_gl_entries(pcv, gle_fields, {"account": ["!=", pcv.closing_account_head]})


def get_pcv_gl_entries_for_closing_accounts(pcv, gle_fields):
return get_gl_entries(pcv, gle_fields, {"account": pcv.closing_account_head})


def get_gl_entries(pcv, gle_fields, accounts_filter=None):
filters = {"voucher_no": pcv.name, "is_cancelled": 0}
if accounts_filter:
filters.update(accounts_filter)

gl_entries = frappe.db.get_all(
"GL Entry",
filters=filters,
fields=gle_fields,
)
for entry in gl_entries:
entry["is_period_closing_voucher_entry"] = 1
entry["closing_date"] = pcv.period_end_date
entry["period_closing_voucher"] = pcv.name

return gl_entries
9 changes: 9 additions & 0 deletions erpnext/patches/v15_0/rename_manufacturing_settings_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from frappe.model.utils.rename_field import rename_field


def execute():
rename_field(
"Manufacturing Settings",
"set_op_cost_and_scrape_from_sub_assemblies",
"set_op_cost_and_scrap_from_sub_assemblies",
)
39 changes: 29 additions & 10 deletions erpnext/stock/doctype/pick_list/pick_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ def onload(self) -> None:

def validate(self):
self.validate_for_qty()
if self.pick_manually and self.get("locations"):
self.validate_stock_qty()
self.check_serial_no_status()
self.validate_stock_qty()
self.check_serial_no_status()

def before_save(self):
self.update_status()
Expand All @@ -90,14 +89,24 @@ def validate_stock_qty(self):
from erpnext.stock.doctype.batch.batch import get_batch_qty

for row in self.get("locations"):
if row.batch_no and not row.qty:
if not row.picked_qty:
continue

if row.batch_no and row.picked_qty:
batch_qty = get_batch_qty(row.batch_no, row.warehouse, row.item_code)

if row.qty > batch_qty:
if row.picked_qty > batch_qty:
frappe.throw(
_(
"At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} for the batch {4} in the warehouse {5}."
).format(row.idx, row.item_code, batch_qty, row.batch_no, bold(row.warehouse)),
"At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} for the batch {4} in the warehouse {5}. Please restock the item."
).format(
row.idx,
row.picked_qty,
row.item_code,
batch_qty,
row.batch_no,
bold(row.warehouse),
),
title=_("Insufficient Stock"),
)

Expand All @@ -109,11 +118,11 @@ def validate_stock_qty(self):
"actual_qty",
)

if row.qty > flt(bin_qty):
if row.picked_qty > flt(bin_qty):
frappe.throw(
_(
"At Row #{0}: The picked quantity {1} for the item {2} is greater than available stock {3} in the warehouse {4}."
).format(row.idx, row.qty, bold(row.item_code), bin_qty, bold(row.warehouse)),
).format(row.idx, row.picked_qty, bold(row.item_code), bin_qty, bold(row.warehouse)),
title=_("Insufficient Stock"),
)

Expand Down Expand Up @@ -429,7 +438,14 @@ def set_item_locations(self, save=False):
locations_replica = self.get("locations")

# reset
self.delete_key("locations")
reset_rows = []
for row in self.get("locations"):
if not row.picked_qty:
reset_rows.append(row)

for row in reset_rows:
self.remove(row)

updated_locations = frappe._dict()
for item_doc in items:
item_code = item_doc.item_code
Expand Down Expand Up @@ -499,6 +515,9 @@ def aggregate_item_qty(self):
# aggregate qty for same item
item_map = OrderedDict()
for item in locations:
if item.picked_qty:
continue

if not item.item_code:
frappe.throw(f"Row #{item.idx}: Item Code is Mandatory")
if not cint(
Expand Down
Loading

0 comments on commit 88582f0

Please sign in to comment.