From b73422e4eef790334e22234f0875f4e18f695cf8 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 18 Apr 2023 18:38:28 +0530 Subject: [PATCH 01/16] fix: internal Purchase Receipt GL Entries (cherry picked from commit 6fca9adcd4a881b09630f5e0ad875df136e1d6e0) --- .../doctype/purchase_receipt/purchase_receipt.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 0384c24503a..28bff671f62 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -370,7 +370,19 @@ class PurchaseReceipt(BuyingController): outgoing_amount = d.base_net_amount if self.is_internal_supplier and d.valuation_rate: - outgoing_amount = d.valuation_rate * d.stock_qty + outgoing_amount = abs( + frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Purchase Receipt", + "voucher_no": self.name, + "voucher_detail_no": d.name, + "warehouse": d.from_warehouse, + "is_cancelled": 0, + }, + "stock_value_difference", + ) + ) credit_amount = outgoing_amount if credit_amount: From 046bf64fa322f11cc61bcd08e841cc53e0f92c66 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 18 Apr 2023 21:03:23 +0530 Subject: [PATCH 02/16] test: add test case for internal PR GL Entries (cherry picked from commit c86c543fbfaa46c45f06a745a0841e821e14e712) # Conflicts: # erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py --- .../purchase_receipt/test_purchase_receipt.py | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 2457c8e80ab..8539aae8560 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1707,6 +1707,201 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertTrue(return_pi.docstatus == 1) +<<<<<<< HEAD +======= + def test_disable_last_purchase_rate(self): + from erpnext.stock.get_item_details import get_item_details + + item = make_item( + "_Test Disable Last Purchase Rate", + {"is_purchase_item": 1, "is_stock_item": 1}, + ) + + frappe.db.set_single_value("Buying Settings", "disable_last_purchase_rate", 1) + + pr = make_purchase_receipt( + qty=1, + rate=100, + item_code=item.name, + ) + + args = pr.items[0].as_dict() + args.update( + { + "supplier": pr.supplier, + "doctype": pr.doctype, + "conversion_rate": pr.conversion_rate, + "currency": pr.currency, + "company": pr.company, + "posting_date": pr.posting_date, + "posting_time": pr.posting_time, + } + ) + + res = get_item_details(args) + self.assertEqual(res.get("last_purchase_rate"), 0) + + frappe.db.set_single_value("Buying Settings", "disable_last_purchase_rate", 0) + + pr = make_purchase_receipt( + qty=1, + rate=100, + item_code=item.name, + ) + + res = get_item_details(args) + self.assertEqual(res.get("last_purchase_rate"), 100) + + def test_validate_received_qty_for_internal_pr(self): + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + + # Step 1: Create Item + item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100}) + + # Step 2: Create Stock Entry (Material Receipt) + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + make_stock_entry( + purpose="Material Receipt", + item_code=item.name, + qty=15, + company=company, + to_warehouse=from_warehouse, + ) + + # Step 3: Create Delivery Note with Internal Customer + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + dn = create_delivery_note( + item_code=item.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=10, + rate=100, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + # Step 4: Create Internal Purchase Receipt + from erpnext.controllers.status_updater import OverAllowanceError + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + + pr = make_inter_company_purchase_receipt(dn.name) + pr.items[0].qty = 15 + pr.items[0].from_warehouse = target_warehouse + pr.items[0].warehouse = to_warehouse + pr.items[0].rejected_warehouse = from_warehouse + pr.save() + + self.assertRaises(OverAllowanceError, pr.submit) + + # Step 5: Test Over Receipt Allowance + frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 50) + + make_stock_entry( + purpose="Material Transfer", + item_code=item.name, + qty=5, + company=company, + from_warehouse=from_warehouse, + to_warehouse=target_warehouse, + ) + + pr.submit() + + frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 0) + + def test_internal_pr_gl_entries(self): + from erpnext.stock import get_warehouse_account_map + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_stock_reconciliation, + ) + + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + + item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100}) + make_stock_entry( + purpose="Material Receipt", + item_code=item.name, + qty=10, + company=company, + to_warehouse=from_warehouse, + posting_date=add_days(today(), -3), + ) + + # Step - 1: Create Delivery Note with Internal Customer + dn = create_delivery_note( + item_code=item.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=10, + rate=100, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + posting_date=add_days(today(), -2), + ) + + # Step - 2: Create Internal Purchase Receipt + pr = make_inter_company_purchase_receipt(dn.name) + pr.items[0].qty = 10 + pr.items[0].from_warehouse = target_warehouse + pr.items[0].warehouse = to_warehouse + pr.items[0].rejected_warehouse = from_warehouse + pr.save() + pr.submit() + + # Step - 3: Create back-date Stock Reconciliation [After DN and Before PR] + create_stock_reconciliation( + item_code=item, + warehouse=target_warehouse, + qty=10, + rate=50, + company=company, + posting_date=add_days(today(), -1), + ) + + warehouse_account = get_warehouse_account_map(company) + stock_account_value = frappe.db.get_value( + "GL Entry", + { + "account": warehouse_account[target_warehouse]["account"], + "voucher_type": "Purchase Receipt", + "voucher_no": pr.name, + "is_cancelled": 0, + }, + fieldname=["credit"], + ) + stock_diff = frappe.db.get_value( + "Stock Ledger Entry", + { + "voucher_type": "Purchase Receipt", + "voucher_no": pr.name, + "is_cancelled": 0, + }, + fieldname=["sum(stock_value_difference)"], + ) + + # Value of Stock Account should be equal to the sum of Stock Value Difference + self.assertEqual(stock_account_value, stock_diff) + +>>>>>>> c86c543fbf (test: add test case for internal PR GL Entries) def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From d010b048dc73251742b5cecd4fa4611e0077f7ea Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 19 Apr 2023 16:01:45 +0530 Subject: [PATCH 03/16] fix(test): `test_backdated_stock_reco_cancellation_future_negative_stock` (cherry picked from commit 11c8503180cda70b1aebce3711def05eec840133) --- .../doctype/stock_reconciliation/test_stock_reconciliation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index a7f5f4a8ae2..a09ad946155 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -529,7 +529,9 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): # check if cancellation of stock reco is blocked self.assertRaises(NegativeStockError, sr.cancel) - repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name})) + repost_exists = bool( + frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name, "status": "Queued"}) + ) self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation") def test_intermediate_sr_bin_update(self): From 5680045f2b6a9042ea91ab7099fe6135e3a65883 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 20 Apr 2023 12:39:30 +0530 Subject: [PATCH 04/16] fix: `PermissionError` in Work Order (cherry picked from commit 8108b2de0aa86b743c767808e38349644c2f9be9) --- .../doctype/work_order/work_order.js | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 0e8c9bcec49..d5f88eceac7 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -592,20 +592,18 @@ erpnext.work_order = { // all materials transferred for manufacturing, make this primary finish_btn.addClass('btn-primary'); } - } else { - frappe.db.get_doc("Manufacturing Settings").then((doc) => { - let allowance_percentage = doc.overproduction_percentage_for_work_order; + } else if (frm.doc.__onload && frm.doc.__onload.overproduction_percentage) { + let allowance_percentage = frm.doc.__onload.overproduction_percentage; - if (allowance_percentage > 0) { - let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty); + if (allowance_percentage > 0) { + let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty); - if ((flt(doc.produced_qty) < allowed_qty)) { - frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); - }); - } + if ((flt(doc.produced_qty) < allowed_qty)) { + frm.add_custom_button(__('Finish'), function() { + erpnext.work_order.make_se(frm, 'Manufacture'); + }); } - }); + } } } } else { From 102ac9f74d7450bb397bd339361072ab00169a48 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 20 Apr 2023 16:20:25 +0530 Subject: [PATCH 05/16] chore: `conflicts` --- .../purchase_receipt/test_purchase_receipt.py | 115 +----------------- 1 file changed, 2 insertions(+), 113 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 8539aae8560..91755e61697 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1707,117 +1707,6 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertTrue(return_pi.docstatus == 1) -<<<<<<< HEAD -======= - def test_disable_last_purchase_rate(self): - from erpnext.stock.get_item_details import get_item_details - - item = make_item( - "_Test Disable Last Purchase Rate", - {"is_purchase_item": 1, "is_stock_item": 1}, - ) - - frappe.db.set_single_value("Buying Settings", "disable_last_purchase_rate", 1) - - pr = make_purchase_receipt( - qty=1, - rate=100, - item_code=item.name, - ) - - args = pr.items[0].as_dict() - args.update( - { - "supplier": pr.supplier, - "doctype": pr.doctype, - "conversion_rate": pr.conversion_rate, - "currency": pr.currency, - "company": pr.company, - "posting_date": pr.posting_date, - "posting_time": pr.posting_time, - } - ) - - res = get_item_details(args) - self.assertEqual(res.get("last_purchase_rate"), 0) - - frappe.db.set_single_value("Buying Settings", "disable_last_purchase_rate", 0) - - pr = make_purchase_receipt( - qty=1, - rate=100, - item_code=item.name, - ) - - res = get_item_details(args) - self.assertEqual(res.get("last_purchase_rate"), 100) - - def test_validate_received_qty_for_internal_pr(self): - prepare_data_for_internal_transfer() - customer = "_Test Internal Customer 2" - company = "_Test Company with perpetual inventory" - from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) - target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) - to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) - - # Step 1: Create Item - item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100}) - - # Step 2: Create Stock Entry (Material Receipt) - from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry - - make_stock_entry( - purpose="Material Receipt", - item_code=item.name, - qty=15, - company=company, - to_warehouse=from_warehouse, - ) - - # Step 3: Create Delivery Note with Internal Customer - from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note - - dn = create_delivery_note( - item_code=item.name, - company=company, - customer=customer, - cost_center="Main - TCP1", - expense_account="Cost of Goods Sold - TCP1", - qty=10, - rate=100, - warehouse=from_warehouse, - target_warehouse=target_warehouse, - ) - - # Step 4: Create Internal Purchase Receipt - from erpnext.controllers.status_updater import OverAllowanceError - from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt - - pr = make_inter_company_purchase_receipt(dn.name) - pr.items[0].qty = 15 - pr.items[0].from_warehouse = target_warehouse - pr.items[0].warehouse = to_warehouse - pr.items[0].rejected_warehouse = from_warehouse - pr.save() - - self.assertRaises(OverAllowanceError, pr.submit) - - # Step 5: Test Over Receipt Allowance - frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 50) - - make_stock_entry( - purpose="Material Transfer", - item_code=item.name, - qty=5, - company=company, - from_warehouse=from_warehouse, - to_warehouse=target_warehouse, - ) - - pr.submit() - - frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 0) - def test_internal_pr_gl_entries(self): from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt @@ -1869,12 +1758,13 @@ class TestPurchaseReceipt(FrappeTestCase): # Step - 3: Create back-date Stock Reconciliation [After DN and Before PR] create_stock_reconciliation( - item_code=item, + item_code=item.name, warehouse=target_warehouse, qty=10, rate=50, company=company, posting_date=add_days(today(), -1), + expense_account="Stock Adjustment - TCP1", ) warehouse_account = get_warehouse_account_map(company) @@ -1901,7 +1791,6 @@ class TestPurchaseReceipt(FrappeTestCase): # Value of Stock Account should be equal to the sum of Stock Value Difference self.assertEqual(stock_account_value, stock_diff) ->>>>>>> c86c543fbf (test: add test case for internal PR GL Entries) def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From d215a857477d26d82b1423f9a9cc07e9ffa6385d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 21 Apr 2023 13:24:07 +0530 Subject: [PATCH 06/16] fix: Advance payment against payment terms (#34872) * fix: Advance payment against payment terms (#34872) (cherry picked from commit 5c75894065cd0d20be33eeed43a8f0d6e9ed3656) # Conflicts: # erpnext/accounts/doctype/payment_entry/payment_entry.py * chore: resolve conflicts * chore: fix tests --------- Co-authored-by: Deepesh Garg --- .../accounts_settings/accounts_settings.json | 3 +- .../doctype/payment_entry/payment_entry.py | 7 +++- erpnext/controllers/accounts_controller.py | 34 +++++++++++-------- erpnext/public/js/controllers/transaction.js | 4 +-- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 07b4318a9dc..4dd503097bc 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -175,6 +175,7 @@ }, { "default": "0", + "description": "Payment Terms from orders will be fetched into the invoices as is", "fieldname": "automatically_fetch_payment_terms", "fieldtype": "Check", "label": "Automatically Fetch Payment Terms from Order" @@ -299,7 +300,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-03-28 09:50:20.375233", + "modified": "2023-04-14 17:22:03.680886", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 44b8dbe5326..8634a47c6d8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1917,7 +1917,12 @@ def get_payment_entry( if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked(): frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date)) else: - if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_value( + if doc.doctype in ( + "Sales Invoice", + "Purchase Invoice", + "Purchase Order", + "Sales Order", + ) and frappe.get_value( "Payment Terms Template", {"name": doc.payment_terms_template}, "allocate_payment_based_on_payment_terms", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ef12483baac..8c9063feac1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -256,8 +256,8 @@ class AccountsController(TransactionBase): self.validate_payment_schedule_dates() self.set_due_date() self.set_payment_schedule() - self.validate_payment_schedule_amount() if not self.get("ignore_default_payment_terms_template"): + self.validate_payment_schedule_amount() self.validate_due_date() self.validate_advance_entries() @@ -1577,6 +1577,7 @@ class AccountsController(TransactionBase): base_grand_total = self.get("base_rounded_total") or self.base_grand_total grand_total = self.get("rounded_total") or self.grand_total + automatically_fetch_payment_terms = 0 if self.doctype in ("Sales Invoice", "Purchase Invoice"): base_grand_total = base_grand_total - flt(self.base_write_off_amount) @@ -1622,19 +1623,20 @@ class AccountsController(TransactionBase): ) self.append("payment_schedule", data) - for d in self.get("payment_schedule"): - if d.invoice_portion: - d.payment_amount = flt( - grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount") - ) - d.base_payment_amount = flt( - base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount") - ) - d.outstanding = d.payment_amount - elif not d.invoice_portion: - d.base_payment_amount = flt( - d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount") - ) + if not automatically_fetch_payment_terms: + for d in self.get("payment_schedule"): + if d.invoice_portion: + d.payment_amount = flt( + grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount") + ) + d.base_payment_amount = flt( + base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount") + ) + d.outstanding = d.payment_amount + elif not d.invoice_portion: + d.base_payment_amount = flt( + d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount") + ) def get_order_details(self): if self.doctype == "Sales Invoice": @@ -1687,6 +1689,10 @@ class AccountsController(TransactionBase): "invoice_portion": schedule.invoice_portion, "mode_of_payment": schedule.mode_of_payment, "description": schedule.description, + "payment_amount": schedule.payment_amount, + "base_payment_amount": schedule.base_payment_amount, + "outstanding": schedule.outstanding, + "paid_amount": schedule.paid_amount, } if schedule.discount_type == "Percentage": diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 972e6b1516c..8eb5f3e40da 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2011,7 +2011,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, prompt_user_for_reference_date(){ - var me = this; + let me = this; frappe.prompt({ label: __("Cheque/Reference Date"), fieldname: "reference_date", @@ -2038,7 +2038,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ let has_payment_schedule = this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length; if(!is_eligible || !has_payment_schedule) return false; - let has_discount = this.frm.doc.payment_schedule.some(row => row.discount_date); + let has_discount = this.frm.doc.payment_schedule.some(row => row.discount); return has_discount; }, From 91b5a335648c3992ab9de3f2df46cf9649d8cca2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 21 Apr 2023 16:49:48 +0530 Subject: [PATCH 07/16] fix: validation for internal transfer entry (cherry picked from commit 19911b48fdb89ca943ce1e5131eb465ebe12547c) # Conflicts: # erpnext/accounts/doctype/sales_invoice/sales_invoice.js --- .../doctype/sales_invoice/sales_invoice.js | 5 ++ erpnext/controllers/accounts_controller.py | 11 +++- .../purchase_receipt/test_purchase_receipt.py | 58 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index a624c638c36..92c9b850579 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -315,7 +315,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte } }, +<<<<<<< HEAD make_inter_company_invoice: function() { +======= + make_inter_company_invoice() { + let me = this; +>>>>>>> 19911b48fd (fix: validation for internal transfer entry) frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_inter_company_purchase_invoice", frm: me.frm diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8c9063feac1..8bcd1c3a2d4 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -5,7 +5,7 @@ import json import frappe -from frappe import _, throw +from frappe import _, bold, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied from frappe.query_builder.functions import Sum from frappe.utils import ( @@ -388,6 +388,15 @@ class AccountsController(TransactionBase): msg += _("Please create purchase from internal sale or delivery document itself") frappe.throw(msg, title=_("Internal Sales Reference Missing")) + label = "Delivery Note Item" if self.doctype == "Purchase Receipt" else "Sales Invoice Item" + + field = frappe.scrub(label) + + for row in self.get("items"): + if not row.get(field): + msg = f"At Row {row.idx}: The field {bold(label)} is mandatory for internal transfer" + frappe.throw(_(msg), title=_("Internal Transfer Reference Missing")) + def disable_pricing_rule_on_internal_transfer(self): if not self.get("ignore_pricing_rule") and self.is_internal_transfer(): self.ignore_pricing_rule = 1 diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 91755e61697..197d4278e94 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1791,6 +1791,64 @@ class TestPurchaseReceipt(FrappeTestCase): # Value of Stock Account should be equal to the sum of Stock Value Difference self.assertEqual(stock_account_value, stock_diff) + def test_internal_pr_reference(self): + item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100}) + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + from_warehouse = create_warehouse("_Test Internal From Warehouse New 1", company=company) + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New 1", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New 1", company=company) + + # Step 2: Create Stock Entry (Material Receipt) + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + make_stock_entry( + purpose="Material Receipt", + item_code=item.name, + qty=15, + company=company, + to_warehouse=from_warehouse, + ) + + # Step 3: Create Delivery Note with Internal Customer + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + dn = create_delivery_note( + item_code=item.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=10, + rate=100, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + # Step 4: Create Internal Purchase Receipt + from erpnext.controllers.status_updater import OverAllowanceError + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + + pr = make_inter_company_purchase_receipt(dn.name) + pr.inter_company_reference = "" + self.assertRaises(frappe.ValidationError, pr.save) + + pr.inter_company_reference = dn.name + pr.items[0].qty = 10 + pr.items[0].from_warehouse = target_warehouse + pr.items[0].warehouse = to_warehouse + pr.items[0].rejected_warehouse = from_warehouse + pr.save() + + delivery_note_item = pr.items[0].delivery_note_item + pr.items[0].delivery_note_item = "" + + self.assertRaises(frappe.ValidationError, pr.save) + + pr.load_from_db() + pr.items[0].delivery_note_item = delivery_note_item + pr.save() + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From 82b46f2bfe47cc95015f821fac3d451e22dc48a4 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 21 Apr 2023 17:52:11 +0530 Subject: [PATCH 08/16] chore: fix conflicts --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 92c9b850579..451b405e00c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -315,12 +315,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte } }, -<<<<<<< HEAD make_inter_company_invoice: function() { -======= - make_inter_company_invoice() { let me = this; ->>>>>>> 19911b48fd (fix: validation for internal transfer entry) frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_inter_company_purchase_invoice", frm: me.frm From c1187bed26f227764c3de5b8b87792109580bf35 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 21 Apr 2023 18:05:29 +0530 Subject: [PATCH 09/16] fix: SLA permissions (#34981) (cherry picked from commit ac871797b28d87a09c5ca26e618fdf01b087f9de) # Conflicts: # erpnext/support/doctype/service_level_agreement/service_level_agreement.json --- .../service_level_agreement.json | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 1678f04def8..c48d114a821 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -198,7 +198,11 @@ } ], "links": [], +<<<<<<< HEAD "modified": "2021-07-27 11:16:45.596579", +======= + "modified": "2023-04-21 17:16:56.192560", +>>>>>>> ac871797b2 (fix: SLA permissions (#34981)) "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", @@ -217,19 +221,12 @@ "write": 1 }, { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, "read": 1, - "report": 1, - "role": "All", - "share": 1, - "write": 1 + "role": "All" } ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } From cfa1a2b05085369536ed68074175efb9d142c40a Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sat, 22 Apr 2023 10:08:26 +0530 Subject: [PATCH 10/16] chore: conflicts --- .../service_level_agreement/service_level_agreement.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index c48d114a821..551d9eb68e4 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -198,11 +198,7 @@ } ], "links": [], -<<<<<<< HEAD - "modified": "2021-07-27 11:16:45.596579", -======= "modified": "2023-04-21 17:16:56.192560", ->>>>>>> ac871797b2 (fix: SLA permissions (#34981)) "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", From 9087ac08290c2641afb60586d1552d15544f0a62 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 13:33:51 +0530 Subject: [PATCH 11/16] fix: value of depreciable assets not updating after manual depr entry [v14] (backport #35010) (#35029) * fix: value of depreciable assets not updating after manual depr entry [v14] (#35010) * fix: update value of asset with calc_depr on after manual depr entry * fix: value of asset with calc_depr on after manual depr entry not reflecting in asset_depr_and_bal report * chore: add validation for depr journal entry * test: manual_depr_for_depreciable_asset and manual_depr_w_incorrect_jv_voucher_type * chore: unlink depreciable asset from manual depr entry (cherry picked from commit 3c75e55cb91f2c78356054b9c1ac9ea280032959) # Conflicts: # erpnext/accounts/doctype/journal_entry/journal_entry.js * chore: fixed conflicts --------- Co-authored-by: Anand Baburajan --- .../accounts/doctype/account/test_account.py | 2 +- .../doctype/journal_entry/journal_entry.js | 2 +- .../doctype/journal_entry/journal_entry.py | 71 +++++++++++-------- .../asset_depreciations_and_balances.py | 24 +------ erpnext/assets/doctype/asset/depreciation.py | 1 + erpnext/assets/doctype/asset/test_asset.py | 62 +++++++++++++++- 6 files changed, 105 insertions(+), 57 deletions(-) diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py index f9c9173af08..3a360c48c43 100644 --- a/erpnext/accounts/doctype/account/test_account.py +++ b/erpnext/accounts/doctype/account/test_account.py @@ -297,7 +297,7 @@ def _make_test_records(verbose=None): # fixed asset depreciation ["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None], ["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None], - ["_Test Depreciations", "Expenses", 0, None, None], + ["_Test Depreciations", "Expenses", 0, "Depreciation", None], ["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None], # Receivable / Payable Account ["_Test Receivable", "Current Assets", 0, "Receivable", None], diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 2808141f732..992a02cf8b3 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry"); frappe.ui.form.on("Journal Entry", { setup: function(frm) { frm.add_fetch("bank_account", "account", "account"); - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice']; + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Asset', 'Asset Movement']; }, refresh: function(frm) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 9ea93f90e03..f988222c6c2 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -75,6 +75,7 @@ class JournalEntry(AccountsController): self.validate_empty_accounts_table() self.set_account_and_party_balance() self.validate_inter_company_accounts() + self.validate_depr_entry_voucher_type() if self.docstatus == 0: self.apply_tax_withholding() @@ -134,6 +135,13 @@ class JournalEntry(AccountsController): if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit: frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry")) + def validate_depr_entry_voucher_type(self): + if ( + any(d.account_type == "Depreciation" for d in self.get("accounts")) + and self.voucher_type != "Depreciation Entry" + ): + frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation")) + def validate_stock_accounts(self): stock_accounts = get_stock_accounts(self.company, self.doctype, self.name) for account in stock_accounts: @@ -237,25 +245,30 @@ class JournalEntry(AccountsController): self.remove(d) def update_asset_value(self): - if self.voucher_type != "Depreciation Entry": + if self.flags.planned_depr_entry or self.voucher_type != "Depreciation Entry": return - processed_assets = [] - for d in self.get("accounts"): if ( - d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets + d.reference_type == "Asset" + and d.reference_name + and d.account_type == "Depreciation" + and d.debit ): - processed_assets.append(d.reference_name) - asset = frappe.get_doc("Asset", d.reference_name) if asset.calculate_depreciation: - continue - - depr_value = d.debit or d.credit - - asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value) + fb_idx = 1 + if self.finance_book: + for fb_row in asset.get("finance_books"): + if fb_row.finance_book == self.finance_book: + fb_idx = fb_row.idx + break + fb_row = asset.get("finance_books")[fb_idx - 1] + fb_row.value_after_depreciation -= d.debit + fb_row.db_update() + else: + asset.db_set("value_after_depreciation", asset.value_after_depreciation - d.debit) asset.set_status() @@ -320,35 +333,35 @@ class JournalEntry(AccountsController): if self.voucher_type != "Depreciation Entry": return - processed_assets = [] - for d in self.get("accounts"): if ( - d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets + d.reference_type == "Asset" + and d.reference_name + and d.account_type == "Depreciation" + and d.debit ): - processed_assets.append(d.reference_name) - asset = frappe.get_doc("Asset", d.reference_name) if asset.calculate_depreciation: + fb_idx = None for s in asset.get("schedules"): if s.journal_entry == self.name: s.db_set("journal_entry", None) - - idx = cint(s.finance_book_id) or 1 - finance_books = asset.get("finance_books")[idx - 1] - finance_books.value_after_depreciation += s.depreciation_amount - finance_books.db_update() - - asset.set_status() - + fb_idx = cint(s.finance_book_id) or 1 break + if not fb_idx: + fb_idx = 1 + if self.finance_book: + for fb_row in asset.get("finance_books"): + if fb_row.finance_book == self.finance_book: + fb_idx = fb_row.idx + break + fb_row = asset.get("finance_books")[fb_idx - 1] + fb_row.value_after_depreciation += d.debit + fb_row.db_update() else: - depr_value = d.debit or d.credit - - asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value) - - asset.set_status() + asset.db_set("value_after_depreciation", asset.value_after_depreciation + d.debit) + asset.set_status() def unlink_inter_company_jv(self): if ( diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 9cea37c4f80..d67eee3552d 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -114,28 +114,6 @@ def get_assets(filters): sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period from (SELECT a.asset_category, - ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then - ds.depreciation_amount - else - 0 - end), 0) as accumulated_depreciation_as_on_from_date, - ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s - and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then - ds.depreciation_amount - else - 0 - end), 0) as depreciation_eliminated_during_the_period, - ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s - and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then - ds.depreciation_amount - else - 0 - end), 0) as depreciation_amount_during_the_period - from `tabAsset` a, `tabDepreciation Schedule` ds - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' - group by a.asset_category - union - SELECT a.asset_category, ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then gle.debit else @@ -160,7 +138,7 @@ def get_assets(filters): aca.parent = a.asset_category and aca.company_name = %(company)s join `tabCompany` company on company.name = %(company)s - where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) group by a.asset_category union SELECT a.asset_category, diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index ca0cd4d978d..56d2fef2209 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -133,6 +133,7 @@ def make_depreciation_entry(asset_name, date=None): je.append("accounts", debit_entry) je.flags.ignore_permissions = True + je.flags.planned_depr_entry = True je.save() if not je.meta.get_workflow(): je.submit() diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a441856b241..0cf4d5b603a 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -1421,7 +1421,7 @@ class TestDepreciationBasics(AssetSetup): ) self.assertEqual(asset.status, "Submitted") - self.assertEqual(asset.get("value_after_depreciation"), 100000) + self.assertEqual(asset.get_value_after_depreciation(), 100000) jv = make_journal_entry( "_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False @@ -1434,12 +1434,68 @@ class TestDepreciationBasics(AssetSetup): jv.submit() asset.reload() - self.assertEqual(asset.get("value_after_depreciation"), 99900) + self.assertEqual(asset.get_value_after_depreciation(), 99900) jv.cancel() asset.reload() - self.assertEqual(asset.get("value_after_depreciation"), 100000) + self.assertEqual(asset.get_value_after_depreciation(), 100000) + + def test_manual_depreciation_for_depreciable_asset(self): + asset = create_asset( + item_code="Macbook Pro", + calculate_depreciation=1, + purchase_date="2020-01-30", + available_for_use_date="2020-01-30", + expected_value_after_useful_life=10000, + total_number_of_depreciations=10, + frequency_of_depreciation=1, + submit=1, + ) + + self.assertEqual(asset.status, "Submitted") + self.assertEqual(asset.get_value_after_depreciation(), 100000) + + jv = make_journal_entry( + "_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False + ) + for d in jv.accounts: + d.reference_type = "Asset" + d.reference_name = asset.name + jv.voucher_type = "Depreciation Entry" + jv.insert() + jv.submit() + + asset.reload() + self.assertEqual(asset.get_value_after_depreciation(), 99900) + + jv.cancel() + + asset.reload() + self.assertEqual(asset.get_value_after_depreciation(), 100000) + + def test_manual_depreciation_with_incorrect_jv_voucher_type(self): + asset = create_asset( + item_code="Macbook Pro", + calculate_depreciation=1, + purchase_date="2020-01-30", + available_for_use_date="2020-01-30", + expected_value_after_useful_life=10000, + total_number_of_depreciations=10, + frequency_of_depreciation=1, + submit=1, + ) + + jv = make_journal_entry( + "_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False + ) + for d in jv.accounts: + d.reference_type = "Asset" + d.reference_name = asset.name + d.account_type = "Depreciation" + jv.voucher_type = "Journal Entry" + + self.assertRaises(frappe.ValidationError, jv.insert) def create_asset_data(): From be2095ad03d0e2ee8ed24eb2d0a6f10b442911af Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 15:53:06 +0530 Subject: [PATCH 12/16] fix: use filter_by_finance_book instead of only_depreciable_assets in fixed asset register (backport #35031) (#35036) fix: use filter_by_finance_book instead of only_depreciable_assets in fixed asset register (#35031) fix: use filter_by_finance_book instead of only_depreciable_assets (cherry picked from commit e08d636bf7979356f60301260177e57213e84fd7) Co-authored-by: Anand Baburajan --- .../report/fixed_asset_register/fixed_asset_register.js | 6 +++--- .../report/fixed_asset_register/fixed_asset_register.py | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 65a4226ebdf..4f7b8361077 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -94,11 +94,11 @@ frappe.query_reports["Fixed Asset Register"] = { label: __("Finance Book"), fieldtype: "Link", options: "Finance Book", - depends_on: "eval: doc.only_depreciable_assets == 1", + depends_on: "eval: doc.filter_by_finance_book == 1", }, { - fieldname:"only_depreciable_assets", - label: __("Only depreciable assets"), + fieldname:"filter_by_finance_book", + label: __("Filter by Finance Book"), fieldtype: "Check" }, { 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 63f9889f054..c6fbc6c58e8 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -45,8 +45,6 @@ def get_conditions(filters): filters.year_end_date = getdate(fiscal_year.year_end_date) conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]] - if filters.get("only_depreciable_assets"): - conditions["calculate_depreciation"] = filters.get("only_depreciable_assets") if filters.get("only_existing_assets"): conditions["is_existing_asset"] = filters.get("only_existing_assets") if filters.get("asset_category"): @@ -106,7 +104,7 @@ def get_data(filters): assets_linked_to_fb = None - if filters.only_depreciable_assets: + if filters.filter_by_finance_book: assets_linked_to_fb = frappe.db.get_all( doctype="Asset Finance Book", filters={"finance_book": filters.finance_book or ("is", "not set")}, From 189b020d228bdb1c0c589697162cf91718b2fa27 Mon Sep 17 00:00:00 2001 From: danjeremynavarro <46537526+danjeremynavarro@users.noreply.github.com> Date: Tue, 25 Apr 2023 07:23:52 -0600 Subject: [PATCH 13/16] fix: wrong qty of remaining work orders to be created when using "Create" > "Work Order" (#34726) * fix: convert asynchronous field update to synchronous * fix: wrong qty of remaining work orders to be created when using "Create" > "Work Order" --- erpnext/selling/doctype/sales_order/sales_order.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2f2a06f6fb1..fa7534aa02f 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1389,8 +1389,9 @@ def get_work_order_items(sales_order, for_raw_material_request=0): .select(Sum(wo.qty)) .where( (wo.production_item == i.item_code) - & (wo.sales_order == so.name) * (wo.sales_order_item == i.name) - & (wo.docstatus.lte(2)) + & (wo.sales_order == so.name) + & (wo.sales_order_item == i.name) + & (wo.docstatus.lt(2)) ) .run()[0][0] ) From d5a80b56153e1de256d414eb3926c782aa9b7137 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 20:49:18 +0530 Subject: [PATCH 14/16] fix: Payment Request flow fixes from Order to Payment Entry (#33350) * fix: Payment Request flow fixes from Order to Payment Entry (cherry picked from commit dc178984aefc3ae01e37989d9e7755749072ce82) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.js # erpnext/public/js/controllers/transaction.js * chore: Update test case (cherry picked from commit e25b98b620114ba233a48d3e3a402f40001281b8) * chore: More fixes (cherry picked from commit 31c95deb88b376ff86746bc07b07590465594fd0) * chore: resolve conflicts --------- Co-authored-by: Deepesh Garg --- .../doctype/payment_entry/payment_entry.py | 2 +- .../payment_request/payment_request.js | 2 +- .../payment_request/payment_request.py | 28 +++++++++---------- .../doctype/purchase_order/purchase_order.js | 2 +- erpnext/public/js/controllers/transaction.js | 6 ++-- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 8634a47c6d8..f959a4508d4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1855,7 +1855,7 @@ def get_payment_entry( ): reference_doc = None doc = frappe.get_doc(dt, dn) - if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: + if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= 99.99: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) party_type = set_party_type(dt) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index 901ef1987b4..e9139120283 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -42,7 +42,7 @@ frappe.ui.form.on("Payment Request", "refresh", function(frm) { }); } - if(!frm.doc.payment_gateway_account && frm.doc.status == "Initiated") { + if((!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") && frm.doc.status == "Initiated") { frm.add_custom_button(__('Create Payment Entry'), function(){ frappe.call({ method: "erpnext.accounts.doctype.payment_request.payment_request.make_payment_entry", diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index f05dd5ba496..178cd855061 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -254,6 +254,7 @@ class PaymentRequest(Document): payment_entry.update( { + "mode_of_payment": self.mode_of_payment, "reference_no": self.name, "reference_date": nowdate(), "remarks": "Payment Entry against {0} {1} via Payment Request {2}".format( @@ -403,25 +404,22 @@ def make_payment_request(**args): else "" ) - existing_payment_request = None - if args.order_type == "Shopping Cart": - existing_payment_request = frappe.db.get_value( - "Payment Request", - {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)}, - ) + draft_payment_request = frappe.db.get_value( + "Payment Request", + {"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": 0}, + ) - if existing_payment_request: + existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn) + + if existing_payment_request_amount: + grand_total -= existing_payment_request_amount + + if draft_payment_request: frappe.db.set_value( - "Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False + "Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False ) - pr = frappe.get_doc("Payment Request", existing_payment_request) + pr = frappe.get_doc("Payment Request", draft_payment_request) else: - if args.order_type != "Shopping Cart": - existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn) - - if existing_payment_request_amount: - grand_total -= existing_payment_request_amount - pr = frappe.new_doc("Payment Request") pr.update( { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 72329e9a22e..a16c1c5486f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -199,7 +199,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( ); } - if(flt(doc.per_billed)==0) { + if(flt(doc.per_billed) < 100) { this.frm.add_custom_button(__('Payment Request'), function() { me.make_payment_request() }, __('Create')); } diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 8eb5f3e40da..b05db329639 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -297,8 +297,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); }, - make_payment_request: function() { - var me = this; + make_payment_request() { + let me = this; const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype)) ? "Inward" : "Outward"; @@ -314,7 +314,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, callback: function(r) { if(!r.exc){ - var doc = frappe.model.sync(r.message); + frappe.model.sync(r.message); frappe.set_route("Form", r.message.doctype, r.message.name); } } From 61a3121172d3a186bd6425d70e3c6decc9403857 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 20:51:35 +0530 Subject: [PATCH 15/16] fix: Unable to allocate advance against invoice (#35007) fix: Unable to allocate advance against invoice (#35007) (cherry picked from commit f7b50f2adef11b9f2bd843d6bca3f2b6cf7be19b) Co-authored-by: Deepesh Garg --- erpnext/controllers/accounts_controller.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8bcd1c3a2d4..225c1b01c20 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1632,7 +1632,10 @@ class AccountsController(TransactionBase): ) self.append("payment_schedule", data) - if not automatically_fetch_payment_terms: + if not ( + automatically_fetch_payment_terms + and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype) + ): for d in self.get("payment_schedule"): if d.invoice_portion: d.payment_amount = flt( From 5f28b1d3305d59478c78d91f6b23eb7f715de38e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Apr 2023 21:13:42 +0530 Subject: [PATCH 16/16] fix: Payment entry with TDS in bank reco statement (#34961) fix: Payment entry with TDS in bank reco statement (#34961) (cherry picked from commit ecea9b44a339fcfb261696118add5873a4d625fb) Co-authored-by: Deepesh Garg --- erpnext/accounts/doctype/bank_clearance/bank_clearance.py | 2 +- .../report/bank_clearance_summary/bank_clearance_summary.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 98ba399a35d..a88bc3df5fe 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -56,7 +56,7 @@ class BankClearance(Document): select "Payment Entry" as payment_document, name as payment_entry, reference_no as cheque_number, reference_date as cheque_date, - if(paid_from=%(account)s, paid_amount, 0) as credit, + if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit, if(paid_from=%(account)s, 0, received_amount) as debit, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py index 449ebdcd924..173fed3edbf 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -79,7 +79,7 @@ def get_entries(filters): payment_entries = frappe.db.sql( """SELECT "Payment Entry", name, posting_date, reference_no, clearance_date, party, - if(paid_from=%(account)s, paid_amount * -1, received_amount) + if(paid_from=%(account)s, ((paid_amount * -1) - total_taxes_and_charges) , received_amount) FROM `tabPayment Entry` WHERE