From 976abf7b3c92cd6387275442f87c784d4adf16e1 Mon Sep 17 00:00:00 2001 From: Syed Mujeer Hashmi Date: Wed, 24 Jul 2024 11:59:45 +0530 Subject: [PATCH 01/41] fix: Update get_amount to return currency precision grand total In case of multi-currency purchase invoice, we are getting the error "Total Payment Request amount cannot be greater than Purchase Invoice amount" because of rounding difference. --- erpnext/accounts/doctype/payment_request/payment_request.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 03435a26011..74fd5f5301a 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -15,7 +15,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import ( ) from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate from erpnext.accounts.party import get_party_account, get_party_bank_account -from erpnext.accounts.utils import get_account_currency +from erpnext.accounts.utils import get_account_currency, get_currency_precision from erpnext.utilities import payment_app_import_guard @@ -540,7 +540,7 @@ def get_amount(ref_doc, payment_account=None): grand_total = ref_doc.outstanding_amount if grand_total > 0: - return grand_total + return flt(grand_total, get_currency_precision()) else: frappe.throw(_("Payment Entry is already created")) From 2203ea9301eea01714809811ec8f774ebbc59b1e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:20:50 +0530 Subject: [PATCH 02/41] fix: last purchase rate not updated from purchase invoice (backport #42847) (#42853) fix: last purchase rate not updated from purchase invoice (#42847) (cherry picked from commit 5b9309cf3410a4be0e94682ca6151bcc0b4ccb53) Co-authored-by: rohitwaghchaure --- erpnext/controllers/buying_controller.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a55eded2a4c..f797374d64e 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -689,9 +689,11 @@ class BuyingController(SubcontractingController): if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: self.process_fixed_asset() - if self.doctype in ["Purchase Order", "Purchase Receipt"] and not frappe.db.get_single_value( - "Buying Settings", "disable_last_purchase_rate" - ): + if self.doctype in [ + "Purchase Order", + "Purchase Receipt", + "Purchase Invoice", + ] and not frappe.db.get_single_value("Buying Settings", "disable_last_purchase_rate"): update_last_purchase_rate(self, is_submit=1) def on_cancel(self): From 72c16097d64384a4218e7646e2c4efcdc0790d82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:12:33 +0530 Subject: [PATCH 03/41] fix: incorrect Received Qty Amount in Purchase Order Analysis (backport #42852) (#42854) fix: incorrect Received Qty Amount in Purchase Order Analysis (#42852) (cherry picked from commit fb846ffa125cbc67f775a35376d0b541b1f5d9d1) Co-authored-by: rohitwaghchaure --- .../purchase_order_analysis/purchase_order_analysis.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index da1c70d3179..084c3b5fc2b 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -40,6 +40,7 @@ def get_data(filters): po = frappe.qb.DocType("Purchase Order") po_item = frappe.qb.DocType("Purchase Order Item") pi_item = frappe.qb.DocType("Purchase Invoice Item") + pr_item = frappe.qb.DocType("Purchase Receipt Item") query = ( frappe.qb.from_(po) @@ -47,6 +48,8 @@ def get_data(filters): .on(po_item.parent == po.name) .left_join(pi_item) .on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1)) + .left_join(pr_item) + .on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1)) .select( po.transaction_date.as_("date"), po_item.schedule_date.as_("required_date"), @@ -60,7 +63,7 @@ def get_data(filters): (po_item.qty - po_item.received_qty).as_("pending_qty"), Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"), po_item.base_amount.as_("amount"), - (po_item.received_qty * po_item.base_rate).as_("received_qty_amount"), + (pr_item.base_amount).as_("received_qty_amount"), (po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"), (po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_( "pending_amount" From 831e2aaf1840a2350249eefecb10ea29e7567908 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 21 Aug 2024 16:46:05 +0530 Subject: [PATCH 04/41] fix: custom stock entry type issue (backport #42835) (#42846) * fix: custom stock entry type issue (#42835) (cherry picked from commit 9c82c2b5d3bf790ba87a672d1b87e9efff8bc010) # Conflicts: # erpnext/stock/doctype/stock_entry_type/stock_entry_type.py * chore: fix conflicts * chore: fix linters issue --------- Co-authored-by: rohitwaghchaure --- erpnext/patches.txt | 1 + .../v15_0/set_standard_stock_entry_type.py | 16 ++++++++++ .../operations/install_fixtures.py | 32 ++++++++++++++++--- .../stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_entry_type/stock_entry_type.json | 12 +++++-- .../stock_entry_type/stock_entry_type.py | 17 +++++++++- .../stock_entry_type/test_stock_entry_type.py | 29 ++++++++++++++++- 7 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 erpnext/patches/v15_0/set_standard_stock_entry_type.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index bece96d64e8..31ba6b23c01 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -372,3 +372,4 @@ erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle +erpnext.patches.v15_0.set_standard_stock_entry_type diff --git a/erpnext/patches/v15_0/set_standard_stock_entry_type.py b/erpnext/patches/v15_0/set_standard_stock_entry_type.py new file mode 100644 index 00000000000..7551b0d4afc --- /dev/null +++ b/erpnext/patches/v15_0/set_standard_stock_entry_type.py @@ -0,0 +1,16 @@ +import frappe + + +def execute(): + for stock_entry_type in [ + "Material Issue", + "Material Receipt", + "Material Transfer", + "Material Transfer for Manufacture", + "Material Consumption for Manufacture", + "Manufacture", + "Repack", + "Send to Subcontractor", + ]: + if frappe.db.exists("Stock Entry Type", stock_entry_type): + frappe.db.set_value("Stock Entry Type", stock_entry_type, "is_standard", 1) diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 0bcb9fb6019..a99289416ba 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -66,29 +66,53 @@ def install(country=None): "parent_item_group": _("All Item Groups"), }, # Stock Entry Type - {"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"}, - {"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"}, + { + "doctype": "Stock Entry Type", + "name": "Material Issue", + "purpose": "Material Issue", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": "Material Receipt", + "purpose": "Material Receipt", + "is_standard": 1, + }, { "doctype": "Stock Entry Type", "name": "Material Transfer", "purpose": "Material Transfer", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": "Manufacture", + "purpose": "Manufacture", + "is_standard": 1, + }, + { + "doctype": "Stock Entry Type", + "name": "Repack", + "purpose": "Repack", + "is_standard": 1, }, - {"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"}, - {"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"}, { "doctype": "Stock Entry Type", "name": "Send to Subcontractor", "purpose": "Send to Subcontractor", + "is_standard": 1, }, { "doctype": "Stock Entry Type", "name": "Material Transfer for Manufacture", "purpose": "Material Transfer for Manufacture", + "is_standard": 1, }, { "doctype": "Stock Entry Type", "name": "Material Consumption for Manufacture", "purpose": "Material Consumption for Manufacture", + "is_standard": 1, }, # territory: with two default territories, one for home country and one named Rest of the World { diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 83ae8d09f66..6b4164dee5f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -983,7 +983,7 @@ class StockEntry(StockController): def set_stock_entry_type(self): if self.purpose: self.stock_entry_type = frappe.get_cached_value( - "Stock Entry Type", {"purpose": self.purpose}, "name" + "Stock Entry Type", {"purpose": self.purpose, "is_standard": 1}, "name" ) def set_purpose_for_stock_entry(self): diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json index db8e1fef69b..9b426a6c940 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json @@ -7,7 +7,8 @@ "engine": "InnoDB", "field_order": [ "purpose", - "add_to_transit" + "add_to_transit", + "is_standard" ], "fields": [ { @@ -26,10 +27,17 @@ "fieldname": "add_to_transit", "fieldtype": "Check", "label": "Add to Transit" + }, + { + "default": "0", + "fieldname": "is_standard", + "fieldtype": "Check", + "label": "Is Standard", + "read_only": 1 } ], "links": [], - "modified": "2024-07-08 08:41:19.385020", + "modified": "2024-08-20 15:35:45.696958", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Type", diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py index 034223122f6..efbdd680c69 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py @@ -2,7 +2,7 @@ # For license information, please see license.txt -# import frappe +import frappe from frappe.model.document import Document @@ -16,6 +16,7 @@ class StockEntryType(Document): from frappe.types import DF add_to_transit: DF.Check + is_standard: DF.Check purpose: DF.Literal[ "", "Material Issue", @@ -30,5 +31,19 @@ class StockEntryType(Document): # end: auto-generated types def validate(self): + self.validate_standard_type() if self.add_to_transit and self.purpose != "Material Transfer": self.add_to_transit = 0 + + def validate_standard_type(self): + if self.is_standard and self.name not in [ + "Material Issue", + "Material Receipt", + "Material Transfer", + "Material Transfer for Manufacture", + "Material Consumption for Manufacture", + "Manufacture", + "Repack", + "Send to Subcontractor", + ]: + frappe.throw(f"Stock Entry Type {self.name} cannot be set as standard") diff --git a/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py index 83ebe7e651b..61156545e34 100644 --- a/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/test_stock_entry_type.py @@ -3,6 +3,33 @@ import unittest +import frappe + class TestStockEntryType(unittest.TestCase): - pass + def test_stock_entry_type_non_standard(self): + stock_entry_type = "Test Manufacturing" + + doc = frappe.get_doc( + { + "doctype": "Stock Entry Type", + "__newname": stock_entry_type, + "purpose": "Manufacture", + "is_standard": 1, + } + ) + + self.assertRaises(frappe.ValidationError, doc.insert) + + def test_stock_entry_type_is_standard(self): + for stock_entry_type in [ + "Material Issue", + "Material Receipt", + "Material Transfer", + "Material Transfer for Manufacture", + "Material Consumption for Manufacture", + "Manufacture", + "Repack", + "Send to Subcontractor", + ]: + self.assertTrue(frappe.db.get_value("Stock Entry Type", stock_entry_type, "is_standard")) From fa8548266290e543e77b8255b3b98645c7f0ed05 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 22 Aug 2024 02:22:51 +0530 Subject: [PATCH 05/41] fix: calculation correction for annual depreciation (cherry picked from commit f440243b750ed61d1cbb0799b3f97116f2bb98ce) --- .../asset_depreciation_schedule.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index bad89e93259..679fbfe2e58 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -767,8 +767,12 @@ def get_daily_depr_amount(asset, row, schedule_idx, amount): every_year_depr = amount / total_years + depr_period_start_date = add_days( + get_last_day(add_months(row.depreciation_start_date, row.frequency_of_depreciation * -1)), 1 + ) + year_start_date = add_years( - row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12 + depr_period_start_date, ((row.frequency_of_depreciation * schedule_idx) // 12) ) year_end_date = add_days(add_years(year_start_date, 1), -1) From 7428df8778ac770085bda24bdca21bc1959e298a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 12 Aug 2024 17:45:11 +0530 Subject: [PATCH 06/41] fix: include erpnext in apps page (cherry picked from commit 1d52ef7afe34b229f33160d95b9acd463afb832f) --- erpnext/hooks.py | 10 ++++++++++ erpnext/public/images/erpnext-logo-blue.png | Bin 0 -> 4235 bytes 2 files changed, 10 insertions(+) create mode 100644 erpnext/public/images/erpnext-logo-blue.png diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 527be6ab337..ee0b33e27db 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,6 +10,16 @@ source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" +include_as_app = [ + { + "name": "erpnext", + "logo": "/assets/erpnext/images/erpnext-logo-blue.png", + "title": "ERPNext", + "route": "/app/home", + # "has_permission": "erpnext.api.permission.has_app_permission" + } +] + develop_version = "14.x.x-develop" app_include_js = "erpnext.bundle.js" diff --git a/erpnext/public/images/erpnext-logo-blue.png b/erpnext/public/images/erpnext-logo-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..0134b3f0627c5bc95aee1682f7979f9590751b4e GIT binary patch literal 4235 zcmds5X;f3!7Cs50f>0}zp$e$27MUc15RF0{7=$nd1B5|>BA^i&gvcO}I8Z<)6&lb8 zC}T*%Ok^f;K-4f<3?LW~kXV8cQw&I$Ug%q^|K5+jAMIQ3$60sXefC}F+xy#hf9ISe zCkJaq`K|H*07aV<7R~@bxNBqMda0$P;q<)pAs2GOJq!SS)Y^a)nUPDSMo5^m^)XQ1 zr75B{wM%d>6?~L%L4H24jYT3m`KPpuk(_ZUo>0vu~xS9s%57AWw}c()pk4k zYSb6xVQ!>Y*?~x~S$Wl162aQozeB zGStK zj_lvbn=%L38wLE(0sZfZ%G=!%s-E{5(j2F925xSq>`uILdaEqfbXjLE`D-EKrb&$! z+5puQ4Bt{V9;>XwezoUnY_$XG8C{dRg7Ij--dhs}!CJaBpBs~$<&HPrLmRZ)PDKXl z%3_WE&ez*j=tX}bF!Obb0p+yic;i;I0jIfc47E>o0JghaJCx``j?X$oYf;>)1s7_q z1C$GvX$^aWF?rnWCf$n;N1*SSuD$W;bnU7_vLUToP<`rs7^($$6?om8eQf0YvC#{? z%!t#-;Ro*oFgrc-bfgLD397bq^^yhie&U;(6$N#rW*~*eQ}B%-PhcP$YqL5=hl``e|@OIbut`3dqBNOr1G zZLHb`F%L^xuo6GmTX%M`k68jboX&UjCm-lSu&7$$s}tVqK*#Y0i?FWKIog7?ukn}RHHEUDXo;e z8+TOJC+gF4PtjrjbEX+IUnLubuw>4~ z-^oNytkcwtgAJ(EooOrKy4~I-Es2&GR%*4`cYu*LUVI~>Iw;aS7IP(nqN6Nu0BnKH z&<&!*m)0^=H@yxpigW5!a8}nQ^9K*@dl_T{7>^KhMh;&0VixK&tPScy!rH$FeU^0h z^(*y=6ghkI(ns%}D3VNi&y%lm8jdO7)0a}Ibr}l!I1bI4%7!xOtfir*7=8jE5llvn zK7Y-?3!|lgamssT($%;D;+D!!o7j?}BwQ+y!1;~Cx@-^i=?x#7J3u*>VqQ92{Za;O z^Y5_VX(grT{yKK?*73rk*FSMR_lfr^gUUOC*V_)nIGYqfD<`e)yCyi&iZQ^Hm)I-l z%%JJ0fM~01*Wg@?eJSWMeo`B~T!^=oB_;oCNALf`v;N$R+QG(A_>3$FDXta$Y<6{n zHCKxTU;~O*pg4GGoH}6d*nVpMF_%hK&eUIqvI&pIYoddDHnh=V& z?cEwGVoD7>51r6|-`5b)1OX}KVO2Q+Xy%tqV7%c?*K9k+o(5oP(LIHd=Hb>Q2!Or0 ze3*`#uTkS7H|YYq5ASSR%X&Yy0Px(ct!cS#*_S3<4`4|s4(pdPIf%~>V)x^oCAbhf z74TCHk9S1J7DM!0`*1v5rpPtWT_Q`;na(V6t>*L)Rdw<2{@w5&f%n%^6e!#AIC;ccW5tdAq?Rb3eGu)p_tob8$TuWH zz=GO8mgBz`#D7%?YcvilHt|LC5aX~f?Q1ORud%qBnoiV&85_nK()4pDaDRJw{q@k_ z%KPXWveLiuH$T2}e)YS`kbHSx32*z4-b>cN>-7IOi+^V85mcQsj+@tF zjD4N|`CjoSy#Lg%)~fKp3d&-!gcOfh$s^sRjXfxr0oe}PL7prB!}tp=n3{kcuH|wu zx49Mu#6( z@hj-EF&H3;axR=Rq-jc}0(QI4{W!fJ7hbIdQiNZ26^$*O!h6dCSR*RI(X&4m00lHo z<3xA9#Hv|}EW9jP<~(8aX@EGwo)94e)cN%Xb0zSPte(ctBAXaxzz*SZRfhYDaNDJd z=M_u%wnd$5&3)+k7{cBd*MLZTR339uyHy^%k9KKJTo%0gHrj;7tRX?(TZ!L?-J)F` zSz@d_T2Q1P{@VOA{G6Q+a1pC|z@OuHI9){FP$;hHz|f}@k*0`JSH{nT?%iAFr7U=2zLypY?LPIN`#V%;G@;SBWKYD=A~(kS#JRwu_wV)+iPxm+v=wgPT=W{ zY{9lDy(bhq^U}GN*QeG?Hw%p7;R$28;&lA#R~nBDu$B`}{O{V)-4t_L{5V}Ytt~R- za!%Y)=H_d>o35uQ0=;pYFbx7y$)1@1tKX_Cad_z@QXXKvaVAGw)L#Dn6StLEu4S?C z6w1DYxQQD(+Hqv1a4^D&buJgF0I*uSsS#&b_g=naE@a?GD4iov8h`UY?c9}nn(Ufp)bRgJ}2 z#Lh`Xaynkb7{0i2Jw}s!9indSKC{fK&xn`gB}dYI8TwsbJa z5apCSk&Wp-L=~ELcRzh2&)$JD&B?8cK~IMDoYa1C7g~!Y zr>xID^W~U_DLzsK39mp!r=I^=oF<5BS&pA?R1SPruA}N>)m8l3t#fJ=;ZWHB_CD3S zFFjB$P(_HyQ1>}AJ(FZbSlWxQw_E-7a9`xsmX~}h!Y!r24F78v3EFR~DZPg}-Rhcz z^cni3>VUqVfI+bimgx7Vtqe2==d~gQ!`mtS76hf1np8+%7S`dzDul`$3P%<1^_1Qn O02@mOi}GVW*Zu`KlJAZH literal 0 HcmV?d00001 From 4297895bd92baad6555f478403109c54b4689d00 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 12 Aug 2024 19:13:55 +0530 Subject: [PATCH 07/41] chore: renamed include_as_app to include_in_apps_screen (cherry picked from commit 528013242369d1dd6a32063e5011a91aff0550cd) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ee0b33e27db..448d0d2a57b 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -10,7 +10,7 @@ source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" -include_as_app = [ +add_to_apps_screen = [ { "name": "erpnext", "logo": "/assets/erpnext/images/erpnext-logo-blue.png", From 72ca2ec9a59732441d52a3940af29aa693c2cfdf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 11:16:41 +0530 Subject: [PATCH 08/41] refactor: allow equity types on Payment Entry (cherry picked from commit 6cbf98294ae9231843764cda9aa60b7bd15444f8) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index c28dcf525df..1f1bb0d50dd 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -35,6 +35,11 @@ frappe.ui.form.on("Payment Entry", { var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; + + if (frm.doc.party_type == "Shareholder") { + account_types.push("Equity"); + } + return { filters: { account_type: ["in", account_types], @@ -90,6 +95,9 @@ frappe.ui.form.on("Payment Entry", { var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; + if (frm.doc.party_type == "Shareholder") { + account_types.push("Equity"); + } return { filters: { account_type: ["in", account_types], From 49d995c3ac98734d1b60d9aac15f72f535dbe861 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 21 Aug 2024 14:19:30 +0530 Subject: [PATCH 09/41] refactor: filter shareholder on company (cherry picked from commit 63ad9f4f86f841ffbf7c2e8b31fc16adc694259f) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 1f1bb0d50dd..57107c7a453 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -420,6 +420,12 @@ frappe.ui.form.on("Payment Entry", { return { query: "erpnext.controllers.queries.employee_query", }; + } else if (frm.doc.party_type == "Shareholder") { + return { + filters: { + company: frm.doc.company, + }, + }; } }); From 758282739ed6d7ab4f60c4e1fa4aaf752400a681 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 22 Aug 2024 16:58:39 +0530 Subject: [PATCH 10/41] fix: call 'process' directly instead of creating 'process_subscripti reason: 'process' follows simple DB transaction model. (cherry picked from commit b4d22c29369ba92c0b59be56c8b275e6e05f778c) --- erpnext/accounts/doctype/subscription/subscription.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 916757a8d6d..d57f1de4379 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -737,10 +737,7 @@ class Subscription(Document): elif self.generate_invoice_at == "Days before the current subscription period": processing_date = add_days(self.current_invoice_start, -self.number_of_days) - process_subscription = frappe.new_doc("Process Subscription") - process_subscription.posting_date = processing_date - process_subscription.subscription = self.name - process_subscription.save().submit() + self.process(posting_date=processing_date) def is_prorate() -> int: From 7401dc4015fe15862faf59d0492e63a57cf13d73 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 20 Aug 2024 13:45:57 +0530 Subject: [PATCH 11/41] fix: do not copy date fields in opportunity doctype (cherry picked from commit 74afa57a9fd6ef1e49eba1e284d02528c5bf08bf) # Conflicts: # erpnext/crm/doctype/opportunity/opportunity.json --- erpnext/crm/doctype/opportunity/opportunity.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 07641d20c33..8ca7bcf5c7e 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -185,7 +185,8 @@ { "fieldname": "expected_closing", "fieldtype": "Date", - "label": "Expected Closing Date" + "label": "Expected Closing Date", + "no_copy": 1 }, { "fieldname": "section_break_14", @@ -357,6 +358,7 @@ "fieldname": "transaction_date", "fieldtype": "Date", "label": "Opportunity Date", + "no_copy": 1, "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, @@ -388,6 +390,7 @@ "fieldname": "first_response_time", "fieldtype": "Duration", "label": "First Response Time", + "no_copy": 1, "read_only": 1 }, { @@ -622,7 +625,11 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], +<<<<<<< HEAD "modified": "2022-10-13 12:42:21.545636", +======= + "modified": "2024-08-20 04:12:29.095761", +>>>>>>> 74afa57a9f (fix: do not copy date fields in opportunity doctype) "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From d9ca680a29eea5a4c8b90fd66309df346c248359 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:22:23 +0530 Subject: [PATCH 12/41] fix: incorrect in and out qty in the Batch-Wise Balance History (backport #42866) (#42876) fix: incorrect in and out qty in the Batch-Wise Balance History (#42866) (cherry picked from commit ce7f6ee71c29d02599195e2015c1ef0815db5306) Co-authored-by: rohitwaghchaure --- .../batch_wise_balance_history/batch_wise_balance_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 822da13cc72..0bb9d40581a 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -168,7 +168,7 @@ def get_stock_ledger_entries_for_batch_bundle(filters): & (sle.has_batch_no == 1) & (sle.posting_date <= filters["to_date"]) ) - .groupby(batch_package.batch_no, batch_package.warehouse) + .groupby(sle.voucher_no, batch_package.batch_no, batch_package.warehouse) .orderby(sle.item_code, sle.warehouse) ) From 08bed618f611560635b17fc5d77b6934b31b7551 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:22:39 +0530 Subject: [PATCH 13/41] fix: not able to make stock entry against MR (backport #42874) (#42875) fix: not able to make stock entry against MR (#42874) (cherry picked from commit 63ca1025bcba469bb300f299fcbf31e09667526e) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 117ed261439..7bf3ca4d728 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -267,6 +267,7 @@ class MaterialRequest(BuyingController): mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance") for d in self.get("items"): + precision = d.precision("ordered_qty") if d.name in mr_items: if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"): d.ordered_qty = flt(mr_items_ordered_qty.get(d.name)) @@ -276,14 +277,14 @@ class MaterialRequest(BuyingController): (d.qty + (d.qty * (mr_qty_allowance / 100))), d.precision("ordered_qty") ) - if d.ordered_qty and d.ordered_qty > allowed_qty: + if d.ordered_qty and flt(d.ordered_qty, precision) > flt(allowed_qty, precision): frappe.throw( _( "The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than allowed requested quantity {2} for Item {3}" ).format(d.ordered_qty, d.parent, allowed_qty, d.item_code) ) - elif d.ordered_qty and d.ordered_qty > d.stock_qty: + elif d.ordered_qty and flt(d.ordered_qty, precision) > flt(d.stock_qty, precision): frappe.throw( _( "The total Issue / Transfer quantity {0} in Material Request {1} cannot be greater than requested quantity {2} for Item {3}" From 3a149b3c9b07ea276ad6d7c30764cbde16e81202 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Sat, 24 Aug 2024 12:38:49 +0530 Subject: [PATCH 14/41] chore: resolve conflicts --- erpnext/crm/doctype/opportunity/opportunity.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 8ca7bcf5c7e..75373398b09 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -625,11 +625,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], -<<<<<<< HEAD - "modified": "2022-10-13 12:42:21.545636", -======= "modified": "2024-08-20 04:12:29.095761", ->>>>>>> 74afa57a9f (fix: do not copy date fields in opportunity doctype) "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From ff868a9290f272b3dc6a47c5b9f890d392cc8c82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:25:18 +0530 Subject: [PATCH 15/41] fix: LCV based on purchase invoice amount with multi-currency (backport #42890) (#42894) fix: LCV based on purchase invoice amount with multi-currency (#42890) (cherry picked from commit 6721ae76dea1bebd328192a9c14325d904c8986b) Co-authored-by: rohitwaghchaure --- .../purchase_invoice/test_purchase_invoice.py | 56 +++++++++++++++++++ .../purchase_receipt/purchase_receipt.py | 1 + 2 files changed, 57 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index eb7c486f2e1..31143fb72b8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -2236,6 +2236,62 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertEqual(pi_expected_values[i][1], gle.debit) self.assertEqual(pi_expected_values[i][2], gle.credit) + def test_adjust_incoming_rate_from_pi_with_multi_currency(self): + from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import ( + make_landed_cost_voucher, + ) + + frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0) + + frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1) + + # Increase the cost of the item + + pr = make_purchase_receipt( + qty=10, rate=1, currency="USD", do_not_save=1, supplier="_Test Supplier USD" + ) + pr.conversion_rate = 6300 + pr.plc_conversion_rate = 1 + pr.save() + pr.submit() + + self.assertEqual(pr.conversion_rate, 6300) + self.assertEqual(pr.plc_conversion_rate, 1) + self.assertEqual(pr.base_grand_total, 6300 * 10) + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr.name}, + "stock_value_difference", + ) + self.assertEqual(stock_value_difference, 6300 * 10) + + make_landed_cost_voucher( + company=pr.company, + receipt_document_type="Purchase Receipt", + receipt_document=pr.name, + charges=3000, + distribute_charges_based_on="Qty", + ) + + pi = create_purchase_invoice_from_receipt(pr.name) + for row in pi.items: + row.rate = 1.1 + + pi.save() + pi.submit() + + stock_value_difference = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr.name}, + "stock_value_difference", + ) + self.assertEqual(stock_value_difference, 7230 * 10) + + frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0) + + frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 3c97d13b904..afcb00141a1 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1075,6 +1075,7 @@ def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate if item.billed_amt and item.amount: adjusted_amt = flt(item.billed_amt) - flt(item.amount) + adjusted_amt = adjusted_amt * flt(pr_doc.conversion_rate) item.db_set("rate_difference_with_purchase_invoice", adjusted_amt, update_modified=False) percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6) From a3e5ffe915b78887a639827fbede35fadcdcdd94 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:05:19 +0530 Subject: [PATCH 16/41] refactor: better err msg on clearance tool (cherry picked from commit 092411b54ff21d5bed74eb896b8d6bf7bc0d0ad9) --- .../accounts/doctype/bank_clearance/bank_clearance.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py index 63758a5e7fb..85713c6c9a6 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py @@ -6,7 +6,7 @@ import frappe from frappe import _, msgprint from frappe.model.document import Document from frappe.query_builder.custom import ConstantColumn -from frappe.utils import flt, fmt_money, getdate +from frappe.utils import flt, fmt_money, get_link_to_form, getdate from pypika import Order import erpnext @@ -96,8 +96,11 @@ class BankClearance(Document): if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): frappe.throw( - _("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format( - d.idx, d.clearance_date, d.cheque_date + _("Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3}").format( + d.idx, + get_link_to_form(d.payment_document, d.payment_entry), + d.clearance_date, + d.cheque_date, ) ) From 25193c5e92f7423668c696bd5996150299f58288 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:20:21 +0530 Subject: [PATCH 17/41] feat: report to identify incorrectly cleared cheques (cherry picked from commit 28890fa833b31a40fd4f11affef4a15274335ebb) --- .../__init__.py | 0 ...heques_and_deposits_incorrectly_cleared.js | 6 ++++ ...ques_and_deposits_incorrectly_cleared.json | 29 +++++++++++++++++++ ...heques_and_deposits_incorrectly_cleared.py | 9 ++++++ 4 files changed, 44 insertions(+) create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json create mode 100644 erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js new file mode 100644 index 00000000000..f28714d70fa --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -0,0 +1,6 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { + filters: [], +}; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json new file mode 100644 index 00000000000..50cf765ca3d --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2024-07-30 17:20:07.570971", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "letterhead": null, + "modified": "2024-07-30 17:20:07.570971", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Cheques and Deposits Incorrectly cleared", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Payment Entry", + "report_name": "Cheques and Deposits Incorrectly cleared", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py new file mode 100644 index 00000000000..9a1d41262fc --- /dev/null +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe + + +def execute(filters=None): + columns, data = [], [] + return columns, data From 80a5df0e960fe1a01e1ad7b4d7e59f47bf9b0b1b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:24:46 +0530 Subject: [PATCH 18/41] refactor: barebones functions (cherry picked from commit ceaa1be72935e8bd46921f465659d04edb164482) --- ...heques_and_deposits_incorrectly_cleared.js | 45 ++++++++++++++++++- ...heques_and_deposits_incorrectly_cleared.py | 37 ++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js index f28714d70fa..f2554c4442c 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -2,5 +2,48 @@ // For license information, please see license.txt frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { - filters: [], + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), + }, + { + fieldname: "account", + label: __("Bank Account"), + fieldtype: "Link", + options: "Account", + default: frappe.defaults.get_user_default("Company") + ? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"] + : "", + reqd: 1, + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); + return { + query: "erpnext.controllers.queries.get_account_list", + filters: [ + ["Account", "account_type", "in", "Bank, Cash"], + ["Account", "is_group", "=", 0], + ["Account", "disabled", "=", 0], + ["Account", "company", "=", company], + ], + }; + }, + }, + { + fieldname: "report_date", + label: __("Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + fieldname: "include_pos_transactions", + label: __("Include POS Transactions"), + fieldtype: "Check", + }, + ], }; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 9a1d41262fc..774c843b90a 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -1,9 +1,44 @@ # Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -# import frappe +import frappe +from frappe import _ def execute(filters=None): columns, data = [], [] return columns, data + + +def get_columns(): + return [ + {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, + { + "fieldname": "payment_document", + "label": _("Payment Document Type"), + "fieldtype": "Data", + "width": 220, + }, + { + "fieldname": "payment_entry", + "label": _("Payment Document"), + "fieldtype": "Dynamic Link", + "options": "payment_document", + "width": 220, + }, + { + "fieldname": "debit", + "label": _("Debit"), + "fieldtype": "Currency", + "options": "account_currency", + "width": 120, + }, + { + "fieldname": "credit", + "label": _("Credit"), + "fieldtype": "Currency", + "options": "account_currency", + "width": 120, + }, + {"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110}, + ] From 5a28a1728ed4531e6ab4bf818ec8a3005ec47d6f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jul 2024 17:52:52 +0530 Subject: [PATCH 19/41] refactor: working state with minimum functions (cherry picked from commit 4cd023444acc8b951d24c1a04ec8bbb120c4aeb7) --- ...heques_and_deposits_incorrectly_cleared.py | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 774c843b90a..14295a5a711 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -2,14 +2,80 @@ # For license information, please see license.txt import frappe -from frappe import _ +from frappe import _, qb +from frappe.query_builder import CustomFunction def execute(filters=None): - columns, data = [], [] + columns = get_columns() + data = build_data(filters) return columns, data +def build_data(filters): + vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) + data = [] + for x in vouchers: + data.append( + frappe._dict( + payment_document="Payment Entry", + payment_entry=x.name, + debit=x.amount, + credit=0, + posting_date=x.posting_date, + clearance_date=x.clearance_date, + ) + ) + return data + + +def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters): + je = qb.DocType("Journal Entry") + jea = qb.DocType("Journal Entry Account") + + journals = ( + qb.from_(je) + .inner_join(jea) + .on(je.name == jea.parent) + .select( + je.name, + jea.debit_in_account_currency, + jea.credit_in_account_currency, + je.posting_date, + je.clearance_date, + ) + .where( + je.docstatus.eq(1) + & jea.account.eq(filters.account) + & je.posting_date.gt(filters.report_date) + & je.clearance_date.lte(filters.report_date) + & (je.is_opening.isnull() | je.is_opening.eq("No")) + ) + .run(as_dict=1) + ) + + ifelse = CustomFunction("IF", ["condition", "then", "else"]) + pe = qb.DocType("Payment Entry") + payments = ( + qb.from_(pe) + .select( + pe.name, + ifelse(pe.paid_from.eq(filters.account), pe.paid_amount, pe.received_amount).as_("amount"), + pe.posting_date, + pe.clearance_date, + ) + .where( + pe.docstatus.eq(1) + & (pe.paid_from.eq(filters.account) | pe.paid_to.eq(filters.account)) + & pe.posting_date.gt(filters.report_date) + & pe.clearance_date.lte(filters.report_date) + ) + .run(as_dict=1) + ) + + return journals + payments + + def get_columns(): return [ {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, @@ -40,5 +106,6 @@ def get_columns(): "options": "account_currency", "width": 120, }, + {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 110}, {"fieldname": "clearance_date", "label": _("Clearance Date"), "fieldtype": "Date", "width": 110}, ] From 993114942e65110cca75425aa84e0090a1821adf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 31 Jul 2024 17:57:25 +0530 Subject: [PATCH 20/41] refactor: build dict for payment entry (cherry picked from commit 784dec24c8c521461f232658aca8c58c82c39ff8) --- ...heques_and_deposits_incorrectly_cleared.py | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 14295a5a711..995ce4efd0b 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -4,6 +4,7 @@ import frappe from frappe import _, qb from frappe.query_builder import CustomFunction +from frappe.query_builder.custom import ConstantColumn def execute(filters=None): @@ -12,20 +13,39 @@ def execute(filters=None): return columns, data +def build_payment_entry_dict(row: dict) -> dict: + row_dict = frappe._dict() + row_dict.update( + { + "payment_document": row.get("doctype"), + "payment_entry": row.get("name"), + "posting_date": row.get("posting_date"), + "clearance_date": row.get("clearance_date"), + } + ) + if row.get("payment_type") == "Receive" and row.get("party_type") in ["Customer", "Supplier"]: + row_dict.update( + { + "debit": row.get("amount"), + "credit": 0, + } + ) + else: + row_dict.update( + { + "debit": 0, + "credit": row.get("amount"), + } + ) + return row_dict + + def build_data(filters): vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) data = [] for x in vouchers: - data.append( - frappe._dict( - payment_document="Payment Entry", - payment_entry=x.name, - debit=x.amount, - credit=0, - posting_date=x.posting_date, - clearance_date=x.clearance_date, - ) - ) + if x.doctype == "Payment Entry": + data.append(build_payment_entry_dict(x)) return data @@ -56,11 +76,15 @@ def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filter ifelse = CustomFunction("IF", ["condition", "then", "else"]) pe = qb.DocType("Payment Entry") + doctype_name = ConstantColumn("Payment Entry") payments = ( qb.from_(pe) .select( + doctype_name.as_("doctype"), pe.name, ifelse(pe.paid_from.eq(filters.account), pe.paid_amount, pe.received_amount).as_("amount"), + pe.payment_type, + pe.party_type, pe.posting_date, pe.clearance_date, ) From 42382b39454483a80edccafe4dbbe02a6649317e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:16:32 +0530 Subject: [PATCH 21/41] chore: remove redundant column (cherry picked from commit 74b36db24e54e8e0a881259f51e79270a7057be2) --- .../cheques_and_deposits_incorrectly_cleared.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 995ce4efd0b..6bb090fd1b7 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -102,7 +102,6 @@ def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filter def get_columns(): return [ - {"fieldname": "posting_date", "label": _("Posting Date"), "fieldtype": "Date", "width": 90}, { "fieldname": "payment_document", "label": _("Payment Document Type"), From ecb0506dba19b503af5e82e3ab87ffa1ca7b4c70 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Aug 2024 14:29:47 +0530 Subject: [PATCH 22/41] refactor: build dictionary for Journal remove redundant filter (cherry picked from commit 2144e0337d8194e466c315b8585872f8b21b7a44) --- ...heques_and_deposits_incorrectly_cleared.js | 5 ----- ...heques_and_deposits_incorrectly_cleared.py | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js index f2554c4442c..e83fc6f5b57 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.js @@ -40,10 +40,5 @@ frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = { default: frappe.datetime.get_today(), reqd: 1, }, - { - fieldname: "include_pos_transactions", - label: __("Include POS Transactions"), - fieldtype: "Check", - }, ], }; diff --git a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py index 6bb090fd1b7..891dc2c4bb1 100644 --- a/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py +++ b/erpnext/accounts/report/cheques_and_deposits_incorrectly_cleared/cheques_and_deposits_incorrectly_cleared.py @@ -40,24 +40,43 @@ def build_payment_entry_dict(row: dict) -> dict: return row_dict +def build_journal_entry_dict(row: dict) -> dict: + row_dict = frappe._dict() + row_dict.update( + { + "payment_document": row.get("doctype"), + "payment_entry": row.get("name"), + "posting_date": row.get("posting_date"), + "clearance_date": row.get("clearance_date"), + "debit": row.get("debit_in_account_currency"), + "credit": row.get("credit_in_account_currency"), + } + ) + return row_dict + + def build_data(filters): vouchers = get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters) data = [] for x in vouchers: if x.doctype == "Payment Entry": data.append(build_payment_entry_dict(x)) + elif x.doctype == "Journal Entry": + data.append(build_journal_entry_dict(x)) return data def get_amounts_not_reflected_in_system_for_bank_reconciliation_statement(filters): je = qb.DocType("Journal Entry") jea = qb.DocType("Journal Entry Account") + doctype_name = ConstantColumn("Journal Entry") journals = ( qb.from_(je) .inner_join(jea) .on(je.name == jea.parent) .select( + doctype_name.as_("doctype"), je.name, jea.debit_in_account_currency, jea.credit_in_account_currency, From caa6ca1d0b300e30f08bd431186e31161f8ef0a2 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 22 Aug 2024 11:50:19 +0530 Subject: [PATCH 23/41] fix: update dimesions in exchange_gain_loss jv based on base document (cherry picked from commit 96df19149de806cea9798420640f9088f55925bc) --- erpnext/controllers/accounts_controller.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 58e120e14b0..ce5d813b801 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1334,6 +1334,12 @@ class AccountsController(TransactionBase): # Cancelling existing exchange gain/loss journals is handled during the `on_cancel` event. # see accounts/utils.py:cancel_exchange_gain_loss_journal() if self.docstatus == 1: + if dimensions_dict is None: + dimensions_dict = frappe._dict() + active_dimensions = get_dimensions()[0] + for dim in active_dimensions: + dimensions_dict[dim.fieldname] = self.get(dim.fieldname) + if self.get("doctype") == "Journal Entry": # 'args' is populated with exchange gain/loss account and the amount to be booked. # These are generated by Sales/Purchase Invoice during reconciliation and advance allocation. From 8c350d43b26fe195824eedd73092bc9345668e3f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:35:20 +0530 Subject: [PATCH 24/41] fix: Column 'valuation_rate' cannot be null (backport #42909) (#42913) fix: Column 'valuation_rate' cannot be null (#42909) (cherry picked from commit 92bde71ab13f90aef0d51672f1dcbbe079da6b9a) Co-authored-by: rohitwaghchaure --- erpnext/stock/serial_batch_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 71e664dd066..46724be5927 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -183,7 +183,7 @@ class SerialBatchBundle: } if self.sle.actual_qty < 0 and self.is_material_transfer(): - values_to_update["valuation_rate"] = sn_doc.avg_rate + values_to_update["valuation_rate"] = flt(sn_doc.avg_rate) if not frappe.db.get_single_value( "Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle" From 8d29dc6a81e95703b1215b2278a7186f565eabe9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:18:31 +0530 Subject: [PATCH 25/41] fix: unsupported operand type(s) for *: 'float' and 'NoneType' (backport #42916) (#42918) fix: unsupported operand type(s) for *: 'float' and 'NoneType' (#42916) (cherry picked from commit 10434742e965d4003bb42f1e2335b8785ba3667f) Co-authored-by: rohitwaghchaure --- erpnext/accounts/doctype/pricing_rule/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 9c7911d7cae..b848fde08d8 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -486,7 +486,7 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules, row_item): continue stock_qty = row.get("qty") * (row.get("conversion_factor") or 1.0) - amount = stock_qty * (row.get("price_list_rate") or row.get("rate")) + amount = stock_qty * (flt(row.get("price_list_rate")) or flt(row.get("rate"))) pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, row) if pricing_rules and pricing_rules[0]: From 26248924b651d466fc9cf7ffe071787d65159af0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 09:30:47 +0530 Subject: [PATCH 26/41] fix: same posting date and time, creation causing incorrect balance qty (backport #42904) (#42920) fix: same posting date and time, creation causing incorrect balance qty (#42904) fix: same posting date and time, creation causing incorrect balance quantity (cherry picked from commit 27364b7e6b0b03aa0303e5b180cd867bbb312232) Co-authored-by: rohitwaghchaure --- .../purchase_receipt/test_purchase_receipt.py | 67 ++++++++++++++++++- .../test_stock_ledger_entry.py | 4 +- erpnext/stock/stock_ledger.py | 12 ++-- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c239360d945..4cb53e753e7 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -3,7 +3,7 @@ import frappe from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import add_days, cint, cstr, flt, getdate, nowtime, today +from frappe.utils import add_days, cint, cstr, flt, get_datetime, getdate, nowtime, today from pypika import functions as fn import erpnext @@ -3592,6 +3592,71 @@ class TestPurchaseReceipt(FrappeTestCase): inter_transfer_dn.cancel() frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1) + def test_sles_with_same_posting_datetime_and_creation(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + from erpnext.stock.report.stock_balance.stock_balance import execute + + item_code = "Test Item for SLE with same posting datetime and creation" + create_item(item_code) + + pr = make_purchase_receipt( + item_code=item_code, + qty=10, + rate=100, + posting_date="2023-11-06", + posting_time="00:00:00", + ) + + sr = make_stock_entry( + item_code=item_code, + source=pr.items[0].warehouse, + qty=10, + posting_date="2023-11-07", + posting_time="14:28:0.330404", + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code}, + "name", + ) + + sle_doc = frappe.get_doc("Stock Ledger Entry", sle) + sle_doc.db_set("creation", "2023-11-07 14:28:01.208930") + + sle_doc.reload() + self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.208930")) + + sr = make_stock_entry( + item_code=item_code, + target=pr.items[0].warehouse, + qty=50, + posting_date="2023-11-07", + posting_time="14:28:0.920825", + ) + + sle = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": sr.doctype, "voucher_no": sr.name, "item_code": sr.items[0].item_code}, + "name", + ) + + sle_doc = frappe.get_doc("Stock Ledger Entry", sle) + sle_doc.db_set("creation", "2023-11-07 14:28:01.044561") + + sle_doc.reload() + self.assertEqual(get_datetime(sle_doc.creation), get_datetime("2023-11-07 14:28:01.044561")) + + pr.repost_future_sle_and_gle(force=True) + + columns, data = execute( + filters=frappe._dict( + {"item_code": item_code, "warehouse": pr.items[0].warehouse, "company": pr.company} + ) + ) + + self.assertEqual(data[0].get("bal_qty"), 50.0) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 587b25ea154..42e402e0005 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1187,7 +1187,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): qty=5, posting_date="2021-01-01", rate=10, - posting_time="02:00:00.1234", + posting_time="02:00:00", ) time.sleep(3) @@ -1199,7 +1199,7 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin): qty=100, rate=10, posting_date="2021-01-01", - posting_time="02:00:00", + posting_time="02:00:00.1234", ) sle = frappe.get_all( diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 0961d9bd35e..2aacd86e3d2 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1543,7 +1543,7 @@ def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_vouc and ( posting_datetime {operator} %(posting_datetime)s ) - order by posting_datetime desc, creation desc + order by posting_date desc, posting_time desc, creation desc limit 1 for update""", { @@ -1636,7 +1636,7 @@ def get_stock_ledger_entries( where item_code = %(item_code)s and is_cancelled = 0 {conditions} - order by posting_datetime {order}, creation {order} + order by posting_date {order}, posting_time {order}, creation {order} {limit} {for_update}""".format( conditions=conditions, limit=limit or "", @@ -1753,7 +1753,7 @@ def get_valuation_rate( AND valuation_rate >= 0 AND is_cancelled = 0 AND NOT (voucher_no = %s AND voucher_type = %s) - order by posting_datetime desc, name desc limit 1""", + order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type), ): return flt(last_valuation_rate[0][0]) @@ -2004,7 +2004,7 @@ def get_future_sle_with_negative_qty(args): and posting_datetime >= %(posting_datetime)s and is_cancelled = 0 and qty_after_transaction < 0 - order by posting_datetime asc + order by posting_date asc, posting_time asc limit 1 """, args, @@ -2018,14 +2018,14 @@ def get_future_sle_with_negative_batch_qty(args): with batch_ledger as ( select posting_date, posting_time, posting_datetime, voucher_type, voucher_no, - sum(actual_qty) over (order by posting_datetime, creation) as cumulative_total + sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total from `tabStock Ledger Entry` where item_code = %(item_code)s and warehouse = %(warehouse)s and batch_no=%(batch_no)s and is_cancelled = 0 - order by posting_datetime, creation + order by posting_date, posting_time, creation ) select * from batch_ledger where From ec26c92263ba2c7acdd3941b2569f59af6a6bed9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:03:45 +0530 Subject: [PATCH 27/41] fix: timeout while submitting stock entry (backport #42929) (#42931) fix: timeout while submitting stock entry (#42929) (cherry picked from commit ca2fde891ec32b162f6b6c33d9ff10b60e4ca57c) Co-authored-by: rohitwaghchaure --- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.json | 5 +++-- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index 58b6e4a74b6..d9af64c3983 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -352,7 +352,8 @@ { "fieldname": "posting_datetime", "fieldtype": "Datetime", - "label": "Posting Datetime" + "label": "Posting Datetime", + "search_index": 1 } ], "hide_toolbar": 1, @@ -361,7 +362,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-06-27 16:23:18.820049", + "modified": "2024-08-27 09:28:03.961443", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index cff1886d7fe..319303dbbb0 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -351,3 +351,4 @@ def on_doctype_update(): frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"]) frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"]) frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse") + frappe.db.add_index("Stock Ledger Entry", ["posting_datetime", "creation"]) From 0650c22b53cfd801970f88aa3b0230869ace8a00 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 26 Aug 2024 19:30:30 +0530 Subject: [PATCH 28/41] fix: make party naming sequential when naming_by set as auto name (cherry picked from commit c9015f7c041a5a0af64972991cc0dfc48fe8cb20) --- erpnext/buying/doctype/supplier/supplier.py | 2 +- erpnext/selling/doctype/customer/customer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index bccab8b01e0..3b72953c563 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -97,7 +97,7 @@ class Supplier(TransactionBase): elif supp_master_name == "Naming Series": set_name_by_naming_series(self) else: - self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) + set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) def on_update(self): self.create_primary_contact() diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 9bd9b5e760a..0e9c1f3e790 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -105,7 +105,7 @@ class Customer(TransactionBase): elif cust_master_name == "Naming Series": set_name_by_naming_series(self) else: - self.name = set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) + set_name_from_naming_options(frappe.get_meta(self.doctype).autoname, self) def get_customer_name(self): if frappe.db.get_value("Customer", self.customer_name) and not frappe.flags.in_import: From 5bdd2989c67293ed0e5d786637f4373ef7bb7c97 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:02:01 +0530 Subject: [PATCH 29/41] feat: added finance book filter in depreciation and balances report (cherry picked from commit 45804c68f0d52281b233f91279abdc86644bac10) --- .../asset_depreciations_and_balances.js | 6 ++++++ .../asset_depreciations_and_balances.py | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js index 49771d7ebe9..0f74df6f909 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.js @@ -46,5 +46,11 @@ frappe.query_reports["Asset Depreciations and Balances"] = { options: "Asset", depends_on: "eval: doc.group_by == 'Asset'", }, + { + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", + }, ], }; 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 f9a008ade7f..4a6318382cc 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 @@ -69,6 +69,9 @@ def get_asset_categories_for_grouped_by_category(filters): condition = "" if filters.get("asset_category"): condition += " and asset_category = %(asset_category)s" + if filters.get("finance_book"): + condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)" + # nosemgrep return frappe.db.sql( f""" @@ -119,6 +122,7 @@ def get_asset_categories_for_grouped_by_category(filters): "from_date": filters.from_date, "company": filters.company, "asset_category": filters.get("asset_category"), + "finance_book": filters.get("finance_book"), }, as_dict=1, ) @@ -128,6 +132,8 @@ def get_asset_details_for_grouped_by_category(filters): condition = "" if filters.get("asset"): condition += " and name = %(asset)s" + if filters.get("finance_book"): + condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)" return frappe.db.sql( f""" SELECT name, @@ -176,6 +182,7 @@ def get_asset_details_for_grouped_by_category(filters): "from_date": filters.from_date, "company": filters.company, "asset": filters.get("asset"), + "finance_book": filters.get("finance_book"), }, as_dict=1, ) From 9fc0ac1a926a7977cc840b1116a7e007f93e42be Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 27 Aug 2024 15:27:37 +0530 Subject: [PATCH 30/41] chore: resolved linter warnings with #nosemgrep (cherry picked from commit adf1e487e1ecb9ae1e85cb08654eeb105f982ac8) --- .../asset_depreciations_and_balances.py | 2 ++ 1 file changed, 2 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 4a6318382cc..34cced2ca17 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 @@ -134,6 +134,8 @@ def get_asset_details_for_grouped_by_category(filters): condition += " and name = %(asset)s" if filters.get("finance_book"): condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)" + + # nosemgrep return frappe.db.sql( f""" SELECT name, From c54e97b89a09f6cecd7380cccca71ece07fed5f6 Mon Sep 17 00:00:00 2001 From: venkat102 Date: Mon, 26 Aug 2024 20:04:51 +0530 Subject: [PATCH 31/41] fix: get amount with taxes and charges from payment entry (cherry picked from commit b3a901b631e800f52554214261a59608b2fe8a4e) --- .../bank_reconciliation_statement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py index 8a8e3a59972..c7dba16492e 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py @@ -154,8 +154,8 @@ def get_payment_entries(filters): select "Payment Entry" as payment_document, name as payment_entry, reference_no, reference_date as ref_date, - if(paid_to=%(account)s, received_amount, 0) as debit, - if(paid_from=%(account)s, paid_amount, 0) as credit, + if(paid_to=%(account)s, received_amount_after_tax, 0) as debit, + if(paid_from=%(account)s, paid_amount_after_tax, 0) as credit, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency from `tabPayment Entry` From 80244bafa44bdd2198788453bd9bae0973dc16ec Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 27 Aug 2024 14:40:12 +0530 Subject: [PATCH 32/41] fix: use of incorrect attribute (cherry picked from commit fb32d2cafb1757216ec3f7db5f3e745ea5b15906) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 454101027e7..13baff9e7a8 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1740,7 +1740,7 @@ def get_outstanding_reference_documents(args, validate=False): d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no") # Get negative outstanding sales /purchase invoices - if args.get("party_type") != "Employee" and not args.get("voucher_no"): + if args.get("party_type") != "Employee": negative_outstanding_invoices = get_negative_outstanding_invoices( args.get("party_type"), args.get("party"), From 3d469db47bafbea009ab011787b38758a981c000 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:31:52 +0530 Subject: [PATCH 33/41] refactor: item-wise purchase history (query to script report) (cherry picked from commit 5de91cf55e768abfe419e6e8b6f83fa381769cc1) --- .../item_wise_purchase_history.js | 69 +++++ .../item_wise_purchase_history.json | 45 +-- .../item_wise_purchase_history.py | 292 ++++++++++++++++++ 3 files changed, 385 insertions(+), 21 deletions(-) create mode 100644 erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js create mode 100644 erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js new file mode 100644 index 00000000000..f4f311cc1dd --- /dev/null +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -0,0 +1,69 @@ +// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Item-wise Purchase History"] = { + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "from_date", + reqd: 1, + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months( + frappe.datetime.get_today(), + -1, + ), + }, + { + fieldname: "to_date", + reqd: 1, + default: frappe.datetime.get_today(), + label: __("To Date"), + fieldtype: "Date", + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: () => { + return { + query: "erpnext.controllers.queries.item_query", + }; + }, + }, + { + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", + }, + ], + + formatter: function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["received_qty", "billed_amt"]; + + if ( + in_list(format_fields, column.fieldname) && + data && + data[column.fieldname] > 0 + ) { + value = "" + value + ""; + } + return value; + }, +}; diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json index 521c68c5329..e6c3c624e01 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json @@ -1,30 +1,33 @@ { - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2013-05-03 14:55:53", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:08:57.446613", - "modified_by": "Administrator", - "module": "Buying", - "name": "Item-wise Purchase History", - "owner": "Administrator", - "query": "select\n po_item.item_code as \"Item Code:Link/Item:120\",\n\tpo_item.item_name as \"Item Name::120\",\n po_item.item_group as \"Item Group:Link/Item Group:120\",\n\tpo_item.description as \"Description::150\",\n\tpo_item.qty as \"Qty:Float:100\",\n\tpo_item.uom as \"UOM:Link/UOM:80\",\n\tpo_item.base_rate as \"Rate:Currency:120\",\n\tpo_item.base_amount as \"Amount:Currency:120\",\n\tpo.name as \"Purchase Order:Link/Purchase Order:120\",\n\tpo.transaction_date as \"Transaction Date:Date:140\",\n\tpo.supplier as \"Supplier:Link/Supplier:130\",\n sup.supplier_name as \"Supplier Name::150\",\n\tpo_item.project as \"Project:Link/Project:130\",\n\tifnull(po_item.received_qty, 0) as \"Received Qty:Float:120\",\n\tpo.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order` po, `tabPurchase Order Item` po_item, `tabSupplier` sup\nwhere\n\tpo.name = po_item.parent and po.supplier = sup.name and po.docstatus = 1\norder by po.name desc", - "ref_doctype": "Purchase Order", - "report_name": "Item-wise Purchase History", - "report_type": "Query Report", - "roles": [ + "add_total_row": 1, + "columns": [], + "creation": "2013-05-03 14:55:53", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 5, + "is_standard": "Yes", + "letterhead": null, + "modified": "2024-06-19 12:12:15.418799", + "modified_by": "Administrator", + "module": "Buying", + "name": "Item-wise Purchase History", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Purchase Order", + "report_name": "Item-wise Purchase History", + "report_type": "Script Report", +"roles": [ { "role": "Stock User" - }, + }, { "role": "Purchase Manager" - }, + }, { "role": "Purchase User" } - ] + ] } \ No newline at end of file diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py new file mode 100644 index 00000000000..6665c7a8033 --- /dev/null +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py @@ -0,0 +1,292 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.utils import flt +from frappe.utils.nestedset import get_descendants_of + + +def execute(filters=None): + filters = frappe._dict(filters or {}) + if filters.from_date > filters.to_date: + frappe.throw(_("From Date cannot be greater than To Date")) + + columns = get_columns(filters) + data = get_data(filters) + + chart_data = get_chart_data(data) + + return columns, data, None, chart_data + + +def get_columns(filters): + return [ + { + "label": _("Item Code"), + "fieldtype": "Link", + "fieldname": "item_code", + "options": "Item", + "width": 120, + }, + { + "label": _("Item Name"), + "fieldtype": "Data", + "fieldname": "item_name", + "width": 140, + }, + { + "label": _("Item Group"), + "fieldtype": "Link", + "fieldname": "item_group", + "options": "Item Group", + "width": 120, + }, + { + "label": _("Description"), + "fieldtype": "Data", + "fieldname": "description", + "width": 140, + }, + { + "label": _("Quantity"), + "fieldtype": "Float", + "fieldname": "quantity", + "width": 120, + }, + { + "label": _("UOM"), + "fieldtype": "Link", + "fieldname": "uom", + "options": "UOM", + "width": 90, + }, + { + "label": _("Rate"), + "fieldname": "rate", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Purchase Order"), + "fieldtype": "Link", + "fieldname": "purchase_order", + "options": "Purchase Order", + "width": 160, + }, + { + "label": _("Transaction Date"), + "fieldtype": "Date", + "fieldname": "transaction_date", + "width": 110, + }, + { + "label": _("Supplier"), + "fieldtype": "Link", + "fieldname": "supplier", + "options": "Supplier", + "width": 100, + }, + { + "label": _("Supplier Name"), + "fieldtype": "Data", + "fieldname": "supplier_name", + "width": 140, + }, + { + "label": _("Supplier Group"), + "fieldtype": "Link", + "fieldname": "supplier_group", + "options": "Supplier Group", + "width": 120, + }, + { + "label": _("Project"), + "fieldtype": "Link", + "fieldname": "project", + "options": "Project", + "width": 100, + }, + { + "label": _("Received Quantity"), + "fieldtype": "Float", + "fieldname": "received_qty", + "width": 150, + }, + { + "label": _("Billed Amount"), + "fieldtype": "Currency", + "fieldname": "billed_amt", + "options": "currency", + "width": 120, + }, + { + "label": _("Company"), + "fieldtype": "Link", + "fieldname": "company", + "options": "Company", + "width": 100, + }, + { + "label": _("Currency"), + "fieldtype": "Link", + "fieldname": "currency", + "options": "Currency", + "hidden": 1, + }, + ] + + +def get_data(filters): + data = [] + + company_list = get_descendants_of("Company", filters.get("company")) + company_list.append(filters.get("company")) + + supplier_details = get_supplier_details() + item_details = get_item_details() + purchase_order_records = get_purchase_order_details(company_list, filters) + + for record in purchase_order_records: + supplier_record = supplier_details.get(record.supplier) + item_record = item_details.get(record.item_code) + row = { + "item_code": record.get("item_code"), + "item_name": item_record.get("item_name"), + "item_group": item_record.get("item_group"), + "description": record.get("description"), + "quantity": record.get("qty"), + "uom": record.get("uom"), + "rate": record.get("base_rate"), + "amount": record.get("base_amount"), + "purchase_order": record.get("name"), + "transaction_date": record.get("transaction_date"), + "supplier": record.get("supplier"), + "supplier_name": supplier_record.get("supplier_name"), + "supplier_group": supplier_record.get("supplier_group"), + "project": record.get("project"), + "received_qty": flt(record.get("received_qty")), + "billed_amt": flt(record.get("billed_amt")), + "company": record.get("company"), + } + row["currency"] = frappe.get_cached_value( + "Company", row["company"], "default_currency" + ) + data.append(row) + + return data + + +def get_supplier_details(): + details = frappe.get_all( + "Supplier", fields=["name", "supplier_name", "supplier_group"] + ) + supplier_details = {} + for d in details: + supplier_details.setdefault( + d.name, + frappe._dict( + {"supplier_name": d.supplier_name, "supplier_group": d.supplier_group} + ), + ) + return supplier_details + + +def get_item_details(): + details = frappe.db.get_all("Item", fields=["name", "item_name", "item_group"]) + item_details = {} + for d in details: + item_details.setdefault( + d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group}) + ) + return item_details + + +def get_purchase_order_details(company_list, filters): + db_po = frappe.qb.DocType("Purchase Order") + db_po_item = frappe.qb.DocType("Purchase Order Item") + + query = ( + frappe.qb.from_(db_po) + .inner_join(db_po_item) + .on(db_po_item.parent == db_po.name) + .select( + db_po.name, + db_po.supplier, + db_po.transaction_date, + db_po.project, + db_po.company, + db_po_item.item_code, + db_po_item.description, + db_po_item.qty, + db_po_item.uom, + db_po_item.base_rate, + db_po_item.base_amount, + db_po_item.received_qty, + (db_po_item.billed_amt * db_po.conversion_rate).as_("billed_amt"), + ) + .where(db_po.docstatus == 1) + .where(db_po.company.isin(tuple(company_list))) + ) + + if filters.get("item_group"): + query = query.where(db_po_item.item_group == filters.item_group) + + if filters.get("from_date"): + query = query.where(db_po.transaction_date >= filters.from_date) + + if filters.get("to_date"): + query = query.where(db_po.transaction_date <= filters.to_date) + + if filters.get("item_code"): + query = query.where(db_po_item.item_code == filters.item_code) + + if filters.get("supplier"): + query = query.where(db_po.supplier == filters.supplier) + + return query.run(as_dict=1) + + +def get_chart_data(data): + item_wise_purchase_map = {} + labels, datapoints = [], [] + + for row in data: + item_key = row.get("item_code") + + if item_key not in item_wise_purchase_map: + item_wise_purchase_map[item_key] = 0 + + item_wise_purchase_map[item_key] = flt(item_wise_purchase_map[item_key]) + flt( + row.get("amount") + ) + + item_wise_purchase_map = { + item: value + for item, value in ( + sorted(item_wise_purchase_map.items(), key=lambda i: i[1], reverse=True) + ) + } + + for key in item_wise_purchase_map: + labels.append(key) + datapoints.append(item_wise_purchase_map[key]) + + return { + "data": { + "labels": labels[:30], # show max of 30 items in chart + "datasets": [ + {"name": _("Total Purchase Amount"), "values": datapoints[:30]} + ], + }, + "type": "bar", + "fieldtype": "Currency", + } From 76d32ab07ae50ca77a09b86034e9c7da8613f096 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:37:29 +0530 Subject: [PATCH 34/41] refactor: item-wise purchase history (query to script report) (cherry picked from commit 49331e6109fec93619c1fdf02a12f0c228973d3b) --- .../item_wise_purchase_history.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json index e6c3c624e01..35045afcf8b 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.json @@ -1,25 +1,22 @@ { "add_total_row": 1, - "columns": [], "creation": "2013-05-03 14:55:53", + "disable_prepared_report": 0, "disabled": 0, "docstatus": 0, "doctype": "Report", - "filters": [], "idx": 5, "is_standard": "Yes", - "letterhead": null, "modified": "2024-06-19 12:12:15.418799", "modified_by": "Administrator", "module": "Buying", "name": "Item-wise Purchase History", "owner": "Administrator", "prepared_report": 0, - "query": "", "ref_doctype": "Purchase Order", "report_name": "Item-wise Purchase History", "report_type": "Script Report", -"roles": [ + "roles": [ { "role": "Stock User" }, From b4171e4bd99487fd4af66a9fe6d5cb2519d461bc Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:57:02 +0530 Subject: [PATCH 35/41] refactor: item-wise purchase history (query to script report) -- formatter (cherry picked from commit 003a9608dcecd5f5691c23ff9fb189bf541aea87) --- .../item_wise_purchase_history.js | 120 +++++++++--------- .../item_wise_purchase_history.py | 28 +--- 2 files changed, 65 insertions(+), 83 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js index f4f311cc1dd..fd1cbf7bede 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -2,68 +2,64 @@ // For license information, please see license.txt frappe.query_reports["Item-wise Purchase History"] = { - filters: [ - { - fieldname: "company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1, - }, - { - fieldname: "from_date", - reqd: 1, - label: __("From Date"), - fieldtype: "Date", - default: frappe.datetime.add_months( - frappe.datetime.get_today(), - -1, - ), - }, - { - fieldname: "to_date", - reqd: 1, - default: frappe.datetime.get_today(), - label: __("To Date"), - fieldtype: "Date", - }, - { - fieldname: "item_group", - label: __("Item Group"), - fieldtype: "Link", - options: "Item Group", - }, - { - fieldname: "item_code", - label: __("Item"), - fieldtype: "Link", - options: "Item", - get_query: () => { - return { - query: "erpnext.controllers.queries.item_query", - }; - }, - }, - { - fieldname: "supplier", - label: __("Supplier"), - fieldtype: "Link", - options: "Supplier", - }, - ], + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + { + fieldname: "from_date", + reqd: 1, + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months( + frappe.datetime.get_today(), + -1, + ), + }, + { + fieldname: "to_date", + reqd: 1, + default: frappe.datetime.get_today(), + label: __("To Date"), + fieldtype: "Date", + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: () => { + return { + query: "erpnext.controllers.queries.item_query", + }; + }, + }, + { + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", + }, + ], - formatter: function (value, row, column, data, default_formatter) { - value = default_formatter(value, row, column, data); - let format_fields = ["received_qty", "billed_amt"]; + formatter: function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["received_qty", "billed_amt"]; - if ( - in_list(format_fields, column.fieldname) && - data && - data[column.fieldname] > 0 - ) { - value = "" + value + ""; - } - return value; - }, + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + }, }; diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py index 6665c7a8033..27aec9c3abe 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py @@ -177,25 +177,19 @@ def get_data(filters): "billed_amt": flt(record.get("billed_amt")), "company": record.get("company"), } - row["currency"] = frappe.get_cached_value( - "Company", row["company"], "default_currency" - ) + row["currency"] = frappe.get_cached_value("Company", row["company"], "default_currency") data.append(row) return data def get_supplier_details(): - details = frappe.get_all( - "Supplier", fields=["name", "supplier_name", "supplier_group"] - ) + details = frappe.get_all("Supplier", fields=["name", "supplier_name", "supplier_group"]) supplier_details = {} for d in details: supplier_details.setdefault( d.name, - frappe._dict( - {"supplier_name": d.supplier_name, "supplier_group": d.supplier_group} - ), + frappe._dict({"supplier_name": d.supplier_name, "supplier_group": d.supplier_group}), ) return supplier_details @@ -204,9 +198,7 @@ def get_item_details(): details = frappe.db.get_all("Item", fields=["name", "item_name", "item_group"]) item_details = {} for d in details: - item_details.setdefault( - d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group}) - ) + item_details.setdefault(d.name, frappe._dict({"item_name": d.item_name, "item_group": d.item_group})) return item_details @@ -265,15 +257,11 @@ def get_chart_data(data): if item_key not in item_wise_purchase_map: item_wise_purchase_map[item_key] = 0 - item_wise_purchase_map[item_key] = flt(item_wise_purchase_map[item_key]) + flt( - row.get("amount") - ) + item_wise_purchase_map[item_key] = flt(item_wise_purchase_map[item_key]) + flt(row.get("amount")) item_wise_purchase_map = { item: value - for item, value in ( - sorted(item_wise_purchase_map.items(), key=lambda i: i[1], reverse=True) - ) + for item, value in (sorted(item_wise_purchase_map.items(), key=lambda i: i[1], reverse=True)) } for key in item_wise_purchase_map: @@ -283,9 +271,7 @@ def get_chart_data(data): return { "data": { "labels": labels[:30], # show max of 30 items in chart - "datasets": [ - {"name": _("Total Purchase Amount"), "values": datapoints[:30]} - ], + "datasets": [{"name": _("Total Purchase Amount"), "values": datapoints[:30]}], }, "type": "bar", "fieldtype": "Currency", From a4d3934f75fee7599a29c82036cba1a0f130b2bf Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 14:59:24 +0530 Subject: [PATCH 36/41] refactor: item-wise purchase history (query to script report) --prettier (cherry picked from commit f740c943638c5e226e5ef4ee41e434d8ab6ec509) --- .../item_wise_purchase_history/item_wise_purchase_history.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js index fd1cbf7bede..174f2f7cf9d 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -16,10 +16,7 @@ frappe.query_reports["Item-wise Purchase History"] = { reqd: 1, label: __("From Date"), fieldtype: "Date", - default: frappe.datetime.add_months( - frappe.datetime.get_today(), - -1, - ), + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { fieldname: "to_date", From 6f75a3c6172e6f1c5e6b783e58733d1fc76cbcb7 Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 15:06:09 +0530 Subject: [PATCH 37/41] refactor: item-wise purchase history (query to script report) --prettier (cherry picked from commit 7bae18aba81ca9dbe64299a707f6713553d31336) --- .../item_wise_purchase_history/item_wise_purchase_history.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js index 174f2f7cf9d..37870b43b6d 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.js @@ -54,7 +54,7 @@ frappe.query_reports["Item-wise Purchase History"] = { value = default_formatter(value, row, column, data); let format_fields = ["received_qty", "billed_amt"]; - if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + if (format_fields.includes(column.fieldname) && data && data[column.fieldname] > 0) { value = "" + value + ""; } return value; From a0a932a23520852ccf4ed2e8cd78b8a1627bc16e Mon Sep 17 00:00:00 2001 From: Nihantra Patel Date: Wed, 19 Jun 2024 16:09:44 +0530 Subject: [PATCH 38/41] refactor: item-wise purchase history (query to script report) --upd (cherry picked from commit 2851764ed6ba977a0327aa09c32be7a1db27c301) --- .../item_wise_purchase_history.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py index 27aec9c3abe..a8950af3ea3 100644 --- a/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py +++ b/erpnext/buying/report/item_wise_purchase_history/item_wise_purchase_history.py @@ -229,8 +229,9 @@ def get_purchase_order_details(company_list, filters): .where(db_po.company.isin(tuple(company_list))) ) - if filters.get("item_group"): - query = query.where(db_po_item.item_group == filters.item_group) + for field in ("item_code", "item_group"): + if filters.get(field): + query = query.where(db_po_item[field] == filters[field]) if filters.get("from_date"): query = query.where(db_po.transaction_date >= filters.from_date) @@ -238,9 +239,6 @@ def get_purchase_order_details(company_list, filters): if filters.get("to_date"): query = query.where(db_po.transaction_date <= filters.to_date) - if filters.get("item_code"): - query = query.where(db_po_item.item_code == filters.item_code) - if filters.get("supplier"): query = query.where(db_po.supplier == filters.supplier) From 2c990758992bc3f04be6f2606efb5450b8b89c52 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 17:15:23 +0530 Subject: [PATCH 39/41] fix: Cannot read properties of null (reading 'doctype') (backport #42941) (#42943) fix: Cannot read properties of null (reading 'doctype') (#42941) (cherry picked from commit 86d3a9ab035fed3183eebfcfb3e961bfd9c31ea8) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/transaction.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4696c7f3b4d..d8bf4a5cf17 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1421,12 +1421,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe let show = cint(this.frm.doc.discount_amount) || ((this.frm.doc.taxes || []).filter(function(d) {return d.included_in_print_rate===1}).length); - if(frappe.meta.get_docfield(cur_frm.doctype, "net_total")) + if(this.frm.doc.doctype && frappe.meta.get_docfield(this.frm.doc.doctype, "net_total")) { this.frm.toggle_display("net_total", show); + } - if(frappe.meta.get_docfield(cur_frm.doctype, "base_net_total")) + if(this.frm.doc.doctype && frappe.meta.get_docfield(this.frm.doc.doctype, "base_net_total")) { this.frm.toggle_display("base_net_total", (show && (me.frm.doc.currency != company_currency))); - + } } change_grid_labels(company_currency) { From f56ee58e8161edd867fa19e9d0beeb8d87354985 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 17 Jun 2024 10:25:48 +0200 Subject: [PATCH 40/41] fix: spec mobile and email fields for notifications (cherry picked from commit 18993a97ce684c231e40a870e1a96d7160d63bdf) --- erpnext/selling/doctype/customer/customer.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 59f082431b8..dec09e512fe 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -308,13 +308,15 @@ "fetch_from": "customer_primary_contact.mobile_no", "fieldname": "mobile_no", "fieldtype": "Read Only", - "label": "Mobile No" + "label": "Mobile No", + "options": "Mobile" }, { "fetch_from": "customer_primary_contact.email_id", "fieldname": "email_id", "fieldtype": "Read Only", - "label": "Email Id" + "label": "Email Id", + "options": "Email" }, { "fieldname": "column_break_26", @@ -592,7 +594,7 @@ "link_fieldname": "party" } ], - "modified": "2024-05-08 18:03:20.716169", + "modified": "2024-06-17 03:24:59.612974", "modified_by": "Administrator", "module": "Selling", "name": "Customer", @@ -677,4 +679,4 @@ "states": [], "title_field": "customer_name", "track_changes": 1 -} \ No newline at end of file +} From 8d8dd0cd2bbb34e23832aa83055e78fa08be2245 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 23:07:37 +0530 Subject: [PATCH 41/41] feat: Disassembly Order (backport #42655) (#42957) * feat: Disassembly Order (#42655) (cherry picked from commit 663a08e4cd73a18e51d6d9e56fa9bf867525f370) # Conflicts: # erpnext/stock/doctype/stock_entry_type/stock_entry_type.json * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../doctype/work_order/test_work_order.py | 50 +++++++++++ .../doctype/work_order/work_order.js | 85 ++++++++++++++++--- .../doctype/work_order/work_order.py | 11 ++- erpnext/patches.txt | 1 + .../add_disassembly_order_stock_entry_type.py | 13 +++ .../v15_0/set_standard_stock_entry_type.py | 1 + .../operations/install_fixtures.py | 1 + .../doctype/stock_entry/stock_entry.json | 8 +- .../stock/doctype/stock_entry/stock_entry.py | 55 ++++++++++++ .../stock_entry_type/stock_entry_type.json | 5 +- .../stock_entry_type/stock_entry_type.py | 2 + 11 files changed, 212 insertions(+), 20 deletions(-) create mode 100644 erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index d1f2cdbd1aa..627e5262dc9 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -2053,6 +2053,55 @@ class TestWorkOrder(FrappeTestCase): "BOM", ) + def test_disassemby_order(self): + fg_item = "Test Disassembly Item" + source_warehouse = "Stores - _TC" + raw_materials = ["Test Disassembly RM Item 1", "Test Disassembly RM Item 2"] + + make_item(fg_item, {"is_stock_item": 1}) + for item in raw_materials: + make_item(item, {"is_stock_item": 1}) + test_stock_entry.make_stock_entry( + item_code=item, + target=source_warehouse, + qty=1, + basic_rate=100, + ) + + make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials) + + wo = make_wo_order_test_record( + item=fg_item, + qty=1, + source_warehouse=source_warehouse, + skip_transfer=1, + ) + + stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1)) + for row in stock_entry.items: + if row.item_code in raw_materials: + row.s_warehouse = source_warehouse + + stock_entry.submit() + + wo.reload() + self.assertEqual(wo.status, "Completed") + + stock_entry = frappe.get_doc(make_stock_entry(wo.name, "Disassemble", 1)) + stock_entry.save() + + self.assertEqual(stock_entry.purpose, "Disassemble") + + for row in stock_entry.items: + if row.item_code == fg_item: + self.assertTrue(row.s_warehouse) + self.assertFalse(row.t_warehouse) + else: + self.assertFalse(row.s_warehouse) + self.assertTrue(row.t_warehouse) + + stock_entry.submit() + def make_operation(**kwargs): kwargs = frappe._dict(kwargs) @@ -2370,6 +2419,7 @@ def make_wo_order_test_record(**args): wo_order.batch_size = args.batch_size or 0 if args.source_warehouse: + wo_order.source_warehouse = args.source_warehouse for item in wo_order.get("required_items"): item.source_warehouse = args.source_warehouse diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 1da33f0ad9b..ae888c79714 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -183,13 +183,30 @@ frappe.ui.form.on("Work Order", { } } + if (frm.doc.status == "Completed") { + if (frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") { + frm.add_custom_button( + __("BOM"), + () => { + frm.trigger("make_bom"); + }, + __("Create") + ); + } + } + if ( - frm.doc.status == "Completed" && - frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture" + frm.doc.docstatus === 1 && + ["Closed", "Completed"].includes(frm.doc.status) && + frm.doc.produced_qty > 0 ) { - frm.add_custom_button(__("Create BOM"), () => { - frm.trigger("make_bom"); - }); + frm.add_custom_button( + __("Disassembly Order"), + () => { + frm.trigger("make_disassembly_order"); + }, + __("Create") + ); } frm.trigger("add_custom_button_to_return_components"); @@ -337,6 +354,23 @@ frappe.ui.form.on("Work Order", { }); }, + make_disassembly_order(frm) { + erpnext.work_order + .show_prompt_for_qty_input(frm, "Disassemble") + .then((data) => { + return frappe.xcall("erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", { + work_order_id: frm.doc.name, + purpose: "Disassemble", + qty: data.qty, + target_warehouse: data.target_warehouse, + }); + }) + .then((stock_entry) => { + frappe.model.sync(stock_entry); + frappe.set_route("Form", stock_entry.doctype, stock_entry.name); + }); + }, + show_progress_for_items: function (frm) { var bars = []; var message = ""; @@ -745,6 +779,10 @@ erpnext.work_order = { get_max_transferable_qty: (frm, purpose) => { let max = 0; + if (purpose === "Disassemble") { + return flt(frm.doc.produced_qty); + } + if (frm.doc.skip_transfer) { max = flt(frm.doc.qty) - flt(frm.doc.produced_qty); } else { @@ -759,15 +797,38 @@ erpnext.work_order = { show_prompt_for_qty_input: function (frm, purpose) { let max = this.get_max_transferable_qty(frm, purpose); + + let fields = [ + { + fieldtype: "Float", + label: __("Qty for {0}", [__(purpose)]), + fieldname: "qty", + description: __("Max: {0}", [max]), + default: max, + }, + ]; + + if (purpose === "Disassemble") { + fields.push({ + fieldtype: "Link", + options: "Warehouse", + fieldname: "target_warehouse", + label: __("Target Warehouse"), + default: frm.doc.source_warehouse || frm.doc.wip_warehouse, + get_query() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + }, + }; + }, + }); + } + return new Promise((resolve, reject) => { frappe.prompt( - { - fieldtype: "Float", - label: __("Qty for {0}", [__(purpose)]), - fieldname: "qty", - description: __("Max: {0}", [max]), - default: max, - }, + fields, (data) => { max += (frm.doc.qty * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100; diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 904e42a6a03..057c49949ec 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1359,7 +1359,7 @@ def set_work_order_ops(name): @frappe.whitelist() -def make_stock_entry(work_order_id, purpose, qty=None): +def make_stock_entry(work_order_id, purpose, qty=None, target_warehouse=None): work_order = frappe.get_doc("Work Order", work_order_id) if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"): wip_warehouse = work_order.wip_warehouse @@ -1389,9 +1389,16 @@ def make_stock_entry(work_order_id, purpose, qty=None): stock_entry.to_warehouse = work_order.fg_warehouse stock_entry.project = work_order.project + if purpose == "Disassemble": + stock_entry.from_warehouse = work_order.fg_warehouse + stock_entry.to_warehouse = target_warehouse or work_order.source_warehouse + stock_entry.set_stock_entry_type() stock_entry.get_items() - stock_entry.set_serial_no_batch_for_finished_good() + + if purpose != "Disassemble": + stock_entry.set_serial_no_batch_for_finished_good() + return stock_entry.as_dict() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 31ba6b23c01..feaac948099 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -372,4 +372,5 @@ erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry erpnext.patches.v15_0.update_total_number_of_booked_depreciations erpnext.patches.v15_0.do_not_use_batchwise_valuation erpnext.patches.v15_0.drop_index_posting_datetime_from_sle +erpnext.patches.v15_0.add_disassembly_order_stock_entry_type #1 erpnext.patches.v15_0.set_standard_stock_entry_type diff --git a/erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py b/erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py new file mode 100644 index 00000000000..1f3413b172b --- /dev/null +++ b/erpnext/patches/v15_0/add_disassembly_order_stock_entry_type.py @@ -0,0 +1,13 @@ +import frappe + + +def execute(): + if not frappe.db.exists("Stock Entry Type", "Disassemble"): + frappe.get_doc( + { + "doctype": "Stock Entry Type", + "name": "Disassemble", + "purpose": "Disassemble", + "is_standard": 1, + } + ).insert(ignore_permissions=True) diff --git a/erpnext/patches/v15_0/set_standard_stock_entry_type.py b/erpnext/patches/v15_0/set_standard_stock_entry_type.py index 7551b0d4afc..4a721abae40 100644 --- a/erpnext/patches/v15_0/set_standard_stock_entry_type.py +++ b/erpnext/patches/v15_0/set_standard_stock_entry_type.py @@ -11,6 +11,7 @@ def execute(): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ]: if frappe.db.exists("Stock Entry Type", stock_entry_type): frappe.db.set_value("Stock Entry Type", stock_entry_type, "is_standard", 1) diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index a99289416ba..270a9e06054 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -96,6 +96,7 @@ def install(country=None): "purpose": "Repack", "is_standard": 1, }, + {"doctype": "Stock Entry Type", "name": "Disassemble", "purpose": "Disassemble", "is_standard": 1}, { "doctype": "Stock Entry Type", "name": "Send to Subcontractor", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 3f467d3627a..5ba9f2973a1 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -127,7 +127,7 @@ "label": "Purpose", "oldfieldname": "purpose", "oldfieldtype": "Select", - "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor", + "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble", "read_only": 1, "search_index": 1 }, @@ -143,7 +143,7 @@ "reqd": 1 }, { - "depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\", \"Material Consumption for Manufacture\"], doc.purpose)", + "depends_on": "eval:in_list([\"Material Transfer for Manufacture\", \"Manufacture\", \"Material Consumption for Manufacture\", \"Disassemble\"], doc.purpose)", "fieldname": "work_order", "fieldtype": "Link", "label": "Work Order", @@ -242,7 +242,7 @@ }, { "default": "0", - "depends_on": "eval:in_list([\"Material Issue\", \"Material Transfer\", \"Manufacture\", \"Repack\", \"Send to Subcontractor\", \"Material Transfer for Manufacture\", \"Material Consumption for Manufacture\"], doc.purpose)", + "depends_on": "eval:in_list([\"Material Issue\", \"Material Transfer\", \"Manufacture\", \"Repack\", \"Send to Subcontractor\", \"Material Transfer for Manufacture\", \"Material Consumption for Manufacture\", \"Disassemble\"], doc.purpose)", "fieldname": "from_bom", "fieldtype": "Check", "label": "From BOM", @@ -697,7 +697,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-08-13 19:02:42.386955", + "modified": "2024-08-13 19:05:42.386955", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 6b4164dee5f..0ef54c78555 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -132,6 +132,7 @@ class StockEntry(StockController): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ] remarks: DF.Text | None sales_invoice_no: DF.Link | None @@ -337,6 +338,7 @@ class StockEntry(StockController): "Repack", "Send to Subcontractor", "Material Consumption for Manufacture", + "Disassemble", ] if self.purpose not in valid_purposes: @@ -616,6 +618,7 @@ class StockEntry(StockController): "Manufacture", "Material Transfer for Manufacture", "Material Consumption for Manufacture", + "Disassemble", ): # check if work order is entered @@ -1703,11 +1706,63 @@ class StockEntry(StockController): }, ) + def get_items_for_disassembly(self): + """Get items for Disassembly Order""" + + if not self.work_order: + frappe.throw(_("The Work Order is mandatory for Disassembly Order")) + + items = self.get_items_from_manufacture_entry() + + s_warehouse = "" + if self.work_order: + s_warehouse = frappe.db.get_value("Work Order", self.work_order, "fg_warehouse") + + for row in items: + child_row = self.append("items", {}) + for field, value in row.items(): + if value is not None: + child_row.set(field, value) + + child_row.s_warehouse = (self.from_warehouse or s_warehouse) if row.is_finished_item else "" + child_row.t_warehouse = self.to_warehouse if not row.is_finished_item else "" + child_row.is_finished_item = 0 if row.is_finished_item else 1 + + def get_items_from_manufacture_entry(self): + return frappe.get_all( + "Stock Entry", + fields=[ + "`tabStock Entry Detail`.`item_code`", + "`tabStock Entry Detail`.`item_name`", + "`tabStock Entry Detail`.`description`", + "`tabStock Entry Detail`.`qty`", + "`tabStock Entry Detail`.`transfer_qty`", + "`tabStock Entry Detail`.`stock_uom`", + "`tabStock Entry Detail`.`uom`", + "`tabStock Entry Detail`.`basic_rate`", + "`tabStock Entry Detail`.`conversion_factor`", + "`tabStock Entry Detail`.`is_finished_item`", + "`tabStock Entry Detail`.`batch_no`", + "`tabStock Entry Detail`.`serial_no`", + "`tabStock Entry Detail`.`use_serial_batch_fields`", + ], + filters=[ + ["Stock Entry", "purpose", "=", "Manufacture"], + ["Stock Entry", "work_order", "=", self.work_order], + ["Stock Entry", "docstatus", "=", 1], + ["Stock Entry Detail", "docstatus", "=", 1], + ], + order_by="`tabStock Entry Detail`.`idx` desc, `tabStock Entry Detail`.`is_finished_item` desc", + ) + @frappe.whitelist() def get_items(self): self.set("items", []) self.validate_work_order() + if self.purpose == "Disassemble": + return self.get_items_for_disassembly() + if not self.posting_date or not self.posting_time: frappe.throw(_("Posting date and posting time is mandatory")) diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json index 9b426a6c940..c522df59941 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json @@ -17,7 +17,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Purpose", - "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor", + "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nDisassemble", "reqd": 1, "set_only_once": 1 }, @@ -37,10 +37,11 @@ } ], "links": [], - "modified": "2024-08-20 15:35:45.696958", + "modified": "2024-08-24 16:00:22.696958", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Type", + "naming_rule": "Set by user", "owner": "Administrator", "permissions": [ { diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py index efbdd680c69..8d05a43408b 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py @@ -27,6 +27,7 @@ class StockEntryType(Document): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ] # end: auto-generated types @@ -45,5 +46,6 @@ class StockEntryType(Document): "Manufacture", "Repack", "Send to Subcontractor", + "Disassemble", ]: frappe.throw(f"Stock Entry Type {self.name} cannot be set as standard")