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(
- '' +
- '
' +
- ''+ agreement_status.msg +' ' +
- '
' +
- '
'
- );
}
- },
+ }
});
- });
- }
+ },
+
+ 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(
+ '' +
+ '
' +
+ ''+ agreement_status.msg +' ' +
+ '
' +
+ '
'
+ );
+ }
+ },
+ });
});
});
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",
|