diff --git a/.github/helper/.flake8_strict b/.github/helper/.flake8_strict index 198ec7bfe54..3e8f7dd11ab 100644 --- a/.github/helper/.flake8_strict +++ b/.github/helper/.flake8_strict @@ -66,7 +66,8 @@ ignore = F841, E713, E712, - B023 + B023, + B028 max-line-length = 200 diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 2cd858c2676..1473b79bea5 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -132,6 +132,10 @@ frappe.ui.form.on('Asset', { }, __("Manage")); } + if (frm.doc.depr_entry_posting_status === "Failed") { + frm.trigger("set_depr_posting_failure_alert"); + } + frm.trigger("setup_chart"); } @@ -142,6 +146,19 @@ frappe.ui.form.on('Asset', { } }, + set_depr_posting_failure_alert: function (frm) { + const alert = ` +
+
+ + Failed to post depreciation entries + +
+
`; + + frm.dashboard.set_headline_alert(alert); + }, + toggle_reference_doc: function(frm) { if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) { frm.set_df_property('purchase_invoice', 'read_only', 1); diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 04e9c32f379..8132dbd411a 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -68,6 +68,7 @@ "column_break_51", "purchase_receipt_amount", "default_finance_book", + "depr_entry_posting_status", "amended_from" ], "fields": [ @@ -473,6 +474,16 @@ "fieldname": "section_break_36", "fieldtype": "Section Break", "label": "Finance Books" + }, + { + "fieldname": "depr_entry_posting_status", + "fieldtype": "Select", + "hidden": 1, + "label": "Depreciation Entry Posting Status", + "no_copy": 1, + "options": "\nSuccessful\nFailed", + "print_hide": 1, + "read_only": 1 } ], "idx": 72, @@ -487,7 +498,7 @@ { "group": "Repair", "link_doctype": "Asset Repair", - "link_fieldname": "asset_name" + "link_fieldname": "asset" }, { "group": "Value", @@ -495,7 +506,7 @@ "link_fieldname": "asset" } ], - "modified": "2022-07-20 16:22:44.437579", + "modified": "2023-01-17 00:28:37.789345", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 3f7e9459943..f8f581d8ae2 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -4,7 +4,8 @@ import frappe from frappe import _ -from frappe.utils import cint, flt, getdate, today +from frappe.utils import cint, flt, get_link_to_form, getdate, today +from frappe.utils.user import get_users_with_role from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_checks_for_pl_and_bs_accounts, @@ -20,9 +21,22 @@ def post_depreciation_entries(date=None): if not date: date = today() - for asset in get_depreciable_assets(date): - make_depreciation_entry(asset, date) - frappe.db.commit() + + failed_asset_names = [] + + for asset_name in get_depreciable_assets(date): + try: + make_depreciation_entry(asset_name, date) + frappe.db.commit() + except Exception as e: + frappe.db.rollback() + failed_asset_names.append(asset_name) + + if failed_asset_names: + set_depr_entry_posting_status_for_failed_assets(failed_asset_names) + notify_depr_entry_posting_error(failed_asset_names) + + frappe.db.commit() def get_depreciable_assets(date): @@ -121,6 +135,8 @@ def make_depreciation_entry(asset_name, date=None): finance_books.value_after_depreciation -= d.depreciation_amount finance_books.db_update() + frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful") + asset.set_status() return asset @@ -184,6 +200,42 @@ def get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation return credit_account, debit_account +def set_depr_entry_posting_status_for_failed_assets(failed_asset_names): + for asset_name in failed_asset_names: + frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed") + + +def notify_depr_entry_posting_error(failed_asset_names): + recipients = get_users_with_role("Accounts Manager") + + if not recipients: + recipients = get_users_with_role("System Manager") + + subject = _("Error while posting depreciation entries") + + asset_links = get_comma_separated_asset_links(failed_asset_names) + + message = ( + _("Hi,") + + "
" + + _("The following assets have failed to post depreciation entries: {0}").format(asset_links) + + "." + ) + + frappe.sendmail(recipients=recipients, subject=subject, message=message) + + +def get_comma_separated_asset_links(asset_names): + asset_links = [] + + for asset_name in asset_names: + asset_links.append(get_link_to_form("Asset", asset_name)) + + asset_links = ", ".join(asset_links) + + return asset_links + + @frappe.whitelist() def scrap_asset(asset_name): asset = frappe.get_doc("Asset", asset_name) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 493bd40a5dd..26db6396df3 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1387,6 +1387,7 @@ def create_asset(**args): "location": args.location or "Test Location", "asset_owner": args.asset_owner or "Company", "is_existing_asset": args.is_existing_asset or 1, + "depr_entry_posting_status": args.depr_entry_posting_status or "", } ) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 6b14dce084e..dd5dfca8a22 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -86,6 +86,7 @@ def get_data(filters): "status", "department", "cost_center", + "calculate_depreciation", "purchase_receipt", "asset_category", "purchase_date", @@ -98,11 +99,7 @@ def get_data(filters): assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) for asset in assets_record: - asset_value = ( - asset.gross_purchase_amount - - flt(asset.opening_accumulated_depreciation) - - flt(depreciation_amount_map.get(asset.name)) - ) + asset_value = get_asset_value(asset, filters.finance_book) row = { "asset_id": asset.asset_id, "asset_name": asset.asset_name, @@ -125,6 +122,21 @@ def get_data(filters): return data +def get_asset_value(asset, finance_book=None): + if not asset.calculate_depreciation: + return flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) + + finance_book_filter = ["finance_book", "is", "not set"] + if finance_book: + finance_book_filter = ["finance_book", "=", finance_book] + + return frappe.db.get_value( + doctype="Asset Finance Book", + filters=[["parent", "=", asset.asset_id], finance_book_filter], + fieldname="value_after_depreciation", + ) + + def prepare_chart_data(data, filters): labels_values_map = {} date_field = frappe.scrub(filters.date_based_on) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 74f6b6542e5..ef12483baac 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -383,7 +383,7 @@ class AccountsController(TransactionBase): self.get("inter_company_reference") or self.get("inter_company_invoice_reference") or self.get("inter_company_order_reference") - ): + ) and not self.get("is_return"): msg = _("Internal Sale or Delivery Reference missing.") msg += _("Please create purchase from internal sale or delivery document itself") frappe.throw(msg, title=_("Internal Sales Reference Missing")) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 436852913f1..3bfe79e3f38 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -191,14 +191,7 @@ def get_list_context(context=None): @frappe.whitelist() -def make_sales_order(source_name, target_doc=None): - quotation = frappe.db.get_value( - "Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1 - ) - if quotation.valid_till and ( - quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate()) - ): - frappe.throw(_("Validity period of this quotation has ended.")) +def make_sales_order(source_name: str, target_doc=None): return _make_sales_order(source_name, target_doc) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 6f0b381fc16..6ab4a52d9d2 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -118,17 +118,20 @@ class TestQuotation(FrappeTestCase): sales_order.payment_schedule[1].due_date, getdate(add_days(quotation.transaction_date, 30)) ) - def test_valid_till(self): - from erpnext.selling.doctype.quotation.quotation import make_sales_order - + def test_valid_till_before_transaction_date(self): quotation = frappe.copy_doc(test_records[0]) quotation.valid_till = add_days(quotation.transaction_date, -1) self.assertRaises(frappe.ValidationError, quotation.validate) + def test_so_from_expired_quotation(self): + from erpnext.selling.doctype.quotation.quotation import make_sales_order + + quotation = frappe.copy_doc(test_records[0]) quotation.valid_till = add_days(nowdate(), -1) quotation.insert() quotation.submit() - self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name) + + make_sales_order(quotation.name) def test_shopping_cart_without_website_item(self): if frappe.db.exists("Website Item", {"item_code": "_Test Item Home Desktop 100"}): diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index 5c4b57813d3..cbc40bbf90b 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -14,7 +14,6 @@ def get_data(): }, "internal_links": { "Quotation": ["items", "prevdoc_docname"], - "Material Request": ["items", "material_request"], }, "transactions": [ { diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 21abb94557c..2b783758c18 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -809,7 +809,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-04-27 03:15:34.366563", + "modified": "2022-12-25 02:51:10.247569", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", @@ -820,4 +820,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 462220c90e3..3314f8c9d57 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -104,7 +104,6 @@ class TestItem(FrappeTestCase): "conversion_factor": 1.0, "reserved_qty": 1, "actual_qty": 5, - "ordered_qty": 10, "projected_qty": 14, } diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 4c02d94fe83..effba8e579b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -198,7 +198,8 @@ class PickList(Document): frappe.throw(_("Qty of Finished Goods Item should be greater than 0.")) def before_print(self, settings=None): - self.group_similar_items() + if self.group_same_items: + self.group_similar_items() def group_similar_items(self): group_item_qty = defaultdict(float) diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py index 92e57bed220..7fbcbafbac1 100644 --- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py +++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py @@ -1,7 +1,10 @@ def get_data(): return { "fieldname": "pick_list", + "internal_links": { + "Sales Order": ["locations", "sales_order"], + }, "transactions": [ - {"items": ["Stock Entry", "Delivery Note"]}, + {"items": ["Stock Entry", "Sales Order", "Delivery Note"]}, ], } diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py index e8cebc8e622..fbb9fb5f051 100644 --- a/erpnext/stock/doctype/pick_list/test_pick_list.py +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -432,10 +432,10 @@ class TestPickList(FrappeTestCase): pl.before_print() self.assertEqual(len(pl.locations), 4) - # grouping should halve the number of items + # grouping should not happen if group_same_items is False pl = frappe.get_doc( doctype="Pick List", - group_same_items=True, + group_same_items=False, locations=[ _dict(item_code="A", warehouse="X", qty=5, picked_qty=1), _dict(item_code="B", warehouse="Y", qty=4, picked_qty=2), @@ -444,6 +444,11 @@ class TestPickList(FrappeTestCase): ], ) pl.before_print() + self.assertEqual(len(pl.locations), 4) + + # grouping should halve the number of items + pl.group_same_items = True + pl.before_print() self.assertEqual(len(pl.locations), 2) expected_items = [ diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index ba07d01f469..6fb9205d4b9 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1161,7 +1161,7 @@ def get_projected_qty(item_code, warehouse): @frappe.whitelist() def get_bin_details(item_code, warehouse, company=None, include_child_warehouses=False): - bin_details = {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0, "ordered_qty": 0} + bin_details = {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0} if warehouse: from frappe.query_builder.functions import Coalesce, Sum @@ -1177,7 +1177,6 @@ def get_bin_details(item_code, warehouse, company=None, include_child_warehouses Coalesce(Sum(bin.projected_qty), 0).as_("projected_qty"), Coalesce(Sum(bin.actual_qty), 0).as_("actual_qty"), Coalesce(Sum(bin.reserved_qty), 0).as_("reserved_qty"), - Coalesce(Sum(bin.ordered_qty), 0).as_("ordered_qty"), ) .where((bin.item_code == item_code) & (bin.warehouse.isin(warehouses))) ).run(as_dict=True)[0] diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 99f820ecac6..106e877c4cd 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -41,7 +41,7 @@ def get_data(report_filters): key = (d.voucher_type, d.voucher_no) gl_data = voucher_wise_gl_data.get(key) or {} d.account_value = gl_data.get("account_value", 0) - d.difference_value = abs(d.stock_value - d.account_value) + d.difference_value = d.stock_value - d.account_value if abs(d.difference_value) > 0.1: data.append(d) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index d7c362b4978..4e0528e536d 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1184,20 +1184,6 @@ def get_valuation_rate( (item_code, warehouse, voucher_no, voucher_type), ) - if not last_valuation_rate: - # Get valuation rate from last sle for the item against any warehouse - last_valuation_rate = frappe.db.sql( - """select valuation_rate - from `tabStock Ledger Entry` force index (item_code) - where - item_code = %s - AND valuation_rate > 0 - AND is_cancelled = 0 - AND NOT(voucher_no = %s AND voucher_type = %s) - order by posting_date desc, posting_time desc, name desc limit 1""", - (item_code, voucher_no, voucher_type), - ) - if last_valuation_rate: return flt(last_valuation_rate[0][0])