Merge branch 'staging-fixes' into staging
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
new file mode 100644
index 0000000..61fc211
--- /dev/null
+++ b/erpnext/accounts/deferred_revenue.py
@@ -0,0 +1,179 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe import _
+from frappe.utils import date_diff, add_months, today, getdate, add_days, flt
+from erpnext.accounts.utils import get_account_currency
+from erpnext.accounts.general_ledger import make_gl_entries
+
+def validate_service_stop_date(doc):
+ ''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
+
+ enable_check = "enable_deferred_revenue" \
+ if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
+
+ old_stop_dates = {}
+ old_doc = frappe.db.get_all("{0} Item".format(doc.doctype),
+ {"parent": doc.name}, ["name", "service_stop_date"])
+
+ for d in old_doc:
+ old_stop_dates[d.name] = d.service_stop_date or ""
+
+ for item in doc.items:
+ if not item.get(enable_check): continue
+
+ if item.service_stop_date:
+ if date_diff(item.service_stop_date, item.service_start_date) < 0:
+ frappe.throw(_("Service Stop Date cannot be before Service Start Date"))
+
+ if date_diff(item.service_stop_date, item.service_end_date) > 0:
+ frappe.throw(_("Service Stop Date cannot be after Service End Date"))
+
+ if old_stop_dates and old_stop_dates[item.name] and item.service_stop_date!=old_stop_dates[item.name]:
+ 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):
+ # check for the purchase invoice for which GL entries has to be done
+ invoices = frappe.db.sql_list('''
+ select parent from `tabPurchase Invoice Item` where service_start_date<=%s and service_end_date>=%s
+ and enable_deferred_expense = 1 and docstatus = 1
+ ''', (end_date or today(), start_date or add_months(today(), -1)))
+
+ # For each invoice, book deferred expense
+ for invoice in invoices:
+ doc = frappe.get_doc("Purchase Invoice", invoice)
+ book_deferred_income_or_expense(doc, start_date, end_date)
+
+def convert_deferred_revenue_to_income(start_date=None, end_date=None):
+ # check for the sales invoice for which GL entries has to be done
+ invoices = frappe.db.sql_list('''
+ select parent from `tabSales Invoice Item` where service_start_date<=%s and service_end_date>=%s
+ and enable_deferred_revenue = 1 and docstatus = 1
+ ''', (end_date or today(), start_date or add_months(today(), -1)))
+
+ # For each invoice, book deferred revenue
+ for invoice in invoices:
+ doc = frappe.get_doc("Sales Invoice", invoice)
+ book_deferred_income_or_expense(doc, start_date, end_date)
+
+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"
+ 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('''
+ select name, posting_date from `tabGL Entry` where company=%s and account=%s and
+ voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
+ order by posting_date desc limit 1
+ ''', (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:
+ booking_start_date = item.service_start_date
+ elif prev_gl_entry:
+ booking_start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
+ skip = True if booking_start_date > booking_end_date else False
+
+ return last_gl_entry, booking_start_date, booking_end_date, skip
+
+def calculate_amount_and_base_amount(doc, item, last_gl_entry, total_days, total_booking_days):
+ account_currency = get_account_currency(item.expense_account)
+
+ 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"
+
+ amount, base_amount = 0, 0
+ if not last_gl_entry:
+ base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
+ if account_currency==doc.company_currency:
+ amount = base_amount
+ else:
+ amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
+ else:
+ gl_entries_details = frappe.db.sql('''
+ select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
+ from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
+ group by voucher_detail_no
+ '''.format(total_credit_debit, total_credit_debit_currency),
+ (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
+
+ already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
+ base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
+ if account_currency==doc.company_currency:
+ amount = base_amount
+ else:
+ already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
+ amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
+
+ return amount, base_amount
+
+def book_deferred_income_or_expense(doc, 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
+ # start_date: 1st of the last month or the start date
+ # end_date: end_date or today-1
+
+ gl_entries = []
+ for item in doc.get('items'):
+ 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)
+ total_booking_days = date_diff(booking_end_date, booking_start_date) + 1
+
+ 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":
+ against, project = doc.customer, doc.project
+ credit_account, debit_account = item.income_account, item.deferred_revenue_account
+ else:
+ against, project = doc.supplier, item.project
+ credit_account, debit_account = item.deferred_expense_account, item.expense_account
+
+ # GL Entry for crediting the amount in the deferred expense
+ gl_entries.append(
+ doc.get_gl_dict({
+ "account": credit_account,
+ "against": against,
+ "credit": base_amount,
+ "credit_in_account_currency": amount,
+ "cost_center": item.cost_center,
+ 'posting_date': booking_end_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": item.cost_center,
+ "voucher_detail_no": item.name,
+ 'posting_date': booking_end_date,
+ 'project': project
+ }, account_currency)
+ )
+
+ if gl_entries:
+ make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 5154b90..c37fd15 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -496,6 +496,16 @@
'Purchase Invoice': 'Debit Note',
'Payment Entry': 'Payment'
}
+
+ frm.fields_dict['items'].grid.get_field('deferred_expense_account').get_query = function(doc) {
+ return {
+ filters: {
+ 'root_type': 'Asset',
+ 'company': doc.company,
+ "is_group": 0
+ }
+ }
+ }
},
onload: function(frm) {
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 273a6e4..11e48db 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -22,6 +22,7 @@
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\
unlink_inter_company_invoice
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
+from erpnext.accounts.deferred_revenue import validate_service_stop_date
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -74,6 +75,9 @@
if (self.is_paid == 1):
self.validate_cash()
+ # validate service stop date to lie in between start and end date
+ validate_service_stop_date(self)
+
if self._action=="submit" and self.update_stock:
self.make_batches('warehouse')
@@ -448,7 +452,7 @@
elif not item.is_fixed_asset:
gl_entries.append(
self.get_gl_dict({
- "account": item.expense_account,
+ "account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account,
"against": self.supplier,
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
"debit_in_account_currency": (flt(item.base_net_amount,
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index eaafdc0..936be10 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -1680,8 +1680,229 @@
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "collapsible": 1,
+ "columns": 0,
+ "fieldname": "deferred_expense_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Deferred Expense",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "deferred_expense_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Deferred Expense Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "service_stop_date",
+ "fieldtype": "Date",
+ "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": "Service Stop Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "enable_deferred_expense",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Enable Deferred Expense",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_58",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "service_start_date",
+ "fieldtype": "Date",
+ "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": "Service Start Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "service_end_date",
+ "fieldtype": "Date",
+ "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": "Service End Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "reference",
"fieldtype": "Section Break",
"hidden": 0,
@@ -2323,7 +2544,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2018-08-06 05:18:38.205356",
+ "modified": "2018-09-04 10:11:28.246395",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
@@ -2336,5 +2557,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
- "track_seen": 0
-}
+ "track_seen": 0,
+ "track_views": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 216d3e6..ca74d4d 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -786,39 +786,6 @@
}
})
-frappe.ui.form.on('Sales Invoice Item', {
- service_stop_date: function(frm, cdt, cdn) {
- var child = locals[cdt][cdn];
-
- if(child.service_stop_date) {
- let start_date = Date.parse(child.service_start_date);
- let end_date = Date.parse(child.service_end_date);
- let stop_date = Date.parse(child.service_stop_date);
-
- if(stop_date < start_date) {
- frappe.model.set_value(cdt, cdn, "service_stop_date", "");
- frappe.throw(__("Service Stop Date cannot be before Service Start Date"));
- } else if (stop_date > end_date) {
- frappe.model.set_value(cdt, cdn, "service_stop_date", "");
- frappe.throw(__("Service Stop Date cannot be after Service End Date"));
- }
- }
- },
- service_start_date: function(frm, cdt, cdn) {
- var child = locals[cdt][cdn];
-
- if(child.service_start_date) {
- frappe.call({
- "method": "erpnext.stock.get_item_details.calculate_service_end_date",
- args: {"args": child},
- callback: function(r) {
- frappe.model.set_value(cdt, cdn, "service_end_date", r.message.service_end_date);
- }
- })
- }
- }
-})
-
var calculate_total_billing_amount = function(frm) {
var doc = frm.doc;
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 4708edf..d085d2a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -23,6 +23,7 @@
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
+from erpnext.accounts.deferred_revenue import validate_service_stop_date
from erpnext.healthcare.utils import manage_invoice_submit_cancel
@@ -92,7 +93,7 @@
self.validate_delivery_note()
# validate service stop date to lie in between start and end date
- self.validate_service_stop_date()
+ validate_service_stop_date(self)
if not self.is_opening:
self.is_opening = 'No'
@@ -558,24 +559,6 @@
if frappe.db.get_value("Sales Order Item", item.so_detail, "delivered_by_supplier"):
frappe.throw(_("Could not update stock, invoice contains drop shipping item."))
- def validate_service_stop_date(self):
- old_doc = frappe.db.get_all("Sales Invoice Item", {"parent": self.name}, ["name", "service_stop_date"])
- old_stop_dates = {}
- for d in old_doc:
- old_stop_dates[d.name] = d.service_stop_date or ""
-
- for item in self.items:
- if item.enable_deferred_revenue:
- if item.service_stop_date:
- if date_diff(item.service_stop_date, item.service_start_date) < 0:
- frappe.throw(_("Service Stop Date cannot be before Service Start Date"))
-
- if date_diff(item.service_stop_date, item.service_end_date) > 0:
- frappe.throw(_("Service Stop Date cannot be after Service End Date"))
-
- if old_stop_dates and old_stop_dates[item.name] and item.service_stop_date!=old_stop_dates[item.name]:
- frappe.throw(_("Cannot change Service Stop Date for item in row {0}".format(item.idx)))
-
def update_current_stock(self):
for d in self.get('items'):
if d.item_code and d.warehouse:
@@ -1059,9 +1042,11 @@
# valdite the redemption and then delete the loyalty points earned on cancel of the invoice
def delete_loyalty_point_entry(self):
lp_entry = frappe.db.sql("select name from `tabLoyalty Point Entry` where sales_invoice=%s",
- (self.name), as_dict=1)[0]
+ (self.name), as_dict=1)
+
+ if not lp_entry: return
against_lp_entry = frappe.db.sql('''select name, sales_invoice from `tabLoyalty Point Entry`
- where redeem_against=%s''', (lp_entry.name), as_dict=1)
+ where redeem_against=%s''', (lp_entry[0].name), as_dict=1)
if against_lp_entry:
invoice_list = ", ".join([d.sales_invoice for d in against_lp_entry])
frappe.throw(_('''Sales Invoice can't be cancelled since the Loyalty Points earned has been redeemed.
@@ -1119,92 +1104,6 @@
if points_to_redeem < 1: # since points_to_redeem is integer
break
- def book_income_for_deferred_revenue(self, start_date=None, end_date=None):
- # book the 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
-
- gl_entries = []
- for item in self.get('items'):
- last_gl_entry = 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):
- continue
- 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
-
- if item.service_start_date < booking_start_date:
- prev_gl_entry = frappe.db.sql('''
- select name, posting_date from `tabGL Entry` where company=%s and account=%s and
- voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
- order by posting_date desc limit 1
- ''', (self.company, item.deferred_revenue_account, "Sales Invoice", self.name, item.name), as_dict=True)
-
- if not prev_gl_entry:
- booking_start_date = item.service_start_date
- else:
- booking_start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
-
- total_days = date_diff(item.service_end_date, item.service_start_date)
- total_booking_days = date_diff(booking_end_date, booking_start_date) + 1
-
- account_currency = get_account_currency(item.income_account)
- if not last_gl_entry:
- base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
- if account_currency==self.company_currency:
- amount = base_amount
- else:
- amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
- else:
- gl_entries_details = frappe.db.sql('''
- select sum(debit) as total_debit, sum(debit_in_account_currency) as total_debit_in_account_currency, voucher_detail_no
- from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
- group by voucher_detail_no
- ''', (self.company, item.deferred_revenue_account, "Sales Invoice", self.name, item.name), as_dict=True)
- already_booked_amount = gl_entries_details[0].total_debit if gl_entries_details else 0
- base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
- if account_currency==self.company_currency:
- amount = base_amount
- else:
- already_booked_amount_in_account_currency = gl_entries_details[0].total_debit_in_account_currency if gl_entries_details else 0
- amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
-
- # GL Entry for crediting the amount in the income
- gl_entries.append(
- self.get_gl_dict({
- "account": item.income_account,
- "against": self.customer,
- "credit": base_amount,
- "credit_in_account_currency": amount,
- "cost_center": item.cost_center,
- 'posting_date': booking_end_date
- }, account_currency)
- )
- # GL Entry to debit the amount from the deferred account
- gl_entries.append(
- self.get_gl_dict({
- "account": item.deferred_revenue_account,
- "against": self.customer,
- "debit": base_amount,
- "debit_in_account_currency": amount,
- "cost_center": item.cost_center,
- "voucher_detail_no": item.name,
- 'posting_date': booking_end_date
- }, account_currency)
- )
-
- if gl_entries:
- from erpnext.accounts.general_ledger import make_gl_entries
- make_gl_entries(gl_entries, cancel=(self.docstatus == 2), merge_entries=True)
-
# Healthcare
def set_healthcare_services(self, checked_values):
self.set("items", [])
@@ -1243,19 +1142,6 @@
self.set_missing_values(for_validate = True)
-def booked_deferred_revenue(start_date=None, end_date=None):
- # check for the sales invoice for which GL entries has to be done
- invoices = frappe.db.sql_list('''
- select parent from `tabSales Invoice Item` where service_start_date<=%s and service_end_date>=%s
- and enable_deferred_revenue = 1 and docstatus = 1
- ''', (end_date or today(), start_date or add_months(today(), -1)))
-
- # ToDo also find the list on the basic of the GL entry, and make another list
- for invoice in invoices:
- doc = frappe.get_doc("Sales Invoice", invoice)
- doc.book_income_for_deferred_revenue(start_date, end_date)
-
-
def validate_inter_company_party(doctype, party, company, inter_company_invoice_reference):
if doctype == "Sales Invoice":
partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer"
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d944874..22c9257 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -12,7 +12,7 @@
source_link = "https://github.com/frappe/erpnext"
develop_version = '11.x.x-develop'
-staging_version = '11.0.1'
+staging_version = '11.0.2'
error_report_email = "support@erpnext.com"
@@ -250,7 +250,8 @@
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"
],
"monthly": [
- "erpnext.accounts.doctype.sales_invoice.sales_invoice.booked_deferred_revenue",
+ "erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income",
+ "erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense",
"erpnext.hr.utils.allocate_earned_leaves"
]
}
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index cd4a7b2..3b6f169 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -796,6 +796,38 @@
this.apply_pricing_rule(frappe.get_doc(cdt, cdn), true);
},
+ service_stop_date: function(frm, cdt, cdn) {
+ var child = locals[cdt][cdn];
+
+ if(child.service_stop_date) {
+ let start_date = Date.parse(child.service_start_date);
+ let end_date = Date.parse(child.service_end_date);
+ let stop_date = Date.parse(child.service_stop_date);
+
+ if(stop_date < start_date) {
+ frappe.model.set_value(cdt, cdn, "service_stop_date", "");
+ frappe.throw(__("Service Stop Date cannot be before Service Start Date"));
+ } else if (stop_date > end_date) {
+ frappe.model.set_value(cdt, cdn, "service_stop_date", "");
+ frappe.throw(__("Service Stop Date cannot be after Service End Date"));
+ }
+ }
+ },
+
+ service_start_date: function(frm, cdt, cdn) {
+ var child = locals[cdt][cdn];
+
+ if(child.service_start_date) {
+ frappe.call({
+ "method": "erpnext.stock.get_item_details.calculate_service_end_date",
+ args: {"args": child},
+ callback: function(r) {
+ frappe.model.set_value(cdt, cdn, "service_end_date", r.message.service_end_date);
+ }
+ })
+ }
+ },
+
calculate_net_weight: function(){
/* Calculate Total Net Weight then further applied shipping rule to calculate shipping charges.*/
var me = this;
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 9a245e2..9377cad 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -837,7 +837,7 @@
"label": "Create Chart Of Accounts Based On",
"length": 0,
"no_copy": 0,
- "options": "\nStandard Template\nExisting Company",
+ "options": "\nStandard Template\nExisting Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -871,7 +871,7 @@
"label": "Chart Of Accounts Template",
"length": 0,
"no_copy": 1,
- "options": "",
+ "options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1158,6 +1158,39 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "round_off_cost_center",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Round Off Cost Center",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cost Center",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "write_off_account",
"fieldtype": "Link",
"hidden": 0,
@@ -1458,7 +1491,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
- "fieldname": "default_payroll_payable_account",
+ "fieldname": "default_deferred_expense_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
@@ -1467,7 +1500,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Default Payroll Payable Account",
+ "label": "Default Deferred Expense Account",
"length": 0,
"no_copy": 1,
"options": "Account",
@@ -1492,7 +1525,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.__islocal",
- "fieldname": "default_expense_claim_payable_account",
+ "fieldname": "default_payroll_payable_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
@@ -1501,7 +1534,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Default Expense Claim Payable Account",
+ "label": "Default Payroll Payable Account",
"length": 0,
"no_copy": 1,
"options": "Account",
@@ -1525,19 +1558,20 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "round_off_cost_center",
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "default_expense_claim_payable_account",
"fieldtype": "Link",
"hidden": 0,
- "ignore_user_permissions": 0,
+ "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Round Off Cost Center",
+ "label": "Default Expense Claim Payable Account",
"length": 0,
- "no_copy": 0,
- "options": "Cost Center",
+ "no_copy": 1,
+ "options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -2836,8 +2870,8 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2018-09-07 16:03:30.716918",
- "modified_by": "cave@aperture.com",
+ "modified": "2018-09-13 10:00:47.915706",
+ "modified_by": "Administrator",
"module": "Setup",
"name": "Company",
"owner": "Administrator",
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index e78e8d5..7d1bb6d 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -2690,18 +2690,20 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "enable_deferred_revenue",
- "fieldtype": "Check",
+ "depends_on": "enable_deferred_revenue",
+ "fieldname": "deferred_revenue_account",
+ "fieldtype": "Link",
"hidden": 0,
- "ignore_user_permissions": 0,
+ "ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Enable Deferred Revenue",
+ "label": "Deferred Revenue Account",
"length": 0,
"no_copy": 0,
+ "options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -2722,20 +2724,18 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "enable_deferred_revenue",
- "fieldname": "deferred_revenue_account",
- "fieldtype": "Link",
+ "fieldname": "enable_deferred_revenue",
+ "fieldtype": "Check",
"hidden": 0,
- "ignore_user_permissions": 1,
+ "ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Deferred Revenue Account",
+ "label": "Enable Deferred Revenue",
"length": 0,
"no_copy": 0,
- "options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -2820,6 +2820,168 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
+ "fieldname": "deferred_expense_section",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Deferred Expense",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "deferred_expense_account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 1,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Deferred Expense Account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "enable_deferred_expense",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Enable Deferred Expense",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_88",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "no_of_months_exp",
+ "fieldtype": "Int",
+ "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": "No of Months",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "translatable": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 1,
+ "columns": 0,
"fieldname": "customer_details",
"fieldtype": "Section Break",
"hidden": 0,
@@ -3951,7 +4113,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
- "modified": "2018-09-19 16:17:41.039657",
+ "modified": "2018-09-20 11:14:21.031369",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index be138d7..d90db56 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -310,14 +310,18 @@
if not item:
item = frappe.get_cached_doc("Item", args.item_code)
+ enable_deferred = "enable_deferred_revenue" if args.doctype=="Sales Invoice" else "enable_deferred_expense"
+ no_of_months = "no_of_months" if args.doctype=="Sales Invoice" else "no_of_months_exp"
+ account = "deferred_revenue_account" if args.doctype=="Sales Invoice" else "deferred_expense_account"
+
service_start_date = args.service_start_date if args.service_start_date else args.transaction_date
- service_end_date = add_months(service_start_date, item.no_of_months)
+ service_end_date = add_months(service_start_date, item.get(no_of_months))
deferred_detail = {
- "enable_deferred_revenue": item.enable_deferred_revenue,
- "deferred_revenue_account": get_default_deferred_revenue_account(args, item),
"service_start_date": service_start_date,
"service_end_date": service_end_date
}
+ deferred_detail[enable_deferred] = item.get(enable_deferred)
+ deferred_detail[account] = get_default_deferred_account(args, item, fieldname=account)
return deferred_detail
@@ -331,11 +335,11 @@
or item_group.get("expense_account")
or args.expense_account)
-def get_default_deferred_revenue_account(args, item):
+def get_default_deferred_account(args, item, fieldname=None):
if item.enable_deferred_revenue:
- return (item.deferred_revenue_account
- or args.deferred_revenue_account
- or frappe.get_cached_value('Company', args.company, "default_deferred_revenue_account"))
+ return (item.get(fieldname)
+ or args.get(fieldname)
+ or frappe.get_cached_value('Company', args.company, "default_"+fieldname))
else:
return None