From 5a1c61f4d94eab0f839c6006ea04163c694a0f4a Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Sat, 21 Feb 2026 01:58:14 +0530 Subject: [PATCH 1/4] refactor: `Fiscal Year` DocType cleanup (cherry picked from commit 74ac28fc70d9a4efdfa4083e2d8656ae95dbd0e1) # Conflicts: # erpnext/accounts/doctype/fiscal_year/fiscal_year.py --- .../doctype/fiscal_year/fiscal_year.py | 57 ++++++------------- 1 file changed, 17 insertions(+), 40 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index f97fcf1ec34..86083114403 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -33,24 +33,6 @@ class FiscalYear(Document): self.validate_dates() self.validate_overlap() - if not self.is_new(): - year_start_end_dates = frappe.db.sql( - """select year_start_date, year_end_date - from `tabFiscal Year` where name=%s""", - (self.name), - ) - - if year_start_end_dates: - if ( - getdate(self.year_start_date) != year_start_end_dates[0][0] - or getdate(self.year_end_date) != year_start_end_dates[0][1] - ): - frappe.throw( - _( - "Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved." - ) - ) - def validate_dates(self): self.validate_from_to_dates("year_start_date", "year_end_date") if self.is_short_year: @@ -66,28 +48,20 @@ class FiscalYear(Document): frappe.exceptions.InvalidDates, ) - def on_update(self): - check_duplicate_fiscal_year(self) - frappe.cache().delete_value("fiscal_years") - - def on_trash(self): - frappe.cache().delete_value("fiscal_years") - def validate_overlap(self): - existing_fiscal_years = frappe.db.sql( - """select name from `tabFiscal Year` - where ( - (%(year_start_date)s between year_start_date and year_end_date) - or (%(year_end_date)s between year_start_date and year_end_date) - or (year_start_date between %(year_start_date)s and %(year_end_date)s) - or (year_end_date between %(year_start_date)s and %(year_end_date)s) - ) and name!=%(name)s""", - { - "year_start_date": self.year_start_date, - "year_end_date": self.year_end_date, - "name": self.name or "No Name", - }, - as_dict=True, + fy = frappe.qb.DocType("Fiscal Year") + + name = self.name or self.year + + existing_fiscal_years = ( + frappe.qb.from_(fy) + .select(fy.name) + .where( + (fy.year_start_date <= self.year_end_date) + & (fy.year_end_date >= self.year_start_date) + & (fy.name != name) + ) + .run(as_dict=True) ) if existing_fiscal_years: @@ -110,12 +84,13 @@ class FiscalYear(Document): frappe.throw( _( "Year start date or end date is overlapping with {0}. To avoid please set company" - ).format(existing.name), + ).format(frappe.get_desk_link("Fiscal Year", existing.name, open_in_new_tab=True)), frappe.NameError, ) @frappe.whitelist() +<<<<<<< HEAD def check_duplicate_fiscal_year(doc): year_start_end_dates = frappe.db.sql( """select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""", @@ -133,6 +108,8 @@ def check_duplicate_fiscal_year(doc): @frappe.whitelist() +======= +>>>>>>> 74ac28fc70 (refactor: `Fiscal Year` DocType cleanup) def auto_create_fiscal_year(): for d in frappe.db.sql( """select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)""" From 2fffc9448b8d38c50d238de6d14a0f8c6eca6fc8 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Sat, 21 Feb 2026 02:02:03 +0530 Subject: [PATCH 2/4] fix(`fiscal_year_company`): made `company` field mandatory (cherry picked from commit 94fb7e11b4713886dc6400fb8631dd7539ab2fd0) # Conflicts: # erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json --- .../fiscal_year_company/fiscal_year_company.json | 14 ++++++++++++-- .../fiscal_year_company/fiscal_year_company.py | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json index 67acb26c7ee..4dc16a782bc 100644 --- a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json +++ b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json @@ -15,19 +15,29 @@ "ignore_user_permissions": 1, "in_list_view": 1, "label": "Company", - "options": "Company" + "options": "Company", + "reqd": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2021-09-28 18:01:53.495929", +======= + "modified": "2026-02-20 23:02:26.193606", +>>>>>>> 94fb7e11b4 (fix(`fiscal_year_company`): made `company` field mandatory) "modified_by": "Administrator", "module": "Accounts", "name": "Fiscal Year Company", "owner": "Administrator", "permissions": [], +<<<<<<< HEAD "sort_field": "modified", +======= + "row_format": "Dynamic", + "sort_field": "creation", +>>>>>>> 94fb7e11b4 (fix(`fiscal_year_company`): made `company` field mandatory) "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py index 9447120d326..b68069bca27 100644 --- a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py +++ b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.py @@ -14,7 +14,7 @@ class FiscalYearCompany(Document): if TYPE_CHECKING: from frappe.types import DF - company: DF.Link | None + company: DF.Link parent: DF.Data parentfield: DF.Data parenttype: DF.Data From 397f39e27108294c44174f165dfa4ec9e3bef81e Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Sat, 21 Feb 2026 02:42:53 +0530 Subject: [PATCH 3/4] fix(`fiscal_year`): `Fiscal Year` auto-generation and notification (cherry picked from commit 4c76786ce44eb5f3813964092ac8211336c9c9eb) # Conflicts: # erpnext/accounts/doctype/fiscal_year/fiscal_year.py # erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.html # erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json --- .../doctype/fiscal_year/fiscal_year.py | 28 +++++++++--- .../notification_for_new_fiscal_year.html | 43 +++++++++++++++++++ .../notification_for_new_fiscal_year.json | 19 +++++++- 3 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.html diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index 86083114403..befc9189016 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 _ +from frappe import _, cint from frappe.model.document import Document from frappe.utils import add_days, add_years, cstr, getdate @@ -89,6 +89,7 @@ class FiscalYear(Document): ) +<<<<<<< HEAD @frappe.whitelist() <<<<<<< HEAD def check_duplicate_fiscal_year(doc): @@ -110,14 +111,27 @@ def check_duplicate_fiscal_year(doc): @frappe.whitelist() ======= >>>>>>> 74ac28fc70 (refactor: `Fiscal Year` DocType cleanup) +======= +>>>>>>> 4c76786ce4 (fix(`fiscal_year`): `Fiscal Year` auto-generation and notification) def auto_create_fiscal_year(): - for d in frappe.db.sql( - """select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)""" - ): + fy = frappe.qb.DocType("Fiscal Year") + + # Skipped auto-creating Short Year, as it has very rare use case. + # Reference: https://www.irs.gov/businesses/small-businesses-self-employed/tax-years (US) + follow_up_date = add_days(getdate(), days=3) + fiscal_year = ( + frappe.qb.from_(fy) + .select(fy.name) + .where((fy.year_end_date == follow_up_date) & (fy.is_short_year == 0)) + .run() + ) + + for d in fiscal_year: try: current_fy = frappe.get_doc("Fiscal Year", d[0]) - new_fy = frappe.copy_doc(current_fy, ignore_no_copy=False) + new_fy = frappe.new_doc("Fiscal Year") + new_fy.disabled = cint(current_fy.disabled) new_fy.year_start_date = add_days(current_fy.year_end_date, 1) new_fy.year_end_date = add_years(current_fy.year_end_date, 1) @@ -125,6 +139,10 @@ def auto_create_fiscal_year(): start_year = cstr(new_fy.year_start_date.year) end_year = cstr(new_fy.year_end_date.year) new_fy.year = start_year if start_year == end_year else (start_year + "-" + end_year) + + for row in current_fy.companies: + new_fy.append("companies", {"company": row.company}) + new_fy.auto_created = 1 new_fy.insert(ignore_permissions=True) diff --git a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.html b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.html new file mode 100644 index 00000000000..542070ab6f2 --- /dev/null +++ b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.html @@ -0,0 +1,43 @@ +

{{ _("New Fiscal Year - {0}").format(doc.name) }}

+ +

{{ _("A new fiscal year has been automatically created.") }}

+ +

{{ _("Fiscal Year Details") }}

+ + + + + + + + + + + + + + + {% if doc.companies|length > 0 %} + + + + + {% for idx in range(1, doc.companies|length) %} + + + + {% endfor %} + {% endif %} +
{{ _("Year Name") }}{{ doc.name }}
{{ _("Start Date") }}{{ frappe.format_value(doc.year_start_date) }}
{{ _("End Date") }}{{ frappe.format_value(doc.year_end_date) }}
+ {% if doc.companies|length < 2 %} + {{ _("Company") }} + {% else %} + {{ _("Companies") }} + {% endif %} + {{ doc.companies[0].company }}
{{ doc.companies[idx].company }}
+ +{% if doc.disabled %} +

{{ _("The fiscal year has been automatically created in a Disabled state to maintain consistency with the previous fiscal year's status.") }}

+{% endif %} + +

{{ _("Please review the {0} configuration and complete any required financial setup activities.").format(frappe.utils.get_link_to_form("Fiscal Year", doc.name, frappe.bold("Fiscal Year"))) }}

\ No newline at end of file diff --git a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json index 4c7faf4f65b..abcbb790a12 100644 --- a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json +++ b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json @@ -1,7 +1,8 @@ { "attach_print": 0, "channel": "Email", - "condition": "doc.auto_created", + "condition": "doc.auto_created == 1", + "condition_type": "Python", "creation": "2018-04-25 14:19:05.440361", "days_in_advance": 0, "docstatus": 0, @@ -11,8 +12,15 @@ "event": "New", "idx": 0, "is_standard": 1, +<<<<<<< HEAD "message": "

{{_(\"Fiscal Year\")}}

\n\n

{{ _(\"New fiscal year created :- \") }} {{ doc.name }}

", "modified": "2018-04-25 14:30:38.588534", +======= + "message": "

{{ _(\"New Fiscal Year - {0}\").format(doc.name) }}

\n\n

{{ _(\"A new fiscal year has been automatically created.\") }}

\n\n

{{ _(\"Fiscal Year Details\") }}

\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n {% if doc.companies|length > 0 %}\n \n \n \n \n {% for idx in range(1, doc.companies|length) %}\n \n \n \n {% endfor %}\n {% endif %}\n
{{ _(\"Year Name\") }}{{ doc.name }}
{{ _(\"Start Date\") }}{{ frappe.format_value(doc.year_start_date) }}
{{ _(\"End Date\") }}{{ frappe.format_value(doc.year_end_date) }}
\n {% if doc.companies|length < 2 %}\n {{ _(\"Company\") }}\n {% else %}\n {{ _(\"Companies\") }}\n {% endif %}\n {{ doc.companies[0].company }}
{{ doc.companies[idx].company }}
\n\n{% if doc.disabled %}\n

{{ _(\"The fiscal year has been automatically created in a Disabled state to maintain consistency with the previous fiscal year's status.\") }}

\n{% endif %}\n\n

{{ _(\"Please review the {0} configuration and complete any required financial setup activities.\").format(frappe.utils.get_link_to_form(\"Fiscal Year\", doc.name, frappe.bold(\"Fiscal Year\"))) }}

", + "message_type": "HTML", + "minutes_offset": 0, + "modified": "2026-02-21 12:14:54.736795", +>>>>>>> 4c76786ce4 (fix(`fiscal_year`): `Fiscal Year` auto-generation and notification) "modified_by": "Administrator", "module": "Accounts", "name": "Notification for new fiscal year", @@ -25,5 +33,12 @@ "email_by_role": "Accounts Manager" } ], +<<<<<<< HEAD "subject": "Notification for new fiscal year {{ doc.name }}" -} \ No newline at end of file +} +======= + "send_system_notification": 0, + "send_to_all_assignees": 0, + "subject": "{{ _(\"New Fiscal Year {0} - Review Required\").format(doc.name) }}" +} +>>>>>>> 4c76786ce4 (fix(`fiscal_year`): `Fiscal Year` auto-generation and notification) From bb8e5adadc24e1659fbf8922acbddf675b93f96b Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Sat, 21 Feb 2026 16:36:58 +0530 Subject: [PATCH 4/4] chore: resolve conflicts --- .../doctype/fiscal_year/fiscal_year.py | 24 ------------------- .../fiscal_year_company.json | 9 ------- .../notification_for_new_fiscal_year.json | 20 ++++------------ 3 files changed, 4 insertions(+), 49 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index befc9189016..38f3a91c8fe 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -89,30 +89,6 @@ class FiscalYear(Document): ) -<<<<<<< HEAD -@frappe.whitelist() -<<<<<<< HEAD -def check_duplicate_fiscal_year(doc): - year_start_end_dates = frappe.db.sql( - """select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""", - (doc.name), - ) - for fiscal_year, ysd, yed in year_start_end_dates: - if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and ( - not frappe.flags.in_test - ): - frappe.throw( - _( - "Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}" - ).format(fiscal_year) - ) - - -@frappe.whitelist() -======= ->>>>>>> 74ac28fc70 (refactor: `Fiscal Year` DocType cleanup) -======= ->>>>>>> 4c76786ce4 (fix(`fiscal_year`): `Fiscal Year` auto-generation and notification) def auto_create_fiscal_year(): fy = frappe.qb.DocType("Fiscal Year") diff --git a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json index 4dc16a782bc..d1f1ecc0a09 100644 --- a/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json +++ b/erpnext/accounts/doctype/fiscal_year_company/fiscal_year_company.json @@ -22,22 +22,13 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2021-09-28 18:01:53.495929", -======= "modified": "2026-02-20 23:02:26.193606", ->>>>>>> 94fb7e11b4 (fix(`fiscal_year_company`): made `company` field mandatory) "modified_by": "Administrator", "module": "Accounts", "name": "Fiscal Year Company", "owner": "Administrator", "permissions": [], -<<<<<<< HEAD "sort_field": "modified", -======= - "row_format": "Dynamic", - "sort_field": "creation", ->>>>>>> 94fb7e11b4 (fix(`fiscal_year_company`): made `company` field mandatory) "sort_order": "DESC", "track_changes": 1 } diff --git a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json index abcbb790a12..9160ebbae3e 100644 --- a/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json +++ b/erpnext/accounts/notification/notification_for_new_fiscal_year/notification_for_new_fiscal_year.json @@ -2,7 +2,6 @@ "attach_print": 0, "channel": "Email", "condition": "doc.auto_created == 1", - "condition_type": "Python", "creation": "2018-04-25 14:19:05.440361", "days_in_advance": 0, "docstatus": 0, @@ -12,33 +11,22 @@ "event": "New", "idx": 0, "is_standard": 1, -<<<<<<< HEAD - "message": "

{{_(\"Fiscal Year\")}}

\n\n

{{ _(\"New fiscal year created :- \") }} {{ doc.name }}

", - "modified": "2018-04-25 14:30:38.588534", -======= "message": "

{{ _(\"New Fiscal Year - {0}\").format(doc.name) }}

\n\n

{{ _(\"A new fiscal year has been automatically created.\") }}

\n\n

{{ _(\"Fiscal Year Details\") }}

\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n {% if doc.companies|length > 0 %}\n \n \n \n \n {% for idx in range(1, doc.companies|length) %}\n \n \n \n {% endfor %}\n {% endif %}\n
{{ _(\"Year Name\") }}{{ doc.name }}
{{ _(\"Start Date\") }}{{ frappe.format_value(doc.year_start_date) }}
{{ _(\"End Date\") }}{{ frappe.format_value(doc.year_end_date) }}
\n {% if doc.companies|length < 2 %}\n {{ _(\"Company\") }}\n {% else %}\n {{ _(\"Companies\") }}\n {% endif %}\n {{ doc.companies[0].company }}
{{ doc.companies[idx].company }}
\n\n{% if doc.disabled %}\n

{{ _(\"The fiscal year has been automatically created in a Disabled state to maintain consistency with the previous fiscal year's status.\") }}

\n{% endif %}\n\n

{{ _(\"Please review the {0} configuration and complete any required financial setup activities.\").format(frappe.utils.get_link_to_form(\"Fiscal Year\", doc.name, frappe.bold(\"Fiscal Year\"))) }}

", "message_type": "HTML", - "minutes_offset": 0, - "modified": "2026-02-21 12:14:54.736795", ->>>>>>> 4c76786ce4 (fix(`fiscal_year`): `Fiscal Year` auto-generation and notification) + "modified": "2026-02-21 15:59:07.775679", "modified_by": "Administrator", "module": "Accounts", "name": "Notification for new fiscal year", "owner": "Administrator", "recipients": [ { - "email_by_role": "Accounts User" + "receiver_by_role": "Accounts Manager" }, { - "email_by_role": "Accounts Manager" + "receiver_by_role": "Accounts User" } ], -<<<<<<< HEAD - "subject": "Notification for new fiscal year {{ doc.name }}" -} -======= "send_system_notification": 0, "send_to_all_assignees": 0, "subject": "{{ _(\"New Fiscal Year {0} - Review Required\").format(doc.name) }}" -} ->>>>>>> 4c76786ce4 (fix(`fiscal_year`): `Fiscal Year` auto-generation and notification) +} \ No newline at end of file