From 2c21404813719a666c443964aa3583d2ad731538 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 11 Jul 2023 17:51:27 +0530 Subject: [PATCH 01/26] fix: circular dependency during reposting causing timeout error (cherry picked from commit c16a5814d41610136ce00e5aca4269ea3d308971) --- .../purchase_receipt/test_purchase_receipt.py | 32 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 20 ++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index c87b3587fec..585871cf391 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1908,6 +1908,32 @@ class TestPurchaseReceipt(FrappeTestCase): ste5.reload() self.assertEqual(ste5.items[0].valuation_rate, 275.00) + ste6 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -3), + source=warehouse1, + target=warehouse, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste6.reload() + self.assertEqual(ste6.items[0].valuation_rate, 275.00) + + ste7 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -3), + source=warehouse, + target=warehouse1, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste7.reload() + self.assertEqual(ste7.items[0].valuation_rate, 275.00) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2500 * -1) pr.reload() @@ -1928,6 +1954,12 @@ class TestPurchaseReceipt(FrappeTestCase): ste5.reload() self.assertEqual(ste5.items[0].valuation_rate, valuation_rate) + ste6.reload() + self.assertEqual(ste6.items[0].valuation_rate, valuation_rate) + + ste7.reload() + self.assertEqual(ste7.items[0].valuation_rate, valuation_rate) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index e0de9913f08..7e6d3edae10 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -512,7 +512,7 @@ class update_entries_after(object): def update_distinct_item_warehouses(self, dependant_sle): key = (dependant_sle.item_code, dependant_sle.warehouse) - val = frappe._dict({"sle": dependant_sle}) + val = frappe._dict({"sle": dependant_sle, "dependent_voucher_detail_nos": []}) if key not in self.distinct_item_warehouses: self.distinct_item_warehouses[key] = val @@ -521,13 +521,26 @@ class update_entries_after(object): existing_sle_posting_date = ( self.distinct_item_warehouses[key].get("sle", {}).get("posting_date") ) + + dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key) + if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date): val.sle_changed = True self.distinct_item_warehouses[key] = val self.new_items_found = True - elif self.distinct_item_warehouses[key].get("reposting_status"): - self.distinct_item_warehouses[key] = val + elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos): + # Future dependent voucher needs to be repost to get the correct stock value + # If dependent voucher has not reposted, then add it to the list + dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no) self.new_items_found = True + val.dependent_voucher_detail_nos = dependent_voucher_detail_nos + self.distinct_item_warehouses[key] = val + + def get_dependent_voucher_detail_nos(self, key): + if "dependent_voucher_detail_nos" not in self.distinct_item_warehouses[key]: + self.distinct_item_warehouses[key].dependent_voucher_detail_nos = [] + + return self.distinct_item_warehouses[key].dependent_voucher_detail_nos def process_sle(self, sle): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -1263,6 +1276,7 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): "qty_after_transaction", "posting_date", "posting_time", + "voucher_detail_no", "timestamp(posting_date, posting_time) as timestamp", ], as_dict=1, From 4aaa1a15d7dfe9ad81d3cc1f000dac3e324cfa6f Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 11 Jul 2023 23:21:37 +0530 Subject: [PATCH 02/26] fix: allow manual asset receipt mov from nowhere (#36093) --- .../doctype/asset_movement/asset_movement.js | 2 +- .../doctype/asset_movement/asset_movement.py | 31 +++++++------------ 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index f9c600731b3..4ccc3f8013b 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -63,7 +63,7 @@ frappe.ui.form.on('Asset Movement', { fieldnames_to_be_altered = { target_location: { read_only: 0, reqd: 1 }, source_location: { read_only: 1, reqd: 0 }, - from_employee: { read_only: 0, reqd: 1 }, + from_employee: { read_only: 0, reqd: 0 }, to_employee: { read_only: 1, reqd: 0 } }; } diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index b58ca10482b..22055dcb736 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -62,29 +62,20 @@ class AssetMovement(Document): frappe.throw(_("Source and Target Location cannot be same")) if self.purpose == "Receipt": - # only when asset is bought and first entry is made - if not d.source_location and not (d.target_location or d.to_employee): + if not (d.source_location or d.from_employee) and not (d.target_location or d.to_employee): frappe.throw( _("Target Location or To Employee is required while receiving Asset {0}").format(d.asset) ) - elif d.source_location: - # when asset is received from an employee - if d.target_location and not d.from_employee: - frappe.throw( - _("From employee is required while receiving Asset {0} to a target location").format( - d.asset - ) - ) - if d.from_employee and not d.target_location: - frappe.throw( - _("Target Location is required while receiving Asset {0} from an employee").format(d.asset) - ) - if d.to_employee and d.target_location: - frappe.throw( - _( - "Asset {0} cannot be received at a location and given to employee in a single movement" - ).format(d.asset) - ) + elif d.from_employee and not d.target_location: + frappe.throw( + _("Target Location is required while receiving Asset {0} from an employee").format(d.asset) + ) + elif d.to_employee and d.target_location: + frappe.throw( + _( + "Asset {0} cannot be received at a location and given to an employee in a single movement" + ).format(d.asset) + ) def validate_employee(self): for d in self.assets: From ea807f8d9a32b15a718fe35dfe026fd6c688ccd0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:18:31 +0530 Subject: [PATCH 03/26] ci: regen release notes with GH API (backport #36098) (#36099) ci: regen release notes with GH API (#36098) [skip ci] (cherry picked from commit 0340bfc90da1bc4567ee01db03d3ef89669c007a) Co-authored-by: Ankush Menat --- .github/workflows/release_notes.yml | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/release_notes.yml diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml new file mode 100644 index 00000000000..91139dea654 --- /dev/null +++ b/.github/workflows/release_notes.yml @@ -0,0 +1,38 @@ +# This action: +# +# 1. Generates release notes using github API. +# 2. Strips unnecessary info like chore/style etc from notes. +# 3. Updates release info. + +# This action needs to be maintained on all branches that do releases. + +name: 'Release Notes' + +on: + workflow_dispatch: + inputs: + tag_name: + description: 'Tag of release like v13.0.0' + required: true + type: string + release: + types: [released] + +permissions: + contents: read + +jobs: + regen-notes: + name: 'Regenerate release notes' + runs-on: ubuntu-latest + + steps: + - name: Update notes + run: | + NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' ) + RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/tags/$RELEASE_TAG | jq -r '.id') + gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body="$NEW_NOTES" + + env: + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }} From a3a052bb514a726e1a1ca558f5e02a6c9fab59c6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 12 Jul 2023 13:23:07 +0530 Subject: [PATCH 04/26] fix: Ambiguous column error while submitting stock entry (#36058) fix: Ambiguous column error while submitting stock entry (#36058) --- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 19e081acf73..92678e44c84 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1001,7 +1001,7 @@ class WorkOrder(Document): consumed_qty = frappe.db.sql( """ SELECT - SUM(qty) + SUM(detail.qty) FROM `tabStock Entry` entry, `tabStock Entry Detail` detail From 63e26e39d47e963868c2ca69afbf970c41de2808 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 12 Jul 2023 19:16:10 +0530 Subject: [PATCH 05/26] feat: add project filter in reports importing financial statements js file (#36097) feat: add project filter in reports importing financial statements js file (#36097) feat: add project filter in financial statements js file (cherry picked from commit 596a14e34fd58df0c2377a93ad56b21e70edbd3f) Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> --- .../gross_and_net_profit_report.js | 8 -------- .../profit_and_loss_statement.js | 10 ---------- erpnext/public/js/financial_statements.js | 10 ++++++++++ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js index 8dc5ab36dd9..92cf36ebc52 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js @@ -12,14 +12,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.financial_statements); frappe.query_reports["Gross and Net Profit Report"]["filters"].push( - { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt); - } - }, { "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 298d83894c6..e794f270c2b 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -9,16 +9,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Profit and Loss Statement', 10); frappe.query_reports["Profit and Loss Statement"]["filters"].push( - { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt, { - company: frappe.query_report.get_filter_value("company") - }); - }, - }, { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index b0082bdb281..2b50a75e728 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -182,6 +182,16 @@ function get_filters() { company: frappe.query_report.get_filter_value("company") }); } + }, + { + "fieldname": "project", + "label": __("Project"), + "fieldtype": "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Project', txt, { + company: frappe.query_report.get_filter_value("company") + }); + }, } ] From cf29156139b56f3aaf72d45095e82a4329348c5e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 13 Jul 2023 16:13:23 +0530 Subject: [PATCH 06/26] fix: Accounts closing balance patch (#36113) fix: Accounts closing balance patch (#36113) (cherry picked from commit d631c7dffaab22709cfbeaf6d306a9eba3c2a4ca) Co-authored-by: Deepesh Garg --- erpnext/patches/v14_0/update_closing_balances.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index d66467775c8..9a814f3ee4f 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -62,7 +62,10 @@ def execute(): entry["closing_date"] = pcv_doc.posting_date entry["period_closing_voucher"] = pcv_doc.name - make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name) + entries = gl_entries + closing_entries + if entries: + make_closing_entries(entries, voucher_name=pcv.name) + company_wise_order[pcv.company].append(pcv.posting_date) i += 1 From 0147754273c726d6e64b283231c3cc3c0a83287e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 10:41:18 +0530 Subject: [PATCH 07/26] fix: Account balance patch and query fixes (#36117) * fix: Account balance patch and query fixes (#36117) (cherry picked from commit b4bd978791166e24b126683fc2f9dde8eadb4341) # Conflicts: # erpnext/patches.txt * chore: resolve conflicts --------- Co-authored-by: Deepesh Garg --- .../report/trial_balance/trial_balance.py | 7 +- erpnext/patches.txt | 2 +- .../patches/v14_0/update_closing_balances.py | 101 +++++++++--------- 3 files changed, 59 insertions(+), 51 deletions(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index d51c4c4acba..7a8b7dc5819 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -159,6 +159,8 @@ def get_rootwise_opening_balances(filters, report_type): accounting_dimensions, period_closing_voucher=last_period_closing_voucher[0].name, ) + + # Report getting generate from the mid of a fiscal year if getdate(last_period_closing_voucher[0].posting_date) < getdate( add_days(filters.from_date, -1) ): @@ -220,7 +222,10 @@ def get_opening_balance( if start_date: opening_balance = opening_balance.where(closing_balance.posting_date >= start_date) opening_balance = opening_balance.where(closing_balance.is_opening == "No") - opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date) + else: + opening_balance = opening_balance.where( + (closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes") + ) if ( not filters.show_unclosed_fy_pl_balances diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 337e9c79a61..2e8b26ff475 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -336,4 +336,4 @@ erpnext.patches.v14_0.cleanup_workspaces erpnext.patches.v14_0.enable_allow_existing_serial_no erpnext.patches.v14_0.set_report_in_process_SOA erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -erpnext.patches.v14_0.update_closing_balances +erpnext.patches.v14_0.update_closing_balances #14-07-2023 diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 9a814f3ee4f..8849c11fcac 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -13,59 +13,62 @@ from erpnext.accounts.utils import get_fiscal_year def execute(): frappe.db.truncate("Account Closing Balance") - i = 0 - company_wise_order = {} - for pcv in frappe.db.get_all( - "Period Closing Voucher", - fields=["company", "posting_date", "name"], - filters={"docstatus": 1}, - order_by="posting_date", - ): + for company in frappe.get_all("Company", pluck="name"): + i = 0 + company_wise_order = {} + for pcv in frappe.db.get_all( + "Period Closing Voucher", + fields=["company", "posting_date", "name"], + filters={"docstatus": 1, "company": company}, + order_by="posting_date", + ): - company_wise_order.setdefault(pcv.company, []) - if pcv.posting_date not in company_wise_order[pcv.company]: - pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) - pcv_doc.year_start_date = get_fiscal_year( - pcv.posting_date, pcv.fiscal_year, company=pcv.company - )[1] + company_wise_order.setdefault(pcv.company, []) + if pcv.posting_date not in company_wise_order[pcv.company]: + pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) + pcv_doc.year_start_date = get_fiscal_year( + pcv.posting_date, pcv.fiscal_year, company=pcv.company + )[1] - # get gl entries against pcv - gl_entries = frappe.db.get_all( - "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] - ) - for entry in gl_entries: - entry["is_period_closing_voucher_entry"] = 1 - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name - - # get all gl entries for the year - closing_entries = frappe.db.get_all( - "GL Entry", - filters={ - "is_cancelled": 0, - "voucher_no": ["!=", pcv.name], - "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], - "is_opening": "No", - }, - fields=["*"], - ) - - if i == 0: - # add opening entries only for the first pcv - closing_entries += frappe.db.get_all( - "GL Entry", - filters={"is_cancelled": 0, "is_opening": "Yes"}, - fields=["*"], + # get gl entries against pcv + gl_entries = frappe.db.get_all( + "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] ) + for entry in gl_entries: + entry["is_period_closing_voucher_entry"] = 1 + entry["closing_date"] = pcv_doc.posting_date + entry["period_closing_voucher"] = pcv_doc.name - for entry in closing_entries: - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name + closing_entries = [] - entries = gl_entries + closing_entries - if entries: - make_closing_entries(entries, voucher_name=pcv.name) + if pcv.posting_date not in company_wise_order[pcv.company]: + # get all gl entries for the year + closing_entries = frappe.db.get_all( + "GL Entry", + filters={ + "is_cancelled": 0, + "voucher_no": ["!=", pcv.name], + "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], + "is_opening": "No", + }, + fields=["*"], + ) - company_wise_order[pcv.company].append(pcv.posting_date) + if i == 0: + # add opening entries only for the first pcv + closing_entries += frappe.db.get_all( + "GL Entry", + filters={"is_cancelled": 0, "is_opening": "Yes"}, + fields=["*"], + ) - i += 1 + for entry in closing_entries: + entry["closing_date"] = pcv_doc.posting_date + entry["period_closing_voucher"] = pcv_doc.name + + entries = gl_entries + closing_entries + + if entries: + make_closing_entries(entries, voucher_name=pcv.name) + i += 1 + company_wise_order[pcv.company].append(pcv.posting_date) From b167bffd22c843c601a3be1b83db9ee6e63fbd18 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 13 Jul 2023 21:03:46 +0530 Subject: [PATCH 08/26] ci: fix repo name in relase notes workflow [skip ci] (cherry picked from commit b5f6a1cc20271fd538f1184ad91b131044057339) --- .github/workflows/release_notes.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml index 91139dea654..e765a66f691 100644 --- a/.github/workflows/release_notes.yml +++ b/.github/workflows/release_notes.yml @@ -29,9 +29,9 @@ jobs: steps: - name: Update notes run: | - NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' ) - RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/tags/$RELEASE_TAG | jq -r '.id') - gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/frappe/releases/$RELEASE_ID -f body="$NEW_NOTES" + NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' ) + RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id') + gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES" env: GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} From b86571d9d450ae0e992713267751bf449a4b4d55 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:03:26 +0530 Subject: [PATCH 09/26] fix: improve "Update Items" modal (#36105) fix: improve "Update Items" modal (#36105) * fix: make "Update Items" modal larger * fix: remove conversion factor from overview Conversion factor doesn't make much sense without two different UOMs next to it, hence moving it to row detail view (cherry picked from commit d5fe1432f80c661de6c0d12ff1d0d56c40e96cd2) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/public/js/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 58aa8d7da23..541f6720732 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -600,7 +600,6 @@ erpnext.utils.update_child_items = function(opts) { fields.splice(3, 0, { fieldtype: 'Float', fieldname: "conversion_factor", - in_list_view: 1, label: __("Conversion Factor"), precision: get_precision('conversion_factor') }) @@ -608,6 +607,7 @@ erpnext.utils.update_child_items = function(opts) { new frappe.ui.Dialog({ title: __("Update Items"), + size: "extra-large", fields: [ { fieldname: "trans_items", From 42c93a1f000445b736ef64bcf32cfeb69b81ddb4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:04:07 +0530 Subject: [PATCH 10/26] fix: get_dimension with_cost_center_and_project=false is not working (#35974) fix: get_dimension with_cost_center_and_project=false is not working (#35974) * fix: get_dimension with_cost_center_and_project=false is not working. with_cost_center_and_project is no python str, and it always evaluated as True, despite JS call it with false * chore: Linting Issues --------- Co-authored-by: Deepesh Garg (cherry picked from commit 3b884efca93be67384f1008cf1aed4c2a6505645) Co-authored-by: Kitti U. @ Ecosoft --- .../doctype/accounting_dimension/accounting_dimension.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 81ff6a52db1..15c84d462f1 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -271,6 +271,12 @@ def get_dimensions(with_cost_center_and_project=False): as_dict=1, ) + if isinstance(with_cost_center_and_project, str): + if with_cost_center_and_project.lower().strip() == "true": + with_cost_center_and_project = True + else: + with_cost_center_and_project = False + if with_cost_center_and_project: dimension_filters.extend( [ From e7f57542ab26042521873d9a3a6b7135d1566a36 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 15:59:57 +0530 Subject: [PATCH 11/26] fix: Handle multi-company in patch (#36127) * fix: Handle multi-company in patch (#36127) fix: Handle multi-compnay in patch (cherry picked from commit ac9ad8ec364fcf507357062db37355b18af66ba9) * chore: re trigger patch --------- Co-authored-by: Deepesh Garg --- erpnext/patches.txt | 2 +- erpnext/patches/v14_0/update_closing_balances.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2e8b26ff475..4b09e0c10f6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -336,4 +336,4 @@ erpnext.patches.v14_0.cleanup_workspaces erpnext.patches.v14_0.enable_allow_existing_serial_no erpnext.patches.v14_0.set_report_in_process_SOA erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -erpnext.patches.v14_0.update_closing_balances #14-07-2023 +erpnext.patches.v14_0.update_closing_balances #15-07-2023 diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 8849c11fcac..2947b98740b 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -50,6 +50,7 @@ def execute(): "voucher_no": ["!=", pcv.name], "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], "is_opening": "No", + "company": company, }, fields=["*"], ) @@ -58,7 +59,7 @@ def execute(): # add opening entries only for the first pcv closing_entries += frappe.db.get_all( "GL Entry", - filters={"is_cancelled": 0, "is_opening": "Yes"}, + filters={"is_cancelled": 0, "is_opening": "Yes", "company": company}, fields=["*"], ) From dd5101056dd6f54d13f981c608a80cf868ea2b15 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 18:03:53 +0530 Subject: [PATCH 12/26] fix: `TypeError` while creating WO from PP (backport #36136) (#36137) fix: `TypeError` while creating WO from PP (#36136) (cherry picked from commit 8f5b94f5fd6a7ba283f412cb644193264e6f15c7) Co-authored-by: s-aga-r --- .../manufacturing/doctype/production_plan/production_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 5f957a5442b..a988badd744 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -621,7 +621,7 @@ class ProductionPlan(Document): def create_work_order(self, item): from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError - if item.get("qty") <= 0: + if flt(item.get("qty")) <= 0: return wo = frappe.new_doc("Work Order") From d10e5e666bef96b35af79c68d8e97ad9fb129d83 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 18:46:28 +0530 Subject: [PATCH 13/26] fix: Opening entries showing up incorrectly in TB report (#36135) fix: Opening entries showing up incorrectly in TB report (#36135) * fix: Opening entries showing up incorrectly in TB report * chore: Linting Issue (cherry picked from commit 297c7e833c41c3e867a9bfb66e39f8dfe12eb2b6) Co-authored-by: Deepesh Garg --- erpnext/accounts/report/financial_statements.py | 2 +- erpnext/accounts/report/trial_balance/trial_balance.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index f3a892ba43c..db9609debe6 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -416,6 +416,7 @@ def set_gl_entries_by_account( filters, gl_entries_by_account, ignore_closing_entries=False, + ignore_opening_entries=False, ): """Returns a dict like { "account": [gl entries], ... }""" gl_entries = [] @@ -426,7 +427,6 @@ def set_gl_entries_by_account( pluck="name", ) - ignore_opening_entries = False if accounts_list: # For balance sheet if not from_date: diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 7a8b7dc5819..5176c31be71 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -117,6 +117,7 @@ def get_data(filters): filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry), + ignore_opening_entries=True, ) calculate_values(accounts, gl_entries_by_account, opening_balances) From 94caf7f5a86c3028b93a34525f1478eb2ba24d17 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:37:48 +0530 Subject: [PATCH 14/26] feat: add local holidays (#36116) * feat: add local holidays (cherry picked from commit aa18b25a71430d11923bbfb5abca79214c0a467f) * test(Holiday List): weekly off and local holidays (cherry picked from commit fd23bd043404f8ccfe4e688789a0aadbadb06755) * feat(Holiday List): display localized country name (cherry picked from commit 4888d75e72fe53d98d62e734f3c6f0abe8edd0e3) * fix: German translations of Holiday List (cherry picked from commit 509061f05be9329ab2aa4801c33091818cb787fb) # Conflicts: # erpnext/translations/de.csv * fix(Holiday List): use current user's language For consistency with "weekly off" descriptions (cherry picked from commit 8271a39cdb00ebf3d20ab635b298ee1edbf19311) * fix(Holiday List): allow empty value (cherry picked from commit 8aff5a1dab6292dd85f94d5570ef9e5cef3a506c) * refactor(Holiday List): use autocomplete fieldtype (cherry picked from commit dab9688410cca4565f28cfcd7660dbce3732ae02) * chore: resolve merge conflicts --------- Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- .../doctype/holiday_list/holiday_list.js | 30 +- .../doctype/holiday_list/holiday_list.json | 436 +++--------------- .../doctype/holiday_list/holiday_list.py | 84 +++- .../doctype/holiday_list/test_holiday_list.py | 37 +- erpnext/translations/de.csv | 29 +- pyproject.toml | 1 + 6 files changed, 210 insertions(+), 407 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js index ea033c7ed92..90d9f1b6f50 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list.js @@ -6,13 +6,41 @@ frappe.ui.form.on("Holiday List", { if (frm.doc.holidays) { frm.set_value("total_holidays", frm.doc.holidays.length); } + + frm.call("get_supported_countries").then(r => { + frm.subdivisions_by_country = r.message.subdivisions_by_country; + frm.fields_dict.country.set_data( + r.message.countries.sort((a, b) => a.label.localeCompare(b.label)) + ); + + if (frm.doc.country) { + frm.trigger("set_subdivisions"); + } + }); }, from_date: function(frm) { if (frm.doc.from_date && !frm.doc.to_date) { var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); } - } + }, + country: function(frm) { + frm.set_value("subdivision", ""); + + if (frm.doc.country) { + frm.trigger("set_subdivisions"); + } + }, + set_subdivisions: function(frm) { + const subdivisions = [...frm.subdivisions_by_country[frm.doc.country]]; + if (subdivisions && subdivisions.length > 0) { + frm.fields_dict.subdivision.set_data(subdivisions); + frm.set_df_property("subdivision", "hidden", 0); + } else { + frm.fields_dict.subdivision.set_data([]); + frm.set_df_property("subdivision", "hidden", 1); + } + }, }); frappe.tour["Holiday List"] = [ diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json index 4bbe6a6cb21..45671d181b3 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.json +++ b/erpnext/setup/doctype/holiday_list/holiday_list.json @@ -1,480 +1,166 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:holiday_list_name", - "beta": 0, "creation": "2013-01-10 16:34:14", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "holiday_list_name", + "from_date", + "to_date", + "column_break_4", + "total_holidays", + "add_weekly_holidays", + "weekly_off", + "get_weekly_off_dates", + "add_local_holidays", + "country", + "subdivision", + "get_local_holidays", + "holidays_section", + "holidays", + "clear_table", + "section_break_9", + "color" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holiday_list_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Holiday List Name", - "length": 0, - "no_copy": 0, "oldfieldname": "holiday_list_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "to_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_holidays", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Total Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, + "depends_on": "eval: doc.from_date && doc.to_date", "fieldname": "add_weekly_holidays", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Add Weekly Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Add Weekly Holidays" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "weekly_off", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Weekly Off", - "length": 0, "no_copy": 1, "options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_weekly_off_dates", "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Add to Holidays", - "length": 0, - "no_copy": 0, - "options": "get_weekly_off_dates", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "get_weekly_off_dates" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holidays_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Holidays" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holidays", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Holidays", - "length": 0, - "no_copy": 0, "oldfieldname": "holiday_list_details", "oldfieldtype": "Table", - "options": "Holiday", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Holiday" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "clear_table", "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Clear Table", - "length": 0, - "no_copy": 0, - "options": "clear_table", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "clear_table" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "color", "fieldtype": "Color", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 + }, + { + "fieldname": "country", + "fieldtype": "Autocomplete", + "label": "Country" + }, + { + "depends_on": "country", + "fieldname": "subdivision", + "fieldtype": "Autocomplete", + "label": "Subdivision" + }, + { + "collapsible": 1, + "depends_on": "eval: doc.from_date && doc.to_date", + "fieldname": "add_local_holidays", + "fieldtype": "Section Break", + "label": "Add Local Holidays" + }, + { + "fieldname": "get_local_holidays", + "fieldtype": "Button", + "label": "Add to Holidays", + "options": "get_local_holidays" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-calendar", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-03 07:22:46.474096", + "links": [], + "modified": "2023-07-14 13:28:53.156421", "modified_by": "Administrator", "module": "Setup", "name": "Holiday List", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 84d0d352871..2ef4e655b2d 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -3,11 +3,15 @@ import json +from datetime import date import frappe +from babel import Locale from frappe import _, throw from frappe.model.document import Document -from frappe.utils import cint, formatdate, getdate, today +from frappe.utils import formatdate, getdate, today +from holidays import country_holidays +from holidays.utils import list_supported_countries class OverlapError(frappe.ValidationError): @@ -21,25 +25,66 @@ class HolidayList(Document): @frappe.whitelist() def get_weekly_off_dates(self): - self.validate_values() - date_list = self.get_weekly_off_date_list(self.from_date, self.to_date) - last_idx = max( - [cint(d.idx) for d in self.get("holidays")] - or [ - 0, - ] - ) - for i, d in enumerate(date_list): - ch = self.append("holidays", {}) - ch.description = _(self.weekly_off) - ch.holiday_date = d - ch.weekly_off = 1 - ch.idx = last_idx + i + 1 - - def validate_values(self): if not self.weekly_off: throw(_("Please select weekly off day")) + existing_holidays = self.get_holidays() + + for d in self.get_weekly_off_date_list(self.from_date, self.to_date): + if d in existing_holidays: + continue + + self.append("holidays", {"description": _(self.weekly_off), "holiday_date": d, "weekly_off": 1}) + + self.sort_holidays() + + @frappe.whitelist() + def get_supported_countries(self): + subdivisions_by_country = list_supported_countries() + countries = [ + {"value": country, "label": local_country_name(country)} + for country in subdivisions_by_country.keys() + ] + return { + "countries": countries, + "subdivisions_by_country": subdivisions_by_country, + } + + @frappe.whitelist() + def get_local_holidays(self): + if not self.country: + throw(_("Please select a country")) + + existing_holidays = self.get_holidays() + from_date = getdate(self.from_date) + to_date = getdate(self.to_date) + + for holiday_date, holiday_name in country_holidays( + self.country, + subdiv=self.subdivision, + years=[from_date.year, to_date.year], + language=frappe.local.lang, + ).items(): + if holiday_date in existing_holidays: + continue + + if holiday_date < from_date or holiday_date > to_date: + continue + + self.append( + "holidays", {"description": holiday_name, "holiday_date": holiday_date, "weekly_off": 0} + ) + + self.sort_holidays() + + def sort_holidays(self): + self.holidays.sort(key=lambda x: getdate(x.holiday_date)) + for i in range(len(self.holidays)): + self.holidays[i].idx = i + 1 + + def get_holidays(self) -> list[date]: + return [getdate(holiday.holiday_date) for holiday in self.holidays] + def validate_days(self): if getdate(self.from_date) > getdate(self.to_date): throw(_("To Date cannot be before From Date")) @@ -120,3 +165,8 @@ def is_holiday(holiday_list, date=None): ) else: return False + + +def local_country_name(country_code: str) -> str: + """Return the localized country name for the given country code.""" + return Locale.parse(frappe.local.lang).territories.get(country_code, country_code) diff --git a/erpnext/setup/doctype/holiday_list/test_holiday_list.py b/erpnext/setup/doctype/holiday_list/test_holiday_list.py index d32cfe82650..23b08fd1170 100644 --- a/erpnext/setup/doctype/holiday_list/test_holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/test_holiday_list.py @@ -3,7 +3,7 @@ import unittest from contextlib import contextmanager -from datetime import timedelta +from datetime import date, timedelta import frappe from frappe.utils import getdate @@ -23,6 +23,41 @@ class TestHolidayList(unittest.TestCase): fetched_holiday_list = frappe.get_value("Holiday List", holiday_list.name) self.assertEqual(holiday_list.name, fetched_holiday_list) + def test_weekly_off(self): + holiday_list = frappe.new_doc("Holiday List") + holiday_list.from_date = "2023-01-01" + holiday_list.to_date = "2023-02-28" + holiday_list.weekly_off = "Sunday" + holiday_list.get_weekly_off_dates() + + holidays = [holiday.holiday_date for holiday in holiday_list.holidays] + + self.assertNotIn(date(2022, 12, 25), holidays) + self.assertIn(date(2023, 1, 1), holidays) + self.assertIn(date(2023, 1, 8), holidays) + self.assertIn(date(2023, 1, 15), holidays) + self.assertIn(date(2023, 1, 22), holidays) + self.assertIn(date(2023, 1, 29), holidays) + self.assertIn(date(2023, 2, 5), holidays) + self.assertIn(date(2023, 2, 12), holidays) + self.assertIn(date(2023, 2, 19), holidays) + self.assertIn(date(2023, 2, 26), holidays) + self.assertNotIn(date(2023, 3, 5), holidays) + + def test_local_holidays(self): + holiday_list = frappe.new_doc("Holiday List") + holiday_list.from_date = "2023-04-01" + holiday_list.to_date = "2023-04-30" + holiday_list.country = "DE" + holiday_list.subdivision = "SN" + holiday_list.get_local_holidays() + + holidays = [holiday.holiday_date for holiday in holiday_list.holidays] + self.assertNotIn(date(2023, 1, 1), holidays) + self.assertIn(date(2023, 4, 7), holidays) + self.assertIn(date(2023, 4, 10), holidays) + self.assertNotIn(date(2023, 5, 1), holidays) + def make_holiday_list( name, from_date=getdate() - timedelta(days=10), to_date=getdate(), holiday_dates=None diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 100be91164a..840ba6b9d0d 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1221,7 +1221,7 @@ High Sensitivity,Hohe Empfindlichkeit, Hold,Anhalten, Hold Invoice,Rechnung zurückhalten, Holiday,Urlaub, -Holiday List,Urlaubsübersicht, +Holiday List,Feiertagsliste, Hotel Rooms of type {0} are unavailable on {1},Hotelzimmer vom Typ {0} sind auf {1} nicht verfügbar, Hotels,Hotels, Hourly,Stündlich, @@ -3330,7 +3330,7 @@ Workflow,Workflow, Working,In Bearbeitung, Working Hours,Arbeitszeit, Workstation,Arbeitsplatz, -Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Urlaubsliste geschlossen: {0}, +Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Feiertagsliste geschlossen: {0}, Wrapping up,Aufwickeln, Wrong Password,Falsches Passwort, Year start date or end date is overlapping with {0}. To avoid please set company,"Jahresbeginn oder Enddatum überlappt mit {0}. Bitte ein Unternehmen wählen, um dies zu verhindern", @@ -3599,6 +3599,7 @@ Activity,Aktivität, Add / Manage Email Accounts.,Hinzufügen/Verwalten von E-Mail-Konten, Add Child,Unterpunkt hinzufügen, Add Loan Security,Darlehenssicherheit hinzufügen, +Add Local Holidays,Lokale Feiertage hinzufügen, Add Multiple,Mehrere hinzufügen, Add Participants,Teilnehmer hinzufügen, Add to Featured Item,Zum empfohlenen Artikel hinzufügen, @@ -4088,6 +4089,7 @@ Stock Ledger ID,Bestandsbuch-ID, Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.,Der Bestandswert ({0}) und der Kontostand ({1}) sind für das Konto {2} und die verknüpften Lager nicht synchron., Stores - {0},Stores - {0}, Student with email {0} does not exist,Der Student mit der E-Mail-Adresse {0} existiert nicht, +Subdivision,Teilgebiet, Submit Review,Bewertung abschicken, Submitted,Gebucht, Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten, @@ -4236,6 +4238,7 @@ Mode Of Payment,Zahlungsart, No students Found,Keine Schüler gefunden, Not in Stock,Nicht lagernd, Please select a Customer,Bitte wählen Sie einen Kunden aus, +Please select a country,Bitte wählen Sie ein Land aus, Printed On,Gedruckt auf, Received From,Erhalten von, Sales Person,Verkäufer, @@ -6546,7 +6549,7 @@ Reports to,Vorgesetzter, Attendance and Leave Details,Anwesenheits- und Urlaubsdetails, Leave Policy,Urlaubsrichtlinie, Attendance Device ID (Biometric/RF tag ID),Anwesenheitsgeräte-ID (biometrische / RF-Tag-ID), -Applicable Holiday List,Geltende Urlaubsliste, +Applicable Holiday List,Geltende Feiertagsliste, Default Shift,Standardverschiebung, Salary Details,Gehaltsdetails, Salary Mode,Gehaltsmodus, @@ -6708,15 +6711,15 @@ More Details,Mehr Details, Expense Claim Account,Kostenabrechnung Konto, Expense Claim Advance,Auslagenvorschuss, Unclaimed amount,Nicht beanspruchter Betrag, -Expense Claim Detail,Aufwandsabrechnungsdetail, -Expense Date,Datum der Aufwendung, -Expense Claim Type,Art der Aufwandsabrechnung, -Holiday List Name,Urlaubslistenname, -Total Holidays,Insgesamt Feiertage, -Add Weekly Holidays,Wöchentliche Feiertage hinzufügen, +Expense Claim Detail,Auslage, +Expense Date,Datum der Auslage, +Expense Claim Type,Art der Auslagenabrechnung, +Holiday List Name,Name der Feiertagsliste, +Total Holidays,Insgesamt freie Tage, +Add Weekly Holidays,Wöchentlich freie Tage hinzufügen, Weekly Off,Wöchentlich frei, -Add to Holidays,Zu Feiertagen hinzufügen, -Holidays,Ferien, +Add to Holidays,Zu freien Tagen hinzufügen, +Holidays,Arbeitsfreie Tage, Clear Table,Tabelle leeren, HR Settings,Einstellungen zum Modul Personalwesen, Employee Settings,Mitarbeitereinstellungen, @@ -6826,7 +6829,7 @@ Transaction Name,Transaktionsname, Is Carry Forward,Ist Übertrag, Is Expired,Ist abgelaufen, Is Leave Without Pay,Ist unbezahlter Urlaub, -Holiday List for Optional Leave,Urlaubsliste für optionalen Urlaub, +Holiday List for Optional Leave,Feiertagsliste für optionalen Urlaub, Leave Allocations,Zuteilungen verlassen, Leave Policy Details,Urlaubsrichtliniendetails, Leave Policy Detail,Urlaubsrichtliniendetail, @@ -7786,7 +7789,7 @@ Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Org Change Abbreviation,Abkürzung ändern, Parent Company,Muttergesellschaft, Default Values,Standardwerte, -Default Holiday List,Standard-Urlaubsliste, +Default Holiday List,Standard Feiertagsliste, Default Selling Terms,Standardverkaufsbedingungen, Default Buying Terms,Standard-Einkaufsbedingungen, Create Chart Of Accounts Based On,"Kontenplan erstellen, basierend auf", diff --git a/pyproject.toml b/pyproject.toml index 9b48e6b9668..7d1e7af50e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ dependencies = [ "Unidecode~=1.2.0", "redisearch~=2.1.0", "rapidfuzz~=2.15.0", + "holidays~=0.28", # integration dependencies "gocardless-pro~=1.22.0", From 68d35b08f6783b034f42236d338537e0c56a702a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 15 Jul 2023 10:10:19 +0530 Subject: [PATCH 15/26] refactor: `Batch Item Expiry Status` report (backport #36106) (#36111) refactor: `Batch Item Expiry Status` report (#36106) (cherry picked from commit 5f307f92e0f9f90c79cfac802c5aa05de3a9d6d8) Co-authored-by: s-aga-r --- .../batch_item_expiry_status.js | 16 +- .../batch_item_expiry_status.py | 145 +++++++----------- 2 files changed, 74 insertions(+), 87 deletions(-) diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js index a7d7149c382..48a72a2bfe5 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js @@ -9,13 +9,27 @@ frappe.query_reports["Batch Item Expiry Status"] = { "fieldtype": "Date", "width": "80", "default": frappe.sys_defaults.year_start_date, + "reqd": 1, }, { "fieldname":"to_date", "label": __("To Date"), "fieldtype": "Date", "width": "80", - "default": frappe.datetime.get_today() + "default": frappe.datetime.get_today(), + "reqd": 1, + }, + { + "fieldname":"item", + "label": __("Item"), + "fieldtype": "Link", + "options": "Item", + "width": "100", + "get_query": function () { + return { + filters: {"has_batch_no": 1} + } + } } ] } diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py index ef7d6e6816c..5661e8b2609 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py @@ -4,113 +4,86 @@ import frappe from frappe import _ -from frappe.query_builder.functions import IfNull -from frappe.utils import cint, getdate +from frappe.query_builder.functions import Date def execute(filters=None): - if not filters: - filters = {} + validate_filters(filters) - float_precision = cint(frappe.db.get_default("float_precision")) or 3 - - columns = get_columns(filters) - item_map = get_item_details(filters) - iwb_map = get_item_warehouse_batch_map(filters, float_precision) - - data = [] - for item in sorted(iwb_map): - for wh in sorted(iwb_map[item]): - for batch in sorted(iwb_map[item][wh]): - qty_dict = iwb_map[item][wh][batch] - - data.append( - [ - item, - item_map[item]["item_name"], - item_map[item]["description"], - wh, - batch, - frappe.db.get_value("Batch", batch, "expiry_date"), - qty_dict.expiry_status, - ] - ) + columns = get_columns() + data = get_data(filters) return columns, data -def get_columns(filters): - """return columns based on filters""" +def validate_filters(filters): + if not filters: + frappe.throw(_("Please select the required filters")) - columns = ( - [_("Item") + ":Link/Item:100"] - + [_("Item Name") + "::150"] - + [_("Description") + "::150"] - + [_("Warehouse") + ":Link/Warehouse:100"] - + [_("Batch") + ":Link/Batch:100"] - + [_("Expires On") + ":Date:90"] - + [_("Expiry (In Days)") + ":Int:120"] - ) - - return columns - - -def get_stock_ledger_entries(filters): if not filters.get("from_date"): frappe.throw(_("'From Date' is required")) if not filters.get("to_date"): frappe.throw(_("'To Date' is required")) - sle = frappe.qb.DocType("Stock Ledger Entry") - query = ( - frappe.qb.from_(sle) - .select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty) - .where( - (sle.is_cancelled == 0) - & (sle.docstatus < 2) - & (IfNull(sle.batch_no, "") != "") - & (sle.posting_date <= filters["to_date"]) - ) - .orderby(sle.item_code, sle.warehouse) + +def get_columns(): + return ( + [_("Item") + ":Link/Item:150"] + + [_("Item Name") + "::150"] + + [_("Batch") + ":Link/Batch:150"] + + [_("Stock UOM") + ":Link/UOM:100"] + + [_("Quantity") + ":Float:100"] + + [_("Expires On") + ":Date:100"] + + [_("Expiry (In Days)") + ":Int:130"] ) - return query.run(as_dict=True) +def get_data(filters): + data = [] -def get_item_warehouse_batch_map(filters, float_precision): - sle = get_stock_ledger_entries(filters) - iwb_map = {} - - from_date = getdate(filters["from_date"]) - to_date = getdate(filters["to_date"]) - - for d in sle: - iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault( - d.batch_no, frappe._dict({"expires_on": None, "expiry_status": None}) + for batch in get_batch_details(filters): + data.append( + [ + batch.item, + batch.item_name, + batch.name, + batch.stock_uom, + batch.batch_qty, + batch.expiry_date, + max((batch.expiry_date - frappe.utils.datetime.date.today()).days, 0) + if batch.expiry_date + else None, + ] ) - qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no] - - expiry_date_unicode = frappe.db.get_value("Batch", d.batch_no, "expiry_date") - qty_dict.expires_on = expiry_date_unicode - - exp_date = frappe.utils.data.getdate(expiry_date_unicode) - qty_dict.expires_on = exp_date - - expires_in_days = (exp_date - frappe.utils.datetime.date.today()).days - - if expires_in_days > 0: - qty_dict.expiry_status = expires_in_days - else: - qty_dict.expiry_status = 0 - - return iwb_map + return data -def get_item_details(filters): - item_map = {} - for d in (frappe.qb.from_("Item").select("name", "item_name", "description")).run(as_dict=True): - item_map.setdefault(d.name, d) +def get_batch_details(filters): + batch = frappe.qb.DocType("Batch") + query = ( + frappe.qb.from_(batch) + .select( + batch.name, + batch.creation, + batch.expiry_date, + batch.item, + batch.item_name, + batch.stock_uom, + batch.batch_qty, + ) + .where( + (batch.disabled == 0) + & (batch.batch_qty > 0) + & ( + (Date(batch.creation) >= filters["from_date"]) & (Date(batch.creation) <= filters["to_date"]) + ) + ) + .orderby(batch.creation) + ) - return item_map + if filters.get("item"): + query = query.where(batch.item == filters["item"]) + + return query.run(as_dict=True) From 619898350af2090fb4f68e1638fb7c77da12c589 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 15 Jul 2023 10:11:17 +0530 Subject: [PATCH 16/26] perf: index in `Item` and `Item Variant Attribute` (backport #36133) (#36144) * perf: index `variant_of` and `attribute` in `Item Variant Attribute` (cherry picked from commit e4128a5c91badd63c70a1e72e6d244727f9f059e) * perf: index `disabled` in `Item` (cherry picked from commit 04400eb2e41997adcf28338752fc3cfb1a617730) --------- Co-authored-by: s-aga-r --- erpnext/stock/doctype/item/item.json | 5 +- .../item_variant_attribute.json | 432 +++--------------- 2 files changed, 79 insertions(+), 358 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index e519b9b2133..7f4ba032e86 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -194,7 +194,8 @@ "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disabled" + "label": "Disabled", + "search_index": 1 }, { "default": "0", @@ -911,7 +912,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, - "modified": "2023-02-14 04:48:26.343620", + "modified": "2023-07-14 17:18:18.658942", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json index 6d02ea9db0c..9699ecbb3db 100644 --- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json +++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json @@ -1,370 +1,90 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2015-05-19 05:12:30.344797", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, + "actions": [], + "creation": "2015-05-19 05:12:30.344797", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "variant_of", + "attribute", + "column_break_2", + "attribute_value", + "numeric_values", + "section_break_4", + "from_range", + "increment", + "column_break_8", + "to_range" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "variant_of", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Variant Of", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "variant_of", + "fieldtype": "Link", + "label": "Variant Of", + "options": "Item", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attribute", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attribute", - "length": 0, - "no_copy": 0, - "options": "Item Attribute", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "attribute", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Attribute", + "options": "Item Attribute", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "attribute_value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attribute Value", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "attribute_value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Attribute Value" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "has_variants", - "fieldname": "numeric_values", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Numeric Values", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "has_variants", + "fieldname": "numeric_values", + "fieldtype": "Check", + "label": "Numeric Values" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "numeric_values", - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "numeric_values", + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "from_range", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_range", + "fieldtype": "Float", + "label": "From Range" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "increment", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Increment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "increment", + "fieldtype": "Float", + "label": "Increment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "to_range", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "to_range", + "fieldtype": "Float", + "label": "To Range" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-03 15:36:59.129006", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Variant Attribute", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2023-07-14 17:15:19.112119", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Variant Attribute", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file From adb69188340e5ec841caf70d67f3c81c554b958b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 16 Jul 2023 11:34:42 +0530 Subject: [PATCH 17/26] perf: send SLA doctypes in boot This request is fired on every load, data rarely if ever changes though. (cherry picked from commit bd9ef74ef7ebb56b8ba8691517124604ba60a628) --- erpnext/hooks.py | 5 + erpnext/public/js/utils.js | 158 +++++++++--------- .../service_level_agreement.py | 10 ++ 3 files changed, 90 insertions(+), 83 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b859ce091cb..3b3c6e00e22 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -612,3 +612,8 @@ global_search_doctypes = { additional_timeline_content = { "*": ["erpnext.telephony.doctype.call_log.call_log.get_linked_call_logs"] } + + +extend_bootinfo = [ + "erpnext.support.doctype.service_level_agreement.service_level_agreement.add_sla_doctypes", +] diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 541f6720732..e0dab49d175 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -822,95 +822,87 @@ $(document).on('app_ready', function() { // Show SLA dashboard $(document).on('app_ready', function() { - frappe.call({ - method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_sla_doctypes', - callback: function(r) { - if (!r.message) - return; + $.each(frappe.boot.service_level_agreement_doctypes, function(_i, d) { + frappe.ui.form.on(d, { + onload: function(frm) { + if (!frm.doc.service_level_agreement) + return; - $.each(r.message, function(_i, d) { - frappe.ui.form.on(d, { - onload: function(frm) { - if (!frm.doc.service_level_agreement) - return; - - frappe.call({ - method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters', - args: { - doctype: frm.doc.doctype, - name: frm.doc.service_level_agreement, - customer: frm.doc.customer - }, - callback: function (r) { - if (r && r.message) { - frm.set_query('priority', function() { - return { - filters: { - 'name': ['in', r.message.priority], - } - }; - }); - frm.set_query('service_level_agreement', function() { - return { - filters: { - 'name': ['in', r.message.service_level_agreements], - } - }; - }); - } - } - }); + frappe.call({ + method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters', + args: { + doctype: frm.doc.doctype, + name: frm.doc.service_level_agreement, + customer: frm.doc.customer }, - - refresh: function(frm) { - if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement - && ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { - frappe.call({ - 'method': 'frappe.client.get', - args: { - doctype: 'Service Level Agreement', - name: frm.doc.service_level_agreement - }, - callback: function(data) { - let statuses = data.message.pause_sla_on; - const hold_statuses = []; - $.each(statuses, (_i, entry) => { - hold_statuses.push(entry.status); - }); - if (hold_statuses.includes(frm.doc.status)) { - frm.dashboard.clear_headline(); - let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])}; - frm.dashboard.set_headline_alert( - '
' + - '
' + - ''+ message.msg +' ' + - '
' + - '
' - ); - } else { - set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution); + callback: function (r) { + if (r && r.message) { + frm.set_query('priority', function() { + return { + filters: { + 'name': ['in', r.message.priority], } - } + }; + }); + frm.set_query('service_level_agreement', function() { + return { + filters: { + 'name': ['in', r.message.service_level_agreements], + } + }; }); - } else if (frm.doc.service_level_agreement) { - frm.dashboard.clear_headline(); - - let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ? - {'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} : - {'indicator': 'red', 'msg': 'Service Level Agreement Failed'}; - - frm.dashboard.set_headline_alert( - '
' + - '
' + - ' ' + - '
' + - '
' - ); } - }, + } }); - }); - } + }, + + refresh: function(frm) { + if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement + && ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { + frappe.call({ + 'method': 'frappe.client.get', + args: { + doctype: 'Service Level Agreement', + name: frm.doc.service_level_agreement + }, + callback: function(data) { + let statuses = data.message.pause_sla_on; + const hold_statuses = []; + $.each(statuses, (_i, entry) => { + hold_statuses.push(entry.status); + }); + if (hold_statuses.includes(frm.doc.status)) { + frm.dashboard.clear_headline(); + let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])}; + frm.dashboard.set_headline_alert( + '
' + + '
' + + ''+ message.msg +' ' + + '
' + + '
' + ); + } else { + set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution); + } + } + }); + } else if (frm.doc.service_level_agreement) { + frm.dashboard.clear_headline(); + + let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ? + {'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} : + {'indicator': 'red', 'msg': 'Service Level Agreement Failed'}; + + frm.dashboard.set_headline_alert( + '
' + + '
' + + ' ' + + '
' + + '
' + ); + } + }, + }); }); }); diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 7f4e9efa948..2a023e09c49 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -21,6 +21,7 @@ from frappe.utils import ( time_diff_in_seconds, to_timedelta, ) +from frappe.utils.caching import redis_cache from frappe.utils.nestedset import get_ancestors_of from frappe.utils.safe_exec import get_safe_globals @@ -209,6 +210,10 @@ class ServiceLevelAgreement(Document): def on_update(self): set_documents_with_active_service_level_agreement() + def clear_cache(self): + get_sla_doctypes.clear_cache() + return super().clear_cache() + def create_docfields(self, meta, service_level_agreement_fields): last_index = len(meta.fields) @@ -990,6 +995,7 @@ def get_user_time(user, to_string=False): @frappe.whitelist() +@redis_cache() def get_sla_doctypes(): doctypes = [] data = frappe.get_all("Service Level Agreement", {"enabled": 1}, ["document_type"], distinct=1) @@ -998,3 +1004,7 @@ def get_sla_doctypes(): doctypes.append(entry.document_type) return doctypes + + +def add_sla_doctypes(bootinfo): + bootinfo.service_level_agreement_doctypes = get_sla_doctypes() From c7a97cfb314d97904b5ec1911537e83866cc1fa4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 11:35:32 +0530 Subject: [PATCH 18/26] chore: add validation for account type of party type and account (#36141) chore: add validation for account type of party type and account (#36141) chore: add validation to check if account type of party type and account match (cherry picked from commit 305c37917ffe7188c9270f8f5ca8b37301990889) Co-authored-by: Anand Baburajan --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 0411fd1104e..594339591f5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -396,6 +396,15 @@ class JournalEntry(AccountsController): d.idx, d.account ) ) + elif ( + d.party_type + and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type + ): + frappe.throw( + _("Row {0}: Account {1} and Party Type {2} have different account types").format( + d.idx, d.account, d.party_type + ) + ) def check_credit_limit(self): customers = list( From b877fa60dba309c5dc7809c01a315b1aa6fb2214 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 11:52:37 +0530 Subject: [PATCH 19/26] fix: Remove current fiscal year from Global Defaults (#35960) * fix: Remove current fiscal year from Global Defaults (#35960) * fix: Remove current fiscal year from Global Defaults * fix: Remove button to set default * fix: Add utils to get fiscal year * fix: Incorrect import * feat: Add hook for naming series parser (cherry picked from commit 6270607c6d49f91f11274cc4d5d1ef0b13eb19f1) # Conflicts: # erpnext/accounts/doctype/fiscal_year/fiscal_year.py # erpnext/patches.txt # erpnext/public/js/utils.js * chore: resolve conflicts --------- Co-authored-by: Deepesh Garg --- .../doctype/fiscal_year/fiscal_year.js | 11 - .../doctype/fiscal_year/fiscal_year.py | 25 +- .../consolidated_financial_statement.js | 6 +- .../deferred_revenue_and_expense.js | 6 +- .../deferred_revenue_and_expense.py | 5 +- .../test_deferred_revenue_and_expense.py | 7 +- .../dimension_wise_accounts_balance_report.js | 2 +- .../profitability_analysis.js | 2 +- .../report/trial_balance/trial_balance.js | 2 +- .../trial_balance_for_party.js | 2 +- erpnext/accounts/utils.py | 5 + .../fixed_asset_register.js | 4 +- erpnext/hooks.py | 5 + .../job_card_summary/job_card_summary.js | 2 +- erpnext/patches.txt | 1 + erpnext/public/js/financial_statements.js | 6 +- erpnext/public/js/utils.js | 17 + .../fichier_des_ecritures_comptables_[fec].js | 2 +- erpnext/regional/report/irs_1099/irs_1099.js | 2 +- .../global_defaults/global_defaults.json | 419 ++++-------------- .../global_defaults/global_defaults.py | 17 - .../operations/install_fixtures.py | 2 - 22 files changed, 137 insertions(+), 413 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js index bc77dac1cdd..508b2eaf2a4 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js @@ -8,17 +8,6 @@ frappe.ui.form.on('Fiscal Year', { frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)); } }, - refresh: function (frm) { - if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) { - frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm)); - frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'")); - } else { - frm.set_intro(""); - } - }, - set_as_default: function(frm) { - return frm.call('set_as_default'); - }, year_start_date: function(frm) { if (!frm.doc.is_short_year) { let year_end_date = diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index c3e83ad168f..a945860a711 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -4,7 +4,7 @@ import frappe from dateutil.relativedelta import relativedelta -from frappe import _, msgprint +from frappe import _ from frappe.model.document import Document from frappe.utils import add_days, add_years, cstr, getdate @@ -14,22 +14,6 @@ class FiscalYearIncorrectDate(frappe.ValidationError): class FiscalYear(Document): - @frappe.whitelist() - def set_as_default(self): - frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name) - global_defaults = frappe.get_doc("Global Defaults") - global_defaults.check_permission("write") - global_defaults.on_update() - - # clear cache - frappe.clear_cache() - - msgprint( - _( - "{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect." - ).format(self.name) - ) - def validate(self): self.validate_dates() self.validate_overlap() @@ -77,13 +61,6 @@ class FiscalYear(Document): frappe.cache().delete_value("fiscal_years") def on_trash(self): - global_defaults = frappe.get_doc("Global Defaults") - if global_defaults.current_fiscal_year == self.name: - frappe.throw( - _( - "You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings" - ).format(self.name) - ) frappe.cache().delete_value("fiscal_years") def validate_overlap(self): diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index dd965a9813e..d58fd95a840 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -49,7 +49,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, on_change: () => { frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) { @@ -65,7 +65,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, on_change: () => { frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) { @@ -139,7 +139,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { return value; }, onload: function() { - let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js index 0056b9e8f56..96e0c844ca5 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js @@ -48,7 +48,7 @@ function get_filters() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 }, { @@ -56,7 +56,7 @@ function get_filters() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 }, { @@ -100,7 +100,7 @@ frappe.query_reports["Deferred Revenue and Expense"] = { return default_formatter(value, row, column, data); }, onload: function(report){ - let fiscal_year = frappe.defaults.get_user_default("fiscal_year"); + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index 3e11643776e..cad5325c6e9 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -4,9 +4,10 @@ import frappe from frappe import _, qb from frappe.query_builder import Column, functions -from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, rounded +from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, getdate, rounded from erpnext.accounts.report.financial_statements import get_period_list +from erpnext.accounts.utils import get_fiscal_year class Deferred_Item(object): @@ -226,7 +227,7 @@ class Deferred_Revenue_and_Expense_Report(object): # If no filters are provided, get user defaults if not filters: - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date=getdate())) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py index 023ff225eea..c84b843f1fd 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py @@ -10,6 +10,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import ( Deferred_Revenue_and_Expense_Report, ) +from erpnext.accounts.utils import get_fiscal_year from erpnext.buying.doctype.supplier.test_supplier import create_supplier from erpnext.stock.doctype.item.test_item import create_item @@ -116,7 +117,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), @@ -209,7 +210,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), @@ -297,7 +298,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js index ea05a35b259..9d416db4fdd 100644 --- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js +++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js @@ -18,7 +18,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 889ede5a824..6caebd34a2f 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -25,7 +25,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 078b06519f1..e45c3adcb6d 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -17,7 +17,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js index 0e93035a35d..0f7578cdc17 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js @@ -16,7 +16,7 @@ frappe.query_reports["Trial Balance for Party"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 7ab1b1c2768..84c71b376ef 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1106,6 +1106,11 @@ def get_autoname_with_number(number_value, doc_title, company): return " - ".join(parts) +def parse_naming_series_variable(doc, variable): + if variable == "FY": + return get_fiscal_year(date=doc.get("posting_date"), company=doc.get("company"))[0] + + @frappe.whitelist() def get_coa(doctype, parent, is_root, chart=None): from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import ( diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index b788a32d6ab..48b17f58fb2 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -82,7 +82,7 @@ frappe.query_reports["Fixed Asset Register"] = { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", }, { @@ -90,7 +90,7 @@ frappe.query_reports["Fixed Asset Register"] = { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", }, { diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 3b3c6e00e22..115d5e6ea88 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -359,6 +359,11 @@ doc_events = { }, } +# function should expect the variable and doc as arguments +naming_series_variables = { + "FY": "erpnext.accounts.utils.parse_naming_series_variable", +} + # On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled. # to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled. # if payment entry not in auto cancel exempted doctypes it will cancel payment entry. diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index 782ce8110a8..a874f224820 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -17,7 +17,7 @@ frappe.query_reports["Job Card Summary"] = { label: __("Fiscal Year"), fieldtype: "Link", options: "Fiscal Year", - default: frappe.defaults.get_user_default("fiscal_year"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), reqd: 1, on_change: function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4b09e0c10f6..75f728afa88 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -337,3 +337,4 @@ erpnext.patches.v14_0.enable_allow_existing_serial_no erpnext.patches.v14_0.set_report_in_process_SOA erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.update_closing_balances #15-07-2023 +execute:frappe.defaults.clear_default("fiscal_year") \ No newline at end of file diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 2b50a75e728..959cf507d53 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -56,7 +56,7 @@ erpnext.financial_statements = { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters() - let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); @@ -137,7 +137,7 @@ function get_filters() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, @@ -146,7 +146,7 @@ function get_filters() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index e0dab49d175..ec331289e9a 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -350,6 +350,23 @@ $.extend(erpnext.utils, { } }, + + get_fiscal_year: function(date) { + let fiscal_year = ''; + frappe.call({ + method: "erpnext.accounts.utils.get_fiscal_year", + args: { + date: date + }, + async: false, + callback: function(r) { + if (r.message) { + fiscal_year = r.message[0]; + } + } + }); + return fiscal_year; + } }); erpnext.utils.select_alternate_items = function(opts) { diff --git a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js index a4c7640c813..b85b58f636a 100644 --- a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js +++ b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js @@ -16,7 +16,7 @@ frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 } ], diff --git a/erpnext/regional/report/irs_1099/irs_1099.js b/erpnext/regional/report/irs_1099/irs_1099.js index 070ff43f78c..b3508e40a9f 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.js +++ b/erpnext/regional/report/irs_1099/irs_1099.js @@ -17,7 +17,7 @@ frappe.query_reports["IRS 1099"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "width": 80, }, diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.json b/erpnext/setup/doctype/global_defaults/global_defaults.json index bafb97a5d80..823d2ba7d7b 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.json +++ b/erpnext/setup/doctype/global_defaults/global_defaults.json @@ -1,352 +1,99 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-05-02 17:53:24", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "creation": "2013-05-02 17:53:24", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "default_company", + "country", + "default_distance_unit", + "column_break_8", + "default_currency", + "hide_currency_symbol", + "disable_rounded_total", + "disable_in_words" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_company", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_fiscal_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Current Fiscal Year", - "length": 0, - "no_copy": 0, - "options": "Fiscal Year", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "country", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Country", - "length": 0, - "no_copy": 0, - "options": "Country", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_distance_unit", + "fieldtype": "Link", + "label": "Default Distance Unit", + "options": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "default_distance_unit", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Distance Unit", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "INR", + "fieldname": "default_currency", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Default Currency", + "options": "Currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "INR", - "fieldname": "default_currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Default Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Do not show any symbol like $ etc next to currencies.", + "fieldname": "hide_currency_symbol", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Hide Currency Symbol", + "options": "\nNo\nYes" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Do not show any symbol like $ etc next to currencies.", - "fieldname": "hide_currency_symbol", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Hide Currency Symbol", - "length": 0, - "no_copy": 0, - "options": "\nNo\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "If disable, 'Rounded Total' field will not be visible in any transaction", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disable Rounded Total" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If disable, 'Rounded Total' field will not be visible in any transaction", - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Disable Rounded Total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If disable, 'In Words' field will not be visible in any transaction", - "fieldname": "disable_in_words", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Disable In Words", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "description": "If disable, 'In Words' field will not be visible in any transaction", + "fieldname": "disable_in_words", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disable In Words" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-cog", - "idx": 1, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-10-15 03:08:19.886212", - "modified_by": "Administrator", - "module": "Setup", - "name": "Global Defaults", - "owner": "Administrator", + ], + "icon": "fa fa-cog", + "idx": 1, + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2023-07-01 19:45:00.323953", + "modified_by": "Administrator", + "module": "Setup", + "name": "Global Defaults", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py index 16e94343a37..fc80483e8ee 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.py +++ b/erpnext/setup/doctype/global_defaults/global_defaults.py @@ -10,7 +10,6 @@ from frappe.utils import cint keydict = { # "key in defaults": "key in Global Defaults" - "fiscal_year": "current_fiscal_year", "company": "default_company", "currency": "default_currency", "country": "country", @@ -29,22 +28,6 @@ class GlobalDefaults(Document): for key in keydict: frappe.db.set_default(key, self.get(keydict[key], "")) - # update year start date and year end date from fiscal_year - if self.current_fiscal_year: - if fiscal_year := frappe.get_all( - "Fiscal Year", - filters={"name": self.current_fiscal_year}, - fields=["year_start_date", "year_end_date"], - limit=1, - order_by=None, - ): - ysd = fiscal_year[0].year_start_date or "" - yed = fiscal_year[0].year_end_date or "" - - if ysd and yed: - frappe.db.set_default("year_start_date", ysd.strftime("%Y-%m-%d")) - frappe.db.set_default("year_end_date", yed.strftime("%Y-%m-%d")) - # enable default currency if self.default_currency: frappe.db.set_value("Currency", self.default_currency, "enabled", 1) diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 6bc17718ae0..081a51573e0 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -462,11 +462,9 @@ def install_defaults(args=None): # nosemgrep def set_global_defaults(args): global_defaults = frappe.get_doc("Global Defaults", "Global Defaults") - current_fiscal_year = frappe.get_all("Fiscal Year")[0] global_defaults.update( { - "current_fiscal_year": current_fiscal_year.name, "default_currency": args.get("currency"), "default_company": args.get("company_name"), "country": args.get("country"), From 30cbc682b46f593340ef92686d3a5dd2583975f1 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 17 Jul 2023 15:45:36 +0530 Subject: [PATCH 20/26] chore: use consistent quotes (cherry picked from commit bccb718cc204218703c19e34a846b78752ca5040) --- erpnext/templates/includes/itemised_tax_breakup.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/includes/itemised_tax_breakup.html b/erpnext/templates/includes/itemised_tax_breakup.html index 5652bb1dddd..fbc80de7d0d 100644 --- a/erpnext/templates/includes/itemised_tax_breakup.html +++ b/erpnext/templates/includes/itemised_tax_breakup.html @@ -15,7 +15,7 @@ {% for item, taxes in itemised_tax.items() %} {{ item }} - + {% if doc.get('is_return') %} {{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }} {% else %} @@ -25,7 +25,7 @@ {% for tax_account in tax_accounts %} {% set tax_details = taxes.get(tax_account) %} {% if tax_details %} - + {% if tax_details.tax_rate or not tax_details.tax_amount %} ({{ tax_details.tax_rate }}%) {% endif %} From 2f632d031aa53e1e03763623449e7cde7dca3c55 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 17 Jul 2023 15:21:48 +0530 Subject: [PATCH 21/26] fix: incorrect Reserved Qty for Production Plan in BIN for multi-uom case --- .../production_plan/production_plan.py | 2 +- .../production_plan/test_production_plan.py | 48 +++++++++++++++++++ .../material_request/material_request.py | 12 +++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 0800bdd2af9..76f577bb676 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1534,7 +1534,7 @@ def get_reserved_qty_for_production_plan(item_code, warehouse): frappe.qb.from_(table) .inner_join(child) .on(table.name == child.parent) - .select(Sum(child.required_bom_qty * IfNull(child.conversion_factor, 1.0))) + .select(Sum(child.quantity * IfNull(child.conversion_factor, 1.0))) .where( (table.docstatus == 1) & (child.item_code == item_code) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 75b43ec1c30..208caf7a550 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -926,6 +926,54 @@ class TestProductionPlan(FrappeTestCase): self.assertEqual(after_qty, before_qty) + def test_resered_qty_for_production_plan_for_material_requests_with_multi_UOM(self): + from erpnext.stock.utils import get_or_make_bin + + fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name + bom_item = make_item( + properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1", "purchase_uom": "Nos"} + ).name + + if not frappe.db.exists("UOM Conversion Detail", {"parent": bom_item, "uom": "Nos"}): + doc = frappe.get_doc("Item", bom_item) + doc.append("uoms", {"uom": "Nos", "conversion_factor": 25}) + doc.save() + + make_bom(item=fg_item, raw_materials=[bom_item], source_warehouse="_Test Warehouse - _TC") + + bin_name = get_or_make_bin(bom_item, "_Test Warehouse - _TC") + before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) + + pln = create_production_plan( + item_code=fg_item, planned_qty=100, ignore_existing_ordered_qty=1, stock_uom="_Test UOM 1" + ) + + for row in pln.mr_items: + self.assertEqual(row.uom, "Nos") + self.assertEqual(row.quantity, 4) + + reserved_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) + self.assertEqual(reserved_qty - before_qty, 100.0) + + pln.submit_material_request = 1 + pln.make_work_order() + + for work_order in frappe.get_all( + "Work Order", + fields=["name"], + filters={"production_plan": pln.name}, + ): + wo_doc = frappe.get_doc("Work Order", work_order.name) + wo_doc.source_warehouse = "_Test Warehouse - _TC" + wo_doc.wip_warehouse = "_Test Warehouse 1 - _TC" + wo_doc.fg_warehouse = "_Test Warehouse - _TC" + wo_doc.submit() + + reserved_qty_after_mr = flt( + frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan") + ) + self.assertEqual(reserved_qty_after_mr, before_qty) + def test_skip_available_qty_for_sub_assembly_items(self): from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 9576f409487..b430d03d92b 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -121,8 +121,8 @@ class MaterialRequest(BuyingController): self.title = _("{0} Request for {1}").format(self.material_request_type, items)[:100] def on_submit(self): - self.update_requested_qty() self.update_requested_qty_in_production_plan() + self.update_requested_qty() if self.material_request_type == "Purchase": self.validate_budget() @@ -181,8 +181,8 @@ class MaterialRequest(BuyingController): ) def on_cancel(self): - self.update_requested_qty() self.update_requested_qty_in_production_plan() + self.update_requested_qty() def get_mr_items_ordered_qty(self, mr_items): mr_items_ordered_qty = {} @@ -273,7 +273,13 @@ class MaterialRequest(BuyingController): item_wh_list.append([d.item_code, d.warehouse]) for item_code, warehouse in item_wh_list: - update_bin_qty(item_code, warehouse, {"indented_qty": get_indented_qty(item_code, warehouse)}) + update_bin_qty( + item_code, + warehouse, + { + "indented_qty": get_indented_qty(item_code, warehouse), + }, + ) def update_requested_qty_in_production_plan(self): production_plans = [] From 373b868c1d3a6a10387eee74d0b09c829a361de1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 17 Jul 2023 20:55:16 +0530 Subject: [PATCH 22/26] fix: incorrect Balance Qty in the stock ledger for the item with Serial and Batch no --- .../test_stock_reconciliation.py | 24 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 621b9df1245..88d4e468977 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -314,6 +314,30 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Inactive") self.assertEqual(frappe.db.exists("Batch", batch_no), None) + def test_stock_reco_balance_qty_for_serial_and_batch_item(self): + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + item = create_item("_TestBatchSerialItemReco_1") + item.has_batch_no = 1 + item.create_new_batch = 1 + item.has_serial_no = 1 + item.batch_number_series = "TBS-BATCH1-.##" + item.serial_no_series = "TBS1-.####" + item.save() + + warehouse = "_Test Warehouse for Stock Reco2" + warehouse = create_warehouse(warehouse) + + make_stock_entry(item_code=item.name, target=warehouse, qty=10, basic_rate=100) + + sr = create_stock_reconciliation(item_code=item.name, warehouse=warehouse, qty=10, rate=200) + + qty_after_transaction = frappe.db.get_value( + "Stock Ledger Entry", {"voucher_no": sr.name, "is_cancelled": 0}, "qty_after_transaction" + ) + + self.assertEqual(qty_after_transaction, 20) + def test_stock_reco_for_serial_and_batch_item_with_future_dependent_entry(self): """ Behaviour: 1) Create Stock Reconciliation, which will be the origin document diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 7e6d3edae10..4f8f06023de 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -579,7 +579,7 @@ class update_entries_after(object): if get_serial_nos(sle.serial_no): self.get_serialized_values(sle) self.wh_data.qty_after_transaction += flt(sle.actual_qty) - if sle.voucher_type == "Stock Reconciliation": + if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no: self.wh_data.qty_after_transaction = sle.qty_after_transaction self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt( From 313ad7ae89d77969d5c950ac45a1a2dda60fa881 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 14:20:28 +0530 Subject: [PATCH 23/26] fix: Opening balance in TB report (#36171) fix: Opening balance in TB report (#36171) (cherry picked from commit cfae52a40a145fe204639d1b2abf84012f88e542) Co-authored-by: Deepesh Garg --- erpnext/accounts/report/trial_balance/trial_balance.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 5176c31be71..39917f90c93 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -221,7 +221,10 @@ def get_opening_balance( ) else: if start_date: - opening_balance = opening_balance.where(closing_balance.posting_date >= start_date) + opening_balance = opening_balance.where( + (closing_balance.posting_date >= start_date) + & (closing_balance.posting_date < filters.from_date) + ) opening_balance = opening_balance.where(closing_balance.is_opening == "No") else: opening_balance = opening_balance.where( From ea66f18032816734c6b1431a95dc7325de0b0354 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 14:26:47 +0530 Subject: [PATCH 24/26] refactor: Leave Application should not be in hook.py (backport #36008) (#36158) Co-authored-by: Kitti U. @ Ecosoft Co-authored-by: Rucha Mahabal --- erpnext/hooks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 115d5e6ea88..4d2cb617b5c 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -75,7 +75,6 @@ webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform calendars = [ "Task", "Work Order", - "Leave Application", "Sales Order", "Holiday List", ] From 8a607db493212414a2c74704930b3aef2c15087a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 15:20:11 +0530 Subject: [PATCH 25/26] fix: Trailing opening entries in Accounts closing balance (#36175) fix: Trailing opening entries in Accounts closing balance (#36175) (cherry picked from commit fbea61bbc60a723c07595a8ae8e30982205388df) Co-authored-by: Deepesh Garg --- .../account_closing_balance/account_closing_balance.py | 4 +--- .../period_closing_voucher/period_closing_voucher.py | 8 +++++--- erpnext/patches/v14_0/update_closing_balances.py | 7 +++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py index 7c842372de8..ea67051fb49 100644 --- a/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py +++ b/erpnext/accounts/doctype/account_closing_balance/account_closing_balance.py @@ -14,10 +14,8 @@ class AccountClosingBalance(Document): pass -def make_closing_entries(closing_entries, voucher_name): +def make_closing_entries(closing_entries, voucher_name, company, closing_date): accounting_dimensions = get_accounting_dimensions() - company = closing_entries[0].get("company") - closing_date = closing_entries[0].get("closing_date") previous_closing_entries = get_previous_closing_entries( company, closing_date, accounting_dimensions diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 641f4528c53..922722f04d3 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -133,6 +133,8 @@ class PeriodClosingVoucher(AccountsController): gl_entries=gl_entries, closing_entries=closing_entries, voucher_name=self.name, + company=self.company, + closing_date=self.posting_date, queue="long", ) frappe.msgprint( @@ -140,7 +142,7 @@ class PeriodClosingVoucher(AccountsController): alert=True, ) else: - process_gl_entries(gl_entries, closing_entries, voucher_name=self.name) + process_gl_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date) def get_grouped_gl_entries(self, get_opening_entries=False): closing_entries = [] @@ -321,7 +323,7 @@ class PeriodClosingVoucher(AccountsController): return query.run(as_dict=1) -def process_gl_entries(gl_entries, closing_entries, voucher_name=None): +def process_gl_entries(gl_entries, closing_entries, voucher_name, company, closing_date): from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import ( make_closing_entries, ) @@ -329,7 +331,7 @@ def process_gl_entries(gl_entries, closing_entries, voucher_name=None): try: make_gl_entries(gl_entries, merge_entries=False) - make_closing_entries(gl_entries + closing_entries, voucher_name=voucher_name) + make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date) frappe.db.set_value( "Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed" ) diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index 2947b98740b..2c842814839 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -69,7 +69,6 @@ def execute(): entries = gl_entries + closing_entries - if entries: - make_closing_entries(entries, voucher_name=pcv.name) - i += 1 - company_wise_order[pcv.company].append(pcv.posting_date) + make_closing_entries(entries, pcv.name, pcv.company, pcv.posting_date) + company_wise_order[pcv.company].append(pcv.posting_date) + i += 1 From 5985e02574e387ef92a2bf0a9d2d1b49ad57cdd3 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 18 Jul 2023 16:40:06 +0530 Subject: [PATCH 26/26] fix: validate docs in closed accounting period on save (#36157) --- .../accounting_period/accounting_period.js | 6 +++ .../accounting_period/accounting_period.py | 43 +++++++++++++++++++ .../test_accounting_period.py | 10 +++-- erpnext/accounts/general_ledger.py | 5 +-- erpnext/controllers/queries.py | 9 ++++ erpnext/hooks.py | 33 ++++++++++---- 6 files changed, 89 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.js b/erpnext/accounts/doctype/accounting_period/accounting_period.js index e3d805a1681..f17b6f9c695 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.js +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.js @@ -20,5 +20,11 @@ frappe.ui.form.on('Accounting Period', { } }); } + + frm.set_query("document_type", "closed_documents", () => { + return { + query: "erpnext.controllers.queries.get_doctypes_for_closing", + } + }); } }); diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py index 80c9715e8e1..d5f37a68067 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -11,6 +11,10 @@ class OverlapError(frappe.ValidationError): pass +class ClosedAccountingPeriod(frappe.ValidationError): + pass + + class AccountingPeriod(Document): def validate(self): self.validate_overlap() @@ -65,3 +69,42 @@ class AccountingPeriod(Document): "closed_documents", {"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed}, ) + + +def validate_accounting_period_on_doc_save(doc, method=None): + if doc.doctype == "Bank Clearance": + return + elif doc.doctype == "Asset": + if doc.is_existing_asset: + return + else: + date = doc.available_for_use_date + elif doc.doctype == "Asset Repair": + date = doc.completion_date + else: + date = doc.posting_date + + ap = frappe.qb.DocType("Accounting Period") + cd = frappe.qb.DocType("Closed Document") + + accounting_period = ( + frappe.qb.from_(ap) + .from_(cd) + .select(ap.name) + .where( + (ap.name == cd.parent) + & (ap.company == doc.company) + & (cd.closed == 1) + & (cd.document_type == doc.doctype) + & (date >= ap.start_date) + & (date <= ap.end_date) + ) + ).run(as_dict=1) + + if accounting_period: + frappe.throw( + _("You cannot create a {0} within the closed Accounting Period {1}").format( + doc.doctype, frappe.bold(accounting_period[0]["name"]) + ), + ClosedAccountingPeriod, + ) diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py index 85025d190f5..41d94797ad6 100644 --- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py @@ -6,9 +6,11 @@ import unittest import frappe from frappe.utils import add_months, nowdate -from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError +from erpnext.accounts.doctype.accounting_period.accounting_period import ( + ClosedAccountingPeriod, + OverlapError, +) from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.accounts.general_ledger import ClosedAccountingPeriod test_dependencies = ["Item"] @@ -33,9 +35,9 @@ class TestAccountingPeriod(unittest.TestCase): ap1.save() doc = create_sales_invoice( - do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC" + do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC" ) - self.assertRaises(ClosedAccountingPeriod, doc.submit) + self.assertRaises(ClosedAccountingPeriod, doc.save) def tearDown(self): for d in frappe.get_all("Accounting Period"): diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a929ff17b09..16b61236dba 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -13,14 +13,11 @@ import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, ) +from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.accounts.utils import create_payment_ledger_entry -class ClosedAccountingPeriod(frappe.ValidationError): - pass - - def make_gl_entries( gl_map, cancel=False, diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 799fed99cc7..80bc3eef745 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -771,6 +771,15 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_doctypes_for_closing(doctype, txt, searchfield, start, page_len, filters): + doctypes = frappe.get_hooks("period_closing_doctypes") + if txt: + doctypes = [d for d in doctypes if txt.lower() in d.lower()] + return [(d,) for d in set(doctypes)] + + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_tax_template(doctype, txt, searchfield, start, page_len, filters): diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 4d2cb617b5c..16c488d9047 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -278,10 +278,34 @@ standard_queries = { "Customer": "erpnext.controllers.queries.customer_query", } +period_closing_doctypes = [ + "Sales Invoice", + "Purchase Invoice", + "Journal Entry", + "Bank Clearance", + "Stock Entry", + "Dunning", + "Invoice Discounting", + "Payment Entry", + "Period Closing Voucher", + "Process Deferred Accounting", + "Asset", + "Asset Capitalization", + "Asset Repair", + "Delivery Note", + "Landed Cost Voucher", + "Purchase Receipt", + "Stock Reconciliation", + "Subcontracting Receipt", +] + doc_events = { "*": { "validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply", }, + tuple(period_closing_doctypes): { + "validate": "erpnext.accounts.doctype.accounting_period.accounting_period.validate_accounting_period_on_doc_save", + }, "Stock Entry": { "on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty", "on_cancel": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty", @@ -471,15 +495,6 @@ advance_payment_doctypes = ["Sales Order", "Purchase Order"] invoice_doctypes = ["Sales Invoice", "Purchase Invoice"] -period_closing_doctypes = [ - "Sales Invoice", - "Purchase Invoice", - "Journal Entry", - "Bank Clearance", - "Asset", - "Stock Entry", -] - bank_reconciliation_doctypes = [ "Payment Entry", "Journal Entry",