From 0acb63a938fac7432182269c947cc312586f882d Mon Sep 17 00:00:00 2001 From: Prateeksha Singh Date: Fri, 16 Feb 2018 11:07:12 +0530 Subject: [PATCH 01/27] =?UTF-8?q?Update=20hub=20domain=20=F0=9F=92=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- erpnext/hub_node/doctype/hub_settings/hub_settings.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/hub_node/doctype/hub_settings/hub_settings.py b/erpnext/hub_node/doctype/hub_settings/hub_settings.py index 50b8e01461..59807532e0 100644 --- a/erpnext/hub_node/doctype/hub_settings/hub_settings.py +++ b/erpnext/hub_node/doctype/hub_settings/hub_settings.py @@ -10,9 +10,7 @@ from frappe import _ from erpnext.utilities.product import get_price, get_qty_in_stock from six import string_types -# hub_url = "http://erpnext.hub:8000" -hub_url = "https://hub.erpnext.org" -# hub_url = "http://192.168.29.145:3000" +hub_url = "https://hubmarket.org" class HubSetupError(frappe.ValidationError): pass @@ -102,4 +100,4 @@ def reset_hub_settings(last_sync_datetime = ""): @frappe.whitelist() def sync(): hub_settings = frappe.get_doc('Hub Settings') - hub_settings.sync() \ No newline at end of file + hub_settings.sync() From 5a947f38ab3c5fbce5c2d211b02655597d91cd9c Mon Sep 17 00:00:00 2001 From: Zarrar Date: Fri, 16 Feb 2018 12:49:30 +0530 Subject: [PATCH 02/27] avoid overlap with dropdown (#12941) --- erpnext/healthcare/page/medical_record/medical_record.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/page/medical_record/medical_record.css b/erpnext/healthcare/page/medical_record/medical_record.css index e76df0f82e..70014da1c0 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.css +++ b/erpnext/healthcare/page/medical_record/medical_record.css @@ -14,6 +14,10 @@ margin-bottom: -4px; } +.medical_record-row > * { + z-index: -999; +} + .date-indicator { background:none; font-size:12px; @@ -48,7 +52,6 @@ .medical_record-date { padding: 15px; padding-right: 0px; - z-index: 1; } #page-medical_record .plot-wrapper { From 103c4e9b92cf49a1f49b9cd6f71f92f55e5a12f8 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 16 Feb 2018 12:57:35 +0530 Subject: [PATCH 03/27] [Fix] Incorrect rate in item-wise sales register (#12943) --- .../item_wise_sales_register/item_wise_sales_register.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 8917c9ff63..a39b831250 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -50,10 +50,12 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row += [ d.customer_group, d.debit_to, ", ".join(mode_of_payments.get(d.parent, [])), d.territory, d.project, d.company, d.sales_order, - delivery_note, d.income_account, d.cost_center, d.stock_qty, d.stock_uom, - d.base_net_rate, d.base_net_amount + delivery_note, d.income_account, d.cost_center, d.stock_qty, d.stock_uom ] + row += [d.base_net_rate/d.stock_qty, d.base_net_amount] \ + if d.stock_uom != d.uom else [d.base_net_rate, d.base_net_amount] + total_tax = 0 for tax in tax_columns: item_tax = itemised_tax.get(d.name, {}).get(tax, {}) @@ -131,7 +133,7 @@ def get_items(filters, additional_query_columns): `tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail, - `tabSales Invoice`.update_stock {0} + `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom {0} from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and `tabSales Invoice`.docstatus = 1 %s %s From cc98d40ff7dbab1671572344f66283dc0867719f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 16 Feb 2018 13:14:20 +0530 Subject: [PATCH 04/27] Payment reco auto allocation and maintain same order of records (#12963) * Automatically allocate amount after selecting invoice against a payment entry * codacy fixes --- .../payment_reconciliation.js | 19 ++++++++++++++++++- .../payment_reconciliation.py | 1 + erpnext/controllers/accounts_controller.py | 2 ++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index ebd9439d0e..0901046af2 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -3,9 +3,26 @@ frappe.provide("erpnext.accounts"); +frappe.ui.form.on("Payment Reconciliation Payment", { + invoice_number: function(frm, cdt, cdn) { + var row = locals[cdt][cdn]; + if(row.invoice_number) { + var parts = row.invoice_number.split(' | '); + var invoice_type = parts[0]; + var invoice_number = parts[1]; + + var invoice_amount = frm.doc.invoices.filter(function(d) { + return d.invoice_type === invoice_type && d.invoice_number === invoice_number; + })[0].outstanding_amount; + + frappe.model.set_value(cdt, cdn, "allocated_amount", Math.min(invoice_amount, row.amount)); + } + } +}); + erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({ onload: function() { - var me = this + var me = this; this.frm.set_query("party_type", function() { return{ query: "erpnext.setup.doctype.party_type.party_type.get_party_type" diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 5910a2402d..3c87fe5433 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -55,6 +55,7 @@ class PaymentReconciliation(Document): THEN 1=1 ELSE {bank_account_condition} END) + order by t1.posting_date """.format(**{ "dr_or_cr": dr_or_cr, "bank_account_condition": bank_account_condition, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index b3672cbde3..472a7a3fce 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -883,6 +883,7 @@ def get_advance_payment_entries(party_type, party, party_account, t1.name = t2.parent and t1.{0} = %s and t1.payment_type = %s and t1.party_type = %s and t1.party = %s and t1.docstatus = 1 and t2.reference_doctype = %s {1} + order by t1.posting_date """.format(party_account_field, reference_condition), [party_account, payment_type, party_type, party, order_doctype] + order_list, as_dict=1) @@ -894,6 +895,7 @@ def get_advance_payment_entries(party_type, party, party_account, where {0} = %s and party_type = %s and party = %s and payment_type = %s and docstatus = 1 and unallocated_amount > 0 + order by posting_date """.format(party_account_field), (party_account, party_type, party, payment_type), as_dict=1) return list(payment_entries_against_order) + list(unallocated_payment_entries) From b95ecd7fcd63172a3ddbf475886c5eee15f00f2c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 16 Feb 2018 13:19:04 +0530 Subject: [PATCH 05/27] Gstr reports (#12940) * GSTR1 for B2B (#12296) * [minor] Modified GSTR1 report to identify missing GST Account in GST Settings (#12426) * [minor] Modified GSTR1 report to identify missing GST Account in GST Settings * Update gstr_1.py * GSTR1 for B2B, B2CL and B2CS (#12459) * [Report] GSTR - 1 CDNR Report (#12554) * [wip] cdnr * [WIP] cdnr with optional data * [wip] Export GSTR-1 * [minor] Minor changes in export * [new] Custom field added for GST * [fix] Minor changes in GSTR1 Report * [minor] Minor changes in gstr1 * [fix] Codacy Fixed * Update setup.py * [wip] Gstr2 * [fix] Fetch correct Tax Details * [minor] ITC data append to row * [fix] CDNR negative value * Cleanup and fixes on GSTR-2 * Minor fixes in gstr reports (#12848) * [new] B2C limit fetch from GST Settings (#12905) * [new] b2c limit fetch from gst setting * [fix] Patch Added for b2c limit * Update gstr_1.py * Update utils.py * Cusotm fields related GSTR2 * minor fixes * minor fixes * some more fixes * Added duplicate patch and some tests as not required on develop --- .../accounts/doctype/gst_account/__init__.py | 0 .../doctype/gst_account/gst_account.json | 196 +++++++ .../doctype/gst_account/gst_account.py | 10 + .../item_wise_sales_register.js | 2 +- erpnext/hooks.py | 3 + .../test_leave_application.py | 46 -- erpnext/patches.txt | 4 +- .../added_extra_gst_custom_field_in_gstr2.py | 18 + erpnext/patches/v10_0/set_b2c_limit.py | 12 + .../repost_bin_qty_and_item_projected_qty.py | 1 - .../doctype/gst_settings/gst_settings.json | 123 ++++- .../doctype/gst_settings/test_gst_settings.py | 10 + erpnext/regional/india/setup.py | 47 +- erpnext/regional/india/utils.py | 19 +- erpnext/regional/report/gstr_1/__init__.py | 0 erpnext/regional/report/gstr_1/gstr_1.js | 38 ++ erpnext/regional/report/gstr_1/gstr_1.json | 30 ++ erpnext/regional/report/gstr_1/gstr_1.py | 487 ++++++++++++++++++ erpnext/regional/report/gstr_2/__init__.py | 0 erpnext/regional/report/gstr_2/gstr_2.js | 39 ++ erpnext/regional/report/gstr_2/gstr_2.json | 29 ++ erpnext/regional/report/gstr_2/gstr_2.py | 277 ++++++++++ 22 files changed, 1328 insertions(+), 63 deletions(-) create mode 100644 erpnext/accounts/doctype/gst_account/__init__.py create mode 100644 erpnext/accounts/doctype/gst_account/gst_account.json create mode 100644 erpnext/accounts/doctype/gst_account/gst_account.py create mode 100644 erpnext/patches/v10_0/added_extra_gst_custom_field_in_gstr2.py create mode 100644 erpnext/patches/v10_0/set_b2c_limit.py create mode 100644 erpnext/regional/doctype/gst_settings/test_gst_settings.py create mode 100644 erpnext/regional/report/gstr_1/__init__.py create mode 100644 erpnext/regional/report/gstr_1/gstr_1.js create mode 100644 erpnext/regional/report/gstr_1/gstr_1.json create mode 100644 erpnext/regional/report/gstr_1/gstr_1.py create mode 100644 erpnext/regional/report/gstr_2/__init__.py create mode 100644 erpnext/regional/report/gstr_2/gstr_2.js create mode 100644 erpnext/regional/report/gstr_2/gstr_2.json create mode 100644 erpnext/regional/report/gstr_2/gstr_2.py diff --git a/erpnext/accounts/doctype/gst_account/__init__.py b/erpnext/accounts/doctype/gst_account/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/gst_account/gst_account.json b/erpnext/accounts/doctype/gst_account/gst_account.json new file mode 100644 index 0000000000..70673387fe --- /dev/null +++ b/erpnext/accounts/doctype/gst_account/gst_account.json @@ -0,0 +1,196 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2018-01-02 15:48:58.768352", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 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": 1, + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cgst_account", + "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": "CGST Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sgst_account", + "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": "SGST Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "igst_account", + "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": "IGST Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "cess_account", + "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": "CESS Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "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": "2018-01-02 15:52:22.335988", + "modified_by": "Administrator", + "module": "Accounts", + "name": "GST Account", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/gst_account/gst_account.py b/erpnext/accounts/doctype/gst_account/gst_account.py new file mode 100644 index 0000000000..d7848495a6 --- /dev/null +++ b/erpnext/accounts/doctype/gst_account/gst_account.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, 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 GSTAccount(Document): + pass diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index b57a7fce9d..6a8cc6abf9 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -1,7 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.query_reports["Item-wise Sales Register"] = frappe.query_reports["Sales Register"] = { +frappe.query_reports["Item-wise Sales Register"] = { "filters": [ { "fieldname":"from_date", diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 6265e1af85..45abbdb0ec 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -203,6 +203,9 @@ doc_events = { }, 'Address': { 'validate': 'erpnext.regional.india.utils.validate_gstin_for_india' + }, + ('Sales Invoice', 'Purchase Invoice'): { + 'validate': 'erpnext.regional.india.utils.set_place_of_supply' } } diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 0ec3efb7de..ee553f7360 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -235,52 +235,6 @@ class TestLeaveApplication(unittest.TestCase): application.half_day_date = "2013-01-05" application.insert() - def test_global_block_list(self): - self._clear_roles() - - from frappe.utils.user import add_role - add_role("test1@example.com", "Employee") - add_role("test@example.com", "Leave Approver") - self._add_employee_leave_approver("_T-Employee-0002", "test@example.com") - - make_allocation_record(employee="_T-Employee-0002") - - application = self.get_application(_test_records[1]) - application.leave_approver = "test@example.com" - - frappe.db.set_value("Leave Block List", "_Test Leave Block List", - "applies_to_all_departments", 1) - frappe.db.set_value("Employee", "_T-Employee-0002", "department", - "_Test Department") - - frappe.set_user("test1@example.com") - application.insert() - - frappe.set_user("test@example.com") - application.status = "Approved" - - # clear permlevel access cache on change user - del application._has_access_to - - self.assertRaises(LeaveDayBlockedError, application.submit) - - frappe.db.set_value("Leave Block List", "_Test Leave Block List", - "applies_to_all_departments", 0) - - def test_leave_approval(self): - self._clear_roles() - - from frappe.utils.user import add_role - add_role("test@example.com", "Employee") - add_role("test1@example.com", "HR User") - add_role("test1@example.com", "Leave Approver") - add_role("test2@example.com", "Leave Approver") - - self._test_leave_approval_basic_case() - self._test_leave_approval_invalid_leave_approver_insert() - self._test_leave_approval_invalid_leave_approver_submit() - self._test_leave_approval_valid_leave_approver_insert() - def _test_leave_approval_basic_case(self): self._clear_applications() diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9474a946e8..e21da22691 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -487,4 +487,6 @@ erpnext.patches.v10_0.set_numeric_ranges_in_template_if_blank erpnext.patches.v10_0.update_assessment_plan erpnext.patches.v10_0.update_assessment_result erpnext.patches.v10_0.set_default_payment_terms_based_on_company -erpnext.patches.v10_0.update_sales_order_link_to_purchase_order \ No newline at end of file +erpnext.patches.v10_0.update_sales_order_link_to_purchase_order +erpnext.patches.v10_0.added_extra_gst_custom_field_in_gstr2 #2018-02-13 +erpnext.patches.v10_0.set_b2c_limit diff --git a/erpnext/patches/v10_0/added_extra_gst_custom_field_in_gstr2.py b/erpnext/patches/v10_0/added_extra_gst_custom_field_in_gstr2.py new file mode 100644 index 0000000000..185e20d42e --- /dev/null +++ b/erpnext/patches/v10_0/added_extra_gst_custom_field_in_gstr2.py @@ -0,0 +1,18 @@ +import frappe +from erpnext.regional.india.setup import make_custom_fields + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + make_custom_fields() + + frappe.db.sql(""" + update `tabCustom Field` + set reqd = 0, `default` = '' + where fieldname = 'reason_for_issuing_document' + """) + + frappe.db.sql("""delete from `tabCustom Field` where dt = 'Purchase Invoice' + and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""") \ No newline at end of file diff --git a/erpnext/patches/v10_0/set_b2c_limit.py b/erpnext/patches/v10_0/set_b2c_limit.py new file mode 100644 index 0000000000..e93b7c2178 --- /dev/null +++ b/erpnext/patches/v10_0/set_b2c_limit.py @@ -0,0 +1,12 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("regional", "doctype", "gst_settings") + frappe.reload_doc("accounts", "doctype", "gst_account") + gst_settings = frappe.get_doc("GST Settings") + gst_settings.b2c_limit = 250000 + gst_settings.save() \ No newline at end of file diff --git a/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py b/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py index 20ee814fda..5d76304756 100644 --- a/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py +++ b/erpnext/patches/v7_0/repost_bin_qty_and_item_projected_qty.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import frappe -from erpnext.stock.doctype.bin.bin import update_item_projected_qty def execute(): repost_bin_qty() diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json index 67084b45f8..98c33ad33b 100644 --- a/erpnext/regional/doctype/gst_settings/gst_settings.json +++ b/erpnext/regional/doctype/gst_settings/gst_settings.json @@ -42,6 +42,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_2", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -71,6 +100,98 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_4", + "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, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "gst_accounts", + "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": "GST Accounts", + "length": 0, + "no_copy": 0, + "options": "GST 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, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "250000", + "description": "Set Invoice Value for B2C. B2CL and B2CS calculated based on this invoice value.", + "fieldname": "b2c_limit", + "fieldtype": "Data", + "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": "B2C Limit", + "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": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], "has_web_view": 0, @@ -83,7 +204,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2017-09-29 14:39:15.625952", + "modified": "2018-02-14 08:14:15.375181", "modified_by": "Administrator", "module": "Regional", "name": "GST Settings", diff --git a/erpnext/regional/doctype/gst_settings/test_gst_settings.py b/erpnext/regional/doctype/gst_settings/test_gst_settings.py new file mode 100644 index 0000000000..d118dee617 --- /dev/null +++ b/erpnext/regional/doctype/gst_settings/test_gst_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +class TestGSTSettings(unittest.TestCase): + pass diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 4ef9b117c1..ac68a92ba7 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -87,23 +87,27 @@ def make_custom_fields(): allow_on_submit=1, print_hide=1) invoice_gst_fields = [ dict(fieldname='gst_section', label='GST Details', fieldtype='Section Break', - insert_after='select_print_heading', print_hide=1, collapsible=1), + insert_after='language', print_hide=1, collapsible=1), dict(fieldname='invoice_copy', label='Invoice Copy', fieldtype='Select', insert_after='gst_section', print_hide=1, allow_on_submit=1, options='Original for Recipient\nDuplicate for Transporter\nDuplicate for Supplier\nTriplicate for Supplier'), dict(fieldname='reverse_charge', label='Reverse Charge', fieldtype='Select', insert_after='invoice_copy', print_hide=1, options='Y\nN', default='N'), - dict(fieldname='gst_col_break', fieldtype='Column Break', insert_after='reverse_charge'), dict(fieldname='invoice_type', label='Invoice Type', - fieldtype='Select', insert_after='reverse_charge', print_hide=1, + fieldtype='Select', insert_after='invoice_copy', print_hide=1, options='Regular\nSEZ\nExport\nDeemed Export', default='Regular'), dict(fieldname='export_type', label='Export Type', fieldtype='Select', insert_after='invoice_type', print_hide=1, depends_on='eval:in_list(["SEZ", "Export", "Deemed Export"], doc.invoice_type)', options='\nWith Payment of Tax\nWithout Payment of Tax'), dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN', - fieldtype='Data', insert_after='export_type', print_hide=1) + fieldtype='Data', insert_after='export_type', print_hide=1), + dict(fieldname='gst_col_break', fieldtype='Column Break', insert_after='ecommerce_gstin'), + dict(fieldname='reason_for_issuing_document', label='Reason For Issuing document', + fieldtype='Select', insert_after='gst_col_break', print_hide=1, + depends_on='eval:doc.is_return==1', + options='\n01-Sales Return\n02-Post Sale Discount\n03-Deficiency in services\n04-Correction in Invoice\n05-Change in POS\n06-Finalization of Provisional assessment\n07-Others') ] purchase_invoice_gst_fields = [ @@ -112,7 +116,21 @@ def make_custom_fields(): options='supplier_address.gstin', print_hide=1), dict(fieldname='company_gstin', label='Company GSTIN', fieldtype='Data', insert_after='shipping_address', - options='shipping_address.gstin', print_hide=1) + options='shipping_address.gstin', print_hide=1), + dict(fieldname='place_of_supply', label='Place of Supply', + fieldtype='Data', insert_after='shipping_address', + print_hide=1, read_only=0), + dict(fieldname='eligibility_for_itc', label='Eligibility For ITC', + fieldtype='Select', insert_after='reason_for_issuing_document', print_hide=1, + options='input\ninput service\ncapital goods\nineligible', default="ineligible"), + dict(fieldname='itc_integrated_tax', label='Availed ITC Integrated Tax', + fieldtype='Data', insert_after='eligibility_for_itc', print_hide=1), + dict(fieldname='itc_central_tax', label='Availed ITC Central Tax', + fieldtype='Data', insert_after='itc_integrated_tax', print_hide=1), + dict(fieldname='itc_state_tax', label='Availed ITC State/UT Tax', + fieldtype='Data', insert_after='itc_central_tax', print_hide=1), + dict(fieldname='itc_cess_amount', label='Availed ITC Cess', + fieldtype='Data', insert_after='itc_state_tax', print_hide=1), ] sales_invoice_gst_fields = [ @@ -123,11 +141,20 @@ def make_custom_fields(): fieldtype='Data', insert_after='shipping_address', options='shipping_address_name.gstin', print_hide=1), dict(fieldname='place_of_supply', label='Place of Supply', - fieldtype='Data', insert_after='customer_gstin', print_hide=1, - options='shipping_address_name.gst_state_number', read_only=0), + fieldtype='Data', insert_after='customer_gstin', + print_hide=1, read_only=0), dict(fieldname='company_gstin', label='Company GSTIN', fieldtype='Data', insert_after='company_address', - options='company_address.gstin', print_hide=1) + options='company_address.gstin', print_hide=1), + dict(fieldname='port_code', label='Port Code', + fieldtype='Data', insert_after='reason_for_issuing_document', print_hide=1, + depends_on="eval:doc.invoice_type=='Export' "), + dict(fieldname='shipping_bill_number', label=' Shipping Bill Number', + fieldtype='Data', insert_after='port_code', print_hide=1, + depends_on="eval:doc.invoice_type=='Export' "), + dict(fieldname='shipping_bill_date', label='Shipping Bill Date', + fieldtype='Date', insert_after='shipping_bill_number', print_hide=1, + depends_on="eval:doc.invoice_type=='Export' ") ] custom_fields = { @@ -139,8 +166,8 @@ def make_custom_fields(): dict(fieldname='gst_state_number', label='GST State Number', fieldtype='Int', insert_after='gst_state', read_only=1), ], - 'Purchase Invoice': purchase_invoice_gst_fields + invoice_gst_fields, - 'Sales Invoice': sales_invoice_gst_fields + invoice_gst_fields, + 'Purchase Invoice': invoice_gst_fields + purchase_invoice_gst_fields, + 'Sales Invoice': invoice_gst_fields + sales_invoice_gst_fields, "Delivery Note": sales_invoice_gst_fields, 'Item': [ dict(fieldname='gst_hsn_code', label='HSN/SAC', diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 2cd71a5bec..1b912183b1 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -1,5 +1,6 @@ import frappe, re from frappe import _ +from frappe.utils import cstr from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount @@ -29,12 +30,12 @@ def get_itemised_tax_breakup_header(item_doctype, tax_accounts): return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts else: return [_("Item"), _("Taxable Amount")] + tax_accounts - + def get_itemised_tax_breakup_data(doc): itemised_tax = get_itemised_tax(doc.taxes) itemised_taxable_amount = get_itemised_taxable_amount(doc.items) - + if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'): return itemised_tax, itemised_taxable_amount @@ -60,7 +61,19 @@ def get_itemised_tax_breakup_data(doc): return hsn_tax, hsn_taxable_amount +def set_place_of_supply(doc, method): + if not frappe.get_meta('Address').has_field('gst_state'): return + + if doc.doctype == "Sales Invoice": + address_name = doc.shipping_address_name or doc.customer_address + elif doc.doctype == "Purchase Invoice": + address_name = doc.shipping_address or doc.supplier_address + + if address_name: + address = frappe.db.get_value("Address", address_name, ["gst_state", "gst_state_number"], as_dict=1) + doc.place_of_supply = cstr(address.gst_state_number) + "-" + cstr(address.gst_state) + # don't remove this function it is used in tests def test_method(): '''test function''' - return 'overridden' \ No newline at end of file + return 'overridden' diff --git a/erpnext/regional/report/gstr_1/__init__.py b/erpnext/regional/report/gstr_1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/report/gstr_1/gstr_1.js b/erpnext/regional/report/gstr_1/gstr_1.js new file mode 100644 index 0000000000..3a635277e4 --- /dev/null +++ b/erpnext/regional/report/gstr_1/gstr_1.js @@ -0,0 +1,38 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +frappe.query_reports["GSTR-1"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -3), + "width": "80" + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname":"type_of_business", + "label": __("Type of Business"), + "fieldtype": "Select", + "reqd": 1, + "options": ["B2B", "B2C Large", "B2C Small","CDNR", "EXPORT"], + "default": "B2B" + } + ] +} diff --git a/erpnext/regional/report/gstr_1/gstr_1.json b/erpnext/regional/report/gstr_1/gstr_1.json new file mode 100644 index 0000000000..a71d89eaee --- /dev/null +++ b/erpnext/regional/report/gstr_1/gstr_1.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2018-01-02 15:54:41.424225", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "test", + "modified": "2018-01-02 17:56:15.379347", + "modified_by": "Administrator", + "module": "Regional", + "name": "GSTR-1", + "owner": "Administrator", + "ref_doctype": "GL Entry", + "report_name": "GSTR-1", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py new file mode 100644 index 0000000000..96c55ff625 --- /dev/null +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -0,0 +1,487 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +from frappe.utils import flt +from datetime import date + +def execute(filters=None): + return Gstr1Report(filters).run() + +class Gstr1Report(object): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.columns = [] + self.data = [] + self.doctype = "Sales Invoice" + self.tax_doctype = "Sales Taxes and Charges" + self.select_columns = """ + name as invoice_number, + customer_name, + posting_date, + base_grand_total, + base_rounded_total, + customer_gstin, + place_of_supply, + ecommerce_gstin, + reverse_charge, + invoice_type, + return_against, + is_return, + invoice_type, + export_type, + port_code, + shipping_bill_number, + shipping_bill_date, + reason_for_issuing_document + """ + self.customer_type = "Company" if self.filters.get("type_of_business") == "B2B" else "Individual" + + def run(self): + self.get_columns() + self.get_gst_accounts() + self.get_invoice_data() + + if self.invoices: + self.get_invoice_items() + self.get_items_based_on_tax_rate() + self.invoice_fields = [d["fieldname"] for d in self.invoice_columns] + self.get_data() + + return self.columns, self.data + + def get_data(self): + for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): + invoice_details = self.invoices.get(inv) + for rate, items in items_based_on_rate.items(): + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) + if self.filters.get("type_of_business") == "B2C Small": + row.append("E" if invoice_details.ecommerce_gstin else "OE") + + if self.filters.get("type_of_business") == "CDNR": + row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N") + row.append("C" if invoice_details.return_against else "R") + + self.data.append(row) + + def get_row_data_for_invoice(self, invoice, invoice_details, tax_rate, items): + row = [] + for fieldname in self.invoice_fields: + if self.filters.get("type_of_business") == "CDNR" and fieldname == "invoice_value": + row.append(abs(invoice_details.base_rounded_total) or abs(invoice_details.base_grand_total)) + elif fieldname == "invoice_value": + row.append(invoice_details.base_rounded_total or invoice_details.base_grand_total) + else: + row.append(invoice_details.get(fieldname)) + + taxable_value = sum([abs(net_amount) + for item_code, net_amount in self.invoice_items.get(invoice).items() if item_code in items]) + row += [tax_rate, taxable_value] + + return row, taxable_value + + def get_invoice_data(self): + self.invoices = frappe._dict() + conditions = self.get_conditions() + invoice_data = frappe.db.sql(""" + select + {select_columns} + from `tab{doctype}` + where docstatus = 1 {where_conditions} + order by posting_date desc + """.format(select_columns=self.select_columns, doctype=self.doctype, + where_conditions=conditions), self.filters, as_dict=1) + + for d in invoice_data: + self.invoices.setdefault(d.invoice_number, d) + + def get_conditions(self): + conditions = "" + + for opts in (("company", " and company=%(company)s"), + ("from_date", " and posting_date>=%(from_date)s"), + ("to_date", " and posting_date<=%(to_date)s")): + if self.filters.get(opts[0]): + conditions += opts[1] + + customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type}) + + if self.filters.get("type_of_business") == "B2B": + conditions += " and invoice_type != 'Export' and is_return != 1 and customer in ('{0}')".\ + format("', '".join([frappe.db.escape(c.name) for c in customers])) + + if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"): + b2c_limit = frappe.db.get_single_value('GSt Settings', 'b2c_limit') + if not b2c_limit: + frappe.throw(_("Please set B2C Limit in GST Settings.")) + + if self.filters.get("type_of_business") == "B2C Large": + conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2) + and grand_total > {0} and is_return != 1 and customer in ('{1}')""".\ + format(flt(b2c_limit), "', '".join([frappe.db.escape(c.name) for c in customers]) ) + + elif self.filters.get("type_of_business") == "B2C Small": + conditions += """ and ( + SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2) + or grand_total <= {0}) and is_return != 1 and customer in ('{1}')""".\ + format(flt(b2c_limit), "', '".join([frappe.db.escape(c.name) for c in customers])) + + elif self.filters.get("type_of_business") == "CDNR": + conditions += """ and is_return = 1 """ + + elif self.filters.get("type_of_business") == "EXPORT": + conditions += """ and is_return !=1 and invoice_type = 'Export' """ + return conditions + + def get_invoice_items(self): + self.invoice_items = frappe._dict() + items = frappe.db.sql(""" + select item_code, parent, base_net_amount + from `tab%s Item` + where parent in (%s) + """ % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) + + for d in items: + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, d.base_net_amount) + + def get_items_based_on_tax_rate(self): + self.tax_details = frappe.db.sql(""" + select + parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount + from `tab%s` + where + parenttype = %s and docstatus = 1 + and parent in (%s) + order by account_head + """ % (self.tax_doctype, '%s', ', '.join(['%s']*len(self.invoices.keys()))), + tuple([self.doctype] + self.invoices.keys())) + + self.items_based_on_tax_rate = {} + self.invoice_cess = frappe._dict() + unidentified_gst_accounts = [] + for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: + if account in self.gst_accounts.cess_account: + self.invoice_cess.setdefault(parent, tax_amount) + else: + if item_wise_tax_detail: + try: + item_wise_tax_detail = json.loads(item_wise_tax_detail) + cgst_or_sgst = False + if account in self.gst_accounts.cgst_account \ + or account in self.gst_accounts.sgst_account: + cgst_or_sgst = True + + if not (cgst_or_sgst or account in self.gst_accounts.igst_account): + if "gst" in account.lower() and account not in unidentified_gst_accounts: + unidentified_gst_accounts.append(account) + continue + + for item_code, tax_amounts in item_wise_tax_detail.items(): + tax_rate = tax_amounts[0] + if cgst_or_sgst: + tax_rate *= 2 + + rate_based_dict = self.items_based_on_tax_rate\ + .setdefault(parent, {}).setdefault(tax_rate, []) + if item_code not in rate_based_dict: + rate_based_dict.append(item_code) + except ValueError: + continue + if unidentified_gst_accounts: + frappe.msgprint(_("Following accounts might be selected in GST Settings:") + + "
" + "
".join(unidentified_gst_accounts), alert=True) + + def get_gst_accounts(self): + self.gst_accounts = frappe._dict() + gst_settings_accounts = frappe.get_list("GST Account", + filters={"parent": "GST Settings", "company": self.filters.company}, + fields=["cgst_account", "sgst_account", "igst_account", "cess_account"]) + + if not gst_settings_accounts: + frappe.throw(_("Please set GST Accounts in GST Settings")) + + for d in gst_settings_accounts: + for acc, val in d.items(): + self.gst_accounts.setdefault(acc, []).append(val) + + def get_columns(self): + self.tax_columns = [ + { + "fieldname": "rate", + "label": "Rate", + "fieldtype": "Int", + "width": 60 + }, + { + "fieldname": "taxable_value", + "label": "Taxable Value", + "fieldtype": "Currency", + "width": 100 + } + ] + self.other_columns = [] + + if self.filters.get("type_of_business") == "B2B": + self.invoice_columns = [ + { + "fieldname": "customer_gstin", + "label": "GSTIN/UIN of Recipient", + "fieldtype": "Data", + "width": 150 + }, + { + "fieldname": "customer_name", + "label": "Receiver Name", + "fieldtype": "Data", + "width":100 + }, + { + "fieldname": "invoice_number", + "label": "Invoice Number", + "fieldtype": "Link", + "options": "Sales Invoice", + "width":100 + }, + { + "fieldname": "posting_date", + "label": "Invoice date", + "fieldtype": "Date", + "width":80 + }, + { + "fieldname": "invoice_value", + "label": "Invoice Value", + "fieldtype": "Currency", + "width":100 + }, + { + "fieldname": "place_of_supply", + "label": "Place of Supply", + "fieldtype": "Data", + "width":100 + }, + { + "fieldname": "reverse_charge", + "label": "Reverse Charge", + "fieldtype": "Data" + }, + { + "fieldname": "invoice_type", + "label": "Invoice Type", + "fieldtype": "Data" + }, + { + "fieldname": "ecommerce_gstin", + "label": "E-Commerce GSTIN", + "fieldtype": "Data", + "width":120 + } + ] + self.other_columns = [ + { + "fieldname": "cess_amount", + "label": "Cess Amount", + "fieldtype": "Currency", + "width": 100 + } + ] + + elif self.filters.get("type_of_business") == "B2C Large": + self.invoice_columns = [ + { + "fieldname": "invoice_number", + "label": "Invoice Number", + "fieldtype": "Link", + "options": "Sales Invoice", + "width": 120 + }, + { + "fieldname": "posting_date", + "label": "Invoice date", + "fieldtype": "Date", + "width": 100 + }, + { + "fieldname": "invoice_value", + "label": "Invoice Value", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "place_of_supply", + "label": "Place of Supply", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "ecommerce_gstin", + "label": "E-Commerce GSTIN", + "fieldtype": "Data", + "width": 130 + } + ] + self.other_columns = [ + { + "fieldname": "cess_amount", + "label": "Cess Amount", + "fieldtype": "Currency", + "width": 100 + } + ] + elif self.filters.get("type_of_business") == "CDNR": + self.invoice_columns = [ + { + "fieldname": "customer_gstin", + "label": "GSTIN/UIN of Recipient", + "fieldtype": "Data", + "width": 150 + }, + { + "fieldname": "customer_name", + "label": "Receiver Name", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "return_against", + "label": "Invoice/Advance Receipt Number", + "fieldtype": "Link", + "options": "Sales Invoice", + "width": 120 + }, + { + "fieldname": "posting_date", + "label": "Invoice/Advance Receipt date", + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "invoice_number", + "label": "Invoice/Advance Receipt Number", + "fieldtype": "Link", + "options": "Sales Invoice", + "width":120 + }, + { + "fieldname": "posting_date", + "label": "Invoice/Advance Receipt date", + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "reason_for_issuing_document", + "label": "Reason For Issuing document", + "fieldtype": "Data", + "width": 140 + }, + { + "fieldname": "place_of_supply", + "label": "Place of Supply", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "invoice_value", + "label": "Invoice Value", + "fieldtype": "Currency", + "width": 120 + } + ] + self.other_columns = [ + { + "fieldname": "cess_amount", + "label": "Cess Amount", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "pre_gst", + "label": "PRE GST", + "fieldtype": "Data", + "width": 80 + }, + { + "fieldname": "document_type", + "label": "Document Type", + "fieldtype": "Data", + "width": 80 + } + ] + elif self.filters.get("type_of_business") == "B2C Small": + self.invoice_columns = [ + { + "fieldname": "place_of_supply", + "label": "Place of Supply", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "ecommerce_gstin", + "label": "E-Commerce GSTIN", + "fieldtype": "Data", + "width": 130 + } + ] + self.other_columns = [ + { + "fieldname": "cess_amount", + "label": "Cess Amount", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "type", + "label": "Type", + "fieldtype": "Data", + "width": 50 + } + ] + elif self.filters.get("type_of_business") == "EXPORT": + self.invoice_columns = [ + { + "fieldname": "export_type", + "label": "Export Type", + "fieldtype": "Data", + "width":120 + }, + { + "fieldname": "invoice_number", + "label": "Invoice Number", + "fieldtype": "Link", + "options": "Sales Invoice", + "width":120 + }, + { + "fieldname": "posting_date", + "label": "Invoice date", + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "invoice_value", + "label": "Invoice Value", + "fieldtype": "Currency", + "width": 120 + }, + { + "fieldname": "port_code", + "label": "Port Code", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "shipping_bill_number", + "label": "Shipping Bill Number", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "shipping_bill_date", + "label": "Shipping Bill Date", + "fieldtype": "Date", + "width": 120 + } + ] + self.columns = self.invoice_columns + self.tax_columns + self.other_columns diff --git a/erpnext/regional/report/gstr_2/__init__.py b/erpnext/regional/report/gstr_2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/report/gstr_2/gstr_2.js b/erpnext/regional/report/gstr_2/gstr_2.js new file mode 100644 index 0000000000..5c1ea67e8e --- /dev/null +++ b/erpnext/regional/report/gstr_2/gstr_2.js @@ -0,0 +1,39 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["GSTR-2"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -3), + "width": "80" + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname":"type_of_business", + "label": __("Type of Business"), + "fieldtype": "Select", + "reqd": 1, + "options": ["B2B","CDNR"], + "default": "B2B" + } + ] +} diff --git a/erpnext/regional/report/gstr_2/gstr_2.json b/erpnext/regional/report/gstr_2/gstr_2.json new file mode 100644 index 0000000000..929ed914d8 --- /dev/null +++ b/erpnext/regional/report/gstr_2/gstr_2.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2018-01-29 12:59:55.650445", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2018-01-29 12:59:55.650445", + "modified_by": "Administrator", + "module": "Regional", + "name": "GSTR-2", + "owner": "Administrator", + "ref_doctype": "GL Entry", + "report_name": "GSTR-2", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file diff --git a/erpnext/regional/report/gstr_2/gstr_2.py b/erpnext/regional/report/gstr_2/gstr_2.py new file mode 100644 index 0000000000..679e567d42 --- /dev/null +++ b/erpnext/regional/report/gstr_2/gstr_2.py @@ -0,0 +1,277 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from datetime import date +from erpnext.regional.report.gstr_1.gstr_1 import Gstr1Report + +def execute(filters=None): + return Gstr2Report(filters).run() + +class Gstr2Report(Gstr1Report): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.columns = [] + self.data = [] + self.doctype = "Purchase Invoice" + self.tax_doctype = "Purchase Taxes and Charges" + self.select_columns = """ + name as invoice_number, + supplier_name, + posting_date, + base_grand_total, + base_rounded_total, + supplier_gstin, + place_of_supply, + ecommerce_gstin, + reverse_charge, + invoice_type, + return_against, + is_return, + invoice_type, + export_type, + reason_for_issuing_document, + eligibility_for_itc, + itc_integrated_tax, + itc_central_tax, + itc_state_tax, + itc_cess_amount + """ + + def get_data(self): + self.get_igst_invoices() + for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): + invoice_details = self.invoices.get(inv) + for rate, items in items_based_on_rate.items(): + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) + tax_amount = taxable_value * rate / 100 + if inv in self.igst_invoices: + row += [tax_amount, 0, 0] + else: + row += [0, tax_amount / 2, tax_amount / 2] + + row += [ + self.invoice_cess.get(inv), + invoice_details.get('eligibility_for_itc'), + invoice_details.get('itc_integrated_tax'), + invoice_details.get('itc_central_tax'), + invoice_details.get('itc_state_tax'), + invoice_details.get('itc_cess_amount') + ] + if self.filters.get("type_of_business") == "CDNR": + row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N") + row.append("C" if invoice_details.return_against else "R") + + self.data.append(row) + + def get_igst_invoices(self): + self.igst_invoices = [] + for d in self.tax_details: + is_igst = True if d[1] in self.gst_accounts.igst_account else False + if is_igst and d[0] not in self.igst_invoices: + self.igst_invoices.append(d[0]) + if is_igst: + break + + def get_conditions(self): + conditions = "" + + for opts in (("company", " and company=%(company)s"), + ("from_date", " and posting_date>=%(from_date)s"), + ("to_date", " and posting_date<=%(to_date)s")): + if self.filters.get(opts[0]): + conditions += opts[1] + + if self.filters.get("type_of_business") == "B2B": + conditions += "and invoice_type != 'Export' and is_return != 1 " + + elif self.filters.get("type_of_business") == "CDNR": + conditions += """ and is_return = 1 """ + + return conditions + + def get_columns(self): + self.tax_columns = [ + { + "fieldname": "rate", + "label": "Rate", + "fieldtype": "Int", + "width": 60 + }, + { + "fieldname": "taxable_value", + "label": "Taxable Value", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "integrated_tax_paid", + "label": "Integrated Tax Paid", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "central_tax_paid", + "label": "Central Tax Paid", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "state_tax_paid", + "label": "State/UT Tax Paid", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "cess_amount", + "label": "Cess Paid", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "eligibility_for_itc", + "label": "Eligibility For ITC", + "fieldtype": "Data", + "width": 100 + }, + { + "fieldname": "itc_integrated_tax", + "label": "Availed ITC Integrated Tax", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "itc_central_tax", + "label": "Availed ITC Central Tax", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "itc_state_tax", + "label": "Availed ITC State/UT Tax", + "fieldtype": "Currency", + "width": 100 + }, + { + "fieldname": "itc_cess_amount", + "label": "Availed ITC Cess ", + "fieldtype": "Currency", + "width": 100 + } + ] + self.other_columns = [] + + if self.filters.get("type_of_business") == "B2B": + self.invoice_columns = [ + { + "fieldname": "supplier_gstin", + "label": "GSTIN of Supplier", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "invoice_number", + "label": "Invoice Number", + "fieldtype": "Link", + "options": "Purchase Invoice", + "width": 120 + }, + { + "fieldname": "posting_date", + "label": "Invoice date", + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "invoice_value", + "label": "Invoice Value", + "fieldtype": "Currency", + "width": 120 + }, + { + "fieldname": "place_of_supply", + "label": "Place of Supply", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "reverse_charge", + "label": "Reverse Charge", + "fieldtype": "Data", + "width": 80 + }, + { + "fieldname": "invoice_type", + "label": "Invoice Type", + "fieldtype": "Data", + "width": 80 + } + ] + elif self.filters.get("type_of_business") == "CDNR": + self.invoice_columns = [ + { + "fieldname": "supplier_gstin", + "label": "GSTIN of Supplier", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "invoice_number", + "label": "Note/Refund Voucher Number", + "fieldtype": "Link", + "options": "Purchase Invoice" + }, + { + "fieldname": "posting_date", + "label": "Note/Refund Voucher date", + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "return_against", + "label": "Invoice/Advance Payment Voucher Number", + "fieldtype": "Link", + "options": "Purchase Invoice", + "width": 120 + }, + { + "fieldname": "posting_date", + "label": "Invoice/Advance Payment Voucher date", + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "reason_for_issuing_document", + "label": "Reason For Issuing document", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "supply_type", + "label": "Supply Type", + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "invoice_value", + "label": "Invoice Value", + "fieldtype": "Currency", + "width": 120 + } + ] + self.other_columns = [ + { + "fieldname": "pre_gst", + "label": "PRE GST", + "fieldtype": "Data", + "width": 50 + }, + { + "fieldname": "document_type", + "label": "Document Type", + "fieldtype": "Data", + "width": 50 + } + ] + self.columns = self.invoice_columns + self.tax_columns + self.other_columns From 33b26143779728e929e72cf04af0596e9a3b0c23 Mon Sep 17 00:00:00 2001 From: Jay Parikh Date: Fri, 16 Feb 2018 14:45:40 +0530 Subject: [PATCH 06/27] [Bug] Wrong Calculation of Total Weight at Purchase Invoice item for Purchasing in Different Unit (UoM) #12965 (#12966) --- erpnext/public/js/controllers/taxes_and_totals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index c1b020bbe5..f00b84f368 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -92,7 +92,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ item.amount = flt(item.rate * item.qty, precision("amount", item)); item.net_amount = item.amount; item.item_tax_amount = 0.0; - item.total_weight = flt(item.weight_per_unit * item.qty); + item.total_weight = flt(item.weight_per_unit * item.stock_qty); me.set_in_company_currency(item, ["price_list_rate", "rate", "amount", "net_rate", "net_amount"]); }); From ec23a6f0eb33792e3a5d4a5c0e902164c3286a12 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Mon, 19 Feb 2018 11:33:11 +0530 Subject: [PATCH 07/27] [fix] section break for raw_material_details (#12976) --- .../purchase_order/purchase_order.json | 107 +++++++++++------- 1 file changed, 69 insertions(+), 38 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a895a54a79..53771d1742 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -41,11 +41,11 @@ "search_index": 0, "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, "collapsible": 0, "columns": 0, "default": "{supplier_name}", @@ -292,40 +292,40 @@ "search_index": 1, "set_only_once": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "schedule_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": "Reqd By 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, + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "schedule_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": "Reqd By 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, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -3096,6 +3096,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": "supplied_items", + "columns": 0, + "fieldname": "raw_material_details", + "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": "Raw Materials Supplied", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3291,9 +3322,9 @@ "is_submittable": 1, "issingle": 0, "istable": 0, - "max_attachments": 0, - "modified": "2017-12-21 14:45:34.140128", - "modified_by": "Administrator", + "max_attachments": 0, + "modified": "2018-02-17 11:00:05.037716", + "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", "owner": "Administrator", From 174a18399c10c3f0b13cf394132f3ed6768708db Mon Sep 17 00:00:00 2001 From: Pawan Mehta Date: Tue, 9 Jan 2018 10:53:13 +0530 Subject: [PATCH 08/27] Added Payment Details to Sales Payment Summary Report (#12358) * [fix] #12357 * label changes --- .../sales_payment_summary.js | 9 +- .../sales_payment_summary.py | 83 ++++++++++++++++--- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js index a0e56ded96..068926b063 100644 --- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js +++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js @@ -33,8 +33,13 @@ frappe.query_reports["Sales Payment Summary"] = { }, { "fieldname":"is_pos", - "label": __("POS?"), + "label": __("Show only POS"), "fieldtype": "Check" - } + }, + { + "fieldname":"payment_detail", + "label": __("Show Payment Details"), + "fieldtype": "Check" + }, ] }; \ No newline at end of file diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py index c2b145683d..9969c55a91 100644 --- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py @@ -14,25 +14,41 @@ def execute(filters=None): def get_columns(): return [ _("Date") + ":Date:80", - _("Owner") + "::150", - _("Payment Mode") + "::140", + _("Owner") + ":Data:200", + _("Payment Mode") + ":Data:240", _("Sales and Returns") + ":Currency/currency:120", _("Taxes") + ":Currency/currency:120", - _("Payments") + ":Currency/currency:120", - _("Outstanding Amount") + ":Currency/currency:150", + _("Payments") + ":Currency/currency:120" ] def get_sales_payment_data(filters, columns): - sales_invoice_data = get_sales_invoice_data(filters) data = [] + show_payment_detail = False + + sales_invoice_data = get_sales_invoice_data(filters) mode_of_payments = get_mode_of_payments(filters) + mode_of_payment_details = get_mode_of_payment_details(filters) + + if filters.get("payment_detail"): + show_payment_detail = True + else: + show_payment_detail = False + for inv in sales_invoice_data: - mode_of_payment = inv["owner"]+cstr(inv["posting_date"]) - row = [inv.posting_date, inv.owner,", ".join(mode_of_payments.get(mode_of_payment, [])), - inv.net_total, - inv.total_taxes, (inv.net_total + inv.total_taxes - inv.outstanding_amount), - inv.outstanding_amount] - data.append(row) + owner_posting_date = inv["owner"]+cstr(inv["posting_date"]) + if show_payment_detail: + row = [inv.posting_date, inv.owner," ",inv.net_total,inv.total_taxes, 0] + data.append(row) + for mop_detail in mode_of_payment_details.get(owner_posting_date,[]): + row = [inv.posting_date, inv.owner,mop_detail[0],0,0,mop_detail[1],0] + data.append(row) + else: + total_payment = 0 + for mop_detail in mode_of_payment_details.get(owner_posting_date,[]): + total_payment = total_payment + mop_detail[1] + row = [inv.posting_date, inv.owner,", ".join(mode_of_payments.get(owner_posting_date, [])), + inv.net_total,inv.total_taxes,total_payment] + data.append(row) return data def get_conditions(filters): @@ -73,9 +89,17 @@ def get_mode_of_payments(filters): union select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c - where a.name = c.reference_name + where a.name = c.reference_name and b.name = c.parent and a.name in ({invoice_list_names}) + union + select a.owner, a.posting_date, + ifnull(a.voucher_type,'') as mode_of_payment + from `tabJournal Entry` a, `tabJournal Entry Account` b + where a.name = b.parent + and a.docstatus = 1 + and b.reference_type = "Sales Invoice" + and b.reference_name in ({invoice_list_names}) """.format(invoice_list_names=invoice_list_names), as_dict=1) for d in inv_mop: mode_of_payments.setdefault(d["owner"]+cstr(d["posting_date"]), []).append(d.mode_of_payment) @@ -86,4 +110,37 @@ def get_invoices(filters): return frappe.db.sql("""select a.name from `tabSales Invoice` a where a.docstatus = 1 and {conditions}""".format(conditions=conditions), - filters, as_dict=1) \ No newline at end of file + filters, as_dict=1) + +def get_mode_of_payment_details(filters): + mode_of_payment_details = {} + invoice_list = get_invoices(filters) + invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list]) + if invoice_list: + inv_mop_detail = frappe.db.sql("""select a.owner, a.posting_date, + ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_amount) as paid_amount + from `tabSales Invoice` a, `tabSales Invoice Payment` b + where a.name = b.parent + and a.name in ({invoice_list_names}) + group by a.owner, a.posting_date, mode_of_payment + union + select a.owner,a.posting_date, + ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_paid_amount) as paid_amount + from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c + where a.name = c.reference_name + and b.name = c.parent + and a.name in ({invoice_list_names}) + group by a.owner, a.posting_date, mode_of_payment + union + select a.owner, a.posting_date, + ifnull(a.voucher_type,'') as mode_of_payment, sum(b.credit) + from `tabJournal Entry` a, `tabJournal Entry Account` b + where a.name = b.parent + and a.docstatus = 1 + and b.reference_type = "Sales Invoice" + and b.reference_name in ({invoice_list_names}) + group by a.owner, a.posting_date, mode_of_payment + """.format(invoice_list_names=invoice_list_names), as_dict=1) + for d in inv_mop_detail: + mode_of_payment_details.setdefault(d["owner"]+cstr(d["posting_date"]), []).append((d.mode_of_payment,d.paid_amount)) + return mode_of_payment_details \ No newline at end of file From 54483181463dff6110347cc9862fd5ba0278e903 Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Mon, 19 Feb 2018 12:44:52 +0530 Subject: [PATCH 09/27] fix total field (#12992) --- .../sales_person_wise_transaction_summary.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py index 558a9af061..025e740173 100644 --- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py +++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -13,10 +13,8 @@ def execute(filters=None): entries = get_entries(filters) item_details = get_item_details() data = [] - total_contribution_amount = 0 - for d in entries: - total_contribution_amount += flt(d.contribution_amt) + for d in entries: data.append([ d.name, d.customer, d.territory, d.posting_date, d.item_code, item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"), @@ -25,8 +23,6 @@ def execute(filters=None): if data: total_row = [""]*len(data[0]) - total_row[0] = _("Total") - total_row[-1] = total_contribution_amount data.append(total_row) return columns, data From 6c5d7986ab52c2be6233b4ce7122b24e8ad52729 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Mon, 19 Feb 2018 08:41:56 +0100 Subject: [PATCH 10/27] Outstanding Amount not getting updated in Payment Entry #12713 (#12975) * allow to `set_missing_ref_details` by force * update payment entry references during submit and cancel --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c32acc13c8..e19295cf4c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -58,16 +58,21 @@ class PaymentEntry(AccountsController): if self.difference_amount: frappe.throw(_("Difference Amount must be zero")) self.make_gl_entries() + self.update_outstanding_amounts() self.update_advance_paid() self.update_expense_claim() def on_cancel(self): self.setup_party_account_field() self.make_gl_entries(cancel=1) + self.update_outstanding_amounts() self.update_advance_paid() self.update_expense_claim() self.delink_advance_entry_references() + def update_outstanding_amounts(self): + self.set_missing_ref_details(force=True) + def validate_duplicate_entry(self): reference_names = [] for d in self.get("references"): @@ -129,14 +134,14 @@ class PaymentEntry(AccountsController): self.set_missing_ref_details() - def set_missing_ref_details(self): + def set_missing_ref_details(self, force=False): for d in self.get("references"): if d.allocated_amount: ref_details = get_reference_details(d.reference_doctype, d.reference_name, self.party_account_currency) for field, value in ref_details.items(): - if not d.get(field): + if not d.get(field) or force: d.set(field, value) def validate_payment_type(self): From a5003f8f5b130b0d273c16a5a8a5d1483dbf88c8 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Tue, 20 Feb 2018 11:14:54 +0530 Subject: [PATCH 11/27] [minor] Balance Qty added in reorder level email notification (#13000) --- erpnext/templates/emails/reorder_item.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/templates/emails/reorder_item.html b/erpnext/templates/emails/reorder_item.html index c4039e3ee0..c1aa897038 100644 --- a/erpnext/templates/emails/reorder_item.html +++ b/erpnext/templates/emails/reorder_item.html @@ -7,8 +7,9 @@ {{ _("Item") }} {{ _("Warehouse") }} - {{ _("Quantity") }} + {{ _("Quantity") }} {{ _("UOM") }} + {{ _("Balance Qty") }} @@ -21,6 +22,7 @@ {{ item.warehouse }} {{ item.qty }} {{ item.uom }} + {{ item.balance_qty }} {%- endfor %} From 554f2de23d7af8faf2cd392004296d2722f8d0d7 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 20 Feb 2018 11:18:01 +0530 Subject: [PATCH 12/27] Code cleanup online POS (#12985) --- .../doctype/pos_profile/pos_profile.py | 2 +- .../doctype/sales_invoice/sales_invoice.js | 4 ++ .../page/point_of_sale/point_of_sale.js | 69 +++++++++---------- .../page/point_of_sale/point_of_sale.py | 13 ---- 4 files changed, 37 insertions(+), 51 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py index 0bce49c016..20a90f4bfe 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py @@ -32,7 +32,7 @@ class POSProfile(Document): .format(res[0][0], row.user), raise_exception=1) elif not row.default and not res: msgprint(_("User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.") - .format(row.user, row.idx), raise_exception=1) + .format(row.user, row.idx)) def validate_all_link_fields(self): accounts = {"Account": [self.income_account, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 754fc6b177..93c22067d8 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -106,6 +106,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte on_submit: function(doc, dt, dn) { var me = this; + if (frappe.get_route()[0] != 'Sales Invoice') { + return + } + $.each(doc["items"], function(i, row) { if(row.delivery_note) frappe.model.clear_doc("Delivery Note", row.delivery_note) }) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 9484dc7029..d1f6b7a7c2 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -256,17 +256,22 @@ erpnext.pos.PointOfSale = class PointOfSale { if (field == 'qty' && value < 0) { frappe.msgprint(__("Quantity must be positive")); value = item.qty; + } else { + item[field] = value; + if (field == "serial_no" && value) { + let serial_nos = value.split("\n"); + item["qty"] = serial_nos.filter(d => { + return d!==""; + }).length; + } } - if (field) { - return frappe.model.set_value(item.doctype, item.name, field, value) - .then(() => this.frm.script_manager.trigger('qty', item.doctype, item.name)) - .then(() => { - if (field === 'qty' && item.qty === 0) { - frappe.model.clear_doc(item.doctype, item.name); - } - }) - } + return this.frm.script_manager.trigger('qty', item.doctype, item.name) + .then(() => { + if (field === 'qty' && item.qty === 0) { + frappe.model.clear_doc(item.doctype, item.name); + } + }) return Promise.resolve(); } @@ -283,20 +288,13 @@ erpnext.pos.PointOfSale = class PointOfSale { } submit_sales_invoice() { - - frappe.confirm(__("Permanently Submit {0}?", [this.frm.doc.name]), () => { - frappe.call({ - method: 'erpnext.selling.page.point_of_sale.point_of_sale.submit_invoice', - freeze: true, - args: { - doc: this.frm.doc - } - }).then(r => { - if(r.message) { - this.frm.doc = r.message; + this.frm.savesubmit() + .then((r) => { + if (r && r.doc) { + this.frm.doc.docstatus = r.doc.docstatus; frappe.show_alert({ indicator: 'green', - message: __(`Sales invoice ${r.message.name} created succesfully`) + message: __(`Sales invoice ${r.doc.name} created succesfully`) }); this.toggle_editing(); @@ -304,21 +302,22 @@ erpnext.pos.PointOfSale = class PointOfSale { this.set_primary_action_in_modal(); } }); - }); } set_primary_action_in_modal() { - this.frm.msgbox = frappe.msgprint( - ` - ${__('Print')} - - ${__('New')}` - ); + if (!this.frm.msgbox) { + this.frm.msgbox = frappe.msgprint( + ` + ${__('Print')} + + ${__('New')}` + ); - $(this.frm.msgbox.body).find('.btn-default').on('click', () => { - this.frm.msgbox.hide(); - this.make_new_invoice(); - }) + $(this.frm.msgbox.body).find('.btn-default').on('click', () => { + this.frm.msgbox.hide(); + this.make_new_invoice(); + }) + } } change_pos_profile() { @@ -487,11 +486,6 @@ erpnext.pos.PointOfSale = class PointOfSale { // // }).addClass('visible-xs'); - this.page.add_menu_item(__("Form View"), function () { - frappe.model.sync(me.frm.doc); - frappe.set_route("Form", me.frm.doc.doctype, me.frm.doc.name); - }); - this.page.add_menu_item(__("POS Profile"), function () { frappe.set_route('List', 'POS Profile'); }); @@ -589,6 +583,7 @@ class POSCart { this.$taxes_and_totals.html(this.get_taxes_and_totals()); this.numpad && this.numpad.reset_value(); this.customer_field.set_value(""); + this.frm.msgbox = ""; this.$discount_amount.find('input:text').val(''); this.wrapper.find('.grand-total-value').text( diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index d98a01706e..8654287208 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -87,19 +87,6 @@ def get_conditions(item_code, serial_no, batch_no, barcode): return '%%%s%%'%(frappe.db.escape(item_code)), condition -@frappe.whitelist() -def submit_invoice(doc): - if isinstance(doc, basestring): - args = json.loads(doc) - - doc = frappe.new_doc('Sales Invoice') - doc.update(args) - doc.run_method("set_missing_values") - doc.run_method("calculate_taxes_and_totals") - doc.submit() - - return doc - def get_item_group_condition(pos_profile): cond = "and 1=1" item_groups = get_item_groups(pos_profile) From 2fbb4923a7aa3022885a7204772d256a0e68d62a Mon Sep 17 00:00:00 2001 From: Pawan Mehta Date: Tue, 20 Feb 2018 12:17:49 +0530 Subject: [PATCH 13/27] item balance report (#12983) * item balance report * remove hard coding * Update item_balance.json * Update item_balance.json Change name to "Item Balance (Simple)" * Update item_balance.json --- erpnext/stock/report/item_balance/__init__.py | 0 .../report/item_balance/item_balance.json | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 erpnext/stock/report/item_balance/__init__.py create mode 100644 erpnext/stock/report/item_balance/item_balance.json diff --git a/erpnext/stock/report/item_balance/__init__.py b/erpnext/stock/report/item_balance/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/report/item_balance/item_balance.json b/erpnext/stock/report/item_balance/item_balance.json new file mode 100644 index 0000000000..af58fb2c60 --- /dev/null +++ b/erpnext/stock/report/item_balance/item_balance.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "apply_user_permissions": 1, + "creation": "2018-02-17 19:52:16.370979", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2018-02-19 12:03:53.902167", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Balance (Simple)", + "owner": "Administrator", + "query": "select a.item_code as \"Item:Link/Item:120\",\n\t a.item_name as \"Item Name::150\",\n\t a.item_group as \"Item Group:Link/Item Group:120\",\n\t a.brand as \"Brand:Link/Brand:120\",\n\t a.description as \"Description::150\",\n\t b.warehouse as \"Warehouse:Link/Warehouse:120\",\n\t b.actual_qty as \"Balance Qty:Float:140\"\n from `tabItem` a left join `tabBin` b\n ON a.item_code = b.item_code", + "ref_doctype": "Bin", + "report_name": "Item Balance (Simple)", + "report_type": "Query Report", + "roles": [ + { + "role": "Sales User" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock User" + } + ] +} From f65afac353e2ffb38c8595877ced350dfa63cba1 Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Wed, 21 Feb 2018 11:12:04 +0530 Subject: [PATCH 14/27] db_update instead of save to avoid unnecessary validations (#13009) --- .../doctype/landed_cost_voucher/landed_cost_voucher.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index f51652cf15..d36b6cf827 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -109,9 +109,9 @@ class LandedCostVoucher(Document): # set valuation amount in pr item doc.update_valuation_rate("items") - # save will update landed_cost_voucher_amount and voucher_amount in PR, - # as those fields are allowed to edit after submit - doc.save() + # db_update will update and save landed_cost_voucher_amount and voucher_amount in PR + for item in doc.get("items"): + item.db_update() # update latest valuation rate in serial no self.update_rate_in_serial_no(doc) From bc5515651b1a2dc1676ea62e914b110f330eeb49 Mon Sep 17 00:00:00 2001 From: Zarrar Date: Wed, 21 Feb 2018 11:13:14 +0530 Subject: [PATCH 15/27] [Hotfix] Selecting create on blank field throws error (#13014) * selecting create on blank field error fix * error when fetching default_print_format --- erpnext/stock/doctype/delivery_trip/delivery_trip.py | 2 +- erpnext/utilities/user_progress.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index 7599cd229b..45de5e7550 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -163,9 +163,9 @@ def notify_customers(docname, date, driver, vehicle, sender_email, delivery_noti if delivery_stop_info.delivery_notes: delivery_notes = (delivery_stop_info.delivery_notes).split(",") + default_print_format = frappe.get_meta('Delivery Note').default_print_format attachments = [] for delivery_note in delivery_notes: - default_print_format = frappe.get_value('Delivery Note', delivery_note, 'default_print_format') attachments.append( frappe.attach_print('Delivery Note', delivery_note, diff --git a/erpnext/utilities/user_progress.py b/erpnext/utilities/user_progress.py index 38732138e2..cdb0e14f30 100644 --- a/erpnext/utilities/user_progress.py +++ b/erpnext/utilities/user_progress.py @@ -45,7 +45,7 @@ def get_slide_settings(): help=_("Set a sales goal you'd like to achieve for your company."), fields=[ {"fieldtype":"Currency", "fieldname":"monthly_sales_target", - "label":_("Monthly Sales Target (" + currency + ")")}, + "label":_("Monthly Sales Target (" + currency + ")"), "reqd":1}, ], submit_method="erpnext.utilities.user_progress_utils.set_sales_target", done_state_title=_("Go to " + company), From 8a77a0e1e47bae179fe5f31f9862176fe2f61d6e Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Wed, 21 Feb 2018 14:46:07 +0530 Subject: [PATCH 16/27] Add total row in all trends reports (#13010) * total in sales order trends report * total in all trends reports --- .../purchase_invoice_trends/purchase_invoice_trends.json | 4 ++-- .../report/sales_invoice_trends/sales_invoice_trends.json | 4 ++-- .../report/purchase_order_trends/purchase_order_trends.json | 4 ++-- erpnext/selling/report/quotation_trends/quotation_trends.json | 4 ++-- .../selling/report/sales_order_trends/sales_order_trends.json | 4 ++-- .../report/delivery_note_trends/delivery_note_trends.json | 4 ++-- .../purchase_receipt_trends/purchase_receipt_trends.json | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json index f167d064ca..dfae780347 100644 --- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json +++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "apply_user_permissions": 1, "creation": "2013-06-13 18:46:55", "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:16:25.027061", + "modified": "2018-02-21 01:28:31.261299", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Trends", diff --git a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json index d7837e378c..7437550cc2 100644 --- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json +++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "apply_user_permissions": 1, "creation": "2013-06-13 18:44:21", "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:15:12.885723", + "modified": "2018-02-21 01:28:03.622485", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Trends", diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json index aabb5b617a..ca58c4ed44 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "apply_user_permissions": 1, "creation": "2013-06-13 18:45:01", "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:16:13.121638", + "modified": "2018-02-21 01:28:37.416562", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Trends", diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.json b/erpnext/selling/report/quotation_trends/quotation_trends.json index 0dae26bdb2..5587cc68b4 100644 --- a/erpnext/selling/report/quotation_trends/quotation_trends.json +++ b/erpnext/selling/report/quotation_trends/quotation_trends.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "apply_user_permissions": 1, "creation": "2013-06-07 16:01:16", "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:15:33.967044", + "modified": "2018-02-21 01:28:14.928929", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Trends", diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.json b/erpnext/selling/report/sales_order_trends/sales_order_trends.json index 1ae8957ba4..9dfc7d0789 100644 --- a/erpnext/selling/report/sales_order_trends/sales_order_trends.json +++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "apply_user_permissions": 1, "creation": "2013-06-13 18:43:30", "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:15:01.634392", + "modified": "2018-02-20 08:05:46.191588", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Trends", diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json index 29aeab105c..fe133e9d5a 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "apply_user_permissions": 1, "creation": "2013-06-13 18:42:11", "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:18:32.793637", + "modified": "2018-02-21 01:28:47.049042", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Trends", diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json index 3a09b43a78..696f81675c 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "apply_user_permissions": 1, "creation": "2013-06-13 18:45:44", "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:16:00.211762", + "modified": "2018-02-21 01:28:22.682161", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Trends", From 67f74414c45ff84a7fce0e1e3854ff5611d84bd3 Mon Sep 17 00:00:00 2001 From: Zarrar Date: Wed, 21 Feb 2018 14:46:41 +0530 Subject: [PATCH 17/27] disable expand_all button for BOM (#13015) --- erpnext/manufacturing/doctype/bom/bom_tree.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js index 6cbd7a2ab1..4ec9bef998 100644 --- a/erpnext/manufacturing/doctype/bom/bom_tree.js +++ b/erpnext/manufacturing/doctype/bom/bom_tree.js @@ -13,6 +13,7 @@ frappe.treeview_settings["BOM"] = { disable_add_node: true, root_label: "BOM", //fieldname from filters get_tree_root: false, + show_expand_all: false, get_label: function(node) { if(node.data.qty) { return node.data.qty + " x " + node.data.item_code; From 09acb779d79330186c0e273d05ec8b3c6c217de4 Mon Sep 17 00:00:00 2001 From: vishdha Date: Tue, 20 Feb 2018 12:44:11 +0530 Subject: [PATCH 18/27] [new] Leader board --- .../utilities/page/leaderboard/leaderboard.js | 22 +- .../utilities/page/leaderboard/leaderboard.py | 232 +++++++++--------- 2 files changed, 121 insertions(+), 133 deletions(-) diff --git a/erpnext/utilities/page/leaderboard/leaderboard.js b/erpnext/utilities/page/leaderboard/leaderboard.js index 7e70f76fa0..d229b59799 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.js +++ b/erpnext/utilities/page/leaderboard/leaderboard.js @@ -17,14 +17,15 @@ frappe.Leaderboard = Class.extend({ this.$sidebar_list = this.page.sidebar.find('ul'); // const list of doctypes - this.doctypes = ["Customer", "Item", "Supplier", "Sales Partner"]; + this.doctypes = ["Customer", "Item", "Supplier", "Sales Partner","Sales Person"]; this.timespans = ["Week", "Month", "Quarter", "Year"]; - this.desc_fields = ["total_amount", "total_request", "annual_billing", "commission_rate"]; + this.desc_fields = ["total_sales_amount", "total_request","total_purchase_amount","commission_rate"]; this.filters = { - "Customer": ["total_amount", "total_item_purchased"], - "Item": ["total_request", "total_purchase", "avg_price"], - "Supplier": ["annual_billing", "total_unpaid"], - "Sales Partner": ["commission_rate", "target_qty", "target_amount"], + "Customer": ["total_item_purchased_qty", "total_sales_amount","receivable_amount_outstanding_amount"], + "Item": ["total_purchase_amount", "total_purchased_qty", "total_sales_amount", "total_sold_qty","available_stock_qty"], + "Supplier": ["total_item_sold_qty", "total_purchase_amount","payable_amount_outstanding_amount"], + "Sales Partner": ["commission_rate", "target_qty", "target_amount", "total_sales_amount"], + "Sales Person": ["target_qty", "target_amount", "total_sales_amount"], }; // for saving current selected filters @@ -258,7 +259,7 @@ frappe.Leaderboard = Class.extend({ var me = this; const _selected_filter = me.options.selected_filter .map(i => frappe.model.unscrub(i)); - const fields = ['name', me.options.selected_filter_item]; + const fields = ['name', 'value']; const html = `
@@ -272,11 +273,8 @@ frappe.Leaderboard = Class.extend({ return ( `
- ${ - col === "Name" - ? ` ${val} ` - : ` ${val}` + ${(filter == "value") ? "text-right" : ""}"> + ${ col === "Name" ? ` ${val} ` : ` ${val}` }
`); }).join("") diff --git a/erpnext/utilities/page/leaderboard/leaderboard.py b/erpnext/utilities/page/leaderboard/leaderboard.py index 6e7eabc826..6db40abadb 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.py +++ b/erpnext/utilities/page/leaderboard/leaderboard.py @@ -13,135 +13,145 @@ from erpnext.accounts.utils import get_currency_precision def get_leaderboard(doctype, timespan, field, start=0): """return top 10 items for that doctype based on conditions""" - filters = {"modified":(">=", get_date_from_string(timespan))} + filters = get_date_from_string(timespan) items = [] if doctype == "Customer": - items = get_all_customers(doctype, filters, [], field) + items = get_all_customers(filters, [], field) elif doctype == "Item": - items = get_all_items(doctype, filters, [], field) + items = get_all_items(filters, [], field) elif doctype == "Supplier": - items = get_all_suppliers(doctype, filters, [], field) + items = get_all_suppliers(filters, [], field) elif doctype == "Sales Partner": - items = get_all_sales_partner(doctype, filters, [], field) + items = get_all_sales_partner(filters, [], field) + elif doctype == "Sales Person": + items = get_all_sales_person(filters, [], field) if len(items) > 0: return items return [] -def get_all_customers(doctype, filters, items, field, start=0, limit=20): +def get_all_customers(filters, items, field, start=0, limit=20): """return all customers""" + if field == "total_sales_amount": + select_field = "sum(sales_order_item.base_net_amount)" + elif field == "total_item_purchased_qty": + select_field = "count(sales_order_item.name)" + elif field == "receivable_amount_outstanding_amount": + return frappe.db.sql(""" + select sales_invoice.customer as name, sum(sales_invoice.outstanding_amount) as value + FROM `tabSales Invoice` as sales_invoice + where sales_invoice.docstatus = 1 and sales_invoice.modified >= "{0}" + group by sales_invoice.customer + order by value DESC + limit {1}""".format(filters, limit), as_dict=1) - x = frappe.get_list(doctype, filters=filters, limit_start=start, limit_page_length=limit) + return frappe.db.sql(""" + select sales_order.customer as name, {0} as value + FROM `tabSales Order` as sales_order LEFT JOIN `tabSales Order Item` + as sales_order_item ON sales_order.name = sales_order_item.parent + where sales_order.docstatus = 1 and sales_order.modified >= "{1}" + group by sales_order.customer + order by value DESC + limit {2}""".format(select_field, filters, limit), as_dict=1) - for val in x: - y = dict(frappe.db.sql('''select name, grand_total from `tabSales Invoice` where customer = %s''', (val.name))) - invoice_list = y.keys() - if len(invoice_list) > 0: - item_count = frappe.db.sql('''select count(name) from `tabSales Invoice Item` where parent in (%s)''' % ", ".join( - ['%s'] * len(invoice_list)), tuple(invoice_list)) - value = 0 - if(field=="total_amount"): - value = sum(y.values()) - elif(field=="total_item_purchased"): - value = sum(destructure_tuple_of_tuples(item_count)) - item_obj = {"name": val.name, - "total_amount": get_formatted_value(sum(y.values())), - "total_item_purchased": sum(destructure_tuple_of_tuples(item_count)), - "href":"#Form/Customer/" + val.name, - "value": value} - items.append(item_obj) - items.sort(key=lambda k: k['value'], reverse=True) - return items - -def get_all_items(doctype, filters, items, field, start=0, limit=20): +def get_all_items(filters, items, field, start=0, limit=20): """return all items""" + if field == "total_sales_amount": + select_field = "sum(B.amount)" + select_doctype = "`tabSales Order Item`" + if field == "total_purchase_amount": + select_field = "sum(B.amount)" + select_doctype = "`tabPurchase Order Item`" + if field == "total_sold_qty": + select_field = "sum(B.qty)" + select_doctype = "`tabSales Order Item`" + if field == "total_purchased_qty": + select_field = "sum(B.qty)" + select_doctype = "`tabPurchase Order Item`" + if field == "available_stock_qty": + select_field = "sum(B.actual_qty)" + select_doctype = "`tabBin`" + return frappe.db.sql("""select + item.name as name , {0} as value + from `tabItem` as item join {1} as B on item.name = B.item_code and item.modified >= "{2}" + group by item.name""".format(select_field, select_doctype, filters), as_dict=1) - x = frappe.get_list(doctype, filters=filters, limit_start=start, limit_page_length=limit) - for val in x: - data = frappe.db.sql('''select item_code from `tabMaterial Request Item` where item_code = %s''', (val.name), as_list=1) - requests = destructure_tuple_of_tuples(data) - data = frappe.db.sql('''select price_list_rate from `tabItem Price` where item_code = %s''', (val.name), as_list=1) - avg_price = get_avg(destructure_tuple_of_tuples(data)) - data = frappe.db.sql('''select item_code from `tabPurchase Invoice Item` where item_code = %s''', (val.name), as_list=1) - purchases = destructure_tuple_of_tuples(data) - - value = 0 - if(field=="total_request"): - value = len(requests) - elif(field=="total_purchase"): - value = len(purchases) - elif(field=="avg_price"): - value=avg_price - item_obj = {"name": val.name, - "total_request":len(requests), - "total_purchase": len(purchases), - "avg_price": get_formatted_value(avg_price), - "href":"#Form/Item/" + val.name, - "value": value} - items.append(item_obj) - - items.sort(key=lambda k: k['value'], reverse=True) - return items - -def get_all_suppliers(doctype, filters, items, field, start=0, limit=20): +def get_all_suppliers(filters, items, field, start=0, limit=20): """return all suppliers""" - x = frappe.get_list(doctype, filters=filters, limit_start=start, limit_page_length=limit) + if field == "total_purchase_amount": + select_field = "sum(purchase_order_item.base_net_amount)" + elif field == "total_item_sold_qty": + select_field = "count(purchase_order_item.name)" + elif field == "payable_amount_outstanding_amount": + return frappe.db.sql(""" + select purchase_invoice.supplier as name, sum(purchase_invoice.outstanding_amount) as value + FROM `tabPurchase Invoice` as purchase_invoice + where purchase_invoice.docstatus = 1 and purchase_invoice.modified >= "{0}" + group by purchase_invoice.supplier + order by value DESC + limit {1}""".format(filters, limit), as_dict=1) - for val in x: + 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 >= "{1}" + group by purchase_order.supplier + order by value DESC + limit {2}""".format(select_field, filters, limit), as_dict=1) - info = get_dashboard_info(doctype, val.name) - value = 0 - if(field=="annual_billing"): - value = info["billing_this_year"] - elif(field=="total_unpaid"): - value = abs(info["total_unpaid"]) - item_obj = {"name": val.name, - "annual_billing": get_formatted_value(info["billing_this_year"]), - "total_unpaid": get_formatted_value(abs(info["total_unpaid"])), - "href":"#Form/Supplier/" + val.name, - "value": value} - items.append(item_obj) - items.sort(key=lambda k: k['value'], reverse=True) - return items - -def get_all_sales_partner(doctype, filters, items, field, start=0, limit=20): +def get_all_sales_partner(filters, items, field, start=0, limit=20): """return all sales partner""" - x = frappe.get_list(doctype, fields=["name", "commission_rate", "modified"], filters=filters, limit_start=start, limit_page_length=limit) - for val in x: - y = frappe.db.sql('''select target_qty, target_amount from `tabTarget Detail` where parent = %s''', (val.name), as_dict=1) - target_qty = sum([f["target_qty"] for f in y]) - target_amount = sum([f["target_amount"] for f in y]) + if field == "commission_rate": + select_field = "sales_partner.commission_rate" + elif field == "target_qty": + select_field = "target_detail.target_qty" + elif field == "target_amount": + select_field = "target_detail.target_amount" + elif field == "total_sales_amount": + select_field = "sum(sales_invoice.total_commission)" - value = 0 - if(field=="commission_rate"): - value = val.commission_rate - elif(field=="target_qty"): - value = target_qty - elif(field=="target_amount"): - value = target_qty - - item_obj = {"name": val.name, - "commission_rate": get_formatted_value(val.commission_rate, False), - "target_qty": target_qty, - "target_amount": get_formatted_value(target_qty), - "href":"#Form/Sales Partner/" + val.name, - "value": value} - items.append(item_obj) - - items.sort(key=lambda k: k['value'], reverse=True) - return items + return frappe.db.sql("""select sales_partner.partner_name as name, {0} as value + from + `tabSales Partner` as sales_partner inner join `tabTarget Detail` as target_detail ON sales_partner.name = target_detail.parent + inner join + `tabSales Invoice` as sales_invoice ON sales_invoice.sales_partner = sales_partner.name + where + sales_invoice.docstatus = 1 and sales_invoice.modified >= "{1}" + group by + sales_partner.partner_name + order by value DESC + limit {2}""".format(select_field, filters, limit), as_dict=1) -def destructure_tuple_of_tuples(tup_of_tup): - """return tuple(tuples) as list""" - return [y for x in tup_of_tup for y in x] +def get_all_sales_person(filters, items, field, start=0, limit=20): + """return all sales partner""" + if field == "target_qty": + select_field = "target_detail.target_qty" + elif field == "target_amount": + select_field = "target_detail.target_amount" + elif field == "total_sales_amount": + select_field = "sum(sales_team.allocated_amount)" + + return frappe.db.sql("""select sales_person.name as name, {0} as value + from + `tabSales Person` as sales_person + inner join + `tabTarget Detail` as target_detail ON sales_person.name = target_detail.parent + inner join + `tabSales Team` as sales_team ON sales_team.sales_person = sales_person.name + where sales_person.is_group = 0 and sales_team.modified >= "{1}" + group by sales_person.name + order by value DESC + limit {2}""".format(select_field,filters,limit), as_dict=1) + def get_date_from_string(seleted_timespan): """return string for ex:this week as date:string""" @@ -155,24 +165,4 @@ def get_date_from_string(seleted_timespan): else: days = -7 - return add_to_date(None, years=years, months=months, days=days, as_string=True, as_datetime=True) - -def get_filter_list(selected_filter): - """return list of keys""" - return map((lambda y : y["field"]), filter(lambda x : not (x["field"] == "name" or x["field"] == "modified"), selected_filter)) - -def get_avg(items): - """return avg of list items""" - length = len(items) - if length > 0: - return sum(items) / length - return 0 - -def get_formatted_value(value, add_symbol=True): - """return formatted value""" - if not add_symbol: - return '{:.{pre}f}'.format(value, pre=(get_currency_precision() or 2)) - currency_precision = get_currency_precision() or 2 - company = frappe.db.get_default("company") - currency = frappe.get_doc("Company", company).default_currency or frappe.boot.sysdefaults.currency - return fmt_money(value, currency_precision, currency) + return add_to_date(None, years=years, months=months, days=days, as_string=True, as_datetime=True) \ No newline at end of file From d4491d361fec426d8ae63daa878884491e8b59ed Mon Sep 17 00:00:00 2001 From: vishdha Date: Wed, 21 Feb 2018 09:33:35 +0530 Subject: [PATCH 19/27] [new] Filter based on company: --- .../utilities/page/leaderboard/leaderboard.js | 29 +++++++++++++-- .../utilities/page/leaderboard/leaderboard.py | 35 ++++++++++--------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/erpnext/utilities/page/leaderboard/leaderboard.js b/erpnext/utilities/page/leaderboard/leaderboard.js index d229b59799..f6c3fc08bc 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.js +++ b/erpnext/utilities/page/leaderboard/leaderboard.js @@ -39,6 +39,7 @@ frappe.Leaderboard = Class.extend({ selected_filter: _initial_filter, selected_filter_item: _initial_filter[0], selected_timespan: _initial_timespan, + /*selected_company: frappe.defaults.get_default('company')*/ }; this.message = null; @@ -59,12 +60,30 @@ frappe.Leaderboard = Class.extend({ this.get_sidebar_item(doctype).appendTo(this.$sidebar_list); }); + + this.company_select = this.page.add_field({ + fieldname: 'company', + label: __('Company'), + fieldtype:'Link', + options:'Company', + default:frappe.defaults.get_default('company'), + change: function(fieldname) { + me.options.selected_company = this.value; + me.make_request($container); + } + + }); + + console.log("com", this.company_select); + this.timespan_select = this.page.add_select(__("Timespan"), this.timespans.map(d => { return {"label": __(d), value: d } }) ); + + // this.timespan_select.val(this.timespans[1]); this.type_select = this.page.add_select(__("Type"), @@ -77,6 +96,7 @@ frappe.Leaderboard = Class.extend({ let $li = $(this); let doctype = $li.find('span').html(); + me.options.selected_company = frappe.defaults.get_default('company') me.options.selected_doctype = doctype; me.options.selected_filter = me.filters[doctype]; me.options.selected_filter_item = me.filters[doctype][0]; @@ -116,6 +136,7 @@ frappe.Leaderboard = Class.extend({ }, get_leaderboard: function (notify, $container, start=0) { + console.log("get", notify) var me = this; frappe.call({ @@ -123,6 +144,7 @@ frappe.Leaderboard = Class.extend({ args: { doctype: me.options.selected_doctype, timespan: me.options.selected_timespan, + company: me.options.selected_company, field: me.options.selected_filter_item, start: start }, @@ -155,6 +177,7 @@ frappe.Leaderboard = Class.extend({ }, get_leaderboard_data: function (me, res, $container) { + console.log("me res", me, res) if (res && res.message) { me.message = null; $container.find(".leaderboard-list").html(me.render_list_view(res.message)); @@ -257,9 +280,11 @@ frappe.Leaderboard = Class.extend({ get_item_html: function (item) { var me = this; + const company = frappe.defaults.get_default('Company') + const currency = frappe.get_doc(":Company", company).default_currency const _selected_filter = me.options.selected_filter .map(i => frappe.model.unscrub(i)); - const fields = ['name', 'value']; + const fields = ['name','value']; const html = `
@@ -274,7 +299,7 @@ frappe.Leaderboard = Class.extend({ `
- ${ col === "Name" ? ` ${val} ` : ` ${val}` + ${ col === "Name" ? ` ${val} ` : ` ${format_currency(val, currency)}` }
`); }).join("") diff --git a/erpnext/utilities/page/leaderboard/leaderboard.py b/erpnext/utilities/page/leaderboard/leaderboard.py index 6db40abadb..14994ddb76 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.py +++ b/erpnext/utilities/page/leaderboard/leaderboard.py @@ -10,27 +10,28 @@ from erpnext.accounts.party import get_dashboard_info from erpnext.accounts.utils import get_currency_precision @frappe.whitelist() -def get_leaderboard(doctype, timespan, field, start=0): +def get_leaderboard(doctype, timespan, company, field, start=0): """return top 10 items for that doctype based on conditions""" - + for x in xrange(1,10): + print('company', company) filters = get_date_from_string(timespan) items = [] if doctype == "Customer": - items = get_all_customers(filters, [], field) + items = get_all_customers(filters, company, [], field) elif doctype == "Item": items = get_all_items(filters, [], field) elif doctype == "Supplier": - items = get_all_suppliers(filters, [], field) + items = get_all_suppliers(filters, company, [], field) elif doctype == "Sales Partner": items = get_all_sales_partner(filters, [], field) elif doctype == "Sales Person": - items = get_all_sales_person(filters, [], field) + items = get_all_sales_person(filters, [], field) if len(items) > 0: return items return [] -def get_all_customers(filters, items, field, start=0, limit=20): +def get_all_customers(filters, company, items, field, start=0, limit=20): """return all customers""" if field == "total_sales_amount": select_field = "sum(sales_order_item.base_net_amount)" @@ -40,19 +41,19 @@ def get_all_customers(filters, items, field, start=0, limit=20): return frappe.db.sql(""" select sales_invoice.customer as name, sum(sales_invoice.outstanding_amount) as value FROM `tabSales Invoice` as sales_invoice - where sales_invoice.docstatus = 1 and sales_invoice.modified >= "{0}" + where sales_invoice.docstatus = 1 and sales_invoice.modified >= "{0}" and sales_invoice.company = "{1}" group by sales_invoice.customer order by value DESC - limit {1}""".format(filters, limit), as_dict=1) + limit {2}""".format(filters, company, limit), as_dict=1) return frappe.db.sql(""" select sales_order.customer as name, {0} as value FROM `tabSales Order` as sales_order LEFT JOIN `tabSales Order Item` as sales_order_item ON sales_order.name = sales_order_item.parent - where sales_order.docstatus = 1 and sales_order.modified >= "{1}" + where sales_order.docstatus = 1 and sales_order.modified >= "{1}" and sales_order.company = "{2}" group by sales_order.customer order by value DESC - limit {2}""".format(select_field, filters, limit), as_dict=1) + limit {3}""".format(select_field, filters, company, limit), as_dict=1) @@ -79,7 +80,7 @@ def get_all_items(filters, items, field, start=0, limit=20): from `tabItem` as item join {1} as B on item.name = B.item_code and item.modified >= "{2}" group by item.name""".format(select_field, select_doctype, filters), as_dict=1) -def get_all_suppliers(filters, items, field, start=0, limit=20): +def get_all_suppliers(filters, company, items, field, start=0, limit=20): """return all suppliers""" if field == "total_purchase_amount": @@ -90,19 +91,19 @@ def get_all_suppliers(filters, items, field, start=0, limit=20): return frappe.db.sql(""" select purchase_invoice.supplier as name, sum(purchase_invoice.outstanding_amount) as value FROM `tabPurchase Invoice` as purchase_invoice - where purchase_invoice.docstatus = 1 and purchase_invoice.modified >= "{0}" + where purchase_invoice.docstatus = 1 and purchase_invoice.modified >= "{0}" and purchase_invoice.company = "{1}" group by purchase_invoice.supplier order by value DESC - limit {1}""".format(filters, limit), as_dict=1) + limit {1}""".format(filters, company, limit), as_dict=1) 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 >= "{1}" + where purchase_order.docstatus = 1 and purchase_order.modified >= "{1}" and purchase_order.company = "{2}" group by purchase_order.supplier order by value DESC - limit {2}""".format(select_field, filters, limit), as_dict=1) + limit {3}""".format(select_field, filters, company, limit), as_dict=1) @@ -165,4 +166,6 @@ def get_date_from_string(seleted_timespan): else: days = -7 - return add_to_date(None, years=years, months=months, days=days, as_string=True, as_datetime=True) \ No newline at end of file + return add_to_date(None, years=years, months=months, days=days, as_string=True, as_datetime=True) + + From 9a64d4371efb354c548ed5fc90513e4bce63a18f Mon Sep 17 00:00:00 2001 From: vishdha Date: Wed, 21 Feb 2018 11:26:58 +0530 Subject: [PATCH 20/27] [fix] Codacy Issue --- erpnext/utilities/page/leaderboard/leaderboard.js | 15 ++++----------- erpnext/utilities/page/leaderboard/leaderboard.py | 6 ++---- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/erpnext/utilities/page/leaderboard/leaderboard.js b/erpnext/utilities/page/leaderboard/leaderboard.js index f6c3fc08bc..860506b961 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.js +++ b/erpnext/utilities/page/leaderboard/leaderboard.js @@ -39,7 +39,6 @@ frappe.Leaderboard = Class.extend({ selected_filter: _initial_filter, selected_filter_item: _initial_filter[0], selected_timespan: _initial_timespan, - /*selected_company: frappe.defaults.get_default('company')*/ }; this.message = null; @@ -60,22 +59,18 @@ frappe.Leaderboard = Class.extend({ this.get_sidebar_item(doctype).appendTo(this.$sidebar_list); }); - this.company_select = this.page.add_field({ fieldname: 'company', label: __('Company'), fieldtype:'Link', options:'Company', default:frappe.defaults.get_default('company'), - change: function(fieldname) { + change: function() { me.options.selected_company = this.value; me.make_request($container); } }); - - console.log("com", this.company_select); - this.timespan_select = this.page.add_select(__("Timespan"), this.timespans.map(d => { return {"label": __(d), value: d } @@ -96,7 +91,7 @@ frappe.Leaderboard = Class.extend({ let $li = $(this); let doctype = $li.find('span').html(); - me.options.selected_company = frappe.defaults.get_default('company') + me.options.selected_company = frappe.defaults.get_default('company'); me.options.selected_doctype = doctype; me.options.selected_filter = me.filters[doctype]; me.options.selected_filter_item = me.filters[doctype][0]; @@ -136,7 +131,6 @@ frappe.Leaderboard = Class.extend({ }, get_leaderboard: function (notify, $container, start=0) { - console.log("get", notify) var me = this; frappe.call({ @@ -177,7 +171,6 @@ frappe.Leaderboard = Class.extend({ }, get_leaderboard_data: function (me, res, $container) { - console.log("me res", me, res) if (res && res.message) { me.message = null; $container.find(".leaderboard-list").html(me.render_list_view(res.message)); @@ -280,8 +273,8 @@ frappe.Leaderboard = Class.extend({ get_item_html: function (item) { var me = this; - const company = frappe.defaults.get_default('Company') - const currency = frappe.get_doc(":Company", company).default_currency + const company = frappe.defaults.get_default('Company'); + const currency = frappe.get_doc(":Company", company).default_currency; const _selected_filter = me.options.selected_filter .map(i => frappe.model.unscrub(i)); const fields = ['name','value']; diff --git a/erpnext/utilities/page/leaderboard/leaderboard.py b/erpnext/utilities/page/leaderboard/leaderboard.py index 14994ddb76..0d580b4250 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.py +++ b/erpnext/utilities/page/leaderboard/leaderboard.py @@ -12,8 +12,6 @@ from erpnext.accounts.utils import get_currency_precision @frappe.whitelist() def get_leaderboard(doctype, timespan, company, field, start=0): """return top 10 items for that doctype based on conditions""" - for x in xrange(1,10): - print('company', company) filters = get_date_from_string(timespan) items = [] if doctype == "Customer": @@ -94,7 +92,7 @@ def get_all_suppliers(filters, company, items, field, start=0, limit=20): where purchase_invoice.docstatus = 1 and purchase_invoice.modified >= "{0}" and purchase_invoice.company = "{1}" group by purchase_invoice.supplier order by value DESC - limit {1}""".format(filters, company, limit), as_dict=1) + limit {2}""".format(filters, company, limit), as_dict=1) return frappe.db.sql(""" select purchase_order.supplier as name, {0} as value @@ -103,7 +101,7 @@ def get_all_suppliers(filters, company, items, field, start=0, limit=20): where purchase_order.docstatus = 1 and purchase_order.modified >= "{1}" and purchase_order.company = "{2}" group by purchase_order.supplier order by value DESC - limit {3}""".format(select_field, filters, company, limit), as_dict=1) + limit {3}""".format(select_field, filters, company, limit), as_dict=1) From e9166d7c19f288dd397b4cf1d26466efe376dc75 Mon Sep 17 00:00:00 2001 From: vishdha Date: Wed, 21 Feb 2018 15:35:36 +0530 Subject: [PATCH 21/27] [fix] Sales partner details get from sales Order and company currency details from selected company --- erpnext/utilities/page/leaderboard/leaderboard.js | 2 +- erpnext/utilities/page/leaderboard/leaderboard.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/utilities/page/leaderboard/leaderboard.js b/erpnext/utilities/page/leaderboard/leaderboard.js index 860506b961..36dbc629f4 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.js +++ b/erpnext/utilities/page/leaderboard/leaderboard.js @@ -273,7 +273,7 @@ frappe.Leaderboard = Class.extend({ get_item_html: function (item) { var me = this; - const company = frappe.defaults.get_default('Company'); + const company = me.options.selected_company const currency = frappe.get_doc(":Company", company).default_currency; const _selected_filter = me.options.selected_filter .map(i => frappe.model.unscrub(i)); diff --git a/erpnext/utilities/page/leaderboard/leaderboard.py b/erpnext/utilities/page/leaderboard/leaderboard.py index 0d580b4250..9797d47ea8 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.py +++ b/erpnext/utilities/page/leaderboard/leaderboard.py @@ -115,15 +115,15 @@ def get_all_sales_partner(filters, items, field, start=0, limit=20): elif field == "target_amount": select_field = "target_detail.target_amount" elif field == "total_sales_amount": - select_field = "sum(sales_invoice.total_commission)" + select_field = "sum(sales_order.total_commission)" return frappe.db.sql("""select sales_partner.partner_name as name, {0} as value from `tabSales Partner` as sales_partner inner join `tabTarget Detail` as target_detail ON sales_partner.name = target_detail.parent inner join - `tabSales Invoice` as sales_invoice ON sales_invoice.sales_partner = sales_partner.name + `tabSales Order` as sales_order ON sales_order.sales_partner = sales_partner.name where - sales_invoice.docstatus = 1 and sales_invoice.modified >= "{1}" + sales_order.docstatus = 1 and sales_order.modified >= "{1}" group by sales_partner.partner_name order by value DESC From 09d56754ea9ff826379b35ae0626b198e46753e6 Mon Sep 17 00:00:00 2001 From: vishdha Date: Wed, 21 Feb 2018 16:10:09 +0530 Subject: [PATCH 22/27] [fix] Codacy issue --- erpnext/utilities/page/leaderboard/leaderboard.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/utilities/page/leaderboard/leaderboard.js b/erpnext/utilities/page/leaderboard/leaderboard.js index 36dbc629f4..95102240ca 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.js +++ b/erpnext/utilities/page/leaderboard/leaderboard.js @@ -69,7 +69,6 @@ frappe.Leaderboard = Class.extend({ me.options.selected_company = this.value; me.make_request($container); } - }); this.timespan_select = this.page.add_select(__("Timespan"), this.timespans.map(d => { @@ -273,7 +272,7 @@ frappe.Leaderboard = Class.extend({ get_item_html: function (item) { var me = this; - const company = me.options.selected_company + const company = me.options.selected_company; const currency = frappe.get_doc(":Company", company).default_currency; const _selected_filter = me.options.selected_filter .map(i => frappe.model.unscrub(i)); From 0bde9e11c71d3d1d2b40997aef0c5ed0a087b1d4 Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Thu, 22 Feb 2018 10:55:40 +0530 Subject: [PATCH 23/27] clear log if no attachments (#13023) --- erpnext/utilities/doctype/rename_tool/rename_tool.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.js b/erpnext/utilities/doctype/rename_tool/rename_tool.js index 77d2ba3b4a..7823055e52 100644 --- a/erpnext/utilities/doctype/rename_tool/rename_tool.js +++ b/erpnext/utilities/doctype/rename_tool/rename_tool.js @@ -13,6 +13,9 @@ frappe.ui.form.on("Rename Tool", { }, refresh: function(frm) { frm.disable_save(); + if (!frm.doc.file_to_rename) { + frm.get_field("rename_log").$wrapper.html(""); + } frm.page.set_primary_action(__("Rename"), function() { frm.get_field("rename_log").$wrapper.html("

Renaming...

"); frappe.call({ From 23a2b655764f9311ad0ea87087994cca756ab79c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Feb 2018 11:32:09 +0530 Subject: [PATCH 24/27] [Fix] BOM Update Tool not update grandparent's exploded BOM (#13026) --- erpnext/manufacturing/doctype/bom/bom.py | 8 +++++++ .../bom_update_tool/bom_update_tool.py | 21 ++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 2e69475c9a..8cafb9105d 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -200,6 +200,14 @@ class BOM(WebsiteGenerator): if not from_child_bom: frappe.msgprint(_("Cost Updated")) + def update_parent_cost(self): + if self.total_cost: + cost = self.total_cost / self.quantity + + frappe.db.sql("""update `tabBOM Item` set rate=%s, amount=stock_qty*%s + where bom_no = %s and docstatus < 2 and parenttype='BOM'""", + (cost, cost, self.name)) + def get_bom_unitcost(self, bom_no): bom = frappe.db.sql("""select name, base_total_cost/quantity as unit_cost from `tabBOM` where is_active = 1 and name = %s""", bom_no, as_dict=1) diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 3b6c3a15ef..ec948eb5ed 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -13,11 +13,14 @@ class BOMUpdateTool(Document): def replace_bom(self): self.validate_bom() self.update_new_bom() - bom_list = self.get_parent_boms() + bom_list = self.get_parent_boms(self.new_bom) updated_bom = [] for bom in bom_list: bom_obj = frappe.get_doc("BOM", bom) updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) + bom_obj.calculate_cost() + bom_obj.update_parent_cost() + bom_obj.db_update() frappe.msgprint(_("BOM replaced")) @@ -38,10 +41,18 @@ class BOMUpdateTool(Document): rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2 and parenttype='BOM'""", (self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom)) - def get_parent_boms(self): - return [d[0] for d in frappe.db.sql("""select distinct parent - from `tabBOM Item` where ifnull(bom_no, '') = %s and docstatus < 2 and parenttype='BOM'""", - self.new_bom)] + def get_parent_boms(self, bom, bom_list=None): + if not bom_list: + bom_list = [] + + data = frappe.db.sql(""" select distinct parent from `tabBOM Item` + where ifnull(bom_no, '') = %s and docstatus < 2 and parenttype='BOM'""", bom) + + for d in data: + bom_list.append(d[0]) + self.get_parent_boms(d[0], bom_list) + + return bom_list @frappe.whitelist() def enqueue_update_cost(): From 7d862276afb4f3bfd22dd13fddef8db1abfc7d5c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 22 Feb 2018 13:59:41 +0530 Subject: [PATCH 25/27] Cleanup and fixes on leaderboard --- .../utilities/page/leaderboard/leaderboard.js | 46 ++-- .../utilities/page/leaderboard/leaderboard.py | 256 ++++++++---------- 2 files changed, 142 insertions(+), 160 deletions(-) diff --git a/erpnext/utilities/page/leaderboard/leaderboard.js b/erpnext/utilities/page/leaderboard/leaderboard.js index 95102240ca..746ac79488 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.js +++ b/erpnext/utilities/page/leaderboard/leaderboard.js @@ -19,13 +19,13 @@ frappe.Leaderboard = Class.extend({ // const list of doctypes this.doctypes = ["Customer", "Item", "Supplier", "Sales Partner","Sales Person"]; this.timespans = ["Week", "Month", "Quarter", "Year"]; - this.desc_fields = ["total_sales_amount", "total_request","total_purchase_amount","commission_rate"]; this.filters = { - "Customer": ["total_item_purchased_qty", "total_sales_amount","receivable_amount_outstanding_amount"], - "Item": ["total_purchase_amount", "total_purchased_qty", "total_sales_amount", "total_sold_qty","available_stock_qty"], - "Supplier": ["total_item_sold_qty", "total_purchase_amount","payable_amount_outstanding_amount"], - "Sales Partner": ["commission_rate", "target_qty", "target_amount", "total_sales_amount"], - "Sales Person": ["target_qty", "target_amount", "total_sales_amount"], + "Customer": ["total_sales_amount", "total_qty_sold", "outstanding_amount", ], + "Item": ["total_sales_amount", "total_qty_sold", "total_purchase_amount", + "total_qty_purchased", "available_stock_qty", "available_stock_value"], + "Supplier": ["total_purchase_amount", "total_qty_purchased", "outstanding_amount"], + "Sales Partner": ["total_sales_amount", "total_commision"], + "Sales Person": ["total_sales_amount"], }; // for saving current selected filters @@ -65,6 +65,7 @@ frappe.Leaderboard = Class.extend({ fieldtype:'Link', options:'Company', default:frappe.defaults.get_default('company'), + reqd: 1, change: function() { me.options.selected_company = this.value; me.make_request($container); @@ -76,10 +77,6 @@ frappe.Leaderboard = Class.extend({ }) ); - - - // this.timespan_select.val(this.timespans[1]); - this.type_select = this.page.add_select(__("Type"), me.options.selected_filter.map(d => { return {"label": __(frappe.model.unscrub(d)), value: d } @@ -129,9 +126,11 @@ frappe.Leaderboard = Class.extend({ }); }, - get_leaderboard: function (notify, $container, start=0) { + get_leaderboard: function (notify, $container) { var me = this; - + if(!me.options.selected_company) { + frappe.throw(__("Please select Company")); + } frappe.call({ method: "erpnext.utilities.page.leaderboard.leaderboard.get_leaderboard", args: { @@ -139,7 +138,6 @@ frappe.Leaderboard = Class.extend({ timespan: me.options.selected_timespan, company: me.options.selected_company, field: me.options.selected_filter_item, - start: start }, callback: function (r) { let results = r.message || []; @@ -274,25 +272,25 @@ frappe.Leaderboard = Class.extend({ var me = this; const company = me.options.selected_company; const currency = frappe.get_doc(":Company", company).default_currency; - const _selected_filter = me.options.selected_filter - .map(i => frappe.model.unscrub(i)); const fields = ['name','value']; const html = `
${ - fields.map(filter => { - const col = frappe.model.unscrub(filter); - let val = item[filter]; - if (col === "Modified") { - val = comment_when(val); + fields.map(col => { + let val = item[col]; + if(col=="name") { + var formatted_value = ` ${val} ` + } else { + var formatted_value = ` + ${(me.options.selected_filter_item.indexOf('qty') == -1) ? format_currency(val, currency) : val}` } + return ( `
- ${ col === "Name" ? ` ${val} ` : ` ${format_currency(val, currency)}` - } + ${(col == "value") ? "text-right" : ""}"> + ${formatted_value}
`); }).join("") } diff --git a/erpnext/utilities/page/leaderboard/leaderboard.py b/erpnext/utilities/page/leaderboard/leaderboard.py index 9797d47ea8..87cf2a43be 100644 --- a/erpnext/utilities/page/leaderboard/leaderboard.py +++ b/erpnext/utilities/page/leaderboard/leaderboard.py @@ -3,156 +3,141 @@ from __future__ import unicode_literals, print_function import frappe -import json -from operator import itemgetter -from frappe.utils import add_to_date, fmt_money -from erpnext.accounts.party import get_dashboard_info -from erpnext.accounts.utils import get_currency_precision +from frappe.utils import add_to_date @frappe.whitelist() -def get_leaderboard(doctype, timespan, company, field, start=0): +def get_leaderboard(doctype, timespan, company, field): """return top 10 items for that doctype based on conditions""" - filters = get_date_from_string(timespan) - items = [] + from_date = get_from_date(timespan) + records = [] if doctype == "Customer": - items = get_all_customers(filters, company, [], field) + records = get_all_customers(from_date, company, field) elif doctype == "Item": - items = get_all_items(filters, [], field) + records = get_all_items(from_date, company, field) elif doctype == "Supplier": - items = get_all_suppliers(filters, company, [], field) + records = get_all_suppliers(from_date, company, field) elif doctype == "Sales Partner": - items = get_all_sales_partner(filters, [], field) + records = get_all_sales_partner(from_date, company, field) elif doctype == "Sales Person": - items = get_all_sales_person(filters, [], field) + records = get_all_sales_person(from_date, company) - if len(items) > 0: - return items - return [] + return records -def get_all_customers(filters, company, items, field, start=0, limit=20): - """return all customers""" - if field == "total_sales_amount": - select_field = "sum(sales_order_item.base_net_amount)" - elif field == "total_item_purchased_qty": - select_field = "count(sales_order_item.name)" - elif field == "receivable_amount_outstanding_amount": +def get_all_customers(from_date, company, field): + if field == "outstanding_amount": return frappe.db.sql(""" - select sales_invoice.customer as name, sum(sales_invoice.outstanding_amount) as value - FROM `tabSales Invoice` as sales_invoice - where sales_invoice.docstatus = 1 and sales_invoice.modified >= "{0}" and sales_invoice.company = "{1}" - group by sales_invoice.customer - order by value DESC - limit {2}""".format(filters, company, limit), as_dict=1) + select customer as name, sum(outstanding_amount) as value + FROM `tabSales Invoice` + where docstatus = 1 and posting_date >= %s and company = %s + group by customer + order by value DESC + limit 20 + """, (from_date, company), as_dict=1) + else: + if field == "total_sales_amount": + select_field = "sum(so_item.base_net_amount)" + elif field == "total_qty_sold": + select_field = "sum(so_item.stock_qty)" + + 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 + group by so.customer + order by value DESC + limit 20 + """.format(select_field), (from_date, company), as_dict=1) + +def get_all_items(from_date, company, field): + if field in ("available_stock_qty", "available_stock_value"): + return frappe.db.sql(""" + select item_code as name, {0} as value + from tabBin + group by item_code + order by value desc + limit 20 + """.format("sum(actual_qty)" if field=="available_stock_qty" else "sum(stock_value)"), as_dict=1) + else: + if field == "total_sales_amount": + select_field = "sum(order_item.base_net_amount)" + select_doctype = "Sales Order" + elif field == "total_purchase_amount": + select_field = "sum(order_item.base_net_amount)" + select_doctype = "Purchase Order" + elif field == "total_qty_sold": + select_field = "sum(order_item.stock_qty)" + select_doctype = "Sales Order" + elif field == "total_qty_purchased": + select_field = "sum(order_item.stock_qty)" + select_doctype = "Purchase Order" + + 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 + group by order_item.item_code + order by value desc + limit 20 + """.format(select_field, select_doctype), (company, from_date), as_dict=1) + +def get_all_suppliers(from_date, company, field): + if field == "outstanding_amount": + return frappe.db.sql(""" + select supplier as name, sum(outstanding_amount) as value + FROM `tabPurchase Invoice` + where docstatus = 1 and posting_date >= %s and company = %s + group by supplier + order by value DESC + limit 20""", (from_date, company), as_dict=1) + else: + if field == "total_purchase_amount": + select_field = "sum(purchase_order_item.base_net_amount)" + elif field == "total_qty_purchased": + select_field = "sum(purchase_order_item.stock_qty)" + + 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 + and purchase_order.company = %s + group by purchase_order.supplier + order by value DESC + limit 20""".format(select_field), (from_date, company), as_dict=1) + +def get_all_sales_partner(from_date, company, field): + if field == "total_sales_amount": + select_field = "sum(base_net_total)" + elif field == "total_commission": + select_field = "sum(total_commission)" return frappe.db.sql(""" - select sales_order.customer as name, {0} as value - FROM `tabSales Order` as sales_order LEFT JOIN `tabSales Order Item` - as sales_order_item ON sales_order.name = sales_order_item.parent - where sales_order.docstatus = 1 and sales_order.modified >= "{1}" and sales_order.company = "{2}" - group by sales_order.customer - order by value DESC - limit {3}""".format(select_field, filters, company, limit), as_dict=1) - - - - -def get_all_items(filters, items, field, start=0, limit=20): - """return all items""" - if field == "total_sales_amount": - select_field = "sum(B.amount)" - select_doctype = "`tabSales Order Item`" - if field == "total_purchase_amount": - select_field = "sum(B.amount)" - select_doctype = "`tabPurchase Order Item`" - if field == "total_sold_qty": - select_field = "sum(B.qty)" - select_doctype = "`tabSales Order Item`" - if field == "total_purchased_qty": - select_field = "sum(B.qty)" - select_doctype = "`tabPurchase Order Item`" - if field == "available_stock_qty": - select_field = "sum(B.actual_qty)" - select_doctype = "`tabBin`" - return frappe.db.sql("""select - item.name as name , {0} as value - from `tabItem` as item join {1} as B on item.name = B.item_code and item.modified >= "{2}" - group by item.name""".format(select_field, select_doctype, filters), as_dict=1) - -def get_all_suppliers(filters, company, items, field, start=0, limit=20): - """return all suppliers""" - - if field == "total_purchase_amount": - select_field = "sum(purchase_order_item.base_net_amount)" - elif field == "total_item_sold_qty": - select_field = "count(purchase_order_item.name)" - elif field == "payable_amount_outstanding_amount": - return frappe.db.sql(""" - select purchase_invoice.supplier as name, sum(purchase_invoice.outstanding_amount) as value - FROM `tabPurchase Invoice` as purchase_invoice - where purchase_invoice.docstatus = 1 and purchase_invoice.modified >= "{0}" and purchase_invoice.company = "{1}" - group by purchase_invoice.supplier - order by value DESC - limit {2}""".format(filters, company, limit), as_dict=1) + select sales_partner as name, {0} as value + from `tabSales Order` + where ifnull(sales_partner, '') != '' and docstatus = 1 + and transaction_date >= %s and company = %s + group by sales_partner + order by value DESC + limit 20 + """.format(select_field), (from_date, company), as_dict=1) +def get_all_sales_person(from_date, company): 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 >= "{1}" and purchase_order.company = "{2}" - group by purchase_order.supplier - order by value DESC - limit {3}""".format(select_field, filters, company, limit), as_dict=1) - - - -def get_all_sales_partner(filters, items, field, start=0, limit=20): - """return all sales partner""" - - if field == "commission_rate": - select_field = "sales_partner.commission_rate" - elif field == "target_qty": - select_field = "target_detail.target_qty" - elif field == "target_amount": - select_field = "target_detail.target_amount" - elif field == "total_sales_amount": - select_field = "sum(sales_order.total_commission)" - - return frappe.db.sql("""select sales_partner.partner_name as name, {0} as value - from - `tabSales Partner` as sales_partner inner join `tabTarget Detail` as target_detail ON sales_partner.name = target_detail.parent - inner join - `tabSales Order` as sales_order ON sales_order.sales_partner = sales_partner.name - where - sales_order.docstatus = 1 and sales_order.modified >= "{1}" - group by - sales_partner.partner_name + 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 + group by sales_team.sales_person order by value DESC - limit {2}""".format(select_field, filters, limit), as_dict=1) + limit 20 + """, (from_date, company), as_dict=1) - -def get_all_sales_person(filters, items, field, start=0, limit=20): - """return all sales partner""" - if field == "target_qty": - select_field = "target_detail.target_qty" - elif field == "target_amount": - select_field = "target_detail.target_amount" - elif field == "total_sales_amount": - select_field = "sum(sales_team.allocated_amount)" - - return frappe.db.sql("""select sales_person.name as name, {0} as value - from - `tabSales Person` as sales_person - inner join - `tabTarget Detail` as target_detail ON sales_person.name = target_detail.parent - inner join - `tabSales Team` as sales_team ON sales_team.sales_person = sales_person.name - where sales_person.is_group = 0 and sales_team.modified >= "{1}" - group by sales_person.name - order by value DESC - limit {2}""".format(select_field,filters,limit), as_dict=1) - - -def get_date_from_string(seleted_timespan): +def get_from_date(seleted_timespan): """return string for ex:this week as date:string""" days = months = years = 0 if "month" == seleted_timespan.lower(): @@ -164,6 +149,5 @@ def get_date_from_string(seleted_timespan): else: days = -7 - return add_to_date(None, years=years, months=months, days=days, as_string=True, as_datetime=True) - - + return add_to_date(None, years=years, months=months, days=days, + as_string=True, as_datetime=True) \ No newline at end of file From 06bace9089269e7e282e861324fa382ce62d9755 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 22 Feb 2018 14:36:26 +0530 Subject: [PATCH 26/27] Fixed develop version --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 45abbdb0ec..698c373866 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -11,7 +11,7 @@ app_email = "info@erpnext.com" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" -develop_version = '9.x.x-develop' +develop_version = '10.x.x-develop' error_report_email = "support@erpnext.com" From 9b530fb2cbfd5dec98037cea3c5c61f7ea7b5310 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 22 Feb 2018 15:09:37 +0600 Subject: [PATCH 27/27] bumped to version 10.1.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a16794b696..e852de2d7b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.0.23' +__version__ = '10.1.0' def get_default_company(user=None): '''Get default company for user'''