Merge pull request #21690 from alyf-de/refactor_datev
fix(regional): DATEV report
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 4480110..d5ab1c1 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -2,10 +2,11 @@
import frappe
from frappe import _
-from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, cint, get_link_to_form
+from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, get_first_day, cint, get_link_to_form, rounded
from erpnext.accounts.utils import get_account_currency
from frappe.email import sendmail_to_system_managers
from frappe.utils.background_jobs import enqueue
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
def validate_service_stop_date(doc):
''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
@@ -109,6 +110,18 @@
order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+ prev_gl_via_je = frappe.db.sql('''
+ SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c
+ WHERE p.name = c.parent and p.company=%s and c.account=%s
+ and c.reference_type=%s and c.reference_name=%s
+ and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1
+ ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+
+ if prev_gl_via_je:
+ if (not prev_gl_entry) or (prev_gl_entry and
+ prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date):
+ prev_gl_entry = prev_gl_via_je
+
if prev_gl_entry:
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
else:
@@ -130,14 +143,48 @@
else:
return None, None, None
-def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
- if doc.doctype == "Sales Invoice":
- total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
- deferred_account = "deferred_revenue_account"
- else:
- total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
- deferred_account = "deferred_expense_account"
+def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
+ amount, base_amount = 0, 0
+ if not last_gl_entry:
+ total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \
+ (item.service_end_date.month - item.service_start_date.month) + 1
+
+ prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \
+ / flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date)))
+
+ actual_months = rounded(total_months * prorate_factor, 1)
+
+ already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
+ base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount"))
+
+ if base_amount + already_booked_amount > item.base_net_amount:
+ base_amount = item.base_net_amount - already_booked_amount
+
+ if account_currency==doc.company_currency:
+ amount = base_amount
+ else:
+ amount = flt(item.net_amount/actual_months, item.precision("net_amount"))
+ if amount + already_booked_amount_in_account_currency > item.net_amount:
+ amount = item.net_amount - already_booked_amount_in_account_currency
+
+ if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
+ partial_month = flt(date_diff(end_date, start_date)) \
+ / flt(date_diff(get_last_day(end_date), get_first_day(start_date)))
+
+ base_amount = rounded(partial_month, 1) * base_amount
+ amount = rounded(partial_month, 1) * amount
+ else:
+ already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
+ base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
+ if account_currency==doc.company_currency:
+ amount = base_amount
+ else:
+ amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
+
+ return amount, base_amount
+
+def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
amount, base_amount = 0, 0
if not last_gl_entry:
base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
@@ -146,27 +193,55 @@
else:
amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
else:
- gl_entries_details = frappe.db.sql('''
- select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
- from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
- group by voucher_detail_no
- '''.format(total_credit_debit, total_credit_debit_currency),
- (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
- already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
+ already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
+
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
amount = base_amount
else:
- already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
return amount, base_amount
+def get_already_booked_amount(doc, item):
+ if doc.doctype == "Sales Invoice":
+ total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
+ deferred_account = "deferred_revenue_account"
+ else:
+ total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
+ deferred_account = "deferred_expense_account"
+
+ gl_entries_details = frappe.db.sql('''
+ select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
+ from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
+ group by voucher_detail_no
+ '''.format(total_credit_debit, total_credit_debit_currency),
+ (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+
+ journal_entry_details = frappe.db.sql('''
+ SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
+ FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
+ p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
+ and p.docstatus < 2 group by reference_detail_no
+ '''.format(total_credit_debit, total_credit_debit_currency),
+ (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+
+ already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
+ already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0
+
+ if doc.currency == doc.company_currency:
+ already_booked_amount_in_account_currency = already_booked_amount
+ else:
+ already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
+ already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
+
+ return already_booked_amount, already_booked_amount_in_account_currency
+
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
- def _book_deferred_revenue_or_expense(item):
+ def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return
@@ -181,23 +256,34 @@
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(end_date, start_date) + 1
- amount, base_amount = calculate_amount(doc, item, last_gl_entry,
- total_days, total_booking_days, account_currency)
+ if book_deferred_entries_based_on == 'Months':
+ amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry,
+ start_date, end_date, total_days, total_booking_days, account_currency)
+ else:
+ amount, base_amount = calculate_amount(doc, item, last_gl_entry,
+ total_days, total_booking_days, account_currency)
- make_gl_entries(doc, credit_account, debit_account, against,
- amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
+ if via_journal_entry:
+ book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
+ base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
+ else:
+ make_gl_entries(doc, credit_account, debit_account, against,
+ amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error:
return
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
- _book_deferred_revenue_or_expense(item)
+ _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
+ via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry'))
+ submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries'))
+ book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on')
for item in doc.get('items'):
if item.get(enable_check):
- _book_deferred_revenue_or_expense(item)
+ _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
def process_deferred_accounting(posting_date=None):
''' Converts deferred income/expense into income/expense
@@ -281,3 +367,83 @@
and submit manually after resolving errors
""").format(get_link_to_form('Process Deferred Accounting', deferred_process))
sendmail_to_system_managers(title, content)
+
+def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
+ amount, base_amount, posting_date, project, account_currency, cost_center, item,
+ deferred_process=None, submit='No'):
+
+ if amount == 0: return
+
+ journal_entry = frappe.new_doc('Journal Entry')
+ journal_entry.posting_date = posting_date
+ journal_entry.company = doc.company
+ journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \
+ else 'Deferred Expense'
+
+ debit_entry = {
+ 'account': credit_account,
+ 'credit': base_amount,
+ 'credit_in_account_currency': amount,
+ 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
+ 'party': against,
+ 'account_currency': account_currency,
+ 'reference_name': doc.name,
+ 'reference_type': doc.doctype,
+ 'reference_detail_no': item.name,
+ 'cost_center': cost_center,
+ 'project': project,
+ }
+
+ credit_entry = {
+ 'account': debit_account,
+ 'debit': base_amount,
+ 'debit_in_account_currency': amount,
+ 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
+ 'party': against,
+ 'account_currency': account_currency,
+ 'reference_name': doc.name,
+ 'reference_type': doc.doctype,
+ 'reference_detail_no': item.name,
+ 'cost_center': cost_center,
+ 'project': project,
+ }
+
+ for dimension in get_accounting_dimensions():
+ debit_entry.update({
+ dimension: item.get(dimension)
+ })
+
+ credit_entry.update({
+ dimension: item.get(dimension)
+ })
+
+ journal_entry.append('accounts', debit_entry)
+ journal_entry.append('accounts', credit_entry)
+
+ try:
+ journal_entry.save()
+
+ if submit:
+ journal_entry.submit()
+ except:
+ frappe.db.rollback()
+ traceback = frappe.get_traceback()
+ frappe.log_error(message=traceback)
+
+ frappe.flags.deferred_accounting_error = True
+
+def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
+
+ if doctype == 'Sales Invoice':
+ credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no},
+ ['income_account', 'deferred_revenue_account'])
+ else:
+ credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no},
+ ['deferred_expense_account', 'expense_account'])
+
+ if dr_or_cr == 'Debit':
+ return debit_account
+ else:
+ return credit_account
+
+
diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json
index af252e6..d2659d4 100644
--- a/erpnext/accounts/doctype/account/account.json
+++ b/erpnext/accounts/doctype/account/account.json
@@ -34,11 +34,15 @@
{
"fieldname": "properties",
"fieldtype": "Section Break",
- "oldfieldtype": "Section Break"
+ "oldfieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -49,7 +53,9 @@
"no_copy": 1,
"oldfieldname": "account_name",
"oldfieldtype": "Data",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "account_number",
@@ -57,13 +63,17 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Account Number",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "is_group",
"fieldtype": "Check",
- "label": "Is Group"
+ "label": "Is Group",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "company",
@@ -75,7 +85,9 @@
"options": "Company",
"read_only": 1,
"remember_last_selected_value": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "root_type",
@@ -83,7 +95,9 @@
"in_standard_filter": 1,
"label": "Root Type",
"options": "\nAsset\nLiability\nIncome\nExpense\nEquity",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "report_type",
@@ -91,24 +105,32 @@
"in_standard_filter": 1,
"label": "Report Type",
"options": "\nBalance Sheet\nProfit and Loss",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"depends_on": "eval:doc.is_group==0",
"fieldname": "account_currency",
"fieldtype": "Link",
"label": "Currency",
- "options": "Currency"
+ "options": "Currency",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "inter_company_account",
"fieldtype": "Check",
- "label": "Inter Company Account"
+ "label": "Inter Company Account",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1,
"width": "50%"
},
{
@@ -120,7 +142,9 @@
"oldfieldtype": "Link",
"options": "Account",
"reqd": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"description": "Setting Account Type helps in selecting this Account in transactions.",
@@ -130,7 +154,9 @@
"label": "Account Type",
"oldfieldname": "account_type",
"oldfieldtype": "Select",
- "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nTax\nTemporary"
+ "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"description": "Rate at which this tax is applied",
@@ -138,7 +164,9 @@
"fieldtype": "Float",
"label": "Rate",
"oldfieldname": "tax_rate",
- "oldfieldtype": "Currency"
+ "oldfieldtype": "Currency",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"description": "If the account is frozen, entries are allowed to restricted users.",
@@ -147,13 +175,17 @@
"label": "Frozen",
"oldfieldname": "freeze_account",
"oldfieldtype": "Select",
- "options": "No\nYes"
+ "options": "No\nYes",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "balance_must_be",
"fieldtype": "Select",
"label": "Balance must be",
- "options": "\nDebit\nCredit"
+ "options": "\nDebit\nCredit",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "lft",
@@ -162,7 +194,9 @@
"label": "Lft",
"print_hide": 1,
"read_only": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "rgt",
@@ -171,7 +205,9 @@
"label": "Rgt",
"print_hide": 1,
"read_only": 1,
- "search_index": 1
+ "search_index": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "old_parent",
@@ -179,27 +215,33 @@
"hidden": 1,
"label": "Old Parent",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)",
"fieldname": "include_in_gross",
"fieldtype": "Check",
- "label": "Include in gross"
+ "label": "Include in gross",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
- "label": "Disable"
+ "label": "Disable",
+ "show_days": 1,
+ "show_seconds": 1
}
],
"icon": "fa fa-money",
"idx": 1,
"is_tree": 1,
"links": [],
- "modified": "2020-03-18 17:57:52.063233",
+ "modified": "2020-06-11 15:15:54.338622",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 353ff77..e4b96ae 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -22,7 +22,12 @@
"allow_cost_center_in_entry_of_bs_account",
"add_taxes_from_item_tax_template",
"automatically_fetch_payment_terms",
+ "deferred_accounting_settings_section",
"automatically_process_deferred_accounting_entry",
+ "book_deferred_entries_based_on",
+ "column_break_18",
+ "book_deferred_entries_via_journal_entry",
+ "submit_journal_entries",
"print_settings",
"show_inclusive_tax_in_print",
"column_break_12",
@@ -189,13 +194,45 @@
"fieldname": "automatically_process_deferred_accounting_entry",
"fieldtype": "Check",
"label": "Automatically Process Deferred Accounting Entry"
+ },
+ {
+ "fieldname": "deferred_accounting_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Deferred Accounting Settings"
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "description": "If this is unchecked direct GL Entries will be created to book Deferred Revenue/Expense",
+ "fieldname": "book_deferred_entries_via_journal_entry",
+ "fieldtype": "Check",
+ "label": "Book Deferred Entries Via Journal Entry"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.book_deferred_entries_via_journal_entry",
+ "description": "If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually",
+ "fieldname": "submit_journal_entries",
+ "fieldtype": "Check",
+ "label": "Submit Journal Entries"
+ },
+ {
+ "default": "Days",
+ "description": "If \"Months\" is selected then fixed amount will be booked as deferred revenue or expense for each month irrespective of number of days in a month. Will be prorated if deferred revenue or expense is not booked for an entire month.",
+ "fieldname": "book_deferred_entries_based_on",
+ "fieldtype": "Select",
+ "label": "Book Deferred Entries Based On",
+ "options": "Days\nMonths"
}
],
"icon": "icon-cog",
"idx": 1,
"issingle": 1,
"links": [],
- "modified": "2019-12-19 16:58:17.395595",
+ "modified": "2020-06-22 20:13:26.043092",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
index 0fab8b7..db4f7c4 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js
@@ -188,14 +188,15 @@
},
show_general_ledger: (frm) => {
- if(frm.doc.docstatus===1) {
+ if(frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
from_date: frm.doc.posting_date,
- to_date: frm.doc.posting_date,
+ to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
company: frm.doc.company,
- group_by: "Group by Voucher (Consolidated)"
+ group_by: "Group by Voucher (Consolidated)",
+ show_cancelled_entries: frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 5685f83..a09face 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -13,15 +13,16 @@
refresh: function(frm) {
erpnext.toggle_naming_series();
- if(frm.doc.docstatus==1) {
+ if(frm.doc.docstatus > 0) {
frm.add_custom_button(__('Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.posting_date,
- "to_date": frm.doc.posting_date,
+ "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'),
"company": frm.doc.company,
"finance_book": frm.doc.finance_book,
- "group_by_voucher": 0
+ "group_by": '',
+ "show_cancelled_entries": frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, __('View'));
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index af2aa65..4573c50 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -83,7 +83,7 @@
"label": "Entry Type",
"oldfieldname": "voucher_type",
"oldfieldtype": "Select",
- "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation",
+ "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense",
"reqd": 1,
"search_index": 1
},
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index caaf30f..7360b39 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -10,6 +10,7 @@
from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
+from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
from six import string_types, iteritems
@@ -265,7 +266,10 @@
# set totals
if not d.reference_name in self.reference_totals:
self.reference_totals[d.reference_name] = 0.0
- self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
+
+ if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'):
+ self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
+
self.reference_types[d.reference_name] = d.reference_type
self.reference_accounts[d.reference_name] = d.account
@@ -277,10 +281,16 @@
# check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
- if d.reference_type == "Sales Invoice":
- party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
+ if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no:
+ debit_or_credit = 'Debit' if d.debit else 'Credit'
+ party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
+ debit_or_credit)
else:
- party_account = against_voucher[1]
+ if d.reference_type == "Sales Invoice":
+ party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
+ else:
+ party_account = against_voucher[1]
+
if (against_voucher[0] != d.party or party_account != d.account):
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
@@ -513,14 +523,20 @@
"against_voucher_type": d.reference_type,
"against_voucher": d.reference_name,
"remarks": remarks,
+ "voucher_detail_no": d.reference_detail_no,
"cost_center": d.cost_center,
"project": d.project,
"finance_book": self.finance_book
}, item=d)
)
+ if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
+ update_outstanding = 'No'
+ else:
+ update_outstanding = 'Yes'
+
if gl_map:
- make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj)
+ make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
def get_balance(self):
if not self.get('accounts'):
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index ff3533a..774159d 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -33,6 +33,7 @@
"reference_type",
"reference_name",
"reference_due_date",
+ "reference_detail_no",
"col_break3",
"is_advance",
"user_remark",
@@ -268,12 +269,18 @@
"fieldtype": "Link",
"label": "Bank Account",
"options": "Bank Account"
+ },
+ {
+ "fieldname": "reference_detail_no",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Reference Detail No"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-06-18 14:06:54.833738",
+ "modified": "2020-06-24 14:06:54.833738",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index a378a51..42c9fde 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -172,8 +172,8 @@
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
frm.toggle_display("base_received_amount", (
- frm.doc.paid_to_account_currency != company_currency
- && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency
+ frm.doc.paid_to_account_currency != company_currency
+ && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency
&& frm.doc.base_paid_amount != frm.doc.base_received_amount
));
@@ -234,14 +234,15 @@
},
show_general_ledger: function(frm) {
- if(frm.doc.docstatus==1) {
+ if(frm.doc.docstatus > 0) {
frm.add_custom_button(__('Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.posting_date,
- "to_date": frm.doc.posting_date,
+ "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'),
"company": frm.doc.company,
- group_by: ""
+ "group_by": "",
+ "show_cancelled_entries": frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, "fa fa-table");
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js
index 87e02fe..e923d4e 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js
@@ -25,9 +25,10 @@
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.posting_date,
- "to_date": frm.doc.posting_date,
+ "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'),
"company": frm.doc.company,
- group_by_voucher: 0
+ "group_by": "",
+ "show_cancelled_entries": frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, "fa fa-table");
diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js
index 975c60c..2800c19 100644
--- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js
+++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js
@@ -10,6 +10,18 @@
}
};
});
+
+ if (frm.doc.company) {
+ frm.set_query("account", function() {
+ return {
+ filters: {
+ 'company': frm.doc.company,
+ 'root_type': 'Liability',
+ 'is_group': 0
+ }
+ };
+ });
+ }
},
validate: function() {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 870718c..3cd57d4 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -587,6 +587,20 @@
else:
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
+ auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items'))
+
+ if auto_accounting_for_non_stock_items:
+ service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
+
+ if item.purchase_receipt:
+ # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
+ expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0,
+ 'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail,
+ 'account':service_received_but_not_billed_account}, ['name'])
+
+ if expense_booked_in_pr:
+ expense_account = service_received_but_not_billed_account
+
gl_entries.append(self.get_gl_dict({
"account": expense_account,
"against": self.supplier,
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 6170005..b5955ca 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -7,14 +7,15 @@
import frappe, erpnext
import frappe.model
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
-from frappe.utils import cint, flt, today, nowdate, add_days
+from frappe.utils import cint, flt, today, nowdate, add_days, getdate
import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records, make_purchase_receipt, get_taxes
from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
-from erpnext.accounts.doctype.account.test_account import get_inventory_account
+from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
+from erpnext.stock.doctype.item.test_item import create_item
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
test_ignore = ["Serial No"]
@@ -866,6 +867,67 @@
for gle in gl_entries:
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
+ def test_deferred_expense_via_journal_entry(self):
+ deferred_account = create_account(account_name="Deferred Expense",
+ parent_account="Current Assets - _TC", company="_Test Company")
+
+ acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+ acc_settings.book_deferred_entries_via_journal_entry = 1
+ acc_settings.submit_journal_entries = 1
+ acc_settings.save()
+
+ item = create_item("_Test Item for Deferred Accounting")
+ item.enable_deferred_expense = 1
+ item.deferred_expense_account = deferred_account
+ item.save()
+
+ pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True)
+ pi.set_posting_time = 1
+ pi.posting_date = '2019-03-15'
+ pi.items[0].enable_deferred_expense = 1
+ pi.items[0].service_start_date = "2019-01-10"
+ pi.items[0].service_end_date = "2019-03-15"
+ pi.items[0].deferred_expense_account = deferred_account
+ pi.save()
+ pi.submit()
+
+ pda1 = frappe.get_doc(dict(
+ doctype='Process Deferred Accounting',
+ posting_date=nowdate(),
+ start_date="2019-01-01",
+ end_date="2019-03-31",
+ type="Expense",
+ company="_Test Company"
+ ))
+
+ pda1.insert()
+ pda1.submit()
+
+ expected_gle = [
+ ["_Test Account Cost for Goods Sold - _TC", 0.0, 33.85, "2019-01-31"],
+ [deferred_account, 33.85, 0.0, "2019-01-31"],
+ ["_Test Account Cost for Goods Sold - _TC", 0.0, 43.08, "2019-02-28"],
+ [deferred_account, 43.08, 0.0, "2019-02-28"],
+ ["_Test Account Cost for Goods Sold - _TC", 0.0, 23.07, "2019-03-15"],
+ [deferred_account, 23.07, 0.0, "2019-03-15"]
+ ]
+
+ gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
+ from `tabGL Entry`
+ where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
+ order by posting_date asc, account asc""", (pi.items[0].name, pi.posting_date), as_dict=1)
+
+ for i, gle in enumerate(gl_entries):
+ self.assertEqual(expected_gle[i][0], gle.account)
+ self.assertEqual(expected_gle[i][1], gle.credit)
+ self.assertEqual(expected_gle[i][2], gle.debit)
+ self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
+
+ acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+ acc_settings.book_deferred_entries_via_journal_entry = 0
+ acc_settings.submit_journal_entriessubmit_journal_entries = 0
+ acc_settings.save()
+
def unlink_payment_on_cancel_of_invoice(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 6cdf9b5..311cc12 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1720,8 +1720,6 @@
si.save()
si.submit()
- from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
-
pda1 = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
posting_date=nowdate(),
@@ -1745,6 +1743,55 @@
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
+ def test_fixed_deferred_revenue(self):
+ deferred_account = create_account(account_name="Deferred Revenue",
+ parent_account="Current Liabilities - _TC", company="_Test Company")
+
+ acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+ acc_settings.book_deferred_entries_based_on = 'Months'
+ acc_settings.save()
+
+ item = create_item("_Test Item for Deferred Accounting")
+ item.enable_deferred_revenue = 1
+ item.deferred_revenue_account = deferred_account
+ item.no_of_months = 12
+ item.save()
+
+ si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True)
+ si.items[0].enable_deferred_revenue = 1
+ si.items[0].service_start_date = "2019-01-16"
+ si.items[0].service_end_date = "2019-03-31"
+ si.items[0].deferred_revenue_account = deferred_account
+ si.save()
+ si.submit()
+
+ pda1 = frappe.get_doc(dict(
+ doctype='Process Deferred Accounting',
+ posting_date='2019-03-31',
+ start_date="2019-01-01",
+ end_date="2019-03-31",
+ type="Income",
+ company="_Test Company"
+ ))
+
+ pda1.insert()
+ pda1.submit()
+
+ expected_gle = [
+ [deferred_account, 10000.0, 0.0, "2019-01-31"],
+ ["Sales - _TC", 0.0, 10000.0, "2019-01-31"],
+ [deferred_account, 20000.0, 0.0, "2019-02-28"],
+ ["Sales - _TC", 0.0, 20000.0, "2019-02-28"],
+ [deferred_account, 20000.0, 0.0, "2019-03-31"],
+ ["Sales - _TC", 0.0, 20000.0, "2019-03-31"]
+ ]
+
+ check_gl_entries(self, si.name, expected_gle, "2019-01-30")
+
+ acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+ acc_settings.book_deferred_entries_based_on = 'Days'
+ acc_settings.save()
+
def test_inter_company_transaction(self):
if not frappe.db.exists("Customer", "_Test Internal Customer"):
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 2ecabe6..9ae5a87 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -408,6 +408,8 @@
row.expected_value_after_useful_life = asset_value_after_full_schedule
def validate_cancellation(self):
+ if self.status in ("In Maintenance", "Out of Order"):
+ frappe.throw(_("There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset."))
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index dfdb487..56af4d9 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -51,7 +51,7 @@
def validate_email_id(self, args):
if not args.email_id:
- frappe.throw(_("Row {0}: For supplier {0} Email Address is required to send email").format(args.idx, args.supplier))
+ frappe.throw(_("Row {0}: For Supplier {0}, Email Address is Required to Send Email").format(args.idx, args.supplier))
def on_submit(self):
frappe.db.set(self, 'status', 'Submitted')
@@ -154,7 +154,7 @@
sender=sender,attachments = attachments, send_email=True,
doctype=self.doctype, name=self.name)["name"]
- frappe.msgprint(_("Email sent to supplier {0}").format(data.supplier))
+ frappe.msgprint(_("Email Sent to Supplier {0}").format(data.supplier))
def get_attachments(self):
attachments = [d.name for d in get_attachments(self.doctype, self.name)]
@@ -193,7 +193,7 @@
def check_portal_enabled(reference_doctype):
if not frappe.db.get_value('Portal Menu Item',
{'reference_doctype': reference_doctype}, 'enabled'):
- frappe.throw(_("Request for Quotation is disabled to access from portal, for more check portal settings."))
+ frappe.throw(_("The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings."))
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
@@ -259,7 +259,7 @@
sq_doc.flags.ignore_permissions = True
sq_doc.run_method("set_missing_values")
sq_doc.save()
- frappe.msgprint(_("Supplier Quotation {0} created").format(sq_doc.name))
+ frappe.msgprint(_("Supplier Quotation {0} Created").format(sq_doc.name))
return sq_doc.name
except Exception:
return None
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index f54b593..ead503e 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -20,6 +20,7 @@
from six import text_type
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from erpnext.stock.get_item_details import get_item_warehouse
+from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
@@ -1301,6 +1302,7 @@
parent.set_qty_as_per_stock_uom()
parent.calculate_taxes_and_totals()
if parent_doctype == "Sales Order":
+ make_packing_list(parent)
parent.set_gross_profit()
frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype,
parent.company, parent.base_grand_total)
diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py
index 092baa4..9b4b0eb 100644
--- a/erpnext/controllers/trends.py
+++ b/erpnext/controllers/trends.py
@@ -33,7 +33,7 @@
frappe.throw(_("{0} is mandatory").format(f))
if not frappe.db.exists("Fiscal Year", filters.get("fiscal_year")):
- frappe.throw(_("Fiscal Year: {0} does not exists").format(filters.get("fiscal_year")))
+ frappe.throw(_("Fiscal Year {0} Does Not Exist").format(filters.get("fiscal_year")))
if filters.get("based_on") == filters.get("group_by"):
frappe.throw(_("'Based On' and 'Group By' can not be same"))
diff --git a/erpnext/crm/doctype/contract/contract.json b/erpnext/crm/doctype/contract/contract.json
index e04ad30..0026e4a 100755
--- a/erpnext/crm/doctype/contract/contract.json
+++ b/erpnext/crm/doctype/contract/contract.json
@@ -29,6 +29,9 @@
"requires_fulfilment",
"fulfilment_deadline",
"fulfilment_terms",
+ "authorised_by_section",
+ "signee_company",
+ "signed_by_company",
"sb_references",
"document_type",
"cb_links",
@@ -223,10 +226,28 @@
"options": "Contract",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "signee_company",
+ "fieldtype": "Signature",
+ "label": "Signee (Company)"
+ },
+ {
+ "fieldname": "signed_by_company",
+ "fieldtype": "Link",
+ "label": "Signed By (Company)",
+ "options": "User",
+ "read_only": 1
+ },
+ {
+ "fieldname": "authorised_by_section",
+ "fieldtype": "Section Break",
+ "label": "Authorised By"
}
],
"is_submittable": 1,
- "modified": "2019-09-30 00:56:41.559681",
+ "links": [],
+ "modified": "2020-03-30 06:56:07.257932",
"modified_by": "Administrator",
"module": "CRM",
"name": "Contract",
diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py
index 18444cc..c39397b 100644
--- a/erpnext/crm/doctype/contract/contract.py
+++ b/erpnext/crm/doctype/contract/contract.py
@@ -29,6 +29,9 @@
self.update_contract_status()
self.update_fulfilment_status()
+ def before_submit(self):
+ self.signed_by_company = frappe.session.user
+
def before_update_after_submit(self):
self.update_contract_status()
self.update_fulfilment_status()
diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json
index b883ce2..ef9974f 100644
--- a/erpnext/crm/doctype/contract_template/contract_template.json
+++ b/erpnext/crm/doctype/contract_template/contract_template.json
@@ -1,306 +1,106 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "field:title",
- "beta": 0,
- "creation": "2018-04-16 06:44:48.791312",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "field:title",
+ "creation": "2018-04-16 06:44:48.791312",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "title",
+ "contract_terms",
+ "sb_fulfilment",
+ "requires_fulfilment",
+ "fulfilment_terms"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "title",
- "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": "Title",
- "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,
- "unique": 0
- },
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "label": "Title",
+ "unique": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sb_terms",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "",
- "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,
- "unique": 0
- },
+ "fieldname": "contract_terms",
+ "fieldtype": "Text Editor",
+ "label": "Contract Terms and Conditions",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "contract_terms",
- "fieldtype": "Text Editor",
- "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": "Contract Terms and Conditions",
- "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,
- "unique": 0
- },
+ "fieldname": "sb_fulfilment",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sb_fulfilment",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "",
- "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,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "requires_fulfilment",
+ "fieldtype": "Check",
+ "label": "Requires Fulfilment"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "requires_fulfilment",
- "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": "Requires Fulfilment",
- "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,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.requires_fulfilment==1",
- "fieldname": "fulfilment_terms",
- "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": "Fulfilment Terms and Conditions",
- "length": 0,
- "no_copy": 0,
- "options": "Contract Template Fulfilment Terms",
- "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,
- "unique": 0
+ "depends_on": "eval:doc.requires_fulfilment==1",
+ "fieldname": "fulfilment_terms",
+ "fieldtype": "Table",
+ "label": "Fulfilment Terms and Conditions",
+ "options": "Contract Template Fulfilment Terms"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-04-17 07:36:05.217599",
- "modified_by": "Administrator",
- "module": "CRM",
- "name": "Contract Template",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "links": [],
+ "modified": "2020-06-03 00:24:58.179816",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Contract Template",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Sales Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Sales Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Purchase Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Purchase Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "HR Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js
index 17ef449..867866f 100644
--- a/erpnext/education/doctype/fees/fees.js
+++ b/erpnext/education/doctype/fees/fees.js
@@ -55,14 +55,15 @@
frm.set_df_property('posting_date', 'read_only', 1);
frm.set_df_property('posting_time', 'read_only', 1);
}
- if(frm.doc.docstatus===1) {
+ if(frm.doc.docstatus > 0) {
frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
from_date: frm.doc.posting_date,
- to_date: frm.doc.posting_date,
+ to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
company: frm.doc.company,
- group_by_voucher: false
+ group_by: '',
+ show_cancelled_entries: frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
diff --git a/erpnext/education/report/absent_student_report/absent_student_report.json b/erpnext/education/report/absent_student_report/absent_student_report.json
index 0d5eeba..92ad860 100644
--- a/erpnext/education/report/absent_student_report/absent_student_report.json
+++ b/erpnext/education/report/absent_student_report/absent_student_report.json
@@ -1,20 +1,21 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2013-05-13 14:04:03",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2017-11-10 19:42:36.457449",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Absent Student Report",
- "owner": "Administrator",
- "ref_doctype": "Student Attendance",
- "report_name": "Absent Student Report",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2013-05-13 14:04:03",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 3,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:16:40.251116",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Absent Student Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Student Attendance",
+ "report_name": "Absent Student Report",
+ "report_type": "Script Report",
"roles": [
{
"role": "Academics User"
diff --git a/erpnext/education/report/assessment_plan_status/assessment_plan_status.json b/erpnext/education/report/assessment_plan_status/assessment_plan_status.json
index 3000bec..cbca648 100644
--- a/erpnext/education/report/assessment_plan_status/assessment_plan_status.json
+++ b/erpnext/education/report/assessment_plan_status/assessment_plan_status.json
@@ -1,20 +1,21 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2017-11-09 15:07:30.404428",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2017-11-28 18:35:44.903665",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Assessment Plan Status",
- "owner": "Administrator",
- "ref_doctype": "Assessment Plan",
- "report_name": "Assessment Plan Status",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2017-11-09 15:07:30.404428",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:16:02.027410",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Assessment Plan Status",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Assessment Plan",
+ "report_name": "Assessment Plan Status",
+ "report_type": "Script Report",
"roles": [
{
"role": "Academics User"
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json
index 61976b4..416db9d 100644
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json
+++ b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json
@@ -1,24 +1,26 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2017-05-05 14:46:13.776133",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2018-02-08 15:11:24.904628",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Course wise Assessment Report",
- "owner": "Administrator",
- "ref_doctype": "Assessment Result",
- "report_name": "Course wise Assessment Report",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2017-05-05 14:46:13.776133",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:15:15.477530",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Course wise Assessment Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "query": "",
+ "ref_doctype": "Assessment Result",
+ "report_name": "Course wise Assessment Report",
+ "report_type": "Script Report",
"roles": [
{
"role": "Instructor"
- },
+ },
{
"role": "Education Manager"
}
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.json b/erpnext/education/report/final_assessment_grades/final_assessment_grades.json
index 4d444b4..6a23494 100644
--- a/erpnext/education/report/final_assessment_grades/final_assessment_grades.json
+++ b/erpnext/education/report/final_assessment_grades/final_assessment_grades.json
@@ -1,24 +1,25 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2018-01-22 17:04:43.412054",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2019-02-08 15:11:35.339434",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Final Assessment Grades",
- "owner": "Administrator",
- "ref_doctype": "Assessment Result",
- "report_name": "Final Assessment Grades",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2018-01-22 17:04:43.412054",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:13:35.373756",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Final Assessment Grades",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Assessment Result",
+ "report_name": "Final Assessment Grades",
+ "report_type": "Script Report",
"roles": [
{
"role": "Instructor"
- },
+ },
{
"role": "Education Manager"
}
diff --git a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json
index fe7d158..fa9be65 100644
--- a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json
+++ b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json
@@ -1,24 +1,25 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2017-03-27 17:47:16.831433",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2017-11-10 19:42:30.300729",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student and Guardian Contact Details",
- "owner": "Administrator",
- "ref_doctype": "Program Enrollment",
- "report_name": "Student and Guardian Contact Details",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2017-03-27 17:47:16.831433",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:16:50.639488",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Student and Guardian Contact Details",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Program Enrollment",
+ "report_name": "Student and Guardian Contact Details",
+ "report_type": "Script Report",
"roles": [
{
"role": "Instructor"
- },
+ },
{
"role": "Academics User"
}
diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json
index eb547b7..8baf8f9 100644
--- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json
+++ b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json
@@ -1,20 +1,21 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2016-11-28 22:07:03.859124",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 2,
- "is_standard": "Yes",
- "modified": "2017-11-10 19:41:12.328346",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Batch-Wise Attendance",
- "owner": "Administrator",
- "ref_doctype": "Student Attendance",
- "report_name": "Student Batch-Wise Attendance",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2016-11-28 22:07:03.859124",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 2,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:16:59.823709",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Student Batch-Wise Attendance",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Student Attendance",
+ "report_name": "Student Batch-Wise Attendance",
+ "report_type": "Script Report",
"roles": [
{
"role": "Academics User"
diff --git a/erpnext/education/report/student_fee_collection/student_fee_collection.json b/erpnext/education/report/student_fee_collection/student_fee_collection.json
index eb945cf..8deb865 100644
--- a/erpnext/education/report/student_fee_collection/student_fee_collection.json
+++ b/erpnext/education/report/student_fee_collection/student_fee_collection.json
@@ -1,21 +1,22 @@
{
- "add_total_row": 0,
- "creation": "2016-06-22 02:58:41.024538",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2018-12-17 16:46:46.176620",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Fee Collection",
- "owner": "Administrator",
- "prepared_report": 0,
- "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
- "ref_doctype": "Fees",
- "report_name": "Student Fee Collection",
- "report_type": "Query Report",
+ "add_total_row": 0,
+ "creation": "2016-06-22 02:58:41.024538",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 3,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:14:39.452551",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Student Fee Collection",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
+ "ref_doctype": "Fees",
+ "report_name": "Student Fee Collection",
+ "report_type": "Query Report",
"roles": [
{
"role": "Academics User"
diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json
index e10f190..1423d4f 100644
--- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json
+++ b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json
@@ -1,20 +1,21 @@
{
- "add_total_row": 0,
- "apply_user_permissions": 1,
- "creation": "2013-05-13 14:04:03",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2017-11-10 19:42:43.376658",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Student Monthly Attendance Sheet",
- "owner": "Administrator",
- "ref_doctype": "Student Attendance",
- "report_name": "Student Monthly Attendance Sheet",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2013-05-13 14:04:03",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 3,
+ "is_standard": "Yes",
+ "modified": "2020-06-24 17:16:13.307053",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Student Monthly Attendance Sheet",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Student Attendance",
+ "report_name": "Student Monthly Attendance Sheet",
+ "report_type": "Script Report",
"roles": [
{
"role": "Academics User"
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 6bb9af9..fa63ec2 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -213,12 +213,15 @@
refresh: function(frm) {
frm.trigger("toggle_fields");
- if(frm.doc.docstatus === 1 && frm.doc.approval_status !== "Rejected") {
+ if(frm.doc.docstatus > 0 && frm.doc.approval_status !== "Rejected") {
frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
company: frm.doc.company,
- group_by_voucher: false
+ from_date: frm.doc.posting_date,
+ to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
+ group_by: '',
+ show_cancelled_entries: frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_list.js b/erpnext/hr/doctype/job_applicant/job_applicant_list.js
new file mode 100644
index 0000000..3b9141b
--- /dev/null
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_list.js
@@ -0,0 +1,15 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+// MIT License. See license.txt
+
+frappe.listview_settings['Job Applicant'] = {
+ add_fields: ["company", "designation", "job_applicant", "status"],
+ get_indicator: function (doc) {
+ if (doc.status == "Accepted") {
+ return [__(doc.status), "green", "status,=," + doc.status];
+ } else if (["Open", "Replied"].includes(doc.status)) {
+ return [__(doc.status), "orange", "status,=," + doc.status];
+ } else if (["Hold", "Rejected"].includes(doc.status)) {
+ return [__(doc.status), "red", "status,=," + doc.status];
+ }
+ }
+};
diff --git a/erpnext/hr/doctype/job_offer/job_offer.json b/erpnext/hr/doctype/job_offer/job_offer.json
index ccbfdc5..c0b7f69 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.json
+++ b/erpnext/hr/doctype/job_offer/job_offer.json
@@ -30,7 +30,6 @@
{
"fieldname": "job_applicant",
"fieldtype": "Link",
- "in_list_view": 1,
"label": "Job Applicant",
"options": "Job Applicant",
"print_hide": 1,
@@ -161,7 +160,7 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2019-12-31 02:40:33.650728",
+ "modified": "2020-06-25 00:56:24.756395",
"modified_by": "Administrator",
"module": "HR",
"name": "Job Offer",
diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py
index 32f1b89..9a2c4c6 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.py
+++ b/erpnext/hr/doctype/job_offer/job_offer.py
@@ -20,10 +20,9 @@
staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date)
check_vacancies = frappe.get_single("HR Settings").check_vacancies
if staffing_plan and check_vacancies:
- vacancies = frappe.db.get_value("Staffing Plan Detail", filters={"name": staffing_plan.name}, fieldname=['vacancies'])
- job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date))
- if vacancies - job_offers <= 0:
- frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent)))
+ job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)
+ if staffing_plan.vacancies - len(job_offers) <= 0:
+ frappe.throw(_("There are no vacancies under staffing plan {0}").format(frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent))))
def on_change(self):
update_job_applicant(self.status, self.job_applicant)
@@ -42,18 +41,22 @@
def get_staffing_plan_detail(designation, company, offer_date):
detail = frappe.db.sql("""
- SELECT spd.name as name,
+ SELECT DISTINCT spd.parent,
sp.from_date as from_date,
sp.to_date as to_date,
- sp.name as parent
+ sp.name,
+ sum(spd.vacancies) as vacancies,
+ spd.designation
FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp
WHERE
sp.docstatus=1
AND spd.designation=%s
AND sp.company=%s
+ AND spd.parent = sp.name
AND %s between sp.from_date and sp.to_date
""", (designation, company, offer_date), as_dict=1)
- return detail[0] if detail else None
+
+ return frappe._dict(detail[0]) if detail else None
@frappe.whitelist()
def make_employee(source_name, target_doc=None):
diff --git a/erpnext/hr/doctype/job_offer/job_offer_list.js b/erpnext/hr/doctype/job_offer/job_offer_list.js
new file mode 100644
index 0000000..4fa5be7
--- /dev/null
+++ b/erpnext/hr/doctype/job_offer/job_offer_list.js
@@ -0,0 +1,15 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+// MIT License. See license.txt
+
+frappe.listview_settings['Job Offer'] = {
+ add_fields: ["company", "designation", "job_applicant", "status"],
+ get_indicator: function (doc) {
+ if (doc.status == "Accepted") {
+ return [__(doc.status), "green", "status,=," + doc.status];
+ } else if (doc.status == "Awaiting Response") {
+ return [__(doc.status), "orange", "status,=," + doc.status];
+ } else if (doc.status == "Rejected") {
+ return [__(doc.status), "red", "status,=," + doc.status];
+ }
+ }
+};
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index 76e10e5..4e805d4 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -235,8 +235,10 @@
@frappe.whitelist()
def create_loan_security_unpledge(loan, applicant_type, applicant, company, as_dict=1):
loan_security_pledge_details = frappe.db.sql("""
- SELECT p.parent, p.loan_security, p.qty as qty FROM `tabLoan Security Pledge` lsp , `tabPledge` p
+ SELECT p.loan_security, sum(p.qty) as qty
+ FROM `tabLoan Security Pledge` lsp , `tabPledge` p
WHERE p.parent = lsp.name AND lsp.loan = %s AND lsp.docstatus = 1
+ GROUP BY p.loan_security
""",(loan), as_dict=1)
unpledge_request = frappe.new_doc("Loan Security Unpledge")
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index c28994e..9605045 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -116,7 +116,7 @@
def allocate_amounts(self, paid_entries):
self.set('repayment_details', [])
self.principal_amount_paid = 0
- interest_paid = 0
+ interest_paid = self.amount_paid - self.penalty_amount
if self.amount_paid - self.penalty_amount > 0 and paid_entries:
interest_paid = self.amount_paid - self.penalty_amount
diff --git a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json
index db260a4..a55b482 100644
--- a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json
+++ b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "LM-LSP-.####",
"creation": "2019-09-03 18:20:31.382887",
"doctype": "DocType",
@@ -46,6 +47,7 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Loan Security Price",
+ "options": "Company:company:default_currency",
"reqd": 1
},
{
@@ -79,7 +81,8 @@
"read_only": 1
}
],
- "modified": "2019-10-26 09:46:46.069667",
+ "links": [],
+ "modified": "2020-06-11 03:41:33.900340",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Security Price",
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 308c438..ffd9673 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -19,7 +19,9 @@
return
if security_value >= loan_security_shortfall.shortfall_amount:
- frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, "status", "Completed")
+ frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, {
+ "status": "Completed",
+ "shortfall_value": loan_security_shortfall.shortfall_amount})
else:
frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name,
"shortfall_amount", loan_security_shortfall.shortfall_amount - security_value)
diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js
index 3a47a88..d9dd415 100644
--- a/erpnext/loan_management/loan_common.js
+++ b/erpnext/loan_management/loan_common.js
@@ -9,12 +9,15 @@
}
if (['Loan Disbursement', 'Loan Repayment', 'Loan Interest Accrual'].includes(frm.doc.doctype)
- && frm.doc.docstatus == 1) {
+ && frm.doc.docstatus > 0) {
frm.add_custom_button(__("Accounting Ledger"), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
- company: frm.doc.company
+ company: frm.doc.company,
+ from_date: frm.doc.posting_date,
+ to_date: moment(frm.doc.modified).format('YYYY-MM-DD'),
+ show_cancelled_entries: frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 560286e..c889237 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -98,11 +98,17 @@
elif self.get_items_from == "Material Request":
self.get_mr_items()
+ def get_so_mr_list(self, field, table):
+ """Returns a list of Sales Orders or Material Requests from the respective tables"""
+ so_mr_list = [d.get(field) for d in self.get(table) if d.get(field)]
+ return so_mr_list
+
def get_so_items(self):
- so_list = [d.sales_order for d in self.sales_orders if d.sales_order]
- if not so_list:
- msgprint(_("Please enter Sales Orders in the above table"))
- return []
+ # Check for empty table or empty rows
+ if not self.get("sales_orders") or not self.get_so_mr_list("sales_order", "sales_orders"):
+ frappe.throw(_("Please fill the Sales Orders table"), title=_("Sales Orders Required"))
+
+ so_list = self.get_so_mr_list("sales_order", "sales_orders")
item_condition = ""
if self.item_code:
@@ -134,10 +140,11 @@
self.calculate_total_planned_qty()
def get_mr_items(self):
- mr_list = [d.material_request for d in self.material_requests if d.material_request]
- if not mr_list:
- msgprint(_("Please enter Material Requests in the above table"))
- return []
+ # Check for empty table or empty rows
+ if not self.get("material_requests") or not self.get_so_mr_list("material_request", "material_requests"):
+ frappe.throw(_("Please fill the Material Requests table"), title=_("Material Requests Required"))
+
+ mr_list = self.get_so_mr_list("material_request", "material_requests")
item_condition = ""
if self.item_code:
@@ -628,16 +635,19 @@
if warehouse_list:
warehouses = list(set(warehouse_list))
-
+
if doc.get("for_warehouse") and doc.get("for_warehouse") in warehouses:
warehouses.remove(doc.get("for_warehouse"))
warehouse_list = None
doc['mr_items'] = []
+
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
- if not po_items:
- frappe.throw(_("Items are required to pull the raw materials which is associated with it."))
+ # Check for empty table or empty rows
+ if not po_items or not [row.get('item_code') for row in po_items if row.get('item_code')]:
+ frappe.throw(_("Items to Manufacture are required to pull the Raw Materials associated with it."),
+ title=_("Items Required"))
company = doc.get('company')
ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 928c0ab..17fbcc2 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -699,6 +699,7 @@
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
erpnext.patches.v13_0.update_sla_enhancements
erpnext.patches.v12_0.update_address_template_for_india
+erpnext.patches.v13_0.update_deferred_settings
erpnext.patches.v12_0.set_multi_uom_in_rfq
erpnext.patches.v13_0.delete_old_sales_reports
execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")
diff --git a/erpnext/patches/v13_0/update_deferred_settings.py b/erpnext/patches/v13_0/update_deferred_settings.py
new file mode 100644
index 0000000..a7d8207
--- /dev/null
+++ b/erpnext/patches/v13_0/update_deferred_settings.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2019, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
+ accounts_settings.book_deferred_entries_based_on = 'Days'
+ accounts_settings.book_deferred_entries_via_journal_entry = 0
+ accounts_settings.submit_journal_entries = 0
+ accounts_settings.save()
\ No newline at end of file
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json
index 663a3ef..88931c2 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.json
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json
@@ -381,7 +381,6 @@
{
"fieldname": "earning",
"fieldtype": "Column Break",
- "label": "Earning",
"oldfieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1,
@@ -400,7 +399,6 @@
{
"fieldname": "deduction",
"fieldtype": "Column Break",
- "label": "Deduction",
"oldfieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1,
@@ -616,7 +614,7 @@
"idx": 9,
"is_submittable": 1,
"links": [],
- "modified": "2020-06-22 14:42:43.921828",
+ "modified": "2020-06-25 14:42:43.921828",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip",
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index f42b9ad..be9a2d3 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -271,6 +271,7 @@
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
frappe.db.sql("""delete from `tabPayroll Period`""")
frappe.db.sql("""delete from `tabSalary Component`""")
+ frappe.db.sql("""delete from `tabAdditional Salary`""")
payroll_period = create_payroll_period()
diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
index e59d78d..d07a1ab 100644
--- a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
+++ b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html
@@ -8,7 +8,7 @@
Variables from Employee:<br> <code>Employment Type = employment_type</code>, <code>Branch = branch</code> etc.
</li>
<li>
- Variables Salary Slip:<br>
+ Variables from Salary Slip:<br>
<code>Payment Days = payment_days</code>, <code>Leave without pay = leave_without_pay</code> etc.
</li>
<li>
diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js
index ca458f9..ad93a2f 100755
--- a/erpnext/payroll/doctype/salary_structure/salary_structure.js
+++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js
@@ -35,7 +35,9 @@
d.show()
});
- frm.get_field("conditions_and_formula_variable_and_example").$wrapper.append(frm.doc.filters_html).append(help_button)
+ let help_button_wrapper = frm.get_field("conditions_and_formula_variable_and_example").$wrapper;
+ help_button_wrapper.empty();
+ help_button_wrapper.append(frm.doc.filters_html).append(help_button)
frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet)
diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json
index ce9512f..94eda4c 100644
--- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json
+++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json
@@ -14,6 +14,7 @@
],
"fields": [
{
+ "default": "0",
"fieldname": "from_amount",
"fieldtype": "Currency",
"in_list_view": 1,
@@ -27,6 +28,7 @@
"label": "To Amount"
},
{
+ "default": "0",
"fieldname": "percent_deduction",
"fieldtype": "Percent",
"in_list_view": 1,
@@ -51,7 +53,7 @@
],
"istable": 1,
"links": [],
- "modified": "2020-06-22 23:32:47.253106",
+ "modified": "2020-06-22 18:16:07.596493",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Taxable Salary Slab",
diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
index 3bad587..8a79416 100644
--- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
+++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py
@@ -6,8 +6,8 @@
from frappe import _
def execute(filters=None):
- columns = get_columns(filters)
data = get_data(filters)
+ columns = get_columns(filters) if len(data) else []
return columns, data
@@ -78,8 +78,11 @@
if filters.get("company"):
conditions.append("sal.company = '%s' " % (filters["company"]) )
- if filters.get("period"):
- conditions.append("month(sal.start_date) = '%s' " % (filters["period"]))
+ if filters.get("month"):
+ conditions.append("month(sal.start_date) = '%s' " % (filters["month"]))
+
+ if filters.get("year"):
+ conditions.append("year(start_date) = '%s' " % (filters["year"]))
return " and ".join(conditions)
@@ -96,6 +99,9 @@
component_types = [comp_type[0] for comp_type in component_types]
+ if not len(component_types):
+ return []
+
conditions = get_conditions(filters)
entry = frappe.db.sql(""" select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay
diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
index 073bd91..d09745c 100644
--- a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
+++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py
@@ -84,9 +84,11 @@
if filters.get("company"):
conditions.append("company = '%s' " % (filters["company"]) )
- if filters.get("period"):
- conditions.append("month(start_date) = '%s' " % (filters["period"]))
- conditions.append("year(start_date) = '%s' " % (frappe.utils.getdate().year))
+ if filters.get("month"):
+ conditions.append("month(start_date) = '%s' " % (filters["month"]))
+
+ if filters.get("year"):
+ conditions.append("year(start_date) = '%s' " % (filters["year"]))
return " and ".join(conditions)
diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py
index 6b6b8c5..f8af30a 100644
--- a/erpnext/portal/product_configurator/utils.py
+++ b/erpnext/portal/product_configurator/utils.py
@@ -239,13 +239,12 @@
if exact_match:
data = get_product_info_for_website(exact_match[0])
product_info = data.product_info
+ product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock)
if not data.cart_settings.show_price:
product_info = None
else:
product_info = None
- product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock)
-
return {
'next_attribute': next_attribute,
'valid_options_for_attributes': valid_options_for_attributes,
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 03b67b1..a5ce44d 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -140,52 +140,6 @@
settings.ignore_employee_time_overlap = initial_setting
settings.save()
- def test_timesheet_std_working_hours(self):
- emp = make_employee("test_employee_6@salary.com")
-
- company = frappe.get_doc('Company', "_Test Company")
- company.standard_working_hours = 8
- company.save()
-
- timesheet = frappe.new_doc("Timesheet")
- timesheet.employee = emp
- timesheet.company = '_Test Company'
- timesheet.append(
- 'time_logs',
- {
- "activity_type": "_Test Activity Type",
- "from_time": now_datetime(),
- "to_time": now_datetime() + datetime.timedelta(days= 4)
- }
- )
- timesheet.save()
-
- ts = frappe.get_doc('Timesheet', timesheet.name)
- self.assertEqual(ts.total_hours, 32)
- ts.submit()
- ts.cancel()
-
- company = frappe.get_doc('Company', "_Test Company")
- company.standard_working_hours = 0
- company.save()
-
- timesheet = frappe.new_doc("Timesheet")
- timesheet.employee = emp
- timesheet.company = '_Test Company'
- timesheet.append(
- 'time_logs',
- {
- "activity_type": "_Test Activity Type",
- "from_time": now_datetime(),
- "to_time": now_datetime() + datetime.timedelta(days= 4)
- }
- )
- timesheet.save()
-
- ts = frappe.get_doc('Timesheet', timesheet.name)
- self.assertEqual(ts.total_hours, 96)
- ts.submit()
- ts.cancel()
def make_salary_structure_for_timesheet(employee):
salary_structure_name = "Timesheet Salary Structure Test"
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index defc18b..5de2930 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -162,19 +162,11 @@
to_time: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
- var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / ( 60 * 60 * 24);
- var std_working_hours = 0;
if(frm._setting_hours) return;
var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
- std_working_hours = time_diff * frappe.working_hours;
-
- if (std_working_hours < hours && std_working_hours > 0) {
- frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
- } else {
- frappe.model.set_value(cdt, cdn, "hours", hours);
- }
+ frappe.model.set_value(cdt, cdn, "hours", hours);
},
time_logs_add: function(frm) {
@@ -236,23 +228,12 @@
let d = moment(child.from_time);
if(child.hours) {
- var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / (60 * 60 * 24);
- var std_working_hours = 0;
- var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600;
-
- std_working_hours = time_diff * frappe.working_hours;
-
- if (std_working_hours < hours && std_working_hours > 0) {
- frappe.model.set_value(cdt, cdn, "hours", std_working_hours);
- frappe.model.set_value(cdt, cdn, "to_time", d.add(hours, "hours").format(frappe.defaultDatetimeFormat));
- } else {
- d.add(child.hours, "hours");
- frm._setting_hours = true;
- frappe.model.set_value(cdt, cdn, "to_time",
- d.format(frappe.defaultDatetimeFormat)).then(() => {
- frm._setting_hours = false;
- });
- }
+ d.add(child.hours, "hours");
+ frm._setting_hours = true;
+ frappe.model.set_value(cdt, cdn, "to_time",
+ d.format(frappe.defaultDatetimeFormat)).then(() => {
+ frm._setting_hours = false;
+ });
}
};
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index e908216..7fe22be 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -24,7 +24,6 @@
self.set_status()
self.validate_dates()
self.validate_time_logs()
- self.calculate_std_hours()
self.update_cost()
self.calculate_total_amounts()
self.calculate_percentage_billed()
@@ -91,17 +90,6 @@
self.start_date = getdate(start_date)
self.end_date = getdate(end_date)
- def calculate_std_hours(self):
- std_working_hours = frappe.get_value("Company", self.company, 'standard_working_hours')
-
- for time in self.time_logs:
- if time.from_time and time.to_time:
- if flt(std_working_hours) and date_diff(time.to_time, time.from_time):
- time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time)
- else:
- if not time.hours:
- time.hours = time_diff_in_hours(time.to_time, time.from_time)
-
def before_cancel(self):
self.set_status()
diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js
index 2ce49e7..87b21b7 100644
--- a/erpnext/public/js/controllers/stock_controller.js
+++ b/erpnext/public/js/controllers/stock_controller.js
@@ -55,8 +55,9 @@
frappe.route_options = {
voucher_no: me.frm.doc.name,
from_date: me.frm.doc.posting_date,
- to_date: me.frm.doc.posting_date,
- company: me.frm.doc.company
+ to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'),
+ company: me.frm.doc.company,
+ show_cancelled_entries: me.frm.doc.docstatus === 2
};
frappe.set_route("query-report", "Stock Ledger");
}, __("View"));
@@ -71,9 +72,10 @@
frappe.route_options = {
voucher_no: me.frm.doc.name,
from_date: me.frm.doc.posting_date,
- to_date: me.frm.doc.posting_date,
+ to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'),
company: me.frm.doc.company,
- group_by: "Group by Voucher (Consolidated)"
+ group_by: "Group by Voucher (Consolidated)",
+ show_cancelled_entries: me.frm.doc.docstatus === 2
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ca897dd..905f6fb 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -159,6 +159,26 @@
};
});
}
+ if (this.frm.fields_dict["items"].grid.get_field("cost_center")) {
+ this.frm.set_query("cost_center", "items", function(doc) {
+ return {
+ filters: {
+ "company": doc.company,
+ "is_group": 0
+ }
+ };
+ });
+ }
+
+ if (this.frm.fields_dict["items"].grid.get_field("expense_account")) {
+ this.frm.set_query("expense_account", "items", function(doc) {
+ return {
+ filters: {
+ "company": doc.company
+ }
+ };
+ });
+ }
if(frappe.meta.get_docfield(this.frm.doc.doctype, "pricing_rules")) {
this.frm.set_indicator_formatter('pricing_rule', function(doc) {
diff --git a/erpnext/public/js/salary_slip_deductions_report_filters.js b/erpnext/public/js/salary_slip_deductions_report_filters.js
index 2420379..2b30e65 100644
--- a/erpnext/public/js/salary_slip_deductions_report_filters.js
+++ b/erpnext/public/js/salary_slip_deductions_report_filters.js
@@ -11,8 +11,8 @@
default: frappe.defaults.get_user_default("Company"),
},
{
- fieldname: "period",
- label: __("Period"),
+ fieldname: "month",
+ label: __("Month"),
fieldtype: "Select",
reqd: 1 ,
options: [
@@ -32,6 +32,12 @@
default: frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1
},
{
+ fieldname:"year",
+ label: __("Year"),
+ fieldtype: "Select",
+ reqd: 1
+ },
+ {
fieldname: "department",
label: __("Department"),
fieldtype: "Link",
@@ -43,5 +49,18 @@
fieldtype: "Link",
options: "Branch",
}
- ]
+ ],
+
+ "onload": function() {
+ return frappe.call({
+ method: "erpnext.regional.report.provident_fund_deductions.provident_fund_deductions.get_years",
+ callback: function(r) {
+ var year_filter = frappe.query_report.get_filter('year');
+ year_filter.df.options = r.message;
+ year_filter.df.default = r.message.split("\n")[0];
+ year_filter.refresh();
+ year_filter.set_input(year_filter.df.default);
+ }
+ });
+ }
}
\ No newline at end of file
diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
index 900fe96..acde68a 100644
--- a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
+++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py
@@ -7,8 +7,8 @@
from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions
def execute(filters=None):
- columns = get_columns(filters)
data = get_data(filters)
+ columns = get_columns(filters) if len(data) else []
return columns, data
@@ -45,6 +45,9 @@
component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component`
where component_type = 'Professional Tax' """))
+ if not len(component_type_dict):
+ return []
+
conditions = get_conditions(filters)
entry = frappe.db.sql(""" select sal.employee, sal.employee_name, ded.salary_component, ded.amount
diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
index 9f58957..084890e 100644
--- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
+++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py
@@ -3,11 +3,12 @@
from __future__ import unicode_literals
import frappe
+from frappe.utils import getdate
from frappe import _
def execute(filters=None):
- columns = get_columns(filters)
data = get_data(filters)
+ columns = get_columns(filters) if len(data) else []
return columns, data
@@ -71,10 +72,13 @@
conditions.append("sal.branch = '%s' " % (filters["branch"]) )
if filters.get("company"):
- conditions.append("sal.company = '%s' " % (filters["company"]) )
+ conditions.append("sal.company = '%s' " % (filters["company"]))
- if filters.get("period"):
- conditions.append("month(sal.start_date) = '%s' " % (filters["period"]))
+ if filters.get("month"):
+ conditions.append("month(sal.start_date) = '%s' " % (filters["month"]))
+
+ if filters.get("year"):
+ conditions.append("year(start_date) = '%s' " % (filters["year"]))
if filters.get("mode_of_payment"):
conditions.append("sal.mode_of_payment = '%s' " % (filters["mode_of_payment"]))
@@ -114,6 +118,9 @@
component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component`
where component_type in ('Provident Fund', 'Additional Provident Fund', 'Provident Fund Loan')"""))
+ if not len(component_type_dict):
+ return []
+
entry = frappe.db.sql(""" select sal.name, sal.employee, sal.employee_name, ded.salary_component, ded.amount
from `tabSalary Slip` sal, `tabSalary Detail` ded
where sal.name = ded.parent
@@ -150,4 +157,12 @@
data.append(employee)
- return data
\ No newline at end of file
+ return data
+
+@frappe.whitelist()
+def get_years():
+ year_list = frappe.db.sql_list("""select distinct YEAR(end_date) from `tabSalary Slip` ORDER BY YEAR(end_date) DESC""")
+ if not year_list:
+ year_list = [getdate().year]
+
+ return "\n".join(str(year) for year in year_list)
\ No newline at end of file
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 682dfed..d70c64f 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -388,8 +388,7 @@
credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager")
# form a list of emails and names to show to the user
- credit_controller_users_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})]
- credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list]
+ credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users]
if not credit_controller_users:
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
@@ -409,7 +408,7 @@
'customer': customer,
'customer_outstanding': customer_outstanding,
'credit_limit': credit_limit,
- 'credit_controller_users_list': credit_controller_users_list
+ 'credit_controller_users_list': credit_controller_users
}
}
)
diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
index 85a2b20..7d1d372 100644
--- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py
+++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py
@@ -7,7 +7,7 @@
import frappe
test_records = frappe.get_test_records('Product Bundle')
-def make_product_bundle(parent, items):
+def make_product_bundle(parent, items, qty=None):
if frappe.db.exists("Product Bundle", parent):
return frappe.get_doc("Product Bundle", parent)
@@ -17,7 +17,7 @@
})
for item in items:
- product_bundle.append("items", {"item_code": item, "qty": 1})
+ product_bundle.append("items", {"item_code": item, "qty": qty or 1})
product_bundle.insert()
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 74e742f..accaa9c 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
+import json
from frappe.utils import flt, add_days, nowdate
import frappe.permissions
import unittest
@@ -10,9 +11,10 @@
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
from erpnext.controllers.accounts_controller import update_child_qty_rate
-import json
from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order
+from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+from erpnext.stock.doctype.item.test_item import make_item
class TestSalesOrder(unittest.TestCase):
def tearDown(self):
@@ -417,6 +419,26 @@
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
frappe.set_user("Administrator")
+ def test_update_child_qty_rate_product_bundle(self):
+ # test Update Items with product bundle
+ if not frappe.db.exists("Item", "_Product Bundle Item"):
+ bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0})
+ bundle_item.append("item_defaults", {
+ "company": "_Test Company",
+ "default_warehouse": "_Test Warehouse - _TC"})
+ bundle_item.save(ignore_permissions=True)
+
+ make_item("_Packed Item", {"is_stock_item": 1})
+ make_product_bundle("_Product Bundle Item", ["_Packed Item"], 2)
+
+ so = make_sales_order(item_code = "_Test Item", warehouse=None)
+
+ added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}])
+ update_child_qty_rate('Sales Order', added_item, so.name)
+
+ so.reload()
+ self.assertEqual(so.packed_items[0].qty, 4)
+
def test_warehouse_user(self):
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
@@ -457,8 +479,6 @@
self.assertRaises(frappe.CancelledLinkError, dn.submit)
def test_service_type_product_bundle(self):
- from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
- from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Service Product Bundle", {"is_stock_item": 0})
make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0})
make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0})
@@ -472,8 +492,6 @@
self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items])
def test_mix_type_product_bundle(self):
- from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
- from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Mix Product Bundle", {"is_stock_item": 0})
make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1})
make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0})
@@ -484,7 +502,6 @@
self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="")
def test_auto_insert_price(self):
- from erpnext.stock.doctype.item.test_item import make_item
make_item("_Test Item for Auto Price List", {"is_stock_item": 0})
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
@@ -519,7 +536,6 @@
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
- from erpnext.stock.doctype.item.test_item import make_item
po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1})
dn_item = make_item("_Test Regular Item", {"is_stock_item": 1})
@@ -714,7 +730,6 @@
def test_serial_no_based_delivery(self):
frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
- from erpnext.stock.doctype.item.test_item import make_item
item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1,
"maintain_stock": 1,
"has_serial_no": 1,
@@ -835,7 +850,6 @@
self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
def test_request_for_raw_materials(self):
- from erpnext.stock.doctype.item.test_item import make_item
item = make_item("_Test Finished Item", {"is_stock_item": 1,
"maintain_stock": 1,
"valuation_rate": 500,
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
index bd59be6..1bc4657 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
@@ -9,6 +9,9 @@
def execute(filters=None):
filters = frappe._dict(filters or {})
+ if filters.from_date > filters.to_date:
+ frappe.throw(_('From Date cannot be greater than To Date'))
+
columns = get_columns(filters)
data = get_data(filters)
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 875904f..7ae5385 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -264,7 +264,10 @@
["expenses_included_in_valuation",
{"root_type": "Expense", "account_type": "Expenses Included in Valuation"}],
["stock_received_but_not_billed",
- {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}]
+ {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}],
+ ["service_received_but_not_billed",
+ {"root_type": "Liability", "account_type": "Service Received But Not Billed"}],
+
], function(i, v) {
erpnext.company.set_custom_query(frm, v);
});
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 020a93f..221044d 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -22,7 +22,6 @@
"default_letter_head",
"default_holiday_list",
"default_finance_book",
- "standard_working_hours",
"default_selling_terms",
"default_buying_terms",
"default_warehouse_for_sales_return",
@@ -67,10 +66,12 @@
"payment_terms",
"auto_accounting_for_stock_settings",
"enable_perpetual_inventory",
+ "enable_perpetual_inventory_for_non_stock_items",
"default_inventory_account",
"stock_adjustment_account",
"column_break_32",
"stock_received_but_not_billed",
+ "service_received_but_not_billed",
"expenses_included_in_valuation",
"fixed_asset_depreciation_settings",
"accumulated_depreciation_account",
@@ -239,11 +240,6 @@
"options": "Holiday List"
},
{
- "fieldname": "standard_working_hours",
- "fieldtype": "Float",
- "label": "Standard Working Hours"
- },
- {
"fieldname": "default_warehouse_for_sales_return",
"fieldtype": "Link",
"label": "Default warehouse for Sales Return",
@@ -723,6 +719,20 @@
"fieldtype": "Link",
"label": "Default Buying Terms",
"options": "Terms and Conditions"
+ },
+ {
+ "fieldname": "service_received_but_not_billed",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Service Received But Not Billed",
+ "no_copy": 1,
+ "options": "Account"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_perpetual_inventory_for_non_stock_items",
+ "fieldtype": "Check",
+ "label": "Enable Perpetual Inventory For Non Stock Items"
}
],
"icon": "fa fa-building",
@@ -730,7 +740,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
- "modified": "2020-03-21 18:09:53.534211",
+ "modified": "2020-06-24 12:45:31.462195",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 8bcaa28..47b41a9 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -46,6 +46,7 @@
self.validate_currency()
self.validate_coa_input()
self.validate_perpetual_inventory()
+ self.validate_perpetual_inventory_for_non_stock_items()
self.check_country_change()
self.set_chart_of_accounts()
self.validate_parent_company()
@@ -182,6 +183,12 @@
frappe.msgprint(_("Set default inventory account for perpetual inventory"),
alert=True, indicator='orange')
+ def validate_perpetual_inventory_for_non_stock_items(self):
+ if not self.get("__islocal"):
+ if cint(self.enable_perpetual_inventory_for_non_stock_items) == 1 and not self.service_received_but_not_billed:
+ frappe.throw(_("Set default {0} account for perpetual inventory for non stock items").format(
+ frappe.bold('Service Received But Not Billed')))
+
def check_country_change(self):
frappe.flags.country_change = False
diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js
index c199a8e..44a5019 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.js
+++ b/erpnext/setup/doctype/customer_group/customer_group.js
@@ -8,7 +8,7 @@
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root customer group
- if(!doc.parent_customer_group) {
+ if(!doc.parent_customer_group && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root customer group and cannot be edited."));
} else {
@@ -20,7 +20,8 @@
cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) {
return {
filters: {
- 'is_group': 1
+ 'is_group': 1,
+ 'name': ['!=', cur_frm.doc.customer_group_name]
}
}
}
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index 7c0be3b..b30bd78 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -405,8 +405,8 @@
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
count(*) from `tabSales Order`
- where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed"
- and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
+ where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed" and company = %(company)s
+ and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_bill"),
report_type="Report Builder",
@@ -430,8 +430,8 @@
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0),
count(*) from `tabSales Order`
- where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered"
- and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
+ where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered" and company = %(company)s
+ and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_deliver"),
report_type="Report Builder",
@@ -455,8 +455,8 @@
value, count = frappe.db.sql("""select ifnull((sum(grand_total))-(sum(grand_total*per_received/100)),0),
count(*) from `tabPurchase Order`
- where (transaction_date <= %(to_date)s) and per_received < 100
- and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
+ where (transaction_date <= %(to_date)s) and per_received < 100 and company = %(company)s
+ and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_receive"),
report_type="Report Builder",
@@ -480,8 +480,8 @@
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
count(*) from `tabPurchase Order`
- where (transaction_date <= %(to_date)s) and per_billed < 100
- and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
+ where (transaction_date <= %(to_date)s) and per_billed < 100 and company = %(company)s
+ and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0]
label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_bill"),
report_type="Report Builder",
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index df22231..9892dc3 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -66,7 +66,7 @@
set_root_readonly: function(frm) {
// read-only for root item group
frm.set_intro("");
- if(!frm.doc.parent_item_group) {
+ if(!frm.doc.parent_item_group && !frm.doc.__islocal) {
frm.set_read_only();
frm.set_intro(__("This is a root item group and cannot be edited."), true);
}
diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js
index 89ca4a9..8f7593d 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.js
+++ b/erpnext/setup/doctype/sales_person/sales_person.js
@@ -19,7 +19,7 @@
}
}
};
-
+
frm.make_methods = {
'Sales Order': () => frappe.new_doc("Sales Order")
.then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name}))
@@ -33,7 +33,7 @@
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root
- if(!doc.parent_sales_person) {
+ if(!doc.parent_sales_person && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root sales person and cannot be edited."));
} else {
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js
index ac5bda6..e75030d 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.js
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.js
@@ -8,7 +8,7 @@
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root customer group
- if(!doc.parent_supplier_group) {
+ if(!doc.parent_supplier_group && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root supplier group and cannot be edited."));
} else {
@@ -20,7 +20,8 @@
cur_frm.fields_dict['parent_supplier_group'].get_query = function() {
return {
filters: {
- 'is_group': 1
+ 'is_group': 1,
+ 'name': ['!=', cur_frm.doc.supplier_group_name]
}
};
};
diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js
index 1eb9958..ceec47ae 100644
--- a/erpnext/setup/doctype/territory/territory.js
+++ b/erpnext/setup/doctype/territory/territory.js
@@ -20,7 +20,7 @@
cur_frm.cscript.set_root_readonly = function(doc) {
// read-only for root territory
- if(!doc.parent_territory) {
+ if(!doc.parent_territory && !doc.__islocal) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root territory and cannot be edited."));
} else {
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js
index e1510f5..ffc5dab 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js
@@ -13,6 +13,12 @@
},
enable_checkout: function(){
toggle_mandatory(cur_frm)
+ },
+ enabled: function() {
+ if (cur_frm.doc.enabled === 1) {
+ cur_frm.doc.show_configure_button = 1;
+ cur_frm.refresh_field('show_configure_button');
+ }
}
});
diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py
index 7c08f5b..29617a8 100644
--- a/erpnext/shopping_cart/product_info.py
+++ b/erpnext/shopping_cart/product_info.py
@@ -55,7 +55,7 @@
def set_product_info_for_website(item):
"""set product price uom for website"""
- product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True)
+ product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get("product_info")
if product_info:
item.update(product_info)
diff --git a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json
index 07992b8..7eeb147 100644
--- a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json
+++ b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json
@@ -71,7 +71,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 1,
+ "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
@@ -88,7 +88,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-09-12 09:30:03.951743",
+ "modified": "2020-06-26 09:30:03.951743",
"modified_by": "Administrator",
"module": "Stock",
"name": "Customs Tariff Number",
@@ -143,4 +143,4 @@
"track_changes": 1,
"track_seen": 0,
"track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 3436a5d..a75ee67 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -597,7 +597,7 @@
def stock_ledger_created(self):
if not hasattr(self, '_stock_ledger_created'):
self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry`
- where item_code = %s limit 1""", self.name))
+ where item_code = %s and is_cancelled = 0 limit 1""", self.name))
return self._stock_ledger_created
def validate_name_with_item_group(self):
@@ -883,7 +883,12 @@
linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"]
for doctype in linked_doctypes:
- if frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \
+ if doctype in ("Purchase Invoice Item", "Sales Invoice Item",):
+ # If Invoice has Stock impact, only then consider it.
+ if self.stock_ledger_created():
+ return True
+
+ elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \
frappe.db.get_value("Production Order",
filters={"production_item": self.name, "docstatus": 1}):
return True
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index e6ab8d6..d0ba001 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -211,6 +211,7 @@
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
landed_cost_entries = get_item_account_wise_additional_cost(self.name)
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
+ auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items'))
gl_entries = []
warehouse_with_no_account = []
@@ -301,6 +302,32 @@
elif d.warehouse not in warehouse_with_no_account or \
d.rejected_warehouse not in warehouse_with_no_account:
warehouse_with_no_account.append(d.warehouse)
+ elif d.item_code not in stock_items and flt(d.qty) and auto_accounting_for_non_stock_items:
+
+ service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed")
+ credit_currency = get_account_currency(service_received_but_not_billed_account)
+
+ gl_entries.append(self.get_gl_dict({
+ "account": service_received_but_not_billed_account,
+ "against": d.expense_account,
+ "cost_center": d.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Service"),
+ "project": d.project,
+ "credit": d.amount,
+ "voucher_detail_no": d.name
+ }, credit_currency, item=d))
+
+ debit_currency = get_account_currency(d.expense_account)
+
+ gl_entries.append(self.get_gl_dict({
+ "account": d.expense_account,
+ "against": service_received_but_not_billed_account,
+ "cost_center": d.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Service"),
+ "project": d.project,
+ "debit": d.amount,
+ "voucher_detail_no": d.name
+ }, debit_currency, item=d))
self.get_asset_gl_entry(gl_entries)
# Cost center-wise amount breakup for other charges included for valuation
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index d9f8b62..2be14c8 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -1,7 +1,6 @@
{
"actions": [],
"allow_import": 1,
- "allow_rename": 1,
"autoname": "field:serial_no",
"creation": "2013-05-16 10:59:15",
"description": "Distinct unit of an Item",
@@ -427,7 +426,7 @@
"icon": "fa fa-barcode",
"idx": 1,
"links": [],
- "modified": "2020-05-21 19:29:58.517772",
+ "modified": "2020-06-25 15:53:50.900855",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial No",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 5fbd512..229cf02 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -574,9 +574,7 @@
{"parent": self.purchase_order, "item_code": se_item.subcontracted_item},
"bom")
- allow_alternative_item = frappe.get_value("BOM", bom_no, "allow_alternative_item")
-
- if allow_alternative_item:
+ if se_item.allow_alternative_item:
original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code")
required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
@@ -743,7 +741,7 @@
def get_item_details(self, args=None, for_update=False):
item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
- i.has_batch_no, i.sample_quantity, i.has_serial_no,
+ i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item,
id.expense_account, id.buying_cost_center
from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s
where i.name=%s
@@ -778,6 +776,9 @@
'expense_account' : item.expense_account
})
+ if self.purpose == 'Send to Subcontractor':
+ ret["allow_alternative_item"] = item.allow_alternative_item
+
# update uom
if args.get("uom") and for_update:
ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty')))
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index c9a3527..9c5d3d8 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -216,7 +216,7 @@
"idx": 1,
"issingle": 1,
"links": [],
- "modified": "2020-04-01 18:11:25.417678",
+ "modified": "2020-06-20 11:39:15.344112",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 723ed5c..d5878cb 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -187,7 +187,7 @@
transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0))
else:
# all from current batch
- batch[0] -= qty_to_pop
+ batch[0] = flt(batch[0]) - qty_to_pop
transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]])
qty_to_pop = 0
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index e7e5bd3..9e15757 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -79,7 +79,7 @@
method: "erpnext.support.doctype.issue.issue.make_task",
frm: frm
});
- }, __("Make"));
+ }, __("Create"));
} else {
if (frm.doc.service_level_agreement) {
@@ -232,4 +232,4 @@
} else {
return {"diff_display": "Failed", "indicator": "red"};
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 883e603..87168e1 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -79,47 +79,52 @@
def handle_hold_time(self, status):
if self.service_level_agreement:
- # set response and resolution variance as None as the issue is on Hold for status as Replied
+ # set response and resolution variance as None as the issue is on Hold
pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"],
filters={"parent": self.service_level_agreement})
hold_statuses = [entry.status for entry in pause_sla_on]
update_values = {}
- if self.status in hold_statuses and status not in hold_statuses:
- update_values['on_hold_since'] = frappe.flags.current_time or now_datetime()
- if not self.first_responded_on:
- update_values['response_by'] = None
- update_values['response_by_variance'] = 0
- update_values['resolution_by'] = None
- update_values['resolution_by_variance'] = 0
+ if hold_statuses:
+ if self.status in hold_statuses and status not in hold_statuses:
+ update_values['on_hold_since'] = frappe.flags.current_time or now_datetime()
+ if not self.first_responded_on:
+ update_values['response_by'] = None
+ update_values['response_by_variance'] = 0
+ update_values['resolution_by'] = None
+ update_values['resolution_by_variance'] = 0
- # calculate hold time when status is changed from Replied to any other status
- if self.status not in hold_statuses and status in hold_statuses:
- hold_time = self.total_hold_time if self.total_hold_time else 0
- now_time = frappe.flags.current_time or now_datetime()
- update_values['total_hold_time'] = hold_time + time_diff_in_seconds(now_time, self.on_hold_since)
+ # calculate hold time when status is changed from any hold status to any non-hold status
+ if self.status not in hold_statuses and status in hold_statuses:
+ hold_time = self.total_hold_time if self.total_hold_time else 0
+ now_time = frappe.flags.current_time or now_datetime()
+ last_hold_time = 0
+ if self.on_hold_since:
+ # last_hold_time will be added to the sla variables
+ last_hold_time = time_diff_in_seconds(now_time, self.on_hold_since)
+ update_values['total_hold_time'] = hold_time + last_hold_time
- # re-calculate SLA variables after issue changes from Replied to Open
- # add hold time to SLA variables
- if self.status == "Open" and status in hold_statuses:
- start_date_time = get_datetime(self.service_level_agreement_creation)
- priority = get_priority(self)
- now_time = frappe.flags.current_time or now_datetime()
- hold_time = time_diff_in_seconds(now_time, self.on_hold_since)
+ # re-calculate SLA variables after issue changes from any hold status to any non-hold status
+ # add hold time to SLA variables
+ start_date_time = get_datetime(self.service_level_agreement_creation)
+ priority = get_priority(self)
+ now_time = frappe.flags.current_time or now_datetime()
- if not self.first_responded_on:
- response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
- update_values['response_by'] = add_to_date(response_by, seconds=round(hold_time))
- response_by_variance = round(time_diff_in_hours(self.response_by, now_time))
- update_values['response_by_variance'] = response_by_variance + (hold_time // 3600)
+ if not self.first_responded_on:
+ response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
+ response_by = add_to_date(response_by, seconds=round(last_hold_time))
+ response_by_variance = round(time_diff_in_hours(response_by, now_time))
+ update_values['response_by'] = response_by
+ update_values['response_by_variance'] = response_by_variance + (last_hold_time // 3600)
- resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
- update_values['resolution_by'] = add_to_date(resolution_by, seconds=round(hold_time))
- resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time))
- update_values['resolution_by_variance'] = resolution_by_variance + (hold_time // 3600)
- update_values['on_hold_since'] = None
+ resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
+ resolution_by = add_to_date(resolution_by, seconds=round(last_hold_time))
+ resolution_by_variance = round(time_diff_in_hours(resolution_by, now_time))
+ update_values['resolution_by'] = resolution_by
+ update_values['resolution_by_variance'] = resolution_by_variance + (last_hold_time // 3600)
+ update_values['on_hold_since'] = None
- self.db_set(update_values)
+ self.db_set(update_values)
def update_agreement_status(self):
if self.service_level_agreement and self.agreement_fulfilled == "Ongoing":
diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html
index 4274ba1..82b2716 100644
--- a/erpnext/templates/includes/footer/footer_powered.html
+++ b/erpnext/templates/includes/footer/footer_powered.html
@@ -12,8 +12,9 @@
} %}
{% set link = '' %}
-{% set label = domains[0].domain %}
+{% set label = '' %}
{% if domains %}
+ {% set label = domains[0].domain %}
{% set link = links[label] %}
{% endif %}