From 12e27e96a8d2e5d08402769399c53ed1ca2bb02f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 11 Jul 2023 14:12:25 +0000 Subject: [PATCH 01/28] chore(release): Bumped to Version 13.52.4 ## [13.52.4](https://github.com/frappe/erpnext/compare/v13.52.3...v13.52.4) (2023-07-11) ### Bug Fixes * also check on_hold ([#35910](https://github.com/frappe/erpnext/issues/35910)) ([5aa02b8](https://github.com/frappe/erpnext/commit/5aa02b8571efdc1710a9b3069a18a80ed17856a2)) * circular dependency during reposting causing timeout error ([ba69be1](https://github.com/frappe/erpnext/commit/ba69be1cedb0915deab150601c6ed748145fc08f)) * **Employee Advance:** check if return amount is set before validating ([#36080](https://github.com/frappe/erpnext/issues/36080)) ([beaf13e](https://github.com/frappe/erpnext/commit/beaf13e00ed638abeae9d4489f91de9d96a9dd8a)) * error messages while evaluating formulas and handle line boundaries ([#35989](https://github.com/frappe/erpnext/issues/35989)) ([4af57a7](https://github.com/frappe/erpnext/commit/4af57a7318fdc7c1150b984d2f75efb3efcc90f3)) * gst_hsn_code is ambiguous on gst reports ([3a00052](https://github.com/frappe/erpnext/commit/3a00052b496f645b9930a8f8c5d4cb43736a2011)) * incorrect status in MR created from PP (backport [#36085](https://github.com/frappe/erpnext/issues/36085)) ([#36087](https://github.com/frappe/erpnext/issues/36087)) ([e05bb10](https://github.com/frappe/erpnext/commit/e05bb103f39a72e447f03fde89e62250770b868b)) * payment entry `voucher_type` error ([#35779](https://github.com/frappe/erpnext/issues/35779)) ([9c3ec41](https://github.com/frappe/erpnext/commit/9c3ec41803cc5fb73f1c7ec4d245fec21727e6c5)) * **Payment Entry:** compare rounded amount ([#36011](https://github.com/frappe/erpnext/issues/36011)) ([b04c190](https://github.com/frappe/erpnext/commit/b04c190e33578ac307e881c06d6f468ca982a196)) * precision causing outstanding issue on partly paid invoices ([#36030](https://github.com/frappe/erpnext/issues/36030)) ([cf3ec93](https://github.com/frappe/erpnext/commit/cf3ec935a7f14bfd9ff99feb76f875043147d89e)) * single column indexes ([#32425](https://github.com/frappe/erpnext/issues/32425)) ([53f7764](https://github.com/frappe/erpnext/commit/53f7764c677ed90ae67b305ad42e2fae23a163f4)) ### Reverts * Revert "perf: improve item wise register reports (backport #35908) (#35912)" ([b992366](https://github.com/frappe/erpnext/commit/b99236624686839c0a96b99a78da22cda25fb3ad)), closes [#35908](https://github.com/frappe/erpnext/issues/35908) [#35912](https://github.com/frappe/erpnext/issues/35912) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c7293f04b98..268f987f916 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.3" +__version__ = "13.52.4" def get_default_company(user=None): From 73366de12f497568b2731362c3c8964c710d614c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:18:07 +0530 Subject: [PATCH 02/28] fix: HSN code not showing up in GST itemised register reports (backport #36142) (#36143) fix: HSN code not showing up in GST itemised register reports (#36142) fix: hsn code not showing up in gst itemised registers (cherry picked from commit 1d3917b3359a138d5b3441fbfe162d5c0fdcc175) Co-authored-by: Anand Baburajan --- .../gst_itemised_purchase_register.py | 2 +- .../gst_itemised_sales_register/gst_itemised_sales_register.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py index 4dcdf4b88e2..fec63f2f18a 100644 --- a/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py +++ b/erpnext/regional/report/gst_itemised_purchase_register/gst_itemised_purchase_register.py @@ -28,7 +28,7 @@ def execute(filters=None): "gst_category", "export_type", "ecommerce_gstin", - "`tabPurchase Invoice Item`.gst_hsn_code", + "gst_hsn_code", "bill_no", "bill_date", ], diff --git a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py index 3280c75fb34..aef69b17868 100644 --- a/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py +++ b/erpnext/regional/report/gst_itemised_sales_register/gst_itemised_sales_register.py @@ -39,7 +39,7 @@ def execute(filters=None): "gst_category", "export_type", "ecommerce_gstin", - "`tabSales Invoice Item`.gst_hsn_code", + "gst_hsn_code", ] additional_conditions = get_conditions(filters, additional_query_columns) From cefa78b8641054c9e7d335bb55c1749caa1d7e98 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 14 Jul 2023 14:49:30 +0000 Subject: [PATCH 03/28] chore(release): Bumped to Version 13.52.5 ## [13.52.5](https://github.com/frappe/erpnext/compare/v13.52.4...v13.52.5) (2023-07-14) ### Bug Fixes * HSN code not showing up in GST itemised register reports (backport [#36142](https://github.com/frappe/erpnext/issues/36142)) ([#36143](https://github.com/frappe/erpnext/issues/36143)) ([73366de](https://github.com/frappe/erpnext/commit/73366de12f497568b2731362c3c8964c710d614c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 268f987f916..c992ed076f7 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.4" +__version__ = "13.52.5" def get_default_company(user=None): From 128ea0d7fcb6f55f21d3888f17c1de137bf0e468 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 18 Jul 2023 12:21:14 +0000 Subject: [PATCH 04/28] chore(release): Bumped to Version 13.52.6 ## [13.52.6](https://github.com/frappe/erpnext/compare/v13.52.5...v13.52.6) (2023-07-18) ### Bug Fixes * allow manual asset receipt mov from nowhere (backport [#36093](https://github.com/frappe/erpnext/issues/36093)) ([#36095](https://github.com/frappe/erpnext/issues/36095)) ([2993eb5](https://github.com/frappe/erpnext/commit/2993eb5ce9c666074d259ed03d5455d9c466e0e8)) * HSN code not showing up in GST itemised register reports ([#36142](https://github.com/frappe/erpnext/issues/36142)) ([1d3917b](https://github.com/frappe/erpnext/commit/1d3917b3359a138d5b3441fbfe162d5c0fdcc175)) * improve "Update Items" modal ([#36105](https://github.com/frappe/erpnext/issues/36105)) ([3bc899f](https://github.com/frappe/erpnext/commit/3bc899f354ced45c58178b69ed248bdd5cae3468)) ### Performance Improvements * index in `Item` and `Item Variant Attribute` (backport [#36133](https://github.com/frappe/erpnext/issues/36133)) ([#36145](https://github.com/frappe/erpnext/issues/36145)) ([120de24](https://github.com/frappe/erpnext/commit/120de249dddfde606ea5da58bc20e7a291e37e2e)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c992ed076f7..e3b2ff93f0f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.5" +__version__ = "13.52.6" def get_default_company(user=None): From 46966f4b7c2226fc7982b2eccbbb3c5581498f7c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 26 Jul 2023 04:10:14 +0000 Subject: [PATCH 05/28] chore(release): Bumped to Version 13.52.7 ## [13.52.7](https://github.com/frappe/erpnext/compare/v13.52.6...v13.52.7) (2023-07-26) ### Bug Fixes * allow both custodian and location while creating asset (backport [#36263](https://github.com/frappe/erpnext/issues/36263)) ([#36270](https://github.com/frappe/erpnext/issues/36270)) ([e4f28e8](https://github.com/frappe/erpnext/commit/e4f28e8a5bc9ec6505691cf7dd7d33870c6fccdb)) * apply discount on item after applying price list ([#36125](https://github.com/frappe/erpnext/issues/36125)) ([bde9e89](https://github.com/frappe/erpnext/commit/bde9e8958205c8afdcd107c75fd680b9dd6b9b65)) * group by in fixed asset register (copy [#36310](https://github.com/frappe/erpnext/issues/36310)) ([#36312](https://github.com/frappe/erpnext/issues/36312)) ([a5e1c47](https://github.com/frappe/erpnext/commit/a5e1c4798fb3778818e9ffba48e5220d6fb90456)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e3b2ff93f0f..67a90aeb004 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.6" +__version__ = "13.52.7" def get_default_company(user=None): From 0281afceaddcf8969f2de057aff0bd997d161067 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 1 Aug 2023 18:04:50 +0000 Subject: [PATCH 06/28] chore(release): Bumped to Version 13.52.8 ## [13.52.8](https://github.com/frappe/erpnext/compare/v13.52.7...v13.52.8) (2023-08-01) ### Bug Fixes * added validation for unique serial numbers in pos invoice ([#36302](https://github.com/frappe/erpnext/issues/36302)) ([a165b37](https://github.com/frappe/erpnext/commit/a165b37fd73f6b68f17c19f7e537e08ba035bb2f)) * only publish repost progress to doc subscriber (backport [#36400](https://github.com/frappe/erpnext/issues/36400)) ([#36403](https://github.com/frappe/erpnext/issues/36403)) ([e9df064](https://github.com/frappe/erpnext/commit/e9df06406f63824ceb101d59641254484bf1a84c)) * removed "fetch_from" (backport [#36365](https://github.com/frappe/erpnext/issues/36365)) ([#36387](https://github.com/frappe/erpnext/issues/36387)) ([c574494](https://github.com/frappe/erpnext/commit/c574494ddd0d78f0ccddc38d38996a11001a19a0)) ### Performance Improvements * use `LEFT JOIN` instead of `NOT EXISTS` (backport [#36221](https://github.com/frappe/erpnext/issues/36221)) ([#36384](https://github.com/frappe/erpnext/issues/36384)) ([cdc86bd](https://github.com/frappe/erpnext/commit/cdc86bd76c5e3f3db8cae9c6abcb5ad2522cb4f9)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 67a90aeb004..8ad90760818 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.7" +__version__ = "13.52.8" def get_default_company(user=None): From 3ccb511e2525e1c5d92eecde4d7751fd8eb0b9b2 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 8 Aug 2023 18:37:46 +0000 Subject: [PATCH 07/28] chore(release): Bumped to Version 13.52.9 ## [13.52.9](https://github.com/frappe/erpnext/compare/v13.52.8...v13.52.9) (2023-08-08) ### Bug Fixes * Tax withholding against order via Payment Entry ([#36493](https://github.com/frappe/erpnext/issues/36493)) ([5dbca09](https://github.com/frappe/erpnext/commit/5dbca09899c0c41f92e950cd8079531159f5ad94)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8ad90760818..6d7abf3325c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.8" +__version__ = "13.52.9" def get_default_company(user=None): From 8f977f40f0f70eb435d59e1aafdffd37015b38f6 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 16 Aug 2023 02:46:48 +0000 Subject: [PATCH 08/28] chore(release): Bumped to Version 13.52.10 ## [13.52.10](https://github.com/frappe/erpnext/compare/v13.52.9...v13.52.10) (2023-08-16) ### Bug Fixes * Allow backdated repayment cancels for term loans ([c417365](https://github.com/frappe/erpnext/commit/c417365e030b05d9252fca996795e49a1c7ea422)) * payment allocation in invoice payment schedule ([#36440](https://github.com/frappe/erpnext/issues/36440)) ([e5b3860](https://github.com/frappe/erpnext/commit/e5b38607cea925d8efce0b5a3a66e29390955f8f)) ### Performance Improvements * **invoice:** Faster return amount query (backport [#36556](https://github.com/frappe/erpnext/issues/36556)) ([#36558](https://github.com/frappe/erpnext/issues/36558)) ([a801bba](https://github.com/frappe/erpnext/commit/a801bba83e8bd09722fc783d14820394b95e05e8)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6d7abf3325c..f7593b2739e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.9" +__version__ = "13.52.10" def get_default_company(user=None): From bdaae811711dc9dd7b362cd357d15d45b53c6741 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 11 Aug 2023 09:50:43 +0530 Subject: [PATCH 09/28] fix: disallow mulitple SO with same PO No (cherry picked from commit dbd3fdbb415e032d738c0055288c41a12e4cd9b4) --- .../doctype/sales_order/sales_order.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index fa7534aa02f..ff3ea575629 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -96,18 +96,26 @@ class SalesOrder(SellingController): and customer = %s", (self.po_no, self.name, self.customer), ) - if ( - so - and so[0][0] - and not cint( + if so and so[0][0]: + if cint( frappe.db.get_single_value("Selling Settings", "allow_against_multiple_purchase_orders") - ) - ): - frappe.msgprint( - _("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format( - so[0][0], self.po_no + ): + frappe.msgprint( + _("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format( + frappe.bold(so[0][0]), frappe.bold(self.po_no) + ) + ) + else: + frappe.throw( + _( + "Sales Order {0} already exists against Customer's Purchase Order {1}. To allow multiple Sales Orders, Enable {2} in {3}" + ).format( + frappe.bold(so[0][0]), + frappe.bold(self.po_no), + frappe.bold(_("'Allow Multiple Sales Orders Against a Customer's Purchase Order'")), + get_link_to_form("Selling Settings", "Selling Settings"), + ) ) - ) def validate_for_items(self): for d in self.get("items"): From 9df10dbc40513fab03caac19553b033e8020880c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 11 Aug 2023 10:45:42 +0530 Subject: [PATCH 10/28] refactor(test): don't set po_no by default (cherry picked from commit 64614cd9152c54fe8a0b59416a0621f9992d41d4) # Conflicts: # erpnext/stock/doctype/delivery_note/test_delivery_note.py --- .../doctype/sales_order/test_sales_order.py | 2 +- .../delivery_note/test_delivery_note.py | 59 +++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index e551f83db86..c9a8016fc71 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1741,7 +1741,7 @@ def make_sales_order(**args): so.company = args.company or "_Test Company" so.customer = args.customer or "_Test Customer" so.currency = args.currency or "INR" - so.po_no = args.po_no or "12345" + so.po_no = args.po_no or "" if args.selling_price_list: so.selling_price_list = args.selling_price_list diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 903e2af3cb3..3b266790a63 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -697,7 +697,7 @@ class TestDeliveryNote(FrappeTestCase): def test_dn_billing_status_case1(self): # SO -> DN -> SI - so = make_sales_order() + so = make_sales_order(po_no="12345") dn = create_dn_against_so(so.name, delivered_qty=2) self.assertEqual(dn.status, "To Bill") @@ -724,7 +724,7 @@ class TestDeliveryNote(FrappeTestCase): make_sales_invoice, ) - so = make_sales_order() + so = make_sales_order(po_no="12345") si = make_sales_invoice(so.name) si.get("items")[0].qty = 5 @@ -768,7 +768,7 @@ class TestDeliveryNote(FrappeTestCase): frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) - so = make_sales_order() + so = make_sales_order(po_no="12345") dn1 = make_delivery_note(so.name) dn1.get("items")[0].qty = 2 @@ -814,7 +814,7 @@ class TestDeliveryNote(FrappeTestCase): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice - so = make_sales_order() + so = make_sales_order(po_no="12345") si = make_sales_invoice(so.name) si.submit() @@ -1180,6 +1180,57 @@ class TestDeliveryNote(FrappeTestCase): self.assertTrue(return_dn.docstatus == 1) +<<<<<<< HEAD +======= + def test_reserve_qty_on_sales_return(self): + frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0) + self.reserved_qty_check() + + def test_dont_reserve_qty_on_sales_return(self): + frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 1) + self.reserved_qty_check() + + def reserved_qty_check(self): + from erpnext.controllers.sales_and_purchase_return import make_return_doc + from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note + from erpnext.stock.stock_balance import get_reserved_qty + + dont_reserve_qty = frappe.db.get_single_value( + "Selling Settings", "dont_reserve_sales_order_qty_on_sales_return" + ) + + item = make_item().name + warehouse = "_Test Warehouse - _TC" + qty_to_reserve = 5 + + so = make_sales_order(item_code=item, qty=qty_to_reserve) + + # Make qty avl for test. + make_stock_entry(item_code=item, to_warehouse=warehouse, qty=10, basic_rate=100) + + # Test that item qty has been reserved on submit of sales order. + self.assertEqual(get_reserved_qty(item, warehouse), qty_to_reserve) + + dn = make_delivery_note(so.name) + dn.save().submit() + + # Test that item qty is no longer reserved since qty has been delivered. + self.assertEqual(get_reserved_qty(item, warehouse), 0) + + dn_return = make_return_doc("Delivery Note", dn.name) + dn_return.save().submit() + + returned = frappe.get_doc("Delivery Note", dn_return.name) + returned.update_prevdoc_status() + + # Test that item qty is not reserved on sales return, if selling setting don't reserve qty is checked. + self.assertEqual(get_reserved_qty(item, warehouse), 0 if dont_reserve_qty else qty_to_reserve) + + def tearDown(self): + frappe.db.rollback() + frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0) + +>>>>>>> 64614cd915 (refactor(test): don't set po_no by default) def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") From 0ff871e38ef814f88edbc11c564fdb7f945edb93 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 17 Aug 2023 09:11:47 +0530 Subject: [PATCH 11/28] chore: resolve conflict --- .../delivery_note/test_delivery_note.py | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 3b266790a63..edc7b996bac 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1180,57 +1180,10 @@ class TestDeliveryNote(FrappeTestCase): self.assertTrue(return_dn.docstatus == 1) -<<<<<<< HEAD -======= - def test_reserve_qty_on_sales_return(self): - frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0) - self.reserved_qty_check() - - def test_dont_reserve_qty_on_sales_return(self): - frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 1) - self.reserved_qty_check() - - def reserved_qty_check(self): - from erpnext.controllers.sales_and_purchase_return import make_return_doc - from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note - from erpnext.stock.stock_balance import get_reserved_qty - - dont_reserve_qty = frappe.db.get_single_value( - "Selling Settings", "dont_reserve_sales_order_qty_on_sales_return" - ) - - item = make_item().name - warehouse = "_Test Warehouse - _TC" - qty_to_reserve = 5 - - so = make_sales_order(item_code=item, qty=qty_to_reserve) - - # Make qty avl for test. - make_stock_entry(item_code=item, to_warehouse=warehouse, qty=10, basic_rate=100) - - # Test that item qty has been reserved on submit of sales order. - self.assertEqual(get_reserved_qty(item, warehouse), qty_to_reserve) - - dn = make_delivery_note(so.name) - dn.save().submit() - - # Test that item qty is no longer reserved since qty has been delivered. - self.assertEqual(get_reserved_qty(item, warehouse), 0) - - dn_return = make_return_doc("Delivery Note", dn.name) - dn_return.save().submit() - - returned = frappe.get_doc("Delivery Note", dn_return.name) - returned.update_prevdoc_status() - - # Test that item qty is not reserved on sales return, if selling setting don't reserve qty is checked. - self.assertEqual(get_reserved_qty(item, warehouse), 0 if dont_reserve_qty else qty_to_reserve) - def tearDown(self): frappe.db.rollback() frappe.db.set_single_value("Selling Settings", "dont_reserve_sales_order_qty_on_sales_return", 0) ->>>>>>> 64614cd915 (refactor(test): don't set po_no by default) def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") From cfbd9af100aedb8c674cfb09494ea473d22bd9b8 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 17 Aug 2023 10:12:01 +0000 Subject: [PATCH 12/28] chore(release): Bumped to Version 13.52.11 ## [13.52.11](https://github.com/frappe/erpnext/compare/v13.52.10...v13.52.11) (2023-08-17) ### Bug Fixes * disallow mulitple SO with same PO No ([bdaae81](https://github.com/frappe/erpnext/commit/bdaae811711dc9dd7b362cd357d15d45b53c6741)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index f7593b2739e..593d5d07b6a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.10" +__version__ = "13.52.11" def get_default_company(user=None): From 9876019c69e56fa6c3a8a2103e36becd04ef5975 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 22 Aug 2023 16:59:52 +0000 Subject: [PATCH 13/28] chore(release): Bumped to Version 13.52.12 ## [13.52.12](https://github.com/frappe/erpnext/compare/v13.52.11...v13.52.12) (2023-08-22) ### Bug Fixes * disallow mulitple SO with same PO No ([3b9ac9f](https://github.com/frappe/erpnext/commit/3b9ac9f46a1a8215ae6c463500197c3281bad002)) * timeout error coming during reposting (backport [#36715](https://github.com/frappe/erpnext/issues/36715)) ([#36753](https://github.com/frappe/erpnext/issues/36753)) ([783bb93](https://github.com/frappe/erpnext/commit/783bb93913498d5970cfbecd487dc589df10895a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 593d5d07b6a..1df0651b7b8 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.11" +__version__ = "13.52.12" def get_default_company(user=None): From 8b2c6ed61a4a1f416d26ec2fe24369fea409ffc5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 6 Sep 2023 07:24:52 +0000 Subject: [PATCH 14/28] chore(release): Bumped to Version 13.52.13 ## [13.52.13](https://github.com/frappe/erpnext/compare/v13.52.12...v13.52.13) (2023-09-06) ### Bug Fixes * ask for asset related accounts only when needed (backport [#36960](https://github.com/frappe/erpnext/issues/36960)) ([#36972](https://github.com/frappe/erpnext/issues/36972)) ([3a71fa9](https://github.com/frappe/erpnext/commit/3a71fa9d96b4e5e9912d7a2e846928b155ca631d)) * update repay_from_salary, payment_account and payroll_payable_account fields ([#36949](https://github.com/frappe/erpnext/issues/36949)) ([6f40d0c](https://github.com/frappe/erpnext/commit/6f40d0cdf6ac1049043368794b0c0e17203d27ea)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1df0651b7b8..94c1645a329 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.12" +__version__ = "13.52.13" def get_default_company(user=None): From d7f09f879542d9f681b30ef74ee19fd86c7fcd6f Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:14:16 +0530 Subject: [PATCH 15/28] fix: prorate factor for subscription plan (#36953) (cherry picked from commit fc79af592648912fe25a64802370eb5389de754a) (cherry picked from commit 9a15ed80832cdb252880b850da2c858e35533842) --- .../doctype/subscription/test_subscription.py | 20 ++++++++++++++ .../subscription_plan/subscription_plan.py | 27 +++++++++---------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index eb17daa282f..c911e7fe12d 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -694,3 +694,23 @@ class TestSubscription(unittest.TestCase): # Check the currency of the created invoice currency = frappe.db.get_value("Sales Invoice", subscription.invoices[0].invoice, "currency") self.assertEqual(currency, "USD") + + def test_plan_rate_for_midmonth_start_date(self): + subscription = frappe.new_doc("Subscription") + subscription.party_type = "Supplier" + subscription.party = "_Test Supplier" + subscription.generate_invoice_at_period_start = 1 + subscription.follow_calendar_months = 1 + subscription.generate_new_invoices_past_due_date = 1 + subscription.start_date = "2023-04-08" + subscription.end_date = "2024-02-27" + subscription.append("plans", {"plan": "_Test Plan Name 4", "qty": 1}) + subscription.save() + + subscription.process() + + self.assertEqual(len(subscription.invoices), 1) + pi = frappe.get_doc("Purchase Invoice", subscription.invoices[0].invoice) + self.assertEqual(pi.total, 55333.33) + + subscription.delete() diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index a95e0a9c2da..1fe75bf2d04 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -56,18 +56,17 @@ def get_plan_rate( prorate = frappe.db.get_single_value("Subscription Settings", "prorate") if prorate: - prorate_factor = flt( - date_diff(start_date, get_first_day(start_date)) - / date_diff(get_last_day(start_date), get_first_day(start_date)), - 1, - ) - - prorate_factor += flt( - date_diff(get_last_day(end_date), end_date) - / date_diff(get_last_day(end_date), get_first_day(end_date)), - 1, - ) - - cost -= plan.cost * prorate_factor - + cost -= plan.cost * get_prorate_factor(start_date, end_date) return cost + + +def get_prorate_factor(start_date, end_date): + total_days_to_skip = date_diff(start_date, get_first_day(start_date)) + total_days_in_month = int(get_last_day(start_date).strftime("%d")) + prorate_factor = flt(total_days_to_skip / total_days_in_month) + + total_days_to_skip = date_diff(get_last_day(end_date), end_date) + total_days_in_month = int(get_last_day(end_date).strftime("%d")) + prorate_factor += flt(total_days_to_skip / total_days_in_month) + + return prorate_factor From efab1e7361123b18efc22e450eb5c86b2cbc5a26 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sun, 10 Sep 2023 18:30:02 +0000 Subject: [PATCH 16/28] chore(release): Bumped to Version 13.52.14 ## [13.52.14](https://github.com/frappe/erpnext/compare/v13.52.13...v13.52.14) (2023-09-10) ### Bug Fixes * prorate factor for subscription plan ([#36953](https://github.com/frappe/erpnext/issues/36953)) ([d7f09f8](https://github.com/frappe/erpnext/commit/d7f09f879542d9f681b30ef74ee19fd86c7fcd6f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 94c1645a329..0446e59d6b7 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.13" +__version__ = "13.52.14" def get_default_company(user=None): From 11b16569e58add71fe801c085edf5d87a9b9ef8c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 12 Sep 2023 12:21:41 +0000 Subject: [PATCH 17/28] chore(release): Bumped to Version 13.52.15 ## [13.52.15](https://github.com/frappe/erpnext/compare/v13.52.14...v13.52.15) (2023-09-12) ### Bug Fixes * prorate factor for subscription plan ([#36953](https://github.com/frappe/erpnext/issues/36953)) ([9a15ed8](https://github.com/frappe/erpnext/commit/9a15ed80832cdb252880b850da2c858e35533842)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0446e59d6b7..af077d04d1d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.14" +__version__ = "13.52.15" def get_default_company(user=None): From 7c1288f72699374be7f51eb55818084a11b21248 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 19 Sep 2023 13:22:41 +0000 Subject: [PATCH 18/28] chore(release): Bumped to Version 13.53.0 # [13.53.0](https://github.com/frappe/erpnext/compare/v13.52.15...v13.53.0) (2023-09-19) ### Bug Fixes * fetch logic for repay_from_salary in loan_repayment [v13] ([#37136](https://github.com/frappe/erpnext/issues/37136)) ([d4f8b05](https://github.com/frappe/erpnext/commit/d4f8b057e1ea6b007632833fa581ba8ff3306972)) ### Features * provision to create RIV from `Stock Ledger Invariant Check` report (backport [#37115](https://github.com/frappe/erpnext/issues/37115)) ([#37149](https://github.com/frappe/erpnext/issues/37149)) ([86b152f](https://github.com/frappe/erpnext/commit/86b152fe5c950a6987ac20cd531591676c728849)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index af077d04d1d..4dded81c32f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.52.15" +__version__ = "13.53.0" def get_default_company(user=None): From 4285bbcdc05be30f55f3fa32b1e0741c67c466ad Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 27 Sep 2023 09:25:47 +0530 Subject: [PATCH 19/28] chore: release v13 (#37245) * feat: provision to make reposting entries from Stock and Account Value Comparison Report (backport #35365) (#37171) * feat: provision to make reposting entries from Stock and Account Value Comparison Report (cherry picked from commit 7b818e9d34c87ad2fd80493454bd86674ddb54c5) * fix: `linter` * fix(ux): throw if no row selected to create repost entries --------- Co-authored-by: Rohit Waghchaure Co-authored-by: s-aga-r * fix: incorrect stock ledger entries in DN (backport #36944) (#37067) * fix: incorrect stock ledger entries in DN (#36944) (cherry picked from commit 0e83190c190ea122fd6d221704c29556b0394d48) # Conflicts: # erpnext/stock/doctype/delivery_note/delivery_note.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure * feat: `Stock Ledger Variance` report (backport #37165) (#37184) feat: `Stock Ledger Variance` report (#37165) * feat: `Stock Ledger Variance` report * refactor: `get_data()` (cherry picked from commit acda72d6165aa395fedebf9522aa0adf45c25fa2) Co-authored-by: s-aga-r * fix: Update `advance_paid` in SO/PO after unlinking from advance entry (cherry picked from commit 426350eee6efdfeddf300bbeccd7674f0a6d7b9b) * test: Impact on SO of advance PE submit and unlinking/replacement by SI (cherry picked from commit 8a4954d713a31a60581414be38ca90c1fad3c794) --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Rohit Waghchaure Co-authored-by: s-aga-r Co-authored-by: marination Co-authored-by: ruthra kumar --- .../sales_invoice/test_sales_invoice.py | 32 +- erpnext/accounts/utils.py | 11 + .../doctype/delivery_note/delivery_note.json | 5 +- .../stock_and_account_value_comparison.js | 40 ++- .../stock_and_account_value_comparison.py | 33 +++ .../report/stock_ledger_variance/__init__.py | 0 .../stock_ledger_variance.js | 101 +++++++ .../stock_ledger_variance.json | 22 ++ .../stock_ledger_variance.py | 279 ++++++++++++++++++ 9 files changed, 516 insertions(+), 7 deletions(-) create mode 100644 erpnext/stock/report/stock_ledger_variance/__init__.py create mode 100644 erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js create mode 100644 erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.json create mode 100644 erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 46ffd7e18d0..84a56404123 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1783,6 +1783,10 @@ class TestSalesInvoice(unittest.TestCase): ) def test_outstanding_amount_after_advance_payment_entry_cancellation(self): + """Test impact of advance PE submission/cancellation on SI and SO.""" + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + + sales_order = make_sales_order(item_code="138-CMS Shoe", qty=1, price_list_rate=500) pe = frappe.get_doc( { "doctype": "Payment Entry", @@ -1802,10 +1806,25 @@ class TestSalesInvoice(unittest.TestCase): "paid_to": "_Test Cash - _TC", } ) + pe.append( + "references", + { + "reference_doctype": "Sales Order", + "reference_name": sales_order.name, + "total_amount": sales_order.grand_total, + "outstanding_amount": sales_order.grand_total, + "allocated_amount": 300, + }, + ) pe.insert() pe.submit() + sales_order.reload() + self.assertEqual(sales_order.advance_paid, 300) + si = frappe.copy_doc(test_records[0]) + si.items[0].sales_order = sales_order.name + si.items[0].so_detail = sales_order.get("items")[0].name si.is_pos = 0 si.append( "advances", @@ -1813,6 +1832,7 @@ class TestSalesInvoice(unittest.TestCase): "doctype": "Sales Invoice Advance", "reference_type": "Payment Entry", "reference_name": pe.name, + "reference_row": pe.references[0].name, "advance_amount": 300, "allocated_amount": 300, "remarks": pe.remarks, @@ -1821,7 +1841,13 @@ class TestSalesInvoice(unittest.TestCase): si.insert() si.submit() - si.load_from_db() + si.reload() + pe.reload() + sales_order.reload() + + # Check if SO is unlinked/replaced by SI in PE & if SO advance paid is 0 + self.assertEqual(pe.references[0].reference_name, si.name) + self.assertEqual(sales_order.advance_paid, 0.0) # check outstanding after advance allocation self.assertEqual( @@ -1829,11 +1855,9 @@ class TestSalesInvoice(unittest.TestCase): flt(si.rounded_total - si.total_advance, si.precision("outstanding_amount")), ) - # added to avoid Document has been modified exception - pe = frappe.get_doc("Payment Entry", pe.name) pe.cancel() + si.reload() - si.load_from_db() # check outstanding after advance cancellation self.assertEqual( flt(si.outstanding_amount), diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 301813979c4..f611fa32914 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -537,6 +537,10 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): """ jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0] + # Update Advance Paid in SO/PO since they might be getting unlinked + if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"): + frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid() + if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0: # adjust the unreconciled balance amount_in_account_currency = flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) @@ -596,6 +600,13 @@ def update_reference_in_payment_entry(d, payment_entry, do_not_save=False): if d.voucher_detail_no: existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0] + + # Update Advance Paid in SO/PO since they are getting unlinked + if existing_row.get("reference_doctype") in ("Sales Order", "Purchase Order"): + frappe.get_doc( + existing_row.reference_doctype, existing_row.reference_name + ).set_total_advance_paid() + original_row = existing_row.as_dict().copy() existing_row.update(reference_details) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index a8f907ed711..f028b1257e8 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1268,6 +1268,7 @@ "depends_on": "eval: doc.is_internal_customer", "fieldname": "set_target_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "in_standard_filter": 1, "label": "Set Target Warehouse", "no_copy": 1, @@ -1335,7 +1336,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:46:17.701904", + "modified": "2023-09-04 14:15:28.363184", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", @@ -1405,4 +1406,4 @@ "title_field": "title", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js index 7a170beec37..b1aef14e677 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js @@ -33,5 +33,43 @@ frappe.query_reports["Stock and Account Value Comparison"] = { "fieldtype": "Date", "default": frappe.datetime.get_today(), }, - ] + ], + + get_datatable_options(options) { + return Object.assign(options, { + checkboxColumn: true, + }); + }, + + onload(report) { + report.page.add_inner_button(__("Create Reposting Entries"), function() { + let message = ` +
+

+ Reposting Entries will change the value of + accounts Stock In Hand, and Stock Expenses + in the Trial Balance report and will also change + the Balance Value in the Stock Balance report. +

+

Are you sure you want to create Reposting Entries?

+
`; + let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); + let selected_rows = indexes.map(i => frappe.query_report.data[i]); + + if (!selected_rows.length) { + frappe.throw(__("Please select rows to create Reposting Entries")); + } + + frappe.confirm(__(message), () => { + frappe.call({ + method: "erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison.create_reposting_entries", + args: { + rows: selected_rows, + company: frappe.query_report.get_filter_values().company + } + }); + + }); + }); + } }; diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 106e877c4cd..416cf48871a 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.utils import get_link_to_form, parse_json import erpnext from erpnext.accounts.utils import get_currency_precision, get_stock_accounts @@ -134,3 +135,35 @@ def get_columns(filters): "width": "120", }, ] + + +@frappe.whitelist() +def create_reposting_entries(rows, company): + if isinstance(rows, str): + rows = parse_json(rows) + + entries = [] + for row in rows: + row = frappe._dict(row) + + try: + doc = frappe.get_doc( + { + "doctype": "Repost Item Valuation", + "based_on": "Transaction", + "status": "Queued", + "voucher_type": row.voucher_type, + "voucher_no": row.voucher_no, + "posting_date": row.posting_date, + "company": company, + "allow_nagative_stock": 1, + } + ).submit() + + entries.append(get_link_to_form("Repost Item Valuation", doc.name)) + except frappe.DuplicateEntryError: + pass + + if entries: + entries = ", ".join(entries) + frappe.msgprint(_("Reposting entries created: {0}").format(entries)) diff --git a/erpnext/stock/report/stock_ledger_variance/__init__.py b/erpnext/stock/report/stock_ledger_variance/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js new file mode 100644 index 00000000000..b1e4a74571e --- /dev/null +++ b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js @@ -0,0 +1,101 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +const DIFFERENCE_FIELD_NAMES = [ + "difference_in_qty", + "fifo_qty_diff", + "fifo_value_diff", + "fifo_valuation_diff", + "valuation_diff", + "fifo_difference_diff", + "diff_value_diff" +]; + +frappe.query_reports["Stock Ledger Variance"] = { + "filters": [ + { + "fieldname": "item_code", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + get_query: function() { + return { + filters: {is_stock_item: 1, has_serial_no: 0} + } + } + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse", + get_query: function() { + return { + filters: {is_group: 0, disabled: 0} + } + } + }, + { + "fieldname": "difference_in", + "fieldtype": "Select", + "label": "Difference In", + "options": [ + "", + "Qty", + "Value", + "Valuation", + ], + }, + { + "fieldname": "include_disabled", + "fieldtype": "Check", + "label": "Include Disabled", + } + ], + + formatter (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (DIFFERENCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) { + value = "" + value + ""; + } + + return value; + }, + + get_datatable_options(options) { + return Object.assign(options, { + checkboxColumn: true, + }); + }, + + onload(report) { + report.page.add_inner_button(__('Create Reposting Entries'), () => { + let message = ` +
+

+ Reposting Entries will change the value of + accounts Stock In Hand, and Stock Expenses + in the Trial Balance report and will also change + the Balance Value in the Stock Balance report. +

+

Are you sure you want to create Reposting Entries?

+
`; + let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); + let selected_rows = indexes.map(i => frappe.query_report.data[i]); + + if (!selected_rows.length) { + frappe.throw(__("Please select rows to create Reposting Entries")); + } + + frappe.confirm(__(message), () => { + frappe.call({ + method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries', + args: { + rows: selected_rows, + } + }); + }); + }); + }, +}; diff --git a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.json b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.json new file mode 100644 index 00000000000..f36ed1b9ca6 --- /dev/null +++ b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.json @@ -0,0 +1,22 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2023-09-20 10:44:19.414449", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letterhead": null, + "modified": "2023-09-20 10:44:19.414449", + "modified_by": "Administrator", + "module": "Stock", + "name": "Stock Ledger Variance", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Stock Ledger Entry", + "report_name": "Stock Ledger Variance", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py new file mode 100644 index 00000000000..732f108ac41 --- /dev/null +++ b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.py @@ -0,0 +1,279 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.utils import cint, flt + +from erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check import ( + get_data as stock_ledger_invariant_check, +) + + +def execute(filters=None): + columns, data = [], [] + + filters = frappe._dict(filters or {}) + columns = get_columns() + data = get_data(filters) + + return columns, data + + +def get_columns(): + return [ + { + "fieldname": "name", + "fieldtype": "Link", + "label": _("Stock Ledger Entry"), + "options": "Stock Ledger Entry", + }, + { + "fieldname": "posting_date", + "fieldtype": "Data", + "label": _("Posting Date"), + }, + { + "fieldname": "posting_time", + "fieldtype": "Data", + "label": _("Posting Time"), + }, + { + "fieldname": "creation", + "fieldtype": "Data", + "label": _("Creation"), + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "label": _("Item"), + "options": "Item", + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": _("Warehouse"), + "options": "Warehouse", + }, + { + "fieldname": "voucher_type", + "fieldtype": "Link", + "label": _("Voucher Type"), + "options": "DocType", + }, + { + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "label": _("Voucher No"), + "options": "voucher_type", + }, + { + "fieldname": "batch_no", + "fieldtype": "Link", + "label": _("Batch"), + "options": "Batch", + }, + { + "fieldname": "use_batchwise_valuation", + "fieldtype": "Check", + "label": _("Batchwise Valuation"), + }, + { + "fieldname": "actual_qty", + "fieldtype": "Float", + "label": _("Qty Change"), + }, + { + "fieldname": "incoming_rate", + "fieldtype": "Float", + "label": _("Incoming Rate"), + }, + { + "fieldname": "consumption_rate", + "fieldtype": "Float", + "label": _("Consumption Rate"), + }, + { + "fieldname": "qty_after_transaction", + "fieldtype": "Float", + "label": _("(A) Qty After Transaction"), + }, + { + "fieldname": "expected_qty_after_transaction", + "fieldtype": "Float", + "label": _("(B) Expected Qty After Transaction"), + }, + { + "fieldname": "difference_in_qty", + "fieldtype": "Float", + "label": _("A - B"), + }, + { + "fieldname": "stock_queue", + "fieldtype": "Data", + "label": _("FIFO/LIFO Queue"), + }, + { + "fieldname": "fifo_queue_qty", + "fieldtype": "Float", + "label": _("(C) Total Qty in Queue"), + }, + { + "fieldname": "fifo_qty_diff", + "fieldtype": "Float", + "label": _("A - C"), + }, + { + "fieldname": "stock_value", + "fieldtype": "Float", + "label": _("(D) Balance Stock Value"), + }, + { + "fieldname": "fifo_stock_value", + "fieldtype": "Float", + "label": _("(E) Balance Stock Value in Queue"), + }, + { + "fieldname": "fifo_value_diff", + "fieldtype": "Float", + "label": _("D - E"), + }, + { + "fieldname": "stock_value_difference", + "fieldtype": "Float", + "label": _("(F) Change in Stock Value"), + }, + { + "fieldname": "stock_value_from_diff", + "fieldtype": "Float", + "label": _("(G) Sum of Change in Stock Value"), + }, + { + "fieldname": "diff_value_diff", + "fieldtype": "Float", + "label": _("G - D"), + }, + { + "fieldname": "fifo_stock_diff", + "fieldtype": "Float", + "label": _("(H) Change in Stock Value (FIFO Queue)"), + }, + { + "fieldname": "fifo_difference_diff", + "fieldtype": "Float", + "label": _("H - F"), + }, + { + "fieldname": "valuation_rate", + "fieldtype": "Float", + "label": _("(I) Valuation Rate"), + }, + { + "fieldname": "fifo_valuation_rate", + "fieldtype": "Float", + "label": _("(J) Valuation Rate as per FIFO"), + }, + { + "fieldname": "fifo_valuation_diff", + "fieldtype": "Float", + "label": _("I - J"), + }, + { + "fieldname": "balance_value_by_qty", + "fieldtype": "Float", + "label": _("(K) Valuation = Value (D) รท Qty (A)"), + }, + { + "fieldname": "valuation_diff", + "fieldtype": "Float", + "label": _("I - K"), + }, + ] + + +def get_data(filters=None): + filters = frappe._dict(filters or {}) + item_warehouse_map = get_item_warehouse_combinations(filters) + + data = [] + if item_warehouse_map: + precision = cint(frappe.db.get_single_value("System Settings", "float_precision")) + + for item_warehouse in item_warehouse_map: + report_data = stock_ledger_invariant_check(item_warehouse) + + if not report_data: + continue + + for row in report_data: + if has_difference(row, precision, filters.difference_in): + data.append(add_item_warehouse_details(row, item_warehouse)) + break + + return data + + +def get_item_warehouse_combinations(filters: dict = None) -> dict: + filters = frappe._dict(filters or {}) + + bin = frappe.qb.DocType("Bin") + item = frappe.qb.DocType("Item") + warehouse = frappe.qb.DocType("Warehouse") + + query = ( + frappe.qb.from_(bin) + .inner_join(item) + .on(bin.item_code == item.name) + .inner_join(warehouse) + .on(bin.warehouse == warehouse.name) + .select( + bin.item_code, + bin.warehouse, + ) + .where((item.is_stock_item == 1) & (item.has_serial_no == 0) & (warehouse.is_group == 0)) + ) + + if filters.item_code: + query = query.where(item.name == filters.item_code) + if filters.warehouse: + query = query.where(warehouse.name == filters.warehouse) + if not filters.include_disabled: + query = query.where((item.disabled == 0) & (warehouse.disabled == 0)) + + return query.run(as_dict=1) + + +def has_difference(row, precision, difference_in): + has_qty_difference = flt(row.difference_in_qty, precision) or flt(row.fifo_qty_diff, precision) + has_value_difference = ( + flt(row.diff_value_diff, precision) + or flt(row.fifo_value_diff, precision) + or flt(row.fifo_difference_diff, precision) + ) + has_valuation_difference = flt(row.valuation_diff, precision) or flt( + row.fifo_valuation_diff, precision + ) + + if difference_in == "Qty" and has_qty_difference: + return True + elif difference_in == "Value" and has_value_difference: + return True + elif difference_in == "Valuation" and has_valuation_difference: + return True + elif difference_in not in ["Qty", "Value", "Valuation"] and ( + has_qty_difference or has_value_difference or has_valuation_difference + ): + return True + + return False + + +def add_item_warehouse_details(row, item_warehouse): + row.update( + { + "item_code": item_warehouse.item_code, + "warehouse": item_warehouse.warehouse, + } + ) + + return row From b0708d29a85cc4f25da21284c945e492a7e332c1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Oct 2023 04:15:19 +0000 Subject: [PATCH 20/28] chore(release): Bumped to Version 13.54.0 # [13.54.0](https://github.com/frappe/erpnext/compare/v13.53.0...v13.54.0) (2023-10-04) ### Bug Fixes * incorrect stock ledger entries in DN (backport [#36944](https://github.com/frappe/erpnext/issues/36944)) ([#37067](https://github.com/frappe/erpnext/issues/37067)) ([5833c4d](https://github.com/frappe/erpnext/commit/5833c4dae252c23c87379e02274470d899780e8e)) * trial balance report freezes when adding filters (backport [#37264](https://github.com/frappe/erpnext/issues/37264)) ([#37267](https://github.com/frappe/erpnext/issues/37267)) ([ff6b38c](https://github.com/frappe/erpnext/commit/ff6b38c9e7a3115d4fbf5fc8ff21fe0f3bb12b10)) * Update `advance_paid` in SO/PO after unlinking from advance entry ([a6bef64](https://github.com/frappe/erpnext/commit/a6bef64c8e89cfd3c60612a61cb917fe26f316a5)) ### Features * `Stock Ledger Variance` report (backport [#37165](https://github.com/frappe/erpnext/issues/37165)) ([#37184](https://github.com/frappe/erpnext/issues/37184)) ([5092ea1](https://github.com/frappe/erpnext/commit/5092ea175eb9ac9c827c9f0bfc8a26d2c39b346e)) * provision to make reposting entries from Stock and Account Value Comparison Report (backport [#35365](https://github.com/frappe/erpnext/issues/35365)) ([#37171](https://github.com/frappe/erpnext/issues/37171)) ([48eb6a6](https://github.com/frappe/erpnext/commit/48eb6a65733205f26642cfd09333a4eb260dcc9b)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 4dded81c32f..6472f1fa180 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.53.0" +__version__ = "13.54.0" def get_default_company(user=None): From 1da808a125fd69b91dc868e6aec31165a461771d Mon Sep 17 00:00:00 2001 From: dsnetprofitxl <139927603+dsnetprofitxl@users.noreply.github.com> Date: Tue, 3 Oct 2023 20:51:28 -0400 Subject: [PATCH 21/28] fix: change currency exchange API to api.frankfurter.app (cherry picked from commit 76919c4af2c127b19339fef9968dc3954e095d4c) --- erpnext/setup/utils.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 5a019c68c9d..5c7a5632374 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -113,13 +113,12 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if not value: import requests - api_url = "https://api.exchangerate.host/convert" - response = requests.get( - api_url, params={"date": transaction_date, "from": from_currency, "to": to_currency} - ) + api_url = f"https://api.frankfurter.app/{transaction_date}" + response = requests.get(api_url, params={"from": from_currency, "to": to_currency}) + # expire in 6 hours response.raise_for_status() - value = response.json()["result"] + value = response.json()["rates"][to_currency] cache.setex(name=key, time=21600, value=flt(value)) return flt(value) except Exception: From 40e475836e1f9a20be23c8b79037032c87ba5e27 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 4 Oct 2023 21:04:05 +0530 Subject: [PATCH 22/28] refactor(test): update exchange rate based on 'frankfurter' provider (cherry picked from commit a3fd4db45044efbe4e8b3844e5c62e57ab392549) --- .../doctype/currency_exchange/test_currency_exchange.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index dcd06607c3e..c598cdf9819 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -75,7 +75,7 @@ class TestCurrencyExchange(unittest.TestCase): self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_selling") self.assertFalse(exchange_rate == 60) - self.assertEqual(flt(exchange_rate, 3), 66.999) + self.assertEqual(flt(exchange_rate, 3), 66.894) def test_exchange_rate_strict(self): # strict currency settings @@ -87,7 +87,7 @@ class TestCurrencyExchange(unittest.TestCase): self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying") - self.assertEqual(flt(exchange_rate, 3), 67.235) + self.assertEqual(flt(exchange_rate, 3), 67.79) exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling") self.assertEqual(exchange_rate, 62.9) @@ -95,7 +95,7 @@ class TestCurrencyExchange(unittest.TestCase): # Exchange rate as on 15th Dec, 2015 self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_buying") - self.assertEqual(flt(exchange_rate, 3), 66.999) + self.assertEqual(flt(exchange_rate, 3), 66.894) def test_exchange_rate_strict_switched(self): # Start with allow_stale is True @@ -108,4 +108,4 @@ class TestCurrencyExchange(unittest.TestCase): # Will fetch from fixer.io self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying") - self.assertEqual(flt(exchange_rate, 3), 67.235) + self.assertEqual(flt(exchange_rate, 3), 67.79) From fa0eef96b9032b637f037303601ff9b51247691b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 5 Oct 2023 03:51:29 +0000 Subject: [PATCH 23/28] chore(release): Bumped to Version 13.54.1 ## [13.54.1](https://github.com/frappe/erpnext/compare/v13.54.0...v13.54.1) (2023-10-05) ### Bug Fixes * change currency exchange API to api.frankfurter.app ([1da808a](https://github.com/frappe/erpnext/commit/1da808a125fd69b91dc868e6aec31165a461771d)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6472f1fa180..215e5e71759 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.54.0" +__version__ = "13.54.1" def get_default_company(user=None): From 9eb02637d8dff9986f1042f0ab2ac6fc88199c1d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 6 Oct 2023 15:05:57 +0530 Subject: [PATCH 24/28] refactor: selectable exchange rate service providers (cherry picked from commit 55dfd8e99573f53c2e88ff9fc543e9995fa231d5) --- .../accounts_settings/accounts_settings.json | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index d3b780b56f6..9a85b94e9a9 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -46,6 +46,7 @@ "currency_exchange_section", "allow_stale", "stale_days", + "service_provider", "report_settings_sb", "use_custom_cash_flow" ], @@ -282,32 +283,38 @@ "label": "Enable Common Party Accounting" }, { - "default": "0", - "description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency", - "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" - }, - { - "default": "0", - "fieldname": "show_taxes_as_table_in_print", - "fieldtype": "Check", - "label": "Show Taxes as Table in Print" - } + "default": "0", + "description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency", + "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" + }, + { + "default": "0", + "fieldname": "show_taxes_as_table_in_print", + "fieldtype": "Check", + "label": "Show Taxes as Table in Print" + }, + { + "fieldname": "service_provider", + "fieldtype": "Select", + "label": "Service Provider", + "options": "frankfurter.app\nexchangerate.host" + } ], "icon": "icon-cog", "idx": 1, "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-06-13 18:47:46.430291", + "modified": "2023-10-06 15:05:51.999404", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", From 32ae68eb5c6b6fb419836108e69ec8285993a696 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 6 Oct 2023 17:12:40 +0530 Subject: [PATCH 25/28] refactor: access_key field for service_provider (cherry picked from commit 22fb65621c0ba66d41409971d09eea9ef71e802e) --- .../accounts_settings/accounts_settings.js | 6 ++++ .../accounts_settings/accounts_settings.json | 23 ++++++++++++- .../accounts_settings/accounts_settings.py | 33 ++++++++++++++++++- erpnext/setup/utils.py | 24 ++++++++++++-- 4 files changed, 81 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js index e44af3a9167..51e1f7a91dc 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js @@ -4,6 +4,12 @@ frappe.ui.form.on('Accounts Settings', { refresh: function(frm) { + }, + validate_access_key(frm) { + frappe.call({ + doc: frm.doc, + method: "validate_access_key" + }); } }); diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 9a85b94e9a9..41805b1bd50 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -47,6 +47,9 @@ "allow_stale", "stale_days", "service_provider", + "column_break_eiyok", + "access_key", + "validate_access_key", "report_settings_sb", "use_custom_cash_flow" ], @@ -303,10 +306,28 @@ "label": "Show Taxes as Table in Print" }, { + "default": "frankfurter.app", "fieldname": "service_provider", "fieldtype": "Select", "label": "Service Provider", "options": "frankfurter.app\nexchangerate.host" + }, + { + "depends_on": "eval:doc.service_provider == \"exchangerate.host\"", + "description": "Access Key is mandatory for exchangerate.host", + "fieldname": "access_key", + "fieldtype": "Data", + "label": "Access Key" + }, + { + "depends_on": "eval:doc.service_provider == \"exchangerate.host\"", + "fieldname": "validate_access_key", + "fieldtype": "Button", + "label": "Validate Access Key" + }, + { + "fieldname": "column_break_eiyok", + "fieldtype": "Column Break" } ], "icon": "icon-cog", @@ -314,7 +335,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-10-06 15:05:51.999404", + "modified": "2023-10-07 14:20:01.779208", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 835498176c7..7eafbd70e3a 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -8,12 +8,43 @@ import frappe from frappe import _ from frappe.custom.doctype.property_setter.property_setter import make_property_setter from frappe.model.document import Document -from frappe.utils import cint +from frappe.utils import cint, nowdate from erpnext.stock.utils import check_pending_reposting class AccountsSettings(Document): + @frappe.whitelist() + def validate_access_key(self): + if self.service_provider == "exchangerate.host": + if not self.access_key: + frappe.throw(_("Access Key is required for exchangerate.host")) + else: + import requests + + # Validate access key + api_url = "https://api.exchangerate.host/convert" + response = requests.get( + api_url, + params={ + "access_key": self.access_key, + "transaction_date": nowdate(), + "amount": 1, + "from": "USD", + "to": "INR", + }, + ) + # exchangerate.host return 200 for all requests. Can't rely on it to raise exception + if not response.json()["success"]: + frappe.throw( + title=_("Service Provider Error"), + msg=_("Currency exchange rate serivce provider: {0} returned Error. {1}").format( + frappe.bold(self.service_provider), response.json() + ), + exc=frappe.ValidationError, + ) + frappe.msgprint(msg=_("Success"), title=_("Access Key Validation")) + def on_update(self): frappe.clear_cache() diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 5c7a5632374..647c4015419 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -113,12 +113,30 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No if not value: import requests - api_url = f"https://api.frankfurter.app/{transaction_date}" - response = requests.get(api_url, params={"from": from_currency, "to": to_currency}) + if currency_settings.service_provider == "exchangerate.host": + api_url = "https://api.exchangerate.host/convert" + response = requests.get( + api_url, + params={ + "access_key": currency_settings.access_key, + "transaction_date": transaction_date, + "amount": 1, + "from": from_currency, + "to": to_currency, + }, + ) + # exchangerate.host return 200 for all requests. Can't rely on it to raise exception + value = response.json()["result"] + if not response.json()["success"]: + raise frappe.ValidationError + + else: + api_url = f"https://api.frankfurter.app/{transaction_date}" + response = requests.get(api_url, params={"from": from_currency, "to": to_currency}) + value = response.json()["rates"][to_currency] # expire in 6 hours response.raise_for_status() - value = response.json()["rates"][to_currency] cache.setex(name=key, time=21600, value=flt(value)) return flt(value) except Exception: From 0000c38563fc18bb4c28d0ebc6c02c1768568f22 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Oct 2023 14:47:51 +0000 Subject: [PATCH 26/28] chore(release): Bumped to Version 13.54.3 ## [13.54.3](https://github.com/frappe/erpnext/compare/v13.54.2...v13.54.3) (2023-10-10) ### Bug Fixes * change currency exchange API to api.frankfurter.app ([76919c4](https://github.com/frappe/erpnext/commit/76919c4af2c127b19339fef9968dc3954e095d4c)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 215e5e71759..6ca7b603667 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.54.1" +__version__ = "13.54.3" def get_default_company(user=None): From f6b35324effbcb5c369d1e81c76109e5dc281e62 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 19:53:10 +0530 Subject: [PATCH 27/28] fix: negative valuation rate in PR return (backport #37424) (backport #37462) (#37463) fix: negative valuation rate in PR return (backport #37424) (#37462) * fix: negative valuation rate in PR return (#37424) * fix: negative valuation rate in PR return * test: add test case for PR return (cherry picked from commit 26ad6885845962130cfb178fcbdbe2c4e75ce194) # Conflicts: # erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py * chore: `conflicts` --------- Co-authored-by: s-aga-r (cherry picked from commit 66ad82341752b40bb1cfb98e1552d071afafbaec) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/controllers/buying_controller.py | 20 +++++-- .../purchase_receipt/test_purchase_receipt.py | 54 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 18 ++++--- 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 56ecb45fdff..4156570ebb0 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -14,7 +14,8 @@ from erpnext.controllers.sales_and_purchase_return import get_rate_for_return from erpnext.controllers.stock_controller import StockController from erpnext.controllers.subcontracting import Subcontracting from erpnext.stock.get_item_details import get_conversion_factor -from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.stock_ledger import get_previous_sle +from erpnext.stock.utils import get_incoming_rate, get_valuation_method class QtyMismatchError(ValidationError): @@ -504,9 +505,20 @@ class BuyingController(StockController, Subcontracting): ) if self.is_return: - outgoing_rate = get_rate_for_return( - self.doctype, self.name, d.item_code, self.return_against, item_row=d - ) + if get_valuation_method(d.item_code) == "Moving Average": + previous_sle = get_previous_sle( + { + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + } + ) + outgoing_rate = flt(previous_sle.get("valuation_rate")) + else: + outgoing_rate = get_rate_for_return( + self.doctype, self.name, d.item_code, self.return_against, item_row=d + ) sle.update({"outgoing_rate": outgoing_rate, "recalculate_rate": 1}) if d.from_warehouse: diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 464c781f469..6d7be972fff 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1996,6 +1996,60 @@ class TestPurchaseReceipt(FrappeTestCase): ste7.reload() self.assertEqual(ste7.items[0].valuation_rate, valuation_rate) + def test_valuation_rate_in_return_purchase_receipt_for_moving_average(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + from erpnext.stock.stock_ledger import get_previous_sle + + # Step - 1: Create an Item (Valuation Method = Moving Average) + item_code = make_item(properties={"is_stock_item": 1, "valuation_method": "Moving Average"}).name + + # Step - 2: Create a Purchase Receipt (Qty = 10, Rate = 100) + pr = make_purchase_receipt(qty=10, rate=100, item_code=item_code) + + # Step - 3: Create a Material Receipt Stock Entry (Qty = 100, Basic Rate = 10) + warehouse = "_Test Warehouse - _TC" + make_stock_entry( + purpose="Material Receipt", + item_code=item_code, + to_warehouse=warehouse, + qty=100, + rate=10, + ) + + # Step - 4: Create a Material Issue Stock Entry (Qty = 100, Basic Rate = 18.18 [Auto Fetched]) + make_stock_entry( + purpose="Material Issue", item_code=item_code, from_warehouse=warehouse, qty=100 + ) + + # Step - 5: Create a Return Purchase Return (Qty = -8, Rate = 100 [Auto fetched]) + return_pr = make_purchase_receipt( + is_return=1, + return_against=pr.name, + item_code=item_code, + qty=-8, + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_no": return_pr.name, "voucher_detail_no": return_pr.items[0].name}, + ["posting_date", "posting_time", "outgoing_rate", "valuation_rate"], + as_dict=1, + ) + previous_sle_valuation_rate = get_previous_sle( + { + "item_code": item_code, + "warehouse": warehouse, + "posting_date": sle.posting_date, + "posting_time": sle.posting_time, + } + ).get("valuation_rate") + + # Test - 1: Valuation Rate should be equal to Outgoing Rate + self.assertEqual(flt(sle.outgoing_rate, 2), flt(sle.valuation_rate, 2)) + + # Test - 2: Valuation Rate should be equal to Previous SLE Valuation Rate + self.assertEqual(flt(sle.valuation_rate, 2), flt(previous_sle_valuation_rate, 2)) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 65e9cb7eaec..c8e75d5c43c 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -658,14 +658,16 @@ class update_entries_after(object): get_rate_for_return, # don't move this import to top ) - rate = get_rate_for_return( - sle.voucher_type, - sle.voucher_no, - sle.item_code, - voucher_detail_no=sle.voucher_detail_no, - sle=sle, - ) - + if self.valuation_method == "Moving Average": + rate = self.data[self.args.warehouse].previous_sle.valuation_rate + else: + rate = get_rate_for_return( + sle.voucher_type, + sle.voucher_no, + sle.item_code, + voucher_detail_no=sle.voucher_detail_no, + sle=sle, + ) elif ( sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and sle.voucher_detail_no From 35a62e2e8dd7e71d1753803f2e71dfb6e73c6e64 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 11 Oct 2023 14:24:45 +0000 Subject: [PATCH 28/28] chore(release): Bumped to Version 13.54.4 ## [13.54.4](https://github.com/frappe/erpnext/compare/v13.54.3...v13.54.4) (2023-10-11) ### Bug Fixes * negative valuation rate in PR return (backport [#37424](https://github.com/frappe/erpnext/issues/37424)) (backport [#37462](https://github.com/frappe/erpnext/issues/37462)) ([#37463](https://github.com/frappe/erpnext/issues/37463)) ([f6b3532](https://github.com/frappe/erpnext/commit/f6b35324effbcb5c369d1e81c76109e5dc281e62)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6ca7b603667..26a31058987 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import frappe from erpnext.hooks import regional_overrides -__version__ = "13.54.3" +__version__ = "13.54.4" def get_default_company(user=None):