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])