Merge branch 'develop' into gst-category
diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml
index 92a1962..f8abb6c 100644
--- a/.github/workflows/patch.yml
+++ b/.github/workflows/patch.yml
@@ -86,4 +86,27 @@
cd ~/frappe-bench/
wget https://erpnext.com/files/v10-erpnext.sql.gz
bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz
+
+ git -C "apps/frappe" remote set-url upstream https://github.com/frappe/frappe.git
+ git -C "apps/erpnext" remote set-url upstream https://github.com/frappe/erpnext.git
+
+ for version in $(seq 12 13)
+ do
+ echo "Updating to v$version"
+ branch_name="version-$version"
+
+ git -C "apps/frappe" fetch --depth 1 upstream $branch_name:$branch_name
+ git -C "apps/erpnext" fetch --depth 1 upstream $branch_name:$branch_name
+
+ git -C "apps/frappe" checkout -q -f $branch_name
+ git -C "apps/erpnext" checkout -q -f $branch_name
+
+ bench setup requirements --python
+ bench --site test_site migrate
+ done
+
+
+ echo "Updating to latest version"
+ git -C "apps/frappe" checkout -q -f "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}"
+ git -C "apps/erpnext" checkout -q -f "$GITHUB_SHA"
bench --site test_site migrate
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 1e983b1..60015f6 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -58,7 +58,8 @@
# Update outstanding amt on against voucher
if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
- and self.against_voucher and self.flags.update_outstanding == 'Yes'):
+ and self.against_voucher and self.flags.update_outstanding == 'Yes'
+ and not frappe.flags.is_reverse_depr_entry):
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
self.against_voucher)
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index e568a82..f3a0bdb 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -58,7 +58,10 @@
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
- self.validate_against_jv()
+ if not frappe.flags.is_reverse_depr_entry:
+ self.validate_against_jv()
+ self.validate_stock_accounts()
+
self.validate_reference_doc()
if self.docstatus == 0:
self.set_against_account()
@@ -69,7 +72,6 @@
self.validate_empty_accounts_table()
self.set_account_and_party_balance()
self.validate_inter_company_accounts()
- self.validate_stock_accounts()
if self.docstatus == 0:
self.apply_tax_withholding()
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 9b4a91d..8bbe3db 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -389,7 +389,7 @@
invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding
invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
- for key, allocated_amount in iteritems(invoice_payment_amount_map):
+ for idx, (key, allocated_amount) in enumerate(iteritems(invoice_payment_amount_map), 1):
if not invoice_paid_amount_map.get(key):
frappe.throw(_('Payment term {0} not used in {1}').format(key[0], key[1]))
@@ -407,7 +407,7 @@
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
else:
if allocated_amount > outstanding:
- frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
+ frappe.throw(_('Row #{0}: Cannot allocate more than {1} against payment term {2}').format(idx, outstanding, key[0]))
if allocated_amount and outstanding:
frappe.db.sql("""
@@ -1053,12 +1053,6 @@
party_account_currency = get_account_currency(args.get("party_account"))
company_currency = frappe.get_cached_value('Company', args.get("company"), "default_currency")
- # Get negative outstanding sales /purchase invoices
- negative_outstanding_invoices = []
- if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
- negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
- args.get("party_account"), args.get("company"), party_account_currency, company_currency)
-
# Get positive outstanding sales /purchase invoices/ Fees
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
@@ -1105,6 +1099,12 @@
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
+ # Get negative outstanding sales /purchase invoices
+ negative_outstanding_invoices = []
+ if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
+ negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
+ args.get("party_account"), party_account_currency, company_currency, condition=condition)
+
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
if not data:
@@ -1137,22 +1137,26 @@
'invoice_amount': flt(d.invoice_amount),
'outstanding_amount': flt(d.outstanding_amount),
'payment_amount': payment_term.payment_amount,
- 'payment_term': payment_term.payment_term,
- 'allocated_amount': payment_term.outstanding
+ 'payment_term': payment_term.payment_term
}))
+ outstanding_invoices_after_split = []
if invoice_ref_based_on_payment_terms:
for idx, ref in invoice_ref_based_on_payment_terms.items():
- voucher_no = outstanding_invoices[idx]['voucher_no']
- voucher_type = outstanding_invoices[idx]['voucher_type']
+ voucher_no = ref[0]['voucher_no']
+ voucher_type = ref[0]['voucher_type']
- frappe.msgprint(_("Spliting {} {} into {} rows as per payment terms").format(
+ frappe.msgprint(_("Spliting {} {} into {} row(s) as per Payment Terms").format(
voucher_type, voucher_no, len(ref)), alert=True)
- outstanding_invoices.pop(idx - 1)
- outstanding_invoices += invoice_ref_based_on_payment_terms[idx]
+ outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
- return outstanding_invoices
+ existing_row = list(filter(lambda x: x.get('voucher_no') == voucher_no, outstanding_invoices))
+ index = outstanding_invoices.index(existing_row[0])
+ outstanding_invoices.pop(index)
+
+ outstanding_invoices_after_split += outstanding_invoices
+ return outstanding_invoices_after_split
def get_orders_to_be_billed(posting_date, party_type, party,
company, party_account_currency, company_currency, cost_center=None, filters=None):
@@ -1219,7 +1223,7 @@
return order_list
def get_negative_outstanding_invoices(party_type, party, party_account,
- company, party_account_currency, company_currency, cost_center=None):
+ party_account_currency, company_currency, cost_center=None, condition=None):
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
@@ -1241,19 +1245,21 @@
`tab{voucher_type}`
where
{party_type} = %s and {party_account} = %s and docstatus = 1 and
- company = %s and outstanding_amount < 0
+ outstanding_amount < 0
{supplier_condition}
+ {condition}
order by
posting_date, name
""".format(**{
"supplier_condition": supplier_condition,
+ "condition": condition,
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
"cost_center": cost_center
- }), (party, party_account, company), as_dict=True)
+ }), (party, party_account), as_dict=True)
@frappe.whitelist()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 9190124..cd204ba 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -37,7 +37,7 @@
get_disposal_account_and_cost_center,
get_gl_entries_on_asset_disposal,
get_gl_entries_on_asset_regain,
- post_depreciation_entries,
+ make_depreciation_entry,
)
from erpnext.controllers.selling_controller import SellingController
from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
@@ -934,6 +934,7 @@
asset.db_set("disposal_date", None)
if asset.calculate_depreciation:
+ self.reverse_depreciation_entry_made_after_sale(asset)
self.reset_depreciation_schedule(asset)
else:
@@ -997,22 +998,20 @@
def depreciate_asset(self, asset):
asset.flags.ignore_validate_update_after_submit = True
- asset.prepare_depreciation_data(self.posting_date)
+ asset.prepare_depreciation_data(date_of_sale=self.posting_date)
asset.save()
- post_depreciation_entries(self.posting_date)
+ make_depreciation_entry(asset.name, self.posting_date)
def reset_depreciation_schedule(self, asset):
asset.flags.ignore_validate_update_after_submit = True
# recreate original depreciation schedule of the asset
- asset.prepare_depreciation_data()
+ asset.prepare_depreciation_data(date_of_return=self.posting_date)
self.modify_depreciation_schedule_for_asset_repairs(asset)
asset.save()
- self.delete_depreciation_entry_made_after_sale(asset)
-
def modify_depreciation_schedule_for_asset_repairs(self, asset):
asset_repairs = frappe.get_all(
'Asset Repair',
@@ -1026,7 +1025,7 @@
asset_repair.modify_depreciation_schedule()
asset.prepare_depreciation_data()
- def delete_depreciation_entry_made_after_sale(self, asset):
+ def reverse_depreciation_entry_made_after_sale(self, asset):
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
@@ -1041,11 +1040,19 @@
row += 1
if schedule.schedule_date == posting_date_of_original_invoice:
- if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice):
+ if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \
+ or self.sale_happens_in_the_future(posting_date_of_original_invoice):
+
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
reverse_journal_entry.posting_date = nowdate()
+ frappe.flags.is_reverse_depr_entry = True
reverse_journal_entry.submit()
+ frappe.flags.is_reverse_depr_entry = False
+ asset.flags.ignore_validate_update_after_submit = True
+ schedule.journal_entry = None
+ asset.save()
+
def get_posting_date_of_sales_invoice(self):
return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
@@ -1060,6 +1067,12 @@
return True
return False
+ def sale_happens_in_the_future(self, posting_date_of_original_invoice):
+ if posting_date_of_original_invoice > getdate():
+ return True
+
+ return False
+
@property
def enable_discount_accounting(self):
if not hasattr(self, "_enable_discount_accounting"):
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 56de3c6..262c083 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -2237,9 +2237,9 @@
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
enable_discount_accounting(enable=0)
- def test_asset_depreciation_on_sale(self):
+ def test_asset_depreciation_on_sale_with_pro_rata(self):
"""
- Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on Sept 30.
+ Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
"""
create_asset_data()
@@ -2252,7 +2252,7 @@
expected_values = [
["2020-06-30", 1311.48, 1311.48],
["2021-06-30", 20000.0, 21311.48],
- ["2021-09-30", 3966.76, 25278.24]
+ ["2021-09-30", 5041.1, 26352.58]
]
for i, schedule in enumerate(asset.schedules):
@@ -2261,6 +2261,59 @@
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertTrue(schedule.journal_entry)
+ def test_asset_depreciation_on_sale_without_pro_rata(self):
+ """
+ Tests if an Asset set to depreciate yearly on Dec 31, that gets sold on Dec 31 after two years, created an additional depreciation entry on its date of sale.
+ """
+
+ create_asset_data()
+ asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1,
+ available_for_use_date=getdate("2019-12-31"), total_number_of_depreciations=3,
+ expected_value_after_useful_life=10000, depreciation_start_date=getdate("2020-12-31"), submit=1)
+
+ post_depreciation_entries(getdate("2021-09-30"))
+
+ create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-12-31"))
+ asset.load_from_db()
+
+ expected_values = [
+ ["2020-12-31", 30000, 30000],
+ ["2021-12-31", 30000, 60000]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+ self.assertTrue(schedule.journal_entry)
+
+ def test_depreciation_on_return_of_sold_asset(self):
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+ create_asset_data()
+ asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1)
+ post_depreciation_entries(getdate("2021-09-30"))
+
+ si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30"))
+ return_si = make_return_doc("Sales Invoice", si.name)
+ return_si.submit()
+ asset.load_from_db()
+
+ expected_values = [
+ ["2020-06-30", 1311.48, 1311.48, True],
+ ["2021-06-30", 20000.0, 21311.48, True],
+ ["2022-06-30", 20000.0, 41311.48, False],
+ ["2023-06-30", 20000.0, 61311.48, False],
+ ["2024-06-30", 20000.0, 81311.48, False],
+ ["2025-06-06", 18688.52, 100000.0, False]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+ self.assertEqual(schedule.journal_entry, schedule.journal_entry)
+
def test_sales_invoice_against_supplier(self):
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 99a6cc3..cf62f49 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -75,12 +75,12 @@
if self.is_existing_asset and self.purchase_invoice:
frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
- def prepare_depreciation_data(self, date_of_sale=None):
+ def prepare_depreciation_data(self, date_of_sale=None, date_of_return=None):
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
self.make_depreciation_schedule(date_of_sale)
- self.set_accumulated_depreciation(date_of_sale)
+ self.set_accumulated_depreciation(date_of_sale, date_of_return)
else:
self.finance_books = []
self.value_after_depreciation = (flt(self.gross_purchase_amount) -
@@ -182,7 +182,7 @@
d.precision("rate_of_depreciation"))
def make_depreciation_schedule(self, date_of_sale):
- if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules:
+ if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.get('schedules'):
self.schedules = []
if not self.available_for_use_date:
@@ -232,13 +232,15 @@
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
from_date, date_of_sale)
- self.append("schedules", {
- "schedule_date": date_of_sale,
- "depreciation_amount": depreciation_amount,
- "depreciation_method": d.depreciation_method,
- "finance_book": d.finance_book,
- "finance_book_id": d.idx
- })
+ if depreciation_amount > 0:
+ self.append("schedules", {
+ "schedule_date": date_of_sale,
+ "depreciation_amount": depreciation_amount,
+ "depreciation_method": d.depreciation_method,
+ "finance_book": d.finance_book,
+ "finance_book_id": d.idx
+ })
+
break
# For first row
@@ -257,11 +259,15 @@
self.to_date = add_months(self.available_for_use_date,
n * cint(d.frequency_of_depreciation))
+ depreciation_amount_without_pro_rata = depreciation_amount
+
depreciation_amount, days, months = self.get_pro_rata_amt(d,
depreciation_amount, schedule_date, self.to_date)
- monthly_schedule_date = add_months(schedule_date, 1)
+ depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
+ depreciation_amount, d.finance_book)
+ monthly_schedule_date = add_months(schedule_date, 1)
schedule_date = add_days(schedule_date, days)
last_schedule_date = schedule_date
@@ -397,7 +403,28 @@
frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date")
.format(row.idx))
- def set_accumulated_depreciation(self, date_of_sale=None, ignore_booked_entry = False):
+ # to ensure that final accumulated depreciation amount is accurate
+ def get_adjusted_depreciation_amount(self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row, finance_book):
+ depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row(finance_book)
+
+ if depreciation_amount_for_first_row + depreciation_amount_for_last_row != depreciation_amount_without_pro_rata:
+ depreciation_amount_for_last_row = depreciation_amount_without_pro_rata - depreciation_amount_for_first_row
+
+ return depreciation_amount_for_last_row
+
+ def get_depreciation_amount_for_first_row(self, finance_book):
+ if self.has_only_one_finance_book():
+ return self.schedules[0].depreciation_amount
+ else:
+ for schedule in self.schedules:
+ if schedule.finance_book == finance_book:
+ return schedule.depreciation_amount
+
+ def has_only_one_finance_book(self):
+ if len(self.finance_books) == 1:
+ return True
+
+ def set_accumulated_depreciation(self, date_of_sale=None, date_of_return=None, ignore_booked_entry = False):
straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line']
finance_books = []
@@ -414,7 +441,7 @@
value_after_depreciation -= flt(depreciation_amount)
# for the last row, if depreciation method = Straight Line
- if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale:
+ if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale and not date_of_return:
book = self.get('finance_books')[cint(d.finance_book_id) - 1]
depreciation_amount += flt(value_after_depreciation -
flt(book.expected_value_after_useful_life), d.precision("depreciation_amount"))
@@ -833,7 +860,7 @@
if row.depreciation_method in ("Straight Line", "Manual"):
# if the Depreciation Schedule is being prepared for the first time
if not asset.flags.increase_in_asset_life:
- depreciation_amount = (flt(row.value_after_depreciation) -
+ depreciation_amount = (flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) -
flt(row.expected_value_after_useful_life)) / depreciation_left
# if the Depreciation Schedule is being modified after Asset Repair
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index cf4581b..f162d9f 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -21,12 +21,72 @@
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
-class TestAsset(unittest.TestCase):
- def setUp(self):
+class AssetSetup(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
set_depreciation_settings_in_company()
create_asset_data()
+ enable_cwip_accounting("Computers")
+ make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location")
frappe.db.sql("delete from `tabTax Rule`")
+ @classmethod
+ def tearDownClass(cls):
+ frappe.db.rollback()
+
+class TestAsset(AssetSetup):
+ def test_asset_category_is_fetched(self):
+ """Tests if the Item's Asset Category value is assigned to the Asset, if the field is empty."""
+
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.asset_category = None
+ asset.save()
+
+ self.assertEqual(asset.asset_category, "Computers")
+
+ def test_gross_purchase_amount_is_mandatory(self):
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.gross_purchase_amount = 0
+
+ self.assertRaises(frappe.MandatoryError, asset.save)
+
+ def test_pr_or_pi_mandatory_if_not_existing_asset(self):
+ """Tests if either PI or PR is present if CWIP is enabled and is_existing_asset=0."""
+
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.is_existing_asset=0
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_available_for_use_date_is_after_purchase_date(self):
+ asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, do_not_save=1)
+ asset.is_existing_asset = 0
+ asset.purchase_date = getdate("2021-10-10")
+ asset.available_for_use_date = getdate("2021-10-1")
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_item_exists(self):
+ asset = create_asset(item_code="MacBook", do_not_save=1)
+
+ self.assertRaises(frappe.DoesNotExistError, asset.save)
+
+ def test_validate_item(self):
+ asset = create_asset(item_code="MacBook Pro", do_not_save=1)
+ item = frappe.get_doc("Item", "MacBook Pro")
+
+ item.disabled = 1
+ item.save()
+ self.assertRaises(frappe.ValidationError, asset.save)
+ item.disabled = 0
+
+ item.is_fixed_asset = 0
+ self.assertRaises(frappe.ValidationError, asset.save)
+ item.is_fixed_asset = 1
+
+ item.is_stock_item = 1
+ self.assertRaises(frappe.ValidationError, asset.save)
+
def test_purchase_asset(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=100000.0, location="Test Location")
@@ -89,302 +149,16 @@
doc.set_missing_values()
self.assertEqual(doc.items[0].is_fixed_asset, 1)
- def test_schedule_for_straight_line_method(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
-
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save()
-
- self.assertEqual(asset.status, "Draft")
- expected_schedules = [
- ["2030-12-31", 30000.00, 30000.00],
- ["2031-12-31", 30000.00, 60000.00],
- ["2032-12-31", 30000.00, 90000.00]
- ]
-
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_straight_line_method_for_existing_asset(self):
- create_asset(is_existing_asset=1)
- asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
- asset.calculate_depreciation = 1
- asset.number_of_depreciations_booked = 1
- asset.opening_accumulated_depreciation = 40000
- asset.available_for_use_date = "2030-06-06"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- self.assertEqual(asset.status, "Draft")
- asset.save()
- expected_schedules = [
- ["2030-12-31", 14246.58, 54246.58],
- ["2031-12-31", 25000.00, 79246.58],
- ["2032-06-06", 10753.42, 90000.00]
- ]
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_double_declining_method(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Double Declining Balance",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": '2030-12-31'
- })
- asset.save()
- self.assertEqual(asset.status, "Draft")
-
- expected_schedules = [
- ['2030-12-31', 66667.00, 66667.00],
- ['2031-12-31', 22222.11, 88889.11],
- ['2032-12-31', 1110.89, 90000.0]
- ]
-
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_double_declining_method_for_existing_asset(self):
- create_asset(is_existing_asset = 1)
- asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
- asset.calculate_depreciation = 1
- asset.is_existing_asset = 1
- asset.number_of_depreciations_booked = 1
- asset.opening_accumulated_depreciation = 50000
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2029-11-30'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Double Declining Balance",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save()
- self.assertEqual(asset.status, "Draft")
-
- expected_schedules = [
- ["2030-12-31", 33333.50, 83333.50],
- ["2031-12-31", 6666.50, 90000.0]
- ]
-
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_schedule_for_prorated_straight_line_method(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.purchase_date = '2030-01-30'
- asset.is_existing_asset = 0
- asset.available_for_use_date = "2030-01-30"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
-
- asset.save()
-
- expected_schedules = [
- ["2030-12-31", 27534.25, 27534.25],
- ["2031-12-31", 30000.0, 57534.25],
- ["2032-12-31", 30000.0, 87534.25],
- ["2033-01-30", 2465.75, 90000.0]
- ]
-
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_depreciation(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.purchase_date = '2020-01-30'
- asset.available_for_use_date = "2020-01-30"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.submit()
- asset.load_from_db()
- self.assertEqual(asset.status, "Submitted")
-
- frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-")
- post_depreciation_entries(date="2021-01-01")
- asset.load_from_db()
-
- # check depreciation entry series
- self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
-
- expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
- ("_Test Depreciations - _TC", 30000.0, 0.0)
- )
-
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where against_voucher_type='Asset' and against_voucher = %s
- order by account""", asset.name)
-
- self.assertEqual(gle, expected_gle)
- self.assertEqual(asset.get("value_after_depreciation"), 0)
-
- def test_depreciation_entry_for_wdv_without_pro_rata(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=8000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
-
- self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
-
- expected_schedules = [
- ["2030-12-31", 4000.00, 4000.00],
- ["2031-12-31", 2000.00, 6000.00],
- ["2032-12-31", 1000.00, 7000.0],
- ]
-
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_pro_rata_depreciation_entry_for_wdv(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=8000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-06-06'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
-
- self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
-
- expected_schedules = [
- ["2030-12-31", 2279.45, 2279.45],
- ["2031-12-31", 2860.28, 5139.73],
- ["2032-12-31", 1430.14, 6569.87],
- ["2033-06-06", 430.13, 7000.0],
- ]
-
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
-
- self.assertEqual(schedules, expected_schedules)
-
- def test_depreciation_entry_cancellation(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.submit()
- post_depreciation_entries(date="2021-01-01")
-
- asset.load_from_db()
-
- # cancel depreciation entry
- depr_entry = asset.get("schedules")[0].journal_entry
- self.assertTrue(depr_entry)
- frappe.get_doc("Journal Entry", depr_entry).cancel()
-
- asset.load_from_db()
- depr_entry = asset.get("schedules")[0].journal_entry
- self.assertFalse(depr_entry)
-
def test_scrap_asset(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-01-01'
- asset.purchase_date = '2020-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 10,
- "frequency_of_depreciation": 1
- })
- asset.submit()
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = '2020-01-01',
+ purchase_date = '2020-01-01',
+ expected_value_after_useful_life = 10000,
+ total_number_of_depreciations = 10,
+ frequency_of_depreciation = 1,
+ submit = 1
+ )
post_depreciation_entries(date=add_months('2020-01-01', 4))
@@ -411,23 +185,18 @@
self.assertFalse(asset.journal_entry_for_scrap)
self.assertEqual(asset.status, "Partially Depreciated")
- def test_asset_sale(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ def test_gle_made_by_asset_sale(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = '2020-06-06',
+ purchase_date = '2020-01-01',
+ expected_value_after_useful_life = 10000,
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 10,
+ depreciation_start_date = '2020-12-31',
+ submit = 1
+ )
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.submit()
post_depreciation_entries(date="2021-01-01")
si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
@@ -455,30 +224,14 @@
si.cancel()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
- def test_asset_expected_value_after_useful_life(self):
+ def test_expense_head(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ qty=2, rate=200000.0, location="Test Location")
+ doc = make_invoice(pr.name)
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10
- })
- asset.save()
- accumulated_depreciation_after_full_schedule = \
- max(d.accumulated_depreciation_amount for d in asset.get("schedules"))
+ self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
- asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
- flt(accumulated_depreciation_after_full_schedule))
-
- self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
-
+ # CWIP: Capital Work In Progress
def test_cwip_accounting(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=5000, do_not_submit=True, location="Test Location")
@@ -561,14 +314,6 @@
self.assertEqual(gle, expected_gle)
- def test_expense_head(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=2, rate=200000.0, location="Test Location")
-
- doc = make_invoice(pr.name)
-
- self.assertEqual('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
-
def test_asset_cwip_toggling_cases(self):
cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
name = frappe.db.get_value("Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"])
@@ -637,41 +382,211 @@
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
+class TestDepreciationMethods(AssetSetup):
+ def test_schedule_for_straight_line_method(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ purchase_date = "2030-01-01",
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+ expected_schedules = [
+ ["2030-12-31", 30000.00, 30000.00],
+ ["2031-12-31", 30000.00, 60000.00],
+ ["2032-12-31", 30000.00, 90000.00]
+ ]
+
+ schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_straight_line_method_for_existing_asset(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-06-06",
+ is_existing_asset = 1,
+ number_of_depreciations_booked = 1,
+ opening_accumulated_depreciation = 40000,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+ expected_schedules = [
+ ["2030-12-31", 14246.58, 54246.58],
+ ["2031-12-31", 25000.00, 79246.58],
+ ["2032-06-06", 10753.42, 90000.00]
+ ]
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_double_declining_method(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ purchase_date = "2030-01-01",
+ depreciation_method = "Double Declining Balance",
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+
+ expected_schedules = [
+ ['2030-12-31', 66667.00, 66667.00],
+ ['2031-12-31', 22222.11, 88889.11],
+ ['2032-12-31', 1110.89, 90000.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_double_declining_method_for_existing_asset(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ is_existing_asset = 1,
+ depreciation_method = "Double Declining Balance",
+ number_of_depreciations_booked = 1,
+ opening_accumulated_depreciation = 50000,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.status, "Draft")
+
+ expected_schedules = [
+ ["2030-12-31", 33333.50, 83333.50],
+ ["2031-12-31", 6666.50, 90000.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ def test_schedule_for_prorated_straight_line_method(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-30",
+ purchase_date = "2030-01-30",
+ depreciation_method = "Straight Line",
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ expected_schedules = [
+ ["2030-12-31", 27534.25, 27534.25],
+ ["2031-12-31", 30000.0, 57534.25],
+ ["2032-12-31", 30000.0, 87534.25],
+ ["2033-01-30", 2465.75, 90000.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ # WDV: Written Down Value method
+ def test_depreciation_entry_for_wdv_without_pro_rata(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-01-01",
+ purchase_date = "2030-01-01",
+ depreciation_method = "Written Down Value",
+ expected_value_after_useful_life = 12500,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+
+ expected_schedules = [
+ ["2030-12-31", 50000.0, 50000.0],
+ ["2031-12-31", 25000.0, 75000.0],
+ ["2032-12-31", 12500.0, 87500.0],
+ ]
+
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
+ # WDV: Written Down Value method
+ def test_pro_rata_depreciation_entry_for_wdv(self):
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-06-06",
+ purchase_date = "2030-01-01",
+ depreciation_method = "Written Down Value",
+ expected_value_after_useful_life = 12500,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
+
+ self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+
+ expected_schedules = [
+ ["2030-12-31", 28493.15, 28493.15],
+ ["2031-12-31", 35753.43, 64246.58],
+ ["2032-12-31", 17876.71, 82123.29],
+ ["2033-06-06", 5376.71, 87500.0]
+ ]
+
+ schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ for d in asset.get("schedules")]
+
+ self.assertEqual(schedules, expected_schedules)
+
def test_discounted_wdv_depreciation_rate_for_indian_region(self):
# set indian company
company_flag = frappe.flags.company
frappe.flags.company = "_Test Company"
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=8000.0, location="Test Location")
-
- finance_book = frappe.new_doc('Finance Book')
- finance_book.finance_book_name = 'Income Tax'
+ finance_book = frappe.new_doc("Finance Book")
+ finance_book.finance_book_name = "Income Tax"
finance_book.for_income_tax = 1
- finance_book.insert(ignore_if_duplicate=1)
+ finance_book.insert(ignore_if_duplicate = True)
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-07-12'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "finance_book": finance_book.name,
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
+ asset = create_asset(
+ calculate_depreciation = 1,
+ available_for_use_date = "2030-07-12",
+ purchase_date = "2030-01-01",
+ finance_book = finance_book.name,
+ depreciation_method = "Written Down Value",
+ expected_value_after_useful_life = 12500,
+ depreciation_start_date = "2030-12-31",
+ total_number_of_depreciations = 3,
+ frequency_of_depreciation = 12
+ )
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [
- ["2030-12-31", 942.47, 942.47],
- ["2031-12-31", 3528.77, 4471.24],
- ["2032-12-31", 1764.38, 6235.62],
- ["2033-07-12", 764.38, 7000.00]
+ ["2030-12-31", 11780.82, 11780.82],
+ ["2031-12-31", 44109.59, 55890.41],
+ ["2032-12-31", 22054.8, 77945.21],
+ ["2033-07-12", 9554.79, 87500.0]
]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
@@ -682,6 +597,379 @@
# reset indian company
frappe.flags.company = company_flag
+class TestDepreciationBasics(AssetSetup):
+ def test_depreciation_without_pro_rata(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = getdate("2019-12-31"),
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = getdate("2020-12-31"),
+ submit = 1
+ )
+
+ expected_values = [
+ ["2020-12-31", 30000, 30000],
+ ["2021-12-31", 30000, 60000],
+ ["2022-12-31", 30000, 90000]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+
+ def test_depreciation_with_pro_rata(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = getdate("2019-12-31"),
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = getdate("2020-07-01"),
+ submit = 1
+ )
+
+ expected_values = [
+ ["2020-07-01", 15000, 15000],
+ ["2021-07-01", 30000, 45000],
+ ["2022-07-01", 30000, 75000],
+ ["2022-12-31", 15000, 90000]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+ self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
+
+ def test_get_depreciation_amount(self):
+ """Tests if get_depreciation_amount() returns the right value."""
+
+ from erpnext.assets.doctype.asset.asset import get_depreciation_amount
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ available_for_use_date = "2019-12-31"
+ )
+
+ asset.calculate_depreciation = 1
+ asset.append("finance_books", {
+ "depreciation_method": "Straight Line",
+ "frequency_of_depreciation": 12,
+ "total_number_of_depreciations": 3,
+ "expected_value_after_useful_life": 10000,
+ "depreciation_start_date": "2020-12-31"
+ })
+
+ depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0])
+ self.assertEqual(depreciation_amount, 30000)
+
+ def test_make_depreciation_schedule(self):
+ """Tests if make_depreciation_schedule() returns the right values."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_method = "Straight Line",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-12-31"
+ )
+
+ expected_values = [
+ ['2020-12-31', 30000.0],
+ ['2021-12-31', 30000.0],
+ ['2022-12-31', 30000.0]
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(expected_values[i][0], schedule.schedule_date)
+ self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
+
+ def test_set_accumulated_depreciation(self):
+ """Tests if set_accumulated_depreciation() returns the right values."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_method = "Straight Line",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-12-31"
+ )
+
+ expected_values = [30000.0, 60000.0, 90000.0]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(expected_values[i], schedule.accumulated_depreciation_amount)
+
+ def test_check_is_pro_rata(self):
+ """Tests if check_is_pro_rata() returns the right value(i.e. checks if has_pro_rata is accurate)."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ available_for_use_date = "2019-12-31",
+ do_not_save = 1
+ )
+
+ asset.calculate_depreciation = 1
+ asset.append("finance_books", {
+ "depreciation_method": "Straight Line",
+ "frequency_of_depreciation": 12,
+ "total_number_of_depreciations": 3,
+ "expected_value_after_useful_life": 10000,
+ "depreciation_start_date": "2020-12-31"
+ })
+
+ has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0])
+ self.assertFalse(has_pro_rata)
+
+ asset.finance_books = []
+ asset.append("finance_books", {
+ "depreciation_method": "Straight Line",
+ "frequency_of_depreciation": 12,
+ "total_number_of_depreciations": 3,
+ "expected_value_after_useful_life": 10000,
+ "depreciation_start_date": "2020-07-01"
+ })
+
+ has_pro_rata = asset.check_is_pro_rata(asset.finance_books[0])
+ self.assertTrue(has_pro_rata)
+
+ def test_expected_value_after_useful_life_greater_than_purchase_amount(self):
+ """Tests if an error is raised when expected_value_after_useful_life(110,000) > gross_purchase_amount(100,000)."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 110000,
+ depreciation_start_date = "2020-07-01",
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_depreciation_start_date(self):
+ """Tests if an error is raised when neither depreciation_start_date nor available_for_use_date are specified."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 110000,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_opening_accumulated_depreciation(self):
+ """Tests if an error is raised when opening_accumulated_depreciation > (gross_purchase_amount - expected_value_after_useful_life)."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-07-01",
+ opening_accumulated_depreciation = 100000,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_number_of_depreciations_booked(self):
+ """Tests if an error is raised when number_of_depreciations_booked is not specified when opening_accumulated_depreciation is."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-07-01",
+ opening_accumulated_depreciation = 10000,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_number_of_depreciations(self):
+ """Tests if an error is raised when number_of_depreciations_booked > total_number_of_depreciations."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2020-07-01",
+ opening_accumulated_depreciation = 10000,
+ number_of_depreciations_booked = 5,
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_depreciation_start_date_is_before_purchase_date(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2014-07-01",
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_depreciation_start_date_is_before_available_for_use_date(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ depreciation_start_date = "2018-07-01",
+ do_not_save = 1
+ )
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_finance_books_are_present_if_calculate_depreciation_is_enabled(self):
+ asset = create_asset(item_code="Macbook Pro", do_not_save=1)
+ asset.calculate_depreciation = 1
+
+ self.assertRaises(frappe.ValidationError, asset.save)
+
+ def test_post_depreciation_entries(self):
+ """Tests if post_depreciation_entries() works as expected."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-06-01")
+ asset.load_from_db()
+
+ self.assertTrue(asset.schedules[0].journal_entry)
+ self.assertFalse(asset.schedules[1].journal_entry)
+ self.assertFalse(asset.schedules[2].journal_entry)
+
+ def test_clear_depreciation_schedule(self):
+ """Tests if clear_depreciation_schedule() works as expected."""
+
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2019-12-31",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 12,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-06-01")
+ asset.load_from_db()
+
+ asset.clear_depreciation_schedule()
+
+ self.assertEqual(len(asset.schedules), 1)
+
+ def test_depreciation_entry_cancellation(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ purchase_date = "2020-06-06",
+ available_for_use_date = "2020-06-06",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 10,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ post_depreciation_entries(date="2021-01-01")
+
+ asset.load_from_db()
+
+ # cancel depreciation entry
+ depr_entry = asset.get("schedules")[0].journal_entry
+ self.assertTrue(depr_entry)
+ frappe.get_doc("Journal Entry", depr_entry).cancel()
+
+ asset.load_from_db()
+ depr_entry = asset.get("schedules")[0].journal_entry
+ self.assertFalse(depr_entry)
+
+ def test_asset_expected_value_after_useful_life(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ available_for_use_date = "2020-06-06",
+ purchase_date = "2020-06-06",
+ frequency_of_depreciation = 10,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000
+ )
+
+ accumulated_depreciation_after_full_schedule = \
+ max(d.accumulated_depreciation_amount for d in asset.get("schedules"))
+
+ asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
+ flt(accumulated_depreciation_after_full_schedule))
+
+ self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
+
+ def test_gle_made_by_depreciation_entries(self):
+ asset = create_asset(
+ item_code = "Macbook Pro",
+ calculate_depreciation = 1,
+ purchase_date = "2020-01-30",
+ available_for_use_date = "2020-01-30",
+ depreciation_start_date = "2020-12-31",
+ frequency_of_depreciation = 10,
+ total_number_of_depreciations = 3,
+ expected_value_after_useful_life = 10000,
+ submit = 1
+ )
+
+ self.assertEqual(asset.status, "Submitted")
+
+ frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-")
+ post_depreciation_entries(date="2021-01-01")
+ asset.load_from_db()
+
+ # check depreciation entry series
+ self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
+
+ expected_gle = (
+ ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
+ ("_Test Depreciations - _TC", 30000.0, 0.0)
+ )
+
+ gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ where against_voucher_type='Asset' and against_voucher = %s
+ order by account""", asset.name)
+
+ self.assertEqual(gle, expected_gle)
+ self.assertEqual(asset.get("value_after_depreciation"), 0)
def test_expected_value_change(self):
"""
tests if changing `expected_value_after_useful_life`
@@ -724,32 +1012,37 @@
asset = frappe.get_doc({
"doctype": "Asset",
"asset_name": args.asset_name or "Macbook Pro 1",
- "asset_category": "Computers",
+ "asset_category": args.asset_category or "Computers",
"item_code": args.item_code or "Macbook Pro",
- "company": args.company or"_Test Company",
- "purchase_date": "2015-01-01",
+ "company": args.company or "_Test Company",
+ "purchase_date": args.purchase_date or "2015-01-01",
"calculate_depreciation": args.calculate_depreciation or 0,
- "gross_purchase_amount": 100000,
- "purchase_receipt_amount": 100000,
- "expected_value_after_useful_life": 10000,
+ "opening_accumulated_depreciation": args.opening_accumulated_depreciation or 0,
+ "number_of_depreciations_booked": args.number_of_depreciations_booked or 0,
+ "gross_purchase_amount": args.gross_purchase_amount or 100000,
+ "purchase_receipt_amount": args.purchase_receipt_amount or 100000,
"warehouse": args.warehouse or "_Test Warehouse - _TC",
- "available_for_use_date": "2020-06-06",
- "location": "Test Location",
- "asset_owner": "Company",
- "is_existing_asset": 1
+ "available_for_use_date": args.available_for_use_date or "2020-06-06",
+ "location": args.location or "Test Location",
+ "asset_owner": args.asset_owner or "Company",
+ "is_existing_asset": args.is_existing_asset or 1
})
if asset.calculate_depreciation:
asset.append("finance_books", {
- "depreciation_method": "Straight Line",
- "frequency_of_depreciation": 12,
- "total_number_of_depreciations": 5
+ "finance_book": args.finance_book,
+ "depreciation_method": args.depreciation_method or "Straight Line",
+ "frequency_of_depreciation": args.frequency_of_depreciation or 12,
+ "total_number_of_depreciations": args.total_number_of_depreciations or 5,
+ "expected_value_after_useful_life": args.expected_value_after_useful_life or 0,
+ "depreciation_start_date": args.depreciation_start_date
})
- try:
- asset.save()
- except frappe.DuplicateEntryError:
- pass
+ if not args.do_not_save:
+ try:
+ asset.save()
+ except frappe.DuplicateEntryError:
+ pass
if args.submit:
asset.submit()
@@ -800,3 +1093,6 @@
# Enable booking asset depreciation entry automatically
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
+
+def enable_cwip_accounting(asset_category, enable=1):
+ frappe.db.set_value("Asset Category", asset_category, "enable_cwip_accounting", enable)
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
index 1b25dd4..a566d56 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
@@ -41,10 +41,13 @@
if filters.get("from_date") and filters.get("to_date"):
conditions += " and po.transaction_date between %(from_date)s and %(to_date)s"
- for field in ['company', 'name', 'status']:
+ for field in ['company', 'name']:
if filters.get(field):
conditions += f" and po.{field} = %({field})s"
+ if filters.get('status'):
+ conditions += " and po.status in %(status)s"
+
if filters.get('project'):
conditions += " and poi.project = %(project)s"
diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json
index 3d6fba5..0e872ac 100644
--- a/erpnext/crm/doctype/prospect/prospect.json
+++ b/erpnext/crm/doctype/prospect/prospect.json
@@ -20,6 +20,7 @@
"website",
"column_break_13",
"prospect_owner",
+ "company",
"leads_section",
"prospect_lead",
"address_and_contact_section",
@@ -153,14 +154,23 @@
"fieldname": "address_and_contact_section",
"fieldtype": "Section Break",
"label": "Address and Contact"
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2021-08-27 16:24:42.961967",
+ "migration_hash": "f39fb8f4e18a0e7fd391f0b4b52d8375",
+ "modified": "2021-11-01 13:10:36.759249",
"modified_by": "Administrator",
"module": "CRM",
"name": "Prospect",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index 7afbc00..821e017 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -31,7 +31,7 @@
<= fiscal_year.year_end_date):
frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
- def tax_withholding_category(self):
+ def validate_supplier_against_tax_category(self):
duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate',
{'supplier': self.supplier, 'tax_withholding_category': self.tax_withholding_category, 'name': ("!=", self.name)},
['name', 'valid_from', 'valid_upto'], as_dict=True)
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 102e84f..a9102a2 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -855,7 +855,7 @@
if row.depreciation_method in ("Straight Line", "Manual"):
# if the Depreciation Schedule is being prepared for the first time
if not asset.flags.increase_in_asset_life:
- depreciation_amount = (flt(row.value_after_depreciation) -
+ depreciation_amount = (flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) -
flt(row.expected_value_after_useful_life)) / depreciation_left
# if the Depreciation Schedule is being modified after Asset Repair
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index ddd4c4e..a86e604 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -206,8 +206,10 @@
var me = this;
var item = frappe.get_doc(cdt, cdn);
- if (item.serial_no && item.qty === item.serial_no.split(`\n`).length) {
- return;
+ // check if serial nos entered are as much as qty in row
+ if (item.serial_no) {
+ let serial_nos = item.serial_no.split(`\n`).filter(sn => sn.trim()); // filter out whitespaces
+ if (item.qty === serial_nos.length) return;
}
if (item.serial_no && !item.batch_no) {
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 04e4653..fa42c7d 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -676,6 +676,8 @@
def after_rename(self, old_name, new_name, merge):
if merge:
self.validate_duplicate_item_in_stock_reconciliation(old_name, new_name)
+ frappe.msgprint(_("It can take upto few hours for accurate stock values to be visible after merging items."),
+ indicator="orange", title="Note")
if self.route:
invalidate_cache_for_item(self)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index ac8303e..c4b8131 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -88,7 +88,11 @@
}
}
- filters["warehouse"] = item.s_warehouse || item.t_warehouse;
+ // User could want to select a manually created empty batch (no warehouse)
+ // or a pre-existing batch
+ if (frm.doc.purpose != "Material Receipt") {
+ filters["warehouse"] = item.s_warehouse || item.t_warehouse;
+ }
return {
query : "erpnext.controllers.queries.get_batch_no",
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index bdbec52..70f4bca 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -600,7 +600,7 @@
if not allow_zero_rate:
self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
- currency=erpnext.get_company_currency(sle.company))
+ currency=erpnext.get_company_currency(sle.company), company=sle.company)
def get_incoming_value_for_serial_nos(self, sle, serial_nos):
# get rate from serial nos within same company
@@ -667,7 +667,7 @@
if not allow_zero_valuation_rate:
self.wh_data.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
- currency=erpnext.get_company_currency(sle.company))
+ currency=erpnext.get_company_currency(sle.company), company=sle.company)
def get_fifo_values(self, sle):
incoming_rate = flt(sle.incoming_rate)
@@ -700,7 +700,7 @@
if not allow_zero_valuation_rate:
_rate = get_valuation_rate(sle.item_code, sle.warehouse,
sle.voucher_type, sle.voucher_no, self.allow_zero_rate,
- currency=erpnext.get_company_currency(sle.company))
+ currency=erpnext.get_company_currency(sle.company), company=sle.company)
else:
_rate = 0
@@ -911,10 +911,11 @@
def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True):
- # Get valuation rate from last sle for the same item and warehouse
- if not company:
- company = erpnext.get_default_company()
+ if not company:
+ company = frappe.get_cached_value("Warehouse", warehouse, "company")
+
+ # Get valuation rate from last sle for the same item and warehouse
last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry` force index (item_warehouse)
where