Merge branch 'hotfix'
This commit is contained in:
commit
05c8474db3
@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '11.1.17'
|
__version__ = '11.1.18'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
@ -2,9 +2,9 @@ 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
|
from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from frappe.email import sendmail_to_system_managers
|
||||||
|
|
||||||
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 '''
|
||||||
@ -33,47 +33,49 @@ def validate_service_stop_date(doc):
|
|||||||
frappe.throw(_("Cannot change Service Stop Date for item in row {0}".format(item.idx)))
|
frappe.throw(_("Cannot change Service Stop Date for item in row {0}".format(item.idx)))
|
||||||
|
|
||||||
def convert_deferred_expense_to_expense(start_date=None, end_date=None):
|
def convert_deferred_expense_to_expense(start_date=None, end_date=None):
|
||||||
|
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||||
|
if not start_date:
|
||||||
|
start_date = add_months(today(), -1)
|
||||||
|
if not end_date:
|
||||||
|
end_date = add_days(today(), -1)
|
||||||
|
|
||||||
# check for the purchase invoice for which GL entries has to be done
|
# check for the purchase invoice for which GL entries has to be done
|
||||||
invoices = frappe.db.sql_list('''
|
invoices = frappe.db.sql_list('''
|
||||||
select distinct parent from `tabPurchase Invoice Item` where service_start_date<=%s and service_end_date>=%s
|
select distinct parent from `tabPurchase Invoice Item`
|
||||||
|
where service_start_date<=%s and service_end_date>=%s
|
||||||
and enable_deferred_expense = 1 and docstatus = 1 and ifnull(amount, 0) > 0
|
and enable_deferred_expense = 1 and docstatus = 1 and ifnull(amount, 0) > 0
|
||||||
''', (end_date or today(), start_date or add_months(today(), -1)))
|
''', (end_date, start_date))
|
||||||
|
|
||||||
# For each invoice, book deferred expense
|
# For each invoice, book deferred expense
|
||||||
for invoice in invoices:
|
for invoice in invoices:
|
||||||
doc = frappe.get_doc("Purchase Invoice", invoice)
|
doc = frappe.get_doc("Purchase Invoice", invoice)
|
||||||
book_deferred_income_or_expense(doc, start_date, end_date)
|
book_deferred_income_or_expense(doc, end_date)
|
||||||
|
|
||||||
def convert_deferred_revenue_to_income(start_date=None, end_date=None):
|
def convert_deferred_revenue_to_income(start_date=None, end_date=None):
|
||||||
|
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
||||||
|
if not start_date:
|
||||||
|
start_date = add_months(today(), -1)
|
||||||
|
if not end_date:
|
||||||
|
end_date = add_days(today(), -1)
|
||||||
|
|
||||||
# check for the sales invoice for which GL entries has to be done
|
# check for the sales invoice for which GL entries has to be done
|
||||||
invoices = frappe.db.sql_list('''
|
invoices = frappe.db.sql_list('''
|
||||||
select distinct parent from `tabSales Invoice Item` where service_start_date<=%s and service_end_date>=%s
|
select distinct parent from `tabSales Invoice Item`
|
||||||
|
where service_start_date<=%s and service_end_date>=%s
|
||||||
and enable_deferred_revenue = 1 and docstatus = 1 and ifnull(amount, 0) > 0
|
and enable_deferred_revenue = 1 and docstatus = 1 and ifnull(amount, 0) > 0
|
||||||
''', (end_date or today(), start_date or add_months(today(), -1)))
|
''', (end_date, start_date))
|
||||||
|
|
||||||
# For each invoice, book deferred revenue
|
|
||||||
for invoice in invoices:
|
for invoice in invoices:
|
||||||
doc = frappe.get_doc("Sales Invoice", invoice)
|
doc = frappe.get_doc("Sales Invoice", invoice)
|
||||||
book_deferred_income_or_expense(doc, start_date, end_date)
|
book_deferred_income_or_expense(doc, end_date)
|
||||||
|
|
||||||
|
def get_booking_dates(doc, item, posting_date=None):
|
||||||
|
if not posting_date:
|
||||||
|
posting_date = add_days(today(), -1)
|
||||||
|
|
||||||
|
last_gl_entry = False
|
||||||
|
|
||||||
def get_booking_dates(doc, item, start_date=None, end_date=None):
|
|
||||||
deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
|
deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
|
||||||
last_gl_entry, skip = False, False
|
|
||||||
|
|
||||||
booking_end_date = getdate(add_days(today(), -1) if not end_date else end_date)
|
|
||||||
if booking_end_date < item.service_start_date or \
|
|
||||||
(item.service_stop_date and booking_end_date.month > item.service_stop_date.month):
|
|
||||||
return None, None, None, True
|
|
||||||
elif booking_end_date >= item.service_end_date:
|
|
||||||
last_gl_entry = True
|
|
||||||
booking_end_date = item.service_end_date
|
|
||||||
elif item.service_stop_date and item.service_stop_date <= booking_end_date:
|
|
||||||
last_gl_entry = True
|
|
||||||
booking_end_date = item.service_stop_date
|
|
||||||
|
|
||||||
booking_start_date = getdate(add_months(today(), -1) if not start_date else start_date)
|
|
||||||
booking_start_date = booking_start_date \
|
|
||||||
if booking_start_date > item.service_start_date else item.service_start_date
|
|
||||||
|
|
||||||
prev_gl_entry = frappe.db.sql('''
|
prev_gl_entry = frappe.db.sql('''
|
||||||
select name, posting_date from `tabGL Entry` where company=%s and account=%s and
|
select name, posting_date from `tabGL Entry` where company=%s and account=%s and
|
||||||
@ -81,17 +83,28 @@ def get_booking_dates(doc, item, start_date=None, end_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)
|
||||||
|
|
||||||
if not prev_gl_entry and item.service_start_date < booking_start_date:
|
if prev_gl_entry:
|
||||||
booking_start_date = item.service_start_date
|
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
|
||||||
elif prev_gl_entry:
|
else:
|
||||||
booking_start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
|
start_date = item.service_start_date
|
||||||
skip = True if booking_start_date > booking_end_date else False
|
|
||||||
|
|
||||||
return last_gl_entry, booking_start_date, booking_end_date, skip
|
end_date = get_last_day(start_date)
|
||||||
|
if end_date >= item.service_end_date:
|
||||||
|
end_date = item.service_end_date
|
||||||
|
last_gl_entry = True
|
||||||
|
elif item.service_stop_date and end_date >= item.service_stop_date:
|
||||||
|
end_date = item.service_stop_date
|
||||||
|
last_gl_entry = True
|
||||||
|
|
||||||
def calculate_amount_and_base_amount(doc, item, last_gl_entry, total_days, total_booking_days):
|
if end_date > getdate(posting_date):
|
||||||
account_currency = get_account_currency(item.expense_account)
|
end_date = posting_date
|
||||||
|
|
||||||
|
if getdate(start_date) <= getdate(end_date):
|
||||||
|
return start_date, end_date, last_gl_entry
|
||||||
|
else:
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
|
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
|
||||||
if doc.doctype == "Sales Invoice":
|
if doc.doctype == "Sales Invoice":
|
||||||
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
|
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
|
||||||
deferred_account = "deferred_revenue_account"
|
deferred_account = "deferred_revenue_account"
|
||||||
@ -123,28 +136,15 @@ def calculate_amount_and_base_amount(doc, item, last_gl_entry, total_days, total
|
|||||||
|
|
||||||
return amount, base_amount
|
return amount, base_amount
|
||||||
|
|
||||||
def book_deferred_income_or_expense(doc, start_date=None, end_date=None):
|
def book_deferred_income_or_expense(doc, posting_date=None):
|
||||||
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
|
|
||||||
# start_date: 1st of the last month or the start date
|
|
||||||
# end_date: end_date or today-1
|
|
||||||
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"
|
||||||
|
|
||||||
gl_entries = []
|
def _book_deferred_revenue_or_expense(item):
|
||||||
for item in doc.get('items'):
|
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
|
||||||
if not item.get(enable_check): continue
|
if not (start_date and end_date): return
|
||||||
|
|
||||||
skip = False
|
|
||||||
last_gl_entry, booking_start_date, booking_end_date, skip = \
|
|
||||||
get_booking_dates(doc, item, start_date, end_date)
|
|
||||||
|
|
||||||
if skip: continue
|
|
||||||
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
|
|
||||||
total_booking_days = date_diff(booking_end_date, booking_start_date) + 1
|
|
||||||
|
|
||||||
account_currency = get_account_currency(item.expense_account)
|
account_currency = get_account_currency(item.expense_account)
|
||||||
amount, base_amount = calculate_amount_and_base_amount(doc, item, last_gl_entry, total_days, total_booking_days)
|
|
||||||
|
|
||||||
if doc.doctype == "Sales Invoice":
|
if doc.doctype == "Sales Invoice":
|
||||||
against, project = doc.customer, doc.project
|
against, project = doc.customer, doc.project
|
||||||
credit_account, debit_account = item.income_account, item.deferred_revenue_account
|
credit_account, debit_account = item.income_account, item.deferred_revenue_account
|
||||||
@ -152,36 +152,62 @@ def book_deferred_income_or_expense(doc, start_date=None, end_date=None):
|
|||||||
against, project = doc.supplier, item.project
|
against, project = doc.supplier, item.project
|
||||||
credit_account, debit_account = item.deferred_expense_account, item.expense_account
|
credit_account, debit_account = item.deferred_expense_account, item.expense_account
|
||||||
|
|
||||||
# GL Entry for crediting the amount in the deferred expense
|
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
|
||||||
gl_entries.append(
|
total_booking_days = date_diff(end_date, start_date) + 1
|
||||||
doc.get_gl_dict({
|
|
||||||
"account": credit_account,
|
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
|
||||||
"against": against,
|
total_days, total_booking_days, account_currency)
|
||||||
"credit": base_amount,
|
|
||||||
"credit_in_account_currency": amount,
|
make_gl_entries(doc, credit_account, debit_account, against,
|
||||||
"cost_center": item.cost_center,
|
amount, base_amount, end_date, project, account_currency, item.cost_center, item.name)
|
||||||
"voucher_detail_no": item.name,
|
|
||||||
'posting_date': booking_end_date,
|
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
|
||||||
'project': project
|
_book_deferred_revenue_or_expense(item)
|
||||||
}, account_currency)
|
|
||||||
)
|
|
||||||
# GL Entry to debit the amount from the expense
|
for item in doc.get('items'):
|
||||||
gl_entries.append(
|
if item.get(enable_check):
|
||||||
doc.get_gl_dict({
|
_book_deferred_revenue_or_expense(item)
|
||||||
"account": debit_account,
|
|
||||||
"against": against,
|
def make_gl_entries(doc, credit_account, debit_account, against,
|
||||||
"debit": base_amount,
|
amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no):
|
||||||
"debit_in_account_currency": amount,
|
# GL Entry for crediting the amount in the deferred expense
|
||||||
"cost_center": item.cost_center,
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
"voucher_detail_no": item.name,
|
|
||||||
'posting_date': booking_end_date,
|
gl_entries = []
|
||||||
'project': project
|
gl_entries.append(
|
||||||
}, account_currency)
|
doc.get_gl_dict({
|
||||||
)
|
"account": credit_account,
|
||||||
|
"against": against,
|
||||||
|
"credit": base_amount,
|
||||||
|
"credit_in_account_currency": amount,
|
||||||
|
"cost_center": cost_center,
|
||||||
|
"voucher_detail_no": voucher_detail_no,
|
||||||
|
'posting_date': posting_date,
|
||||||
|
'project': project
|
||||||
|
}, account_currency)
|
||||||
|
)
|
||||||
|
# GL Entry to debit the amount from the expense
|
||||||
|
gl_entries.append(
|
||||||
|
doc.get_gl_dict({
|
||||||
|
"account": debit_account,
|
||||||
|
"against": against,
|
||||||
|
"debit": base_amount,
|
||||||
|
"debit_in_account_currency": amount,
|
||||||
|
"cost_center": cost_center,
|
||||||
|
"voucher_detail_no": voucher_detail_no,
|
||||||
|
'posting_date': posting_date,
|
||||||
|
'project': project
|
||||||
|
}, account_currency)
|
||||||
|
)
|
||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
try:
|
try:
|
||||||
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
|
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except:
|
except:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
frappe.log_error(message = frappe.get_traceback(), title = _("Error while processing deferred accounting for {0}").format(doc.name))
|
title = _("Error while processing deferred accounting for {0}").format(doc.name)
|
||||||
|
traceback = frappe.get_traceback()
|
||||||
|
frappe.log_error(message=traceback , title=title)
|
||||||
|
sendmail_to_system_managers(title, traceback)
|
@ -112,6 +112,8 @@ class Account(NestedSet):
|
|||||||
["company", "name"], as_dict=True):
|
["company", "name"], as_dict=True):
|
||||||
acc_name_map[d["company"]] = d["name"]
|
acc_name_map[d["company"]] = d["name"]
|
||||||
|
|
||||||
|
if not acc_name_map: return
|
||||||
|
|
||||||
for company in descendants:
|
for company in descendants:
|
||||||
doc = frappe.copy_doc(self)
|
doc = frappe.copy_doc(self)
|
||||||
doc.flags.ignore_root_company_validation = True
|
doc.flags.ignore_root_company_validation = True
|
||||||
|
@ -144,7 +144,7 @@ def _make_test_records(verbose):
|
|||||||
|
|
||||||
# related to Account Inventory Integration
|
# related to Account Inventory Integration
|
||||||
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
|
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
|
||||||
|
|
||||||
# fixed asset depreciation
|
# fixed asset depreciation
|
||||||
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
||||||
["_Test Accumulated Depreciations", "Current Assets", 0, None, None],
|
["_Test Accumulated Depreciations", "Current Assets", 0, None, None],
|
||||||
@ -181,13 +181,17 @@ def get_inventory_account(company, warehouse=None):
|
|||||||
return account
|
return account
|
||||||
|
|
||||||
def create_account(**kwargs):
|
def create_account(**kwargs):
|
||||||
account = frappe.get_doc(dict(
|
account = frappe.db.get_value("Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")})
|
||||||
doctype = "Account",
|
if account:
|
||||||
account_name = kwargs.get('account_name'),
|
return account
|
||||||
account_type = kwargs.get('account_type'),
|
else:
|
||||||
parent_account = kwargs.get('parent_account'),
|
account = frappe.get_doc(dict(
|
||||||
company = kwargs.get('company')
|
doctype = "Account",
|
||||||
))
|
account_name = kwargs.get('account_name'),
|
||||||
|
account_type = kwargs.get('account_type'),
|
||||||
account.save()
|
parent_account = kwargs.get('parent_account'),
|
||||||
return account.name
|
company = kwargs.get('company')
|
||||||
|
))
|
||||||
|
|
||||||
|
account.save()
|
||||||
|
return account.name
|
||||||
|
@ -23,36 +23,36 @@ class BankReconciliation(Document):
|
|||||||
|
|
||||||
|
|
||||||
journal_entries = frappe.db.sql("""
|
journal_entries = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
"Journal Entry" as payment_document, t1.name as payment_entry,
|
"Journal Entry" as payment_document, t1.name as payment_entry,
|
||||||
t1.cheque_no as cheque_number, t1.cheque_date,
|
t1.cheque_no as cheque_number, t1.cheque_date,
|
||||||
sum(t2.debit_in_account_currency) as debit, sum(t2.credit_in_account_currency) as credit,
|
sum(t2.debit_in_account_currency) as debit, sum(t2.credit_in_account_currency) as credit,
|
||||||
t1.posting_date, t2.against_account, t1.clearance_date, t2.account_currency
|
t1.posting_date, t2.against_account, t1.clearance_date, t2.account_currency
|
||||||
from
|
from
|
||||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||||
where
|
where
|
||||||
t2.parent = t1.name and t2.account = %s and t1.docstatus=1
|
t2.parent = t1.name and t2.account = %s and t1.docstatus=1
|
||||||
and t1.posting_date >= %s and t1.posting_date <= %s
|
and t1.posting_date >= %s and t1.posting_date <= %s
|
||||||
and ifnull(t1.is_opening, 'No') = 'No' {0}
|
and ifnull(t1.is_opening, 'No') = 'No' {0}
|
||||||
group by t2.account, t1.name
|
group by t2.account, t1.name
|
||||||
order by t1.posting_date ASC, t1.name DESC
|
order by t1.posting_date ASC, t1.name DESC
|
||||||
""".format(condition), (self.bank_account, self.from_date, self.to_date), as_dict=1)
|
""".format(condition), (self.bank_account, self.from_date, self.to_date), as_dict=1)
|
||||||
|
|
||||||
payment_entries = frappe.db.sql("""
|
payment_entries = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
"Payment Entry" as payment_document, name as payment_entry,
|
"Payment Entry" as payment_document, name as payment_entry,
|
||||||
reference_no as cheque_number, reference_date as cheque_date,
|
reference_no as cheque_number, reference_date as cheque_date,
|
||||||
if(paid_from=%(account)s, paid_amount, "") as credit,
|
if(paid_from=%(account)s, paid_amount, 0) as credit,
|
||||||
if(paid_from=%(account)s, "", received_amount) as debit,
|
if(paid_from=%(account)s, 0, received_amount) as debit,
|
||||||
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
|
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
|
||||||
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
|
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
|
||||||
from `tabPayment Entry`
|
from `tabPayment Entry`
|
||||||
where
|
where
|
||||||
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
|
||||||
and posting_date >= %(from)s and posting_date <= %(to)s {0}
|
and posting_date >= %(from)s and posting_date <= %(to)s {0}
|
||||||
order by
|
order by
|
||||||
posting_date ASC, name DESC
|
posting_date ASC, name DESC
|
||||||
""".format(condition),
|
""".format(condition),
|
||||||
{"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1)
|
{"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1)
|
||||||
|
|
||||||
pos_entries = []
|
pos_entries = []
|
||||||
@ -80,7 +80,7 @@ class BankReconciliation(Document):
|
|||||||
for d in entries:
|
for d in entries:
|
||||||
row = self.append('payment_entries', {})
|
row = self.append('payment_entries', {})
|
||||||
|
|
||||||
amount = d.get('debit', 0) - d.get('credit', 0)
|
amount = flt(d.get('debit', 0)) - flt(d.get('credit', 0))
|
||||||
|
|
||||||
formatted_amount = fmt_money(abs(amount), 2, d.account_currency)
|
formatted_amount = fmt_money(abs(amount), 2, d.account_currency)
|
||||||
d.amount = formatted_amount + " " + (_("Dr") if amount > 0 else _("Cr"))
|
d.amount = formatted_amount + " " + (_("Dr") if amount > 0 else _("Cr"))
|
||||||
@ -107,10 +107,10 @@ class BankReconciliation(Document):
|
|||||||
d.clearance_date = None
|
d.clearance_date = None
|
||||||
|
|
||||||
frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
|
frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
|
||||||
frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
|
frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
|
||||||
where name=%s""".format(d.payment_document),
|
where name=%s""".format(d.payment_document),
|
||||||
(d.clearance_date, nowdate(), d.payment_entry))
|
(d.clearance_date, nowdate(), d.payment_entry))
|
||||||
|
|
||||||
clearance_date_updated = True
|
clearance_date_updated = True
|
||||||
|
|
||||||
if clearance_date_updated:
|
if clearance_date_updated:
|
||||||
|
@ -14,8 +14,9 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
|
|||||||
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
|
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
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.controllers.taxes_and_totals import get_itemised_tax_breakup_data
|
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
|
||||||
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
class TestSalesInvoice(unittest.TestCase):
|
class TestSalesInvoice(unittest.TestCase):
|
||||||
def make(self):
|
def make(self):
|
||||||
@ -762,7 +763,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
set_perpetual_inventory(0)
|
set_perpetual_inventory(0)
|
||||||
|
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
|
||||||
def test_pos_si_without_payment(self):
|
def test_pos_si_without_payment(self):
|
||||||
set_perpetual_inventory()
|
set_perpetual_inventory()
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
@ -1514,6 +1515,56 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||||
accounts_settings.save()
|
accounts_settings.save()
|
||||||
|
|
||||||
|
def test_deferred_revenue(self):
|
||||||
|
deferred_account = create_account(account_name="Deferred Revenue",
|
||||||
|
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||||
|
|
||||||
|
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-10", do_not_submit=True)
|
||||||
|
si.items[0].enable_deferred_revenue = 1
|
||||||
|
si.items[0].service_start_date = "2019-01-10"
|
||||||
|
si.items[0].service_end_date = "2019-03-15"
|
||||||
|
si.items[0].deferred_revenue_account = deferred_account
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
|
||||||
|
convert_deferred_revenue_to_income(start_date="2019-01-01", end_date="2019-01-31")
|
||||||
|
|
||||||
|
expected_gle = [
|
||||||
|
[deferred_account, 33.85, 0.0, "2019-01-31"],
|
||||||
|
["Sales - _TC", 0.0, 33.85, "2019-01-31"]
|
||||||
|
]
|
||||||
|
|
||||||
|
self.check_gl_entries(si.name, expected_gle, "2019-01-10")
|
||||||
|
|
||||||
|
convert_deferred_revenue_to_income(start_date="2019-01-01", end_date="2019-03-31")
|
||||||
|
|
||||||
|
expected_gle = [
|
||||||
|
[deferred_account, 43.08, 0.0, "2019-02-28"],
|
||||||
|
["Sales - _TC", 0.0, 43.08, "2019-02-28"],
|
||||||
|
[deferred_account, 23.07, 0.0, "2019-03-15"],
|
||||||
|
["Sales - _TC", 0.0, 23.07, "2019-03-15"]
|
||||||
|
]
|
||||||
|
|
||||||
|
self.check_gl_entries(si.name, expected_gle, "2019-01-31")
|
||||||
|
|
||||||
|
def check_gl_entries(self, voucher_no, expected_gle, posting_date):
|
||||||
|
gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
|
||||||
|
from `tabGL Entry`
|
||||||
|
where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s
|
||||||
|
order by posting_date asc, account asc""", (voucher_no, 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.debit)
|
||||||
|
self.assertEqual(expected_gle[i][2], gle.credit)
|
||||||
|
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
|
||||||
|
|
||||||
def create_sales_invoice(**args):
|
def create_sales_invoice(**args):
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
@ -1611,4 +1662,4 @@ def get_outstanding_amount(against_voucher_type, against_voucher, account, party
|
|||||||
if against_voucher_type == 'Purchase Invoice':
|
if against_voucher_type == 'Purchase Invoice':
|
||||||
bal = bal * -1
|
bal = bal * -1
|
||||||
|
|
||||||
return bal
|
return bal
|
@ -146,6 +146,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
|||||||
|
|
||||||
row += [partywise_advance_amount.get(party, 0)]
|
row += [partywise_advance_amount.get(party, 0)]
|
||||||
|
|
||||||
|
paid_amt = 0
|
||||||
if party_dict.paid_amt > 0:
|
if party_dict.paid_amt > 0:
|
||||||
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
|
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ frappe.ui.form.on('Healthcare Service Unit Type', {
|
|||||||
var disable = function(frm){
|
var disable = function(frm){
|
||||||
var doc = frm.doc;
|
var doc = frm.doc;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable",
|
method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable",
|
||||||
args: {status: 1, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable},
|
args: {status: 1, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable},
|
||||||
callback: function(){
|
callback: function(){
|
||||||
cur_frm.reload_doc();
|
cur_frm.reload_doc();
|
||||||
@ -60,7 +60,7 @@ var disable = function(frm){
|
|||||||
var enable = function(frm){
|
var enable = function(frm){
|
||||||
var doc = frm.doc;
|
var doc = frm.doc;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable",
|
method: "erpnext.healthcare.doctype.healthcare_service_unit_type.healthcare_service_unit_type.disable_enable",
|
||||||
args: {status: 0, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable},
|
args: {status: 0, doc_name: doc.name, item: doc.item, is_billable: doc.is_billable},
|
||||||
callback: function(){
|
callback: function(){
|
||||||
cur_frm.reload_doc();
|
cur_frm.reload_doc();
|
||||||
|
@ -111,7 +111,7 @@ def change_item_code(item, item_code, doc_name):
|
|||||||
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code)
|
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def disable_enable(status, doc_name, item, is_billable):
|
def disable_enable(status, doc_name, item=None, is_billable=None):
|
||||||
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "disabled", status)
|
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "disabled", status)
|
||||||
if(is_billable == 1):
|
if(is_billable == 1):
|
||||||
frappe.db.set_value("Item", item, "disabled", status)
|
frappe.db.set_value("Item", item, "disabled", status)
|
||||||
|
@ -441,7 +441,7 @@ class SalarySlip(TransactionBase):
|
|||||||
def calculate_net_pay(self):
|
def calculate_net_pay(self):
|
||||||
if self.salary_structure:
|
if self.salary_structure:
|
||||||
self.calculate_component_amounts()
|
self.calculate_component_amounts()
|
||||||
|
|
||||||
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
||||||
precision = frappe.defaults.get_global_default("currency_precision")
|
precision = frappe.defaults.get_global_default("currency_precision")
|
||||||
self.total_deduction = 0
|
self.total_deduction = 0
|
||||||
@ -452,10 +452,10 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
self.set_loan_repayment()
|
self.set_loan_repayment()
|
||||||
|
|
||||||
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
|
self.net_pay = (flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))) * flt(self.payment_days / self.total_working_days)
|
||||||
self.rounded_total = rounded(self.net_pay,
|
self.rounded_total = rounded(self.net_pay,
|
||||||
self.precision("net_pay") if disable_rounded_total else 0)
|
self.precision("net_pay") if disable_rounded_total else 0)
|
||||||
|
|
||||||
if self.net_pay < 0:
|
if self.net_pay < 0:
|
||||||
frappe.throw(_("Net Pay cannnot be negative"))
|
frappe.throw(_("Net Pay cannnot be negative"))
|
||||||
|
|
||||||
|
@ -618,7 +618,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
|
|||||||
is_stock_item=is_stock_item,
|
is_stock_item=is_stock_item,
|
||||||
qty_field="stock_qty",
|
qty_field="stock_qty",
|
||||||
select_columns = """, bom_item.source_warehouse, bom_item.operation, bom_item.include_item_in_manufacturing,
|
select_columns = """, bom_item.source_warehouse, bom_item.operation, bom_item.include_item_in_manufacturing,
|
||||||
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s ) as idx""")
|
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""")
|
||||||
|
|
||||||
items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
|
items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
|
||||||
elif fetch_scrap_items:
|
elif fetch_scrap_items:
|
||||||
|
@ -159,6 +159,13 @@ class Task(NestedSet):
|
|||||||
|
|
||||||
self.update_nsm_model()
|
self.update_nsm_model()
|
||||||
|
|
||||||
|
def update_status(self):
|
||||||
|
if self.status not in ('Cancelled', 'Closed') and self.exp_end_date:
|
||||||
|
from datetime import datetime
|
||||||
|
if self.exp_end_date < datetime.now().date():
|
||||||
|
self.db_set('status', 'Overdue')
|
||||||
|
self.update_project()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def check_if_child_exists(name):
|
def check_if_child_exists(name):
|
||||||
child_tasks = frappe.get_all("Task", filters={"parent_task": name})
|
child_tasks = frappe.get_all("Task", filters={"parent_task": name})
|
||||||
@ -186,10 +193,9 @@ def set_multiple_status(names, status):
|
|||||||
task.save()
|
task.save()
|
||||||
|
|
||||||
def set_tasks_as_overdue():
|
def set_tasks_as_overdue():
|
||||||
frappe.db.sql("""update tabTask set `status`='Overdue'
|
tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Closed']]})
|
||||||
where exp_end_date is not null
|
for task in tasks:
|
||||||
and exp_end_date < CURDATE()
|
frappe.get_doc("Task", task.name).update_status()
|
||||||
and `status` not in ('Closed', 'Cancelled')""")
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_children(doctype, parent, task=None, project=None, is_root=False):
|
def get_children(doctype, parent, task=None, project=None, is_root=False):
|
||||||
|
@ -117,4 +117,4 @@ def create_task(subject, start=None, end=None, depends_on=None, project=None, sa
|
|||||||
if save:
|
if save:
|
||||||
task.save()
|
task.save()
|
||||||
|
|
||||||
return task
|
return task
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"brand": "_Test Brand",
|
"brand": "_Test Brand",
|
||||||
"doctype": "Brand"
|
"doctype": "Brand"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -758,6 +758,9 @@ class Item(WebsiteGenerator):
|
|||||||
d.conversion_factor = value
|
d.conversion_factor = value
|
||||||
|
|
||||||
def validate_attributes(self):
|
def validate_attributes(self):
|
||||||
|
if not self.variant_based_on:
|
||||||
|
self.variant_based_on = 'Item Attribute'
|
||||||
|
|
||||||
if (self.has_variants or self.variant_of) and self.variant_based_on == 'Item Attribute':
|
if (self.has_variants or self.variant_of) and self.variant_based_on == 'Item Attribute':
|
||||||
attributes = []
|
attributes = []
|
||||||
if not self.attributes:
|
if not self.attributes:
|
||||||
@ -780,7 +783,7 @@ class Item(WebsiteGenerator):
|
|||||||
variant = get_variant(self.variant_of, args, self.name)
|
variant = get_variant(self.variant_of, args, self.name)
|
||||||
if variant:
|
if variant:
|
||||||
frappe.throw(_("Item variant {0} exists with same attributes")
|
frappe.throw(_("Item variant {0} exists with same attributes")
|
||||||
.format(variant), ItemVariantExistsError)
|
.format(variant), ItemVariantExistsError)
|
||||||
|
|
||||||
validate_item_variant_attributes(self, args)
|
validate_item_variant_attributes(self, args)
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ from erpnext.stock.get_item_details import get_item_details
|
|||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
|
||||||
test_ignore = ["BOM"]
|
test_ignore = ["BOM"]
|
||||||
test_dependencies = ["Warehouse", "Item Group"]
|
test_dependencies = ["Warehouse", "Item Group", "Brand"]
|
||||||
|
|
||||||
def make_item(item_code, properties=None):
|
def make_item(item_code, properties=None):
|
||||||
if frappe.db.exists("Item", item_code):
|
if frappe.db.exists("Item", item_code):
|
||||||
@ -393,3 +393,6 @@ def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None,
|
|||||||
"company": "_Test Company"
|
"company": "_Test Company"
|
||||||
})
|
})
|
||||||
item.save()
|
item.save()
|
||||||
|
else:
|
||||||
|
item = frappe.get_doc("Item", item_code)
|
||||||
|
return item
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<p class='text-muted'>Please update your GSTIN for us to issue correct tax invoice</p>
|
<p class='text-muted'>Please update your GSTIN for us to issue correct tax invoice</p>
|
||||||
<form method='GET' action='/regional/india/update-gstin.html'>
|
<form method='GET' action='/regional/india/update-gstin.html'>
|
||||||
<input type='hidden' value='{{ party.name }}' name='party'>
|
<input type='hidden' value='{{ party.name }}' name='party'>
|
||||||
{% for address in party.__onload.addr_list %}
|
{% for address in party.get_onload('addr_list') %}
|
||||||
<div class='bordered' style='max-width: 300px; margin-bottom: 15px;'>
|
<div class='bordered' style='max-width: 300px; margin-bottom: 15px;'>
|
||||||
{{ address.display }}
|
{{ address.display }}
|
||||||
<p><input type='text' class='form-control'
|
<p><input type='text' class='form-control'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user