Merge branch 'develop' into grn_posting
This commit is contained in:
commit
d63c59ced4
@ -2,10 +2,11 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
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 erpnext.accounts.utils import get_account_currency
|
||||||
from frappe.email import sendmail_to_system_managers
|
from frappe.email import sendmail_to_system_managers
|
||||||
from frappe.utils.background_jobs import enqueue
|
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):
|
def validate_service_stop_date(doc):
|
||||||
''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
|
''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
|
||||||
@ -109,6 +110,18 @@ def get_booking_dates(doc, item, posting_date=None):
|
|||||||
order by posting_date desc limit 1
|
order by posting_date desc limit 1
|
||||||
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
|
''', (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:
|
if prev_gl_entry:
|
||||||
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
|
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
|
||||||
else:
|
else:
|
||||||
@ -130,14 +143,48 @@ def get_booking_dates(doc, item, posting_date=None):
|
|||||||
else:
|
else:
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
|
def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
|
||||||
if doc.doctype == "Sales Invoice":
|
amount, base_amount = 0, 0
|
||||||
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"
|
|
||||||
|
|
||||||
|
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
|
amount, base_amount = 0, 0
|
||||||
if not last_gl_entry:
|
if not last_gl_entry:
|
||||||
base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
|
base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
|
||||||
@ -146,27 +193,55 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
|
|||||||
else:
|
else:
|
||||||
amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
|
amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
|
||||||
else:
|
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 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('''
|
gl_entries_details = frappe.db.sql('''
|
||||||
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
|
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
|
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
|
group by voucher_detail_no
|
||||||
'''.format(total_credit_debit, total_credit_debit_currency),
|
'''.format(total_credit_debit, total_credit_debit_currency),
|
||||||
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
|
(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 = gl_entries_details[0].total_credit if gl_entries_details else 0
|
||||||
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
|
already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0
|
||||||
if account_currency==doc.company_currency:
|
|
||||||
amount = base_amount
|
if doc.currency == doc.company_currency:
|
||||||
|
already_booked_amount_in_account_currency = already_booked_amount
|
||||||
else:
|
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 = 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"))
|
already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
|
||||||
|
|
||||||
return amount, base_amount
|
return already_booked_amount, already_booked_amount_in_account_currency
|
||||||
|
|
||||||
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||||
enable_check = "enable_deferred_revenue" \
|
enable_check = "enable_deferred_revenue" \
|
||||||
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
|
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)
|
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
|
||||||
if not (start_date and end_date): return
|
if not (start_date and end_date): return
|
||||||
|
|
||||||
@ -181,9 +256,17 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
|
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
|
||||||
total_booking_days = date_diff(end_date, start_date) + 1
|
total_booking_days = date_diff(end_date, start_date) + 1
|
||||||
|
|
||||||
|
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,
|
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
|
||||||
total_days, total_booking_days, account_currency)
|
total_days, total_booking_days, account_currency)
|
||||||
|
|
||||||
|
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,
|
make_gl_entries(doc, credit_account, debit_account, against,
|
||||||
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
|
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
|
||||||
|
|
||||||
@ -192,12 +275,15 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
|
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'):
|
for item in doc.get('items'):
|
||||||
if item.get(enable_check):
|
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):
|
def process_deferred_accounting(posting_date=None):
|
||||||
''' Converts deferred income/expense into income/expense
|
''' Converts deferred income/expense into income/expense
|
||||||
@ -281,3 +367,83 @@ def send_mail(deferred_process):
|
|||||||
and submit manually after resolving errors
|
and submit manually after resolving errors
|
||||||
""").format(get_link_to_form('Process Deferred Accounting', deferred_process))
|
""").format(get_link_to_form('Process Deferred Accounting', deferred_process))
|
||||||
sendmail_to_system_managers(title, content)
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,12 @@
|
|||||||
"allow_cost_center_in_entry_of_bs_account",
|
"allow_cost_center_in_entry_of_bs_account",
|
||||||
"add_taxes_from_item_tax_template",
|
"add_taxes_from_item_tax_template",
|
||||||
"automatically_fetch_payment_terms",
|
"automatically_fetch_payment_terms",
|
||||||
|
"deferred_accounting_settings_section",
|
||||||
"automatically_process_deferred_accounting_entry",
|
"automatically_process_deferred_accounting_entry",
|
||||||
|
"book_deferred_entries_based_on",
|
||||||
|
"column_break_18",
|
||||||
|
"book_deferred_entries_via_journal_entry",
|
||||||
|
"submit_journal_entries",
|
||||||
"print_settings",
|
"print_settings",
|
||||||
"show_inclusive_tax_in_print",
|
"show_inclusive_tax_in_print",
|
||||||
"column_break_12",
|
"column_break_12",
|
||||||
@ -189,13 +194,45 @@
|
|||||||
"fieldname": "automatically_process_deferred_accounting_entry",
|
"fieldname": "automatically_process_deferred_accounting_entry",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Automatically Process Deferred Accounting Entry"
|
"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",
|
"icon": "icon-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-19 16:58:17.395595",
|
"modified": "2020-06-22 20:13:26.043092",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
@ -71,8 +71,13 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
"label": "Cost Center Number",
|
"label": "Cost Center Number",
|
||||||
"fieldname": "cost_center_number",
|
"fieldname": "cost_center_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1,
|
|
||||||
"default": frm.doc.cost_center_number
|
"default": frm.doc.cost_center_number
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": __("Merge with existing"),
|
||||||
|
"fieldname": "merge",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"default": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
primary_action: function() {
|
primary_action: function() {
|
||||||
@ -87,8 +92,9 @@ frappe.ui.form.on('Cost Center', {
|
|||||||
args: {
|
args: {
|
||||||
docname: frm.doc.name,
|
docname: frm.doc.name,
|
||||||
cost_center_name: data.cost_center_name,
|
cost_center_name: data.cost_center_name,
|
||||||
cost_center_number: data.cost_center_number,
|
cost_center_number: cstr(data.cost_center_number),
|
||||||
company: frm.doc.company
|
company: frm.doc.company,
|
||||||
|
merge: data.merge
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
frappe.dom.unfreeze();
|
frappe.dom.unfreeze();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt, fmt_money, getdate, formatdate
|
from frappe.utils import flt, fmt_money, getdate, formatdate, cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.naming import set_name_from_naming_options
|
from frappe.model.naming import set_name_from_naming_options
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
@ -134,10 +134,17 @@ class GLEntry(Document):
|
|||||||
|
|
||||||
return self.cost_center_company[self.cost_center]
|
return self.cost_center_company[self.cost_center]
|
||||||
|
|
||||||
|
def _check_is_group():
|
||||||
|
return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group'))
|
||||||
|
|
||||||
if self.cost_center and _get_cost_center_company() != self.company:
|
if self.cost_center and _get_cost_center_company() != self.company:
|
||||||
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
|
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
|
||||||
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
|
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
|
||||||
|
|
||||||
|
if self.cost_center and _check_is_group():
|
||||||
|
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot
|
||||||
|
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
|
||||||
|
|
||||||
def validate_party(self):
|
def validate_party(self):
|
||||||
validate_party_frozen_disabled(self.party_type, self.party)
|
validate_party_frozen_disabled(self.party_type, self.party)
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
"label": "Entry Type",
|
"label": "Entry Type",
|
||||||
"oldfieldname": "voucher_type",
|
"oldfieldname": "voucher_type",
|
||||||
"oldfieldtype": "Select",
|
"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,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_balance_on, get_account_currency
|
|||||||
from erpnext.accounts.party import get_party_account
|
from erpnext.accounts.party import get_party_account
|
||||||
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
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.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
|
from six import string_types, iteritems
|
||||||
|
|
||||||
@ -265,7 +266,10 @@ class JournalEntry(AccountsController):
|
|||||||
# set totals
|
# set totals
|
||||||
if not d.reference_name in self.reference_totals:
|
if not d.reference_name in self.reference_totals:
|
||||||
self.reference_totals[d.reference_name] = 0.0
|
self.reference_totals[d.reference_name] = 0.0
|
||||||
|
|
||||||
|
if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'):
|
||||||
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
|
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
|
||||||
|
|
||||||
self.reference_types[d.reference_name] = d.reference_type
|
self.reference_types[d.reference_name] = d.reference_type
|
||||||
self.reference_accounts[d.reference_name] = d.account
|
self.reference_accounts[d.reference_name] = d.account
|
||||||
|
|
||||||
@ -277,10 +281,16 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
# check if party and account match
|
# check if party and account match
|
||||||
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||||
|
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:
|
||||||
if d.reference_type == "Sales Invoice":
|
if d.reference_type == "Sales Invoice":
|
||||||
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
|
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
|
||||||
else:
|
else:
|
||||||
party_account = against_voucher[1]
|
party_account = against_voucher[1]
|
||||||
|
|
||||||
if (against_voucher[0] != d.party or party_account != d.account):
|
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}")
|
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],
|
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
|
||||||
@ -513,14 +523,20 @@ class JournalEntry(AccountsController):
|
|||||||
"against_voucher_type": d.reference_type,
|
"against_voucher_type": d.reference_type,
|
||||||
"against_voucher": d.reference_name,
|
"against_voucher": d.reference_name,
|
||||||
"remarks": remarks,
|
"remarks": remarks,
|
||||||
|
"voucher_detail_no": d.reference_detail_no,
|
||||||
"cost_center": d.cost_center,
|
"cost_center": d.cost_center,
|
||||||
"project": d.project,
|
"project": d.project,
|
||||||
"finance_book": self.finance_book
|
"finance_book": self.finance_book
|
||||||
}, item=d)
|
}, item=d)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
|
||||||
|
update_outstanding = 'No'
|
||||||
|
else:
|
||||||
|
update_outstanding = 'Yes'
|
||||||
|
|
||||||
if gl_map:
|
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):
|
def get_balance(self):
|
||||||
if not self.get('accounts'):
|
if not self.get('accounts'):
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"reference_type",
|
"reference_type",
|
||||||
"reference_name",
|
"reference_name",
|
||||||
"reference_due_date",
|
"reference_due_date",
|
||||||
|
"reference_detail_no",
|
||||||
"col_break3",
|
"col_break3",
|
||||||
"is_advance",
|
"is_advance",
|
||||||
"user_remark",
|
"user_remark",
|
||||||
@ -268,6 +269,12 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Bank Account",
|
"label": "Bank Account",
|
||||||
"options": "Bank Account"
|
"options": "Bank Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reference_detail_no",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Reference Detail No"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
|
@ -17,6 +17,8 @@ from six import string_types
|
|||||||
apply_on_dict = {"Item Code": "items",
|
apply_on_dict = {"Item Code": "items",
|
||||||
"Item Group": "item_groups", "Brand": "brands"}
|
"Item Group": "item_groups", "Brand": "brands"}
|
||||||
|
|
||||||
|
other_fields = ["other_item_code", "other_item_group", "other_brand"]
|
||||||
|
|
||||||
class PricingRule(Document):
|
class PricingRule(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
@ -47,6 +49,13 @@ class PricingRule(Document):
|
|||||||
if tocheck and not self.get(tocheck):
|
if tocheck and not self.get(tocheck):
|
||||||
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
||||||
|
|
||||||
|
if self.apply_rule_on_other:
|
||||||
|
o_field = 'other_' + frappe.scrub(self.apply_rule_on_other)
|
||||||
|
if not self.get(o_field) and o_field in other_fields:
|
||||||
|
frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory")
|
||||||
|
.format(frappe.bold(self.apply_rule_on_other)))
|
||||||
|
|
||||||
|
|
||||||
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
||||||
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
||||||
|
|
||||||
@ -80,13 +89,27 @@ class PricingRule(Document):
|
|||||||
for f in options:
|
for f in options:
|
||||||
if not f: continue
|
if not f: continue
|
||||||
|
|
||||||
f = frappe.scrub(f)
|
scrubbed_f = frappe.scrub(f)
|
||||||
if f!=fieldname:
|
|
||||||
self.set(f, None)
|
if logic_field == 'apply_on':
|
||||||
|
apply_on_f = apply_on_dict.get(f, f)
|
||||||
|
else:
|
||||||
|
apply_on_f = scrubbed_f
|
||||||
|
|
||||||
|
if scrubbed_f != fieldname:
|
||||||
|
self.set(apply_on_f, None)
|
||||||
|
|
||||||
if self.mixed_conditions and self.get("same_item"):
|
if self.mixed_conditions and self.get("same_item"):
|
||||||
self.same_item = 0
|
self.same_item = 0
|
||||||
|
|
||||||
|
apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "")
|
||||||
|
|
||||||
|
cleanup_other_fields = (other_fields if not apply_rule_on_other
|
||||||
|
else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other])
|
||||||
|
|
||||||
|
for other_field in cleanup_other_fields:
|
||||||
|
self.set(other_field, None)
|
||||||
|
|
||||||
def validate_rate_or_discount(self):
|
def validate_rate_or_discount(self):
|
||||||
for field in ["Rate"]:
|
for field in ["Rate"]:
|
||||||
if flt(self.get(frappe.scrub(field))) < 0:
|
if flt(self.get(frappe.scrub(field))) < 0:
|
||||||
|
@ -10,6 +10,18 @@ frappe.ui.form.on('Process Deferred Accounting', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (frm.doc.company) {
|
||||||
|
frm.set_query("account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'company': frm.doc.company,
|
||||||
|
'root_type': 'Liability',
|
||||||
|
'is_group': 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
validate: function() {
|
validate: function() {
|
||||||
|
@ -238,6 +238,12 @@ class PurchaseInvoice(BuyingController):
|
|||||||
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
|
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
|
||||||
|
|
||||||
if self.update_stock and (not item.from_warehouse):
|
if self.update_stock and (not item.from_warehouse):
|
||||||
|
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
|
||||||
|
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2}
|
||||||
|
is not linked to warehouse {3} or it is not the default inventory account'''.format(
|
||||||
|
item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]),
|
||||||
|
frappe.bold(item.expense_account), frappe.bold(item.warehouse))))
|
||||||
|
|
||||||
item.expense_account = warehouse_account[item.warehouse]["account"]
|
item.expense_account = warehouse_account[item.warehouse]["account"]
|
||||||
else:
|
else:
|
||||||
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
|
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
|
||||||
@ -247,10 +253,21 @@ class PurchaseInvoice(BuyingController):
|
|||||||
(item.purchase_receipt, stock_not_billed_account))
|
(item.purchase_receipt, stock_not_billed_account))
|
||||||
|
|
||||||
if negative_expense_booked_in_pr:
|
if negative_expense_booked_in_pr:
|
||||||
|
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
|
||||||
|
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because
|
||||||
|
expense is booked against this account in Purchase Receipt {2}'''.format(
|
||||||
|
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))))
|
||||||
|
|
||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
else:
|
else:
|
||||||
# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
|
# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
|
||||||
# This is done in cases when Purchase Invoice is created before Purchase Receipt
|
# This is done in cases when Purchase Invoice is created before Purchase Receipt
|
||||||
|
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
|
||||||
|
frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase
|
||||||
|
Receipt is created against Item {2}. This is done to handle accounting for cases
|
||||||
|
when Purchase Receipt is created after Purchase Invoice'''.format(
|
||||||
|
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))))
|
||||||
|
|
||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
|
|
||||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
||||||
|
@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = {
|
|||||||
} else if(frappe.datetime.get_diff(doc.due_date) < 0) {
|
} else if(frappe.datetime.get_diff(doc.due_date) < 0) {
|
||||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
|
||||||
} else {
|
} else {
|
||||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"];
|
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
|
||||||
}
|
}
|
||||||
} else if(cint(doc.is_return)) {
|
} else if(cint(doc.is_return)) {
|
||||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||||
|
@ -7,14 +7,15 @@ import unittest
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
import frappe.model
|
import frappe.model
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
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
|
import frappe.defaults
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
||||||
test_records as pr_test_records, make_purchase_receipt, get_taxes
|
test_records as pr_test_records, make_purchase_receipt, get_taxes
|
||||||
from erpnext.controllers.accounts_controller import get_payment_terms
|
from erpnext.controllers.accounts_controller import get_payment_terms
|
||||||
from erpnext.exceptions import InvalidCurrency
|
from erpnext.exceptions import InvalidCurrency
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
|
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_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
|
||||||
test_ignore = ["Serial No"]
|
test_ignore = ["Serial No"]
|
||||||
@ -866,6 +867,67 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
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):
|
def unlink_payment_on_cancel_of_invoice(enable=1):
|
||||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||||
|
@ -1720,8 +1720,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.save()
|
si.save()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
|
|
||||||
|
|
||||||
pda1 = frappe.get_doc(dict(
|
pda1 = frappe.get_doc(dict(
|
||||||
doctype='Process Deferred Accounting',
|
doctype='Process Deferred Accounting',
|
||||||
posting_date=nowdate(),
|
posting_date=nowdate(),
|
||||||
@ -1745,6 +1743,55 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
|
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):
|
def test_inter_company_transaction(self):
|
||||||
|
|
||||||
if not frappe.db.exists("Customer", "_Test Internal Customer"):
|
if not frappe.db.exists("Customer", "_Test Internal Customer"):
|
||||||
|
@ -61,7 +61,7 @@ def make_sales_invoice():
|
|||||||
debit_to = 'Debtors - _TC2',
|
debit_to = 'Debtors - _TC2',
|
||||||
income_account = 'Sales - _TC2',
|
income_account = 'Sales - _TC2',
|
||||||
expense_account = 'Cost of Goods Sold - _TC2',
|
expense_account = 'Cost of Goods Sold - _TC2',
|
||||||
cost_center = '_Test Company 2 - _TC2')
|
cost_center = 'Main - _TC2')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ def make_sales_invoice():
|
|||||||
debit_to = 'Debtors - _TC2',
|
debit_to = 'Debtors - _TC2',
|
||||||
income_account = 'Sales - _TC2',
|
income_account = 'Sales - _TC2',
|
||||||
expense_account = 'Cost of Goods Sold - _TC2',
|
expense_account = 'Cost of Goods Sold - _TC2',
|
||||||
cost_center = '_Test Company 2 - _TC2',
|
cost_center = 'Main - _TC2',
|
||||||
do_not_save=1)
|
do_not_save=1)
|
||||||
|
|
||||||
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
|
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
|
||||||
@ -90,7 +90,7 @@ def make_credit_note(docname):
|
|||||||
debit_to = 'Debtors - _TC2',
|
debit_to = 'Debtors - _TC2',
|
||||||
income_account = 'Sales - _TC2',
|
income_account = 'Sales - _TC2',
|
||||||
expense_account = 'Cost of Goods Sold - _TC2',
|
expense_account = 'Cost of Goods Sold - _TC2',
|
||||||
cost_center = '_Test Company 2 - _TC2',
|
cost_center = 'Main - _TC2',
|
||||||
is_return = 1,
|
is_return = 1,
|
||||||
return_against = docname)
|
return_against = docname)
|
||||||
|
|
||||||
|
@ -837,7 +837,7 @@ def create_payment_gateway_account(gateway):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_cost_center(docname, cost_center_name, cost_center_number, company):
|
def update_cost_center(docname, cost_center_name, cost_center_number, company, merge):
|
||||||
'''
|
'''
|
||||||
Renames the document by adding the number as a prefix to the current name and updates
|
Renames the document by adding the number as a prefix to the current name and updates
|
||||||
all transaction where it was present.
|
all transaction where it was present.
|
||||||
@ -853,7 +853,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company):
|
|||||||
|
|
||||||
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
|
new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
|
||||||
if docname != new_name:
|
if docname != new_name:
|
||||||
frappe.rename_doc("Cost Center", docname, new_name, force=1)
|
frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge)
|
||||||
return new_name
|
return new_name
|
||||||
|
|
||||||
def validate_field_number(doctype_name, docname, number_value, company, field_name):
|
def validate_field_number(doctype_name, docname, number_value, company, field_name):
|
||||||
|
@ -20,6 +20,7 @@ from erpnext.exceptions import InvalidCurrency
|
|||||||
from six import text_type
|
from six import text_type
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
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.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")
|
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
||||||
|
|
||||||
@ -1301,6 +1302,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
parent.set_qty_as_per_stock_uom()
|
parent.set_qty_as_per_stock_uom()
|
||||||
parent.calculate_taxes_and_totals()
|
parent.calculate_taxes_and_totals()
|
||||||
if parent_doctype == "Sales Order":
|
if parent_doctype == "Sales Order":
|
||||||
|
make_packing_list(parent)
|
||||||
parent.set_gross_profit()
|
parent.set_gross_profit()
|
||||||
frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype,
|
frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype,
|
||||||
parent.company, parent.base_grand_total)
|
parent.company, parent.base_grand_total)
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
"requires_fulfilment",
|
"requires_fulfilment",
|
||||||
"fulfilment_deadline",
|
"fulfilment_deadline",
|
||||||
"fulfilment_terms",
|
"fulfilment_terms",
|
||||||
|
"authorised_by_section",
|
||||||
|
"signee_company",
|
||||||
|
"signed_by_company",
|
||||||
"sb_references",
|
"sb_references",
|
||||||
"document_type",
|
"document_type",
|
||||||
"cb_links",
|
"cb_links",
|
||||||
@ -223,10 +226,28 @@
|
|||||||
"options": "Contract",
|
"options": "Contract",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 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,
|
"is_submittable": 1,
|
||||||
"modified": "2019-09-30 00:56:41.559681",
|
"links": [],
|
||||||
|
"modified": "2020-03-30 06:56:07.257932",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Contract",
|
"name": "Contract",
|
||||||
|
@ -29,6 +29,9 @@ class Contract(Document):
|
|||||||
self.update_contract_status()
|
self.update_contract_status()
|
||||||
self.update_fulfilment_status()
|
self.update_fulfilment_status()
|
||||||
|
|
||||||
|
def before_submit(self):
|
||||||
|
self.signed_by_company = frappe.session.user
|
||||||
|
|
||||||
def before_update_after_submit(self):
|
def before_update_after_submit(self):
|
||||||
self.update_contract_status()
|
self.update_contract_status()
|
||||||
self.update_fulfilment_status()
|
self.update_fulfilment_status()
|
||||||
|
@ -1,306 +1,106 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"allow_rename": 1,
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "field:title",
|
"autoname": "field:title",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-04-16 06:44:48.791312",
|
"creation": "2018-04-16 06:44:48.791312",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title",
|
||||||
|
"contract_terms",
|
||||||
|
"sb_fulfilment",
|
||||||
|
"requires_fulfilment",
|
||||||
|
"fulfilment_terms"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "title",
|
"fieldname": "title",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
"label": "Title",
|
||||||
"length": 0,
|
"unique": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "contract_terms",
|
"fieldname": "contract_terms",
|
||||||
"fieldtype": "Text Editor",
|
"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",
|
"label": "Contract Terms and Conditions",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sb_fulfilment",
|
"fieldname": "sb_fulfilment",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "0",
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "requires_fulfilment",
|
"fieldname": "requires_fulfilment",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Requires Fulfilment"
|
||||||
"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",
|
"depends_on": "eval:doc.requires_fulfilment==1",
|
||||||
"fieldname": "fulfilment_terms",
|
"fieldname": "fulfilment_terms",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Fulfilment Terms and Conditions",
|
"label": "Fulfilment Terms and Conditions",
|
||||||
"length": 0,
|
"options": "Contract Template Fulfilment Terms"
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2020-06-03 00:24:58.179816",
|
||||||
"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",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Contract Template",
|
"name": "Contract Template",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales Manager",
|
"role": "Sales Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Purchase Manager",
|
"role": "Purchase Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "HR Manager",
|
"role": "HR Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -96,6 +96,7 @@ frappe.ui.form.on("Opportunity", {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
frm.add_custom_button(__("Reopen"), function() {
|
frm.add_custom_button(__("Reopen"), function() {
|
||||||
|
frm.set_value("lost_reasons",[])
|
||||||
frm.set_value("status", "Open");
|
frm.set_value("status", "Open");
|
||||||
frm.save();
|
frm.save();
|
||||||
});
|
});
|
||||||
|
@ -22,6 +22,7 @@ class EmployeeAdvance(Document):
|
|||||||
self.validate_employee_advance_account()
|
self.validate_employee_advance_account()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
|
self.ignore_linked_doctypes = ('GL Entry')
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
|
@ -20,10 +20,9 @@ class JobOffer(Document):
|
|||||||
staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date)
|
staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date)
|
||||||
check_vacancies = frappe.get_single("HR Settings").check_vacancies
|
check_vacancies = frappe.get_single("HR Settings").check_vacancies
|
||||||
if staffing_plan and 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 = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)
|
||||||
job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date))
|
if staffing_plan.vacancies - len(job_offers) <= 0:
|
||||||
if vacancies - 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))))
|
||||||
frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent)))
|
|
||||||
|
|
||||||
def on_change(self):
|
def on_change(self):
|
||||||
update_job_applicant(self.status, self.job_applicant)
|
update_job_applicant(self.status, self.job_applicant)
|
||||||
@ -42,18 +41,22 @@ def update_job_applicant(status, job_applicant):
|
|||||||
|
|
||||||
def get_staffing_plan_detail(designation, company, offer_date):
|
def get_staffing_plan_detail(designation, company, offer_date):
|
||||||
detail = frappe.db.sql("""
|
detail = frappe.db.sql("""
|
||||||
SELECT spd.name as name,
|
SELECT DISTINCT spd.parent,
|
||||||
sp.from_date as from_date,
|
sp.from_date as from_date,
|
||||||
sp.to_date as to_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
|
FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp
|
||||||
WHERE
|
WHERE
|
||||||
sp.docstatus=1
|
sp.docstatus=1
|
||||||
AND spd.designation=%s
|
AND spd.designation=%s
|
||||||
AND sp.company=%s
|
AND sp.company=%s
|
||||||
|
AND spd.parent = sp.name
|
||||||
AND %s between sp.from_date and sp.to_date
|
AND %s between sp.from_date and sp.to_date
|
||||||
""", (designation, company, offer_date), as_dict=1)
|
""", (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()
|
@frappe.whitelist()
|
||||||
def make_employee(source_name, target_doc=None):
|
def make_employee(source_name, target_doc=None):
|
||||||
|
@ -699,9 +699,10 @@ erpnext.patches.v13_0.delete_old_purchase_reports
|
|||||||
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
|
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
|
||||||
erpnext.patches.v13_0.update_sla_enhancements
|
erpnext.patches.v13_0.update_sla_enhancements
|
||||||
erpnext.patches.v12_0.update_address_template_for_india
|
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.v12_0.set_multi_uom_in_rfq
|
||||||
erpnext.patches.v13_0.delete_old_sales_reports
|
erpnext.patches.v13_0.delete_old_sales_reports
|
||||||
execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")
|
execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation")
|
||||||
erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll
|
erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020
|
||||||
erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings
|
erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020
|
||||||
erpnext.patches.v13_0.check_is_income_tax_component
|
erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020
|
||||||
|
@ -4,9 +4,28 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
|
from erpnext.regional.india.setup import setup
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doc('Payroll', 'doctype', 'salary_structure')
|
|
||||||
|
doctypes = ['salary_component',
|
||||||
|
'Employee Tax Exemption Declaration',
|
||||||
|
'Employee Tax Exemption Proof Submission',
|
||||||
|
'Employee Tax Exemption Declaration Category',
|
||||||
|
'Employee Tax Exemption Proof Submission Detail'
|
||||||
|
]
|
||||||
|
|
||||||
|
for doctype in doctypes:
|
||||||
|
frappe.reload_doc('Payroll', 'doctype', doctype)
|
||||||
|
|
||||||
|
|
||||||
|
reports = ['Professional Tax Deductions', 'Provident Fund Deductions']
|
||||||
|
for report in reports:
|
||||||
|
frappe.reload_doc('Regional', 'Report', report)
|
||||||
|
frappe.reload_doc('Regional', 'Report', report)
|
||||||
|
|
||||||
|
if erpnext.get_region() == "India":
|
||||||
|
setup(patch=True)
|
||||||
|
|
||||||
if frappe.db.exists("Salary Component", "Income Tax"):
|
if frappe.db.exists("Salary Component", "Income Tax"):
|
||||||
frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)
|
frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1)
|
||||||
|
11
erpnext/patches/v13_0/update_deferred_settings.py
Normal file
11
erpnext/patches/v13_0/update_deferred_settings.py
Normal file
@ -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()
|
@ -163,7 +163,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 21:10:50.374063",
|
"modified": "2020-06-22 21:10:50.374063",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Additional Salary",
|
"name": "Additional Salary",
|
||||||
|
@ -119,7 +119,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 22:58:31.271922",
|
"modified": "2020-06-22 22:58:31.271922",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Application",
|
"name": "Employee Benefit Application",
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:45:00.519134",
|
"modified": "2020-06-22 23:45:00.519134",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Application Detail",
|
"name": "Employee Benefit Application Detail",
|
||||||
|
@ -123,7 +123,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:01:50.791676",
|
"modified": "2020-06-22 23:01:50.791676",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Benefit Claim",
|
"name": "Employee Benefit Claim",
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 22:42:51.209630",
|
"modified": "2020-06-22 22:42:51.209630",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Incentive",
|
"name": "Employee Incentive",
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 22:55:17.604688",
|
"modified": "2020-06-22 22:55:17.604688",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Other Income",
|
"name": "Employee Other Income",
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:16:47.472910",
|
"modified": "2020-06-22 23:16:47.472910",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Category",
|
"name": "Employee Tax Exemption Category",
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 22:49:43.829892",
|
"modified": "2020-06-22 22:49:43.829892",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Declaration",
|
"name": "Employee Tax Exemption Declaration",
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:41:03.638739",
|
"modified": "2020-06-22 23:41:03.638739",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Declaration Category",
|
"name": "Employee Tax Exemption Declaration Category",
|
||||||
|
@ -130,7 +130,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 22:53:10.412321",
|
"modified": "2020-06-22 22:53:10.412321",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Proof Submission",
|
"name": "Employee Tax Exemption Proof Submission",
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:37:08.265600",
|
"modified": "2020-06-22 23:37:08.265600",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Proof Submission Detail",
|
"name": "Employee Tax Exemption Proof Submission Detail",
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:18:08.254645",
|
"modified": "2020-06-22 23:18:08.254645",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Employee Tax Exemption Sub Category",
|
"name": "Employee Tax Exemption Sub Category",
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 20:27:13.425084",
|
"modified": "2020-06-22 20:27:13.425084",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Income Tax Slab",
|
"name": "Income Tax Slab",
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:33:17.931912",
|
"modified": "2020-06-22 23:33:17.931912",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Income Tax Slab Other Charges",
|
"name": "Income Tax Slab Other Charges",
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:25:13.779032",
|
"modified": "2020-06-22 23:25:13.779032",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Payroll Employee Detail",
|
"name": "Payroll Employee Detail",
|
||||||
|
@ -262,7 +262,7 @@
|
|||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 20:06:06.953904",
|
"modified": "2020-06-22 20:06:06.953904",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Payroll Entry",
|
"name": "Payroll Entry",
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 20:12:32.684189",
|
"modified": "2020-06-22 20:12:32.684189",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Payroll Period",
|
"name": "Payroll Period",
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:30:15.943356",
|
"modified": "2020-06-22 23:30:15.943356",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Payroll Period Date",
|
"name": "Payroll Period Date",
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
"description": "The fraction of daily wages to be paid for half-day attendance",
|
"description": "The fraction of daily wages to be paid for half-day attendance",
|
||||||
"fieldname": "daily_wages_fraction_for_half_day",
|
"fieldname": "daily_wages_fraction_for_half_day",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Daily Wages Fraction for Half Day",
|
"label": "Fraction of Daily Salary for Half Day",
|
||||||
"show_days": 1,
|
"show_days": 1,
|
||||||
"show_seconds": 1
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-05 12:35:34.861674",
|
"modified": "2020-06-22 17:00:58.408030",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Payroll Settings",
|
"name": "Payroll Settings",
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 22:42:05.251951",
|
"modified": "2020-06-22 22:42:05.251951",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Retention Bonus",
|
"name": "Retention Bonus",
|
||||||
|
@ -245,7 +245,7 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-flag",
|
"icon": "fa fa-flag",
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-01 15:39:20.826565",
|
"modified": "2020-06-22 15:39:20.826565",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Component",
|
"name": "Salary Component",
|
||||||
|
@ -211,7 +211,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:21:26.300951",
|
"modified": "2020-06-22 23:21:26.300951",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Detail",
|
"name": "Salary Detail",
|
||||||
|
@ -616,7 +616,7 @@
|
|||||||
"idx": 9,
|
"idx": 9,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-05 14:42:43.921828",
|
"modified": "2020-06-22 14:42:43.921828",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:27:43.463532",
|
"modified": "2020-06-22 23:27:43.463532",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Slip Timesheet",
|
"name": "Salary Slip Timesheet",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
Variables from Employee:<br> <code>Employment Type = employment_type</code>, <code>Branch = branch</code> etc.
|
Variables from Employee:<br> <code>Employment Type = employment_type</code>, <code>Branch = branch</code> etc.
|
||||||
</li>
|
</li>
|
||||||
<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.
|
<code>Payment Days = payment_days</code>, <code>Leave without pay = leave_without_pay</code> etc.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
@ -282,7 +282,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-05 17:07:26.129355",
|
"modified": "2020-06-22 17:07:26.129355",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Structure",
|
"name": "Salary Structure",
|
||||||
|
@ -124,7 +124,7 @@
|
|||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 19:58:09.964692",
|
"modified": "2020-06-22 19:58:09.964692",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Salary Structure Assignment",
|
"name": "Salary Structure Assignment",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "from_amount",
|
"fieldname": "from_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"label": "To Amount"
|
"label": "To Amount"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "percent_deduction",
|
"fieldname": "percent_deduction",
|
||||||
"fieldtype": "Percent",
|
"fieldtype": "Percent",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -51,7 +53,7 @@
|
|||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-27 23:32:47.253106",
|
"modified": "2020-06-22 18:16:07.596493",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Taxable Salary Slab",
|
"name": "Taxable Salary Slab",
|
||||||
|
@ -86,6 +86,7 @@ def get_conditions(filters):
|
|||||||
|
|
||||||
if filters.get("period"):
|
if filters.get("period"):
|
||||||
conditions.append("month(start_date) = '%s' " % (filters["period"]))
|
conditions.append("month(start_date) = '%s' " % (filters["period"]))
|
||||||
|
conditions.append("year(start_date) = '%s' " % (frappe.utils.getdate().year))
|
||||||
|
|
||||||
return " and ".join(conditions)
|
return " and ".join(conditions)
|
||||||
|
|
||||||
|
@ -335,6 +335,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
|||||||
var tax_rate = this._get_tax_rate(tax, item_tax_map);
|
var tax_rate = this._get_tax_rate(tax, item_tax_map);
|
||||||
var current_tax_amount = 0.0;
|
var current_tax_amount = 0.0;
|
||||||
|
|
||||||
|
// To set row_id by default as previous row.
|
||||||
|
if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) {
|
||||||
|
if (tax.idx === 1) {
|
||||||
|
frappe.throw(
|
||||||
|
__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"));
|
||||||
|
}
|
||||||
|
if (!tax.row_id) {
|
||||||
|
tax.row_id = tax.idx - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
if(tax.charge_type == "Actual") {
|
if(tax.charge_type == "Actual") {
|
||||||
// distribute the tax amount proportionally to each item row
|
// distribute the tax amount proportionally to each item row
|
||||||
var actual = flt(tax.tax_amount, precision("tax_amount", tax));
|
var actual = flt(tax.tax_amount, precision("tax_amount", tax));
|
||||||
|
@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
test_records = frappe.get_test_records('Product Bundle')
|
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):
|
if frappe.db.exists("Product Bundle", parent):
|
||||||
return frappe.get_doc("Product Bundle", parent)
|
return frappe.get_doc("Product Bundle", parent)
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ def make_product_bundle(parent, items):
|
|||||||
})
|
})
|
||||||
|
|
||||||
for item in items:
|
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()
|
product_bundle.insert()
|
||||||
|
|
||||||
|
@ -99,6 +99,8 @@ class Quotation(SellingController):
|
|||||||
self.update_lead()
|
self.update_lead()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
|
if self.lost_reasons:
|
||||||
|
self.lost_reasons = []
|
||||||
super(Quotation, self).on_cancel()
|
super(Quotation, self).on_cancel()
|
||||||
|
|
||||||
#update enquiry status
|
#update enquiry status
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
import json
|
||||||
from frappe.utils import flt, add_days, nowdate
|
from frappe.utils import flt, add_days, nowdate
|
||||||
import frappe.permissions
|
import frappe.permissions
|
||||||
import unittest
|
import unittest
|
||||||
@ -10,9 +11,10 @@ from erpnext.selling.doctype.sales_order.sales_order \
|
|||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
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.selling.doctype.sales_order.sales_order import make_work_orders
|
||||||
from erpnext.controllers.accounts_controller import update_child_qty_rate
|
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.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.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):
|
class TestSalesOrder(unittest.TestCase):
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
@ -417,6 +419,26 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
||||||
frappe.set_user("Administrator")
|
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):
|
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 1 - _TC", "test@example.com")
|
||||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
||||||
@ -457,8 +479,6 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
self.assertRaises(frappe.CancelledLinkError, dn.submit)
|
self.assertRaises(frappe.CancelledLinkError, dn.submit)
|
||||||
|
|
||||||
def test_service_type_product_bundle(self):
|
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", {"is_stock_item": 0})
|
||||||
make_item("_Test Service Product Bundle Item 1", {"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})
|
make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0})
|
||||||
@ -472,8 +492,6 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items])
|
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):
|
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", {"is_stock_item": 0})
|
||||||
make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1})
|
make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1})
|
||||||
make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0})
|
make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0})
|
||||||
@ -484,7 +502,6 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="")
|
self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="")
|
||||||
|
|
||||||
def test_auto_insert_price(self):
|
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})
|
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)
|
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
|
||||||
|
|
||||||
@ -519,7 +536,6 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
|
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
|
||||||
|
|
||||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
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})
|
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})
|
dn_item = make_item("_Test Regular Item", {"is_stock_item": 1})
|
||||||
@ -714,7 +730,6 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
|
|
||||||
def test_serial_no_based_delivery(self):
|
def test_serial_no_based_delivery(self):
|
||||||
frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1)
|
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,
|
item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1,
|
||||||
"maintain_stock": 1,
|
"maintain_stock": 1,
|
||||||
"has_serial_no": 1,
|
"has_serial_no": 1,
|
||||||
@ -835,7 +850,6 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
|
self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
|
||||||
|
|
||||||
def test_request_for_raw_materials(self):
|
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,
|
item = make_item("_Test Finished Item", {"is_stock_item": 1,
|
||||||
"maintain_stock": 1,
|
"maintain_stock": 1,
|
||||||
"valuation_rate": 500,
|
"valuation_rate": 500,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"add_total_row": 0,
|
"add_total_row": 1,
|
||||||
"creation": "2018-09-21 12:46:29.451048",
|
"creation": "2018-09-21 12:46:29.451048",
|
||||||
"disable_prepared_report": 0,
|
"disable_prepared_report": 0,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
@ -7,7 +7,7 @@
|
|||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2020-04-30 19:49:02.303320",
|
"modified": "2020-06-19 17:41:03.132101",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Sales Analytics",
|
"name": "Sales Analytics",
|
||||||
|
@ -23,7 +23,14 @@ class Analytics(object):
|
|||||||
self.get_columns()
|
self.get_columns()
|
||||||
self.get_data()
|
self.get_data()
|
||||||
self.get_chart_data()
|
self.get_chart_data()
|
||||||
return self.columns, self.data, None, self.chart
|
|
||||||
|
# Skipping total row for tree-view reports
|
||||||
|
skip_total_row = 0
|
||||||
|
|
||||||
|
if self.filters.tree_type in ["Supplier Group", "Item Group", "Customer Group", "Territory"]:
|
||||||
|
skip_total_row = 1
|
||||||
|
|
||||||
|
return self.columns, self.data, None, self.chart, None, skip_total_row
|
||||||
|
|
||||||
def get_columns(self):
|
def get_columns(self):
|
||||||
self.columns = [{
|
self.columns = [{
|
||||||
@ -194,9 +201,6 @@ class Analytics(object):
|
|||||||
def get_rows(self):
|
def get_rows(self):
|
||||||
self.data = []
|
self.data = []
|
||||||
self.get_periodic_data()
|
self.get_periodic_data()
|
||||||
total_row = {
|
|
||||||
"entity": "Total",
|
|
||||||
}
|
|
||||||
|
|
||||||
for entity, period_data in iteritems(self.entity_periodic_data):
|
for entity, period_data in iteritems(self.entity_periodic_data):
|
||||||
row = {
|
row = {
|
||||||
@ -210,9 +214,6 @@ class Analytics(object):
|
|||||||
row[scrub(period)] = amount
|
row[scrub(period)] = amount
|
||||||
total += amount
|
total += amount
|
||||||
|
|
||||||
if not total_row.get(scrub(period)): total_row[scrub(period)] = 0
|
|
||||||
total_row[scrub(period)] += amount
|
|
||||||
|
|
||||||
row["total"] = total
|
row["total"] = total
|
||||||
|
|
||||||
if self.filters.tree_type == "Item":
|
if self.filters.tree_type == "Item":
|
||||||
@ -220,8 +221,6 @@ class Analytics(object):
|
|||||||
|
|
||||||
self.data.append(row)
|
self.data.append(row)
|
||||||
|
|
||||||
self.data.append(total_row)
|
|
||||||
|
|
||||||
def get_rows_by_group(self):
|
def get_rows_by_group(self):
|
||||||
self.get_periodic_data()
|
self.get_periodic_data()
|
||||||
out = []
|
out = []
|
||||||
|
@ -33,21 +33,6 @@ class TestAnalytics(unittest.TestCase):
|
|||||||
report = execute(filters)
|
report = execute(filters)
|
||||||
|
|
||||||
expected_data = [
|
expected_data = [
|
||||||
{
|
|
||||||
'entity': 'Total',
|
|
||||||
'apr_2017': 0.0,
|
|
||||||
'may_2017': 0.0,
|
|
||||||
'jun_2017': 2000.0,
|
|
||||||
'jul_2017': 1000.0,
|
|
||||||
'aug_2017': 0.0,
|
|
||||||
'sep_2017': 1500.0,
|
|
||||||
'oct_2017': 1000.0,
|
|
||||||
'nov_2017': 0.0,
|
|
||||||
'dec_2017': 0.0,
|
|
||||||
'jan_2018': 0.0,
|
|
||||||
'feb_2018': 2000.0,
|
|
||||||
'mar_2018': 0.0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"entity": "_Test Customer 1",
|
"entity": "_Test Customer 1",
|
||||||
"entity_name": "_Test Customer 1",
|
"entity_name": "_Test Customer 1",
|
||||||
@ -149,21 +134,6 @@ class TestAnalytics(unittest.TestCase):
|
|||||||
report = execute(filters)
|
report = execute(filters)
|
||||||
|
|
||||||
expected_data = [
|
expected_data = [
|
||||||
{
|
|
||||||
'entity': 'Total',
|
|
||||||
'apr_2017': 0.0,
|
|
||||||
'may_2017': 0.0,
|
|
||||||
'jun_2017': 20.0,
|
|
||||||
'jul_2017': 10.0,
|
|
||||||
'aug_2017': 0.0,
|
|
||||||
'sep_2017': 15.0,
|
|
||||||
'oct_2017': 10.0,
|
|
||||||
'nov_2017': 0.0,
|
|
||||||
'dec_2017': 0.0,
|
|
||||||
'jan_2018': 0.0,
|
|
||||||
'feb_2018': 20.0,
|
|
||||||
'mar_2018': 0.0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"entity": "_Test Customer 1",
|
"entity": "_Test Customer 1",
|
||||||
"entity_name": "_Test Customer 1",
|
"entity_name": "_Test Customer 1",
|
||||||
|
@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
|||||||
|
|
||||||
cur_frm.cscript.set_root_readonly = function(doc) {
|
cur_frm.cscript.set_root_readonly = function(doc) {
|
||||||
// read-only for root customer group
|
// 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_read_only();
|
||||||
cur_frm.set_intro(__("This is a root customer group and cannot be edited."));
|
cur_frm.set_intro(__("This is a root customer group and cannot be edited."));
|
||||||
} else {
|
} else {
|
||||||
@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) {
|
|||||||
cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) {
|
cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'is_group': 1
|
'is_group': 1,
|
||||||
|
'name': ['!=', cur_frm.doc.customer_group_name]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ frappe.ui.form.on("Item Group", {
|
|||||||
set_root_readonly: function(frm) {
|
set_root_readonly: function(frm) {
|
||||||
// read-only for root item group
|
// read-only for root item group
|
||||||
frm.set_intro("");
|
frm.set_intro("");
|
||||||
if(!frm.doc.parent_item_group) {
|
if(!frm.doc.parent_item_group && !frm.doc.__islocal) {
|
||||||
frm.set_read_only();
|
frm.set_read_only();
|
||||||
frm.set_intro(__("This is a root item group and cannot be edited."), true);
|
frm.set_intro(__("This is a root item group and cannot be edited."), true);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
|||||||
|
|
||||||
cur_frm.cscript.set_root_readonly = function(doc) {
|
cur_frm.cscript.set_root_readonly = function(doc) {
|
||||||
// read-only for root
|
// read-only for root
|
||||||
if(!doc.parent_sales_person) {
|
if(!doc.parent_sales_person && !doc.__islocal) {
|
||||||
cur_frm.set_read_only();
|
cur_frm.set_read_only();
|
||||||
cur_frm.set_intro(__("This is a root sales person and cannot be edited."));
|
cur_frm.set_intro(__("This is a root sales person and cannot be edited."));
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc) {
|
|||||||
|
|
||||||
cur_frm.cscript.set_root_readonly = function(doc) {
|
cur_frm.cscript.set_root_readonly = function(doc) {
|
||||||
// read-only for root customer group
|
// 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_read_only();
|
||||||
cur_frm.set_intro(__("This is a root supplier group and cannot be edited."));
|
cur_frm.set_intro(__("This is a root supplier group and cannot be edited."));
|
||||||
} else {
|
} else {
|
||||||
@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) {
|
|||||||
cur_frm.fields_dict['parent_supplier_group'].get_query = function() {
|
cur_frm.fields_dict['parent_supplier_group'].get_query = function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'is_group': 1
|
'is_group': 1,
|
||||||
|
'name': ['!=', cur_frm.doc.supplier_group_name]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
|||||||
|
|
||||||
cur_frm.cscript.set_root_readonly = function(doc) {
|
cur_frm.cscript.set_root_readonly = function(doc) {
|
||||||
// read-only for root territory
|
// read-only for root territory
|
||||||
if(!doc.parent_territory) {
|
if(!doc.parent_territory && !doc.__islocal) {
|
||||||
cur_frm.set_read_only();
|
cur_frm.set_read_only();
|
||||||
cur_frm.set_intro(__("This is a root territory and cannot be edited."));
|
cur_frm.set_intro(__("This is a root territory and cannot be edited."));
|
||||||
} else {
|
} else {
|
||||||
|
@ -597,7 +597,7 @@ class Item(WebsiteGenerator):
|
|||||||
def stock_ledger_created(self):
|
def stock_ledger_created(self):
|
||||||
if not hasattr(self, '_stock_ledger_created'):
|
if not hasattr(self, '_stock_ledger_created'):
|
||||||
self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry`
|
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
|
return self._stock_ledger_created
|
||||||
|
|
||||||
def validate_name_with_item_group(self):
|
def validate_name_with_item_group(self):
|
||||||
@ -883,7 +883,12 @@ class Item(WebsiteGenerator):
|
|||||||
linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"]
|
linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"]
|
||||||
|
|
||||||
for doctype in linked_doctypes:
|
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",
|
frappe.db.get_value("Production Order",
|
||||||
filters={"production_item": self.name, "docstatus": 1}):
|
filters={"production_item": self.name, "docstatus": 1}):
|
||||||
return True
|
return True
|
||||||
|
@ -574,9 +574,7 @@ class StockEntry(StockController):
|
|||||||
{"parent": self.purchase_order, "item_code": se_item.subcontracted_item},
|
{"parent": self.purchase_order, "item_code": se_item.subcontracted_item},
|
||||||
"bom")
|
"bom")
|
||||||
|
|
||||||
allow_alternative_item = frappe.get_value("BOM", bom_no, "allow_alternative_item")
|
if se_item.allow_alternative_item:
|
||||||
|
|
||||||
if allow_alternative_item:
|
|
||||||
original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code")
|
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 \
|
required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \
|
||||||
@ -743,7 +741,7 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
def get_item_details(self, args=None, for_update=False):
|
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,
|
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
|
id.expense_account, id.buying_cost_center
|
||||||
from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s
|
from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s
|
||||||
where i.name=%s
|
where i.name=%s
|
||||||
@ -778,6 +776,9 @@ class StockEntry(StockController):
|
|||||||
'expense_account' : item.expense_account
|
'expense_account' : item.expense_account
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if self.purpose == 'Send to Subcontractor':
|
||||||
|
ret["allow_alternative_item"] = item.allow_alternative_item
|
||||||
|
|
||||||
# update uom
|
# update uom
|
||||||
if args.get("uom") and for_update:
|
if args.get("uom") and for_update:
|
||||||
ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty')))
|
ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty')))
|
||||||
|
@ -12,8 +12,9 @@
|
|||||||
} %}
|
} %}
|
||||||
|
|
||||||
{% set link = '' %}
|
{% set link = '' %}
|
||||||
{% set label = domains[0].domain %}
|
{% set label = '' %}
|
||||||
{% if domains %}
|
{% if domains %}
|
||||||
|
{% set label = domains[0].domain %}
|
||||||
{% set link = links[label] %}
|
{% set link = links[label] %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -161,19 +161,19 @@ class TransactionBase(StatusUpdater):
|
|||||||
if not (self.get("update_stock") or self.get("is_pos")):
|
if not (self.get("update_stock") or self.get("is_pos")):
|
||||||
return
|
return
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year(self.get('posting_date'), as_dict=True).name
|
for item in self.get('items'):
|
||||||
|
|
||||||
last_transaction_time = frappe.db.sql("""
|
last_transaction_time = frappe.db.sql("""
|
||||||
select MAX(timestamp(posting_date, posting_time)) as posting_time
|
select MAX(timestamp(posting_date, posting_time)) as posting_time
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where docstatus = 1""")[0][0]
|
where docstatus = 1 and item_code = %s """, (item.item_code))[0][0]
|
||||||
|
|
||||||
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
|
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
|
||||||
|
|
||||||
if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
|
if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
|
||||||
frappe.throw(_("""Posting timestamp of current transaction
|
msg = _("Last Stock Transaction for item {0} was on {1}.").format(frappe.bold(item.item_code), frappe.bold(last_transaction_time))
|
||||||
must be after last Stock transaction's timestamp which is {0}""").format(frappe.bold(last_transaction_time)),
|
msg += "<br><br>" + _("Stock Transactions for Item {0} cannot be posted before this time.").format(frappe.bold(item.item_code))
|
||||||
title=_("Backdated Stock Entry"))
|
msg += "<br><br>" + _("Please remove this item and try to submit again or update the posting time.")
|
||||||
|
frappe.throw(msg, title=_("Backdated Stock Entry"))
|
||||||
|
|
||||||
def delete_events(ref_type, ref_name):
|
def delete_events(ref_type, ref_name):
|
||||||
events = frappe.db.sql_list(""" SELECT
|
events = frappe.db.sql_list(""" SELECT
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
{% macro title() %}
|
{% macro title() %}
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<a href="/lms/course?name={{ course }}&program={{ program }}" class="text-muted">
|
<a href="/lms/course?name={{ course }}&program={{ program }}" class="text-muted">
|
||||||
Back to Course
|
{{_('Back to Course')}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -69,15 +69,15 @@
|
|||||||
|
|
||||||
{% macro navigation() %}
|
{% macro navigation() %}
|
||||||
{% if previous %}
|
{% if previous %}
|
||||||
<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ previous.content_type }}&content={{ previous.content }}" class='btn text-muted' style="box-shadow: none;">Previous</a>
|
<a href="/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ previous.content_type }}&content={{ previous.content }}" class='btn text-muted' style="box-shadow: none;">{{_('Previous')}}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="/lms/course?name={{ course }}&program={{ program }}" class='btn text-muted' style="box-shadow: none;">Back to Course</a>
|
<a href="/lms/course?name={{ course }}&program={{ program }}" class='btn text-muted' style="box-shadow: none;">{{ _('Back to Course') }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if next %}
|
{% if next %}
|
||||||
<button id="nextButton" onclick="handle('/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}')" class='btn btn-primary' disabled="true">Next</button>
|
<button id="nextButton" onclick="handle('/lms/content?program={{ program }}&course={{ course }}&topic={{ topic }}&type={{ next.content_type }}&content={{ next.content }}')" class='btn btn-primary' disabled="true">{{_('Next')}}</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
<button id="nextButton" onclick="handle('/lms/course?name={{ course }}&program={{ program }}')" class='btn btn-primary' disabled="true">Finish Topic</button>
|
<button id="nextButton" onclick="handle('/lms/course?name={{ course }}&program={{ program }}')" class='btn btn-primary' disabled="true">{{_('Finish Topic')}}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@
|
|||||||
{{ title() }}
|
{{ title() }}
|
||||||
<div class="text-muted">
|
<div class="text-muted">
|
||||||
{% if content.duration %}
|
{% if content.duration %}
|
||||||
{{ content.duration }} Mins
|
{{ content.duration }} {{_('Mins')}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if content.publish_date and content.duration%}
|
{% if content.publish_date and content.duration%}
|
||||||
@ -94,7 +94,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if content.publish_date %}
|
{% if content.publish_date %}
|
||||||
Published on {{ content.publish_date.strftime('%d, %b %Y') }}
|
{{_('Published on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -109,13 +109,13 @@
|
|||||||
{{ title() }}
|
{{ title() }}
|
||||||
<div class="text-muted">
|
<div class="text-muted">
|
||||||
{% if content.author or content.publish_date %}
|
{% if content.author or content.publish_date %}
|
||||||
Published
|
{{_('Published')}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if content.author %}
|
{% if content.author %}
|
||||||
by {{ content.author }}
|
{{_('by')}} {{ content.author }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if content.publish_date %}
|
{% if content.publish_date %}
|
||||||
on {{ content.publish_date.strftime('%d, %b %Y') }}
|
{{_('on')}} {{ content.publish_date.strftime('%d, %b %Y') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,11 +72,11 @@
|
|||||||
{% if has_access %}
|
{% if has_access %}
|
||||||
<div class='card-footer'>
|
<div class='card-footer'>
|
||||||
{% if progress[topic.name].completed %}
|
{% if progress[topic.name].completed %}
|
||||||
<span class="indicator green">Completed</span>
|
<span class="indicator green">{{_('Completed')}}</span>
|
||||||
{% elif progress[topic.name].started %}
|
{% elif progress[topic.name].started %}
|
||||||
<span class="indicator orange">In Progress</span>
|
<span class="indicator orange">{{_('In Progress')}}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="indicator blue">Start</span>
|
<span class="indicator blue">{{_('Start')}}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
<p class='lead'>{{ education_settings.description }}</p>
|
<p class='lead'>{{ education_settings.description }}</p>
|
||||||
<p class="mt-4">
|
<p class="mt-4">
|
||||||
{% if frappe.session.user == 'Guest' %}
|
{% if frappe.session.user == 'Guest' %}
|
||||||
<a class="btn btn-primary btn-lg" href="'/login#signup'">Sign Up</a>
|
<a class="btn btn-primary btn-lg" href="'/login#signup'">{{_('Sign Up')}}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if has_access or program.intro_video%}
|
{% if has_access or program.intro_video%}
|
||||||
<div class='card-footer'>
|
<div class='card-footer'>
|
||||||
{% if has_access %} <span class="indicator green">Enrolled</span>
|
{% if has_access %} <span class="indicator green">{{_('Enrolled')}}</span>
|
||||||
{% elif program.intro_video %} <span><a href="{{ program.intro_video }}" target="blank">Watch Intro</a></span>
|
{% elif program.intro_video %} <span><a href="{{ program.intro_video }}" target="blank">{{_('Watch Intro')}}</a></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
<div class='container pb-5'>
|
<div class='container pb-5'>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<a href="{{ back.url }}" class="text-muted">
|
<a href="{{ back.url }}" class="text-muted">
|
||||||
Back to {{ back.name }}
|
{{_('Back to')}} {{ _(back.name) }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}</h1>
|
||||||
<p class='lead' style="max-width: 100%;">{{ description or ''}}</p>
|
<p class='lead' style="max-width: 100%;">{{ description or ''}}</p>
|
||||||
<p class="mt-4">
|
<p class="mt-4">
|
||||||
{% if frappe.session.user == 'Guest' %}
|
{% if frappe.session.user == 'Guest' %}
|
||||||
<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">Sign Up</a>
|
<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
|
||||||
{% elif not has_access %}
|
{% elif not has_access %}
|
||||||
<button id="enroll" class="btn btn-primary btn-lg" onclick="enroll()" disabled>Enroll</button>
|
<button id="enroll" class="btn btn-primary btn-lg" onclick="enroll()" disabled>{{_('Enroll')}}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
let btn = document.getElementById('enroll');
|
let btn = document.getElementById('enroll');
|
||||||
btn.disbaled = true;
|
btn.disbaled = true;
|
||||||
btn.innerText = 'Enrolling...'
|
btn.innerText = __('Enrolling...')
|
||||||
|
|
||||||
let opts = {
|
let opts = {
|
||||||
method: 'erpnext.education.utils.enroll_in_program',
|
method: 'erpnext.education.utils.enroll_in_program',
|
||||||
@ -44,7 +44,7 @@
|
|||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
success_dialog.set_message('You have successfully enrolled for the program ');
|
success_dialog.set_message(__('You have successfully enrolled for the program '));
|
||||||
success_dialog.$message.show()
|
success_dialog.$message.show()
|
||||||
success_dialog.show();
|
success_dialog.show();
|
||||||
btn.disbaled = false;
|
btn.disbaled = false;
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class='card-footer'>
|
<div class='card-footer'>
|
||||||
<span class="small">{{ program.completion }}% Complete</span>
|
<span class="small">{{ program.completion }}{{_('% Complete')}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@ -43,11 +43,11 @@
|
|||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<div class="col-md-7">
|
<div class="col-md-7">
|
||||||
<a href="/lms" class="text-muted">
|
<a href="/lms" class="text-muted">
|
||||||
Back to Home
|
{{_('Back to Home')}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-5 text-right">
|
<div class="col-md-5 text-right">
|
||||||
<a href="/update-profile?name={{ frappe.session.user }}" target="blank" class="mt-0 text-muted">Edit Profile</a>
|
<a href="/update-profile?name={{ frappe.session.user }}" target="blank" class="mt-0 text-muted">{{_('Edit Profile')}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h1>{{ student.first_name }} {{ student.last_name or '' }}</h1>
|
<h1>{{ student.first_name }} {{ student.last_name or '' }}</h1>
|
||||||
|
@ -55,11 +55,11 @@
|
|||||||
{% if has_access and progress[course.name] %}
|
{% if has_access and progress[course.name] %}
|
||||||
<div class='card-footer'>
|
<div class='card-footer'>
|
||||||
{% if progress[course.name].completed %}
|
{% if progress[course.name].completed %}
|
||||||
<span class="indicator green">Completed</span>
|
<span class="indicator green">{{_('Completed')}}</span>
|
||||||
{% elif progress[course.name].started %}
|
{% elif progress[course.name].started %}
|
||||||
<span class="indicator orange">In Progress</span>
|
<span class="indicator orange">{{_('In Progress')}}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="indicator blue">Start</span>
|
<span class="indicator blue">{{_('Start')}}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -23,13 +23,13 @@
|
|||||||
{% if has_access %}
|
{% if has_access %}
|
||||||
<div class='card-footer'>
|
<div class='card-footer'>
|
||||||
{% if content.content_type == 'Quiz' %}
|
{% if content.content_type == 'Quiz' %}
|
||||||
{% if content.result == 'Fail' %} <span class="indicator red">Fail <span class="text-muted">({{ content.score }}/100)</span></span>
|
{% if content.result == 'Fail' %} <span class="indicator red">{{_('Fail')}} <span class="text-muted">({{ content.score }}/100)</span></span>
|
||||||
{% elif content.result == 'Pass' %} <span class="indicator green">Pass <span class="text-muted">({{ content.score }}/100)</span>
|
{% elif content.result == 'Pass' %} <span class="indicator green">{{_('Pass')}} <span class="text-muted">({{ content.score }}/100)</span>
|
||||||
{% else %} <span class="indicator blue">Start</span>
|
{% else %} <span class="indicator blue">{{_('Start')}}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if content.completed %} <span class="indicator green">Completed</span>
|
{% if content.completed %} <span class="indicator green">{{_('Completed')}}</span>
|
||||||
{% else %} <span class="indicator blue">Start</span>
|
{% else %} <span class="indicator blue">{{_('Start')}}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user