From 3efa5215a0c397b1274a4728e423dc325c0c36c5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Jan 2023 15:44:05 +0000 Subject: [PATCH 01/76] chore(release): Bumped to Version 13.43.0 # [13.43.0](https://github.com/frappe/erpnext/compare/v13.42.7...v13.43.0) (2023-01-04) ### Bug Fixes * `shipping_address` for non-drop shipping item ([19feebb](https://github.com/frappe/erpnext/commit/19feebbcb6a30a57de79a74fd2189b7d4dce828e)) * `shipping_address` in PO ([1068d0e](https://github.com/frappe/erpnext/commit/1068d0ec63f7e240b08d52426dac3216826fdbcc)) * add missing 'ordered_qty' to get_bin_details ([66ba098](https://github.com/frappe/erpnext/commit/66ba098462517de67dc1b0d8aab7aac7a7781e1b)) * conflicts ([8521e12](https://github.com/frappe/erpnext/commit/8521e12753aeb7a423a5bb0ba89ab365255ff7fb)) * conflicts ([1c7c591](https://github.com/frappe/erpnext/commit/1c7c591ee253ed403b747843c69c201aab3a13f9)) * conflicts ([c18a451](https://github.com/frappe/erpnext/commit/c18a451362ab909bef35fc6b3d4a49cb927bf64e)) * consider child nodes while getting bin details ([c9bf062](https://github.com/frappe/erpnext/commit/c9bf062f633ddc723d43753cae4064c765683037)) * Conversion factor error for invoices without item code (petty expenses) ([#32714](https://github.com/frappe/erpnext/issues/32714)) ([acf8b46](https://github.com/frappe/erpnext/commit/acf8b464f30154ff804b9004e124de6bf362d9b7)) * debit note not pulled on reconciliation tool ([cf133b2](https://github.com/frappe/erpnext/commit/cf133b2f1ce0a66164144071bd9cb300648ddf8f)) * Deferred revenue date comparison (backport [#33515](https://github.com/frappe/erpnext/issues/33515)) ([#33517](https://github.com/frappe/erpnext/issues/33517)) ([ea99ac9](https://github.com/frappe/erpnext/commit/ea99ac9c296aa92f018c72a73761fdfcc3173fe1)) * **ecommerce:** remove query parameters from referrer (backport [#33269](https://github.com/frappe/erpnext/issues/33269)) ([#33513](https://github.com/frappe/erpnext/issues/33513)) ([6516e80](https://github.com/frappe/erpnext/commit/6516e8042b97ef92f2632f8fa722080dc8df4668)) * ERR journals reported in AR/AP ([c850635](https://github.com/frappe/erpnext/commit/c8506355513ac55fc582603b8ffd7a7e4fa2144b)) * linter ([f0475e9](https://github.com/frappe/erpnext/commit/f0475e9cc503e8622363b9cecdbeed8d9398b0b6)) * Missing opening entry in general ledger (backport [#33519](https://github.com/frappe/erpnext/issues/33519)) ([#33527](https://github.com/frappe/erpnext/issues/33527)) ([865f233](https://github.com/frappe/erpnext/commit/865f233add436141cf4d12354f4dd79335ca48aa)) * Multi-currency issues in Bank Reconciliation Tool (backport [#33488](https://github.com/frappe/erpnext/issues/33488)) ([#33493](https://github.com/frappe/erpnext/issues/33493)) ([4ba2f1e](https://github.com/frappe/erpnext/commit/4ba2f1ec96a5fc370039bead72f036919e37c6c0)) * No permission to read doctype ([8e1c0cd](https://github.com/frappe/erpnext/commit/8e1c0cd234ac1d74fa452c84bb13b27da91ec475)) * patch ([7b813d6](https://github.com/frappe/erpnext/commit/7b813d6045a7e28b1fbc008c3e3063ab74aa617d)) * provision to set tax_deducted_till_date after document is subnmmited ([64454e0](https://github.com/frappe/erpnext/commit/64454e0d4e68cc9208ebe1daff2cad3a5d6055bf)) * reconciled credit notes being fetched again in Payment Reconciliation tool ([#33471](https://github.com/frappe/erpnext/issues/33471)) ([5ec11ba](https://github.com/frappe/erpnext/commit/5ec11bad4fecc0bd3a16b53952608ce34e76ed00)) * Tax withheld vouchers naming rule ([#33467](https://github.com/frappe/erpnext/issues/33467)) ([334219e](https://github.com/frappe/erpnext/commit/334219e36a58865ca2faed5b7ad9a267243b9366)) * **test:** holiday list dates in attendance test setup ([8df1151](https://github.com/frappe/erpnext/commit/8df11516be28d011159438e977d5cce448988a4a)) * **test:** monthly attendance sheet ([e5a187e](https://github.com/frappe/erpnext/commit/e5a187e08c48775ca95d6b21bb5ee05db281b60b)) * typerror on multi warehouse in Packed Items ([6a394c5](https://github.com/frappe/erpnext/commit/6a394c5be70a6d5b02f50571206b82c90599f9a9)) * use base_net_amount in case of missing stock qty ([#33457](https://github.com/frappe/erpnext/issues/33457)) ([6e363a6](https://github.com/frappe/erpnext/commit/6e363a62db3bdd0340aef4a22f131a7e4816d1e0)) * use get_all instead of get_value as get_value api dont supports between condition ([bc04e05](https://github.com/frappe/erpnext/commit/bc04e05b4658a0c70b46e96720aa341c3b985ff5)) ### Features * explicit time period for mark attendance ([d2f86ea](https://github.com/frappe/erpnext/commit/d2f86ead747680906db6648afb1cebc610879d53)) * provision to setup opening balances for earnings and deductions while creating SSA ([c3b9059](https://github.com/frappe/erpnext/commit/c3b9059c1b8a5a829c0391dd1a97db33cf9adc3d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f102a71850f..76c78c6abc3 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.42.7" +__version__ = "13.43.0" def get_default_company(user=None): From ab30e2a9c72d7d2c4dae5fe2b46003c6fe0091d5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Jan 2023 16:51:45 +0000 Subject: [PATCH 02/76] chore(release): Bumped to Version 13.43.1 ## [13.43.1](https://github.com/frappe/erpnext/compare/v13.43.0...v13.43.1) (2023-01-10) ### Bug Fixes * better handling of duplicate bundle items ([0b952e8](https://github.com/frappe/erpnext/commit/0b952e8bba66032e296a83377afbf2438e49af8a)) * remove hard-coded roles for populating leave balance reports ([#249](https://github.com/frappe/erpnext/issues/249)) ([#33557](https://github.com/frappe/erpnext/issues/33557)) ([c20d469](https://github.com/frappe/erpnext/commit/c20d469f31be2421428783ca261c4206506e6b6e)) * remove unnecessary permissions from Appointment and Appointment Booking Settings ([#33468](https://github.com/frappe/erpnext/issues/33468)) ([a50ad1d](https://github.com/frappe/erpnext/commit/a50ad1d292249cc36da0c68412bd92911e2f6633)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 76c78c6abc3..88850f6f141 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.43.0" +__version__ = "13.43.1" def get_default_company(user=None): From 550daf21089f9a05cfe7997e6e1e470dd19ee829 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 17 Jan 2023 15:36:54 +0000 Subject: [PATCH 03/76] chore(release): Bumped to Version 13.43.2 ## [13.43.2](https://github.com/frappe/erpnext/compare/v13.43.1...v13.43.2) (2023-01-17) ### Bug Fixes * allow to create sales order from expired quotation ([#33582](https://github.com/frappe/erpnext/issues/33582)) ([2f81f15](https://github.com/frappe/erpnext/commit/2f81f15f025bfb66278b153f6548c26e8eee7425)) * asset value in fixed asset register ([#33608](https://github.com/frappe/erpnext/issues/33608)) ([42fe63d](https://github.com/frappe/erpnext/commit/42fe63da2ce778ee0cd3831ba2f94c34747e1e06)) * better comparision of difference value between stock and account ([a450c8d](https://github.com/frappe/erpnext/commit/a450c8dce948540ee13f53338089f73f5bc7655d)) * don't check other warehouse ledgers to calculate valuation rate ([66bf107](https://github.com/frappe/erpnext/commit/66bf1071bb6de1ea07dff60e2352dfe6121ed9cc)) * handle post depr entries fail and fix asset repair link ([5f7dc8a](https://github.com/frappe/erpnext/commit/5f7dc8a5b97b7061bfd2fc42178aa0009893d75c)) * only group similar items in print format if group_same_items is checked in pick list (backport [#33627](https://github.com/frappe/erpnext/issues/33627)) ([#33631](https://github.com/frappe/erpnext/issues/33631)) ([7dcf0f0](https://github.com/frappe/erpnext/commit/7dcf0f0866c62f9b798383b6d74df046179a591b)) * Return against internal purchase invoice ([#33635](https://github.com/frappe/erpnext/issues/33635)) ([eef0f45](https://github.com/frappe/erpnext/commit/eef0f453d25f8ca0419cafd71fe4a86da947de2d)) * Sales ORder Connections on Material Request ([97488ae](https://github.com/frappe/erpnext/commit/97488aee88e1067b912a45e6e4a69a2120a58e4a)) * Updating SO throws ordered_qty not allowed to change after submission ([a46aa80](https://github.com/frappe/erpnext/commit/a46aa808be99c2606e90e5959a8b7c0984ce6766)) ### Reverts * Reverting changes done on 33495 ([#33662](https://github.com/frappe/erpnext/issues/33662)) ([0f0a2b1](https://github.com/frappe/erpnext/commit/0f0a2b100cee8b43948fe9f8c155daad8c465ae5)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 88850f6f141..a95b7fb6f9c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.43.1" +__version__ = "13.43.2" def get_default_company(user=None): From 71395b9a8e04be6b4dd7f088467a8ae684517fbb Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 25 Jan 2023 04:00:37 +0000 Subject: [PATCH 04/76] chore(release): Bumped to Version 13.44.0 # [13.44.0](https://github.com/frappe/erpnext/compare/v13.43.2...v13.44.0) (2023-01-25) ### Bug Fixes * accumulated_depreciation in reverse_depreciation_entry_made_after_disposal ([b7e9e4a](https://github.com/frappe/erpnext/commit/b7e9e4a7c5155b5f6b100ca29427eb81f68d5cf4)) * backport of [#32226](https://github.com/frappe/erpnext/issues/32226) ([d6913ff](https://github.com/frappe/erpnext/commit/d6913fffe65c139273b75b36f095557227bfa223)) * calculate correct amount for qty == 0 (backport [#33739](https://github.com/frappe/erpnext/issues/33739)) ([#33752](https://github.com/frappe/erpnext/issues/33752)) ([d650432](https://github.com/frappe/erpnext/commit/d6504320b152629c22b63488a29fb019e78014ea)) * conflicts ([d717ca0](https://github.com/frappe/erpnext/commit/d717ca0325a09780212fd1fe63dad5c41a3f456f)) * conflicts ([055f853](https://github.com/frappe/erpnext/commit/055f8536c304e750926b6b44c91b83c013dfae24)) * don't add template item in sales/purchase transaction ([f81d4a7](https://github.com/frappe/erpnext/commit/f81d4a79eab95148a4a6591bb0920cd2db13afed)) * e-Invoicing for SEZ Customer(v13) ([#33796](https://github.com/frappe/erpnext/issues/33796)) ([1b11566](https://github.com/frappe/erpnext/commit/1b115664859016ae849922ecdb782937d8c51200)) * **ecommerce:** breadcrumb: fallback to `/all-products` ([#33718](https://github.com/frappe/erpnext/issues/33718)) ([2da543e](https://github.com/frappe/erpnext/commit/2da543ebd4a15ef48d9bdb1c81f456b50bda0137)) * fb issue in asset chart ([ae031ce](https://github.com/frappe/erpnext/commit/ae031cea6313628a404cf5314cf60d91a9be9803)) * incorrect actual qty for the packed item ([09e13d2](https://github.com/frappe/erpnext/commit/09e13d279c1a4dbaab5924bb4e877c33bfa06984)) * incorrect row order and accumulated_depreciation when schedule with multiple FBs is scrapped ([7174a2c](https://github.com/frappe/erpnext/commit/7174a2cd933e177fb2903fd96b861bae21212318)) * linter issue ([593d7f3](https://github.com/frappe/erpnext/commit/593d7f3dd673ec8da83242313f923af0a1048ce2)) * linting ([13906cb](https://github.com/frappe/erpnext/commit/13906cba9a412a3cc0afd698be932616346f58d7)) * **minor:** Label updates in Statement of Accounts ([#33639](https://github.com/frappe/erpnext/issues/33639)) ([47e500c](https://github.com/frappe/erpnext/commit/47e500c2ebf7dd7185e7b4dfc607971d9622e2da)) * missing constant definition ([fc4be1b](https://github.com/frappe/erpnext/commit/fc4be1b337426c727eee6e5ad9a3e28562ff177c)) * patch item_reposting_for_incorrect_sl_and_gl ([1c5c067](https://github.com/frappe/erpnext/commit/1c5c06716b63bbfe9a7e0d56d63473074405c2b6)) * rewrite logic for duplicate check in Item Attribute ([4741ce1](https://github.com/frappe/erpnext/commit/4741ce13c630967e7eb06112c096c2f2349d5027)) * Short closed order, receipt and delivery note status on cancellation ([#33743](https://github.com/frappe/erpnext/issues/33743)) ([3daaa02](https://github.com/frappe/erpnext/commit/3daaa021eb618333f51398c44366747c2d8e9904)) ### Features * provision to select date type based on filter ([5ed6a74](https://github.com/frappe/erpnext/commit/5ed6a74fc419b66f96609ac794c74477837d6f73)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a95b7fb6f9c..a1afb9b62a2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.43.2" +__version__ = "13.44.0" def get_default_company(user=None): From 81e4be37ffaea17927d4db6809feff673115d31a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 31 Jan 2023 06:19:21 +0000 Subject: [PATCH 05/76] chore(release): Bumped to Version 13.45.0 # [13.45.0](https://github.com/frappe/erpnext/compare/v13.44.0...v13.45.0) (2023-01-31) ### Bug Fixes * disposal_was_made_on_original_schedule_date ([939a312](https://github.com/frappe/erpnext/commit/939a3121b76a8cc2d194c1c5d48b4c1bfc842a7c)) * enable customs in Selling Workpace by default ([#33853](https://github.com/frappe/erpnext/issues/33853)) ([54c1642](https://github.com/frappe/erpnext/commit/54c1642e3b51d1f180152fa4c4dc61c9ec236d8f)) * Fetch commission rate from sales partner ([#33851](https://github.com/frappe/erpnext/issues/33851)) ([3425a3b](https://github.com/frappe/erpnext/commit/3425a3bef914026b822ec3c49de48145ab74ebb2)) * **gp:** fetch buying amount from dn related to so ([be5edd3](https://github.com/frappe/erpnext/commit/be5edd329f3935444704ae12ac87ace339161952)) * item rate not fetching ([bb56415](https://github.com/frappe/erpnext/commit/bb5641535b7f103a3719872064a3c5a49057a41d)) * manual depr entry not updating asset value [v13] ([#33890](https://github.com/frappe/erpnext/issues/33890)) ([f5efb20](https://github.com/frappe/erpnext/commit/f5efb2057c6ba2284f84b3694da4d6e8fba2dcf4)) * use correct filter name in `item_query` (backport [#33814](https://github.com/frappe/erpnext/issues/33814)) ([#33817](https://github.com/frappe/erpnext/issues/33817)) ([b38ad66](https://github.com/frappe/erpnext/commit/b38ad66012e458b943e1775f3b6c331720d6e576)) ### Features * **gp:** test for inv and dn related via so ([b72a35a](https://github.com/frappe/erpnext/commit/b72a35a622ac5abcca5e6bba4760571059612cb3)) ### Performance Improvements * show update items dialog ([0ff5099](https://github.com/frappe/erpnext/commit/0ff5099cbc413e75909f449e831a38d5c40988d3)) * Timeout while doing payment reconciliation (v13) ([#33818](https://github.com/frappe/erpnext/issues/33818)) ([4bf3e31](https://github.com/frappe/erpnext/commit/4bf3e310e1fe32b26b573cbc0fc97ec66992e126)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a1afb9b62a2..198bed755e0 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.44.0" +__version__ = "13.45.0" def get_default_company(user=None): From e3ad0b1655412552928ebebc4b043080ca8fe539 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 1 Feb 2023 15:38:12 +0530 Subject: [PATCH 06/76] fix: incorrect actual qty in Bin (cherry picked from commit f8c852c54ccf7a33d26e15378b76557ceffd77e5) (cherry picked from commit 8f42833fbac6c891800825d12933e4b9612d4d40) --- erpnext/stock/doctype/bin/bin.py | 7 ++++++- erpnext/stock/stock_ledger.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 75951eb60e5..fe88bdca2e7 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -150,12 +150,17 @@ def update_qty(bin_name, args): last_sle_qty = ( frappe.qb.from_(sle) .select(sle.qty_after_transaction) - .where((sle.item_code == args.get("item_code")) & (sle.warehouse == args.get("warehouse"))) + .where( + (sle.item_code == args.get("item_code")) + & (sle.warehouse == args.get("warehouse")) + & (sle.is_cancelled == 0) + ) .orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=Order.desc) .orderby(sle.creation, order=Order.desc) .run() ) + actual_qty = 0.0 if last_sle_qty: actual_qty = last_sle_qty[0][0] diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 4e0528e536d..4d40da65935 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1144,7 +1144,7 @@ def get_stock_ledger_entries( def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): return frappe.db.get_value( "Stock Ledger Entry", - {"voucher_detail_no": voucher_detail_no, "name": ["!=", excluded_sle]}, + {"voucher_detail_no": voucher_detail_no, "name": ["!=", excluded_sle], "is_cancelled": 0}, [ "item_code", "warehouse", From 6a9660de65c4ef7d39debc72bee9b7419831f482 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 1 Feb 2023 17:46:11 +0000 Subject: [PATCH 07/76] chore(release): Bumped to Version 13.45.1 ## [13.45.1](https://github.com/frappe/erpnext/compare/v13.45.0...v13.45.1) (2023-02-01) ### Bug Fixes * incorrect actual qty in Bin ([e3ad0b1](https://github.com/frappe/erpnext/commit/e3ad0b1655412552928ebebc4b043080ca8fe539)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 198bed755e0..073824ab5fd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.45.0" +__version__ = "13.45.1" def get_default_company(user=None): From ab71a7bba8e04317250ed2db101553a7829040b3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 14 Feb 2023 10:40:00 +0000 Subject: [PATCH 08/76] chore(release): Bumped to Version 13.46.0 # [13.46.0](https://github.com/frappe/erpnext/compare/v13.45.1...v13.46.0) (2023-02-14) ### Bug Fixes * `amount` in `Material Request` ([813e8bb](https://github.com/frappe/erpnext/commit/813e8bb664ed0ebadf53f1fe87102b3e9c4455f6)) * allow PI cancel if linked asset is cancelled ([fbeaabf](https://github.com/frappe/erpnext/commit/fbeaabffc9a5f1073c225476a07cb0c6ebe8f3e7)) * conflicts ([a9f5be3](https://github.com/frappe/erpnext/commit/a9f5be3f981ef600aec8091e56a1535f75e2d05d)) * currency formatting in item-wise sales history ([#33903](https://github.com/frappe/erpnext/issues/33903)) ([f641039](https://github.com/frappe/erpnext/commit/f6410393ce2fec6b29af243404be676475384b25)) * Debit and Credit not equal while submitting PI containing asset item ([#33092](https://github.com/frappe/erpnext/issues/33092)) ([5be4c6f](https://github.com/frappe/erpnext/commit/5be4c6ffbcb26cd49dd7fc7865571d39948bb9bb)) * german chart of accounts "SKR03" ([#33909](https://github.com/frappe/erpnext/issues/33909)) ([b2a3e01](https://github.com/frappe/erpnext/commit/b2a3e014e91962097a0abca8de7324e6abca7cd7)) * incorrect actual qty in Bin ([8f42833](https://github.com/frappe/erpnext/commit/8f42833fbac6c891800825d12933e4b9612d4d40)) * LWP calculation ([c1de4e4](https://github.com/frappe/erpnext/commit/c1de4e44207d391237f593c9b81bf0e1de2320f6)) * negative stock error ([2f4ffe1](https://github.com/frappe/erpnext/commit/2f4ffe137ea173c974f3111b266723f6f7bb5f9b)) * stock entry from item dashboard (stock levels) ([8106c64](https://github.com/frappe/erpnext/commit/8106c64c91fac5de0e7660a874876b8406024726)) * **UX:** make Item attachments public by default (backport [#32196](https://github.com/frappe/erpnext/issues/32196)) ([#33949](https://github.com/frappe/erpnext/issues/33949)) ([124d7de](https://github.com/frappe/erpnext/commit/124d7dea1b25a0db89f85c2b02d21f787cec09bb)) * validate working day list against holidays ([a8ea3ef](https://github.com/frappe/erpnext/commit/a8ea3efae29200d470435dc665ca00899b356d76)) ### Features * Setting to allow Sales Order creation against expired quotation ([#33952](https://github.com/frappe/erpnext/issues/33952)) ([f04542e](https://github.com/frappe/erpnext/commit/f04542eac9db8a1bf9118f943271fcb3fb72294d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 073824ab5fd..fab07cc6c91 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.45.1" +__version__ = "13.46.0" def get_default_company(user=None): From 6308fca587271b4e8f35eba377581dffe16c6a2a Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:22:54 +0530 Subject: [PATCH 09/76] fix: opening_accumulated_depreciation and precision in charts (cherry picked from commit 47cc8ab6c6e6ad3cf13ba750e9410696be827c23) (cherry picked from commit 4f10f48f7c39c5896f1adab60bcf39aacfd41509) --- erpnext/assets/doctype/asset/asset.js | 39 ++++++++++--------- erpnext/assets/doctype/asset/asset.py | 8 ++-- .../fixed_asset_register.py | 10 ++--- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index a4a9d6c8bc8..1f158a3a942 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -206,52 +206,53 @@ frappe.ui.form.on('Asset', { return } - var x_intervals = [frm.doc.purchase_date]; + var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: 'Date' })]; var asset_values = [frm.doc.gross_purchase_amount]; - var last_depreciation_date = frm.doc.purchase_date; - - if(frm.doc.opening_accumulated_depreciation) { - last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date, - -1*frm.doc.frequency_of_depreciation); - - x_intervals.push(last_depreciation_date); - asset_values.push(flt(frm.doc.gross_purchase_amount) - - flt(frm.doc.opening_accumulated_depreciation)); - } if(frm.doc.calculate_depreciation) { + if(frm.doc.opening_accumulated_depreciation) { + var depreciation_date = frappe.datetime.add_months( + frm.doc.finance_books[0].depreciation_start_date, + -1 * frm.doc.finance_books[0].frequency_of_depreciation + ); + x_intervals.push(frappe.format(depreciation_date, { fieldtype: 'Date' })); + asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + } + $.each(frm.doc.schedules || [], function(i, v) { - x_intervals.push(v.schedule_date); - var asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount); + x_intervals.push(frappe.format(v.schedule_date, { fieldtype: 'Date' })); + var asset_value = flt(frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, precision('gross_purchase_amount')); if(v.journal_entry) { - last_depreciation_date = v.schedule_date; asset_values.push(asset_value); } else { if (in_list(["Scrapped", "Sold"], frm.doc.status)) { asset_values.push(null); } else { - asset_values.push(asset_value) + asset_values.push(asset_value); } } }); } else { + if(frm.doc.opening_accumulated_depreciation) { + x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: 'Date' })); + asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + } + let depr_entries = (await frappe.call({ method: "get_manual_depreciation_entries", doc: frm.doc, })).message; $.each(depr_entries || [], function(i, v) { - x_intervals.push(v.posting_date); - last_depreciation_date = v.posting_date; + x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); let last_asset_value = asset_values[asset_values.length - 1] asset_values.push(last_asset_value - v.value); }); } if(in_list(["Scrapped", "Sold"], frm.doc.status)) { - x_intervals.push(frm.doc.disposal_date); + x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: 'Date' })); asset_values.push(0); - last_depreciation_date = frm.doc.disposal_date; } frm.dashboard.render_graph({ diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 549241b8f10..9033710b324 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -683,14 +683,16 @@ class Asset(AccountsController): def get_value_after_depreciation(self, finance_book=None): if not self.calculate_depreciation: - return self.value_after_depreciation + return flt(self.value_after_depreciation, self.precision("gross_purchase_amount")) if not finance_book: - return self.get("finance_books")[0].value_after_depreciation + return flt( + self.get("finance_books")[0].value_after_depreciation, self.precision("gross_purchase_amount") + ) for row in self.get("finance_books"): if finance_book == row.finance_book: - return row.value_after_depreciation + return flt(row.value_after_depreciation, self.precision("gross_purchase_amount")) def get_default_finance_book_idx(self): if not self.get("default_finance_book") and self.company: diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 5bfd4840d73..0ab3e16e424 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.query_builder.functions import Sum -from frappe.utils import cstr, formatdate, getdate +from frappe.utils import cstr, flt, formatdate, getdate from erpnext.accounts.report.financial_statements import ( get_fiscal_year_data, @@ -102,13 +102,9 @@ def get_data(filters): ] assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) - finance_book_filter = ("is", "not set") - if filters.finance_book: - finance_book_filter = ("=", filters.finance_book) - assets_linked_to_fb = frappe.db.get_all( doctype="Asset Finance Book", - filters={"finance_book": finance_book_filter}, + filters={"finance_book": filters.finance_book or ("is", "not set")}, pluck="parent", ) @@ -194,7 +190,7 @@ def get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters): else: depr_amount = get_manual_depreciation_amount_of_asset(asset, filters) - return depr_amount + return flt(depr_amount, 2) def get_finance_book_value_map(filters): From f80fb97c717cda4c7f7bb84b18762c0bd86f188c Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:28:46 +0530 Subject: [PATCH 10/76] chore: break look if je processed (cherry picked from commit a220dc0c9c694a9213189b96be8eb9431457280c) (cherry picked from commit ff2e617c0c4a6e7ef2665692cfb66ed92ba3ce36) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index a8b8ec6d12e..c50336e1e61 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -346,6 +346,8 @@ class JournalEntry(AccountsController): finance_books.db_update() asset.set_status() + + break else: depr_value = d.debit or d.credit From 62dc68bb57f21900b77bb28ab41e581b57f468f2 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 14 Feb 2023 17:54:51 +0530 Subject: [PATCH 11/76] fix: asset_depreciation_and_balances report doesn't reflect manual depr entries (cherry picked from commit 1535c3d856a50c31ed1e2adaf022ca1a221aab9b) (cherry picked from commit 6227c16374587975371179cb2ee202a0691b06eb) --- .../asset_depreciations_and_balances.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index ad9b1ba58eb..9cea37c4f80 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -135,6 +135,34 @@ def get_assets(filters): where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union + SELECT a.asset_category, + ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then + gle.debit + else + 0 + end), 0) as accumulated_depreciation_as_on_from_date, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then + gle.debit + else + 0 + end), 0) as depreciation_eliminated_during_the_period, + ifnull(sum(case when gle.posting_date >= %(from_date)s and gle.posting_date <= %(to_date)s + and (ifnull(a.disposal_date, 0) = 0 or gle.posting_date <= a.disposal_date) then + gle.debit + else + 0 + end), 0) as depreciation_amount_during_the_period + from `tabGL Entry` gle + join `tabAsset` a on + gle.against_voucher = a.name + join `tabAsset Category Account` aca on + aca.parent = a.asset_category and aca.company_name = %(company)s + join `tabCompany` company on + company.name = %(company)s + where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account) + group by a.asset_category + union SELECT a.asset_category, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 From 4a95c9d642f504f9055b015adebb1b12604b2edf Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 15 Feb 2023 10:33:52 +0000 Subject: [PATCH 12/76] chore(release): Bumped to Version 13.46.1 ## [13.46.1](https://github.com/frappe/erpnext/compare/v13.46.0...v13.46.1) (2023-02-15) ### Bug Fixes * asset_depreciation_and_balances report doesn't reflect manual depr entries ([62dc68b](https://github.com/frappe/erpnext/commit/62dc68bb57f21900b77bb28ab41e581b57f468f2)) * opening_accumulated_depreciation and precision in charts ([6308fca](https://github.com/frappe/erpnext/commit/6308fca587271b4e8f35eba377581dffe16c6a2a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index fab07cc6c91..3fcfb346186 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.46.0" +__version__ = "13.46.1" def get_default_company(user=None): From 9766827a08332c75e3d5bdeef2c057ea17119526 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 21 Feb 2023 17:21:05 +0000 Subject: [PATCH 13/76] chore(release): Bumped to Version 13.47.0 # [13.47.0](https://github.com/frappe/erpnext/compare/v13.46.1...v13.47.0) (2023-02-21) ### Bug Fixes * add missing import ([8add12d](https://github.com/frappe/erpnext/commit/8add12d5686235781dc4eb8631c786acc51a0916)) * Amount for debit and credit notes with 0 qty line items (backport [#33902](https://github.com/frappe/erpnext/issues/33902)) ([#34123](https://github.com/frappe/erpnext/issues/34123)) ([2408966](https://github.com/frappe/erpnext/commit/2408966090d2b341053e2247c481d4f08d05a77e)) * asset repair status after deletion and asset status after manual depr entry ([922b30a](https://github.com/frappe/erpnext/commit/922b30a5663e9ac74619ece488844b32397fb792)) * asset_depreciation_and_balances report doesn't reflect manual depr entries ([6227c16](https://github.com/frappe/erpnext/commit/6227c16374587975371179cb2ee202a0691b06eb)) * check for duplicate in pos closing and pos merge log entry ([92da1ed](https://github.com/frappe/erpnext/commit/92da1ed3c27e78784fe1e383690510dee4c72d38)) * don't get chart data if data is empty ([acdf7fd](https://github.com/frappe/erpnext/commit/acdf7fd8dfd4e69abda2405394f414740777a761)) * **ecommerce:** throw invalid doctype error in shop by category ([#33901](https://github.com/frappe/erpnext/issues/33901)) ([de87786](https://github.com/frappe/erpnext/commit/de87786db4f742c807edc84a586e467895bd827b)) * Filters in item-wise sales history report ([#34145](https://github.com/frappe/erpnext/issues/34145)) ([9826245](https://github.com/frappe/erpnext/commit/9826245d8a8428c095f89f0e553bc0cf23d405d5)) * fiscal year error for existing assets in fixed asset register ([1fb3a28](https://github.com/frappe/erpnext/commit/1fb3a281288b9e84caacbfcad53e346507783185)) * opening_accumulated_depreciation and precision in charts ([4f10f48](https://github.com/frappe/erpnext/commit/4f10f48f7c39c5896f1adab60bcf39aacfd41509)) * should never get cutomer price on purchase document ([#34002](https://github.com/frappe/erpnext/issues/34002)) ([117dbe3](https://github.com/frappe/erpnext/commit/117dbe38c45bc56d6972fa6f6f2613367e8e4825)), closes [#33998](https://github.com/frappe/erpnext/issues/33998) * update `reserved_qty` when `Sales Order` marked as `Hold` ([2391c37](https://github.com/frappe/erpnext/commit/2391c37238b55e02afb31c4800fe419825ca0440)) * Use normal rounding for Tax Withholding Category ([#34114](https://github.com/frappe/erpnext/issues/34114)) ([26ed460](https://github.com/frappe/erpnext/commit/26ed460a4f0c21d7651f01d541c0cef8d1dcba4d)) * **ux:** `ReferenceError: me is not defined` Delivery Note ([495d1b2](https://github.com/frappe/erpnext/commit/495d1b2548f233d0b97fc287118ce49d861d61a8)) ### Features * **UX:** Add option to disable consolidating leave types in balance reports ([ccd2568](https://github.com/frappe/erpnext/commit/ccd25684f9b7a3e47269a5437da4918058cd2b5b)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3fcfb346186..18d0c66175f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.46.1" +__version__ = "13.47.0" def get_default_company(user=None): From 0490e3bfe6e20e4a3a1274fd4ad8b91e75beed68 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 22 Feb 2023 14:24:18 +0530 Subject: [PATCH 14/76] fix: incorrect color in the BOM Stock Report (cherry picked from commit a8f03ebf7f61c14d2cff45510acd670f0671a1b5) (cherry picked from commit e98b34617f194892fae1feaaef99b3551a478b83) --- .../manufacturing/report/bom_stock_report/bom_stock_report.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index 7beecaceedf..e7f67caf249 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -25,8 +25,9 @@ frappe.query_reports["BOM Stock Report"] = { ], "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); + if (column.id == "item") { - if (data["enough_parts_to_build"] > 0) { + if (data["in_stock_qty"] >= data["required_qty"]) { value = `${data['item']}`; } else { value = `${data['item']}`; From c8ec365594cbab3a8ed969c7932d6761c068593e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 17 Feb 2023 20:46:09 +0530 Subject: [PATCH 15/76] feat: provision to convert transaction based reposting to item warehouse based reposting (cherry picked from commit f1383b5ef91a536dc13d2b3638823597a9954f14) (cherry picked from commit 59c6eb591bb0ed10bb1d80cd7fcdc6bf347e9c6b) --- .../stock_reposting_settings.js | 19 +++++- .../stock_reposting_settings.py | 61 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js index 42d0723d427..5f81679bade 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js @@ -2,7 +2,22 @@ // For license information, please see license.txt frappe.ui.form.on('Stock Reposting Settings', { - // refresh: function(frm) { + refresh: function(frm) { + frm.trigger('convert_to_item_based_reposting'); + }, - // } + convert_to_item_based_reposting: function(frm) { + frm.add_custom_button(__('Convert to Item Based Reposting'), function() { + frm.call({ + method: 'convert_to_item_wh_reposting', + frezz: true, + doc: frm.doc, + callback: function(r) { + if (!r.exc) { + frm.reload_doc(); + } + } + }) + }) + } }); diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py index e0c8ed12e7d..51fb5ac4c40 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.py @@ -1,6 +1,8 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import frappe +from frappe import _ from frappe.model.document import Document from frappe.utils import add_to_date, get_datetime, get_time_str, time_diff_in_hours @@ -24,3 +26,62 @@ class StockRepostingSettings(Document): if diff < 10: self.end_time = get_time_str(add_to_date(self.start_time, hours=10, as_datetime=True)) + + @frappe.whitelist() + def convert_to_item_wh_reposting(self): + """Convert Transaction reposting to Item Warehouse based reposting if Item Based Reposting has enabled.""" + + reposting_data = get_reposting_entries() + + vouchers = [d.voucher_no for d in reposting_data] + + item_warehouses = {} + + for ledger in get_stock_ledgers(vouchers): + key = (ledger.item_code, ledger.warehouse) + if key not in item_warehouses: + item_warehouses[key] = ledger.posting_date + elif frappe.utils.getdate(item_warehouses.get(key)) > frappe.utils.getdate(ledger.posting_date): + item_warehouses[key] = ledger.posting_date + + for key, posting_date in item_warehouses.items(): + item_code, warehouse = key + create_repost_item_valuation(item_code, warehouse, posting_date) + + for row in reposting_data: + frappe.db.set_value("Repost Item Valuation", row.name, "status", "Skipped") + + self.db_set("item_based_reposting", 1) + frappe.msgprint(_("Item Warehouse based reposting has been enabled.")) + + +def get_reposting_entries(): + return frappe.get_all( + "Repost Item Valuation", + fields=["voucher_no", "name"], + filters={"status": ("in", ["Queued", "In Progress"]), "docstatus": 1, "based_on": "Transaction"}, + ) + + +def get_stock_ledgers(vouchers): + return frappe.get_all( + "Stock Ledger Entry", + fields=["item_code", "warehouse", "posting_date"], + filters={"voucher_no": ("in", vouchers)}, + ) + + +def create_repost_item_valuation(item_code, warehouse, posting_date): + frappe.get_doc( + { + "doctype": "Repost Item Valuation", + "company": frappe.get_cached_value("Warehouse", warehouse, "company"), + "posting_date": posting_date, + "based_on": "Item and Warehouse", + "posting_time": "00:00:01", + "item_code": item_code, + "warehouse": warehouse, + "allow_negative_stock": True, + "status": "Queued", + } + ).submit() From 5f25cea3225d5ea7f3be054875e2254453f8b571 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 24 Feb 2023 09:08:54 +0000 Subject: [PATCH 16/76] chore(release): Bumped to Version 13.48.0 # [13.48.0](https://github.com/frappe/erpnext/compare/v13.47.0...v13.48.0) (2023-02-24) ### Bug Fixes * incorrect color in the BOM Stock Report ([0490e3b](https://github.com/frappe/erpnext/commit/0490e3bfe6e20e4a3a1274fd4ad8b91e75beed68)) ### Features * provision to convert transaction based reposting to item warehouse based reposting ([c8ec365](https://github.com/frappe/erpnext/commit/c8ec365594cbab3a8ed969c7932d6761c068593e)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 18d0c66175f..bda50b1eb4b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.47.0" +__version__ = "13.48.0" def get_default_company(user=None): From a34aff6f49be9a2d8b2629ff66b66778dcf31767 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 24 Feb 2023 17:50:00 +0530 Subject: [PATCH 17/76] fix: not able to repost gl entries (cherry picked from commit 7d10dd9ea8671c27709e88350c6799ba187c4929) (cherry picked from commit 2039bd066d6723b10d5bee0b4944a6c5b417a644) --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 390704fa3ed..651300f37b9 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -436,7 +436,7 @@ class PurchaseReceipt(BuyingController): ) divisional_loss = flt( - valuation_amount_as_per_doc - stock_value_diff, d.precision("base_net_amount") + valuation_amount_as_per_doc - flt(stock_value_diff), d.precision("base_net_amount") ) if divisional_loss: From c4d9576f9f09f5c78981d2d1cd91e1f24de2b2d2 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 27 Feb 2023 14:34:43 +0000 Subject: [PATCH 18/76] chore(release): Bumped to Version 13.48.1 ## [13.48.1](https://github.com/frappe/erpnext/compare/v13.48.0...v13.48.1) (2023-02-27) ### Bug Fixes * not able to repost gl entries ([a34aff6](https://github.com/frappe/erpnext/commit/a34aff6f49be9a2d8b2629ff66b66778dcf31767)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index bda50b1eb4b..82f450f80ed 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.48.0" +__version__ = "13.48.1" def get_default_company(user=None): From 573cd3c33bfc7bdc2e2c3c76d6d6711bf1e452a6 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 28 Feb 2023 13:29:25 +0000 Subject: [PATCH 19/76] chore(release): Bumped to Version 13.49.0 # [13.49.0](https://github.com/frappe/erpnext/compare/v13.48.1...v13.49.0) (2023-02-28) ### Bug Fixes * conversion factor not set ([59d5797](https://github.com/frappe/erpnext/commit/59d579764d2fcecf66ba2b7d704808d41d4f976d)) * german translations ([#31732](https://github.com/frappe/erpnext/issues/31732)) ([d44da6c](https://github.com/frappe/erpnext/commit/d44da6c8206ea9564331e9df823b6867a248c968)) * ignore remaining leaves calculation for cf leaves after expiry ([d82ba4e](https://github.com/frappe/erpnext/commit/d82ba4e86f023b2d436de7b44f157d94596234a8)) * incorrect acc depr amount if multiple FBs with straight line or manual method ([304e6bb](https://github.com/frappe/erpnext/commit/304e6bb9963649f5e0802c63771a6e6b295aea54)) * incorrect color in the BOM Stock Report ([e98b346](https://github.com/frappe/erpnext/commit/e98b34617f194892fae1feaaef99b3551a478b83)) * incorrect leave balance after carry-forwarded leave expiry ([a3a9cd5](https://github.com/frappe/erpnext/commit/a3a9cd517459d3b75e73c7808f3ea749af2c7111)) * manual depr schedule ([7176799](https://github.com/frappe/erpnext/commit/71767994a77349797af70676b1ff42bdc902bc2b)) * multiple pos conversion issue resolved ([de631e6](https://github.com/frappe/erpnext/commit/de631e65cc0d15ed6616d20000a5d3248095006d)) * not able to repost gl entries ([2039bd0](https://github.com/frappe/erpnext/commit/2039bd066d6723b10d5bee0b4944a6c5b417a644)) * **patch:** create only 80G custom fields instead of running the whole setup ([#34183](https://github.com/frappe/erpnext/issues/34183)) ([806f7e5](https://github.com/frappe/erpnext/commit/806f7e5eef1fb220ee00889e3aba765f7d4f0deb)) * permission error while calling get_work_order_items ([3d7b2b1](https://github.com/frappe/erpnext/commit/3d7b2b1a6d41b9de3d40e67493a2223b86b2960f)) * pos return throwing amount greater than grand total ([f6607a6](https://github.com/frappe/erpnext/commit/f6607a60501297dca70322cbe0d375323fba3fc4)) * Remove missing DocField in fetch_from ([45645c1](https://github.com/frappe/erpnext/commit/45645c10644beca26e4d1787adc09a1a0ae2f45a)) * set `from_warehouse` and `to_warehouse` while mapping SE ([b1ecca3](https://github.com/frappe/erpnext/commit/b1ecca3a164ac849653ff86aacfc986c7aac572c)) * **test:** use standalone method to fetch work orders from SO ([7971c14](https://github.com/frappe/erpnext/commit/7971c149eda88f4f8c10cfd847f48401fb92764a)) * ui freeze on item selection in sales invoice ([d1b611d](https://github.com/frappe/erpnext/commit/d1b611d37f7d7fbb8fc1c4e304aaf9ac10fa4757)) * user shouldn't able to make item price for item template ([69f1247](https://github.com/frappe/erpnext/commit/69f1247fabd1e645d7a9fbc96d64cca6111fd5a0)) * zero division error while making LCV ([91a95ad](https://github.com/frappe/erpnext/commit/91a95adcb6180016f87cf3c2b1b1f28d375401b6)) ### Features * provision to convert transaction based reposting to item warehouse based reposting ([59c6eb5](https://github.com/frappe/erpnext/commit/59c6eb591bb0ed10bb1d80cd7fcdc6bf347e9c6b)) ### Performance Improvements * fetch SLE's on demand and memoize ([642692a](https://github.com/frappe/erpnext/commit/642692a04049c35cfc8d84769ee79104d65b06dd)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 82f450f80ed..e1ae9fa9a7b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.48.1" +__version__ = "13.49.0" def get_default_company(user=None): From e6945508f14be90f31f5faaaa749a6f832544428 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 1 Mar 2023 16:16:58 +0530 Subject: [PATCH 20/76] fix: Wrap unexpectedly long text in remark (cherry picked from commit ba66a6714c495f9d70dad79344342779c8011c6e) --- .../report/general_ledger/general_ledger.html | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 1aad36ca909..b77c4cecf60 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -38,8 +38,11 @@ {% if(data[i].posting_date) { %} {%= frappe.datetime.str_to_user(data[i].posting_date) %} {%= data[i].voucher_type %} -
{%= data[i].voucher_no %} - +
{%= data[i].voucher_no %} + + {% var longest_word = cstr(data[i].remarks).split(" ").reduce((longest, word) => word.length > longest.length ? word : longest, ""); %} + 45 %} class="overflow-wrap-anywhere" {% endif %}> + {% if(!(filters.party || filters.account)) { %} {%= data[i].party || data[i].account %}
@@ -49,11 +52,14 @@ {% if(data[i].bill_no) { %}
{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %} {% } %} - - - {%= format_currency(data[i].debit, filters.presentation_currency) %} - - {%= format_currency(data[i].credit, filters.presentation_currency) %} +
+ + + {%= format_currency(data[i].debit, filters.presentation_currency) %} + + + {%= format_currency(data[i].credit, filters.presentation_currency) %} + {% } else { %} From 178be42369514cdceb87e8ab05646fbd443a9801 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 1 Mar 2023 10:55:31 +0000 Subject: [PATCH 21/76] chore(release): Bumped to Version 13.49.1 ## [13.49.1](https://github.com/frappe/erpnext/compare/v13.49.0...v13.49.1) (2023-03-01) ### Bug Fixes * Wrap unexpectedly long text in remark ([e694550](https://github.com/frappe/erpnext/commit/e6945508f14be90f31f5faaaa749a6f832544428)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e1ae9fa9a7b..c57b8076fad 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.0" +__version__ = "13.49.1" def get_default_company(user=None): From 0ec34e5880cdcce96c03d8cf11913d1beab35f9c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 7 Mar 2023 10:43:39 +0000 Subject: [PATCH 22/76] chore(release): Bumped to Version 13.49.2 ## [13.49.2](https://github.com/frappe/erpnext/compare/v13.49.1...v13.49.2) (2023-03-07) ### Bug Fixes * `rejected_serial_no` not getting copied from PR to PR(Return) ([bb55210](https://github.com/frappe/erpnext/commit/bb55210f492d5291a107f335b79888f82b80828c)) * `Serial No is mandatory` even if the `qty` is `0` ([9bea2fc](https://github.com/frappe/erpnext/commit/9bea2fcdfc71608f6fd02209e39359951403b6d8)) * Default sales team not getting set ([#34284](https://github.com/frappe/erpnext/issues/34284)) ([65c0189](https://github.com/frappe/erpnext/commit/65c0189c4dd8822e5c6a3f169058db5b18aceb74)) * **minor:** Dirty the form after clicking on Get advances button in Invoices ([#34323](https://github.com/frappe/erpnext/issues/34323)) ([3a1475a](https://github.com/frappe/erpnext/commit/3a1475a90b29ffd52f5fe47c3d21f3e0ae93afe5)) * **test:** check for batch_no in returned dict ([8c5322c](https://github.com/frappe/erpnext/commit/8c5322c1cb6659149fcd1db0e48f6cff1250d27f)) * UI freeze while selecting batched items in sales invoice ([82a8f2b](https://github.com/frappe/erpnext/commit/82a8f2b1b206b32776fd2eed2e1fcd413c7dc0f5)) * Wrap unexpectedly long text in remark ([ba66a67](https://github.com/frappe/erpnext/commit/ba66a6714c495f9d70dad79344342779c8011c6e)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c57b8076fad..a053978c6eb 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.1" +__version__ = "13.49.2" def get_default_company(user=None): From 56a422deedaf8e55f1b3c6ae6b44790f40c73b54 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sun, 5 Mar 2023 21:57:02 +0530 Subject: [PATCH 23/76] perf: Stock Entry (Material Transfer) (cherry picked from commit de18f98c5c3d0ee5cc9d3df5b389917670514e64) (cherry picked from commit 7a159a7187e2b667267ed92ba1447ca1ab8a4b1b) --- .../doctype/job_card/job_card.py | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 49e5569644f..092ad1ffe83 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -536,7 +536,34 @@ class JobCard(Document): ) def set_transferred_qty_in_job_card_item(self, ste_doc): - from frappe.query_builder.functions import Sum + def _get_job_card_items_transferred_qty(ste_doc): + from frappe.query_builder.functions import Sum + + job_card_items_transferred_qty = {} + job_card_items = [ + x.get("job_card_item") for x in ste_doc.get("items") if x.get("job_card_item") + ] + + if job_card_items: + se = frappe.qb.DocType("Stock Entry") + sed = frappe.qb.DocType("Stock Entry Detail") + + query = ( + frappe.qb.from_(sed) + .join(se) + .on(sed.parent == se.name) + .select(sed.job_card_item, Sum(sed.qty)) + .where( + (sed.job_card_item.isin(job_card_items)) + & (se.docstatus == 1) + & (se.purpose == "Material Transfer for Manufacture") + ) + .groupby(sed.job_card_item) + ) + + job_card_items_transferred_qty = frappe._dict(query.run(as_list=True)) + + return job_card_items_transferred_qty def _validate_over_transfer(row, transferred_qty): "Block over transfer of items if not allowed in settings." @@ -553,29 +580,23 @@ class JobCard(Document): exc=JobCardOverTransferError, ) - for row in ste_doc.items: - if not row.job_card_item: - continue - - sed = frappe.qb.DocType("Stock Entry Detail") - se = frappe.qb.DocType("Stock Entry") - transferred_qty = ( - frappe.qb.from_(sed) - .join(se) - .on(sed.parent == se.name) - .select(Sum(sed.qty)) - .where( - (sed.job_card_item == row.job_card_item) - & (se.docstatus == 1) - & (se.purpose == "Material Transfer for Manufacture") - ) - ).run()[0][0] + job_card_items_transferred_qty = _get_job_card_items_transferred_qty(ste_doc) + if job_card_items_transferred_qty: allow_excess = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer") - if not allow_excess: - _validate_over_transfer(row, transferred_qty) - frappe.db.set_value("Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty)) + for row in ste_doc.items: + if not row.job_card_item: + continue + + transferred_qty = flt(job_card_items_transferred_qty.get(row.job_card_item)) + + if not allow_excess: + _validate_over_transfer(row, transferred_qty) + + frappe.db.set_value( + "Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty) + ) def set_transferred_qty(self, update_status=False): "Set total FG Qty in Job Card for which RM was transferred." From 6841e22ffed2b66968366faf65f67686de6b2134 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 7 Mar 2023 01:11:23 +0530 Subject: [PATCH 24/76] perf: `update_completed_qty()` in `material_request.py` (cherry picked from commit 8ad9e99cea5e3f235d0b3ead812d2a3a11d40081) (cherry picked from commit 5bc2b8f68584c12fefd86d3fda71b87cc7d3bb7a) --- .../material_request/material_request.py | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 4697e36778d..8a29ac877b0 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -10,6 +10,7 @@ import json import frappe from frappe import _, msgprint from frappe.model.mapper import get_mapped_doc +from frappe.query_builder.functions import Sum from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, new_line_sep, nowdate from six import string_types @@ -185,6 +186,34 @@ class MaterialRequest(BuyingController): self.update_requested_qty() self.update_requested_qty_in_production_plan() + def get_mr_items_ordered_qty(self, mr_items): + mr_items_ordered_qty = {} + mr_items = [d.name for d in self.get("items") if d.name in mr_items] + + doctype = qty_field = None + if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"): + doctype = frappe.qb.DocType("Stock Entry Detail") + qty_field = doctype.transfer_qty + elif self.material_request_type == "Manufacture": + doctype = frappe.qb.DocType("Work Order") + qty_field = doctype.qty + + if doctype and qty_field: + query = ( + frappe.qb.from_(doctype) + .select(doctype.material_request_item, Sum(qty_field)) + .where( + (doctype.material_request == self.name) + & (doctype.material_request_item.isin(mr_items)) + & (doctype.docstatus == 1) + ) + .groupby(doctype.material_request_item) + ) + + mr_items_ordered_qty = frappe._dict(query.run()) + + return mr_items_ordered_qty + def update_completed_qty(self, mr_items=None, update_modified=True): if self.material_request_type == "Purchase": return @@ -192,18 +221,13 @@ class MaterialRequest(BuyingController): if not mr_items: mr_items = [d.name for d in self.get("items")] + mr_items_ordered_qty = self.get_mr_items_ordered_qty(mr_items) + mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance") + for d in self.get("items"): if d.name in mr_items: if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"): - d.ordered_qty = flt( - frappe.db.sql( - """select sum(transfer_qty) - from `tabStock Entry Detail` where material_request = %s - and material_request_item = %s and docstatus = 1""", - (self.name, d.name), - )[0][0] - ) - mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance") + d.ordered_qty = flt(mr_items_ordered_qty.get(d.name)) if mr_qty_allowance: allowed_qty = d.qty + (d.qty * (mr_qty_allowance / 100)) @@ -224,14 +248,7 @@ class MaterialRequest(BuyingController): ) elif self.material_request_type == "Manufacture": - d.ordered_qty = flt( - frappe.db.sql( - """select sum(qty) - from `tabWork Order` where material_request = %s - and material_request_item = %s and docstatus = 1""", - (self.name, d.name), - )[0][0] - ) + d.ordered_qty = flt(mr_items_ordered_qty.get(d.name)) frappe.db.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty) From 07ff956fd83431839e4a1bc4c3f8a8f7c923fea4 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 7 Mar 2023 17:01:01 +0000 Subject: [PATCH 25/76] chore(release): Bumped to Version 13.49.3 ## [13.49.3](https://github.com/frappe/erpnext/compare/v13.49.2...v13.49.3) (2023-03-07) ### Performance Improvements * `update_completed_qty()` in `material_request.py` ([6841e22](https://github.com/frappe/erpnext/commit/6841e22ffed2b66968366faf65f67686de6b2134)) * Stock Entry (Material Transfer) ([56a422d](https://github.com/frappe/erpnext/commit/56a422deedaf8e55f1b3c6ae6b44790f40c73b54)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a053978c6eb..931c728778e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.2" +__version__ = "13.49.3" def get_default_company(user=None): From b712aea3a444c1e9f777dbc065d42dea9fcd5d8e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:40:32 +0530 Subject: [PATCH 26/76] Revert "fix: Default sales team not getting set" (#34376) Revert "fix: Default sales team not getting set" (#34376) Revert "fix: Default sales team not getting set (#34284)" This reverts commit 7d0199d743c7861e883cadd582c036cc8d9b0a62. (cherry picked from commit 9a8f8e8b7da532499a8916755a915d2da8081577) Co-authored-by: Deepesh Garg (cherry picked from commit 42bda6e37b3ef4a523cc05d5055dc45f95f967cf) --- erpnext/controllers/selling_controller.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 6b1e3ea328a..c52a2dfa95b 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -86,9 +86,6 @@ class SellingController(StockController): ) if not self.meta.get_field("sales_team"): party_details.pop("sales_team") - else: - self.set("sales_team", party_details.get("sales_team")) - self.update_if_missing(party_details) elif lead: From 5157f5dd0e05e1d908aec6bc067d479592efe161 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 9 Mar 2023 10:26:41 +0000 Subject: [PATCH 27/76] chore(release): Bumped to Version 13.49.4 ## [13.49.4](https://github.com/frappe/erpnext/compare/v13.49.3...v13.49.4) (2023-03-09) ### Reverts * Revert "fix: Default sales team not getting set" (#34376) ([b712aea](https://github.com/frappe/erpnext/commit/b712aea3a444c1e9f777dbc065d42dea9fcd5d8e)), closes [#34376](https://github.com/frappe/erpnext/issues/34376) [#34376](https://github.com/frappe/erpnext/issues/34376) [#34284](https://github.com/frappe/erpnext/issues/34284) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 931c728778e..643aa6cfc58 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.3" +__version__ = "13.49.4" def get_default_company(user=None): From cfcbdfcaec8015296a06b6b7d692bd9ab51f6e4d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 14 Mar 2023 16:38:26 +0000 Subject: [PATCH 28/76] chore(release): Bumped to Version 13.49.5 ## [13.49.5](https://github.com/frappe/erpnext/compare/v13.49.4...v13.49.5) (2023-03-14) ### Bug Fixes * `BOM Stock Report` ([eb1f8f9](https://github.com/frappe/erpnext/commit/eb1f8f932d0517df677bb131744e06140a5c5f2e)) * `required_qty` get reset to `1` for Alternative Item in WO ([d117101](https://github.com/frappe/erpnext/commit/d1171016b34789fa11e952a007377e7f4b326432)) * adjust excess cf leaves in new leaves taken if number exceeds cf leaves allocation ([bc12269](https://github.com/frappe/erpnext/commit/bc12269ef46940473dc07bdb4ddf5f84557ef919)) * consider leaves taken while calculating expired carry-forwarded leaves ([e74e02b](https://github.com/frappe/erpnext/commit/e74e02b7654dadd3090aa868a95692a3b3cca09f)) * consider leaves taken within carry-forwarded period separately while calculating balance ([52108d5](https://github.com/frappe/erpnext/commit/52108d52e24b097bf0dc8c87acb23d0e58f7e467)) * consider relieving date while calculating payment days based on lwp ([563f83f](https://github.com/frappe/erpnext/commit/563f83f0f50b8508099ebae088ef1dd460cf83e9)) * Don't use get_list & get_all interchangeably ([b70a37f](https://github.com/frappe/erpnext/commit/b70a37f6fa09f28503739974253c272f05745c6c)) * Error in consolidated financial statement ([#34330](https://github.com/frappe/erpnext/issues/34330)) ([470dc10](https://github.com/frappe/erpnext/commit/470dc10b15f7f16568da851e106662279a094736)) * exclude cancelled leave ledger entries ([91cad9e](https://github.com/frappe/erpnext/commit/91cad9e985982aa1b86d14a8f554c996fccaa9ae)) * exclude carry forwarding leaves while updating leaves after submission ([88c5de5](https://github.com/frappe/erpnext/commit/88c5de533a862429c5fc9e1b5fa2cb36cda79235)) * leave allocation tests ([2f62a96](https://github.com/frappe/erpnext/commit/2f62a9641e7260e2fcb73cf00d8ade6262d448e6)) * linter ([341eab2](https://github.com/frappe/erpnext/commit/341eab2b2abcbd74522d81467082297c544f37bf)) * operation time for multi-level BOM in WO ([f4d07cc](https://github.com/frappe/erpnext/commit/f4d07cc84e15f5e1c0b3e2f33fadcf0262a896bf)) * precision for newly allocated leaves ([238769e](https://github.com/frappe/erpnext/commit/238769e6b51b5decac6ab3bbf5747211e757982d)) * **test:** `get_leave_allocation_records` ([c01bed9](https://github.com/frappe/erpnext/commit/c01bed98621caf7ef6f087661365640c0bead379)) * **test:** `TestBomStockReport` ([4824302](https://github.com/frappe/erpnext/commit/482430281136f3a6a56b5ff2ca36bd96445de9d7)) * Total row in trail balance report (backport [#34395](https://github.com/frappe/erpnext/issues/34395)) ([#34431](https://github.com/frappe/erpnext/issues/34431)) ([809d6d6](https://github.com/frappe/erpnext/commit/809d6d638ee5c89813fa8a51d74b30ead4df1e95)) * Use customer name instead of name(id) in PSOA (backport [#34412](https://github.com/frappe/erpnext/issues/34412)) ([#34426](https://github.com/frappe/erpnext/issues/34426)) ([a24f050](https://github.com/frappe/erpnext/commit/a24f0507e1534b338e6dc1904c593381f519d54c)) ### Performance Improvements * `update_completed_qty()` in `material_request.py` ([5bc2b8f](https://github.com/frappe/erpnext/commit/5bc2b8f68584c12fefd86d3fda71b87cc7d3bb7a)) * Stock Entry (Material Transfer) ([7a159a7](https://github.com/frappe/erpnext/commit/7a159a7187e2b667267ed92ba1447ca1ab8a4b1b)) ### Reverts * Revert "fix: Default sales team not getting set" (#34376) ([42bda6e](https://github.com/frappe/erpnext/commit/42bda6e37b3ef4a523cc05d5055dc45f95f967cf)), closes [#34376](https://github.com/frappe/erpnext/issues/34376) [#34376](https://github.com/frappe/erpnext/issues/34376) [#34284](https://github.com/frappe/erpnext/issues/34284) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 643aa6cfc58..6faf292ad41 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.4" +__version__ = "13.49.5" def get_default_company(user=None): From 6e492ec514cbc97d9ce8ca7ce8989f7b78e891e5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 21 Mar 2023 18:20:16 +0530 Subject: [PATCH 29/76] chore: release v13 (#34531) * chore: Update user manual link (#34478) * chore: Update user manual link (#34478) (cherry picked from commit be723bb9d483c615fa0b14b0115338e39e32a698) # Conflicts: # erpnext/patches.txt * chore: resolve conflicts * chore: Update version Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> * chore: Update version Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --------- Co-authored-by: Deepesh Garg Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> * fix: Overallocation of 'qty' from Cr Notes to Parent Invoice Cr Notes 'qty' are overallocated to parent invoice, when there are mulitple instances of same item in Invoice. (cherry picked from commit e2f19c6a14f95d4f26acd8dfa91f50335f58b290) * refactor: Ignore linked Cr Notes in Report output (cherry picked from commit d0715a82ebfbd691c70c9e01cdf2357f40f19d04) * test: Gross Profit report output for Cr notes 2 New test cases added. 1. Standalone Cr notes will be reported as normal Invoices 2. Cr notes against an Invoice will not overallocate qty if there are multiple instances of same item (cherry picked from commit cc61daeec4fc78f0c50af44db62998d12b2d5ea5) * fix: incorrect depr schedules after asset repair [v13] (#34520) * fix: incorrect schedule after repair for WDV and DD * chore: only fix schedules for assets with calc_depr true * fix: incorrect schedule after repair for straight line and manual * refactor: calc depr in asset repair and if statement (#34526) refactor: minor asset repair of calc depr and if statement * fix(client): Amount calculation for 0 qty debit notes (#34455) fix(client): Amount calculation for 0 qty debit notes (#34455) fix(client): Amount calculaton for 0 qty debit notes Co-authored-by: Anand Baburajan (cherry picked from commit ee6c107d588ca30e909d9add4a026755eda722de) Co-authored-by: Deepesh Garg * fix: german translations (#34312) fix: german translations (#34312) fix: some german translations (cherry picked from commit 59c2e7ec3ee25198a44fb7ac7157b8117eb3e4e6) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> * fix: exchange gain/loss GL's should be removed if advance is cancelled (#34529) * fix: report GL for invoice when advance has different exchange rate If deferred revenue/expense is enabled for any item, don't repost. * test: cancelling advance should remove exchange gain/loss If there are no deferred revenue/expense cancelling advance should cancel the exchange gain/loss booked due to different exchange rates of payment and its linked invoice --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Deepesh Garg Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Co-authored-by: ruthra kumar Co-authored-by: Anand Baburajan --- .../doctype/payment_entry/payment_entry.py | 25 +++++- .../sales_invoice/test_sales_invoice.py | 72 +++++++++++++++++ .../report/gross_profit/gross_profit.py | 23 +++++- .../report/gross_profit/test_gross_profit.py | 79 +++++++++++++++++++ erpnext/assets/doctype/asset/asset.py | 37 ++++++--- .../doctype/asset_repair/asset_repair.py | 42 ++++++++-- erpnext/patches.txt | 1 + erpnext/patches/v13_0/update_docs_link.py | 14 ++++ .../public/js/controllers/taxes_and_totals.js | 2 +- erpnext/regional/india/utils.py | 21 ++--- erpnext/setup/install.py | 2 +- erpnext/translations/de.csv | 27 +++---- 12 files changed, 296 insertions(+), 49 deletions(-) create mode 100644 erpnext/patches/v13_0/update_docs_link.py diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f4367cdafd6..f0d7d57fc64 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -7,7 +7,7 @@ from functools import reduce import frappe from frappe import ValidationError, _, scrub, throw -from frappe.utils import cint, comma_or, flt, getdate, nowdate +from frappe.utils import cint, comma_or, flt, get_link_to_form, getdate, nowdate from six import iteritems, string_types import erpnext @@ -168,8 +168,31 @@ class PaymentEntry(AccountsController): for reference in self.references: if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"): doc = frappe.get_doc(reference.reference_doctype, reference.reference_name) + + repost_required = False + for adv_reference in doc.get("advances"): + if adv_reference.exchange_gain_loss != 0: + repost_required = True + break + if repost_required: + for item in doc.get("items"): + if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): + frappe.msgprint( + _( + "Linked Invoice {0} has Exchange Gain/Loss GL entries due to this Payment. Submit a Journal manually to reverse its effects." + ).format(get_link_to_form(doc.doctype, doc.name)) + ) + repost_required = False + doc.delink_advance_entries(self.name) + if repost_required: + doc.reload() + doc.docstatus = 2 + doc.make_gl_entries() + doc.docstatus = 1 + doc.make_gl_entries() + def set_missing_values(self): if self.payment_type == "Internal Transfer": for field in ( diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6035e86d067..46ffd7e18d0 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3470,6 +3470,78 @@ class TestSalesInvoice(unittest.TestCase): "Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled ) + def test_gain_loss_on_advance_cancellation(self): + unlink_enabled = frappe.db.get_single_value( + "Accounts Settings", "unlink_payment_on_cancellation_of_invoice" + ) + + frappe.db.set_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice", 1) + + pe = frappe.get_doc( + { + "doctype": "Payment Entry", + "payment_type": "Receive", + "party_type": "Customer", + "party": "_Test Customer USD", + "company": "_Test Company", + "paid_from_account_currency": "USD", + "paid_to_account_currency": "INR", + "source_exchange_rate": 70, + "target_exchange_rate": 1, + "reference_no": "1", + "reference_date": nowdate(), + "received_amount": 70, + "paid_amount": 1, + "paid_from": "_Test Receivable USD - _TC", + "paid_to": "_Test Cash - _TC", + } + ) + pe.insert() + pe.submit() + + si = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=75, + do_not_save=1, + rate=1, + ) + si = si.save() + + si.append( + "advances", + { + "reference_type": "Payment Entry", + "reference_name": pe.name, + "advance_amount": 1, + "allocated_amount": 1, + "ref_exchange_rate": 70, + }, + ) + si.save() + si.submit() + expected_gle = [ + ["_Test Receivable USD - _TC", 75.0, 5.0], + ["Exchange Gain/Loss - _TC", 5.0, 0.0], + ["Sales - _TC", 0.0, 75.0], + ] + check_gl_entries(self, si.name, expected_gle, nowdate()) + + # cancel advance payment + pe.reload() + pe.cancel() + + expected_gle_after = [ + ["_Test Receivable USD - _TC", 75.0, 0.0], + ["Sales - _TC", 0.0, 75.0], + ] + check_gl_entries(self, si.name, expected_gle_after, nowdate()) + + frappe.db.set_single_value( + "Accounts Settings", "unlink_payment_on_cancellation_of_invoice", unlink_enabled + ) + def test_batch_expiry_for_sales_invoice_return(self): from erpnext.controllers.sales_and_purchase_return import make_return_doc from erpnext.stock.doctype.item.test_item import make_item diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index c73cb050f01..35213121b32 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -8,6 +8,7 @@ from frappe.query_builder import Order from frappe.utils import cint, flt from erpnext.controllers.queries import get_match_cond +from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition from erpnext.stock.utils import get_incoming_rate @@ -465,7 +466,14 @@ class GrossProfitGenerator(object): ): returned_item_rows = self.returned_invoices[row.parent][row.item_code] for returned_item_row in returned_item_rows: - row.qty += flt(returned_item_row.qty) + # returned_items 'qty' should be stateful + if returned_item_row.qty != 0: + if row.qty >= abs(returned_item_row.qty): + row.qty += returned_item_row.qty + returned_item_row.qty = 0 + else: + row.qty = 0 + returned_item_row.qty += row.qty row.base_amount += flt(returned_item_row.base_amount, self.currency_precision) row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision) if flt(row.qty) or row.base_amount: @@ -664,6 +672,19 @@ class GrossProfitGenerator(object): if self.filters.to_date: conditions += " and posting_date <= %(to_date)s" + conditions += " and (is_return = 0 or (is_return=1 and return_against is null))" + + if self.filters.item_group: + conditions += " and {0}".format(get_item_group_condition(self.filters.item_group)) + + if self.filters.sales_person: + conditions += """ + and exists(select 1 + from `tabSales Team` st + where st.parent = `tabSales Invoice`.name + and st.sales_person = %(sales_person)s) + """ + if self.filters.group_by == "Sales Person": sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives" sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name" diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index 06a173ee35a..89ed2637e6f 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -380,3 +380,82 @@ class TestGrossProfit(FrappeTestCase): } gp_entry = [x for x in data if x.parent_invoice == sinv.name] self.assertDictContainsSubset(expected_entry, gp_entry[0]) + + def test_crnote_against_invoice_with_multiple_instances_of_same_item(self): + """ + Item Qty for Sales Invoices with multiple instances of same item go in the -ve. Ideally, the credit noteshould cancel out the invoice items. + """ + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return + + # Invoice with an item added twice + sinv = self.create_sales_invoice(qty=1, rate=100, posting_date=nowdate(), do_not_submit=True) + sinv.append("items", frappe.copy_doc(sinv.items[0], ignore_no_copy=False)) + sinv = sinv.save().submit() + + # Create Credit Note for Invoice + cr_note = make_sales_return(sinv.name) + cr_note = cr_note.save().submit() + + filters = frappe._dict( + company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice" + ) + + columns, data = execute(filters=filters) + expected_entry = { + "parent_invoice": sinv.name, + "currency": "INR", + "sales_invoice": self.item, + "customer": self.customer, + "posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()), + "item_code": self.item, + "item_name": self.item, + "warehouse": "Stores - _GP", + "qty": 0.0, + "avg._selling_rate": 0.0, + "valuation_rate": 0.0, + "selling_amount": -100.0, + "buying_amount": 0.0, + "gross_profit": -100.0, + "gross_profit_%": 100.0, + } + gp_entry = [x for x in data if x.parent_invoice == sinv.name] + # Both items of Invoice should have '0' qty + self.assertEqual(len(gp_entry), 2) + self.assertDictContainsSubset(expected_entry, gp_entry[0]) + self.assertDictContainsSubset(expected_entry, gp_entry[1]) + + def test_standalone_cr_notes(self): + """ + Standalone cr notes will be reported as usual + """ + # Make Cr Note + sinv = self.create_sales_invoice( + qty=-1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True + ) + sinv.is_return = 1 + sinv = sinv.save().submit() + + filters = frappe._dict( + company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice" + ) + + columns, data = execute(filters=filters) + expected_entry = { + "parent_invoice": sinv.name, + "currency": "INR", + "sales_invoice": self.item, + "customer": self.customer, + "posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()), + "item_code": self.item, + "item_name": self.item, + "warehouse": "Stores - _GP", + "qty": -1.0, + "avg._selling_rate": 100.0, + "valuation_rate": 0.0, + "selling_amount": -100.0, + "buying_amount": 0.0, + "gross_profit": -100.0, + "gross_profit_%": 100.0, + } + gp_entry = [x for x in data if x.parent_invoice == sinv.name] + self.assertDictContainsSubset(expected_entry, gp_entry[0]) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index d44e2ec0c5d..e46cdb9fc64 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -380,12 +380,19 @@ class Asset(AccountsController): value_after_depreciation -= flt(depreciation_amount, self.precision("gross_purchase_amount")) # Adjust depreciation amount in the last period based on the expected value after useful life - if finance_book.expected_value_after_useful_life and ( - ( - n == cint(number_of_pending_depreciations) - 1 - and value_after_depreciation != finance_book.expected_value_after_useful_life + if ( + finance_book.expected_value_after_useful_life + and ( + ( + n == cint(number_of_pending_depreciations) - 1 + and value_after_depreciation != finance_book.expected_value_after_useful_life + ) + or value_after_depreciation < finance_book.expected_value_after_useful_life + ) + and ( + not self.flags.increase_in_asset_value_due_to_repair + or not finance_book.depreciation_method in ("Written Down Value", "Double Declining Balance") ) - or value_after_depreciation < finance_book.expected_value_after_useful_life ): depreciation_amount += ( value_after_depreciation - finance_book.expected_value_after_useful_life @@ -1171,17 +1178,21 @@ def get_total_days(date, frequency): @erpnext.allow_regional def get_depreciation_amount(asset, depreciable_value, row): if row.depreciation_method in ("Straight Line", "Manual"): - # if the Depreciation Schedule is being prepared for the first time - if not asset.flags.increase_in_asset_life: - depreciation_amount = ( - flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) - - # if the Depreciation Schedule is being modified after Asset Repair - else: + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value + if asset.flags.increase_in_asset_life: depreciation_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value + elif asset.flags.increase_in_asset_value_due_to_repair: + depreciation_amount = ( + flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations) + # if the Depreciation Schedule is being prepared for the first time + else: + depreciation_amount = ( + flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations) else: depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index cd8fe5b18b7..edcbf3e2fd0 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -39,7 +39,11 @@ class AssetRepair(AccountsController): def before_submit(self): self.check_repair_status() + self.asset_doc.flags.increase_in_asset_value_due_to_repair = False + if self.get("stock_consumption") or self.get("capitalize_repair_cost"): + self.asset_doc.flags.increase_in_asset_value_due_to_repair = True + self.increase_asset_value() if self.get("stock_consumption"): @@ -49,20 +53,23 @@ class AssetRepair(AccountsController): if self.get("capitalize_repair_cost"): self.make_gl_entries() - if ( - frappe.db.get_value("Asset", self.asset, "calculate_depreciation") - and self.increase_in_asset_life - ): + if self.asset_doc.calculate_depreciation and self.increase_in_asset_life: self.modify_depreciation_schedule() self.asset_doc.flags.ignore_validate_update_after_submit = True self.asset_doc.prepare_depreciation_data() + if self.asset_doc.calculate_depreciation: + self.update_asset_expected_value_after_useful_life() self.asset_doc.save() def before_cancel(self): self.asset_doc = frappe.get_doc("Asset", self.asset) + self.asset_doc.flags.increase_in_asset_value_due_to_repair = False + if self.get("stock_consumption") or self.get("capitalize_repair_cost"): + self.asset_doc.flags.increase_in_asset_value_due_to_repair = True + self.decrease_asset_value() if self.get("stock_consumption"): @@ -72,14 +79,13 @@ class AssetRepair(AccountsController): self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") self.make_gl_entries(cancel=True) - if ( - frappe.db.get_value("Asset", self.asset, "calculate_depreciation") - and self.increase_in_asset_life - ): + if self.asset_doc.calculate_depreciation and self.increase_in_asset_life: self.revert_depreciation_schedule_on_cancellation() self.asset_doc.flags.ignore_validate_update_after_submit = True self.asset_doc.prepare_depreciation_data() + if self.asset_doc.calculate_depreciation: + self.update_asset_expected_value_after_useful_life() self.asset_doc.save() def after_delete(self): @@ -100,6 +106,26 @@ class AssetRepair(AccountsController): title=_("Missing Warehouse"), ) + def update_asset_expected_value_after_useful_life(self): + for row in self.asset_doc.get("finance_books"): + if row.depreciation_method in ("Written Down Value", "Double Declining Balance"): + accumulated_depreciation_after_full_schedule = [ + d.accumulated_depreciation_amount + for d in self.asset_doc.get("schedules") + if cint(d.finance_book_id) == row.idx + ] + + accumulated_depreciation_after_full_schedule = max( + accumulated_depreciation_after_full_schedule + ) + + asset_value_after_full_schedule = flt( + flt(row.value_after_depreciation) - flt(accumulated_depreciation_after_full_schedule), + row.precision("expected_value_after_useful_life"), + ) + + row.expected_value_after_useful_life = asset_value_after_full_schedule + def increase_asset_value(self): total_value_of_stock_consumed = self.get_total_value_of_stock_consumed() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ecbf1f8000c..79ec14a28fb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -376,3 +376,4 @@ erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair execute:frappe.db.set_value("Naming Series", "Naming Series", {"select_doc_for_series": "", "set_options": "", "prefix": "", "current_value": 0, "user_must_always_select": 0}) erpnext.patches.v13_0.update_schedule_type_in_loans erpnext.patches.v13_0.update_asset_value_for_manual_depr_entries +erpnext.patches.v13_0.update_docs_link diff --git a/erpnext/patches/v13_0/update_docs_link.py b/erpnext/patches/v13_0/update_docs_link.py new file mode 100644 index 00000000000..d6b1c4cffa7 --- /dev/null +++ b/erpnext/patches/v13_0/update_docs_link.py @@ -0,0 +1,14 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: MIT. See LICENSE + + +import frappe + + +def execute(): + navbar_settings = frappe.get_single("Navbar Settings") + for item in navbar_settings.help_dropdown: + if item.is_standard and item.route == "https://erpnext.com/docs/user/manual": + item.route = "https://docs.erpnext.com/docs/v13/user/manual/en/introduction" + + navbar_settings.save() diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 79196c976f8..7b64087102f 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -125,7 +125,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } else { // allow for '0' qty on Credit/Debit notes - let qty = item.qty || -1 + let qty = item.qty || me.frm.doc.is_debit_note ? 1 : -1; item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item)); } diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 0b61e7faf9b..d5ef3981faf 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -1101,18 +1101,21 @@ def update_taxable_values(doc, method): def get_depreciation_amount(asset, depreciable_value, row): if row.depreciation_method in ("Straight Line", "Manual"): - # if the Depreciation Schedule is being prepared for the first time - if not asset.flags.increase_in_asset_life: - depreciation_amount = ( - flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) - - # if the Depreciation Schedule is being modified after Asset Repair - else: + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value + if asset.flags.increase_in_asset_life: depreciation_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) - + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value + elif asset.flags.increase_in_asset_value_due_to_repair: + depreciation_amount = ( + flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations) + # if the Depreciation Schedule is being prepared for the first time + else: + depreciation_amount = ( + flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations) else: rate_of_depreciation = row.rate_of_depreciation # if its the first depreciation diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 20ba74b8cde..634b4c6a72b 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -150,7 +150,7 @@ def add_standard_navbar_items(): { "item_label": "Documentation", "item_type": "Route", - "route": "https://erpnext.com/docs/user/manual", + "route": "https://docs.erpnext.com/docs/v13/user/manual/en/introduction", "is_standard": 1, }, { diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 976c5bf0b7f..552a968f9bb 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -2007,30 +2007,27 @@ Please identify/create Account (Ledger) for type - {0},Bitte identifizieren / er Please login as another user to register on Marketplace,"Bitte melden Sie sich als anderer Benutzer an, um sich auf dem Marktplatz zu registrieren", Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.,"Bitte sicher stellen, dass wirklich alle Transaktionen dieses Unternehmens gelöscht werden sollen. Die Stammdaten bleiben bestehen. Diese Aktion kann nicht rückgängig gemacht werden.", Please mention Basic and HRA component in Company,Bitte erwähnen Sie die Basis- und HRA-Komponente in der Firma, -Please mention Round Off Account in Company,Bitte Abschlusskonto in Unternehmen vermerken, -Please mention Round Off Cost Center in Company,Bitte Abschlusskostenstelle in Unternehmen vermerken, -Please mention no of visits required,"Bitte bei ""Besuche erforderlich"" NEIN angeben", -Please mention the Lead Name in Lead {0},Bitte erwähnen Sie den Lead Name in Lead {0}, -Please pull items from Delivery Note,Bitte Artikel vom Lieferschein nehmen, +Please mention Round Off Account in Company,Bitte ein Standardkonto Konto für Rundungsdifferenzen in Unternehmen einstellen, +Please mention Round Off Cost Center in Company,Bitte eine Kostenstelle für Rundungsdifferenzen in Unternehmen einstellen, +Please mention no of visits required,Bitte die Anzahl der benötigten Wartungsbesuche angeben, +Please pull items from Delivery Note,Bitte Artikel aus dem Lieferschein ziehen, Please register the SIREN number in the company information file,Bitte registrieren Sie die SIREN-Nummer in der Unternehmensinformationsdatei, Please remove this Invoice {0} from C-Form {1},Bitte diese Rechnung {0} vom Kontaktformular {1} entfernen, Please save the patient first,Bitte speichern Sie den Patienten zuerst, Please save the report again to rebuild or update,"Speichern Sie den Bericht erneut, um ihn neu zu erstellen oder zu aktualisieren", "Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row","Bitte zugewiesenen Betrag, Rechnungsart und Rechnungsnummer in mindestens einer Zeile auswählen", Please select Apply Discount On,"Bitte ""Rabatt anwenden auf"" auswählen", -Please select BOM against item {0},Bitte wählen Sie Stückliste gegen Artikel {0}, -Please select BOM for Item in Row {0},Bitte Stückliste für Artikel in Zeile {0} auswählen, -Please select BOM in BOM field for Item {0},Bitte aus dem Stücklistenfeld eine Stückliste für Artikel {0} auswählen, -Please select Category first,Bitte zuerst Kategorie auswählen, -Please select Charge Type first,Bitte zuerst Chargentyp auswählen, -Please select Company,Bitte Unternehmen auswählen, +Please select BOM against item {0},Bitte eine Stückliste für Artikel {0} auswählen, +Please select BOM for Item in Row {0},Bitte eine Stückliste für den Artikel in Zeile {0} auswählen, +Please select BOM in BOM field for Item {0},Bitte im Stücklistenfeld eine Stückliste für Artikel {0} auswählen, +Please select Category first,Bitte zuerst eine Kategorie auswählen, +Please select Charge Type first,Bitte zuerst einen Chargentyp auswählen, +Please select Company,Bitte ein Unternehmen auswählen, Please select Company and Designation,Bitte wählen Sie Unternehmen und Position, Please select Company and Posting Date to getting entries,"Bitte wählen Sie Unternehmen und Buchungsdatum, um Einträge zu erhalten", Please select Company first,Bitte zuerst Unternehmen auswählen, Please select Completion Date for Completed Asset Maintenance Log,Bitte wählen Sie Fertigstellungsdatum für das abgeschlossene Wartungsprotokoll für den Vermögenswert, Please select Completion Date for Completed Repair,Bitte wählen Sie das Abschlussdatum für die abgeschlossene Reparatur, -Please select Course,Bitte wählen Sie Kurs, -Please select Drug,Bitte wählen Sie Arzneimittel, Please select Employee,Bitte wählen Sie Mitarbeiter, Please select Existing Company for creating Chart of Accounts,Bitte wählen Sie Bestehende Unternehmen für die Erstellung von Konten, Please select Healthcare Service,Bitte wählen Sie Gesundheitsdienst, @@ -7797,7 +7794,7 @@ Default Employee Advance Account,Standardkonto für Vorschüsse an Arbeitnehmer, Default Cost of Goods Sold Account,Standard-Herstellkosten, Default Income Account,Standard-Ertragskonto, Default Deferred Revenue Account,Standardkonto für passive Rechnungsabgrenzung, -Default Deferred Expense Account,Standard-Rechnungsabgrenzungsposten, +Default Deferred Expense Account,Standardkonto für aktive Rechnungsabgrenzung, Default Payroll Payable Account,Standardkonto für Verbindlichkeiten aus Lohn und Gehalt, Default Expense Claim Payable Account,Standard-Expense Claim Zahlbares Konto, Stock Settings,Lager-Einstellungen, @@ -8865,7 +8862,7 @@ Add Topic to Courses,Hinzufügen eines Themas zu Kursen, This topic is already added to the existing courses,Dieses Thema wurde bereits zu den bestehenden Kursen hinzugefügt, "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order","Wenn Shopify keinen Kunden in der Bestellung hat, berücksichtigt das System beim Synchronisieren der Bestellungen den Standardkunden für die Bestellung", The accounts are set by the system automatically but do confirm these defaults,"Die Konten werden vom System automatisch festgelegt, bestätigen jedoch diese Standardeinstellungen", -Default Round Off Account,Standard-Rundungskonto, +Default Round Off Account,Standardkonto für Rundungsdifferenzen, Failed Import Log,Importprotokoll fehlgeschlagen, Fixed Error Log,Fehlerprotokoll behoben, Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts,Firma {0} existiert bereits. Durch Fortfahren werden das Unternehmen und der Kontenplan überschrieben, From d5efeec0a4ca5df59ed5e6525eab1638f2aa811e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 22 Mar 2023 14:58:10 +0530 Subject: [PATCH 30/76] fix: `Blanket Order` (backport #34279) (backport #34548) (#34553) fix: `Blanket Order` (backport #34279) (#34548) * fix: hide `+` button based on `Blanket Order Type` (cherry picked from commit abf9a28d6af8b3c9bfab1e892e56bf3adb18ee8e) * feat: add field `Over Order Allowance (%)` in `Buying Settings` (cherry picked from commit f5937f46cb60f3521463f7a4c80c765f8a65e52b) # Conflicts: # erpnext/buying/doctype/buying_settings/buying_settings.json * refactor: rewrite `blanket_order.py` queries in `QB` (cherry picked from commit f3993783a3fc431a2909b445e9d09d9f584ff73e) * fix: don't map item row having `0` qty (cherry picked from commit fc1088d9c4787b12bd9734597604492044eff4a0) * feat: consider `over_order_allowance` while validating order qty (cherry picked from commit 8bcbc45add7767ac947fa7c9b3aaca99fc9dda9b) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.py * feat: add field `Over Order Allowance (%)` in `Selling Settings` (cherry picked from commit d7da8928ac44df3a84f6099fc7bfbc9a9161be20) # Conflicts: # erpnext/selling/doctype/selling_settings/selling_settings.json * feat: consider `over_order_allowance` while validating sales order qty (cherry picked from commit 53701c37b18c7aecfaa00efabf4d3be768e59cb3) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.py * test: add test cases for `Over Order Allowance` against `Blanket Order` (cherry picked from commit 66f650061dbae8c1093878f5b808e2a62f3a144a) * chore: `conflicts` --------- Co-authored-by: s-aga-r (cherry picked from commit 8ddbac515882469ed1ec19124e39f656c6581311) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../buying_settings/buying_settings.json | 112 +++++----- .../doctype/purchase_order/purchase_order.py | 4 + .../doctype/blanket_order/blanket_order.js | 6 + .../doctype/blanket_order/blanket_order.py | 67 ++++-- .../blanket_order/test_blanket_order.py | 27 +++ .../doctype/sales_order/sales_order.py | 4 + .../selling_settings/selling_settings.json | 192 +++++++++--------- 7 files changed, 254 insertions(+), 158 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index b828a43d3cf..52465c1a962 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -14,6 +14,7 @@ "column_break_3", "po_required", "pr_required", + "over_order_allowance", "maintain_same_rate", "allow_multiple_items", "bill_for_rejected_quantity_in_purchase_invoice", @@ -42,57 +43,6 @@ "label": "Default Buying Price List", "options": "Price List" }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "po_required", - "fieldtype": "Select", - "label": "Is Purchase Order Required for Purchase Invoice & Receipt Creation?", - "options": "No\nYes" - }, - { - "fieldname": "pr_required", - "fieldtype": "Select", - "label": "Is Purchase Receipt Required for Purchase Invoice Creation?", - "options": "No\nYes" - }, - { - "default": "0", - "fieldname": "maintain_same_rate", - "fieldtype": "Check", - "label": "Maintain Same Rate Throughout the Purchase Cycle" - }, - { - "default": "0", - "fieldname": "allow_multiple_items", - "fieldtype": "Check", - "label": "Allow Item To Be Added Multiple Times in a Transaction" - }, - { - "fieldname": "subcontract", - "fieldtype": "Section Break", - "label": "Subcontract" - }, - { - "default": "Material Transferred for Subcontract", - "fieldname": "backflush_raw_materials_of_subcontract_based_on", - "fieldtype": "Select", - "label": "Backflush Raw Materials of Subcontract Based On", - "options": "BOM\nMaterial Transferred for Subcontract" - }, - { - "depends_on": "eval:doc.backflush_raw_materials_of_subcontract_based_on == \"BOM\"", - "description": "Percentage you are allowed to transfer more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to transfer 110 units.", - "fieldname": "over_transfer_allowance", - "fieldtype": "Float", - "label": "Over Transfer Allowance (%)" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, { "default": "Stop", "depends_on": "maintain_same_rate", @@ -110,12 +60,70 @@ "label": "Role Allowed to Override Stop Action", "options": "Role" }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "po_required", + "fieldtype": "Select", + "label": "Is Purchase Order Required for Purchase Invoice & Receipt Creation?", + "options": "No\nYes" + }, + { + "fieldname": "pr_required", + "fieldtype": "Select", + "label": "Is Purchase Receipt Required for Purchase Invoice Creation?", + "options": "No\nYes" + }, + { + "default": "0", + "description": "Percentage you are allowed to order more against the Blanket Order Quantity. For example: If you have a Blanket Order of Quantity 100 units. and your Allowance is 10% then you are allowed to order 110 units.", + "fieldname": "over_order_allowance", + "fieldtype": "Float", + "label": "Over Order Allowance (%)" + }, + { + "default": "0", + "fieldname": "maintain_same_rate", + "fieldtype": "Check", + "label": "Maintain Same Rate Throughout the Purchase Cycle" + }, + { + "default": "0", + "fieldname": "allow_multiple_items", + "fieldtype": "Check", + "label": "Allow Item To Be Added Multiple Times in a Transaction" + }, { "default": "1", "description": "If checked, Rejected Quantity will be included while making Purchase Invoice from Purchase Receipt.", "fieldname": "bill_for_rejected_quantity_in_purchase_invoice", "fieldtype": "Check", "label": "Bill for Rejected Quantity in Purchase Invoice" + }, + { + "fieldname": "subcontract", + "fieldtype": "Section Break", + "label": "Subcontract" + }, + { + "default": "Material Transferred for Subcontract", + "fieldname": "backflush_raw_materials_of_subcontract_based_on", + "fieldtype": "Select", + "label": "Backflush Raw Materials of Subcontract Based On", + "options": "BOM\nMaterial Transferred for Subcontract" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.backflush_raw_materials_of_subcontract_based_on == \"BOM\"", + "description": "Percentage you are allowed to transfer more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to transfer 110 units.", + "fieldname": "over_transfer_allowance", + "fieldtype": "Float", + "label": "Over Transfer Allowance (%)" } ], "icon": "fa fa-cog", @@ -123,7 +131,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-09-08 19:26:23.548837", + "modified": "2023-03-22 13:01:49.640869", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 3888622d563..9a2495ed484 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -21,6 +21,9 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category from erpnext.accounts.party import get_party_account, get_party_account_currency from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items from erpnext.controllers.buying_controller import BuyingController +from erpnext.manufacturing.doctype.blanket_order.blanket_order import ( + validate_against_blanket_order, +) from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.stock.doctype.item.item import get_item_defaults, get_last_purchase_details from erpnext.stock.stock_balance import get_ordered_qty, update_bin_qty @@ -72,6 +75,7 @@ class PurchaseOrder(BuyingController): self.validate_bom_for_subcontracting_items() self.create_raw_materials_supplied("supplied_items") self.set_received_qty_for_drop_ship_items() + validate_against_blanket_order(self) validate_inter_company_party( self.doctype, self.supplier, self.company, self.inter_company_order_reference ) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index d3bb33e86e0..7b26a14a57b 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -7,6 +7,12 @@ frappe.ui.form.on('Blanket Order', { }, setup: function(frm) { + frm.custom_make_buttons = { + 'Purchase Order': 'Purchase Order', + 'Sales Order': 'Sales Order', + 'Quotation': 'Quotation', + }; + frm.add_fetch("customer", "customer_name", "customer_name"); frm.add_fetch("supplier", "supplier_name", "supplier_name"); }, diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index ff2140199de..32f1c365ade 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -6,6 +6,7 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.model.mapper import get_mapped_doc +from frappe.query_builder.functions import Sum from frappe.utils import flt, getdate from erpnext.stock.doctype.item.item import get_item_defaults @@ -29,21 +30,23 @@ class BlanketOrder(Document): def update_ordered_qty(self): ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order" + + trans = frappe.qb.DocType(ref_doctype) + trans_item = frappe.qb.DocType(f"{ref_doctype} Item") + item_ordered_qty = frappe._dict( - frappe.db.sql( - """ - select trans_item.item_code, sum(trans_item.stock_qty) as qty - from `tab{0} Item` trans_item, `tab{0}` trans - where trans.name = trans_item.parent - and trans_item.blanket_order=%s - and trans.docstatus=1 - and trans.status not in ('Closed', 'Stopped') - group by trans_item.item_code - """.format( - ref_doctype - ), - self.name, - ) + ( + frappe.qb.from_(trans_item) + .from_(trans) + .select(trans_item.item_code, Sum(trans_item.stock_qty).as_("qty")) + .where( + (trans.name == trans_item.parent) + & (trans_item.blanket_order == self.name) + & (trans.docstatus == 1) + & (trans.status.notin(["Stopped", "Closed"])) + ) + .groupby(trans_item.item_code) + ).run() ) for d in self.items: @@ -79,7 +82,43 @@ def make_order(source_name): "doctype": doctype + " Item", "field_map": {"rate": "blanket_order_rate", "parent": "blanket_order"}, "postprocess": update_item, + "condition": lambda item: (flt(item.qty) - flt(item.ordered_qty)) > 0, }, }, ) return target_doc + + +def validate_against_blanket_order(order_doc): + if order_doc.doctype in ("Sales Order", "Purchase Order"): + order_data = {} + + for item in order_doc.get("items"): + if item.against_blanket_order and item.blanket_order: + if item.blanket_order in order_data: + if item.item_code in order_data[item.blanket_order]: + order_data[item.blanket_order][item.item_code] += item.qty + else: + order_data[item.blanket_order][item.item_code] = item.qty + else: + order_data[item.blanket_order] = {item.item_code: item.qty} + + if order_data: + allowance = flt( + frappe.db.get_single_value( + "Selling Settings" if order_doc.doctype == "Sales Order" else "Buying Settings", + "over_order_allowance", + ) + ) + for bo_name, item_data in order_data.items(): + bo_doc = frappe.get_doc("Blanket Order", bo_name) + for item in bo_doc.get("items"): + if item.item_code in item_data: + remaining_qty = item.qty - item.ordered_qty + allowed_qty = remaining_qty + (remaining_qty * (allowance / 100)) + if allowed_qty < item_data[item.item_code]: + frappe.throw( + _("Item {0} cannot be ordered more than {1} against Blanket Order {2}.").format( + item.item_code, allowed_qty, bo_name + ) + ) diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py index 2f1f3ae0f52..58f3c950598 100644 --- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py @@ -63,6 +63,33 @@ class TestBlanketOrder(FrappeTestCase): po1.currency = get_company_currency(po1.company) self.assertEqual(po1.items[0].qty, (bo.items[0].qty - bo.items[0].ordered_qty)) + def test_over_order_allowance(self): + # Sales Order + bo = make_blanket_order(blanket_order_type="Selling", quantity=100) + + frappe.flags.args.doctype = "Sales Order" + so = make_order(bo.name) + so.currency = get_company_currency(so.company) + so.delivery_date = today() + so.items[0].qty = 110 + self.assertRaises(frappe.ValidationError, so.submit) + + frappe.db.set_single_value("Selling Settings", "over_order_allowance", 10) + so.submit() + + # Purchase Order + bo = make_blanket_order(blanket_order_type="Purchasing", quantity=100) + + frappe.flags.args.doctype = "Purchase Order" + po = make_order(bo.name) + po.currency = get_company_currency(po.company) + po.schedule_date = today() + po.items[0].qty = 110 + self.assertRaises(frappe.ValidationError, po.submit) + + frappe.db.set_single_value("Buying Settings", "over_order_allowance", 10) + po.submit() + def make_blanket_order(**args): args = frappe._dict(args) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 865b9585618..2f2a06f6fb1 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -22,6 +22,9 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( ) from erpnext.accounts.party import get_party_account from erpnext.controllers.selling_controller import SellingController +from erpnext.manufacturing.doctype.blanket_order.blanket_order import ( + validate_against_blanket_order, +) from erpnext.manufacturing.doctype.production_plan.production_plan import ( get_items_for_material_requests, ) @@ -53,6 +56,7 @@ class SalesOrder(SellingController): self.validate_warehouse() self.validate_drop_ship() self.validate_serial_no_based_delivery() + validate_against_blanket_order(self) validate_inter_company_party( self.doctype, self.customer, self.company, self.inter_company_order_reference ) diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index ce976547dcd..a51993ff7ff 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -30,12 +30,18 @@ "so_required", "dn_required", "sales_update_frequency", + "over_order_allowance", "allow_multiple_items", "allow_against_multiple_purchase_orders", "hide_tax_id", "allow_sales_order_creation_for_expired_quotation" ], "fields": [ + { + "fieldname": "customer_defaults_section", + "fieldtype": "Section Break", + "label": "Customer Defaults" + }, { "default": "Customer Name", "fieldname": "cust_master_name", @@ -44,13 +50,6 @@ "label": "Customer Naming By", "options": "Customer Name\nNaming Series\nAuto Name" }, - { - "fieldname": "campaign_naming_by", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Campaign Naming By", - "options": "Campaign Name\nNaming Series\nAuto Name" - }, { "fieldname": "customer_group", "fieldtype": "Link", @@ -58,6 +57,10 @@ "label": "Default Customer Group", "options": "Customer Group" }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { "fieldname": "territory", "fieldtype": "Link", @@ -66,11 +69,31 @@ "options": "Territory" }, { - "fieldname": "selling_price_list", - "fieldtype": "Link", + "fieldname": "crm_settings_section", + "fieldtype": "Section Break", + "label": "CRM Settings" + }, + { + "fieldname": "campaign_naming_by", + "fieldtype": "Select", "in_list_view": 1, - "label": "Default Price List", - "options": "Price List" + "label": "Campaign Naming By", + "options": "Campaign Name\nNaming Series\nAuto Name" + }, + { + "fieldname": "contract_naming_by", + "fieldtype": "Select", + "label": "Contract Naming By", + "options": "Party Name\nNaming Series" + }, + { + "fieldname": "default_valid_till", + "fieldtype": "Data", + "label": "Default Quotation Validity Days" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" }, { "default": "15", @@ -80,9 +103,65 @@ "label": "Close Opportunity After Days" }, { - "fieldname": "default_valid_till", - "fieldtype": "Data", - "label": "Default Quotation Validity Days" + "fieldname": "item_price_settings_section", + "fieldtype": "Section Break", + "label": "Item Price Settings" + }, + { + "fieldname": "selling_price_list", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Default Price List", + "options": "Price List" + }, + { + "default": "Stop", + "depends_on": "maintain_same_sales_rate", + "fieldname": "maintain_same_rate_action", + "fieldtype": "Select", + "label": "Action if Same Rate is Not Maintained Throughout Sales Cycle", + "mandatory_depends_on": "maintain_same_sales_rate", + "options": "Stop\nWarn" + }, + { + "depends_on": "eval: doc.maintain_same_sales_rate && doc.maintain_same_rate_action == 'Stop'", + "fieldname": "role_to_override_stop_action", + "fieldtype": "Link", + "label": "Role Allowed to Override Stop Action", + "options": "Role" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "maintain_same_sales_rate", + "fieldtype": "Check", + "label": "Maintain Same Rate Throughout Sales Cycle" + }, + { + "default": "0", + "fieldname": "editable_price_list_rate", + "fieldtype": "Check", + "label": "Allow User to Edit Price List Rate in Transactions" + }, + { + "default": "0", + "fieldname": "validate_selling_price", + "fieldtype": "Check", + "label": "Validate Selling Price for Item Against Purchase Rate or Valuation Rate" + }, + { + "default": "0", + "fieldname": "editable_bundle_item_rates", + "fieldtype": "Check", + "label": "Calculate Product Bundle Price based on Child Items' Rates" + }, + { + "fieldname": "sales_transactions_settings_section", + "fieldtype": "Section Break", + "label": "Transaction Settings" }, { "fieldname": "so_required", @@ -107,15 +186,10 @@ }, { "default": "0", - "fieldname": "maintain_same_sales_rate", - "fieldtype": "Check", - "label": "Maintain Same Rate Throughout Sales Cycle" - }, - { - "default": "0", - "fieldname": "editable_price_list_rate", - "fieldtype": "Check", - "label": "Allow User to Edit Price List Rate in Transactions" + "description": "Percentage you are allowed to order more against the Blanket Order Quantity. For example: If you have a Blanket Order of Quantity 100 units. and your Allowance is 10% then you are allowed to order 110 units.", + "fieldname": "over_order_allowance", + "fieldtype": "Float", + "label": "Over Order Allowance (%)" }, { "default": "0", @@ -129,83 +203,17 @@ "fieldtype": "Check", "label": "Allow Multiple Sales Orders Against a Customer's Purchase Order" }, - { - "default": "0", - "fieldname": "validate_selling_price", - "fieldtype": "Check", - "label": "Validate Selling Price for Item Against Purchase Rate or Valuation Rate" - }, { "default": "0", "fieldname": "hide_tax_id", "fieldtype": "Check", "label": "Hide Customer's Tax ID from Sales Transactions" }, - { - "default": "Stop", - "depends_on": "maintain_same_sales_rate", - "fieldname": "maintain_same_rate_action", - "fieldtype": "Select", - "label": "Action if Same Rate is Not Maintained Throughout Sales Cycle", - "mandatory_depends_on": "maintain_same_sales_rate", - "options": "Stop\nWarn" - }, - { - "depends_on": "eval: doc.maintain_same_sales_rate && doc.maintain_same_rate_action == 'Stop'", - "fieldname": "role_to_override_stop_action", - "fieldtype": "Link", - "label": "Role Allowed to Override Stop Action", - "options": "Role" - }, - { - "fieldname": "column_break_15", - "fieldtype": "Column Break" - }, { "default": "0", - "fieldname": "editable_bundle_item_rates", + "fieldname": "allow_sales_order_creation_for_expired_quotation", "fieldtype": "Check", - "label": "Calculate Product Bundle Price based on Child Items' Rates" - }, - { - "fieldname": "customer_defaults_section", - "fieldtype": "Section Break", - "label": "Customer Defaults" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "crm_settings_section", - "fieldtype": "Section Break", - "label": "CRM Settings" - }, - { - "fieldname": "column_break_9", - "fieldtype": "Column Break" - }, - { - "fieldname": "item_price_settings_section", - "fieldtype": "Section Break", - "label": "Item Price Settings" - }, - { - "fieldname": "sales_transactions_settings_section", - "fieldtype": "Section Break", - "label": "Transaction Settings" - }, - { - "fieldname": "contract_naming_by", - "fieldtype": "Select", - "label": "Contract Naming By", - "options": "Party Name\nNaming Series" - }, - { - "default": "0", - "fieldname": "allow_sales_order_creation_for_expired_quotation", - "fieldtype": "Check", - "label": "Allow Sales Order Creation For Expired Quotation" + "label": "Allow Sales Order Creation For Expired Quotation" } ], "icon": "fa fa-cog", @@ -213,7 +221,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-02-04 12:37:53.380857", + "modified": "2023-03-22 13:09:38.513317", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", From f17b2de420c9c855b454fb301b2e8cf096ae7d01 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 22 Mar 2023 09:29:45 +0000 Subject: [PATCH 31/76] chore(release): Bumped to Version 13.49.6 ## [13.49.6](https://github.com/frappe/erpnext/compare/v13.49.5...v13.49.6) (2023-03-22) ### Bug Fixes * `Blanket Order` (backport [#34279](https://github.com/frappe/erpnext/issues/34279)) (backport [#34548](https://github.com/frappe/erpnext/issues/34548)) ([#34553](https://github.com/frappe/erpnext/issues/34553)) ([d5efeec](https://github.com/frappe/erpnext/commit/d5efeec0a4ca5df59ed5e6525eab1638f2aa811e)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6faf292ad41..05b77fd5f34 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.5" +__version__ = "13.49.6" def get_default_company(user=None): From 5f5fa843acd95a1752e2997feae6088e102ef11c Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Thu, 23 Mar 2023 19:44:13 +0530 Subject: [PATCH 32/76] fix: recalculate WDV rate after asset repair [v13] (#34567) fix: recalculate wdv rate after asset repair (cherry picked from commit c8bde399e5a18aec34daa002cc708087adcd8325) --- erpnext/assets/doctype/asset/asset.py | 30 ++++++++++--------- .../doctype/asset_repair/asset_repair.py | 24 --------------- 2 files changed, 16 insertions(+), 38 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index e46cdb9fc64..662411e0510 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -380,19 +380,12 @@ class Asset(AccountsController): value_after_depreciation -= flt(depreciation_amount, self.precision("gross_purchase_amount")) # Adjust depreciation amount in the last period based on the expected value after useful life - if ( - finance_book.expected_value_after_useful_life - and ( - ( - n == cint(number_of_pending_depreciations) - 1 - and value_after_depreciation != finance_book.expected_value_after_useful_life - ) - or value_after_depreciation < finance_book.expected_value_after_useful_life - ) - and ( - not self.flags.increase_in_asset_value_due_to_repair - or not finance_book.depreciation_method in ("Written Down Value", "Double Declining Balance") + if finance_book.expected_value_after_useful_life and ( + ( + n == cint(number_of_pending_depreciations) - 1 + and value_after_depreciation != finance_book.expected_value_after_useful_life ) + or value_after_depreciation < finance_book.expected_value_after_useful_life ): depreciation_amount += ( value_after_depreciation - finance_book.expected_value_after_useful_life @@ -903,10 +896,19 @@ class Asset(AccountsController): return 200.0 / args.get("total_number_of_depreciations") if args.get("depreciation_method") == "Written Down Value": - if args.get("rate_of_depreciation") and on_validate: + if ( + args.get("rate_of_depreciation") + and on_validate + and not self.flags.increase_in_asset_value_due_to_repair + ): return args.get("rate_of_depreciation") - value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount) + if self.flags.increase_in_asset_value_due_to_repair: + value = flt(args.get("expected_value_after_useful_life")) / flt( + args.get("value_after_depreciation") + ) + else: + value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount) depreciation_rate = math.pow(value, 1.0 / flt(args.get("total_number_of_depreciations"), 2)) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index edcbf3e2fd0..19df1b641d3 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -58,8 +58,6 @@ class AssetRepair(AccountsController): self.asset_doc.flags.ignore_validate_update_after_submit = True self.asset_doc.prepare_depreciation_data() - if self.asset_doc.calculate_depreciation: - self.update_asset_expected_value_after_useful_life() self.asset_doc.save() def before_cancel(self): @@ -84,8 +82,6 @@ class AssetRepair(AccountsController): self.asset_doc.flags.ignore_validate_update_after_submit = True self.asset_doc.prepare_depreciation_data() - if self.asset_doc.calculate_depreciation: - self.update_asset_expected_value_after_useful_life() self.asset_doc.save() def after_delete(self): @@ -106,26 +102,6 @@ class AssetRepair(AccountsController): title=_("Missing Warehouse"), ) - def update_asset_expected_value_after_useful_life(self): - for row in self.asset_doc.get("finance_books"): - if row.depreciation_method in ("Written Down Value", "Double Declining Balance"): - accumulated_depreciation_after_full_schedule = [ - d.accumulated_depreciation_amount - for d in self.asset_doc.get("schedules") - if cint(d.finance_book_id) == row.idx - ] - - accumulated_depreciation_after_full_schedule = max( - accumulated_depreciation_after_full_schedule - ) - - asset_value_after_full_schedule = flt( - flt(row.value_after_depreciation) - flt(accumulated_depreciation_after_full_schedule), - row.precision("expected_value_after_useful_life"), - ) - - row.expected_value_after_useful_life = asset_value_after_full_schedule - def increase_asset_value(self): total_value_of_stock_consumed = self.get_total_value_of_stock_consumed() From 544e37ca5cadb870e48bce8e07300806e5d85945 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 23 Mar 2023 15:43:53 +0000 Subject: [PATCH 33/76] chore(release): Bumped to Version 13.49.7 ## [13.49.7](https://github.com/frappe/erpnext/compare/v13.49.6...v13.49.7) (2023-03-23) ### Bug Fixes * recalculate WDV rate after asset repair [v13] ([#34567](https://github.com/frappe/erpnext/issues/34567)) ([5f5fa84](https://github.com/frappe/erpnext/commit/5f5fa843acd95a1752e2997feae6088e102ef11c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 05b77fd5f34..f41a33fe44b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.6" +__version__ = "13.49.7" def get_default_company(user=None): From 78bd698f9e30fd7dec1978bb80241fb0852c8b95 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 22 Mar 2023 16:52:07 +0530 Subject: [PATCH 34/76] fix: valuation rate issue while making stock entry from PO (cherry picked from commit 3574d490dbf2b3e71722e60a73a50d8af371a2d1) --- erpnext/buying/doctype/purchase_order/purchase_order.py | 2 +- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 9a2495ed484..a28b65bf907 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -644,7 +644,7 @@ def make_rm_stock_entry(purchase_order, rm_items): } stock_entry.add_to_stock_entry_detail(items_dict) - stock_entry.set_missing_values() + stock_entry.set_missing_values(raise_error_if_no_rate=False) return stock_entry.as_dict() else: frappe.throw(_("No Items selected for transfer")) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 4711d039c36..4d96e544676 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2229,11 +2229,11 @@ class StockEntry(StockController): return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos))) - def set_missing_values(self): + def set_missing_values(self, raise_error_if_no_rate=True): "Updates rate and availability of all the items of mapped doc." self.set_transfer_qty() self.set_actual_qty() - self.calculate_rate_and_amount() + self.calculate_rate_and_amount(raise_error_if_no_rate=raise_error_if_no_rate) @frappe.whitelist() From 8c4f45307e62ec5d9f84b78973304ec3a085a1b6 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 25 Mar 2023 14:50:08 +0000 Subject: [PATCH 35/76] chore(release): Bumped to Version 13.49.8 ## [13.49.8](https://github.com/frappe/erpnext/compare/v13.49.7...v13.49.8) (2023-03-25) ### Bug Fixes * valuation rate issue while making stock entry from PO ([78bd698](https://github.com/frappe/erpnext/commit/78bd698f9e30fd7dec1978bb80241fb0852c8b95)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f41a33fe44b..d797580c0cf 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.7" +__version__ = "13.49.8" def get_default_company(user=None): From 0da6237d222e162820e42eaf33878afb842b0e5f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 28 Mar 2023 18:17:57 +0000 Subject: [PATCH 36/76] chore(release): Bumped to Version 13.49.9 ## [13.49.9](https://github.com/frappe/erpnext/compare/v13.49.8...v13.49.9) (2023-03-28) ### Bug Fixes * `Blanket Order` (backport [#34279](https://github.com/frappe/erpnext/issues/34279)) ([#34548](https://github.com/frappe/erpnext/issues/34548)) ([8ddbac5](https://github.com/frappe/erpnext/commit/8ddbac515882469ed1ec19124e39f656c6581311)) * **client:** Amount calculation for 0 qty debit notes ([#34455](https://github.com/frappe/erpnext/issues/34455)) ([19dda80](https://github.com/frappe/erpnext/commit/19dda807d1a63034dfc217a5adfee7af6580cf18)) * exchange gain/loss GL's should be removed if advance is cancelled ([#34529](https://github.com/frappe/erpnext/issues/34529)) ([00518eb](https://github.com/frappe/erpnext/commit/00518eb384df35d331633c3860931152fc666734)) * german translations ([#34312](https://github.com/frappe/erpnext/issues/34312)) ([661030a](https://github.com/frappe/erpnext/commit/661030aba1a6847b37ac2a7d8ff73f092f697fc1)) * incorrect `Opening Value` in `Stock Balance` report (backport [#34461](https://github.com/frappe/erpnext/issues/34461)) ([#34622](https://github.com/frappe/erpnext/issues/34622)) ([e53a96a](https://github.com/frappe/erpnext/commit/e53a96ae1d87229fcd6fcaf5ccfb6040e1a14095)) * incorrect depr schedules after asset repair [v13] ([#34520](https://github.com/frappe/erpnext/issues/34520)) ([ae88ba5](https://github.com/frappe/erpnext/commit/ae88ba5d18dfbf6ff68518be5a66f445d7799f15)) * Overallocation of 'qty' from Cr Notes to Parent Invoice ([d2a1acc](https://github.com/frappe/erpnext/commit/d2a1acc2e2662eb7e96df7058f521a00ec2bd654)) * Party Name in SOA print when viewed from Customer/Supplier master ([#34597](https://github.com/frappe/erpnext/issues/34597)) ([4bdea43](https://github.com/frappe/erpnext/commit/4bdea436e35ee4c2ea7f8a7aae33e3ca49d4e303)) * Percentage billing in Sales Order ([#34606](https://github.com/frappe/erpnext/issues/34606)) ([3aab6e6](https://github.com/frappe/erpnext/commit/3aab6e6fa8c241770c78763d0b0db1ed4e1c9f33)) * recalculate WDV rate after asset repair [v13] ([#34567](https://github.com/frappe/erpnext/issues/34567)) ([c8bde39](https://github.com/frappe/erpnext/commit/c8bde399e5a18aec34daa002cc708087adcd8325)) * Search field not working for customer, supplier ([#32693](https://github.com/frappe/erpnext/issues/32693)) ([dbe289e](https://github.com/frappe/erpnext/commit/dbe289e734fd13709bd6b1c8963d604b070e116c)) * unset address and contact on trash (backport [#34495](https://github.com/frappe/erpnext/issues/34495)) ([#34561](https://github.com/frappe/erpnext/issues/34561)) ([7f83d15](https://github.com/frappe/erpnext/commit/7f83d15bda8c8df80ff3f3648af46c6e45e48b01)) * valuation rate issue while making stock entry from PO ([3574d49](https://github.com/frappe/erpnext/commit/3574d490dbf2b3e71722e60a73a50d8af371a2d1)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d797580c0cf..b5acc8028db 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.8" +__version__ = "13.49.9" def get_default_company(user=None): From 4cf66f058565f73100835d3f9d69cc4e4b3b0f67 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 30 Mar 2023 11:47:32 +0530 Subject: [PATCH 37/76] fix: serial no with zero quantity issue in stock reco (cherry picked from commit 17131e5a02ac51ea2a605a180571b1f31bf02110) (cherry picked from commit 46638b19dbf051d580f9bab93e7d20d6851b6b23) --- .../stock_reconciliation/stock_reconciliation.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index b8ba5347510..0248cdfbcf0 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -3,7 +3,7 @@ import frappe -from frappe import _, msgprint +from frappe import _, bold, msgprint from frappe.utils import cint, cstr, flt import erpnext @@ -88,7 +88,7 @@ class StockReconciliation(StockController): if item_dict.get("serial_nos"): item.current_serial_no = item_dict.get("serial_nos") - if self.purpose == "Stock Reconciliation" and not item.serial_no: + if self.purpose == "Stock Reconciliation" and not item.serial_no and item.qty: item.serial_no = item.current_serial_no item.current_qty = item_dict.get("qty") @@ -139,6 +139,14 @@ class StockReconciliation(StockController): self.validate_item(row.item_code, row) + if row.serial_no and not row.qty: + self.validation_messages.append( + _get_msg( + row_num, + f"Quantity should not be zero for the {bold(row.item_code)} since serial nos are specified", + ) + ) + # validate warehouse if not frappe.db.get_value("Warehouse", row.warehouse): self.validation_messages.append(_get_msg(row_num, _("Warehouse not found in the system"))) From 278f38f2aa99a80d4b916ace10254e217e933dfe Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 30 Mar 2023 08:48:15 +0000 Subject: [PATCH 38/76] chore(release): Bumped to Version 13.49.10 ## [13.49.10](https://github.com/frappe/erpnext/compare/v13.49.9...v13.49.10) (2023-03-30) ### Bug Fixes * serial no with zero quantity issue in stock reco ([4cf66f0](https://github.com/frappe/erpnext/commit/4cf66f058565f73100835d3f9d69cc4e4b3b0f67)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b5acc8028db..c8457084c41 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.9" +__version__ = "13.49.10" def get_default_company(user=None): From 2c40be23378d3d948d7ac0ac3b8651a7433a9d31 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 5 Apr 2023 13:48:11 +0530 Subject: [PATCH 39/76] chore: release v13 (#34732) --- .../accounts_settings/accounts_settings.json | 10 +- .../doctype/payment_entry/payment_entry.js | 2 - .../doctype/payment_entry/payment_entry.py | 242 +++++- .../payment_entry/test_payment_entry.py | 221 ++++- .../payment_entry_deduction.json | 29 +- .../purchase_invoice/purchase_invoice.js | 8 +- .../doctype/sales_invoice/sales_invoice.js | 9 +- .../asset_depreciation_ledger.py | 1 + erpnext/assets/doctype/asset/asset.js | 3 + erpnext/assets/doctype/asset/asset.json | 7 +- erpnext/assets/doctype/asset/asset.py | 188 ++++- erpnext/assets/doctype/asset/depreciation.py | 12 +- erpnext/assets/doctype/asset/test_asset.py | 12 +- .../asset_maintenance/asset_maintenance.py | 2 + .../asset_maintenance_task.json | 770 +++--------------- .../asset_value_adjustment.js | 2 +- .../doctype/purchase_order/purchase_order.js | 8 +- .../crm/report/lead_details/lead_details.py | 2 +- .../lost_opportunity/lost_opportunity.py | 2 +- .../doctype/website_item/test_website_item.py | 8 +- erpnext/hooks.py | 4 + .../test_employee_transfer.py | 11 + .../leave_application/leave_application.py | 3 + .../test_leave_application.py | 36 +- erpnext/hr/utils.py | 43 +- erpnext/manufacturing/doctype/bom/bom.py | 12 +- .../doctype/bom_update_log/bom_update_log.py | 2 +- .../production_plan_item_reference.json | 5 +- .../public/js/controllers/taxes_and_totals.js | 2 +- erpnext/public/js/controllers/transaction.js | 56 +- erpnext/public/js/website_utils.js | 15 - erpnext/regional/india/utils.py | 41 +- .../setup/doctype/item_group/item_group.py | 9 +- erpnext/stock/doctype/batch/batch.py | 8 +- .../stock/report/stock_ledger/stock_ledger.py | 3 + erpnext/templates/utils.py | 9 +- erpnext/www/shop-by-category/index.py | 12 +- 37 files changed, 982 insertions(+), 827 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index ea427aa7d80..07b4318a9dc 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -26,6 +26,7 @@ "determine_address_tax_category_from", "column_break_19", "add_taxes_from_item_tax_template", + "book_tax_discount_loss", "period_closing_settings_section", "acc_frozen_upto", "frozen_accounts_modifier", @@ -284,6 +285,13 @@ "fieldname": "allow_multi_currency_invoices_against_single_party_account", "fieldtype": "Check", "label": "Allow multi-currency invoices against single party account" + }, + { + "default": "0", + "description": "Split Early Payment Discount Loss into Income and Tax Loss", + "fieldname": "book_tax_discount_loss", + "fieldtype": "Check", + "label": "Book Tax Loss on Early Payment Discount" } ], "icon": "icon-cog", @@ -291,7 +299,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-07-11 13:37:50.605141", + "modified": "2023-03-28 09:50:20.375233", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 6be0920d2a8..2e5674874cc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -256,8 +256,6 @@ frappe.ui.form.on('Payment Entry', { frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"], party_account_currency, "references"); - frm.set_currency_labels(["amount"], company_currency, "deductions"); - cur_frm.set_df_property("source_exchange_rate", "description", ("1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency)); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f0d7d57fc64..44b8dbe5326 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -440,7 +440,7 @@ class PaymentEntry(AccountsController): for ref in self.get("references"): if ref.payment_term and ref.reference_name: - key = (ref.payment_term, ref.reference_name) + key = (ref.payment_term, ref.reference_name, ref.reference_doctype) invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map[key] += ref.allocated_amount @@ -448,20 +448,37 @@ class PaymentEntry(AccountsController): payment_schedule = frappe.get_all( "Payment Schedule", filters={"parent": ref.reference_name}, - fields=["paid_amount", "payment_amount", "payment_term", "discount", "outstanding"], + fields=[ + "paid_amount", + "payment_amount", + "payment_term", + "discount", + "outstanding", + "discount_type", + ], ) for term in payment_schedule: - invoice_key = (term.payment_term, ref.reference_name) + invoice_key = (term.payment_term, ref.reference_name, ref.reference_doctype) invoice_paid_amount_map.setdefault(invoice_key, {}) invoice_paid_amount_map[invoice_key]["outstanding"] = term.outstanding - invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * ( - term.discount / 100 - ) + if not (term.discount_type and term.discount): + continue + + if term.discount_type == "Percentage": + invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * ( + term.discount / 100 + ) + else: + invoice_paid_amount_map[invoice_key]["discounted_amt"] = term.discount for idx, (key, allocated_amount) in enumerate(iteritems(invoice_payment_amount_map), 1): if not invoice_paid_amount_map.get(key): frappe.throw(_("Payment term {0} not used in {1}").format(key[0], key[1])) + allocated_amount = self.get_allocated_amount_in_transaction_currency( + allocated_amount, key[2], key[1] + ) + outstanding = flt(invoice_paid_amount_map.get(key, {}).get("outstanding")) discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get("discounted_amt")) @@ -496,6 +513,33 @@ class PaymentEntry(AccountsController): (allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]), ) + def get_allocated_amount_in_transaction_currency( + self, allocated_amount, reference_doctype, reference_docname + ): + """ + Payment Entry could be in base currency while reference's payment schedule + is always in transaction currency. + E.g. + * SI with base=INR and currency=USD + * SI with payment schedule in USD + * PE in INR (accounting done in base currency) + """ + ref_currency, ref_exchange_rate = frappe.db.get_value( + reference_doctype, reference_docname, ["currency", "conversion_rate"] + ) + is_single_currency = self.paid_from_account_currency == self.paid_to_account_currency + # PE in different currency + reference_is_multi_currency = self.paid_from_account_currency != ref_currency + + if not (is_single_currency and reference_is_multi_currency): + return allocated_amount + + allocated_amount = flt( + allocated_amount / ref_exchange_rate, self.precision("total_allocated_amount") + ) + + return allocated_amount + def set_status(self): if self.docstatus == 2: self.status = "Cancelled" @@ -1801,7 +1845,14 @@ def get_bill_no_and_update_amounts( @frappe.whitelist() -def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): +def get_payment_entry( + dt, + dn, + party_amount=None, + bank_account=None, + bank_amount=None, + reference_date=None, +): reference_doc = None doc = frappe.get_doc(dt, dn) if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: @@ -1822,8 +1873,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc ) - paid_amount, received_amount, discount_amount = apply_early_payment_discount( - paid_amount, received_amount, doc + reference_date = getdate(reference_date) + paid_amount, received_amount, discount_amount, valid_discounts = apply_early_payment_discount( + paid_amount, received_amount, doc, party_account_currency, reference_date ) pe = frappe.new_doc("Payment Entry") @@ -1831,6 +1883,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.company = doc.company pe.cost_center = doc.get("cost_center") pe.posting_date = nowdate() + pe.reference_date = reference_date pe.mode_of_payment = doc.get("mode_of_payment") pe.party_type = party_type pe.party = doc.get(scrub(party_type)) @@ -1871,7 +1924,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= ): for reference in get_reference_as_per_payment_terms( - doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount + doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount, party_account_currency ): pe.append("references", reference) else: @@ -1922,16 +1975,17 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= reference_doc = doc pe.set_exchange_rate(ref_doc=reference_doc) pe.set_amounts() + if discount_amount: - pe.set_gain_or_loss( - account_details={ - "account": frappe.get_cached_value("Company", pe.company, "default_discount_account"), - "cost_center": pe.cost_center - or frappe.get_cached_value("Company", pe.company, "cost_center"), - "amount": discount_amount * (-1 if payment_type == "Pay" else 1), - } + base_total_discount_loss = 0 + if frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss"): + base_total_discount_loss = split_early_payment_discount_loss(pe, doc, valid_discounts) + + set_pending_discount_loss( + pe, doc, discount_amount, base_total_discount_loss, party_account_currency ) - pe.set_difference_amount() + + pe.set_difference_amount() return pe @@ -2067,20 +2121,30 @@ def set_paid_amount_and_received_amount( return paid_amount, received_amount -def apply_early_payment_discount(paid_amount, received_amount, doc): +def apply_early_payment_discount( + paid_amount, received_amount, doc, party_account_currency, reference_date +): total_discount = 0 + valid_discounts = [] eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"] has_payment_schedule = hasattr(doc, "payment_schedule") and doc.payment_schedule if doc.doctype in eligible_for_payments and has_payment_schedule: + # Non eligible documents may not have `company_currency` field + is_multi_currency = party_account_currency != doc.company_currency + for term in doc.payment_schedule: - if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date: + if not term.discounted_amount and term.discount and reference_date <= term.discount_date: + if term.discount_type == "Percentage": - discount_amount = flt(doc.get("grand_total")) * (term.discount / 100) + grand_total = doc.get("grand_total") if is_multi_currency else doc.get("base_grand_total") + discount_amount = flt(grand_total) * (term.discount / 100) else: discount_amount = term.discount - discount_amount_in_foreign_currency = discount_amount * doc.get("conversion_rate", 1) + # if accounting is done in the same currency, paid_amount = received_amount + conversion_rate = doc.get("conversion_rate", 1) if is_multi_currency else 1 + discount_amount_in_foreign_currency = discount_amount * conversion_rate if doc.doctype == "Sales Invoice": paid_amount -= discount_amount @@ -2089,23 +2153,151 @@ def apply_early_payment_discount(paid_amount, received_amount, doc): received_amount -= discount_amount paid_amount -= discount_amount_in_foreign_currency + valid_discounts.append({"type": term.discount_type, "discount": term.discount}) total_discount += discount_amount if total_discount: - money = frappe.utils.fmt_money(total_discount, currency=doc.get("currency")) + currency = doc.get("currency") if is_multi_currency else doc.company_currency + money = frappe.utils.fmt_money(total_discount, currency=currency) frappe.msgprint(_("Discount of {} applied as per Payment Term").format(money), alert=1) - return paid_amount, received_amount, total_discount + return paid_amount, received_amount, total_discount, valid_discounts + + +def set_pending_discount_loss( + pe, doc, discount_amount, base_total_discount_loss, party_account_currency +): + # If multi-currency, get base discount amount to adjust with base currency deductions/losses + if party_account_currency != doc.company_currency: + discount_amount = discount_amount * doc.get("conversion_rate", 1) + + # Avoid considering miniscule losses + discount_amount = flt(discount_amount - base_total_discount_loss, doc.precision("grand_total")) + + # Set base discount amount (discount loss/pending rounding loss) in deductions + if discount_amount > 0.0: + positive_negative = -1 if pe.payment_type == "Pay" else 1 + + # If tax loss booking is enabled, pending loss will be rounding loss. + # Otherwise it will be the total discount loss. + book_tax_loss = frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss") + account_type = "round_off_account" if book_tax_loss else "default_discount_account" + + pe.set_gain_or_loss( + account_details={ + "account": frappe.get_cached_value("Company", pe.company, account_type), + "cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), + "amount": discount_amount * positive_negative, + } + ) + + +def split_early_payment_discount_loss(pe, doc, valid_discounts) -> float: + """Split early payment discount into Income Loss & Tax Loss.""" + total_discount_percent = get_total_discount_percent(doc, valid_discounts) + + if not total_discount_percent: + return 0.0 + + base_loss_on_income = add_income_discount_loss(pe, doc, total_discount_percent) + base_loss_on_taxes = add_tax_discount_loss(pe, doc, total_discount_percent) + + # Round off total loss rather than individual losses to reduce rounding error + return flt(base_loss_on_income + base_loss_on_taxes, doc.precision("grand_total")) + + +def get_total_discount_percent(doc, valid_discounts) -> float: + """Get total percentage and amount discount applied as a percentage.""" + total_discount_percent = ( + sum( + discount.get("discount") for discount in valid_discounts if discount.get("type") == "Percentage" + ) + or 0.0 + ) + + # Operate in percentages only as it makes the income & tax split easier + total_discount_amount = ( + sum(discount.get("discount") for discount in valid_discounts if discount.get("type") == "Amount") + or 0.0 + ) + + if total_discount_amount: + discount_percentage = (total_discount_amount / doc.get("grand_total")) * 100 + total_discount_percent += discount_percentage + return total_discount_percent + + return total_discount_percent + + +def add_income_discount_loss(pe, doc, total_discount_percent) -> float: + """Add loss on income discount in base currency.""" + precision = doc.precision("total") + base_loss_on_income = doc.get("base_total") * (total_discount_percent / 100) + + pe.append( + "deductions", + { + "account": frappe.get_cached_value("Company", pe.company, "default_discount_account"), + "cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), + "amount": flt(base_loss_on_income, precision), + }, + ) + + return base_loss_on_income # Return loss without rounding + + +def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float: + """Add loss on tax discount in base currency.""" + tax_discount_loss = {} + base_total_tax_loss = 0 + precision = doc.precision("tax_amount_after_discount_amount", "taxes") + + # The same account head could be used more than once + for tax in doc.get("taxes", []): + base_tax_loss = tax.get("base_tax_amount_after_discount_amount") * ( + total_discount_percentage / 100 + ) + + account = tax.get("account_head") + if not tax_discount_loss.get(account): + tax_discount_loss[account] = base_tax_loss + else: + tax_discount_loss[account] += base_tax_loss + + for account, loss in tax_discount_loss.items(): + base_total_tax_loss += loss + if loss == 0.0: + continue + + pe.append( + "deductions", + { + "account": account, + "cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), + "amount": flt(loss, precision), + }, + ) + + return base_total_tax_loss # Return loss without rounding def get_reference_as_per_payment_terms( - payment_schedule, dt, dn, doc, grand_total, outstanding_amount + payment_schedule, dt, dn, doc, grand_total, outstanding_amount, party_account_currency ): references = [] + is_multi_currency_acc = (doc.currency != doc.company_currency) and ( + party_account_currency != doc.company_currency + ) + for payment_term in payment_schedule: payment_term_outstanding = flt( payment_term.payment_amount - payment_term.paid_amount, payment_term.precision("payment_amount") ) + if not is_multi_currency_acc: + # If accounting is done in company currency for multi-currency transaction + payment_term_outstanding = flt( + payment_term_outstanding * doc.get("conversion_rate"), payment_term.precision("payment_amount") + ) if payment_term_outstanding: references.append( diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 004c84c0221..740f62a360a 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -5,6 +5,7 @@ import unittest import frappe from frappe import qb +from frappe.tests.utils import change_settings from frappe.utils import flt, nowdate from erpnext.accounts.doctype.payment_entry.payment_entry import ( @@ -252,10 +253,25 @@ class TestPaymentEntry(unittest.TestCase): }, ) si.save() - si.submit() + frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 1) + pe_with_tax_loss = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC") + + self.assertEqual(pe_with_tax_loss.references[0].payment_term, "30 Credit Days with 10% Discount") + self.assertEqual(pe_with_tax_loss.references[0].allocated_amount, 236.0) + self.assertEqual(pe_with_tax_loss.paid_amount, 212.4) + self.assertEqual(pe_with_tax_loss.deductions[0].amount, 20.0) # Loss on Income + self.assertEqual(pe_with_tax_loss.deductions[1].amount, 3.6) # Loss on Tax + self.assertEqual(pe_with_tax_loss.deductions[1].account, "_Test Account Service Tax - _TC") + + frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 0) pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC") + + self.assertEqual(pe.references[0].allocated_amount, 236.0) + self.assertEqual(pe.paid_amount, 212.4) + self.assertEqual(pe.deductions[0].amount, 23.6) + pe.submit() si.load_from_db() @@ -265,6 +281,190 @@ class TestPaymentEntry(unittest.TestCase): self.assertEqual(si.payment_schedule[0].outstanding, 0) self.assertEqual(si.payment_schedule[0].discounted_amount, 23.6) + def test_payment_entry_against_payment_terms_with_discount_amount(self): + si = create_sales_invoice(do_not_save=1, qty=1, rate=200) + + si.payment_terms_template = "Test Discount Amount Template" + create_payment_terms_template_with_discount( + name="30 Credit Days with Rs.50 Discount", + discount_type="Amount", + discount=50, + template_name="Test Discount Amount Template", + ) + frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC") + + si.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 18, + }, + ) + si.save() + si.submit() + + # Set reference date past discount cut off date + pe_1 = get_payment_entry( + "Sales Invoice", + si.name, + bank_account="_Test Cash - _TC", + reference_date=frappe.utils.add_days(si.posting_date, 2), + ) + self.assertEqual(pe_1.paid_amount, 236.0) # discount not applied + + # Test if tax loss is booked on enabling configuration + frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 1) + pe_with_tax_loss = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC") + self.assertEqual(pe_with_tax_loss.deductions[0].amount, 42.37) # Loss on Income + self.assertEqual(pe_with_tax_loss.deductions[1].amount, 7.63) # Loss on Tax + self.assertEqual(pe_with_tax_loss.deductions[1].account, "_Test Account Service Tax - _TC") + + frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 0) + pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC") + self.assertEqual(pe.references[0].allocated_amount, 236.0) + self.assertEqual(pe.paid_amount, 186) + self.assertEqual(pe.deductions[0].amount, 50.0) + + pe.submit() + si.load_from_db() + + self.assertEqual(si.payment_schedule[0].payment_amount, 236.0) + self.assertEqual(si.payment_schedule[0].paid_amount, 186) + self.assertEqual(si.payment_schedule[0].outstanding, 0) + self.assertEqual(si.payment_schedule[0].discounted_amount, 50) + + @change_settings( + "Accounts Settings", + { + "allow_multi_currency_invoices_against_single_party_account": 1, + "book_tax_discount_loss": 1, + }, + ) + def test_payment_entry_multicurrency_si_with_base_currency_accounting_early_payment_discount( + self, + ): + """ + 1. Multi-currency SI with single currency accounting (company currency) + 2. PE with early payment discount + 3. Test if Paid Amount is calculated in company currency + 4. Test if deductions are calculated in company currency + + SI is in USD to document agreed amounts that are in USD, but the accounting is in base currency. + """ + si = create_sales_invoice( + customer="_Test Customer", + currency="USD", + conversion_rate=50, + do_not_save=1, + ) + create_payment_terms_template_with_discount() + si.payment_terms_template = "Test Discount Template" + + frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC") + si.save() + si.submit() + + pe = get_payment_entry( + "Sales Invoice", + si.name, + bank_account="_Test Bank - _TC", + ) + pe.reference_no = si.name + pe.reference_date = nowdate() + + # Early payment discount loss on income + self.assertEqual(pe.paid_amount, 4500.0) # Amount in company currency + self.assertEqual(pe.received_amount, 4500.0) + self.assertEqual(pe.deductions[0].amount, 500.0) + self.assertEqual(pe.deductions[0].account, "Write Off - _TC") + self.assertEqual(pe.difference_amount, 0.0) + + pe.insert() + pe.submit() + + expected_gle = dict( + (d[0], d) + for d in [ + ["Debtors - _TC", 0, 5000, si.name], + ["_Test Bank - _TC", 4500, 0, None], + ["Write Off - _TC", 500.0, 0, None], + ] + ) + + self.validate_gl_entries(pe.name, expected_gle) + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + + def test_payment_entry_multicurrency_accounting_si_with_early_payment_discount(self): + """ + 1. Multi-currency SI with multi-currency accounting + 2. PE with early payment discount and also exchange loss + 3. Test if Paid Amount is calculated in transaction currency + 4. Test if deductions are calculated in base/company currency + 5. Test if exchange loss is reflected in difference + """ + si = create_sales_invoice( + customer="_Test Customer USD", + debit_to="_Test Receivable USD - _TC", + currency="USD", + conversion_rate=50, + do_not_save=1, + ) + create_payment_terms_template_with_discount() + si.payment_terms_template = "Test Discount Template" + + frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC") + si.save() + si.submit() + + pe = get_payment_entry( + "Sales Invoice", si.name, bank_account="_Test Bank - _TC", bank_amount=4700 + ) + pe.reference_no = si.name + pe.reference_date = nowdate() + + # Early payment discount loss on income + self.assertEqual(pe.paid_amount, 90.0) + self.assertEqual(pe.received_amount, 4200.0) # 5000 - 500 (discount) - 300 (exchange loss) + self.assertEqual(pe.deductions[0].amount, 500.0) + self.assertEqual(pe.deductions[0].account, "Write Off - _TC") + + # Exchange loss + self.assertEqual(pe.difference_amount, 300.0) + + pe.append( + "deductions", + { + "account": "_Test Exchange Gain/Loss - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 300.0, + }, + ) + + pe.insert() + pe.submit() + + self.assertEqual(pe.difference_amount, 0.0) + + expected_gle = dict( + (d[0], d) + for d in [ + ["_Test Receivable USD - _TC", 0, 5000, si.name], + ["_Test Bank - _TC", 4200, 0, None], + ["Write Off - _TC", 500.0, 0, None], + ["_Test Exchange Gain/Loss - _TC", 300.0, 0, None], + ] + ) + + self.validate_gl_entries(pe.name, expected_gle) + + outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) + self.assertEqual(outstanding_amount, 0) + def test_payment_against_purchase_invoice_to_check_status(self): pi = make_purchase_invoice( supplier="_Test Supplier USD", @@ -856,24 +1056,27 @@ def create_payment_terms_template(): ).insert() -def create_payment_terms_template_with_discount(): +def create_payment_terms_template_with_discount( + name=None, discount_type=None, discount=None, template_name=None +): + create_payment_term(name or "30 Credit Days with 10% Discount") + template_name = template_name or "Test Discount Template" - create_payment_term("30 Credit Days with 10% Discount") - - if not frappe.db.exists("Payment Terms Template", "Test Discount Template"): - payment_term_template = frappe.get_doc( + if not frappe.db.exists("Payment Terms Template", template_name): + frappe.get_doc( { "doctype": "Payment Terms Template", - "template_name": "Test Discount Template", + "template_name": template_name, "allocate_payment_based_on_payment_terms": 1, "terms": [ { "doctype": "Payment Terms Template Detail", - "payment_term": "30 Credit Days with 10% Discount", + "payment_term": name or "30 Credit Days with 10% Discount", "invoice_portion": 100, "credit_days_based_on": "Day(s) after invoice date", "credit_days": 2, - "discount": 10, + "discount_type": discount_type or "Percentage", + "discount": discount or 10, "discount_validity_based_on": "Day(s) after invoice date", "discount_validity": 1, } diff --git a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json index 61a1462dd7a..1c31829f0ea 100644 --- a/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json +++ b/erpnext/accounts/doctype/payment_entry_deduction/payment_entry_deduction.json @@ -3,6 +3,7 @@ "creation": "2016-06-15 15:56:30.815503", "doctype": "DocType", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "account", "cost_center", @@ -17,9 +18,7 @@ "in_list_view": 1, "label": "Account", "options": "Account", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "cost_center", @@ -28,37 +27,30 @@ "label": "Cost Center", "options": "Cost Center", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "amount", "fieldtype": "Currency", "in_list_view": 1, - "label": "Amount", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "label": "Amount (Company Currency)", + "options": "Company:company:default_currency", + "reqd": 1 }, { "fieldname": "column_break_2", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "description", "fieldtype": "Small Text", - "label": "Description", - "show_days": 1, - "show_seconds": 1 + "label": "Description" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-09-12 20:38:08.110674", + "modified": "2023-03-06 07:11:57.739619", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Deduction", @@ -66,5 +58,6 @@ "permissions": [], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 0208975513b..76b85ecf0e2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -81,8 +81,12 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ } if(doc.docstatus == 1 && doc.outstanding_amount != 0 - && !(doc.is_return && doc.return_against)) { - this.frm.add_custom_button(__('Payment'), this.make_payment_entry, __('Create')); + && !(doc.is_return && doc.return_against) && !doc.on_hold) { + this.frm.add_custom_button( + __('Payment'), + () => this.make_payment_entry(), + __('Create') + ); cur_frm.page.set_inner_btn_group_as_primary(__('Create')); } diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 462233524f8..a624c638c36 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -75,9 +75,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte if (doc.docstatus == 1 && doc.outstanding_amount!=0 && !(cint(doc.is_return) && doc.return_against)) { - cur_frm.add_custom_button(__('Payment'), - this.make_payment_entry, __('Create')); - cur_frm.page.set_inner_btn_group_as_primary(__('Create')); + this.frm.add_custom_button( + __('Payment'), + () => this.make_payment_entry(), + __('Create') + ); + this.frm.page.set_inner_btn_group_as_primary(__('Create')); } if(doc.docstatus==1 && !doc.is_return) { diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py index 57d80492ae0..f21c94b4940 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py @@ -25,6 +25,7 @@ def get_data(filters): ["posting_date", "<=", filters.get("to_date")], ["against_voucher_type", "=", "Asset"], ["account", "in", depreciation_accounts], + ["is_cancelled", "=", 0], ] if filters.get("asset"): diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 6d0b77abcd7..01d05c1327c 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -421,6 +421,9 @@ frappe.ui.form.on('Asset', { } else { frm.set_value('purchase_date', purchase_doc.posting_date); } + if (!frm.doc.is_existing_asset && !frm.doc.available_for_use_date) { + frm.set_value('available_for_use_date', frm.doc.purchase_date); + } const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code); if (!item) { doctype_field = frappe.scrub(doctype) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 511afdf0854..c6d1154387e 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -79,6 +79,9 @@ "options": "ACC-ASS-.YYYY.-" }, { + "depends_on": "item_code", + "fetch_from": "item_code.item_name", + "fetch_if_empty": 1, "fieldname": "asset_name", "fieldtype": "Data", "in_list_view": 1, @@ -512,7 +515,7 @@ "table_fieldname": "accounts" } ], - "modified": "2023-01-31 01:03:09.467817", + "modified": "2023-03-30 15:07:41.542374", "modified_by": "Administrator", "module": "Assets", "name": "Asset", @@ -554,4 +557,4 @@ "sort_order": "DESC", "title_field": "asset_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 662411e0510..38a66e470cf 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -296,17 +296,42 @@ class Asset(AccountsController): if has_pro_rata: number_of_pending_depreciations += 1 + has_wdv_or_dd_non_yearly_pro_rata = False + if ( + finance_book.depreciation_method in ("Written Down Value", "Double Declining Balance") + and cint(finance_book.frequency_of_depreciation) != 12 + ): + has_wdv_or_dd_non_yearly_pro_rata = self.check_is_pro_rata( + finance_book, wdv_or_dd_non_yearly=True + ) + skip_row = False should_get_last_day = is_last_day_of_the_month(finance_book.depreciation_start_date) + depreciation_amount = 0 + for n in range(start[finance_book.idx - 1], number_of_pending_depreciations): # If depreciation is already completed (for double declining balance) if skip_row: continue - depreciation_amount = get_depreciation_amount(self, value_after_depreciation, finance_book) + if n > 0 and len(self.get("schedules")) > n - 1: + prev_depreciation_amount = self.get("schedules")[n - 1].depreciation_amount + else: + prev_depreciation_amount = 0 - if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1: + depreciation_amount = get_depreciation_amount( + self, + value_after_depreciation, + finance_book, + n, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + ) + + if not has_pro_rata or ( + n < (cint(number_of_pending_depreciations) - 1) or number_of_pending_depreciations == 2 + ): schedule_date = add_months( finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation) ) @@ -322,7 +347,10 @@ class Asset(AccountsController): if date_of_disposal: from_date = self.get_from_date(finance_book.finance_book) depreciation_amount, days, months = self.get_pro_rata_amt( - finance_book, depreciation_amount, from_date, date_of_disposal + finance_book, + depreciation_amount, + from_date, + date_of_disposal, ) if depreciation_amount > 0: @@ -340,12 +368,20 @@ class Asset(AccountsController): break # For first row - if has_pro_rata and not self.opening_accumulated_depreciation and n == 0: + if ( + (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) + and not self.opening_accumulated_depreciation + and n == 0 + ): from_date = add_days( self.available_for_use_date, -1 ) # needed to calc depr amount for available_for_use_date too depreciation_amount, days, months = self.get_pro_rata_amt( - finance_book, depreciation_amount, from_date, finance_book.depreciation_start_date + finance_book, + depreciation_amount, + from_date, + finance_book.depreciation_start_date, + has_wdv_or_dd_non_yearly_pro_rata, ) # For first depr schedule date will be the start date @@ -364,7 +400,11 @@ class Asset(AccountsController): depreciation_amount_without_pro_rata = depreciation_amount depreciation_amount, days, months = self.get_pro_rata_amt( - finance_book, depreciation_amount, schedule_date, self.to_date + finance_book, + depreciation_amount, + schedule_date, + self.to_date, + has_wdv_or_dd_non_yearly_pro_rata, ) depreciation_amount = self.get_adjusted_depreciation_amount( @@ -469,28 +509,37 @@ class Asset(AccountsController): return add_days(self.available_for_use_date, -1) # if it returns True, depreciation_amount will not be equal for the first and last rows - def check_is_pro_rata(self, row): + def check_is_pro_rata(self, row, wdv_or_dd_non_yearly=False): has_pro_rata = False # if not existing asset, from_date = available_for_use_date # otherwise, if number_of_depreciations_booked = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12 # from_date = 01/01/2022 - from_date = self.get_modified_available_for_use_date(row) + from_date = self.get_modified_available_for_use_date(row, wdv_or_dd_non_yearly) days = date_diff(row.depreciation_start_date, from_date) + 1 - # if frequency_of_depreciation is 12 months, total_days = 365 - total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation) + if wdv_or_dd_non_yearly: + total_days = get_total_days(row.depreciation_start_date, 12) + else: + # if frequency_of_depreciation is 12 months, total_days = 365 + total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation) if days < total_days: has_pro_rata = True return has_pro_rata - def get_modified_available_for_use_date(self, row): - return add_months( - self.available_for_use_date, - (self.number_of_depreciations_booked * row.frequency_of_depreciation), - ) + def get_modified_available_for_use_date(self, row, wdv_or_dd_non_yearly=False): + if wdv_or_dd_non_yearly: + return add_months( + self.available_for_use_date, + (self.number_of_depreciations_booked * 12), + ) + else: + return add_months( + self.available_for_use_date, + (self.number_of_depreciations_booked * row.frequency_of_depreciation), + ) def validate_asset_finance_books(self, row): if flt(row.expected_value_after_useful_life) >= flt(self.gross_purchase_amount): @@ -893,7 +942,12 @@ class Asset(AccountsController): float_precision = cint(frappe.db.get_default("float_precision")) or 2 if args.get("depreciation_method") == "Double Declining Balance": - return 200.0 / args.get("total_number_of_depreciations") + return 200.0 / ( + ( + flt(args.get("total_number_of_depreciations"), 2) * flt(args.get("frequency_of_depreciation")) + ) + / 12 + ) if args.get("depreciation_method") == "Written Down Value": if ( @@ -910,14 +964,29 @@ class Asset(AccountsController): else: value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount) - depreciation_rate = math.pow(value, 1.0 / flt(args.get("total_number_of_depreciations"), 2)) + depreciation_rate = math.pow( + value, + 1.0 + / ( + ( + flt(args.get("total_number_of_depreciations"), 2) + * flt(args.get("frequency_of_depreciation")) + ) + / 12 + ), + ) return flt((100 * (1 - depreciation_rate)), float_precision) - def get_pro_rata_amt(self, row, depreciation_amount, from_date, to_date): + def get_pro_rata_amt( + self, row, depreciation_amount, from_date, to_date, has_wdv_or_dd_non_yearly_pro_rata=False + ): days = date_diff(to_date, from_date) months = month_diff(to_date, from_date) - total_days = get_total_days(to_date, row.frequency_of_depreciation) + if has_wdv_or_dd_non_yearly_pro_rata: + total_days = get_total_days(to_date, 12) + else: + total_days = get_total_days(to_date, row.frequency_of_depreciation) return (depreciation_amount * flt(days)) / flt(total_days), days, months @@ -1178,24 +1247,69 @@ def get_total_days(date, frequency): @erpnext.allow_regional -def get_depreciation_amount(asset, depreciable_value, row): +def get_depreciation_amount( + asset, + depreciable_value, + row, + schedule_idx=0, + prev_depreciation_amount=0, + has_wdv_or_dd_non_yearly_pro_rata=False, +): if row.depreciation_method in ("Straight Line", "Manual"): - # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value - if asset.flags.increase_in_asset_life: - depreciation_amount = ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) - # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value - elif asset.flags.increase_in_asset_value_due_to_repair: - depreciation_amount = ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) - # if the Depreciation Schedule is being prepared for the first time - else: - depreciation_amount = ( - flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) + return get_straight_line_or_manual_depr_amount(asset, row) else: - depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) + return get_wdv_or_dd_depr_amount( + depreciable_value, + row.rate_of_depreciation, + row.frequency_of_depreciation, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + ) - return depreciation_amount + +def get_straight_line_or_manual_depr_amount(asset, row): + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value + if asset.flags.increase_in_asset_life: + return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / ( + date_diff(asset.to_date, asset.available_for_use_date) / 365 + ) + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value + elif asset.flags.increase_in_asset_value_due_to_repair: + return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / flt( + row.total_number_of_depreciations + ) + # if the Depreciation Schedule is being prepared for the first time + else: + return (flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)) / flt( + row.total_number_of_depreciations + ) + + +def get_wdv_or_dd_depr_amount( + depreciable_value, + rate_of_depreciation, + frequency_of_depreciation, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, +): + if cint(frequency_of_depreciation) == 12: + return flt(depreciable_value) * (flt(rate_of_depreciation) / 100) + else: + if has_wdv_or_dd_non_yearly_pro_rata: + if schedule_idx == 0: + return flt(depreciable_value) * (flt(rate_of_depreciation) / 100) + elif schedule_idx % (12 / cint(frequency_of_depreciation)) == 1: + return ( + flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200) + ) + else: + return prev_depreciation_amount + else: + if schedule_idx % (12 / cint(frequency_of_depreciation)) == 0: + return ( + flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200) + ) + else: + return prev_depreciation_amount diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index fc3af44947d..ca0cd4d978d 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -225,10 +225,16 @@ def notify_depr_entry_posting_error(failed_asset_names): asset_links = get_comma_separated_asset_links(failed_asset_names) message = ( - _("Hi,") - + "
" - + _("The following assets have failed to post depreciation entries: {0}").format(asset_links) + _("Hello,") + + "

" + + _("The following assets have failed to automatically post depreciation entries: {0}").format( + asset_links + ) + "." + + "

" + + _( + "Please raise a support ticket and share this email, or forward this email to your development team so that they can find the issue in the developer console by manually creating the depreciation entry via the asset's depreciation schedule table." + ) ) frappe.sendmail(recipients=recipients, subject=subject, message=message) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 625a45b5098..a441856b241 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -806,12 +806,12 @@ class TestDepreciationMethods(AssetSetup): ) expected_schedules = [ - ["2022-02-28", 647.25, 647.25], - ["2022-03-31", 1210.71, 1857.96], - ["2022-04-30", 1053.99, 2911.95], - ["2022-05-31", 917.55, 3829.5], - ["2022-06-30", 798.77, 4628.27], - ["2022-07-15", 371.73, 5000.0], + ["2022-02-28", 310.89, 310.89], + ["2022-03-31", 654.45, 965.34], + ["2022-04-30", 654.45, 1619.79], + ["2022-05-31", 654.45, 2274.24], + ["2022-06-30", 654.45, 2928.69], + ["2022-07-15", 2071.31, 5000.0], ] schedules = [ diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index e603d346266..9bc35bc7360 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -82,6 +82,8 @@ def calculate_next_due_date( next_due_date = add_years(start_date, 1) if periodicity == "2 Yearly": next_due_date = add_years(start_date, 2) + if periodicity == "3 Yearly": + next_due_date = add_years(start_date, 3) if periodicity == "Quarterly": next_due_date = add_months(start_date, 3) if end_date and ( diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json index 20963e3fdc7..b7cb23e6687 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.json @@ -1,664 +1,156 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2017-10-20 07:10:55.903571", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-10-20 07:10:55.903571", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "maintenance_task", + "maintenance_type", + "column_break_2", + "maintenance_status", + "section_break_2", + "start_date", + "periodicity", + "column_break_4", + "end_date", + "certificate_required", + "section_break_9", + "assign_to", + "column_break_10", + "assign_to_name", + "section_break_10", + "next_due_date", + "column_break_14", + "last_completion_date", + "section_break_7", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_task", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Maintenance Task", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_task", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Maintenance Task", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Type", - "length": 0, - "no_copy": 0, - "options": "Preventive Maintenance\nCalibration", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_type", + "fieldtype": "Select", + "label": "Maintenance Type", + "options": "Preventive Maintenance\nCalibration" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "maintenance_status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maintenance Status", - "length": 0, - "no_copy": 0, - "options": "Planned\nOverdue\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Maintenance Status", + "options": "Planned\nOverdue\nCancelled", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "periodicity", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Periodicity", - "length": 0, - "no_copy": 0, - "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nYearly\n2 Yearly", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "periodicity", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Periodicity", + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nYearly\n2 Yearly\n3 Yearly", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "certificate_required", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Certificate Required", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "certificate_required", + "fieldtype": "Check", + "label": "Certificate Required", + "search_index": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "assign_to", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Assign To", - "length": 0, - "no_copy": 0, - "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "assign_to", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Assign To", + "options": "User" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_10", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "assign_to.full_name", - "fieldname": "assign_to_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Assign to Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign to Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "next_due_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Next Due Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "next_due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Next Due Date" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "last_completion_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Last Completion Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "last_completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Last Completion Date" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "description", + "fieldtype": "Text Editor", + "label": "Description" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-06-18 16:12:04.330021", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Task", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2023-03-23 07:03:07.113452", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Task", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js index ae0e1bda020..d07f40cdf42 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js @@ -49,7 +49,7 @@ frappe.ui.form.on('Asset Value Adjustment', { frm.call({ method: "erpnext.assets.doctype.asset.asset.get_asset_value_after_depreciation", args: { - asset: frm.doc.asset, + asset_name: frm.doc.asset, finance_book: frm.doc.finance_book }, callback: function(r) { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 2559ce76da6..72329e9a22e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -191,8 +191,12 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( cur_frm.add_custom_button(__('Purchase Invoice'), this.make_purchase_invoice, __('Create')); - if(flt(doc.per_billed)==0 && doc.status != "Delivered") { - cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create')); + if(flt(doc.per_billed) < 100 && doc.status != "Delivered") { + this.frm.add_custom_button( + __('Payment'), + () => this.make_payment_entry(), + __('Create') + ); } if(flt(doc.per_billed)==0) { diff --git a/erpnext/crm/report/lead_details/lead_details.py b/erpnext/crm/report/lead_details/lead_details.py index 8660c733103..7b8c43b2d65 100644 --- a/erpnext/crm/report/lead_details/lead_details.py +++ b/erpnext/crm/report/lead_details/lead_details.py @@ -98,7 +98,7 @@ def get_data(filters): `tabAddress`.name=`tabDynamic Link`.parent) WHERE company = %(company)s - AND `tabLead`.creation BETWEEN %(from_date)s AND %(to_date)s + AND DATE(`tabLead`.creation) BETWEEN %(from_date)s AND %(to_date)s {conditions} ORDER BY `tabLead`.creation asc """.format( diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.py b/erpnext/crm/report/lost_opportunity/lost_opportunity.py index a57b44be477..ad8d8484e0e 100644 --- a/erpnext/crm/report/lost_opportunity/lost_opportunity.py +++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.py @@ -90,7 +90,7 @@ def get_data(filters): {join} WHERE `tabOpportunity`.status = 'Lost' and `tabOpportunity`.company = %(company)s - AND `tabOpportunity`.modified BETWEEN %(from_date)s AND %(to_date)s + AND DATE(`tabOpportunity`.modified) BETWEEN %(from_date)s AND %(to_date)s {conditions} GROUP BY `tabOpportunity`.name diff --git a/erpnext/e_commerce/doctype/website_item/test_website_item.py b/erpnext/e_commerce/doctype/website_item/test_website_item.py index ebf01bf43fb..93c8b439b15 100644 --- a/erpnext/e_commerce/doctype/website_item/test_website_item.py +++ b/erpnext/e_commerce/doctype/website_item/test_website_item.py @@ -198,8 +198,14 @@ class TestWebsiteItem(unittest.TestCase): breadcrumbs = get_parent_item_groups(item.item_group) + settings = frappe.get_cached_doc("E Commerce Settings") + if settings.enable_field_filters: + base_breadcrumb = "Shop by Category" + else: + base_breadcrumb = "All Products" + self.assertEqual(breadcrumbs[0]["name"], "Home") - self.assertEqual(breadcrumbs[1]["name"], "All Products") + self.assertEqual(breadcrumbs[1]["name"], base_breadcrumb) self.assertEqual(breadcrumbs[2]["name"], "_Test Item Group B") # parent item group self.assertEqual(breadcrumbs[3]["name"], "_Test Item Group B - 1") diff --git a/erpnext/hooks.py b/erpnext/hooks.py index fb56860dae5..651e6a0ac22 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -29,6 +29,10 @@ doctype_js = { override_doctype_class = {"Address": "erpnext.accounts.custom.address.ERPNextAddress"} +override_whitelisted_methods = { + "frappe.www.contact.send_message": "erpnext.templates.utils.send_message" +} + welcome_email = "erpnext.setup.utils.welcome_email" # setup wizard diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py index c6bd7d23a76..5c7cf2038e8 100644 --- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py @@ -4,6 +4,7 @@ import unittest import frappe +from frappe.tests.utils import change_settings from frappe.utils import add_days, getdate from erpnext.hr.doctype.employee.test_employee import make_employee @@ -99,6 +100,16 @@ class TestEmployeeTransfer(unittest.TestCase): self.assertEqual(data.from_date, dt[0]) self.assertEqual(data.to_date, None) + @change_settings("System Settings", {"number_format": "#.###,##"}) + def test_data_formatting_in_history(self): + from erpnext.hr.utils import get_formatted_value + + value = get_formatted_value("12.500,00", "Float") + self.assertEqual(value, 12500.0) + + value = get_formatted_value("12.500,00", "Currency") + self.assertEqual(value, 12500.0) + def create_company(): if not frappe.db.exists("Company", "Test Company"): diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 08bc93760a3..d6f8c25b424 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -873,6 +873,9 @@ def get_leave_allocation_records(employee, date, leave_type=None): | ( (Ledger.is_carry_forward == 1) & (Ledger.to_date.between(LeaveAllocation.from_date, LeaveAllocation.to_date)) + # only consider cf leaves from current allocation + & (LeaveAllocation.from_date <= date) + & (date <= LeaveAllocation.to_date) ) ) ) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index e30b84bbf34..8d1adaca62b 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -1170,25 +1170,51 @@ class TestLeaveApplication(unittest.TestCase): details = get_leave_allocation_records(employee.name, add_days(cf_expiry, 1), leave_type.name) self.assertEqual(details.get(leave_type.name), expected_data) + @set_holiday_list("Salary Slip Test Holiday List", "_Test Company") + def test_filtered_old_cf_entries_in_get_leave_allocation_records(self): + """Tests whether old cf entries are ignored while fetching current allocation records""" + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carry_forwarded_leaves_after_days=90, + ) + + # old allocation with cf leaves + create_carry_forwarded_allocation(employee, leave_type, date="2019-01-01") + # new allocation with cf leaves + leave_alloc = create_carry_forwarded_allocation(employee, leave_type) + cf_expiry = frappe.db.get_value( + "Leave Ledger Entry", {"transaction_name": leave_alloc.name, "is_carry_forward": 1}, "to_date" + ) + + # test total leaves allocated before cf leave expiry + details = get_leave_allocation_records(employee.name, add_days(cf_expiry, -1), leave_type.name) + # filters out old CF leaves (15 i.e total 45) + self.assertEqual(details[leave_type.name]["total_leaves_allocated"], 30.0) + + +def create_carry_forwarded_allocation(employee, leave_type, date=None): + date = date or nowdate() -def create_carry_forwarded_allocation(employee, leave_type): # initial leave allocation leave_allocation = create_leave_allocation( leave_type="_Test_CF_leave_expiry", employee=employee.name, employee_name=employee.employee_name, - from_date=add_months(nowdate(), -24), - to_date=add_months(nowdate(), -12), + from_date=add_months(date, -24), + to_date=add_months(date, -12), carry_forward=0, ) leave_allocation.submit() + # carry forward leave allocation leave_allocation = create_leave_allocation( leave_type="_Test_CF_leave_expiry", employee=employee.name, employee_name=employee.employee_name, - from_date=add_days(nowdate(), -84), - to_date=add_days(nowdate(), 100), + from_date=add_days(date, -84), + to_date=add_days(date, 100), carry_forward=1, ) leave_allocation.submit() diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index d256f34732e..181e3b12b98 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -13,6 +13,7 @@ from frappe.utils import ( formatdate, get_datetime, get_link_to_form, + get_number_format_info, getdate, nowdate, today, @@ -185,15 +186,11 @@ def update_employee_work_history(employee, details, date=None, cancel=False): field = frappe.get_meta("Employee").get_field(item.fieldname) if not field: continue - fieldtype = field.fieldtype - new_data = item.new if not cancel else item.current - if fieldtype == "Date" and new_data: - new_data = getdate(new_data) - elif fieldtype == "Datetime" and new_data: - new_data = get_datetime(new_data) - elif fieldtype in ["Currency", "Float"] and new_data: - new_data = flt(new_data) - setattr(employee, item.fieldname, new_data) + + new_value = item.new if not cancel else item.current + new_value = get_formatted_value(new_value, field.fieldtype) + setattr(employee, item.fieldname, new_value) + if item.fieldname in ["department", "designation", "branch"]: internal_work_history[item.fieldname] = item.new @@ -207,6 +204,34 @@ def update_employee_work_history(employee, details, date=None, cancel=False): return employee +def get_formatted_value(value, fieldtype): + """ + Since the fields in Internal Work History table are `Data` fields + format them as per relevant field types + """ + if not value: + return + + if fieldtype == "Date": + value = getdate(value) + elif fieldtype == "Datetime": + value = get_datetime(value) + elif fieldtype in ["Currency", "Float"]: + # in case of currency/float, the value might be in user's prefered number format + # instead of machine readable format. Convert it into a machine readable format + number_format = frappe.db.get_default("number_format") or "#,###.##" + decimal_str, comma_str, _number_format_precision = get_number_format_info(number_format) + + if comma_str == "." and decimal_str == ",": + value = value.replace(",", "#$") + value = value.replace(".", ",") + value = value.replace("#$", ".") + + value = flt(value) + + return value + + def delete_employee_work_history(details, employee, date): filters = {} for d in details: diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 36bac4c6840..9ae9b1a4af2 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -949,7 +949,8 @@ def get_valuation_rate(data): 2) If no value, get last valuation rate from SLE 3) If no value, get valuation rate from Item """ - from frappe.query_builder.functions import Sum + from frappe.query_builder.functions import Count, IfNull, Sum + from pypika import Case item_code, company = data.get("item_code"), data.get("company") valuation_rate = 0.0 @@ -960,7 +961,14 @@ def get_valuation_rate(data): frappe.qb.from_(bin_table) .join(wh_table) .on(bin_table.warehouse == wh_table.name) - .select((Sum(bin_table.stock_value) / Sum(bin_table.actual_qty)).as_("valuation_rate")) + .select( + Case() + .when( + Count(bin_table.name) > 0, IfNull(Sum(bin_table.stock_value) / Sum(bin_table.actual_qty), 0.0) + ) + .else_(None) + .as_("valuation_rate") + ) .where((bin_table.item_code == item_code) & (wh_table.company == company)) ).run(as_dict=True)[0] diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py index af5ff8e1c21..9c35e49b20b 100644 --- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py +++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py @@ -151,7 +151,7 @@ def queue_bom_cost_jobs( while current_boms_list: batch_no += 1 - batch_size = 20_000 + batch_size = 7_000 boms_to_process = current_boms_list[:batch_size] # slice out batch of 20k BOMs # update list to exclude 20K (queued) BOMs diff --git a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json index 84dee4ad284..15ef20794cb 100644 --- a/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json +++ b/erpnext/manufacturing/doctype/production_plan_item_reference/production_plan_item_reference.json @@ -28,7 +28,7 @@ "fieldname": "qty", "fieldtype": "Data", "in_list_view": 1, - "label": "qty" + "label": "Qty" }, { "fieldname": "item_reference", @@ -40,7 +40,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-05-07 17:03:49.707487", + "modified": "2023-03-31 10:30:14.604051", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Item Reference", @@ -48,5 +48,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 7b64087102f..7ea12a86b8a 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -125,7 +125,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } else { // allow for '0' qty on Credit/Debit notes - let qty = item.qty || me.frm.doc.is_debit_note ? 1 : -1; + let qty = item.qty || (me.frm.doc.is_debit_note ? 1 : -1); item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item)); } diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index eb78ca72bc1..972e6b1516c 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1987,22 +1987,62 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, - make_payment_entry: function() { + make_payment_entry() { + let via_journal_entry = this.frm.doc.__onload && this.frm.doc.__onload.make_payment_via_journal_entry; + if(this.has_discount_in_schedule() && !via_journal_entry) { + // If early payment discount is applied, ask user for reference date + this.prompt_user_for_reference_date(); + } else { + this.make_mapped_payment_entry(); + } + }, + + make_mapped_payment_entry(args) { + var me = this; + args = args || { "dt": this.frm.doc.doctype, "dn": this.frm.doc.name }; return frappe.call({ - method: cur_frm.cscript.get_method_for_payment(), - args: { - "dt": cur_frm.doc.doctype, - "dn": cur_frm.doc.name - }, + method: me.get_method_for_payment(), + args: args, callback: function(r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - // cur_frm.refresh_fields() } }); }, - make_quality_inspection: function () { + prompt_user_for_reference_date(){ + var me = this; + frappe.prompt({ + label: __("Cheque/Reference Date"), + fieldname: "reference_date", + fieldtype: "Date", + reqd: 1, + }, (values) => { + let args = { + "dt": me.frm.doc.doctype, + "dn": me.frm.doc.name, + "reference_date": values.reference_date + } + me.make_mapped_payment_entry(args); + }, + __("Reference Date for Early Payment Discount"), + __("Continue") + ); + }, + + has_discount_in_schedule() { + let is_eligible = in_list( + ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"], + this.frm.doctype + ); + let has_payment_schedule = this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length; + if(!is_eligible || !has_payment_schedule) return false; + + let has_discount = this.frm.doc.payment_schedule.some(row => row.discount_date); + return has_discount; + }, + + make_quality_inspection() { let data = []; const fields = [ { diff --git a/erpnext/public/js/website_utils.js b/erpnext/public/js/website_utils.js index b5416065d79..2bb5255eebc 100644 --- a/erpnext/public/js/website_utils.js +++ b/erpnext/public/js/website_utils.js @@ -3,18 +3,6 @@ if(!window.erpnext) window.erpnext = {}; -// Add / update a new Lead / Communication -// subject, sender, description -frappe.send_message = function(opts, btn) { - return frappe.call({ - type: "POST", - method: "erpnext.templates.utils.send_message", - btn: btn, - args: opts, - callback: opts.callback - }); -}; - erpnext.subscribe_to_newsletter = function(opts, btn) { return frappe.call({ type: "POST", @@ -24,6 +12,3 @@ erpnext.subscribe_to_newsletter = function(opts, btn) { callback: opts.callback }); } - -// for backward compatibility -erpnext.send_message = frappe.send_message; diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index d5ef3981faf..107c700b521 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -17,6 +17,10 @@ from frappe.utils import ( ) from six import string_types +from erpnext.assets.doctype.asset.asset import ( + get_straight_line_or_manual_depr_amount, + get_wdv_or_dd_depr_amount, +) from erpnext.controllers.accounts_controller import get_taxes_and_charges from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount from erpnext.hr.utils import get_salary_assignments @@ -1099,23 +1103,16 @@ def update_taxable_values(doc, method): doc.get("items")[item_count - 1].taxable_value += diff -def get_depreciation_amount(asset, depreciable_value, row): +def get_depreciation_amount( + asset, + depreciable_value, + row, + schedule_idx=0, + prev_depreciation_amount=0, + has_wdv_or_dd_non_yearly_pro_rata=False, +): if row.depreciation_method in ("Straight Line", "Manual"): - # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value - if asset.flags.increase_in_asset_life: - depreciation_amount = ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) - # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value - elif asset.flags.increase_in_asset_value_due_to_repair: - depreciation_amount = ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) - # if the Depreciation Schedule is being prepared for the first time - else: - depreciation_amount = ( - flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) + return get_straight_line_or_manual_depr_amount(asset, row) else: rate_of_depreciation = row.rate_of_depreciation # if its the first depreciation @@ -1130,10 +1127,14 @@ def get_depreciation_amount(asset, depreciable_value, row): "As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%." ) ) - - depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100)) - - return depreciation_amount + return get_wdv_or_dd_depr_amount( + depreciable_value, + rate_of_depreciation, + row.frequency_of_depreciation, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + ) def set_item_tax_from_hsn_code(item): diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index a11cfc3407a..5600689f65b 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -149,12 +149,17 @@ def get_item_for_list_in_html(context): def get_parent_item_groups(item_group_name, from_item=False): - base_nav_page = {"name": _("All Products"), "route": "/all-products"} + settings = frappe.get_cached_doc("E Commerce Settings") + + if settings.enable_field_filters: + base_nav_page = {"name": _("Shop by Category"), "route": "/shop-by-category"} + else: + base_nav_page = {"name": _("All Products"), "route": "/all-products"} if from_item and frappe.request.environ.get("HTTP_REFERER"): # base page after 'Home' will vary on Item page last_page = frappe.request.environ["HTTP_REFERER"].split("/")[-1].split("?")[0] - if last_page and last_page == "shop-by-category": + if last_page and last_page in ("shop-by-category", "all-products"): base_nav_page_title = " ".join(last_page.split("-")).title() base_nav_page = {"name": _(base_nav_page_title), "route": "/" + last_page} diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index 768ea9b6024..d13edd5c326 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -6,7 +6,7 @@ import frappe from frappe import _ from frappe.model.document import Document from frappe.model.naming import make_autoname, revert_series_if_last -from frappe.utils import cint, flt, get_link_to_form +from frappe.utils import cint, flt, get_link_to_form, nowtime from frappe.utils.data import add_days from frappe.utils.jinja import render_template from six import text_type @@ -173,7 +173,11 @@ def get_batch_qty( out = 0 if batch_no and warehouse: cond = "" - if posting_date and posting_time: + + if posting_date: + if posting_time is None: + posting_time = nowtime() + cond = " and timestamp(posting_date, posting_time) <= timestamp('{0}', '{1}')".format( posting_date, posting_time ) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 7ca771f0a73..e9c53fb2c5d 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -30,6 +30,9 @@ def execute(filters=None): 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 = {} for sle in sl_entries: diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py index 4295188dc0b..ae74ffa2772 100644 --- a/erpnext/templates/utils.py +++ b/erpnext/templates/utils.py @@ -6,13 +6,12 @@ import frappe @frappe.whitelist(allow_guest=True) -def send_message(subject="Website Query", message="", sender="", status="Open"): +def send_message(sender, message, subject="Website Query"): from frappe.www.contact import send_message as website_send_message + website_send_message(sender, message, subject) + lead = customer = None - - website_send_message(subject, message, sender) - customer = frappe.db.sql( """select distinct dl.link_name from `tabDynamic Link` dl left join `tabContact` c on dl.parent=c.name where dl.link_doctype='Customer' @@ -59,5 +58,3 @@ def send_message(subject="Website Query", message="", sender="", status="Open"): } ) comm.insert(ignore_permissions=True) - - return "okay" diff --git a/erpnext/www/shop-by-category/index.py b/erpnext/www/shop-by-category/index.py index 219747c9f8a..913c1836acd 100644 --- a/erpnext/www/shop-by-category/index.py +++ b/erpnext/www/shop-by-category/index.py @@ -53,6 +53,7 @@ def get_tabs(categories): def get_category_records(categories: list): categorical_data = {} + website_item_meta = frappe.get_meta("Website Item", cached=True) for c in categories: if c == "item_group": @@ -64,7 +65,16 @@ def get_category_records(categories: list): continue - doctype = frappe.unscrub(c) + field_type = website_item_meta.get_field(c).fieldtype + + if field_type == "Table MultiSelect": + child_doc = website_item_meta.get_field(c).options + for field in frappe.get_meta(child_doc, cached=True).fields: + if field.fieldtype == "Link" and field.reqd: + doctype = field.options + else: + doctype = website_item_meta.get_field(c).options + fields = ["name"] try: From 21cd7898427f9f4f4bebb1b98e6cdd061d6863d3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 5 Apr 2023 11:45:33 +0000 Subject: [PATCH 41/76] chore(release): Bumped to Version 13.49.11 ## [13.49.11](https://github.com/frappe/erpnext/compare/v13.49.10...v13.49.11) (2023-04-05) ### Reverts * Revert "fix: `payment entry is already created` on posawesome. (backport #34712)" (backport #34756) (#34757) ([98de1f2](https://github.com/frappe/erpnext/commit/98de1f201d811158085343f470e66a8f034b7eaf)), closes [#34712](https://github.com/frappe/erpnext/issues/34712) [#34756](https://github.com/frappe/erpnext/issues/34756) [#34757](https://github.com/frappe/erpnext/issues/34757) [#34712](https://github.com/frappe/erpnext/issues/34712) [#34712](https://github.com/frappe/erpnext/issues/34712) [#34753](https://github.com/frappe/erpnext/issues/34753) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c8457084c41..d51035ce022 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.10" +__version__ = "13.49.11" def get_default_company(user=None): From c93a5ab8f006d0bd8795abc43eab5ac9ee436a2d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 11 Apr 2023 11:41:15 +0000 Subject: [PATCH 42/76] chore(release): Bumped to Version 13.49.12 ## [13.49.12](https://github.com/frappe/erpnext/compare/v13.49.11...v13.49.12) (2023-04-11) ### Bug Fixes * `payment entry is already created` on posawesome. (backport [#34712](https://github.com/frappe/erpnext/issues/34712)) ([#34753](https://github.com/frappe/erpnext/issues/34753)) ([b48fca3](https://github.com/frappe/erpnext/commit/b48fca3e5ae12a31aa5c06bc3b1bdcb714100a80)) * Allocate tax loss to tax account head on early payment discount ([#34287](https://github.com/frappe/erpnext/issues/34287)) ([92a26dd](https://github.com/frappe/erpnext/commit/92a26dda3c900a8286c7c11f5219b93e54128d95)) * asset monthly WDV and DD schedule [v13] ([#34645](https://github.com/frappe/erpnext/issues/34645)) ([fed43ae](https://github.com/frappe/erpnext/commit/fed43aeb85bc3f6252cdf061196fa358ebce755d)) * BOM Update Cost, when no actual qty ([9725698](https://github.com/frappe/erpnext/commit/9725698b7944fafd9fac57f05248655a94270430)) * bom update log not working for large batch size ([9cf30d7](https://github.com/frappe/erpnext/commit/9cf30d7621d0a68bce39314f4555aa26ecd95842)) * don't include cancelled JVs in assdeprledger report ([#34737](https://github.com/frappe/erpnext/issues/34737)) ([3007ac3](https://github.com/frappe/erpnext/commit/3007ac3c2035c1e6e25454ca15fa157898e8a82f)) * enclose ternary operator in parentheses ([198830a](https://github.com/frappe/erpnext/commit/198830a6c80463554d5fb2e7d4e4455c1d0cc612)) * filter out old allocation's cf leaves while fetching leave details ([#34723](https://github.com/frappe/erpnext/issues/34723)) ([50de045](https://github.com/frappe/erpnext/commit/50de0452478492924284690a29ea117980946d15)) * format currency/float as per number format in work history ([#34545](https://github.com/frappe/erpnext/issues/34545)) ([892c480](https://github.com/frappe/erpnext/commit/892c480408f631f73f636a00423412db667e0f73)) * incorrect arg name in asset value adjustment ([545807a](https://github.com/frappe/erpnext/commit/545807a91ed968e98150741d694b180ee9017aba)) * incorrect balance qty in the stock ledger report ([dab1f1a](https://github.com/frappe/erpnext/commit/dab1f1a0d095677fc639b0f2a45cd96ceadcf71d)) * Item tax validity comparison fixes ([#34784](https://github.com/frappe/erpnext/issues/34784)) ([71bafab](https://github.com/frappe/erpnext/commit/71bafab41b34d4df2b374bf29ee02fe12346875f)) * lost opportunity report issue ([#34626](https://github.com/frappe/erpnext/issues/34626)) ([ab06cb4](https://github.com/frappe/erpnext/commit/ab06cb42a3169bfeae6d39d2799283e7305ce43d)) * posting time issue ([f22e777](https://github.com/frappe/erpnext/commit/f22e7775b32999f970369004a18d50079df4e425)) * provide filter by depreciable assets in fixed asset register ([#34803](https://github.com/frappe/erpnext/issues/34803)) ([8609bf4](https://github.com/frappe/erpnext/commit/8609bf4a12ede87fff548145ae734f5d51116795)) * serial no with zero quantity issue in stock reco ([46638b1](https://github.com/frappe/erpnext/commit/46638b19dbf051d580f9bab93e7d20d6851b6b23)) * Shop by category fixes (backport [#34688](https://github.com/frappe/erpnext/issues/34688)) ([#34751](https://github.com/frappe/erpnext/issues/34751)) ([af828e4](https://github.com/frappe/erpnext/commit/af828e455434fe335ccb243cbfd14298c0fe9301)) ### Reverts * Revert "fix: `payment entry is already created` on posawesome. (#34712)" ([034e35e](https://github.com/frappe/erpnext/commit/034e35e7f61c7c052521e4f789a05f546acb5287)), closes [#34712](https://github.com/frappe/erpnext/issues/34712) [#34712](https://github.com/frappe/erpnext/issues/34712) [#34753](https://github.com/frappe/erpnext/issues/34753) * remove frappe.send_message (v13) ([#34820](https://github.com/frappe/erpnext/issues/34820)) ([77f1322](https://github.com/frappe/erpnext/commit/77f13227329b9895751d52fe2ded5d388875dda3)), closes [#34816](https://github.com/frappe/erpnext/issues/34816) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d51035ce022..d66280e65d7 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.11" +__version__ = "13.49.12" def get_default_company(user=None): From acecd07fa2fa4a96a1ba664bf3d4cb9b909cfcc4 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 19 Apr 2023 01:36:18 +0000 Subject: [PATCH 43/76] chore(release): Bumped to Version 13.49.13 ## [13.49.13](https://github.com/frappe/erpnext/compare/v13.49.12...v13.49.13) (2023-04-19) ### Bug Fixes * change discuss forum url ([#34891](https://github.com/frappe/erpnext/issues/34891)) ([ba984ac](https://github.com/frappe/erpnext/commit/ba984acef2c5df9b7febcf47372d304e6d8791b0)) * unable to change `company` for manual `Serial No` entry ([5d51103](https://github.com/frappe/erpnext/commit/5d511035ec3e0610ff244fe7c9c271aa8c221c25)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d66280e65d7..261f962edcd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.12" +__version__ = "13.49.13" def get_default_company(user=None): From 1d6917f340448c0e4c9a9b9625749f664ae8248a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 25 Apr 2023 16:57:14 +0000 Subject: [PATCH 44/76] chore(release): Bumped to Version 13.49.14 ## [13.49.14](https://github.com/frappe/erpnext/compare/v13.49.13...v13.49.14) (2023-04-25) ### Bug Fixes * `PermissionError` in Work Order ([5680045](https://github.com/frappe/erpnext/commit/5680045f2b6a9042ea91ab7099fe6135e3a65883)) * Advance payment against payment terms ([#34872](https://github.com/frappe/erpnext/issues/34872)) ([d215a85](https://github.com/frappe/erpnext/commit/d215a857477d26d82b1423f9a9cc07e9ffa6385d)) * internal Purchase Receipt GL Entries ([b73422e](https://github.com/frappe/erpnext/commit/b73422e4eef790334e22234f0875f4e18f695cf8)) * Payment entry with TDS in bank reco statement ([#34961](https://github.com/frappe/erpnext/issues/34961)) ([5f28b1d](https://github.com/frappe/erpnext/commit/5f28b1d3305d59478c78d91f6b23eb7f715de38e)) * Payment Request flow fixes from Order to Payment Entry ([#33350](https://github.com/frappe/erpnext/issues/33350)) ([d5a80b5](https://github.com/frappe/erpnext/commit/d5a80b56153e1de256d414eb3926c782aa9b7137)) * SLA permissions ([#34981](https://github.com/frappe/erpnext/issues/34981)) ([c1187be](https://github.com/frappe/erpnext/commit/c1187bed26f227764c3de5b8b87792109580bf35)) * **test:** `test_backdated_stock_reco_cancellation_future_negative_stock` ([d010b04](https://github.com/frappe/erpnext/commit/d010b048dc73251742b5cecd4fa4611e0077f7ea)) * Unable to allocate advance against invoice ([#35007](https://github.com/frappe/erpnext/issues/35007)) ([61a3121](https://github.com/frappe/erpnext/commit/61a3121172d3a186bd6425d70e3c6decc9403857)) * use filter_by_finance_book instead of only_depreciable_assets in fixed asset register (backport [#35031](https://github.com/frappe/erpnext/issues/35031)) ([#35036](https://github.com/frappe/erpnext/issues/35036)) ([be2095a](https://github.com/frappe/erpnext/commit/be2095ad03d0e2ee8ed24eb2d0a6f10b442911af)) * validation for internal transfer entry ([91b5a33](https://github.com/frappe/erpnext/commit/91b5a335648c3992ab9de3f2df46cf9649d8cca2)) * value of depreciable assets not updating after manual depr entry [v14] (backport [#35010](https://github.com/frappe/erpnext/issues/35010)) ([#35029](https://github.com/frappe/erpnext/issues/35029)) ([9087ac0](https://github.com/frappe/erpnext/commit/9087ac08290c2641afb60586d1552d15544f0a62)) * wrong qty of remaining work orders to be created when using "Create" > "Work Order" ([#34726](https://github.com/frappe/erpnext/issues/34726)) ([189b020](https://github.com/frappe/erpnext/commit/189b020d228bdb1c0c589697162cf91718b2fa27)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 261f962edcd..80758121e1e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.13" +__version__ = "13.49.14" def get_default_company(user=None): From cef7126a355dff37afbd28dc34d5bacbbfe78b80 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Tue, 25 Apr 2023 14:58:37 +0530 Subject: [PATCH 45/76] chore: set correct currency symbol in salary register for multi-currency salary slip (cherry picked from commit 83afaf48df80d47126450f9436557e5cd221aa1b) --- .../report/salary_register/salary_register.py | 433 +++++++++++------- 1 file changed, 267 insertions(+), 166 deletions(-) diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py index 0a62b43a8ea..d9d68212665 100644 --- a/erpnext/payroll/report/salary_register/salary_register.py +++ b/erpnext/payroll/report/salary_register/salary_register.py @@ -8,222 +8,323 @@ from frappe.utils import flt import erpnext +salary_slip = frappe.qb.DocType("Salary Slip") +salary_detail = frappe.qb.DocType("Salary Detail") +salary_component = frappe.qb.DocType("Salary Component") def execute(filters=None): if not filters: filters = {} + currency = None if filters.get("currency"): currency = filters.get("currency") company_currency = erpnext.get_company_currency(filters.get("company")) + salary_slips = get_salary_slips(filters, company_currency) if not salary_slips: return [], [] - columns, earning_types, ded_types = get_columns(salary_slips) - ss_earning_map = get_ss_earning_map(salary_slips, currency, company_currency) - ss_ded_map = get_ss_ded_map(salary_slips, currency, company_currency) + earning_types, ded_types = get_earning_and_deduction_types(salary_slips) + columns = get_columns(earning_types, ded_types) + + ss_earning_map = get_salary_slip_details(salary_slips, currency, company_currency, "earnings") + ss_ded_map = get_salary_slip_details(salary_slips, currency, company_currency, "deductions") + doj_map = get_employee_doj_map() data = [] for ss in salary_slips: - row = [ - ss.name, - ss.employee, - ss.employee_name, - doj_map.get(ss.employee), - ss.branch, - ss.department, - ss.designation, - ss.company, - ss.start_date, - ss.end_date, - ss.leave_without_pay, - ss.payment_days, - ] + row = { + "salary_slip_id": ss.name, + "employee": ss.employee, + "employee_name": ss.employee_name, + "data_of_joining": doj_map.get(ss.employee), + "branch": ss.branch, + "department": ss.department, + "designation": ss.designation, + "company": ss.company, + "start_date": ss.start_date, + "end_date": ss.end_date, + "leave_without_pay": ss.leave_without_pay, + "payment_days": ss.payment_days, + "currency": currency or company_currency, + "total_loan_repayment": ss.total_loan_repayment, + } - if ss.branch is not None: - columns[3] = columns[3].replace("-1", "120") - if ss.department is not None: - columns[4] = columns[4].replace("-1", "120") - if ss.designation is not None: - columns[5] = columns[5].replace("-1", "120") - if ss.leave_without_pay is not None: - columns[9] = columns[9].replace("-1", "130") + update_column_width(ss, columns) for e in earning_types: - row.append(ss_earning_map.get(ss.name, {}).get(e)) - - if currency == company_currency: - row += [flt(ss.gross_pay) * flt(ss.exchange_rate)] - else: - row += [ss.gross_pay] + row.update({frappe.scrub(e): ss_earning_map.get(ss.name, {}).get(e)}) for d in ded_types: - row.append(ss_ded_map.get(ss.name, {}).get(d)) - - row.append(ss.total_loan_repayment) + row.update({frappe.scrub(d): ss_ded_map.get(ss.name, {}).get(d)}) if currency == company_currency: - row += [ - flt(ss.total_deduction) * flt(ss.exchange_rate), - flt(ss.net_pay) * flt(ss.exchange_rate), - ] + row.update( + { + "gross_pay": flt(ss.gross_pay) * flt(ss.exchange_rate), + "total_deduction": flt(ss.total_deduction) * flt(ss.exchange_rate), + "net_pay": flt(ss.net_pay) * flt(ss.exchange_rate), + } + ) + else: - row += [ss.total_deduction, ss.net_pay] - row.append(currency or company_currency) + row.update( + {"gross_pay": ss.gross_pay, "total_deduction": ss.total_deduction, "net_pay": ss.net_pay} + ) + data.append(row) return columns, data -def get_columns(salary_slips): - """ +def get_earning_and_deduction_types(salary_slips): + salary_component_and_type = {_("Earning"): [], _("Deduction"): []} + + for salary_compoent in get_salary_components(salary_slips): + component_type = get_salary_component_type(salary_compoent[0]) + salary_component_and_type[_(component_type)].append(salary_compoent[0]) + + return sorted(salary_component_and_type[_("Earning")]), sorted( + salary_component_and_type[_("Deduction")] + ) + + +def update_column_width(ss, columns): + if ss.branch is not None: + columns[3].update({"width": 120}) + if ss.department is not None: + columns[4].update({"width": 120}) + if ss.designation is not None: + columns[5].update({"width": 120}) + if ss.leave_without_pay is not None: + columns[9].update({"width": 120}) + + +def get_columns(earning_types, ded_types): columns = [ - _("Salary Slip ID") + ":Link/Salary Slip:150", - _("Employee") + ":Link/Employee:120", - _("Employee Name") + "::140", - _("Date of Joining") + "::80", - _("Branch") + ":Link/Branch:120", - _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", - _("Company") + ":Link/Company:120", - _("Start Date") + "::80", - _("End Date") + "::80", - _("Leave Without Pay") + ":Float:130", - _("Payment Days") + ":Float:120", - _("Currency") + ":Link/Currency:80" - ] - """ - columns = [ - _("Salary Slip ID") + ":Link/Salary Slip:150", - _("Employee") + ":Link/Employee:120", - _("Employee Name") + "::140", - _("Date of Joining") + "::80", - _("Branch") + ":Link/Branch:-1", - _("Department") + ":Link/Department:-1", - _("Designation") + ":Link/Designation:120", - _("Company") + ":Link/Company:120", - _("Start Date") + "::80", - _("End Date") + "::80", - _("Leave Without Pay") + ":Float:50", - _("Payment Days") + ":Float:120", + { + "label": _("Salary Slip ID"), + "fieldname": "salary_slip_id", + "fieldtype": "Link", + "options": "Salary Slip", + "width": 150, + }, + { + "label": _("Employee"), + "fieldname": "employee", + "fieldtype": "Link", + "options": "Employee", + "width": 120, + }, + { + "label": _("Employee Name"), + "fieldname": "employee_name", + "fieldtype": "Data", + "width": 140, + }, + { + "label": _("Date of Joining"), + "fieldname": "data_of_joining", + "fieldtype": "Date", + "width": 80, + }, + { + "label": _("Branch"), + "fieldname": "branch", + "fieldtype": "Link", + "options": "Branch", + "width": -1, + }, + { + "label": _("Department"), + "fieldname": "department", + "fieldtype": "Link", + "options": "Department", + "width": -1, + }, + { + "label": _("Designation"), + "fieldname": "designation", + "fieldtype": "Link", + "options": "Designation", + "width": 120, + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 120, + }, + { + "label": _("Start Date"), + "fieldname": "start_date", + "fieldtype": "Data", + "width": 80, + }, + { + "label": _("End Date"), + "fieldname": "end_date", + "fieldtype": "Data", + "width": 80, + }, + { + "label": _("Leave Without Pay"), + "fieldname": "leave_without_pay", + "fieldtype": "Float", + "width": 50, + }, + { + "label": _("Payment Days"), + "fieldname": "payment_days", + "fieldtype": "Float", + "width": 120, + }, + { + "label": _("Currency"), + "fieldname": "currency", + "fieldtype": "Link", + "options": "Currency", + "hidden": 1, + }, ] - salary_components = {_("Earning"): [], _("Deduction"): []} + for earning in earning_types: + columns.append( + { + "label": earning, + "fieldname": frappe.scrub(earning), + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ) - for component in frappe.db.sql( - """select distinct sd.salary_component, sc.type - from `tabSalary Detail` sd, `tabSalary Component` sc - where sc.name=sd.salary_component and sd.amount != 0 and sd.parent in (%s)""" - % (", ".join(["%s"] * len(salary_slips))), - tuple([d.name for d in salary_slips]), - as_dict=1, - ): - salary_components[_(component.type)].append(component.salary_component) + columns.append( + { + "label": _("Gross Pay"), + "fieldname": "gross_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ) - columns = ( - columns - + [(e + ":Currency:120") for e in salary_components[_("Earning")]] - + [_("Gross Pay") + ":Currency:120"] - + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] - + [ - _("Loan Repayment") + ":Currency:120", - _("Total Deduction") + ":Currency:120", - _("Net Pay") + ":Currency:120", + for deduction in ded_types: + columns.append( + { + "label": deduction, + "fieldname": frappe.scrub(deduction), + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ) + + columns.extend( + [ + { + "label": _("Loan Repayment"), + "fieldname": "total_loan_repayment", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Total Deduction"), + "fieldname": "total_deduction", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Net Pay"), + "fieldname": "net_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, ] ) - return columns, salary_components[_("Earning")], salary_components[_("Deduction")] + return columns + + +def get_salary_components(salary_slips): + return ( + frappe.qb.from_(salary_detail) + .where((salary_detail.amount != 0) & (salary_detail.parent.isin([d.name for d in salary_slips]))) + .select(salary_detail.salary_component) + .distinct() + ).run(as_list=True) + +def get_salary_component_type(salary_component): + return frappe.db.get_value("Salary Component", salary_component, "type", cache=True) def get_salary_slips(filters, company_currency): - filters.update({"from_date": filters.get("from_date"), "to_date": filters.get("to_date")}) - conditions, filters = get_conditions(filters, company_currency) - salary_slips = frappe.db.sql( - """select * from `tabSalary Slip` where %s - order by employee""" - % conditions, - filters, - as_dict=1, - ) + doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} + + query = frappe.qb.from_(salary_slip).select(salary_slip.star) + + if filters.get("docstatus"): + query = query.where(salary_slip.docstatus == doc_status[filters.get("docstatus")]) + + if filters.get("from_date"): + query = query.where(salary_slip.start_date >= filters.get("from_date")) + + if filters.get("to_date"): + query = query.where(salary_slip.end_date <= filters.get("to_date")) + + if filters.get("company"): + query = query.where(salary_slip.company == filters.get("company")) + + if filters.get("employee"): + query = query.where(salary_slip.employee == filters.get("employee")) + + if filters.get("currency") and filters.get("currency") != company_currency: + query = query.where(salary_slip.currency == filters.get("currency")) + + salary_slips = query.run(as_dict=1) return salary_slips or [] -def get_conditions(filters, company_currency): - conditions = "" - doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2} - - if filters.get("docstatus"): - conditions += "docstatus = {0}".format(doc_status[filters.get("docstatus")]) - - if filters.get("from_date"): - conditions += " and start_date >= %(from_date)s" - if filters.get("to_date"): - conditions += " and end_date <= %(to_date)s" - if filters.get("company"): - conditions += " and company = %(company)s" - if filters.get("employee"): - conditions += " and employee = %(employee)s" - if filters.get("currency") and filters.get("currency") != company_currency: - conditions += " and currency = %(currency)s" - - return conditions, filters - - def get_employee_doj_map(): - return frappe._dict( - frappe.db.sql( - """ - SELECT - employee, - date_of_joining - FROM `tabEmployee` - """ + employee = frappe.qb.DocType("Employee") + + result = (frappe.qb.from_(employee).select(employee.name, employee.date_of_joining)).run() + + return frappe._dict(result) + + +def get_salary_slip_details(salary_slips, currency, company_currency, component_type): + salary_slips = [ss.name for ss in salary_slips] + + result = ( + frappe.qb.from_(salary_slip) + .join(salary_detail) + .on(salary_slip.name == salary_detail.parent) + .where((salary_detail.parent.isin(salary_slips)) & (salary_detail.parentfield == component_type)) + .select( + salary_detail.parent, + salary_detail.salary_component, + salary_detail.amount, + salary_slip.exchange_rate, ) - ) + ).run(as_dict=1) + ss_map = {} -def get_ss_earning_map(salary_slips, currency, company_currency): - ss_earnings = frappe.db.sql( - """select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name - from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" - % (", ".join(["%s"] * len(salary_slips))), - tuple([d.name for d in salary_slips]), - as_dict=1, - ) - - ss_earning_map = {} - for d in ss_earnings: - ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) + for d in result: + ss_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) if currency == company_currency: - ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt( + ss_map[d.parent][d.salary_component] += flt(d.amount) * flt( d.exchange_rate if d.exchange_rate else 1 ) else: - ss_earning_map[d.parent][d.salary_component] += flt(d.amount) + ss_map[d.parent][d.salary_component] += flt(d.amount) - return ss_earning_map - - -def get_ss_ded_map(salary_slips, currency, company_currency): - ss_deductions = frappe.db.sql( - """select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name - from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)""" - % (", ".join(["%s"] * len(salary_slips))), - tuple([d.name for d in salary_slips]), - as_dict=1, - ) - - ss_ded_map = {} - for d in ss_deductions: - ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0) - if currency == company_currency: - ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt( - d.exchange_rate if d.exchange_rate else 1 - ) - else: - ss_ded_map[d.parent][d.salary_component] += flt(d.amount) - - return ss_ded_map + return ss_map From fc42e026ab09f657dbcfaffd37c6a2f2ab858673 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 26 Apr 2023 15:44:34 +0530 Subject: [PATCH 46/76] chore: fix linter (cherry picked from commit 31bda37970d4d4e4c1a83cb3d288d49d164afa63) --- erpnext/payroll/report/salary_register/salary_register.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/payroll/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py index d9d68212665..94369e4a468 100644 --- a/erpnext/payroll/report/salary_register/salary_register.py +++ b/erpnext/payroll/report/salary_register/salary_register.py @@ -12,6 +12,7 @@ salary_slip = frappe.qb.DocType("Salary Slip") salary_detail = frappe.qb.DocType("Salary Detail") salary_component = frappe.qb.DocType("Salary Component") + def execute(filters=None): if not filters: filters = {} @@ -260,6 +261,7 @@ def get_salary_components(salary_slips): .distinct() ).run(as_list=True) + def get_salary_component_type(salary_component): return frappe.db.get_value("Salary Component", salary_component, "type", cache=True) From ef2d4febddc3bdebd6875b6231e9d0ee945e4450 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 3 May 2023 06:38:15 +0000 Subject: [PATCH 47/76] chore(release): Bumped to Version 13.50.0 # [13.50.0](https://github.com/frappe/erpnext/compare/v13.49.14...v13.50.0) (2023-05-03) ### Bug Fixes * allow user to set standard deductions in income tax slab without allowing other exemptions ([c5261cd](https://github.com/frappe/erpnext/commit/c5261cde9c08c0ba78ce192f015d5e7fbefe7177)) * check for session user rather than owner ([7d6e2f9](https://github.com/frappe/erpnext/commit/7d6e2f979fe4575e9efda97e67f49ba1619d90a1)) * conflicts ([778ba69](https://github.com/frappe/erpnext/commit/778ba6956cb92148aa0e4b495658957cb6e06ca0)) * conflicts ([b19b0a4](https://github.com/frappe/erpnext/commit/b19b0a4a98b1f74785f18d0d754827257ac8c2b0)) * conflicts ([6bdf143](https://github.com/frappe/erpnext/commit/6bdf143084ce02eb2e08b7c61922541fd48ca5b1)) * don't allow to make reposting for the closed period ([b31d8ee](https://github.com/frappe/erpnext/commit/b31d8eec057f3c34b4139c427b8d07ffb0054e59)) * handle expected_value_after_useful_life properly in asset value adjustment (backport [#35117](https://github.com/frappe/erpnext/issues/35117)) ([#35120](https://github.com/frappe/erpnext/issues/35120)) ([635559d](https://github.com/frappe/erpnext/commit/635559d9052b9c65a16e7ddf3ec92fc9fbfb5334)) * handle finance book properly in trial balance and general ledger ([#35136](https://github.com/frappe/erpnext/issues/35136)) ([9a37603](https://github.com/frappe/erpnext/commit/9a376039aae56e4632faab8a4406ef8818082e67)) * Hyperlink in Quality Inspection Summary ([54388e8](https://github.com/frappe/erpnext/commit/54388e8d925e06031738c23d4590efd9025f4cc7)) * Naming series error in Journal Entry template ([#35084](https://github.com/frappe/erpnext/issues/35084)) ([d3c769c](https://github.com/frappe/erpnext/commit/d3c769c183beba4864b4da113082a7c49b051933)) * per_billed condition for Payment Entry ([#34969](https://github.com/frappe/erpnext/issues/34969)) ([563e5c0](https://github.com/frappe/erpnext/commit/563e5c0b6947063745cc2103d3d0e14e2473ba7e)) * test case ([db6d0e0](https://github.com/frappe/erpnext/commit/db6d0e03f5b2a5db4f121856d42828dbbb4b5bdc)) ### Features * validate repost item valuation against accounts freeze date ([a852dc1](https://github.com/frappe/erpnext/commit/a852dc1f11db26bf744f1657a2fc0dfdc7c8a137)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 80758121e1e..b9bfe64671e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.49.14" +__version__ = "13.50.0" def get_default_company(user=None): From af8142cf851566826ac5ca1bae701302e1632a4d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 6 May 2023 23:41:57 +0530 Subject: [PATCH 48/76] fix: handle empty FBs properly in TB and GL [v14] (backport #35189) (backport #35192) (#35195) fix: handle empty FBs properly in TB and GL [v14] (backport #35189) (#35192) fix: handle empty FBs properly in TB and GL [v14] (#35189) fix: handle empty FBs properly in TB and GL (cherry picked from commit ed5f39c2c2f7cb960e0cccac80b252cdce1077cf) Co-authored-by: Anand Baburajan (cherry picked from commit e2af66c7beab2e1fd51e221f7440cb14ab15a316) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/accounts/report/financial_statements.py | 12 ++++++++---- .../accounts/report/general_ledger/general_ledger.py | 8 ++++---- .../accounts/report/trial_balance/trial_balance.py | 10 ++++++---- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index d61330e0cfb..03ae13c53a4 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -532,14 +532,18 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): _("To use a different finance book, please uncheck 'Include Default Book Entries'") ) else: - additional_conditions.append("(finance_book in (%(finance_book)s) OR finance_book IS NULL)") + additional_conditions.append( + "(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)" + ) else: - additional_conditions.append("(finance_book in (%(company_fb)s) OR finance_book IS NULL)") + additional_conditions.append("(finance_book in (%(company_fb)s, '') OR finance_book IS NULL)") else: if filters.get("finance_book"): - additional_conditions.append("(finance_book in (%(finance_book)s) OR finance_book IS NULL)") + additional_conditions.append( + "(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)" + ) else: - additional_conditions.append("(finance_book IS NULL)") + additional_conditions.append("(finance_book in ('') OR finance_book IS NULL)") if accounting_dimensions: for dimension in accounting_dimensions: diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 34534f883ed..41088370897 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -296,14 +296,14 @@ def get_conditions(filters): _("To use a different finance book, please uncheck 'Include Default Book Entries'") ) else: - conditions.append("(finance_book in (%(finance_book)s) OR finance_book IS NULL)") + conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") else: - conditions.append("(finance_book in (%(company_fb)s) OR finance_book IS NULL)") + conditions.append("(finance_book in (%(company_fb)s, '') OR finance_book IS NULL)") else: if filters.get("finance_book"): - conditions.append("(finance_book in (%(finance_book)s) OR finance_book IS NULL)") + conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") else: - conditions.append("(finance_book IS NULL)") + conditions.append("(finance_book in ('') OR finance_book IS NULL)") if not filters.get("show_cancelled_entries"): conditions.append("is_cancelled = 0") diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index d2300cd1238..3bcd1bc68d0 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -166,14 +166,16 @@ def get_rootwise_opening_balances(filters, report_type): _("To use a different finance book, please uncheck 'Include Default Book Entries'") ) else: - additional_conditions += " AND (finance_book in (%(finance_book)s) OR finance_book IS NULL)" + additional_conditions += ( + " AND (finance_book in (%(finance_book)s, '') OR finance_book IS NULL)" + ) else: - additional_conditions += " AND (finance_book in (%(company_fb)s) OR finance_book IS NULL)" + additional_conditions += " AND (finance_book in (%(company_fb)s, '') OR finance_book IS NULL)" else: if filters.get("finance_book"): - additional_conditions += " AND (finance_book in (%(finance_book)s) OR finance_book IS NULL)" + additional_conditions += " AND (finance_book in (%(finance_book)s, '') OR finance_book IS NULL)" else: - additional_conditions += " AND (finance_book IS NULL)" + additional_conditions += " AND (finance_book in ('') OR finance_book IS NULL)" accounting_dimensions = get_accounting_dimensions(as_list=False) From 40cfd5215c17e3a4a3f8b3cbb3f04dd48362016a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 6 May 2023 18:13:48 +0000 Subject: [PATCH 49/76] chore(release): Bumped to Version 13.50.1 ## [13.50.1](https://github.com/frappe/erpnext/compare/v13.50.0...v13.50.1) (2023-05-06) ### Bug Fixes * handle empty FBs properly in TB and GL [v14] (backport [#35189](https://github.com/frappe/erpnext/issues/35189)) (backport [#35192](https://github.com/frappe/erpnext/issues/35192)) ([#35195](https://github.com/frappe/erpnext/issues/35195)) ([af8142c](https://github.com/frappe/erpnext/commit/af8142cf851566826ac5ca1bae701302e1632a4d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b9bfe64671e..4a9dc8dd91b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.50.0" +__version__ = "13.50.1" def get_default_company(user=None): From 1380f7a7ec84bcf27646cba760e783bedbd1d20b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 10 May 2023 05:40:17 +0000 Subject: [PATCH 50/76] chore(release): Bumped to Version 13.50.2 ## [13.50.2](https://github.com/frappe/erpnext/compare/v13.50.1...v13.50.2) (2023-05-10) ### Bug Fixes * handle empty FBs properly in TB and GL [v14] (backport [#35189](https://github.com/frappe/erpnext/issues/35189)) ([#35192](https://github.com/frappe/erpnext/issues/35192)) ([e2af66c](https://github.com/frappe/erpnext/commit/e2af66c7beab2e1fd51e221f7440cb14ab15a316)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4a9dc8dd91b..b09a9b28454 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.50.1" +__version__ = "13.50.2" def get_default_company(user=None): From ac26e4ba2a1e7e5c9fb30b59fadccd7f0fdbabf5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 May 2023 15:38:35 +0530 Subject: [PATCH 51/76] fix: internal transfer condition (cherry picked from commit b5a2ccf21d0b0ca9fb4c8d958d29d60dcfb5ecb2) (cherry picked from commit a1d717053acd0b3b5dd1e746cf6ea777311846bf) --- .../doctype/purchase_receipt/purchase_receipt.py | 2 +- erpnext/stock/stock_ledger.py | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 28bff671f62..0f4606d3947 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -369,7 +369,7 @@ class PurchaseReceipt(BuyingController): ) outgoing_amount = d.base_net_amount - if self.is_internal_supplier and d.valuation_rate: + if self.is_internal_transfer() and d.valuation_rate: outgoing_amount = abs( frappe.db.get_value( "Stock Ledger Entry", diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 361e00a8449..2f876cddac8 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -536,7 +536,7 @@ class update_entries_after(object): sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and sle.voucher_detail_no and sle.actual_qty < 0 - and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + and is_internal_transfer(sle) ): sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle) @@ -648,7 +648,7 @@ class update_entries_after(object): elif ( sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and sle.voucher_detail_no - and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + and is_internal_transfer(sle) ): rate = get_incoming_rate_for_inter_company_transfer(sle) else: @@ -1488,3 +1488,15 @@ def get_incoming_rate_for_inter_company_transfer(sle) -> float: ) return rate + + +def is_internal_transfer(sle): + data = frappe.get_cached_value( + sle.voucher_type, + sle.voucher_no, + ["is_internal_supplier", "represents_company", "company"], + as_dict=True, + ) + + if data.is_internal_supplier and data.represents_company == data.company: + return True From 7626d51db185e5cf0733cb3b49eb862d5a9a7cff Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 11 May 2023 17:26:55 +0000 Subject: [PATCH 52/76] chore(release): Bumped to Version 13.50.3 ## [13.50.3](https://github.com/frappe/erpnext/compare/v13.50.2...v13.50.3) (2023-05-11) ### Bug Fixes * internal transfer condition ([ac26e4b](https://github.com/frappe/erpnext/commit/ac26e4ba2a1e7e5c9fb30b59fadccd7f0fdbabf5)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b09a9b28454..680552f42c8 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.50.2" +__version__ = "13.50.3" def get_default_company(user=None): From c2ae8eaec0241064769c354160d3a39da607009e Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 16 May 2023 16:59:38 +0000 Subject: [PATCH 53/76] chore(release): Bumped to Version 13.50.4 ## [13.50.4](https://github.com/frappe/erpnext/compare/v13.50.3...v13.50.4) (2023-05-16) ### Bug Fixes * add missing options for `Content Align` ([e37b903](https://github.com/frappe/erpnext/commit/e37b9030fb101804e822e4e766cc66470c9e9399)) * cancelled vouchers in tax withheld vouchers list ([#35309](https://github.com/frappe/erpnext/issues/35309)) ([188cfc2](https://github.com/frappe/erpnext/commit/188cfc2e3c0876233bceb169896ccddaef191eb9)) * internal transfer condition ([a1d7170](https://github.com/frappe/erpnext/commit/a1d717053acd0b3b5dd1e746cf6ea777311846bf)) * **Salary Slip:** exchange rate overwritten on form load ([#507](https://github.com/frappe/erpnext/issues/507)) ([#35245](https://github.com/frappe/erpnext/issues/35245)) ([8b3d6ee](https://github.com/frappe/erpnext/commit/8b3d6ee7b0cd080daa515549b24b409e2570b34f)) * update reference data for statistical component ([77f548c](https://github.com/frappe/erpnext/commit/77f548c8145c69d4ceaddcd1f0b0449729c5291d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 680552f42c8..636a7cca7dd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.50.3" +__version__ = "13.50.4" def get_default_company(user=None): From 2e3f8e8846398df0d3cd548f8c2430ffdbb95df1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 17 May 2023 13:40:33 +0530 Subject: [PATCH 54/76] fix: tds incorrectly calculated for invoice that are below threshold Two purchase invoices for the same supplier, using different tax withholding categories have this issue. | Category | single | cumulative | |----------+--------+------------| | cat1 | 100 | 500 | | cat2 | 1000 | 5000 | 1. PINV1 of net total: 105/- uses cat1. TDS is calculated as it breached single threshold 2. PINV2 of net total: 200/- uses cat2. TDS incorrectly calculated as PINV1 already has TDS calculated and 'consider_party_ledger_amount' is enabled. (cherry picked from commit 84b7c1bba09f89a390a17898b4d8fa665adcbc8f) --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index ea3ec5fc606..778bd0881e3 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -274,7 +274,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): "docstatus": 1, } - if not tax_details.get("consider_party_ledger_amount") and doctype != "Sales Invoice": + if doctype != "Sales Invoice": filters.update( {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) From d316955d181c6519349a84cfbdb5d9d604e0d9f3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 May 2023 12:00:59 +0530 Subject: [PATCH 55/76] fix(test): cumulative threshold checks (cherry picked from commit 132846bbd107a921dfbfde9240f368bea9382cf7) --- .../test_tax_withholding_category.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index e80fe11ab30..913326e9c7b 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -110,9 +110,9 @@ class TestTaxWithholdingCategory(unittest.TestCase): invoices.append(pi1) # Cumulative threshold is 30000 - # Threshold calculation should be on both the invoices - # TDS should be applied only on 1000 - self.assertEqual(pi1.taxes[0].tax_amount, 1000) + # Threshold calculation should be only on the Second invoice + # Second didn't breach, no TDS should be applied + self.assertEqual(pi1.taxes, []) for d in reversed(invoices): d.cancel() From 6bc8749eafb1a1387bd0d8a3eff22a9a5e419857 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 18 May 2023 14:55:22 +0000 Subject: [PATCH 56/76] chore(release): Bumped to Version 13.50.5 ## [13.50.5](https://github.com/frappe/erpnext/compare/v13.50.4...v13.50.5) (2023-05-18) ### Bug Fixes * tds incorrectly calculated for invoice that are below threshold ([2e3f8e8](https://github.com/frappe/erpnext/commit/2e3f8e8846398df0d3cd548f8c2430ffdbb95df1)) * **test:** cumulative threshold checks ([d316955](https://github.com/frappe/erpnext/commit/d316955d181c6519349a84cfbdb5d9d604e0d9f3)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 636a7cca7dd..6fd8a5971b6 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.50.4" +__version__ = "13.50.5" def get_default_company(user=None): From fd04bd0f72cdd8d05a7fd8a1deb1390379b8a313 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 24 May 2023 03:03:13 +0000 Subject: [PATCH 57/76] chore(release): Bumped to Version 13.50.6 ## [13.50.6](https://github.com/frappe/erpnext/compare/v13.50.5...v13.50.6) (2023-05-24) ### Bug Fixes * allow over-payment against SO ([#35079](https://github.com/frappe/erpnext/issues/35079)) ([eb243c2](https://github.com/frappe/erpnext/commit/eb243c2470b7a13291cddb12a8d42fbe5d83d37f)) * bypass flag in Customer Group wasn't effective ([f0c9d89](https://github.com/frappe/erpnext/commit/f0c9d89aab6aa61da44553ed88918930eb13228d)) * change field-type to remove currency field from total row in export ([f65be40](https://github.com/frappe/erpnext/commit/f65be40037f381955b38971b314c988d3fff8f6e)) * consider 0 if rate/qty are null (backport [#35338](https://github.com/frappe/erpnext/issues/35338)) ([#35341](https://github.com/frappe/erpnext/issues/35341)) ([387f8b9](https://github.com/frappe/erpnext/commit/387f8b9e1a98a80b7ddd985d7a80cb346c65c9a2)) * depreciation schedule for existing assets [v14] (backport [#35255](https://github.com/frappe/erpnext/issues/35255)) ([#35347](https://github.com/frappe/erpnext/issues/35347)) ([7506132](https://github.com/frappe/erpnext/commit/7506132861bcfe5397e4073642f3aa459e6da60b)) * error while saving job card ([d6427cf](https://github.com/frappe/erpnext/commit/d6427cfe5362ab1f20151ac1f313453ce57d9292)) * get_query filters ([2aa7729](https://github.com/frappe/erpnext/commit/2aa7729243eeb7f2036059a96a941c4492015548)) * Incorrect Earned Leaves Proration ([#35156](https://github.com/frappe/erpnext/issues/35156)) ([dc04b24](https://github.com/frappe/erpnext/commit/dc04b24234cfce54c9adef2cb9c1f0fdaef8cfd5)) * linter ([0a42e6f](https://github.com/frappe/erpnext/commit/0a42e6ff0f6dd1c7ef53031bdf58aa422c09bb73)) * non manufacturing items/fixed asset items in BOM ([66ba74f](https://github.com/frappe/erpnext/commit/66ba74f3fc60a5faad5d80f542c61895060a5a6c)) * Pick List TypeError ([137898d](https://github.com/frappe/erpnext/commit/137898d55d8dbac591c28b298ba2761b85e0f579)) * tds incorrectly calculated for invoice that are below threshold ([6c170ab](https://github.com/frappe/erpnext/commit/6c170abdf939b26d07964964fcd69de68cadaf6d)) * **test:** cumulative threshold checks ([06deecb](https://github.com/frappe/erpnext/commit/06deecbd9272a0af855df2bd9cb338e0a8ec4862)) * use flt instead of mandatory field ([f63b866](https://github.com/frappe/erpnext/commit/f63b866de301bf405c8df6d96f7c6760e239ee97)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6fd8a5971b6..ed83866482b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.50.5" +__version__ = "13.50.6" def get_default_company(user=None): From b5b34c14b21fbde02880a043434c60bbc1070602 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 31 May 2023 05:49:42 +0000 Subject: [PATCH 58/76] chore(release): Bumped to Version 13.51.0 # [13.51.0](https://github.com/frappe/erpnext/compare/v13.50.6...v13.51.0) (2023-05-31) ### Bug Fixes * force to do reposting for cancelled document ([0228933](https://github.com/frappe/erpnext/commit/022893391b92f8eb771ec97162be72cd0bbc906b)) * **Gross Profit:** 'company' column is ambiguous in filter ([270eb1d](https://github.com/frappe/erpnext/commit/270eb1db4df0d57f09910a37ab61b2037953262f)) * incorrect `POS Reserved Qty` in `Stock Projected Qty` Report ([#35437](https://github.com/frappe/erpnext/issues/35437)) ([139a193](https://github.com/frappe/erpnext/commit/139a193f1ded455d249d762b0a055177ffec6d5c)) * incorrect depr schedule and posting dates on selling of existing assets [v13] ([#35404](https://github.com/frappe/erpnext/issues/35404)) ([20d3381](https://github.com/frappe/erpnext/commit/20d3381010061dea96371430b6b0258bd785db8e)) * monthly WDV depr schedule for existing assets [v13] ([#35461](https://github.com/frappe/erpnext/issues/35461)) ([6f43829](https://github.com/frappe/erpnext/commit/6f43829c32eef9056c6378217e9c8893a857d130)) * retention stock entry: grab conversion factor from source ([d8dd22a](https://github.com/frappe/erpnext/commit/d8dd22adafbd1cee33ab4b00841973517dc116cc)) ### Features * Allow ceil & floor functions in salary slip formulae ([#35475](https://github.com/frappe/erpnext/issues/35475)) ([63fba9d](https://github.com/frappe/erpnext/commit/63fba9db3969167ae804230a9e01678092df4aa1)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ed83866482b..34c80c22311 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.50.6" +__version__ = "13.51.0" def get_default_company(user=None): From f182fc1f8ee4b3bdee1df2a8e28e4282e3871f02 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 7 Jun 2023 06:18:08 +0000 Subject: [PATCH 59/76] chore(release): Bumped to Version 13.51.1 ## [13.51.1](https://github.com/frappe/erpnext/compare/v13.51.0...v13.51.1) (2023-06-07) ### Bug Fixes * Interest Accrual on Loan Topup ([#35555](https://github.com/frappe/erpnext/issues/35555)) ([1415f40](https://github.com/frappe/erpnext/commit/1415f40dfb5ab1b070a600e5520034525b3e5ec8)) * Task gantt popup style ([5544801](https://github.com/frappe/erpnext/commit/55448017d744687568473f89b1e747cee38fb790)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 34c80c22311..5454855737d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.0" +__version__ = "13.51.1" def get_default_company(user=None): From 9a659254e393717a249603a75fc6d4afe478c5fa Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 14 Jun 2023 06:09:31 +0000 Subject: [PATCH 60/76] chore(release): Bumped to Version 13.51.2 ## [13.51.2](https://github.com/frappe/erpnext/compare/v13.51.1...v13.51.2) (2023-06-14) ### Bug Fixes * **accounts:** validate payment entry references with latest data. (backport [#31166](https://github.com/frappe/erpnext/issues/31166)) ([#35674](https://github.com/frappe/erpnext/issues/35674)) ([4d4f218](https://github.com/frappe/erpnext/commit/4d4f218175360e7215f604e526397d615ea5164e)) * Asset Depreciation Ledger Report - Add Total Row Checkbox Enabled ([3831c79](https://github.com/frappe/erpnext/commit/3831c7920d20eeafb78c783c2d1029fc54ea0bfc)) * calculate wdv depr schedule properly for existing assets [v13] ([#35615](https://github.com/frappe/erpnext/issues/35615)) ([97f4af8](https://github.com/frappe/erpnext/commit/97f4af8d978ab43929f26a4c11be3883cd300cbf)) * CSS not applied to product title (backport [#35582](https://github.com/frappe/erpnext/issues/35582)) ([#35635](https://github.com/frappe/erpnext/issues/35635)) ([1b69b37](https://github.com/frappe/erpnext/commit/1b69b3722912aa2403a779a613e714695b6ff615)) * don't set default payment amount in case of invoice return (backport [#35645](https://github.com/frappe/erpnext/issues/35645)) ([#35648](https://github.com/frappe/erpnext/issues/35648)) ([8e3636f](https://github.com/frappe/erpnext/commit/8e3636ff53636b1b79ee0207321e7c2bc5bb59ed)) * Lower deduction certificate not getting applied ([#35667](https://github.com/frappe/erpnext/issues/35667)) ([c2bf8e3](https://github.com/frappe/erpnext/commit/c2bf8e35024292865f2db3b0bedb9d1c4ef1c854)) * make showing taxes as table in print configurable ([#35672](https://github.com/frappe/erpnext/issues/35672)) ([4c2c037](https://github.com/frappe/erpnext/commit/4c2c037a860abf33267e9a3841b6e996420dfadd)) * Project in item-wise sales register ([#35596](https://github.com/frappe/erpnext/issues/35596)) ([9d5b500](https://github.com/frappe/erpnext/commit/9d5b50006023da0a8a3b867476cb38ba2407a220)) * savepoint policy assignment submission, log errors & inform the user about failures ([#35507](https://github.com/frappe/erpnext/issues/35507)) ([4a35ff0](https://github.com/frappe/erpnext/commit/4a35ff0e5763d4a124de0d84be6a1dce288e1a37)) ### Performance Improvements * refactor `get_all_nodes` in Org Chart ([986a90e](https://github.com/frappe/erpnext/commit/986a90efe05661ec80db7b6e3cde82a698b58438)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5454855737d..062e8c10772 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.1" +__version__ = "13.51.2" def get_default_company(user=None): From f8a8cf30462e3646750e30b409ff0a10e93893fa Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 16 Jun 2023 11:15:42 +0530 Subject: [PATCH 61/76] fix: Incorrect field while calculating Tax withholding net total (cherry picked from commit b95d45981295ab3fb9605c0ef83869e5c4c83059) --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 6c11e51b267..32eaec8cf3e 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -526,7 +526,7 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net "docstatus": 1, "posting_date": ("between", (ldc.valid_from, ldc.valid_upto)), }, - "sum(tax_withholding_net_total)", + "sum(base_net_total)", ) if is_valid_certificate( From ac9f1fefe66bc3756abcced50aa392662689158f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 16 Jun 2023 05:51:43 +0000 Subject: [PATCH 62/76] chore(release): Bumped to Version 13.51.3 ## [13.51.3](https://github.com/frappe/erpnext/compare/v13.51.2...v13.51.3) (2023-06-16) ### Bug Fixes * Incorrect field while calculating Tax withholding net total ([f8a8cf3](https://github.com/frappe/erpnext/commit/f8a8cf30462e3646750e30b409ff0a10e93893fa)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 062e8c10772..53b6073a0db 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.2" +__version__ = "13.51.3" def get_default_company(user=None): From a98a13b683f4333cf214f43d202976e8e815779c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 16 Jun 2023 12:17:31 +0530 Subject: [PATCH 63/76] fix: Incorrect field while calculating Tax withholding net total (cherry picked from commit 571c977e8e871ff1b12350fc85fc3a995a34789a) # Conflicts: # erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py --- .../tax_withholding_category.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 32eaec8cf3e..0c9df9656dd 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -518,6 +518,7 @@ def get_invoice_total_without_tcs(inv, tax_details): def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net_total): tds_amount = 0 +<<<<<<< HEAD limit_consumed = frappe.db.get_value( "Purchase Invoice", { @@ -527,6 +528,21 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net "posting_date": ("between", (ldc.valid_from, ldc.valid_upto)), }, "sum(base_net_total)", +======= + + limit_consumed = flt( + frappe.db.get_all( + "Purchase Invoice", + filters={ + "supplier": ("in", parties), + "apply_tds": 1, + "docstatus": 1, + "tax_withholding_category": ldc.tax_withholding_category, + "posting_date": ("between", (ldc.valid_from, ldc.valid_upto)), + }, + fields=["sum(base_net_total) as limit_consumed"], + )[0].get("limit_consumed") +>>>>>>> 571c977e8e (fix: Incorrect field while calculating Tax withholding net total) ) if is_valid_certificate( From d49a8ad74fb4c5ee55fb1c1abb7c1d740a8050fd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 16 Jun 2023 12:22:23 +0530 Subject: [PATCH 64/76] chore: resolve conflicts --- .../tax_withholding_category.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 0c9df9656dd..86c6341b68d 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -518,17 +518,6 @@ def get_invoice_total_without_tcs(inv, tax_details): def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net_total): tds_amount = 0 -<<<<<<< HEAD - limit_consumed = frappe.db.get_value( - "Purchase Invoice", - { - "supplier": ("in", parties), - "apply_tds": 1, - "docstatus": 1, - "posting_date": ("between", (ldc.valid_from, ldc.valid_upto)), - }, - "sum(base_net_total)", -======= limit_consumed = flt( frappe.db.get_all( @@ -542,7 +531,6 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net }, fields=["sum(base_net_total) as limit_consumed"], )[0].get("limit_consumed") ->>>>>>> 571c977e8e (fix: Incorrect field while calculating Tax withholding net total) ) if is_valid_certificate( From 6400a574b6234c08a08d36b3ec3c9aa0ccb4f76e Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 16 Jun 2023 06:54:26 +0000 Subject: [PATCH 65/76] chore(release): Bumped to Version 13.51.4 ## [13.51.4](https://github.com/frappe/erpnext/compare/v13.51.3...v13.51.4) (2023-06-16) ### Bug Fixes * Incorrect field while calculating Tax withholding net total ([a98a13b](https://github.com/frappe/erpnext/commit/a98a13b683f4333cf214f43d202976e8e815779c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 53b6073a0db..48a02af6213 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.3" +__version__ = "13.51.4" def get_default_company(user=None): From 75b3423ab10f8c046d58805a95d5dbe19aa96fa9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 12:53:00 +0530 Subject: [PATCH 66/76] fix: Allocated amount validation for other party types (#35741) * fix: Allocated amount validation for other party types (#35741) * fix: Allocated amount validation for other party types * chore: Validation for return allocations * chore: minor typo --------- Co-authored-by: anandbaburajan (cherry picked from commit 9d27a25e5f2335386402f96f83a10729695b20c8) * chore: remove unnecessary donor party type check --------- Co-authored-by: Deepesh Garg Co-authored-by: Anand Baburajan (cherry picked from commit 3d0add81fa89a639428d484c2f0d25a409a3cd2a) --- .../doctype/payment_entry/payment_entry.py | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 90ae1d792c7..9db4c45dae0 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -150,9 +150,22 @@ class PaymentEntry(AccountsController): ) def validate_allocated_amount(self): - if self.payment_type == "Internal Transfer" or self.party_type in ("Donor"): + if self.payment_type == "Internal Transfer": return + if self.party_type in ("Customer", "Supplier"): + self.validate_allocated_amount_with_latest_data() + else: + fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") + for d in self.get("references"): + if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(d.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) + + # Check for negative outstanding invoices as well + if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) + + def validate_allocated_amount_with_latest_data(self): latest_references = get_outstanding_reference_documents( { "posting_date": self.posting_date, @@ -170,7 +183,7 @@ class PaymentEntry(AccountsController): d = frappe._dict(d) latest_lookup.update({(d.voucher_type, d.voucher_no): d}) - for d in self.get("references").copy(): + for d in self.get("references"): latest = latest_lookup.get((d.reference_doctype, d.reference_name)) # The reference has already been fully paid @@ -189,18 +202,14 @@ class PaymentEntry(AccountsController): ).format(d.reference_doctype, d.reference_name) ) - d.outstanding_amount = latest.outstanding_amount - fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") - if (flt(d.allocated_amount)) > 0: - if flt(d.allocated_amount) > flt(d.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) # Check for negative outstanding invoices as well - if flt(d.allocated_amount) < 0: - if flt(d.allocated_amount) < flt(d.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) def delink_advance_entry_references(self): for reference in self.references: From 0ec74b059e4a33014647e2bec83d7e9ba82ce543 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 19 Jun 2023 11:38:29 +0000 Subject: [PATCH 67/76] chore(release): Bumped to Version 13.51.5 ## [13.51.5](https://github.com/frappe/erpnext/compare/v13.51.4...v13.51.5) (2023-06-19) ### Bug Fixes * Allocated amount validation for other party types ([#35741](https://github.com/frappe/erpnext/issues/35741)) ([75b3423](https://github.com/frappe/erpnext/commit/75b3423ab10f8c046d58805a95d5dbe19aa96fa9)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 48a02af6213..975199a0656 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.4" +__version__ = "13.51.5" def get_default_company(user=None): From 60a170d1a45b6e12497500282818e0805a39a6ef Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 21 Jun 2023 02:06:13 +0000 Subject: [PATCH 68/76] chore(release): Bumped to Version 13.51.6 ## [13.51.6](https://github.com/frappe/erpnext/compare/v13.51.5...v13.51.6) (2023-06-21) ### Bug Fixes * account group totals calculation to consider include_in_gross ([f22969d](https://github.com/frappe/erpnext/commit/f22969d2665bd47e1b9f6428f197025f52334d4c)) * add total col for gross and net profit ([e899c30](https://github.com/frappe/erpnext/commit/e899c30428ce26e2a175708f968cccac49253eef)) * add validation for QI in PR (backport [#35677](https://github.com/frappe/erpnext/issues/35677)) ([#35758](https://github.com/frappe/erpnext/issues/35758)) ([0a8b714](https://github.com/frappe/erpnext/commit/0a8b7148a5bc7854c04e1d57d3e874eecb51da8d)) * Allocated amount validation for other party types ([#35741](https://github.com/frappe/erpnext/issues/35741)) ([3d0add8](https://github.com/frappe/erpnext/commit/3d0add81fa89a639428d484c2f0d25a409a3cd2a)) * date and finance book fixes in fixed asset register (backport [#35751](https://github.com/frappe/erpnext/issues/35751)) ([#35800](https://github.com/frappe/erpnext/issues/35800)) ([aa8446d](https://github.com/frappe/erpnext/commit/aa8446d7949c8d61d3a759bf0da46ef953b54bc2)) * don't add GL Entry for Acc. Depr. while scrapping non-depreciable assets (backport [#35714](https://github.com/frappe/erpnext/issues/35714)) ([#35716](https://github.com/frappe/erpnext/issues/35716)) ([0e11317](https://github.com/frappe/erpnext/commit/0e113173032aba443fc542d50b672fc30aaea750)) * fix get outstanding invoices btn and add get outstanding orders btn (backport [#35776](https://github.com/frappe/erpnext/issues/35776)) ([#35788](https://github.com/frappe/erpnext/issues/35788)) ([04990d5](https://github.com/frappe/erpnext/commit/04990d51db5e68ec5c0405e3c0cfbc639b1c1f80)) * Incorrect field while calculating Tax withholding net total ([571c977](https://github.com/frappe/erpnext/commit/571c977e8e871ff1b12350fc85fc3a995a34789a)) * Incorrect field while calculating Tax withholding net total ([b95d459](https://github.com/frappe/erpnext/commit/b95d45981295ab3fb9605c0ef83869e5c4c83059)) * loan interest accrual date ([#35695](https://github.com/frappe/erpnext/issues/35695)) ([46d0b7d](https://github.com/frappe/erpnext/commit/46d0b7d3170c798c156cf2d124b77a814108611f)) ### Performance Improvements * index `purpose` in `Stock Entry` (backport [#35782](https://github.com/frappe/erpnext/issues/35782)) ([#35784](https://github.com/frappe/erpnext/issues/35784)) ([7239e83](https://github.com/frappe/erpnext/commit/7239e839a0c948573895c067b6228cda99ced949)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 975199a0656..2e9a7f61a97 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.5" +__version__ = "13.51.6" def get_default_company(user=None): From 26489121f33bc6c07337982f31869bf3b517d70c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 28 Jun 2023 16:08:20 +0000 Subject: [PATCH 69/76] chore(release): Bumped to Version 13.51.7 ## [13.51.7](https://github.com/frappe/erpnext/compare/v13.51.6...v13.51.7) (2023-06-28) ### Bug Fixes * asset movement (backport [#35918](https://github.com/frappe/erpnext/issues/35918)) ([#35924](https://github.com/frappe/erpnext/issues/35924)) ([0bcd047](https://github.com/frappe/erpnext/commit/0bcd0476a24b5c75b03013d1b897ba7c93b1a205)) * employee link fields in payroll reports ([#619](https://github.com/frappe/erpnext/issues/619)) ([#35845](https://github.com/frappe/erpnext/issues/35845)) ([6c4dff3](https://github.com/frappe/erpnext/commit/6c4dff38da24b67febda67728416389ccd3b7539)) * filter parent warehouses not showing (backport [#35897](https://github.com/frappe/erpnext/issues/35897)) ([#35900](https://github.com/frappe/erpnext/issues/35900)) ([bcfd770](https://github.com/frappe/erpnext/commit/bcfd7708f265df9160e24adeaed9e19714e23896)) * frappe.exceptions.DoesNotExistError: DocType KSA VAT Setting ([#35797](https://github.com/frappe/erpnext/issues/35797)) ([3785fe6](https://github.com/frappe/erpnext/commit/3785fe6927ef620d34be649526c5cc92c57879a8)), closes [#35795](https://github.com/frappe/erpnext/issues/35795) * show non-depreciable assets in fixed asset register (backport [#35858](https://github.com/frappe/erpnext/issues/35858)) ([#35861](https://github.com/frappe/erpnext/issues/35861)) ([2e2c319](https://github.com/frappe/erpnext/commit/2e2c319f20eb7d034fba10a4b6b804775fa97e04)) * TDS amount calculation post LDC breach ([#35886](https://github.com/frappe/erpnext/issues/35886)) ([4dd088c](https://github.com/frappe/erpnext/commit/4dd088cba4cc6f7392e0fe34445b87a05728eee5)) * use correct fieldname for purchase receipt column in item_wise_purchase_register report (backport [#35828](https://github.com/frappe/erpnext/issues/35828)) ([#35848](https://github.com/frappe/erpnext/issues/35848)) ([de529f0](https://github.com/frappe/erpnext/commit/de529f0adf43d39de7cbfbb531d4de6082014e72)) * **ux:** PO Get Items From Open Material Requests (backport [#35894](https://github.com/frappe/erpnext/issues/35894)) ([#35896](https://github.com/frappe/erpnext/issues/35896)) ([12b6257](https://github.com/frappe/erpnext/commit/12b62571b8a3619adbcca5050ab165564e6ea8d9)) ### Performance Improvements * improve item wise register reports (backport [#35908](https://github.com/frappe/erpnext/issues/35908)) ([#35912](https://github.com/frappe/erpnext/issues/35912)) ([4134459](https://github.com/frappe/erpnext/commit/41344593c963e71ba0247f3e2adb3d3f647c7823)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2e9a7f61a97..23f43b9dd85 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.6" +__version__ = "13.51.7" def get_default_company(user=None): From 77d019cc3bcbf9c30f9ee91778032fb43ccb3a35 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 5 Jul 2023 09:49:20 +0000 Subject: [PATCH 70/76] chore(release): Bumped to Version 13.52.0 # [13.52.0](https://github.com/frappe/erpnext/compare/v13.51.7...v13.52.0) (2023-07-05) ### Bug Fixes * conflicts ([b9833db](https://github.com/frappe/erpnext/commit/b9833db7bd537f3adc44e8858ca3a150b9c61a9b)) * Expense Account filter in Sales Invoice ([#35944](https://github.com/frappe/erpnext/issues/35944)) ([b63fbe4](https://github.com/frappe/erpnext/commit/b63fbe4286c891ec1d59748d2dbbc7a1fc689508)) * Further sort purchase_order_analysis to get consistent response ([0ef0ff4](https://github.com/frappe/erpnext/commit/0ef0ff470f2dd3526797f433b5deda17cee42674)) * reposting has not changed valuation rate ([d4e680c](https://github.com/frappe/erpnext/commit/d4e680c109838280d0c58ee9c083f5dd97c9abb0)) * Update no copy for received_qty field ([#35965](https://github.com/frappe/erpnext/issues/35965)) ([10c9640](https://github.com/frappe/erpnext/commit/10c9640cbd034ba31913ea7160517d42d551a831)) ### Features * **DATEV:** against account for opening entries ([#35941](https://github.com/frappe/erpnext/issues/35941)) ([0602ddc](https://github.com/frappe/erpnext/commit/0602ddcfc882633e639c72f91e287595aef99cc0)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 23f43b9dd85..075a2506ad3 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.51.7" +__version__ = "13.52.0" def get_default_company(user=None): From a370dc3dcc75713808fd00eda4e0190deb4db9c7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 21:38:11 +0530 Subject: [PATCH 71/76] fix(Payment Entry): compare rounded amount (#36011) fix(Payment Entry): compare rounded amount (#36011) (cherry picked from commit 4badac8e9e43473c663c49f3f4d3fa4bf3172941) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> (cherry picked from commit b04c190e33578ac307e881c06d6f468ca982a196) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 6cb514e45d2..619d573e261 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -196,7 +196,7 @@ class PaymentEntry(AccountsController): # The reference has already been partly paid elif ( latest.outstanding_amount < latest.invoice_amount - and d.outstanding_amount != latest.outstanding_amount + and flt(d.outstanding_amount, d.precision("outstanding_amount")) != latest.outstanding_amount ): frappe.throw( _( From 1116cee83110ae22bd2425296049f60caf242078 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 5 Jul 2023 16:23:25 +0000 Subject: [PATCH 72/76] chore(release): Bumped to Version 13.52.1 ## [13.52.1](https://github.com/frappe/erpnext/compare/v13.52.0...v13.52.1) (2023-07-05) ### Bug Fixes * **Payment Entry:** compare rounded amount ([#36011](https://github.com/frappe/erpnext/issues/36011)) ([a7d26b0](https://github.com/frappe/erpnext/commit/a7d26b0c20a1db72a827636c513c5a8d3af96b84)) * **Payment Entry:** compare rounded amount ([#36011](https://github.com/frappe/erpnext/issues/36011)) ([a370dc3](https://github.com/frappe/erpnext/commit/a370dc3dcc75713808fd00eda4e0190deb4db9c7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 075a2506ad3..dad9ef498ae 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.0" +__version__ = "13.52.1" def get_default_company(user=None): From 2893ae72f54c979241c39721ff72647be70dcfae Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 8 Jul 2023 06:49:39 +0530 Subject: [PATCH 73/76] fix: gst_hsn_code is ambiguous on gst reports (cherry picked from commit 3a00052b496f645b9930a8f8c5d4cb43736a2011) --- .../gst_itemised_purchase_register.py | 2 +- .../gst_itemised_sales_register/gst_itemised_sales_register.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py index fec63f2f18a..4dcdf4b88e2 100644 --- a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py +++ b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py @@ -28,7 +28,7 @@ def execute(filters=None): "gst_category", "export_type", "ecommerce_gstin", - "gst_hsn_code", + "`tabPurchase Invoice Item`.gst_hsn_code", "bill_no", "bill_date", ], diff --git a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py index aef69b17868..3280c75fb34 100644 --- a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py +++ b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py @@ -39,7 +39,7 @@ def execute(filters=None): "gst_category", "export_type", "ecommerce_gstin", - "gst_hsn_code", + "`tabSales Invoice Item`.gst_hsn_code", ] additional_conditions = get_conditions(filters, additional_query_columns) From 6490b7d561956a93906e883b5151e14ed55f443b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 8 Jul 2023 13:17:58 +0000 Subject: [PATCH 74/76] chore(release): Bumped to Version 13.52.2 ## [13.52.2](https://github.com/frappe/erpnext/compare/v13.52.1...v13.52.2) (2023-07-08) ### Bug Fixes * gst_hsn_code is ambiguous on gst reports ([2893ae7](https://github.com/frappe/erpnext/commit/2893ae72f54c979241c39721ff72647be70dcfae)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index dad9ef498ae..175dbc2cf57 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.1" +__version__ = "13.52.2" def get_default_company(user=None): From 0feb393fff972295a341f84c4983eabe690bdb50 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 9 Jul 2023 11:42:23 +0530 Subject: [PATCH 75/76] Revert "perf: improve item wise register reports (backport #35908) (#35912)" This reverts commit 41344593c963e71ba0247f3e2adb3d3f647c7823. (cherry picked from commit b99236624686839c0a96b99a78da22cda25fb3ad) --- .../item_wise_purchase_register.py | 31 +++++++------------ .../item_wise_sales_register.py | 30 ++++++------------ 2 files changed, 22 insertions(+), 39 deletions(-) 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 6fdb2f337c0..924c14bdb94 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 @@ -15,6 +15,7 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i get_group_by_conditions, get_tax_accounts, ) +from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import get_item_details def execute(filters=None): @@ -39,16 +40,6 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum tax_doctype="Purchase Taxes and Charges", ) - scrubbed_tax_fields = {} - - for tax in tax_columns: - scrubbed_tax_fields.update( - { - tax + " Rate": frappe.scrub(tax + " Rate"), - tax + " Amount": frappe.scrub(tax + " Amount"), - } - ) - po_pr_map = get_purchase_receipts_against_purchase_order(item_list) data = [] @@ -59,7 +50,11 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum if filters.get("group_by"): grand_total = get_grand_total(filters, "Purchase Invoice") + item_details = get_item_details() + for d in item_list: + item_record = item_details.get(d.item_code) + purchase_receipt = None if d.purchase_receipt: purchase_receipt = d.purchase_receipt @@ -72,8 +67,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row = { "item_code": d.item_code, - "item_name": d.pi_item_name if d.pi_item_name else d.i_item_name, - "item_group": d.pi_item_group if d.pi_item_group else d.i_item_group, + "item_name": item_record.item_name if item_record else d.item_name, + "item_group": item_record.item_group if item_record else d.item_group, "description": d.description, "invoice": d.parent, "posting_date": d.posting_date, @@ -106,8 +101,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum item_tax = itemised_tax.get(d.name, {}).get(tax, {}) row.update( { - scrubbed_tax_fields[tax + " Rate"]: item_tax.get("tax_rate", 0), - scrubbed_tax_fields[tax + " Amount"]: item_tax.get("tax_amount", 0), + frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0), + frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0), } ) total_tax += flt(item_tax.get("tax_amount")) @@ -330,17 +325,15 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice`.unrealized_profit_loss_account, `tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description, - `tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` as pi_item_group, - `tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group, + `tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, `tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`, `tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`, `tabPurchase Invoice`.`supplier_name`, `tabPurchase Invoice`.`mode_of_payment` {0} - from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem` + from `tabPurchase Invoice`, `tabPurchase Invoice Item` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and - `tabItem`.name = `tabPurchase Invoice Item`.`item_code` and - `tabPurchase Invoice`.docstatus = 1 %s + `tabPurchase Invoice`.docstatus = 1 %s """.format( additional_query_columns ) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index bd7d02e0430..0ebe13f4f32 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -11,6 +11,7 @@ from frappe.utils.xlsxutils import handle_html from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import ( get_customer_details, + get_item_details, ) @@ -34,16 +35,6 @@ def _execute( if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) - scrubbed_tax_fields = {} - - for tax in tax_columns: - scrubbed_tax_fields.update( - { - tax + " Rate": frappe.scrub(tax + " Rate"), - tax + " Amount": frappe.scrub(tax + " Amount"), - } - ) - mode_of_payments = get_mode_of_payments(set(d.parent for d in item_list)) so_dn_map = get_delivery_notes_against_sales_order(item_list) @@ -56,9 +47,11 @@ def _execute( grand_total = get_grand_total(filters, "Sales Invoice") customer_details = get_customer_details() + item_details = get_item_details() for d in item_list: customer_record = customer_details.get(d.customer) + item_record = item_details.get(d.item_code) delivery_note = None if d.delivery_note: @@ -71,8 +64,8 @@ def _execute( row = { "item_code": d.item_code, - "item_name": d.si_item_name if d.si_item_name else d.i_item_name, - "item_group": d.si_item_group if d.si_item_group else d.i_item_group, + "item_name": item_record.item_name if item_record else d.item_name, + "item_group": item_record.item_group if item_record else d.item_group, "description": d.description, "invoice": d.parent, "posting_date": d.posting_date, @@ -114,8 +107,8 @@ def _execute( item_tax = itemised_tax.get(d.name, {}).get(tax, {}) row.update( { - scrubbed_tax_fields[tax + " Rate"]: item_tax.get("tax_rate", 0), - scrubbed_tax_fields[tax + " Amount"]: item_tax.get("tax_amount", 0), + frappe.scrub(tax + " Rate"): item_tax.get("tax_rate", 0), + frappe.scrub(tax + " Amount"): item_tax.get("tax_amount", 0), } ) if item_tax.get("is_other_charges"): @@ -411,18 +404,15 @@ def get_items(filters, additional_query_columns, additional_conditions=None): `tabSales Invoice Item`.project, `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, `tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`, - `tabSales Invoice Item`.`item_name` as si_item_name, `tabSales Invoice Item`.`item_group` as si_item_group, - `tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group, `tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail, `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0} - from `tabSales Invoice`, `tabSales Invoice Item`, `tabItem` - where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and - `tabItem`.name = `tabSales Invoice Item`.`item_code` and - `tabSales Invoice`.docstatus = 1 {1} + from `tabSales Invoice`, `tabSales Invoice Item` + where `tabSales Invoice`.name = `tabSales Invoice Item`.parent + and `tabSales Invoice`.docstatus = 1 {1} """.format( additional_query_columns or "", conditions ), From 77eb11a6e346c7ecbfdc7da4567b05bab022676e Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 9 Jul 2023 07:53:45 +0000 Subject: [PATCH 76/76] chore(release): Bumped to Version 13.52.3 ## [13.52.3](https://github.com/frappe/erpnext/compare/v13.52.2...v13.52.3) (2023-07-09) ### Reverts * Revert "perf: improve item wise register reports (backport #35908) (#35912)" ([0feb393](https://github.com/frappe/erpnext/commit/0feb393fff972295a341f84c4983eabe690bdb50)), closes [#35908](https://github.com/frappe/erpnext/issues/35908) [#35912](https://github.com/frappe/erpnext/issues/35912) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 175dbc2cf57..c7293f04b98 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.2" +__version__ = "13.52.3" def get_default_company(user=None):