diff --git a/erpnext/accounts/accounts_dashboard/accounts/accounts.json b/erpnext/accounts/accounts_dashboard/accounts/accounts.json new file mode 100644 index 0000000000..2fab50e917 --- /dev/null +++ b/erpnext/accounts/accounts_dashboard/accounts/accounts.json @@ -0,0 +1,58 @@ +{ + "cards": [ + { + "card": "Total Outgoing Bills" + }, + { + "card": "Total Incoming Bills" + }, + { + "card": "Total Incoming Payment" + }, + { + "card": "Total Outgoing Payment" + } + ], + "charts": [ + { + "chart": "Profit and Loss", + "width": "Full" + }, + { + "chart": "Incoming Bills (Purchase Invoice)", + "width": "Half" + }, + { + "chart": "Outgoing Bills (Sales Invoice)", + "width": "Half" + }, + { + "chart": "Accounts Receivable Ageing", + "width": "Half" + }, + { + "chart": "Accounts Payable Ageing", + "width": "Half" + }, + { + "chart": "Budget Variance", + "width": "Full" + }, + { + "chart": "Bank Balance", + "width": "Full" + } + ], + "creation": "2020-07-17 11:25:34.796608", + "dashboard_name": "Accounts", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2020-07-22 13:07:34.540574", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Accounts", + "owner": "Administrator" +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_chart/accounts_payable_ageing/accounts_payable_ageing.json b/erpnext/accounts/dashboard_chart/accounts_payable_ageing/accounts_payable_ageing.json new file mode 100644 index 0000000000..fb5ee64545 --- /dev/null +++ b/erpnext/accounts/dashboard_chart/accounts_payable_ageing/accounts_payable_ageing.json @@ -0,0 +1,23 @@ +{ + "chart_name": "Accounts Payable Ageing", + "chart_type": "Report", + "creation": "2020-07-17 11:25:34.564015", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"report_date\":\"frappe.datetime.now_date()\"}", + "filters_json": "{\"ageing_based_on\":\"Due Date\",\"range1\":30,\"range2\":60,\"range3\":90,\"range4\":120,\"group_by_party\":0,\"based_on_payment_terms\":0}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 12:29:33.584419", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Accounts Payable Ageing", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Accounts Payable", + "timeseries": 0, + "type": "Donut", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_chart/accounts_receivable_ageing/accounts_receivable_ageing.json b/erpnext/accounts/dashboard_chart/accounts_receivable_ageing/accounts_receivable_ageing.json new file mode 100644 index 0000000000..48ec781f68 --- /dev/null +++ b/erpnext/accounts/dashboard_chart/accounts_receivable_ageing/accounts_receivable_ageing.json @@ -0,0 +1,23 @@ +{ + "chart_name": "Accounts Receivable Ageing", + "chart_type": "Report", + "creation": "2020-07-17 11:25:34.535388", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"report_date\":\"frappe.datetime.now_date()\"}", + "filters_json": "{\"ageing_based_on\":\"Due Date\",\"range1\":30,\"range2\":60,\"range3\":90,\"range4\":120,\"group_by_party\":0,\"based_on_payment_terms\":0,\"show_future_payments\":0,\"show_delivery_notes\":0,\"show_sales_person\":0}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 12:28:42.743551", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Accounts Receivable Ageing", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Accounts Receivable", + "timeseries": 0, + "type": "Donut", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_chart/bank_balance/bank_balance.json b/erpnext/accounts/dashboard_chart/bank_balance/bank_balance.json new file mode 100644 index 0000000000..6442c022c7 --- /dev/null +++ b/erpnext/accounts/dashboard_chart/bank_balance/bank_balance.json @@ -0,0 +1,26 @@ +{ + "chart_name": "Bank Balance", + "chart_type": "Custom", + "creation": "2020-07-17 11:25:34.620221", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"account\":\"locals[\\\":Company\\\"][frappe.defaults.get_user_default(\\\"Company\\\")][\\\"default_bank_account\\\"]\"}", + "filters_json": "{}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-22 12:19:59.879476", + "modified": "2020-07-22 12:21:48.780513", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Bank Balance", + "number_of_groups": 0, + "owner": "Administrator", + "source": "Account Balance Timeline", + "time_interval": "Quarterly", + "timeseries": 0, + "timespan": "Last Year", + "type": "Line", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_chart/budget_variance/budget_variance.json b/erpnext/accounts/dashboard_chart/budget_variance/budget_variance.json new file mode 100644 index 0000000000..8631d3dc2a --- /dev/null +++ b/erpnext/accounts/dashboard_chart/budget_variance/budget_variance.json @@ -0,0 +1,23 @@ +{ + "chart_name": "Budget Variance", + "chart_type": "Report", + "creation": "2020-07-17 11:25:34.593061", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}", + "filters_json": "{\"period\":\"Monthly\",\"budget_against\":\"Cost Center\",\"show_cumulative\":0}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 12:24:49.144210", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Budget Variance", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Budget Variance Report", + "timeseries": 0, + "type": "Bar", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_chart/incoming_bills_(purchase_invoice)/incoming_bills_(purchase_invoice).json b/erpnext/accounts/dashboard_chart/incoming_bills_(purchase_invoice)/incoming_bills_(purchase_invoice).json new file mode 100644 index 0000000000..55f0d77f72 --- /dev/null +++ b/erpnext/accounts/dashboard_chart/incoming_bills_(purchase_invoice)/incoming_bills_(purchase_invoice).json @@ -0,0 +1,29 @@ +{ + "based_on": "posting_date", + "chart_name": "Incoming Bills (Purchase Invoice)", + "chart_type": "Sum", + "color": "#a83333", + "creation": "2020-07-17 11:25:34.479703", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Purchase Invoice", + "dynamic_filters_json": "", + "filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",1]]", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-21 17:37:30.727306", + "modified": "2020-07-21 17:51:07.374917", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Incoming Bills (Purchase Invoice)", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "base_net_total", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_chart/outgoing_bills_(sales_invoice)/outgoing_bills_(sales_invoice).json b/erpnext/accounts/dashboard_chart/outgoing_bills_(sales_invoice)/outgoing_bills_(sales_invoice).json new file mode 100644 index 0000000000..45de667d58 --- /dev/null +++ b/erpnext/accounts/dashboard_chart/outgoing_bills_(sales_invoice)/outgoing_bills_(sales_invoice).json @@ -0,0 +1,28 @@ +{ + "based_on": "posting_date", + "chart_name": "Outgoing Bills (Sales Invoice)", + "chart_type": "Sum", + "color": "#7b933d", + "creation": "2020-07-17 11:25:34.507547", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Sales Invoice", + "filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",1]]", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "last_synced_on": "2020-07-21 17:37:31.574666", + "modified": "2020-07-21 17:52:03.970530", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Outgoing Bills (Sales Invoice)", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "base_net_total", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_chart/profit_and_loss/profit_and_loss.json b/erpnext/accounts/dashboard_chart/profit_and_loss/profit_and_loss.json new file mode 100644 index 0000000000..3fa995bbe1 --- /dev/null +++ b/erpnext/accounts/dashboard_chart/profit_and_loss/profit_and_loss.json @@ -0,0 +1,23 @@ +{ + "chart_name": "Profit and Loss", + "chart_type": "Report", + "creation": "2020-07-17 11:25:34.448572", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}", + "filters_json": "{\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"periodicity\":\"Yearly\",\"include_default_book_entries\":1}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 12:33:48.888943", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Profit and Loss", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Profit and Loss Statement", + "timeseries": 0, + "type": "Bar", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py deleted file mode 100644 index b2abffc79d..0000000000 --- a/erpnext/accounts/dashboard_fixtures.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -import json -from frappe.utils import nowdate, add_months, get_date_str -from frappe import _ -from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYearError - -def _get_fiscal_year(date=None): - try: - fiscal_year = get_fiscal_year(date=nowdate(), as_dict=True) - return fiscal_year - - except FiscalYearError: - #if no fiscal year for current date then get default fiscal year - try: - fiscal_year = get_fiscal_year(as_dict=True) - return fiscal_year - - except FiscalYearError: - #if still no fiscal year found then no accounting data created, return - return None - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -def get_data(): - - fiscal_year = _get_fiscal_year(nowdate()) - - if not fiscal_year: - return frappe._dict() - - return frappe._dict({ - "dashboards": get_dashboards(), - "charts": get_charts(fiscal_year), - "number_cards": get_number_cards(fiscal_year) - }) - -def get_dashboards(): - return [{ - "name": "Accounts", - "dashboard_name": "Accounts", - "doctype": "Dashboard", - "charts": [ - { "chart": "Profit and Loss" , "width": "Full"}, - { "chart": "Incoming Bills (Purchase Invoice)", "width": "Half"}, - { "chart": "Outgoing Bills (Sales Invoice)", "width": "Half"}, - { "chart": "Accounts Receivable Ageing", "width": "Half"}, - { "chart": "Accounts Payable Ageing", "width": "Half"}, - { "chart": "Budget Variance", "width": "Full"}, - { "chart": "Bank Balance", "width": "Full"} - ], - "cards": [ - {"card": "Total Outgoing Bills"}, - {"card": "Total Incoming Bills"}, - {"card": "Total Incoming Payment"}, - {"card": "Total Outgoing Payment"} - ] - }] - -def get_charts(fiscal_year): - company = frappe.get_doc("Company", get_company_for_dashboards()) - bank_account = company.default_bank_account or get_account_name("Bank", company=company.name) - default_cost_center = company.cost_center - - return [ - { - "doctype": "Dashboard Charts", - "name": "Profit and Loss", - "owner": "Administrator", - "report_name": "Profit and Loss Statement", - "filters_json": json.dumps({ - "company": company.name, - "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year.get('name'), - "to_fiscal_year": fiscal_year.get('name'), - "periodicity": "Monthly", - "include_default_book_entries": 1 - }), - "type": "Bar", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": _("Profit and Loss"), - "is_custom": 1, - "is_public": 1 - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "name": "Incoming Bills (Purchase Invoice)", - "chart_name": _("Incoming Bills (Purchase Invoice)"), - "timespan": "Last Year", - "color": "#a83333", - "value_based_on": "base_net_total", - "filters_json": json.dumps([["Purchase Invoice", "docstatus", "=", 1]]), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Invoice", - "type": "Bar", - "width": "Half", - "is_public": 1 - }, - { - "doctype": "Dashboard Chart", - "name": "Outgoing Bills (Sales Invoice)", - "time_interval": "Monthly", - "chart_name": _("Outgoing Bills (Sales Invoice)"), - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_net_total", - "filters_json": json.dumps([["Sales Invoice", "docstatus", "=", 1]]), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Sales Invoice", - "type": "Bar", - "width": "Half", - "is_public": 1 - }, - { - "doctype": "Dashboard Charts", - "name": "Accounts Receivable Ageing", - "owner": "Administrator", - "report_name": "Accounts Receivable", - "filters_json": json.dumps({ - "company": company.name, - "report_date": nowdate(), - "ageing_based_on": "Due Date", - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120 - }), - "type": "Donut", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": _("Accounts Receivable Ageing"), - "is_custom": 1, - "is_public": 1 - }, - { - "doctype": "Dashboard Charts", - "name": "Accounts Payable Ageing", - "owner": "Administrator", - "report_name": "Accounts Payable", - "filters_json": json.dumps({ - "company": company.name, - "report_date": nowdate(), - "ageing_based_on": "Due Date", - "range1": 30, - "range2": 60, - "range3": 90, - "range4": 120 - }), - "type": "Donut", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": _("Accounts Payable Ageing"), - "is_custom": 1, - "is_public": 1 - }, - { - "doctype": "Dashboard Charts", - "name": "Budget Variance", - "owner": "Administrator", - "report_name": "Budget Variance Report", - "filters_json": json.dumps({ - "company": company.name, - "from_fiscal_year": fiscal_year.get('name'), - "to_fiscal_year": fiscal_year.get('name'), - "period": "Monthly", - "budget_against": "Cost Center" - }), - "type": "Bar", - "timeseries": 0, - "chart_type": "Report", - "chart_name": _("Budget Variance"), - "is_custom": 1, - "is_public": 1 - }, - { - "doctype": "Dashboard Charts", - "name": "Bank Balance", - "time_interval": "Quarterly", - "chart_name": "Bank Balance", - "timespan": "Last Year", - "filters_json": json.dumps({ - "company": company.name, - "account": bank_account - }), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half", - "is_public": 1 - }, - ] - -def get_number_cards(fiscal_year): - - year_start_date = get_date_str(fiscal_year.get("year_start_date")) - year_end_date = get_date_str(fiscal_year.get("year_end_date")) - return [ - { - "doctype": "Number Card", - "document_type": "Payment Entry", - "name": "Total Incoming Payment", - "filters_json": json.dumps([ - ['Payment Entry', 'docstatus', '=', 1], - ['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]], - ['Payment Entry', 'payment_type', '=', 'Receive'] - ]), - "label": _("Total Incoming Payment"), - "function": "Sum", - "aggregate_function_based_on": "base_received_amount", - "is_public": 1, - "is_custom": 1, - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - }, - { - "doctype": "Number Card", - "document_type": "Payment Entry", - "name": "Total Outgoing Payment", - "filters_json": json.dumps([ - ['Payment Entry', 'docstatus', '=', 1], - ['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]], - ['Payment Entry', 'payment_type', '=', 'Pay'] - ]), - "label": _("Total Outgoing Payment"), - "function": "Sum", - "aggregate_function_based_on": "base_paid_amount", - "is_public": 1, - "is_custom": 1, - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - }, - { - "doctype": "Number Card", - "document_type": "Sales Invoice", - "name": "Total Outgoing Bills", - "filters_json": json.dumps([ - ['Sales Invoice', 'docstatus', '=', 1], - ['Sales Invoice', 'posting_date', 'between', [year_start_date, year_end_date]] - ]), - "label": _("Total Outgoing Bills"), - "function": "Sum", - "aggregate_function_based_on": "base_net_total", - "is_public": 1, - "is_custom": 1, - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - }, - { - "doctype": "Number Card", - "document_type": "Purchase Invoice", - "name": "Total Incoming Bills", - "filters_json": json.dumps([ - ['Purchase Invoice', 'docstatus', '=', 1], - ['Purchase Invoice', 'posting_date', 'between', [year_start_date, year_end_date]] - ]), - "label": _("Total Incoming Bills"), - "function": "Sum", - "aggregate_function_based_on": "base_net_total", - "is_public": 1, - "is_custom": 1, - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - } - ] diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 448011016e..d5ab1c1704 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -2,10 +2,11 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, cint, get_link_to_form +from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, get_first_day, cint, get_link_to_form, rounded from erpnext.accounts.utils import get_account_currency from frappe.email import sendmail_to_system_managers from frappe.utils.background_jobs import enqueue +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions def validate_service_stop_date(doc): ''' Validates service_stop_date for Purchase Invoice and Sales Invoice ''' @@ -109,6 +110,18 @@ def get_booking_dates(doc, item, posting_date=None): order by posting_date desc limit 1 ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + prev_gl_via_je = frappe.db.sql(''' + SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c + WHERE p.name = c.parent and p.company=%s and c.account=%s + and c.reference_type=%s and c.reference_name=%s + and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1 + ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + if prev_gl_via_je: + if (not prev_gl_entry) or (prev_gl_entry and + prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date): + prev_gl_entry = prev_gl_via_je + if prev_gl_entry: start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) else: @@ -130,14 +143,48 @@ def get_booking_dates(doc, item, posting_date=None): else: return None, None, None -def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): - if doc.doctype == "Sales Invoice": - total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency" - deferred_account = "deferred_revenue_account" - else: - total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency" - deferred_account = "deferred_expense_account" +def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency): + amount, base_amount = 0, 0 + if not last_gl_entry: + total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \ + (item.service_end_date.month - item.service_start_date.month) + 1 + + prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \ + / flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date))) + + actual_months = rounded(total_months * prorate_factor, 1) + + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount")) + + if base_amount + already_booked_amount > item.base_net_amount: + base_amount = item.base_net_amount - already_booked_amount + + if account_currency==doc.company_currency: + amount = base_amount + else: + amount = flt(item.net_amount/actual_months, item.precision("net_amount")) + if amount + already_booked_amount_in_account_currency > item.net_amount: + amount = item.net_amount - already_booked_amount_in_account_currency + + if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date): + partial_month = flt(date_diff(end_date, start_date)) \ + / flt(date_diff(get_last_day(end_date), get_first_day(start_date))) + + base_amount = rounded(partial_month, 1) * base_amount + amount = rounded(partial_month, 1) * amount + else: + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) + if account_currency==doc.company_currency: + amount = base_amount + else: + amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")) + + return amount, base_amount + +def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): amount, base_amount = 0, 0 if not last_gl_entry: base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount")) @@ -146,27 +193,55 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a else: amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount")) else: - gl_entries_details = frappe.db.sql(''' - select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no - from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s - group by voucher_detail_no - '''.format(total_credit_debit, total_credit_debit_currency), - (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) - already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0 + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) if account_currency==doc.company_currency: amount = base_amount else: - already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0 amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")) return amount, base_amount +def get_already_booked_amount(doc, item): + if doc.doctype == "Sales Invoice": + total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency" + deferred_account = "deferred_revenue_account" + else: + total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency" + deferred_account = "deferred_expense_account" + + gl_entries_details = frappe.db.sql(''' + select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no + from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s + group by voucher_detail_no + '''.format(total_credit_debit, total_credit_debit_currency), + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + journal_entry_details = frappe.db.sql(''' + SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no + FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and + p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s + and p.docstatus < 2 group by reference_detail_no + '''.format(total_credit_debit, total_credit_debit_currency), + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0 + already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0 + + if doc.currency == doc.company_currency: + already_booked_amount_in_account_currency = already_booked_amount + else: + already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0 + already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0 + + return already_booked_amount, already_booked_amount_in_account_currency + def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): enable_check = "enable_deferred_revenue" \ if doc.doctype=="Sales Invoice" else "enable_deferred_expense" - def _book_deferred_revenue_or_expense(item): + def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on): start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) if not (start_date and end_date): return @@ -181,23 +256,34 @@ 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_booking_days = date_diff(end_date, start_date) + 1 - amount, base_amount = calculate_amount(doc, item, last_gl_entry, - total_days, total_booking_days, account_currency) + if book_deferred_entries_based_on == 'Months': + amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry, + start_date, end_date, total_days, total_booking_days, account_currency) + else: + amount, base_amount = calculate_amount(doc, item, last_gl_entry, + total_days, total_booking_days, account_currency) - make_gl_entries(doc, credit_account, debit_account, against, - amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) + if via_journal_entry: + book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount, + base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry) + else: + make_gl_entries(doc, credit_account, debit_account, against, + amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) # Returned in case of any errors because it tries to submit the same record again and again in case of errors if frappe.flags.deferred_accounting_error: return if getdate(end_date) < getdate(posting_date) and not last_gl_entry: - _book_deferred_revenue_or_expense(item) + _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on) + via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry')) + submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries')) + book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on') for item in doc.get('items'): if item.get(enable_check): - _book_deferred_revenue_or_expense(item) + _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on) def process_deferred_accounting(posting_date=None): ''' Converts deferred income/expense into income/expense @@ -281,3 +367,83 @@ def send_mail(deferred_process): and submit manually after resolving errors """).format(get_link_to_form('Process Deferred Accounting', deferred_process)) sendmail_to_system_managers(title, content) + +def book_revenue_via_journal_entry(doc, credit_account, debit_account, against, + amount, base_amount, posting_date, project, account_currency, cost_center, item, + deferred_process=None, submit='No'): + + if amount == 0: return + + journal_entry = frappe.new_doc('Journal Entry') + journal_entry.posting_date = posting_date + journal_entry.company = doc.company + journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \ + else 'Deferred Expense' + + debit_entry = { + 'account': credit_account, + 'credit': base_amount, + 'credit_in_account_currency': amount, + 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier', + 'party': against, + 'account_currency': account_currency, + 'reference_name': doc.name, + 'reference_type': doc.doctype, + 'reference_detail_no': item.name, + 'cost_center': cost_center, + 'project': project, + } + + credit_entry = { + 'account': debit_account, + 'debit': base_amount, + 'debit_in_account_currency': amount, + 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier', + 'party': against, + 'account_currency': account_currency, + 'reference_name': doc.name, + 'reference_type': doc.doctype, + 'reference_detail_no': item.name, + 'cost_center': cost_center, + 'project': project, + } + + for dimension in get_accounting_dimensions(): + debit_entry.update({ + dimension: item.get(dimension) + }) + + credit_entry.update({ + dimension: item.get(dimension) + }) + + journal_entry.append('accounts', debit_entry) + journal_entry.append('accounts', credit_entry) + + try: + journal_entry.save() + + if submit: + journal_entry.submit() + except: + frappe.db.rollback() + traceback = frappe.get_traceback() + frappe.log_error(message=traceback) + + frappe.flags.deferred_accounting_error = True + +def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr): + + if doctype == 'Sales Invoice': + credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no}, + ['income_account', 'deferred_revenue_account']) + else: + credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no}, + ['deferred_expense_account', 'expense_account']) + + if dr_or_cr == 'Debit': + return debit_account + else: + return credit_account + + diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 31315e4c71..a2497838ee 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -147,10 +147,15 @@ "link_to": "Trial Balance", "type": "Report" }, + { + "label": "Point of Sale", + "link_to": "point-of-sale", + "type": "Page" + }, { "label": "Dashboard", "link_to": "Accounts", "type": "Dashboard" } ] -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index af252e6191..d2659d429b 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -34,11 +34,15 @@ { "fieldname": "properties", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -49,7 +53,9 @@ "no_copy": 1, "oldfieldname": "account_name", "oldfieldtype": "Data", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "account_number", @@ -57,13 +63,17 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Account Number", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_group", "fieldtype": "Check", - "label": "Is Group" + "label": "Is Group", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -75,7 +85,9 @@ "options": "Company", "read_only": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "root_type", @@ -83,7 +95,9 @@ "in_standard_filter": 1, "label": "Root Type", "options": "\nAsset\nLiability\nIncome\nExpense\nEquity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "report_type", @@ -91,24 +105,32 @@ "in_standard_filter": 1, "label": "Report Type", "options": "\nBalance Sheet\nProfit and Loss", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_group==0", "fieldname": "account_currency", "fieldtype": "Link", "label": "Currency", - "options": "Currency" + "options": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "inter_company_account", "fieldtype": "Check", - "label": "Inter Company Account" + "label": "Inter Company Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -120,7 +142,9 @@ "oldfieldtype": "Link", "options": "Account", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Setting Account Type helps in selecting this Account in transactions.", @@ -130,7 +154,9 @@ "label": "Account Type", "oldfieldname": "account_type", "oldfieldtype": "Select", - "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nTax\nTemporary" + "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which this tax is applied", @@ -138,7 +164,9 @@ "fieldtype": "Float", "label": "Rate", "oldfieldname": "tax_rate", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "description": "If the account is frozen, entries are allowed to restricted users.", @@ -147,13 +175,17 @@ "label": "Frozen", "oldfieldname": "freeze_account", "oldfieldtype": "Select", - "options": "No\nYes" + "options": "No\nYes", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "balance_must_be", "fieldtype": "Select", "label": "Balance must be", - "options": "\nDebit\nCredit" + "options": "\nDebit\nCredit", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "lft", @@ -162,7 +194,9 @@ "label": "Lft", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rgt", @@ -171,7 +205,9 @@ "label": "Rgt", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "old_parent", @@ -179,27 +215,33 @@ "hidden": 1, "label": "Old Parent", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)", "fieldname": "include_in_gross", "fieldtype": "Check", - "label": "Include in gross" + "label": "Include in gross", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disable" + "label": "Disable", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-money", "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 17:57:52.063233", + "modified": "2020-06-11 15:15:54.338622", "modified_by": "Administrator", "module": "Accounts", "name": "Account", diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 353ff77223..8ca8b71ef8 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -19,10 +19,14 @@ "unlink_payment_on_cancellation_of_invoice", "unlink_advance_payment_on_cancelation_of_order", "book_asset_depreciation_entry_automatically", - "allow_cost_center_in_entry_of_bs_account", "add_taxes_from_item_tax_template", "automatically_fetch_payment_terms", + "deferred_accounting_settings_section", "automatically_process_deferred_accounting_entry", + "book_deferred_entries_based_on", + "column_break_18", + "book_deferred_entries_via_journal_entry", + "submit_journal_entries", "print_settings", "show_inclusive_tax_in_print", "column_break_12", @@ -108,12 +112,6 @@ "fieldtype": "Check", "label": "Book Asset Depreciation Entry Automatically" }, - { - "default": "0", - "fieldname": "allow_cost_center_in_entry_of_bs_account", - "fieldtype": "Check", - "label": "Allow Cost Center In Entry of Balance Sheet Account" - }, { "default": "1", "fieldname": "add_taxes_from_item_tax_template", @@ -189,13 +187,45 @@ "fieldname": "automatically_process_deferred_accounting_entry", "fieldtype": "Check", "label": "Automatically Process Deferred Accounting Entry" + }, + { + "fieldname": "deferred_accounting_settings_section", + "fieldtype": "Section Break", + "label": "Deferred Accounting Settings" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If this is unchecked direct GL Entries will be created to book Deferred Revenue/Expense", + "fieldname": "book_deferred_entries_via_journal_entry", + "fieldtype": "Check", + "label": "Book Deferred Entries Via Journal Entry" + }, + { + "default": "0", + "depends_on": "eval:doc.book_deferred_entries_via_journal_entry", + "description": "If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually", + "fieldname": "submit_journal_entries", + "fieldtype": "Check", + "label": "Submit Journal Entries" + }, + { + "default": "Days", + "description": "If \"Months\" is selected then fixed amount will be booked as deferred revenue or expense for each month irrespective of number of days in a month. Will be prorated if deferred revenue or expense is not booked for an entire month.", + "fieldname": "book_deferred_entries_based_on", + "fieldtype": "Select", + "label": "Book Deferred Entries Based On", + "options": "Days\nMonths" } ], "icon": "icon-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-19 16:58:17.395595", + "modified": "2020-06-22 20:13:26.043092", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 2473d715d0..5593466fc2 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -20,7 +20,6 @@ class AccountsSettings(Document): self.validate_stale_days() self.enable_payment_schedule_in_print() - self.enable_fields_for_cost_center_settings() def validate_stale_days(self): if not self.allow_stale and cint(self.stale_days) <= 0: @@ -33,8 +32,3 @@ class AccountsSettings(Document): for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"): make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check") make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check") - - def enable_fields_for_cost_center_settings(self): - show_field = 0 if cint(self.allow_cost_center_in_entry_of_bs_account) else 1 - for doctype in ("Sales Invoice", "Purchase Invoice", "Payment Entry"): - make_property_setter(doctype, "cost_center", "hidden", show_field, "Check") diff --git a/erpnext/accounts/doctype/bank/bank.json b/erpnext/accounts/doctype/bank/bank.json index 99978e657d..56bae72a15 100644 --- a/erpnext/accounts/doctype/bank/bank.json +++ b/erpnext/accounts/doctype/bank/bank.json @@ -13,7 +13,6 @@ "bank_name", "swift_number", "column_break_1", - "branch_code", "website", "address_and_contact", "address_html", @@ -51,15 +50,6 @@ "fieldtype": "Column Break", "search_index": 1 }, - { - "allow_in_quick_entry": 1, - "fieldname": "branch_code", - "fieldtype": "Data", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Branch Code", - "unique": 1 - }, { "fieldname": "address_and_contact", "fieldtype": "Section Break", @@ -111,7 +101,7 @@ } ], "links": [], - "modified": "2020-03-25 21:22:33.496264", + "modified": "2020-07-17 14:00:13.105433", "modified_by": "Administrator", "module": "Accounts", "name": "Bank", diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 65a0a5138c..b42f1f9d58 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -23,6 +23,7 @@ "account_details_section", "iban", "column_break_12", + "branch_code", "bank_account_no", "address_and_contact", "address_html", @@ -197,10 +198,16 @@ "fieldtype": "Data", "label": "Mask", "read_only": 1 + }, + { + "fieldname": "branch_code", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Branch Code" } ], "links": [], - "modified": "2020-04-06 21:00:45.379804", + "modified": "2020-07-17 13:59:50.795412", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js index 065d25e6c3..febf85ca6c 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js @@ -4,7 +4,7 @@ cur_frm.add_fetch('bank_account','account','account'); cur_frm.add_fetch('bank_account','bank_account_no','bank_account_no'); cur_frm.add_fetch('bank_account','iban','iban'); -cur_frm.add_fetch('bank','branch_code','branch_code'); +cur_frm.add_fetch('bank_account','branch_code','branch_code'); cur_frm.add_fetch('bank','swift_number','swift_number'); frappe.ui.form.on('Bank Guarantee', { diff --git a/erpnext/accounts/page/pos/__init__.py b/erpnext/accounts/doctype/dunning/__init__.py similarity index 100% rename from erpnext/accounts/page/pos/__init__.py rename to erpnext/accounts/doctype/dunning/__init__.py diff --git a/erpnext/accounts/doctype/dunning/dunning.js b/erpnext/accounts/doctype/dunning/dunning.js new file mode 100644 index 0000000000..c563368894 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning.js @@ -0,0 +1,149 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Dunning", { + setup: function (frm) { + frm.set_query("sales_invoice", () => { + return { + filters: { + docstatus: 1, + company: frm.doc.company, + outstanding_amount: [">", 0], + status: "Overdue" + }, + }; + }); + frm.set_query("income_account", () => { + return { + filters: { + company: frm.doc.company, + root_type: "Income", + is_group: 0 + } + }; + }); + }, + refresh: function (frm) { + frm.set_df_property("company", "read_only", frm.doc.__islocal ? 0 : 1); + frm.set_df_property( + "sales_invoice", + "read_only", + frm.doc.__islocal ? 0 : 1 + ); + if (frm.doc.docstatus === 1 && frm.doc.status === "Unresolved") { + frm.add_custom_button(__("Resolve"), () => { + frm.set_value("status", "Resolved"); + }); + } + if (frm.doc.docstatus === 1 && frm.doc.status !== "Resolved") { + frm.add_custom_button( + __("Payment"), + function () { + frm.events.make_payment_entry(frm); + },__("Create") + ); + frm.page.set_inner_btn_group_as_primary(__("Create")); + } + }, + overdue_days: function (frm) { + frappe.db.get_value( + "Dunning Type", + { + start_day: ["<", frm.doc.overdue_days], + end_day: [">=", frm.doc.overdue_days], + }, + "dunning_type", + (r) => { + if (r) { + frm.set_value("dunning_type", r.dunning_type); + } else { + frm.set_value("dunning_type", ""); + frm.set_value("rate_of_interest", ""); + frm.set_value("dunning_fee", ""); + } + } + ); + }, + dunning_type: function (frm) { + frm.trigger("get_dunning_letter_text"); + }, + language: function (frm) { + frm.trigger("get_dunning_letter_text"); + }, + get_dunning_letter_text: function (frm) { + if (frm.doc.dunning_type) { + frappe.call({ + method: + "erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text", + args: { + dunning_type: frm.doc.dunning_type, + language: frm.doc.language, + doc: frm.doc, + }, + callback: function (r) { + if (r.message) { + frm.set_value("body_text", r.message.body_text); + frm.set_value("closing_text", r.message.closing_text); + frm.set_value("language", r.message.language); + } else { + frm.set_value("body_text", ""); + frm.set_value("closing_text", ""); + } + }, + }); + } + }, + due_date: function (frm) { + frm.trigger("calculate_overdue_days"); + }, + posting_date: function (frm) { + frm.trigger("calculate_overdue_days"); + }, + rate_of_interest: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + outstanding_amount: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + interest_amount: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + dunning_fee: function (frm) { + frm.trigger("calculate_interest_and_amount"); + }, + sales_invoice: function (frm) { + frm.trigger("calculate_overdue_days"); + }, + calculate_overdue_days: function (frm) { + if (frm.doc.posting_date && frm.doc.due_date) { + const overdue_days = moment(frm.doc.posting_date).diff( + frm.doc.due_date, + "days" + ); + frm.set_value("overdue_days", overdue_days); + } + }, + calculate_interest_and_amount: function (frm) { + const interest_per_year = frm.doc.outstanding_amount * frm.doc.rate_of_interest / 100; + const interest_amount = interest_per_year / 365 * frm.doc.overdue_days || 0; + const dunning_amount = interest_amount + frm.doc.dunning_fee; + const grand_total = frm.doc.outstanding_amount + dunning_amount; + frm.set_value("interest_amount", interest_amount); + frm.set_value("dunning_amount", dunning_amount); + frm.set_value("grand_total", grand_total); + }, + make_payment_entry: function (frm) { + return frappe.call({ + method: + "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry", + args: { + dt: frm.doc.doctype, + dn: frm.doc.name, + }, + callback: function (r) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + }, + }); + }, +}); diff --git a/erpnext/accounts/doctype/dunning/dunning.json b/erpnext/accounts/doctype/dunning/dunning.json new file mode 100644 index 0000000000..b3eddf5f22 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning.json @@ -0,0 +1,370 @@ +{ + "actions": [], + "allow_events_in_timeline": 1, + "autoname": "naming_series:", + "creation": "2019-07-05 16:34:31.013238", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "title", + "naming_series", + "sales_invoice", + "customer", + "customer_name", + "outstanding_amount", + "currency", + "conversion_rate", + "column_break_3", + "company", + "posting_date", + "posting_time", + "due_date", + "overdue_days", + "address_and_contact_section", + "address_display", + "contact_display", + "contact_mobile", + "contact_email", + "column_break_18", + "company_address_display", + "section_break_6", + "dunning_type", + "interest_amount", + "column_break_8", + "rate_of_interest", + "dunning_fee", + "section_break_12", + "dunning_amount", + "grand_total", + "income_account", + "column_break_17", + "status", + "printing_setting_section", + "language", + "body_text", + "column_break_22", + "letter_head", + "closing_text", + "amended_from" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "default": "DUNN-.MM.-.YY.-", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "DUNN-.MM.-.YY.-" + }, + { + "fieldname": "sales_invoice", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Sales Invoice", + "options": "Sales Invoice", + "reqd": 1 + }, + { + "fetch_from": "sales_invoice.customer_name", + "fieldname": "customer_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Customer Name", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.outstanding_amount", + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "label": "Outstanding Amount", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Date" + }, + { + "fieldname": "overdue_days", + "fieldtype": "Int", + "label": "Overdue Days", + "read_only": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "dunning_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Dunning Type", + "options": "Dunning Type", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "interest_amount", + "fieldtype": "Currency", + "label": "Interest Amount", + "precision": "2", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fetch_from": "dunning_type.dunning_fee", + "fetch_if_empty": 1, + "fieldname": "dunning_fee", + "fieldtype": "Currency", + "label": "Dunning Fee", + "precision": "2" + }, + { + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "printing_setting_section", + "fieldtype": "Section Break", + "label": "Printing Setting" + }, + { + "fieldname": "language", + "fieldtype": "Link", + "label": "Print Language", + "options": "Language" + }, + { + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" + }, + { + "fetch_from": "sales_invoice.currency", + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Dunning", + "print_hide": 1, + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "{customer_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title" + }, + { + "fieldname": "body_text", + "fieldtype": "Text Editor", + "label": "Body Text" + }, + { + "fieldname": "closing_text", + "fieldtype": "Text Editor", + "label": "Closing Text" + }, + { + "fetch_from": "sales_invoice.due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "label": "Due Date", + "read_only": 1 + }, + { + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time" + }, + { + "default": "0", + "fetch_from": "dunning_type.interest_rate", + "fetch_if_empty": 1, + "fieldname": "rate_of_interest", + "fieldtype": "Float", + "label": "Rate of Interest (%) Yearly" + }, + { + "fieldname": "address_and_contact_section", + "fieldtype": "Section Break", + "label": "Address and Contact" + }, + { + "fetch_from": "sales_invoice.address_display", + "fieldname": "address_display", + "fieldtype": "Small Text", + "label": "Address", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.contact_display", + "fieldname": "contact_display", + "fieldtype": "Small Text", + "label": "Contact", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.contact_mobile", + "fieldname": "contact_mobile", + "fieldtype": "Small Text", + "label": "Mobile No", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "fetch_from": "sales_invoice.company_address_display", + "fieldname": "company_address_display", + "fieldtype": "Small Text", + "label": "Company Address", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.contact_email", + "fieldname": "contact_email", + "fieldtype": "Data", + "label": "Contact Email", + "options": "Email", + "read_only": 1 + }, + { + "fetch_from": "sales_invoice.customer", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "grand_total", + "fieldtype": "Currency", + "label": "Grand Total", + "precision": "2", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "default": "Unresolved", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "options": "Draft\nResolved\nUnresolved\nCancelled" + }, + { + "fieldname": "dunning_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Dunning Amount", + "read_only": 1 + }, + { + "fieldname": "income_account", + "fieldtype": "Link", + "label": "Income Account", + "options": "Account" + }, + { + "fetch_from": "sales_invoice.conversion_rate", + "fieldname": "conversion_rate", + "fieldtype": "Float", + "hidden": 1, + "label": "Conversion Rate", + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-07-21 18:20:23.512151", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Dunning", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC", + "title_field": "customer_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py new file mode 100644 index 0000000000..0be6a480c9 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import json +from six import string_types +from frappe.utils import getdate, get_datetime, rounded, flt +from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year +from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from erpnext.controllers.accounts_controller import AccountsController + + +class Dunning(AccountsController): + def validate(self): + self.validate_overdue_days() + self.validate_amount() + if not self.income_account: + self.income_account = frappe.db.get_value('Company', self.company, 'default_income_account') + + def validate_overdue_days(self): + self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0 + + def validate_amount(self): + amounts = calculate_interest_and_amount( + self.posting_date, self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days) + if self.interest_amount != amounts.get('interest_amount'): + self.interest_amount = amounts.get('interest_amount') + if self.dunning_amount != amounts.get('dunning_amount'): + self.dunning_amount = amounts.get('dunning_amount') + if self.grand_total != amounts.get('grand_total'): + self.grand_total = amounts.get('grand_total') + + def on_submit(self): + self.make_gl_entries() + + def on_cancel(self): + if self.dunning_amount: + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') + make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) + + def make_gl_entries(self): + if not self.dunning_amount: + return + gl_entries = [] + invoice_fields = ["project", "cost_center", "debit_to", "party_account_currency", "conversion_rate", "cost_center"] + inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1) + accounting_dimensions = get_accounting_dimensions() + invoice_fields.extend(accounting_dimensions) + dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate) + default_cost_center = frappe.get_cached_value('Company', self.company, 'cost_center') + gl_entries.append( + self.get_gl_dict({ + "account": inv.debit_to, + "party_type": "Customer", + "party": self.customer, + "due_date": self.due_date, + "against": self.income_account, + "debit": dunning_in_company_currency, + "debit_in_account_currency": self.dunning_amount, + "against_voucher": self.name, + "against_voucher_type": "Dunning", + "cost_center": inv.cost_center or default_cost_center, + "project": inv.project + }, inv.party_account_currency, item=inv) + ) + gl_entries.append( + self.get_gl_dict({ + "account": self.income_account, + "against": self.customer, + "credit": dunning_in_company_currency, + "cost_center": inv.cost_center or default_cost_center, + "credit_in_account_currency": self.dunning_amount, + "project": inv.project + }, item=inv) + ) + make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False) + + +def resolve_dunning(doc, state): + for reference in doc.references: + if reference.reference_doctype == 'Sales Invoice' and reference.outstanding_amount <= 0: + dunnings = frappe.get_list('Dunning', filters={ + 'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}) + + for dunning in dunnings: + frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved') + +def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_interest, dunning_fee, overdue_days): + interest_amount = 0 + if rate_of_interest: + interest_per_year = rounded(flt(outstanding_amount) * flt(rate_of_interest))/100 + interest_amount = ( + interest_per_year / days_in_year(get_datetime(posting_date).year)) * int(overdue_days) + grand_total = flt(outstanding_amount) + flt(interest_amount) + flt(dunning_fee) + dunning_amount = flt(interest_amount) + flt(dunning_fee) + return { + 'interest_amount': interest_amount, + 'grand_total': grand_total, + 'dunning_amount': dunning_amount} + +@frappe.whitelist() +def get_dunning_letter_text(dunning_type, doc, language=None): + if isinstance(doc, string_types): + doc = json.loads(doc) + if language: + filters = {'parent': dunning_type, 'language': language} + else: + filters = {'parent': dunning_type, 'is_default_language': 1} + letter_text = frappe.db.get_value('Dunning Letter Text', filters, + ['body_text', 'closing_text', 'language'], as_dict=1) + if letter_text: + return { + 'body_text': frappe.render_template(letter_text.body_text, doc), + 'closing_text': frappe.render_template(letter_text.closing_text, doc), + 'language': letter_text.language + } diff --git a/erpnext/accounts/doctype/dunning/dunning_list.js b/erpnext/accounts/doctype/dunning/dunning_list.js new file mode 100644 index 0000000000..8dc0a8c857 --- /dev/null +++ b/erpnext/accounts/doctype/dunning/dunning_list.js @@ -0,0 +1,9 @@ +frappe.listview_settings["Dunning"] = { + get_indicator: function (doc) { + if (doc.status === "Resolved") { + return [__("Resolved"), "green", "status,=,Resolved"]; + } else { + return [__("Unresolved"), "red", "status,=,Unresolved"]; + } + }, +}; diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py new file mode 100644 index 0000000000..cb18309e3c --- /dev/null +++ b/erpnext/accounts/doctype/dunning/test_dunning.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from frappe.utils import add_days, today, nowdate +from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice_against_cost_center +from erpnext.accounts.doctype.dunning.dunning import calculate_interest_and_amount +from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + + +class TestDunning(unittest.TestCase): + @classmethod + def setUpClass(self): + create_dunning_type() + unlink_payment_on_cancel_of_invoice() + + @classmethod + def tearDownClass(self): + unlink_payment_on_cancel_of_invoice(0) + + def test_dunning(self): + dunning = create_dunning() + amounts = calculate_interest_and_amount( + dunning.posting_date, dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days) + self.assertEqual(round(amounts.get('interest_amount'), 2), 0.44) + self.assertEqual(round(amounts.get('dunning_amount'), 2), 20.44) + self.assertEqual(round(amounts.get('grand_total'), 2), 120.44) + + def test_gl_entries(self): + dunning = create_dunning() + dunning.submit() + gl_entries = frappe.db.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Dunning' and voucher_no=%s + order by account asc""", dunning.name, as_dict=1) + self.assertTrue(gl_entries) + expected_values = dict((d[0], d) for d in [ + ['Debtors - _TC', 20.44, 0.0], + ['Sales - _TC', 0.0, 20.44] + ]) + for gle in gl_entries: + self.assertEquals(expected_values[gle.account][0], gle.account) + self.assertEquals(expected_values[gle.account][1], gle.debit) + self.assertEquals(expected_values[gle.account][2], gle.credit) + + def test_payment_entry(self): + dunning = create_dunning() + dunning.submit() + pe = get_payment_entry("Dunning", dunning.name) + pe.reference_no = "1" + pe.reference_date = nowdate() + pe.paid_from_account_currency = dunning.currency + pe.paid_to_account_currency = dunning.currency + pe.source_exchange_rate = 1 + pe.target_exchange_rate = 1 + pe.insert() + pe.submit() + si_doc = frappe.get_doc('Sales Invoice', dunning.sales_invoice) + self.assertEqual(si_doc.outstanding_amount, 0) + + +def create_dunning(): + posting_date = add_days(today(), -20) + due_date = add_days(today(), -15) + sales_invoice = create_sales_invoice_against_cost_center( + posting_date=posting_date, due_date=due_date, status='Overdue') + dunning_type = frappe.get_doc("Dunning Type", 'First Notice') + dunning = frappe.new_doc("Dunning") + dunning.sales_invoice = sales_invoice.name + dunning.customer_name = sales_invoice.customer_name + dunning.outstanding_amount = sales_invoice.outstanding_amount + dunning.debit_to = sales_invoice.debit_to + dunning.currency = sales_invoice.currency + dunning.company = sales_invoice.company + dunning.posting_date = nowdate() + dunning.due_date = sales_invoice.due_date + dunning.dunning_type = 'First Notice' + dunning.rate_of_interest = dunning_type.rate_of_interest + dunning.dunning_fee = dunning_type.dunning_fee + dunning.save() + return dunning + +def create_dunning_type(): + dunning_type = frappe.new_doc("Dunning Type") + dunning_type.dunning_type = 'First Notice' + dunning_type.start_day = 10 + dunning_type.end_day = 20 + dunning_type.dunning_fee = 20 + dunning_type.rate_of_interest = 8 + dunning_type.append( + "dunning_letter_text", { + 'language': 'en', + 'body_text': 'We have still not received payment for our invoice ', + 'closing_text': 'We kindly request that you pay the outstanding amount immediately, including interest and late fees.' + } + ) + dunning_type.save() diff --git a/erpnext/buying/report/requested_items_to_order/__init__.py b/erpnext/accounts/doctype/dunning_letter_text/__init__.py similarity index 100% rename from erpnext/buying/report/requested_items_to_order/__init__.py rename to erpnext/accounts/doctype/dunning_letter_text/__init__.py diff --git a/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json new file mode 100644 index 0000000000..5ede3a1071 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.json @@ -0,0 +1,70 @@ +{ + "actions": [], + "creation": "2019-12-06 04:25:40.215625", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "language", + "is_default_language", + "section_break_4", + "body_text", + "closing_text", + "section_break_7", + "body_and_closing_text_help" + ], + "fields": [ + { + "fieldname": "language", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Language", + "options": "Language" + }, + { + "default": "0", + "fieldname": "is_default_language", + "fieldtype": "Check", + "label": "Is Default Language" + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, + { + "description": "Letter or Email Body Text", + "fieldname": "body_text", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Body Text" + }, + { + "description": "Letter or Email Closing Text", + "fieldname": "closing_text", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Closing Text" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, + { + "fieldname": "body_and_closing_text_help", + "fieldtype": "HTML", + "label": "Body and Closing Text Help", + "options": "
The fieldnames you can use in your template are the fields in the document. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)
\n\nTemplates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.
" + } + ], + "istable": 1, + "links": [], + "modified": "2020-07-14 18:02:35.988958", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Dunning Letter Text", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py new file mode 100644 index 0000000000..426497b607 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_letter_text/dunning_letter_text.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class DunningLetterText(Document): + pass diff --git a/erpnext/healthcare/doctype/lab_test_groups/__init__.py b/erpnext/accounts/doctype/dunning_type/__init__.py similarity index 100% rename from erpnext/healthcare/doctype/lab_test_groups/__init__.py rename to erpnext/accounts/doctype/dunning_type/__init__.py diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.js b/erpnext/accounts/doctype/dunning_type/dunning_type.js new file mode 100644 index 0000000000..54156b488d --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/dunning_type.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Dunning Type', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.json b/erpnext/accounts/doctype/dunning_type/dunning_type.json new file mode 100644 index 0000000000..da43664472 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/dunning_type.json @@ -0,0 +1,129 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:dunning_type", + "creation": "2019-12-04 04:59:08.003664", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "dunning_type", + "overdue_interval_section", + "start_day", + "column_break_4", + "end_day", + "section_break_6", + "dunning_fee", + "column_break_8", + "rate_of_interest", + "text_block_section", + "dunning_letter_text" + ], + "fields": [ + { + "fieldname": "dunning_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Dunning Type", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "dunning_fee", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Dunning Fee" + }, + { + "description": "This section allows the user to set the Body and Closing text of the Dunning Letter for the Dunning Type based on language, which can be used in Print.", + "fieldname": "text_block_section", + "fieldtype": "Section Break", + "label": "Dunning Letter" + }, + { + "fieldname": "dunning_letter_text", + "fieldtype": "Table", + "options": "Dunning Letter Text" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "overdue_interval_section", + "fieldtype": "Section Break", + "label": "Overdue Interval" + }, + { + "fieldname": "start_day", + "fieldtype": "Int", + "label": "Start Day" + }, + { + "fieldname": "end_day", + "fieldtype": "Int", + "label": "End Day" + }, + { + "fieldname": "rate_of_interest", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Rate of Interest (%) Yearly" + } + ], + "links": [], + "modified": "2020-07-15 17:14:17.835074", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Dunning Type", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Administrator", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.py b/erpnext/accounts/doctype/dunning_type/dunning_type.py new file mode 100644 index 0000000000..8708748428 --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/dunning_type.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class DunningType(Document): + pass diff --git a/erpnext/accounts/doctype/dunning_type/test_dunning_type.py b/erpnext/accounts/doctype/dunning_type/test_dunning_type.py new file mode 100644 index 0000000000..b2fb26f34a --- /dev/null +++ b/erpnext/accounts/doctype/dunning_type/test_dunning_type.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestDunningType(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 645da341a3..def9ed6803 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -72,12 +72,6 @@ class GLEntry(Document): if not self.cost_center and self.voucher_type != 'Period Closing Voucher': frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.") .format(self.voucher_type, self.voucher_no, self.account)) - else: - from erpnext.accounts.utils import get_allow_cost_center_in_entry_of_bs_account - if not get_allow_cost_center_in_entry_of_bs_account() and self.cost_center: - self.cost_center = None - if self.project: - self.project = None def validate_dimensions_for_pl_and_bs(self): diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js index 0fab8b70f4..db4f7c423f 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js @@ -188,14 +188,15 @@ frappe.ui.form.on('Invoice Discounting', { }, show_general_ledger: (frm) => { - if(frm.doc.docstatus===1) { + if(frm.doc.docstatus > 0) { cur_frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, from_date: frm.doc.posting_date, - to_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), company: frm.doc.company, - group_by: "Group by Voucher (Consolidated)" + group_by: "Group by Voucher (Consolidated)", + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 594b4d4a22..8083b21f75 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, json +import frappe, json, erpnext from frappe import _ from frappe.utils import flt, getdate, nowdate, add_days from erpnext.controllers.accounts_controller import AccountsController @@ -134,16 +134,19 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.bank_account, "debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges), + "cost_center": erpnext.get_default_cost_center(self.company) }) je.append("accounts", { "account": self.bank_charges_account, - "debit_in_account_currency": flt(self.bank_charges) + "debit_in_account_currency": flt(self.bank_charges), + "cost_center": erpnext.get_default_cost_center(self.company) }) je.append("accounts", { "account": self.short_term_loan, "credit_in_account_currency": flt(self.total_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name }) @@ -151,6 +154,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_discounted, "debit_in_account_currency": flt(d.outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", @@ -160,6 +164,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_credit, "credit_in_account_currency": flt(d.outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", @@ -177,13 +182,15 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.short_term_loan, "debit_in_account_currency": flt(self.total_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, }) je.append("accounts", { "account": self.bank_account, - "credit_in_account_currency": flt(self.total_amount) + "credit_in_account_currency": flt(self.total_amount), + "cost_center": erpnext.get_default_cost_center(self.company) }) if getdate(self.loan_end_date) > getdate(nowdate()): @@ -193,6 +200,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_discounted, "credit_in_account_currency": flt(outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", @@ -202,6 +210,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_unpaid, "debit_in_account_currency": flt(outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js index 42abdb809b..e921a0d949 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js @@ -6,6 +6,18 @@ frappe.ui.form.on('Item Tax Template', { frm.set_query("tax_type", "taxes", function(doc) { return { filters: [ + ['Account', 'company', '=', frm.doc.company], + ['Account', 'is_group', '=', 0], + ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] + ] + } + }); + }, + company: function (frm) { + frm.set_query("tax_type", "taxes", function(doc) { + return { + filters: [ + ['Account', 'company', '=', frm.doc.company], ['Account', 'is_group', '=', 0], ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] ] diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json index f713cfc0ba..856c371ecf 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json @@ -1,168 +1,85 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:title", - "beta": 0, - "creation": "2018-11-22 22:45:00.370913", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:title", + "creation": "2018-11-22 22:45:00.370913", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "company", + "taxes" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "title", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 1, + "label": "Title", + "no_copy": 1, + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes", - "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": "Tax Rates", - "length": 0, - "no_copy": 0, - "options": "Item Tax Template Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Tax Rates", + "options": "Item Tax Template Detail", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-12-21 23:51:16.328340", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Item Tax Template", - "name_case": "", - "owner": "Administrator", + ], + "modified": "2020-06-18 20:27:42.615842", + "modified_by": "ahmad@havenir.com", + "module": "Accounts", + "name": "Item Tax Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/item_tax_template/test_records.json b/erpnext/accounts/doctype/item_tax_template/test_records.json index db540e86aa..4d9537d4b8 100644 --- a/erpnext/accounts/doctype/item_tax_template/test_records.json +++ b/erpnext/accounts/doctype/item_tax_template/test_records.json @@ -2,6 +2,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 10", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -14,6 +15,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 12", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -26,6 +28,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 15", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -38,6 +41,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 20", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -50,6 +54,7 @@ { "doctype": "Item Tax Template", "title": "_Test Item Tax Template 1", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 5685f839fe..a09face791 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -13,15 +13,16 @@ frappe.ui.form.on("Journal Entry", { refresh: function(frm) { erpnext.toggle_naming_series(); - if(frm.doc.docstatus==1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Ledger'), function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, "finance_book": frm.doc.finance_book, - "group_by_voucher": 0 + "group_by": '', + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __('View')); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index af2aa65e6b..4573c50134 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -83,7 +83,7 @@ "label": "Entry Type", "oldfieldname": "voucher_type", "oldfieldtype": "Select", - "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation", + "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense", "reqd": 1, "search_index": 1 }, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index caaf30f11f..cfdae936a4 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_balance_on, get_account_currency from erpnext.accounts.party import get_party_account from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting +from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts from six import string_types, iteritems @@ -265,7 +266,10 @@ class JournalEntry(AccountsController): # set totals if not d.reference_name in self.reference_totals: self.reference_totals[d.reference_name] = 0.0 - self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) + + if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'): + self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) + self.reference_types[d.reference_name] = d.reference_type self.reference_accounts[d.reference_name] = d.account @@ -277,10 +281,16 @@ class JournalEntry(AccountsController): # check if party and account match if d.reference_type in ("Sales Invoice", "Purchase Invoice"): - if d.reference_type == "Sales Invoice": - party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] + if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no: + debit_or_credit = 'Debit' if d.debit else 'Credit' + party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no, + debit_or_credit) else: - party_account = against_voucher[1] + if d.reference_type == "Sales Invoice": + party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] + else: + party_account = against_voucher[1] + if (against_voucher[0] != d.party or party_account != d.account): frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], @@ -513,14 +523,20 @@ class JournalEntry(AccountsController): "against_voucher_type": d.reference_type, "against_voucher": d.reference_name, "remarks": remarks, + "voucher_detail_no": d.reference_detail_no, "cost_center": d.cost_center, "project": d.project, "finance_book": self.finance_book }, item=d) ) + if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'): + update_outstanding = 'No' + else: + update_outstanding = 'Yes' + if gl_map: - make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj) + make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding) def get_balance(self): if not self.get('accounts'): @@ -824,6 +840,7 @@ def get_opening_accounts(company): return [{"account": a, "balance": get_balance_on(a)} for a in accounts] +@frappe.whitelist() def get_against_jv(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 6996c775b3..23ad1eef14 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -204,11 +204,8 @@ class TestJournalEntry(unittest.TestCase): self.assertEqual(jv.inter_company_journal_entry_reference, "") self.assertEqual(jv1.inter_company_journal_entry_reference, "") - def test_jv_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_jv_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) @@ -237,15 +234,45 @@ class TestJournalEntry(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() + def test_jv_with_project(self): + from erpnext.projects.doctype.project.test_project import make_project + project = make_project({ + 'project_name': 'Journal Entry Project', + 'project_template_name': 'Test Project Template', + 'start_date': '2020-01-01' + }) - def test_jv_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self): + jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False) + for d in jv.accounts: + d.project = project.project_name + jv.voucher_type = "Bank Entry" + jv.multi_currency = 0 + jv.cheque_no = "112233" + jv.cheque_date = nowdate() + jv.insert() + jv.submit() + + expected_values = { + "_Test Cash - _TC": { + "project": project.project_name + }, + "_Test Bank - _TC": { + "project": project.project_name + } + } + + gl_entries = frappe.db.sql("""select account, project, debit, credit + from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s + order by account asc""", jv.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["project"], gle.project) + + def test_jv_account_and_party_balance_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.utils import get_balance_on - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) @@ -261,9 +288,6 @@ class TestJournalEntry(unittest.TestCase): account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center) self.assertEqual(expected_account_balance, account_balance) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None): if not cost_center: cost_center = "_Test Cost Center - _TC" diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index ff3533a679..774159d691 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -33,6 +33,7 @@ "reference_type", "reference_name", "reference_due_date", + "reference_detail_no", "col_break3", "is_advance", "user_remark", @@ -268,12 +269,18 @@ "fieldtype": "Link", "label": "Bank Account", "options": "Bank Account" + }, + { + "fieldname": "reference_detail_no", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Detail No" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-06-18 14:06:54.833738", + "modified": "2020-06-24 14:06:54.833738", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json index 597519858a..4c1be6517c 100644 --- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json +++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.json @@ -1,426 +1,123 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2018-01-23 05:40:18.117583", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2018-01-23 05:40:18.117583", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "loyalty_program", + "loyalty_program_tier", + "customer", + "invoice_type", + "invoice", + "redeem_against", + "loyalty_points", + "purchase_amount", + "expiry_date", + "posting_date", + "company" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "loyalty_program", - "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": "Loyalty Program", - "length": 0, - "no_copy": 0, - "options": "Loyalty Program", - "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 - }, + "fieldname": "loyalty_program", + "fieldtype": "Link", + "label": "Loyalty Program", + "options": "Loyalty Program" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "loyalty_program_tier", - "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": "Loyalty Program Tier", - "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 - }, + "fieldname": "loyalty_program_tier", + "fieldtype": "Data", + "label": "Loyalty Program Tier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "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 - }, + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_invoice", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Sales Invoice", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice", - "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 - }, + "fieldname": "redeem_against", + "fieldtype": "Link", + "label": "Redeem Against", + "options": "Loyalty Point Entry" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "redeem_against", - "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": "Redeem Against", - "length": 0, - "no_copy": 0, - "options": "Loyalty Point Entry", - "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 - }, + "fieldname": "loyalty_points", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Loyalty Points" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "loyalty_points", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Loyalty Points", - "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 - }, + "fieldname": "purchase_amount", + "fieldtype": "Currency", + "label": "Purchase Amount" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_amount", - "fieldtype": "Currency", - "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": "Purchase Amount", - "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 - }, + "fieldname": "expiry_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expiry Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expiry_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Expiry 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 - }, + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "posting_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": "Posting 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 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "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": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "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 + "fieldname": "invoice_type", + "fieldtype": "Link", + "label": "Invoice Type", + "options": "DocType" + }, + { + "fieldname": "invoice", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Invoice", + "options": "invoice_type" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 16:05:22.810347", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Loyalty Point Entry", - "name_case": "", - "owner": "Administrator", + ], + "in_create": 1, + "modified": "2020-01-30 17:27:55.964242", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Loyalty Point Entry", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User" } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "customer", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "customer", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py index d65a7d88e6..3579a1a960 100644 --- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py +++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py @@ -18,7 +18,7 @@ def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=No date = today() return frappe.db.sql(''' - select name, loyalty_points, expiry_date, loyalty_program_tier, sales_invoice + select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice from `tabLoyalty Point Entry` where customer=%s and loyalty_program=%s and expiry_date>=%s and loyalty_points>0 and company=%s diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py index 563165b2cc..cb753a3723 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.py @@ -36,7 +36,8 @@ def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=Non return {"loyalty_points": 0, "total_spent": 0} @frappe.whitelist() -def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False, current_transaction_amount=0): +def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, \ + silent=False, include_expired_entry=False, current_transaction_amount=0): lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent) loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program) lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)) @@ -59,10 +60,10 @@ def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None if not loyalty_program: loyalty_program = frappe.db.get_value("Customer", customer, "loyalty_program") - if not (loyalty_program or silent): + if not loyalty_program and not silent: frappe.throw(_("Customer isn't enrolled in any Loyalty Program")) elif silent and not loyalty_program: - return frappe._dict({"loyalty_program": None}) + return frappe._dict({"loyalty_programs": None}) if not company: company = frappe.db.get_default("company") or frappe.get_all("Company")[0].name diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 341884c190..ee73ccaa61 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -27,7 +27,7 @@ class TestLoyaltyProgram(unittest.TestCase): customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"}) earned_points = get_points_earned(si_original) - lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program) self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier) @@ -42,8 +42,8 @@ class TestLoyaltyProgram(unittest.TestCase): earned_after_redemption = get_points_earned(si_redeem) - lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'redeem_against': lpe.name}) - lpe_earn = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) + lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name}) + lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption) self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points)) @@ -66,7 +66,7 @@ class TestLoyaltyProgram(unittest.TestCase): earned_points = get_points_earned(si_original) - lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program) self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier) @@ -82,8 +82,8 @@ class TestLoyaltyProgram(unittest.TestCase): customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"}) earned_after_redemption = get_points_earned(si_redeem) - lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'redeem_against': lpe.name}) - lpe_earn = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) + lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name}) + lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]}) self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption) self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points)) @@ -101,7 +101,7 @@ class TestLoyaltyProgram(unittest.TestCase): si.insert() si.submit() - lpe = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si.name, 'customer': si.customer}) + lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si.name, 'customer': si.customer}) self.assertEqual(True, not (lpe is None)) # cancelling sales invoice @@ -118,7 +118,7 @@ class TestLoyaltyProgram(unittest.TestCase): si_original.submit() earned_points = get_points_earned(si_original) - lpe_original = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe_original = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(lpe_original.loyalty_points, earned_points) # create sales invoice return @@ -130,10 +130,10 @@ class TestLoyaltyProgram(unittest.TestCase): si_return.submit() # fetch original invoice again as its status would have been updated - si_original = frappe.get_doc('Sales Invoice', lpe_original.sales_invoice) + si_original = frappe.get_doc('Sales Invoice', lpe_original.invoice) earned_points = get_points_earned(si_original) - lpe_after_return = frappe.get_doc('Loyalty Point Entry', {'sales_invoice': si_original.name, 'customer': si_original.customer}) + lpe_after_return = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer}) self.assertEqual(lpe_after_return.loyalty_points, earned_points) self.assertEqual(True, (lpe_original.loyalty_points > lpe_after_return.loyalty_points)) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index 54464e71c4..a53417eedf 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -68,6 +68,9 @@ class OpeningInvoiceCreationTool(Document): if not self.company: frappe.throw(_("Please select the Company")) + company_details = frappe.get_cached_value('Company', self.company, + ["default_currency", "default_letter_head"], as_dict=1) or {} + for row in self.invoices: if not row.qty: row.qty = 1.0 @@ -99,6 +102,12 @@ class OpeningInvoiceCreationTool(Document): if not args: continue + if company_details: + args.update({ + "currency": company_details.get("default_currency"), + "letter_head": company_details.get("default_letter_head") + }) + doc = frappe.get_doc(args).insert() doc.submit() names.append(doc.name) @@ -172,8 +181,7 @@ class OpeningInvoiceCreationTool(Document): "due_date": row.due_date, "posting_date": row.posting_date, frappe.scrub(party_type): row.party, - "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice", - "currency": frappe.get_cached_value('Company', self.company, "default_currency") + "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice" }) accounting_dimension = get_accounting_dimensions() diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index a47da78f1f..adfaade36e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -90,7 +90,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("reference_doctype", "references", function() { if (frm.doc.party_type=="Customer") { - var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry"]; + var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"]; } else if (frm.doc.party_type=="Supplier") { var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"]; } else if (frm.doc.party_type=="Employee") { @@ -125,7 +125,7 @@ frappe.ui.form.on('Payment Entry', { const child = locals[cdt][cdn]; const filters = {"docstatus": 1, "company": doc.company}; const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice', - 'Purchase Order', 'Expense Claim', 'Fees']; + 'Purchase Order', 'Expense Claim', 'Fees', 'Dunning']; if (in_list(party_type_doctypes, child.reference_doctype)) { filters[doc.party_type.toLowerCase()] = doc.party; @@ -172,8 +172,8 @@ frappe.ui.form.on('Payment Entry', { frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); frm.toggle_display("base_received_amount", ( - frm.doc.paid_to_account_currency != company_currency - && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency + frm.doc.paid_to_account_currency != company_currency + && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency && frm.doc.base_paid_amount != frm.doc.base_received_amount )); @@ -234,14 +234,15 @@ frappe.ui.form.on('Payment Entry', { }, show_general_ledger: function(frm) { - if(frm.doc.docstatus==1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Ledger'), function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, - group_by: "" + "group_by": "", + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, "fa fa-table"); @@ -862,10 +863,10 @@ frappe.ui.form.on('Payment Entry', { } if(frm.doc.party_type=="Customer" && - !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.reference_doctype) + !in_list(["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"], row.reference_doctype) ) { frappe.model.set_value(row.doctype, row.name, "reference_doctype", null); - frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice or Journal Entry", [row.idx])); + frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning", [row.idx])); return false; } diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 59611bc74c..f9db14b90f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext, json from frappe import _, scrub, ValidationError from frappe.utils import flt, comma_or, nowdate, getdate -from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on, get_allow_cost_center_in_entry_of_bs_account +from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on from erpnext.accounts.party import get_party_account from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.setup.utils import get_exchange_rate @@ -199,8 +199,8 @@ class PaymentEntry(AccountsController): def validate_account_type(self, account, account_types): account_type = frappe.db.get_value("Account", account, "account_type") - if account_type not in account_types: - frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) + # if account_type not in account_types: + # frappe.throw(_("Account Type for {0} must be {1}").format(account, comma_or(account_types))) def set_exchange_rate(self): if self.paid_from and not self.source_exchange_rate: @@ -223,7 +223,7 @@ class PaymentEntry(AccountsController): if self.party_type == "Student": valid_reference_doctypes = ("Fees") elif self.party_type == "Customer": - valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry") + valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning") elif self.party_type == "Supplier": valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") elif self.party_type == "Employee": @@ -658,7 +658,7 @@ def get_outstanding_reference_documents(args): .format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])) # Add cost center condition - if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): + if args.get("cost_center"): condition += " and cost_center='%s'" % args.get("cost_center") date_fields_dict = { @@ -897,6 +897,10 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre total_amount = ref_doc.get("grand_total") exchange_rate = 1 outstanding_amount = ref_doc.get("outstanding_amount") + if reference_doctype == "Dunning": + total_amount = ref_doc.get("dunning_amount") + exchange_rate = 1 + outstanding_amount = ref_doc.get("dunning_amount") elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1: total_amount = ref_doc.get("total_amount") if ref_doc.multi_currency: @@ -951,7 +955,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0: frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) - if dt in ("Sales Invoice", "Sales Order"): + if dt in ("Sales Invoice", "Sales Order", "Dunning"): party_type = "Customer" elif dt in ("Purchase Invoice", "Purchase Order"): party_type = "Supplier" @@ -980,7 +984,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) # payment type - if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \ + if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)) \ or (dt=="Purchase Invoice" and doc.outstanding_amount < 0): payment_type = "Receive" else: @@ -1006,6 +1010,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= elif dt == "Fees": grand_total = doc.grand_total outstanding_amount = doc.outstanding_amount + elif dt == "Dunning": + grand_total = doc.grand_total + outstanding_amount = doc.grand_total else: if party_account_currency == doc.company_currency: grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total) @@ -1029,14 +1036,14 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= if bank_amount: received_amount = bank_amount else: - received_amount = paid_amount * doc.conversion_rate + received_amount = paid_amount * doc.get('conversion_rate', 1) else: received_amount = abs(outstanding_amount) if bank_amount: paid_amount = bank_amount else: # if party account currency and bank currency is different then populate paid amount as well - paid_amount = received_amount * doc.conversion_rate + paid_amount = received_amount * doc.get('conversion_rate', 1) pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type @@ -1075,15 +1082,35 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= for reference in get_reference_as_per_payment_terms(doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount): pe.append('references', reference) else: - pe.append("references", { - 'reference_doctype': dt, - 'reference_name': dn, - "bill_no": doc.get("bill_no"), - "due_date": doc.get("due_date"), - 'total_amount': grand_total, - 'outstanding_amount': outstanding_amount, - 'allocated_amount': outstanding_amount - }) + if dt == "Dunning": + pe.append("references", { + 'reference_doctype': 'Sales Invoice', + 'reference_name': doc.get('sales_invoice'), + "bill_no": doc.get("bill_no"), + "due_date": doc.get("due_date"), + 'total_amount': doc.get('outstanding_amount'), + 'outstanding_amount': doc.get('outstanding_amount'), + 'allocated_amount': doc.get('outstanding_amount') + }) + pe.append("references", { + 'reference_doctype': dt, + 'reference_name': dn, + "bill_no": doc.get("bill_no"), + "due_date": doc.get("due_date"), + 'total_amount': doc.get('dunning_amount'), + 'outstanding_amount': doc.get('dunning_amount'), + 'allocated_amount': doc.get('dunning_amount') + }) + else: + pe.append("references", { + 'reference_doctype': dt, + 'reference_name': dn, + "bill_no": doc.get("bill_no"), + "due_date": doc.get("due_date"), + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'allocated_amount': outstanding_amount + }) pe.setup_party_account_field() pe.set_missing_values() @@ -1172,4 +1199,4 @@ def make_payment_order(source_name, target_doc=None): }, target_doc, set_missing_values) - return doclist + return doclist \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 8bb741f0b2..772fc1a252 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -460,11 +460,8 @@ class TestPaymentEntry(unittest.TestCase): outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 0) - def test_payment_entry_against_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_payment_entry_against_sales_invoice_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -499,39 +496,8 @@ class TestPaymentEntry(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_payment_entry_against_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - si = create_sales_invoice(debit_to="Debtors - _TC") - - pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") - - pe.reference_no = "112211-2" - pe.reference_date = nowdate() - pe.paid_to = "_Test Bank - _TC" - pe.paid_amount = si.grand_total - pe.insert() - pe.submit() - - gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, - debit_in_account_currency, credit_in_account_currency - from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s - order by account asc""", pe.name, as_dict=1) - - self.assertTrue(gl_entries) - - for gle in gl_entries: - self.assertEqual(gle.cost_center, None) - - def test_payment_entry_against_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_payment_entry_against_purchase_invoice_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -566,40 +532,9 @@ class TestPaymentEntry(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_payment_entry_against_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - pi = make_purchase_invoice(credit_to="Creditors - _TC") - - pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") - - pe.reference_no = "112222-2" - pe.reference_date = nowdate() - pe.paid_from = "_Test Bank - _TC" - pe.paid_amount = pi.grand_total - pe.insert() - pe.submit() - - gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, - debit_in_account_currency, credit_in_account_currency - from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s - order by account asc""", pe.name, as_dict=1) - - self.assertTrue(gl_entries) - - for gle in gl_entries: - self.assertEqual(gle.cost_center, None) - - def test_payment_entry_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_payment_entry_account_and_party_balance_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.utils import get_balance_on - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -630,9 +565,6 @@ class TestPaymentEntry(unittest.TestCase): self.assertEqual(expected_party_balance, party_balance) self.assertEqual(expected_party_account_balance, party_account_balance) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - def create_payment_terms_template(): create_payment_term('Basic Amount Receivable') @@ -665,4 +597,4 @@ def create_payment_term(name): frappe.get_doc({ 'doctype': 'Payment Term', 'payment_term_name': name - }).insert() \ No newline at end of file + }).insert() diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py index 7ecdc41d03..4702e58cef 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.py +++ b/erpnext/accounts/doctype/payment_order/payment_order.py @@ -26,6 +26,7 @@ class PaymentOrder(Document): for d in self.references: frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status) +@frappe.whitelist() def get_mop_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference` where parent = %(parent)s and mode_of_payment like %(txt)s @@ -36,6 +37,7 @@ def get_mop_query(doctype, txt, searchfield, start, page_len, filters): 'txt': "%%%s%%" % txt }) +@frappe.whitelist() def get_supplier_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(""" select supplier from `tabPayment Order Reference` where parent = %(parent)s and supplier like %(txt)s and @@ -86,4 +88,4 @@ def make_journal_entry(doc, supplier, mode_of_payment=None): je.flags.ignore_mandatory = True je.save() - frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name)) \ No newline at end of file + frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name)) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index d3992d5111..355fe96c96 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -73,6 +73,10 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext }; } }); + + this.frm.set_value('party_type', ''); + this.frm.set_value('party', ''); + this.frm.set_value('receivable_payable_account', ''); }, refresh: function() { diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 3080496186..2f8b634664 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -48,7 +48,8 @@ class PaymentReconciliation(Document): select "Journal Entry" as reference_type, t1.name as reference_name, t1.posting_date, t1.remark as remarks, t2.name as reference_row, - {dr_or_cr} as amount, t2.is_advance + {dr_or_cr} as amount, t2.is_advance, + t2.account_currency as currency from `tabJournal Entry` t1, `tabJournal Entry Account` t2 where @@ -88,7 +89,8 @@ class PaymentReconciliation(Document): if self.party_type == 'Customer' else "Purchase Invoice") return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type, - (sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount + (sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount, + account_currency as currency FROM `tab{doc}`, `tabGL Entry` WHERE (`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no) @@ -101,10 +103,10 @@ class PaymentReconciliation(Document): Having amount > 0 """.format( - doc=voucher_type, - dr_or_cr=dr_or_cr, - reconciled_dr_or_cr=reconciled_dr_or_cr, - party_type_field=frappe.scrub(self.party_type)), + doc=voucher_type, + dr_or_cr=dr_or_cr, + reconciled_dr_or_cr=reconciled_dr_or_cr, + party_type_field=frappe.scrub(self.party_type)), { 'party': self.party, 'party_type': self.party_type, @@ -141,6 +143,7 @@ class PaymentReconciliation(Document): ent.invoice_number = e.get('voucher_no') ent.invoice_date = e.get('posting_date') ent.amount = flt(e.get('invoice_amount')) + ent.currency = e.get('currency') ent.outstanding_amount = e.get('outstanding_amount') def reconcile(self, args): @@ -170,7 +173,7 @@ class PaymentReconciliation(Document): reconcile_against_document(lst) if dr_or_cr_notes: - reconcile_dr_cr_note(dr_or_cr_notes) + reconcile_dr_cr_note(dr_or_cr_notes, self.company) msgprint(_("Successfully Reconciled")) self.get_unreconciled_entries() @@ -261,7 +264,7 @@ class PaymentReconciliation(Document): return cond -def reconcile_dr_cr_note(dr_cr_notes): +def reconcile_dr_cr_note(dr_cr_notes, company): for d in dr_cr_notes: voucher_type = ('Credit Note' if d.voucher_type == 'Sales Invoice' else 'Debit Note') @@ -269,10 +272,14 @@ def reconcile_dr_cr_note(dr_cr_notes): reconcile_dr_or_cr = ('debit_in_account_currency' if d.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency') + company_currency = erpnext.get_company_currency(company) + jv = frappe.get_doc({ "doctype": "Journal Entry", "voucher_type": voucher_type, "posting_date": today(), + "company": company, + "multi_currency": 1 if d.currency != company_currency else 0, "accounts": [ { 'account': d.account, @@ -280,7 +287,8 @@ def reconcile_dr_cr_note(dr_cr_notes): 'party_type': d.party_type, d.dr_or_cr: abs(d.allocated_amount), 'reference_type': d.against_voucher_type, - 'reference_name': d.against_voucher + 'reference_name': d.against_voucher, + 'cost_center': erpnext.get_default_cost_center(company) }, { 'account': d.account, @@ -289,7 +297,8 @@ def reconcile_dr_cr_note(dr_cr_notes): reconcile_dr_or_cr: (abs(d.allocated_amount) if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)), 'reference_type': d.voucher_type, - 'reference_name': d.voucher_no + 'reference_name': d.voucher_no, + 'cost_center': erpnext.get_default_cost_center(company) } ] }) diff --git a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json index ce7ce98edb..6a79a85c34 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json +++ b/erpnext/accounts/doctype/payment_reconciliation_invoice/payment_reconciliation_invoice.json @@ -1,183 +1,80 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2014-07-09 16:14:23.672922", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2014-07-09 16:14:23.672922", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "invoice_type", + "invoice_number", + "invoice_date", + "col_break1", + "amount", + "outstanding_amount", + "currency" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "invoice_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Invoice Type", - "length": 0, - "no_copy": 0, - "options": "Sales Invoice\nPurchase Invoice\nJournal Entry", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "invoice_type", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Invoice Type", + "options": "Sales Invoice\nPurchase Invoice\nJournal Entry", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "invoice_number", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Invoice Number", - "length": 0, - "no_copy": 0, - "options": "invoice_type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "invoice_number", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Invoice Number", + "options": "invoice_type", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "invoice_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Invoice Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "invoice_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Invoice Date", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "col_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "currency", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "outstanding_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Outstanding Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Outstanding Amount", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-07-11 03:28:03.588476", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Payment Reconciliation Invoice", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-07-19 18:12:27.964073", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Payment Reconciliation Invoice", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json index 018bfd028a..925a6f10a5 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json +++ b/erpnext/accounts/doctype/payment_reconciliation_payment/payment_reconciliation_payment.json @@ -1,7 +1,9 @@ { + "actions": [], "creation": "2014-07-09 16:13:35.452759", "doctype": "DocType", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "reference_type", "reference_name", @@ -16,7 +18,8 @@ "difference_account", "difference_amount", "sec_break1", - "remark" + "remark", + "currency" ], "fields": [ { @@ -73,6 +76,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Amount", + "options": "currency", "read_only": 1 }, { @@ -81,6 +85,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Allocated amount", + "options": "currency", "reqd": 1 }, { @@ -106,16 +111,25 @@ "fieldname": "difference_amount", "fieldtype": "Currency", "label": "Difference Amount", + "options": "currency", "print_hide": 1, "read_only": 1 }, { "fieldname": "section_break_10", "fieldtype": "Section Break" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Currency", + "options": "Currency" } ], "istable": 1, - "modified": "2019-06-24 00:08:11.150796", + "links": [], + "modified": "2020-07-19 18:12:41.682347", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Payment", diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index eef6be1a7a..8eadfd0b24 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -211,7 +211,7 @@ "label": "IBAN" }, { - "fetch_from": "bank.branch_code", + "fetch_from": "bank_account.branch_code", "fetch_if_empty": 1, "fieldname": "branch_code", "fieldtype": "Read Only", @@ -352,7 +352,7 @@ "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2020-05-29 17:38:49.392713", + "modified": "2020-07-17 14:06:42.185763", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js index 87e02fef1b..e923d4ed5e 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js @@ -25,9 +25,10 @@ frappe.ui.form.on('Period Closing Voucher', { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, - group_by_voucher: 0 + "group_by": "", + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, "fa fa-table"); diff --git a/erpnext/healthcare/doctype/normal_test_items/__init__.py b/erpnext/accounts/doctype/pos_closing_entry/__init__.py similarity index 100% rename from erpnext/healthcare/doctype/normal_test_items/__init__.py rename to erpnext/accounts/doctype/pos_closing_entry/__init__.py diff --git a/erpnext/selling/doctype/pos_closing_voucher/closing_voucher_details.html b/erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html similarity index 71% rename from erpnext/selling/doctype/pos_closing_voucher/closing_voucher_details.html rename to erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html index 2412b071b9..983f49563c 100644 --- a/erpnext/selling/doctype/pos_closing_voucher/closing_voucher_details.html +++ b/erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html @@ -12,15 +12,15 @@| {{ _("Account") }} | {{ _("Rate") }} | {{ _("Amount") }} | |
|---|---|---|---|
| {{ d.account_head }} | {{ d.rate }} % | -{{ d.amount }} {{ currency.symbol }} | +{{ frappe.utils.fmt_money(d.amount, currency=currency) }} |
' + html + '
') - .get(0); - } - }); - - this.prepare_customer_mapper() - this.autocomplete_customers(); - - this.party_field.$input - .on('input', function (e) { - if(me.customers_mapper.length <= 1) { - me.prepare_customer_mapper(e.target.value); - } - me.party_field.awesomeplete.list = me.customers_mapper; - }) - .on('awesomplete-select', function (e) { - var customer = me.party_field.awesomeplete - .get_item(e.originalEvent.text.value); - if (!customer) return; - // create customer link - if (customer.action) { - customer.action.apply(me); - return; - } - me.toggle_list_customer(false); - me.toggle_edit_button(true); - me.update_customer_data(customer); - me.refresh(); - me.set_focus(); - me.list_customers_btn.removeClass("view_customer"); - }) - .on('focus', function (e) { - $(e.target).val('').trigger('input'); - me.toggle_edit_button(false); - - if(me.frm.doc.items.length) { - me.toggle_list_customer(false) - me.toggle_item_cart(true) - } else { - me.toggle_list_customer(true) - me.toggle_item_cart(false) - } - }) - .on("awesomplete-selectcomplete", function (e) { - var item = me.party_field.awesomeplete - .get_item(e.originalEvent.text.value); - // clear text input if item is action - if (item.action) { - $(this).val(""); - } - me.make_item_list(item.customer_name); - }); - }, - - prepare_customer_mapper: function(key) { - var me = this; - var customer_data = ''; - - if (key) { - key = key.toLowerCase().trim(); - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '\\w*\\s*[a-zA-Z0-9]*')); - - customer_data = $.grep(this.customers, function(data) { - contact = me.contacts[data.name]; - if(reg.test(data.name.toLowerCase()) - || reg.test(data.customer_name.toLowerCase()) - || (contact && reg.test(contact["phone"])) - || (contact && reg.test(contact["mobile_no"])) - || (data.customer_group && reg.test(data.customer_group.toLowerCase()))){ - return data; - } - }) - } else { - customer_data = this.customers; - } - - this.customers_mapper = []; - - customer_data.forEach(function (c, index) { - if(index < 30) { - contact = me.contacts[c.name]; - if(contact && !c['phone']) { - c["phone"] = contact["phone"]; - c["email_id"] = contact["email_id"]; - c["mobile_no"] = contact["mobile_no"]; - } - - me.customers_mapper.push({ - label: c.name, - value: c.name, - customer_name: c.customer_name, - customer_group: c.customer_group, - territory: c.territory, - phone: contact ? contact["phone"] : '', - mobile_no: contact ? contact["mobile_no"] : '', - email_id: contact ? contact["email_id"] : '', - searchtext: ['customer_name', 'customer_group', 'name', 'value', - 'label', 'email_id', 'phone', 'mobile_no'] - .map(key => c[key]).join(' ') - .toLowerCase() - }); - } else { - return; - } - }); - - this.customers_mapper.push({ - label: "" - + " " - + __("Create a new Customer") - + "", - value: 'is_action', - action: me.add_customer - }); - }, - - autocomplete_customers: function() { - this.party_field.awesomeplete.list = this.customers_mapper; - }, - - toggle_edit_button: function(flag) { - this.page.wrapper.find('.edit-customer-btn').toggle(flag); - }, - - toggle_list_customer: function(flag) { - this.list_customers.toggle(flag); - }, - - toggle_item_cart: function(flag) { - this.wrapper.find('.pos-bill-wrapper').toggle(flag); - }, - - add_customer: function() { - this.frm.doc.customer = ""; - this.update_customer(true); - this.numeric_keypad.show(); - }, - - update_customer: function (new_customer) { - var me = this; - - this.customer_doc = new frappe.ui.Dialog({ - 'title': 'Customer', - fields: [ - { - "label": __("Full Name"), - "fieldname": "full_name", - "fieldtype": "Data", - "reqd": 1 - }, - { - "fieldtype": "Section Break" - }, - { - "label": __("Email Id"), - "fieldname": "email_id", - "fieldtype": "Data" - }, - { - "fieldtype": "Column Break" - }, - { - "label": __("Contact Number"), - "fieldname": "phone", - "fieldtype": "Data" - }, - { - "fieldtype": "Section Break" - }, - { - "label": __("Address Name"), - "read_only": 1, - "fieldname": "name", - "fieldtype": "Data" - }, - { - "label": __("Address Line 1"), - "fieldname": "address_line1", - "fieldtype": "Data" - }, - { - "label": __("Address Line 2"), - "fieldname": "address_line2", - "fieldtype": "Data" - }, - { - "fieldtype": "Column Break" - }, - { - "label": __("City"), - "fieldname": "city", - "fieldtype": "Data" - }, - { - "label": __("State"), - "fieldname": "state", - "fieldtype": "Data" - }, - { - "label": __("ZIP Code"), - "fieldname": "pincode", - "fieldtype": "Data" - }, - { - "label": __("Customer POS Id"), - "fieldname": "customer_pos_id", - "fieldtype": "Data", - "hidden": 1 - } - ] - }) - this.customer_doc.show() - this.render_address_data() - - this.customer_doc.set_primary_action(__("Save"), function () { - me.make_offline_customer(new_customer); - me.pos_bill.show(); - me.list_customers.hide(); - }); - }, - - render_address_data: function() { - var me = this; - this.address_data = this.address[this.frm.doc.customer] || {}; - if(!this.address_data.email_id || !this.address_data.phone) { - this.address_data = this.contacts[this.frm.doc.customer]; - } - - this.customer_doc.set_values(this.address_data) - if(!this.customer_doc.fields_dict.full_name.$input.val()) { - this.customer_doc.set_value("full_name", this.frm.doc.customer) - } - - if(!this.customer_doc.fields_dict.customer_pos_id.value) { - this.customer_doc.set_value("customer_pos_id", frappe.datetime.now_datetime()) - } - }, - - get_address_from_localstorage: function() { - this.address_details = this.get_customers_details() - return this.address_details[this.frm.doc.customer] - }, - - make_offline_customer: function(new_customer) { - this.frm.doc.customer = this.frm.doc.customer || this.customer_doc.get_values().full_name; - this.frm.doc.customer_pos_id = this.customer_doc.fields_dict.customer_pos_id.value; - this.customer_details = this.get_customers_details(); - this.customer_details[this.frm.doc.customer] = this.get_prompt_details(); - this.party_field.$input.val(this.frm.doc.customer); - this.update_address_and_customer_list(new_customer) - this.autocomplete_customers(); - this.update_customer_in_localstorage() - this.update_customer_in_localstorage() - this.customer_doc.hide() - }, - - update_address_and_customer_list: function(new_customer) { - var me = this; - if(new_customer) { - this.customers_mapper.push({ - label: this.frm.doc.customer, - value: this.frm.doc.customer, - customer_group: "", - territory: "" - }); - } - - this.address[this.frm.doc.customer] = JSON.parse(this.get_prompt_details()) - }, - - get_prompt_details: function() { - this.prompt_details = this.customer_doc.get_values(); - this.prompt_details['country'] = this.pos_profile_data.country; - this.prompt_details['territory'] = this.pos_profile_data["territory"]; - this.prompt_details['customer_group'] = this.pos_profile_data["customer_group"]; - this.prompt_details['customer_pos_id'] = this.customer_doc.fields_dict.customer_pos_id.value; - return JSON.stringify(this.prompt_details) - }, - - update_customer_data: function (doc) { - var me = this; - this.frm.doc.customer = doc.label || doc.name; - this.frm.doc.customer_name = doc.customer_name; - this.frm.doc.customer_group = doc.customer_group; - this.frm.doc.territory = doc.territory; - this.pos_bill.show(); - this.numeric_keypad.show(); - }, - - make_item_list: function (customer) { - var me = this; - if (!this.price_list) { - frappe.msgprint(__("Price List not found or disabled")); - return; - } - - me.item_timeout = null; - - var $wrap = me.wrapper.find(".item-list"); - me.wrapper.find(".item-list").empty(); - - if (this.items.length > 0) { - $.each(this.items, function(index, obj) { - let customer_price_list = me.customer_wise_price_list[customer]; - let item_price - if (customer && customer_price_list && customer_price_list[obj.name]) { - item_price = format_currency(customer_price_list[obj.name], me.frm.doc.currency); - } else { - item_price = format_currency(me.price_list_data[obj.name], me.frm.doc.currency); - } - if(index < me.page_len) { - $(frappe.render_template("pos_item", { - item_code: obj.name, - item_price: item_price, - item_name: obj.name === obj.item_name ? "" : obj.item_name, - item_image: obj.image, - item_stock: __('Stock Qty') + ": " + me.get_actual_qty(obj), - item_uom: obj.stock_uom, - color: frappe.get_palette(obj.item_name), - abbr: frappe.get_abbr(obj.item_name) - })).tooltip().appendTo($wrap); - } - }); - - $wrap.append(` -" - +__("Not items found")+"
").appendTo($wrap) - } - - if (this.items.length == 1 - && this.search_item.$input.val()) { - this.search_item.$input.val(""); - this.add_to_cart(); - } - }, - - get_items: function (item_code) { - // To search item as per the key enter - - var me = this; - this.item_serial_no = {}; - this.item_batch_no = {}; - - if (item_code) { - return $.grep(this.item_data, function (item) { - if (item.item_code == item_code) { - return true - } - }) - } - - this.items_list = this.apply_category(); - - key = this.search_item.$input.val().toLowerCase().replace(/[&\/\\#,+()\[\]$~.'":*?<>{}]/g, '\\$&'); - var re = new RegExp('%', 'g'); - var reg = new RegExp(key.replace(re, '[\\w*\\s*[a-zA-Z0-9]*]*')) - search_status = true - - if (key) { - return $.grep(this.items_list, function (item) { - if (search_status) { - if (me.batch_no_data[item.item_code] && - in_list(me.batch_no_data[item.item_code], me.search_item.$input.val())) { - search_status = false; - return me.item_batch_no[item.item_code] = me.search_item.$input.val() - } else if (me.serial_no_data[item.item_code] - && in_list(Object.keys(me.serial_no_data[item.item_code]), me.search_item.$input.val())) { - search_status = false; - me.item_serial_no[item.item_code] = [me.search_item.$input.val(), me.serial_no_data[item.item_code][me.search_item.$input.val()]] - return true - } else if (me.barcode_data[item.item_code] && - in_list(me.barcode_data[item.item_code], me.search_item.$input.val())) { - search_status = false; - return true; - } else if (reg.test(item.item_code.toLowerCase()) || (item.description && reg.test(item.description.toLowerCase())) || - reg.test(item.item_name.toLowerCase()) || reg.test(item.item_group.toLowerCase())) { - return true - } - } - }) - } else { - return this.items_list; - } - }, - - apply_category: function() { - var me = this; - category = this.selected_item_group || "All Item Groups"; - if(category == 'All Item Groups') { - return this.item_data - } else { - return this.item_data.filter(function(element, index, array){ - return element.item_group == category; - }); - } - }, - - bind_items_event: function() { - var me = this; - $(this.wrapper).on('click', '.pos-bill-item', function() { - $(me.wrapper).find('.pos-bill-item').removeClass('active'); - $(this).addClass('active'); - me.numeric_val = ""; - me.numeric_id = "" - me.item_code = $(this).attr("data-item-code"); - me.render_selected_item() - me.bind_qty_event() - me.update_rate() - $(me.wrapper).find(".selected-item").scrollTop(1000); - }) - }, - - bind_qty_event: function () { - var me = this; - - $(this.wrapper).on("change", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var qty = $(this).val(); - me.update_qty(item_code, qty); - me.update_value(); - }) - - $(this.wrapper).on("focusout", ".pos-item-qty", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var qty = $(this).val(); - me.update_qty(item_code, qty, true); - me.update_value(); - }) - - $(this.wrapper).find("[data-action='increase-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) + 1; - me.update_qty(item_code, qty); - }) - - $(this.wrapper).find("[data-action='decrease-qty']").on("click", function () { - var item_code = $(this).parents(".pos-bill-item").attr("data-item-code"); - var qty = flt($(this).parents(".pos-bill-item").find('.pos-item-qty').val()) - 1; - me.update_qty(item_code, qty); - }) - - $(this.wrapper).on("change", ".pos-item-disc", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - var discount = $(this).val(); - if(discount > 100){ - discount = $(this).val(''); - frappe.show_alert({ - indicator: 'red', - message: __('Discount amount cannot be greater than 100%') - }); - me.update_discount(item_code, discount); - }else{ - me.update_discount(item_code, discount); - me.update_value(); - } - }) - }, - - bind_events: function() { - var me = this; - // if form is local then allow this function - // $(me.wrapper).find(".pos-item-wrapper").on("click", function () { - $(this.wrapper).on("click", ".pos-item-wrapper", function () { - me.item_code = ''; - me.customer_validate(); - if($(me.pos_bill).is(":hidden")) return; - - if (me.frm.doc.docstatus == 0) { - me.items = me.get_items($(this).attr("data-item-code")) - me.add_to_cart(); - me.clear_selected_row(); - } - }); - - me.bind_delete_event() - }, - - update_qty: function (item_code, qty, remove_zero_qty_items) { - var me = this; - this.items = this.get_items(item_code); - this.validate_serial_no() - this.set_item_details(item_code, "qty", qty, remove_zero_qty_items); - }, - - update_discount: function(item_code, discount) { - var me = this; - this.items = this.get_items(item_code); - this.set_item_details(item_code, "discount_percentage", discount); - }, - - update_rate: function () { - var me = this; - $(this.wrapper).on("change", ".pos-item-price", function () { - var item_code = $(this).parents(".pos-selected-item-action").attr("data-item-code"); - me.set_item_details(item_code, "rate", $(this).val()); - me.update_value() - }) - }, - - update_value: function() { - var me = this; - var fields = {qty: ".pos-item-qty", "discount_percentage": ".pos-item-disc", - "rate": ".pos-item-price", "amount": ".pos-amount"} - this.child_doc = this.get_child_item(this.item_code); - - if(me.child_doc.length) { - $.each(fields, function(key, field) { - $(me.selected_row).find(field).val(me.child_doc[0][key]) - }) - } else { - this.clear_selected_row(); - } - }, - - clear_selected_row: function() { - $(this.wrapper).find('.selected-item').empty(); - }, - - render_selected_item: function() { - this.child_doc = this.get_child_item(this.item_code); - $(this.wrapper).find('.selected-item').empty(); - if(this.child_doc.length) { - this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false, - this.child_doc[0]["allow_user_to_edit_discount"] = this.pos_profile_data["allow_user_to_edit_discount"] ? true : false; - this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0])) - $(this.wrapper).find('.selected-item').html(this.selected_row) - } - - $(this.selected_row).find('.form-control').click(function(){ - $(this).select(); - }) - }, - - get_child_item: function(item_code) { - var me = this; - return $.map(me.frm.doc.items, function(doc){ - if(doc.item_code == item_code) { - return doc - } - }) - }, - - set_item_details: function (item_code, field, value, remove_zero_qty_items) { - var me = this; - if (value < 0) { - frappe.throw(__("Enter value must be positive")); - } - - this.remove_item = [] - $.each(this.frm.doc["items"] || [], function (i, d) { - if (d.item_code == item_code) { - if (d.serial_no && field == 'qty') { - me.validate_serial_no_qty(d, item_code, field, value) - } - - d[field] = flt(value); - d.amount = flt(d.rate) * flt(d.qty); - if (d.qty == 0 && remove_zero_qty_items) { - me.remove_item.push(d.idx) - } - - if(field=="discount_percentage" && value == 0) { - d.rate = d.price_list_rate; - } - } - }); - - if (field == 'qty') { - this.remove_zero_qty_items_from_cart(); - } - - this.update_paid_amount_status(false) - }, - - remove_zero_qty_items_from_cart: function () { - var me = this; - var idx = 0; - this.items = [] - $.each(this.frm.doc["items"] || [], function (i, d) { - if (!in_list(me.remove_item, d.idx)) { - d.idx = idx; - me.items.push(d); - idx++; - } - }); - - this.frm.doc["items"] = this.items; - }, - - make_discount_field: function () { - var me = this; - - this.wrapper.find('input.discount-percentage').on("change", function () { - me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage")); - - if(me.frm.doc.additional_discount_percentage && me.frm.doc.discount_amount) { - // Reset discount amount - me.frm.doc.discount_amount = 0; - } - - var total = me.frm.doc.grand_total - - if (me.frm.doc.apply_discount_on == 'Net Total') { - total = me.frm.doc.net_total - } - - me.frm.doc.discount_amount = flt(total * flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount")); - me.refresh(); - me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount) - }); - - this.wrapper.find('input.discount-amount').on("change", function () { - me.frm.doc.discount_amount = flt($(this).val(), precision("discount_amount")); - me.frm.doc.additional_discount_percentage = 0.0; - me.refresh(); - me.wrapper.find('input.discount-percentage').val(0); - }); - }, - - customer_validate: function () { - var me = this; - if (!this.frm.doc.customer || this.party_field.get_value() == "") { - frappe.throw(__("Please select customer")) - } - }, - - add_to_cart: function () { - var me = this; - var caught = false; - var no_of_items = me.wrapper.find(".pos-bill-item").length; - - this.customer_validate(); - this.mandatory_batch_no(); - this.validate_serial_no(); - this.validate_warehouse(); - - if (no_of_items != 0) { - $.each(this.frm.doc["items"] || [], function (i, d) { - if (d.item_code == me.items[0].item_code) { - caught = true; - d.qty += 1; - d.amount = flt(d.rate) * flt(d.qty); - if (me.item_serial_no[d.item_code]) { - d.serial_no += '\n' + me.item_serial_no[d.item_code][0] - d.warehouse = me.item_serial_no[d.item_code][1] - } - - if (me.item_batch_no.length) { - d.batch_no = me.item_batch_no[d.item_code] - } - } - }); - } - - // if item not found then add new item - if (!caught) - this.add_new_item_to_grid(); - - this.update_paid_amount_status(false) - this.wrapper.find(".item-cart-items").scrollTop(1000); - }, - - add_new_item_to_grid: function () { - var me = this; - this.child = frappe.model.add_child(this.frm.doc, this.frm.doc.doctype + " Item", "items"); - this.child.item_code = this.items[0].item_code; - this.child.item_name = this.items[0].item_name; - this.child.stock_uom = this.items[0].stock_uom; - this.child.uom = this.items[0].sales_uom || this.items[0].stock_uom; - this.child.conversion_factor = this.items[0].conversion_factor || 1; - this.child.brand = this.items[0].brand; - this.child.description = this.items[0].description || this.items[0].item_name; - this.child.discount_percentage = 0.0; - this.child.qty = 1; - this.child.item_group = this.items[0].item_group; - this.child.cost_center = this.pos_profile_data['cost_center'] || this.items[0].cost_center; - this.child.income_account = this.pos_profile_data['income_account'] || this.items[0].income_account; - this.child.warehouse = (this.item_serial_no[this.child.item_code] - ? this.item_serial_no[this.child.item_code][1] : (this.pos_profile_data['warehouse'] || this.items[0].default_warehouse)); - - customer = this.frm.doc.customer; - let rate; - - customer_price_list = this.customer_wise_price_list[customer] - if (customer_price_list && customer_price_list[this.child.item_code]){ - rate = flt(this.customer_wise_price_list[customer][this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9); - } - else{ - rate = flt(this.price_list_data[this.child.item_code] * this.child.conversion_factor, 9) / flt(this.frm.doc.conversion_rate, 9); - } - - this.child.price_list_rate = rate; - this.child.rate = rate; - this.child.actual_qty = me.get_actual_qty(this.items[0]); - this.child.amount = flt(this.child.qty) * flt(this.child.rate); - this.child.batch_no = this.item_batch_no[this.child.item_code]; - this.child.serial_no = (this.item_serial_no[this.child.item_code] - ? this.item_serial_no[this.child.item_code][0] : ''); - this.child.item_tax_rate = JSON.stringify(this.tax_data[this.child.item_code]); - }, - - update_paid_amount_status: function (update_paid_amount) { - if (this.frm.doc.offline_pos_name) { - update_paid_amount = update_paid_amount ? false : true; - } - - this.refresh(update_paid_amount); - }, - - refresh: function (update_paid_amount) { - var me = this; - this.refresh_fields(update_paid_amount); - this.set_primary_action(); - }, - - refresh_fields: function (update_paid_amount) { - this.apply_pricing_rule(); - this.discount_amount_applied = false; - this._calculate_taxes_and_totals(); - this.calculate_discount_amount(); - this.show_items_in_item_cart(); - this.set_taxes(); - this.calculate_outstanding_amount(update_paid_amount); - this.set_totals(); - this.update_total_qty(); - }, - - get_company_currency: function () { - return erpnext.get_currency(this.frm.doc.company); - }, - - show_items_in_item_cart: function () { - var me = this; - var $items = this.wrapper.find(".items").empty(); - var $no_items_message = this.wrapper.find(".no-items-message"); - $no_items_message.toggle(this.frm.doc.items.length === 0); - - var $totals_area = this.wrapper.find('.totals-area'); - $totals_area.toggle(this.frm.doc.items.length > 0); - - $.each(this.frm.doc.items || [], function (i, d) { - $(frappe.render_template("pos_bill_item_new", { - item_code: d.item_code, - item_name: (d.item_name === d.item_code || !d.item_name) ? "" : ("| {{_(\\\"Description\\\")}} | \\n\\t{{_(\\\"Amount\\\")}} | \\n
|---|---|
| \\n {{_(\\\"Outstanding Amount\\\")}}\\n | \\n\\n {{doc.get_formatted(\\\"outstanding_amount\\\")}}\\n | \\n
| \\n {{_(\\\"Interest \\\")}} {{doc.rate_of_interest}}% p.a. ({{doc.overdue_days}} {{_(\\\"days\\\")}})\\n | \\n\\n {{doc.get_formatted(\\\"interest_amount\\\")}}\\n | \\n
| \\n {{_(\\\"Dunning Fee\\\")}}\\n | \\n\\n {{doc.get_formatted(\\\"dunning_fee\\\")}}\\n | \\n
\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t {{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"Serial No\") }}: {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.rate }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t | \n\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t {{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"Serial No\") }}: {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.rate }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t | \n\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", "idx": 0, "line_breaks": 0, - "modified": "2019-12-09 17:39:23.356573", + "modified": "2020-04-29 16:39:12.936215", "modified_by": "Administrator", "module": "Accounts", "name": "GST POS Invoice", diff --git a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json index be699228c5..13a973d234 100644 --- a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json @@ -6,10 +6,10 @@ "doc_type": "Sales Invoice", "docstatus": 0, "doctype": "Print Format", - "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{{ row.description }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", "idx": 1, "line_breaks": 0, - "modified": "2019-12-09 17:40:53.183574", + "modified": "2020-04-29 16:35:07.043058", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 2aa9618e55..6abd6e5cf7 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -17,41 +17,6 @@ frappe.query_reports["Accounts Payable"] = { "fieldtype": "Date", "default": frappe.datetime.get_today() }, - { - "fieldname":"ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date\nSupplier Invoice Date', - "default": "Due Date" - }, - { - "fieldname":"range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 - }, - { - "fieldname":"range2", - "label": __("Ageing Range 2"), - "fieldtype": "Int", - "default": "60", - "reqd": 1 - }, - { - "fieldname":"range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "reqd": 1 - }, - { - "fieldname":"range4", - "label": __("Ageing Range 4"), - "fieldtype": "Int", - "default": "120", - "reqd": 1 - }, { "fieldname":"finance_book", "label": __("Finance Book"), @@ -88,6 +53,41 @@ frappe.query_reports["Accounts Payable"] = { } } }, + { + "fieldname":"ageing_based_on", + "label": __("Ageing Based On"), + "fieldtype": "Select", + "options": 'Posting Date\nDue Date\nSupplier Invoice Date', + "default": "Due Date" + }, + { + "fieldname":"range1", + "label": __("Ageing Range 1"), + "fieldtype": "Int", + "default": "30", + "reqd": 1 + }, + { + "fieldname":"range2", + "label": __("Ageing Range 2"), + "fieldtype": "Int", + "default": "60", + "reqd": 1 + }, + { + "fieldname":"range3", + "label": __("Ageing Range 3"), + "fieldtype": "Int", + "default": "90", + "reqd": 1 + }, + { + "fieldname":"range4", + "label": __("Ageing Range 4"), + "fieldtype": "Int", + "default": "120", + "reqd": 1 + }, { "fieldname":"payment_terms_template", "label": __("Payment Terms Template"), diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 8dc558a611..c999eb9b8e 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -17,41 +17,6 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Date", "default": frappe.datetime.get_today() }, - { - "fieldname":"ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date', - "default": "Due Date" - }, - { - "fieldname":"range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 - }, - { - "fieldname":"range2", - "label": __("Ageing Range 2"), - "fieldtype": "Int", - "default": "60", - "reqd": 1 - }, - { - "fieldname":"range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "reqd": 1 - }, - { - "fieldname":"range4", - "label": __("Ageing Range 4"), - "fieldtype": "Int", - "default": "120", - "reqd": 1 - }, { "fieldname":"finance_book", "label": __("Finance Book"), @@ -101,6 +66,41 @@ frappe.query_reports["Accounts Receivable"] = { } } }, + { + "fieldname":"ageing_based_on", + "label": __("Ageing Based On"), + "fieldtype": "Select", + "options": 'Posting Date\nDue Date', + "default": "Due Date" + }, + { + "fieldname":"range1", + "label": __("Ageing Range 1"), + "fieldtype": "Int", + "default": "30", + "reqd": 1 + }, + { + "fieldname":"range2", + "label": __("Ageing Range 2"), + "fieldtype": "Int", + "default": "60", + "reqd": 1 + }, + { + "fieldname":"range3", + "label": __("Ageing Range 3"), + "fieldtype": "Int", + "default": "90", + "reqd": 1 + }, + { + "fieldname":"range4", + "label": __("Ageing Range 4"), + "fieldtype": "Int", + "default": "120", + "reqd": 1 + }, { "fieldname":"customer_group", "label": __("Customer Group"), @@ -113,12 +113,6 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Link", "options": "Payment Terms Template" }, - { - "fieldname":"territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory" - }, { "fieldname":"sales_partner", "label": __("Sales Partner"), @@ -131,6 +125,12 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Link", "options": "Sales Person" }, + { + "fieldname":"territory", + "label": __("Territory"), + "fieldtype": "Link", + "options": "Territory" + }, { "fieldname": "group_by_party", "label": __("Group By Customer"), diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py index 8566f5375a..fe2bc725e0 100644 --- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py +++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py @@ -334,10 +334,9 @@ def compute_data(filters, company_currency, profit_data, period_list, light_mapp def execute(filters=None): if not filters.periodicity: filters.periodicity = "Monthly" - period_list = get_period_list( - filters.from_fiscal_year, filters.to_fiscal_year, filters.periodicity, - filters.accumulated_values, filters.company - ) + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + filters.period_start_date, filters.period_end_date, filters.filter_based_on, + filters.periodicity, company=filters.company) mappers = get_mappers_from_db() @@ -396,7 +395,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) from `tabGL Entry` - where company=%s and posting_date >= %s and posting_date <= %s + where company=%s and posting_date >= %s and posting_date <= %s and voucher_type != 'Period Closing Voucher' and account in ( SELECT name FROM tabAccount WHERE name IN (%s) OR parent_account IN (%s)) @@ -405,7 +404,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) from `tabGL Entry` - where company=%s and posting_date >= %s and posting_date <= %s + where company=%s and posting_date >= %s and posting_date <= %s and voucher_type != 'Period Closing Voucher' and account in ( SELECT name FROM tabAccount WHERE name IN (%s) OR parent_account IN (%s)) diff --git a/erpnext/accounts/report/financial_statements.html b/erpnext/accounts/report/financial_statements.html index 50947ecf5e..2bb09cf0dc 100644 --- a/erpnext/accounts/report/financial_statements.html +++ b/erpnext/accounts/report/financial_statements.html @@ -44,7 +44,7 @@ - {% for(let j=0, k=data.length-1; j| Name of Test | \nResult | \nNormal Range | \n
|---|---|---|
| {{ doc.lab_test_name }} | ||
| \n {%- if doc.normal_test_items|length > 1 %} {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%} {%- endif -%}\n {%- if row.lab_test_event -%} {{ row.lab_test_event }}{%- endif -%}\n | \n\n\n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%} \n {%- if row.lab_test_uom -%}{{ row.lab_test_uom }}{%- endif -%}\n | \n\n\n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n \n | \n
| Name of Test | \nResult | \n
|---|---|
| {{ doc.lab_test_name }} | |
| {{ row.lab_test_particulars }} | \n\n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n | \n
| Antibiotic | \nSensitivity | \n
| {{ row.antibiotic }} | \n{{ row.antibiotic_sensitivity }} | \n
| Name of Test | \nResult | \nNormal Range | \n
|---|---|---|
| {{ doc.lab_test_name }} | ||
| \n {%- if doc.normal_test_items|length > 1 %} {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%} {%- endif -%}\n {%- if row.lab_test_event -%} {{ row.lab_test_event }}{%- endif -%}\n | \n\n\n {%- if row.lab_test_uom -%} {{ row.lab_test_uom }}{%- endif -%}\n | \n\n\n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n \n | \n
| Name of Test | \nResult | \n
|---|---|
| {{ doc.lab_test_name }} | |
| {{ row.lab_test_name }} | \n\t\t\t\t\n\t\t\t |
| {{ row.lab_test_particulars }} | \n\n |
| Name of Test | \nResult | \nNormal Range | \n
|---|---|---|
| {{ doc.lab_test_name }} | ||
| \n {%- if doc.normal_test_items|length > 1 %} {%- endif -%}\n {%- if row.lab_test_name -%}{{ row.lab_test_name }}\n {%- else -%} {%- endif -%}\n {%- if row.lab_test_event -%} {{ row.lab_test_event }}{%- endif -%}\n | \n\n\n\t\t\t\t\t{%- if row.result_value -%}\n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n {{ row.result_value }}\n {%- if row.lab_test_uom -%} {{ row.lab_test_uom }}{%- endif -%}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t{%- endif -%}\n \n\t\t\t\t\t{%- if row.secondary_uom and row.conversion_factor and row.secondary_uom_result -%}\n\t\t\t\t\t\t \n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n {{ row.secondary_uom_result }}\n {{ row.secondary_uom }}\n\t\t\t\t\t\t{%- if row.italic -%}{% endif %}\n\t\t\t\t\t\t{%- if row.underline -%}{% endif %}\n\t\t\t\t\t\t{%- if row.bold -%}{% endif %}\n\t\t\t\t\t\t \n\t\t\t\t\t{%- endif -%}\n | \n\n \n \n {%- if row.normal_range -%}{{ row.normal_range }}{%- endif -%}\n \n | \n
| Name of Test | \nResult | \n
|---|---|
| {{ doc.lab_test_name }} | |
| {{ row.lab_test_name }} | \n\t\t\t\t\n\t\t\t |
| {{ row.lab_test_particulars }} | \n\n {%- if row.result_value -%}{{ row.result_value }}{%- endif -%}\n | \n
| Organism | \n\t\t\t\tColony Population | \n\t\t\t
| {{ row.organism }} | \n\t\t\t\t\n\t\t\t\t\t{{ row.colony_population }}\n\t\t\t\t\t{% if row.colony_uom %}\n\t\t\t\t\t\t{{ row.colony_uom }}\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t
| Antibiotic | \n\t\t\t\tSensitivity | \n\t\t\t
| {{ row.antibiotic }} | \n\t\t\t\t{{ row.antibiotic_sensitivity }} | \n\t\t\t
Employment Type = employment_type, Branch = branch etc.
Payment Days = payment_days, Leave without pay = leave_without_pay etc.
\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t {{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"SR.No\") }}: \n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.rate }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t\t{% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t | \n\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "idx": 0, + "line_breaks": 0, + "modified": "2020-04-29 16:47:02.743246", + "modified_by": "Administrator", + "module": "Selling", + "name": "GST POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/selling/print_format/pos_invoice/__init__.py b/erpnext/selling/print_format/pos_invoice/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/selling/print_format/pos_invoice/pos_invoice.json b/erpnext/selling/print_format/pos_invoice/pos_invoice.json new file mode 100644 index 0000000000..99094ed9b0 --- /dev/null +++ b/erpnext/selling/print_format/pos_invoice/pos_invoice.json @@ -0,0 +1,22 @@ +{ + "align_labels_right": 0, + "creation": "2011-12-21 11:08:55", + "custom_format": 1, + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"SR.No\") }}: \n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "idx": 1, + "line_breaks": 0, + "modified": "2020-04-29 16:45:58.942375", + "modified_by": "Administrator", + "module": "Selling", + "name": "POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/selling/print_format/return_pos_invoice/__init__.py b/erpnext/selling/print_format/return_pos_invoice/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/selling/print_format/return_pos_invoice/return_pos_invoice.json b/erpnext/selling/print_format/return_pos_invoice/return_pos_invoice.json new file mode 100644 index 0000000000..d7f335059c --- /dev/null +++ b/erpnext/selling/print_format/return_pos_invoice/return_pos_invoice.json @@ -0,0 +1,24 @@ +{ + "align_labels_right": 0, + "creation": "2020-05-14 17:02:44.207166", + "custom_format": 1, + "default_print_language": "en", + "disabled": 0, + "doc_type": "POS Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Return Invoice\") }}
\n
\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Original Invoice\") }}: {{ doc.return_against }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n
| {{ _(\"Item\") }} | \n\t\t\t{{ _(\"Qty\") }} | \n\t\t\t{{ _(\"Amount\") }} | \n\t\t
|---|---|---|
| \n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t {{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t {{ _(\"SR.No\") }}: \n\t\t\t\t\t{{ item.serial_no | replace(\"\\n\", \", \") }}\n\t\t\t\t{%- endif -%}\n\t\t\t | \n\t\t\t{{ item.qty }} @ {{ item.get_formatted(\"rate\") }} | \n\t\t\t{{ item.get_formatted(\"amount\") }} | \n\t\t
| \n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t | \n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t | \n\t\t\t{% endif %}\n\t\t
| \n\t\t\t\t {% if '%' in row.description %}\n\t\t\t\t\t {{ row.description }}\n\t\t\t\t\t{% else %}\n\t\t\t\t\t {{ row.description }}@{{ row.rate }}%\n\t\t\t\t\t{% endif %}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc)}}\n\t\t\t\t | \n\t\t\t||
| \n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t | \n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t | \n\t\t||
| \n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t | \n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\")}}\n\t\t\t\t | \n\t\t\t
{{ doc.terms or \"\" }}
\n{{ _(\"Thank you, please visit again.\") }}
", + "idx": 0, + "line_breaks": 0, + "modified": "2020-05-14 17:13:29.354015", + "modified_by": "Administrator", + "module": "Selling", + "name": "Return POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index bd59be663a..0a70b97648 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -9,6 +9,9 @@ from frappe.utils.nestedset import get_descendants_of def execute(filters=None): filters = frappe._dict(filters or {}) + if filters.from_date > filters.to_date: + frappe.throw(_('From Date cannot be greater than To Date')) + columns = get_columns(filters) data = get_data(filters) @@ -188,7 +191,7 @@ def get_conditions(filters): conditions += "AND so_item.item_code = '%s'" %frappe.db.escape(filters.item_code) if filters.get("customer"): - conditions += "AND so.customer = '%s'" %frappe.db.escape(filters.customer) + conditions += "AND so.customer = %s" %frappe.db.escape(filters.customer) return conditions diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py index 14d8031582..e89c45182f 100644 --- a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py +++ b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py @@ -158,7 +158,7 @@ def get_data(): } pending_so.append(so_record) else: - for item in bundled_item_map.get((so.name, so.item_code)): + for item in bundled_item_map.get((so.name, so.item_code), []): material_requests_against_so = materials_request_dict.get((so.name, item.item_code)) or {} if flt(item.qty) > flt(material_requests_against_so.get('qty')): so_record = { diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 4a7dd5ad9b..333a563aa5 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -142,7 +142,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]); // check if child doctype is Sales Order Item/Qutation Item and calculate the rate - if(in_list(["Quotation Item", "Sales Order Item", "Delivery Note Item", "Sales Invoice Item"]), cdt) + if(in_list(["Quotation Item", "Sales Order Item", "Delivery Note Item", "Sales Invoice Item", "POS Invoice Item"]), cdt) this.apply_pricing_rule_on_item(item); else item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0), @@ -312,6 +312,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ batch_no: function(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); + + if (item.serial_no) { + return; + } + item.serial_no = null; var has_serial_no; frappe.db.get_value('Item', {'item_code': item.item_code}, 'has_serial_no', (r) => { diff --git a/erpnext/selling/selling_dashboard/selling/selling.json b/erpnext/selling/selling_dashboard/selling/selling.json new file mode 100644 index 0000000000..52e6714965 --- /dev/null +++ b/erpnext/selling/selling_dashboard/selling/selling.json @@ -0,0 +1,46 @@ +{ + "cards": [ + { + "card": "Annual Sales" + }, + { + "card": "Sales Orders to Deliver" + }, + { + "card": "Sales Orders to Bill" + }, + { + "card": "Active Customers" + } + ], + "charts": [ + { + "chart": "Sales Order Trends", + "width": "Full" + }, + { + "chart": "Top Customers", + "width": "Half" + }, + { + "chart": "Sales Order Analysis", + "width": "Half" + }, + { + "chart": "Item-wise Annual Sales", + "width": "Full" + } + ], + "creation": "2020-07-20 20:17:16.688162", + "dashboard_name": "Selling", + "docstatus": 0, + "doctype": "Dashboard", + "idx": 0, + "is_default": 0, + "is_standard": 1, + "modified": "2020-07-22 15:31:22.299903", + "modified_by": "Administrator", + "module": "Selling", + "name": "Selling", + "owner": "Administrator" +} \ No newline at end of file diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 875904fe6f..7ae5385a23 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -264,7 +264,10 @@ erpnext.company.setup_queries = function(frm) { ["expenses_included_in_valuation", {"root_type": "Expense", "account_type": "Expenses Included in Valuation"}], ["stock_received_but_not_billed", - {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}] + {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}], + ["service_received_but_not_billed", + {"root_type": "Liability", "account_type": "Service Received But Not Billed"}], + ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 020a93ff6a..221044df3a 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -22,7 +22,6 @@ "default_letter_head", "default_holiday_list", "default_finance_book", - "standard_working_hours", "default_selling_terms", "default_buying_terms", "default_warehouse_for_sales_return", @@ -67,10 +66,12 @@ "payment_terms", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", + "enable_perpetual_inventory_for_non_stock_items", "default_inventory_account", "stock_adjustment_account", "column_break_32", "stock_received_but_not_billed", + "service_received_but_not_billed", "expenses_included_in_valuation", "fixed_asset_depreciation_settings", "accumulated_depreciation_account", @@ -238,11 +239,6 @@ "label": "Default Holiday List", "options": "Holiday List" }, - { - "fieldname": "standard_working_hours", - "fieldtype": "Float", - "label": "Standard Working Hours" - }, { "fieldname": "default_warehouse_for_sales_return", "fieldtype": "Link", @@ -723,6 +719,20 @@ "fieldtype": "Link", "label": "Default Buying Terms", "options": "Terms and Conditions" + }, + { + "fieldname": "service_received_but_not_billed", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Service Received But Not Billed", + "no_copy": 1, + "options": "Account" + }, + { + "default": "0", + "fieldname": "enable_perpetual_inventory_for_non_stock_items", + "fieldtype": "Check", + "label": "Enable Perpetual Inventory For Non Stock Items" } ], "icon": "fa fa-building", @@ -730,7 +740,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2020-03-21 18:09:53.534211", + "modified": "2020-06-24 12:45:31.462195", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 8bcaa28707..47b41a97ad 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -46,6 +46,7 @@ class Company(NestedSet): self.validate_currency() self.validate_coa_input() self.validate_perpetual_inventory() + self.validate_perpetual_inventory_for_non_stock_items() self.check_country_change() self.set_chart_of_accounts() self.validate_parent_company() @@ -182,6 +183,12 @@ class Company(NestedSet): frappe.msgprint(_("Set default inventory account for perpetual inventory"), alert=True, indicator='orange') + def validate_perpetual_inventory_for_non_stock_items(self): + if not self.get("__islocal"): + if cint(self.enable_perpetual_inventory_for_non_stock_items) == 1 and not self.service_received_but_not_billed: + frappe.throw(_("Set default {0} account for perpetual inventory for non stock items").format( + frappe.bold('Service Received But Not Billed'))) + def check_country_change(self): frappe.flags.country_change = False diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index c199a8e57f..44a5019120 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_customer_group) { + if(!doc.parent_customer_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root customer group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.customer_group_name] } } } diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index f62613ea1b..68e1ccb635 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -6,9 +6,12 @@ import frappe from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class CustomerGroup(NestedSet): nsm_parent_field = 'parent_customer_group' + def validate(self): + if not self.parent_customer_group: + self.parent_customer_group = get_root_of("Customer Group") def on_update(self): self.validate_name_with_customer() diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 7c0be3bff6..b30bd7814b 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -405,8 +405,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0), count(*) from `tabSales Order` - where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed" - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed" and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_bill"), report_type="Report Builder", @@ -430,8 +430,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0), count(*) from `tabSales Order` - where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered" - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered" and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_deliver"), report_type="Report Builder", @@ -455,8 +455,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total))-(sum(grand_total*per_received/100)),0), count(*) from `tabPurchase Order` - where (transaction_date <= %(to_date)s) and per_received < 100 - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and per_received < 100 and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_receive"), report_type="Report Builder", @@ -480,8 +480,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0), count(*) from `tabPurchase Order` - where (transaction_date <= %(to_date)s) and per_billed < 100 - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and per_billed < 100 and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_bill"), report_type="Report Builder", diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js index df2223192b..9892dc3dcc 100644 --- a/erpnext/setup/doctype/item_group/item_group.js +++ b/erpnext/setup/doctype/item_group/item_group.js @@ -66,7 +66,7 @@ frappe.ui.form.on("Item Group", { set_root_readonly: function(frm) { // read-only for root item group frm.set_intro(""); - if(!frm.doc.parent_item_group) { + if(!frm.doc.parent_item_group && !frm.doc.__islocal) { frm.set_read_only(); frm.set_intro(__("This is a root item group and cannot be edited."), true); } diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index 89ca4a9dd7..8f7593d6ee 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -19,7 +19,7 @@ frappe.ui.form.on('Sales Person', { } } }; - + frm.make_methods = { 'Sales Order': () => frappe.new_doc("Sales Order") .then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name})) @@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root - if(!doc.parent_sales_person) { + if(!doc.parent_sales_person && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root sales person and cannot be edited.")); } else { diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py index 3379534cf8..19c2e5b954 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.py +++ b/erpnext/setup/doctype/sales_person/sales_person.py @@ -5,13 +5,16 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of from erpnext import get_default_currency class SalesPerson(NestedSet): nsm_parent_field = 'parent_sales_person' def validate(self): + if not self.parent_sales_person: + self.parent_sales_person = get_root_of("Sales Person") + for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): frappe.throw(_("Either target qty or target amount is mandatory.")) diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index ac5bda6e2c..e75030d441 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_supplier_group) { + if(!doc.parent_supplier_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root supplier group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_supplier_group'].get_query = function() { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.supplier_group_name] } }; }; diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js index 1eb9958ce7..ceec47ae8c 100644 --- a/erpnext/setup/doctype/territory/territory.js +++ b/erpnext/setup/doctype/territory/territory.js @@ -20,7 +20,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root territory - if(!doc.parent_territory) { + if(!doc.parent_territory && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root territory and cannot be edited.")); } else { diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py index 808b5386ab..05e8f666cf 100644 --- a/erpnext/setup/doctype/territory/territory.py +++ b/erpnext/setup/doctype/territory/territory.py @@ -6,12 +6,14 @@ import frappe from frappe.utils import flt from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class Territory(NestedSet): nsm_parent_field = 'parent_territory' def validate(self): + if not self.parent_territory: + self.parent_territory = get_root_of("Territory") for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 74ff0ecfd8..aa9fbc0a92 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -29,12 +29,10 @@ def after_install(): def check_setup_wizard_not_completed(): - if frappe.db.get_default('desktop:home_page') == 'desktop': - print() - print("ERPNext can only be installed on a fresh site where the setup wizard is not completed") - print("You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall") - print() - return False + if frappe.db.get_default('desktop:home_page') != 'setup-wizard': + message = """ERPNext can only be installed on a fresh site where the setup wizard is not completed. +You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall""" + frappe.throw(message) def set_single_defaults(): @@ -105,4 +103,3 @@ def add_company_to_session_defaults(): "ref_doctype": "Company" }) settings.save() - diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js index e1510f5335..14500ba6b3 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js @@ -1,25 +1,16 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -$.extend(cur_frm.cscript, { - onload: function() { - if(cur_frm.doc.__onload && cur_frm.doc.__onload.quotation_series) { - cur_frm.fields_dict.quotation_series.df.options = cur_frm.doc.__onload.quotation_series; - cur_frm.refresh_field("quotation_series"); +frappe.ui.form.on("Shopping Cart Settings", { + onload: function(frm) { + if(frm.doc.__onload && frm.doc.__onload.quotation_series) { + frm.fields_dict.quotation_series.df.options = frm.doc.__onload.quotation_series; + frm.refresh_field("quotation_series"); } }, - refresh: function(){ - toggle_mandatory(cur_frm) - }, - enable_checkout: function(){ - toggle_mandatory(cur_frm) + enabled: function(frm) { + if (frm.doc.enabled === 1) { + frm.set_value('enable_variants', 1); + } } }); - - -function toggle_mandatory (cur_frm){ - cur_frm.toggle_reqd("payment_gateway_account", false); - if(cur_frm.doc.enabled && cur_frm.doc.enable_checkout) { - cur_frm.toggle_reqd("payment_gateway_account", true); - } -} diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json index e828f54878..c574afa68c 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.json @@ -1,750 +1,192 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-06-19 15:57:32", - "custom": 0, - "description": "Default settings for Shopping Cart", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Enable Shopping Cart", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "display_settings", - "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": "Display Settings", - "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": "", - "description": "", - "fieldname": "show_attachments", - "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": "Show Public Attachments", - "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": "", - "description": "", - "fieldname": "show_price", - "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": "Show Price", - "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": "show_stock_availability", - "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": "Show Stock Availability", - "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": "show_configure_button", - "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": "Show Configure Button", - "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": "show_contact_us_button", - "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": "Show Contact Us Button", - "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": "show_stock_availability", - "fieldname": "show_quantity_in_website", - "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": "Show Stock Quantity", - "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": "", - "fetch_if_empty": 0, - "fieldname": "show_apply_coupon_code_in_website", - "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": "Show Apply Coupon Code", - "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, - "fetch_if_empty": 0, - "fieldname": "allow_items_not_in_stock", - "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": "Allow items not in stock to be added to cart", - "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": "enabled", - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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": "", - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Prices will not be shown if Price List is not set", - "fieldname": "price_list", - "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": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "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_4", - "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, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "default_customer_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "quotation_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quotation Series", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval:doc.enable_checkout", - "columns": 0, - "depends_on": "enabled", - "fieldname": "section_break_8", - "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": "Checkout Settings", - "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, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "", - "fieldname": "enable_checkout", - "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 Checkout", - "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, - "default": "Orders", - "description": "After payment completion redirect user to selected page.", - "fieldname": "payment_success_url", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Success Url", - "length": 0, - "no_copy": 0, - "options": "\nOrders\nInvoices\nMy 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": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_gateway_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": "Payment Gateway Account", - "length": 0, - "no_copy": 0, - "options": "Payment Gateway 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 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-shopping-cart", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-10-14 13:54:24.575322", - "modified_by": "Administrator", - "module": "Shopping Cart", - "name": "Shopping Cart Settings", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Website Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 - } \ No newline at end of file + "actions": [], + "creation": "2013-06-19 15:57:32", + "description": "Default settings for Shopping Cart", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "enabled", + "display_settings", + "show_attachments", + "show_price", + "show_stock_availability", + "enable_variants", + "column_break_7", + "show_contact_us_button", + "show_quantity_in_website", + "show_apply_coupon_code_in_website", + "allow_items_not_in_stock", + "section_break_2", + "company", + "price_list", + "column_break_4", + "default_customer_group", + "quotation_series", + "section_break_8", + "enable_checkout", + "payment_success_url", + "column_break_11", + "payment_gateway_account" + ], + "fields": [ + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enable Shopping Cart" + }, + { + "fieldname": "display_settings", + "fieldtype": "Section Break", + "label": "Display Settings" + }, + { + "default": "0", + "fieldname": "show_attachments", + "fieldtype": "Check", + "label": "Show Public Attachments" + }, + { + "default": "0", + "fieldname": "show_price", + "fieldtype": "Check", + "label": "Show Price" + }, + { + "default": "0", + "fieldname": "show_stock_availability", + "fieldtype": "Check", + "label": "Show Stock Availability" + }, + { + "default": "0", + "fieldname": "show_contact_us_button", + "fieldtype": "Check", + "label": "Show Contact Us Button" + }, + { + "default": "0", + "depends_on": "show_stock_availability", + "fieldname": "show_quantity_in_website", + "fieldtype": "Check", + "label": "Show Stock Quantity" + }, + { + "default": "0", + "fieldname": "show_apply_coupon_code_in_website", + "fieldtype": "Check", + "label": "Show Apply Coupon Code" + }, + { + "default": "0", + "fieldname": "allow_items_not_in_stock", + "fieldtype": "Check", + "label": "Allow items not in stock to be added to cart" + }, + { + "depends_on": "enabled", + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "description": "Prices will not be shown if Price List is not set", + "fieldname": "price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_customer_group", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Customer Group", + "options": "Customer Group", + "reqd": 1 + }, + { + "fieldname": "quotation_series", + "fieldtype": "Select", + "label": "Quotation Series", + "reqd": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.enable_checkout", + "depends_on": "enabled", + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Checkout Settings" + }, + { + "default": "0", + "fieldname": "enable_checkout", + "fieldtype": "Check", + "label": "Enable Checkout" + }, + { + "default": "Orders", + "description": "After payment completion redirect user to selected page.", + "fieldname": "payment_success_url", + "fieldtype": "Select", + "label": "Payment Success Url", + "options": "\nOrders\nInvoices\nMy Account" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "payment_gateway_account", + "fieldtype": "Link", + "label": "Payment Gateway Account", + "options": "Payment Gateway Account" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "enable_variants", + "fieldtype": "Check", + "label": "Enable Variants" + } + ], + "icon": "fa fa-shopping-cart", + "idx": 1, + "issingle": 1, + "links": [], + "modified": "2020-07-17 17:53:22.667228", + "modified_by": "Administrator", + "module": "Shopping Cart", + "name": "Shopping Cart Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Website Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py index 3098190383..c069b90e98 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py @@ -80,6 +80,7 @@ def get_shopping_cart_settings(): return frappe.local.shopping_cart_settings +@frappe.whitelist(allow_guest=True) def is_cart_enabled(): return get_shopping_cart_settings().enabled diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 7c08f5b5b2..29617a8748 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -55,7 +55,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): def set_product_info_for_website(item): """set product price uom for website""" - product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True) + product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get("product_info") if product_info: item.update(product_info) diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py index 90ecd46259..5545f13e8c 100644 --- a/erpnext/startup/leaderboard.py +++ b/erpnext/startup/leaderboard.py @@ -50,11 +50,12 @@ def get_leaderboards(): return leaderboards @frappe.whitelist() -def get_all_customers(from_date, company, field, limit = None): +def get_all_customers(date_range, company, field, limit = None): if field == "outstanding_amount": filters = [['docstatus', '=', '1'], ['company', '=', company]] - if from_date: - filters.append(['posting_date', '>=', from_date]) + if date_range: + date_range = frappe.parse_json(date_range) + filters.append(['posting_date', '>=', 'between', [date_range[0], date_range[1]]]) return frappe.db.get_all('Sales Invoice', fields = ['customer as name', 'sum(outstanding_amount) as value'], filters = filters, @@ -68,18 +69,20 @@ def get_all_customers(from_date, company, field, limit = None): elif field == "total_qty_sold": select_field = "sum(so_item.stock_qty)" + date_condition = get_date_condition(date_range, 'so.transaction_date') + return frappe.db.sql(""" select so.customer as name, {0} as value FROM `tabSales Order` as so JOIN `tabSales Order Item` as so_item ON so.name = so_item.parent - where so.docstatus = 1 and so.transaction_date >= %s and so.company = %s + where so.docstatus = 1 {1} and so.company = %s group by so.customer order by value DESC limit %s - """.format(select_field), (from_date, company, cint(limit)), as_dict=1) #nosec + """.format(select_field, date_condition), (company, cint(limit)), as_dict=1) @frappe.whitelist() -def get_all_items(from_date, company, field, limit = None): +def get_all_items(date_range, company, field, limit = None): if field in ("available_stock_qty", "available_stock_value"): select_field = "sum(actual_qty)" if field=="available_stock_qty" else "sum(stock_value)" return frappe.db.get_all('Bin', @@ -102,23 +105,25 @@ def get_all_items(from_date, company, field, limit = None): select_field = "sum(order_item.stock_qty)" select_doctype = "Purchase Order" + date_condition = get_date_condition(date_range, 'sales_order.transaction_date') + return frappe.db.sql(""" select order_item.item_code as name, {0} as value from `tab{1}` sales_order join `tab{1} Item` as order_item on sales_order.name = order_item.parent where sales_order.docstatus = 1 - and sales_order.company = %s and sales_order.transaction_date >= %s + and sales_order.company = %s {2} group by order_item.item_code order by value desc limit %s - """.format(select_field, select_doctype), (company, from_date, cint(limit)), as_dict=1) #nosec + """.format(select_field, select_doctype, date_condition), (company, cint(limit)), as_dict=1) #nosec @frappe.whitelist() -def get_all_suppliers(from_date, company, field, limit = None): +def get_all_suppliers(date_range, company, field, limit = None): if field == "outstanding_amount": filters = [['docstatus', '=', '1'], ['company', '=', company]] - if from_date: - filters.append(['posting_date', '>=', from_date]) + if date_range: + filters.append(['posting_date', 'between' [date_range[0], date_range[1]]]) return frappe.db.get_all('Purchase Invoice', fields = ['supplier as name', 'sum(outstanding_amount) as value'], filters = filters, @@ -132,18 +137,22 @@ def get_all_suppliers(from_date, company, field, limit = None): elif field == "total_qty_purchased": select_field = "sum(purchase_order_item.stock_qty)" + date_condition = get_date_condition(date_range, 'purchase_order.modified') + return frappe.db.sql(""" select purchase_order.supplier as name, {0} as value FROM `tabPurchase Order` as purchase_order LEFT JOIN `tabPurchase Order Item` as purchase_order_item ON purchase_order.name = purchase_order_item.parent - where purchase_order.docstatus = 1 and purchase_order.modified >= %s + where + purchase_order.docstatus = 1 + {1} and purchase_order.company = %s group by purchase_order.supplier order by value DESC - limit %s""".format(select_field), (from_date, company, cint(limit)), as_dict=1) #nosec + limit %s""".format(select_field, date_condition), (company, cint(limit)), as_dict=1) #nosec @frappe.whitelist() -def get_all_sales_partner(from_date, company, field, limit = None): +def get_all_sales_partner(date_range, company, field, limit = None): if field == "total_sales_amount": select_field = "sum(`base_net_total`)" elif field == "total_commission": @@ -154,8 +163,9 @@ def get_all_sales_partner(from_date, company, field, limit = None): 'docstatus': 1, 'company': company } - if from_date: - filters['transaction_date'] = ['>=', from_date] + if date_range: + date_range = frappe.parse_json(date_range) + filters['transaction_date'] = ['between', [date_range[0], date_range[1]]] return frappe.get_list('Sales Order', fields=[ '`sales_partner` as name', @@ -163,15 +173,27 @@ def get_all_sales_partner(from_date, company, field, limit = None): ], filters=filters, group_by='sales_partner', order_by='value DESC', limit=limit) @frappe.whitelist() -def get_all_sales_person(from_date, company, field = None, limit = 0): +def get_all_sales_person(date_range, company, field = None, limit = 0): + date_condition = get_date_condition(date_range, 'sales_order.transaction_date') + return frappe.db.sql(""" select sales_team.sales_person as name, sum(sales_order.base_net_total) as value from `tabSales Order` as sales_order join `tabSales Team` as sales_team on sales_order.name = sales_team.parent and sales_team.parenttype = 'Sales Order' where sales_order.docstatus = 1 - and sales_order.transaction_date >= %s and sales_order.company = %s + {date_condition} group by sales_team.sales_person order by value DESC limit %s - """, (from_date, company, cint(limit)), as_dict=1) + """.format(date_condition=date_condition), (company, cint(limit)), as_dict=1) + +def get_date_condition(date_range, field): + date_condition = '' + if date_range: + date_range = frappe.parse_json(date_range) + from_date, to_date = date_range + date_condition = "and {0} between {1} and {2}".format( + field, frappe.db.escape(from_date), frappe.db.escape(to_date) + ) + return date_condition \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart/delivery_trends/delivery_trends.json b/erpnext/stock/dashboard_chart/delivery_trends/delivery_trends.json new file mode 100644 index 0000000000..b3f6e35012 --- /dev/null +++ b/erpnext/stock/dashboard_chart/delivery_trends/delivery_trends.json @@ -0,0 +1,27 @@ +{ + "based_on": "posting_date", + "chart_name": "Delivery Trends", + "chart_type": "Sum", + "color": "#4d4da8", + "creation": "2020-07-20 21:01:04.255291", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Delivery Note", + "filters_json": "[[\"Delivery Note\",\"docstatus\",\"=\",1]]", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 13:03:24.937045", + "modified_by": "Administrator", + "module": "Stock", + "name": "Delivery Trends", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "base_net_total", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart/item_shortage_summary/item_shortage_summary.json b/erpnext/stock/dashboard_chart/item_shortage_summary/item_shortage_summary.json new file mode 100644 index 0000000000..ce711247e7 --- /dev/null +++ b/erpnext/stock/dashboard_chart/item_shortage_summary/item_shortage_summary.json @@ -0,0 +1,23 @@ +{ + "chart_name": "Item Shortage Summary", + "chart_type": "Report", + "creation": "2020-07-20 21:01:04.383451", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\"}", + "filters_json": "{}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 13:07:01.905334", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Shortage Summary", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Item Shortage Report", + "timeseries": 0, + "type": "Bar", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json new file mode 100644 index 0000000000..6da3b28baf --- /dev/null +++ b/erpnext/stock/dashboard_chart/oldest_items/oldest_items.json @@ -0,0 +1,24 @@ +{ + "chart_name": "Oldest Items", + "chart_type": "Report", + "creation": "2020-07-20 21:01:04.336845", + "custom_options": "{\"colors\": [\"#5e64ff\"]}", + "docstatus": 0, + "doctype": "Dashboard Chart", + "dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"to_date\":\"frappe.datetime.nowdate()\"}", + "filters_json": "{\"show_warehouse_wise_stock\":0}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 13:04:36.271198", + "modified_by": "Administrator", + "module": "Stock", + "name": "Oldest Items", + "number_of_groups": 0, + "owner": "Administrator", + "report_name": "Stock Ageing", + "timeseries": 0, + "type": "Bar", + "use_report_chart": 1, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart/purchase_receipt_trends/purchase_receipt_trends.json b/erpnext/stock/dashboard_chart/purchase_receipt_trends/purchase_receipt_trends.json new file mode 100644 index 0000000000..584a6cc867 --- /dev/null +++ b/erpnext/stock/dashboard_chart/purchase_receipt_trends/purchase_receipt_trends.json @@ -0,0 +1,27 @@ +{ + "based_on": "posting_date", + "chart_name": "Purchase Receipt Trends", + "chart_type": "Sum", + "color": "#78d6ff", + "creation": "2020-07-20 21:01:04.205230", + "docstatus": 0, + "doctype": "Dashboard Chart", + "document_type": "Purchase Receipt", + "filters_json": "[[\"Purchase Receipt\",\"docstatus\",\"=\",1]]", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 13:05:25.923130", + "modified_by": "Administrator", + "module": "Stock", + "name": "Purchase Receipt Trends", + "number_of_groups": 0, + "owner": "Administrator", + "time_interval": "Monthly", + "timeseries": 1, + "timespan": "Last Year", + "type": "Bar", + "use_report_chart": 0, + "value_based_on": "base_net_total", + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart/warehouse_wise_stock_value/warehouse_wise_stock_value.json b/erpnext/stock/dashboard_chart/warehouse_wise_stock_value/warehouse_wise_stock_value.json new file mode 100644 index 0000000000..a07b55382c --- /dev/null +++ b/erpnext/stock/dashboard_chart/warehouse_wise_stock_value/warehouse_wise_stock_value.json @@ -0,0 +1,22 @@ +{ + "chart_name": "Warehouse wise Stock Value", + "chart_type": "Custom", + "creation": "2020-07-20 21:01:04.296157", + "docstatus": 0, + "doctype": "Dashboard Chart", + "filters_json": "{}", + "idx": 0, + "is_public": 1, + "is_standard": 1, + "modified": "2020-07-22 13:01:01.815123", + "modified_by": "Administrator", + "module": "Stock", + "name": "Warehouse wise Stock Value", + "number_of_groups": 0, + "owner": "Administrator", + "source": "Warehouse wise Stock Value", + "timeseries": 0, + "type": "Bar", + "use_report_chart": 0, + "y_axis": [] +} \ No newline at end of file diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py deleted file mode 100644 index 7625b1ad28..0000000000 --- a/erpnext/stock/dashboard_fixtures.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe -import json -from frappe import _ -from frappe.utils import nowdate -from erpnext.accounts.dashboard_fixtures import _get_fiscal_year -from erpnext.buying.dashboard_fixtures import get_company_for_dashboards - -def get_data(): - fiscal_year = _get_fiscal_year(nowdate()) - - if not fiscal_year: - return frappe._dict() - - company = frappe.get_doc("Company", get_company_for_dashboards()) - fiscal_year_name = fiscal_year.get("name") - start_date = str(fiscal_year.get("year_start_date")) - end_date = str(fiscal_year.get("year_end_date")) - - return frappe._dict({ - "dashboards": get_dashboards(), - "charts": get_charts(company, fiscal_year_name, start_date, end_date), - "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), - }) - -def get_dashboards(): - return [{ - "name": "Stock", - "dashboard_name": "Stock", - "charts": [ - { "chart": "Warehouse wise Stock Value", "width": "Full"}, - { "chart": "Purchase Receipt Trends", "width": "Half"}, - { "chart": "Delivery Trends", "width": "Half"}, - { "chart": "Oldest Items", "width": "Half"}, - { "chart": "Item Shortage Summary", "width": "Half"} - ], - "cards": [ - { "card": "Total Active Items"}, - { "card": "Total Warehouses"}, - { "card": "Total Stock Value"} - ] - }] - -def get_charts(company, fiscal_year_name, start_date, end_date): - return [ - { - "doctype": "Dashboard Chart", - "name": "Purchase Receipt Trends", - "time_interval": "Monthly", - "chart_name": _("Purchase Receipt Trends"), - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_net_total", - "filters_json": json.dumps([["Purchase Receipt", "docstatus", "=", 1]]), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Receipt", - "type": "Bar", - "width": "Half", - "is_public": 1 - }, - { - "doctype": "Dashboard Chart", - "name": "Delivery Trends", - "time_interval": "Monthly", - "chart_name": _("Delivery Trends"), - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_net_total", - "filters_json": json.dumps([["Delivery Note", "docstatus", "=", 1]]), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Delivery Note", - "type": "Bar", - "width": "Half", - "is_public": 1 - }, - { - "name": "Warehouse wise Stock Value", - "chart_name": _("Warehouse wise Stock Value"), - "chart_type": "Custom", - "doctype": "Dashboard Chart", - "filters_json": json.dumps({}), - "is_custom": 0, - "is_public": 1, - "owner": "Administrator", - "source": "Warehouse wise Stock Value", - "type": "Bar" - }, - { - "name": "Oldest Items", - "chart_name": _("Oldest Items"), - "chart_type": "Report", - "custom_options": json.dumps({ - "colors": ["#5e64ff"] - }), - "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "company": company.name, - "to_date": nowdate(), - "show_warehouse_wise_stock": 0 - }), - "is_custom": 1, - "is_public": 1, - "owner": "Administrator", - "report_name": "Stock Ageing", - "type": "Bar" - }, - { - "name": "Item Shortage Summary", - "chart_name": _("Item Shortage Summary"), - "chart_type": "Report", - "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "company": company.name - }), - "is_custom": 1, - "is_public": 1, - "owner": "Administrator", - "report_name": "Item Shortage Report", - "type": "Bar" - } - ] - -def get_number_cards(company, fiscal_year_name, start_date, end_date): - return [ - { - "name": "Total Active Items", - "label": _("Total Active Items"), - "function": "Count", - "doctype": "Number Card", - "document_type": "Item", - "filters_json": json.dumps([["Item", "disabled", "=", 0]]), - "is_public": 1, - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - }, - { - "name": "Total Warehouses", - "label": _("Total Warehouses"), - "function": "Count", - "doctype": "Number Card", - "document_type": "Warehouse", - "filters_json": json.dumps([["Warehouse", "disabled", "=", 0]]), - "is_public": 1, - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Monthly" - }, - { - "name": "Total Stock Value", - "label": _("Total Stock Value"), - "function": "Sum", - "aggregate_function_based_on": "stock_value", - "doctype": "Number Card", - "document_type": "Bin", - "filters_json": json.dumps([]), - "is_public": 1, - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - } - ] \ No newline at end of file diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index a091ac7fae..c8424f13e1 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -143,7 +143,7 @@ class Batch(Document): @frappe.whitelist() -def get_batch_qty(batch_no=None, warehouse=None, item_code=None): +def get_batch_qty(batch_no=None, warehouse=None, item_code=None, posting_date=None, posting_time=None): """Returns batch actual qty if warehouse is passed, or returns dict of qty by warehouse if warehouse is None @@ -155,9 +155,14 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None): out = 0 if batch_no and warehouse: + cond = "" + if posting_date and posting_time: + cond = " and timestamp(posting_date, posting_time) <= timestamp('{0}', '{1}')".format(posting_date, + posting_time) + out = float(frappe.db.sql("""select sum(actual_qty) from `tabStock Ledger Entry` - where warehouse=%s and batch_no=%s""", + where warehouse=%s and batch_no=%s {0}""".format(cond), (warehouse, batch_no))[0][0] or 0) if batch_no and not warehouse: diff --git a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json index 07992b8231..7eeb14760f 100644 --- a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json +++ b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json @@ -71,7 +71,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -88,7 +88,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-09-12 09:30:03.951743", + "modified": "2020-06-26 09:30:03.951743", "modified_by": "Administrator", "module": "Stock", "name": "Customs Tariff Number", @@ -143,4 +143,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 84d2057f96..66efcf8cd8 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -801,6 +801,7 @@ "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", + "length": 240, "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, @@ -851,6 +852,7 @@ "fieldname": "in_words", "fieldtype": "Data", "label": "In Words", + "length": 240, "oldfieldname": "in_words_export", "oldfieldtype": "Data", "print_hide": 1, @@ -1253,7 +1255,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-05-19 17:03:45.880106", + "modified": "2020-07-18 05:13:55.580420", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index a921a56f53..4b04a0a8c3 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -537,11 +537,8 @@ class TestDeliveryNote(unittest.TestCase): dt = make_delivery_trip(dn.name) self.assertEqual(dn.name, dt.delivery_stops[0].delivery_note) - def test_delivery_note_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_delivery_note_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - TCP1" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company with perpetual inventory") @@ -567,13 +564,8 @@ class TestDeliveryNote(unittest.TestCase): } for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - def test_delivery_note_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() + def test_delivery_note_cost_center_with_balance_sheet_account(self): cost_center = "Main - TCP1" company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') @@ -583,7 +575,11 @@ class TestDeliveryNote(unittest.TestCase): make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100) stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory') - dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1") + dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", + do_not_submit=1) + + dn.get('items')[0].cost_center = None + dn.submit() gl_entries = get_gl_entries("Delivery Note", dn.name) @@ -593,7 +589,7 @@ class TestDeliveryNote(unittest.TestCase): "cost_center": cost_center }, stock_in_hand_account: { - "cost_center": None + "cost_center": cost_center } } for i, gle in enumerate(gl_entries): diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 7ea2de2753..3d57f47601 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -1,5 +1,4 @@ { - "actions": [], "autoname": "hash", "creation": "2013-04-22 13:15:44", "doctype": "DocType", @@ -82,6 +81,7 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "project", "section_break_72", "page_break" ], @@ -701,6 +701,12 @@ "fieldname": "dimension_col_break", "fieldtype": "Column Break" }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, { "fieldname": "dn_detail", "fieldtype": "Data", @@ -714,7 +720,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-05 14:18:33.131672", + "modified": "2020-07-20 12:25:06.177894", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index c371999a27..963c87a0af 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -987,6 +987,7 @@ "read_only": 1 }, { + "collapsible": 1, "depends_on": "eval:(!doc.is_item_from_hub)", "fieldname": "hub_publishing_sb", "fieldtype": "Section Break", @@ -1060,7 +1061,7 @@ "image_field": "image", "links": [], "max_attachments": 1, - "modified": "2020-04-08 15:56:06.195722", + "modified": "2020-06-30 12:01:07.534447", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -1122,4 +1123,4 @@ "sort_order": "DESC", "title_field": "item_name", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3436a5d013..d5f479ff82 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -13,7 +13,7 @@ from erpnext.controllers.item_variant import (ItemVariantExistsError, from erpnext.setup.doctype.item_group.item_group import (get_parent_item_groups, invalidate_cache_for) from frappe import _, msgprint from frappe.utils import (cint, cstr, flt, formatdate, get_timestamp, getdate, - now_datetime, random_string, strip) + now_datetime, random_string, strip, get_link_to_form) from frappe.utils.html_utils import clean_html from frappe.website.doctype.website_slideshow.website_slideshow import \ get_slideshow @@ -597,7 +597,7 @@ class Item(WebsiteGenerator): def stock_ledger_created(self): if not hasattr(self, '_stock_ledger_created'): self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry` - where item_code = %s limit 1""", self.name)) + where item_code = %s and is_cancelled = 0 limit 1""", self.name)) return self._stock_ledger_created def validate_name_with_item_group(self): @@ -634,6 +634,9 @@ class Item(WebsiteGenerator): + ": \n" + ", ".join([self.meta.get_label(fld) for fld in field_list])) def after_rename(self, old_name, new_name, merge): + if merge: + self.validate_duplicate_item_in_stock_reconciliation(old_name, new_name) + if self.route: invalidate_cache_for_item(self) clear_cache(self.route) @@ -656,6 +659,27 @@ class Item(WebsiteGenerator): frappe.db.set_value(dt, d.name, "item_wise_tax_detail", json.dumps(item_wise_tax_detail), update_modified=False) + def validate_duplicate_item_in_stock_reconciliation(self, old_name, new_name): + records = frappe.db.sql(""" SELECT parent, COUNT(*) as records + FROM `tabStock Reconciliation Item` + WHERE item_code = %s and docstatus = 1 + GROUP By item_code, warehouse, parent + HAVING records > 1 + """, new_name, as_dict=1) + + if not records: return + document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations") + + msg = _("The items {0} and {1} are present in the following {2} :