Merge pull request #36176 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
Deepesh Garg
2023-07-18 19:27:44 +05:30
committed by GitHub
53 changed files with 796 additions and 1421 deletions

38
.github/workflows/release_notes.yml vendored Normal file
View File

@@ -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 }}

View File

@@ -14,10 +14,8 @@ class AccountClosingBalance(Document):
pass 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() 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( previous_closing_entries = get_previous_closing_entries(
company, closing_date, accounting_dimensions company, closing_date, accounting_dimensions

View File

@@ -271,6 +271,12 @@ def get_dimensions(with_cost_center_and_project=False):
as_dict=1, 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: if with_cost_center_and_project:
dimension_filters.extend( dimension_filters.extend(
[ [

View File

@@ -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",
}
});
} }
}); });

View File

@@ -11,6 +11,10 @@ class OverlapError(frappe.ValidationError):
pass pass
class ClosedAccountingPeriod(frappe.ValidationError):
pass
class AccountingPeriod(Document): class AccountingPeriod(Document):
def validate(self): def validate(self):
self.validate_overlap() self.validate_overlap()
@@ -65,3 +69,42 @@ class AccountingPeriod(Document):
"closed_documents", "closed_documents",
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed}, {"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,
)

View File

@@ -6,9 +6,11 @@ import unittest
import frappe import frappe
from frappe.utils import add_months, nowdate 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.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
test_dependencies = ["Item"] test_dependencies = ["Item"]
@@ -33,9 +35,9 @@ class TestAccountingPeriod(unittest.TestCase):
ap1.save() ap1.save()
doc = create_sales_invoice( 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): def tearDown(self):
for d in frappe.get_all("Accounting Period"): for d in frappe.get_all("Accounting Period"):

View File

@@ -8,17 +8,6 @@ frappe.ui.form.on('Fiscal Year', {
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)); 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) { year_start_date: function(frm) {
if (!frm.doc.is_short_year) { if (!frm.doc.is_short_year) {
let year_end_date = let year_end_date =

View File

@@ -4,7 +4,7 @@
import frappe import frappe
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from frappe import _, msgprint from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import add_days, add_years, cstr, getdate from frappe.utils import add_days, add_years, cstr, getdate
@@ -14,22 +14,6 @@ class FiscalYearIncorrectDate(frappe.ValidationError):
class FiscalYear(Document): 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): def validate(self):
self.validate_dates() self.validate_dates()
self.validate_overlap() self.validate_overlap()
@@ -77,13 +61,6 @@ class FiscalYear(Document):
frappe.cache().delete_value("fiscal_years") frappe.cache().delete_value("fiscal_years")
def on_trash(self): 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") frappe.cache().delete_value("fiscal_years")
def validate_overlap(self): def validate_overlap(self):

View File

@@ -396,6 +396,15 @@ class JournalEntry(AccountsController):
d.idx, d.account 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): def check_credit_limit(self):
customers = list( customers = list(

View File

@@ -133,6 +133,8 @@ class PeriodClosingVoucher(AccountsController):
gl_entries=gl_entries, gl_entries=gl_entries,
closing_entries=closing_entries, closing_entries=closing_entries,
voucher_name=self.name, voucher_name=self.name,
company=self.company,
closing_date=self.posting_date,
queue="long", queue="long",
) )
frappe.msgprint( frappe.msgprint(
@@ -140,7 +142,7 @@ class PeriodClosingVoucher(AccountsController):
alert=True, alert=True,
) )
else: 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): def get_grouped_gl_entries(self, get_opening_entries=False):
closing_entries = [] closing_entries = []
@@ -321,7 +323,7 @@ class PeriodClosingVoucher(AccountsController):
return query.run(as_dict=1) 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 ( from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries, make_closing_entries,
) )
@@ -329,7 +331,7 @@ def process_gl_entries(gl_entries, closing_entries, voucher_name=None):
try: try:
make_gl_entries(gl_entries, merge_entries=False) 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( frappe.db.set_value(
"Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed" "Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed"
) )

View File

@@ -13,14 +13,11 @@ import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions, 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.doctype.budget.budget import validate_expense_against_budget
from erpnext.accounts.utils import create_payment_ledger_entry from erpnext.accounts.utils import create_payment_ledger_entry
class ClosedAccountingPeriod(frappe.ValidationError):
pass
def make_gl_entries( def make_gl_entries(
gl_map, gl_map,
cancel=False, cancel=False,

View File

@@ -49,7 +49,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Start Year"), "label": __("Start Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
on_change: () => { on_change: () => {
frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) { 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"), "label": __("End Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
on_change: () => { on_change: () => {
frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) { 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; return value;
}, },
onload: function() { 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) { frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);

View File

@@ -48,7 +48,7 @@ function get_filters() {
"label": __("Start Year"), "label": __("Start Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1 "reqd": 1
}, },
{ {
@@ -56,7 +56,7 @@ function get_filters() {
"label": __("End Year"), "label": __("End Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1 "reqd": 1
}, },
{ {
@@ -100,7 +100,7 @@ frappe.query_reports["Deferred Revenue and Expense"] = {
return default_formatter(value, row, column, data); return default_formatter(value, row, column, data);
}, },
onload: function(report){ 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) { frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);

View File

@@ -4,9 +4,10 @@
import frappe import frappe
from frappe import _, qb from frappe import _, qb
from frappe.query_builder import Column, functions 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.report.financial_statements import get_period_list
from erpnext.accounts.utils import get_fiscal_year
class Deferred_Item(object): class Deferred_Item(object):
@@ -226,7 +227,7 @@ class Deferred_Revenue_and_Expense_Report(object):
# If no filters are provided, get user defaults # If no filters are provided, get user defaults
if not filters: 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( self.filters = frappe._dict(
{ {
"company": frappe.defaults.get_user_default("Company"), "company": frappe.defaults.get_user_default("Company"),

View File

@@ -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 ( from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import (
Deferred_Revenue_and_Expense_Report, 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.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
@@ -116,7 +117,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
pda.submit() pda.submit()
# execute report # 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( self.filters = frappe._dict(
{ {
"company": frappe.defaults.get_user_default("Company"), "company": frappe.defaults.get_user_default("Company"),
@@ -209,7 +210,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
pda.submit() pda.submit()
# execute report # 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( self.filters = frappe._dict(
{ {
"company": frappe.defaults.get_user_default("Company"), "company": frappe.defaults.get_user_default("Company"),
@@ -297,7 +298,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
pda.submit() pda.submit()
# execute report # 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( self.filters = frappe._dict(
{ {
"company": frappe.defaults.get_user_default("Company"), "company": frappe.defaults.get_user_default("Company"),

View File

@@ -18,7 +18,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Fiscal Year"), "label": __("Fiscal Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
"on_change": function(query_report) { "on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year; var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -12,14 +12,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
erpnext.financial_statements); erpnext.financial_statements);
frappe.query_reports["Gross and Net Profit Report"]["filters"].push( 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", "fieldname": "accumulated_values",
"label": __("Accumulated Values"), "label": __("Accumulated Values"),

View File

@@ -9,16 +9,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
erpnext.utils.add_dimensions('Profit and Loss Statement', 10); erpnext.utils.add_dimensions('Profit and Loss Statement', 10);
frappe.query_reports["Profit and Loss Statement"]["filters"].push( 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", "fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"), "label": __("Include Default Book Entries"),

View File

@@ -25,7 +25,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Fiscal Year"), "label": __("Fiscal Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
"on_change": function(query_report) { "on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year; var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -17,7 +17,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Fiscal Year"), "label": __("Fiscal Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
"on_change": function(query_report) { "on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year; var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -221,7 +221,10 @@ def get_opening_balance(
) )
else: else:
if start_date: 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") opening_balance = opening_balance.where(closing_balance.is_opening == "No")
else: else:
opening_balance = opening_balance.where( opening_balance = opening_balance.where(

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Trial Balance for Party"] = {
"label": __("Fiscal Year"), "label": __("Fiscal Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
"on_change": function(query_report) { "on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year; var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -1106,6 +1106,11 @@ def get_autoname_with_number(number_value, doc_title, company):
return " - ".join(parts) 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() @frappe.whitelist()
def get_coa(doctype, parent, is_root, chart=None): def get_coa(doctype, parent, is_root, chart=None):
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import ( from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (

View File

@@ -63,7 +63,7 @@ frappe.ui.form.on('Asset Movement', {
fieldnames_to_be_altered = { fieldnames_to_be_altered = {
target_location: { read_only: 0, reqd: 1 }, target_location: { read_only: 0, reqd: 1 },
source_location: { read_only: 1, reqd: 0 }, 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 } to_employee: { read_only: 1, reqd: 0 }
}; };
} }

View File

@@ -62,29 +62,20 @@ class AssetMovement(Document):
frappe.throw(_("Source and Target Location cannot be same")) frappe.throw(_("Source and Target Location cannot be same"))
if self.purpose == "Receipt": if self.purpose == "Receipt":
# only when asset is bought and first entry is made if not (d.source_location or d.from_employee) and not (d.target_location or d.to_employee):
if not d.source_location and not (d.target_location or d.to_employee):
frappe.throw( frappe.throw(
_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset) _("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)
) )
elif d.source_location: elif d.from_employee and not d.target_location:
# when asset is received from an employee frappe.throw(
if d.target_location and not d.from_employee: _("Target Location is required while receiving Asset {0} from an employee").format(d.asset)
frappe.throw( )
_("From employee is required while receiving Asset {0} to a target location").format( elif d.to_employee and d.target_location:
d.asset frappe.throw(
) _(
) "Asset {0} cannot be received at a location and given to an employee in a single movement"
if d.from_employee and not d.target_location: ).format(d.asset)
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)
)
def validate_employee(self): def validate_employee(self):
for d in self.assets: for d in self.assets:

View File

@@ -82,7 +82,7 @@ frappe.query_reports["Fixed Asset Register"] = {
"label": __("Start Year"), "label": __("Start Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "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'", "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
}, },
{ {
@@ -90,7 +90,7 @@ frappe.query_reports["Fixed Asset Register"] = {
"label": __("End Year"), "label": __("End Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "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'", "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
}, },
{ {

View File

@@ -771,6 +771,15 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(query, 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.whitelist()
@frappe.validate_and_sanitize_search_inputs @frappe.validate_and_sanitize_search_inputs
def get_tax_template(doctype, txt, searchfield, start, page_len, filters): def get_tax_template(doctype, txt, searchfield, start, page_len, filters):

View File

@@ -75,7 +75,6 @@ webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform
calendars = [ calendars = [
"Task", "Task",
"Work Order", "Work Order",
"Leave Application",
"Sales Order", "Sales Order",
"Holiday List", "Holiday List",
] ]
@@ -279,10 +278,34 @@ standard_queries = {
"Customer": "erpnext.controllers.queries.customer_query", "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 = { doc_events = {
"*": { "*": {
"validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply", "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": { "Stock Entry": {
"on_submit": "erpnext.stock.doctype.material_request.material_request.update_completed_and_requested_qty", "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", "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. # 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. # 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. # 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"] invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
period_closing_doctypes = [
"Sales Invoice",
"Purchase Invoice",
"Journal Entry",
"Bank Clearance",
"Asset",
"Stock Entry",
]
bank_reconciliation_doctypes = [ bank_reconciliation_doctypes = [
"Payment Entry", "Payment Entry",
"Journal Entry", "Journal Entry",
@@ -612,3 +631,8 @@ global_search_doctypes = {
additional_timeline_content = { additional_timeline_content = {
"*": ["erpnext.telephony.doctype.call_log.call_log.get_linked_call_logs"] "*": ["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",
]

View File

@@ -1001,7 +1001,7 @@ class WorkOrder(Document):
consumed_qty = frappe.db.sql( consumed_qty = frappe.db.sql(
""" """
SELECT SELECT
SUM(qty) SUM(detail.qty)
FROM FROM
`tabStock Entry` entry, `tabStock Entry` entry,
`tabStock Entry Detail` detail `tabStock Entry Detail` detail

View File

@@ -17,7 +17,7 @@ frappe.query_reports["Job Card Summary"] = {
label: __("Fiscal Year"), label: __("Fiscal Year"),
fieldtype: "Link", fieldtype: "Link",
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.defaults.get_user_default("fiscal_year"), default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1, reqd: 1,
on_change: function(query_report) { on_change: function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year; var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -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.set_report_in_process_SOA
erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance
erpnext.patches.v14_0.update_closing_balances #15-07-2023 erpnext.patches.v14_0.update_closing_balances #15-07-2023
execute:frappe.defaults.clear_default("fiscal_year")

View File

@@ -69,7 +69,6 @@ def execute():
entries = gl_entries + closing_entries entries = gl_entries + closing_entries
if entries: make_closing_entries(entries, pcv.name, pcv.company, pcv.posting_date)
make_closing_entries(entries, voucher_name=pcv.name) company_wise_order[pcv.company].append(pcv.posting_date)
i += 1 i += 1
company_wise_order[pcv.company].append(pcv.posting_date)

View File

@@ -56,7 +56,7 @@ erpnext.financial_statements = {
// dropdown for links to other financial statements // dropdown for links to other financial statements
erpnext.financial_statements.filters = get_filters() 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) { frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
@@ -137,7 +137,7 @@ function get_filters() {
"label": __("Start Year"), "label": __("Start Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
}, },
@@ -146,7 +146,7 @@ function get_filters() {
"label": __("End Year"), "label": __("End Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
}, },
@@ -182,6 +182,16 @@ function get_filters() {
company: frappe.query_report.get_filter_value("company") 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")
});
},
} }
] ]

View File

@@ -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) { erpnext.utils.select_alternate_items = function(opts) {
@@ -600,7 +617,6 @@ erpnext.utils.update_child_items = function(opts) {
fields.splice(3, 0, { fields.splice(3, 0, {
fieldtype: 'Float', fieldtype: 'Float',
fieldname: "conversion_factor", fieldname: "conversion_factor",
in_list_view: 1,
label: __("Conversion Factor"), label: __("Conversion Factor"),
precision: get_precision('conversion_factor') precision: get_precision('conversion_factor')
}) })
@@ -608,6 +624,7 @@ erpnext.utils.update_child_items = function(opts) {
new frappe.ui.Dialog({ new frappe.ui.Dialog({
title: __("Update Items"), title: __("Update Items"),
size: "extra-large",
fields: [ fields: [
{ {
fieldname: "trans_items", fieldname: "trans_items",
@@ -822,95 +839,87 @@ $(document).on('app_ready', function() {
// Show SLA dashboard // Show SLA dashboard
$(document).on('app_ready', function() { $(document).on('app_ready', function() {
frappe.call({ $.each(frappe.boot.service_level_agreement_doctypes, function(_i, d) {
method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_sla_doctypes', frappe.ui.form.on(d, {
callback: function(r) { onload: function(frm) {
if (!r.message) if (!frm.doc.service_level_agreement)
return; return;
$.each(r.message, function(_i, d) { frappe.call({
frappe.ui.form.on(d, { method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters',
onload: function(frm) { args: {
if (!frm.doc.service_level_agreement) doctype: frm.doc.doctype,
return; name: frm.doc.service_level_agreement,
customer: frm.doc.customer
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],
}
};
});
}
}
});
}, },
callback: function (r) {
refresh: function(frm) { if (r && r.message) {
if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement frm.set_query('priority', function() {
&& ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { return {
frappe.call({ filters: {
'method': 'frappe.client.get', 'name': ['in', r.message.priority],
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(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap '+ message.indicator +'"><span>'+ message.msg +'</span></span> ' +
'</div>' +
'</div>'
);
} else {
set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution);
} }
} };
});
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(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap '+ agreement_status.indicator +'"><span class="hidden-xs">'+ agreement_status.msg +'</span></span> ' +
'</div>' +
'</div>'
);
} }
}, }
}); });
}); },
}
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(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap '+ message.indicator +'"><span>'+ message.msg +'</span></span> ' +
'</div>' +
'</div>'
);
} 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(
'<div class="row">' +
'<div class="col-xs-12">' +
'<span class="indicator whitespace-nowrap '+ agreement_status.indicator +'"><span class="hidden-xs">'+ agreement_status.msg +'</span></span> ' +
'</div>' +
'</div>'
);
}
},
});
}); });
}); });

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = {
"label": __("Fiscal Year"), "label": __("Fiscal Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1 "reqd": 1
} }
], ],

View File

@@ -17,7 +17,7 @@ frappe.query_reports["IRS 1099"] = {
"label": __("Fiscal Year"), "label": __("Fiscal Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Fiscal Year", "options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"), "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1, "reqd": 1,
"width": 80, "width": 80,
}, },

View File

@@ -1,352 +1,99 @@
{ {
"allow_copy": 1, "actions": [],
"allow_guest_to_view": 0, "allow_copy": 1,
"allow_import": 0, "creation": "2013-05-02 17:53:24",
"allow_rename": 0, "doctype": "DocType",
"beta": 0, "engine": "InnoDB",
"creation": "2013-05-02 17:53:24", "field_order": [
"custom": 0, "default_company",
"docstatus": 0, "country",
"doctype": "DocType", "default_distance_unit",
"editable_grid": 0, "column_break_8",
"default_currency",
"hide_currency_symbol",
"disable_rounded_total",
"disable_in_words"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "fieldname": "default_company",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "ignore_user_permissions": 1,
"bold": 0, "label": "Default Company",
"collapsible": 0, "options": "Company"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "country",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "label": "Country",
"bold": 0, "options": "Country"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "default_distance_unit",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "label": "Default Distance Unit",
"bold": 0, "options": "UOM"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "column_break_8",
"allow_in_quick_entry": 0, "fieldtype": "Column Break"
"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
},
{ {
"allow_bulk_edit": 0, "default": "INR",
"allow_in_quick_entry": 0, "fieldname": "default_currency",
"allow_on_submit": 0, "fieldtype": "Link",
"bold": 0, "ignore_user_permissions": 1,
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "Default Currency",
"fieldname": "column_break_8", "options": "Currency",
"fieldtype": "Column Break", "reqd": 1
"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
},
{ {
"allow_bulk_edit": 0, "description": "Do not show any symbol like $ etc next to currencies.",
"allow_in_quick_entry": 0, "fieldname": "hide_currency_symbol",
"allow_on_submit": 0, "fieldtype": "Select",
"bold": 0, "in_list_view": 1,
"collapsible": 0, "label": "Hide Currency Symbol",
"columns": 0, "options": "\nNo\nYes"
"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
},
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_in_quick_entry": 0, "description": "If disable, 'Rounded Total' field will not be visible in any transaction",
"allow_on_submit": 0, "fieldname": "disable_rounded_total",
"bold": 0, "fieldtype": "Check",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "Disable Rounded Total"
"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
},
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_in_quick_entry": 0, "description": "If disable, 'In Words' field will not be visible in any transaction",
"allow_on_submit": 0, "fieldname": "disable_in_words",
"bold": 0, "fieldtype": "Check",
"collapsible": 0, "in_list_view": 1,
"columns": 0, "label": "Disable In Words"
"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
} }
], ],
"has_web_view": 0, "icon": "fa fa-cog",
"hide_heading": 0, "idx": 1,
"hide_toolbar": 0, "in_create": 1,
"icon": "fa fa-cog", "issingle": 1,
"idx": 1, "links": [],
"image_view": 0, "modified": "2023-07-01 19:45:00.323953",
"in_create": 1, "modified_by": "Administrator",
"is_submittable": 0, "module": "Setup",
"issingle": 1, "name": "Global Defaults",
"istable": 0, "owner": "Administrator",
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-10-15 03:08:19.886212",
"modified_by": "Administrator",
"module": "Setup",
"name": "Global Defaults",
"owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0, "create": 1,
"cancel": 0, "read": 1,
"create": 1, "role": "System Manager",
"delete": 0, "share": 1,
"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,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0, "read_only": 1,
"read_only": 1, "sort_field": "modified",
"read_only_onload": 0, "sort_order": "DESC",
"show_name_in_global_search": 0, "states": []
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
} }

View File

@@ -10,7 +10,6 @@ from frappe.utils import cint
keydict = { keydict = {
# "key in defaults": "key in Global Defaults" # "key in defaults": "key in Global Defaults"
"fiscal_year": "current_fiscal_year",
"company": "default_company", "company": "default_company",
"currency": "default_currency", "currency": "default_currency",
"country": "country", "country": "country",
@@ -29,22 +28,6 @@ class GlobalDefaults(Document):
for key in keydict: for key in keydict:
frappe.db.set_default(key, self.get(keydict[key], "")) 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 # enable default currency
if self.default_currency: if self.default_currency:
frappe.db.set_value("Currency", self.default_currency, "enabled", 1) frappe.db.set_value("Currency", self.default_currency, "enabled", 1)

View File

@@ -6,13 +6,41 @@ frappe.ui.form.on("Holiday List", {
if (frm.doc.holidays) { if (frm.doc.holidays) {
frm.set_value("total_holidays", frm.doc.holidays.length); 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) { from_date: function(frm) {
if (frm.doc.from_date && !frm.doc.to_date) { if (frm.doc.from_date && !frm.doc.to_date) {
var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); 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)); 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"] = [ frappe.tour["Holiday List"] = [

View File

@@ -1,480 +1,166 @@
{ {
"allow_copy": 0, "actions": [],
"allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "field:holiday_list_name", "autoname": "field:holiday_list_name",
"beta": 0,
"creation": "2013-01-10 16:34:14", "creation": "2013-01-10 16:34:14",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Setup",
"editable_grid": 0,
"engine": "InnoDB", "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": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "holiday_list_name", "fieldname": "holiday_list_name",
"fieldtype": "Data", "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", "label": "Holiday List Name",
"length": 0,
"no_copy": 0,
"oldfieldname": "holiday_list_name", "oldfieldname": "holiday_list_name",
"oldfieldtype": "Data", "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, "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1 "unique": 1
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date", "fieldname": "from_date",
"fieldtype": "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_list_view": 1,
"in_standard_filter": 0,
"label": "From Date", "label": "From Date",
"length": 0, "reqd": 1
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date", "fieldname": "to_date",
"fieldtype": "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_list_view": 1,
"in_standard_filter": 0,
"label": "To Date", "label": "To Date",
"length": 0, "reqd": 1
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4", "fieldname": "column_break_4",
"fieldtype": "Column Break", "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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_holidays", "fieldname": "total_holidays",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Holidays", "label": "Total Holidays",
"length": 0, "read_only": 1
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1, "collapsible": 1,
"columns": 0, "depends_on": "eval: doc.from_date && doc.to_date",
"fieldname": "add_weekly_holidays", "fieldname": "add_weekly_holidays",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Add Weekly Holidays"
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "weekly_off", "fieldname": "weekly_off",
"fieldtype": "Select", "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, "in_standard_filter": 1,
"label": "Weekly Off", "label": "Weekly Off",
"length": 0,
"no_copy": 1, "no_copy": 1,
"options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", "options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday",
"permlevel": 0,
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "report_hide": 1
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"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,
"fieldname": "get_weekly_off_dates", "fieldname": "get_weekly_off_dates",
"fieldtype": "Button", "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", "label": "Add to Holidays",
"length": 0, "options": "get_weekly_off_dates"
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "holidays_section", "fieldname": "holidays_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Holidays"
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "holidays", "fieldname": "holidays",
"fieldtype": "Table", "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", "label": "Holidays",
"length": 0,
"no_copy": 0,
"oldfieldname": "holiday_list_details", "oldfieldname": "holiday_list_details",
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "Holiday", "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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "clear_table", "fieldname": "clear_table",
"fieldtype": "Button", "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", "label": "Clear Table",
"length": 0, "options": "clear_table"
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_9", "fieldname": "section_break_9",
"fieldtype": "Section Break", "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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "color", "fieldname": "color",
"fieldtype": "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", "label": "Color",
"length": 0, "print_hide": 1
"no_copy": 0, },
"permlevel": 0, {
"precision": "", "fieldname": "country",
"print_hide": 1, "fieldtype": "Autocomplete",
"print_hide_if_no_value": 0, "label": "Country"
"read_only": 0, },
"remember_last_selected_value": 0, {
"report_hide": 0, "depends_on": "country",
"reqd": 0, "fieldname": "subdivision",
"search_index": 0, "fieldtype": "Autocomplete",
"set_only_once": 0, "label": "Subdivision"
"translatable": 0, },
"unique": 0 {
"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", "icon": "fa fa-calendar",
"idx": 1, "idx": 1,
"image_view": 0, "links": [],
"in_create": 0, "modified": "2023-07-14 13:28:53.156421",
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-07-03 07:22:46.474096",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Holiday List", "name": "Holiday List",
"naming_rule": "By fieldname",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "HR Manager", "role": "HR Manager",
"set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 0, "states": []
"track_seen": 0,
"track_views": 0
} }

View File

@@ -3,11 +3,15 @@
import json import json
from datetime import date
import frappe import frappe
from babel import Locale
from frappe import _, throw from frappe import _, throw
from frappe.model.document import Document 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): class OverlapError(frappe.ValidationError):
@@ -21,25 +25,66 @@ class HolidayList(Document):
@frappe.whitelist() @frappe.whitelist()
def get_weekly_off_dates(self): 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: if not self.weekly_off:
throw(_("Please select weekly off day")) 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): def validate_days(self):
if getdate(self.from_date) > getdate(self.to_date): if getdate(self.from_date) > getdate(self.to_date):
throw(_("To Date cannot be before From Date")) throw(_("To Date cannot be before From Date"))
@@ -120,3 +165,8 @@ def is_holiday(holiday_list, date=None):
) )
else: else:
return False 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)

View File

@@ -3,7 +3,7 @@
import unittest import unittest
from contextlib import contextmanager from contextlib import contextmanager
from datetime import timedelta from datetime import date, timedelta
import frappe import frappe
from frappe.utils import getdate from frappe.utils import getdate
@@ -23,6 +23,41 @@ class TestHolidayList(unittest.TestCase):
fetched_holiday_list = frappe.get_value("Holiday List", holiday_list.name) fetched_holiday_list = frappe.get_value("Holiday List", holiday_list.name)
self.assertEqual(holiday_list.name, fetched_holiday_list) 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( def make_holiday_list(
name, from_date=getdate() - timedelta(days=10), to_date=getdate(), holiday_dates=None name, from_date=getdate() - timedelta(days=10), to_date=getdate(), holiday_dates=None

View File

@@ -462,11 +462,9 @@ def install_defaults(args=None): # nosemgrep
def set_global_defaults(args): def set_global_defaults(args):
global_defaults = frappe.get_doc("Global Defaults", "Global Defaults") global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
current_fiscal_year = frappe.get_all("Fiscal Year")[0]
global_defaults.update( global_defaults.update(
{ {
"current_fiscal_year": current_fiscal_year.name,
"default_currency": args.get("currency"), "default_currency": args.get("currency"),
"default_company": args.get("company_name"), "default_company": args.get("company_name"),
"country": args.get("country"), "country": args.get("country"),

View File

@@ -194,7 +194,8 @@
"default": "0", "default": "0",
"fieldname": "disabled", "fieldname": "disabled",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Disabled" "label": "Disabled",
"search_index": 1
}, },
{ {
"default": "0", "default": "0",
@@ -911,7 +912,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"make_attachments_public": 1, "make_attachments_public": 1,
"modified": "2023-02-14 04:48:26.343620", "modified": "2023-07-14 17:18:18.658942",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item", "name": "Item",

View File

@@ -1,370 +1,90 @@
{ {
"allow_copy": 0, "actions": [],
"allow_events_in_timeline": 0, "creation": "2015-05-19 05:12:30.344797",
"allow_guest_to_view": 0, "doctype": "DocType",
"allow_import": 0, "document_type": "Other",
"allow_rename": 0, "editable_grid": 1,
"autoname": "", "engine": "InnoDB",
"beta": 0, "field_order": [
"creation": "2015-05-19 05:12:30.344797", "variant_of",
"custom": 0, "attribute",
"docstatus": 0, "column_break_2",
"doctype": "DocType", "attribute_value",
"document_type": "Other", "numeric_values",
"editable_grid": 1, "section_break_4",
"from_range",
"increment",
"column_break_8",
"to_range"
],
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0, "fieldname": "variant_of",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "label": "Variant Of",
"bold": 0, "options": "Item",
"collapsible": 0, "search_index": 1
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "attribute",
"allow_in_quick_entry": 0, "fieldtype": "Link",
"allow_on_submit": 0, "in_list_view": 1,
"bold": 0, "label": "Attribute",
"collapsible": 0, "options": "Item Attribute",
"columns": 0, "reqd": 1,
"fieldname": "attribute", "search_index": 1
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "column_break_2",
"allow_in_quick_entry": 0, "fieldtype": "Column Break"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "attribute_value",
"allow_in_quick_entry": 0, "fieldtype": "Data",
"allow_on_submit": 0, "in_list_view": 1,
"bold": 0, "label": "Attribute Value"
"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
},
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_in_quick_entry": 0, "depends_on": "has_variants",
"allow_on_submit": 0, "fieldname": "numeric_values",
"bold": 0, "fieldtype": "Check",
"collapsible": 0, "label": "Numeric Values"
"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
},
{ {
"allow_bulk_edit": 0, "depends_on": "numeric_values",
"allow_in_quick_entry": 0, "fieldname": "section_break_4",
"allow_on_submit": 0, "fieldtype": "Section Break"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "from_range",
"allow_in_quick_entry": 0, "fieldtype": "Float",
"allow_on_submit": 0, "label": "From Range"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "increment",
"allow_in_quick_entry": 0, "fieldtype": "Float",
"allow_on_submit": 0, "label": "Increment"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "column_break_8",
"allow_in_quick_entry": 0, "fieldtype": "Column Break"
"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
},
{ {
"allow_bulk_edit": 0, "fieldname": "to_range",
"allow_in_quick_entry": 0, "fieldtype": "Float",
"allow_on_submit": 0, "label": "To Range"
"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
} }
], ],
"has_web_view": 0, "istable": 1,
"hide_heading": 0, "links": [],
"hide_toolbar": 0, "modified": "2023-07-14 17:15:19.112119",
"icon": "", "modified_by": "Administrator",
"idx": 0, "module": "Stock",
"image_view": 0, "name": "Item Variant Attribute",
"in_create": 0, "owner": "Administrator",
"is_submittable": 0, "permissions": [],
"issingle": 0, "sort_field": "modified",
"istable": 1, "sort_order": "DESC",
"max_attachments": 0, "states": []
"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
} }

View File

@@ -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.get_value("Serial No", serial_nos[0], "status"), "Inactive")
self.assertEqual(frappe.db.exists("Batch", batch_no), None) 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): 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 Behaviour: 1) Create Stock Reconciliation, which will be the origin document

View File

@@ -9,13 +9,27 @@ frappe.query_reports["Batch Item Expiry Status"] = {
"fieldtype": "Date", "fieldtype": "Date",
"width": "80", "width": "80",
"default": frappe.sys_defaults.year_start_date, "default": frappe.sys_defaults.year_start_date,
"reqd": 1,
}, },
{ {
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"width": "80", "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}
}
}
} }
] ]
} }

View File

@@ -4,113 +4,86 @@
import frappe import frappe
from frappe import _ from frappe import _
from frappe.query_builder.functions import IfNull from frappe.query_builder.functions import Date
from frappe.utils import cint, getdate
def execute(filters=None): def execute(filters=None):
if not filters: validate_filters(filters)
filters = {}
float_precision = cint(frappe.db.get_default("float_precision")) or 3 columns = get_columns()
data = get_data(filters)
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,
]
)
return columns, data return columns, data
def get_columns(filters): def validate_filters(filters):
"""return columns based on 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"): if not filters.get("from_date"):
frappe.throw(_("'From Date' is required")) frappe.throw(_("'From Date' is required"))
if not filters.get("to_date"): if not filters.get("to_date"):
frappe.throw(_("'To Date' is required")) frappe.throw(_("'To Date' is required"))
sle = frappe.qb.DocType("Stock Ledger Entry")
query = ( def get_columns():
frappe.qb.from_(sle) return (
.select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty) [_("Item") + ":Link/Item:150"]
.where( + [_("Item Name") + "::150"]
(sle.is_cancelled == 0) + [_("Batch") + ":Link/Batch:150"]
& (sle.docstatus < 2) + [_("Stock UOM") + ":Link/UOM:100"]
& (IfNull(sle.batch_no, "") != "") + [_("Quantity") + ":Float:100"]
& (sle.posting_date <= filters["to_date"]) + [_("Expires On") + ":Date:100"]
) + [_("Expiry (In Days)") + ":Int:130"]
.orderby(sle.item_code, sle.warehouse)
) )
return query.run(as_dict=True)
def get_data(filters):
data = []
def get_item_warehouse_batch_map(filters, float_precision): for batch in get_batch_details(filters):
sle = get_stock_ledger_entries(filters) data.append(
iwb_map = {} [
batch.item,
from_date = getdate(filters["from_date"]) batch.item_name,
to_date = getdate(filters["to_date"]) batch.name,
batch.stock_uom,
for d in sle: batch.batch_qty,
iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault( batch.expiry_date,
d.batch_no, frappe._dict({"expires_on": None, "expiry_status": None}) 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] return data
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
def get_item_details(filters): def get_batch_details(filters):
item_map = {} batch = frappe.qb.DocType("Batch")
for d in (frappe.qb.from_("Item").select("name", "item_name", "description")).run(as_dict=True): query = (
item_map.setdefault(d.name, d) 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)

View File

@@ -579,7 +579,7 @@ class update_entries_after(object):
if get_serial_nos(sle.serial_no): if get_serial_nos(sle.serial_no):
self.get_serialized_values(sle) self.get_serialized_values(sle)
self.wh_data.qty_after_transaction += flt(sle.actual_qty) 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.qty_after_transaction = sle.qty_after_transaction
self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt( self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(

View File

@@ -21,6 +21,7 @@ from frappe.utils import (
time_diff_in_seconds, time_diff_in_seconds,
to_timedelta, to_timedelta,
) )
from frappe.utils.caching import redis_cache
from frappe.utils.nestedset import get_ancestors_of from frappe.utils.nestedset import get_ancestors_of
from frappe.utils.safe_exec import get_safe_globals from frappe.utils.safe_exec import get_safe_globals
@@ -209,6 +210,10 @@ class ServiceLevelAgreement(Document):
def on_update(self): def on_update(self):
set_documents_with_active_service_level_agreement() 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): def create_docfields(self, meta, service_level_agreement_fields):
last_index = len(meta.fields) last_index = len(meta.fields)
@@ -990,6 +995,7 @@ def get_user_time(user, to_string=False):
@frappe.whitelist() @frappe.whitelist()
@redis_cache()
def get_sla_doctypes(): def get_sla_doctypes():
doctypes = [] doctypes = []
data = frappe.get_all("Service Level Agreement", {"enabled": 1}, ["document_type"], distinct=1) 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) doctypes.append(entry.document_type)
return doctypes return doctypes
def add_sla_doctypes(bootinfo):
bootinfo.service_level_agreement_doctypes = get_sla_doctypes()

View File

@@ -15,7 +15,7 @@
{% for item, taxes in itemised_tax.items() %} {% for item, taxes in itemised_tax.items() %}
<tr> <tr>
<td>{{ item }}</td> <td>{{ item }}</td>
<td class='text-right'> <td class="text-right">
{% if doc.get('is_return') %} {% if doc.get('is_return') %}
{{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }} {{ frappe.utils.fmt_money((itemised_taxable_amount.get(item, 0))|abs, None, doc.currency) }}
{% else %} {% else %}
@@ -25,7 +25,7 @@
{% for tax_account in tax_accounts %} {% for tax_account in tax_accounts %}
{% set tax_details = taxes.get(tax_account) %} {% set tax_details = taxes.get(tax_account) %}
{% if tax_details %} {% if tax_details %}
<td class='text-right'> <td class="text-right">
{% if tax_details.tax_rate or not tax_details.tax_amount %} {% if tax_details.tax_rate or not tax_details.tax_amount %}
({{ tax_details.tax_rate }}%) ({{ tax_details.tax_rate }}%)
{% endif %} {% endif %}

View File

@@ -1221,7 +1221,7 @@ High Sensitivity,Hohe Empfindlichkeit,
Hold,Anhalten, Hold,Anhalten,
Hold Invoice,Rechnung zurückhalten, Hold Invoice,Rechnung zurückhalten,
Holiday,Urlaub, 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, Hotel Rooms of type {0} are unavailable on {1},Hotelzimmer vom Typ {0} sind auf {1} nicht verfügbar,
Hotels,Hotels, Hotels,Hotels,
Hourly,Stündlich, Hourly,Stündlich,
@@ -3330,7 +3330,7 @@ Workflow,Workflow,
Working,In Bearbeitung, Working,In Bearbeitung,
Working Hours,Arbeitszeit, Working Hours,Arbeitszeit,
Workstation,Arbeitsplatz, 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, Wrapping up,Aufwickeln,
Wrong Password,Falsches Passwort, 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", 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 / Manage Email Accounts.,Hinzufügen/Verwalten von E-Mail-Konten,
Add Child,Unterpunkt hinzufügen, Add Child,Unterpunkt hinzufügen,
Add Loan Security,Darlehenssicherheit hinzufügen, Add Loan Security,Darlehenssicherheit hinzufügen,
Add Local Holidays,Lokale Feiertage hinzufügen,
Add Multiple,Mehrere hinzufügen, Add Multiple,Mehrere hinzufügen,
Add Participants,Teilnehmer hinzufügen, Add Participants,Teilnehmer hinzufügen,
Add to Featured Item,Zum empfohlenen Artikel 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., 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}, Stores - {0},Stores - {0},
Student with email {0} does not exist,Der Student mit der E-Mail-Adresse {0} existiert nicht, Student with email {0} does not exist,Der Student mit der E-Mail-Adresse {0} existiert nicht,
Subdivision,Teilgebiet,
Submit Review,Bewertung abschicken, Submit Review,Bewertung abschicken,
Submitted,Gebucht, Submitted,Gebucht,
Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten, Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten,
@@ -4236,6 +4238,7 @@ Mode Of Payment,Zahlungsart,
No students Found,Keine Schüler gefunden, No students Found,Keine Schüler gefunden,
Not in Stock,Nicht lagernd, Not in Stock,Nicht lagernd,
Please select a Customer,Bitte wählen Sie einen Kunden aus, 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, Printed On,Gedruckt auf,
Received From,Erhalten von, Received From,Erhalten von,
Sales Person,Verkäufer, Sales Person,Verkäufer,
@@ -6546,7 +6549,7 @@ Reports to,Vorgesetzter,
Attendance and Leave Details,Anwesenheits- und Urlaubsdetails, Attendance and Leave Details,Anwesenheits- und Urlaubsdetails,
Leave Policy,Urlaubsrichtlinie, Leave Policy,Urlaubsrichtlinie,
Attendance Device ID (Biometric/RF tag ID),Anwesenheitsgeräte-ID (biometrische / RF-Tag-ID), 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, Default Shift,Standardverschiebung,
Salary Details,Gehaltsdetails, Salary Details,Gehaltsdetails,
Salary Mode,Gehaltsmodus, Salary Mode,Gehaltsmodus,
@@ -6708,15 +6711,15 @@ More Details,Mehr Details,
Expense Claim Account,Kostenabrechnung Konto, Expense Claim Account,Kostenabrechnung Konto,
Expense Claim Advance,Auslagenvorschuss, Expense Claim Advance,Auslagenvorschuss,
Unclaimed amount,Nicht beanspruchter Betrag, Unclaimed amount,Nicht beanspruchter Betrag,
Expense Claim Detail,Aufwandsabrechnungsdetail, Expense Claim Detail,Auslage,
Expense Date,Datum der Aufwendung, Expense Date,Datum der Auslage,
Expense Claim Type,Art der Aufwandsabrechnung, Expense Claim Type,Art der Auslagenabrechnung,
Holiday List Name,Urlaubslistenname, Holiday List Name,Name der Feiertagsliste,
Total Holidays,Insgesamt Feiertage, Total Holidays,Insgesamt freie Tage,
Add Weekly Holidays,Wöchentliche Feiertage hinzufügen, Add Weekly Holidays,Wöchentlich freie Tage hinzufügen,
Weekly Off,Wöchentlich frei, Weekly Off,Wöchentlich frei,
Add to Holidays,Zu Feiertagen hinzufügen, Add to Holidays,Zu freien Tagen hinzufügen,
Holidays,Ferien, Holidays,Arbeitsfreie Tage,
Clear Table,Tabelle leeren, Clear Table,Tabelle leeren,
HR Settings,Einstellungen zum Modul Personalwesen, HR Settings,Einstellungen zum Modul Personalwesen,
Employee Settings,Mitarbeitereinstellungen, Employee Settings,Mitarbeitereinstellungen,
@@ -6826,7 +6829,7 @@ Transaction Name,Transaktionsname,
Is Carry Forward,Ist Übertrag, Is Carry Forward,Ist Übertrag,
Is Expired,Ist abgelaufen, Is Expired,Ist abgelaufen,
Is Leave Without Pay,Ist unbezahlter Urlaub, 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 Allocations,Zuteilungen verlassen,
Leave Policy Details,Urlaubsrichtliniendetails, Leave Policy Details,Urlaubsrichtliniendetails,
Leave Policy Detail,Urlaubsrichtliniendetail, 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, Change Abbreviation,Abkürzung ändern,
Parent Company,Muttergesellschaft, Parent Company,Muttergesellschaft,
Default Values,Standardwerte, Default Values,Standardwerte,
Default Holiday List,Standard-Urlaubsliste, Default Holiday List,Standard Feiertagsliste,
Default Selling Terms,Standardverkaufsbedingungen, Default Selling Terms,Standardverkaufsbedingungen,
Default Buying Terms,Standard-Einkaufsbedingungen, Default Buying Terms,Standard-Einkaufsbedingungen,
Create Chart Of Accounts Based On,"Kontenplan erstellen, basierend auf", Create Chart Of Accounts Based On,"Kontenplan erstellen, basierend auf",
Can't render this file because it is too large.

View File

@@ -14,6 +14,7 @@ dependencies = [
"Unidecode~=1.2.0", "Unidecode~=1.2.0",
"redisearch~=2.1.0", "redisearch~=2.1.0",
"rapidfuzz~=2.15.0", "rapidfuzz~=2.15.0",
"holidays~=0.28",
# integration dependencies # integration dependencies
"gocardless-pro~=1.22.0", "gocardless-pro~=1.22.0",