From 36b99265274f5e34a9507a29bf4ed13507d6be47 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 12 Oct 2022 12:21:48 +0000 Subject: [PATCH 01/54] chore(release): Bumped to Version 14.3.0 # [14.3.0](https://github.com/frappe/erpnext/compare/v14.2.3...v14.3.0) (2022-10-12) ### Bug Fixes * consider outgoingrate while valuation rate calculate ([423c019](https://github.com/frappe/erpnext/commit/423c0190e29a99af1aef223d3f4d8d7e6dbfcf0c)) * consider sales rate as incoming rate for transit warehouse in purchase flow ([948b231](https://github.com/frappe/erpnext/commit/948b231e92755b725785865ec2e0ae385c5efb7f)) * Do not add tax withheld vouchers post tax withheding in one document ([bd8e61b](https://github.com/frappe/erpnext/commit/bd8e61b2dc0bee580b19925c5ecb20dc4f837118)) * Explicitly update modified (backport [#32519](https://github.com/frappe/erpnext/issues/32519)) ([#32575](https://github.com/frappe/erpnext/issues/32575)) ([154904e](https://github.com/frappe/erpnext/commit/154904e9609a572f1c6d9f0cbffec220881dbb33)) * Hanlde rounding loss for internal transfer ([8eda2db](https://github.com/frappe/erpnext/commit/8eda2dbdf8c26798af0859baaac4458330096ef5)) * Incoming rate precision fixes for intra company transfer ([b653f43](https://github.com/frappe/erpnext/commit/b653f43b70f76e3620948fd483a6f3f7eb201dac)) * make readings status mandatory in Quality Inspection ([50d790c](https://github.com/frappe/erpnext/commit/50d790c919b4b653844e93797e3f7ab3b4aee5c2)) * minor cleanup ([236a5f3](https://github.com/frappe/erpnext/commit/236a5f37c758652092336888451c788bfddfd75b)) * more fields reordering related to Tab Break ([db4fbc9](https://github.com/frappe/erpnext/commit/db4fbc9e58e193439024ca309210b0f2fbdf1d71)) * PO cancel post advance payment cancel against PO ([bda25c4](https://github.com/frappe/erpnext/commit/bda25c4d5d2b6e30f6c4e29897a5404d31a41100)) * removed unnecessary imports ([31782d6](https://github.com/frappe/erpnext/commit/31782d6f71d8066f64f90ce56e29d1832ae32235)) * set Quality Inspection status based on readings status ([e25db8b](https://github.com/frappe/erpnext/commit/e25db8b1b5d4cd118c4e6a3fe49f5262373e2aea)) * single column indexes (backport [#32425](https://github.com/frappe/erpnext/issues/32425)) ([#32513](https://github.com/frappe/erpnext/issues/32513)) ([2f1b8af](https://github.com/frappe/erpnext/commit/2f1b8af13c2b5f155225859ea65770b7e5a9ba69)) * Tax withholding related fixes ([d5f6938](https://github.com/frappe/erpnext/commit/d5f693806b4e7699b07594ea1565b24809b1d82e)) * test case ([0b26131](https://github.com/frappe/erpnext/commit/0b26131b65e7b68b5ebc7fcde164f2937958c0e7)) * **test:** `test_rejected_qi_validation` ([943e619](https://github.com/frappe/erpnext/commit/943e6192cc4da77ffa4672a0fc5c67adb59684c7)) * TooManyWritesError during reposting of stock ([73742ff](https://github.com/frappe/erpnext/commit/73742ff56566170bcf37058fc315d94eccae1eff)) * unlink payment on invoice cancellation ([8ba503b](https://github.com/frappe/erpnext/commit/8ba503bf0e2a6ffe9f5b3e5210c3a9dc9ec4942b)) * use naming_series in budget ([0a24859](https://github.com/frappe/erpnext/commit/0a24859c9c00e8f0ef857e4859d1e483f94150b6)) * value error on pos submit ([4b65dd7](https://github.com/frappe/erpnext/commit/4b65dd7f2a55076ff9922e7113e704d1bfaf089a)) ### Features * **JE:** trigger account field when fetched from template ([17dd1ab](https://github.com/frappe/erpnext/commit/17dd1abb34af9ee6a01080e15491faa7596cd923)), closes [#32409](https://github.com/frappe/erpnext/issues/32409) * provision to return non consumed components against the work order ([d0f3818](https://github.com/frappe/erpnext/commit/d0f3818060de365df90b6eda24d3d217af9a616f)) * Tab Break in Material Request ([79151be](https://github.com/frappe/erpnext/commit/79151be8ce1442cc232ab6eb2a74e84659dd684d)) * Tab Break in Purchase Invoice ([89314d4](https://github.com/frappe/erpnext/commit/89314d4171e7ef736ab1c77c199aa17034f80eae)) * Tab Break in Purchase Order ([5a8329a](https://github.com/frappe/erpnext/commit/5a8329add1cc8457108ed2ff7d2821bf07a4efa6)) * Tab Break in Purchase Receipt ([b092791](https://github.com/frappe/erpnext/commit/b092791ac803c7f229ea5c8202b04cd1e5ffe06d)) * Tab Break in Quotation ([b0a9f79](https://github.com/frappe/erpnext/commit/b0a9f79e181826eef48a62fc28a17d73abdbb129)) * Tab Break in Sales Order, Delivery Note, Sales Invoice and Purchase Order ([31cd96b](https://github.com/frappe/erpnext/commit/31cd96bc89f9f1b0ca29359ceb9ae542e32289ff)) * Tab Break in Supplier Quotation ([22ad3ad](https://github.com/frappe/erpnext/commit/22ad3ad6f39a2c717d44fea2d039bbe76a750aaf)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 7a447fef30e6..8866fa756cdb 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.2.3" +__version__ = "14.3.0" def get_default_company(user=None): From 9703771b757ec38c907d8f4a5527b622f30110c4 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 18 Oct 2022 18:03:17 +0000 Subject: [PATCH 02/54] chore(release): Bumped to Version 14.3.1 ## [14.3.1](https://github.com/frappe/erpnext/compare/v14.3.0...v14.3.1) (2022-10-18) ### Bug Fixes * `Brand Defaults` filters ([cb0c4b5](https://github.com/frappe/erpnext/commit/cb0c4b566455a71577e084fa910e4ce15b114984)) * add project settings to projects workspace (backport [#32568](https://github.com/frappe/erpnext/issues/32568)) ([#32600](https://github.com/frappe/erpnext/issues/32600)) ([af4dafd](https://github.com/frappe/erpnext/commit/af4dafdc64c53883c55c73620926fea00c021591)) * delete old ple's on item value repost ([62cabdf](https://github.com/frappe/erpnext/commit/62cabdf792d98c39bedddb20f6d1e641ed2b40c5)) * don't try to update youtube data if disabled in settings (backport [#32588](https://github.com/frappe/erpnext/issues/32588)) ([#32589](https://github.com/frappe/erpnext/issues/32589)) ([ed85683](https://github.com/frappe/erpnext/commit/ed85683fea42bd9cb96e9a28a37446db48a38033)) * group warehouse filter not working for Batchwise Balance history report ([faedd85](https://github.com/frappe/erpnext/commit/faedd85b660fc0647ab877fc99ddd41634809f44)) * Ignore linked purchase invoice on cancel ([cc938fb](https://github.com/frappe/erpnext/commit/cc938fb028ca26df9f1f764664375885149d0421)) * linter ([831f60f](https://github.com/frappe/erpnext/commit/831f60f439a71fb39c9d2ef287bcaf3fe49a6d38)) * Party account for multi-order invoices ([eaea846](https://github.com/frappe/erpnext/commit/eaea8469fc092458454f7859574833efa29211f4)) * pricing rule item code UOM apply & conversions (backport [#32566](https://github.com/frappe/erpnext/issues/32566)) ([#32637](https://github.com/frappe/erpnext/issues/32637)) ([ffd82f3](https://github.com/frappe/erpnext/commit/ffd82f330233ed00bbafbeabef9ceef21a491f4f)) * Renamed Dashboard tab label to Connections ([dac5989](https://github.com/frappe/erpnext/commit/dac5989d72732a83c4e1c45851be635b860f50cb)) * Renamed Notes section to Comments ([9e94dd9](https://github.com/frappe/erpnext/commit/9e94dd9203396f745ab32740942e169536e1fac7)) * type-cast while saving an item (backport [#32549](https://github.com/frappe/erpnext/issues/32549)) ([#32578](https://github.com/frappe/erpnext/issues/32578)) ([829a0ff](https://github.com/frappe/erpnext/commit/829a0ff827d81beca6bb98caff0c9295da204e2c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8866fa756cdb..94d6b34de46b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.3.0" +__version__ = "14.3.1" def get_default_company(user=None): From 123d720616fb2f3901329789c77990ed93092f23 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 26 Oct 2022 05:09:19 +0000 Subject: [PATCH 03/54] chore(release): Bumped to Version 14.4.0 # [14.4.0](https://github.com/frappe/erpnext/compare/v14.3.1...v14.4.0) (2022-10-26) ### Bug Fixes * Advance paid amount in orders (backport [#32642](https://github.com/frappe/erpnext/issues/32642)) ([#32648](https://github.com/frappe/erpnext/issues/32648)) ([8a88105](https://github.com/frappe/erpnext/commit/8a88105aed3de147a8fc176467435292870195c9)) * allow to create Sales Order from expired Quotation ([#32641](https://github.com/frappe/erpnext/issues/32641)) ([ccc58f4](https://github.com/frappe/erpnext/commit/ccc58f48e38f1aaeaf23410e8b3a8a45dbcd9eea)) * Billing Address for inter-company purchase docs ([f8934fa](https://github.com/frappe/erpnext/commit/f8934faa73c020660c189a9b458a94afaebae892)) * BOM cost update message ([e539579](https://github.com/frappe/erpnext/commit/e539579fb4e3e3147517aaafb076e55097730dfa)) * dont update item info twice ([8876904](https://github.com/frappe/erpnext/commit/887690449dbc56d3e801ccb4ab3d649e78278a44)) * incorrect qty in material request ([da538a3](https://github.com/frappe/erpnext/commit/da538a37addaec6911d30dd3aec361f93d58df35)) * number of months subscription plan ([fff9e76](https://github.com/frappe/erpnext/commit/fff9e7671844d8b6df9ff9a25dfdbfb5b5b58ba1)) * overlap error not raised for job card in case of workstation with production capacity ([ed2a093](https://github.com/frappe/erpnext/commit/ed2a093e493ea41efbfc9b47c9c32856d4fdd9de)) * party type and party mandatory on updating outstanding ([9a5e238](https://github.com/frappe/erpnext/commit/9a5e238702fa7f7469e5269e84e1c274f92e0821)) * searchfield not working for cuctsomer, supplier as per customize form ([fb1c307](https://github.com/frappe/erpnext/commit/fb1c30718ba5f1cf9dcbf5c5f21b603bca10b4a8)) * unset contact details ([d7a65b1](https://github.com/frappe/erpnext/commit/d7a65b1d419fb9c8131a48ada866f1fe8ed5037a)) ### Features * Basic Payment Ledger report ([5cb9f7b](https://github.com/frappe/erpnext/commit/5cb9f7b3a451d609872384005238b252d02cc882)) * Repayment schedule types for term loans ([6ce32fd](https://github.com/frappe/erpnext/commit/6ce32fd5a983739186d690f3209dbc69d6e888f0)) ### Performance Improvements * cache barcode scan result (backport [#32629](https://github.com/frappe/erpnext/issues/32629)) ([#32672](https://github.com/frappe/erpnext/issues/32672)) ([3300856](https://github.com/frappe/erpnext/commit/3300856fb462a9aa1d146f50d157f5a862df6e97)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 94d6b34de46b..ea34b889777a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.3.1" +__version__ = "14.4.0" def get_default_company(user=None): From ce5dbf891a211de2d0ec6072b6d7ff25e6530dc3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 1 Nov 2022 17:19:41 +0000 Subject: [PATCH 04/54] chore(release): Bumped to Version 14.5.0 # [14.5.0](https://github.com/frappe/erpnext/compare/v14.4.0...v14.5.0) (2022-11-01) ### Bug Fixes * add `Sales Order` reference in Material Request Dashboard ([fc63892](https://github.com/frappe/erpnext/commit/fc6389280ce790aa98c345578d3da39579c2cf25)) * Add condition for discount section collapse ([953f78d](https://github.com/frappe/erpnext/commit/953f78d6a9ac4e346f7a0ae71aaf10be95ae0cd9)) * Budget validation for main cost center ([89a1c83](https://github.com/frappe/erpnext/commit/89a1c83431950129bd305c35f012e07843763098)) * Clear invoice table post importing invoices ([6eafff8](https://github.com/frappe/erpnext/commit/6eafff86941d49983b6c7934a580479b3781f5e4)) * Company bank account filter in Bank Clearance ([797512c](https://github.com/frappe/erpnext/commit/797512ca131ea70e844a5b9bf2b38f64605e945a)) * Curreny in SOA print for multi-currency party ([195500c](https://github.com/frappe/erpnext/commit/195500cb32b274b21ff63525bbe898f022bf1845)) * duplicate custom fields for inventory dimension ([1152ac3](https://github.com/frappe/erpnext/commit/1152ac3ff10c73acddb0ec9973baa2cada2e9345)) * Filter fixes in Accounts Payable report ([29197dc](https://github.com/frappe/erpnext/commit/29197dcd7fd56ad5ab962cb77362940f6b3c4bf0)) * Issues while cancel/amending Purchase Invoice with TDS enabled ([74a6479](https://github.com/frappe/erpnext/commit/74a6479f707c7c849b5aac626e698dca94111a03)) * key error in filter access ([6114241](https://github.com/frappe/erpnext/commit/6114241ff2545cbede7bf55ccfa5b68dba07955b)) * Mode of payment for returns in POS Sales Invoice ([a260426](https://github.com/frappe/erpnext/commit/a260426dd471d7b186fb1f2a9352e48d06b0cd4e)) * Pass project to stock entry items ([4035873](https://github.com/frappe/erpnext/commit/403587329508822ec33cae87c86de562d26277e5)) * pro_rata_amount calculation in assets tests ([d1b2786](https://github.com/frappe/erpnext/commit/d1b2786f24559c46fbee02e354016243f9c0ecfe)) * Reference due date field type in Journal Entry Accounts table ([faf25c0](https://github.com/frappe/erpnext/commit/faf25c0b950e72a164acaa892c1124e956110a09)) * Reset advance paid amount on Oreder cancel and amend ([34bd783](https://github.com/frappe/erpnext/commit/34bd7837e27cbc57ebedbdfaa1dc5745a4f36869)) * Total Sales amount update in project via Sales Order ([d742e6d](https://github.com/frappe/erpnext/commit/d742e6d56b8c66df9f582a763da13a716faf4850)) ### Features * additional filters on Payment terms report ([a03ec0a](https://github.com/frappe/erpnext/commit/a03ec0afb382e506bfdf2af109b89644d82c1fd7)) * **pricing rule:** free qty rounding and recursion qty ([#32577](https://github.com/frappe/erpnext/issues/32577)) ([9b66020](https://github.com/frappe/erpnext/commit/9b66020fc70209196cda3d9b6e6a654b3b32d8c1)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ea34b889777a..2c7ca0af0d11 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.4.0" +__version__ = "14.5.0" def get_default_company(user=None): From dd4dbd4b000e6ccc3ee3e93fefdd2ce3a03b1c7d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 3 Nov 2022 11:24:58 +0530 Subject: [PATCH 05/54] fix: not able to select customer / supplier (cherry picked from commit b0fc568c80ec5bead83fc0bc61be78e95ba24813) (cherry picked from commit 6989cdf4f2c327bcc7565ce70a51b794a83f5c94) --- erpnext/buying/doctype/supplier/test_supplier.py | 4 ++++ erpnext/controllers/queries.py | 4 ++-- erpnext/selling/doctype/customer/test_customer.py | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py index e2dbf21be2c8..b9fc344647b7 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.py +++ b/erpnext/buying/doctype/supplier/test_supplier.py @@ -156,6 +156,8 @@ def test_party_details_tax_category(self): def test_serach_fields_for_supplier(self): from erpnext.controllers.queries import supplier_query + frappe.db.set_value("Buying Settings", None, "supp_master_name", "Naming Series") + supplier_name = create_supplier(supplier_name="Test Supplier 1").name make_property_setter( @@ -187,6 +189,8 @@ def test_serach_fields_for_supplier(self): self.assertEqual(data[0].supplier_type, "Company") self.assertTrue("supplier_type" in data[0]) + frappe.db.set_value("Buying Settings", None, "supp_master_name", "Supplier Name") + def create_supplier(**args): args = frappe._dict(args) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 3bdc01706825..b0cf72416691 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -85,7 +85,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters, as_dict= fields = ["name"] if cust_master_name != "Customer Name": - fields = ["customer_name"] + fields.append("customer_name") fields = get_fields(doctype, fields) searchfields = frappe.get_meta(doctype).get_search_fields() @@ -123,7 +123,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters, as_dict= fields = ["name"] if supp_master_name != "Supplier Name": - fields = ["supplier_name"] + fields.append("supplier_name") fields = get_fields(doctype, fields) diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 691adccd4dd1..a621c737ed38 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -345,6 +345,8 @@ def test_customer_payment_terms(self): def test_serach_fields_for_customer(self): from erpnext.controllers.queries import customer_query + frappe.db.set_value("Selling Settings", None, "cust_master_name", "Naming Series") + make_property_setter( "Customer", None, "search_fields", "customer_group", "Data", for_doctype="Doctype" ) @@ -369,6 +371,8 @@ def test_serach_fields_for_customer(self): self.assertEqual(data[0].territory, "_Test Territory") self.assertTrue("territory" in data[0]) + frappe.db.set_value("Selling Settings", None, "cust_master_name", "Customer Name") + def get_customer_dict(customer_name): return { From 3967773fbeddfd05b53f722f1b51ea813a57b3c1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 3 Nov 2022 08:07:36 +0000 Subject: [PATCH 06/54] chore(release): Bumped to Version 14.5.1 ## [14.5.1](https://github.com/frappe/erpnext/compare/v14.5.0...v14.5.1) (2022-11-03) ### Bug Fixes * not able to select customer / supplier ([dd4dbd4](https://github.com/frappe/erpnext/commit/dd4dbd4b000e6ccc3ee3e93fefdd2ce3a03b1c7d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2c7ca0af0d11..6295cc7e96d2 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.5.0" +__version__ = "14.5.1" def get_default_company(user=None): From e917212849cc5444a80d3e28a0e9a1ee9495d0fa Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 8 Nov 2022 12:15:30 +0000 Subject: [PATCH 07/54] chore(release): Bumped to Version 14.6.0 # [14.6.0](https://github.com/frappe/erpnext/compare/v14.5.1...v14.6.0) (2022-11-08) ### Bug Fixes * `Material Consumption` option in case of `Skip Transfer to WIP` in WO ([8c856cd](https://github.com/frappe/erpnext/commit/8c856cd5fc41c8603edf85e66db8ed1879b0ebb5)) * add translate function to name of chart labels in budget_variance_report.py ([16f364d](https://github.com/frappe/erpnext/commit/16f364da37c4a67cdee1a00714a3560b155ef9fe)) * add translate function to name of chart labels in deferred_revenue_and_expense.py ([b8caa58](https://github.com/frappe/erpnext/commit/b8caa587d2296cf856122bb2f57a85263a9a69f1)) * add translate function to period in stock_analytics.py ([b0c06d5](https://github.com/frappe/erpnext/commit/b0c06d5a04adb12704fa1c10e35cca329dedc594)) * add translate function to period in sales_analytics.py ([e681f06](https://github.com/frappe/erpnext/commit/e681f068832384ee55e8eb170ce6a637a902a81d)) * add translate function to string on budget_variance_report.js to match the variance word translated ([595aaad](https://github.com/frappe/erpnext/commit/595aaad99d5956f6345a6dadfd1994a12ac0bf26)) * Auto advance allocation against partial invoices ([b7763d9](https://github.com/frappe/erpnext/commit/b7763d953ad11f5770c03fb5376f6da0cc93d78e)) * auto increment qty if item table has no items ([d8e403b](https://github.com/frappe/erpnext/commit/d8e403bf5de17cdbda349a0f16680c2af482dd06)) * conflicts ([ab87a95](https://github.com/frappe/erpnext/commit/ab87a950e522bed10ca85951cae8bf89e483b7fa)) * correct linters ([8f6f9a4](https://github.com/frappe/erpnext/commit/8f6f9a429a3131ee3bdab4836775223bb4a4b1b4)) * correct linters ([440e208](https://github.com/frappe/erpnext/commit/440e20859f1b26e253141652d9a4c1bca06165de)) * correct linters ([5acc9be](https://github.com/frappe/erpnext/commit/5acc9be5c931aaa22b17be36b581068501d10c87)) * Create POS Opening Entry POS Profile filter. ([60af9c0](https://github.com/frappe/erpnext/commit/60af9c0516935963538a50a623ab32052099eda2)) * Disable tax included prices for internal transfers ([#32794](https://github.com/frappe/erpnext/issues/32794)) ([6838e5e](https://github.com/frappe/erpnext/commit/6838e5ea3b3c3c2a948ab7569dee8a3745b6ad2a)) * for asset's purchase_date, if bill_date is set, use that instead of posting_date ([01a1c96](https://github.com/frappe/erpnext/commit/01a1c963148b60d23dbdd66638d6b8d2a5456b2c)) * get `consumed_qty` based on `received_qty` in SCR ([ea9a502](https://github.com/frappe/erpnext/commit/ea9a50278d1a067275294db2f37fbbc51b0d5887)) * Increase columns width in Warehouse wise Item Balance Age and Value ([0b09c31](https://github.com/frappe/erpnext/commit/0b09c31cb0cb174a79d2e26d520b869e31d02328)) * linter ([af60c8f](https://github.com/frappe/erpnext/commit/af60c8f7599ab03d920a6fe7ef2e7a05c4018bb1)) * make `BOM` required in SCR Item ([3f79a05](https://github.com/frappe/erpnext/commit/3f79a057e42cf09f75913801cb5d6133f7b6175f)) * make `consumed_qty` editable when backflush based on Material Transfer ([2c5a8c4](https://github.com/frappe/erpnext/commit/2c5a8c43f6515e8dc46a092093fdb2e565a6cc99)) * make `consumed_qty` read-only in SCR Supplied Items ([68229f0](https://github.com/frappe/erpnext/commit/68229f06d18eac15b699372a4fe2dfdef867716d)) * map `BOM` while mapping the return SCR ([e629cba](https://github.com/frappe/erpnext/commit/e629cba2b71668f526edf008c1880b5ec3b0671d)) * mysql syntax issue ([4d9bbd4](https://github.com/frappe/erpnext/commit/4d9bbd4c9c06811086e0ae4e2c16763fe7d171a3)) * not able to select customer / supplier ([6989cdf](https://github.com/frappe/erpnext/commit/6989cdf4f2c327bcc7565ce70a51b794a83f5c94)) * refactor code for better translatable string ([2dc24f2](https://github.com/frappe/erpnext/commit/2dc24f22ea6762e9e6fb4f1a9bb73dca45433ed7)) * refactor code for better translatable string in stock_ageing.py ([0ead516](https://github.com/frappe/erpnext/commit/0ead51642f718d08c7345ff78ffc844fd1de5071)) * rename test method ([97445d9](https://github.com/frappe/erpnext/commit/97445d951646c4b663e000109efb338999f3e72d)) * Scan Barcode UX ([1944f4d](https://github.com/frappe/erpnext/commit/1944f4df4d3d2ef87da41c29e5fe0a00699c87ca)) * set `received_qty` before_validate SCR ([e316558](https://github.com/frappe/erpnext/commit/e31655828668cdaf6647f76185ba3ee0bf4400bf)) * test cases ([0feec4c](https://github.com/frappe/erpnext/commit/0feec4ca8a630471371e83fb4005037d0c6e1f41)) * trailing whitespace ([31bada9](https://github.com/frappe/erpnext/commit/31bada9205e2557e564a305d284be3cb135f2c05)) * update advance paid in SO/PO from Payment Ledger ([a561432](https://github.com/frappe/erpnext/commit/a561432908191bd903a8041cee128c0b81c5087c)) * use `flt` instead of `cint` in `get_batch_no` ([6510464](https://github.com/frappe/erpnext/commit/65104644822683c5cd4774d7411fded24f0e4f1e)) ### Features * Item Wise TDS Calculation ([b9fb104](https://github.com/frappe/erpnext/commit/b9fb1045d7f4331c064c491072199f65376159af)) ### Performance Improvements * use `get_cached_value` instead of `db.get_value` in controllers ([#32776](https://github.com/frappe/erpnext/issues/32776)) ([34ca17a](https://github.com/frappe/erpnext/commit/34ca17ab11cc5150ed951b0222e0c1c55905c27a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6295cc7e96d2..4a05dd4e58f0 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.5.1" +__version__ = "14.6.0" def get_default_company(user=None): From 964abce057f222af6b44099f8dfb07273abfbc63 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 15 Nov 2022 12:59:04 +0000 Subject: [PATCH 08/54] chore(release): Bumped to Version 14.7.0 # [14.7.0](https://github.com/frappe/erpnext/compare/v14.6.0...v14.7.0) (2022-11-15) ### Bug Fixes * add translate function to valitate company msg in chart of accounts importer ([8de4430](https://github.com/frappe/erpnext/commit/8de4430662961036398f38a34ff197d71bbebffb)) * check type for reference name ([a305793](https://github.com/frappe/erpnext/commit/a30579393ed966d46bb5e91f39347a840b220bb2)) * don't set WIP Warehouse if is checked in WO ([f923183](https://github.com/frappe/erpnext/commit/f923183b64757c2bbff33f0ff8298c9d70ec3079)) * GP incorrect buying amount if no upd on SI and Delivery Note ([2d8f00a](https://github.com/frappe/erpnext/commit/2d8f00afade29fbfe8a175b319e979fb8de255b6)) * incorrect fix of conversion factor in PP ([c48d00a](https://github.com/frappe/erpnext/commit/c48d00ad7751468df456bbf06fed97c26fa2bddd)) * Label for applicable dimension table ([eb4f8e4](https://github.com/frappe/erpnext/commit/eb4f8e4bd8012f9a258181c735b3150c7374e716)) * Maintain same rate between Quotation and Sales Order ([6c155d2](https://github.com/frappe/erpnext/commit/6c155d2825ac111b795ee7dd899a53b967a0f1f8)) * Project filter in timesheet ([37bed12](https://github.com/frappe/erpnext/commit/37bed12df463a16baac209d1a34fa97fdfe7fd80)) * Purchase Receipt timeout error ([0d5b726](https://github.com/frappe/erpnext/commit/0d5b7269d409dde23d36a27af14f7f30510802d5)) * repayment schedule regeneration ([2f5033b](https://github.com/frappe/erpnext/commit/2f5033b70f7d887e3605bef811b526ab764d3f2f)) * set `WIP Warehouse` in Job Card ([c294652](https://github.com/frappe/erpnext/commit/c294652dab2ef4a5d182fa2779789a6badd58032)) * set stock UOM in args to ensure item price is fetched ([a4187b9](https://github.com/frappe/erpnext/commit/a4187b9d8f9cefa51591e5ad09bb4787e515036a)) * test cases ([071ee5d](https://github.com/frappe/erpnext/commit/071ee5d81c08b0045b6822959d6b6d3dfc42ec55)) * **ux:** Tab break in Customer and Supplier form ([eeaa932](https://github.com/frappe/erpnext/commit/eeaa9329f6db11705e227ec3a6a347c686a777a1)) * Write Off section visibility for non POS Invoices ([07badbc](https://github.com/frappe/erpnext/commit/07badbc0f214ef1cca98215db4770e7d265523b4)) ### Features * Repost Payment Ledger entries for vouchers ([de59b50](https://github.com/frappe/erpnext/commit/de59b5040739bbc0cc3e5a3fcef42f2253eb3f96)) ### Reverts * Revert "fix: get `consumed_qty` based on `received_qty` in SCR" ([7fd6c43](https://github.com/frappe/erpnext/commit/7fd6c43752ce94e8821b7bf62a4b2d1f7e34f126)) * Revert "fix: set `received_qty` before_validate SCR" ([0ecb44d](https://github.com/frappe/erpnext/commit/0ecb44d40c22858a99cd7c0af44253aa114b20fa)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4a05dd4e58f0..19fa0849ecc0 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.6.0" +__version__ = "14.7.0" def get_default_company(user=None): From df743aec29e6545cf8fb5a303d1a9cc281eae04f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 22 Nov 2022 16:00:31 +0000 Subject: [PATCH 09/54] chore(release): Bumped to Version 14.8.0 # [14.8.0](https://github.com/frappe/erpnext/compare/v14.7.0...v14.8.0) (2022-11-22) ### Bug Fixes * Accounting Dimension filtering for Sales and Purchase Report ([b782209](https://github.com/frappe/erpnext/commit/b78220957b2362f0f05205b6c53075b6402bfda2)) * add missing commas and brackets ([ecd4eab](https://github.com/frappe/erpnext/commit/ecd4eab2da1c89f05acef5bf3eccf521ba114774)) * always send account currency in response ([f2fde83](https://github.com/frappe/erpnext/commit/f2fde8327d0cdedd78024a7ca8b9896b47f659ae)) * Bulk payment generation against invoices ([57b00e3](https://github.com/frappe/erpnext/commit/57b00e3b16d6150c8f1263506c73eebc2c4ac0bc)) * cast POS query inputs to integers (backport [#32975](https://github.com/frappe/erpnext/issues/32975)) ([#32977](https://github.com/frappe/erpnext/issues/32977)) ([55e1592](https://github.com/frappe/erpnext/commit/55e1592b709d49b5483f0e4a66d1d3edf7a84a0d)) * don't set `rejected-qty` in return SCR ([4de02dc](https://github.com/frappe/erpnext/commit/4de02dc2588d11b415157315f069461eedd53764)) * Don't show payment button for invoices on hold ([7487acd](https://github.com/frappe/erpnext/commit/7487acdeb6d6003450d09e6c2033c248a0d9446c)) * hide reject-fields in return SCR ([71d6f2a](https://github.com/frappe/erpnext/commit/71d6f2a4901a6af6f05a9cb20a6d784c1b49dbe8)) * incorrect currency in Exchange rate revaluation ([a26470a](https://github.com/frappe/erpnext/commit/a26470a65f10ecc33beaae1d4afbdbb62248e9ed)) * Internal Transfer Material Request cycle and tracking fixed till purchase receipt ([eaf0950](https://github.com/frappe/erpnext/commit/eaf09503a998ef27aec916b1b3acf7ec720bec07)) * link to brand doctype. ([b428307](https://github.com/frappe/erpnext/commit/b428307e9f4c3013b8bc8e14ba4379ac9d447ca3)) * linters failing ([24aafb3](https://github.com/frappe/erpnext/commit/24aafb3866174214087da2c38728d321b3d04968)) * make `is_internal_supplier` read-only ([caef140](https://github.com/frappe/erpnext/commit/caef140a9b515d1d25c04ede6c5baa3935667e50)) * minor change ([ac3120b](https://github.com/frappe/erpnext/commit/ac3120b6f9e9d4e4f8ca64e01b8b434c1ad41293)) * minor issue fixed ([c356d2c](https://github.com/frappe/erpnext/commit/c356d2cabdf79bae32c52afe7cf7406efdc47577)) * naming ([a198a55](https://github.com/frappe/erpnext/commit/a198a55d2d16e3c050d288701b1acd83dce3c0e0)) * Opening journal entry templates ([6aada76](https://github.com/frappe/erpnext/commit/6aada762970eef2e6ab2d84582678382305f07e6)) * **pos:** item selector image border radius ([aaed4ab](https://github.com/frappe/erpnext/commit/aaed4ab9585c2fcea72436ae3e1ea199e17a66e4)) * precision in asset tests ([444f241](https://github.com/frappe/erpnext/commit/444f241263e6ba89e316e06b9f67302c698da343)) * **realtime:** Restrict updates to only last modified or current user ([#33034](https://github.com/frappe/erpnext/issues/33034)) ([9e8a835](https://github.com/frappe/erpnext/commit/9e8a8356e9ede1e19e68bde1c7f8021a6842feb9)) * Remove unnecessary filters from Journal Entry ([387665d](https://github.com/frappe/erpnext/commit/387665d221d28450a4f7a49e71fcabc0c56062da)) * test case added for MR internal Transfer ([2a892f5](https://github.com/frappe/erpnext/commit/2a892f5c523171814511c1ed983212746ba930bd)) * test case updated for mr ([6ddf273](https://github.com/frappe/erpnext/commit/6ddf27380f845c8d0f5b9a574cafbe53d807abee)) * Timesheet timer button ([53cf6b8](https://github.com/frappe/erpnext/commit/53cf6b8c89a75d6959dbe727b2527820091f6982)) * update advace paid in SO/PO in account currency ([14235f2](https://github.com/frappe/erpnext/commit/14235f24b2905fc026f3a42e6e17e6e60dc253e2)) * use `list()` on self mutating iteration ([eb968d7](https://github.com/frappe/erpnext/commit/eb968d7f0247e40f149a279250e3f4dbab89619e)) * Viewing account ledger from party master ([da2dfcc](https://github.com/frappe/erpnext/commit/da2dfcc10b33bb6ba75dd4652ddc1caf71dd3325)) ### Features * Workstation Type for BOM ([8323775](https://github.com/frappe/erpnext/commit/8323775282d99aeb6d6362fccff703aaaa41a9c9)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 19fa0849ecc0..5e58d0c32a4f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.7.0" +__version__ = "14.8.0" def get_default_company(user=None): From 7d63bcbeb66cd7ef39d785526e4d243542230b3e Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 29 Nov 2022 13:19:34 +0000 Subject: [PATCH 10/54] chore(release): Bumped to Version 14.9.0 # [14.9.0](https://github.com/frappe/erpnext/compare/v14.8.0...v14.9.0) (2022-11-29) ### Bug Fixes * `production_item` filter in `Job Card Summary Report` ([c9f4f60](https://github.com/frappe/erpnext/commit/c9f4f6042584632cac884b1e2c687a8633435a24)) * `Work Order` filter typo in `Job Card Summary Report` ([b157193](https://github.com/frappe/erpnext/commit/b1571932d033dbf87c1fc84612d2797d70e65795)) * Auto repeat date validations ([2aada1a](https://github.com/frappe/erpnext/commit/2aada1a0d95d9fde65314d1078eb850dedce7801)) * check for session user rather than owner ([c9c7222](https://github.com/frappe/erpnext/commit/c9c7222ded872f504f8b0b7b9d2d99f5b9e844dd)) * company name with `,` in `Job Card Summary Report` ([9c6d020](https://github.com/frappe/erpnext/commit/9c6d020e499375dd441899b37263a1431abd2c34)) * company name with `,` in `Work Order Summary Report` ([bc649b3](https://github.com/frappe/erpnext/commit/bc649b371f450284e22de436fbddd80ec0c00f19)) * create rounding gl entry for PCV during gle post processing ([fd4bcd9](https://github.com/frappe/erpnext/commit/fd4bcd9f7f1b603394a8ab4290fb04c7ceafbf24)) * Debit and Credit not equal while submitting PI containing asset item ([c11a31b](https://github.com/frappe/erpnext/commit/c11a31b39045e9d54a4e335cef92b0e11f5862a5)) * disbursable amount on currrent security price ([5edaf83](https://github.com/frappe/erpnext/commit/5edaf83733d788f9a2a6aa86a6b703e7a24699cf)) * Dispatch address display ([ef687e2](https://github.com/frappe/erpnext/commit/ef687e22b8bff7e9d8e350451c436dd2601b8ed2)) * incorrect balance qty ([f92b501](https://github.com/frappe/erpnext/commit/f92b5011dace89d76c5038cd91dea567ee286408)) * job card for quantity UX ([59e2ab7](https://github.com/frappe/erpnext/commit/59e2ab702f10ea63f6b1dad173152dc1e2fc2f5e)) * MR Item `description` and `item_name` gets reset on `qty` change ([85d108b](https://github.com/frappe/erpnext/commit/85d108b8100f78745fcf2fc0c90d32a94a7b8503)) * only show serial no batch selector only once ([8d0f715](https://github.com/frappe/erpnext/commit/8d0f715087baf8940cc72986094d33c910e3c58b)) * opportunity list doesn't show assigned user (backport [#33110](https://github.com/frappe/erpnext/issues/33110)) ([#33131](https://github.com/frappe/erpnext/issues/33131)) ([0ba2a4d](https://github.com/frappe/erpnext/commit/0ba2a4d084cd3cde5c3d1c793690059066cd53eb)) * **pos:** filter on customer groups ([c1ae5b0](https://github.com/frappe/erpnext/commit/c1ae5b0af5aa49f53ed64cf45ae99c1845b25234)) * **pos:** warehouse should be in company ([c03ec80](https://github.com/frappe/erpnext/commit/c03ec80d1add7d2b854cf2252cc43c1a51e67c81)) * precision in asset test_scrap_asset ([0fe5e9a](https://github.com/frappe/erpnext/commit/0fe5e9a9d19c2f3ecaefa3d1811c90fd9a342f63)) * production plan UX ([4607590](https://github.com/frappe/erpnext/commit/46075901baa47c87f94452fbbe65d0ff605193f0)) * remove duplicate schema ([cdd13cd](https://github.com/frappe/erpnext/commit/cdd13cd95fe3344ab342de98436c1f125d6b8182)) * remove obsolete comment ([978924a](https://github.com/frappe/erpnext/commit/978924a7e496bb684c1349ada566d93a6adf9c0a)) * reposting error `AttributeError: 'datetime.timedelta' object has no attribute 'replace'` ([4b0c9b6](https://github.com/frappe/erpnext/commit/4b0c9b61157c01a29ee80aff15d0f6e8be6cdc5f)) * reset `voucher_type` and `voucher_no` if `based_on` is set to `Item and Warehouse` ([e530f0b](https://github.com/frappe/erpnext/commit/e530f0b2fbfe876ce164b984a8cd2c55b4799d55)) * UX for inventory dimension ([f1dd4d0](https://github.com/frappe/erpnext/commit/f1dd4d0449ac602d19f90ef1d1e652711ad49f52)) * **ux:** Action buttons in Bank Reconciliation ([93b8cc3](https://github.com/frappe/erpnext/commit/93b8cc3042fe22a6412bd5f084b6cd3ef36db714)) * validation msg in stock entry ([65ac84e](https://github.com/frappe/erpnext/commit/65ac84e020ffdbb6cbdf8f52f500dd2917b292ce)) * Valuation Rate column UX in stock ledger report ([5c065e8](https://github.com/frappe/erpnext/commit/5c065e8a64b5e7a6c29dcf73f5dcb861bd897425)) ### Features * add connections to Incoterm doctype ([89b9a06](https://github.com/frappe/erpnext/commit/89b9a06204b350810f2e6de13db1231666c40cab)) * add doctype Incoterm ([b711931](https://github.com/frappe/erpnext/commit/b7119318a60f00732e5f21b44e2d345793872dfc)) * add german translations for incoterm titles ([f7988de](https://github.com/frappe/erpnext/commit/f7988dea1b9aa216a07f8e45e7880a20767d2890)) * add incoterm to purchasing transactions ([88346b1](https://github.com/frappe/erpnext/commit/88346b17e93193631086ba3a353b6957cb2494a9)) * add incoterm to sales transactions ([fcfe0cb](https://github.com/frappe/erpnext/commit/fcfe0cb9e9afddc9363d6ef278a8253009a20a70)) * create Incoterm records after install ([93e029d](https://github.com/frappe/erpnext/commit/93e029df915521a226b7e9711adf328f6af3e1c6)) * create incoterms and migrate shipments ([014896a](https://github.com/frappe/erpnext/commit/014896a59519672dc24ebc5f63cc997ce5758e0d)) * german tax templates ([5652af0](https://github.com/frappe/erpnext/commit/5652af04ba7587de7fecbf78578a11b7403d363c)) * item wise tds calculation for purchase order ([5f8f574](https://github.com/frappe/erpnext/commit/5f8f574e20579dcebd332c2144173800a775b27e)) * item wise tds calculation for purchase order. ([2bd8bd2](https://github.com/frappe/erpnext/commit/2bd8bd224ba7f0414cdd74126012887da1d10445)) * item wise tds in purchase order ([ba36435](https://github.com/frappe/erpnext/commit/ba3643514ee2011b24736c2b39d71e0107cf22d4)) * make Material Request for sub-assembly items ([5fc4dfa](https://github.com/frappe/erpnext/commit/5fc4dfaad6289d6fdb6ac6544a028968b573c184)) * **pos:** invoice: fitler warehouse by company ([c379baf](https://github.com/frappe/erpnext/commit/c379baf7a2d7f038e7eff5b204a9704434f2e5ed)) * validate repost item valuation against accounts freeze date ([04f50ea](https://github.com/frappe/erpnext/commit/04f50ea76a34ab95c83b4d62c560f0b1fab53ec9)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5e58d0c32a4f..c2041d240bde 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.8.0" +__version__ = "14.9.0" def get_default_company(user=None): From 1e351a9e0bbe17b82e7a09347a2313aaf3f85763 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 6 Dec 2022 14:37:42 +0000 Subject: [PATCH 11/54] chore(release): Bumped to Version 14.10.0 # [14.10.0](https://github.com/frappe/erpnext/compare/v14.9.0...v14.10.0) (2022-12-06) ### Bug Fixes * add company filter in RFQ Items ([7c1595e](https://github.com/frappe/erpnext/commit/7c1595e2c2ba2192183ede916bea8dfbdbb37433)) * Allow item rate udpates for non-stock invoices ([0307656](https://github.com/frappe/erpnext/commit/030765618bb6ec2b3a03f87cca54d533df65305a)) * Bundle item rates ([82cdf49](https://github.com/frappe/erpnext/commit/82cdf49d3268ebf58540cbccdadc2d2295a03c64)) * data import mandatory account_head, charge_type ([653cb9f](https://github.com/frappe/erpnext/commit/653cb9fc3b9c23e19d66cda12441e432a9fa2f10)) * default clear repost logs ([e039a14](https://github.com/frappe/erpnext/commit/e039a14f6fa92e9687dbae9102b5aed6decf97e9)) * Due date for month end payment term ([9b40b7f](https://github.com/frappe/erpnext/commit/9b40b7f2403c6ee80d6725d5b63e49146d99ad48)) * Error on making stock entry from material request ([701ccc3](https://github.com/frappe/erpnext/commit/701ccc36c83d7aed49331f5bd1af20b359a4a0e5)) * key error on p/l and balance sheet reports on foreign currency ([23a0a53](https://github.com/frappe/erpnext/commit/23a0a5337e6ab1d65e32a305c4ac08f98bfa436e)) * key error while filtering on date range and different currency ([08bca7d](https://github.com/frappe/erpnext/commit/08bca7d252e085b543dc3f49343582794ac6a02a)) * **lint:** trailing whitespace ([fae941c](https://github.com/frappe/erpnext/commit/fae941c4e3934dc3543161e2e299420e7d5fd51f)) * non empty FG batch picked while completing work order ([e5e6b5d](https://github.com/frappe/erpnext/commit/e5e6b5d81c949e0e8ea2e44fdfeb7d236b68e1a3)) * **pos:** partial return amount update ([91920f4](https://github.com/frappe/erpnext/commit/91920f4d847fe83d0c86f42380baf420f7508a20)) * reload currency exchange settings ([5b5a85e](https://github.com/frappe/erpnext/commit/5b5a85ee3bd7fc00adb7b8b9b7c0974f5c2e82c1)) * replace sql code with fields list in get_cached_value ([e18d0ec](https://github.com/frappe/erpnext/commit/e18d0eca3c8f917867e52ddb0837509b65ccb2b6)) * Tax withholding net total for PI in reports ([f87c3c6](https://github.com/frappe/erpnext/commit/f87c3c615734e8a307eb7d5470aa11f57f3d7abd)) * test case ([39680ed](https://github.com/frappe/erpnext/commit/39680ed28bf80320063dff04bb10c1d906632bed)) * type error on Sales Pipeline Analytics ([3af2b9b](https://github.com/frappe/erpnext/commit/3af2b9b4232bd2909ee5a565108b1df324934b94)) * use is_last_day_of_the_month in test_scrap_asset ([fc175e2](https://github.com/frappe/erpnext/commit/fc175e2b1fcae9f82da29e8454abc0608766be3e)) * **UX:** Alert on change of item rate in Stock Entry ([b2da76f](https://github.com/frappe/erpnext/commit/b2da76f02c864064d4c8629eb07f41a8027c4a04)) ### Features * warehouse wise stock balance ([7027fda](https://github.com/frappe/erpnext/commit/7027fdae3912351759a8ba6807215e94094c3a79)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c2041d240bde..3ef6b49f4e1a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.9.0" +__version__ = "14.10.0" def get_default_company(user=None): From ac1af3bce98e46187776de1d69fcbfe916c49605 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 13 Dec 2022 12:30:55 +0000 Subject: [PATCH 12/54] chore(release): Bumped to Version 14.10.1 ## [14.10.1](https://github.com/frappe/erpnext/compare/v14.10.0...v14.10.1) (2022-12-13) ### Bug Fixes * `Enough Parts to Build` in `BOM Stock Report` ([3b9e9d2](https://github.com/frappe/erpnext/commit/3b9e9d2c6e4cbdace2067176f18ace3e2908b9f0)) * `Material Request` reference in internal `Sales Order` ([416d178](https://github.com/frappe/erpnext/commit/416d17820944e926a0ac336f4f7748baebfbe5e7)) * add translation variable order ([ef933a8](https://github.com/frappe/erpnext/commit/ef933a8231cc219e5e81d8c8cae7b8b61b6d1dc3)) * Buying and selling check in pricing rule ([f5205a5](https://github.com/frappe/erpnext/commit/f5205a5b5d27769da3f2c60f0250ee20bd2b6b13)) * **ecommerce:** remove query parameters from referer ([40621b9](https://github.com/frappe/erpnext/commit/40621b99c83b8002e76e7e79bd0f526ef846c3fc)) * handle_post_depr_entries_fail, show error alert and send email ([b661f57](https://github.com/frappe/erpnext/commit/b661f5758a8fe2ea2bf63289c9b3201d44abbc1b)) * incorrect balance on parent company due to key mismatch ([436e93c](https://github.com/frappe/erpnext/commit/436e93c129551af187b62e6af7f216b09a46af39)) * index error on customer master ([02cc618](https://github.com/frappe/erpnext/commit/02cc618a1fc52de4fa3e5aaf413f1e045eb35a24)) * Maintain Same Rate Throughout Sales Cycle doesn't work ([5398cf8](https://github.com/frappe/erpnext/commit/5398cf8f227e740dc035eca4d1f44f21109dea1d)), closes [#32923](https://github.com/frappe/erpnext/issues/32923) * order status in `Production Planning Report` ([a657db6](https://github.com/frappe/erpnext/commit/a657db66b4057ce37d8a5fbb56f6ba9757e4425c)) * Permission issue in Tax Detail report ([7a5b80d](https://github.com/frappe/erpnext/commit/7a5b80dfbc5c819db0b80ae82efef2dd5469d4a8)) * **pos:** variable typo: `s_pos` -> `is_pos` ([afbd48f](https://github.com/frappe/erpnext/commit/afbd48f26e67235866c289492b3664a6d402af31)) * Reapply pricing rule on qty change ([c726c16](https://github.com/frappe/erpnext/commit/c726c167026ad99fbb0bfb142a2c441be6a2581f)) * Remove free items ([5e5937d](https://github.com/frappe/erpnext/commit/5e5937d6d020ae910ab9064419bb2700cd0d12c8)) * total value in Warehouse Wise Stock Balance ([c5a54d7](https://github.com/frappe/erpnext/commit/c5a54d79120cad19bbdcc43e550565fbac125375)) ### Performance Improvements * add indexes on payment entry reference (backport [#33288](https://github.com/frappe/erpnext/issues/33288)) ([#33289](https://github.com/frappe/erpnext/issues/33289)) ([ce63086](https://github.com/frappe/erpnext/commit/ce630868132ac1dabf2278dd292a80f214260299)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3ef6b49f4e1a..383be7bb582e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.10.0" +__version__ = "14.10.1" def get_default_company(user=None): From 29bb873347df3e5fe93722bdae20132b0403fd9c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 20 Dec 2022 14:01:35 +0000 Subject: [PATCH 13/54] chore(release): Bumped to Version 14.11.0 # [14.11.0](https://github.com/frappe/erpnext/compare/v14.10.1...v14.11.0) (2022-12-20) ### Bug Fixes * Consolidated financial report ([16ce411](https://github.com/frappe/erpnext/commit/16ce411b8f9838c67667f6f34477288f6338daf3)) * Cost center filter not working in cash flow report ([ce5065b](https://github.com/frappe/erpnext/commit/ce5065b13216ef77327829208d3ac679745fc24a)) * Cost Center for tax withholding invoices ([728643a](https://github.com/frappe/erpnext/commit/728643aa4a8abacf4fc5ac4ad4d7fbe982c08fd2)) * cost_center filter fix for 'Get Outstanding Invoice' in PE ([ff61997](https://github.com/frappe/erpnext/commit/ff61997d251c8f8b3b0eb727517873a0b82d570a)) * cost_center filter gives incorrect output ([9b2b281](https://github.com/frappe/erpnext/commit/9b2b2812ca50f981bdf2f440363a6c0672aafa65)) * daily scheduler to identify and fix stock transfer entries having incorrect valuation ([deb3efd](https://github.com/frappe/erpnext/commit/deb3efdd9ab1b95e3d2e6ab0d3a8079c61d20798)) * disabled items showing in the report 'Itemwise Recommended Reorder Level ([493509e](https://github.com/frappe/erpnext/commit/493509e42d9b39ac2e957333631af0a21c152360)) * get_serial_nos_for_fg() missing 1 required positional argument: 'args' ([bddb5b8](https://github.com/frappe/erpnext/commit/bddb5b8d25ebd9ec934abdd9c0bd9fa90c9f2612)) * incorrect type hints (backport [#33381](https://github.com/frappe/erpnext/issues/33381)) ([#33384](https://github.com/frappe/erpnext/issues/33384)) ([fa77259](https://github.com/frappe/erpnext/commit/fa77259f8d0f78ed7ed9a07eb0e1d764105bfb0e)) * Payment Request flow fixes from Order to Payment Entry ([a01db8f](https://github.com/frappe/erpnext/commit/a01db8fc38378756246dded7cf1fc5fe2098e470)) * remove unnecessary permissions from Appointment and Appointment Booking Settings ([#33358](https://github.com/frappe/erpnext/issues/33358)) ([#33395](https://github.com/frappe/erpnext/issues/33395)) ([6ef7eaf](https://github.com/frappe/erpnext/commit/6ef7eaf82e93278c6e72e0cbaa285f913b9830c4)) * translation for warning on Overbilling/-receipt/-delivery ([ba51d50](https://github.com/frappe/erpnext/commit/ba51d50fef754a1e5853062cd6b54f54b5c391a9)) * Unable to import COA through importer ([f8c09ee](https://github.com/frappe/erpnext/commit/f8c09ee720cce35447e2b339d172bf9045e8c68b)) * unsupported operand type(s) for +: 'int' and 'NoneType' ([7bdfb3d](https://github.com/frappe/erpnext/commit/7bdfb3d181798411d3d00613ad2994dacd7d29dc)) * unsupported operand type(s) for +=: 'int' and 'NoneType' ([88dc81b](https://github.com/frappe/erpnext/commit/88dc81b7d45ac086fc96cd54f3e76b3ea6daad13)) * use highest precision for exchange rate. ([4a8a84d](https://github.com/frappe/erpnext/commit/4a8a84d6f06f08c43f50cbc418733742a5d187da)) ### Features * Ignore company related doctype for other apps via hooks ([cd5a2af](https://github.com/frappe/erpnext/commit/cd5a2af27241cc148c2a655a1789597f7699d827)) * improve visibility of default values ([7ff50b9](https://github.com/frappe/erpnext/commit/7ff50b9446a14301ecdfa2f94e42b4e2e61bad7b)) * incoterm named place ([bfe57ac](https://github.com/frappe/erpnext/commit/bfe57acdbf067602ea4b765c675dc720517b0ab7)) * more control when printing RFQ ([07cda0a](https://github.com/frappe/erpnext/commit/07cda0aeb57ca0814c494ff42c7f791146f59275)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 383be7bb582e..246b8d36ef08 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.10.1" +__version__ = "14.11.0" def get_default_company(user=None): From abfb3bf1c6753cf1e2f3f2a167f625cb8a2606ad Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Dec 2022 15:01:44 +0530 Subject: [PATCH 14/54] fix: Random behaviour while picking items using picklist (backport #33449) (#33451) fix: Random behaviour while picking items using picklist (#33449) (cherry picked from commit 8263bf9a9a716f7651386633fd6f26686f2008bd) Co-authored-by: Deepesh Garg --- erpnext/stock/doctype/pick_list/pick_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index aff5e0539c7d..8704b6718b9b 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -441,7 +441,7 @@ def get_available_item_locations_for_batched_item( sle.`batch_no`, sle.`item_code` HAVING `qty` > 0 - ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation` + ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation`, sle.`batch_no`, sle.`warehouse` """.format( warehouse_condition=warehouse_condition ), From 0dbac5b6897b8a680b97aa8be256d2ed466d79e8 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 27 Dec 2022 09:35:01 +0000 Subject: [PATCH 15/54] chore(release): Bumped to Version 14.11.1 ## [14.11.1](https://github.com/frappe/erpnext/compare/v14.11.0...v14.11.1) (2022-12-27) ### Bug Fixes * Random behaviour while picking items using picklist (backport [#33449](https://github.com/frappe/erpnext/issues/33449)) ([#33451](https://github.com/frappe/erpnext/issues/33451)) ([abfb3bf](https://github.com/frappe/erpnext/commit/abfb3bf1c6753cf1e2f3f2a167f625cb8a2606ad)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 246b8d36ef08..f85a404de53c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.11.0" +__version__ = "14.11.1" def get_default_company(user=None): From 24d161d6708404d225b1619e36b49bcd3f7ea574 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 27 Dec 2022 18:26:24 +0530 Subject: [PATCH 16/54] chore: release v14 (#33452) * fix: typerror on multi warehouse in Packed Items DN(with bundled item with varying warehouses)-> Sales Invoice. (cherry picked from commit e684eb32d0cf62f67f2b1de30ec7368d36708321) * test: type error on bundled products with different warehouses (cherry picked from commit 5918bb03f7db388a27cb9319530b56c383304242) * fix: payment terms and sales partner filter issue in AR/AP report (cherry picked from commit 13c4420f42bb483bbbed7eddf168f4cb62554ab6) * fix: timeout error while submitting stock entry Co-authored-by: Ankush Menat (cherry picked from commit a05c47e49990ad00dc1b33b5d58688bca4b6b021) * fix: `shipping_address` in PO (cherry picked from commit 7e1b6b3c2aa114331ad9085cfefee340b8ca2ad0) * refactor: Customer and Supplier Ledger summary will have hidden fields for better handling of user permission (#33433) * feat: Accounting Dimension updation in Payment Request and Entry (#33411) * fix: `shipping_address` for non-drop shipping item (cherry picked from commit 67a7ccf3cead654e66a3d1b5ccb253d90b19c43b) * fix: Random behaviour while picking items using picklist (backport #33449) (#33450) fix: Random behaviour while picking items using picklist (#33449) (cherry picked from commit 8263bf9a9a716f7651386633fd6f26686f2008bd) Co-authored-by: Deepesh Garg * fix: Multiple rows for same warehouse and batches in pick list (backport #33456) (#33458) fix: Multiple rows for same warehouse and batches in pick list (#33456) (cherry picked from commit d2686ce75bb39bffff4fd1b56ad4880444efb72e) Co-authored-by: Deepesh Garg * fix: Default dimensions on fetching items from BOM (backport #33439) (#33459) fix: Default dimensions on fetching items from BOM (#33439) (cherry picked from commit 0b75aa53907e67d440884a2ea0084044265994a5) Co-authored-by: Deepesh Garg Co-authored-by: ruthra kumar Co-authored-by: Rohit Waghchaure Co-authored-by: s-aga-r Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Deepesh Garg --- .../doctype/payment_entry/payment_entry.py | 14 +++ .../payment_request/payment_request.json | 28 +++++- .../payment_request/payment_request.py | 25 +++++ .../accounts_receivable.py | 16 ++-- .../customer_ledger_summary.py | 72 ++++++++++++++ .../report/gross_profit/gross_profit.py | 1 + .../report/gross_profit/test_gross_profit.py | 95 +++++++++++++++++++ .../supplier_ledger_summary.js | 18 ---- .../purchase_order/purchase_order.json | 16 ++-- erpnext/patches.txt | 3 +- ...counting_dimensions_for_payment_request.py | 31 ++++++ .../doctype/sales_order/sales_order.py | 19 +++- .../customer_group/customer_group.json | 14 ++- .../supplier_group/supplier_group.json | 16 +++- .../setup/doctype/territory/territory.json | 14 ++- erpnext/stock/doctype/bin/bin.py | 1 + erpnext/stock/doctype/pick_list/pick_list.js | 10 +- erpnext/stock/doctype/pick_list/pick_list.py | 22 ++++- .../stock/doctype/stock_entry/stock_entry.js | 15 ++- erpnext/stock/stock_ledger.py | 23 ++++- 20 files changed, 400 insertions(+), 53 deletions(-) create mode 100644 erpnext/patches/v14_0/create_accounting_dimensions_for_payment_request.py diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 1a761b424adf..5dbc91654f97 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1758,6 +1758,8 @@ def get_payment_entry( pe.setup_party_account_field() pe.set_missing_values() + update_accounting_dimensions(pe, doc) + if party_account and bank: pe.set_exchange_rate(ref_doc=reference_doc) pe.set_amounts() @@ -1775,6 +1777,18 @@ def get_payment_entry( return pe +def update_accounting_dimensions(pe, doc): + """ + Updates accounting dimensions in Payment Entry based on the accounting dimensions in the reference document + """ + from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, + ) + + for dimension in get_accounting_dimensions(): + pe.set(dimension, doc.get(dimension)) + + def get_bank_cash_account(doc, bank_account): bank = get_default_bank_cash_account( doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 2f3516e135a4..381f3fb531a9 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -32,6 +32,10 @@ "iban", "branch_code", "swift_number", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "project", "recipient_and_message", "print_format", "email_to", @@ -362,13 +366,35 @@ "label": "Payment Channel", "options": "\nEmail\nPhone", "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "in_create": 1, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-09-30 16:19:43.680025", + "modified": "2022-12-21 16:56:40.115737", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index e09da6780718..f63fba1b7164 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -11,6 +11,9 @@ from frappe.utils.background_jobs import enqueue from payments.utils import get_payment_gateway_controller +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, +) from erpnext.accounts.doctype.payment_entry.payment_entry import ( get_company_defaults, get_payment_entry, @@ -263,6 +266,17 @@ def create_payment_entry(self, submit=True): } ) + # Update dimensions + payment_entry.update( + { + "cost_center": self.get("cost_center"), + "project": self.get("project"), + } + ) + + for dimension in get_accounting_dimensions(): + payment_entry.update({dimension: self.get(dimension)}) + if payment_entry.difference_amount: company_details = get_company_defaults(ref_doc.company) @@ -442,6 +456,17 @@ def make_payment_request(**args): } ) + # Update dimensions + pr.update( + { + "cost_center": ref_doc.get("cost_center"), + "project": ref_doc.get("project"), + } + ) + + for dimension in get_accounting_dimensions(): + pr.update({dimension: ref_doc.get(dimension)}) + if args.order_type == "Shopping Cart" or args.mute_email: pr.flags.mute_email = True diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a195c575866f..e73d5ba390da 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -794,19 +794,19 @@ def add_customer_filters( if self.filters.get("payment_terms_template"): self.qb_selection_filter.append( - self.ple.party_isin( - qb.from_(self.customer).where( - self.customer.payment_terms == self.filters.get("payment_terms_template") - ) + self.ple.party.isin( + qb.from_(self.customer) + .select(self.customer.name) + .where(self.customer.payment_terms == self.filters.get("payment_terms_template")) ) ) if self.filters.get("sales_partner"): self.qb_selection_filter.append( - self.ple.party_isin( - qb.from_(self.customer).where( - self.customer.default_sales_partner == self.filters.get("payment_terms_template") - ) + self.ple.party.isin( + qb.from_(self.customer) + .select(self.customer.name) + .where(self.customer.default_sales_partner == self.filters.get("payment_terms_template")) ) ) diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py index cafe95b3603d..d870a1aaf832 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -26,6 +26,7 @@ def run(self, args): ) self.get_gl_entries() + self.get_additional_columns() self.get_return_invoices() self.get_party_adjustment_amounts() @@ -33,6 +34,42 @@ def run(self, args): data = self.get_data() return columns, data + def get_additional_columns(self): + """ + Additional Columns for 'User Permission' based access control + """ + from frappe import qb + + if self.filters.party_type == "Customer": + self.territories = frappe._dict({}) + self.customer_group = frappe._dict({}) + + customer = qb.DocType("Customer") + result = ( + frappe.qb.from_(customer) + .select( + customer.name, customer.territory, customer.customer_group, customer.default_sales_partner + ) + .where((customer.disabled == 0)) + .run(as_dict=True) + ) + + for x in result: + self.territories[x.name] = x.territory + self.customer_group[x.name] = x.customer_group + else: + self.supplier_group = frappe._dict({}) + supplier = qb.DocType("Supplier") + result = ( + frappe.qb.from_(supplier) + .select(supplier.name, supplier.supplier_group) + .where((supplier.disabled == 0)) + .run(as_dict=True) + ) + + for x in result: + self.supplier_group[x.name] = x.supplier_group + def get_columns(self): columns = [ { @@ -116,6 +153,35 @@ def get_columns(self): }, ] + # Hidden columns for handling 'User Permissions' + if self.filters.party_type == "Customer": + columns += [ + { + "label": _("Territory"), + "fieldname": "territory", + "fieldtype": "Link", + "options": "Territory", + "hidden": 1, + }, + { + "label": _("Customer Group"), + "fieldname": "customer_group", + "fieldtype": "Link", + "options": "Customer Group", + "hidden": 1, + }, + ] + else: + columns += [ + { + "label": _("Supplier Group"), + "fieldname": "supplier_group", + "fieldtype": "Link", + "options": "Supplier Group", + "hidden": 1, + } + ] + return columns def get_data(self): @@ -143,6 +209,12 @@ def get_data(self): ), ) + if self.filters.party_type == "Customer": + self.party_data[gle.party].update({"territory": self.territories.get(gle.party)}) + self.party_data[gle.party].update({"customer_group": self.customer_group.get(gle.party)}) + else: + self.party_data[gle.party].update({"supplier_group": self.supplier_group.get(gle.party)}) + amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr) self.party_data[gle.party].closing_balance += amount diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index dacc809da0d3..ba947f392f81 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -607,6 +607,7 @@ def calculate_buying_amount_from_sle(self, row, my_sle, parenttype, parent, item return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty)) else: return flt(row.qty) * self.get_average_buying_rate(row, item_code) + return 0.0 def get_buying_amount(self, row, item_code): # IMP NOTE diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index 0ea6b5c8a441..fa11a41df4ad 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -6,6 +6,8 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.report.gross_profit.gross_profit import execute +from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice +from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry @@ -14,6 +16,7 @@ class TestGrossProfit(FrappeTestCase): def setUp(self): self.create_company() self.create_item() + self.create_bundle() self.create_customer() self.create_sales_invoice() self.clear_old_entries() @@ -42,6 +45,7 @@ def create_company(self): self.company = company.name self.cost_center = company.cost_center self.warehouse = "Stores - " + abbr + self.finished_warehouse = "Finished Goods - " + abbr self.income_account = "Sales - " + abbr self.expense_account = "Cost of Goods Sold - " + abbr self.debit_to = "Debtors - " + abbr @@ -53,6 +57,23 @@ def create_item(self): ) self.item = item if isinstance(item, str) else item.item_code + def create_bundle(self): + from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle + + item2 = create_item( + item_code="_Test GP Item 2", is_stock_item=1, company=self.company, warehouse=self.warehouse + ) + self.item2 = item2 if isinstance(item2, str) else item2.item_code + + # This will be parent item + bundle = create_item( + item_code="_Test GP bundle", is_stock_item=0, company=self.company, warehouse=self.warehouse + ) + self.bundle = bundle if isinstance(bundle, str) else bundle.item_code + + # Create Product Bundle + self.product_bundle = make_product_bundle(parent=self.bundle, items=[self.item, self.item2]) + def create_customer(self): name = "_Test GP Customer" if frappe.db.exists("Customer", name): @@ -93,6 +114,28 @@ def create_sales_invoice( ) return sinv + def create_delivery_note( + self, item=None, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + """ + Helper function to populate default values in Delivery Note + """ + dnote = create_delivery_note( + company=self.company, + customer=self.customer, + currency="INR", + item=item or self.item, + qty=qty, + rate=rate, + cost_center=self.cost_center, + warehouse=self.warehouse, + return_against=None, + expense_account=self.expense_account, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return dnote + def clear_old_entries(self): doctype_list = [ "Sales Invoice", @@ -207,3 +250,55 @@ def test_invoice_without_only_delivery_note(self): } gp_entry = [x for x in data if x.parent_invoice == sinv.name] self.assertDictContainsSubset(expected_entry_with_dn, gp_entry[0]) + + def test_bundled_delivery_note_with_different_warehouses(self): + """ + Test Delivery Note with bundled item. Packed Item from the bundle having different warehouses + """ + se = make_stock_entry( + company=self.company, + item_code=self.item, + target=self.warehouse, + qty=1, + basic_rate=100, + do_not_submit=True, + ) + item = se.items[0] + se.append( + "items", + { + "item_code": self.item2, + "s_warehouse": "", + "t_warehouse": self.finished_warehouse, + "qty": 1, + "basic_rate": 100, + "conversion_factor": item.conversion_factor or 1.0, + "transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0), + "serial_no": item.serial_no, + "batch_no": item.batch_no, + "cost_center": item.cost_center, + "expense_account": item.expense_account, + }, + ) + se = se.save().submit() + + # Make a Delivery note with Product bundle + # Packed Items will have different warehouses + dnote = self.create_delivery_note(item=self.bundle, qty=1, rate=200, do_not_submit=True) + dnote.packed_items[1].warehouse = self.finished_warehouse + dnote = dnote.submit() + + # make Sales Invoice for above delivery note + sinv = make_sales_invoice(dnote.name) + sinv = sinv.save().submit() + + filters = frappe._dict( + company=self.company, + from_date=nowdate(), + to_date=nowdate(), + group_by="Invoice", + sales_invoice=sinv.name, + ) + + columns, data = execute(filters=filters) + self.assertGreater(len(data), 0) diff --git a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js index f81297760edb..5dc4c3d1c159 100644 --- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js +++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js @@ -63,24 +63,6 @@ frappe.query_reports["Supplier Ledger Summary"] = { "fieldtype": "Link", "options": "Payment Terms Template" }, - { - "fieldname":"territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory" - }, - { - "fieldname":"sales_partner", - "label": __("Sales Partner"), - "fieldtype": "Link", - "options": "Sales Partner" - }, - { - "fieldname":"sales_person", - "label": __("Sales Person"), - "fieldtype": "Link", - "options": "Sales Person" - }, { "fieldname":"tax_id", "label": __("Tax Id"), diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index ce7de874c56f..e1dd67978154 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -108,7 +108,7 @@ "contact_display", "contact_mobile", "contact_email", - "company_shipping_address_section", + "shipping_address_section", "shipping_address", "column_break_99", "shipping_address_display", @@ -385,7 +385,7 @@ { "fieldname": "shipping_address", "fieldtype": "Link", - "label": "Company Shipping Address", + "label": "Shipping Address", "options": "Address", "print_hide": 1 }, @@ -1207,11 +1207,6 @@ "fieldtype": "Tab Break", "label": "Address & Contact" }, - { - "fieldname": "company_shipping_address_section", - "fieldtype": "Section Break", - "label": "Company Shipping Address" - }, { "fieldname": "company_billing_address_section", "fieldtype": "Section Break", @@ -1263,13 +1258,18 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-12-12 18:36:37.455134", + "modified": "2022-12-25 18:08:59.074182", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5e83f8376145..f7d2dedb1b32 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -319,4 +319,5 @@ erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization erpnext.patches.v13_0.update_schedule_type_in_loans erpnext.patches.v14_0.update_partial_tds_fields erpnext.patches.v14_0.create_incoterms_and_migrate_shipment -erpnext.patches.v14_0.setup_clear_repost_logs \ No newline at end of file +erpnext.patches.v14_0.setup_clear_repost_logs +erpnext.patches.v14_0.create_accounting_dimensions_for_payment_request \ No newline at end of file diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_for_payment_request.py b/erpnext/patches/v14_0/create_accounting_dimensions_for_payment_request.py new file mode 100644 index 000000000000..bede419ad29a --- /dev/null +++ b/erpnext/patches/v14_0/create_accounting_dimensions_for_payment_request.py @@ -0,0 +1,31 @@ +import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field + + +def execute(): + accounting_dimensions = frappe.db.get_all( + "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"] + ) + + if not accounting_dimensions: + return + + doctype = "Payment Request" + + for d in accounting_dimensions: + field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname}) + + if field: + continue + + df = { + "fieldname": d.fieldname, + "label": d.label, + "fieldtype": "Link", + "options": d.document_type, + "insert_after": "accounting_dimensions_section", + } + + create_custom_field(doctype, df, ignore_validate=True) + + frappe.clear_cache(doctype=doctype) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 0013c95032f4..7c0601e3dd54 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1024,6 +1024,15 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None): ] items_to_map = list(set(items_to_map)) + def is_drop_ship_order(target): + drop_ship = True + for item in target.items: + if not item.delivered_by_supplier: + drop_ship = False + break + + return drop_ship + def set_missing_values(source, target): target.supplier = "" target.apply_discount_on = "" @@ -1031,8 +1040,14 @@ def set_missing_values(source, target): target.discount_amount = 0.0 target.inter_company_order_reference = "" target.shipping_rule = "" - target.customer = "" - target.customer_name = "" + + if is_drop_ship_order(target): + target.customer = source.customer + target.customer_name = source.customer_name + target.shipping_address = source.shipping_address_name + else: + target.customer = target.customer_name = target.shipping_address = None + target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json index 0e2ed9efcf8c..d6a431ea616e 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.json +++ b/erpnext/setup/doctype/customer_group/customer_group.json @@ -139,10 +139,11 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2021-02-08 17:01:52.162202", + "modified": "2022-12-24 11:15:17.142746", "modified_by": "Administrator", "module": "Setup", "name": "Customer Group", + "naming_rule": "By fieldname", "nsm_parent_field": "parent_customer_group", "owner": "Administrator", "permissions": [ @@ -198,10 +199,19 @@ "role": "Customer", "select": 1, "share": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1 } ], "search_fields": "parent_customer_group", "show_name_in_global_search": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.json b/erpnext/setup/doctype/supplier_group/supplier_group.json index 9119bb947cbd..b3ed608cd033 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.json +++ b/erpnext/setup/doctype/supplier_group/supplier_group.json @@ -6,6 +6,7 @@ "creation": "2013-01-10 16:34:24", "doctype": "DocType", "document_type": "Setup", + "engine": "InnoDB", "field_order": [ "supplier_group_name", "parent_supplier_group", @@ -106,10 +107,11 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 18:10:49.228407", + "modified": "2022-12-24 11:16:12.486719", "modified_by": "Administrator", "module": "Setup", "name": "Supplier Group", + "naming_rule": "By fieldname", "nsm_parent_field": "parent_supplier_group", "owner": "Administrator", "permissions": [ @@ -156,8 +158,18 @@ "permlevel": 1, "read": 1, "role": "Purchase User" + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1 } ], "show_name_in_global_search": 1, - "sort_order": "ASC" + "sort_field": "modified", + "sort_order": "ASC", + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/territory/territory.json b/erpnext/setup/doctype/territory/territory.json index a25bda054b9c..c3a499337468 100644 --- a/erpnext/setup/doctype/territory/territory.json +++ b/erpnext/setup/doctype/territory/territory.json @@ -123,11 +123,12 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2021-02-08 17:10:03.767426", + "modified": "2022-12-24 11:16:39.964956", "modified_by": "Administrator", "module": "Setup", "name": "Territory", "name_case": "Title Case", + "naming_rule": "By fieldname", "nsm_parent_field": "parent_territory", "owner": "Administrator", "permissions": [ @@ -175,10 +176,19 @@ "role": "Customer", "select": 1, "share": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1 } ], "search_fields": "parent_territory,territory_manager", "show_name_in_global_search": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index c28f45aed416..9f409d4b96a0 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -162,6 +162,7 @@ def update_qty(bin_name, args): .where((sle.item_code == args.get("item_code")) & (sle.warehouse == args.get("warehouse"))) .orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=Order.desc) .orderby(sle.creation, order=Order.desc) + .limit(1) .run() ) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 799406cd79e1..8213adb89bf2 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -51,7 +51,15 @@ frappe.ui.form.on('Pick List', { if (!(frm.doc.locations && frm.doc.locations.length)) { frappe.msgprint(__('Add items in the Item Locations table')); } else { - frm.call('set_item_locations', {save: save}); + frappe.call({ + method: "set_item_locations", + doc: frm.doc, + args: { + "save": save, + }, + freeze: 1, + freeze_message: __("Setting Item Locations..."), + }); } }, get_item_locations: (frm) => { diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 8704b6718b9b..953fca7419cc 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -135,6 +135,7 @@ def set_item_locations(self, save=False): # reset self.delete_key("locations") + updated_locations = frappe._dict() for item_doc in items: item_code = item_doc.item_code @@ -155,7 +156,26 @@ def set_item_locations(self, save=False): for row in locations: location = item_doc.as_dict() location.update(row) - self.append("locations", location) + key = ( + location.item_code, + location.warehouse, + location.uom, + location.batch_no, + location.serial_no, + location.sales_order_item or location.material_request_item, + ) + + if key not in updated_locations: + updated_locations.setdefault(key, location) + else: + updated_locations[key].qty += location.qty + updated_locations[key].stock_qty += location.stock_qty + + for location in updated_locations.values(): + if location.picked_qty > location.stock_qty: + location.picked_qty = location.stock_qty + + self.append("locations", location) # If table is empty on update after submit, set stock_qty, picked_qty to 0 so that indicator is red # and give feedback to the user. This is to avoid empty Pick Lists. diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index b9102445e01a..d4b4efa4cdd2 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -112,6 +112,10 @@ frappe.ui.form.on('Stock Entry', { } }); attach_bom_items(frm.doc.bom_no); + + if(!check_should_not_attach_bom_items(frm.doc.bom_no)) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + } }, setup_quality_inspection: function(frm) { @@ -326,7 +330,11 @@ frappe.ui.form.on('Stock Entry', { } frm.trigger("setup_quality_inspection"); - attach_bom_items(frm.doc.bom_no) + attach_bom_items(frm.doc.bom_no); + + if(!check_should_not_attach_bom_items(frm.doc.bom_no)) { + erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); + } }, before_save: function(frm) { @@ -939,7 +947,10 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle method: "get_items", callback: function(r) { if(!r.exc) refresh_field("items"); - if(me.frm.doc.bom_no) attach_bom_items(me.frm.doc.bom_no) + if(me.frm.doc.bom_no) { + attach_bom_items(me.frm.doc.bom_no); + erpnext.accounts.dimensions.update_dimension(me.frm, me.frm.doctype); + } } }); } diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index e7f55e9b35dc..55a11a186716 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -470,8 +470,10 @@ def get_sle_against_current_voucher(self): item_code = %(item_code)s and warehouse = %(warehouse)s and is_cancelled = 0 - and timestamp(posting_date, time_format(posting_time, %(time_format)s)) = timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) - + and ( + posting_date = %(posting_date)s and + time_format(posting_time, %(time_format)s) = time_format(%(posting_time)s, %(time_format)s) + ) order by creation ASC for update @@ -1070,7 +1072,13 @@ def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False): and warehouse = %(warehouse)s and is_cancelled = 0 {voucher_condition} - and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) + and ( + posting_date < %(posting_date)s or + ( + posting_date = %(posting_date)s and + time_format(posting_time, %(time_format)s) < time_format(%(posting_time)s, %(time_format)s) + ) + ) order by timestamp(posting_date, posting_time) desc, creation desc limit 1 for update""".format( @@ -1355,8 +1363,13 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): and warehouse = %(warehouse)s and voucher_no != %(voucher_no)s and is_cancelled = 0 - and timestamp(posting_date, time_format(posting_time, %(time_format)s)) - > timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s)) + and ( + posting_date > %(posting_date)s or + ( + posting_date = %(posting_date)s and + time_format(posting_time, %(time_format)s) > time_format(%(posting_time)s, %(time_format)s) + ) + ) {datetime_limit_condition} """, args, From aab856ceb167896ad686ac8ab88600bacd5b33c5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Jan 2023 02:54:39 +0000 Subject: [PATCH 17/54] chore(release): Bumped to Version 14.12.0 # [14.12.0](https://github.com/frappe/erpnext/compare/v14.11.1...v14.12.0) (2023-01-04) ### Bug Fixes * [concurrency issue] incorrect picked qty in sales order ([e7254fd](https://github.com/frappe/erpnext/commit/e7254fd161c42dee123e4f998340b55c35c4ecb6)) * `fg_item_qty` in non-subcontracted PO ([6e15331](https://github.com/frappe/erpnext/commit/6e15331fd4c906223f9d4605a12373aef4a5f061)) * `shipping_address` for non-drop shipping item ([a7a3654](https://github.com/frappe/erpnext/commit/a7a3654541b75854bdc69a822845e23e4088dee5)) * `shipping_address` in PO ([448fbe5](https://github.com/frappe/erpnext/commit/448fbe55826c06fd85fc95c84b2c678ca19fb61f)) * add missing 'ordered_qty' to get_bin_details ([55e8e45](https://github.com/frappe/erpnext/commit/55e8e45d52970cefb5d7ce63d6cbbc16b73d46c8)) * consider child nodes while getting bin details ([e296408](https://github.com/frappe/erpnext/commit/e2964088b76bbd6dbbd6c016d4dad4850d33b322)) * Conversion factor error for invoices without item code (petty expenses) ([#32714](https://github.com/frappe/erpnext/issues/32714)) ([ba5a149](https://github.com/frappe/erpnext/commit/ba5a149a6b75280ba0984a1435dc92a7af327c39)) * Customer Primary Contact (backport [#33424](https://github.com/frappe/erpnext/issues/33424)) ([#33440](https://github.com/frappe/erpnext/issues/33440)) ([73c9820](https://github.com/frappe/erpnext/commit/73c9820e825e30cb88495c6735339882699a18f7)) * Default dimensions on fetching items from BOM (backport [#33439](https://github.com/frappe/erpnext/issues/33439)) ([#33459](https://github.com/frappe/erpnext/issues/33459)) ([a332b22](https://github.com/frappe/erpnext/commit/a332b229cfb09a2f83d96f1cdb74cc862516b0f4)) * Deferred revenue date comparison ([#33515](https://github.com/frappe/erpnext/issues/33515)) ([027510b](https://github.com/frappe/erpnext/commit/027510b629f612887c20479e7b04fde4ddd83d97)) * ERR journals reported in AR/AP ([823b352](https://github.com/frappe/erpnext/commit/823b352c57af2e2e698c5b2debe615b4605ff651)) * Get payment entry button not visible in Bank Clearance doc (backport [#33518](https://github.com/frappe/erpnext/issues/33518)) ([#33525](https://github.com/frappe/erpnext/issues/33525)) ([fcf052d](https://github.com/frappe/erpnext/commit/fcf052d3c48c64ee46a0a9f8d7391186b15db0d0)) * Missing opening entry in general ledger (backport [#33519](https://github.com/frappe/erpnext/issues/33519)) ([#33526](https://github.com/frappe/erpnext/issues/33526)) ([8e375db](https://github.com/frappe/erpnext/commit/8e375db0b248952e3292069c17bd639c2b0a893c)) * Multi-currency issues in Bank Reconciliation Tool ([#33488](https://github.com/frappe/erpnext/issues/33488)) ([d030852](https://github.com/frappe/erpnext/commit/d03085259d1f8e7f21256451be076ccd9134718f)) * Multiple rows for same warehouse and batches in pick list (backport [#33456](https://github.com/frappe/erpnext/issues/33456)) ([#33458](https://github.com/frappe/erpnext/issues/33458)) ([a166a76](https://github.com/frappe/erpnext/commit/a166a7676847cc8fe34bb879bfcbf59adeb85e47)) * payment terms and sales partner filter issue in AR/AP report ([0f6790b](https://github.com/frappe/erpnext/commit/0f6790be11d9c191cc8640d4ab5827cd86072173)) * **pricing rule:** consider child tables in condition (backport [#33469](https://github.com/frappe/erpnext/issues/33469)) ([#33470](https://github.com/frappe/erpnext/issues/33470)) ([3bceb47](https://github.com/frappe/erpnext/commit/3bceb475427ed13fc2cf57ebe8c6adfc8820fa57)) * Random behaviour while picking items using picklist (backport [#33449](https://github.com/frappe/erpnext/issues/33449)) ([#33450](https://github.com/frappe/erpnext/issues/33450)) ([1edde9c](https://github.com/frappe/erpnext/commit/1edde9c9e05868166b0ba929b373f4968238442d)) * set `supplier` details while mapping SE(Send to Subcontractor) ([06e13b6](https://github.com/frappe/erpnext/commit/06e13b64a429af702c79e0e5a4d7cf0e2668a18c)) * timeout error while submitting stock entry ([f30f77c](https://github.com/frappe/erpnext/commit/f30f77cde693d3df259257b0c9ad8ee836d82310)) * typerror on multi warehouse in Packed Items ([9b24940](https://github.com/frappe/erpnext/commit/9b24940059c5a762cdc60af638d479f2bec346d5)) * use base_net_amount in case of missing stock qty ([#33457](https://github.com/frappe/erpnext/issues/33457)) ([88ca780](https://github.com/frappe/erpnext/commit/88ca7806af440d7ba09382cd5c197c18b0af603a)) ### Features * Accounting Dimension updation in Payment Request and Entry ([#33411](https://github.com/frappe/erpnext/issues/33411)) ([e7704b2](https://github.com/frappe/erpnext/commit/e7704b2321c6c4fdaf4850ba9b37554daf092304)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f85a404de53c..4aa7b4fd90dd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.11.1" +__version__ = "14.12.0" def get_default_company(user=None): From 259639a4563107a15d87289287de77a2a7b6b611 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 3 Jan 2023 16:55:15 +0530 Subject: [PATCH 18/54] fix: Exchange gain and loss booking on multi-currency invoice reconciliation (#32900) * fix: Exchange gain and loss booking on multi-curreny invoice reconciliation * test: Update test cases * chore: Ignore SQL linting rule * chore: Joural Entry for exchange gainand loss booking * chore: Journal entry for exchange gain loss booking * test: Update test case * chore: Default exchange gain and loss account (cherry picked from commit 9a3d947e893a787834bf12a9cf50c4af9e449f40) --- .../doctype/journal_entry/journal_entry.py | 18 +- .../payment_reconciliation.js | 16 +- .../payment_reconciliation.py | 132 ++++++++++-- .../test_payment_reconciliation.py | 198 +++++++++++++++--- .../payment_reconciliation_allocation.json | 26 ++- .../payment_reconciliation_invoice.json | 12 +- .../payment_reconciliation_payment.json | 14 +- 7 files changed, 349 insertions(+), 67 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 5a1b6ba1712f..88b030cae3d5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -592,15 +592,15 @@ def set_against_account(self): d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field) else: for d in self.get("accounts"): - if flt(d.debit > 0): + if flt(d.debit) > 0: accounts_debited.append(d.party or d.account) if flt(d.credit) > 0: accounts_credited.append(d.party or d.account) for d in self.get("accounts"): - if flt(d.debit > 0): + if flt(d.debit) > 0: d.against_account = ", ".join(list(set(accounts_credited))) - if flt(d.credit > 0): + if flt(d.credit) > 0: d.against_account = ", ".join(list(set(accounts_debited))) def validate_debit_credit_amount(self): @@ -762,7 +762,7 @@ def set_print_format_fields(self): pay_to_recd_from = d.party if pay_to_recd_from and pay_to_recd_from == d.party: - party_amount += d.debit_in_account_currency or d.credit_in_account_currency + party_amount += flt(d.debit_in_account_currency) or flt(d.credit_in_account_currency) party_account_currency = d.account_currency elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]: @@ -840,7 +840,7 @@ def make_gl_entries(self, cancel=0, adv_adj=0): make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding) @frappe.whitelist() - def get_balance(self): + def get_balance(self, difference_account=None): if not self.get("accounts"): msgprint(_("'Entries' cannot be empty"), raise_exception=True) else: @@ -855,7 +855,13 @@ def get_balance(self): blank_row = d if not blank_row: - blank_row = self.append("accounts", {}) + blank_row = self.append( + "accounts", + { + "account": difference_account, + "cost_center": erpnext.get_default_cost_center(self.company), + }, + ) blank_row.exchange_rate = 1 if diff > 0: diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 0b334ae076df..d986f320669c 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -170,7 +170,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo } reconcile() { - var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account); + var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount); if (show_dialog && show_dialog.length) { @@ -179,8 +179,12 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo title: __("Select Difference Account"), fields: [ { - fieldname: "allocation", fieldtype: "Table", label: __("Allocation"), - data: this.data, in_place_edit: true, + fieldname: "allocation", + fieldtype: "Table", + label: __("Allocation"), + data: this.data, + in_place_edit: true, + cannot_add_rows: true, get_data: () => { return this.data; }, @@ -218,6 +222,10 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo read_only: 1 }] }, + { + fieldtype: 'HTML', + options: " New Journal Entry will be posted for the difference amount " + } ], primary_action: () => { const args = dialog.get_values()["allocation"]; @@ -234,7 +242,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }); this.frm.doc.allocation.forEach(d => { - if (d.difference_amount && !d.difference_account) { + if (d.difference_amount) { dialog.fields_dict.allocation.df.data.push({ 'docname': d.name, 'reference_name': d.reference_name, diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index ff212f2a35f5..ac033f7db603 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -14,7 +14,6 @@ QueryPaymentLedger, get_outstanding_invoices, reconcile_against_document, - update_reference_in_payment_entry, ) from erpnext.controllers.accounts_controller import get_advance_payment_entries @@ -80,12 +79,13 @@ def get_jv_entries(self): "t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1" ) + # nosemgrep journal_entries = frappe.db.sql( """ select "Journal Entry" as reference_type, t1.name as reference_name, t1.posting_date, t1.remark as remarks, t2.name as reference_row, - {dr_or_cr} as amount, t2.is_advance, + {dr_or_cr} as amount, t2.is_advance, t2.exchange_rate, t2.account_currency as currency from `tabJournal Entry` t1, `tabJournal Entry Account` t2 @@ -215,26 +215,26 @@ def add_invoice_entries(self, non_reconciled_invoices): inv.currency = entry.get("currency") inv.outstanding_amount = flt(entry.get("outstanding_amount")) - def get_difference_amount(self, allocated_entry): - if allocated_entry.get("reference_type") != "Payment Entry": - return + def get_difference_amount(self, payment_entry, invoice, allocated_amount): + difference_amount = 0 + if invoice.get("exchange_rate") and payment_entry.get("exchange_rate", 1) != invoice.get( + "exchange_rate", 1 + ): + allocated_amount_in_ref_rate = payment_entry.get("exchange_rate", 1) * allocated_amount + allocated_amount_in_inv_rate = invoice.get("exchange_rate", 1) * allocated_amount + difference_amount = allocated_amount_in_ref_rate - allocated_amount_in_inv_rate - dr_or_cr = ( - "credit_in_account_currency" - if erpnext.get_party_account_type(self.party_type) == "Receivable" - else "debit_in_account_currency" - ) - - row = self.get_payment_details(allocated_entry, dr_or_cr) - - doc = frappe.get_doc(allocated_entry.reference_type, allocated_entry.reference_name) - update_reference_in_payment_entry(row, doc, do_not_save=True) - - return doc.difference_amount + return difference_amount @frappe.whitelist() def allocate_entries(self, args): self.validate_entries() + + invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices")) + default_exchange_gain_loss_account = frappe.get_cached_value( + "Company", self.company, "exchange_gain_loss_account" + ) + entries = [] for pay in args.get("payments"): pay.update({"unreconciled_amount": pay.get("amount")}) @@ -248,7 +248,10 @@ def allocate_entries(self, args): inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount")) pay["amount"] = 0 - res.difference_amount = self.get_difference_amount(res) + inv["exchange_rate"] = invoice_exchange_map.get(inv.get("invoice_number")) + res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"]) + res.difference_account = default_exchange_gain_loss_account + res.exchange_rate = inv.get("exchange_rate") if pay.get("amount") == 0: entries.append(res) @@ -278,6 +281,7 @@ def get_allocated_entry(self, pay, inv, allocated_amount): "amount": pay.get("amount"), "allocated_amount": allocated_amount, "difference_amount": pay.get("difference_amount"), + "currency": inv.get("currency"), } ) @@ -300,7 +304,11 @@ def reconcile(self): else: reconciled_entry = entry_list - reconciled_entry.append(self.get_payment_details(row, dr_or_cr)) + payment_details = self.get_payment_details(row, dr_or_cr) + reconciled_entry.append(payment_details) + + if payment_details.difference_amount: + self.make_difference_entry(payment_details) if entry_list: reconcile_against_document(entry_list) @@ -311,6 +319,56 @@ def reconcile(self): msgprint(_("Successfully Reconciled")) self.get_unreconciled_entries() + def make_difference_entry(self, row): + journal_entry = frappe.new_doc("Journal Entry") + journal_entry.voucher_type = "Exchange Gain Or Loss" + journal_entry.company = self.company + journal_entry.posting_date = nowdate() + journal_entry.multi_currency = 1 + + party_account_currency = frappe.get_cached_value( + "Account", self.receivable_payable_account, "account_currency" + ) + difference_account_currency = frappe.get_cached_value( + "Account", row.difference_account, "account_currency" + ) + + # Account Currency has balance + dr_or_cr = "debit" if self.party_type == "Customer" else "debit" + reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" + + journal_account = frappe._dict( + { + "account": self.receivable_payable_account, + "party_type": self.party_type, + "party": self.party, + "account_currency": party_account_currency, + "exchange_rate": 0, + "cost_center": erpnext.get_default_cost_center(self.company), + "reference_type": row.against_voucher_type, + "reference_name": row.against_voucher, + dr_or_cr: flt(row.difference_amount), + dr_or_cr + "_in_account_currency": 0, + } + ) + + journal_entry.append("accounts", journal_account) + + journal_account = frappe._dict( + { + "account": row.difference_account, + "account_currency": difference_account_currency, + "exchange_rate": 1, + "cost_center": erpnext.get_default_cost_center(self.company), + reverse_dr_or_cr + "_in_account_currency": flt(row.difference_amount), + } + ) + + journal_entry.append("accounts", journal_account) + + journal_entry.save() + journal_entry.submit() + def get_payment_details(self, row, dr_or_cr): return frappe._dict( { @@ -320,6 +378,7 @@ def get_payment_details(self, row, dr_or_cr): "against_voucher_type": row.get("invoice_type"), "against_voucher": row.get("invoice_number"), "account": self.receivable_payable_account, + "exchange_rate": row.get("exchange_rate"), "party_type": self.party_type, "party": self.party, "is_advance": row.get("is_advance"), @@ -344,6 +403,41 @@ def validate_entries(self): if not self.get("payments"): frappe.throw(_("No records found in the Payments table")) + def get_invoice_exchange_map(self, invoices): + sales_invoices = [ + d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Sales Invoice" + ] + purchase_invoices = [ + d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Purchase Invoice" + ] + invoice_exchange_map = frappe._dict() + + if sales_invoices: + sales_invoice_map = frappe._dict( + frappe.db.get_all( + "Sales Invoice", + filters={"name": ("in", sales_invoices)}, + fields=["name", "conversion_rate"], + as_list=1, + ) + ) + + invoice_exchange_map.update(sales_invoice_map) + + if purchase_invoices: + purchase_invoice_map = frappe._dict( + frappe.db.get_all( + "Purchase Invoice", + filters={"name": ("in", purchase_invoices)}, + fields=["name", "conversion_rate"], + as_list=1, + ) + ) + + invoice_exchange_map.update(purchase_invoice_map) + + return invoice_exchange_map + def validate_allocation(self): unreconciled_invoices = frappe._dict() diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index 6030134fff2d..2ba90b4da9ff 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -6,7 +6,7 @@ import frappe from frappe import qb from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days, nowdate +from frappe.utils import add_days, flt, nowdate from erpnext import get_default_cost_center from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -75,33 +75,11 @@ def create_item(self): self.item = item if isinstance(item, str) else item.item_code def create_customer(self): - if frappe.db.exists("Customer", "_Test PR Customer"): - self.customer = "_Test PR Customer" - else: - customer = frappe.new_doc("Customer") - customer.customer_name = "_Test PR Customer" - customer.type = "Individual" - customer.save() - self.customer = customer.name - - if frappe.db.exists("Customer", "_Test PR Customer 2"): - self.customer2 = "_Test PR Customer 2" - else: - customer = frappe.new_doc("Customer") - customer.customer_name = "_Test PR Customer 2" - customer.type = "Individual" - customer.save() - self.customer2 = customer.name - - if frappe.db.exists("Customer", "_Test PR Customer 3"): - self.customer3 = "_Test PR Customer 3" - else: - customer = frappe.new_doc("Customer") - customer.customer_name = "_Test PR Customer 3" - customer.type = "Individual" - customer.default_currency = "EUR" - customer.save() - self.customer3 = customer.name + self.customer = make_customer("_Test PR Customer") + self.customer2 = make_customer("_Test PR Customer 2") + self.customer3 = make_customer("_Test PR Customer 3", "EUR") + self.customer4 = make_customer("_Test PR Customer 4", "EUR") + self.customer5 = make_customer("_Test PR Customer 5", "EUR") def create_account(self): account_name = "Debtors EUR" @@ -598,6 +576,156 @@ def test_pr_output_foreign_currency_and_amount(self): self.assertEqual(pr.payments[0].amount, amount) self.assertEqual(pr.payments[0].currency, "EUR") + def test_difference_amount_via_journal_entry(self): + # Make Sale Invoice + si = self.create_sales_invoice( + qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True + ) + si.customer = self.customer4 + si.currency = "EUR" + si.conversion_rate = 85 + si.debit_to = self.debtors_eur + si.save().submit() + + # Make payment using Journal Entry + je1 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 100, nowdate()) + je1.multi_currency = 1 + je1.accounts[0].exchange_rate = 1 + je1.accounts[0].credit_in_account_currency = 0 + je1.accounts[0].credit = 0 + je1.accounts[0].debit_in_account_currency = 8000 + je1.accounts[0].debit = 8000 + je1.accounts[1].party_type = "Customer" + je1.accounts[1].party = self.customer4 + je1.accounts[1].exchange_rate = 80 + je1.accounts[1].credit_in_account_currency = 100 + je1.accounts[1].credit = 8000 + je1.accounts[1].debit_in_account_currency = 0 + je1.accounts[1].debit = 0 + je1.save() + je1.submit() + + je2 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 200, nowdate()) + je2.multi_currency = 1 + je2.accounts[0].exchange_rate = 1 + je2.accounts[0].credit_in_account_currency = 0 + je2.accounts[0].credit = 0 + je2.accounts[0].debit_in_account_currency = 16000 + je2.accounts[0].debit = 16000 + je2.accounts[1].party_type = "Customer" + je2.accounts[1].party = self.customer4 + je2.accounts[1].exchange_rate = 80 + je2.accounts[1].credit_in_account_currency = 200 + je1.accounts[1].credit = 16000 + je1.accounts[1].debit_in_account_currency = 0 + je1.accounts[1].debit = 0 + je2.save() + je2.submit() + + pr = self.create_payment_reconciliation() + pr.party = self.customer4 + pr.receivable_payable_account = self.debtors_eur + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 2) + + # Test exact payment allocation + invoices = [x.as_dict() for x in pr.invoices] + payments = [pr.payments[0].as_dict()] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + self.assertEqual(pr.allocation[0].allocated_amount, 100) + self.assertEqual(pr.allocation[0].difference_amount, -500) + + # Test partial payment allocation (with excess payment entry) + pr.set("allocation", []) + pr.get_unreconciled_entries() + invoices = [x.as_dict() for x in pr.invoices] + payments = [pr.payments[1].as_dict()] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.allocation[0].difference_account = "Exchange Gain/Loss - _PR" + + self.assertEqual(pr.allocation[0].allocated_amount, 100) + self.assertEqual(pr.allocation[0].difference_amount, -500) + + # Check if difference journal entry gets generated for difference amount after reconciliation + pr.reconcile() + total_debit_amount = frappe.db.get_all( + "Journal Entry Account", + {"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name}, + "sum(debit) as amount", + group_by="reference_name", + )[0].amount + + self.assertEqual(flt(total_debit_amount, 2), -500) + + def test_difference_amount_via_payment_entry(self): + # Make Sale Invoice + si = self.create_sales_invoice( + qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True + ) + si.customer = self.customer5 + si.currency = "EUR" + si.conversion_rate = 85 + si.debit_to = self.debtors_eur + si.save().submit() + + # Make payment using Payment Entry + pe1 = create_payment_entry( + company=self.company, + payment_type="Receive", + party_type="Customer", + party=self.customer5, + paid_from=self.debtors_eur, + paid_to=self.bank, + paid_amount=100, + ) + + pe1.source_exchange_rate = 80 + pe1.received_amount = 8000 + pe1.save() + pe1.submit() + + pe2 = create_payment_entry( + company=self.company, + payment_type="Receive", + party_type="Customer", + party=self.customer5, + paid_from=self.debtors_eur, + paid_to=self.bank, + paid_amount=200, + ) + + pe2.source_exchange_rate = 80 + pe2.received_amount = 16000 + pe2.save() + pe2.submit() + + pr = self.create_payment_reconciliation() + pr.party = self.customer5 + pr.receivable_payable_account = self.debtors_eur + pr.get_unreconciled_entries() + + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 2) + + invoices = [x.as_dict() for x in pr.invoices] + payments = [pr.payments[0].as_dict()] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + self.assertEqual(pr.allocation[0].allocated_amount, 100) + self.assertEqual(pr.allocation[0].difference_amount, -500) + + pr.set("allocation", []) + pr.get_unreconciled_entries() + invoices = [x.as_dict() for x in pr.invoices] + payments = [pr.payments[1].as_dict()] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + + self.assertEqual(pr.allocation[0].allocated_amount, 100) + self.assertEqual(pr.allocation[0].difference_amount, -500) + def test_differing_cost_center_on_invoice_and_payment(self): """ Cost Center filter should not affect outstanding amount calculation @@ -618,3 +746,17 @@ def test_differing_cost_center_on_invoice_and_payment(self): # check PR tool output self.assertEqual(len(pr.get("invoices")), 0) self.assertEqual(len(pr.get("payments")), 0) + + +def make_customer(customer_name, currency=None): + if not frappe.db.exists("Customer", customer_name): + customer = frappe.new_doc("Customer") + customer.customer_name = customer_name + customer.type = "Individual" + + if currency: + customer.default_currency = currency + customer.save() + return customer.name + else: + return customer_name diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json index 6a21692c6ac1..0f7e47acfee4 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json +++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json @@ -20,7 +20,9 @@ "section_break_5", "difference_amount", "column_break_7", - "difference_account" + "difference_account", + "exchange_rate", + "currency" ], "fields": [ { @@ -37,7 +39,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Allocated Amount", - "options": "Currency", + "options": "currency", "reqd": 1 }, { @@ -112,7 +114,7 @@ "fieldtype": "Currency", "hidden": 1, "label": "Unreconciled Amount", - "options": "Currency", + "options": "currency", "read_only": 1 }, { @@ -120,7 +122,7 @@ "fieldtype": "Currency", "hidden": 1, "label": "Amount", - "options": "Currency", + "options": "currency", "read_only": 1 }, { @@ -129,11 +131,24 @@ "hidden": 1, "label": "Reference Row", "read_only": 1 + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency" + }, + { + "fieldname": "exchange_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2021-10-06 11:48:59.616562", + "modified": "2022-12-24 21:01:14.882747", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Allocation", @@ -141,5 +156,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json index 00c9e1240c54..c4dbd7e84412 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json @@ -11,7 +11,8 @@ "col_break1", "amount", "outstanding_amount", - "currency" + "currency", + "exchange_rate" ], "fields": [ { @@ -62,11 +63,17 @@ "hidden": 1, "label": "Currency", "options": "Currency" + }, + { + "fieldname": "exchange_rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Exchange Rate" } ], "istable": 1, "links": [], - "modified": "2021-08-24 22:42:40.923179", + "modified": "2022-11-08 18:18:02.502149", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Invoice", @@ -75,5 +82,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index add07e870d8e..d300ea97abc4 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -15,7 +15,8 @@ "difference_amount", "sec_break1", "remark", - "currency" + "currency", + "exchange_rate" ], "fields": [ { @@ -91,11 +92,17 @@ "label": "Difference Amount", "options": "currency", "read_only": 1 + }, + { + "fieldname": "exchange_rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Exchange Rate" } ], "istable": 1, "links": [], - "modified": "2021-08-30 10:51:48.140062", + "modified": "2022-11-08 18:18:36.268760", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", @@ -103,5 +110,6 @@ "permissions": [], "quick_entry": 1, "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file From 1b3df094afe4456f5f1d7b915e3fe465f9841518 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Jan 2023 17:48:26 +0000 Subject: [PATCH 19/54] chore(release): Bumped to Version 14.12.1 ## [14.12.1](https://github.com/frappe/erpnext/compare/v14.12.0...v14.12.1) (2023-01-10) ### Bug Fixes * **accounts:** currency fields no longer read as strings by validation function in Payment Entry ([#33535](https://github.com/frappe/erpnext/issues/33535)) ([44a95da](https://github.com/frappe/erpnext/commit/44a95da8ab7cd97cf26bf4bc5b55b5b8309895cb)) * better handling of duplicate bundle items ([b96a97f](https://github.com/frappe/erpnext/commit/b96a97f6b4811ad499940a2525357539b9eca46c)) * customer/supplier quick entry dialog ([#33496](https://github.com/frappe/erpnext/issues/33496)) ([914e2fd](https://github.com/frappe/erpnext/commit/914e2fdded1bfa155b3100b22ac68d762415063c)) * don't check other warehouse ledgers to calculate valuation rate ([ab0a2b4](https://github.com/frappe/erpnext/commit/ab0a2b427291cd381f885dc46b62dcd67bada195)) * Exchange gain and loss booking on multi-currency invoice reconciliation ([#32900](https://github.com/frappe/erpnext/issues/32900)) ([fe82ebc](https://github.com/frappe/erpnext/commit/fe82ebcc38b1eed26a6b722aadfa304f895694b3)) * Exchange gain and loss booking on multi-currency invoice reconciliation ([#32900](https://github.com/frappe/erpnext/issues/32900)) ([259639a](https://github.com/frappe/erpnext/commit/259639a4563107a15d87289287de77a2a7b6b611)) * Incorrect exchange rate in payment entries ([#33481](https://github.com/frappe/erpnext/issues/33481)) ([e995e95](https://github.com/frappe/erpnext/commit/e995e952b576a0aa1e4415e7dda78ea4518bca49)) * incorrect status in the work order ([2658fc9](https://github.com/frappe/erpnext/commit/2658fc9f9b022cac6806ef4aa1152c392c9afc0f)) * incorrect warehouse and selling amount on bundled products ([#33549](https://github.com/frappe/erpnext/issues/33549)) ([c6c3ac3](https://github.com/frappe/erpnext/commit/c6c3ac3e55e5a2b4e17793a120c4a9a840a43269)) * RFQ emails not sent with pdf attachment ([#33604](https://github.com/frappe/erpnext/issues/33604)) ([34df9ab](https://github.com/frappe/erpnext/commit/34df9ab7d52349f9b071609e1d4bc8d4acea88d7)) * **stock entry:** wrong valuation rate in repack ([#33579](https://github.com/frappe/erpnext/issues/33579)) ([a92b4e7](https://github.com/frappe/erpnext/commit/a92b4e7255e173873fa285dc2f5fca414dc601b8)) * Timeout error while saving the purchase invoice ([#33577](https://github.com/frappe/erpnext/issues/33577)) ([d2e3701](https://github.com/frappe/erpnext/commit/d2e3701b1a0221b6638f433f6acfd72fed526e06)) ### Performance Improvements * Drop `name` part from posting sort index ([#33551](https://github.com/frappe/erpnext/issues/33551)) ([f501575](https://github.com/frappe/erpnext/commit/f5015750e4451201c9f9b444a1e2590e8fb892eb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4aa7b4fd90dd..e3f67077086d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.12.0" +__version__ = "14.12.1" def get_default_company(user=None): From e910c949f7d0ad3de2501546aadf333492f2b92f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 17 Jan 2023 16:02:54 +0000 Subject: [PATCH 20/54] chore(release): Bumped to Version 14.13.0 # [14.13.0](https://github.com/frappe/erpnext/compare/v14.12.1...v14.13.0) (2023-01-17) ### Bug Fixes * allow to create sales order from expired quotation ([#33582](https://github.com/frappe/erpnext/issues/33582)) ([fe51343](https://github.com/frappe/erpnext/commit/fe513433b282ccce57745686254a26eeb66eaf3f)) * asset repair link ([bc55f44](https://github.com/frappe/erpnext/commit/bc55f44de6719483b158dec193293d61eed04905)) * asset value in fixed asset register ([#33608](https://github.com/frappe/erpnext/issues/33608)) ([4d2497f](https://github.com/frappe/erpnext/commit/4d2497faf1d3987d0b87c6b48bafbd4c3e2df852)) * attribute error while submitting Repost PLE ([0431a57](https://github.com/frappe/erpnext/commit/0431a57ff067e492fdedd3bd767c7acb1402e00d)) * better comparision of difference value between stock and account ([5869fcb](https://github.com/frappe/erpnext/commit/5869fcbd861fbd5202c24badd06fa2a80b4bc820)) * minor filter issue while reconciliation tool from bench console ([bddf330](https://github.com/frappe/erpnext/commit/bddf3307542cdf98d16016c94d11a775ace019db)) * Missing constructor args in Bank Reco Tool ([#33705](https://github.com/frappe/erpnext/issues/33705)) ([f88c8c4](https://github.com/frappe/erpnext/commit/f88c8c48c98fa44e2058bc66f6e63c4ca5153d7e)) * 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)) ([#33630](https://github.com/frappe/erpnext/issues/33630)) ([28f2d35](https://github.com/frappe/erpnext/commit/28f2d357abb6f59762ff2d721944dc897709e21f)) * patch item_reposting_for_incorrect_sl_and_gl ([1928195](https://github.com/frappe/erpnext/commit/192819516783c7575b30c8757a71b9765e6045f9)) * Rate from LDC in TDS reports (backport [#33699](https://github.com/frappe/erpnext/issues/33699)) ([#33700](https://github.com/frappe/erpnext/issues/33700)) ([9fa4c1a](https://github.com/frappe/erpnext/commit/9fa4c1a3bd4c6a4122f85d57c7beafa926ce44d1)) * Return against internal purchase invoice (backport [#33635](https://github.com/frappe/erpnext/issues/33635)) ([#33658](https://github.com/frappe/erpnext/issues/33658)) ([35fbd67](https://github.com/frappe/erpnext/commit/35fbd67a938b7ffb5bbb7a5a39b4c9a9558acd33)) * Sales ORder Connections on Material Request ([8a04031](https://github.com/frappe/erpnext/commit/8a0403119f246de5b79eb860ee429ec21d65cc1c)) * Updating SO throws ordered_qty not allowed to change after submission ([f915c18](https://github.com/frappe/erpnext/commit/f915c181376f1c3e90ffd3ccde1ddf8a3c60ec66)) * zero rm-cost in SCR ([2dfbc6e](https://github.com/frappe/erpnext/commit/2dfbc6e4ebb39efe8c497205c3351cf0f602622d)) ### Features * Date filters on bank reconciliation tool ([#33271](https://github.com/frappe/erpnext/issues/33271)) ([91b08f1](https://github.com/frappe/erpnext/commit/91b08f179a4e29f3b9400190faa50154cc587709)) * provision to select date type based on filter ([4d65d6f](https://github.com/frappe/erpnext/commit/4d65d6f9bd73008db5682027e1ceb44e44d64e82)) ### Performance Improvements * improve reconciliation speed on JE's with 1000's of rows ([8a498ed](https://github.com/frappe/erpnext/commit/8a498ed029b9ffc2adc8eadade070f154ff5d768)) ### Reverts * Reverting changes done on 33495 ([#33662](https://github.com/frappe/erpnext/issues/33662)) ([23b9f66](https://github.com/frappe/erpnext/commit/23b9f661b641f9727cfdbad24f58110d839da5a9)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e3f67077086d..b1d128aac00c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.12.1" +__version__ = "14.13.0" def get_default_company(user=None): From 9fe9d7de6bcf9565ea2bc6645685c80d0e233b94 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 25 Jan 2023 04:00:05 +0000 Subject: [PATCH 21/54] chore(release): Bumped to Version 14.14.0 # [14.14.0](https://github.com/frappe/erpnext/compare/v14.13.0...v14.14.0) (2023-01-25) ### Bug Fixes * Better budget exceeding validation messages ([#33713](https://github.com/frappe/erpnext/issues/33713)) ([4be8375](https://github.com/frappe/erpnext/commit/4be8375e36d7a1ccd1337d3be84c30afe346a127)) * bom.json updated ([9469488](https://github.com/frappe/erpnext/commit/9469488254262f41aac96125eefc486e8fb8896d)) * calculate correct amount for qty == 0 ([#33739](https://github.com/frappe/erpnext/issues/33739)) ([1c1c903](https://github.com/frappe/erpnext/commit/1c1c903fee23e13da8a8415957e9106bfff0745a)) * don't add template item in sales/purchase transaction ([8c12f7f](https://github.com/frappe/erpnext/commit/8c12f7f2f25d74ea323cd191f1c3c08c11002ed6)) * **ecommerce:** breadcrumb: fallback to `/all-products` ([#33718](https://github.com/frappe/erpnext/issues/33718)) ([1a33324](https://github.com/frappe/erpnext/commit/1a33324b4a0af094653ff47f90637c652c5c2266)) * fb issue in asset chart, asset split and reverse_depreciation_entry_made_after_disposal ([dffdc67](https://github.com/frappe/erpnext/commit/dffdc67455ac07ebb4f66bc85c3274acb6290d0b)) * hide with_operation on selection on fg_based and vice versa ([8ee6db3](https://github.com/frappe/erpnext/commit/8ee6db3b7bb4b61bbfaf385657a553e5163f23e7)) * incorrect `rate` and `amount` in MR Item ([#33547](https://github.com/frappe/erpnext/issues/33547)) ([ff731ea](https://github.com/frappe/erpnext/commit/ff731ea70a0d8232f0d3f64f4349b36f0f85b780)) * incorrect actual qty for the packed item ([bcd1fca](https://github.com/frappe/erpnext/commit/bcd1fca37bcae71c517c7fcc53314655df42f535)) * incorrect row order and accumulated_depreciation when schedule with multiple FBs is scrapped ([96f9b34](https://github.com/frappe/erpnext/commit/96f9b34b19f324b8d083666e9b6e4973953d7932)) * linting ([dedc9ec](https://github.com/frappe/erpnext/commit/dedc9ecc7ca806fae8c2d87cb442ac2e2d027a0a)) * local variable 'stock_rbnb' referenced before assignment ([21cf929](https://github.com/frappe/erpnext/commit/21cf929c7ac4a41cd0527ceb622d74e0940828d1)) * minor change in bom.js added ([19aa237](https://github.com/frappe/erpnext/commit/19aa23765a84b9441e5925274fbaa679a84f82e6)) * minor changes added ([7fd8cef](https://github.com/frappe/erpnext/commit/7fd8cef4d3667ca24c61375d7f26996784d22565)) * **minor:** Label updates in Statement of Accounts ([#33639](https://github.com/frappe/erpnext/issues/33639)) ([8af9a2f](https://github.com/frappe/erpnext/commit/8af9a2fad1a1f5153929c03f78bf908c76802cb6)) * missing constant definition ([219aa81](https://github.com/frappe/erpnext/commit/219aa81eb61851ba9f62745b8e4168ab1efb1e94)) * not able to change default BOM in the Subcontracting Order ([ed1aed2](https://github.com/frappe/erpnext/commit/ed1aed22c079a2a0c38b35a9fa67aa2da0338b35)) * Patch to update reference_due_date in Journal Entry ([#33616](https://github.com/frappe/erpnext/issues/33616)) ([0740120](https://github.com/frappe/erpnext/commit/0740120914866f317daa15959283a184606ac487)) * **pricing rule:** free item duplication ([#33746](https://github.com/frappe/erpnext/issues/33746)) ([5a49884](https://github.com/frappe/erpnext/commit/5a4988463674528ae0e8c7ea4fa9656645a44d09)) * rewrite logic for duplicate check in Item Attribute ([6544cb8](https://github.com/frappe/erpnext/commit/6544cb882285d01e7bfa4e60e04901fb870eddb0)) * Short closed order, receipt, and delivery note status on cancellation ([#33743](https://github.com/frappe/erpnext/issues/33743)) ([89f1eef](https://github.com/frappe/erpnext/commit/89f1eefe2ba02f9d30a2f1675f4984091aa01d03)) * TDS deduction in payment entry ([#33747](https://github.com/frappe/erpnext/issues/33747)) ([f9a43e5](https://github.com/frappe/erpnext/commit/f9a43e5470f153bdf94fa827b688462e8f1cc3f6)) * test case added for FG_BASED OPERTING COST ([30af8c3](https://github.com/frappe/erpnext/commit/30af8c3acb7017c96a297e50099652b6eda18a1b)) * the frappe throw message is corrected in the group task validation ([cf43930](https://github.com/frappe/erpnext/commit/cf439301f6ce1df5ac962b388bac0210079fac7e)) * use hash based naming for tax withheld vouchers child table (backport [#33643](https://github.com/frappe/erpnext/issues/33643)) ([#33748](https://github.com/frappe/erpnext/issues/33748)) ([cf6d454](https://github.com/frappe/erpnext/commit/cf6d4546063f66a36dede61941800c18e19fbed2)) * web supplier quotation ([ceef2d6](https://github.com/frappe/erpnext/commit/ceef2d6553046afb110e24e3939626226ca8369d)) ### Features * Add operating cost based on bom quanity without creating job card ([0035ee2](https://github.com/frappe/erpnext/commit/0035ee2a74a8286330bb859b1ddb7dbb946fa5c9)) * get items from Transit Stock Entry ([31fd6f3](https://github.com/frappe/erpnext/commit/31fd6f300f48f8463a244213b3cb34c269698a18)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b1d128aac00c..0cb2b7ef2a01 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.13.0" +__version__ = "14.14.0" def get_default_company(user=None): From c8226ff64240bbed254aa37d77a885247d31a527 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 31 Jan 2023 06:20:11 +0000 Subject: [PATCH 22/54] chore(release): Bumped to Version 14.15.0 # [14.15.0](https://github.com/frappe/erpnext/compare/v14.14.0...v14.15.0) (2023-01-31) ### Bug Fixes * Amount validation in Payment Request against Purchase Order ([#33855](https://github.com/frappe/erpnext/issues/33855)) ([5605f1e](https://github.com/frappe/erpnext/commit/5605f1e3efe931a5240e8878a911a48bf62713c6)) * Currency symbol for tax withholding net total field ([#33850](https://github.com/frappe/erpnext/issues/33850)) ([f54e862](https://github.com/frappe/erpnext/commit/f54e8625f6d10bb8f2547314d1a4b16411c02047)) * disfuctional cost center filter on Journal Entries ([#33815](https://github.com/frappe/erpnext/issues/33815)) ([58c3e16](https://github.com/frappe/erpnext/commit/58c3e16fec68da1e784800daee76e0f13edd4ed7)) * disposal_was_made_on_original_schedule_date ([4586806](https://github.com/frappe/erpnext/commit/4586806ed12fc3817563c3b2cf660b611dd50815)) * double salutation on quotation print ([#33834](https://github.com/frappe/erpnext/issues/33834)) ([0fcf364](https://github.com/frappe/erpnext/commit/0fcf364aaada1d08bef06369ccbdcabfb5ad2935)) * Fetch commission rate from sales partner ([#33851](https://github.com/frappe/erpnext/issues/33851)) ([868c8d6](https://github.com/frappe/erpnext/commit/868c8d65aec951248944698463af62bbd53a38eb)) * **gp:** fetch buying amount from dn related to so ([f5bde9c](https://github.com/frappe/erpnext/commit/f5bde9cf6d5ec61b3e8c011c9dfe289dba54fb9a)) * GST Category validation broken for pos unregistered customer who dont have address. ([#33800](https://github.com/frappe/erpnext/issues/33800)) ([f124dd3](https://github.com/frappe/erpnext/commit/f124dd31120b52a4de15e8ba612ba82e5ed521d0)) * Ignore linked JE on JE cancellation ([#33852](https://github.com/frappe/erpnext/issues/33852)) ([a0e1ee0](https://github.com/frappe/erpnext/commit/a0e1ee0450478782a2ca320fbc3080703c954b2a)) * item rate not fetching ([b98d351](https://github.com/frappe/erpnext/commit/b98d3514ab1bef6a6a26f2b5c0a3c9dde0f0b6fc)) * Lead to customer creation ([#33859](https://github.com/frappe/erpnext/issues/33859)) ([44692e9](https://github.com/frappe/erpnext/commit/44692e9b575e0af44d6d928a227e595953f71216)) * manual depr entry not updating asset value [v14] ([#33788](https://github.com/frappe/erpnext/issues/33788)) ([f487eae](https://github.com/frappe/erpnext/commit/f487eae28e0ec5fe77cfaff8b1edd16a98992e7f)) * **patch:** validation error on cost center allocation migration ([#33835](https://github.com/frappe/erpnext/issues/33835)) ([5d4967c](https://github.com/frappe/erpnext/commit/5d4967ceeefe9504f421d2230a08bb23f57eca5b)) * use correct filter name in `item_query` (backport [#33814](https://github.com/frappe/erpnext/issues/33814)) ([#33816](https://github.com/frappe/erpnext/issues/33816)) ([f7eabca](https://github.com/frappe/erpnext/commit/f7eabcafde23e02b1db7209549462abbbf4aafde)) ### Features * **gp:** test for inv and dn related via so ([7a793ea](https://github.com/frappe/erpnext/commit/7a793ea58888801e1852756f102455255958be5b)) ### Performance Improvements * show update items dialog ([ac2ebfb](https://github.com/frappe/erpnext/commit/ac2ebfbf5942dc127aec8ed05bba864007a89258)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0cb2b7ef2a01..f59e1633b71f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.14.0" +__version__ = "14.15.0" def get_default_company(user=None): From 4c238f1871cb5ae5db0669f6fb7daa2e8a684db9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 1 Feb 2023 15:38:12 +0530 Subject: [PATCH 23/54] fix: incorrect actual qty in Bin (cherry picked from commit f8c852c54ccf7a33d26e15378b76557ceffd77e5) (cherry picked from commit 01ff6a1f1995e619de90ba861c7b6dd0bf79af94) --- 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 9f409d4b96a0..72654e6f8168 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -159,13 +159,18 @@ 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) .limit(1) .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 5d75bfd05a3f..d8b12ed5b92a 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1179,7 +1179,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 171df324074f22b76c1db242580aa6a7a3257580 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 1 Feb 2023 17:46:44 +0000 Subject: [PATCH 24/54] chore(release): Bumped to Version 14.15.1 ## [14.15.1](https://github.com/frappe/erpnext/compare/v14.15.0...v14.15.1) (2023-02-01) ### Bug Fixes * incorrect actual qty in Bin ([4c238f1](https://github.com/frappe/erpnext/commit/4c238f1871cb5ae5db0669f6fb7daa2e8a684db9)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f59e1633b71f..2fca8ec32c73 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.15.0" +__version__ = "14.15.1" def get_default_company(user=None): From 26c9782960f342f4619022cc2cd73cf8a6b72519 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 14 Feb 2023 10:38:44 +0000 Subject: [PATCH 25/54] chore(release): Bumped to Version 14.16.0 # [14.16.0](https://github.com/frappe/erpnext/compare/v14.15.1...v14.16.0) (2023-02-14) ### Bug Fixes * `amount` in `Material Request` ([f1dd923](https://github.com/frappe/erpnext/commit/f1dd923a501989d0718a7ddc84b1f10397e77711)) * `get_picked_items_details` ([7afbd92](https://github.com/frappe/erpnext/commit/7afbd9201d328fa13170453bd120c6c04a7cec04)) * `pymysql.err.ProgrammingError` ([aa3dd33](https://github.com/frappe/erpnext/commit/aa3dd33f5626b8cbae54ba4f9ca97e1acefc9779)) * Add missing 1 required positional argument: 'bill_date' ([ced9274](https://github.com/frappe/erpnext/commit/ced9274d1b394a3be8c83f47db1b5d2bace36ad9)) * add payment hook to point of sale JS ([#33988](https://github.com/frappe/erpnext/issues/33988)) ([49fd712](https://github.com/frappe/erpnext/commit/49fd712966e270f094ff00089b46f194b061e4d6)) * allow PI cancel if linked asset is cancelled ([c98b2b5](https://github.com/frappe/erpnext/commit/c98b2b59181546f246912fcd8c649ae773b68fab)) * Amount for debit and credit notes with 0 qty line items ([#33902](https://github.com/frappe/erpnext/issues/33902)) ([87a8c17](https://github.com/frappe/erpnext/commit/87a8c17314c0268109d1a60d8a90c470e2548c3f)) * Amount validation in Payment Request against Purchase Order ([#34042](https://github.com/frappe/erpnext/issues/34042)) ([c7c6123](https://github.com/frappe/erpnext/commit/c7c61239a3b95439f8de2342f9e080fed4fec592)) * BOM import failed as importer use same label field for Raw MaterialsItem table and Scrap Item table ([47d17f4](https://github.com/frappe/erpnext/commit/47d17f413639ef3601cc6c562cbbe3c2634ad837)) * Concurrency issues in Sales and Purchase returns ([#34019](https://github.com/frappe/erpnext/issues/34019)) ([087333a](https://github.com/frappe/erpnext/commit/087333abcbe0bca99d49bd2fe88766a8e751e7be)) * consider `stock_qty` if `picked_qty` is zero ([df72e4a](https://github.com/frappe/erpnext/commit/df72e4a2217337d3815808a7bf7fba75eab643ea)) * consider existing pick list ([466a791](https://github.com/frappe/erpnext/commit/466a791f68643cae1d2f1323039e786aace9be77)) * currency formatting in item-wise sales history ([#33903](https://github.com/frappe/erpnext/issues/33903)) ([8e2d7bb](https://github.com/frappe/erpnext/commit/8e2d7bb44a383ed4c7770a2e5c8de2930c0ffc10)) * default due_date was wrong calculated on template "_Test Payment Term Template 1" (last day of next month) ([c8c9c50](https://github.com/frappe/erpnext/commit/c8c9c509932c6ccdc8350d3b77d94244fa3a515b)) * **ecommerce:** throw invalid doctype error in shop by category ([#33901](https://github.com/frappe/erpnext/issues/33901)) ([1d0e71b](https://github.com/frappe/erpnext/commit/1d0e71bfe585c41bfcdc2ed29ade0887b466e10b)) * failed test, convert date time to string ([7228a49](https://github.com/frappe/erpnext/commit/7228a492ef2390b2acb08426b363633690098ecf)) * german chart of accounts "SKR03" ([#33909](https://github.com/frappe/erpnext/issues/33909)) ([02c4c55](https://github.com/frappe/erpnext/commit/02c4c55adc87aa7423fcf2fff05568eccf135cd2)) * Ignore mandatory fields while creating tax templates for new companies ([#34005](https://github.com/frappe/erpnext/issues/34005)) ([b0ed3c8](https://github.com/frappe/erpnext/commit/b0ed3c8aedc3ecf43d957b9a894491f180b77144)) * Ignore Payment Ledger Entry on dunning cancel (backport [#34025](https://github.com/frappe/erpnext/issues/34025)) ([#34028](https://github.com/frappe/erpnext/issues/34028)) ([699e93e](https://github.com/frappe/erpnext/commit/699e93e17f61b9530c0670980de52b0f54473aec)) * incorrect actual qty in Bin ([01ff6a1](https://github.com/frappe/erpnext/commit/01ff6a1f1995e619de90ba861c7b6dd0bf79af94)) * IntegrityError while cancelling journals against cr note ([c71d035](https://github.com/frappe/erpnext/commit/c71d03555f41fe01c6eed9c3cf0184b3e9d58dd5)) * list view for Terms and Conditions ([#33925](https://github.com/frappe/erpnext/issues/33925)) ([bb8e232](https://github.com/frappe/erpnext/commit/bb8e232aea98db7158c64e8f5a0ef9bdc57293a9)) * negative stock error ([e0cd6c2](https://github.com/frappe/erpnext/commit/e0cd6c20a3a317f567778e22c8f8b0df19c1891c)) * set per_billed based on hours when amounts are zero ([#33984](https://github.com/frappe/erpnext/issues/33984)) ([5270fbe](https://github.com/frappe/erpnext/commit/5270fbe01a3e83a432610db94f4bc49359eb8821)) * should never get cutomer price on purchase document ([#34002](https://github.com/frappe/erpnext/issues/34002)) ([6fe7600](https://github.com/frappe/erpnext/commit/6fe7600844dc6ac9f4e83eaa9e8d414350591d02)), closes [#33998](https://github.com/frappe/erpnext/issues/33998) * stock entry from item dashboard (stock levels) ([04a474d](https://github.com/frappe/erpnext/commit/04a474d0a12fa51211b5878a19ada7f8e7848475)) * **test:** `test_pick_list_for_items_with_multiple_UOM()` ([7124c0c](https://github.com/frappe/erpnext/commit/7124c0ca30f26c715766b6d1363a4f041e082fac)) * unwanted difference amount calculation on cr note and invoice with same currency ([#34020](https://github.com/frappe/erpnext/issues/34020)) ([cbafc51](https://github.com/frappe/erpnext/commit/cbafc51e753b03ae9a56f08046e1c598f41877ab)) ### Features * Add filters in Loan Interest Report ([#33907](https://github.com/frappe/erpnext/issues/33907)) ([52bfb66](https://github.com/frappe/erpnext/commit/52bfb667294b39bcd621b536459f0d2b0e43a824)) * add incoterm named place to RFQ ([68a1615](https://github.com/frappe/erpnext/commit/68a1615eae49a0d237bb358d650b3c8e80806da2)) * mandatory and mandatory depends on in inventory dimension ([3aca84c](https://github.com/frappe/erpnext/commit/3aca84c43f4e843802f94ee1e44e57090385c50b)) * Setting to allow Sales Order creation against expired quotation ([#33952](https://github.com/frappe/erpnext/issues/33952)) ([4d0e27e](https://github.com/frappe/erpnext/commit/4d0e27ed2b219e15ad8738d41fea57c4e1a6430c)) ### Performance Improvements * reduce memory usage by paging through records ([3ce8dc7](https://github.com/frappe/erpnext/commit/3ce8dc70cbdfab46789480b1e70f818bc6a328a3)) * reduce memory usage while migrating remarks ([c191a3f](https://github.com/frappe/erpnext/commit/c191a3f7c6ec48269f9b0b6c774297ced9db129a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2fca8ec32c73..ed5c841843dd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.15.1" +__version__ = "14.16.0" def get_default_company(user=None): From 4a209fcb7cb7b2c4cee1680643df155491ed294d Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:22:54 +0530 Subject: [PATCH 26/54] fix: opening_accumulated_depreciation and precision in charts (cherry picked from commit 47cc8ab6c6e6ad3cf13ba750e9410696be827c23) --- 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 276cc925208d..bffe19954579 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -210,52 +210,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 cd9f3b420d1f..d3eb958fa6d7 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -693,14 +693,16 @@ def get_status(self): 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 5bfd4840d73d..0ab3e16e424e 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 be2ddd153641709f5d4d3ce43550323df5c7cb5e Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Mon, 13 Feb 2023 17:28:46 +0530 Subject: [PATCH 27/54] chore: break look if je processed (cherry picked from commit a220dc0c9c694a9213189b96be8eb9431457280c) --- 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 b9507b73e292..e52fd6225fbe 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -336,6 +336,8 @@ def unlink_asset_reference(self): finance_books.db_update() asset.set_status() + + break else: depr_value = d.debit or d.credit From 7692db27bdce071b18c49a3551591eab1f9bd783 Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 14 Feb 2023 17:54:51 +0530 Subject: [PATCH 28/54] fix: asset_depreciation_and_balances report doesn't reflect manual depr entries (cherry picked from commit 1535c3d856a50c31ed1e2adaf022ca1a221aab9b) --- .../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 ad9b1ba58eb3..9cea37c4f802 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 e82c101b13f40301d4cc99e5d1874f73528f9977 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 15 Feb 2023 10:33:52 +0000 Subject: [PATCH 29/54] chore(release): Bumped to Version 14.16.1 ## [14.16.1](https://github.com/frappe/erpnext/compare/v14.16.0...v14.16.1) (2023-02-15) ### Bug Fixes * asset_depreciation_and_balances report doesn't reflect manual depr entries ([7692db2](https://github.com/frappe/erpnext/commit/7692db27bdce071b18c49a3551591eab1f9bd783)) * opening_accumulated_depreciation and precision in charts ([4a209fc](https://github.com/frappe/erpnext/commit/4a209fcb7cb7b2c4cee1680643df155491ed294d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ed5c841843dd..a62534530f4c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.16.0" +__version__ = "14.16.1" def get_default_company(user=None): From 0d986a2ac46317a4a26afba2caf68db29a1ae1e9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 21 Feb 2023 17:17:32 +0000 Subject: [PATCH 30/54] chore(release): Bumped to Version 14.17.0 # [14.17.0](https://github.com/frappe/erpnext/compare/v14.16.1...v14.17.0) (2023-02-21) ### Bug Fixes * asset repair status after deletion and asset status after manual depr entry ([03f07a2](https://github.com/frappe/erpnext/commit/03f07a20e7ae042f840351728f7625525a8b3ef2)) * asset_depreciation_and_balances report doesn't reflect manual depr entries ([1535c3d](https://github.com/frappe/erpnext/commit/1535c3d856a50c31ed1e2adaf022ca1a221aab9b)) * change parameter name for letter head ([4f37ba9](https://github.com/frappe/erpnext/commit/4f37ba9cfedf52973b0f91e7fc27ced7d556e9dc)) * check for duplicate in pos closing and pos merge log entry ([05d6490](https://github.com/frappe/erpnext/commit/05d649087b1aef54c401ab6c18ada95671bed249)) * consider rounded total amount while making payment request ([#34110](https://github.com/frappe/erpnext/issues/34110)) ([7879564](https://github.com/frappe/erpnext/commit/78795643cc13fe544cab03eb3989bd7ee8f31f07)) * create `Delivery Trip` from `Delivery Note` list ([ba5ea88](https://github.com/frappe/erpnext/commit/ba5ea886cf59bf0c490b9401813649cc1107ca93)) * differency entry journal debit/credit missing ([#34104](https://github.com/frappe/erpnext/issues/34104)) ([7556739](https://github.com/frappe/erpnext/commit/75567391a72f91251b721bf1bec2e97833fbd20f)) * Filters in item-wise sales history report ([#34145](https://github.com/frappe/erpnext/issues/34145)) ([44c837f](https://github.com/frappe/erpnext/commit/44c837f86292ce935196d757d088dd15d3f54c4a)) * fiscal year error for existing assets in fixed asset register ([7074c2b](https://github.com/frappe/erpnext/commit/7074c2b161a28de654ea9dee7bbd1bd4bc13b6db)) * forced delete linked desktop_icons (backport [#34107](https://github.com/frappe/erpnext/issues/34107)) ([#34130](https://github.com/frappe/erpnext/issues/34130)) ([53ab4d9](https://github.com/frappe/erpnext/commit/53ab4d92e80a6b8b319887a6555e18da984a5a1e)) * ignore repost payment ledger on basic documents cancellation ([#34054](https://github.com/frappe/erpnext/issues/34054)) ([9890cce](https://github.com/frappe/erpnext/commit/9890cce6808820a3d149bc3ac93543091fecbdb3)) * incorrect consumed qty in subcontracting receipt ([d5f6a5d](https://github.com/frappe/erpnext/commit/d5f6a5d193d538550101f59b6b035de8024e8da7)) * inventory dimension filter not overriding with existing filter for stock ledger report ([6959283](https://github.com/frappe/erpnext/commit/695928389384074764cc902d3fd2ed393210a2d6)) * linters issue ([f65e471](https://github.com/frappe/erpnext/commit/f65e471a75566efbc0ea46bef94b19644ecd767c)) * opening_accumulated_depreciation and precision in charts ([47cc8ab](https://github.com/frappe/erpnext/commit/47cc8ab6c6e6ad3cf13ba750e9410696be827c23)) * purchase invoice performance issue ([8d98599](https://github.com/frappe/erpnext/commit/8d98599a6c634b8ac418bc65196135ab5699829f)) * rename duplicate field name with same type into a DocType to avoid import Error ([#34053](https://github.com/frappe/erpnext/issues/34053)) ([d783168](https://github.com/frappe/erpnext/commit/d7831685afcbb4a4fe15cee7abba113f43a996d1)) * show Purchase Order Portal `Pay` button based on configuration ([84da0c6](https://github.com/frappe/erpnext/commit/84da0c6f1eb8c66d8362ad07079f083aebaf49a9)) * update `reserved_qty` when `Sales Order` marked as `Hold` ([15898cc](https://github.com/frappe/erpnext/commit/15898cc2ec5289ad3852cfe61ec64ff572c262d1)) * Use normal rounding for Tax Withholding Category ([#34114](https://github.com/frappe/erpnext/issues/34114)) ([65aec3e](https://github.com/frappe/erpnext/commit/65aec3e4ff711ebc411fc30bc7db6288150975aa)) * **ux:** `ReferenceError: me is not defined` Delivery Note ([7bd04c2](https://github.com/frappe/erpnext/commit/7bd04c27c8c4b4bf8df61f4782c1c429efc4a4b4)) ### Features * allow to make in transit transfer entry from material request ([a7b682e](https://github.com/frappe/erpnext/commit/a7b682e26b2e1c810891effc3e15606573fb1168)) * Editable Sales Invoice ([#32625](https://github.com/frappe/erpnext/issues/32625)) ([00eb632](https://github.com/frappe/erpnext/commit/00eb6329a7727425d504cc36494f4affa89eb44b)) * provision to convert transaction based reposting to item warehouse based reposting ([72c0b22](https://github.com/frappe/erpnext/commit/72c0b2208fdea3e4660858575c9af17b52338547)) * translate fixtures during runtime, not installation ([#33996](https://github.com/frappe/erpnext/issues/33996)) ([d117de7](https://github.com/frappe/erpnext/commit/d117de7813ea8e773f3b8d5262415ac18fda870f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a62534530f4c..1bc5fa3bf186 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.16.1" +__version__ = "14.17.0" def get_default_company(user=None): From 3c2e21e2aee9bf3ca6bdd167aaa864a13107cff0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 24 Feb 2023 15:28:14 +0530 Subject: [PATCH 31/54] Revert "fix: Concurrency issues in Sales and Purchase returns" (#34202) Revert "fix: Concurrency issues in Sales and Purchase returns (#34019)" This reverts commit a67284e96dabe71b76a373b5f1f3142dccf3952b. (cherry picked from commit e26c6dc76b16707c31e9a0277c2710a4c5cb43f0) (cherry picked from commit 9341d3e60ef528430c1914ddd50c4576d316224a) --- erpnext/controllers/sales_and_purchase_return.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 935796c7a712..8bd09982bf42 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -252,7 +252,6 @@ def get_already_returned_items(doc): child.parent = par.name and par.docstatus = 1 and par.is_return = 1 and par.return_against = %s group by item_code - for update """.format( column, doc.doctype, doc.doctype ), From cb266cd1c65e1b24b52c05952bb18a332de15e36 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 24 Feb 2023 10:07:56 +0000 Subject: [PATCH 32/54] chore(release): Bumped to Version 14.17.1 ## [14.17.1](https://github.com/frappe/erpnext/compare/v14.17.0...v14.17.1) (2023-02-24) ### Reverts * Revert "fix: Concurrency issues in Sales and Purchase returns" (#34202) ([3c2e21e](https://github.com/frappe/erpnext/commit/3c2e21e2aee9bf3ca6bdd167aaa864a13107cff0)), closes [#34202](https://github.com/frappe/erpnext/issues/34202) [#34019](https://github.com/frappe/erpnext/issues/34019) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1bc5fa3bf186..ca10f275302e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.17.0" +__version__ = "14.17.1" def get_default_company(user=None): From cc4448b5d5b7c8fc26a31f3288fee333bc474ac3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 28 Feb 2023 13:29:26 +0000 Subject: [PATCH 33/54] chore(release): Bumped to Version 14.17.2 ## [14.17.2](https://github.com/frappe/erpnext/compare/v14.17.1...v14.17.2) (2023-02-28) ### Bug Fixes * conversion factor not set ([089c7d0](https://github.com/frappe/erpnext/commit/089c7d0a376fa54f7eae825ab4bac565253c952a)) * currency in coa import ([#34174](https://github.com/frappe/erpnext/issues/34174)) ([4d92d46](https://github.com/frappe/erpnext/commit/4d92d469e4c9ff2cfa7f5825dc93a9c53f3e232a)) * default date in Subcontracting reports ([5fce8e2](https://github.com/frappe/erpnext/commit/5fce8e2700a05f77d8f91f6d7278ffa1c7e394ad)) * german translations ([#31732](https://github.com/frappe/erpnext/issues/31732)) ([88a781f](https://github.com/frappe/erpnext/commit/88a781fa431d242e008f1bb7cc2ff072c0e495bc)) * incorrect acc depr amount if multiple FBs with straight line or manual method ([dda6bae](https://github.com/frappe/erpnext/commit/dda6baea3ed18546ce8493ad8ccc519f280d5b29)) * incorrect color in the BOM Stock Report ([001ed9e](https://github.com/frappe/erpnext/commit/001ed9e9ffc89e9f0b94a3d33c79241502f7ed8b)) * manual depr schedule ([971c072](https://github.com/frappe/erpnext/commit/971c0720e52b5120ab3497f3fc5bd5964b0deff7)) * multiple pos conversion issue resolved ([db964e8](https://github.com/frappe/erpnext/commit/db964e82567ead44147711ba9673474a39e700d3)) * not able to repost gl entries ([ae0318e](https://github.com/frappe/erpnext/commit/ae0318ef74f67b515593297744394cfae2d389eb)) * permission error while calling get_work_order_items ([3ea90ee](https://github.com/frappe/erpnext/commit/3ea90ee5cb6dfe83c84804e1d0a062745ce9e667)) * pos return throwing amount greater than grand total ([9cd7b27](https://github.com/frappe/erpnext/commit/9cd7b27ce0ade5bf4ea64c55c0f1f0036b0b7975)) * Remove missing DocField in fetch_from ([dc6ae46](https://github.com/frappe/erpnext/commit/dc6ae46d591d31dda9c44ffadb56650c0cbf01e1)) * set `from_warehouse` and `to_warehouse` while mapping SE ([80e23d0](https://github.com/frappe/erpnext/commit/80e23d035e388d9790ace84a740df948296cf7e1)) * **test:** use standalone method to fetch work orders from SO ([1719884](https://github.com/frappe/erpnext/commit/17198844c0b36aaff667d5cb676e5e95395d3847)) * ui freeze on item selection in sales invoice ([1750ed4](https://github.com/frappe/erpnext/commit/1750ed4fb6648abc8fc0aa1695793ee948fc9303)) * user shouldn't able to make item price for item template ([fb8e45d](https://github.com/frappe/erpnext/commit/fb8e45d3d9520a101ebd7b8f4bd6920edea2b968)) * zero division error while making LCV ([1859be6](https://github.com/frappe/erpnext/commit/1859be6fef648a03cfb10955d7a4b1361505007a)) ### Performance Improvements * fetch SLE's on demand and memoize ([db1f17e](https://github.com/frappe/erpnext/commit/db1f17e5bce13093a97b5cca6da395f0432301e1)) ### Reverts * Revert "fix: Concurrency issues in Sales and Purchase returns" (#34202) ([9341d3e](https://github.com/frappe/erpnext/commit/9341d3e60ef528430c1914ddd50c4576d316224a)), closes [#34202](https://github.com/frappe/erpnext/issues/34202) [#34019](https://github.com/frappe/erpnext/issues/34019) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ca10f275302e..3621f47795ea 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.17.1" +__version__ = "14.17.2" def get_default_company(user=None): From 6ba97504ed11834603fd788974c0659458bb82da Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 1 Mar 2023 11:06:46 +0530 Subject: [PATCH 34/54] fix: consumed qty validation for subcontracting receipt (cherry picked from commit b38fe240900da4e3ac64c7bb03ef3aecbcd09f0d) (cherry picked from commit 7eccf431fd4caa755a9224a4ea635017022d14fe) --- .../subcontracting_receipt.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index f4fd4de169dc..95dbc83bf808 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -191,14 +191,17 @@ def validate_rejected_warehouse(self): def validate_available_qty_for_consumption(self): for item in self.get("supplied_items"): + precision = item.precision("consumed_qty") if ( - item.available_qty_for_consumption and item.available_qty_for_consumption < item.consumed_qty + item.available_qty_for_consumption + and flt(item.available_qty_for_consumption, precision) - flt(item.consumed_qty, precision) < 0 ): - frappe.throw( - _( - "Row {0}: Consumed Qty must be less than or equal to Available Qty For Consumption in Consumed Items Table." - ).format(item.idx) - ) + msg = f"""Row {item.idx}: Consumed Qty {flt(item.consumed_qty, precision)} + must be less than or equal to Available Qty For Consumption + {flt(item.available_qty_for_consumption, precision)} + in Consumed Items Table.""" + + frappe.throw(_(msg)) def validate_items_qty(self): for item in self.items: From 60a1e10b11206bfd4162e11476c9a03b8a1eae49 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 1 Mar 2023 10:22:45 +0000 Subject: [PATCH 35/54] chore(release): Bumped to Version 14.17.3 ## [14.17.3](https://github.com/frappe/erpnext/compare/v14.17.2...v14.17.3) (2023-03-01) ### Bug Fixes * consumed qty validation for subcontracting receipt ([6ba9750](https://github.com/frappe/erpnext/commit/6ba97504ed11834603fd788974c0659458bb82da)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3621f47795ea..c7da6c148137 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.17.2" +__version__ = "14.17.3" def get_default_company(user=None): From 9930adcd28bfc90d90ecb56449676966e1721840 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 2 Mar 2023 11:15:46 +0530 Subject: [PATCH 36/54] fix: `rejected_serial_no` not getting copied from PR to PR(Return) (cherry picked from commit a9f0a11ce62d8337fcbc1ebdda3be1b818a5f8a2) (cherry picked from commit 3db82587eb9d3e55a672bdbd2118a37ee1e72f33) --- erpnext/controllers/sales_and_purchase_return.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 8bd09982bf42..c5ef28a2eeda 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -400,6 +400,16 @@ def update_item(source_doc, target_doc, source_parent): if serial_nos: target_doc.serial_no = "\n".join(serial_nos) + if source_doc.get("rejected_serial_no"): + returned_serial_nos = get_returned_serial_nos( + source_doc, source_parent, serial_no_field="rejected_serial_no" + ) + rejected_serial_nos = list( + set(get_serial_nos(source_doc.rejected_serial_no)) - set(returned_serial_nos) + ) + if rejected_serial_nos: + target_doc.rejected_serial_no = "\n".join(rejected_serial_nos) + if doctype in ["Purchase Receipt", "Subcontracting Receipt"]: returned_qty_map = get_returned_qty_map_for_row( source_parent.name, source_parent.supplier, source_doc.name, doctype @@ -610,7 +620,7 @@ def get_filters( return filters -def get_returned_serial_nos(child_doc, parent_doc): +def get_returned_serial_nos(child_doc, parent_doc, serial_no_field="serial_no"): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos return_ref_field = frappe.scrub(child_doc.doctype) @@ -619,7 +629,7 @@ def get_returned_serial_nos(child_doc, parent_doc): serial_nos = [] - fields = ["`{0}`.`serial_no`".format("tab" + child_doc.doctype)] + fields = [f"`{'tab' + child_doc.doctype}`.`{serial_no_field}`"] filters = [ [parent_doc.doctype, "return_against", "=", parent_doc.name], @@ -629,6 +639,6 @@ def get_returned_serial_nos(child_doc, parent_doc): ] for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters): - serial_nos.extend(get_serial_nos(row.serial_no)) + serial_nos.extend(get_serial_nos(row.get(serial_no_field))) return serial_nos From 7629caa647f5d6a33ffafbb8126af95f2720df75 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 2 Mar 2023 11:27:46 +0530 Subject: [PATCH 37/54] fix: `Serial No is mandatory` even if the `qty` is `0` (cherry picked from commit cb0b6de4b97009d0f7d1053f4e065416971a4832) (cherry picked from commit aa6b891ef0285102797f924d75e2332f60008dd8) --- erpnext/controllers/sales_and_purchase_return.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index c5ef28a2eeda..341d51b8a5fb 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -131,7 +131,7 @@ def validate_returned_items(doc): ) elif ref.serial_no: - if not d.serial_no: + if d.qty and not d.serial_no: frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx)) else: serial_nos = get_serial_nos(d.serial_no) From a59c580480a1e41a155bf0e301447ae8d7a4fbd7 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 2 Mar 2023 08:08:44 +0000 Subject: [PATCH 38/54] chore(release): Bumped to Version 14.17.4 ## [14.17.4](https://github.com/frappe/erpnext/compare/v14.17.3...v14.17.4) (2023-03-02) ### Bug Fixes * `rejected_serial_no` not getting copied from PR to PR(Return) ([9930adc](https://github.com/frappe/erpnext/commit/9930adcd28bfc90d90ecb56449676966e1721840)) * `Serial No is mandatory` even if the `qty` is `0` ([7629caa](https://github.com/frappe/erpnext/commit/7629caa647f5d6a33ffafbb8126af95f2720df75)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c7da6c148137..ff1c3e67a2c5 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.17.3" +__version__ = "14.17.4" def get_default_company(user=None): From 0696128acc8db5039e77b4b89565b82f29624a75 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 7 Mar 2023 14:26:16 +0000 Subject: [PATCH 39/54] chore(release): Bumped to Version 14.18.0 # [14.18.0](https://github.com/frappe/erpnext/compare/v14.17.4...v14.18.0) (2023-03-07) ### Bug Fixes * `Inventory Dimension` for `Stock Reconciliation` ([b08cdc0](https://github.com/frappe/erpnext/commit/b08cdc00f2022c2622939e6af9db8db51c31912e)) * `rejected_serial_no` not getting copied from PR to PR(Return) ([3db8258](https://github.com/frappe/erpnext/commit/3db82587eb9d3e55a672bdbd2118a37ee1e72f33)) * `Serial No is mandatory` even if the `qty` is `0` ([aa6b891](https://github.com/frappe/erpnext/commit/aa6b891ef0285102797f924d75e2332f60008dd8)) * BOM Update log not completed ([235ecca](https://github.com/frappe/erpnext/commit/235ecca9fa96ce4ce6ff75e05012d9b1adca2148)) * consumed qty validation for subcontracting receipt ([7eccf43](https://github.com/frappe/erpnext/commit/7eccf431fd4caa755a9224a4ea635017022d14fe)) * Default sales team not getting set ([#34284](https://github.com/frappe/erpnext/issues/34284)) ([64c758d](https://github.com/frappe/erpnext/commit/64c758d0c0d2dcc168f5eabb5e3d90f19a0e4c47)) * Do not calculate commission post submit ([#34267](https://github.com/frappe/erpnext/issues/34267)) ([480797e](https://github.com/frappe/erpnext/commit/480797e85699ffe76bac841b30b6b7418451fad7)) * labels name ([5e9f1df](https://github.com/frappe/erpnext/commit/5e9f1dfbb3f136aa70f6bb476d90aae77790d80d)) * **minor:** Dirty the form after clicking on Get advances button in Invoices ([#34323](https://github.com/frappe/erpnext/issues/34323)) ([0e9f9c3](https://github.com/frappe/erpnext/commit/0e9f9c31a0deb7ce02e2eb582ce4c72aaf7964ff)) * Payment Request against sales order with disabled rounded total ([#34281](https://github.com/frappe/erpnext/issues/34281)) ([ca59c69](https://github.com/frappe/erpnext/commit/ca59c699cdfe1eddce7263e1ed586021a2b0590e)) * Performance improvement when adding a new item ([#34195](https://github.com/frappe/erpnext/issues/34195)) ([71a281f](https://github.com/frappe/erpnext/commit/71a281fb11c99e2ab37af6346ee038924de071b4)) * Resolve conflicts ([f6469d8](https://github.com/frappe/erpnext/commit/f6469d83981386a7bb57b41178055eae5cda30bf)) * Stock Reconciliation `actual_qty` ([d97c1bf](https://github.com/frappe/erpnext/commit/d97c1bf0f4dd74bfa64819b901f0ce6f7a798cb9)) * update inventory dimensions before returning sle ([ab73742](https://github.com/frappe/erpnext/commit/ab737424c26fb751693e8003e6d9eade104f08cd)) * Wrap unexpectedly long text in remark ([b13bf1e](https://github.com/frappe/erpnext/commit/b13bf1ebc517ccd9c5b6b079208ca3883e7418fd)) ### Features * adjust purchase receipt valuation rate as per purchase invoice rate ([db033c6](https://github.com/frappe/erpnext/commit/db033c686229c12671062404efb409c17100635f)) ### Reverts * Revert "refactor: use renamed timezone utils (#34301)" ([a2e001a](https://github.com/frappe/erpnext/commit/a2e001a2da81ed074dff7439b74a07a57a0388bc)), closes [#34301](https://github.com/frappe/erpnext/issues/34301) [#34301](https://github.com/frappe/erpnext/issues/34301) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ff1c3e67a2c5..46f9074de771 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.17.4" +__version__ = "14.18.0" def get_default_company(user=None): From 59a415eaa920ea3f518632d3983033b02a10fb8f Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sun, 5 Mar 2023 21:57:02 +0530 Subject: [PATCH 40/54] perf: Stock Entry (Material Transfer) (cherry picked from commit de18f98c5c3d0ee5cc9d3df5b389917670514e64) (cherry picked from commit 1b514632d2de619108c3b1345ad7d7513a95c0c6) --- .../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 3133628cbf21..e82f37977cd6 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -561,7 +561,34 @@ def get_current_operation_data(self): ) 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." @@ -578,29 +605,23 @@ def _validate_over_transfer(row, transferred_qty): 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 7a5f7d4920a940aaa36b881ba1d7d9d9e2c45aa0 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 7 Mar 2023 01:11:23 +0530 Subject: [PATCH 41/54] perf: `update_completed_qty()` in `material_request.py` (cherry picked from commit 8ad9e99cea5e3f235d0b3ead812d2a3a11d40081) (cherry picked from commit b37712c038c65e200c5910634b49341199ab2900) --- .../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 bcf6dd79d8de..3548148145e9 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -10,6 +10,7 @@ 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 erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items @@ -183,6 +184,34 @@ def on_cancel(self): 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 @@ -190,18 +219,13 @@ def update_completed_qty(self, mr_items=None, update_modified=True): 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)) @@ -220,14 +244,7 @@ def update_completed_qty(self, mr_items=None, update_modified=True): ) 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 cfe28663bcaad841588340a1f12d8300851dc6d2 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 7 Mar 2023 17:23:00 +0000 Subject: [PATCH 42/54] chore(release): Bumped to Version 14.18.1 ## [14.18.1](https://github.com/frappe/erpnext/compare/v14.18.0...v14.18.1) (2023-03-07) ### Performance Improvements * `update_completed_qty()` in `material_request.py` ([7a5f7d4](https://github.com/frappe/erpnext/commit/7a5f7d4920a940aaa36b881ba1d7d9d9e2c45aa0)) * Stock Entry (Material Transfer) ([59a415e](https://github.com/frappe/erpnext/commit/59a415eaa920ea3f518632d3983033b02a10fb8f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 46f9074de771..9bf1c0aad223 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.18.0" +__version__ = "14.18.1" def get_default_company(user=None): From f71d85d7c3dcf298b42c224e98386cfc624b3a80 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:39:56 +0530 Subject: [PATCH 43/54] 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 ed338b13952ea65e727b8741d8ad2844646b6ecd) --- 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 1a81158b366d..590e00fb11e5 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -87,9 +87,6 @@ def set_missing_lead_customer_details(self, for_validate=False): ) 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 fcbcbc0aa7fffbfe732818768ecd5ed32fd4fda8 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 9 Mar 2023 10:26:37 +0000 Subject: [PATCH 44/54] chore(release): Bumped to Version 14.18.2 ## [14.18.2](https://github.com/frappe/erpnext/compare/v14.18.1...v14.18.2) (2023-03-09) ### Reverts * Revert "fix: Default sales team not getting set" (#34376) ([f71d85d](https://github.com/frappe/erpnext/commit/f71d85d7c3dcf298b42c224e98386cfc624b3a80)), 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 9bf1c0aad223..6976133ba421 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.18.1" +__version__ = "14.18.2" def get_default_company(user=None): From 3e61e763173d14a79912f367bc607a1b6ac1cbfa Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 14 Mar 2023 17:37:03 +0000 Subject: [PATCH 45/54] chore(release): Bumped to Version 14.18.3 ## [14.18.3](https://github.com/frappe/erpnext/compare/v14.18.2...v14.18.3) (2023-03-14) ### Bug Fixes * `BOM Stock Report` ([1c00077](https://github.com/frappe/erpnext/commit/1c0007768b661c6ce031a57f0c937e7a973133df)) * `required_qty` get reset to `1` for Alternative Item in WO ([51bcdb3](https://github.com/frappe/erpnext/commit/51bcdb32f2ab7db1b8a6e5bd0f4b7e59c6d4e641)) * Don't use get_list & get_all interchangeably ([27c524e](https://github.com/frappe/erpnext/commit/27c524e337eecf29dc10cd25a0a8f49edfba52fe)) * Error in consolidated financial statement ([#34330](https://github.com/frappe/erpnext/issues/34330)) ([73866f4](https://github.com/frappe/erpnext/commit/73866f4da7487d7f8fdb815d69fbe6d2cb63764e)) * exchange rate revaluation errors ([#33947](https://github.com/frappe/erpnext/issues/33947)) ([1a629b6](https://github.com/frappe/erpnext/commit/1a629b641845cac7b4b3db32a571d4dd26fbae13)) * filters not getting applied on `Web Form` ([6ef7ddf](https://github.com/frappe/erpnext/commit/6ef7ddfbce271e35123b32bbbe5f605c9b516d1a)) * Linked invoice cancellation issue via timesheet ([#34337](https://github.com/frappe/erpnext/issues/34337)) ([da8cc2b](https://github.com/frappe/erpnext/commit/da8cc2bba9af2e0499975f8649232a7006f5bce0)) * operation time for multi-level BOM in WO ([76e04c8](https://github.com/frappe/erpnext/commit/76e04c8625b92aafdf0467aaa55227789ded214f)) * Set contact filter link in Opportunity ([#34325](https://github.com/frappe/erpnext/issues/34325)) ([c64836d](https://github.com/frappe/erpnext/commit/c64836d3d63b433f6a1df280670fa7b22a8c157f)) * set tax category from address before executing `get_regional_address_details` ([#34372](https://github.com/frappe/erpnext/issues/34372)) ([bf0cbe0](https://github.com/frappe/erpnext/commit/bf0cbe09b9a28f11b4c7d5894b86b7c3f4ef4c99)) * **test:** flaky test case in Payment terms report ([69a5411](https://github.com/frappe/erpnext/commit/69a5411f0ebcc4bbcfb36b3470753e4c556fea1d)) * Total row in trail balance report ([#34395](https://github.com/frappe/erpnext/issues/34395)) ([c353ba7](https://github.com/frappe/erpnext/commit/c353ba741cc04eeb432449731c7ca252929a1fc8)) * Use customer name instead of name(id) in PSOA (backport [#34412](https://github.com/frappe/erpnext/issues/34412)) ([#34425](https://github.com/frappe/erpnext/issues/34425)) ([209adf3](https://github.com/frappe/erpnext/commit/209adf32a5ebf6df70784a1d6567720d6c296343)) ### Performance Improvements * `update_completed_qty()` in `material_request.py` ([b37712c](https://github.com/frappe/erpnext/commit/b37712c038c65e200c5910634b49341199ab2900)) * Stock Entry (Material Transfer) ([1b51463](https://github.com/frappe/erpnext/commit/1b514632d2de619108c3b1345ad7d7513a95c0c6)) ### Reverts * Revert "Update tr.csv (backport #34285)" (#34427) ([b6d059c](https://github.com/frappe/erpnext/commit/b6d059ccb82c7798b6622eb689885dab884cb671)), closes [#34285](https://github.com/frappe/erpnext/issues/34285) [#34427](https://github.com/frappe/erpnext/issues/34427) [#34285](https://github.com/frappe/erpnext/issues/34285) * Revert "fix: Default sales team not getting set" (#34376) ([ed338b1](https://github.com/frappe/erpnext/commit/ed338b13952ea65e727b8741d8ad2844646b6ecd)), 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 6976133ba421..a5f153f52117 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.18.2" +__version__ = "14.18.3" def get_default_company(user=None): From e271935673f1e27d7bd8ab7b7e1731a22b2e0bee Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 21 Mar 2023 12:52:05 +0000 Subject: [PATCH 46/54] chore(release): Bumped to Version 14.19.0 # [14.19.0](https://github.com/frappe/erpnext/compare/v14.18.3...v14.19.0) (2023-03-21) ### Bug Fixes * **client:** Amount calculation for 0 qty debit notes ([#34455](https://github.com/frappe/erpnext/issues/34455)) ([d24f4d2](https://github.com/frappe/erpnext/commit/d24f4d287382939d8a679c1c9444b116a2bf3e18)) * difference amount calculation for company currency accounts ([9ab7bff](https://github.com/frappe/erpnext/commit/9ab7bff0e0ed2ec100307ed7eb29b7c9b0984865)) * don't map item row having `0` qty ([7611a49](https://github.com/frappe/erpnext/commit/7611a49db7a2785bada3f74aeb1bb330cb1e3c67)) * E-commerce issue with Item Variants ([53c3fff](https://github.com/frappe/erpnext/commit/53c3fff2353c8c7363dd55421d7c43350ae3af07)) * german translations ([#34312](https://github.com/frappe/erpnext/issues/34312)) ([dd0c833](https://github.com/frappe/erpnext/commit/dd0c8334cdbe0da2852206c9390004325627c5ab)) * hide `+` button based on `Blanket Order Type` ([daa1bb8](https://github.com/frappe/erpnext/commit/daa1bb86e36f18f8a0d7fd9dcb696bc604ffdbb4)) * incorrect depr schedules after asset repair [v14] ([#34527](https://github.com/frappe/erpnext/issues/34527)) ([560df63](https://github.com/frappe/erpnext/commit/560df6330a1b43648a91e3f1514d8ff5dc382c15)), closes [#30838](https://github.com/frappe/erpnext/issues/30838) * Multiple accounting dimension filtering in AR/AP reports ([#34464](https://github.com/frappe/erpnext/issues/34464)) ([f146479](https://github.com/frappe/erpnext/commit/f146479362d3a2b46d720d635db71c37058e1a41)) * Overallocation of 'qty' from Cr Notes to Parent Invoice ([848e56b](https://github.com/frappe/erpnext/commit/848e56bd4ca07ecbb1ea64dd226680c96a17a29c)) * patch depends on Currency Exchange Settings ([#34494](https://github.com/frappe/erpnext/issues/34494)) ([4acde44](https://github.com/frappe/erpnext/commit/4acde4468fa6574dfeb6ca786fe4b8796a5969eb)) * POS not picking up pos profile company address instead fetch any random company address ([#34521](https://github.com/frappe/erpnext/issues/34521)) ([01f4cc7](https://github.com/frappe/erpnext/commit/01f4cc76fc65ae35ec99e0599818b141a2393f2e)) * Update account number from parent company ([#34474](https://github.com/frappe/erpnext/issues/34474)) ([55d002c](https://github.com/frappe/erpnext/commit/55d002c6364622b40a47d5c8e009d99f097174c7)) * use max function to get default company address (backport [#34116](https://github.com/frappe/erpnext/issues/34116)) ([#34452](https://github.com/frappe/erpnext/issues/34452)) ([ba2fd71](https://github.com/frappe/erpnext/commit/ba2fd71b6522b39d1f17fc00c497d1d59ea00e7e)) ### Features * add field `Over Order Allowance (%)` in `Buying Settings` ([da915f1](https://github.com/frappe/erpnext/commit/da915f15103d969c1c6ae2561ae517c68635cdd8)) * add field `Over Order Allowance (%)` in `Selling Settings` ([46b5ba9](https://github.com/frappe/erpnext/commit/46b5ba9c2aa9093115206cdf79831a86194447de)) * bank reconciliation and plaid changes ([#33986](https://github.com/frappe/erpnext/issues/33986)) ([9b608ea](https://github.com/frappe/erpnext/commit/9b608eaa0fcebcb5a8627ec4c14ccfad1057421e)) * consider `over_order_allowance` while validating order qty ([932639b](https://github.com/frappe/erpnext/commit/932639b4df5747593667849695c01b953d02ef01)) * consider `over_order_allowance` while validating sales order qty ([09b577a](https://github.com/frappe/erpnext/commit/09b577a91fd32fdc3f974e8b7a3a85ad1f37d51d)) * Support for Alternative Items in Quotation ([#33874](https://github.com/frappe/erpnext/issues/33874)) ([9f7da21](https://github.com/frappe/erpnext/commit/9f7da21c9333e61bad2eb561ed3de96c8656ab7e)) ### Performance Improvements * index against_sales_invoice field on DN items (backport [#34509](https://github.com/frappe/erpnext/issues/34509)) ([#34510](https://github.com/frappe/erpnext/issues/34510)) ([baa789b](https://github.com/frappe/erpnext/commit/baa789be347d14420b66498cd978c5a8380d6d2a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a5f153f52117..63dd69aeca66 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.18.3" +__version__ = "14.19.0" def get_default_company(user=None): From a60c8f0e18c27439d710d61050b2232d21e5d3da Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 28 Mar 2023 18:23:52 +0000 Subject: [PATCH 47/54] chore(release): Bumped to Version 14.20.0 # [14.20.0](https://github.com/frappe/erpnext/compare/v14.19.0...v14.20.0) (2023-03-28) ### Bug Fixes * default pos conversion factor set to 1 ([#34437](https://github.com/frappe/erpnext/issues/34437)) ([18d813a](https://github.com/frappe/erpnext/commit/18d813a6561ddc63f6b880d89d0a3b6b2969e786)) * don't get zero value entries for exchange rate calculation ([#34475](https://github.com/frappe/erpnext/issues/34475)) ([ff24b3e](https://github.com/frappe/erpnext/commit/ff24b3e40c419b91306246dfedfcccd060b6cd37)) * incorrect `Opening Value` in `Stock Balance` report ([76b782a](https://github.com/frappe/erpnext/commit/76b782a03fe28078b992f3ae6f5f25148a8ef4a8)) * Note username overlapping with note content(CRM) ([096e5ef](https://github.com/frappe/erpnext/commit/096e5ef19766e032428afd2566817c90e694a16b)) * Party Name in SOA print when viewed from Customer/Supplier master ([#34597](https://github.com/frappe/erpnext/issues/34597)) ([835edbe](https://github.com/frappe/erpnext/commit/835edbe80e76ca03dfddeeb6d656ad4b5eeee1d6)) * Percentage billing in Sales Order ([#34606](https://github.com/frappe/erpnext/issues/34606)) ([477cb12](https://github.com/frappe/erpnext/commit/477cb12240034da391d6fffea13a589fcec83e76)) * recalculate WDV rate after asset repair [v14] ([#34571](https://github.com/frappe/erpnext/issues/34571)) ([d2ca6f8](https://github.com/frappe/erpnext/commit/d2ca6f8d1f36443635d50c27b3134daca494e33e)) * remove unused translation ([#34519](https://github.com/frappe/erpnext/issues/34519)) ([881e92e](https://github.com/frappe/erpnext/commit/881e92e7b363e6790d3260da11822ccdb5949d2f)) * removing redundant validation ([fd6db41](https://github.com/frappe/erpnext/commit/fd6db41b6e4887fcf898e3615d0f0c75c3c45111)) * Sales person variance report without item group ([#34552](https://github.com/frappe/erpnext/issues/34552)) ([90ddc4a](https://github.com/frappe/erpnext/commit/90ddc4a1e2ff277ef676650c899bc2c172bdbcf5)) * Tax Category not able to set hence it calculating zero tax for item whoes tax template set ([#34525](https://github.com/frappe/erpnext/issues/34525)) ([a8567b0](https://github.com/frappe/erpnext/commit/a8567b09e69e41866cb2c3bdcded9563d06dccf2)) * Time button not working in the job card ([8fed33b](https://github.com/frappe/erpnext/commit/8fed33b03bc1d609526e0506eb17612f21b466b0)) * translations and UX in alternative item mapping ([#34433](https://github.com/frappe/erpnext/issues/34433)) ([702d07e](https://github.com/frappe/erpnext/commit/702d07ea7db1a032ee0f617f642f68e6d63a0f77)) * unset address and contact on trash (backport [#34495](https://github.com/frappe/erpnext/issues/34495)) ([#34560](https://github.com/frappe/erpnext/issues/34560)) ([db01bf5](https://github.com/frappe/erpnext/commit/db01bf5dec74a190452b1c5a95fa59eba4db5c82)) * zero rm-cost for batch rm item in SCR (backport [#34616](https://github.com/frappe/erpnext/issues/34616)) ([#34623](https://github.com/frappe/erpnext/issues/34623)) ([cff35d7](https://github.com/frappe/erpnext/commit/cff35d7286d0831698d9b8c23dcd18343a02f22a)) ### Features * deprecate get_customer_list ([#34563](https://github.com/frappe/erpnext/issues/34563)) ([67576ad](https://github.com/frappe/erpnext/commit/67576ad5bdc78aa8ef79b6bf915df2ba87b9e21f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 63dd69aeca66..8a50a5d369da 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.19.0" +__version__ = "14.20.0" def get_default_company(user=None): From da354362bec16d9de42f48b18a0a4b1857070c85 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 3 Apr 2023 14:47:58 +0530 Subject: [PATCH 48/54] fix: bom update log not working for large batch size (cherry picked from commit d56070301cedc3ffbafa8e7c556e775253fddd77) (cherry picked from commit 551190af305f1dcd6862bbaf298729c79e2a4183) --- .../manufacturing/doctype/bom_update_log/bom_update_log.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 51f7b24e745f..7477f9528ecb 100644 --- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py +++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py @@ -164,7 +164,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 @@ -212,7 +212,7 @@ def resume_bom_cost_update_jobs(): ["name", "boms_updated", "status"], ) incomplete_level = any(row.get("status") == "Pending" for row in bom_batches) - if not bom_batches or not incomplete_level: + if not bom_batches or incomplete_level: continue # Prep parent BOMs & updated processed BOMs for next level @@ -252,9 +252,6 @@ def get_processed_current_boms( current_boms = [] for row in bom_batches: - if not row.boms_updated: - continue - boms_updated = json.loads(row.boms_updated) current_boms.extend(boms_updated) boms_updated_dict = {bom: True for bom in boms_updated} From dfadfdc32c5687ee62bbc44e9a6931e14c40a115 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 3 Apr 2023 17:50:43 +0000 Subject: [PATCH 49/54] chore(release): Bumped to Version 14.20.1 ## [14.20.1](https://github.com/frappe/erpnext/compare/v14.20.0...v14.20.1) (2023-04-03) ### Bug Fixes * bom update log not working for large batch size ([da35436](https://github.com/frappe/erpnext/commit/da354362bec16d9de42f48b18a0a4b1857070c85)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8a50a5d369da..643815e38fce 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.20.0" +__version__ = "14.20.1" def get_default_company(user=None): From 33ee958cfb3881cd3c560e8a4c5404e11478d90b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 5 Apr 2023 17:41:20 +0530 Subject: [PATCH 50/54] chore: release v14 (#34733) --- .../accounts_settings/accounts_settings.json | 10 +- .../doctype/bank_clearance/bank_clearance.py | 16 +- .../chart_of_accounts_importer.py | 6 +- .../doctype/journal_entry/journal_entry.py | 3 +- .../doctype/payment_entry/payment_entry.js | 2 - .../doctype/payment_entry/payment_entry.py | 240 +++++- .../payment_entry/test_payment_entry.py | 222 ++++- .../payment_entry_deduction.json | 29 +- .../payment_reconciliation.js | 28 + .../payment_reconciliation.py | 9 + .../payment_request/payment_request.py | 14 +- .../doctype/pos_invoice/pos_invoice.py | 2 +- .../purchase_invoice/purchase_invoice.js | 6 +- .../purchase_invoice/purchase_invoice.json | 12 +- .../purchase_invoice/purchase_invoice.py | 27 +- .../doctype/sales_invoice/sales_invoice.js | 9 +- .../doctype/sales_invoice/sales_invoice.json | 10 +- .../doctype/sales_invoice/sales_invoice.py | 2 +- .../asset_depreciation_ledger.py | 1 + .../bank_clearance_summary.py | 64 +- 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 | 806 ++++-------------- .../asset_value_adjustment.js | 2 +- .../doctype/purchase_order/purchase_order.js | 6 +- .../request_for_quotation.py | 5 +- erpnext/controllers/accounts_controller.py | 43 +- .../crm/report/lead_details/lead_details.py | 2 +- .../lost_opportunity/lost_opportunity.py | 2 +- .../doctype/website_item/test_website_item.py | 8 +- .../doctype/plaid_settings/plaid_settings.py | 2 +- erpnext/hooks.py | 4 + erpnext/manufacturing/doctype/bom/bom.py | 12 +- .../production_plan/production_plan.json | 3 +- .../production_plan_item_reference.json | 5 +- erpnext/public/js/controllers/accounts.js | 8 + .../public/js/controllers/taxes_and_totals.js | 2 +- erpnext/public/js/controllers/transaction.js | 52 +- erpnext/public/js/website_utils.js | 15 - .../setup/doctype/item_group/item_group.py | 9 +- erpnext/stock/doctype/batch/batch.py | 8 +- .../stock_reconciliation.py | 12 +- erpnext/stock/get_item_details.py | 23 +- .../stock/report/stock_ledger/stock_ledger.py | 3 + erpnext/templates/utils.py | 9 +- erpnext/utilities/transaction_base.py | 56 +- erpnext/www/shop-by-category/index.py | 12 +- 51 files changed, 1186 insertions(+), 859 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 1e2e2acd79af..1c0d64f065b8 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -31,6 +31,7 @@ "determine_address_tax_category_from", "column_break_19", "add_taxes_from_item_tax_template", + "book_tax_discount_loss", "print_settings", "show_inclusive_tax_in_print", "column_break_12", @@ -347,6 +348,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", @@ -354,7 +362,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-11-27 21:49:52.538655", + "modified": "2023-03-28 09:50:20.375233", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 80878ac50682..081718726bdf 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -81,7 +81,7 @@ def get_payment_entries(self): loan_disbursement = frappe.qb.DocType("Loan Disbursement") - loan_disbursements = ( + query = ( frappe.qb.from_(loan_disbursement) .select( ConstantColumn("Loan Disbursement").as_("payment_document"), @@ -90,17 +90,22 @@ def get_payment_entries(self): ConstantColumn(0).as_("debit"), loan_disbursement.reference_number.as_("cheque_number"), loan_disbursement.reference_date.as_("cheque_date"), + loan_disbursement.clearance_date.as_("clearance_date"), loan_disbursement.disbursement_date.as_("posting_date"), loan_disbursement.applicant.as_("against_account"), ) .where(loan_disbursement.docstatus == 1) .where(loan_disbursement.disbursement_date >= self.from_date) .where(loan_disbursement.disbursement_date <= self.to_date) - .where(loan_disbursement.clearance_date.isnull()) .where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account])) .orderby(loan_disbursement.disbursement_date) .orderby(loan_disbursement.name, order=frappe.qb.desc) - ).run(as_dict=1) + ) + + if not self.include_reconciled_entries: + query = query.where(loan_disbursement.clearance_date.isnull()) + + loan_disbursements = query.run(as_dict=1) loan_repayment = frappe.qb.DocType("Loan Repayment") @@ -113,16 +118,19 @@ def get_payment_entries(self): ConstantColumn(0).as_("credit"), loan_repayment.reference_number.as_("cheque_number"), loan_repayment.reference_date.as_("cheque_date"), + loan_repayment.clearance_date.as_("clearance_date"), loan_repayment.applicant.as_("against_account"), loan_repayment.posting_date, ) .where(loan_repayment.docstatus == 1) - .where(loan_repayment.clearance_date.isnull()) .where(loan_repayment.posting_date >= self.from_date) .where(loan_repayment.posting_date <= self.to_date) .where(loan_repayment.payment_account.isin([self.bank_account, self.account])) ) + if not self.include_reconciled_entries: + query = query.where(loan_repayment.clearance_date.isnull()) + if frappe.db.has_column("Loan Repayment", "repay_from_salary"): query = query.where((loan_repayment.repay_from_salary == 0)) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index c676c97616c0..e78f5f46dbc1 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -325,14 +325,14 @@ def get_template(template_type): if template_type == "Blank Template": for root_type in get_root_types(): - writer.writerow(["", "", "", 1, "", root_type]) + writer.writerow(["", "", "", "", 1, "", root_type]) for account in get_mandatory_group_accounts(): - writer.writerow(["", "", "", 1, account, "Asset"]) + writer.writerow(["", "", "", "", 1, account, "Asset"]) for account_type in get_mandatory_account_types(): writer.writerow( - ["", "", "", 0, account_type.get("account_type"), account_type.get("root_type")] + ["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")] ) else: writer = get_sample_template(writer) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 56fe333e047e..608267154b60 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -51,7 +51,7 @@ def validate(self): self.validate_multi_currency() self.set_amounts_in_company_currency() self.validate_debit_credit_amount() - + self.set_total_debit_credit() # Do not validate while importing via data import if not frappe.flags.in_import: self.validate_total_debit_and_credit() @@ -659,7 +659,6 @@ def validate_debit_credit_amount(self): frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx)) def validate_total_debit_and_credit(self): - self.set_total_debit_credit() if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency): if self.difference: frappe.throw( diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 91374ae217b2..5a56a6b00466 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -245,8 +245,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 a585924d20fd..58ed7d1822c0 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -416,7 +416,7 @@ def update_payment_schedule(self, cancel=0): 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 @@ -424,20 +424,37 @@ def update_payment_schedule(self, cancel=0): 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(invoice_payment_amount_map.items(), 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")) @@ -472,6 +489,33 @@ def update_payment_schedule(self, cancel=0): (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" @@ -1642,7 +1686,14 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre @frappe.whitelist() def get_payment_entry( - dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None + dt, + dn, + party_amount=None, + bank_account=None, + bank_amount=None, + party_type=None, + payment_type=None, + reference_date=None, ): reference_doc = None doc = frappe.get_doc(dt, dn) @@ -1669,8 +1720,9 @@ def get_payment_entry( 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") @@ -1678,6 +1730,7 @@ def get_payment_entry( 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)) @@ -1718,7 +1771,7 @@ def get_payment_entry( ): 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: @@ -1769,16 +1822,17 @@ def get_payment_entry( if party_account and bank: 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 @@ -1889,20 +1943,28 @@ 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 + is_multi_currency = party_account_currency != doc.company_currency if doc.doctype in eligible_for_payments and has_payment_schedule: 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 @@ -1911,23 +1973,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 123b5dfd512b..67049c47ad05 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -5,7 +5,7 @@ import frappe from frappe import qb -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import flt, nowdate from erpnext.accounts.doctype.payment_entry.payment_entry import ( @@ -256,10 +256,25 @@ def test_payment_entry_against_payment_terms_with_discount(self): }, ) 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() @@ -269,6 +284,190 @@ def test_payment_entry_against_payment_terms_with_discount(self): 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", @@ -839,24 +1038,27 @@ def create_payment_terms_template(): ).insert() -def create_payment_terms_template_with_discount(): - - create_payment_term("30 Credit Days with 10% 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" - 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 61a1462dd7a4..1c31829f0ea6 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/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index d986f320669c..caffac5354ff 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -272,4 +272,32 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo } }; +frappe.ui.form.on('Payment Reconciliation Allocation', { + allocated_amount: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + // filter invoice + let invoice = frm.doc.invoices.filter((x) => (x.invoice_number == row.invoice_number)); + // filter payment + let payment = frm.doc.payments.filter((x) => (x.reference_name == row.reference_name)); + + frm.call({ + doc: frm.doc, + method: 'calculate_difference_on_allocation_change', + args: { + payment_entry: payment, + invoice: invoice, + allocated_amount: row.allocated_amount + }, + callback: (r) => { + if (r.message) { + row.difference_amount = r.message; + frm.refresh(); + } + } + }); + } +}); + + + extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm})); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index c9e3998ac8a0..d8082d058f3f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -233,6 +233,15 @@ def get_difference_amount(self, payment_entry, invoice, allocated_amount): return difference_amount + @frappe.whitelist() + def calculate_difference_on_allocation_change(self, payment_entry, invoice, allocated_amount): + invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry) + invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number")) + new_difference_amount = self.get_difference_amount( + payment_entry[0], invoice[0], allocated_amount + ) + return new_difference_amount + @frappe.whitelist() def allocate_entries(self, args): self.validate_entries() diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index d9b07435fdb3..aa8743e1caa4 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -497,10 +497,16 @@ def get_amount(ref_doc, payment_account=None): if dt in ["Sales Order", "Purchase Order"]: grand_total = flt(ref_doc.rounded_total) or flt(ref_doc.grand_total) elif dt in ["Sales Invoice", "Purchase Invoice"]: - if ref_doc.party_account_currency == ref_doc.currency: - grand_total = flt(ref_doc.outstanding_amount) - else: - grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate + if not ref_doc.is_pos: + if ref_doc.party_account_currency == ref_doc.currency: + grand_total = flt(ref_doc.outstanding_amount) + else: + grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate + elif dt == "Sales Invoice": + for pay in ref_doc.payments: + if pay.type == "Phone" and pay.account == payment_account: + grand_total = pay.amount + break elif dt == "POS Invoice": for pay in ref_doc.payments: if pay.type == "Phone" and pay.account == payment_account: diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 0af4c0ea4804..27e6f0e598f5 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -674,7 +674,7 @@ def get_bin_qty(item_code, warehouse): def get_pos_reserved_qty(item_code, warehouse): reserved_qty = frappe.db.sql( - """select sum(p_item.qty) as qty + """select sum(p_item.stock_qty) as qty from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item where p.name = p_item.parent and ifnull(p.consolidated_invoice, '') = '' diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index e2b4a1ad5bef..5c9168bf9c50 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -82,7 +82,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. if(doc.docstatus == 1 && doc.outstanding_amount != 0 && !(doc.is_return && doc.return_against) && !doc.on_hold) { - this.frm.add_custom_button(__('Payment'), this.make_payment_entry, __('Create')); + 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/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 629a0ffb5822..1c8c4b3193b2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -118,6 +118,7 @@ "paid_amount", "advances_section", "allocate_advances_automatically", + "only_include_allocated_payments", "get_advances", "advances", "advance_tax", @@ -1550,17 +1551,24 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" + }, + { + "default": "0", + "depends_on": "allocate_advances_automatically", + "description": "Advance payments allocated against orders will only be fetched", + "fieldname": "only_include_allocated_payments", + "fieldtype": "Check", + "label": "Only Include Allocated Payments" } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-01-28 19:18:56.586321", + "modified": "2023-04-03 22:57:14.074982", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", - "name_case": "Title Case", "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index ae707ab14355..0ded4a883bb9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -117,7 +117,7 @@ def validate(self): self.validate_expense_account() self.set_against_expense_account() self.validate_write_off_account() - self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items") + self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount") self.create_remarks() self.set_status() self.validate_purchase_receipt_if_update_stock() @@ -232,7 +232,7 @@ def validate_with_previous_doc(self): ) if ( - cint(frappe.db.get_single_value("Buying Settings", "maintain_same_rate")) + cint(frappe.get_cached_value("Buying Settings", "None", "maintain_same_rate")) and not self.is_return and not self.is_internal_supplier ): @@ -581,6 +581,7 @@ def get_gl_entries(self, warehouse_account=None): self.make_supplier_gl_entry(gl_entries) self.make_item_gl_entries(gl_entries) + self.make_precision_loss_gl_entry(gl_entries) if self.check_asset_cwip_enabled(): self.get_asset_gl_entry(gl_entries) @@ -975,6 +976,28 @@ def make_item_gl_entries(self, gl_entries): item.item_tax_amount, item.precision("item_tax_amount") ) + def make_precision_loss_gl_entry(self, gl_entries): + round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( + self.company, "Purchase Invoice", self.name + ) + + precision_loss = self.get("base_net_total") - flt( + self.get("net_total") * self.conversion_rate, self.precision("net_total") + ) + + if precision_loss: + gl_entries.append( + self.get_gl_dict( + { + "account": round_off_account, + "against": self.supplier, + "credit": precision_loss, + "cost_center": self.cost_center or round_off_cost_center, + "remarks": _("Net total calculation precision loss"), + } + ) + ) + def get_asset_gl_entry(self, gl_entries): arbnb_account = self.get_company_default("asset_received_but_not_billed") eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 47e3f9b93548..56e412b297c9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -93,9 +93,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e 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/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 9a5b42be4bb7..9a0d71a3850b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -120,6 +120,7 @@ "account_for_change_amount", "advances_section", "allocate_advances_automatically", + "only_include_allocated_payments", "get_advances", "advances", "write_off_section", @@ -2126,6 +2127,13 @@ "label": "Repost Required", "no_copy": 1, "read_only": 1 + }, + { + "depends_on": "allocate_advances_automatically", + "description": "Advance payments allocated against orders will only be fetched", + "fieldname": "only_include_allocated_payments", + "fieldtype": "Check", + "label": "Only Include Allocated Payments" } ], "icon": "fa fa-file-text", @@ -2138,7 +2146,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2023-03-13 11:43:15.883055", + "modified": "2023-04-03 22:55:14.206473", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index f5be4c7a3f3e..7af98ddf9340 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -145,7 +145,7 @@ def validate(self): self.set_against_income_account() self.validate_time_sheets_are_submitted() - self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items") + self.validate_multiple_billing("Delivery Note", "dn_detail", "amount") if not self.is_return: self.validate_serial_numbers() else: 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 57d80492ae0e..f21c94b4940c 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/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py index 449ebdcd9240..306af722ba84 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.query_builder.custom import ConstantColumn from frappe.utils import getdate, nowdate @@ -91,4 +92,65 @@ def get_entries(filters): as_list=1, ) - return sorted(journal_entries + payment_entries, key=lambda k: k[2] or getdate(nowdate())) + # Loan Disbursement + loan_disbursement = frappe.qb.DocType("Loan Disbursement") + + query = ( + frappe.qb.from_(loan_disbursement) + .select( + ConstantColumn("Loan Disbursement").as_("payment_document_type"), + loan_disbursement.name.as_("payment_entry"), + loan_disbursement.disbursement_date.as_("posting_date"), + loan_disbursement.reference_number.as_("cheque_no"), + loan_disbursement.clearance_date.as_("clearance_date"), + loan_disbursement.applicant.as_("against"), + -loan_disbursement.disbursed_amount.as_("amount"), + ) + .where(loan_disbursement.docstatus == 1) + .where(loan_disbursement.disbursement_date >= filters["from_date"]) + .where(loan_disbursement.disbursement_date <= filters["to_date"]) + .where(loan_disbursement.disbursement_account == filters["account"]) + .orderby(loan_disbursement.disbursement_date, order=frappe.qb.desc) + .orderby(loan_disbursement.name, order=frappe.qb.desc) + ) + + if filters.get("from_date"): + query = query.where(loan_disbursement.disbursement_date >= filters["from_date"]) + if filters.get("to_date"): + query = query.where(loan_disbursement.disbursement_date <= filters["to_date"]) + + loan_disbursements = query.run(as_list=1) + + # Loan Repayment + loan_repayment = frappe.qb.DocType("Loan Repayment") + + query = ( + frappe.qb.from_(loan_repayment) + .select( + ConstantColumn("Loan Repayment").as_("payment_document_type"), + loan_repayment.name.as_("payment_entry"), + loan_repayment.posting_date.as_("posting_date"), + loan_repayment.reference_number.as_("cheque_no"), + loan_repayment.clearance_date.as_("clearance_date"), + loan_repayment.applicant.as_("against"), + loan_repayment.amount_paid.as_("amount"), + ) + .where(loan_repayment.docstatus == 1) + .where(loan_repayment.posting_date >= filters["from_date"]) + .where(loan_repayment.posting_date <= filters["to_date"]) + .where(loan_repayment.payment_account == filters["account"]) + .orderby(loan_repayment.posting_date, order=frappe.qb.desc) + .orderby(loan_repayment.name, order=frappe.qb.desc) + ) + + if filters.get("from_date"): + query = query.where(loan_repayment.posting_date >= filters["from_date"]) + if filters.get("to_date"): + query = query.where(loan_repayment.posting_date <= filters["to_date"]) + + loan_repayments = query.run(as_list=1) + + return sorted( + journal_entries + payment_entries + loan_disbursements + loan_repayments, + key=lambda k: k[2] or getdate(nowdate()), + ) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 21d846f68062..0923d0093f97 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -469,6 +469,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 d581f52153e7..3e93f0f03e3c 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -81,6 +81,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, @@ -527,7 +530,7 @@ "table_fieldname": "accounts" } ], - "modified": "2023-01-25 17:45:48.649543", + "modified": "2023-03-30 15:07:41.542374", "modified_by": "Administrator", "module": "Assets", "name": "Asset", @@ -571,4 +574,4 @@ "states": [], "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 47b5f75e6686..fe1fd98aa098 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -294,17 +294,42 @@ def _make_depreciation_schedule(self, finance_book, start, date_of_disposal): 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 + + 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: + 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) ) @@ -320,7 +345,10 @@ def _make_depreciation_schedule(self, finance_book, start, date_of_disposal): 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: @@ -335,12 +363,20 @@ def _make_depreciation_schedule(self, finance_book, start, date_of_disposal): 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 @@ -359,7 +395,11 @@ def _make_depreciation_schedule(self, finance_book, start, date_of_disposal): 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( @@ -479,28 +519,37 @@ def get_from_date(self, finance_book): 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): @@ -903,7 +952,12 @@ def get_depreciation_rate(self, args, on_validate=False): 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 ( @@ -920,14 +974,29 @@ def get_depreciation_rate(self, args, on_validate=False): 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 @@ -1184,27 +1253,72 @@ 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 @frappe.whitelist() diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index e72e0afb9ce4..74625890a697 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -218,10 +218,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 71f578c67030..7eaa4bf997a8 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -818,12 +818,12 @@ def test_monthly_depreciation_by_wdv_method(self): ) 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 0028d84508d8..83031415ec3d 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -84,6 +84,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 20963e3fdc7a..b7cb23e66878 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 + }, + { + "fieldname": "maintenance_type", + "fieldtype": "Select", + "label": "Maintenance Type", + "options": "Preventive Maintenance\nCalibration" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "maintenance_status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Maintenance Status", + "options": "Planned\nOverdue\nCancelled", + "reqd": 1 + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "default": "Today", + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "periodicity", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Periodicity", + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nYearly\n2 Yearly\n3 Yearly", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date" + }, + { + "default": "0", + "fieldname": "certificate_required", + "fieldtype": "Check", + "label": "Certificate Required", + "search_index": 1, + "set_only_once": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "fieldname": "assign_to", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Assign To", + "options": "User" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign to Name" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "next_due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Next Due Date" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "last_completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Last Completion Date" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "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 ae0e1bda0204..d07f40cdf422 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 47089f7d8503..c6c9f1f98a39 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -236,7 +236,11 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e this.make_purchase_invoice, __('Create')); if(flt(doc.per_billed) < 100 && doc.status != "Delivered") { - cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create')); + this.frm.add_custom_button( + __('Payment'), + () => this.make_payment_entry(), + __('Create') + ); } if(flt(doc.per_billed) < 100) { diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 7927beb8233b..4590f8c3d937 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -113,7 +113,10 @@ def send_to_supplier(self): def get_link(self): # RFQ link for supplier portal - return get_url("/app/request-for-quotation/" + self.name) + route = frappe.db.get_value( + "Portal Menu Item", {"reference_doctype": "Request for Quotation"}, ["route"] + ) + return get_url("/app/{0}/".format(route) + self.name) def update_supplier_part_no(self, supplier): self.vendor = supplier diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3705fcf4990d..a347323e358e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -515,6 +515,8 @@ def set_missing_item_details(self, for_validate=False): parent_dict.update({"customer": parent_dict.get("party_name")}) self.pricing_rules = [] + basic_item_details_map = {} + for item in self.get("items"): if item.get("item_code"): args = parent_dict.copy() @@ -533,7 +535,17 @@ def set_missing_item_details(self, for_validate=False): if self.get("is_subcontracted"): args["is_subcontracted"] = self.is_subcontracted - ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False) + basic_details = basic_item_details_map.get(item.item_code) + ret, basic_item_details = get_item_details( + args, + self, + for_validate=True, + overwrite_warehouse=False, + return_basic_details=True, + basic_details=basic_details, + ) + + basic_item_details_map.setdefault(item.item_code, basic_item_details) for fieldname, value in ret.items(): if item.meta.get_field(fieldname) and value is not None: @@ -833,7 +845,9 @@ def get_shipping_address(self): def set_advances(self): """Returns list of advances against Account, Party, Reference""" - res = self.get_advance_entries() + res = self.get_advance_entries( + include_unallocated=not cint(self.get("only_include_allocated_payments")) + ) self.set("advances", []) advance_allocated = 0 @@ -1232,7 +1246,7 @@ def make_discount_gl_entries(self, gl_entries): ) ) - def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): + def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on): from erpnext.controllers.status_updater import get_allowance_for item_allowance = {} @@ -1245,17 +1259,20 @@ def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): total_overbilled_amt = 0.0 + reference_names = [d.get(item_ref_dn) for d in self.get("items") if d.get(item_ref_dn)] + reference_details = self.get_billing_reference_details( + reference_names, ref_dt + " Item", based_on + ) + for item in self.get("items"): if not item.get(item_ref_dn): continue - ref_amt = flt( - frappe.db.get_value(ref_dt + " Item", item.get(item_ref_dn), based_on), - self.precision(based_on, item), - ) + ref_amt = flt(reference_details.get(item.get(item_ref_dn)), self.precision(based_on, item)) + if not ref_amt: frappe.msgprint( - _("System will not check overbilling since amount for Item {0} in {1} is zero").format( + _("System will not check over billing since amount for Item {0} in {1} is zero").format( item.item_code, ref_dt ), title=_("Warning"), @@ -1302,6 +1319,16 @@ def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): alert=True, ) + def get_billing_reference_details(self, reference_names, reference_doctype, based_on): + return frappe._dict( + frappe.get_all( + reference_doctype, + filters={"name": ("in", reference_names)}, + fields=["name", based_on], + as_list=1, + ) + ) + def get_billed_amount_for_item(self, item, item_ref_dn, based_on): """ Returns Sum of Amount of diff --git a/erpnext/crm/report/lead_details/lead_details.py b/erpnext/crm/report/lead_details/lead_details.py index 8660c7331038..7b8c43b2d65f 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 254511c92fa2..b37cfa449fee 100644 --- a/erpnext/crm/report/lost_opportunity/lost_opportunity.py +++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.py @@ -82,7 +82,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 bbe04d5514d8..019a5f9ee4f4 100644 --- a/erpnext/e_commerce/doctype/website_item/test_website_item.py +++ b/erpnext/e_commerce/doctype/website_item/test_website_item.py @@ -199,8 +199,14 @@ def test_website_item_breadcrumbs(self): 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/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index f3aa6a379355..e57a30a88e18 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -220,7 +220,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None): if e.code == "ITEM_LOGIN_REQUIRED": msg = _("There was an error syncing transactions.") + " " msg += _("Please refresh or reset the Plaid linking of the Bank {}.").format(bank) + " " - frappe.log_error(msg, title=_("Plaid Link Refresh Required")) + frappe.log_error(message=msg, title=_("Plaid Link Refresh Required")) return transactions diff --git a/erpnext/hooks.py b/erpnext/hooks.py index f1ee370e97ee..c4032596f47a 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -30,6 +30,10 @@ 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/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 619a415c8bc9..a085af859a4b 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -943,7 +943,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 @@ -954,7 +955,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/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 2624daa41e2d..fdaa4a2a1d46 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -344,6 +344,7 @@ { "fieldname": "prod_plan_references", "fieldtype": "Table", + "hidden": 1, "label": "Production Plan Item Reference", "options": "Production Plan Item Reference" }, @@ -397,7 +398,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-11-26 14:51:08.774372", + "modified": "2023-03-31 10:30:48.118932", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", 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 84dee4ad284c..15ef20794cb8 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/accounts.js b/erpnext/public/js/controllers/accounts.js index a07f75d1c5d4..d943126018a6 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -55,6 +55,14 @@ frappe.ui.form.on(cur_frm.doctype, { }, allocate_advances_automatically: function(frm) { + frm.trigger('fetch_advances'); + }, + + only_include_allocated_payments: function(frm) { + frm.trigger('fetch_advances'); + }, + + fetch_advances: function(frm) { if(frm.doc.allocate_advances_automatically) { frappe.call({ doc: frm.doc, diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 8e57ebd36774..8efc47d18e5d 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -135,7 +135,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } 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 f7620db2f1e4..07d1955bfafd 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1897,20 +1897,60 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } 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() } }); } + 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 b5416065d791..2bb5255eebc7 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/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 2fdfcf647d01..2eca5cad8e2e 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -148,12 +148,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 f14288beb20f..4a165212dcec 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -6,7 +6,7 @@ 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 @@ -179,7 +179,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/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 3f6a2c881b82..04d1a3a5e221 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -4,7 +4,7 @@ from typing import Optional import frappe -from frappe import _, msgprint +from frappe import _, bold, msgprint from frappe.utils import cint, cstr, flt import erpnext @@ -89,7 +89,7 @@ def _changed(item): 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") @@ -140,6 +140,14 @@ def _get_msg(row_num, msg): 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"))) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 489ec6ebecce..2df39c818326 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -35,7 +35,14 @@ @frappe.whitelist() -def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=True): +def get_item_details( + args, + doc=None, + for_validate=False, + overwrite_warehouse=True, + return_basic_details=False, + basic_details=None, +): """ args = { "item_code": "", @@ -73,7 +80,13 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru if doc.get("doctype") == "Purchase Invoice": args["bill_date"] = doc.get("bill_date") - out = get_basic_details(args, item, overwrite_warehouse) + if not basic_details: + out = get_basic_details(args, item, overwrite_warehouse) + else: + out = basic_details + + basic_details = out.copy() + get_item_tax_template(args, item, out) out["item_tax_rate"] = get_item_tax_map( args.company, @@ -141,7 +154,11 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru out.amount = flt(args.qty) * flt(out.rate) out = remove_standard_fields(out) - return out + + if return_basic_details: + return out, basic_details + else: + return out def remove_standard_fields(details): diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index da17cdeb5aed..77bc4e004de7 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -34,6 +34,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 = {} inventory_dimension_filters_applied = check_inventory_dimension_filters_applied(filters) diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py index 48b44802a8f8..57750a56f6f9 100644 --- a/erpnext/templates/utils.py +++ b/erpnext/templates/utils.py @@ -6,13 +6,12 @@ @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 - lead = customer = None - - website_send_message(subject, message, sender) + website_send_message(sender, message, subject) + lead = customer = None 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' @@ -58,5 +57,3 @@ def send_message(subject="Website Query", message="", sender="", status="Open"): } ) comm.insert(ignore_permissions=True) - - return "okay" diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 21a0a551b627..7eba35dedd93 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -58,11 +58,11 @@ def validate_with_previous_doc(self, ref): def compare_values(self, ref_doc, fields, doc=None): for reference_doctype, ref_dn_list in ref_doc.items(): + prev_doc_detail_map = self.get_prev_doc_reference_details( + ref_dn_list, reference_doctype, fields + ) for reference_name in ref_dn_list: - prevdoc_values = frappe.db.get_value( - reference_doctype, reference_name, [d[0] for d in fields], as_dict=1 - ) - + prevdoc_values = prev_doc_detail_map.get(reference_name) if not prevdoc_values: frappe.throw(_("Invalid reference {0} {1}").format(reference_doctype, reference_name)) @@ -70,6 +70,19 @@ def compare_values(self, ref_doc, fields, doc=None): if prevdoc_values[field] is not None and field not in self.exclude_fields: self.validate_value(field, condition, prevdoc_values[field], doc) + def get_prev_doc_reference_details(self, reference_names, reference_doctype, fields): + prev_doc_detail_map = {} + details = frappe.get_all( + reference_doctype, + filters={"name": ("in", reference_names)}, + fields=["name"] + [d[0] for d in fields], + ) + + for d in details: + prev_doc_detail_map.setdefault(d.name, d) + + return prev_doc_detail_map + def validate_rate_with_reference_doc(self, ref_details): if self.get("is_internal_supplier"): return @@ -77,23 +90,23 @@ def validate_rate_with_reference_doc(self, ref_details): buying_doctypes = ["Purchase Order", "Purchase Invoice", "Purchase Receipt"] if self.doctype in buying_doctypes: - action = frappe.db.get_single_value("Buying Settings", "maintain_same_rate_action") - settings_doc = "Buying Settings" + action, role_allowed_to_override = frappe.get_cached_value( + "Buying Settings", "None", ["maintain_same_rate_action", "role_to_override_stop_action"] + ) else: - action = frappe.db.get_single_value("Selling Settings", "maintain_same_rate_action") - settings_doc = "Selling Settings" + action, role_allowed_to_override = frappe.get_cached_value( + "Selling Settings", "None", ["maintain_same_rate_action", "role_to_override_stop_action"] + ) for ref_dt, ref_dn_field, ref_link_field in ref_details: + reference_names = [d.get(ref_link_field) for d in self.get("items") if d.get(ref_link_field)] + reference_details = self.get_reference_details(reference_names, ref_dt + " Item") for d in self.get("items"): if d.get(ref_link_field): - ref_rate = frappe.db.get_value(ref_dt + " Item", d.get(ref_link_field), "rate") + ref_rate = reference_details.get(d.get(ref_link_field)) if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= 0.01: if action == "Stop": - role_allowed_to_override = frappe.db.get_single_value( - settings_doc, "role_to_override_stop_action" - ) - if role_allowed_to_override not in frappe.get_roles(): frappe.throw( _("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format( @@ -109,6 +122,16 @@ def validate_rate_with_reference_doc(self, ref_details): indicator="orange", ) + def get_reference_details(self, reference_names, reference_doctype): + return frappe._dict( + frappe.get_all( + reference_doctype, + filters={"name": ("in", reference_names)}, + fields=["name", "rate"], + as_list=1, + ) + ) + def get_link_filters(self, for_doctype): if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype): fieldname = self.prev_link_mapper[for_doctype]["fieldname"] @@ -186,12 +209,15 @@ def validate_uom_is_integer(doc, uom_field, qty_fields, child_dt=None): for f in qty_fields: qty = d.get(f) if qty: - if abs(cint(qty) - flt(qty)) > 0.0000001: + if abs(cint(qty) - flt(qty, d.precision(f))) > 0.0000001: frappe.throw( _( "Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}." ).format( - qty, d.idx, frappe.bold(_("Must be Whole Number")), frappe.bold(d.get(uom_field)) + flt(qty, d.precision(f)), + d.idx, + frappe.bold(_("Must be Whole Number")), + frappe.bold(d.get(uom_field)), ), UOMMustBeIntegerError, ) diff --git a/erpnext/www/shop-by-category/index.py b/erpnext/www/shop-by-category/index.py index 219747c9f8a5..913c1836acdf 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 c7cee866850b8dab3a97dde1225a12cda15cb532 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 4 Apr 2023 23:56:57 +0530 Subject: [PATCH 51/54] fix: incorrect stock balance quantity for batch item (cherry picked from commit ef4bd771968274d73ec5df865159d251c91ebb3e) (cherry picked from commit d817c50581fedd46b5ee93f3cfd55247d1fd9823) --- .../stock_reconciliation.py | 49 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 15 +++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 04d1a3a5e221..482b103d1e4b 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -5,6 +5,7 @@ import frappe from frappe import _, bold, msgprint +from frappe.query_builder.functions import Sum from frappe.utils import cint, cstr, flt import erpnext @@ -569,6 +570,54 @@ def cancel(self): else: self._cancel() + def recalculate_current_qty(self, item_code, batch_no): + for row in self.items: + if not (row.item_code == item_code and row.batch_no == batch_no): + continue + + row.current_qty = get_batch_qty_for_stock_reco(item_code, row.warehouse, batch_no) + + qty, val_rate = get_stock_balance( + item_code, + row.warehouse, + self.posting_date, + self.posting_time, + with_valuation_rate=True, + ) + + row.current_valuation_rate = val_rate + + row.db_set( + { + "current_qty": row.current_qty, + "current_valuation_rate": row.current_valuation_rate, + "current_amount": flt(row.current_qty * row.current_valuation_rate), + } + ) + + +def get_batch_qty_for_stock_reco(item_code, warehouse, batch_no): + ledger = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(ledger) + .select( + Sum(ledger.actual_qty).as_("batch_qty"), + ) + .where( + (ledger.item_code == item_code) + & (ledger.warehouse == warehouse) + & (ledger.docstatus == 1) + & (ledger.is_cancelled == 0) + & (ledger.batch_no == batch_no) + ) + .groupby(ledger.batch_no) + ) + + sle = query.run(as_dict=True) + + return flt(sle[0].batch_qty) if sle else 0 + @frappe.whitelist() def get_items( diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 08fc6fbd42fb..c954befdc297 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1337,6 +1337,9 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): next_stock_reco_detail = get_next_stock_reco(args) if next_stock_reco_detail: detail = next_stock_reco_detail[0] + if detail.batch_no: + regenerate_sle_for_batch_stock_reco(detail) + # add condition to update SLEs before this date & time datetime_limit_condition = get_datetime_limit_condition(detail) @@ -1364,6 +1367,16 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): validate_negative_qty_in_future_sle(args, allow_negative_stock) +def regenerate_sle_for_batch_stock_reco(detail): + doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no) + doc.docstatus = 2 + doc.update_stock_ledger() + + doc.recalculate_current_qty(detail.item_code, detail.batch_no) + doc.docstatus = 1 + doc.update_stock_ledger() + + def get_stock_reco_qty_shift(args): stock_reco_qty_shift = 0 if args.get("is_cancelled"): @@ -1393,7 +1406,7 @@ def get_next_stock_reco(args): return frappe.db.sql( """ select - name, posting_date, posting_time, creation, voucher_no + name, posting_date, posting_time, creation, voucher_no, item_code, batch_no, actual_qty from `tabStock Ledger Entry` where From b6ae9a4a72fcfc83e0ff73102eef4736abd5c789 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 5 Apr 2023 18:57:55 +0000 Subject: [PATCH 52/54] chore(release): Bumped to Version 14.20.2 ## [14.20.2](https://github.com/frappe/erpnext/compare/v14.20.1...v14.20.2) (2023-04-05) ### Bug Fixes * incorrect stock balance quantity for batch item ([c7cee86](https://github.com/frappe/erpnext/commit/c7cee866850b8dab3a97dde1225a12cda15cb532)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 643815e38fce..a782703e7605 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.20.1" +__version__ = "14.20.2" def get_default_company(user=None): From f4473b36a5ab5892117e7e2c69f6ef98a7d6c8b6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 13:22:09 +0530 Subject: [PATCH 53/54] fix: Unable to create payment request against purchase invoice (#34762) fix: Unable to create payment request against purchase invoice (#34762) fix: Unable to create payment request against purchase invoice (#34762) (cherry picked from commit 91a26608ee6a8cb09547e2d5059a36ae4daaa0d9) Co-authored-by: Deepesh Garg (cherry picked from commit a1f7e359142132a3e0bbcd49f37bab84071ef4dc) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../payment_request/payment_request.py | 2 +- .../payment_request/test_payment_request.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index aa8743e1caa4..0955664d98be 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -497,7 +497,7 @@ def get_amount(ref_doc, payment_account=None): if dt in ["Sales Order", "Purchase Order"]: grand_total = flt(ref_doc.rounded_total) or flt(ref_doc.grand_total) elif dt in ["Sales Invoice", "Purchase Invoice"]: - if not ref_doc.is_pos: + if not ref_doc.get("is_pos"): if ref_doc.party_account_currency == ref_doc.currency: grand_total = flt(ref_doc.outstanding_amount) else: diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 4279aa4f85c6..e17a846dd814 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -6,6 +6,7 @@ import frappe from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request +from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.setup.utils import get_exchange_rate @@ -74,6 +75,29 @@ def test_payment_request_linkings(self): self.assertEqual(pr.reference_name, si_usd.name) self.assertEqual(pr.currency, "USD") + def test_payment_entry_against_purchase_invoice(self): + si_usd = make_purchase_invoice( + customer="_Test Supplier USD", + debit_to="_Test Payable USD - _TC", + currency="USD", + conversion_rate=50, + ) + + pr = make_payment_request( + dt="Purchase Invoice", + dn=si_usd.name, + recipient_id="user@example.com", + mute_email=1, + payment_gateway_account="_Test Gateway - USD", + submit_doc=1, + return_doc=1, + ) + + pe = pr.create_payment_entry() + pr.load_from_db() + + self.assertEqual(pr.status, "Paid") + def test_payment_entry(self): frappe.db.set_value( "Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC" From 2a8c9f8e69ebb568dd7430de3324203a128cf3b5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 6 Apr 2023 07:54:09 +0000 Subject: [PATCH 54/54] chore(release): Bumped to Version 14.20.3 ## [14.20.3](https://github.com/frappe/erpnext/compare/v14.20.2...v14.20.3) (2023-04-06) ### Bug Fixes * Unable to create payment request against purchase invoice ([#34762](https://github.com/frappe/erpnext/issues/34762)) ([f4473b3](https://github.com/frappe/erpnext/commit/f4473b36a5ab5892117e7e2c69f6ef98a7d6c8b6)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a782703e7605..456ca52020ba 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -2,7 +2,7 @@ import frappe -__version__ = "14.20.2" +__version__ = "14.20.3" def get_default_company(user=None):