diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml new file mode 100644 index 00000000000..e765a66f691 --- /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/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 }} + RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }} 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/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( [ 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/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/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( 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/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/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/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/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/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( 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/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: 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/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 b859ce091cb..16c488d9047 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", ] @@ -279,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", @@ -359,6 +382,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. @@ -467,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", @@ -612,3 +631,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/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 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/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 diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index b0082bdb281..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'" }, @@ -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") + }); + }, } ] diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 58aa8d7da23..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) { @@ -600,7 +617,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 +624,7 @@ erpnext.utils.update_child_items = function(opts) { new frappe.ui.Dialog({ title: __("Update Items"), + size: "extra-large", fields: [ { fieldname: "trans_items", @@ -822,95 +839,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/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/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/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"), 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 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/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) 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( 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() 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 %} 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",