From 08f47b626cfe8fb34bd51b5d32e8fd7892bfdeca Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 12 Mar 2025 14:37:29 +0000 Subject: [PATCH 01/99] chore(release): Bumped to Version 15.54.4 ## [15.54.4](https://github.com/frappe/erpnext/compare/v15.54.3...v15.54.4) (2025-03-12) ### Bug Fixes * `base_net_rate` Required to Check Valid Range (backport [#46332](https://github.com/frappe/erpnext/issues/46332)) ([#46382](https://github.com/frappe/erpnext/issues/46382)) ([877d5bd](https://github.com/frappe/erpnext/commit/877d5bd3aa70881cc394870245f10973041dd5c1)) * **account:** update account number from parent company ([428aedc](https://github.com/frappe/erpnext/commit/428aedc29c599c076a53c2c083ad8ef3a89b05c5)) * Allow rename prospect doctype ([#46352](https://github.com/frappe/erpnext/issues/46352)) ([de46165](https://github.com/frappe/erpnext/commit/de46165768c46724a58a0fe4d80a30f93f1ca1d3)) * auto email report creation ([#46343](https://github.com/frappe/erpnext/issues/46343)) ([5cc251a](https://github.com/frappe/erpnext/commit/5cc251a172a7d09451d3fdd2d11c06a33f138e73)) * backport translations from develop ([#46428](https://github.com/frappe/erpnext/issues/46428)) ([9c70376](https://github.com/frappe/erpnext/commit/9c703765a1b6d0ccdfbe95f28667d20e3c6c53e9)) * calculate due date based on payment term (backport [#46416](https://github.com/frappe/erpnext/issues/46416)) ([#46479](https://github.com/frappe/erpnext/issues/46479)) ([7f14744](https://github.com/frappe/erpnext/commit/7f147446df1382fe73946906c9f20c9d82b3bc04)) * change fieldname for cash_flow to export (backport [#46353](https://github.com/frappe/erpnext/issues/46353)) ([#46366](https://github.com/frappe/erpnext/issues/46366)) ([23c4252](https://github.com/frappe/erpnext/commit/23c4252b9beefbd2cf904d80a647b2eec8aaa8c7)) * check if set_landed_cost_based_on_purchase_invoice_rate is enabled before running patch ([7047fe2](https://github.com/frappe/erpnext/commit/7047fe26812fbb48d80e3a24f438a9c1c5d8683f)) * clear cashe on employee hierarchy change to reflect updated permissions ([4dfdb2b](https://github.com/frappe/erpnext/commit/4dfdb2b0a1d698fa45d6487d30feab61c3e66b57)) * consider account freeze date in recalculate_amount_difference_field patch ([8b67527](https://github.com/frappe/erpnext/commit/8b67527900df9ea6279451bc887eef7d88ac4f60)) * consider stock freeze date in recalculate_amount_difference_field patch ([8264d42](https://github.com/frappe/erpnext/commit/8264d42cd9083f71752a303fded52d698d819c57)) * credit note creation during pos invoice consolidation (backport [#46277](https://github.com/frappe/erpnext/issues/46277)) ([#46469](https://github.com/frappe/erpnext/issues/46469)) ([a4b8b4c](https://github.com/frappe/erpnext/commit/a4b8b4c7714a0b16c56f9066dbebab48f822a76d)) * do not recalculate qty for batch items during reposting ([bac36f3](https://github.com/frappe/erpnext/commit/bac36f342d58c3ef902edc1b28284dbcb2938961)) * doctype name ([1dcbdf3](https://github.com/frappe/erpnext/commit/1dcbdf325766316e4febaf265475c419c528a7bc)) * enable no copy for serial no field ([3f9df2f](https://github.com/frappe/erpnext/commit/3f9df2fb2d63466f13d14343c69483022fd231a1)) * error in production analytics report ([db6ae61](https://github.com/frappe/erpnext/commit/db6ae61935e90c2e76667be8101ef49f6875ba22)) * error when creating delivery note from pick list ([#46417](https://github.com/frappe/erpnext/issues/46417)) ([0b92101](https://github.com/frappe/erpnext/commit/0b921016ffdd4639987a3c82bd2dadff99a730b9)) * filter batches that going to be zero ([ac97489](https://github.com/frappe/erpnext/commit/ac97489a32deb33442645bd32a92f821eaa9fb75)) * incorrect category in list ([002685f](https://github.com/frappe/erpnext/commit/002685fc892eca2a2a3fd49c7bf277dd9b7f84b0)) * make 'company_tax_id' and 'company_fiscal_code' as mandatory ([229f4d3](https://github.com/frappe/erpnext/commit/229f4d3d92e9db12afc2ed6c5d99cadc304dc4f0)) * not able to save work order with alternative item ([9554a49](https://github.com/frappe/erpnext/commit/9554a49bbd97798b4d688741a1ca633f02ee280e)) * **payment entry:** fetch default bank account based on company (backport [#46379](https://github.com/frappe/erpnext/issues/46379)) ([#46471](https://github.com/frappe/erpnext/issues/46471)) ([1371199](https://github.com/frappe/erpnext/commit/13711993fe3687db53579eedb6875355f9d2b74c)) * pricing rule not ignored in Sales Order ([#46248](https://github.com/frappe/erpnext/issues/46248)) ([8def42f](https://github.com/frappe/erpnext/commit/8def42f751ace7659c9912270481ddb78aabd310)) * rare precision issue preventing submission of subcontracting order ([6419d02](https://github.com/frappe/erpnext/commit/6419d020a1289ab08142ff94bfc94e83cfce7548)) * recalculate_amount_difference_field patch ([f247f02](https://github.com/frappe/erpnext/commit/f247f02e49e5bb22585e81e3c687e052ff441361)) * remove no copy for serial no field of purchase receipt item ([baa564f](https://github.com/frappe/erpnext/commit/baa564fc94c717c17d0cefd7e6bf66003f9d4c59)) * rename sla fields patch ([73f11cf](https://github.com/frappe/erpnext/commit/73f11cf19e98d382e4361686bd505fa16e067f2a)) * rename sla fields patch ([#46465](https://github.com/frappe/erpnext/issues/46465)) ([5edbd88](https://github.com/frappe/erpnext/commit/5edbd8851ab6b75cc591abe0a46cb1af3c34f0be)) * rename_sla_fields patch ([7bc7557](https://github.com/frappe/erpnext/commit/7bc7557018bb7764b77e6892b3ffd21f9128437e)) * run bank reconciliation as a background job to prevent request timeout ([739cd18](https://github.com/frappe/erpnext/commit/739cd18604c44fd614c09a936f3a44e512175105)) * set correct account currency for deferred expence account in PI ([f96848a](https://github.com/frappe/erpnext/commit/f96848a3b9c02997a680161327919ab0b841b21d)) * show remaining qty on 'Complete Job' button instead of full qty ([79e6550](https://github.com/frappe/erpnext/commit/79e6550321fd5289f271253ed8569295bcdf96f8)) * sla fields patch ([0d044bc](https://github.com/frappe/erpnext/commit/0d044bc5bb075fc8cb333cea6ac292f04d977986)) * stock balance in and out value ([c2001e9](https://github.com/frappe/erpnext/commit/c2001e9c675795343f2cd03cbe4bda45f2162da9)) * **test:** incorrect transaction exchange rate in test case ([b76c968](https://github.com/frappe/erpnext/commit/b76c96820ec5ca31405e77a8d3cdc15d050bbf95)) * typo in sales_invoice_print ([b610621](https://github.com/frappe/erpnext/commit/b6106212c1470ded73cec524d7baccd72cc9f388)) * uom reverts to default upon selecting do not explode ([#45693](https://github.com/frappe/erpnext/issues/45693)) ([6b1d209](https://github.com/frappe/erpnext/commit/6b1d20970e79b33f94c16a78a301c77dedf1d136)) * validate accounting dimension company in Journal Entry & Stock Entry (backport [#46204](https://github.com/frappe/erpnext/issues/46204)) ([#46369](https://github.com/frappe/erpnext/issues/46369)) ([c816f9b](https://github.com/frappe/erpnext/commit/c816f9bd0abf065231ed053889c6220bac20477c)) * validate last_gl_update exists before comparing (backport [#46464](https://github.com/frappe/erpnext/issues/46464)) ([#46468](https://github.com/frappe/erpnext/issues/46468)) ([3cef94e](https://github.com/frappe/erpnext/commit/3cef94e2edab9abf5d27b687f86b98b688ce3faa)) * validations and account type filter for `Tax Withholding Category` ([#46207](https://github.com/frappe/erpnext/issues/46207)) ([cc30a01](https://github.com/frappe/erpnext/commit/cc30a0189833324525a038d9564e4da40d9412de)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a48e84c7257..d10fdab7afa 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.54.3" +__version__ = "15.54.4" def get_default_company(user=None): From 4d7071299e94998b632f6158689cbf7a4e64e719 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 10 Mar 2025 16:24:43 +0530 Subject: [PATCH 02/99] Revert "fix: Show Credit Note amount in credit note column" (cherry picked from commit 5a9767ca67217a962c99332180aa17afa2e734dc) --- .../accounts_payable/test_accounts_payable.py | 17 ----------------- .../accounts_receivable/accounts_receivable.py | 16 ++-------------- .../test_accounts_receivable.py | 12 +++--------- 3 files changed, 5 insertions(+), 40 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index 69f332d9800..8971dc3d37b 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -38,23 +38,6 @@ class TestAccountsPayable(AccountsTestMixin, FrappeTestCase): self.assertEqual(data[1][0].get("outstanding"), 300) self.assertEqual(data[1][0].get("currency"), "USD") - def test_account_payable_for_debit_note(self): - pi = self.create_purchase_invoice(do_not_submit=True) - pi.is_return = 1 - pi.items[0].qty = -1 - pi = pi.save().submit() - - filters = { - "company": self.company, - "party_type": "Supplier", - "party": [self.supplier], - "report_date": today(), - "range": "30, 60, 90, 120", - } - - data = execute(filters) - self.assertEqual(data[1][0].get("invoiced"), 300) - def create_purchase_invoice(self, do_not_submit=False): frappe.set_user("Administrator") pi = make_purchase_invoice( diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index c7a0da5afe9..1ddf9bce06f 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -267,18 +267,6 @@ class ReceivablePayableReport: row.invoiced_in_account_currency += amount_in_account_currency else: if self.is_invoice(ple): - # when invoice has is_return marked - if self.invoice_details.get(row.voucher_no, {}).get("is_return"): - # for Credit Note - if row.voucher_type == "Sales Invoice": - row.credit_note -= amount - row.credit_note_in_account_currency -= amount_in_account_currency - # for Debit Note - else: - row.invoiced -= amount - row.invoiced_in_account_currency -= amount_in_account_currency - return - if row.voucher_no == ple.voucher_no == ple.against_voucher_no: row.paid -= amount row.paid_in_account_currency -= amount_in_account_currency @@ -433,7 +421,7 @@ class ReceivablePayableReport: # nosemgrep si_list = frappe.db.sql( """ - select name, due_date, po_no, is_return + select name, due_date, po_no from `tabSales Invoice` where posting_date <= %s and company = %s @@ -465,7 +453,7 @@ class ReceivablePayableReport: # nosemgrep for pi in frappe.db.sql( """ - select name, due_date, bill_no, bill_date, is_return + select name, due_date, bill_no, bill_date from `tabPurchase Invoice` where posting_date <= %s diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index f3513286c9e..39ca78153c3 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -204,7 +204,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): expected_data_after_credit_note = [ [100.0, 100.0, 40.0, 0.0, 60.0, si.name], - [0, 0, 0, 100.0, -100.0, cr_note.name], + [0, 0, 100.0, 0.0, -100.0, cr_note.name], ] self.assertEqual(len(report[1]), 2) si_row = next( @@ -478,19 +478,13 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): report = execute(filters)[1] self.assertEqual(len(report), 2) - expected_data = {sr.name: [0.0, 10.0, -10.0, 0.0, -10], si.name: [100.0, 0.0, 100.0, 10.0, 90.0]} + expected_data = {sr.name: [10.0, -10.0, 0.0, -10], si.name: [100.0, 100.0, 10.0, 90.0]} rows = report[:2] for row in rows: self.assertEqual( expected_data[row.voucher_no], - [ - row.invoiced or row.paid, - row.credit_note, - row.outstanding, - row.remaining_balance, - row.future_amount, - ], + [row.invoiced or row.paid, row.outstanding, row.remaining_balance, row.future_amount], ) pe.cancel() From 35ac96f1ec09de17916c447e172e8464f3c160be Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 19 Mar 2025 11:18:07 +0000 Subject: [PATCH 03/99] chore(release): Bumped to Version 15.54.5 ## [15.54.5](https://github.com/frappe/erpnext/compare/v15.54.4...v15.54.5) (2025-03-19) ### Bug Fixes * add parenttype condition to payment schedule query in accounts receivable report (backport [#46370](https://github.com/frappe/erpnext/issues/46370)) ([#46499](https://github.com/frappe/erpnext/issues/46499)) ([32335da](https://github.com/frappe/erpnext/commit/32335da8398876fde80da12e3079ad3a23de6d0e)) * add validation to rename_subcontracting_fields patch ([bc408d9](https://github.com/frappe/erpnext/commit/bc408d979a0ea7fbc3c1bacc6200bb8197233ddb)) * also consider CRM Deal as party type for ERPNext CRM Integration ([65a80cf](https://github.com/frappe/erpnext/commit/65a80cffe7082fe1dd13fd124228a8a66411e326)) * dashboard link for QC from PR ([426222d](https://github.com/frappe/erpnext/commit/426222d8e000f028e4a31165c4f07488db26fbfe)) * Debit and Credit not equal for Purchase Invoice ([46b6e62](https://github.com/frappe/erpnext/commit/46b6e621c2e4384e3650d7d3b32efcc0a5db3edc)) * debit in transaction currency ([8e19b46](https://github.com/frappe/erpnext/commit/8e19b46bd93f4353f4922c628ccfc9de8b83c94c)) * ensure qty conversion when creating production plan from SO ([8162fb3](https://github.com/frappe/erpnext/commit/8162fb3e5d3beeae5bc0b3d0ecc1e1e0686da20e)) * exclude current doc when checking for duplicate ([b638aed](https://github.com/frappe/erpnext/commit/b638aed7588b2237648a6c109819e6dfb489c99e)) * fetch bom_no when updating items in sales order ([41d8b26](https://github.com/frappe/erpnext/commit/41d8b26dd247858271d2994be348ede8709f1f67)) * fetch quality inspection parameter group ([cd0abba](https://github.com/frappe/erpnext/commit/cd0abbae51049a9603f51367364b326073897f5b)) * get bom_no from sales order item and material request item ([e241810](https://github.com/frappe/erpnext/commit/e2418101aba602123735d81053c7c3f374243058)) * hide subcontracted qty field if PO is not subcontracted ([62feec5](https://github.com/frappe/erpnext/commit/62feec5cc3b1d108717bca1590b65ca98cae915b)) * incorrect production item and bom no in job card ([d071a6c](https://github.com/frappe/erpnext/commit/d071a6c9001303d7a6c5eef600fd29e170e0b0b1)) * not able to make PR against stand alone Debit Note ([d62960e](https://github.com/frappe/erpnext/commit/d62960e9253d6b1d0aec57e257f60f10c3ea2f14)) * not able to select the item in the BOM ([59c653e](https://github.com/frappe/erpnext/commit/59c653ef3fcce994b5840619cb4fc90b80587bfe)) * patch ([36ffc2e](https://github.com/frappe/erpnext/commit/36ffc2ee67096d726933b6f54ee3f6448593ee69)) * performance issue for item list view ([34d6e4b](https://github.com/frappe/erpnext/commit/34d6e4bdaa5780da8d8432ac0ed9b55008a78baf)) * remove duplicate ([e5b2801](https://github.com/frappe/erpnext/commit/e5b28018304ecb4e50dc5392f0f2332e4f5486b4)) * repost future sle and gle after capitalization ([#46576](https://github.com/frappe/erpnext/issues/46576)) ([2144f89](https://github.com/frappe/erpnext/commit/2144f8962478840729218c5aa65934a70e414d03)) * SABB validation for packed items ([2d6626e](https://github.com/frappe/erpnext/commit/2d6626e9063489b7d46a5cdf5952e93580b41782)) * set correct currency for offset account gl entries ([e6dd3f3](https://github.com/frappe/erpnext/commit/e6dd3f3e647b59d04f83aba7ddd0a760c7f13896)) * set landed cost based on purchase invoice rate ([56bc26a](https://github.com/frappe/erpnext/commit/56bc26aeccf8a208dd7adf4607c364acd1e2fb48)) * set stock adjustment account in difference account ([6202e30](https://github.com/frappe/erpnext/commit/6202e302b1f15809be9bd1be9238fa1eae71807f)) * take function call outside loop ([ec1a3a1](https://github.com/frappe/erpnext/commit/ec1a3a1e6b4fcecda6e89abb46dca065cc33fdef)) * **Transaction Deletion Record:** sql syntax error while fetching lead address ([ea68cae](https://github.com/frappe/erpnext/commit/ea68caec7d1c4210f1111b19baa93408b7e3c3a9)) * UOM conversion error when creating pick list from material transfer request ([2f3dcc2](https://github.com/frappe/erpnext/commit/2f3dcc2137ab9b122992c40f8faa07c8361b151d)) * use base currency total ([3e2749d](https://github.com/frappe/erpnext/commit/3e2749d6d516e9686567bdce79f77309b2a40c07)) * use party explicitly ([5dd5784](https://github.com/frappe/erpnext/commit/5dd5784716e4ff5a2d8b82ee0f3750dd5e2defb9)) * use shipping_address_name for address validation in sales invoice ([#46473](https://github.com/frappe/erpnext/issues/46473)) ([38dabdf](https://github.com/frappe/erpnext/commit/38dabdf584c54b7a5bb1a7b57f4dc0278bb186ec)) * using `in` for lookup in list instead of directly assigning ([#46492](https://github.com/frappe/erpnext/issues/46492)) ([950656d](https://github.com/frappe/erpnext/commit/950656d6f7b5350059a28a1f31285b1879141475)) * valuation for moving average with batches ([5f1bb1f](https://github.com/frappe/erpnext/commit/5f1bb1f1ba12671bb2e04ce80ce8b6b706183d9c)) * wrong field mapping ([be3e083](https://github.com/frappe/erpnext/commit/be3e083e7d07ef460ddfe7918511b5f5ac6d6b1a)) ### Performance Improvements * faster count estimation (backport [#46550](https://github.com/frappe/erpnext/issues/46550)) ([#46551](https://github.com/frappe/erpnext/issues/46551)) ([01bab8f](https://github.com/frappe/erpnext/commit/01bab8f22be42a1ff2c5237c186a2d860f5b5ca8)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d10fdab7afa..253da831d03 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.54.4" +__version__ = "15.54.5" def get_default_company(user=None): From 8951efb457f47b4891fbec1ed39d65c769872125 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 25 Mar 2025 13:50:33 +0000 Subject: [PATCH 04/99] chore(release): Bumped to Version 15.55.0 # [15.55.0](https://github.com/frappe/erpnext/compare/v15.54.5...v15.55.0) (2025-03-25) ### Bug Fixes * add base_outstanding and base_paid_amount in payment schedule table ([412e6be](https://github.com/frappe/erpnext/commit/412e6be5025d5d451902ced7848f9602ae1786ab)) * add patch to update base_outstanding and base_paid_amount ([c3221c4](https://github.com/frappe/erpnext/commit/c3221c4e9319ba1e960d8aa272484a4c629d96dd)) * correct accumulated depreciation calculation for disposed assets (backport [#46660](https://github.com/frappe/erpnext/issues/46660)) ([#46661](https://github.com/frappe/erpnext/issues/46661)) ([4df5f18](https://github.com/frappe/erpnext/commit/4df5f18d850a2d4b9f595825c1290b3392ba1778)) * correct invoice order in payment reconcillaiton ([2a70791](https://github.com/frappe/erpnext/commit/2a70791bbaeaff33f469cf3014932367c8762059)) * customer credit limit check based on `bypass_credit_limit_check` in Journal Entry ([6c443bd](https://github.com/frappe/erpnext/commit/6c443bd85aba5cc8e911219da4924ab63223d2ed)) * date added to wrong patch ([2bfaf64](https://github.com/frappe/erpnext/commit/2bfaf64fffed8e9e7eeb43c3f35175bc068c8fd1)) * do not validate if conversion rate is 1 for different currency ([391b5c4](https://github.com/frappe/erpnext/commit/391b5c4226616d3e2194fbf93a496b48383a0013)) * don't filter payment entries on Bank Account in Payment Clearance ([dc3b5e2](https://github.com/frappe/erpnext/commit/dc3b5e2f3af271640715ac97e326272471bcb93d)) * **Payment Entry:** get contact details from existing contact ([#40556](https://github.com/frappe/erpnext/issues/40556)) ([f964178](https://github.com/frappe/erpnext/commit/f964178008a961ed4cd42d74e46e27cc9ce6c738)) * unwired order_by argument in get_transaction_list (backport [#46636](https://github.com/frappe/erpnext/issues/46636)) ([#46643](https://github.com/frappe/erpnext/issues/46643)) ([2ebea88](https://github.com/frappe/erpnext/commit/2ebea8866a54da4e09babd58b9c6f306e5c2ecf9)) ### Features * **accounting:** allow chart_of_account.get_chart to be whilelist ([e69c722](https://github.com/frappe/erpnext/commit/e69c7225342c8db4653aefae99e043c5f9af4e14)) * **projects:** add option to hide timesheets for project users ([#46173](https://github.com/frappe/erpnext/issues/46173)) ([3834d6f](https://github.com/frappe/erpnext/commit/3834d6fbce4a52f356c36a1915566aa7a43d99be)) * repost accounting ledger for purchase receipt ([4edfc6f](https://github.com/frappe/erpnext/commit/4edfc6f1258c7d98db125894338e951228f94cc1)) ### Performance Improvements * timeout while renaming cost center ([58eb184](https://github.com/frappe/erpnext/commit/58eb1849d79a4d1721621f26982f0145cae3f0e3)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 253da831d03..92c410b9009 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.54.5" +__version__ = "15.55.0" def get_default_company(user=None): From e393ce9a4741a90b2c10d5c1953522a51a8d7c90 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 25 Mar 2025 13:41:27 +0530 Subject: [PATCH 05/99] fix: expense account in the stock entry (cherry picked from commit 89569d4b324b0af109efdc3776f450ddc9a1fd61) (cherry picked from commit 62f342ef8b09abe821fbe8deebbd68054ee7fd85) --- erpnext/stock/doctype/stock_entry/stock_entry.js | 6 ------ erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 635fd1a1fcf..0c619b22a33 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1026,10 +1026,6 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle }; }); - if (me.frm.doc.company && erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) { - this.frm.add_fetch("company", "stock_adjustment_account", "expense_account"); - } - this.frm.fields_dict.items.grid.get_field("expense_account").get_query = function () { if (erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) { return { @@ -1143,8 +1139,6 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle this.frm.trigger("toggle_display_account_head"); erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); - if (this.frm.doc.company && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) - this.set_default_account("stock_adjustment_account", "expense_account"); this.set_default_account("cost_center", "cost_center"); this.frm.refresh_fields("items"); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 3f548a1324d..52655c9ac03 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1672,7 +1672,7 @@ class StockEntry(StockController): if self.purpose == "Material Issue": ret["expense_account"] = item.get("expense_account") or item_group_defaults.get("expense_account") - if self.purpose == "Manufacture": + if self.purpose == "Manufacture" or not ret.get("expense_account"): ret["expense_account"] = frappe.get_cached_value( "Company", self.company, "stock_adjustment_account" ) From 1b6aeba26779a4591649326d95e9814e2008ac7a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 26 Mar 2025 15:20:12 +0530 Subject: [PATCH 06/99] fix: decimal values causing incorrect batch picking (cherry picked from commit 7bfe703b04646f52132561ba1ba336af688fe524) (cherry picked from commit c5efddae16ae0f31088ab84cb8e15996a37e654a) --- erpnext/controllers/subcontracting_controller.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index a672360046c..739e64fa2eb 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -545,7 +545,11 @@ class SubcontractingController(StockController): def __get_batch_nos_for_bundle(self, qty, key): available_batches = defaultdict(float) + precision = frappe.get_precision("Subcontracting Receipt Supplied Item", "consumed_qty") for batch_no, batch_qty in self.available_materials[key]["batch_no"].items(): + if flt(batch_qty, precision) <= 0: + continue + qty_to_consumed = 0 if qty > 0: if batch_qty >= qty: From f3ba5a81abf4683c5a93c2b0ff1b2be3f97840da Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 26 Mar 2025 21:50:18 +0530 Subject: [PATCH 07/99] fix: slow query (cherry picked from commit 5ddb36af8725108d257e95b68a2994532c605589) # Conflicts: # erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json (cherry picked from commit af0fb131a22c07d82802959bcc2c88189146af20) --- erpnext/stock/deprecated_serial_batch.py | 4 ---- .../stock_entry_detail/stock_entry_detail.json | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index 359027ec6be..f084c324745 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -252,7 +252,6 @@ class DeprecatedBatchNoValuation: from erpnext.stock.utils import get_combine_datetime sle = frappe.qb.DocType("Stock Ledger Entry") - batch = frappe.qb.DocType("Batch") posting_datetime = get_combine_datetime(self.sle.posting_date, self.sle.posting_time) if not self.sle.creation: @@ -267,8 +266,6 @@ class DeprecatedBatchNoValuation: query = ( frappe.qb.from_(sle) - .inner_join(batch) - .on(sle.batch_no == batch.name) .select( sle.stock_value, sle.qty_after_transaction, @@ -276,7 +273,6 @@ class DeprecatedBatchNoValuation: .where( (sle.item_code == self.sle.item_code) & (sle.warehouse == self.sle.warehouse) - & (sle.batch_no.isnotnull()) & (sle.is_cancelled == 0) ) .where(timestamp_condition) diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 48fc31ab663..dec8d575077 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -250,6 +250,7 @@ }, { "depends_on": "eval:doc.uom != doc.stock_uom", + "fetch_from": "item_code.stock_uom", "fieldname": "stock_uom", "fieldtype": "Link", "label": "Stock UOM", @@ -588,7 +589,8 @@ "label": "Serial and Batch Bundle", "no_copy": 1, "options": "Serial and Batch Bundle", - "print_hide": 1 + "print_hide": 1, + "search_index": 1 }, { "default": "0", @@ -606,18 +608,28 @@ "fieldtype": "Column Break" } ], + "grid_page_length": 50, "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2024-02-25 15:58:40.982582", +======= + "modified": "2025-03-26 21:00:58.544797", +>>>>>>> 5ddb36af87 (fix: slow query) "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", "naming_rule": "Random", "owner": "Administrator", "permissions": [], +<<<<<<< HEAD "sort_field": "modified", +======= + "row_format": "Dynamic", + "sort_field": "creation", +>>>>>>> 5ddb36af87 (fix: slow query) "sort_order": "ASC", "states": [] -} \ No newline at end of file +} From ad3f985dc41ebf7309c7e4890545b62829c32ea1 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 26 Mar 2025 22:11:43 +0530 Subject: [PATCH 08/99] chore: fix conflicts (cherry picked from commit 41f20a9c642ab3a634b4c0fc1b8ecf194b019e56) --- .../stock_entry_detail/stock_entry_detail.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index dec8d575077..9824911404e 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -613,23 +613,14 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2024-02-25 15:58:40.982582", -======= - "modified": "2025-03-26 21:00:58.544797", ->>>>>>> 5ddb36af87 (fix: slow query) + "modified": "2025-03-26 21:01:58.544797", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", "naming_rule": "Random", "owner": "Administrator", "permissions": [], -<<<<<<< HEAD "sort_field": "modified", -======= - "row_format": "Dynamic", - "sort_field": "creation", ->>>>>>> 5ddb36af87 (fix: slow query) "sort_order": "ASC", "states": [] } From 9bac43acffcb0645ffa9172713d40ab56af73af2 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 27 Mar 2025 03:45:26 +0000 Subject: [PATCH 09/99] chore(release): Bumped to Version 15.55.1 ## [15.55.1](https://github.com/frappe/erpnext/compare/v15.55.0...v15.55.1) (2025-03-27) ### Bug Fixes * decimal values causing incorrect batch picking ([1b6aeba](https://github.com/frappe/erpnext/commit/1b6aeba26779a4591649326d95e9814e2008ac7a)) * expense account in the stock entry ([e393ce9](https://github.com/frappe/erpnext/commit/e393ce9a4741a90b2c10d5c1953522a51a8d7c90)) * slow query ([f3ba5a8](https://github.com/frappe/erpnext/commit/f3ba5a81abf4683c5a93c2b0ff1b2be3f97840da)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 92c410b9009..b0a44868d50 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.55.0" +__version__ = "15.55.1" def get_default_company(user=None): From 7795030b7baaa8fad39352a4d8c12fef2cba02b8 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 25 Mar 2025 17:51:41 +0530 Subject: [PATCH 10/99] fix: do not use self object for setting party and party type (cherry picked from commit 80b746d4dd7958f19ad2c3d7d95311b1934aa967) --- erpnext/controllers/accounts_controller.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e0d7e2a36df..2e90c446ade 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -443,21 +443,22 @@ class AccountsController(TransactionBase): ) def validate_party_address_and_contact(self): - party, party_type = None, None - if self.get("customer"): - party, party_type = self.customer, "Customer" + party_type, party = self.get_party() + + if not (party_type and party): + return + + if party_type == "Customer": billing_address, shipping_address = ( self.get("customer_address"), self.get("shipping_address_name"), ) self.validate_party_address(party, party_type, billing_address, shipping_address) - elif self.get("supplier"): - party, party_type = self.supplier, "Supplier" + elif party_type == "Supplier": billing_address = self.get("supplier_address") self.validate_party_address(party, party_type, billing_address) - if party and party_type: - self.validate_party_contact(party, party_type) + self.validate_party_contact(party, party_type) def validate_party_address(self, party, party_type, billing_address, shipping_address=None): if billing_address or shipping_address: From de3e6922b5820a8908094089fcd32ccdc526827c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 27 Mar 2025 06:05:55 +0000 Subject: [PATCH 11/99] chore(release): Bumped to Version 15.55.2 ## [15.55.2](https://github.com/frappe/erpnext/compare/v15.55.1...v15.55.2) (2025-03-27) ### Bug Fixes * do not use self object for setting party and party type ([7795030](https://github.com/frappe/erpnext/commit/7795030b7baaa8fad39352a4d8c12fef2cba02b8)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b0a44868d50..c6303d9c483 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.55.1" +__version__ = "15.55.2" def get_default_company(user=None): From c6ce76170babfd6f2d019b6e6c8458dd0dc96522 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 27 Mar 2025 11:58:02 +0530 Subject: [PATCH 12/99] Revert "perf: timeout while renaming cost center (backport #46641)" (cherry picked from commit 326126e741f1670f754566fd87cacf6af9d17466) --- erpnext/accounts/doctype/gl_entry/gl_entry.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index 769fbbc427a..b438dbbe4ec 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -105,8 +105,7 @@ "label": "Cost Center", "oldfieldname": "cost_center", "oldfieldtype": "Link", - "options": "Cost Center", - "search_index": 1 + "options": "Cost Center" }, { "fieldname": "debit", @@ -359,7 +358,7 @@ "idx": 1, "in_create": 1, "links": [], - "modified": "2025-03-21 15:29:11.221890", + "modified": "2025-02-21 14:36:49.431166", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", From 0721816763d01ca5155f511931b9ac0100455471 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 28 Mar 2025 23:43:25 +0530 Subject: [PATCH 13/99] fix: incorrect condition (cherry picked from commit 0c1a8e9c58a0776bcdb7d4781416bc2d40e98a76) (cherry picked from commit 502b8f25b3fefc5c9f8fa31c14aeb8fb4335c272) --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 5440fe8588e..6423fd78fe9 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -335,7 +335,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe let d = locals[cdt][cdn]; return { filters: { - docstatus: ("<", 2), + docstatus: ["<", 2], inspection_type: inspection_type, reference_name: doc.name, item_code: d.item_code From 66d0ad1bc66aea4b1801a77d053a18021a2e5c79 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 29 Mar 2025 07:03:05 +0000 Subject: [PATCH 14/99] chore(release): Bumped to Version 15.55.3 ## [15.55.3](https://github.com/frappe/erpnext/compare/v15.55.2...v15.55.3) (2025-03-29) ### Bug Fixes * incorrect condition ([0721816](https://github.com/frappe/erpnext/commit/0721816763d01ca5155f511931b9ac0100455471)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c6303d9c483..7b26518d2b5 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.55.2" +__version__ = "15.55.3" def get_default_company(user=None): From 57e2619cf1a3e0e239600bbd17b4983c9a1de9e6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 27 Mar 2025 17:33:43 +0530 Subject: [PATCH 15/99] fix: valuation rate not updating for raw materials (cherry picked from commit 5af8378471009713e4b4adf112f0343092f977e2) # Conflicts: # erpnext/manufacturing/doctype/work_order/test_work_order.py (cherry picked from commit 454dd3a2f11451c4dd530c32ee4577fc8bafb21c) --- .../doctype/work_order/test_work_order.py | 287 ++++++++++++++++++ erpnext/stock/stock_ledger.py | 14 +- 2 files changed, 300 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 84f2b75432c..ca14c759088 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2602,6 +2602,293 @@ class TestWorkOrder(FrappeTestCase): status = frappe.db.get_value("Serial No", row, "status") self.assertEqual(status, "Consumed") +<<<<<<< HEAD +======= + def test_stock_reservation_for_serialized_raw_material(self): + from erpnext.stock.doctype.stock_entry.stock_entry_utils import ( + make_stock_entry as make_stock_entry_test_record, + ) + + production_item = "Test Stock Reservation FG 1" + rm_item = "Test Stock Reservation RM 1" + source_warehouse = "Stores - _TC" + + make_item(production_item, {"is_stock_item": 1}) + make_item(rm_item, {"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "TST-SER-RES-.###"}) + + bom = make_bom( + item=production_item, + source_warehouse=source_warehouse, + raw_materials=[rm_item], + operating_cost_per_bom_quantity=100, + do_not_submit=True, + ) + + for row in bom.exploded_items: + make_stock_entry_test_record( + item_code=row.item_code, + target=source_warehouse, + qty=10, + basic_rate=100, + ) + + wo = make_wo_order_test_record( + item=production_item, + qty=10, + reserve_stock=1, + source_warehouse=source_warehouse, + ) + + self.assertTrue(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name})) + + wo1 = make_wo_order_test_record( + item=production_item, + qty=10, + reserve_stock=1, + source_warehouse=source_warehouse, + ) + + self.assertFalse(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo1.name})) + + transfer_entry = frappe.get_doc(make_stock_entry(wo1.name, "Material Transfer for Manufacture", 10)) + transfer_entry.save() + + self.assertRaises(frappe.ValidationError, transfer_entry.submit) + + def test_stock_reservation_for_batched_raw_material(self): + from erpnext.stock.doctype.stock_entry.stock_entry_utils import ( + make_stock_entry as make_stock_entry_test_record, + ) + + production_item = "Test Stock Reservation FG 2" + rm_item = "Test Stock Reservation RM 2" + source_warehouse = "Stores - _TC" + + make_item(production_item, {"is_stock_item": 1}) + make_item( + rm_item, + { + "is_stock_item": 1, + "has_batch_no": 1, + "batch_number_series": "TST-BATCH-RES-.###", + "create_new_batch": 1, + }, + ) + + bom = make_bom( + item=production_item, + source_warehouse=source_warehouse, + raw_materials=[rm_item], + operating_cost_per_bom_quantity=100, + do_not_submit=True, + ) + + for row in bom.exploded_items: + make_stock_entry_test_record( + item_code=row.item_code, + target=source_warehouse, + qty=10, + basic_rate=100, + ) + + wo = make_wo_order_test_record( + item=production_item, + qty=10, + reserve_stock=1, + source_warehouse=source_warehouse, + ) + + self.assertTrue(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name})) + + wo1 = make_wo_order_test_record( + item=production_item, + qty=10, + reserve_stock=1, + source_warehouse=source_warehouse, + ) + + self.assertFalse(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo1.name})) + + transfer_entry = frappe.get_doc(make_stock_entry(wo1.name, "Material Transfer for Manufacture", 10)) + transfer_entry.save() + + self.assertRaises(frappe.ValidationError, transfer_entry.submit) + + def test_auto_stock_reservation_for_batched_raw_material(self): + from erpnext.stock.doctype.stock_entry.stock_entry_utils import ( + make_stock_entry as make_stock_entry_test_record, + ) + + frappe.db.set_single_value("Stock Settings", "auto_reserve_serial_and_batch", 1) + + production_item = "Test Stock Reservation FG 3" + rm_item = "Test Stock Reservation RM 3" + source_warehouse = "Stores - _TC" + + make_item(production_item, {"is_stock_item": 1}) + make_item( + rm_item, + { + "is_stock_item": 1, + "has_batch_no": 1, + "batch_number_series": "TST-BATCH-RES-.###", + "create_new_batch": 1, + }, + ) + + bom = make_bom( + item=production_item, + source_warehouse=source_warehouse, + raw_materials=[rm_item], + operating_cost_per_bom_quantity=100, + do_not_submit=True, + ) + + itemwise_batches = frappe._dict() + for row in bom.exploded_items: + se = make_stock_entry_test_record( + item_code=row.item_code, + target=source_warehouse, + qty=10, + basic_rate=100, + ) + + itemwise_batches[row.item_code] = get_batch_from_bundle(se.items[0].serial_and_batch_bundle) + + wo = make_wo_order_test_record( + item=production_item, + qty=10, + reserve_stock=1, + source_warehouse=source_warehouse, + ) + + self.assertTrue(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name})) + + for row in frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name}): + reservation_entry = frappe.get_doc("Stock Reservation Entry", row.name) + self.assertTrue(reservation_entry.has_batch_no) + self.assertTrue(reservation_entry.sb_entries) + + for row in bom.exploded_items: + make_stock_entry_test_record( + item_code=row.item_code, + target=source_warehouse, + qty=10, + basic_rate=100, + ) + + transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10)) + transfer_entry.save() + transfer_entry.submit() + + for row in transfer_entry.items: + batch_no = get_batch_from_bundle(row.serial_and_batch_bundle) + self.assertEqual(batch_no, itemwise_batches[row.item_code]) + + def test_work_order_valuation_auto_pick(self): + fg_item = "Test FG Item For Non Transfer Item Batch" + rm_item = "Test RM Item For Non Transfer Item Batch" + + make_item(fg_item, {"is_stock_item": 1}) + make_item( + rm_item, + { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "TST-BATCH-NTI-.###", + }, + ) + + source_warehouse = "_Test Warehouse - _TC" + wip_warehouse = "Stores - _TC" + finished_goods_warehouse = create_warehouse("_Test Finished Goods Warehouse", company="_Test Company") + + batches = make_stock_in_entries_and_get_batches(rm_item, source_warehouse, wip_warehouse) + + if not frappe.db.get_value("BOM", {"item": fg_item}): + make_bom(item=fg_item, raw_materials=[rm_item]) + + wo = make_wo_order_test_record( + item=fg_item, + qty=5, + source_warehouse=source_warehouse, + wip_warehouse=wip_warehouse, + fg_warehouse=finished_goods_warehouse, + ) + + stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 5)) + stock_entry.items[0].batch_no = batches[1] + stock_entry.items[0].use_serial_batch_fields = 1 + stock_entry.submit() + stock_entry.reload() + + self.assertEqual(stock_entry.items[0].valuation_rate, 200) + + original_value = frappe.db.get_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward" + ) + original_based_on = frappe.db.get_single_value("Stock Settings", "pick_serial_and_batch_based_on") + + frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1) + frappe.db.set_single_value("Stock Settings", "pick_serial_and_batch_based_on", "Expiry") + + stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 5)) + stock_entry.items[0].use_serial_batch_fields = 1 + stock_entry.submit() + stock_entry.reload() + + batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle) + self.assertEqual(batch_no, batches[1]) + self.assertEqual(stock_entry.items[0].valuation_rate, 200) + self.assertEqual(stock_entry.items[1].valuation_rate, 200) + + frappe.db.set_single_value( + "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", original_value + ) + frappe.db.set_single_value("Stock Settings", "pick_serial_and_batch_based_on", original_based_on) + + +def make_stock_in_entries_and_get_batches(rm_item, source_warehouse, wip_warehouse): + from erpnext.stock.doctype.stock_entry.test_stock_entry import ( + make_stock_entry as make_stock_entry_test_record, + ) + + batches = [] + for qty, rate in ((5, 100), (5, 200)): + stock_entry = make_stock_entry_test_record( + item_code=rm_item, + target=source_warehouse, + qty=qty, + basic_rate=rate, + ) + stock_entry.submit() + stock_entry.reload() + + batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle) + batch_doc = frappe.get_doc("Batch", batch_no) + + # keep early expiry date for the batch having rate 200 + days = 10 if rate == 100 else 1 + batch_doc.db_set("expiry_date", add_to_date(now(), days=days)) + + batches.append(batch_no) + + stock_entry = make_stock_entry_test_record( + item_code=rm_item, + target=wip_warehouse, + qty=qty, + basic_rate=rate, + ) + stock_entry.submit() + stock_entry.reload() + batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle) + batch_doc = frappe.get_doc("Batch", batch_no) + batch_doc.db_set("expiry_date", add_to_date(now(), days=10)) + + return batches + +>>>>>>> 5af8378471 (fix: valuation rate not updating for raw materials) def make_operation(**kwargs): kwargs = frappe._dict(kwargs) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index aaeb90b7d30..4627c8aecfa 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1212,9 +1212,21 @@ class update_entries_after: frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate) # Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount - if not sle.dependant_sle_voucher_detail_no: + if not sle.dependant_sle_voucher_detail_no or self.is_manufacture_entry_with_sabb(sle): self.recalculate_amounts_in_stock_entry(sle.voucher_no, sle.voucher_detail_no) + def is_manufacture_entry_with_sabb(self, sle): + if ( + self.args.get("sle_id") + and sle.serial_and_batch_bundle + and sle.auto_created_serial_and_batch_bundle + ): + purpose = frappe.get_cached_value("Stock Entry", sle.voucher_no, "purpose") + if purpose in ["Manufacture", "Repack"]: + return True + + return False + def recalculate_amounts_in_stock_entry(self, voucher_no, voucher_detail_no): stock_entry = frappe.get_doc("Stock Entry", voucher_no, for_update=True) stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False) From c22869fed96756654bc4c2aa22c2a2700aa186de Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 28 Mar 2025 23:47:43 +0530 Subject: [PATCH 16/99] chore: fix conflicts (cherry picked from commit 5079519863b0492a2a26772edce0330bde03352f) --- .../doctype/work_order/test_work_order.py | 184 ------------------ 1 file changed, 184 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index ca14c759088..dfa02c03772 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2602,189 +2602,6 @@ class TestWorkOrder(FrappeTestCase): status = frappe.db.get_value("Serial No", row, "status") self.assertEqual(status, "Consumed") -<<<<<<< HEAD -======= - def test_stock_reservation_for_serialized_raw_material(self): - from erpnext.stock.doctype.stock_entry.stock_entry_utils import ( - make_stock_entry as make_stock_entry_test_record, - ) - - production_item = "Test Stock Reservation FG 1" - rm_item = "Test Stock Reservation RM 1" - source_warehouse = "Stores - _TC" - - make_item(production_item, {"is_stock_item": 1}) - make_item(rm_item, {"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "TST-SER-RES-.###"}) - - bom = make_bom( - item=production_item, - source_warehouse=source_warehouse, - raw_materials=[rm_item], - operating_cost_per_bom_quantity=100, - do_not_submit=True, - ) - - for row in bom.exploded_items: - make_stock_entry_test_record( - item_code=row.item_code, - target=source_warehouse, - qty=10, - basic_rate=100, - ) - - wo = make_wo_order_test_record( - item=production_item, - qty=10, - reserve_stock=1, - source_warehouse=source_warehouse, - ) - - self.assertTrue(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name})) - - wo1 = make_wo_order_test_record( - item=production_item, - qty=10, - reserve_stock=1, - source_warehouse=source_warehouse, - ) - - self.assertFalse(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo1.name})) - - transfer_entry = frappe.get_doc(make_stock_entry(wo1.name, "Material Transfer for Manufacture", 10)) - transfer_entry.save() - - self.assertRaises(frappe.ValidationError, transfer_entry.submit) - - def test_stock_reservation_for_batched_raw_material(self): - from erpnext.stock.doctype.stock_entry.stock_entry_utils import ( - make_stock_entry as make_stock_entry_test_record, - ) - - production_item = "Test Stock Reservation FG 2" - rm_item = "Test Stock Reservation RM 2" - source_warehouse = "Stores - _TC" - - make_item(production_item, {"is_stock_item": 1}) - make_item( - rm_item, - { - "is_stock_item": 1, - "has_batch_no": 1, - "batch_number_series": "TST-BATCH-RES-.###", - "create_new_batch": 1, - }, - ) - - bom = make_bom( - item=production_item, - source_warehouse=source_warehouse, - raw_materials=[rm_item], - operating_cost_per_bom_quantity=100, - do_not_submit=True, - ) - - for row in bom.exploded_items: - make_stock_entry_test_record( - item_code=row.item_code, - target=source_warehouse, - qty=10, - basic_rate=100, - ) - - wo = make_wo_order_test_record( - item=production_item, - qty=10, - reserve_stock=1, - source_warehouse=source_warehouse, - ) - - self.assertTrue(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name})) - - wo1 = make_wo_order_test_record( - item=production_item, - qty=10, - reserve_stock=1, - source_warehouse=source_warehouse, - ) - - self.assertFalse(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo1.name})) - - transfer_entry = frappe.get_doc(make_stock_entry(wo1.name, "Material Transfer for Manufacture", 10)) - transfer_entry.save() - - self.assertRaises(frappe.ValidationError, transfer_entry.submit) - - def test_auto_stock_reservation_for_batched_raw_material(self): - from erpnext.stock.doctype.stock_entry.stock_entry_utils import ( - make_stock_entry as make_stock_entry_test_record, - ) - - frappe.db.set_single_value("Stock Settings", "auto_reserve_serial_and_batch", 1) - - production_item = "Test Stock Reservation FG 3" - rm_item = "Test Stock Reservation RM 3" - source_warehouse = "Stores - _TC" - - make_item(production_item, {"is_stock_item": 1}) - make_item( - rm_item, - { - "is_stock_item": 1, - "has_batch_no": 1, - "batch_number_series": "TST-BATCH-RES-.###", - "create_new_batch": 1, - }, - ) - - bom = make_bom( - item=production_item, - source_warehouse=source_warehouse, - raw_materials=[rm_item], - operating_cost_per_bom_quantity=100, - do_not_submit=True, - ) - - itemwise_batches = frappe._dict() - for row in bom.exploded_items: - se = make_stock_entry_test_record( - item_code=row.item_code, - target=source_warehouse, - qty=10, - basic_rate=100, - ) - - itemwise_batches[row.item_code] = get_batch_from_bundle(se.items[0].serial_and_batch_bundle) - - wo = make_wo_order_test_record( - item=production_item, - qty=10, - reserve_stock=1, - source_warehouse=source_warehouse, - ) - - self.assertTrue(frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name})) - - for row in frappe.get_all("Stock Reservation Entry", filters={"voucher_no": wo.name}): - reservation_entry = frappe.get_doc("Stock Reservation Entry", row.name) - self.assertTrue(reservation_entry.has_batch_no) - self.assertTrue(reservation_entry.sb_entries) - - for row in bom.exploded_items: - make_stock_entry_test_record( - item_code=row.item_code, - target=source_warehouse, - qty=10, - basic_rate=100, - ) - - transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10)) - transfer_entry.save() - transfer_entry.submit() - - for row in transfer_entry.items: - batch_no = get_batch_from_bundle(row.serial_and_batch_bundle) - self.assertEqual(batch_no, itemwise_batches[row.item_code]) - def test_work_order_valuation_auto_pick(self): fg_item = "Test FG Item For Non Transfer Item Batch" rm_item = "Test RM Item For Non Transfer Item Batch" @@ -2888,7 +2705,6 @@ def make_stock_in_entries_and_get_batches(rm_item, source_warehouse, wip_warehou return batches ->>>>>>> 5af8378471 (fix: valuation rate not updating for raw materials) def make_operation(**kwargs): kwargs = frappe._dict(kwargs) From 5dd99f896ee57bca5fbb85cb0f56f0443ef38b14 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 29 Mar 2025 20:07:32 +0000 Subject: [PATCH 17/99] chore(release): Bumped to Version 15.55.4 ## [15.55.4](https://github.com/frappe/erpnext/compare/v15.55.3...v15.55.4) (2025-03-29) ### Bug Fixes * valuation rate not updating for raw materials ([57e2619](https://github.com/frappe/erpnext/commit/57e2619cf1a3e0e239600bbd17b4983c9a1de9e6)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 7b26518d2b5..7b4b1016acf 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.55.3" +__version__ = "15.55.4" def get_default_company(user=None): From bf3349a4325bf24b36508bd89c5c2469bc6265df Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 1 Apr 2025 13:26:28 +0530 Subject: [PATCH 18/99] fix: set draft QC in purchase document on creation of qc (cherry picked from commit 2553dea78ef35d7bebda98df1bf3db1f97c70027) (cherry picked from commit 54159b9e5e54893572e64e1e7cf54933d620b92a) --- .../quality_inspection/quality_inspection.py | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 7c6b892416b..bf1c918668f 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -197,8 +197,19 @@ class QualityInspection(Document): self.quality_inspection_template = template self.get_item_specification_details() + def on_update(self): + if ( + frappe.db.get_single_value("Stock Settings", "action_if_quality_inspection_is_not_submitted") + == "Warn" + ): + self.update_qc_reference() + def on_submit(self): - self.update_qc_reference() + if ( + frappe.db.get_single_value("Stock Settings", "action_if_quality_inspection_is_not_submitted") + == "Stop" + ): + self.update_qc_reference() def on_cancel(self): self.ignore_linked_doctypes = "Serial and Batch Bundle" @@ -206,15 +217,15 @@ class QualityInspection(Document): self.update_qc_reference() def on_trash(self): - self.update_qc_reference() + self.update_qc_reference(remove_reference=True) def validate_readings_status_mandatory(self): for reading in self.readings: if not reading.status: frappe.throw(_("Row #{0}: Status is mandatory").format(reading.idx)) - def update_qc_reference(self): - quality_inspection = self.name if self.docstatus == 1 else "" + def update_qc_reference(self, remove_reference=False): + quality_inspection = self.name if self.docstatus < 2 and not remove_reference else "" if self.reference_type == "Job Card": if self.reference_name: @@ -244,7 +255,7 @@ class QualityInspection(Document): ) ) - if self.batch_no and self.docstatus == 1: + if self.batch_no and self.docstatus < 2: query = query.where(child_doc.batch_no == self.batch_no) if self.docstatus == 2: # if cancel, then remove qi link wherever same name From b172ae05578ffcd7b024256a10a7cb1377d336d4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 6 Apr 2025 09:52:41 +0530 Subject: [PATCH 19/99] fix: slow query (cherry picked from commit f82c8ea5eb3551a92ce0f2d54c57404a3cb5139d) # Conflicts: # erpnext/stock/deprecated_serial_batch.py (cherry picked from commit 23dc9d58720330d5de17b737883d6d3d5ae064dd) --- erpnext/stock/deprecated_serial_batch.py | 18 ++++++++++-------- .../serial_and_batch_bundle.py | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index f084c324745..d6ce85ef7cc 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -195,6 +195,7 @@ class DeprecatedBatchNoValuation: @deprecated def set_balance_value_for_non_batchwise_valuation_batches(self): + self.last_sle = self.get_last_sle_for_non_batch() self.set_balance_value_from_sl_entries() self.set_balance_value_from_bundle() @@ -242,11 +243,10 @@ class DeprecatedBatchNoValuation: for d in batch_data: self.available_qty[d.batch_no] += flt(d.batch_qty) - last_sle = self.get_last_sle_for_non_batch() for d in batch_data: if self.available_qty.get(d.batch_no): - self.non_batchwise_balance_value[d.batch_no] += flt(last_sle.stock_value) - self.non_batchwise_balance_qty[d.batch_no] += flt(last_sle.qty_after_transaction) + self.non_batchwise_balance_value[d.batch_no] += flt(self.last_sle.stock_value) + self.non_batchwise_balance_qty[d.batch_no] += flt(self.last_sle.qty_after_transaction) def get_last_sle_for_non_batch(self): from erpnext.stock.utils import get_combine_datetime @@ -285,8 +285,8 @@ class DeprecatedBatchNoValuation: query = query.where(sle.name != self.sle.name) data = query.run(as_dict=True) - return data[0] if data else {} +<<<<<<< HEAD @deprecated def get_last_sle_for_sabb_no_batchwise_valuation(self): sabb = frappe.qb.DocType("Serial and Batch Bundle") @@ -339,6 +339,9 @@ class DeprecatedBatchNoValuation: ) return sle if sle else {} +======= + return data[0] if data else frappe._dict() +>>>>>>> f82c8ea5eb (fix: slow query) @deprecated def set_balance_value_from_bundle(self) -> None: @@ -389,10 +392,9 @@ class DeprecatedBatchNoValuation: for d in batch_data: self.available_qty[d.batch_no] += flt(d.batch_qty) - last_sle = self.get_last_sle_for_sabb_no_batchwise_valuation() - if not last_sle: + if not self.last_sle: return for batch_no in self.available_qty: - self.non_batchwise_balance_value[batch_no] = flt(last_sle.stock_value) - self.non_batchwise_balance_qty[batch_no] = flt(last_sle.qty_after_transaction) + self.non_batchwise_balance_value[batch_no] = flt(self.last_sle.stock_value) + self.non_batchwise_balance_qty[batch_no] = flt(self.last_sle.qty_after_transaction) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 8502c0cc3cc..8abe5b7e082 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -2103,7 +2103,8 @@ def get_auto_batch_nos(kwargs): filter_zero_near_batches(available_batches, kwargs) if not kwargs.consider_negative_batches: - available_batches = list(filter(lambda x: x.qty > 0, available_batches)) + precision = frappe.get_precision("Stock Ledger Entry", "actual_qty") + available_batches = [d for d in available_batches if flt(d.qty, precision) > 0] if not qty: return available_batches From b2d71b44cf23b1aeebf2841ae1f8ada0884df27a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 6 Apr 2025 16:25:20 +0530 Subject: [PATCH 20/99] chore: fix conflicts (cherry picked from commit 4bcf05222014857e0ff5b2002623e290e88e1e8d) --- erpnext/stock/deprecated_serial_batch.py | 55 ------------------------ 1 file changed, 55 deletions(-) diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index d6ce85ef7cc..a8e17993c30 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -286,62 +286,7 @@ class DeprecatedBatchNoValuation: data = query.run(as_dict=True) -<<<<<<< HEAD - @deprecated - def get_last_sle_for_sabb_no_batchwise_valuation(self): - sabb = frappe.qb.DocType("Serial and Batch Bundle") - sabb_entry = frappe.qb.DocType("Serial and Batch Entry") - batch = frappe.qb.DocType("Batch") - - posting_datetime = CombineDatetime(self.sle.posting_date, self.sle.posting_time) - timestamp_condition = CombineDatetime(sabb.posting_date, sabb.posting_time) < posting_datetime - - if self.sle.creation: - timestamp_condition |= ( - CombineDatetime(sabb.posting_date, sabb.posting_time) == posting_datetime - ) & (sabb.creation < self.sle.creation) - - query = ( - frappe.qb.from_(sabb) - .inner_join(sabb_entry) - .on(sabb.name == sabb_entry.parent) - .inner_join(batch) - .on(sabb_entry.batch_no == batch.name) - .select(sabb.name) - .where( - (sabb.item_code == self.sle.item_code) - & (sabb.warehouse == self.sle.warehouse) - & (sabb_entry.batch_no.isnotnull()) - & (sabb.is_cancelled == 0) - & (sabb.docstatus == 1) - ) - .where(timestamp_condition) - .orderby(sabb.posting_date, order=Order.desc) - .orderby(sabb.posting_time, order=Order.desc) - .orderby(sabb.creation, order=Order.desc) - .limit(1) - ) - - if self.sle.voucher_detail_no: - query = query.where(sabb.voucher_detail_no != self.sle.voucher_detail_no) - - query = query.where(sabb.voucher_type != "Pick List") - - data = query.run(as_dict=True) - if not data: - return {} - - sle = frappe.db.get_value( - "Stock Ledger Entry", - {"serial_and_batch_bundle": data[0].name}, - ["stock_value", "qty_after_transaction"], - as_dict=1, - ) - - return sle if sle else {} -======= return data[0] if data else frappe._dict() ->>>>>>> f82c8ea5eb (fix: slow query) @deprecated def set_balance_value_from_bundle(self) -> None: From 9397a57d4d0256f2645bf1a6698dc41c1c92bec1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 7 Apr 2025 05:14:16 +0000 Subject: [PATCH 21/99] chore(release): Bumped to Version 15.55.5 ## [15.55.5](https://github.com/frappe/erpnext/compare/v15.55.4...v15.55.5) (2025-04-07) ### Bug Fixes * set draft QC in purchase document on creation of qc ([bf3349a](https://github.com/frappe/erpnext/commit/bf3349a4325bf24b36508bd89c5c2469bc6265df)) * slow query ([b172ae0](https://github.com/frappe/erpnext/commit/b172ae05578ffcd7b024256a10a7cb1377d336d4)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 7b4b1016acf..a0c63c96ff6 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.55.4" +__version__ = "15.55.5" def get_default_company(user=None): From 52257c946e5d87f31571f78cf471cafc91387120 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 8 Apr 2025 13:08:39 +0000 Subject: [PATCH 22/99] chore(release): Bumped to Version 15.56.0 # [15.56.0](https://github.com/frappe/erpnext/compare/v15.55.5...v15.56.0) (2025-04-08) ### Bug Fixes * **accounting:** update outstanding amount based on update_outstanding_for_self ([fb06f88](https://github.com/frappe/erpnext/commit/fb06f886d261c2b674ff3851a5b0bcdde1bdfb0f)) * add `Not Cancelled` filter for `payment_entry` in Bank Transaction ([5d47db7](https://github.com/frappe/erpnext/commit/5d47db78e635354cc91a4bc5b2d92e4776c818f0)) * check payments against orders for getting request amount ([cf7252d](https://github.com/frappe/erpnext/commit/cf7252d3e732759d8d160335e501fdb0972856ad)) * condition to update the last puurchase rate ([353fa0c](https://github.com/frappe/erpnext/commit/353fa0cbc3d90ac5da683b24412f074a81fc5aea)) * correct mapping(schedule_date) sales order to material request ([e2c8ed2](https://github.com/frappe/erpnext/commit/e2c8ed2afdc2cb169003cce03665ba72528709eb)) * correct payment request amount ([23c76aa](https://github.com/frappe/erpnext/commit/23c76aa530cd07c428cc23daf17b3bdcfabb025b)) * decimal values causing incorrect batch picking ([c5efdda](https://github.com/frappe/erpnext/commit/c5efddae16ae0f31088ab84cb8e15996a37e654a)) * do not use self object for setting party and party type ([d1311e6](https://github.com/frappe/erpnext/commit/d1311e619db233cda64c47833538c1669b3bb17e)) * **Dunning:** undefined variable (backport [#46868](https://github.com/frappe/erpnext/issues/46868)) ([#46869](https://github.com/frappe/erpnext/issues/46869)) ([f63595c](https://github.com/frappe/erpnext/commit/f63595cf0cd0d18a6a7aafc2519c0dd86c6e4315)) * empty party filter on change of party type in General Ledger Report. ([95cc282](https://github.com/frappe/erpnext/commit/95cc2827c6fa76bb84e859e75b974c1317ca7c69)) * expense account in the stock entry ([62f342e](https://github.com/frappe/erpnext/commit/62f342ef8b09abe821fbe8deebbd68054ee7fd85)) * Fix fieldtype in UnReconcile dialog ([7dfff8d](https://github.com/frappe/erpnext/commit/7dfff8d3a2f3374873b0460b43673a871c01d8bf)) * for deadlock issue keep status as In Progress ([34e66b1](https://github.com/frappe/erpnext/commit/34e66b1b2777301dec7b4a7be0a1bc0b26b70e2b)) * ignore backflush setting on subcontracting return ([ca56150](https://github.com/frappe/erpnext/commit/ca56150918423c197d3ffcad92d866965ebfd34c)) * improved rounding adjustment when applying discount (backport [#46720](https://github.com/frappe/erpnext/issues/46720)) ([7b864be](https://github.com/frappe/erpnext/commit/7b864bece8d8e0a4bef66607c52ed5f046b84756)) * include auto_reconcile_vouchers flag in background job ([26f93f5](https://github.com/frappe/erpnext/commit/26f93f57b8e488fb8d340631021f7663948734e7)) * incorrect condition ([502b8f2](https://github.com/frappe/erpnext/commit/502b8f25b3fefc5c9f8fa31c14aeb8fb4335c272)) * inventory dimensions columns visibility depends on filter ([fe0e5c2](https://github.com/frappe/erpnext/commit/fe0e5c2d48e529835bbd07cd334f14ddc2dec81f)) * make message translatable (backport [#46863](https://github.com/frappe/erpnext/issues/46863)) ([#46866](https://github.com/frappe/erpnext/issues/46866)) ([d7bb4a2](https://github.com/frappe/erpnext/commit/d7bb4a288cb810882115f836ced846d883287600)) * multiple Bank Reconciliation Tool issues ([#46644](https://github.com/frappe/erpnext/issues/46644)) ([e168483](https://github.com/frappe/erpnext/commit/e168483a58f8eb3a96234f8474d0dfe0413dd857)) * **payment term:** allocate payment amount when payment term is fetched from order ([1b9980b](https://github.com/frappe/erpnext/commit/1b9980bb86225213949e3db2681ece877c23b7fd)) * **portal:** context pay_amount for button ([b7ae17a](https://github.com/frappe/erpnext/commit/b7ae17aaaf18be38be8ac53c826ff4f9a73a9058)) * **portal:** payment amount for orders ([b0302d7](https://github.com/frappe/erpnext/commit/b0302d71b7a001e1298f3cc5c43c793c6525da32)) * pos checking opened entry closed or not (backport [#46726](https://github.com/frappe/erpnext/issues/46726)) ([#46830](https://github.com/frappe/erpnext/issues/46830)) ([80f144a](https://github.com/frappe/erpnext/commit/80f144ac2236fc6f935a5f9897c716129e3d7a4c)) * pos closed dialog on pos closing entry (backport [#46881](https://github.com/frappe/erpnext/issues/46881)) ([#46882](https://github.com/frappe/erpnext/issues/46882)) ([c1fe8f6](https://github.com/frappe/erpnext/commit/c1fe8f600070e1db74eed4d7c9fda3d44c1c7eac)) * pos opening entry's status not getting updated on cancel (backport [#46909](https://github.com/frappe/erpnext/issues/46909)) ([#46911](https://github.com/frappe/erpnext/issues/46911)) ([8b11d13](https://github.com/frappe/erpnext/commit/8b11d13cd4c3b155ebfebe7ad45dbb3e80500685)) * remove against_voucher from General Ledger Report ([ba1e7e1](https://github.com/frappe/erpnext/commit/ba1e7e17fbcbdb4aeff41cca8ee54bae7871b364)) * remove all serial/batch fields when use button is unselected ([13f1afa](https://github.com/frappe/erpnext/commit/13f1afa14127cb3fb2e3772857ffb24787591b26)) * removed customer_group query in customer.js ([1aac8d3](https://github.com/frappe/erpnext/commit/1aac8d31f467eb68b0d1fb6f2d3f6584770146db)) * removed hardcoded search fields to fix performance issue ([48822f6](https://github.com/frappe/erpnext/commit/48822f6feefe0dbbcae9efcb746e8375de3b10b9)) * resolve conflicts ([4d8984e](https://github.com/frappe/erpnext/commit/4d8984e4e962562c768227d72f36326eff8f8fe0)) * restrict customer change if creating from opportunity ([2661147](https://github.com/frappe/erpnext/commit/26611475f6fd63eda6bbe3fea4a3b1c754efe68b)) * set draft QC in purchase document on creation of qc ([54159b9](https://github.com/frappe/erpnext/commit/54159b9e5e54893572e64e1e7cf54933d620b92a)) * slow query ([23dc9d5](https://github.com/frappe/erpnext/commit/23dc9d58720330d5de17b737883d6d3d5ae064dd)) * slow query ([af0fb13](https://github.com/frappe/erpnext/commit/af0fb131a22c07d82802959bcc2c88189146af20)) * stock entry repack amount calculation ([8c61639](https://github.com/frappe/erpnext/commit/8c61639062e9cf88b750cc237836c25919607b1a)) * Translate UnReconcile dialog title (backport [#46818](https://github.com/frappe/erpnext/issues/46818)) ([#46861](https://github.com/frappe/erpnext/issues/46861)) ([fcade5d](https://github.com/frappe/erpnext/commit/fcade5d8cd87fd51d002aee190af1271b6c6c672)) * update outstanding with precision ([e115409](https://github.com/frappe/erpnext/commit/e1154090f66e3d204136b24a47f89ff97223e5fe)) * update payment amount if automatically_fetch_payment_terms is enabled ([ea289a4](https://github.com/frappe/erpnext/commit/ea289a40fb4656bbac9ba7cc49e62a1caca4bd38)) * update posting date before running validations ([2bf44dc](https://github.com/frappe/erpnext/commit/2bf44dc326ea285bd9a65daa7ea6a7269ac8629d)) * use `grand_total_diff` instead of `rounding_adjustment` in `taxes_and_totals` ([55b17b9](https://github.com/frappe/erpnext/commit/55b17b918f9df9bea12a801013455c761121f03d)) * use docstatus for status filter ([ab52524](https://github.com/frappe/erpnext/commit/ab52524f12a2e937e3e8d8ec33851d363bf3a390)) * use get instead of dot operator to access dict value ([f2df8e5](https://github.com/frappe/erpnext/commit/f2df8e531d345d26c42057f17a84dc4de790bb3b)) * use work_order bom_no if no bom present in operation ([c6979ab](https://github.com/frappe/erpnext/commit/c6979ab260dcb6ce97bbdff6b28b8efd4ec2da41)) * user permissions in sales and purchase report ([c705623](https://github.com/frappe/erpnext/commit/c705623fdc1a002169344c4165279f18fbe4fdb0)) * validate if pos is opened before pos invoice creation (backport [#46907](https://github.com/frappe/erpnext/issues/46907)) ([#46910](https://github.com/frappe/erpnext/issues/46910)) ([999ab28](https://github.com/frappe/erpnext/commit/999ab28bf089aac0a0558c6cf149b14564b96bc6)) * valuation rate not updating for raw materials ([454dd3a](https://github.com/frappe/erpnext/commit/454dd3a2f11451c4dd530c32ee4577fc8bafb21c)) ### Features * allow UOMs to select for which converstion rate defined in item master ([288aad6](https://github.com/frappe/erpnext/commit/288aad6f5dbd6865da6d58cc6c66032e6347edc6)) * **Customer:** add Dunning to dashboard ([1128b5f](https://github.com/frappe/erpnext/commit/1128b5f09c89929700b9f5b4a8bdcdf0348bc73f)) * option to recreate Stock Ledger Entries against stock transactions ([64fdcb7](https://github.com/frappe/erpnext/commit/64fdcb752dc0937010e61089bb3498bc0b1483ba)) ### Performance Improvements * reduce query when validating any doc ([890abf6](https://github.com/frappe/erpnext/commit/890abf6b90a25b57a35a76afe6a12ee76f418d49)) * Stock entry cancel is slow ([1bdfd33](https://github.com/frappe/erpnext/commit/1bdfd338166b357bbd48456fb9d9527c68846021)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a0c63c96ff6..3244606893b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.55.5" +__version__ = "15.56.0" def get_default_company(user=None): From 837509ae471a258568017f20f70ce2cd94789654 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 8 Apr 2025 13:30:12 +0530 Subject: [PATCH 23/99] fix: item code not showing in the error message (cherry picked from commit 86dee69c2f08c2ae247a1c1d65d2f2a0a1214169) (cherry picked from commit 663e2b7e6c9cb5821cd90d3e94657590afc103dc) --- erpnext/stock/stock_ledger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 52f481f1374..e011217c984 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1536,7 +1536,7 @@ class update_entries_after: ) in frappe.local.flags.currently_saving: msg = _("{0} units of {1} needed in {2} to complete this transaction.").format( frappe.bold(abs(deficiency)), - frappe.get_desk_link("Item", exceptions[0]["item_code"]), + frappe.get_desk_link("Item", exceptions[0]["item_code"], show_title_with_name=True), frappe.get_desk_link("Warehouse", warehouse), ) else: @@ -1544,7 +1544,7 @@ class update_entries_after: "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction." ).format( frappe.bold(abs(deficiency)), - frappe.get_desk_link("Item", exceptions[0]["item_code"]), + frappe.get_desk_link("Item", exceptions[0]["item_code"], show_title_with_name=True), frappe.get_desk_link("Warehouse", warehouse), exceptions[0]["posting_date"], exceptions[0]["posting_time"], @@ -2033,7 +2033,7 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=False): if is_negative_with_precision(neg_sle): message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format( abs(neg_sle[0]["qty_after_transaction"]), - frappe.get_desk_link("Item", args.item_code), + frappe.get_desk_link("Item", args.item_code, show_title_with_name=True), frappe.get_desk_link("Warehouse", args.warehouse), neg_sle[0]["posting_date"], neg_sle[0]["posting_time"], @@ -2157,7 +2157,7 @@ def validate_reserved_stock(kwargs): if diff < 0 and abs(diff) > 0.0001: msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format( abs(diff), - frappe.get_desk_link("Item", kwargs.item_code), + frappe.get_desk_link("Item", kwargs.item_code, show_title_with_name=True), frappe.get_desk_link("Warehouse", kwargs.warehouse), nowdate(), nowtime(), From 0f6a7edb53c561649ab92c6e00269252e4cd80d6 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 7 Mar 2025 11:54:15 +0530 Subject: [PATCH 24/99] feat: available serial no report (cherry picked from commit 5592d8e87f3909ae5c6cfe748661dde9f3f64d1c) (cherry picked from commit c472af87b2916cf0508815a84a7aa8506a6fd48a) --- erpnext/stock/doctype/serial_no/serial_no.py | 6 + .../report/available_serial_no/__init__.py | 0 .../available_serial_no.js | 118 ++++ .../available_serial_no.json | 28 + .../available_serial_no.py | 581 ++++++++++++++++++ .../test_available_serial_no.py | 45 ++ 6 files changed, 778 insertions(+) create mode 100644 erpnext/stock/report/available_serial_no/__init__.py create mode 100644 erpnext/stock/report/available_serial_no/available_serial_no.js create mode 100644 erpnext/stock/report/available_serial_no/available_serial_no.json create mode 100644 erpnext/stock/report/available_serial_no/available_serial_no.py create mode 100644 erpnext/stock/report/available_serial_no/test_available_serial_no.py diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 1560db6a114..eff2ef14674 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -149,6 +149,12 @@ def get_serial_nos(serial_no): return [s.strip() for s in cstr(serial_no).strip().replace(",", "\n").split("\n") if s.strip()] +def get_serial_nos_from_serial_and_batch_bundle(serial_and_batch_bundle): + table = frappe.qb.DocType("Serial and Batch Entry") + query = frappe.qb.from_(table).select(table.serial_no).where(table.parent == serial_and_batch_bundle) + return [item[0] for item in query.run(as_list=True)] + + def clean_serial_no_string(serial_no: str) -> str: if not serial_no: return "" diff --git a/erpnext/stock/report/available_serial_no/__init__.py b/erpnext/stock/report/available_serial_no/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/report/available_serial_no/available_serial_no.js b/erpnext/stock/report/available_serial_no/available_serial_no.js new file mode 100644 index 00000000000..17f8c666e04 --- /dev/null +++ b/erpnext/stock/report/available_serial_no/available_serial_no.js @@ -0,0 +1,118 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.query_reports["Available Serial No"] = { + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + get_query: function () { + const company = frappe.query_report.get_filter_value("company"); + return { + filters: { company: company }, + }; + }, + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: function () { + return { + query: "erpnext.controllers.queries.item_query", + filters: { + has_serial_no: 1, + }, + }; + }, + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", + }, + { + fieldname: "batch_no", + label: __("Batch No"), + fieldtype: "Link", + options: "Batch", + on_change() { + const batch_no = frappe.query_report.get_filter_value("batch_no"); + if (batch_no) { + frappe.query_report.set_filter_value("segregate_serial_batch_bundle", 1); + } else { + frappe.query_report.set_filter_value("segregate_serial_batch_bundle", 0); + } + }, + }, + { + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", + }, + { + fieldname: "voucher_no", + label: __("Voucher #"), + fieldtype: "Data", + }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + options: "Project", + }, + { + fieldname: "include_uom", + label: __("Include UOM"), + fieldtype: "Link", + options: "UOM", + }, + { + fieldname: "valuation_field_type", + label: __("Valuation Field Type"), + fieldtype: "Select", + width: "80", + options: "Currency\nFloat", + default: "Currency", + }, + ], + formatter: function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + if (column.fieldname == "out_qty" && data && data.out_qty < 0) { + value = "" + value + ""; + } else if (column.fieldname == "in_qty" && data && data.in_qty > 0) { + value = "" + value + ""; + } + + return value; + }, +}; + +erpnext.utils.add_inventory_dimensions("Balance Serial No", 10); diff --git a/erpnext/stock/report/available_serial_no/available_serial_no.json b/erpnext/stock/report/available_serial_no/available_serial_no.json new file mode 100644 index 00000000000..63bba01e7fa --- /dev/null +++ b/erpnext/stock/report/available_serial_no/available_serial_no.json @@ -0,0 +1,28 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2025-03-07 10:54:09.429215", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2025-03-07 10:54:09.429215", + "modified_by": "Administrator", + "module": "Stock", + "name": "Available Serial No", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Stock Ledger Entry", + "report_name": "Available Serial No", + "report_type": "Script Report", + "roles": [ + { + "role": "Stock User" + }, + { + "role": "Accounts Manager" + } + ], + "timeout": 0 +} \ No newline at end of file diff --git a/erpnext/stock/report/available_serial_no/available_serial_no.py b/erpnext/stock/report/available_serial_no/available_serial_no.py new file mode 100644 index 00000000000..e1839f8957f --- /dev/null +++ b/erpnext/stock/report/available_serial_no/available_serial_no.py @@ -0,0 +1,581 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from collections import defaultdict + +import frappe +from frappe import _ +from frappe.query_builder.functions import CombineDatetime, Sum +from frappe.utils import cint, flt, get_datetime + +from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions +from erpnext.stock.doctype.serial_no.serial_no import ( + get_serial_nos, + get_serial_nos_from_serial_and_batch_bundle, +) +from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter +from erpnext.stock.utils import ( + is_reposting_item_valuation_in_progress, + update_included_uom_in_report, +) + + +def execute(filters=None): + is_reposting_item_valuation_in_progress() + include_uom = filters.get("include_uom") + columns = get_columns(filters) + items = get_items(filters) + sl_entries = get_stock_ledger_entries(filters, items) + item_details = get_item_details(items, sl_entries, include_uom) + if filters.get("batch_no"): + opening_row = get_opening_balance_from_batch(filters, columns, sl_entries) + else: + opening_row = get_opening_balance(filters, columns, sl_entries) + + precision = cint(frappe.db.get_single_value("System Settings", "float_precision")) + + data = [] + conversion_factors = [] + if opening_row: + data.append(opening_row) + conversion_factors.append(0) + + actual_qty = stock_value = 0 + if opening_row: + actual_qty = opening_row.get("qty_after_transaction") + stock_value = opening_row.get("stock_value") + + available_serial_nos = {} + inventory_dimension_filters_applied = check_inventory_dimension_filters_applied(filters) + + batch_balance_dict = frappe._dict({}) + if actual_qty and filters.get("batch_no"): + batch_balance_dict[filters.batch_no] = [actual_qty, stock_value] + + for sle in sl_entries: + item_detail = item_details[sle.item_code] + + sle.update(item_detail) + + if filters.get("batch_no") or inventory_dimension_filters_applied: + actual_qty += flt(sle.actual_qty, precision) + stock_value += sle.stock_value_difference + if sle.batch_no: + if not batch_balance_dict.get(sle.batch_no): + batch_balance_dict[sle.batch_no] = [0, 0] + + batch_balance_dict[sle.batch_no][0] += sle.actual_qty + + if sle.voucher_type == "Stock Reconciliation" and not sle.actual_qty: + actual_qty = sle.qty_after_transaction + stock_value = sle.stock_value + + sle.update({"qty_after_transaction": actual_qty, "stock_value": stock_value}) + + sle.update({"in_qty": max(sle.actual_qty, 0), "out_qty": min(sle.actual_qty, 0)}) + + if frappe.get_value("Item", sle.item_code, "has_serial_no"): + update_available_serial_nos(available_serial_nos, sle) + + if sle.actual_qty: + sle["in_out_rate"] = flt(sle.stock_value_difference / sle.actual_qty, precision) + + elif sle.voucher_type == "Stock Reconciliation": + sle["in_out_rate"] = sle.valuation_rate + + data.append(sle) + + if include_uom: + conversion_factors.append(item_detail.conversion_factor) + + update_included_uom_in_report(columns, data, include_uom, conversion_factors) + return columns, data + + +def update_available_serial_nos(available_serial_nos, sle): + serial_nos = ( + get_serial_nos(sle.serial_no) + if sle.serial_no + else get_serial_nos_from_serial_and_batch_bundle(sle.serial_and_batch_bundle) + ) + key = (sle.item_code, sle.warehouse) + if key not in available_serial_nos: + stock_balance = get_stock_balance_for( + sle.item_code, sle.warehouse, sle.posting_date, sle.posting_time + ) + serials = get_serial_nos(stock_balance["serial_nos"]) if stock_balance["serial_nos"] else [] + available_serial_nos.setdefault(key, serials) + sle.balance_serial_no = "\n".join(serials) + return + + existing_serial_no = available_serial_nos[key] + for sn in serial_nos: + if sn in existing_serial_no: + existing_serial_no.remove(sn) + else: + existing_serial_no.append(sn) + + sle.balance_serial_no = "\n".join(existing_serial_no) + + +def get_columns(filters): + columns = [ + {"label": _("Date"), "fieldname": "date", "fieldtype": "Datetime", "width": 150}, + { + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100, + }, + {"label": _("Item Name"), "fieldname": "item_name", "width": 100}, + { + "label": _("Stock UOM"), + "fieldname": "stock_uom", + "fieldtype": "Link", + "options": "UOM", + "width": 90, + }, + ] + + for dimension in get_inventory_dimensions(): + columns.append( + { + "label": _(dimension.doctype), + "fieldname": dimension.fieldname, + "fieldtype": "Link", + "options": dimension.doctype, + "width": 110, + } + ) + + columns.extend( + [ + { + "label": _("In Qty"), + "fieldname": "in_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty", + }, + { + "label": _("Out Qty"), + "fieldname": "out_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty", + }, + { + "label": _("Balance Qty"), + "fieldname": "qty_after_transaction", + "fieldtype": "Float", + "width": 100, + "convertible": "qty", + }, + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 150, + }, + { + "label": _("Item Group"), + "fieldname": "item_group", + "fieldtype": "Link", + "options": "Item Group", + "width": 100, + }, + { + "label": _("Brand"), + "fieldname": "brand", + "fieldtype": "Link", + "options": "Brand", + "width": 100, + }, + {"label": _("Description"), "fieldname": "description", "width": 200}, + { + "label": _("Incoming Rate"), + "fieldname": "incoming_rate", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate", + }, + { + "label": _("Avg Rate (Balance Stock)"), + "fieldname": "valuation_rate", + "fieldtype": filters.valuation_field_type, + "width": 180, + "options": "Company:company:default_currency" + if filters.valuation_field_type == "Currency" + else None, + "convertible": "rate", + }, + { + "label": _("Valuation Rate"), + "fieldname": "in_out_rate", + "fieldtype": filters.valuation_field_type, + "width": 140, + "options": "Company:company:default_currency" + if filters.valuation_field_type == "Currency" + else None, + "convertible": "rate", + }, + { + "label": _("Balance Value"), + "fieldname": "stock_value", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + }, + { + "label": _("Value Change"), + "fieldname": "stock_value_difference", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + }, + {"label": _("Voucher Type"), "fieldname": "voucher_type", "width": 110}, + { + "label": _("Voucher #"), + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "options": "voucher_type", + "width": 100, + }, + { + "label": _("Batch"), + "fieldname": "batch_no", + "fieldtype": "Link", + "options": "Batch", + "width": 100, + }, + { + "label": _("Serial No"), + "fieldname": "serial_no", + "fieldtype": "Link", + "options": "Serial No", + "width": 100, + }, + { + "label": _("Serial and Batch Bundle"), + "fieldname": "serial_and_batch_bundle", + "fieldtype": "Link", + "options": "Serial and Batch Bundle", + "width": 100, + }, + {"label": _("Balance Serial No"), "fieldname": "balance_serial_no", "width": 100}, + { + "label": _("Project"), + "fieldname": "project", + "fieldtype": "Link", + "options": "Project", + "width": 100, + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 110, + }, + ] + ) + + return columns + + +def get_stock_ledger_entries(filters, items): + from_date = get_datetime(filters.from_date + " 00:00:00") + to_date = get_datetime(filters.to_date + " 23:59:59") + + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .select( + sle.item_code, + sle.posting_datetime.as_("date"), + sle.warehouse, + sle.posting_date, + sle.posting_time, + sle.actual_qty, + sle.incoming_rate, + sle.valuation_rate, + sle.company, + sle.voucher_type, + sle.qty_after_transaction, + sle.stock_value_difference, + sle.serial_and_batch_bundle, + sle.voucher_no, + sle.stock_value, + sle.batch_no, + sle.serial_no, + sle.project, + ) + .where((sle.docstatus < 2) & (sle.is_cancelled == 0) & (sle.posting_datetime[from_date:to_date])) + .orderby(sle.posting_datetime) + .orderby(sle.creation) + ) + + inventory_dimension_fields = get_inventory_dimension_fields() + if inventory_dimension_fields: + for fieldname in inventory_dimension_fields: + query = query.select(fieldname) + if fieldname in filters and filters.get(fieldname): + query = query.where(sle[fieldname].isin(filters.get(fieldname))) + + if items: + query = query.where(sle.item_code.isin(items)) + + for field in ["voucher_no", "project", "company"]: + if filters.get(field) and field not in inventory_dimension_fields: + query = query.where(sle[field] == filters.get(field)) + + if filters.get("batch_no"): + bundles = get_serial_and_batch_bundles(filters) + + if bundles: + query = query.where( + (sle.serial_and_batch_bundle.isin(bundles)) | (sle.batch_no == filters.batch_no) + ) + else: + query = query.where(sle.batch_no == filters.batch_no) + + query = apply_warehouse_filter(query, sle, filters) + + return query.run(as_dict=True) + + +def get_serial_and_batch_bundles(filters): + SBB = frappe.qb.DocType("Serial and Batch Bundle") + SBE = frappe.qb.DocType("Serial and Batch Entry") + + query = ( + frappe.qb.from_(SBE) + .inner_join(SBB) + .on(SBE.parent == SBB.name) + .select(SBE.parent) + .where( + (SBB.docstatus == 1) + & (SBB.has_batch_no == 1) + & (SBB.voucher_no.notnull()) + & (SBE.batch_no == filters.batch_no) + ) + ) + + return query.run(pluck=SBE.parent) + + +def get_inventory_dimension_fields(): + return [dimension.fieldname for dimension in get_inventory_dimensions()] + + +def get_items(filters): + item = frappe.qb.DocType("Item") + query = frappe.qb.from_(item).select(item.name).where(item.has_serial_no == 1) + conditions = [] + + if item_code := filters.get("item_code"): + conditions.append(item.name == item_code) + else: + if brand := filters.get("brand"): + conditions.append(item.brand == brand) + if item_group := filters.get("item_group"): + if condition := get_item_group_condition(item_group, item): + conditions.append(condition) + + items = [] + if conditions: + for condition in conditions: + query = query.where(condition) + items = [r[0] for r in query.run()] + + return items + + +def get_item_details(items, sl_entries, include_uom): + item_details = {} + if not items: + items = list(set(d.item_code for d in sl_entries)) + + if not items: + return item_details + + item = frappe.qb.DocType("Item") + query = ( + frappe.qb.from_(item) + .select(item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom) + .where(item.name.isin(items)) + ) + + if include_uom: + ucd = frappe.qb.DocType("UOM Conversion Detail") + query = ( + query.left_join(ucd) + .on((ucd.parent == item.name) & (ucd.uom == include_uom)) + .select(ucd.conversion_factor) + ) + + res = query.run(as_dict=True) + + for item in res: + item_details.setdefault(item.name, item) + + return item_details + + +def get_sle_conditions(filters): + conditions = [] + if filters.get("warehouse"): + warehouse_condition = get_warehouse_condition(filters.get("warehouse")) + if warehouse_condition: + conditions.append(warehouse_condition) + if filters.get("voucher_no"): + conditions.append("voucher_no=%(voucher_no)s") + if filters.get("batch_no"): + conditions.append("batch_no=%(batch_no)s") + if filters.get("project"): + conditions.append("project=%(project)s") + + for dimension in get_inventory_dimensions(): + if filters.get(dimension.fieldname): + conditions.append(f"{dimension.fieldname} in %({dimension.fieldname})s") + + return "and {}".format(" and ".join(conditions)) if conditions else "" + + +def get_opening_balance_from_batch(filters, columns, sl_entries): + query_filters = { + "batch_no": filters.batch_no, + "docstatus": 1, + "is_cancelled": 0, + "posting_date": ("<", filters.from_date), + "company": filters.company, + } + + for fields in ["item_code", "warehouse"]: + if filters.get(fields): + query_filters[fields] = filters.get(fields) + + opening_data = frappe.get_all( + "Stock Ledger Entry", + fields=["sum(actual_qty) as qty_after_transaction", "sum(stock_value_difference) as stock_value"], + filters=query_filters, + )[0] + + for field in ["qty_after_transaction", "stock_value", "valuation_rate"]: + if opening_data.get(field) is None: + opening_data[field] = 0.0 + + table = frappe.qb.DocType("Stock Ledger Entry") + sabb_table = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(table) + .inner_join(sabb_table) + .on(table.serial_and_batch_bundle == sabb_table.parent) + .select( + Sum(sabb_table.qty).as_("qty"), + Sum(sabb_table.stock_value_difference).as_("stock_value"), + ) + .where( + (sabb_table.batch_no == filters.batch_no) + & (sabb_table.docstatus == 1) + & (table.posting_date < filters.from_date) + & (table.is_cancelled == 0) + ) + ) + + for field in ["item_code", "warehouse", "company"]: + if filters.get(field): + query = query.where(table[field] == filters.get(field)) + + bundle_data = query.run(as_dict=True) + + if bundle_data: + opening_data.qty_after_transaction += flt(bundle_data[0].qty) + opening_data.stock_value += flt(bundle_data[0].stock_value) + if opening_data.qty_after_transaction: + opening_data.valuation_rate = flt(opening_data.stock_value) / flt( + opening_data.qty_after_transaction + ) + + return { + "item_code": _("'Opening'"), + "qty_after_transaction": opening_data.qty_after_transaction, + "valuation_rate": opening_data.valuation_rate, + "stock_value": opening_data.stock_value, + } + + +def get_opening_balance(filters, columns, sl_entries): + if not (filters.item_code and filters.warehouse and filters.from_date): + return + + from erpnext.stock.stock_ledger import get_previous_sle + + last_entry = get_previous_sle( + { + "item_code": filters.item_code, + "warehouse_condition": get_warehouse_condition(filters.warehouse), + "posting_date": filters.from_date, + "posting_time": "00:00:00", + } + ) + + # check if any SLEs are actually Opening Stock Reconciliation + for sle in list(sl_entries): + if ( + sle.get("voucher_type") == "Stock Reconciliation" + and sle.posting_date == filters.from_date + and frappe.db.get_value("Stock Reconciliation", sle.voucher_no, "purpose") == "Opening Stock" + ): + last_entry = sle + sl_entries.remove(sle) + + row = { + "item_code": _("'Opening'"), + "qty_after_transaction": last_entry.get("qty_after_transaction", 0), + "valuation_rate": last_entry.get("valuation_rate", 0), + "stock_value": last_entry.get("stock_value", 0), + } + + return row + + +def get_warehouse_condition(warehouse): + warehouse_details = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"], as_dict=1) + if warehouse_details: + return f" exists (select name from `tabWarehouse` wh \ + where wh.lft >= {warehouse_details.lft} and wh.rgt <= {warehouse_details.rgt} and warehouse = wh.name)" + + return "" + + +def get_item_group_condition(item_group, item_table=None): + item_group_details = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"], as_dict=1) + if item_group_details: + if item_table: + ig = frappe.qb.DocType("Item Group") + return item_table.item_group.isin( + frappe.qb.from_(ig) + .select(ig.name) + .where( + (ig.lft >= item_group_details.lft) + & (ig.rgt <= item_group_details.rgt) + & (item_table.item_group == ig.name) + ) + ) + else: + return f"item.item_group in (select ig.name from `tabItem Group` ig \ + where ig.lft >= {item_group_details.lft} and ig.rgt <= {item_group_details.rgt} and item.item_group = ig.name)" + + +def check_inventory_dimension_filters_applied(filters) -> bool: + for dimension in get_inventory_dimensions(): + if dimension.fieldname in filters and filters.get(dimension.fieldname): + return True + + return False diff --git a/erpnext/stock/report/available_serial_no/test_available_serial_no.py b/erpnext/stock/report/available_serial_no/test_available_serial_no.py new file mode 100644 index 00000000000..b8741af4506 --- /dev/null +++ b/erpnext/stock/report/available_serial_no/test_available_serial_no.py @@ -0,0 +1,45 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe +from frappe.tests import IntegrationTestCase +from frappe.utils import add_days, today + +from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note +from erpnext.stock.doctype.item.test_item import create_item +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt + + +class TestStockLedgerReeport(IntegrationTestCase): + def setUp(self) -> None: + item = create_item("_Test Item with Serial No", is_stock_item=1) + item.has_serial_no = 1 + item.serial_no_series = "TEST.###" + item.save(ignore_permissions=True) + + self.filters = frappe._dict( + company="_Test Company", + from_date=today(), + to_date=add_days(today(), 30), + item_code="_Test Item With Serial No", + ) + + def tearDown(self) -> None: + frappe.db.rollback() + + def test_available_serial_no(self): + report = frappe.get_doc("Report", "Available Serial No") + + make_purchase_receipt(qty=10, item_code="_Test Item with Serial No") + data = report.get_data(filters=self.filters) + serial_nos = [item for item in data[-1][-1]["balance_serial_no"].split("\n")] + + # Test 1: Since we have created an inward entry with Purchase Receipt of 10 qty, we should have 10 serial nos + self.assertEqual(len(serial_nos), 10) + + create_delivery_note(qty=5, item_code="_Test Item with Serial No") + data = report.get_data(filters=self.filters) + serial_nos = [item for item in data[-1][-1]["balance_serial_no"].split("\n")] + + # Test 2: Since we have created a delivery note of 5 qty, we should have 5 serial nos + self.assertEqual(len(serial_nos), 5) From 9cdd32ad6b9b3384587154a3cd8d6e86f6113e96 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Fri, 7 Mar 2025 12:05:31 +0530 Subject: [PATCH 25/99] refactor: import functions in new report instead of redundant code (cherry picked from commit 501f07803e20d41a9e358fbc408742f768f839c5) (cherry picked from commit 0ec026ec7e53e1238118ccaaa17a3c14dafde150) --- .../available_serial_no.py | 286 +----------------- 1 file changed, 10 insertions(+), 276 deletions(-) diff --git a/erpnext/stock/report/available_serial_no/available_serial_no.py b/erpnext/stock/report/available_serial_no/available_serial_no.py index e1839f8957f..8a20fed848a 100644 --- a/erpnext/stock/report/available_serial_no/available_serial_no.py +++ b/erpnext/stock/report/available_serial_no/available_serial_no.py @@ -1,13 +1,10 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - -from collections import defaultdict - import frappe from frappe import _ -from frappe.query_builder.functions import CombineDatetime, Sum -from frappe.utils import cint, flt, get_datetime +from frappe.query_builder.functions import Sum +from frappe.utils import cint, flt from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions from erpnext.stock.doctype.serial_no.serial_no import ( @@ -15,7 +12,14 @@ from erpnext.stock.doctype.serial_no.serial_no import ( get_serial_nos_from_serial_and_batch_bundle, ) from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for -from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter +from erpnext.stock.report.stock_ledger.stock_ledger import ( + check_inventory_dimension_filters_applied, + get_item_details, + get_item_group_condition, + get_opening_balance, + get_opening_balance_from_batch, + get_stock_ledger_entries, +) from erpnext.stock.utils import ( is_reposting_item_valuation_in_progress, update_included_uom_in_report, @@ -288,91 +292,6 @@ def get_columns(filters): return columns -def get_stock_ledger_entries(filters, items): - from_date = get_datetime(filters.from_date + " 00:00:00") - to_date = get_datetime(filters.to_date + " 23:59:59") - - sle = frappe.qb.DocType("Stock Ledger Entry") - query = ( - frappe.qb.from_(sle) - .select( - sle.item_code, - sle.posting_datetime.as_("date"), - sle.warehouse, - sle.posting_date, - sle.posting_time, - sle.actual_qty, - sle.incoming_rate, - sle.valuation_rate, - sle.company, - sle.voucher_type, - sle.qty_after_transaction, - sle.stock_value_difference, - sle.serial_and_batch_bundle, - sle.voucher_no, - sle.stock_value, - sle.batch_no, - sle.serial_no, - sle.project, - ) - .where((sle.docstatus < 2) & (sle.is_cancelled == 0) & (sle.posting_datetime[from_date:to_date])) - .orderby(sle.posting_datetime) - .orderby(sle.creation) - ) - - inventory_dimension_fields = get_inventory_dimension_fields() - if inventory_dimension_fields: - for fieldname in inventory_dimension_fields: - query = query.select(fieldname) - if fieldname in filters and filters.get(fieldname): - query = query.where(sle[fieldname].isin(filters.get(fieldname))) - - if items: - query = query.where(sle.item_code.isin(items)) - - for field in ["voucher_no", "project", "company"]: - if filters.get(field) and field not in inventory_dimension_fields: - query = query.where(sle[field] == filters.get(field)) - - if filters.get("batch_no"): - bundles = get_serial_and_batch_bundles(filters) - - if bundles: - query = query.where( - (sle.serial_and_batch_bundle.isin(bundles)) | (sle.batch_no == filters.batch_no) - ) - else: - query = query.where(sle.batch_no == filters.batch_no) - - query = apply_warehouse_filter(query, sle, filters) - - return query.run(as_dict=True) - - -def get_serial_and_batch_bundles(filters): - SBB = frappe.qb.DocType("Serial and Batch Bundle") - SBE = frappe.qb.DocType("Serial and Batch Entry") - - query = ( - frappe.qb.from_(SBE) - .inner_join(SBB) - .on(SBE.parent == SBB.name) - .select(SBE.parent) - .where( - (SBB.docstatus == 1) - & (SBB.has_batch_no == 1) - & (SBB.voucher_no.notnull()) - & (SBE.batch_no == filters.batch_no) - ) - ) - - return query.run(pluck=SBE.parent) - - -def get_inventory_dimension_fields(): - return [dimension.fieldname for dimension in get_inventory_dimensions()] - - def get_items(filters): item = frappe.qb.DocType("Item") query = frappe.qb.from_(item).select(item.name).where(item.has_serial_no == 1) @@ -394,188 +313,3 @@ def get_items(filters): items = [r[0] for r in query.run()] return items - - -def get_item_details(items, sl_entries, include_uom): - item_details = {} - if not items: - items = list(set(d.item_code for d in sl_entries)) - - if not items: - return item_details - - item = frappe.qb.DocType("Item") - query = ( - frappe.qb.from_(item) - .select(item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom) - .where(item.name.isin(items)) - ) - - if include_uom: - ucd = frappe.qb.DocType("UOM Conversion Detail") - query = ( - query.left_join(ucd) - .on((ucd.parent == item.name) & (ucd.uom == include_uom)) - .select(ucd.conversion_factor) - ) - - res = query.run(as_dict=True) - - for item in res: - item_details.setdefault(item.name, item) - - return item_details - - -def get_sle_conditions(filters): - conditions = [] - if filters.get("warehouse"): - warehouse_condition = get_warehouse_condition(filters.get("warehouse")) - if warehouse_condition: - conditions.append(warehouse_condition) - if filters.get("voucher_no"): - conditions.append("voucher_no=%(voucher_no)s") - if filters.get("batch_no"): - conditions.append("batch_no=%(batch_no)s") - if filters.get("project"): - conditions.append("project=%(project)s") - - for dimension in get_inventory_dimensions(): - if filters.get(dimension.fieldname): - conditions.append(f"{dimension.fieldname} in %({dimension.fieldname})s") - - return "and {}".format(" and ".join(conditions)) if conditions else "" - - -def get_opening_balance_from_batch(filters, columns, sl_entries): - query_filters = { - "batch_no": filters.batch_no, - "docstatus": 1, - "is_cancelled": 0, - "posting_date": ("<", filters.from_date), - "company": filters.company, - } - - for fields in ["item_code", "warehouse"]: - if filters.get(fields): - query_filters[fields] = filters.get(fields) - - opening_data = frappe.get_all( - "Stock Ledger Entry", - fields=["sum(actual_qty) as qty_after_transaction", "sum(stock_value_difference) as stock_value"], - filters=query_filters, - )[0] - - for field in ["qty_after_transaction", "stock_value", "valuation_rate"]: - if opening_data.get(field) is None: - opening_data[field] = 0.0 - - table = frappe.qb.DocType("Stock Ledger Entry") - sabb_table = frappe.qb.DocType("Serial and Batch Entry") - query = ( - frappe.qb.from_(table) - .inner_join(sabb_table) - .on(table.serial_and_batch_bundle == sabb_table.parent) - .select( - Sum(sabb_table.qty).as_("qty"), - Sum(sabb_table.stock_value_difference).as_("stock_value"), - ) - .where( - (sabb_table.batch_no == filters.batch_no) - & (sabb_table.docstatus == 1) - & (table.posting_date < filters.from_date) - & (table.is_cancelled == 0) - ) - ) - - for field in ["item_code", "warehouse", "company"]: - if filters.get(field): - query = query.where(table[field] == filters.get(field)) - - bundle_data = query.run(as_dict=True) - - if bundle_data: - opening_data.qty_after_transaction += flt(bundle_data[0].qty) - opening_data.stock_value += flt(bundle_data[0].stock_value) - if opening_data.qty_after_transaction: - opening_data.valuation_rate = flt(opening_data.stock_value) / flt( - opening_data.qty_after_transaction - ) - - return { - "item_code": _("'Opening'"), - "qty_after_transaction": opening_data.qty_after_transaction, - "valuation_rate": opening_data.valuation_rate, - "stock_value": opening_data.stock_value, - } - - -def get_opening_balance(filters, columns, sl_entries): - if not (filters.item_code and filters.warehouse and filters.from_date): - return - - from erpnext.stock.stock_ledger import get_previous_sle - - last_entry = get_previous_sle( - { - "item_code": filters.item_code, - "warehouse_condition": get_warehouse_condition(filters.warehouse), - "posting_date": filters.from_date, - "posting_time": "00:00:00", - } - ) - - # check if any SLEs are actually Opening Stock Reconciliation - for sle in list(sl_entries): - if ( - sle.get("voucher_type") == "Stock Reconciliation" - and sle.posting_date == filters.from_date - and frappe.db.get_value("Stock Reconciliation", sle.voucher_no, "purpose") == "Opening Stock" - ): - last_entry = sle - sl_entries.remove(sle) - - row = { - "item_code": _("'Opening'"), - "qty_after_transaction": last_entry.get("qty_after_transaction", 0), - "valuation_rate": last_entry.get("valuation_rate", 0), - "stock_value": last_entry.get("stock_value", 0), - } - - return row - - -def get_warehouse_condition(warehouse): - warehouse_details = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"], as_dict=1) - if warehouse_details: - return f" exists (select name from `tabWarehouse` wh \ - where wh.lft >= {warehouse_details.lft} and wh.rgt <= {warehouse_details.rgt} and warehouse = wh.name)" - - return "" - - -def get_item_group_condition(item_group, item_table=None): - item_group_details = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"], as_dict=1) - if item_group_details: - if item_table: - ig = frappe.qb.DocType("Item Group") - return item_table.item_group.isin( - frappe.qb.from_(ig) - .select(ig.name) - .where( - (ig.lft >= item_group_details.lft) - & (ig.rgt <= item_group_details.rgt) - & (item_table.item_group == ig.name) - ) - ) - else: - return f"item.item_group in (select ig.name from `tabItem Group` ig \ - where ig.lft >= {item_group_details.lft} and ig.rgt <= {item_group_details.rgt} and item.item_group = ig.name)" - - -def check_inventory_dimension_filters_applied(filters) -> bool: - for dimension in get_inventory_dimensions(): - if dimension.fieldname in filters and filters.get(dimension.fieldname): - return True - - return False From bff490299340aaf4f7ee7f71bcfd04fd6278b32f Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 20 Mar 2025 15:17:59 +0530 Subject: [PATCH 26/99] fix: remove get_items query.run outside of if condition (cherry picked from commit 80c17cc00559d05f8e54b6085bba04c34078c70c) (cherry picked from commit e7b5303782324f223769fccc167e18ade6c073e9) --- erpnext/stock/doctype/serial_no/serial_no.py | 2 +- .../report/available_serial_no/available_serial_no.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index eff2ef14674..db2dc537ddd 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -152,7 +152,7 @@ def get_serial_nos(serial_no): def get_serial_nos_from_serial_and_batch_bundle(serial_and_batch_bundle): table = frappe.qb.DocType("Serial and Batch Entry") query = frappe.qb.from_(table).select(table.serial_no).where(table.parent == serial_and_batch_bundle) - return [item[0] for item in query.run(as_list=True)] + return query.run(pluck=True) def clean_serial_no_string(serial_no: str) -> str: diff --git a/erpnext/stock/report/available_serial_no/available_serial_no.py b/erpnext/stock/report/available_serial_no/available_serial_no.py index 8a20fed848a..d3a1c0c0ed2 100644 --- a/erpnext/stock/report/available_serial_no/available_serial_no.py +++ b/erpnext/stock/report/available_serial_no/available_serial_no.py @@ -80,8 +80,7 @@ def execute(filters=None): sle.update({"in_qty": max(sle.actual_qty, 0), "out_qty": min(sle.actual_qty, 0)}) - if frappe.get_value("Item", sle.item_code, "has_serial_no"): - update_available_serial_nos(available_serial_nos, sle) + update_available_serial_nos(available_serial_nos, sle) if sle.actual_qty: sle["in_out_rate"] = flt(sle.stock_value_difference / sle.actual_qty, precision) @@ -306,10 +305,8 @@ def get_items(filters): if condition := get_item_group_condition(item_group, item): conditions.append(condition) - items = [] if conditions: for condition in conditions: query = query.where(condition) - items = [r[0] for r in query.run()] - return items + return query.run(pluck=True) From c7fead6bb0a273f90adc73356d0dbdd63c055c94 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 26 Mar 2025 14:39:45 +0530 Subject: [PATCH 27/99] perf: take query out of loop (cherry picked from commit 26de9024961fe12e2dbf28a20c80d626368d2b59) (cherry picked from commit 9af50528f1501913c3efc9ef71ea84dd8fdf5324) --- erpnext/stock/doctype/serial_no/serial_no.py | 11 ++++++++--- .../available_serial_no/available_serial_no.py | 12 ++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index db2dc537ddd..928313576f1 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -149,10 +149,15 @@ def get_serial_nos(serial_no): return [s.strip() for s in cstr(serial_no).strip().replace(",", "\n").split("\n") if s.strip()] -def get_serial_nos_from_serial_and_batch_bundle(serial_and_batch_bundle): +def get_serial_nos_from_sle_list(bundles): table = frappe.qb.DocType("Serial and Batch Entry") - query = frappe.qb.from_(table).select(table.serial_no).where(table.parent == serial_and_batch_bundle) - return query.run(pluck=True) + query = frappe.qb.from_(table).select(table.parent, table.serial_no).where(table.parent.isin(bundles)) + data = query.run(as_dict=True) + + result = {} + for d in data: + result.setdefault(d.parent, []).append(d.serial_no) + return result def clean_serial_no_string(serial_no: str) -> str: diff --git a/erpnext/stock/report/available_serial_no/available_serial_no.py b/erpnext/stock/report/available_serial_no/available_serial_no.py index d3a1c0c0ed2..9a42efd1148 100644 --- a/erpnext/stock/report/available_serial_no/available_serial_no.py +++ b/erpnext/stock/report/available_serial_no/available_serial_no.py @@ -7,10 +7,7 @@ from frappe.query_builder.functions import Sum from frappe.utils import cint, flt from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions -from erpnext.stock.doctype.serial_no.serial_no import ( - get_serial_nos, - get_serial_nos_from_serial_and_batch_bundle, -) +from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_serial_nos_from_sle_list from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for from erpnext.stock.report.stock_ledger.stock_ledger import ( check_inventory_dimension_filters_applied, @@ -51,13 +48,16 @@ def execute(filters=None): actual_qty = opening_row.get("qty_after_transaction") stock_value = opening_row.get("stock_value") - available_serial_nos = {} inventory_dimension_filters_applied = check_inventory_dimension_filters_applied(filters) batch_balance_dict = frappe._dict({}) if actual_qty and filters.get("batch_no"): batch_balance_dict[filters.batch_no] = [actual_qty, stock_value] + available_serial_nos = get_serial_nos_from_sle_list( + [sle.serial_and_batch_bundle for sle in sl_entries if sle.serial_and_batch_bundle] + ) + for sle in sl_entries: item_detail = item_details[sle.item_code] @@ -101,7 +101,7 @@ def update_available_serial_nos(available_serial_nos, sle): serial_nos = ( get_serial_nos(sle.serial_no) if sle.serial_no - else get_serial_nos_from_serial_and_batch_bundle(sle.serial_and_batch_bundle) + else available_serial_nos.get(sle.serial_and_batch_bundle) ) key = (sle.item_code, sle.warehouse) if key not in available_serial_nos: From 81f3f43e14c155d6d7465eb188a0b3131c15ddac Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 27 Mar 2025 21:10:03 +0530 Subject: [PATCH 28/99] refactor: split and clean execute function to be more readable (cherry picked from commit 036af54d5446f946e9cbb531034f367416ece23c) (cherry picked from commit 6fedb7b9db5b9f4eea15d857cac844952f95016b) --- .../available_serial_no.py | 96 +++++++++++-------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/erpnext/stock/report/available_serial_no/available_serial_no.py b/erpnext/stock/report/available_serial_no/available_serial_no.py index 9a42efd1148..bdde9c7f3b6 100644 --- a/erpnext/stock/report/available_serial_no/available_serial_no.py +++ b/erpnext/stock/report/available_serial_no/available_serial_no.py @@ -30,27 +30,41 @@ def execute(filters=None): items = get_items(filters) sl_entries = get_stock_ledger_entries(filters, items) item_details = get_item_details(items, sl_entries, include_uom) + + opening_row, actual_qty, stock_value = get_opening_balance_data(filters, columns, sl_entries) + + precision = cint(frappe.db.get_single_value("System Settings", "float_precision")) + data, conversion_factors = process_stock_ledger_entries( + filters, sl_entries, item_details, opening_row, actual_qty, stock_value, precision + ) + + update_included_uom_in_report(columns, data, include_uom, conversion_factors) + return columns, data + + +def get_opening_balance_data(filters, columns, sl_entries): if filters.get("batch_no"): opening_row = get_opening_balance_from_batch(filters, columns, sl_entries) else: opening_row = get_opening_balance(filters, columns, sl_entries) - precision = cint(frappe.db.get_single_value("System Settings", "float_precision")) + actual_qty = opening_row.get("qty_after_transaction") if opening_row else 0 + stock_value = opening_row.get("stock_value") if opening_row else 0 + return opening_row, actual_qty, stock_value + +def process_stock_ledger_entries( + filters, sl_entries, item_details, opening_row, actual_qty, stock_value, precision +): data = [] conversion_factors = [] + if opening_row: data.append(opening_row) conversion_factors.append(0) - actual_qty = stock_value = 0 - if opening_row: - actual_qty = opening_row.get("qty_after_transaction") - stock_value = opening_row.get("stock_value") - - inventory_dimension_filters_applied = check_inventory_dimension_filters_applied(filters) - batch_balance_dict = frappe._dict({}) + if actual_qty and filters.get("batch_no"): batch_balance_dict[filters.batch_no] = [actual_qty, stock_value] @@ -59,42 +73,44 @@ def execute(filters=None): ) for sle in sl_entries: - item_detail = item_details[sle.item_code] - - sle.update(item_detail) - - if filters.get("batch_no") or inventory_dimension_filters_applied: - actual_qty += flt(sle.actual_qty, precision) - stock_value += sle.stock_value_difference - if sle.batch_no: - if not batch_balance_dict.get(sle.batch_no): - batch_balance_dict[sle.batch_no] = [0, 0] - - batch_balance_dict[sle.batch_no][0] += sle.actual_qty - - if sle.voucher_type == "Stock Reconciliation" and not sle.actual_qty: - actual_qty = sle.qty_after_transaction - stock_value = sle.stock_value - - sle.update({"qty_after_transaction": actual_qty, "stock_value": stock_value}) - - sle.update({"in_qty": max(sle.actual_qty, 0), "out_qty": min(sle.actual_qty, 0)}) - + update_stock_ledger_entry( + sle, item_details, filters, actual_qty, stock_value, batch_balance_dict, precision + ) update_available_serial_nos(available_serial_nos, sle) - - if sle.actual_qty: - sle["in_out_rate"] = flt(sle.stock_value_difference / sle.actual_qty, precision) - - elif sle.voucher_type == "Stock Reconciliation": - sle["in_out_rate"] = sle.valuation_rate - data.append(sle) - if include_uom: - conversion_factors.append(item_detail.conversion_factor) + if filters.get("include_uom"): + conversion_factors.append(item_details[sle.item_code].conversion_factor) - update_included_uom_in_report(columns, data, include_uom, conversion_factors) - return columns, data + return data, conversion_factors + + +def update_stock_ledger_entry( + sle, item_details, filters, actual_qty, stock_value, batch_balance_dict, precision +): + item_detail = item_details[sle.item_code] + sle.update(item_detail) + + if filters.get("batch_no") or check_inventory_dimension_filters_applied(filters): + actual_qty += flt(sle.actual_qty, precision) + stock_value += sle.stock_value_difference + + if sle.batch_no: + batch_balance_dict.setdefault(sle.batch_no, [0, 0]) + batch_balance_dict[sle.batch_no][0] += sle.actual_qty + + if sle.voucher_type == "Stock Reconciliation" and not sle.actual_qty: + actual_qty = sle.qty_after_transaction + stock_value = sle.stock_value + + sle.update({"qty_after_transaction": actual_qty, "stock_value": stock_value}) + + sle.update({"in_qty": max(sle.actual_qty, 0), "out_qty": min(sle.actual_qty, 0)}) + + if sle.actual_qty: + sle["in_out_rate"] = flt(sle.stock_value_difference / sle.actual_qty, precision) + elif sle.voucher_type == "Stock Reconciliation": + sle["in_out_rate"] = sle.valuation_rate def update_available_serial_nos(available_serial_nos, sle): From 88f1cf29438d15cb1fbc38bee1522a3b49758a75 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 9 Apr 2025 20:47:10 +0530 Subject: [PATCH 29/99] fix: test file for v15 (cherry picked from commit cc7756dd49534b9338d18c481e016dcf454a6c0e) --- .../report/available_serial_no/test_available_serial_no.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/available_serial_no/test_available_serial_no.py b/erpnext/stock/report/available_serial_no/test_available_serial_no.py index b8741af4506..3a843c991f7 100644 --- a/erpnext/stock/report/available_serial_no/test_available_serial_no.py +++ b/erpnext/stock/report/available_serial_no/test_available_serial_no.py @@ -2,7 +2,7 @@ # See license.txt import frappe -from frappe.tests import IntegrationTestCase +from frappe.tests.utils import FrappeTestCase from frappe.utils import add_days, today from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note @@ -10,7 +10,7 @@ from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt -class TestStockLedgerReeport(IntegrationTestCase): +class TestStockLedgerReport(FrappeTestCase): def setUp(self) -> None: item = create_item("_Test Item with Serial No", is_stock_item=1) item.has_serial_no = 1 From 3db70febe14967b975e0a7036a56f972153da789 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 9 Apr 2025 12:37:02 +0530 Subject: [PATCH 30/99] fix: Recreate Stock Ledgers issue (cherry picked from commit 229a4cef4512c4683f99dc7b9f6bf29d799a3fde) (cherry picked from commit b819e0a61bb706cdd5e4ffb97902ea30dc0bc7ec) --- erpnext/controllers/selling_controller.py | 4 ++-- .../repost_item_valuation/repost_item_valuation.py | 6 +++--- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 ++-- .../doctype/stock_reconciliation/stock_reconciliation.py | 8 ++++++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 8ba8f06c65f..15526f85e34 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -558,7 +558,7 @@ class SellingController(StockController): self.doctype, self.name, d.item_code, self.return_against, item_row=d ) - def update_stock_ledger(self): + def update_stock_ledger(self, allow_negative_stock=False): self.update_reserved_qty() sl_entries = [] @@ -588,7 +588,7 @@ class SellingController(StockController): ): sl_entries.append(self.get_sle_for_source_warehouse(d)) - self.make_sl_entries(sl_entries) + self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) def get_sle_for_source_warehouse(self, item_row): serial_and_batch_bundle = ( diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 880acb65dc3..ecbb10b6cb5 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -260,10 +260,10 @@ class RepostItemValuation(Document): """Recreate Stock Ledger Entries for the transaction.""" if self.based_on == "Transaction" and self.recreate_stock_ledgers: doc = frappe.get_doc(self.voucher_type, self.voucher_no) - doc.docstatus = 2 - doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) + doc.db_set("docstatus", 2) + doc.update_stock_ledger(allow_negative_stock=True) - doc.docstatus = 1 + doc.db_set("docstatus", 1) doc.update_stock_ledger(allow_negative_stock=True) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 52655c9ac03..efa263a7573 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1347,7 +1347,7 @@ class StockEntry(StockController): ) ) - def update_stock_ledger(self): + def update_stock_ledger(self, allow_negative_stock=False): sl_entries = [] finished_item_row = self.get_finished_item_row() @@ -1361,7 +1361,7 @@ class StockEntry(StockController): if self.docstatus == 2: sl_entries.reverse() - self.make_sl_entries(sl_entries) + self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) def get_finished_item_row(self): finished_item_row = None diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 46f9f9dcf1b..32c4a743999 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -663,7 +663,7 @@ class StockReconciliation(StockController): title=_("Stock Reservation"), ) - def update_stock_ledger(self): + def update_stock_ledger(self, allow_negative_stock=False): """find difference between current and expected entries and create stock ledger entries based on the difference""" from erpnext.stock.stock_ledger import get_previous_sle @@ -719,7 +719,11 @@ class StockReconciliation(StockController): sl_entries.append(self.get_sle_for_items(row)) if sl_entries: - allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) + if not allow_negative_stock: + allow_negative_stock = cint( + frappe.db.get_single_value("Stock Settings", "allow_negative_stock") + ) + self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) def make_adjustment_entry(self, row, sl_entries): From eaaac6e59634a9070c65b9a1d05376dc14f32739 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 9 Apr 2025 23:47:55 +0530 Subject: [PATCH 31/99] fix: bypass validation during reposting (cherry picked from commit 3697b9fd9b4e0b0725fc1c3dadb7c960c076ceb0) (cherry picked from commit 01aad96b1045c048d0350686f2701a52cb06a783) --- .../purchase_receipt/test_purchase_receipt.py | 4 ++++ .../serial_and_batch_bundle.py | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index b7aa97ddb54..4b69b99e69a 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3989,6 +3989,8 @@ class TestPurchaseReceipt(FrappeTestCase): make_purchase_return, ) + frappe.flags.through_repost_item_valuation = False + sn_item_code = make_item( "Test Serial No for Validation", {"has_serial_no": 1, "serial_no_series": "SN-TSNFVAL-.#####"} ).name @@ -4020,6 +4022,8 @@ class TestPurchaseReceipt(FrappeTestCase): make_purchase_return, ) + frappe.flags.through_repost_item_valuation = False + batch_item_code = make_item( "Test Batch No for Validation", {"has_batch_no": 1, "batch_number_series": "BT-TSNFVAL-.#####", "create_new_batch": 1}, diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 8abe5b7e082..5922ecbf026 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -322,6 +322,15 @@ class SerialandBatchBundle(Document): else: valuation_rate = valuation_details["batches"].get(row.batch_no) + if frappe.flags.through_repost_item_valuation and not valuation_rate: + # if different serial nos / batches are returned + if row.serial_no: + serial_nos = sorted(list(valuation_details["serial_nos"].keys())) + valuation_rate = valuation_details["serial_nos"].get(serial_nos[cint(row.idx) - 1]) + else: + batches = sorted(list(valuation_details["batches"].keys())) + valuation_rate = valuation_details["batches"].get(batches[cint(row.idx) - 1]) + row.incoming_rate = flt(valuation_rate) row.stock_value_difference = flt(row.qty) * flt(row.incoming_rate) @@ -337,6 +346,9 @@ class SerialandBatchBundle(Document): self.set_incoming_rate_for_inward_transaction(row, save) def validate_returned_serial_batch_no(self, return_against, row, original_inv_details): + if frappe.flags.through_repost_item_valuation: + return + if row.serial_no and row.serial_no not in original_inv_details["serial_nos"]: self.throw_error_message( _( From 1326ba5326dc98e72da6fa861363ed3a0429f1e3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 10 Apr 2025 07:31:47 +0000 Subject: [PATCH 32/99] chore(release): Bumped to Version 15.57.0 # [15.57.0](https://github.com/frappe/erpnext/compare/v15.56.0...v15.57.0) (2025-04-10) ### Bug Fixes * bypass validation during reposting ([eaaac6e](https://github.com/frappe/erpnext/commit/eaaac6e59634a9070c65b9a1d05376dc14f32739)) * item code not showing in the error message ([837509a](https://github.com/frappe/erpnext/commit/837509ae471a258568017f20f70ce2cd94789654)) * Recreate Stock Ledgers issue ([3db70fe](https://github.com/frappe/erpnext/commit/3db70febe14967b975e0a7036a56f972153da789)) * remove get_items query.run outside of if condition ([bff4902](https://github.com/frappe/erpnext/commit/bff490299340aaf4f7ee7f71bcfd04fd6278b32f)) * test file for v15 ([88f1cf2](https://github.com/frappe/erpnext/commit/88f1cf29438d15cb1fbc38bee1522a3b49758a75)) ### Features * available serial no report ([0f6a7ed](https://github.com/frappe/erpnext/commit/0f6a7edb53c561649ab92c6e00269252e4cd80d6)) ### Performance Improvements * take query out of loop ([c7fead6](https://github.com/frappe/erpnext/commit/c7fead6bb0a273f90adc73356d0dbdd63c055c94)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3244606893b..5eae9777a77 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.56.0" +__version__ = "15.57.0" def get_default_company(user=None): From dded682feb3bbc1856402b6a315cdb6cb41ce772 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 11 Apr 2025 16:16:09 +0530 Subject: [PATCH 33/99] fix: allow to use batchwise valuation for moving average items (cherry picked from commit 65ba79bb857c5d55c9e3b87c15a7f150901802c8) (cherry picked from commit e479975f54a760a8ac104ebbcb71920d733c56cc) --- erpnext/stock/doctype/batch/batch.py | 4 -- .../purchase_receipt/test_purchase_receipt.py | 2 +- .../doctype/stock_entry/test_stock_entry.py | 47 +++++++++++++++++++ .../test_stock_ledger_entry.py | 2 +- erpnext/stock/serial_batch_bundle.py | 4 +- 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 47acebb6634..3c46fcb5aad 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -160,10 +160,6 @@ class Batch(Document): from erpnext.stock.utils import get_valuation_method if self.is_new(): - if get_valuation_method(self.item) == "Moving Average": - self.use_batchwise_valuation = 0 - return - if frappe.db.get_single_value("Stock Settings", "do_not_use_batchwise_valuation"): self.use_batchwise_valuation = 0 return diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 4b69b99e69a..8e8532a904b 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3327,7 +3327,7 @@ class TestPurchaseReceipt(FrappeTestCase): bundle = dn.items[0].serial_and_batch_bundle valuation_rate = frappe.db.get_value("Serial and Batch Bundle", bundle, "avg_rate") - self.assertEqual(valuation_rate, 150) + self.assertEqual(valuation_rate, 100.0) doc = frappe.get_doc("Stock Settings") doc.do_not_use_batchwise_valuation = 1 diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 0791877f522..7fa31cae57b 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1960,6 +1960,53 @@ class TestStockEntry(FrappeTestCase): self.assertEqual(se.items[0].amount, 300) self.assertEqual(se.items[0].basic_amount, 300) + def test_use_batch_wise_valuation_for_moving_average_item(self): + item_code = "_Test Use Batch Wise MA Valuation Item" + + make_item( + item_code, + { + "is_stock_item": 1, + "valuation_method": "Moving Average", + "has_batch_no": 1, + "create_new_batch": 1, + "batch_naming_series": "Test-UBWVMAV-T-NNS.#####", + }, + ) + + frappe.db.set_single_value("Stock Settings", "do_not_use_batchwise_valuation", 0) + + batches = [] + se = make_stock_entry( + item_code=item_code, + qty=10, + to_warehouse="_Test Warehouse - _TC", + basic_rate=100, + posting_date=add_days(nowdate(), -2), + ) + + batches.append(get_batch_from_bundle(se.items[0].serial_and_batch_bundle)) + + se = make_stock_entry( + item_code=item_code, + qty=10, + to_warehouse="_Test Warehouse - _TC", + basic_rate=300, + posting_date=add_days(nowdate(), -1), + ) + + batches.append(get_batch_from_bundle(se.items[0].serial_and_batch_bundle)) + + se = make_stock_entry( + item_code=item_code, + qty=5, + from_warehouse="_Test Warehouse - _TC", + batch_no=batches[1], + posting_date=nowdate(), + ) + + self.assertEqual(se.items[0].basic_rate, 300) + def make_serialized_item(**args): args = frappe._dict(args) diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 8658316ad90..6beab7ce48d 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -523,7 +523,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): dns = create_delivery_note_entries_for_batchwise_item_valuation_test(dn_entry_list) sle_details = fetch_sle_details_for_doc_list(dns, ["stock_value_difference"]) svd_list = [-1 * d["stock_value_difference"] for d in sle_details] - expected_incoming_rates = expected_abs_svd = [100.0, 100.0, 100.0, 100.0] + expected_incoming_rates = expected_abs_svd = [75.0, 125.0, 75.0, 125.0] self.assertEqual(expected_abs_svd, svd_list, "Incorrect 'Stock Value Difference' values") for dn, _incoming_rate in zip(dns, expected_incoming_rates, strict=False): diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 8e64b4e856f..e127960d6bb 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -692,7 +692,9 @@ class BatchNoValuation(DeprecatedBatchNoValuation): self.batchwise_valuation_batches = [] self.non_batchwise_valuation_batches = [] - if get_valuation_method(self.sle.item_code) == "Moving Average": + if get_valuation_method(self.sle.item_code) == "Moving Average" and frappe.db.get_single_value( + "Stock Settings", "do_not_use_batchwise_valuation" + ): self.non_batchwise_valuation_batches = self.batches return From ff41ed534b094811ac00f5676f238df67be59826 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 11 Apr 2025 12:22:21 +0000 Subject: [PATCH 34/99] chore(release): Bumped to Version 15.57.1 ## [15.57.1](https://github.com/frappe/erpnext/compare/v15.57.0...v15.57.1) (2025-04-11) ### Bug Fixes * allow to use batchwise valuation for moving average items ([dded682](https://github.com/frappe/erpnext/commit/dded682feb3bbc1856402b6a315cdb6cb41ce772)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5eae9777a77..fb0453d3826 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.57.0" +__version__ = "15.57.1" def get_default_company(user=None): From 18dd128838fb6b54bb214e5498c3d0fb8c342873 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 11 Apr 2025 19:17:47 +0530 Subject: [PATCH 35/99] fix: condition for use_batchwise_valuation (cherry picked from commit cc171d970665990b455a3334f7c9e110d232108a) (cherry picked from commit 0ff7465e278893442ac4e2f7520440261d263949) --- erpnext/stock/doctype/batch/batch.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 3c46fcb5aad..800d4f70c40 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -160,7 +160,9 @@ class Batch(Document): from erpnext.stock.utils import get_valuation_method if self.is_new(): - if frappe.db.get_single_value("Stock Settings", "do_not_use_batchwise_valuation"): + if get_valuation_method(self.item) == "Moving Average" and frappe.db.get_single_value( + "Stock Settings", "do_not_use_batchwise_valuation" + ): self.use_batchwise_valuation = 0 return From b8436dbd215a8bbbe932af3cbc15a4551e6e587c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 11 Apr 2025 18:58:09 +0530 Subject: [PATCH 36/99] fix: removed display depends on (cherry picked from commit e0bf45e03bf21556fe2c131e66a49bfc1d5202fb) (cherry picked from commit 00b25537f4f452b400eb2c4b6479fb03e1b13917) # Conflicts: # erpnext/stock/doctype/stock_settings/stock_settings.json --- .../doctype/stock_settings/stock_settings.json | 5 ++++- .../doctype/stock_settings/stock_settings.py | 16 ---------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 551104688a2..e62a1159a8b 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -456,7 +456,6 @@ }, { "default": "0", - "depends_on": "eval:doc.valuation_method === \"Moving Average\"", "description": "If enabled, the system will use the moving average valuation method to calculate the valuation rate for the batched items and will not consider the individual batch-wise incoming rate.", "fieldname": "do_not_use_batchwise_valuation", "fieldtype": "Check", @@ -510,7 +509,11 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], +<<<<<<< HEAD "modified": "2025-03-31 15:34:20.752065", +======= + "modified": "2025-04-11 18:56:35.781929", +>>>>>>> 00b25537f4 (fix: removed display depends on) "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py index 4de8057a006..128e19c5b00 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.py +++ b/erpnext/stock/doctype/stock_settings/stock_settings.py @@ -105,22 +105,6 @@ class StockSettings(Document): self.validate_stock_reservation() self.change_precision_for_for_sales() self.change_precision_for_purchase() - self.validate_use_batch_wise_valuation() - - def validate_use_batch_wise_valuation(self): - if not self.do_not_use_batchwise_valuation: - return - - if self.valuation_method == "FIFO": - frappe.throw(_("Cannot disable batch wise valuation for FIFO valuation method.")) - - if frappe.get_all( - "Item", filters={"valuation_method": "FIFO", "is_stock_item": 1, "has_batch_no": 1}, limit=1 - ): - frappe.throw(_("Can't disable batch wise valuation for items with FIFO valuation method.")) - - if frappe.get_all("Batch", filters={"use_batchwise_valuation": 1}, limit=1): - frappe.throw(_("Can't disable batch wise valuation for active batches.")) def validate_warehouses(self): warehouse_fields = ["default_warehouse", "sample_retention_warehouse"] From fa7675488d1fbbd44d8fffbdd804d8577fa7b439 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 11 Apr 2025 20:26:43 +0530 Subject: [PATCH 37/99] chore: fix conflicts --- erpnext/stock/doctype/stock_settings/stock_settings.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index e62a1159a8b..ce0ff8c2f9b 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -509,11 +509,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], -<<<<<<< HEAD - "modified": "2025-03-31 15:34:20.752065", -======= "modified": "2025-04-11 18:56:35.781929", ->>>>>>> 00b25537f4 (fix: removed display depends on) "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", From 8a33866a8c19ea900b1891a92306751b2177bc94 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 11 Apr 2025 15:16:34 +0000 Subject: [PATCH 38/99] chore(release): Bumped to Version 15.57.2 ## [15.57.2](https://github.com/frappe/erpnext/compare/v15.57.1...v15.57.2) (2025-04-11) ### Bug Fixes * condition for use_batchwise_valuation ([18dd128](https://github.com/frappe/erpnext/commit/18dd128838fb6b54bb214e5498c3d0fb8c342873)) * removed display depends on ([b8436db](https://github.com/frappe/erpnext/commit/b8436dbd215a8bbbe932af3cbc15a4551e6e587c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index fb0453d3826..4a8a63a115b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.57.1" +__version__ = "15.57.2" def get_default_company(user=None): From cd68832aa092e4a389cef6a5c23dae714f401bdc Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 11 Apr 2025 15:06:03 +0530 Subject: [PATCH 39/99] fix: correct doctype in item_wise_purchase register (cherry picked from commit b8b8dce733f92acc5d77b005f3e85befe20de671) --- .../item_wise_purchase_register/item_wise_purchase_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index b313ed8b173..ae822c5b413 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -369,7 +369,7 @@ def get_items(filters, additional_table_columns): from frappe.desk.reportview import build_match_conditions query, params = query.walk() - match_conditions = build_match_conditions("Sales Invoice") + match_conditions = build_match_conditions(doctype) if match_conditions: query += " and " + match_conditions From 065c9fa85f8da02985c04c365daceb642254addd Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 12 Apr 2025 02:01:06 +0000 Subject: [PATCH 40/99] chore(release): Bumped to Version 15.57.3 ## [15.57.3](https://github.com/frappe/erpnext/compare/v15.57.2...v15.57.3) (2025-04-12) ### Bug Fixes * correct doctype in item_wise_purchase register ([cd68832](https://github.com/frappe/erpnext/commit/cd68832aa092e4a389cef6a5c23dae714f401bdc)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4a8a63a115b..154cd5906c9 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.57.2" +__version__ = "15.57.3" def get_default_company(user=None): From 70fa366216cfa1363afb412228e06a0cfdb847ac Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 13 Apr 2025 18:53:11 +0530 Subject: [PATCH 41/99] fix: Group GLs by account for TB generation (cherry picked from commit f894c6d275fd6c8d94c4df0ad8dcdb87bb5f14d4) (cherry picked from commit 416d9bce2ce1f1e58091a68313c6542aa548b6bd) --- .../accounts/report/financial_statements.py | 20 +++++++++++++++---- .../report/trial_balance/trial_balance.py | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index faa65fb425e..67154455f95 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -9,6 +9,7 @@ import re import frappe from frappe import _ +from frappe.query_builder.functions import Sum from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate from pypika.terms import ExistsCriterion @@ -428,6 +429,7 @@ def set_gl_entries_by_account( root_type=None, ignore_closing_entries=False, ignore_opening_entries=False, + group_by_account=False, ): """Returns a dict like { "account": [gl entries], ... }""" gl_entries = [] @@ -459,6 +461,7 @@ def set_gl_entries_by_account( root_type, ignore_closing_entries, last_period_closing_voucher[0].name, + group_by_account=group_by_account, ) from_date = add_days(last_period_closing_voucher[0].period_end_date, 1) ignore_opening_entries = True @@ -473,6 +476,7 @@ def set_gl_entries_by_account( root_type, ignore_closing_entries, ignore_opening_entries=ignore_opening_entries, + group_by_account=group_by_account, ) if filters and filters.get("presentation_currency"): @@ -495,21 +499,29 @@ def get_accounting_entries( ignore_closing_entries=None, period_closing_voucher=None, ignore_opening_entries=False, + group_by_account=False, ): gl_entry = frappe.qb.DocType(doctype) query = ( frappe.qb.from_(gl_entry) .select( gl_entry.account, - gl_entry.debit, - gl_entry.credit, - gl_entry.debit_in_account_currency, - gl_entry.credit_in_account_currency, + gl_entry.debit if not group_by_account else Sum(gl_entry.debit).as_("debit"), + gl_entry.credit if not group_by_account else Sum(gl_entry.credit).as_("credit"), + gl_entry.debit_in_account_currency + if not group_by_account + else Sum(gl_entry.debit_in_account_currency).as_("debit_in_account_currency"), + gl_entry.credit_in_account_currency + if not group_by_account + else Sum(gl_entry.credit_in_account_currency).as_("credit_in_account_currency"), gl_entry.account_currency, ) .where(gl_entry.company == filters.company) ) + if group_by_account: + query = query.groupby(gl_entry.account) + ignore_is_opening = frappe.db.get_single_value( "Accounts Settings", "ignore_is_opening_check_for_reporting" ) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 9d1b86ebcdc..5575426dfff 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -116,6 +116,7 @@ def get_data(filters): root_rgt=None, ignore_closing_entries=not flt(filters.with_period_closing_entry_for_current_period), ignore_opening_entries=True, + group_by_account=True, ) calculate_values( From 1da25770357a5e849cf38cab575257d98ccc1dc9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 13 Apr 2025 14:13:43 +0000 Subject: [PATCH 42/99] chore(release): Bumped to Version 15.57.4 ## [15.57.4](https://github.com/frappe/erpnext/compare/v15.57.3...v15.57.4) (2025-04-13) ### Bug Fixes * Group GLs by account for TB generation ([70fa366](https://github.com/frappe/erpnext/commit/70fa366216cfa1363afb412228e06a0cfdb847ac)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 154cd5906c9..4620136b5c6 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.57.3" +__version__ = "15.57.4" def get_default_company(user=None): From 969c3549d5c48f40434c8b885f4730184c8646a6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 14 Apr 2025 12:27:51 +0530 Subject: [PATCH 43/99] fix: revert #46900 - against_voucher filter in general ledger (cherry picked from commit adb331ef7132374ac1b00a3a39287184a435a31a) # Conflicts: # erpnext/accounts/report/general_ledger/general_ledger.py --- .../report/general_ledger/general_ledger.js | 5 +++++ .../report/general_ledger/general_ledger.py | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index bcd850c0896..6fa846910a6 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -52,6 +52,11 @@ frappe.query_reports["General Ledger"] = { frappe.query_report.set_filter_value("group_by", "Group by Voucher (Consolidated)"); }, }, + { + fieldname: "against_voucher_no", + label: __("Against Voucher No"), + fieldtype: "Data", + }, { fieldtype: "Break", }, diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 73a16e730ac..1c2a1b9c753 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -224,6 +224,9 @@ def get_conditions(filters): if filters.get("voucher_no"): conditions.append("voucher_no=%(voucher_no)s") + if filters.get("against_voucher_no"): + conditions.append("against_voucher=%(against_voucher_no)s") + if filters.get("ignore_err"): err_journals = frappe.db.get_all( "Journal Entry", @@ -487,12 +490,22 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, tot data[key][rev_dr_or_cr] = 0 data[key][rev_dr_or_cr + "_in_account_currency"] = 0 + if data[key].against_voucher and gle.against_voucher: + data[key].against_voucher += ", " + gle.against_voucher + from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) show_opening_entries = filters.get("show_opening_entries") for gle in gl_entries: group_by_value = gle.get(group_by) +<<<<<<< HEAD gle.voucher_type = gle.voucher_type +======= + gle.voucher_subtype = _(gle.voucher_subtype) + gle.against_voucher_type = _(gle.against_voucher_type) + gle.remarks = _(gle.remarks) + gle.party_type = _(gle.party_type) +>>>>>>> adb331ef71 (fix: revert #46900 - against_voucher filter in general ledger) if gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries): if not group_by_voucher_consolidated: @@ -689,6 +702,14 @@ def get_columns(filters): columns.extend( [ + {"label": _("Against Voucher Type"), "fieldname": "against_voucher_type", "width": 100}, + { + "label": _("Against Voucher"), + "fieldname": "against_voucher", + "fieldtype": "Dynamic Link", + "options": "against_voucher_type", + "width": 100, + }, {"label": _("Supplier Invoice No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 100}, ] ) From a0908522c1a7c1621ecd84e73c59d71ca3af7945 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 14 Apr 2025 13:21:33 +0530 Subject: [PATCH 44/99] chore: resolve conflict --- erpnext/accounts/report/general_ledger/general_ledger.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 1c2a1b9c753..a62ba2e3732 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -498,14 +498,7 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, tot for gle in gl_entries: group_by_value = gle.get(group_by) -<<<<<<< HEAD gle.voucher_type = gle.voucher_type -======= - gle.voucher_subtype = _(gle.voucher_subtype) - gle.against_voucher_type = _(gle.against_voucher_type) - gle.remarks = _(gle.remarks) - gle.party_type = _(gle.party_type) ->>>>>>> adb331ef71 (fix: revert #46900 - against_voucher filter in general ledger) if gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries): if not group_by_voucher_consolidated: From 2eb7a688cba73582e57f58905a8e68d401b92987 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 14 Apr 2025 08:24:20 +0000 Subject: [PATCH 45/99] chore(release): Bumped to Version 15.57.5 ## [15.57.5](https://github.com/frappe/erpnext/compare/v15.57.4...v15.57.5) (2025-04-14) ### Bug Fixes * revert [#46900](https://github.com/frappe/erpnext/issues/46900) - against_voucher filter in general ledger ([969c354](https://github.com/frappe/erpnext/commit/969c3549d5c48f40434c8b885f4730184c8646a6)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4620136b5c6..6e6cb3119a3 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.57.4" +__version__ = "15.57.5" def get_default_company(user=None): From 929d177b7d3da18c23543b1a2ac90ed09390af67 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 16 Apr 2025 04:05:56 +0000 Subject: [PATCH 46/99] chore(release): Bumped to Version 15.58.0 # [15.58.0](https://github.com/frappe/erpnext/compare/v15.57.5...v15.58.0) (2025-04-16) ### Bug Fixes * added missing project field on pos profile ([8e9ddb7](https://github.com/frappe/erpnext/commit/8e9ddb7a698266b287606a6c1e8c995025cf8fdb)) * allow to use batchwise valuation for moving average items ([e479975](https://github.com/frappe/erpnext/commit/e479975f54a760a8ac104ebbcb71920d733c56cc)) * backport translations from develop ([#47104](https://github.com/frappe/erpnext/issues/47104)) ([188c4f8](https://github.com/frappe/erpnext/commit/188c4f896a1df98f237474efc4a0377985eec8ec)) * batchwise valuation for MA item ([debfcdc](https://github.com/frappe/erpnext/commit/debfcdc61ff6256b22fb7ba15e335f2937d07d89)) * bypass validation during reposting ([01aad96](https://github.com/frappe/erpnext/commit/01aad96b1045c048d0350686f2701a52cb06a783)) * child values for tree doctypes and query refactor ([537a8ef](https://github.com/frappe/erpnext/commit/537a8efe7a25a1b6bc057e74629888e5199b6dca)) * clarify confirmation message ([35daf66](https://github.com/frappe/erpnext/commit/35daf669fe9b5bdfe3a9f1976f0e39b9c52108eb)) * condition for use_batchwise_valuation ([0ff7465](https://github.com/frappe/erpnext/commit/0ff7465e278893442ac4e2f7520440261d263949)) * configuration to accept partial payment in pos invoice ([#47052](https://github.com/frappe/erpnext/issues/47052)) ([a944853](https://github.com/frappe/erpnext/commit/a944853b56a7985a6553500c3dcebec344a2d9bf)) * consider negative stock qty in stock reco ([603f737](https://github.com/frappe/erpnext/commit/603f737c9915231769217e8764a96d4c40ace611)) * correct doctype in item_wise_purchase register ([b2fb4fb](https://github.com/frappe/erpnext/commit/b2fb4fba51d1118f5459dc00c48f7849aaf2038d)) * correct function name ([393d245](https://github.com/frappe/erpnext/commit/393d2459b9980235cb5223b15a43656dd03a18b2)) * correct outstanding amount for invoice in dunning ([b7c3fa2](https://github.com/frappe/erpnext/commit/b7c3fa23d278ca328072e43dd2e9834754b028df)) * current batch qty showing zero in the stock reconciliation ([24c8a06](https://github.com/frappe/erpnext/commit/24c8a06520206c3ef69dab802b210838c8eeed10)) * enabled allow on submit for asset name field (backport [#47093](https://github.com/frappe/erpnext/issues/47093)) ([#47094](https://github.com/frappe/erpnext/issues/47094)) ([3f652bd](https://github.com/frappe/erpnext/commit/3f652bd4e1569497718c52411216c5f49f9de282)) * fetch exchange rate while creating inter-company order and invoice ([aa0b93d](https://github.com/frappe/erpnext/commit/aa0b93d0b29ed9722129fd372d695b0b7d2e02e7)) * go for lower case "on" because we already have translations for that ([7cf83ff](https://github.com/frappe/erpnext/commit/7cf83ffce72867fdf5a999c6e148516eed1db8ae)) * Group GLs by account for TB generation ([416d9bc](https://github.com/frappe/erpnext/commit/416d9bce2ce1f1e58091a68313c6542aa548b6bd)) * item code not showing in the error message ([663e2b7](https://github.com/frappe/erpnext/commit/663e2b7e6c9cb5821cd90d3e94657590afc103dc)) * make report's "printed on" translatable ([18e9a98](https://github.com/frappe/erpnext/commit/18e9a9881ce963f3013767a238680ec3f5158297)) * map tax table while creating purchase order from sales order ([127c7b9](https://github.com/frappe/erpnext/commit/127c7b93ac75480bf56f1386c4e869a62c8a22ba)) * **Payment Entry:** set account type if missing (backport [#47069](https://github.com/frappe/erpnext/issues/47069)) ([#47070](https://github.com/frappe/erpnext/issues/47070)) ([8e02a9b](https://github.com/frappe/erpnext/commit/8e02a9bc28237995c7026b600e4a61d75772b8f9)) * precision issue on qty_to_be_reserved ([c8691b6](https://github.com/frappe/erpnext/commit/c8691b65166ca1f34fedfef4a571db15e147683a)) * recognize trigger from child table ([cf00d42](https://github.com/frappe/erpnext/commit/cf00d427998f7f59741df7644c02e8c732c2001e)) * Recreate Stock Ledgers issue ([b819e0a](https://github.com/frappe/erpnext/commit/b819e0a61bb706cdd5e4ffb97902ea30dc0bc7ec)) * remove get_items query.run outside of if condition ([e7b5303](https://github.com/frappe/erpnext/commit/e7b5303782324f223769fccc167e18ade6c073e9)) * remove redundant letter head ([7896f8a](https://github.com/frappe/erpnext/commit/7896f8a855a3cd6217e0b36a2517922c562c8080)) * removed display depends on ([00b2553](https://github.com/frappe/erpnext/commit/00b25537f4f452b400eb2c4b6479fb03e1b13917)) * resolved conflicts ([bde55d2](https://github.com/frappe/erpnext/commit/bde55d2a07d81bac86e9a1253e7a1b4043e54823)) * revert [#46900](https://github.com/frappe/erpnext/issues/46900) - against_voucher filter in general ledger ([da65f44](https://github.com/frappe/erpnext/commit/da65f44a4749585756bed2c8caa5d82e6556db91)) * test file for v15 ([cc7756d](https://github.com/frappe/erpnext/commit/cc7756dd49534b9338d18c481e016dcf454a6c0e)) * translatability ([79ed02b](https://github.com/frappe/erpnext/commit/79ed02bb2ca4986be96f230f43b68c8c82d1e9e8)) * update the modified date in for SLEs and GLs after rename ([21f0dcb](https://github.com/frappe/erpnext/commit/21f0dcbcc3eb7df7fb540cf75c626c54fa0f32b2)) * use source_fieldname to validate inventory dimension ([250b670](https://github.com/frappe/erpnext/commit/250b67076d19579164783badb95d140254f89afa)) * use the actual field label ([0c260ba](https://github.com/frappe/erpnext/commit/0c260baacd50c1af90acfd885016b30a9a9dae4b)) * wording ([db647a4](https://github.com/frappe/erpnext/commit/db647a4e4274a8a05a6726acffe016e8cd421361)) ### Features * Allow to Make Quality Inspection after Purchase / Delivery ([2e6ba91](https://github.com/frappe/erpnext/commit/2e6ba91589814d6cca57e3cbf94088254ceac247)) * available serial no report ([c472af8](https://github.com/frappe/erpnext/commit/c472af87b2916cf0508815a84a7aa8506a6fd48a)) * clear payment terms and schedule ([830290c](https://github.com/frappe/erpnext/commit/830290c859cc8dae0a2e33a6ae9d97b8505d08a0)) * fetch source_fieldname for inventory dimension ([2ed6c21](https://github.com/frappe/erpnext/commit/2ed6c211f95ca29222cd10c79e7275b47d4d2eff)) * **regional:** Address Template for Germany & Add Switzerland Template ([#46737](https://github.com/frappe/erpnext/issues/46737)) ([42479d9](https://github.com/frappe/erpnext/commit/42479d9a7ff1da255f5fb030083310faaa27ab37)) * update due date in payment schedule ([0b0a6b8](https://github.com/frappe/erpnext/commit/0b0a6b8cfa65a40dc5cba57a6d739a336130acb0)) ### Performance Improvements * refactored customer ledger summary for performance ([50b2196](https://github.com/frappe/erpnext/commit/50b21960203cf5f14b603bec3dd680c39a453cf6)) * take query out of loop ([9af5052](https://github.com/frappe/erpnext/commit/9af50528f1501913c3efc9ef71ea84dd8fdf5324)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6e6cb3119a3..66770c09e37 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.57.5" +__version__ = "15.58.0" def get_default_company(user=None): From 5c04d183bfac556345010d440108be2857d5bf7a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 16 Apr 2025 10:18:29 +0530 Subject: [PATCH 47/99] fix: revert #46903 - disable changing customer on opportunity (cherry picked from commit fc16199a49141315b5e1e78a178a49f7d8cc6b09) --- erpnext/selling/doctype/quotation/quotation.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index b9d46e0b84b..2e88ba9e482 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -70,7 +70,8 @@ erpnext.selling.QuotationController = class QuotationController extends erpnext. onload(doc, dt, dn) { super.onload(doc, dt, dn); - this.frm.trigger("disable_customer_if_creating_from_opportunity"); + // TODO: think of better way to do this + // this.frm.trigger("disable_customer_if_creating_from_opportunity"); } party_name() { var me = this; From 741f29e57a4fc8862bf2f541602c2ed716e77921 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 16 Apr 2025 05:15:20 +0000 Subject: [PATCH 48/99] chore(release): Bumped to Version 15.58.1 ## [15.58.1](https://github.com/frappe/erpnext/compare/v15.58.0...v15.58.1) (2025-04-16) ### Bug Fixes * revert [#46903](https://github.com/frappe/erpnext/issues/46903) - disable changing customer on opportunity ([5c04d18](https://github.com/frappe/erpnext/commit/5c04d183bfac556345010d440108be2857d5bf7a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 66770c09e37..98b45458915 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.58.0" +__version__ = "15.58.1" def get_default_company(user=None): From 0287f34928a61f6cde17c5f5fea354e346204c30 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Thu, 17 Apr 2025 00:12:43 +0530 Subject: [PATCH 49/99] fix: add group by after user permission condition (cherry picked from commit 756d496235af3223e64ae33c51fd5b63d91856da) --- erpnext/accounts/report/financial_statements.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 67154455f95..0d1e185382d 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -519,9 +519,6 @@ def get_accounting_entries( .where(gl_entry.company == filters.company) ) - if group_by_account: - query = query.groupby(gl_entry.account) - ignore_is_opening = frappe.db.get_single_value( "Accounts Settings", "ignore_is_opening_check_for_reporting" ) @@ -551,6 +548,9 @@ def get_accounting_entries( if match_conditions: query += "and" + match_conditions + if group_by_account: + query += " GROUP BY `account`" + return frappe.db.sql(query, params, as_dict=True) From 00a8503dd7359456d23ce0a65eb875684d878242 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 17 Apr 2025 10:54:22 +0000 Subject: [PATCH 50/99] chore(release): Bumped to Version 15.58.2 ## [15.58.2](https://github.com/frappe/erpnext/compare/v15.58.1...v15.58.2) (2025-04-17) ### Bug Fixes * add group by after user permission condition ([0287f34](https://github.com/frappe/erpnext/commit/0287f34928a61f6cde17c5f5fea354e346204c30)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 98b45458915..3f68cc0dd90 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.58.1" +__version__ = "15.58.2" def get_default_company(user=None): From 4c3044cd70b041e7cfa77c993a188fd484322d9d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 22 Apr 2025 13:47:39 +0000 Subject: [PATCH 51/99] chore(release): Bumped to Version 15.59.0 # [15.59.0](https://github.com/frappe/erpnext/compare/v15.58.2...v15.59.0) (2025-04-22) ### Bug Fixes * `TypeError` in group field filter in supplier ledger summary ([3b349f4](https://github.com/frappe/erpnext/commit/3b349f44b1eb7256d14e2895bfa26ab81403a46a)) * add group by after user permission condition ([f07c3d9](https://github.com/frappe/erpnext/commit/f07c3d91240d6bf7f0922cdd298ec38bcc54c3f4)) * backslash in url ([f6edd5a](https://github.com/frappe/erpnext/commit/f6edd5aa7dd31a3db3ff0775413f3fc522c1f7f2)) * change get_url_to_form to get_link_to_form ([982a68b](https://github.com/frappe/erpnext/commit/982a68b71a7215168ad8075e900582689229ea2b)) * cherry pick ([20aba54](https://github.com/frappe/erpnext/commit/20aba541c4218fd4847ec4feb9983aba6ec73d0f)) * consider per_ordered instead of per_billed when creating PO from MR ([be154a4](https://github.com/frappe/erpnext/commit/be154a469fb2b0955f43fffc2365be6b20a43b65)) * correct error message in validate_internal_transfer_qty ([b8a7f6d](https://github.com/frappe/erpnext/commit/b8a7f6dac1bff8f54868ae1251b9beb02f6f45e8)) * create default warehouse (backport [#47125](https://github.com/frappe/erpnext/issues/47125)) ([#47131](https://github.com/frappe/erpnext/issues/47131)) ([ad177e0](https://github.com/frappe/erpnext/commit/ad177e08b80238b2ca3b6f93ed6a8261dbe7bc3f)) * disbaled UOM showing in the list ([3d4f3e1](https://github.com/frappe/erpnext/commit/3d4f3e1be7a6fac4fe6dfef3effca13443851390)) * distributed discounts on si ([ad05e6d](https://github.com/frappe/erpnext/commit/ad05e6dec24a9cb67cb649aa7e91ca1a03575f4d)) * **Employee:** remove User Permissions if create_user_permission is unchecked ([7ab81b7](https://github.com/frappe/erpnext/commit/7ab81b7e546c633a1902e24968238ee9e87ec629)) * expense account in stock entry ([2f1f229](https://github.com/frappe/erpnext/commit/2f1f2291441b10e97491c725ce3881c56e857df2)) * get total without rounding off tax amounts for distributing discount (backport [#47155](https://github.com/frappe/erpnext/issues/47155)) ([8050e65](https://github.com/frappe/erpnext/commit/8050e653ab6a5a36d4f5accdee160e7c2045e4b0)) * group sub assemblies in production plan ([73683b2](https://github.com/frappe/erpnext/commit/73683b275457279402d060fb9e246662b7de2b7b)) * import error ([924e9b9](https://github.com/frappe/erpnext/commit/924e9b94b641f8fb816f3a45868bf8381570a595)) * keep per_billed 100 for billed delivery note after return ([680c221](https://github.com/frappe/erpnext/commit/680c221f05791aaed3270be74f1abb667da6b0ef)) * linter ([c58800a](https://github.com/frappe/erpnext/commit/c58800a92949975e817d38630bdfee32cd78c5e6)) * logic and added test case ([b3e852a](https://github.com/frappe/erpnext/commit/b3e852adfcfdb522152aad235e38c08dae2d0053)) * Modify .json from desk to change `modified` ([1dc9812](https://github.com/frappe/erpnext/commit/1dc98124dc0840e4ef2bc44429d300f7ba544dd7)) * only update User Permissions if a relevant field has changed ([b0f3d62](https://github.com/frappe/erpnext/commit/b0f3d62dd0da7c06964a95cd61a5b115794b518d)) * pos disable customer selection at payment (backport [#47169](https://github.com/frappe/erpnext/issues/47169)) ([#47170](https://github.com/frappe/erpnext/issues/47170)) ([7adba1f](https://github.com/frappe/erpnext/commit/7adba1f0f3b346438ae0b5b705437dfb3c0d61d6)) * provision to recalculate the qty in the Bin ([5535eb4](https://github.com/frappe/erpnext/commit/5535eb481747ebc54f917ae1c5097e3ab810e69e)) * rate based on posting date in Tax Withholding Report ([9184c40](https://github.com/frappe/erpnext/commit/9184c40371ff1449791c25907fa3967a654169ba)) * remove invalid parameter ([2431141](https://github.com/frappe/erpnext/commit/2431141062706bd6979a61fac5194d0e593d2037)) * remove unused import ([4fba4d4](https://github.com/frappe/erpnext/commit/4fba4d49d261826ddfbfe992437330c62cd2fe6a)) * respect field "ignore_user_permissions" property in employee query ([a450ce2](https://github.com/frappe/erpnext/commit/a450ce25b9620a9a50468f344fce0bdad93c757a)) * respect mapped accounting dimensions ([846b24b](https://github.com/frappe/erpnext/commit/846b24ba52c016e5a41b911fdece42029db9119e)) * revert unintended changes ([a09ab90](https://github.com/frappe/erpnext/commit/a09ab902e5b64b396e455d2a6ed2add7a6892419)) * set correct paid/receive amount if doc currency is different from party account currency ([5dc63f9](https://github.com/frappe/erpnext/commit/5dc63f97a11f2f6f129bc4c7a331f03acacd14be)) * set default company address in Sales Doctype on change of company ([05d4c1e](https://github.com/frappe/erpnext/commit/05d4c1e6cad695278797976d289c25fb74d58307)) * show button only when RFQ is submitted ([9655bfa](https://github.com/frappe/erpnext/commit/9655bfa1999b4ecd97a1a4dcd7b14b91b151b301)) * test cases ([dedb19e](https://github.com/frappe/erpnext/commit/dedb19e3e9f0df09a48b56ec2ed0973293f103ee)) * test cases error ([13d3b27](https://github.com/frappe/erpnext/commit/13d3b27a1fe91e8b017968e064d2ab123ef62ba5)) * update country wise fiscal year (backport [#47141](https://github.com/frappe/erpnext/issues/47141)) ([#47176](https://github.com/frappe/erpnext/issues/47176)) ([390780d](https://github.com/frappe/erpnext/commit/390780d8717ac1200574d8f56b426e7d13c58896)) * use get_url_to_form instead ([ad35021](https://github.com/frappe/erpnext/commit/ad350216663f81dad87198703383972e6841d354)) ### Features * add button to show request for comparison report directly from RFQ ([cb2b956](https://github.com/frappe/erpnext/commit/cb2b9563e087d1bbd8718d998e9c00941bed4600)) * add unit tests for distributed_discount_amount ([6f6574c](https://github.com/frappe/erpnext/commit/6f6574c5acc2f9cd673725bdf92637f73b91718f)) ### Reverts * disable customer if creating from opportunity ([99735e0](https://github.com/frappe/erpnext/commit/99735e0af489a2c4faec57436eea0c2ed0cc6680)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3f68cc0dd90..3ff3963662b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.58.2" +__version__ = "15.59.0" def get_default_company(user=None): From 96996bd8a9cabde7da9e67a0a2171b3eab4edf1b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 29 Apr 2025 13:12:18 +0000 Subject: [PATCH 52/99] chore(release): Bumped to Version 15.60.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [15.60.0](https://github.com/frappe/erpnext/compare/v15.59.0...v15.60.0) (2025-04-29) ### Bug Fixes * add transaction_date in field_no_map when creating PO from SQ (backport [#47257](https://github.com/frappe/erpnext/issues/47257)) ([#47313](https://github.com/frappe/erpnext/issues/47313)) ([afb67f1](https://github.com/frappe/erpnext/commit/afb67f1f0cba831df327f8644cc8f141fd83439e)) * allow selling asset at zero rate (backport [#47326](https://github.com/frappe/erpnext/issues/47326)) ([#47332](https://github.com/frappe/erpnext/issues/47332)) ([171b687](https://github.com/frappe/erpnext/commit/171b6876112fd5ffd19d20999f1a92d5adf6fb61)) * allow to change valuation method from FIFO to Moving Average ([b2294ed](https://github.com/frappe/erpnext/commit/b2294ed6e3380f9f43530f7fb11674cc20a45793)) * allow to make quality inspection after Purchase / Delivery ([e0cea49](https://github.com/frappe/erpnext/commit/e0cea492361e9abd57a649ffe22188914ae66aa5)) * cancel pos closing entry failure for return pos invoices (backport [#47248](https://github.com/frappe/erpnext/issues/47248)) ([#47249](https://github.com/frappe/erpnext/issues/47249)) ([10d843e](https://github.com/frappe/erpnext/commit/10d843e49061e21ae9460c981fe5938545be1b5c)) * commas in rfq portal js ([954fec1](https://github.com/frappe/erpnext/commit/954fec16f4a4fff59a064c7ccf2f5d6dfcb9e475)) * compare total debit/credit with precision for Inter Company Journal Entry ([0927155](https://github.com/frappe/erpnext/commit/0927155171f7075e096443faace4f7f86756753f)) * consolidating pos invoices on the basis of accounting dimensions (backport [#46961](https://github.com/frappe/erpnext/issues/46961)) ([#47265](https://github.com/frappe/erpnext/issues/47265)) ([f8da159](https://github.com/frappe/erpnext/commit/f8da1599bbb4ade1f628f64c9617d058e58f0205)) * correct query for dispatch_address; remove unnecessary code; increase reusability; ([ac3b2ba](https://github.com/frappe/erpnext/commit/ac3b2ba0032eb40e61bb7cdc3eee398bb6563874)) * do not check for permission if values are not changed in employee doctype ([#47238](https://github.com/frappe/erpnext/issues/47238)) ([0caba9f](https://github.com/frappe/erpnext/commit/0caba9f70dbefc7aa2114dcd69959a6cf1ea7cb5)) * enable use serial / batch fields on batch selection ([925cc40](https://github.com/frappe/erpnext/commit/925cc40eface504ff2885343d2376de13c3af05a)) * enhance dispatch address query logic and add supplier address query ([290f0b9](https://github.com/frappe/erpnext/commit/290f0b94e56cbe92c684fa50b19f69fdef2a8f95)) * fix sub assembly qty calculation in production plan when bom level >= 1 (backport [#47296](https://github.com/frappe/erpnext/issues/47296)) ([#47315](https://github.com/frappe/erpnext/issues/47315)) ([d15b7ca](https://github.com/frappe/erpnext/commit/d15b7ca9af106989866e9b6fac800cc1ed6e71fd)) * make asset quantity and amount editable (backport [#47226](https://github.com/frappe/erpnext/issues/47226)) ([#47227](https://github.com/frappe/erpnext/issues/47227)) ([c140fd0](https://github.com/frappe/erpnext/commit/c140fd0f12bafd857f17d00972031667b5be2aec)) * map dispatch address correctly for inter company transactions ([d8c0e71](https://github.com/frappe/erpnext/commit/d8c0e7156e4664d9f7a871e8c1eebda227626ea3)) * missing else statement ([8a30a31](https://github.com/frappe/erpnext/commit/8a30a313023458771b094c824ac704bf378a80f9)) * **payment request:** get advance amount based on transaction currency ([c2235e2](https://github.com/frappe/erpnext/commit/c2235e2d17d207dc448a3c72450263725b611ada)) * **PE:** Set account types in get_payment_entry (backport [#47246](https://github.com/frappe/erpnext/issues/47246)) ([#47266](https://github.com/frappe/erpnext/issues/47266)) ([3e733f6](https://github.com/frappe/erpnext/commit/3e733f6ba1029b4b693d4a3bbfd3ca07d65c8128)) * prevent cancellation of last asset movement (backport [#47291](https://github.com/frappe/erpnext/issues/47291)) ([#47312](https://github.com/frappe/erpnext/issues/47312)) ([2edd12b](https://github.com/frappe/erpnext/commit/2edd12b26d8d226e32d6ac9c3ae14ff61d90e3f5)) * price currency in supplier quotation comparison ([6b1b30a](https://github.com/frappe/erpnext/commit/6b1b30a4a613341ac3724230ac1712d826f347ed)) * prohibit consolidated sales invoice return (backport [#47251](https://github.com/frappe/erpnext/issues/47251)) ([#47252](https://github.com/frappe/erpnext/issues/47252)) ([4bcea55](https://github.com/frappe/erpnext/commit/4bcea5556337e9dc5ccaa4d8fc55bb7e54fe1d81)) * QI reference not set if 'Action If Quality Inspection Is Not Sub… (backport [#47294](https://github.com/frappe/erpnext/issues/47294)) ([#47295](https://github.com/frappe/erpnext/issues/47295)) ([b0399fe](https://github.com/frappe/erpnext/commit/b0399fe9488101b4787a4508e96b6269ff28933e)) * Re-insert missing "Serial No Warranty Expiry" Report ([727c32d](https://github.com/frappe/erpnext/commit/727c32d7898d2d5a48780d474d6a6ecaaa456221)) * remove invalid email account creation (backport [#47318](https://github.com/frappe/erpnext/issues/47318)) ([#47323](https://github.com/frappe/erpnext/issues/47323)) ([fc8a8b5](https://github.com/frappe/erpnext/commit/fc8a8b5433a5448ce1a03e211e935d63a3e03cbd)) * remove use of cur_frm ([5c300b8](https://github.com/frappe/erpnext/commit/5c300b893b49131f2d3fda27823619925fddea78)) * **Rename Tool:** allow more than 500 rows (backport [#47117](https://github.com/frappe/erpnext/issues/47117)) ([#47225](https://github.com/frappe/erpnext/issues/47225)) ([c0ae133](https://github.com/frappe/erpnext/commit/c0ae1336f43cbaed5a1a226279a70252ae6500a7)) * require email OR phone in shipment doctype not both (backport [#47300](https://github.com/frappe/erpnext/issues/47300)) ([#47330](https://github.com/frappe/erpnext/issues/47330)) ([0056fb1](https://github.com/frappe/erpnext/commit/0056fb1d0fbbf17ab3d475181e1c63aa853e0418)) * set billing hours to hours ([0763a8d](https://github.com/frappe/erpnext/commit/0763a8d42d7924b7c34efa4728513cec30024ae1)) * set billing hours to hours in timesheet (backport [#47289](https://github.com/frappe/erpnext/issues/47289)) ([#47290](https://github.com/frappe/erpnext/issues/47290)) ([74bdc82](https://github.com/frappe/erpnext/commit/74bdc82bfaab3d0aba04d14837e6c13a47d60d47)) * update additional cost and total asset cost after asset repair (backport [#47233](https://github.com/frappe/erpnext/issues/47233)) ([#47235](https://github.com/frappe/erpnext/issues/47235)) ([4a29a54](https://github.com/frappe/erpnext/commit/4a29a54804df8355c2ec277bb61cb6bf000923e3)) * update billing hours when hours is changed ([a9df1f5](https://github.com/frappe/erpnext/commit/a9df1f5f6bac60c04067255c83e0bdfff2f5a262)) * update quantity validation using asset quantity field instead of… (backport [#46731](https://github.com/frappe/erpnext/issues/46731)) ([#47284](https://github.com/frappe/erpnext/issues/47284)) ([2e6112f](https://github.com/frappe/erpnext/commit/2e6112f21b359abace6a5bbc1d43001d967fd919)) * validate if from and to time are present on submission of job card ([#47325](https://github.com/frappe/erpnext/issues/47325)) ([d640c79](https://github.com/frappe/erpnext/commit/d640c79c1c4fafd00fe47bbd85b937ebe575c840)) * validation if no stock ledger entries against stock reco (backport [#47292](https://github.com/frappe/erpnext/issues/47292)) ([#47293](https://github.com/frappe/erpnext/issues/47293)) ([91bcefe](https://github.com/frappe/erpnext/commit/91bcefef8ceb43a993989d613e6f6cf7cbba63c9)) ### Features * add dispatch address fields to purchase doctypes ([5f101e7](https://github.com/frappe/erpnext/commit/5f101e763565f4146a44103ba94f56f670d69111)) * add dispatch address support in party details and controllers ([1fe1563](https://github.com/frappe/erpnext/commit/1fe1563daba678875c95ca26e193d94bd1c2039a)) * add display dispatch address when dispatch address is selected ([93ea2f9](https://github.com/frappe/erpnext/commit/93ea2f93b686a5501f112e8b2fa5832982ff58f2)) * change sabb qty automatically incase of internal transfer PR if sabb only has 1 batch ([#47256](https://github.com/frappe/erpnext/issues/47256)) ([9495a2a](https://github.com/frappe/erpnext/commit/9495a2ac9df2b531e7dc70c3b35b438abb2ae72b)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3ff3963662b..6f75f031755 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.59.0" +__version__ = "15.60.0" def get_default_company(user=None): From 73a418a2bd986f97ef14820212537019288c7caa Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 2 May 2025 18:34:47 +0530 Subject: [PATCH 53/99] fix: not able to submit the stock entry (#47383) (cherry picked from commit 035394ae6a6c475ff57d13c04b3fa5c2f5143330) --- erpnext/controllers/stock_controller.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 80f860b4553..f079274528c 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1824,7 +1824,11 @@ def make_bundle_for_material_transfer(**kwargs): bundle_doc.is_cancelled = 0 qty = 0 - if len(bundle_doc.entries) == 1 and kwargs.qty < bundle_doc.total_qty and not bundle_doc.has_serial_no: + if ( + len(bundle_doc.entries) == 1 + and flt(kwargs.qty) < flt(bundle_doc.total_qty) + and not bundle_doc.has_serial_no + ): qty = kwargs.qty for row in bundle_doc.entries: From c1ed750bcbb711b30a76cf739378927b4c4e3dcc Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 2 May 2025 13:44:13 +0000 Subject: [PATCH 54/99] chore(release): Bumped to Version 15.60.1 ## [15.60.1](https://github.com/frappe/erpnext/compare/v15.60.0...v15.60.1) (2025-05-02) ### Bug Fixes * not able to submit the stock entry ([#47383](https://github.com/frappe/erpnext/issues/47383)) ([73a418a](https://github.com/frappe/erpnext/commit/73a418a2bd986f97ef14820212537019288c7caa)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6f75f031755..8ed2f581cdb 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.60.0" +__version__ = "15.60.1" def get_default_company(user=None): From 7fb557197acc646e1e39bf104ef96486ac03b32c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 6 May 2025 14:11:12 +0000 Subject: [PATCH 55/99] chore(release): Bumped to Version 15.60.2 ## [15.60.2](https://github.com/frappe/erpnext/compare/v15.60.1...v15.60.2) (2025-05-06) ### Bug Fixes * 'time to resolve: failed' on issue (backport [#47406](https://github.com/frappe/erpnext/issues/47406)) ([#47407](https://github.com/frappe/erpnext/issues/47407)) ([21612fc](https://github.com/frappe/erpnext/commit/21612fc23087a0e86d47c952be25e307ac9ef6a0)) * backward compatibility for renamed group_by filter on reports (backport [#47362](https://github.com/frappe/erpnext/issues/47362)) ([#47403](https://github.com/frappe/erpnext/issues/47403)) ([0e5c709](https://github.com/frappe/erpnext/commit/0e5c709f7b9d96ac4642d6926d27f17b066e0a3d)) * change shipping address fetching condition ([0aabe4f](https://github.com/frappe/erpnext/commit/0aabe4fd1e1054338d338bf753f41a79c6d8d83d)) * completed transactions showing in the list (backport [#47374](https://github.com/frappe/erpnext/issues/47374)) ([#47379](https://github.com/frappe/erpnext/issues/47379)) ([1ef7da8](https://github.com/frappe/erpnext/commit/1ef7da837f9e0574cd8391b95a7320cda20e0317)) * do not allocate amount when ref's doctype or name are not set ([c2e36da](https://github.com/frappe/erpnext/commit/c2e36daa329c3466064f7b1507a67252a695ba29)) * do not mandate depreciation account for assets without depreciation (backport [#47427](https://github.com/frappe/erpnext/issues/47427)) ([#47428](https://github.com/frappe/erpnext/issues/47428)) ([01e975b](https://github.com/frappe/erpnext/commit/01e975b481c0e5a6ec95f2f71eb042225c217a34)) * not able to submit the stock entry ([#47383](https://github.com/frappe/erpnext/issues/47383)) ([035394a](https://github.com/frappe/erpnext/commit/035394ae6a6c475ff57d13c04b3fa5c2f5143330)) * party name in Ledger Summary ([4fc14b3](https://github.com/frappe/erpnext/commit/4fc14b3097c3e994bd87be0c73b563f2596a40c9)) * precision issue ([b6908a7](https://github.com/frappe/erpnext/commit/b6908a79bd3be81eea3586000c0aaf09e7615d8c)) * rename unchanged group_by filter related to general ledger report (backport [#47366](https://github.com/frappe/erpnext/issues/47366)) ([#47405](https://github.com/frappe/erpnext/issues/47405)) ([8d1e855](https://github.com/frappe/erpnext/commit/8d1e855dc87b37a6a69d646a8174d7c6446bb768)) * renaming group by fieldname and value in reports (backport [#47352](https://github.com/frappe/erpnext/issues/47352)) ([#47360](https://github.com/frappe/erpnext/issues/47360)) ([85a8adf](https://github.com/frappe/erpnext/commit/85a8adf804dc0cd999580d5d0c174ed66b546550)) * show party type in due date exceeding message ([f73e99e](https://github.com/frappe/erpnext/commit/f73e99e9d21112d1cf36dc3ee4337696dd0c89ac)) * stock reco recalculate qty not works for opening stock reco ([2bd30e3](https://github.com/frappe/erpnext/commit/2bd30e3c46c9202840bb5a82f147a0f27a2bd27b)) * update accounts on change of mode of payment in sales invoice payment (backport [#47381](https://github.com/frappe/erpnext/issues/47381)) ([#47400](https://github.com/frappe/erpnext/issues/47400)) ([afb44a6](https://github.com/frappe/erpnext/commit/afb44a677ca4f0a40594338a0154130bedbe92ef)) * validation for difference account ([f4a43d0](https://github.com/frappe/erpnext/commit/f4a43d07b00a5c13e30e3ac6677bb61864bf91e8)) * warning message before changing the valuation method (backport [#47340](https://github.com/frappe/erpnext/issues/47340)) ([#47342](https://github.com/frappe/erpnext/issues/47342)) ([4ef2b77](https://github.com/frappe/erpnext/commit/4ef2b7797322c24c00054e7cb3949eb14a6072ed)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8ed2f581cdb..7f9b0b127b0 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.60.1" +__version__ = "15.60.2" def get_default_company(user=None): From 6e699178ae60639e54504fd38c99a75d7543c3b7 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 13 May 2025 14:04:07 +0000 Subject: [PATCH 56/99] chore(release): Bumped to Version 15.61.0 # [15.61.0](https://github.com/frappe/erpnext/compare/v15.60.2...v15.61.0) (2025-05-13) ### Bug Fixes * accumulate values for all the fiscal years in Profit And Loss Statement ([6dbdc36](https://github.com/frappe/erpnext/commit/6dbdc36af911358fc31cbf0c672932a8718dd994)) * added PR/PI overbilling validation (backport [#47385](https://github.com/frappe/erpnext/issues/47385)) ([#47497](https://github.com/frappe/erpnext/issues/47497)) ([309ea7b](https://github.com/frappe/erpnext/commit/309ea7b9cfa9481d14ab50d6f343dae29e6246fa)) * broken test suite due to incorrect OR filter ([4a37f2a](https://github.com/frappe/erpnext/commit/4a37f2a925f4bfc15a4450b51dedac64a34233e8)) * condition for advance_account assignment ([b6e5e33](https://github.com/frappe/erpnext/commit/b6e5e3347d828a79e7ced19552dd73ad92763d64)) * do not mandate depreciation accounts for non depreciable asset category ([a75931c](https://github.com/frappe/erpnext/commit/a75931c90f78ca156449cdec1e9c4a81cd308191)) * dont auto-fetch latest exchange rate ([0adb715](https://github.com/frappe/erpnext/commit/0adb7156cd4ee644897b85a7373b1a07125d082e)) * error while making SABB for backdated stock reco ([7ba7d1a](https://github.com/frappe/erpnext/commit/7ba7d1a2a4bb9f8f61f180ec10dd52dc82f4ae6d)) * ignore "Account Closing Balance" doctype on Period Closing Voucher cancellation ([39c0291](https://github.com/frappe/erpnext/commit/39c029133f4f88e7e809feed845ee0320d590acf)) * only depreciable category assets are allowed for depreciation ([242a119](https://github.com/frappe/erpnext/commit/242a119f952adead8074478b772c5ded111a000e)) * **payment-reconciliation:** use reconciliation_takes_effect_on from company ([25fabda](https://github.com/frappe/erpnext/commit/25fabda40ae14620d8cc8d13c994d7e347eccb68)) * POS non-stock item mistakenly hidden as unavailable (backport [#47493](https://github.com/frappe/erpnext/issues/47493)) ([#47506](https://github.com/frappe/erpnext/issues/47506)) ([b18692c](https://github.com/frappe/erpnext/commit/b18692c1202e7aba117793319b29803ac8bfaf25)) * resolved conflicts ([dcfae61](https://github.com/frappe/erpnext/commit/dcfae61a7a3511d8912eb2c696accd282ceb0497)) * timesheet portal showing total billing hours ([64ae4e1](https://github.com/frappe/erpnext/commit/64ae4e1fecb0d04f666416844a6b8b3f1da9e808)) * typo ([d61a85e](https://github.com/frappe/erpnext/commit/d61a85e31697847f32def4bfb56fcee49a6874e3)) * typo in event.js ([67d24e9](https://github.com/frappe/erpnext/commit/67d24e9635cec7fa2be5517cfbfc9c5f8a83e701)) * warning message for COGS account in the stock entry ([7abe199](https://github.com/frappe/erpnext/commit/7abe199e2ab2d68e370e643adaf9c280249e38d4)) ### Features * add non depreciable category checkbox in asset category ([96d3bfd](https://github.com/frappe/erpnext/commit/96d3bfd2d9901ac740239de9a21eecd1a7cd8c99)) * add routing/sequencing to work order operations (backport [#46975](https://github.com/frappe/erpnext/issues/46975)) ([#47534](https://github.com/frappe/erpnext/issues/47534)) ([56d0357](https://github.com/frappe/erpnext/commit/56d0357f6fc0d86faf9af4ad7d6ed6f36e7d0805)) ### Performance Improvements * Skip link checking on repost's remove_attached_file (backport [#45061](https://github.com/frappe/erpnext/issues/45061)) ([#47450](https://github.com/frappe/erpnext/issues/47450)) ([09e7bfb](https://github.com/frappe/erpnext/commit/09e7bfbacb80c5e42ce6b47ff7dcf63e5810f826)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 7f9b0b127b0..1d2eb5fa7db 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.60.2" +__version__ = "15.61.0" def get_default_company(user=None): From 67741f1a214b046d6e3323b9708f874c59aac091 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 14 May 2025 12:38:38 +0530 Subject: [PATCH 57/99] fix: correct expense amount in party ledger summary. (cherry picked from commit 09a46fcf0ee595217c47f0bcf9caebebca8da4b1) --- .../report/customer_ledger_summary/customer_ledger_summary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py index 68f5f4eab76..963fb3556a6 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -405,7 +405,9 @@ class PartyLedgerSummaryReport: gl = qb.DocType("GL Entry") query = ( qb.from_(gl) - .select(gl.voucher_type, gl.voucher_no) + .select( + gl.posting_date, gl.account, gl.party, gl.voucher_type, gl.voucher_no, gl.debit, gl.credit + ) .where( (gl.docstatus < 2) & (gl.is_cancelled == 0) From 31fa1c9a581c62fa5b1338725a3a334a709d163b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 15 May 2025 06:05:18 +0000 Subject: [PATCH 58/99] chore(release): Bumped to Version 15.61.1 ## [15.61.1](https://github.com/frappe/erpnext/compare/v15.61.0...v15.61.1) (2025-05-15) ### Bug Fixes * correct expense amount in party ledger summary. ([67741f1](https://github.com/frappe/erpnext/commit/67741f1a214b046d6e3323b9708f874c59aac091)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1d2eb5fa7db..5238417d02a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.61.0" +__version__ = "15.61.1" def get_default_company(user=None): From 52a5cd97026b08adaa7868b640c569ece713a6cd Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 20 May 2025 13:54:53 +0000 Subject: [PATCH 59/99] chore(release): Bumped to Version 15.62.0 # [15.62.0](https://github.com/frappe/erpnext/compare/v15.61.1...v15.62.0) (2025-05-20) ### Bug Fixes * alias name and parent to prevent child row mapping issues ([612fa7c](https://github.com/frappe/erpnext/commit/612fa7c67290be5704394360e71845b75f4f727e)) * allow FG as RM by default (backport [#47543](https://github.com/frappe/erpnext/issues/47543)) ([#47550](https://github.com/frappe/erpnext/issues/47550)) ([9355782](https://github.com/frappe/erpnext/commit/9355782397d2438bf90de4ba12f57eaccf8171cd)) * asset cancellation issue (backport [#47639](https://github.com/frappe/erpnext/issues/47639)) ([#47641](https://github.com/frappe/erpnext/issues/47641)) ([ce9da48](https://github.com/frappe/erpnext/commit/ce9da48a5e4176d1a8f9f7ddfa2df0f07d58e761)) * asset image field updation issue (backport [#47615](https://github.com/frappe/erpnext/issues/47615)) ([#47617](https://github.com/frappe/erpnext/issues/47617)) ([35c7af1](https://github.com/frappe/erpnext/commit/35c7af1b9d76dfb5f7fab0cee8195612d0c2f2f7)) * better validation message with solution for BOM recursion (backport [#47472](https://github.com/frappe/erpnext/issues/47472)) ([#47477](https://github.com/frappe/erpnext/issues/47477)) ([a450f4c](https://github.com/frappe/erpnext/commit/a450f4ce642357fc680e504615ec656dbad0b5a1)) * Broken test + use `super()` appropriately ([5b50d5a](https://github.com/frappe/erpnext/commit/5b50d5abf29b5d320a64cbd3a78ce2a77de46fa7)) * Conflicts ([9cede83](https://github.com/frappe/erpnext/commit/9cede83de1915f0352f3db928334bfc303427e6b)) * correct expense amount in party ledger summary. ([09a46fc](https://github.com/frappe/erpnext/commit/09a46fcf0ee595217c47f0bcf9caebebca8da4b1)) * date formatting in process_statement_of_accounts accounts_receivable print format ([cf354c0](https://github.com/frappe/erpnext/commit/cf354c0da39ff2788d795e8c205ca864fa157ebd)) * include only invoices with update_stock = 0 for billed amt in delivery note. ([70e190d](https://github.com/frappe/erpnext/commit/70e190dbbbd48d8c08e8f31b96b72bb1d602d624)) * incorrect qty during reset (backport [#47593](https://github.com/frappe/erpnext/issues/47593)) ([#47595](https://github.com/frappe/erpnext/issues/47595)) ([72ae80e](https://github.com/frappe/erpnext/commit/72ae80e2e3fe597701dfb62d31d7b3b84809add6)) * mapping of dispatch address when creating PO from SO (backport [#47552](https://github.com/frappe/erpnext/issues/47552)) ([#47553](https://github.com/frappe/erpnext/issues/47553)) ([30ec69c](https://github.com/frappe/erpnext/commit/30ec69c977b140306882845b75c8fb88e15de9eb)) * POS Invoice can't use Loyalty Points when Global Rounded Total is Disabled (backport [#47491](https://github.com/frappe/erpnext/issues/47491)) ([#47564](https://github.com/frappe/erpnext/issues/47564)) ([926c0c5](https://github.com/frappe/erpnext/commit/926c0c5cf41545cfb2c367e4a489ec1c7296a364)) * pos item group filter fetching wrong items (backport [#47545](https://github.com/frappe/erpnext/issues/47545)) ([#47546](https://github.com/frappe/erpnext/issues/47546)) ([5a3eff0](https://github.com/frappe/erpnext/commit/5a3eff05a1dbb92f5526a3ec3e31158cb9fa458f)) * **quotation:** use `Text Editor` field in alternative items dialog for item description ([32eeeda](https://github.com/frappe/erpnext/commit/32eeedac24e7df45854d56c825cb67a504196edf)) * remove hardcoded doctype in `make_return_doc` ([1a69d81](https://github.com/frappe/erpnext/commit/1a69d8137fd01bc1ec49909f473f9961dbc85c07)) * removed invalid child param to prevent callback failure ([073d06c](https://github.com/frappe/erpnext/commit/073d06c44f25591162662b44db867f065b3c2502)) * **SalesAnalytics:** Ignore opening entries ([be280a4](https://github.com/frappe/erpnext/commit/be280a408ececcd92e9b2b5000cf2dc952a3d02f)) * set no_copy to party_balance field in Payment Entry ([da4ed5c](https://github.com/frappe/erpnext/commit/da4ed5cc18e1d4e2c33e39934fae5ab407113482)) * set no_copy to party_balance field in Payment Entry ([52cab02](https://github.com/frappe/erpnext/commit/52cab02a5cdf5035f919dddc729291070fe381b8)) * validate inter-company transaction address links ([86aa072](https://github.com/frappe/erpnext/commit/86aa072235655fb29c16e6a8002c5b2846944e0c)) * validation message format (backport [#47542](https://github.com/frappe/erpnext/issues/47542)) ([#47549](https://github.com/frappe/erpnext/issues/47549)) ([f225e19](https://github.com/frappe/erpnext/commit/f225e1986ee3109d77bb09187868204eeb7cefac)) ### Features * add checbox for validating time logs in job card ([80c7661](https://github.com/frappe/erpnext/commit/80c76618ae1d9587257e36ef38d93152abd964f7)) * add option to calculate ageing based on report date or today date ([69337cf](https://github.com/frappe/erpnext/commit/69337cf18b7568f0dfdb224be75de7db2e209ccb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5238417d02a..1ba049ed972 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.61.1" +__version__ = "15.62.0" def get_default_company(user=None): From f59093c6b7c6a1318676866e3a616a3bf0048547 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 27 May 2025 14:56:33 +0000 Subject: [PATCH 60/99] chore(release): Bumped to Version 15.63.0 # [15.63.0](https://github.com/frappe/erpnext/compare/v15.62.0...v15.63.0) (2025-05-27) ### Bug Fixes * absence of rounding causing discrepancy in the valuation rate calculation (backport [#47700](https://github.com/frappe/erpnext/issues/47700)) ([#47711](https://github.com/frappe/erpnext/issues/47711)) ([f41bcc6](https://github.com/frappe/erpnext/commit/f41bcc6fecfe1c4aad32dab16da28cef3d3f110e)) * add no_copy for lost reasons ([db97dbd](https://github.com/frappe/erpnext/commit/db97dbd3940c0e65e51e5a15b0b2ca64240c1c52)) * create Quality Inspection button not showing (backport [#47746](https://github.com/frappe/erpnext/issues/47746)) ([#47750](https://github.com/frappe/erpnext/issues/47750)) ([60dfe36](https://github.com/frappe/erpnext/commit/60dfe36195668f05d9ef5f94fac0e39a4434916f)) * display stock value in currency format in chart warehouse wise stock value ([ba009f4](https://github.com/frappe/erpnext/commit/ba009f4626d4f661e9a7a37b5ad0ee39603f493b)) * do not update same field twice ([63ba27e](https://github.com/frappe/erpnext/commit/63ba27e359a0bf633118b6dca4b5226ef6d4b4c1)) * exchange rate not being fetched when creating supplier quotation from MR ([2c22615](https://github.com/frappe/erpnext/commit/2c22615b6b3a7e50623bb593c335fc99d14126e2)) * filter of item for manufacture type material request (backport [#47712](https://github.com/frappe/erpnext/issues/47712)) ([#47717](https://github.com/frappe/erpnext/issues/47717)) ([2961e59](https://github.com/frappe/erpnext/commit/2961e595c2a8861e2f30f526780f90b6f45cad36)) * handle multiselect filters for tree doctypes in Customer Ledger Summary Report ([f783bf6](https://github.com/frappe/erpnext/commit/f783bf60a4d9611621d2f30d79ba8293e23623bb)) * Headline rendered twice on first save ([f94a14c](https://github.com/frappe/erpnext/commit/f94a14c06ae63afcfe4d6d33757f80bc1f068002)) * include rejected amount in PI/PR overbilling validation logic ([#47572](https://github.com/frappe/erpnext/issues/47572)) ([cd1c10a](https://github.com/frappe/erpnext/commit/cd1c10a43f2ad24ae59da4a6ddee676bef3cc741)) * incorrect inventory dimension for material transfer (backport [#47592](https://github.com/frappe/erpnext/issues/47592)) ([#47644](https://github.com/frappe/erpnext/issues/47644)) ([9a78283](https://github.com/frappe/erpnext/commit/9a78283ecb33d5c901a507e362f3b88193c0e542)) * incorrect valuation rate due to positive qty (backport [#47686](https://github.com/frappe/erpnext/issues/47686)) ([#47688](https://github.com/frappe/erpnext/issues/47688)) ([62aa1cd](https://github.com/frappe/erpnext/commit/62aa1cdb335171f7b4fb8aedb43df1f73a179f5f)) * linter ([c44493f](https://github.com/frappe/erpnext/commit/c44493fd7e5acebb59748321a0a68bc59fe2c894)) * Linter (due to conflicts resolved on gh) ([37f4cf5](https://github.com/frappe/erpnext/commit/37f4cf536756d5a54d4903fd5c60bf0c6c706827)) * Linters ([91e167f](https://github.com/frappe/erpnext/commit/91e167fe72814c04a78a9d94d7d6d0b92fc197d4)) * made changes specifically for value adjustment entry ([74e29f1](https://github.com/frappe/erpnext/commit/74e29f12183a6fb2205720bf4491078d82a56a57)) * Merge conflicts ([3deb11e](https://github.com/frappe/erpnext/commit/3deb11e5b231520e79ee1df424634d87f4c8e486)) * only include advances within the tcs period ([a2f5975](https://github.com/frappe/erpnext/commit/a2f5975133218742ec1e51d4609378f72aef5bac)) * party account based on party type's account type ([d3d22f6](https://github.com/frappe/erpnext/commit/d3d22f699e0b34c4c89b46dbf314b148aa1c2dbb)) * patch to rename group_by filter in custom reports (backport [#47709](https://github.com/frappe/erpnext/issues/47709)) ([#47730](https://github.com/frappe/erpnext/issues/47730)) ([a137944](https://github.com/frappe/erpnext/commit/a13794495556da0d1d00d981efc2d571e1868c91)) * patch to set status cancelled for already cancelled pos invoices (backport [#47725](https://github.com/frappe/erpnext/issues/47725)) ([#47759](https://github.com/frappe/erpnext/issues/47759)) ([4fd1af2](https://github.com/frappe/erpnext/commit/4fd1af21184483cd274aa70bd176fd244e748f44)) * **portal:** User cannot create 0 qty SQ from RFQ ([f95a3f5](https://github.com/frappe/erpnext/commit/f95a3f5b8b9badb095b07b42214c6cd27275b880)) * pos invoice status not updating on cancel (backport [#47556](https://github.com/frappe/erpnext/issues/47556)) ([#47657](https://github.com/frappe/erpnext/issues/47657)) ([db318a4](https://github.com/frappe/erpnext/commit/db318a4e9bbc79a25e97af7814417f3457aecaf5)) * prettier ([0f22646](https://github.com/frappe/erpnext/commit/0f2264658f798b97185985378280b929b8183ef7)) * prettier ([2c8db09](https://github.com/frappe/erpnext/commit/2c8db092a0f0280dcdb20bb3c0c922310aa52248)) * Relabel unit price settings for more clarity ([8891f46](https://github.com/frappe/erpnext/commit/8891f46a22f1f7ed6ffb2ec37c4a7bbef00fb9ba)) * setting paid amount to 0 when is_paid is unchecked in purchase invoice ([895231a](https://github.com/frappe/erpnext/commit/895231a8a75adf127ec3d019909fe7a7ebbdde12)) * show general ledger in doc currency in Process Statement Of Accounts ([b3cbbf2](https://github.com/frappe/erpnext/commit/b3cbbf2ce38e734ae6df27b415a01cba04180d5e)) * skip drop ship items (backport [#47670](https://github.com/frappe/erpnext/issues/47670)) ([#47718](https://github.com/frappe/erpnext/issues/47718)) ([e058885](https://github.com/frappe/erpnext/commit/e05888502f16f577399b9f0ebd7bf480b0390384)) * skip last purchase rate for free item (backport [#47693](https://github.com/frappe/erpnext/issues/47693)) ([#47696](https://github.com/frappe/erpnext/issues/47696)) ([f17b7b5](https://github.com/frappe/erpnext/commit/f17b7b5ee9718e1265a73c1ae1a1f566a64dbc3f)) * space ([fe78bb6](https://github.com/frappe/erpnext/commit/fe78bb60c4a24b8bd087b01ba88f4d355f529472)) * space ([194e41a](https://github.com/frappe/erpnext/commit/194e41a2d99895dd5f34277729e446e84043c952)) * translate_pos_buttons ([01b0d10](https://github.com/frappe/erpnext/commit/01b0d1057e6fed3b414d7a55e85279869e35ea33)) * Treat rows as Unit Price rows only until the qty is 0 ([d963601](https://github.com/frappe/erpnext/commit/d9636018f57692f72e3defc9610bf4b9177a2a44)) * typo in TREE_DOCTYPES list "Terrirtory" should be "Territory" ([3d2d1ba](https://github.com/frappe/erpnext/commit/3d2d1ba0720ef8a14d54af130d4b1fff46b0272f)) * updated value after depreciation after value adjustment ([8ed6e98](https://github.com/frappe/erpnext/commit/8ed6e98565da21ccc6fd147dbef1db8d86605d40)) * use pypika object `LiteralValue` for adding match conditions ([fb2df77](https://github.com/frappe/erpnext/commit/fb2df77da2c97e665f685a4f2503a8a1240ec01b)) ### Features * add validation for Item Tax Template on rate change ([92d5e91](https://github.com/frappe/erpnext/commit/92d5e91e1f6ccd0a8db7b13080a2e225c3fdf2f6)) * Unit Price Contract ([33366fc](https://github.com/frappe/erpnext/commit/33366fce6c97ede0dac51cad19315c922939fffe)) * Unit Price Items in Buying (RFQ, SQ, PO) ([f8fa775](https://github.com/frappe/erpnext/commit/f8fa775af345fe0016d70d76aa2d2ae065088820)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1ba049ed972..0e8e3a9fd81 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.62.0" +__version__ = "15.63.0" def get_default_company(user=None): From 916511ef1a599aa66ddf86801651c5f66497b096 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 3 Jun 2025 11:54:42 +0000 Subject: [PATCH 61/99] chore(release): Bumped to Version 15.64.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [15.64.0](https://github.com/frappe/erpnext/compare/v15.63.0...v15.64.0) (2025-06-03) ### Bug Fixes * Accounts receivable shouldn't fetch DN for employees ([9f5cfdd](https://github.com/frappe/erpnext/commit/9f5cfdd65bba076ed3f1e82d19eb6a9432c52242)) * add company filter to cost center and project in process statement of accounts ([5ebf1b9](https://github.com/frappe/erpnext/commit/5ebf1b9cc4a803698c05695747a9da836a4ec68e)) * add internal link field in Sales Order connections for internal transactions ([3c697e9](https://github.com/frappe/erpnext/commit/3c697e90a3a04327d5b4d8b0193e48278a38e26c)) * calculate discount percentage if discount amount is specified ([#47806](https://github.com/frappe/erpnext/issues/47806)) ([ba8a316](https://github.com/frappe/erpnext/commit/ba8a316b06a58aabd3f4f86191c8caaeedfeadd1)) * cash flow report fixes ([4a1966c](https://github.com/frappe/erpnext/commit/4a1966c6807800556d78ddba5880e229c5e6ca05)) * check return_against exists before api call ([8623a56](https://github.com/frappe/erpnext/commit/8623a5650bbfc9161a64ee63ec8132d7690a5d74)) * decimal issue ([#47839](https://github.com/frappe/erpnext/issues/47839)) ([34b62d2](https://github.com/frappe/erpnext/commit/34b62d226c5290158bc8baa37c21f4b1be817639)) * ensure backend response is awaited before saving ([5a23d7c](https://github.com/frappe/erpnext/commit/5a23d7cdca32431e3563a966acb5eaae8f9b4610)) * GL entries for rejected returned materials ([#47612](https://github.com/frappe/erpnext/issues/47612)) ([5bac652](https://github.com/frappe/erpnext/commit/5bac652b5fc8d3196a4e238b8ca60daea34df96a)) * Handle duplicate Items qty in Quotation ([4c1b415](https://github.com/frappe/erpnext/commit/4c1b415b9d8733a1cb611e4f91eb03b5d7163700)) * improved indexing for SLE queries. (backport [#47194](https://github.com/frappe/erpnext/issues/47194)) ([#47822](https://github.com/frappe/erpnext/issues/47822)) ([3879cbd](https://github.com/frappe/erpnext/commit/3879cbd86dfa75c665d122079cf366c1d372635e)) * incorrect actual qty in product bundle balance report (backport [#47791](https://github.com/frappe/erpnext/issues/47791)) ([#47814](https://github.com/frappe/erpnext/issues/47814)) ([9df3b9b](https://github.com/frappe/erpnext/commit/9df3b9b059daac6f799cc33e145035652d49b236)) * **Timesheet:** Only update to_time if it's more than 1 second off ([#47702](https://github.com/frappe/erpnext/issues/47702)) ([470534a](https://github.com/frappe/erpnext/commit/470534af7825402b20a72f96e46a7d25c0c2689d)) * use `query.walk() `for escaping special chars in receiable/payable report ([2e3ebec](https://github.com/frappe/erpnext/commit/2e3ebec53c95112b8008337d2a024a4b1882c282)) * use user default for company instead of global default in purchase order analysis report ([7d828dc](https://github.com/frappe/erpnext/commit/7d828dc17e68b86dde3c5d71b2beef462ac8289b)) ### Features * add column "Item Name" to "BOM Stock Report" (backport [#47116](https://github.com/frappe/erpnext/issues/47116)) ([#47485](https://github.com/frappe/erpnext/issues/47485)) ([9192913](https://github.com/frappe/erpnext/commit/91929138322bb584b868bbf5b7c5f39684e7b5e9)) * allow to set valuation rate for Rejected Materials (backport [#47582](https://github.com/frappe/erpnext/issues/47582)) ([#47869](https://github.com/frappe/erpnext/issues/47869)) ([3582b32](https://github.com/frappe/erpnext/commit/3582b32f0381ca45725d59183306c67dfe38526a)) * show item name for raw materials in BOM creator ([0c612be](https://github.com/frappe/erpnext/commit/0c612be6fe345ef6b9011c6bdd85efc900d19c38)) * specify expense account and cost center for raw materials in Su… (backport [#47756](https://github.com/frappe/erpnext/issues/47756)) ([#47861](https://github.com/frappe/erpnext/issues/47861)) ([01dd733](https://github.com/frappe/erpnext/commit/01dd7337a2964b6ffc762c7c25f0ee72dfa6540d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0e8e3a9fd81..4a6da176b3a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.63.0" +__version__ = "15.64.0" def get_default_company(user=None): From 29d7593fa7c18ab34f9413325a0027baf18900f4 Mon Sep 17 00:00:00 2001 From: Sagar Vora <16315650+sagarvora@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:35:30 +0530 Subject: [PATCH 62/99] =?UTF-8?q?Revert=20"fix:=20calculate=20discount=20p?= =?UTF-8?q?ercentage=20if=20discount=20amount=20is=20specified=20(#?= =?UTF-8?q?=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bb474f4f42aa6c5385a63df31c9d9af14238fad8. (cherry picked from commit 27dc0f5b7076f0dfdc19074f2d86c032e6bae83b) --- erpnext/controllers/taxes_and_totals.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index c47f04b71f1..5543129d323 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -13,7 +13,6 @@ from frappe.utils.deprecations import deprecated import erpnext from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules -from erpnext.accounts.utils import get_currency_precision from erpnext.controllers.accounts_controller import ( validate_conversion_rate, validate_inclusive_tax, @@ -675,16 +674,7 @@ class calculate_taxes_and_totals: tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(",", ":")) def set_discount_amount(self): - if self.doc.discount_amount: - self.doc.additional_discount_percentage = flt( - flt( - self.doc.discount_amount / flt(self.doc.get(scrub(self.doc.apply_discount_on))), - get_currency_precision(), - ) - * 100, - self.doc.precision("additional_discount_percentage"), - ) - elif self.doc.additional_discount_percentage: + if self.doc.additional_discount_percentage: self.doc.discount_amount = flt( flt(self.doc.get(scrub(self.doc.apply_discount_on))) * self.doc.additional_discount_percentage From 63d165c48abb606d74badfb1590a7480b03d54aa Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Jun 2025 07:07:12 +0000 Subject: [PATCH 63/99] chore(release): Bumped to Version 15.64.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [15.64.1](https://github.com/frappe/erpnext/compare/v15.64.0...v15.64.1) (2025-06-06) ### Reverts * Revert "fix: calculate discount percentage if discount amount is specified (#…" ([29d7593](https://github.com/frappe/erpnext/commit/29d7593fa7c18ab34f9413325a0027baf18900f4)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4a6da176b3a..224cbd2dca0 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.64.0" +__version__ = "15.64.1" def get_default_company(user=None): From dc8bb792d7bde759b7a4e025332ddab1d1b4b261 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Jun 2025 14:33:55 +0000 Subject: [PATCH 64/99] chore(release): Bumped to Version 15.65.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [15.65.0](https://github.com/frappe/erpnext/compare/v15.64.1...v15.65.0) (2025-06-10) ### Bug Fixes * add .length in list validation ([#47974](https://github.com/frappe/erpnext/issues/47974)) ([66f41d4](https://github.com/frappe/erpnext/commit/66f41d44c4249efa4f259356fbef301d7d536faf)) * add change log for bug fix in Additional Discount functionality ([f27e591](https://github.com/frappe/erpnext/commit/f27e591d88c19c0fe7e7a922b5d4e19d43986ece)) * add draft transactions also in calculated mismatch report ([23b5d2d](https://github.com/frappe/erpnext/commit/23b5d2db2c2a8dee7a5fc92c9391a36a49c240cf)) * add user permission while fetching ple ([a2cdd91](https://github.com/frappe/erpnext/commit/a2cdd91a0d265421572556b37ec5fe8564f9a24f)) * **asset:** make purchase date mandatory ([a5e5553](https://github.com/frappe/erpnext/commit/a5e555352008ca7c1c408d6cb46e7db18e3e83f3)) * AttributeError due to incorrect object ([43d4e26](https://github.com/frappe/erpnext/commit/43d4e26ac54fa5ebc73008b6a8770916b9dbdb67)) * available qty in BOM Stock Report ([84b2f87](https://github.com/frappe/erpnext/commit/84b2f871bae87f22cee2c52df39b02ac4e4a4687)) * better description of tab name ([#44697](https://github.com/frappe/erpnext/issues/44697)) ([d05b49b](https://github.com/frappe/erpnext/commit/d05b49b0f8ededce1da1f86fcf6c99db3da3b3db)) * changes in report ([78c6386](https://github.com/frappe/erpnext/commit/78c63869e0043a5b76338ef810d4e3cd659d1b40)) * changes to report and patch ([5237ff8](https://github.com/frappe/erpnext/commit/5237ff8d945644ae32f52e21da076a7d01b5621a)) * conflicts ([aa29c5d](https://github.com/frappe/erpnext/commit/aa29c5dde2a92a25f8c584924ee65a19f9dc7e9c)) * consider expired batches in the stock reco (backport [#47909](https://github.com/frappe/erpnext/issues/47909)) ([#47919](https://github.com/frappe/erpnext/issues/47919)) ([2e78e14](https://github.com/frappe/erpnext/commit/2e78e14c7e8e02247c307875345264e21236ba70)) * consider user permission while populating the data ([617b065](https://github.com/frappe/erpnext/commit/617b0658b8ed962e63c902b10fca520784e169c4)) * do not create repeat work orders ([795108c](https://github.com/frappe/erpnext/commit/795108c1ddaa607d8b47bde8cdc403a823c5e30c)) * do not remove item which has zero qty and zero valuation ([ef77791](https://github.com/frappe/erpnext/commit/ef77791bd6e350d5213e0defb4cf75b47130be14)) * ensure proper float conversion for discount values ([d24c2c4](https://github.com/frappe/erpnext/commit/d24c2c4ccafec82c185af05eced2daac00da9a2f)) * fetch correct item tax template on item rate update ([#47973](https://github.com/frappe/erpnext/issues/47973)) ([f88e682](https://github.com/frappe/erpnext/commit/f88e68230a00e246da7eae99af66eec048b1cc00)) * fieldtype to Currency for discount amounts ([59dd5fe](https://github.com/frappe/erpnext/commit/59dd5fee2626223bd1b75cd30a47b7e20160bcc4)) * incorrect warehouse in MR ([8156d89](https://github.com/frappe/erpnext/commit/8156d89903f7f30accca6bc10e1305a0e2f60be0)) * key-error for COGS By Item Group report (backport [#47914](https://github.com/frappe/erpnext/issues/47914)) ([#47915](https://github.com/frappe/erpnext/issues/47915)) ([996fb75](https://github.com/frappe/erpnext/commit/996fb7552ade1b192ebfcec5fe353d67cdad3803)) * patch to set discount percentange in case of mismatch ([039c47e](https://github.com/frappe/erpnext/commit/039c47e3f294d45083bca3f1ac4e759d15c72390)) * pos permission error on strict permission (backport [#47896](https://github.com/frappe/erpnext/issues/47896)) ([#47897](https://github.com/frappe/erpnext/issues/47897)) ([0314a39](https://github.com/frappe/erpnext/commit/0314a39fabc58f2f397a396e74ef674355691e2b)) * Project argument is passed correctly for MR creation ([e98ad4c](https://github.com/frappe/erpnext/commit/e98ad4ce270cfb7984f18c9e5d6425f7e7f30fd3)) * remove currency col ([35035c2](https://github.com/frappe/erpnext/commit/35035c2a31a9d81295d90900907b8ea28e275644)) * remove use sales invoice check ([#47908](https://github.com/frappe/erpnext/issues/47908)) ([1b15507](https://github.com/frappe/erpnext/commit/1b1550708d1e8e806d7ee237acf7984c32bee6a8)) * **report:** include descendants when filtering by parent item group ([d21bfa2](https://github.com/frappe/erpnext/commit/d21bfa219d75779a005fa93d81708b2064bfdd0b)) * **sales order:** error message on creation of work order from sales order ([129cd7a](https://github.com/frappe/erpnext/commit/129cd7ae8a6eac452d348c2d2e767b8e62bb08ff)) * stock adjustment entry during reposting (backport [#47878](https://github.com/frappe/erpnext/issues/47878)) ([#47883](https://github.com/frappe/erpnext/issues/47883)) ([e5d06f8](https://github.com/frappe/erpnext/commit/e5d06f8c863e610735e1fd5a057cface6ac55501)) * stock reco qty with inventory dimension (backport [#47918](https://github.com/frappe/erpnext/issues/47918)) ([#47922](https://github.com/frappe/erpnext/issues/47922)) ([6d2c14c](https://github.com/frappe/erpnext/commit/6d2c14c75e638891ac90a7851db6d18366d46140)) * test case to verify correct setting of discount amount and percentage ([06ea957](https://github.com/frappe/erpnext/commit/06ea957ae5e398a58237c77e63b5108e97acef9c)) * throw permission error ([#47976](https://github.com/frappe/erpnext/issues/47976)) ([9167d2e](https://github.com/frappe/erpnext/commit/9167d2ef648d0bc04e82ae2bb4009c24a3466b82)) * typo ([8b4824f](https://github.com/frappe/erpnext/commit/8b4824fef5af1378f3061221c95be1ebcebe9025)) * update currency based on transaction ([eaeb18c](https://github.com/frappe/erpnext/commit/eaeb18c6517ebea6ce9d8cf68938980709eb370d)) * zero division error in purchase receipt ([b99f8fd](https://github.com/frappe/erpnext/commit/b99f8fd0213d6b4f608da9498dbf5947f28f0a5f)) ### Features * Add hook to update gl dict by apps ([76c2477](https://github.com/frappe/erpnext/commit/76c2477d2332142bff7403e997400673fd3abedc)) * add validation for inter company transactions ([9a47c50](https://github.com/frappe/erpnext/commit/9a47c507c0187f173cb92c546b1bfa48dc981533)) * populate Timer dialog project field from Timesheet parent_project (backport [#47971](https://github.com/frappe/erpnext/issues/47971)) ([#48001](https://github.com/frappe/erpnext/issues/48001)) ([66b0426](https://github.com/frappe/erpnext/commit/66b0426155b4080507bdb9dab4453ffeaf84664d)) * report to verify discount amount mismatch ([b3eb49d](https://github.com/frappe/erpnext/commit/b3eb49d39d3b3051f30b1969307d1924a08ff78f)) ### Performance Improvements * Batch GLE/SLE rename commits (backport [#47950](https://github.com/frappe/erpnext/issues/47950)) ([#47951](https://github.com/frappe/erpnext/issues/47951)) ([f490de9](https://github.com/frappe/erpnext/commit/f490de928556d302fb345447cb146e868507476f)) ### Reverts * Revert "fix: calculate discount percentage if discount amount is specified (#…" ([5a5449c](https://github.com/frappe/erpnext/commit/5a5449c60c68d58d8901952633920432fa1fe7bb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 224cbd2dca0..b243201a478 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.64.1" +__version__ = "15.65.0" def get_default_company(user=None): From f92b5b9a2e93991319d64cfc7f6befe38934c6d2 Mon Sep 17 00:00:00 2001 From: priyanshshah2442 Date: Wed, 11 Jun 2025 17:47:52 +0530 Subject: [PATCH 65/99] fix: unpack non-iterable NoneType object error (cherry picked from commit 7d940faa4f80837270dbf83280e5351aee3fd31d) --- ...ncorrect_additional_discount_percentage.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py b/erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py index 40be90f1396..6ea6c9399b7 100644 --- a/erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py +++ b/erpnext/patches/v15_0/unset_incorrect_additional_discount_percentage.py @@ -12,16 +12,7 @@ from erpnext.accounts.report.calculated_discount_mismatch.calculated_discount_mi def execute(): # run this patch only if erpnext version before update is v15.64.0 or higher - version, git_branch = frappe.db.get_value( - "Installed Application", - {"app_name": "erpnext"}, - ["app_version", "git_branch"], - ) - - semantic_version = get_semantic_version(version) - if semantic_version and ( - semantic_version.major < 15 or (git_branch == "version-15" and semantic_version.minor < 64) - ): + if not should_run_patch(): return for doctype in AFFECTED_DOCTYPES: @@ -85,3 +76,24 @@ def get_semantic_version(version): return Version(version) except Exception: pass + + +def should_run_patch(): + installed_app = frappe.db.get_value( + "Installed Application", + {"app_name": "erpnext"}, + ["app_version", "git_branch"], + ) + + if not installed_app: + return True + + version, git_branch = installed_app + semantic_version = get_semantic_version(version) + if not semantic_version: + return True + + return not ( + semantic_version.major < 15 + or (git_branch == "version-15" and semantic_version.major == 15 and semantic_version.minor < 64) + ) From 9febca2981a9ea25fad78a77e0a6d496c6237c53 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 11 Jun 2025 12:51:34 +0000 Subject: [PATCH 66/99] chore(release): Bumped to Version 15.65.1 ## [15.65.1](https://github.com/frappe/erpnext/compare/v15.65.0...v15.65.1) (2025-06-11) ### Bug Fixes * unpack non-iterable NoneType object error ([f92b5b9](https://github.com/frappe/erpnext/commit/f92b5b9a2e93991319d64cfc7f6befe38934c6d2)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b243201a478..bb8e5728280 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.65.0" +__version__ = "15.65.1" def get_default_company(user=None): From d2ac603fd7a964b69ccda4769542961557647c10 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 17 Jun 2025 14:51:34 +0000 Subject: [PATCH 67/99] chore(release): Bumped to Version 15.65.2 ## [15.65.2](https://github.com/frappe/erpnext/compare/v15.65.1...v15.65.2) (2025-06-17) ### Bug Fixes * add validation for calculate ageing with filter for summary and other reports ([23db22a](https://github.com/frappe/erpnext/commit/23db22ac0bd4d22df26989861fd8063e96ff4b3b)) * add validation for exchange gain/loss entries ([97f1304](https://github.com/frappe/erpnext/commit/97f13049ee115198078859551e130f99ade2928f)) * add validation for party type ([baa08ce](https://github.com/frappe/erpnext/commit/baa08ce4963a6c03f7299db28210a8ede653f56c)) * batch page length ([994454b](https://github.com/frappe/erpnext/commit/994454bfc370ca35588b8213911e266a30389b62)) * budget naming series (backport [#48075](https://github.com/frappe/erpnext/issues/48075)) ([#48080](https://github.com/frappe/erpnext/issues/48080)) ([4d18fd0](https://github.com/frappe/erpnext/commit/4d18fd0e80a793a581bcf6f42af721a3b94f4e19)) * do not allow capitalization from connection tab for submitted asset ([7b5e2a6](https://github.com/frappe/erpnext/commit/7b5e2a6af0c25fb47a1db9deb51f9410ef2a0e5b)) * do not reset party account for return doc ([ad5421a](https://github.com/frappe/erpnext/commit/ad5421a413eb0d156e09a1954f93064a88ef4fbf)) * float division by zero ([40504b8](https://github.com/frappe/erpnext/commit/40504b8da23d47a55faaf9238c09c80b85ba86d7)) * incorrect condition for setting party account on change of company ([f2b9e73](https://github.com/frappe/erpnext/commit/f2b9e738196dbc273df32790296c68d84c71ad76)) * incorrect warehouse set from SO to MR ([e9365d7](https://github.com/frappe/erpnext/commit/e9365d7272626d23dd724d7b76793cd283de6035)) * pos invoice consolidation row refer issue (backport [#48057](https://github.com/frappe/erpnext/issues/48057)) ([#48058](https://github.com/frappe/erpnext/issues/48058)) ([db9f0b6](https://github.com/frappe/erpnext/commit/db9f0b6f3840f2633e009aaf53dd6b2bf596e609)) * prevent saving negative quantity in BOM ([1c9032a](https://github.com/frappe/erpnext/commit/1c9032a4c2c8d5a71d11bf98b82dec33018c0996)) * unpack non-iterable NoneType object error ([dd39d24](https://github.com/frappe/erpnext/commit/dd39d24da098fc78b80f323bc64606c3b6fe7426)) * update asset status after making asset value adjustment record ([ee30357](https://github.com/frappe/erpnext/commit/ee30357835e521360f802c2249b08af77b0f97c7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index bb8e5728280..285734e1811 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.65.1" +__version__ = "15.65.2" def get_default_company(user=None): From 3057f6ce3564ae383f05e87c4746b0ad3fdfc36c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 10 Jun 2025 12:26:51 +0530 Subject: [PATCH 68/99] fix: stock reconciliation validation for serial and batch (cherry picked from commit 69d54d2e0f831fb07074e43e9893d0049c45144e) (cherry picked from commit 89376ddf8d8bb49225b3b7513623430450481307) --- .../serial_and_batch_bundle.py | 95 +++++++++++++++---- erpnext/stock/serial_batch_bundle.py | 61 ++++++++++-- erpnext/stock/stock_ledger.py | 3 + 3 files changed, 131 insertions(+), 28 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index b83f3c58df9..11697409820 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -225,7 +225,14 @@ class SerialandBatchBundle(Document): if not (self.has_serial_no and self.type_of_transaction == "Outward"): return - serial_nos = [d.serial_no for d in self.entries if d.serial_no] + if self.voucher_type == "Stock Reconciliation": + serial_nos = self.get_serial_nos_for_validate() + else: + serial_nos = [d.serial_no for d in self.entries if d.serial_no] + + if not serial_nos: + return + kwargs = { "item_code": self.item_code, "warehouse": self.warehouse, @@ -235,6 +242,15 @@ class SerialandBatchBundle(Document): if self.voucher_type == "POS Invoice": kwargs["ignore_voucher_nos"] = [self.voucher_no] + if self.voucher_type == "Stock Reconciliation": + kwargs.update( + { + "voucher_no": self.voucher_no, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + } + ) + available_serial_nos = get_available_serial_nos(frappe._dict(kwargs)) serial_no_warehouse = {} @@ -665,14 +681,17 @@ class SerialandBatchBundle(Document): ): self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} should be submit first.") - def check_future_entries_exists(self): + def check_future_entries_exists(self, is_cancelled=False): if self.flags and self.flags.via_landed_cost_voucher: return if not self.has_serial_no: return - serial_nos = [d.serial_no for d in self.entries if d.serial_no] + if self.voucher_type == "Stock Reconciliation": + serial_nos = self.get_serial_nos_for_validate(is_cancelled=is_cancelled) + else: + serial_nos = [d.serial_no for d in self.entries if d.serial_no] if not serial_nos: return @@ -720,6 +739,36 @@ class SerialandBatchBundle(Document): frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransactionError) + def get_serial_nos_for_validate(self, is_cancelled=False): + serial_nos = [d.serial_no for d in self.entries if d.serial_no] + skip_serial_nos = self.get_skip_serial_nos_for_stock_reconciliation(is_cancelled=is_cancelled) + serial_nos = list(set(sorted(serial_nos)) - set(sorted(skip_serial_nos))) + + return serial_nos + + def get_skip_serial_nos_for_stock_reconciliation(self, is_cancelled=False): + data = get_stock_reco_details(self.voucher_detail_no) + if not data: + return [] + + if data.current_serial_no: + current_serial_nos = set(parse_serial_nos(data.current_serial_no)) + serial_nos = set(parse_serial_nos(data.serial_no)) if data.serial_no else set([]) + return list(serial_nos.intersection(current_serial_nos)) + elif data.current_serial_and_batch_bundle: + current_serial_nos = set(get_serial_nos_from_bundle(data.current_serial_and_batch_bundle)) + if is_cancelled: + return current_serial_nos + + serial_nos = ( + set(get_serial_nos_from_bundle(data.serial_and_batch_bundle)) + if data.serial_and_batch_bundle + else set([]) + ) + return list(serial_nos.intersection(current_serial_nos)) + + return [] + def reset_qty(self, row, qty_field=None): qty_field = self.get_qty_field(row, qty_field=qty_field) qty = abs(flt(row.get(qty_field), self.precision("total_qty"))) @@ -923,8 +972,6 @@ class SerialandBatchBundle(Document): ) def validate_serial_and_batch_no_for_returned(self): - from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - if not self.returned_against: return @@ -949,7 +996,7 @@ class SerialandBatchBundle(Document): if d.serial_and_batch_bundle: serial_nos = get_serial_nos_from_bundle(d.serial_and_batch_bundle) else: - serial_nos = get_serial_nos(d.serial_no) + serial_nos = parse_serial_nos(d.serial_no) elif self.has_batch_no: if d.serial_and_batch_bundle: @@ -1111,7 +1158,7 @@ class SerialandBatchBundle(Document): ).run() def validate_serial_and_batch_inventory(self): - self.check_future_entries_exists() + self.check_future_entries_exists(is_cancelled=True) self.validate_batch_inventory() def validate_batch_inventory(self): @@ -1418,13 +1465,6 @@ def make_batch_nos(item_code, batch_nos): frappe.msgprint(_("Batch Nos are created successfully"), alert=True) -def parse_serial_nos(data): - if isinstance(data, list): - return data - - return [s.strip() for s in cstr(data).strip().replace(",", "\n").split("\n") if s.strip()] - - @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): @@ -1811,8 +1851,6 @@ def get_non_expired_batches(batches): def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): - from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - serial_nos = set() data = get_stock_ledgers_for_serial_nos(kwargs) @@ -1826,13 +1864,14 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): serial_nos.difference_update(sns) elif d.serial_no: - sns = get_serial_nos(d.serial_no) + sns = parse_serial_nos(d.serial_no) if d.actual_qty > 0: serial_nos.update(sns) else: serial_nos.difference_update(sns) serial_nos = list(serial_nos) + for serial_no in ignore_serial_nos: if serial_no in serial_nos: serial_nos.remove(serial_no) @@ -1880,7 +1919,6 @@ def get_reserved_serial_nos(kwargs) -> list: def get_reserved_serial_nos_for_pos(kwargs): from erpnext.controllers.sales_and_purchase_return import get_returned_serial_nos - from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos ignore_serial_nos = [] pos_invoices = frappe.get_all( @@ -1916,7 +1954,7 @@ def get_reserved_serial_nos_for_pos(kwargs): returned_serial_nos = [] for pos_invoice in pos_invoices: if pos_invoice.serial_no: - ignore_serial_nos.extend(get_serial_nos(pos_invoice.serial_no)) + ignore_serial_nos.extend(parse_serial_nos(pos_invoice.serial_no)) if pos_invoice.is_return: continue @@ -2449,12 +2487,14 @@ def get_stock_ledgers_for_serial_nos(kwargs): query = ( frappe.qb.from_(stock_ledger_entry) .select( + stock_ledger_entry.posting_datetime, stock_ledger_entry.actual_qty, stock_ledger_entry.serial_no, stock_ledger_entry.serial_and_batch_bundle, ) .where(stock_ledger_entry.is_cancelled == 0) .orderby(stock_ledger_entry.posting_datetime) + .orderby(stock_ledger_entry.creation) ) if kwargs.get("posting_date"): @@ -2583,3 +2623,20 @@ def make_batch_no(batch_no, item_code): @frappe.whitelist() def is_duplicate_serial_no(bundle_id, serial_no): return frappe.db.exists("Serial and Batch Entry", {"parent": bundle_id, "serial_no": serial_no}) + + +def parse_serial_nos(serial_no): + if isinstance(serial_no, list): + return serial_no + + return [s.strip() for s in cstr(serial_no).strip().replace(",", "\n").split("\n") if s.strip()] + + +@frappe.request_cache +def get_stock_reco_details(voucher_detail_no): + return frappe.db.get_value( + "Stock Reconciliation Item", + voucher_detail_no, + ["current_serial_no", "serial_no", "serial_and_batch_bundle", "current_serial_and_batch_bundle"], + as_dict=True, + ) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index bff764228f5..99ad11326ea 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -341,16 +341,26 @@ class SerialBatchBundle: if not self.sle.serial_and_batch_bundle and self.sle.serial_no: serial_nos = get_parsed_serial_nos(self.sle.serial_no) - warehouse = self.warehouse if self.sle.actual_qty > 0 else None - if not serial_nos: return + if self.sle.voucher_type == "Stock Reconciliation" and self.sle.actual_qty > 0: + self.update_serial_no_status_for_stock_reco(serial_nos) + return + + self.update_serial_no_status_warehouse(self.sle, serial_nos) + + def update_serial_no_status_warehouse(self, sle, serial_nos): + warehouse = self.warehouse if sle.actual_qty > 0 else None + + if isinstance(serial_nos, str): + serial_nos = [serial_nos] + status = "Inactive" - if self.sle.actual_qty < 0: + if sle.actual_qty < 0: status = "Delivered" - if self.sle.voucher_type == "Stock Entry": - purpose = frappe.get_cached_value("Stock Entry", self.sle.voucher_no, "purpose") + if sle.voucher_type == "Stock Entry": + purpose = frappe.get_cached_value("Stock Entry", sle.voucher_no, "purpose") if purpose in [ "Manufacture", "Material Issue", @@ -369,17 +379,17 @@ class SerialBatchBundle: "Active" if warehouse else status - if (sn_table.purchase_document_no != self.sle.voucher_no and self.sle.is_cancelled != 1) + if (sn_table.purchase_document_no != sle.voucher_no and sle.is_cancelled != 1) else "Inactive", ) - .set(sn_table.company, self.sle.company) + .set(sn_table.company, sle.company) .where(sn_table.name.isin(serial_nos)) ) if status == "Delivered": - warranty_period = frappe.get_cached_value("Item", self.sle.item_code, "warranty_period") + warranty_period = frappe.get_cached_value("Item", sle.item_code, "warranty_period") if warranty_period: - warranty_expiry_date = add_days(self.sle.posting_date, cint(warranty_period)) + warranty_expiry_date = add_days(sle.posting_date, cint(warranty_period)) query = query.set(sn_table.warranty_expiry_date, warranty_expiry_date) query = query.set(sn_table.warranty_period, warranty_period) else: @@ -388,6 +398,39 @@ class SerialBatchBundle: query.run() + def update_serial_no_status_for_stock_reco(self, serial_nos): + for serial_no in serial_nos: + sle_doctype = frappe.qb.DocType("Stock Ledger Entry") + sn_table = frappe.qb.DocType("Serial and Batch Entry") + + query = ( + frappe.qb.from_(sle_doctype) + .inner_join(sn_table) + .on(sle_doctype.serial_and_batch_bundle == sn_table.parent) + .select( + sle_doctype.warehouse, + sle_doctype.actual_qty, + sle_doctype.voucher_type, + sle_doctype.voucher_no, + sle_doctype.is_cancelled, + sle_doctype.item_code, + sle_doctype.posting_date, + sle_doctype.company, + ) + .where( + (sn_table.serial_no == serial_no) + & (sle_doctype.is_cancelled == 0) + & (sn_table.docstatus == 1) + ) + .orderby(sle_doctype.posting_datetime, order=Order.desc) + .orderby(sle_doctype.creation, order=Order.desc) + .limit(1) + ) + + sle = query.run(as_dict=1) + if sle: + self.update_serial_no_status_warehouse(sle[0], serial_no) + def set_batch_no_in_serial_nos(self): entries = frappe.get_all( "Serial and Batch Entry", diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 2485f704b76..7f5984fb0b7 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1926,6 +1926,9 @@ def get_stock_reco_qty_shift(args): stock_reco_qty_shift = 0 if args.get("is_cancelled"): if args.get("previous_qty_after_transaction"): + if args.get("serial_and_batch_bundle"): + return args.get("previous_qty_after_transaction") + # get qty (balance) that was set at submission last_balance = args.get("previous_qty_after_transaction") stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance) From 6a6130e06c9caab01804f55ccbb7f93c92d5ac68 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 16 Jun 2025 17:03:17 +0530 Subject: [PATCH 69/99] fix: use set_query on sales_order link field in work order (cherry picked from commit 6def182e1a813dda519e7a432c1ac67af08a04c9) (cherry picked from commit b33bec4dadb69cc910180311e59f5f0661a4afb8) --- .../doctype/work_order/work_order.js | 29 +++++++------------ .../doctype/work_order/work_order.py | 10 ++++--- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 67233dc206c..04a87b00260 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -101,6 +101,17 @@ frappe.ui.form.on("Work Order", { }; }); + frm.set_query("sales_order", function () { + if (frm.doc.production_item) { + return { + query: "erpnext.manufacturing.doctype.work_order.work_order.query_sales_order", + filters: { + production_item: frm.doc.production_item, + }, + }; + } + }); + // formatter for work order operation frm.set_indicator_formatter("operation", function (doc) { return frm.doc.qty == doc.completed_qty ? "green" : "orange"; @@ -481,7 +492,6 @@ frappe.ui.form.on("Work Order", { callback: function (r) { if (r.message) { frm.set_value("sales_order", ""); - frm.trigger("set_sales_order"); erpnext.in_production_item_onchange = true; $.each( @@ -543,23 +553,6 @@ frappe.ui.form.on("Work Order", { frm.toggle_reqd("transfer_material_against", frm.doc.operations && frm.doc.operations.length > 0); }, - set_sales_order: function (frm) { - if (frm.doc.production_item) { - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.query_sales_order", - args: { production_item: frm.doc.production_item }, - callback: function (r) { - frm.set_query("sales_order", function () { - erpnext.in_production_item_onchange = true; - return { - filters: [["Sales Order", "name", "in", r.message]], - }; - }); - }, - }); - } - }, - additional_operating_cost: function (frm) { erpnext.work_order.calculate_cost(frm.doc); erpnext.work_order.calculate_total_cost(frm); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 16d551c2cf9..796e9461bee 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1515,17 +1515,19 @@ def stop_unstop(work_order, status): @frappe.whitelist() -def query_sales_order(production_item: str) -> list[str]: +@frappe.validate_and_sanitize_search_inputs +def query_sales_order(doctype, txt, searchfield, start, page_len, filters) -> list[str]: return frappe.get_list( "Sales Order", + fields=["name"], filters=[ ["Sales Order", "docstatus", "=", 1], ], or_filters=[ - ["Sales Order Item", "item_code", "=", production_item], - ["Packed Item", "item_code", "=", production_item], + ["Sales Order Item", "item_code", "=", filters.get("production_item")], + ["Packed Item", "item_code", "=", filters.get("production_item")], ], - pluck="name", + as_list=True, distinct=True, ) From e0ca060475318bd27ce77567e14c2f4fe2dde589 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 19 Jun 2025 05:30:19 +0000 Subject: [PATCH 70/99] chore(release): Bumped to Version 15.65.3 ## [15.65.3](https://github.com/frappe/erpnext/compare/v15.65.2...v15.65.3) (2025-06-19) ### Bug Fixes * stock reconciliation validation for serial and batch ([3057f6c](https://github.com/frappe/erpnext/commit/3057f6ce3564ae383f05e87c4746b0ad3fdfc36c)) * use set_query on sales_order link field in work order ([6a6130e](https://github.com/frappe/erpnext/commit/6a6130e06c9caab01804f55ccbb7f93c92d5ac68)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 285734e1811..2f06aff935b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.65.2" +__version__ = "15.65.3" def get_default_company(user=None): From 0958a3b6436807652d081eae49c01c0c5503d406 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 19 Jun 2025 19:33:26 +0530 Subject: [PATCH 71/99] fix: target inventory dimension for stock entry (cherry picked from commit d65cb56d662b998198bfc26df60253c528439362) (cherry picked from commit 4e700059379f4c9444faaf3133b3d9978e0c40f7) --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 3654ad5e6c5..d6930312b77 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -909,7 +909,7 @@ class StockController(AccountsController): fieldname = f"{dimension.source_fieldname}" sl_dict[dimension.target_fieldname] = row.get(fieldname) - return + continue sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname) else: From bd128d3b3a6860274cb4c8ae092a0db045401958 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 19 Jun 2025 16:33:57 +0000 Subject: [PATCH 72/99] chore(release): Bumped to Version 15.65.4 ## [15.65.4](https://github.com/frappe/erpnext/compare/v15.65.3...v15.65.4) (2025-06-19) ### Bug Fixes * target inventory dimension for stock entry ([0958a3b](https://github.com/frappe/erpnext/commit/0958a3b6436807652d081eae49c01c0c5503d406)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2f06aff935b..e6e456ca700 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.65.3" +__version__ = "15.65.4" def get_default_company(user=None): From de03618b0916f39905ea10c85f2c087b53deb2da Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 25 Jun 2025 04:48:04 +0000 Subject: [PATCH 73/99] chore(release): Bumped to Version 15.66.0 # [15.66.0](https://github.com/frappe/erpnext/compare/v15.65.4...v15.66.0) (2025-06-25) ### Bug Fixes * add descendants item groups to fetch the barcode items ([5cabdbf](https://github.com/frappe/erpnext/commit/5cabdbfe069d5c79de3121fea36e48e627a072d6)) * add is_group filter for warehouse ([ad0819f](https://github.com/frappe/erpnext/commit/ad0819feee4b39174ac2c745eb2a55cebe4c1297)) * add party and party_name columns to trend reports ([ceab26d](https://github.com/frappe/erpnext/commit/ceab26d5f14a170e6c962aaaf23ee41df5be40f7)) * add validation for exchange gain/loss entries ([153ed04](https://github.com/frappe/erpnext/commit/153ed04161f86a1043c7f0d0688be8d12f31cb4f)) * **asset-invoice:** handle asset invoice cancellation ([d3daeaf](https://github.com/frappe/erpnext/commit/d3daeaf475d95f567dc6ed0a434f678b32260654)) * auto append_taxes_from_item_tax_template in backend ([2bf8dff](https://github.com/frappe/erpnext/commit/2bf8dffb604c6c9356a9fa708d49ed3c6ca78dd7)) * coa reset root_type on unchecking is_group on new_node (backport [#48156](https://github.com/frappe/erpnext/issues/48156)) ([#48160](https://github.com/frappe/erpnext/issues/48160)) ([7c2bf02](https://github.com/frappe/erpnext/commit/7c2bf026ef443ddcf58a7bf2d6c66d8fb4e9a545)) * contract autoname ([1223f55](https://github.com/frappe/erpnext/commit/1223f5551f83b66e438060874abaf2e237071a23)) * fallback expense account and cost center in subcontracting receipt ([ac22c42](https://github.com/frappe/erpnext/commit/ac22c422c8e1a9c33d26b0f36b9bf1df9f00fabd)) * get already billed amount from current doc instead of database ([#48079](https://github.com/frappe/erpnext/issues/48079)) ([c2c5e45](https://github.com/frappe/erpnext/commit/c2c5e45bc68691ae6fc5d3643f8ce6f6dd01bef5)) * incoming rate for the stand-alone credit note ([ad40bfe](https://github.com/frappe/erpnext/commit/ad40bfe4ea7c36383525f1e3a204fdcabb84a38a)) * modify query to fetch valid return qty ([764c71d](https://github.com/frappe/erpnext/commit/764c71d3e16cf7c1b22e81351e63b439d5ef8385)) * naming series field in bank transaction (backport [#48121](https://github.com/frappe/erpnext/issues/48121)) ([#48149](https://github.com/frappe/erpnext/issues/48149)) ([f0ddf1b](https://github.com/frappe/erpnext/commit/f0ddf1b223d0a7c8aafe77996764caf5e3690db7)) * **open_opportunity:** remove company=null filter (backport [#48222](https://github.com/frappe/erpnext/issues/48222)) ([#48224](https://github.com/frappe/erpnext/issues/48224)) ([2d7a7d9](https://github.com/frappe/erpnext/commit/2d7a7d99888d5b2981d642405ba42e7283fd9ebb)) * permission issue during reposting ([6896216](https://github.com/frappe/erpnext/commit/6896216276043ba088e3f5146cd09ecae962e326)) * pos item details fetch uoms on stock settings allow_uom_with_conversion_rate_defined_in_item configuration (backport [#48178](https://github.com/frappe/erpnext/issues/48178)) ([#48179](https://github.com/frappe/erpnext/issues/48179)) ([991ddfe](https://github.com/frappe/erpnext/commit/991ddfe1876699f2e968e1f7dbe6f22b54e03b18)) * pos item price in get_item and item search (backport [#47925](https://github.com/frappe/erpnext/issues/47925)) ([#48217](https://github.com/frappe/erpnext/issues/48217)) ([f8cfbda](https://github.com/frappe/erpnext/commit/f8cfbda4e02588c5778df91ca420738d19e6c6df)) * resolved conflicts ([881dcf8](https://github.com/frappe/erpnext/commit/881dcf817f7528dacf5c9649bf24c5dd9df9ca9a)) * SABB validation during the LCV ([b3d337a](https://github.com/frappe/erpnext/commit/b3d337a45b64586d46cae4fa6a91d9a5c8ca4e4c)) * setup wizard load chart of accounts and fiscal year on change of country (backport [#48125](https://github.com/frappe/erpnext/issues/48125)) ([#48128](https://github.com/frappe/erpnext/issues/48128)) ([f85b08d](https://github.com/frappe/erpnext/commit/f85b08d2f549a735f230b4b8c58289a14e6889c4)) * stock adjustment entry to make stock balance zero (backport [#48245](https://github.com/frappe/erpnext/issues/48245)) ([#48247](https://github.com/frappe/erpnext/issues/48247)) ([41d22d0](https://github.com/frappe/erpnext/commit/41d22d0255c34b731a28205ce4d4b112e478c8e4)) * stock reconciliation validation for serial and batch ([89376dd](https://github.com/frappe/erpnext/commit/89376ddf8d8bb49225b3b7513623430450481307)) * target inventory dimension for stock entry ([4e70005](https://github.com/frappe/erpnext/commit/4e700059379f4c9444faaf3133b3d9978e0c40f7)) * Update indexing to populate correct values in trends report chart ([24f892d](https://github.com/frappe/erpnext/commit/24f892d582144264329226e412a6c02643dec443)) * update journal entry title on amend ([4341ac7](https://github.com/frappe/erpnext/commit/4341ac7e7a5ccd09f07e12105cd5c98d22885b8d)) * Update transaction currency to company currency to show correct currency symbol ([651b952](https://github.com/frappe/erpnext/commit/651b9521b973e19e819d542ba51ce6f98451f4a5)) * use currency from opportunity while creating quotation ([#45540](https://github.com/frappe/erpnext/issues/45540)) ([a6c5738](https://github.com/frappe/erpnext/commit/a6c5738f4bf08f35a3639a4831f02c583b56bc9d)) * use set_query on sales_order link field in work order ([b33bec4](https://github.com/frappe/erpnext/commit/b33bec4dadb69cc910180311e59f5f0661a4afb8)) ### Features * add naming series for Contract Doctype ([b3c43e8](https://github.com/frappe/erpnext/commit/b3c43e85278e2d3da5ce44d8f53f9c40be2af504)) * add search field for contract doctype ([27b5d94](https://github.com/frappe/erpnext/commit/27b5d9493a5eb38920e911687f5a8d21a8308263)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e6e456ca700..046d8b4898d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.65.4" +__version__ = "15.66.0" def get_default_company(user=None): From a49026e9d2eda85bf35dec5fd6fcfb3d83c1d2a7 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 27 Jun 2025 16:07:08 +0530 Subject: [PATCH 74/99] fix: not able to save material request (cherry picked from commit c5e36eb3238f8e9dc6d078ad7ce21665e2b09268) (cherry picked from commit 0e2bca4b34d502ddd80223503a768021d1f1eb76) --- erpnext/controllers/accounts_controller.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 73809ad2057..cf0707d9efa 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1134,6 +1134,10 @@ class AccountsController(TransactionBase): return True def set_taxes_and_charges(self): + if self.doctype == "Material Request": + # Material Request does not have taxes + return + if self.get("taxes") or self.get("is_pos"): return From fcf9f82092c43147f599a2a20cf33f0d970c61b4 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 27 Jun 2025 12:28:50 +0000 Subject: [PATCH 75/99] chore(release): Bumped to Version 15.66.1 ## [15.66.1](https://github.com/frappe/erpnext/compare/v15.66.0...v15.66.1) (2025-06-27) ### Bug Fixes * not able to save material request ([a49026e](https://github.com/frappe/erpnext/commit/a49026e9d2eda85bf35dec5fd6fcfb3d83c1d2a7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 046d8b4898d..4062c37ef2d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.66.0" +__version__ = "15.66.1" def get_default_company(user=None): From f087da927aba08542e319ea9a76797cf5bd25b7b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 1 Jul 2025 12:02:06 +0000 Subject: [PATCH 76/99] chore(release): Bumped to Version 15.67.0 # [15.67.0](https://github.com/frappe/erpnext/compare/v15.66.1...v15.67.0) (2025-07-01) ### Bug Fixes * accounting entries for standalone credit notes ([cfc8c61](https://github.com/frappe/erpnext/commit/cfc8c610fa793777b4f1c438abd32ea74db53548)) * better integration of Pick List with Delivery Note (backport [#47831](https://github.com/frappe/erpnext/issues/47831)) ([#48158](https://github.com/frappe/erpnext/issues/48158)) ([8f47505](https://github.com/frappe/erpnext/commit/8f475056048aac4ad5e694b3229d39caac22619a)) * customer section on pos item cart (backport [#48284](https://github.com/frappe/erpnext/issues/48284)) ([#48285](https://github.com/frappe/erpnext/issues/48285)) ([b6e0953](https://github.com/frappe/erpnext/commit/b6e09531d79bad68b7621d571ddcb639e0f65dae)) * customer_group import from lead to customer ([#48266](https://github.com/frappe/erpnext/issues/48266)) ([5463a8b](https://github.com/frappe/erpnext/commit/5463a8b6cfe5f467b17c1a661c4426c29a36b67e)) * default UOMs by new stock Entry created by Stock Level section button ([f1062c6](https://github.com/frappe/erpnext/commit/f1062c61f62164b11a94a689e3e47731ff4480ae)) * disassemble qty calculation & max calculation to be allowed to create it ([bf78f61](https://github.com/frappe/erpnext/commit/bf78f6173c3f28e93f8a08fca4eb0bceb39bc789)) * failing test case ([bde63ed](https://github.com/frappe/erpnext/commit/bde63ed0e50786725759502010b39eb706ab3ed5)) * func parameters ([c69bb74](https://github.com/frappe/erpnext/commit/c69bb746ce44021f2cd68b084d8103b096884d9b)) * not able to save material request ([0e2bca4](https://github.com/frappe/erpnext/commit/0e2bca4b34d502ddd80223503a768021d1f1eb76)) * option to pick serial / batch for asset repair ([7de15b7](https://github.com/frappe/erpnext/commit/7de15b74d411236f0244e71c11e79f220df9c5a6)) * **pos invoice:** search using customer name (backport [#48279](https://github.com/frappe/erpnext/issues/48279)) ([#48323](https://github.com/frappe/erpnext/issues/48323)) ([ab20b96](https://github.com/frappe/erpnext/commit/ab20b965ca3406fd297181747becc4b288fe32b7)) * saperated validations for each purpose of validation ([0c07dfa](https://github.com/frappe/erpnext/commit/0c07dfadfe300befc5bfb0a9cffa1da1cef210c9)) * update salvage value after value adjustment (backport [#48228](https://github.com/frappe/erpnext/issues/48228)) ([#48248](https://github.com/frappe/erpnext/issues/48248)) ([ef202d7](https://github.com/frappe/erpnext/commit/ef202d7cd0c0fda494f24fea07f903366e75c7c8)) * use company default currency in amount_eligible_for_commission ([9b8fffd](https://github.com/frappe/erpnext/commit/9b8fffd1d4457e47b00d06c624b0440de112967d)) * use gain_loss_posting_date instead of today ([ff36284](https://github.com/frappe/erpnext/commit/ff362843cb037eef7c6728fb876d0b6d6fba3793)) * use label "State/Province" for translatability (backport [#48273](https://github.com/frappe/erpnext/issues/48273)) ([#48286](https://github.com/frappe/erpnext/issues/48286)) ([af55ce0](https://github.com/frappe/erpnext/commit/af55ce0f6c1469977ed4812aa1b2ea8327878905)) * validate asset before repair ([a1eab1d](https://github.com/frappe/erpnext/commit/a1eab1db74b93f018b41911706885c499aaae9a5)) ### Features * added Transfer and Issue option in purpose ([1f7eccd](https://github.com/frappe/erpnext/commit/1f7eccdac5b9b71fe49f9524f3c1a0de1331c3f8)) ### Performance Improvements * use set_value for updating bank clearance_date ([a0db227](https://github.com/frappe/erpnext/commit/a0db227a7af8feb2fa00e4e8c90317ace83408b3)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4062c37ef2d..6f40f7a9513 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.66.1" +__version__ = "15.67.0" def get_default_company(user=None): From a94a13a7c12847270af27c4ee4471e957176af57 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 8 Jul 2025 13:01:14 +0000 Subject: [PATCH 77/99] chore(release): Bumped to Version 15.68.0 # [15.68.0](https://github.com/frappe/erpnext/compare/v15.67.0...v15.68.0) (2025-07-08) ### Bug Fixes * add company field on POS Invoice Merge Log (backport [#48357](https://github.com/frappe/erpnext/issues/48357)) ([#48414](https://github.com/frappe/erpnext/issues/48414)) ([26db582](https://github.com/frappe/erpnext/commit/26db582499f4ddbaabd0def2f092c65580feb8d9)) * Add company validation to company related fields in Process Statement Of Accounts ([5362648](https://github.com/frappe/erpnext/commit/536264896e5815c55b981189e16456207d6a2020)) * add not specified key for None respresented customer_group and territory ([8371daf](https://github.com/frappe/erpnext/commit/8371dafb1a4e11f5f2210d496f92490f65bb6ea9)) * add selling price validation on update item ([a10e394](https://github.com/frappe/erpnext/commit/a10e3948b2e696ce68e5ebc90675aa81f82e5b48)) * address not found when creating internal PR from DN ([f1621d1](https://github.com/frappe/erpnext/commit/f1621d15ffc6d09a68734fd5a4c26810f71296d7)) * consider empty string in previous doc validation ([b2de9cd](https://github.com/frappe/erpnext/commit/b2de9cdef21da2c7cecaae072d892d2e325320d0)) * cost center for payment entry against advance payment doctypes in accounts Payable/Receivable report ([3f004db](https://github.com/frappe/erpnext/commit/3f004db14fbf481cccca59d8a4344dbafec069c1)) * duplicate items being created when fetching items from warehouse in stock reco ([818ddc0](https://github.com/frappe/erpnext/commit/818ddc0b8c93b6ff31b7ef471a80504c526ac668)) * fetch from parent optional in inventory dimension ([ed77c15](https://github.com/frappe/erpnext/commit/ed77c15ebcb749336394c90e81498d72e2b59f99)) * get fiscal year based on date ([177b23c](https://github.com/frappe/erpnext/commit/177b23c624cf33ee3d9d606bd47e528002ce2130)) * incorrect pending qty when creating PI from PO and PI rates differ from PO (backport [#48173](https://github.com/frappe/erpnext/issues/48173)) ([#48340](https://github.com/frappe/erpnext/issues/48340)) ([8eede1d](https://github.com/frappe/erpnext/commit/8eede1d266f8c9fa31af2aadffaa999bc85eeb21)) * item list and project not being set in work order when created from material request ([5cd36c3](https://github.com/frappe/erpnext/commit/5cd36c318b4413cbe7054a2372faf097a923c0e7)) * job card material request/transfer buttons UI overlap ([09f8660](https://github.com/frappe/erpnext/commit/09f866022b4784861505522dbdf84bf50e32ae29)) * LCV from PR order mismatch ([74948aa](https://github.com/frappe/erpnext/commit/74948aabdae8edb7316be9ff8e7604229a963d04)) * make labels in error message translatable (backport [#48327](https://github.com/frappe/erpnext/issues/48327)) ([#48436](https://github.com/frappe/erpnext/issues/48436)) ([6b41dc2](https://github.com/frappe/erpnext/commit/6b41dc2fed0340ade26b6df4b50845d17f4dde1b)) * multiple fixes related Deferred Accounting ([a4633d6](https://github.com/frappe/erpnext/commit/a4633d6e7545b684653e716af8f897ec8def2ed8)) * pos recent order display customer code and name (backport [#48379](https://github.com/frappe/erpnext/issues/48379)) ([#48388](https://github.com/frappe/erpnext/issues/48388)) ([f25097d](https://github.com/frappe/erpnext/commit/f25097da1d979e6242d166c557f1fe595a09048f)) * **Quotation:** hide buttons if user cannot use them (backport [#48115](https://github.com/frappe/erpnext/issues/48115)) ([#48405](https://github.com/frappe/erpnext/issues/48405)) ([a2436e4](https://github.com/frappe/erpnext/commit/a2436e4b6e8a677bb759b57c72f7df44560653bf)) * rate not being fetched for product bundles in material request ([cfedaf5](https://github.com/frappe/erpnext/commit/cfedaf5dc1b7cfcaa82ee8eeaed78ea8c21d6208)) * rename journal entry title on update ([b7b5f6a](https://github.com/frappe/erpnext/commit/b7b5f6acf3e833a93e86368001b3bb78a752a4cd)) * unnecessary primary button ([b1abcd5](https://github.com/frappe/erpnext/commit/b1abcd55775e0b38f5bd6f9aaf13e523076cbb62)) * update condition for blank tree fields in pricing rule ([f2d644b](https://github.com/frappe/erpnext/commit/f2d644ba298383cf3be54249bdc520ca7f8824fd)) * update item reference in quality inspection ([65c277f](https://github.com/frappe/erpnext/commit/65c277fd271fdc4e80c2a1f592f14cf6c6ed0418)) * update payment request outstanding on unreconciliation ([450061c](https://github.com/frappe/erpnext/commit/450061c7dbf01b3a33104c6dcc4649c29bdcf5f2)) * use default buying price list when price list is falsy ([a336e19](https://github.com/frappe/erpnext/commit/a336e19bb88fbd059ce7ecb26980c25f49d0b6e6)) * valuation rate of raw materials in subcontracting receipt ([4545213](https://github.com/frappe/erpnext/commit/4545213adc2fa2f3783954a11b289952479e6760)) ### Features * add price list field to material request (backport [#48425](https://github.com/frappe/erpnext/issues/48425)) ([#48429](https://github.com/frappe/erpnext/issues/48429)) ([d4700e5](https://github.com/frappe/erpnext/commit/d4700e5560d912504fc894d8a0730d4519759392)) * add subject field to project ([#48368](https://github.com/frappe/erpnext/issues/48368)) ([9a538c6](https://github.com/frappe/erpnext/commit/9a538c6843e44b1031a4739e1a5b8d5bfe712d32)) ### Reverts * do not convert exchange gain/loss amount to foreign currency ([d0d1d63](https://github.com/frappe/erpnext/commit/d0d1d63d31aa76748ee94d99ed0860e2be186a76)) * Revert "fix: stock reco qty with inventory dimension ([#47918](https://github.com/frappe/erpnext/issues/47918))" ([9a99ccc](https://github.com/frappe/erpnext/commit/9a99ccc166b2058e5cd88be28320dd5b86e8574b)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6f40f7a9513..6a386a30d08 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.67.0" +__version__ = "15.68.0" def get_default_company(user=None): From ba7e0b95062eedd2d4087d9fa79c241ce94b3ab4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 9 Jul 2025 16:05:26 +0530 Subject: [PATCH 78/99] feat: batch rate (valuation) in Batch-Wise Balance History report (cherry picked from commit 8a2a845a16a00fb2d48664ca0b107cc6eabd7f3f) (cherry picked from commit facd2027c3e26ecb6a17a5ca29ad417b604c2948) --- .../batch_wise_balance_history.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 99e0676eca3..f895947f503 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -56,6 +56,11 @@ def execute(filters=None): flt(qty_dict.in_qty, float_precision), flt(qty_dict.out_qty, float_precision), flt(qty_dict.bal_qty, float_precision), + flt( + (qty_dict.bal_value / qty_dict.bal_qty) if qty_dict.bal_qty else 0, + float_precision, + ), + flt(qty_dict.bal_value, float_precision), item_map[item]["stock_uom"], ] ) @@ -68,14 +73,16 @@ def get_columns(filters): columns = [ _("Item") + ":Link/Item:100", - _("Item Name") + "::150", - _("Description") + "::150", + _("Item Name") + "::120", + _("Description") + "::90", _("Warehouse") + ":Link/Warehouse:100", _("Batch") + ":Link/Batch:100", _("Opening Qty") + ":Float:90", _("In Qty") + ":Float:80", _("Out Qty") + ":Float:80", - _("Balance Qty") + ":Float:90", + _("Balance Qty") + ":Float:120", + _("Valuation Rate") + ":Float:120", + _("Balance Value") + ":Currency:120", _("UOM") + "::90", ] @@ -107,6 +114,7 @@ def get_stock_ledger_entries_for_batch_no(filters): sle.batch_no, sle.posting_date, fn.Sum(sle.actual_qty).as_("actual_qty"), + fn.Sum(sle.stock_value_difference).as_("stock_value_difference"), ) .where( (sle.docstatus < 2) @@ -151,6 +159,7 @@ def get_stock_ledger_entries_for_batch_bundle(filters): batch_package.batch_no, sle.posting_date, fn.Sum(batch_package.qty).as_("actual_qty"), + fn.Sum(batch_package.stock_value_difference).as_("stock_value_difference"), ) .where( (sle.docstatus < 2) @@ -191,7 +200,10 @@ def get_item_warehouse_batch_map(filters, float_precision): for d in sle: iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault( - d.batch_no, frappe._dict({"opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0}) + d.batch_no, + frappe._dict( + {"opening_qty": 0.0, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 0.0, "bal_value": 0.0} + ), ) qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no] if d.posting_date < from_date: @@ -207,6 +219,7 @@ def get_item_warehouse_batch_map(filters, float_precision): ) qty_dict.bal_qty = flt(qty_dict.bal_qty, float_precision) + flt(d.actual_qty, float_precision) + qty_dict.bal_value += flt(d.stock_value_difference, float_precision) return iwb_map From 0854333e423e6902be7ae0619951f0b587135b14 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 9 Jul 2025 15:16:52 +0000 Subject: [PATCH 79/99] chore(release): Bumped to Version 15.69.0 # [15.69.0](https://github.com/frappe/erpnext/compare/v15.68.0...v15.69.0) (2025-07-09) ### Features * batch rate (valuation) in Batch-Wise Balance History report ([ba7e0b9](https://github.com/frappe/erpnext/commit/ba7e0b95062eedd2d4087d9fa79c241ce94b3ab4)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6a386a30d08..6b6706922c1 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.68.0" +__version__ = "15.69.0" def get_default_company(user=None): From 9967b1ce4a339292af333908f6b5066ecaf34c82 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 9 Jul 2025 20:59:11 +0530 Subject: [PATCH 80/99] fix: use planned_qty instead of pending_qty to check if WO should be created against PP (cherry picked from commit b11bf8eb795c101b090fa789ab4eb4fa26f361a3) --- .../manufacturing/doctype/production_plan/production_plan.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 268bf847a90..1d55c64663f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -203,8 +203,8 @@ frappe.ui.form.on("Production Plan", { let has_items = items.filter((item) => { - if (item.pending_qty) { - return item.pending_qty > item.ordered_qty; + if (item.planned_qty) { + return item.planned_qty > item.ordered_qty; } else { return item.qty > (item.received_qty || item.ordered_qty); } From 76f7eb0f9f5fabd73402cfc044307fc5c42103b7 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 9 Jul 2025 15:40:37 +0000 Subject: [PATCH 81/99] chore(release): Bumped to Version 15.69.1 ## [15.69.1](https://github.com/frappe/erpnext/compare/v15.69.0...v15.69.1) (2025-07-09) ### Bug Fixes * use planned_qty instead of pending_qty to check if WO should be created against PP ([9967b1c](https://github.com/frappe/erpnext/commit/9967b1ce4a339292af333908f6b5066ecaf34c82)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6b6706922c1..6eb56acd0c9 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.69.0" +__version__ = "15.69.1" def get_default_company(user=None): From 35451fd298dc82dbaa73030104d326fbe8adb818 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 10 Jul 2025 21:39:23 +0530 Subject: [PATCH 82/99] fix: incorrect last sle for no batch wise valuation (cherry picked from commit 93d3eb662fbe9516de0ccf7b0d101d80fc1c2d57) (cherry picked from commit f2af2fe63bd467f0e871761dc4e58e223b1937ea) --- erpnext/stock/deprecated_serial_batch.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index a8e17993c30..11e0d0964f0 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -284,6 +284,12 @@ class DeprecatedBatchNoValuation: if self.sle.name: query = query.where(sle.name != self.sle.name) + if self.sle.serial_and_batch_bundle: + query = query.where( + (sle.serial_and_batch_bundle != self.sle.serial_and_batch_bundle) + | (sle.serial_and_batch_bundle.isnull()) + ) + data = query.run(as_dict=True) return data[0] if data else frappe._dict() From cad5f39b7f8e6f03b2ffcf26563ea4a3f8fdfc59 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 11 Jul 2025 09:55:30 +0000 Subject: [PATCH 83/99] chore(release): Bumped to Version 15.69.2 ## [15.69.2](https://github.com/frappe/erpnext/compare/v15.69.1...v15.69.2) (2025-07-11) ### Bug Fixes * incorrect last sle for no batch wise valuation ([35451fd](https://github.com/frappe/erpnext/commit/35451fd298dc82dbaa73030104d326fbe8adb818)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6eb56acd0c9..a6b841b5f7e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.69.1" +__version__ = "15.69.2" def get_default_company(user=None): From b35f5aca91bb0b5f2cc8d8f351d3b32b5b8ff173 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 15 Jul 2025 12:52:04 +0000 Subject: [PATCH 84/99] chore(release): Bumped to Version 15.70.0 # [15.70.0](https://github.com/frappe/erpnext/compare/v15.69.2...v15.70.0) (2025-07-15) ### Bug Fixes * employee_exit_translatability ([c894b18](https://github.com/frappe/erpnext/commit/c894b1816508e20cb218ecc3d1e4cc785ab471e7)) * **Employee:** add context to status in List View (backport [#48576](https://github.com/frappe/erpnext/issues/48576)) ([#48577](https://github.com/frappe/erpnext/issues/48577)) ([0e67487](https://github.com/frappe/erpnext/commit/0e67487508d74a34cc315683d050cf88bd76e9be)) * error in available serial no report is no serial no present in company ([f1ff5a3](https://github.com/frappe/erpnext/commit/f1ff5a39ae45ec043c446251d62e83e3bda99af4)) * fetch item tax template after setting `base_net_rate` ([b5c4f61](https://github.com/frappe/erpnext/commit/b5c4f61fef3752c3f0966bd2be9180276e95cea9)) * field name of price_list in material request ([ee6ef03](https://github.com/frappe/erpnext/commit/ee6ef03e24fecb134b35422b0d7d46a93e30dea9)) * fix party account field access ([54275db](https://github.com/frappe/erpnext/commit/54275dbe38fded7675bd0eaaa50f94c4df617470)) * gross margin not set in project on submission of stock entry ([81e244b](https://github.com/frappe/erpnext/commit/81e244be55b29ba8190b42390ecaac2e89533401)) * handle cases where distributed discount amount is not set ([78df526](https://github.com/frappe/erpnext/commit/78df52606f7fe990610acf09bc6e3c23a76876c0)) * incorrect if condition ([a195152](https://github.com/frappe/erpnext/commit/a195152cc8514547d50ff57c1f0cb03c38a81a40)) * incorrect last sle for no batch wise valuation ([f2af2fe](https://github.com/frappe/erpnext/commit/f2af2fe63bd467f0e871761dc4e58e223b1937ea)) * incorrect stock reco sle ([1322cc1](https://github.com/frappe/erpnext/commit/1322cc1378be8a57a8451fef655dc7a5f5b77e5c)) * incorrect test ([c57ca1a](https://github.com/frappe/erpnext/commit/c57ca1ae29028d9e366159bd88d0d4cd99e54481)) * indicator in material_request_list.js ([4eb9f73](https://github.com/frappe/erpnext/commit/4eb9f73a522a4c7ea13430eb3e2080be26f55793)) * invalid comparison error in sabb.py ([7ac5463](https://github.com/frappe/erpnext/commit/7ac546333a11934a7b181139d71c45692ebd120c)) * make labels in serial_batch_prompt translatable ([c20a5b0](https://github.com/frappe/erpnext/commit/c20a5b01b4df7d36154023a5592a4384eda25aed)) * missing parameter in precision function ([f80ad4e](https://github.com/frappe/erpnext/commit/f80ad4ee589359ba83e1c16e7a9fc4d975a5933b)) * no attribute error in old subcontracting flow ([5fce819](https://github.com/frappe/erpnext/commit/5fce8191f90375225ec128c0b1a94d28b5f529e2)) * pos adding item multiple times on item group filter ([3a70b5d](https://github.com/frappe/erpnext/commit/3a70b5d7fc276199d2394a032469217a76f8a8d6)) * prevent creation of root accounts in account tree view ([817bcc7](https://github.com/frappe/erpnext/commit/817bcc78a0c3fe7c7f90aa7f83b1abb71b212d66)) * prevent unnecessary db.commit ([00d39eb](https://github.com/frappe/erpnext/commit/00d39eb208dc9b791dcb79a144b6703958a0b6ea)) * prevent unnecessary db.commit for contact insert [Linters] ([5cfeb29](https://github.com/frappe/erpnext/commit/5cfeb2978b5a9913bf576946b2bed5efcf75b215)) * resolve sql error on dimension-wise accounts balance report (backport [#48477](https://github.com/frappe/erpnext/issues/48477)) ([#48478](https://github.com/frappe/erpnext/issues/48478)) ([243b533](https://github.com/frappe/erpnext/commit/243b5331503e8d337066177edee589d8dedb62da)) * set value after depreciation when creating test asset ([4383d29](https://github.com/frappe/erpnext/commit/4383d29d7b2a3cad3d88c82ecdc049da14c51126)) * sort available batches based on expiry when merging SLEs with SABB and those without (backport [#48471](https://github.com/frappe/erpnext/issues/48471)) ([#48473](https://github.com/frappe/erpnext/issues/48473)) ([7a4c8d8](https://github.com/frappe/erpnext/commit/7a4c8d81e23a180481c714d5dade9e06f6710fc8)) * split and set value after depreciation ([3488ba0](https://github.com/frappe/erpnext/commit/3488ba05eb2b3d32083b15c699893f46a136fe6b)) * stock settings save issue ([a5c49d1](https://github.com/frappe/erpnext/commit/a5c49d1e08506819a0f60296cf4dfb8035b9b921)) * system was allowing credit notes with serial numbers for any customer ([4b6444e](https://github.com/frappe/erpnext/commit/4b6444e93bf39029f943a3fc5c8ffa9430646bd0)) * updated test ([f35fd98](https://github.com/frappe/erpnext/commit/f35fd9842e3f285b607d1bb69b0d819a29c729cd)) * use `flt` value of bin qty ([fc8d451](https://github.com/frappe/erpnext/commit/fc8d451c55fd2a64715309306d965704caf2ee80)) * use planned_qty instead of pending_qty to check if WO should be created against PP ([89660c9](https://github.com/frappe/erpnext/commit/89660c90708e7cedecf99125ed267c2b83409c0d)) ### Features * add calculate_ageing_with option in summary reports ([72e154f](https://github.com/frappe/erpnext/commit/72e154fbb761eafd0917c43291f470b998cb6a4f)) * batch rate (valuation) in Batch-Wise Balance History report ([facd202](https://github.com/frappe/erpnext/commit/facd2027c3e26ecb6a17a5ca29ad417b604c2948)) * **BOM:** improve tree display with item_name and qty (backport [#48176](https://github.com/frappe/erpnext/issues/48176)) ([#48494](https://github.com/frappe/erpnext/issues/48494)) ([fdd79c7](https://github.com/frappe/erpnext/commit/fdd79c767780db2407a566b6bbb10e294b87bf3f)) * parent item group support in Stock Projected Qty report ([db525c2](https://github.com/frappe/erpnext/commit/db525c2538dae80e2c04c3cf5be386e9ec293628)) * update the modified date of the SLE after reposting ([8c77ea1](https://github.com/frappe/erpnext/commit/8c77ea16cf9c878f19c0252e98815234fb7bad73)) ### Performance Improvements * optimize code for subcontracting ([9aef305](https://github.com/frappe/erpnext/commit/9aef3058a6fd9e22e8a43b064e3d71b60d847e3a)) * use `cached_doc` for Account Settings ([f1cdd76](https://github.com/frappe/erpnext/commit/f1cdd76fc1adf8fa7714f7824edef29839a393fd)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a6b841b5f7e..b8742f4f510 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.69.2" +__version__ = "15.70.0" def get_default_company(user=None): From c554e2cce7baa306e1b22e886cb11804bb4b723d Mon Sep 17 00:00:00 2001 From: venkat102 Date: Tue, 15 Jul 2025 17:38:37 +0530 Subject: [PATCH 85/99] fix(period closing voucher): closing account head debit and debit in account currency should be equal (cherry picked from commit d6fd6132724b9317b64de9d6b96ce813ab6e40cb) --- .../period_closing_voucher.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 2d065632419..7f51f2add59 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -210,8 +210,10 @@ class PeriodClosingVoucher(AccountsController): return gl_entry def get_gle_for_closing_account(self, dimension_balance, dimensions): - balance_in_account_currency = flt(dimension_balance.balance_in_account_currency) balance_in_company_currency = flt(dimension_balance.balance_in_company_currency) + debit = balance_in_company_currency if balance_in_company_currency > 0 else 0 + credit = abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0 + gl_entry = frappe._dict( { "company": self.company, @@ -220,14 +222,10 @@ class PeriodClosingVoucher(AccountsController): "account_currency": frappe.db.get_value( "Account", self.closing_account_head, "account_currency" ), - "debit_in_account_currency": balance_in_account_currency - if balance_in_account_currency > 0 - else 0, - "debit": balance_in_company_currency if balance_in_company_currency > 0 else 0, - "credit_in_account_currency": abs(balance_in_account_currency) - if balance_in_account_currency < 0 - else 0, - "credit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0, + "debit_in_account_currency": debit, + "debit": debit, + "credit_in_account_currency": credit, + "credit": credit, "is_period_closing_voucher_entry": 1, "voucher_type": "Period Closing Voucher", "voucher_no": self.name, From 83ab16b1613f2967fd22981b0601bed2cc508009 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 16 Jul 2025 15:12:00 +0000 Subject: [PATCH 86/99] chore(release): Bumped to Version 15.70.1 ## [15.70.1](https://github.com/frappe/erpnext/compare/v15.70.0...v15.70.1) (2025-07-16) ### Bug Fixes * **period closing voucher:** closing account head debit and debit in account currency should be equal ([c554e2c](https://github.com/frappe/erpnext/commit/c554e2cce7baa306e1b22e886cb11804bb4b723d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b8742f4f510..8931c608798 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.70.0" +__version__ = "15.70.1" def get_default_company(user=None): From 84cf5ad601a6d44afb272aa1ea856430e712b088 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 15 Jul 2025 23:06:54 +0530 Subject: [PATCH 87/99] fix: stand-alone credit note gl entries (cherry picked from commit f3d6a641560e5fac582fec07b513815ce0d01e34) (cherry picked from commit 93c2a679309825ede65481a070a4e2b608adb9f7) --- .../purchase_invoice/purchase_invoice.py | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3050f3ac266..10d94a21794 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1307,8 +1307,37 @@ class PurchaseInvoice(BuyingController): net_amt_precision, ) - # Stock ledger value is not matching with the warehouse amount - if ( + if self.is_return and self.update_stock and (self.is_internal_supplier or not self.return_against): + net_rate = item.base_net_amount + if item.sales_incoming_rate: # for internal transfer + net_rate = item.qty * item.sales_incoming_rate + + stock_amount = net_rate + item.item_tax_amount + flt(item.landed_cost_voucher_amount) + warehouse_debit_amount = flt( + voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision + ) + + if flt(stock_amount, net_amt_precision) != flt(warehouse_debit_amount, net_amt_precision): + cost_of_goods_sold_account = self.get_company_default("default_expense_account") + stock_adjustment_amt = stock_amount - warehouse_debit_amount + + gl_entries.append( + self.get_gl_dict( + { + "account": cost_of_goods_sold_account, + "against": item.expense_account, + "debit": stock_adjustment_amt, + "debit_in_transaction_currency": stock_adjustment_amt / self.conversion_rate, + "remarks": self.get("remarks") or _("Stock Adjustment"), + "cost_center": item.cost_center, + "project": item.project or self.project, + }, + account_currency, + item=item, + ) + ) + + elif ( self.update_stock and voucher_wise_stock_value.get((item.name, item.warehouse)) and warehouse_debit_amount @@ -1336,33 +1365,6 @@ class PurchaseInvoice(BuyingController): warehouse_debit_amount = stock_amount - elif self.is_return and self.update_stock and (self.is_internal_supplier or not self.return_against): - net_rate = item.base_net_amount - if item.sales_incoming_rate: # for internal transfer - net_rate = item.qty * item.sales_incoming_rate - - stock_amount = net_rate + item.item_tax_amount + flt(item.landed_cost_voucher_amount) - - if flt(stock_amount, net_amt_precision) != flt(warehouse_debit_amount, net_amt_precision): - cost_of_goods_sold_account = self.get_company_default("default_expense_account") - stock_adjustment_amt = stock_amount - warehouse_debit_amount - - gl_entries.append( - self.get_gl_dict( - { - "account": cost_of_goods_sold_account, - "against": item.expense_account, - "debit": stock_adjustment_amt, - "debit_in_transaction_currency": stock_adjustment_amt / self.conversion_rate, - "remarks": self.get("remarks") or _("Stock Adjustment"), - "cost_center": item.cost_center, - "project": item.project or self.project, - }, - account_currency, - item=item, - ) - ) - return warehouse_debit_amount def make_tax_gl_entries(self, gl_entries): From 196c3fc6564e4be93a9afee6ba3d114d22e28c1a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 17 Jul 2025 11:26:04 +0000 Subject: [PATCH 88/99] chore(release): Bumped to Version 15.70.2 ## [15.70.2](https://github.com/frappe/erpnext/compare/v15.70.1...v15.70.2) (2025-07-17) ### Bug Fixes * stand-alone credit note gl entries ([84cf5ad](https://github.com/frappe/erpnext/commit/84cf5ad601a6d44afb272aa1ea856430e712b088)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8931c608798..5ecf812d26d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.70.1" +__version__ = "15.70.2" def get_default_company(user=None): From 32029f4dca49f130d3c0c47b957a87fd56478498 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 23 Jul 2025 02:54:26 +0000 Subject: [PATCH 89/99] chore(release): Bumped to Version 15.71.0 # [15.71.0](https://github.com/frappe/erpnext/compare/v15.70.2...v15.71.0) (2025-07-23) ### Bug Fixes * add alias for order by field ([4bef3cc](https://github.com/frappe/erpnext/commit/4bef3cc92ff0e66aefe0204c015aeecbadb9a9f7)) * add validation for account key ([90fa7db](https://github.com/frappe/erpnext/commit/90fa7db13c31009821449ebf08038e91a510ddb2)) * added serial no condition ([467fe1d](https://github.com/frappe/erpnext/commit/467fe1d72f77c85e5ef65c7d81ba8a397ccfd4e9)) * carry forward the delivered_by_supplier check to PO ([6fddf4c](https://github.com/frappe/erpnext/commit/6fddf4c5aafecd2f6155c61a6327b4a7387ebfe2)) * do not consider cancelled SLEs in report ([32915cf](https://github.com/frappe/erpnext/commit/32915cf2b7141b1ce1653b15d63dc2795f0c4879)) * fetch sales invoice based on mode_of_payment in item-wise sales register ([d04c256](https://github.com/frappe/erpnext/commit/d04c256b73f46ccf2a489881e2aabe0acedf7a57)) * job card linter error (backport [#47561](https://github.com/frappe/erpnext/issues/47561)) ([#48695](https://github.com/frappe/erpnext/issues/48695)) ([a139cd4](https://github.com/frappe/erpnext/commit/a139cd4b5e0733c5bc08d100dd0722b5abc92ce0)) * performance issue while submitting the purchase invoice ([b9e6f52](https://github.com/frappe/erpnext/commit/b9e6f524e571fe00cd72c2b804ca67c9534fc0eb)) * **period closing voucher:** closing account head debit and debit in account currency should be equal ([98bd880](https://github.com/frappe/erpnext/commit/98bd880c73024a79b56c9956e56c278332ca6e24)) * pos customer selection on new order ([#48623](https://github.com/frappe/erpnext/issues/48623)) ([a46cafe](https://github.com/frappe/erpnext/commit/a46cafe652cbb887c2ed4256a8fddb5f71af8378)) * precision issue for Sales Incoming Rate ([3e53660](https://github.com/frappe/erpnext/commit/3e53660bba95c2ed812d6a77229488b1912431d5)) * **production plan:** add company filter to sub assembly warehouse ([e683703](https://github.com/frappe/erpnext/commit/e68370359fc838c5214199eab7c5aeb93f026606)) * resolve bundle item into line item if againt default supplier checked ([725f9ea](https://github.com/frappe/erpnext/commit/725f9ea0126be5faff11acd22bd3f2c8b365c2db)) * resolve sql syntax on accounting dimension ([96a1444](https://github.com/frappe/erpnext/commit/96a1444e922960cd3812bbaa377121703bb2ee08)) * sales partner in pos invoice ([#48670](https://github.com/frappe/erpnext/issues/48670)) ([65efc7e](https://github.com/frappe/erpnext/commit/65efc7e9507ad6240ebf894eef563b5734321043)), closes [#48667](https://github.com/frappe/erpnext/issues/48667) [#48669](https://github.com/frappe/erpnext/issues/48669) * set delivery date if missing ([8f23ca5](https://github.com/frappe/erpnext/commit/8f23ca5c6b9d3dd6a291e3500d324353a3f033c1)) * show amount for exchange gain or loss account ([38b223e](https://github.com/frappe/erpnext/commit/38b223e7327abdc34b7ba3c1edc02ba675d5ab77)) * stand-alone credit note gl entries ([93c2a67](https://github.com/frappe/erpnext/commit/93c2a679309825ede65481a070a4e2b608adb9f7)) * **transaction:** recalculate tax and total when quantity changes (backport [#48565](https://github.com/frappe/erpnext/issues/48565)) ([#48625](https://github.com/frappe/erpnext/issues/48625)) ([2b1fdba](https://github.com/frappe/erpnext/commit/2b1fdba7fd4d06bb0e17784cdd763953956cc70d)) * update outstanding amount on payment reconcillation ([0d496bb](https://github.com/frappe/erpnext/commit/0d496bb05fb23e1576dfedf28c214985e7a771e9)) * view ledger button of company on chart of accounts (backport [#48677](https://github.com/frappe/erpnext/issues/48677)) ([#48678](https://github.com/frappe/erpnext/issues/48678)) ([56f5ec9](https://github.com/frappe/erpnext/commit/56f5ec961fe7e1fbe9f6f124aa81f442137dc266)) ### Features * consider process less when calculating pending qty in work order ([2b42848](https://github.com/frappe/erpnext/commit/2b4284837642a5c7091a09ba367d8fe195ecf350)) ### Reverts * do not set pay_to_recd_from to None ([ab79e5d](https://github.com/frappe/erpnext/commit/ab79e5d94600bc3bb4f02b6ab542a15cadccfbd8)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5ecf812d26d..ee8bdb28636 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.70.2" +__version__ = "15.71.0" def get_default_company(user=None): From 3d7185fad707614a69bd7f52738f704c3de8f8b0 Mon Sep 17 00:00:00 2001 From: "Kitti U. @ Ecosoft" Date: Wed, 23 Jul 2025 12:48:06 +0700 Subject: [PATCH 90/99] fix: call hooks after gle & sle rename (#48706) (cherry picked from commit ed79adebc46d5088d6e569b63e18e9d7fb465e5d) --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 89b184e89d0..2e4f9db3539 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -462,4 +462,9 @@ def rename_temporarily_named_docs(doctype): f"UPDATE `tab{doctype}` SET name = %s, to_rename = 0, modified = %s where name = %s", (newname, now(), oldname), ) + + for hook_type in ("on_gle_rename", "on_sle_rename"): + for hook in frappe.get_hooks(hook_type): + frappe.call(hook, newname=newname, oldname=oldname) + frappe.db.commit() From ad45063c76880a443c772a0ea3aa7ab6ca33da3c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 24 Jul 2025 01:39:52 +0000 Subject: [PATCH 91/99] chore(release): Bumped to Version 15.71.1 ## [15.71.1](https://github.com/frappe/erpnext/compare/v15.71.0...v15.71.1) (2025-07-24) ### Bug Fixes * call hooks after gle & sle rename ([#48706](https://github.com/frappe/erpnext/issues/48706)) ([3d7185f](https://github.com/frappe/erpnext/commit/3d7185fad707614a69bd7f52738f704c3de8f8b0)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ee8bdb28636..dcad5148e55 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.71.0" +__version__ = "15.71.1" def get_default_company(user=None): From b4a6d09d5dcb2440903f90b97c8e6e5b463388e0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 29 Jul 2025 15:39:56 +0000 Subject: [PATCH 92/99] chore(release): Bumped to Version 15.72.0 # [15.72.0](https://github.com/frappe/erpnext/compare/v15.71.1...v15.72.0) (2025-07-29) ### Bug Fixes * append finance book row only when calculate depreciation is checked ([36f22f9](https://github.com/frappe/erpnext/commit/36f22f929d784462f3a31754ceb9e8376a986c21)) * attribute error in payment entry ([3739e2c](https://github.com/frappe/erpnext/commit/3739e2ca5a3d8c205ce094d3cc53a3fb6508c376)) * avoid auto_repeat on duplicate ([65a2706](https://github.com/frappe/erpnext/commit/65a27066cc53ae5c71fbc246c57dacaf45b8b478)) * correct query filter assignment in stock ledger and balance reports ([fa01bdc](https://github.com/frappe/erpnext/commit/fa01bdc4907f3f0542eab81bae7712012602ddd9)) * create job card for selected operations only ([23180da](https://github.com/frappe/erpnext/commit/23180dad421a6bca46039d6ab4db63ad4ff68266)) * do not set value after depreciation as zero ([20bbfc5](https://github.com/frappe/erpnext/commit/20bbfc504fbcf2708aa15341cedfe6ae5b7d0e65)) * enhance warehouse filter to support list and tuple values ([#48755](https://github.com/frappe/erpnext/issues/48755)) ([0cb2c41](https://github.com/frappe/erpnext/commit/0cb2c41cba2c83ba91be48b1f976788e1f019c55)) * error when trying to edit quantity of top most FG in bom creator ([bd7de51](https://github.com/frappe/erpnext/commit/bd7de515b16bcd45e619caa3f3f929339a04ed9a)) * fetch item valuation rate for internal transactions ([b23f7a9](https://github.com/frappe/erpnext/commit/b23f7a9d913656560d3f4c781c277f3705c54f72)) * fetch payment term template from order ([ee8eb36](https://github.com/frappe/erpnext/commit/ee8eb368e7a5ae4fa8bfcd3abb4d85068d1546a4)) * get default company currency ([622052b](https://github.com/frappe/erpnext/commit/622052b950e045a8c98c1030e5da85c17c6f7154)) * handle empty warehouse condition in get_warehouse_condition function; typo; ([1d52a8f](https://github.com/frappe/erpnext/commit/1d52a8fb69844e01de73146927609db4cf4bbe4b)) * ignore is overridden by transaction.js upon clicking cancel ([424baed](https://github.com/frappe/erpnext/commit/424baed077b365a1b21eb8602e210a5a49ce6b6b)) * include empty values in user permission ([cfcd21d](https://github.com/frappe/erpnext/commit/cfcd21d5c69ee7bef9d20ff9994fc52e34288c94)) * incorrect GL entries ([207d2ac](https://github.com/frappe/erpnext/commit/207d2ac63c1d400588eae00b4d3dcd9182968c57)) * Misclassification of Journal Voucher Entries in Customer Ledger Summary ([#48041](https://github.com/frappe/erpnext/issues/48041)) ([01fcd98](https://github.com/frappe/erpnext/commit/01fcd98c84f0c5014b36a79bdca7db10aea63132)) * over billed purchase receipt status ([1efdff0](https://github.com/frappe/erpnext/commit/1efdff0ad1b4f7de527d074c63777393278f72fd)) * patch to enable fetch_valuation_rate_for_internal_transaction ([bf5b6a5](https://github.com/frappe/erpnext/commit/bf5b6a540f660ef66041214a23559400bc350018)) * patch to set default buying price list in material request ([#48680](https://github.com/frappe/erpnext/issues/48680)) ([fd1c213](https://github.com/frappe/erpnext/commit/fd1c213a8d64492cc756517f884555459d893435)) * **pick list:** make warehouse editable ([6a50410](https://github.com/frappe/erpnext/commit/6a5041042eff7882099e5d5f7192ff39158d3eb0)) * post gl entry on completion date of asset repair ([5a82b72](https://github.com/frappe/erpnext/commit/5a82b723c29acbdfdc57626aa4c3b52eb5bf321f)) * prevent concurrency issues ([ad75754](https://github.com/frappe/erpnext/commit/ad75754ca6e642a69c172bca862e8dd32b119bab)) * prevent negative scrap quantity in Job Card ([#48545](https://github.com/frappe/erpnext/issues/48545)) ([ae945b2](https://github.com/frappe/erpnext/commit/ae945b2e6ff18e3aad38353164fd4f804f5f9e50)) * remove alias for order by field ([193fbcb](https://github.com/frappe/erpnext/commit/193fbcba11646b21c6bf98b37072f7af875c0abc)) * **sales-order:** disallow address edits after sales order is submitted ([2073e98](https://github.com/frappe/erpnext/commit/2073e9861314144518a00c6010156b1a8362828d)) * serial no warehouse for backdated stock reco ([b82aea4](https://github.com/frappe/erpnext/commit/b82aea4a87852a683e231c39709eb036857efb66)) * set company as mandatory ([49befc1](https://github.com/frappe/erpnext/commit/49befc1dfd8be8738145f49acb6b16289c9eab96)) * set letter head from company if exists ([d4fae00](https://github.com/frappe/erpnext/commit/d4fae00b803ec4e31d4ba1253f1425070c743533)) * sql error in quality inspection ([3e92ae8](https://github.com/frappe/erpnext/commit/3e92ae8bd01feaed0e3eebcce54014080c98fd1a)) * status in MR (material transfer) when using transit stock entries ([584b442](https://github.com/frappe/erpnext/commit/584b442824861503c3deb564dfa417eb985d866e)) * test case ([9fe1e6d](https://github.com/frappe/erpnext/commit/9fe1e6d0bdf4197ccab26e3a6ca5a922902b496f)) * test case ([c7dcbed](https://github.com/frappe/erpnext/commit/c7dcbed16f92ad9d2e6f501f29a40e43fb65e7c0)) * **test:** update tests ([62033b5](https://github.com/frappe/erpnext/commit/62033b5c7a14c25828d32ba82a55ae21ad651557)) * update advance paid amount on unreconcile ([074a706](https://github.com/frappe/erpnext/commit/074a7065bed1d52c57c570fa1f1e853236fbba3f)) * update asset value after revaluation cancellation ([d9b24a3](https://github.com/frappe/erpnext/commit/d9b24a30ebb6bf1a4d5e5d8f6902153972b13248)) * update get_data function to use item_query ([9bf0d85](https://github.com/frappe/erpnext/commit/9bf0d852ee3daf548eea812a715f6872cdaecb43)) * update subscription details patch ([ebda396](https://github.com/frappe/erpnext/commit/ebda3965183b3749e31523ace3840264a6ba84c3)) * use db_set in email_campaign (backport [#45679](https://github.com/frappe/erpnext/issues/45679)) ([#48806](https://github.com/frappe/erpnext/issues/48806)) ([9b59fb6](https://github.com/frappe/erpnext/commit/9b59fb659b9a47d7c626dd34c4ad7d85552511c4)) * use the item_query for get_data ([a7e8f40](https://github.com/frappe/erpnext/commit/a7e8f404f7a17310dd1fbf8570d58dec3b5d8742)) * valuation for rejected materials ([d378e51](https://github.com/frappe/erpnext/commit/d378e514926fe6b292cf6e92c0824211435cbb0f)) * warehouse filter query by chaining conditions ([b57163b](https://github.com/frappe/erpnext/commit/b57163b7be46bc2e3641b236ea23e79763b39c5f)) ### Features * add fetch_valuation_rate_for_internal_transaction in accounts settings ([f8d1e5a](https://github.com/frappe/erpnext/commit/f8d1e5a0d383e44d137c71597ed218d86cde1662)) * Add non-negative constraint to workstation cost fields (backport [#48557](https://github.com/frappe/erpnext/issues/48557)) ([#48826](https://github.com/frappe/erpnext/issues/48826)) ([e1d7ec9](https://github.com/frappe/erpnext/commit/e1d7ec906f3d0c6542cc26e32de9c3fc1927b84c)) * enhance apply_warehouse_filter to support multiple warehouses in filters ([801cda3](https://github.com/frappe/erpnext/commit/801cda38136fc7c95419092137ed2780d70fc2f1)) * option to recalculate costing and billing fields in project ([6adc8a0](https://github.com/frappe/erpnext/commit/6adc8a09c0adfc57b8a80a991867956c97a93b5e)) * show opening/closing balance in cash flow report ([#47877](https://github.com/frappe/erpnext/issues/47877)) ([7fd5b2b](https://github.com/frappe/erpnext/commit/7fd5b2b26a69502965222fef9116f3f5ac6cfa36)) * update stock balance report to support multi-select for items and warehouses ([2b08c5b](https://github.com/frappe/erpnext/commit/2b08c5b76970c7a6f233a21c0f25cd2eeb130bcb)) * update stock ledger report to support multi-select for warehouses and items ([ecf9e6e](https://github.com/frappe/erpnext/commit/ecf9e6e74834d59510cfb7962831ddd407188e1f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index dcad5148e55..4949084150f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.71.1" +__version__ = "15.72.0" def get_default_company(user=None): From 14d5c7f3cd7046c62456a4be940c8642a86c56e7 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 31 Jul 2025 15:23:14 +0530 Subject: [PATCH 93/99] fix: failing subcontracting patch (cherry picked from commit bb434199441111f79b6e816b98a1ce0751230420) (cherry picked from commit 2f4a9f283dab89815661d7f8dae321797487147b) --- .../repost_gl_entries_with_no_account_subcontracting.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v15_0/repost_gl_entries_with_no_account_subcontracting.py b/erpnext/patches/v15_0/repost_gl_entries_with_no_account_subcontracting.py index b2208667ea6..e7394f1071c 100644 --- a/erpnext/patches/v15_0/repost_gl_entries_with_no_account_subcontracting.py +++ b/erpnext/patches/v15_0/repost_gl_entries_with_no_account_subcontracting.py @@ -19,7 +19,7 @@ def execute(): if not item.cost_center: item.db_set("cost_center", cost_center) - doc.docstatus = 2 - doc.make_gl_entries_on_cancel() - doc.docstatus = 1 - doc.make_gl_entries() + doc.docstatus = 2 + doc.make_gl_entries_on_cancel() + doc.docstatus = 1 + doc.make_gl_entries() From 9e661b206c2446517e7ca6c9466abc96b2103a04 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Thu, 31 Jul 2025 16:00:35 +0530 Subject: [PATCH 94/99] chore: add date so patch can rerun (cherry picked from commit ed927a2147b792c1dfc460bdc4eb4d3f90e28b8c) --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 362486a97bb..7bd30f81bc3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -415,5 +415,5 @@ erpnext.patches.v15_0.set_company_on_pos_inv_merge_log erpnext.patches.v15_0.rename_price_list_to_buying_price_list erpnext.patches.v15_0.patch_missing_buying_price_list_in_material_request erpnext.patches.v15_0.remove_sales_partner_from_consolidated_sales_invoice -erpnext.patches.v15_0.repost_gl_entries_with_no_account_subcontracting +erpnext.patches.v15_0.repost_gl_entries_with_no_account_subcontracting #2025-07-31 execute:frappe.db.set_single_value("Accounts Settings", "fetch_valuation_rate_for_internal_transaction", 1) From 396886c6e87d65053c37b0637ed085df3fd660f0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 31 Jul 2025 11:05:20 +0000 Subject: [PATCH 95/99] chore(release): Bumped to Version 15.72.1 ## [15.72.1](https://github.com/frappe/erpnext/compare/v15.72.0...v15.72.1) (2025-07-31) ### Bug Fixes * failing subcontracting patch ([14d5c7f](https://github.com/frappe/erpnext/commit/14d5c7f3cd7046c62456a4be940c8642a86c56e7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4949084150f..dc442c918b4 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.72.0" +__version__ = "15.72.1" def get_default_company(user=None): From 52b9f925533c19afbb09bb1f0be14dc67fc342ff Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:30:30 +0530 Subject: [PATCH 96/99] fix: multiple fixes for advance payment accounting (backport #48341) (#48896) * fix: multiple fixes for advance payment accounting (cherry picked from commit e70caedddcecf7ed7d1949b112b5cade746c5265) # Conflicts: # erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json # erpnext/accounts/doctype/payment_entry/payment_entry.py # erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json # erpnext/accounts/utils.py # erpnext/controllers/accounts_controller.py # erpnext/patches/v15_0/create_advance_payment_ledger_records.py * chore: resolve conflicts * fix: do not execute patch if no advance doctypes --------- Co-authored-by: Lakshit Jain <108322669+ljain112@users.noreply.github.com> Co-authored-by: ljain112 (cherry picked from commit cb0addc122341f3d8aadc72e342a8dbc103f8ec3) --- .../advance_payment_ledger_entry.json | 16 +- .../advance_payment_ledger_entry.py | 13 +- .../doctype/journal_entry/journal_entry.py | 102 +++++----- .../journal_entry_account.json | 22 ++- .../journal_entry_account.py | 4 +- .../doctype/payment_entry/payment_entry.py | 51 +++-- .../payment_entry/test_payment_entry.py | 4 +- .../payment_entry_reference.json | 24 ++- .../payment_entry_reference.py | 3 +- .../payment_ledger_entry.json | 2 +- .../test_payment_reconciliation.py | 130 +++++++++++++ .../payment_request/test_payment_request.py | 2 +- .../repost_accounting_ledger.py | 4 + .../repost_payment_ledger.py | 3 +- .../test_unreconcile_payment.py | 18 +- .../unreconcile_payment.py | 113 ++++++----- erpnext/accounts/general_ledger.py | 2 + erpnext/accounts/utils.py | 172 +++++++++++------ .../doctype/purchase_order/purchase_order.py | 1 + erpnext/controllers/accounts_controller.py | 103 ++-------- erpnext/patches.txt | 3 +- .../create_advance_payment_ledger_records.py | 180 ++++++++++++------ ...ledger_entries_against_advance_doctypes.py | 25 +++ erpnext/public/js/utils/unreconcile.js | 14 +- .../doctype/sales_order/sales_order.py | 1 + 25 files changed, 650 insertions(+), 362 deletions(-) create mode 100644 erpnext/patches/v15_0/update_payment_ledger_entries_against_advance_doctypes.py diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json index 290ed11c98e..5ad2479e858 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.json @@ -12,7 +12,8 @@ "against_voucher_no", "amount", "currency", - "event" + "event", + "delinked" ], "fields": [ { @@ -68,12 +69,20 @@ "label": "Company", "options": "Company", "read_only": 1 + }, + { + "default": "0", + "fieldname": "delinked", + "fieldtype": "Check", + "label": "DeLinked", + "read_only": 1 } ], + "grid_page_length": 50, "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-11-05 10:31:28.736671", + "modified": "2025-07-29 11:37:42.678556", "modified_by": "Administrator", "module": "Accounts", "name": "Advance Payment Ledger Entry", @@ -107,7 +116,8 @@ "share": 1 } ], + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py index 0ec2d411761..a9392fc9367 100644 --- a/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/advance_payment_ledger_entry/advance_payment_ledger_entry.py @@ -1,9 +1,11 @@ # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document +from erpnext.accounts.utils import update_voucher_outstanding + class AdvancePaymentLedgerEntry(Document): # begin: auto-generated types @@ -19,9 +21,16 @@ class AdvancePaymentLedgerEntry(Document): amount: DF.Currency company: DF.Link | None currency: DF.Link | None + delinked: DF.Check event: DF.Data | None voucher_no: DF.DynamicLink | None voucher_type: DF.Link | None # end: auto-generated types - pass + def on_update(self): + if ( + self.against_voucher_type in ["Purchase Order", "Sales Order"] + and self.flags.update_outstanding == "Yes" + and not frappe.flags.is_reverse_depr_entry + ): + update_voucher_outstanding(self.against_voucher_type, self.against_voucher_no, None, None, None) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d7386902c81..4341aef9a6a 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -191,8 +191,6 @@ class JournalEntry(AccountsController): self.validate_cheque_info() self.check_credit_limit() self.make_gl_entries() - self.make_advance_payment_ledger_entries() - self.update_advance_paid() self.update_asset_value() self.update_inter_company_jv() self.update_invoice_discounting() @@ -225,8 +223,6 @@ class JournalEntry(AccountsController): "Advance Payment Ledger Entry", ) self.make_gl_entries(1) - self.make_advance_payment_ledger_entries() - self.update_advance_paid() self.unlink_advance_entry_reference() self.unlink_asset_reference() self.unlink_inter_company_jv() @@ -237,18 +233,6 @@ class JournalEntry(AccountsController): def get_title(self): return self.pay_to_recd_from or self.accounts[0].account - def update_advance_paid(self): - advance_paid = frappe._dict() - advance_payment_doctypes = get_advance_payment_doctypes() - for d in self.get("accounts"): - if d.is_advance: - if d.reference_type in advance_payment_doctypes: - advance_paid.setdefault(d.reference_type, []).append(d.reference_name) - - for voucher_type, order_list in advance_paid.items(): - for voucher_no in list(set(order_list)): - frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid() - def validate_inter_company_accounts(self): if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference: doc = frappe.db.get_value( @@ -1094,49 +1078,65 @@ class JournalEntry(AccountsController): self.transaction_exchange_rate = row.exchange_rate break + advance_doctypes = get_advance_payment_doctypes() + for d in self.get("accounts"): if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"): r = [d.user_remark, self.remark] r = [x for x in r if x] remarks = "\n".join(r) + row = { + "account": d.account, + "party_type": d.party_type, + "due_date": self.due_date, + "party": d.party, + "against": d.against_account, + "debit": flt(d.debit, d.precision("debit")), + "credit": flt(d.credit, d.precision("credit")), + "account_currency": d.account_currency, + "debit_in_account_currency": flt( + d.debit_in_account_currency, d.precision("debit_in_account_currency") + ), + "credit_in_account_currency": flt( + d.credit_in_account_currency, d.precision("credit_in_account_currency") + ), + "transaction_currency": self.transaction_currency, + "transaction_exchange_rate": self.transaction_exchange_rate, + "debit_in_transaction_currency": flt( + d.debit_in_account_currency, d.precision("debit_in_account_currency") + ) + if self.transaction_currency == d.account_currency + else flt(d.debit, d.precision("debit")) / self.transaction_exchange_rate, + "credit_in_transaction_currency": flt( + d.credit_in_account_currency, d.precision("credit_in_account_currency") + ) + if self.transaction_currency == d.account_currency + else flt(d.credit, d.precision("credit")) / self.transaction_exchange_rate, + "against_voucher_type": d.reference_type, + "against_voucher": d.reference_name, + "remarks": remarks, + "voucher_detail_no": d.reference_detail_no, + "cost_center": d.cost_center, + "project": d.project, + "finance_book": self.finance_book, + "advance_voucher_type": d.advance_voucher_type, + "advance_voucher_no": d.advance_voucher_no, + } + + if d.reference_type in advance_doctypes: + row.update( + { + "against_voucher_type": self.doctype, + "against_voucher": self.name, + "advance_voucher_type": d.reference_type, + "advance_voucher_no": d.reference_name, + } + ) + gl_map.append( self.get_gl_dict( - { - "account": d.account, - "party_type": d.party_type, - "due_date": self.due_date, - "party": d.party, - "against": d.against_account, - "debit": flt(d.debit, d.precision("debit")), - "credit": flt(d.credit, d.precision("credit")), - "account_currency": d.account_currency, - "debit_in_account_currency": flt( - d.debit_in_account_currency, d.precision("debit_in_account_currency") - ), - "credit_in_account_currency": flt( - d.credit_in_account_currency, d.precision("credit_in_account_currency") - ), - "transaction_currency": self.transaction_currency, - "transaction_exchange_rate": self.transaction_exchange_rate, - "debit_in_transaction_currency": flt( - d.debit_in_account_currency, d.precision("debit_in_account_currency") - ) - if self.transaction_currency == d.account_currency - else flt(d.debit, d.precision("debit")) / self.transaction_exchange_rate, - "credit_in_transaction_currency": flt( - d.credit_in_account_currency, d.precision("credit_in_account_currency") - ) - if self.transaction_currency == d.account_currency - else flt(d.credit, d.precision("credit")) / self.transaction_exchange_rate, - "against_voucher_type": d.reference_type, - "against_voucher": d.reference_name, - "remarks": remarks, - "voucher_detail_no": d.reference_detail_no, - "cost_center": d.cost_center, - "project": d.project, - "finance_book": self.finance_book, - }, + row, item=d, ) ) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index 00fd7b9d0ab..45c2b4ce764 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -32,6 +32,8 @@ "reference_name", "reference_due_date", "reference_detail_no", + "advance_voucher_type", + "advance_voucher_no", "col_break3", "is_advance", "user_remark", @@ -263,12 +265,28 @@ "label": "Reference Detail No", "no_copy": 1, "search_index": 1 + }, + { + "fieldname": "advance_voucher_type", + "fieldtype": "Link", + "label": "Advance Voucher Type", + "no_copy": 1, + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "advance_voucher_no", + "fieldtype": "Dynamic Link", + "label": "Advance Voucher No", + "no_copy": 1, + "options": "advance_voucher_type", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2024-02-05 01:10:50.224840", + "modified": "2025-07-25 04:45:28.117715", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", @@ -279,4 +297,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py index 00c9dcb374b..b801ac8c9a5 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.py @@ -17,8 +17,9 @@ class JournalEntryAccount(Document): account: DF.Link account_currency: DF.Link | None account_type: DF.Data | None + advance_voucher_no: DF.DynamicLink | None + advance_voucher_type: DF.Link | None against_account: DF.Text | None - balance: DF.Currency bank_account: DF.Link | None cost_center: DF.Link | None credit: DF.Currency @@ -31,7 +32,6 @@ class JournalEntryAccount(Document): parentfield: DF.Data parenttype: DF.Data party: DF.DynamicLink | None - party_balance: DF.Currency party_type: DF.Link | None project: DF.Link | None reference_detail_no: DF.Data | None diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 6e8e5e02451..b034e48e3ec 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -117,12 +117,10 @@ class PaymentEntry(AccountsController): def on_submit(self): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) + self.update_payment_requests() self.make_gl_entries() self.update_outstanding_amounts() self.update_payment_schedule() - self.update_payment_requests() - self.make_advance_payment_ledger_entries() - self.update_advance_paid() # advance_paid_status depends on the payment request amount self.set_status() def validate_for_repost(self): @@ -222,13 +220,11 @@ class PaymentEntry(AccountsController): "Advance Payment Ledger Entry", ) super().on_cancel() + self.update_payment_requests(cancel=True) self.make_gl_entries(cancel=1) self.update_outstanding_amounts() self.delink_advance_entry_references() self.update_payment_schedule(cancel=1) - self.update_payment_requests(cancel=True) - self.make_advance_payment_ledger_entries() - self.update_advance_paid() # advance_paid_status depends on the payment request amount self.set_status() def update_payment_requests(self, cancel=False): @@ -1366,23 +1362,27 @@ class PaymentEntry(AccountsController): dr_or_cr + "_in_transaction_currency": d.allocated_amount if self.transaction_currency == self.party_account_currency else allocated_amount_in_company_currency / self.transaction_exchange_rate, + "advance_voucher_type": d.advance_voucher_type, + "advance_voucher_no": d.advance_voucher_no, }, item=self, ) ) - if self.book_advance_payments_in_separate_party_account: - if d.reference_doctype in advance_payment_doctypes: - # Upon reconciliation, whole ledger will be reposted. So, reference to SO/PO is fine - gle.update( - { - "against_voucher_type": d.reference_doctype, - "against_voucher": d.reference_name, - } - ) - else: - # Do not reference Invoices while Advance is in separate party account - gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) + if d.reference_doctype in advance_payment_doctypes: + # advance reference + gle.update( + { + "against_voucher_type": self.doctype, + "against_voucher": self.name, + "advance_voucher_type": d.reference_doctype, + "advance_voucher_no": d.reference_name, + } + ) + + elif self.book_advance_payments_in_separate_party_account: + # Do not reference Invoices while Advance is in separate party account + gle.update({"against_voucher_type": self.doctype, "against_voucher": self.name}) else: gle.update( { @@ -1512,6 +1512,8 @@ class PaymentEntry(AccountsController): { "against_voucher_type": invoice.reference_doctype, "against_voucher": invoice.reference_name, + "advance_voucher_type": invoice.advance_voucher_type, + "advance_voucher_no": invoice.advance_voucher_no, "posting_date": posting_date, } ) @@ -1536,6 +1538,8 @@ class PaymentEntry(AccountsController): { "against_voucher_type": "Payment Entry", "against_voucher": self.name, + "advance_voucher_type": invoice.advance_voucher_type, + "advance_voucher_no": invoice.advance_voucher_no, } ) gle = self.get_gl_dict( @@ -1684,17 +1688,6 @@ class PaymentEntry(AccountsController): return flt(gl_dict.get(field, 0) / (conversion_rate or 1)) - def update_advance_paid(self): - if self.payment_type not in ("Receive", "Pay") or not self.party: - return - - advance_payment_doctypes = get_advance_payment_doctypes() - for d in self.get("references"): - if d.allocated_amount and d.reference_doctype in advance_payment_doctypes: - frappe.get_doc( - d.reference_doctype, d.reference_name, for_update=True - ).set_total_advance_paid() - def on_recurring(self, reference_doc, auto_repeat_doc): self.reference_no = reference_doc.name self.reference_date = nowdate() diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 1d0810852f0..da6c2eefae4 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -52,7 +52,7 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(pe.paid_to_account_type, "Cash") expected_gle = dict( - (d[0], d) for d in [["Debtors - _TC", 0, 1000, so.name], ["_Test Cash - _TC", 1000.0, 0, None]] + (d[0], d) for d in [["Debtors - _TC", 0, 1000, pe.name], ["_Test Cash - _TC", 1000.0, 0, None]] ) self.validate_gl_entries(pe.name, expected_gle) @@ -84,7 +84,7 @@ class TestPaymentEntry(FrappeTestCase): expected_gle = dict( (d[0], d) - for d in [["_Test Receivable USD - _TC", 0, 5500, so.name], [pe.paid_to, 5500.0, 0, None]] + for d in [["_Test Receivable USD - _TC", 0, 5500, pe.name], [pe.paid_to, 5500.0, 0, None]] ) self.validate_gl_entries(pe.name, expected_gle) diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index 57e401275fd..111de2ebd4e 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -22,7 +22,9 @@ "exchange_gain_loss", "account", "payment_request", - "payment_request_outstanding" + "payment_request_outstanding", + "advance_voucher_type", + "advance_voucher_no" ], "fields": [ { @@ -151,12 +153,28 @@ "fieldtype": "Date", "label": "Reconcile Effect On", "read_only": 1 + }, + { + "columns": 2, + "fieldname": "advance_voucher_type", + "fieldtype": "Link", + "label": "Advance Voucher Type", + "options": "DocType", + "read_only": 1 + }, + { + "columns": 2, + "fieldname": "advance_voucher_no", + "fieldtype": "Dynamic Link", + "label": "Advance Voucher No", + "options": "advance_voucher_type", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-01-13 15:56:18.895082", + "modified": "2025-07-25 04:32:11.040025", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", @@ -167,4 +185,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py index 1d869b92715..a5e0b21a9af 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.py @@ -16,6 +16,8 @@ class PaymentEntryReference(Document): account: DF.Link | None account_type: DF.Data | None + advance_voucher_no: DF.DynamicLink | None + advance_voucher_type: DF.Link | None allocated_amount: DF.Float bill_no: DF.Data | None due_date: DF.Date | None @@ -26,7 +28,6 @@ class PaymentEntryReference(Document): parentfield: DF.Data parenttype: DF.Data payment_request: DF.Link | None - payment_request_outstanding: DF.Float payment_term: DF.Link | None payment_term_outstanding: DF.Float payment_type: DF.Data | None diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json index 28c9529995d..30a2da24bbb 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -197,4 +197,4 @@ "sort_field": "modified", "sort_order": "DESC", "states": [] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index bd5ba42b0b6..679eb90f19a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -2206,6 +2206,136 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.get("payments")), 0) self.assertEqual(pr.get("invoices")[0].get("outstanding_amount"), 200) + def test_partial_advance_payment_with_closed_fiscal_year(self): + """ + Test Advance Payment partial reconciliation before period closing and partial after period closing + """ + default_settings = frappe.db.get_value( + "Company", + self.company, + [ + "book_advance_payments_in_separate_party_account", + "default_advance_paid_account", + "reconciliation_takes_effect_on", + ], + as_dict=True, + ) + first_fy_start_date = frappe.db.get_value("Fiscal Year", {"disabled": 0}, "min(year_start_date)") + prev_fy_start_date = add_years(first_fy_start_date, -1) + prev_fy_end_date = add_days(first_fy_start_date, -1) + + create_fiscal_year( + company=self.company, year_start_date=prev_fy_start_date, year_end_date=prev_fy_end_date + ) + + frappe.db.set_value( + "Company", + self.company, + { + "book_advance_payments_in_separate_party_account": 1, + "default_advance_paid_account": self.advance_payable_account, + "reconciliation_takes_effect_on": "Oldest Of Invoice Or Advance", + }, + ) + + self.supplier = "_Test Supplier" + + # Create advance payment of 1000 (previous FY) + pe = self.create_payment_entry(amount=1000, posting_date=prev_fy_start_date) + pe.party_type = "Supplier" + pe.party = self.supplier + pe.payment_type = "Pay" + pe.paid_from = self.cash + pe.paid_to = self.advance_payable_account + pe.save().submit() + + # Create purchase invoice of 600 (previous FY) + pi1 = self.create_purchase_invoice(qty=1, rate=600, do_not_submit=True) + pi1.posting_date = prev_fy_start_date + pi1.set_posting_time = 1 + pi1.supplier = self.supplier + pi1.credit_to = self.creditors + pi1.save().submit() + + # Reconcile advance payment + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.party = self.supplier + pr.receivable_payable_account = self.creditors + pr.default_advance_account = self.advance_payable_account + pr.from_invoice_date = pr.to_invoice_date = pi1.posting_date + pr.from_payment_date = pr.to_payment_date = pe.posting_date + pr.get_unreconciled_entries() + invoices = [x.as_dict() for x in pr.invoices if x.invoice_number == pi1.name] + payments = [x.as_dict() for x in pr.payments if x.reference_name == pe.name] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + # Verify partial reconciliation + pe.reload() + pi1.reload() + + self.assertEqual(len(pe.references), 1) + self.assertEqual(pe.references[0].allocated_amount, 600) + self.assertEqual(flt(pe.unallocated_amount), 400) + + self.assertEqual(pi1.outstanding_amount, 0) + self.assertEqual(pi1.status, "Paid") + + # Close accounting period for March (previous FY) + pcv = make_period_closing_voucher( + company=self.company, cost_center=self.cost_center, posting_date=prev_fy_end_date + ) + pcv.reload() + self.assertEqual(pcv.gle_processing_status, "Completed") + + # Change reconciliation setting to "Reconciliation Date" + frappe.db.set_value( + "Company", + self.company, + "reconciliation_takes_effect_on", + "Reconciliation Date", + ) + + # Create new purchase invoice for 400 in new fiscal year + pi2 = self.create_purchase_invoice(qty=1, rate=400, do_not_submit=True) + pi2.posting_date = today() + pi2.set_posting_time = 1 + pi2.supplier = self.supplier + pi2.currency = "INR" + pi2.credit_to = self.creditors + pi2.save() + pi2.submit() + + # Allocate 600 from advance payment to purchase invoice + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.party = self.supplier + pr.receivable_payable_account = self.creditors + pr.default_advance_account = self.advance_payable_account + pr.from_invoice_date = pr.to_invoice_date = pi2.posting_date + pr.from_payment_date = pr.to_payment_date = pe.posting_date + pr.get_unreconciled_entries() + invoices = [x.as_dict() for x in pr.invoices if x.invoice_number == pi2.name] + payments = [x.as_dict() for x in pr.payments if x.reference_name == pe.name] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + + pe.reload() + pi2.reload() + + # Assert advance payment is fully allocated + self.assertEqual(len(pe.references), 2) + self.assertEqual(flt(pe.unallocated_amount), 0) + + # Assert new invoice is fully paid + self.assertEqual(pi2.outstanding_amount, 0) + self.assertEqual(pi2.status, "Paid") + + # Verify reconciliation dates are correct based on company setting + self.assertEqual(getdate(pe.references[0].reconcile_effect_on), getdate(pi1.posting_date)) + self.assertEqual(getdate(pe.references[1].reconcile_effect_on), getdate(pi2.posting_date)) + + frappe.db.set_value("Company", self.company, default_settings) + def make_customer(customer_name, currency=None): if not frappe.db.exists("Customer", customer_name): diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index f38c58d6d32..c3f73f0fc12 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -328,7 +328,7 @@ class TestPaymentRequest(FrappeTestCase): self.assertEqual(pe.paid_amount, 800) # paid amount set from pr's outstanding amount self.assertEqual(pe.references[0].allocated_amount, 800) - self.assertEqual(pe.references[0].outstanding_amount, 800) # for Orders it is not zero + self.assertEqual(pe.references[0].outstanding_amount, 0) # Also for orders it will zero self.assertEqual(pe.references[0].payment_request, pr.name) so.load_from_db() diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index 800f1647471..aec26b280c4 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -169,6 +169,10 @@ def start_repost(account_repost_doc=str) -> None: frappe.db.delete( "Payment Ledger Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name} ) + frappe.db.delete( + "Advance Payment Ledger Entry", + filters={"voucher_type": doc.doctype, "voucher_no": doc.name}, + ) if doc.doctype in ["Sales Invoice", "Purchase Invoice"]: if not repost_doc.delete_cancelled_entries: diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py index 6b90300a899..6fd1b0f2bf2 100644 --- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py +++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py @@ -8,7 +8,7 @@ from frappe import _, qb from frappe.model.document import Document from frappe.query_builder.custom import ConstantColumn -from erpnext.accounts.utils import _delete_pl_entries, create_payment_ledger_entry +from erpnext.accounts.utils import _delete_adv_pl_entries, _delete_pl_entries, create_payment_ledger_entry VOUCHER_TYPES = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"] @@ -16,6 +16,7 @@ VOUCHER_TYPES = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal def repost_ple_for_voucher(voucher_type, voucher_no, gle_map=None): if voucher_type and voucher_no and gle_map: _delete_pl_entries(voucher_type, voucher_no) + _delete_adv_pl_entries(voucher_type, voucher_no) create_payment_ledger_entry(gle_map, cancel=0) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index 021703f6483..256d7870714 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -465,10 +465,26 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): self.assertEqual(len(pr.get("invoices")), 0) self.assertEqual(len(pr.get("payments")), 0) - # Assert 'Advance Paid' so.reload() self.assertEqual(so.advance_paid, 1000) + unreconcile = frappe.get_doc( + { + "doctype": "Unreconcile Payment", + "company": self.company, + "voucher_type": pe.doctype, + "voucher_no": pe.name, + } + ) + unreconcile.add_references() + unreconcile.allocations = [x for x in unreconcile.allocations if x.reference_name == si.name] + unreconcile.save().submit() + + # after unreconcilaition advance paid will be reduced + # Assert 'Advance Paid' + so.reload() + self.assertEqual(so.advance_paid, 0) + self.disable_advance_as_liability() def test_unreconcile_advance_from_journal_entry(self): diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py index f7c826bf3fc..02cad39de8f 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py @@ -12,7 +12,6 @@ from frappe.utils.data import comma_and from erpnext.accounts.utils import ( cancel_exchange_gain_loss_journal, - get_advance_payment_doctypes, unlink_ref_doc_from_payment_entries, update_voucher_outstanding, ) @@ -45,31 +44,12 @@ class UnreconcilePayment(Document): @frappe.whitelist() def get_allocations_from_payment(self): - allocated_references = [] - ple = qb.DocType("Payment Ledger Entry") - allocated_references = ( - qb.from_(ple) - .select( - ple.account, - ple.party_type, - ple.party, - ple.against_voucher_type.as_("reference_doctype"), - ple.against_voucher_no.as_("reference_name"), - Abs(Sum(ple.amount_in_account_currency)).as_("allocated_amount"), - ple.account_currency, - ) - .where( - (ple.docstatus == 1) - & (ple.voucher_type == self.voucher_type) - & (ple.voucher_no == self.voucher_no) - & (ple.voucher_no != ple.against_voucher_no) - ) - .groupby(ple.against_voucher_type, ple.against_voucher_no) - .run(as_dict=True) + return get_linked_payments_for_doc( + company=self.company, + doctype=self.voucher_type, + docname=self.voucher_no, ) - return allocated_references - def add_references(self): allocations = self.get_allocations_from_payment() @@ -82,42 +62,43 @@ class UnreconcilePayment(Document): doc = frappe.get_doc(alloc.reference_doctype, alloc.reference_name) unlink_ref_doc_from_payment_entries(doc, self.voucher_no) cancel_exchange_gain_loss_journal(doc, self.voucher_type, self.voucher_no) + + # update outstanding amounts update_voucher_outstanding( - alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party + alloc.reference_doctype, + alloc.reference_name, + alloc.account, + alloc.party_type, + alloc.party, ) - if doc.doctype in get_advance_payment_doctypes(): - self.make_advance_payment_ledger(alloc) - doc.set_total_advance_paid() frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True) - def make_advance_payment_ledger(self, alloc): - if alloc.allocated_amount > 0: - doc = frappe.new_doc("Advance Payment Ledger Entry") - doc.company = self.company - doc.voucher_type = self.voucher_type - doc.voucher_no = self.voucher_no - doc.against_voucher_type = alloc.reference_doctype - doc.against_voucher_no = alloc.reference_name - doc.amount = -1 * alloc.allocated_amount - doc.event = "Unreconcile" - doc.currency = alloc.account_currency - doc.flags.ignore_permissions = 1 - doc.save() - @frappe.whitelist() def doc_has_references(doctype: str | None = None, docname: str | None = None): + count = 0 if doctype in ["Sales Invoice", "Purchase Invoice"]: - return frappe.db.count( + count = frappe.db.count( "Payment Ledger Entry", filters={"delinked": 0, "against_voucher_no": docname, "amount": ["<", 0]}, ) else: - return frappe.db.count( + count = frappe.db.count( "Payment Ledger Entry", filters={"delinked": 0, "voucher_no": docname, "against_voucher_no": ["!=", docname]}, ) + count += frappe.db.count( + "Advance Payment Ledger Entry", + filters={ + "delinked": 0, + "voucher_no": docname, + "voucher_type": doctype, + "event": ["=", "Submit"], + }, + ) + + return count @frappe.whitelist() @@ -139,9 +120,12 @@ def get_linked_payments_for_doc( res = ( qb.from_(ple) .select( + ple.account, + ple.party_type, + ple.party, ple.company, - ple.voucher_type, - ple.voucher_no, + ple.voucher_type.as_("reference_doctype"), + ple.voucher_no.as_("reference_name"), Abs(Sum(ple.amount_in_account_currency)).as_("allocated_amount"), ple.account_currency, ) @@ -163,19 +147,52 @@ def get_linked_payments_for_doc( qb.from_(ple) .select( ple.company, - ple.against_voucher_type.as_("voucher_type"), - ple.against_voucher_no.as_("voucher_no"), + ple.account, + ple.party_type, + ple.party, + ple.against_voucher_type.as_("reference_doctype"), + ple.against_voucher_no.as_("reference_name"), Abs(Sum(ple.amount_in_account_currency)).as_("allocated_amount"), ple.account_currency, ) .where(Criterion.all(criteria)) .groupby(ple.against_voucher_no) ) + res = query.run(as_dict=True) + + res += get_linked_advances(company, _dn) + return res + return [] +def get_linked_advances(company, docname): + adv = qb.DocType("Advance Payment Ledger Entry") + criteria = [ + (adv.company == company), + (adv.delinked == 0), + (adv.voucher_no == docname), + (adv.event == "Submit"), + ] + + return ( + qb.from_(adv) + .select( + adv.company, + adv.against_voucher_type.as_("reference_doctype"), + adv.against_voucher_no.as_("reference_name"), + Abs(Sum(adv.amount)).as_("allocated_amount"), + adv.currency, + ) + .where(Criterion.all(criteria)) + .having(qb.Field("allocated_amount") > 0) + .groupby(adv.against_voucher_no) + .run(as_dict=True) + ) + + @frappe.whitelist() def create_unreconcile_doc_for_selection(selections=None): if selections: diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 20476b4164c..a0d08792d29 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -305,6 +305,8 @@ def get_merge_properties(dimensions=None): "project", "finance_book", "voucher_no", + "advance_voucher_type", + "advance_voucher_no", ] if dimensions: merge_properties.extend(dimensions) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 97b1bc9ab83..022dfb889a9 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -471,42 +471,27 @@ def reconcile_against_document( reconciled_entries[(row.voucher_type, row.voucher_no)].append(row) for key, entries in reconciled_entries.items(): - voucher_type = key[0] - voucher_no = key[1] + voucher_type, voucher_no = key - # cancel advance entry doc = frappe.get_doc(voucher_type, voucher_no) frappe.flags.ignore_party_validation = True - # When Advance is allocated from an Order to an Invoice - # whole ledger must be reposted - repost_whole_ledger = any([x.voucher_detail_no for x in entries]) - if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: - if repost_whole_ledger: - doc.make_gl_entries(cancel=1) - else: - doc.make_advance_gl_entries(cancel=1) - else: - _delete_pl_entries(voucher_type, voucher_no) - + reposting_rows = [] for entry in entries: check_if_advance_entry_modified(entry) validate_allocated_amount(entry) dimensions_dict = _build_dimensions_dict_for_exc_gain_loss(entry, active_dimensions) - # update ref in advance entry if voucher_type == "Journal Entry": - referenced_row, update_advance_paid = update_reference_in_journal_entry( - entry, doc, do_not_save=False - ) + referenced_row = update_reference_in_journal_entry(entry, doc, do_not_save=False) # advance section in sales/purchase invoice and reconciliation tool,both pass on exchange gain/loss # amount and account in args # referenced_row is used to deduplicate gain/loss journal - entry.update({"referenced_row": referenced_row}) + entry.update({"referenced_row": referenced_row.name}) doc.make_exchange_gain_loss_journal([entry], dimensions_dict) else: - referenced_row, update_advance_paid = update_reference_in_payment_entry( + referenced_row = update_reference_in_payment_entry( entry, doc, do_not_save=True, @@ -515,20 +500,16 @@ def reconcile_against_document( ) if referenced_row.get("outstanding_amount"): referenced_row.outstanding_amount -= flt(entry.allocated_amount) + + reposting_rows.append(referenced_row) + doc.save(ignore_permissions=True) - # re-submit advance entry - doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: - # When Advance is allocated from an Order to an Invoice - # whole ledger must be reposted - if repost_whole_ledger: - doc.make_gl_entries() - else: - # both ledgers must be posted to for `Advance` in separate account feature - # TODO: find a more efficient way post only for the new linked vouchers - doc.make_advance_gl_entries() + for row in reposting_rows: + doc.make_advance_gl_entries(entry=row) else: + _delete_pl_entries(voucher_type, voucher_no) gl_map = doc.build_gl_map() # Make sure there is no overallocation from erpnext.accounts.general_ledger import process_debit_credit_difference @@ -545,11 +526,6 @@ def reconcile_against_document( entry.party_type, entry.party, ) - # update advance paid in Advance Receivable/Payable doctypes - if update_advance_paid: - for t, n in update_advance_paid: - frappe.get_doc(t, n).set_total_advance_paid() - frappe.flags.ignore_party_validation = False @@ -630,12 +606,6 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): """ jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0] - # Update Advance Paid in SO/PO since they might be getting unlinked - update_advance_paid = [] - - if jv_detail.get("reference_type") in ["Sales Order", "Purchase Order"]: - update_advance_paid.append((jv_detail.reference_type, jv_detail.reference_name)) - rev_dr_or_cr = ( "debit_in_account_currency" if d["dr_or_cr"] == "credit_in_account_currency" @@ -688,6 +658,10 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): new_row.is_advance = cstr(jv_detail.is_advance) new_row.docstatus = 1 + if jv_detail.get("reference_type") in get_advance_payment_doctypes(): + new_row.advance_voucher_type = jv_detail.get("reference_type") + new_row.advance_voucher_no = jv_detail.get("reference_name") + # will work as update after submit journal_entry.flags.ignore_validate_update_after_submit = True # Ledgers will be reposted by Reconciliation tool @@ -695,7 +669,7 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): if not do_not_save: journal_entry.save(ignore_permissions=True) - return new_row.name, update_advance_paid + return new_row def update_reference_in_payment_entry( @@ -714,7 +688,8 @@ def update_reference_in_payment_entry( "account": d.account, "dimensions": d.dimensions, } - update_advance_paid = [] + + advance_payment_doctypes = get_advance_payment_doctypes() # Update Reconciliation effect date in reference if payment_entry.book_advance_payments_in_separate_party_account: @@ -726,10 +701,6 @@ def update_reference_in_payment_entry( if d.voucher_detail_no: existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0] - # Update Advance Paid in SO/PO since they are getting unlinked - if existing_row.get("reference_doctype") in ["Sales Order", "Purchase Order"]: - update_advance_paid.append((existing_row.reference_doctype, existing_row.reference_name)) - if d.allocated_amount <= existing_row.allocated_amount: existing_row.allocated_amount -= d.allocated_amount @@ -737,7 +708,13 @@ def update_reference_in_payment_entry( new_row.docstatus = 1 for field in list(reference_details): new_row.set(field, reference_details[field]) + + if existing_row.reference_doctype in advance_payment_doctypes: + new_row.advance_voucher_type = existing_row.reference_doctype + new_row.advance_voucher_no = existing_row.reference_name + row = new_row + else: new_row = payment_entry.append("references") new_row.docstatus = 1 @@ -771,7 +748,8 @@ def update_reference_in_payment_entry( payment_entry.flags.ignore_reposting_on_reconciliation = True if not do_not_save: payment_entry.save(ignore_permissions=True) - return row, update_advance_paid + + return row def get_reconciliation_effect_date(against_voucher_type, against_voucher, company, posting_date): @@ -949,6 +927,24 @@ def update_accounting_ledgers_after_reference_removal( ple_update_query = ple_update_query.where(ple.voucher_no == payment_name) ple_update_query.run() + # Advance Payment + adv = qb.DocType("Advance Payment Ledger Entry") + adv_ple = ( + qb.update(adv) + .set(adv.delinked, 1) + .set(adv.modified, now()) + .set(adv.modified_by, frappe.session.user) + .where(adv.delinked == 0) + .where( + ((adv.against_voucher_type == ref_type) & (adv.against_voucher_no == ref_no)) + | ((adv.voucher_type == ref_type) & (adv.voucher_no == ref_no)) + ) + ) + if payment_name: + adv_ple = adv_ple.where(adv.voucher_no == payment_name) + + adv_ple.run() + def remove_ref_from_advance_section(ref_doc: object = None): # TODO: this might need some testing @@ -985,6 +981,8 @@ def remove_ref_doc_link_from_jv( qb.update(jea) .set(jea.reference_type, None) .set(jea.reference_name, None) + .set(jea.advance_voucher_type, None) + .set(jea.advance_voucher_no, None) .set(jea.modified, now()) .set(jea.modified_by, frappe.session.user) .where((jea.reference_type == ref_type) & (jea.reference_name == ref_no)) @@ -1495,6 +1493,11 @@ def _delete_pl_entries(voucher_type, voucher_no): qb.from_(ple).delete().where((ple.voucher_type == voucher_type) & (ple.voucher_no == voucher_no)).run() +def _delete_adv_pl_entries(voucher_type, voucher_no): + adv = qb.DocType("Advance Payment Ledger Entry") + qb.from_(adv).delete().where((adv.voucher_type == voucher_type) & (adv.voucher_no == voucher_no)).run() + + def _delete_gl_entries(voucher_type, voucher_no): gle = qb.DocType("GL Entry") qb.from_(gle).delete().where((gle.voucher_type == voucher_type) & (gle.voucher_no == voucher_no)).run() @@ -1814,6 +1817,11 @@ def get_payment_ledger_entries(gl_entries, cancel=0): dr_or_cr *= -1 dr_or_cr_account_currency *= -1 + against_voucher_type = ( + gle.against_voucher_type if gle.against_voucher_type else gle.voucher_type + ) + against_voucher_no = gle.against_voucher if gle.against_voucher else gle.voucher_no + ple = frappe._dict( doctype="Payment Ledger Entry", posting_date=gle.posting_date, @@ -1828,14 +1836,12 @@ def get_payment_ledger_entries(gl_entries, cancel=0): voucher_type=gle.voucher_type, voucher_no=gle.voucher_no, voucher_detail_no=gle.voucher_detail_no, - against_voucher_type=gle.against_voucher_type - if gle.against_voucher_type - else gle.voucher_type, - against_voucher_no=gle.against_voucher if gle.against_voucher else gle.voucher_no, + against_voucher_type=against_voucher_type, + against_voucher_no=against_voucher_no, account_currency=gle.account_currency, amount=dr_or_cr, amount_in_account_currency=dr_or_cr_account_currency, - delinked=True if cancel else False, + delinked=cancel, remarks=gle.remarks, ) @@ -1844,10 +1850,40 @@ def get_payment_ledger_entries(gl_entries, cancel=0): for dimension in dimensions_and_defaults[0]: ple[dimension.fieldname] = gle.get(dimension.fieldname) + if gle.advance_voucher_no: + # create advance entry + adv = get_advance_ledger_entry( + gle, against_voucher_type, against_voucher_no, dr_or_cr_account_currency, cancel + ) + + ple_map.append(adv) + ple_map.append(ple) + return ple_map +def get_advance_ledger_entry(gle, against_voucher_type, against_voucher_no, amount, cancel): + event = ( + "Submit" + if (against_voucher_type == gle.voucher_type and against_voucher_no == gle.voucher_no) + else "Adjustment" + ) + return frappe._dict( + doctype="Advance Payment Ledger Entry", + company=gle.company, + voucher_type=gle.voucher_type, + voucher_no=gle.voucher_no, + voucher_detail_no=gle.voucher_detail_no, + against_voucher_type=gle.advance_voucher_type, + against_voucher_no=gle.advance_voucher_no, + amount=amount, + currency=gle.account_currency, + event=event, + delinked=cancel, + ) + + def create_payment_ledger_entry( gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0, partial_cancel=False ): @@ -1868,6 +1904,14 @@ def create_payment_ledger_entry( def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party): + if not voucher_type or not voucher_no: + return + + if voucher_type in ["Purchase Order", "Sales Order"]: + ref_doc = frappe.get_lazy_doc(voucher_type, voucher_no) + ref_doc.set_total_advance_paid() + return + ple = frappe.qb.DocType("Payment Ledger Entry") vouchers = [frappe._dict({"voucher_type": voucher_type, "voucher_no": voucher_no})] common_filter = [] @@ -1910,7 +1954,27 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa def delink_original_entry(pl_entry, partial_cancel=False): - if pl_entry: + if not pl_entry: + return + + if pl_entry.doctype == "Advance Payment Ledger Entry": + adv = qb.DocType("Advance Payment Ledger Entry") + + ( + qb.update(adv) + .set(adv.delinked, 1) + .set(adv.event, "Cancel") + .set(adv.modified, now()) + .set(adv.modified_by, frappe.session.user) + .where(adv.voucher_type == pl_entry.voucher_type) + .where(adv.voucher_no == pl_entry.voucher_no) + .where(adv.against_voucher_type == pl_entry.against_voucher_type) + .where(adv.against_voucher_no == pl_entry.against_voucher_no) + .where(adv.event == pl_entry.event) + .run() + ) + + else: ple = qb.DocType("Payment Ledger Entry") query = ( qb.update(ple) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index e29e695f48c..6419a4ebac2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -505,6 +505,7 @@ class PurchaseOrder(BuyingController): self.ignore_linked_doctypes = ( "GL Entry", "Payment Ledger Entry", + "Advance Payment Ledger Entry", "Unreconcile Payment", "Unreconcile Payment Entries", ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e0e53adeebd..c2fd009fc4a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -397,7 +397,6 @@ class AccountsController(TransactionBase): def on_trash(self): from erpnext.accounts.utils import delete_exchange_gain_loss_journal - self._remove_advance_payment_ledger_entries() self._remove_references_in_repost_doctypes() self._remove_references_in_unreconcile() self.remove_serial_and_batch_bundle() @@ -426,6 +425,8 @@ class AccountsController(TransactionBase): (sle.voucher_type == self.doctype) & (sle.voucher_no == self.name) ).run() + self._remove_advance_payment_ledger_entries() + def remove_serial_and_batch_bundle(self): bundles = frappe.get_all( "Serial and Batch Bundle", @@ -2233,54 +2234,30 @@ class AccountsController(TransactionBase): def calculate_total_advance_from_ledger(self): adv = frappe.qb.DocType("Advance Payment Ledger Entry") - advance = ( - frappe.qb.from_(adv) - .select(adv.currency.as_("account_currency"), Abs(Sum(adv.amount)).as_("amount")) - .where( - (adv.against_voucher_type == self.doctype) - & (adv.against_voucher_no == self.name) - & (adv.company == self.company) - ) + return ( + qb.from_(adv) + .select(Abs(Sum(adv.amount)).as_("amount"), adv.currency.as_("account_currency")) + .where(adv.company == self.company) + .where(adv.delinked == 0) + .where(adv.against_voucher_type == self.doctype) + .where(adv.against_voucher_no == self.name) .run(as_dict=True) ) - return advance def set_total_advance_paid(self): advance = self.calculate_total_advance_from_ledger() - advance_paid, order_total = None, None + advance_paid = 0 if advance: advance = advance[0] advance_paid = flt(advance.amount, self.precision("advance_paid")) - formatted_advance_paid = fmt_money( - advance_paid, precision=self.precision("advance_paid"), currency=advance.account_currency - ) - if advance.account_currency: frappe.db.set_value( self.doctype, self.name, "party_account_currency", advance.account_currency ) - if advance.account_currency == self.currency: - order_total = self.get("rounded_total") or self.grand_total - precision = "rounded_total" if self.get("rounded_total") else "grand_total" - else: - order_total = self.get("base_rounded_total") or self.base_grand_total - precision = "base_rounded_total" if self.get("base_rounded_total") else "base_grand_total" - - formatted_order_total = fmt_money( - order_total, precision=self.precision(precision), currency=advance.account_currency - ) - - if self.currency == self.company_currency and advance_paid > order_total: - frappe.throw( - _( - "Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2})" - ).format(formatted_advance_paid, self.name, formatted_order_total) - ) - - self.db_set("advance_paid", advance_paid) + self.db_set("advance_paid", advance_paid) @property def company_abbr(self): @@ -2924,64 +2901,6 @@ class AccountsController(TransactionBase): def get_advance_payment_doctypes(self) -> list: return _get_advance_payment_doctypes() - def make_advance_payment_ledger_for_journal(self): - advance_payment_doctypes = self.get_advance_payment_doctypes() - advance_doctype_references = [ - x for x in self.accounts if x.reference_type in advance_payment_doctypes - ] - - for x in advance_doctype_references: - # Looking for payments - dr_or_cr = ( - "credit_in_account_currency" - if x.account_type == "Receivable" - else "debit_in_account_currency" - ) - - amount = x.get(dr_or_cr) - if amount > 0: - doc = frappe.new_doc("Advance Payment Ledger Entry") - doc.company = self.company - doc.voucher_type = self.doctype - doc.voucher_no = self.name - doc.against_voucher_type = x.reference_type - doc.against_voucher_no = x.reference_name - doc.amount = amount if self.docstatus == 1 else -1 * amount - doc.event = "Submit" if self.docstatus == 1 else "Cancel" - doc.currency = x.account_currency - doc.flags.ignore_permissions = 1 - doc.save() - - def make_advance_payment_ledger_for_payment(self): - advance_payment_doctypes = self.get_advance_payment_doctypes() - advance_doctype_references = [ - x for x in self.references if x.reference_doctype in advance_payment_doctypes - ] - currency = ( - self.paid_from_account_currency - if self.payment_type == "Receive" - else self.paid_to_account_currency - ) - for x in advance_doctype_references: - doc = frappe.new_doc("Advance Payment Ledger Entry") - doc.company = self.company - doc.voucher_type = self.doctype - doc.voucher_no = self.name - doc.against_voucher_type = x.reference_doctype - doc.against_voucher_no = x.reference_name - doc.amount = x.allocated_amount if self.docstatus == 1 else -1 * x.allocated_amount - doc.currency = currency - doc.event = "Submit" if self.docstatus == 1 else "Cancel" - doc.flags.ignore_permissions = 1 - doc.save() - - def make_advance_payment_ledger_entries(self): - if self.docstatus != 0: - if self.doctype == "Journal Entry": - self.make_advance_payment_ledger_for_journal() - elif self.doctype == "Payment Entry": - self.make_advance_payment_ledger_for_payment() - def set_transaction_currency_and_rate_in_gl_map(self, gl_entries): for x in gl_entries: x["transaction_currency"] = self.currency diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7bd30f81bc3..d87f595a500 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -361,7 +361,7 @@ erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request erpnext.patches.v14_0.update_pos_return_ledger_entries #2024-08-16 -erpnext.patches.v15_0.create_advance_payment_ledger_records +erpnext.patches.v15_0.create_advance_payment_ledger_records #2025-07-04 # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 @@ -412,6 +412,7 @@ erpnext.patches.v15_0.drop_sle_indexes erpnext.patches.v15_0.update_pick_list_fields erpnext.patches.v15_0.update_pegged_currencies erpnext.patches.v15_0.set_company_on_pos_inv_merge_log +erpnext.patches.v15_0.update_payment_ledger_entries_against_advance_doctypes erpnext.patches.v15_0.rename_price_list_to_buying_price_list erpnext.patches.v15_0.patch_missing_buying_price_list_in_material_request erpnext.patches.v15_0.remove_sales_partner_from_consolidated_sales_invoice diff --git a/erpnext/patches/v15_0/create_advance_payment_ledger_records.py b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py index 8d247885cab..e155eaea1d1 100644 --- a/erpnext/patches/v15_0/create_advance_payment_ledger_records.py +++ b/erpnext/patches/v15_0/create_advance_payment_ledger_records.py @@ -1,63 +1,28 @@ import frappe -from frappe import qb -from frappe.query_builder.custom import ConstantColumn +from frappe.model.naming import _generate_random_string +from frappe.query_builder import Case +from frappe.utils import now_datetime +from erpnext.accounts.utils import get_advance_payment_doctypes -def get_advance_doctypes() -> list: - return frappe.get_hooks("advance_payment_doctypes") +DOCTYPE = "Advance Payment Ledger Entry" - -def get_payments_with_so_po_reference() -> list: - advance_payment_entries = [] - advance_doctypes = get_advance_doctypes() - per = qb.DocType("Payment Entry Reference") - payments_with_reference = ( - qb.from_(per) - .select(per.parent) - .distinct() - .where(per.reference_doctype.isin(advance_doctypes) & per.docstatus.eq(1)) - .run() - ) - if payments_with_reference: - pe = qb.DocType("Payment Entry") - advance_payment_entries = ( - qb.from_(pe) - .select(ConstantColumn("Payment Entry").as_("doctype")) - .select(pe.name) - .where(pe.name.isin(payments_with_reference) & pe.docstatus.eq(1)) - .run(as_dict=True) - ) - - return advance_payment_entries - - -def get_journals_with_so_po_reference() -> list: - advance_journal_entries = [] - advance_doctypes = get_advance_doctypes() - jea = qb.DocType("Journal Entry Account") - journals_with_reference = ( - qb.from_(jea) - .select(jea.parent) - .distinct() - .where(jea.reference_type.isin(advance_doctypes) & jea.docstatus.eq(1)) - .run() - ) - if journals_with_reference: - je = qb.DocType("Journal Entry") - advance_journal_entries = ( - qb.from_(je) - .select(ConstantColumn("Journal Entry").as_("doctype")) - .select(je.name) - .where(je.name.isin(journals_with_reference) & je.docstatus.eq(1)) - .run(as_dict=True) - ) - - return advance_journal_entries - - -def make_advance_ledger_entries(vouchers: list): - for x in vouchers: - frappe.get_doc(x.doctype, x.name).make_advance_payment_ledger_entries() +FIELDS = [ + "name", + "creation", + "modified", + "owner", + "modified_by", + "company", + "voucher_type", + "voucher_no", + "against_voucher_type", + "against_voucher_no", + "amount", + "currency", + "event", + "delinked", +] def execute(): @@ -65,9 +30,102 @@ def execute(): Description: Create Advance Payment Ledger Entry for all Payments made against Sales / Purchase Orders """ - frappe.db.truncate("Advance Payment Ledger Entry") - payment_entries = get_payments_with_so_po_reference() - make_advance_ledger_entries(payment_entries) + frappe.db.truncate(DOCTYPE) + advance_doctpyes = get_advance_payment_doctypes() - journals = get_journals_with_so_po_reference() - make_advance_ledger_entries(journals) + if not advance_doctpyes: + return + + make_advance_ledger_entries_for_payment_entries(advance_doctpyes) + make_advance_ledger_entries_for_journal_entries(advance_doctpyes) + + +def make_advance_ledger_entries_for_payment_entries(advance_doctpyes) -> list: + pe = frappe.qb.DocType("Payment Entry") + per = frappe.qb.DocType("Payment Entry Reference") + + entries = ( + frappe.qb.from_(per) + .inner_join(pe) + .on(pe.name == per.parent) + .select( + pe.company, + per.parenttype.as_("voucher_type"), + per.parent.as_("voucher_no"), + per.reference_doctype.as_("against_voucher_type"), + per.reference_name.as_("against_voucher_no"), + per.allocated_amount.as_("amount"), + Case() + .when(pe.payment_type == "Receive", pe.paid_from_account_currency) + .else_(pe.paid_to_account_currency) + .as_("currency"), + ) + .where(per.reference_doctype.isin(advance_doctpyes) & per.docstatus.eq(1)) + .run(as_dict=True) + ) + + if not entries: + return + + bulk_insert_advance_entries(entries) + + +def make_advance_ledger_entries_for_journal_entries(advance_doctpyes) -> list: + je = frappe.qb.DocType("Journal Entry") + jea = frappe.qb.DocType("Journal Entry Account") + + entries = ( + frappe.qb.from_(jea) + .inner_join(je) + .on(je.name == jea.parent) + .select( + je.company, + jea.parenttype.as_("voucher_type"), + jea.parent.as_("voucher_no"), + jea.reference_type.as_("against_voucher_type"), + jea.reference_name.as_("against_voucher_no"), + Case() + .when(jea.account_type == "Receivable", jea.credit_in_account_currency) + .else_(jea.debit_in_account_currency) + .as_("amount"), + jea.account_currency.as_("currency"), + ) + .where(jea.reference_type.isin(advance_doctpyes) & jea.docstatus.eq(1)) + .run(as_dict=True) + ) + + if not entries: + return + + bulk_insert_advance_entries(entries) + + +def bulk_insert_advance_entries(entries): + details = [] + user = frappe.session.user + now = now_datetime() + for entry in entries: + if entry.amount < 0: + continue + details.append(get_values(user, now, entry)) + + frappe.db.bulk_insert(DOCTYPE, fields=FIELDS, values=details) + + +def get_values(user, now, entry): + return ( + _generate_random_string(10), + now, + now, + user, + user, + entry.company, + entry.voucher_type, + entry.voucher_no, + entry.against_voucher_type, + entry.against_voucher_no, + entry.amount * -1, + entry.currency, + "Submit", + 0, + ) diff --git a/erpnext/patches/v15_0/update_payment_ledger_entries_against_advance_doctypes.py b/erpnext/patches/v15_0/update_payment_ledger_entries_against_advance_doctypes.py new file mode 100644 index 00000000000..f5a20803c6e --- /dev/null +++ b/erpnext/patches/v15_0/update_payment_ledger_entries_against_advance_doctypes.py @@ -0,0 +1,25 @@ +import frappe + +from erpnext.accounts.utils import get_advance_payment_doctypes + +DOCTYPE = "Payment Ledger Entry" + + +def execute(): + """ + Description: + Set against_voucher as entry for Payment Ledger Entry against advance vouchers. + """ + advance_payment_doctypes = get_advance_payment_doctypes() + + if not advance_payment_doctypes: + return + ple = frappe.qb.DocType(DOCTYPE) + + ( + frappe.qb.update(ple) + .set(ple.against_voucher_type, ple.voucher_type) + .set(ple.against_voucher_no, ple.voucher_no) + .where(ple.against_voucher_type.isin(advance_payment_doctypes)) + .run() + ) diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index 072b541753d..4ccbf0106d7 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -42,8 +42,8 @@ erpnext.accounts.unreconcile_payment = { selection_map = selections.map(function (elem) { return { company: elem.company, - voucher_type: elem.voucher_type, - voucher_no: elem.voucher_no, + voucher_type: elem.reference_doctype, + voucher_no: elem.reference_name, against_voucher_type: frm.doc.doctype, against_voucher_no: frm.doc.name, }; @@ -54,8 +54,8 @@ erpnext.accounts.unreconcile_payment = { company: elem.company, voucher_type: frm.doc.doctype, voucher_no: frm.doc.name, - against_voucher_type: elem.voucher_type, - against_voucher_no: elem.voucher_no, + against_voucher_type: elem.reference_doctype, + against_voucher_no: elem.reference_name, }; }); } @@ -69,7 +69,7 @@ erpnext.accounts.unreconcile_payment = { let child_table_fields = [ { label: __("Voucher Type"), - fieldname: "voucher_type", + fieldname: "reference_doctype", fieldtype: "Link", options: "DocType", in_list_view: 1, @@ -77,9 +77,9 @@ erpnext.accounts.unreconcile_payment = { }, { label: __("Voucher No"), - fieldname: "voucher_no", + fieldname: "reference_name", fieldtype: "Dynamic Link", - options: "voucher_type", + options: "reference_doctype", in_list_view: 1, read_only: 1, }, diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index b89e14b5bcf..b6a979dbc61 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -444,6 +444,7 @@ class SalesOrder(SellingController): "GL Entry", "Stock Ledger Entry", "Payment Ledger Entry", + "Advance Payment Ledger Entry", "Unreconcile Payment", "Unreconcile Payment Entries", ) From 31343b6287650567a599ce5fa68443f90e7dae6f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 1 Aug 2025 10:25:24 +0000 Subject: [PATCH 97/99] chore(release): Bumped to Version 15.72.2 ## [15.72.2](https://github.com/frappe/erpnext/compare/v15.72.1...v15.72.2) (2025-08-01) ### Bug Fixes * multiple fixes for advance payment accounting (backport [#48341](https://github.com/frappe/erpnext/issues/48341)) ([#48896](https://github.com/frappe/erpnext/issues/48896)) ([52b9f92](https://github.com/frappe/erpnext/commit/52b9f925533c19afbb09bb1f0be14dc67fc342ff)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index dc442c918b4..bf6628cf737 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.72.1" +__version__ = "15.72.2" def get_default_company(user=None): From 58f6534d8b40a1f4d126fda4b6372ed1734dee13 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 2 Aug 2025 12:16:45 +0530 Subject: [PATCH 98/99] perf: process_gl_map causing performance issues in the reposting (cherry picked from commit a96fa557041dbc6be6d0b28b9da773db2a6b2ef7) --- erpnext/controllers/stock_controller.py | 4 +++- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- .../doctype/subcontracting_receipt/subcontracting_receipt.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 81cf7fb80d7..e375c2c362c 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -638,7 +638,9 @@ class StockController(AccountsController): ).format(wh, self.company) ) - return process_gl_map(gl_list, precision=precision) + return process_gl_map( + gl_list, precision=precision, from_repost=frappe.flags.through_repost_item_valuation + ) def get_debit_field_precision(self): if not frappe.flags.debit_field_precision: diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d9597fe413a..9e7dea631a9 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -443,7 +443,7 @@ class PurchaseReceipt(BuyingController): self.make_tax_gl_entries(gl_entries, via_landed_cost_voucher) update_regional_gl_entries(gl_entries, self) - return process_gl_map(gl_entries) + return process_gl_map(gl_entries, from_repost=frappe.flags.through_repost_item_valuation) def make_item_gl_entries(self, gl_entries, warehouse_account=None): from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import ( diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 37c0340d2bf..a1260c5ffdd 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1612,7 +1612,7 @@ class StockEntry(StockController): ) ) - return process_gl_map(gl_entries) + return process_gl_map(gl_entries, from_repost=frappe.flags.through_repost_item_valuation) def update_work_order(self): def _validate_work_order(pro_doc): diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 00c1c08cd5c..23a1ab9ebf4 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -548,7 +548,7 @@ class SubcontractingReceipt(SubcontractingController): gl_entries = [] self.make_item_gl_entries(gl_entries, warehouse_account) - return process_gl_map(gl_entries) + return process_gl_map(gl_entries, from_repost=frappe.flags.through_repost_item_valuation) def make_item_gl_entries(self, gl_entries, warehouse_account=None): warehouse_with_no_account = [] From 8795ce975fa23376c488a4e5084ace5e2e809680 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 3 Aug 2025 10:42:14 +0000 Subject: [PATCH 99/99] chore(release): Bumped to Version 15.72.3 ## [15.72.3](https://github.com/frappe/erpnext/compare/v15.72.2...v15.72.3) (2025-08-03) ### Performance Improvements * process_gl_map causing performance issues in the reposting ([58f6534](https://github.com/frappe/erpnext/commit/58f6534d8b40a1f4d126fda4b6372ed1734dee13)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index bf6628cf737..f69141a37ad 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.72.2" +__version__ = "15.72.3" def get_default_company(user=None):