From 114cc810a0605644dd27d051323ecf452d12692e Mon Sep 17 00:00:00 2001 From: deepeshgarg007 Date: Mon, 3 Dec 2018 15:13:06 +0530 Subject: [PATCH 01/35] List view currency bug fix --- erpnext/stock/doctype/delivery_note/delivery_note_list.js | 4 ++-- .../stock/doctype/purchase_receipt/purchase_receipt_list.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index f1ad92914b..2972e41d4f 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -1,6 +1,6 @@ frappe.listview_settings['Delivery Note'] = { - add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", "per_billed", - "transporter_name", "grand_total", "is_return", "status"], + add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", "per_billed", + "transporter_name", "grand_total", "is_return", "status", "currency"], get_indicator: function(doc) { if(cint(doc.is_return)==1) { return [__("Return"), "darkgrey", "is_return,=,Yes"]; diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js index 5c57fb5f01..5033b2d0b8 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -1,6 +1,6 @@ frappe.listview_settings['Purchase Receipt'] = { add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted", - "transporter_name", "is_return", "status", "per_billed"], + "transporter_name", "is_return", "status", "per_billed", "currency"], get_indicator: function(doc) { if(cint(doc.is_return)==1) { return [__("Return"), "darkgrey", "is_return,=,Yes"]; From 636d186d081ff3e6b725a4605dfcd07c6c70cf8a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 3 Dec 2018 15:52:45 +0530 Subject: [PATCH 02/35] [Fix] In project, tax amount is added in the total billed amount(sales) field --- erpnext/projects/doctype/project/project.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 75582cde6f..ec8dcc9e98 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -254,13 +254,13 @@ class Project(Document): self.total_purchase_cost = total_purchase_cost and total_purchase_cost[0][0] or 0 def update_sales_amount(self): - total_sales_amount = frappe.db.sql("""select sum(base_grand_total) + total_sales_amount = frappe.db.sql("""select sum(base_net_total) from `tabSales Order` where project = %s and docstatus=1""", self.name) self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0 def update_billed_amount(self): - total_billed_amount = frappe.db.sql("""select sum(base_grand_total) + total_billed_amount = frappe.db.sql("""select sum(base_net_total) from `tabSales Invoice` where project = %s and docstatus=1""", self.name) self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0 From 40c15348dab7e486e7cb5dce8c203fced1979a3b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 6 Dec 2018 07:37:32 +0530 Subject: [PATCH 03/35] fix(setup-wizard): Validate abbr length before switching to next slide --- erpnext/public/js/setup_wizard.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 484d81decc..75a27594f1 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -97,6 +97,9 @@ erpnext.setup.slides_settings = [ if (!this.values.company_abbr) { return false; } + if (this.values.company_abbr.length > 5) { + return false; + } return true; } }, From 65a9991d8408ba6623a0191fb6f14482e4f9b844 Mon Sep 17 00:00:00 2001 From: Frappe Bot Date: Mon, 10 Dec 2018 12:14:33 +0000 Subject: [PATCH 04/35] bumped to version 10.1.75 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2f63374928..84b56376e4 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.1.74' +__version__ = '10.1.75' def get_default_company(user=None): '''Get default company for user''' From 07e5786e1bcf45d2874e0de03538a14b34793261 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 10 Dec 2018 19:10:18 +0530 Subject: [PATCH 05/35] Fix user permission checks --- erpnext/accounts/party.py | 8 +-- .../leave_application/leave_application.js | 4 +- ...ip_user_permission_check_for_department.py | 64 ++++++++++++++----- erpnext/public/js/utils.js | 2 +- ...rehouse_wise_item_balance_age_and_value.py | 5 +- 5 files changed, 56 insertions(+), 27 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index f19aaf833b..e3235f006f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -72,7 +72,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company= return out -def set_address_details(out, party, party_type, doctype=None, company=None, party_address=None, shipping_address=None): +def set_address_details(out, party, party_type, doctype=None, company=None, party_address=None, shipping_address=None): billing_address_field = "customer_address" if party_type == "Lead" \ else party_type.lower() + "_address" out[billing_address_field] = party_address or get_default_address(party_type, party.name) @@ -151,10 +151,8 @@ def get_default_price_list(party): def set_price_list(out, party, party_type, given_price_list): # price list - price_list = filter(None, get_user_permissions() - .get("Price List", {}) - .get("docs", [])) - price_list = list(price_list) + price_list = [d.get('doc') for d in get_user_permissions().get('Price List', []) \ + if d.get('doc')] if price_list: price_list = price_list[0] diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index a77dd32c49..5bce348489 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -14,7 +14,7 @@ frappe.ui.form.on("Leave Application", { doctype: frm.doc.doctype } }; - }); + }); frm.set_query("employee", erpnext.queries.employee); }, @@ -83,7 +83,7 @@ frappe.ui.form.on("Leave Application", { if (!frm.doc.employee && frappe.defaults.get_user_permissions()) { const perm = frappe.defaults.get_user_permissions(); if (perm && perm['Employee']) { - frm.set_value('employee', perm['Employee']["docs"][0]) + frm.set_value('employee', perm['Employee'].map(perm_doc => perm_doc.doc)[0]); } } }, diff --git a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py index 123eed5aff..46ad6f300b 100644 --- a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py +++ b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py @@ -1,28 +1,60 @@ import frappe +from frappe.desk.form.linked_with import get_linked_doctypes # Skips user permission check for doctypes where department link field was recently added # https://github.com/frappe/erpnext/pull/14121 def execute(): - user_permissions = frappe.get_all("User Permission", - filters=[['allow', '=', 'Department']], - fields=['name', 'skip_for_doctype']) + doctypes_to_skip = [] + for doctype in ['Appraisal', 'Leave Allocation', 'Expense Claim', 'Instructor', 'Salary Slip', + 'Attendance', 'Training Feedback', 'Training Result Employee', + 'Leave Application', 'Employee Advance', 'Activity Cost', 'Training Event Employee', + 'Timesheet', 'Sales Person', 'Payroll Employee Detail']: + if frappe.db.exists('Custom Field', { 'dt': doctype, 'fieldname': 'department'}): continue + doctypes_to_skip.append(doctype) - doctypes_to_skip = [] + frappe.reload_doctype('User Permission') - for doctype in ['Appraisal', 'Leave Allocation', 'Expense Claim', 'Instructor', 'Salary Slip', - 'Attendance', 'Training Feedback', 'Training Result Employee', - 'Leave Application', 'Employee Advance', 'Activity Cost', 'Training Event Employee', - 'Timesheet', 'Sales Person', 'Payroll Employee Detail']: - if frappe.db.exists('Custom Field', { 'dt': doctype, 'fieldname': 'department'}): continue - doctypes_to_skip.append(doctype) + user_permissions = frappe.get_all("User Permission", + filters=[['allow', '=', 'Department'], ['applicable_for', 'in', [None] + doctypes_to_skip]], + fields=['name', 'applicable_for']) - for perm in user_permissions: - skip_for_doctype = perm.get('skip_for_doctype') + user_permissions_to_delete = [] + new_user_permissions_list = [] - skip_for_doctype = skip_for_doctype.split('\n') + doctypes_to_skip - skip_for_doctype = set(skip_for_doctype) # to remove duplicates - skip_for_doctype = '\n'.join(skip_for_doctype) # convert back to string + for user_permission in user_permissions: + if user_permission.applicable_for: + # simply delete user permission record since it needs to be skipped. + user_permissions_to_delete.append(user_permission.name) + else: + # if applicable_for is `None` it means that user permission is applicable for every doctype + # to avoid this we need to create other user permission records and only skip the listed doctypes in this patch + linked_doctypes = get_linked_doctypes(user_permission.allow, True).keys() + applicable_for_doctypes = list(set(linked_doctypes) - set(doctypes_to_skip)) - frappe.set_value('User Permission', perm.name, 'skip_for_doctype', skip_for_doctype) + user_permissions_to_delete.append(user_permission.name) + for doctype in applicable_for_doctypes: + if doctype: + # Maintain sequence (name, user, allow, for_value, applicable_for, apply_to_all_doctypes) + new_user_permissions_list.append(( + frappe.generate_hash("", 10), + user_permission.user, + user_permission.allow, + user_permission.for_value, + doctype, + 0 + )) + + if new_user_permissions_list: + frappe.db.sql(''' + INSERT INTO `tabUser Permission` + (`name`, `user`, `allow`, `for_value`, `applicable_for`, `apply_to_all_doctypes`) + VALUES {}'''.format(', '.join(['%s'] * len(new_user_permissions_list))), + tuple(new_user_permissions_list) + ) + + if user_permissions_to_delete: + frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `name` IN ({})'.format( + ','.join(['%s'] * len(user_permissions_to_delete)) + ), tuple(user_permissions_to_delete)) \ No newline at end of file diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index baee68e6be..3c14cb449b 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -217,7 +217,7 @@ $.extend(erpnext.utils, { let unscrub_option = frappe.model.unscrub(option); let user_permission = frappe.defaults.get_user_permissions(); if(user_permission && user_permission[unscrub_option]) { - return user_permission[unscrub_option]["docs"]; + return user_permission[unscrub_option].map(perm => perm.doc); } else { return $.map(locals[`:${unscrub_option}`], function(c) { return c.name; }).sort(); } diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index 676cf54d8e..7628368051 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -87,9 +87,8 @@ def validate_filters(filters): def get_warehouse_list(filters): from frappe.defaults import get_user_permissions condition = '' - user_permitted_warehouse = filter(None, get_user_permissions() - .get("Warehouse", {}) - .get("docs", [])) + user_permitted_warehouse = [d.get('doc') for d in get_user_permissions().get('Warehouse', []) \ + if d.get('doc')] value = () if user_permitted_warehouse: condition = "and name in %s" From 7ed37ae6c3ba7d556d650a5b0d2540941b2846ac Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 19 Dec 2018 19:56:42 +0530 Subject: [PATCH 06/35] Commonify code - use get_permitted_documents function to avoid code redundancy --- erpnext/accounts/party.py | 5 ++--- .../warehouse_wise_item_balance_age_and_value.py | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index d72a7a6b65..1085cddab5 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _, msgprint, scrub -from frappe.defaults import get_user_permissions +from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values from frappe.utils import (add_days, getdate, formatdate, date_diff, add_years, get_timestamp, nowdate, flt, add_months, get_last_day) @@ -151,8 +151,7 @@ def get_default_price_list(party): def set_price_list(out, party, party_type, given_price_list): # price list - price_list = [d.get('doc') for d in get_user_permissions().get('Price List', []) \ - if d.get('doc')] + price_list = get_permitted_documents('Price List') if price_list: price_list = price_list[0] diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index 7628368051..176a221064 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -85,10 +85,10 @@ def validate_filters(filters): filters["company"] = frappe.defaults.get_user_default("Company") def get_warehouse_list(filters): - from frappe.defaults import get_user_permissions + from frappe.core.doctype.user_permission.user_permission import get_permitted_documents + condition = '' - user_permitted_warehouse = [d.get('doc') for d in get_user_permissions().get('Warehouse', []) \ - if d.get('doc')] + user_permitted_warehouse = get_permitted_documents('Warehouse') value = () if user_permitted_warehouse: condition = "and name in %s" From ba8c0412063a6c930d255b5cb5b993d676c353f8 Mon Sep 17 00:00:00 2001 From: "shreyashah115@gmail.com" Date: Thu, 20 Dec 2018 13:23:51 +0530 Subject: [PATCH 07/35] fix: Check if items exist --- erpnext/buying/doctype/purchase_order/purchase_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index d4f1de1776..e0781f137a 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -85,6 +85,7 @@ class PurchaseOrder(BuyingController): frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and Purchase Orders to this supplier should be issued with caution.").format(self.supplier, standing), title=_("Caution"), indicator='orange') def validate_minimum_order_qty(self): + if not self.get("items"): return items = list(set([d.item_code for d in self.get("items")])) itemwise_min_order_qty = frappe._dict(frappe.db.sql("""select name, min_order_qty From 813485023b927f6069f0bbeadc6f42a6172b0a80 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 20 Dec 2018 14:04:00 +0000 Subject: [PATCH 08/35] Filter cancelled and draft payments --- .../sales_payment_summary.py | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) 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 05c8fb78de..f4c72b4413 100644 --- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py @@ -108,33 +108,33 @@ def get_conditions(filters): def get_pos_invoice_data(filters): conditions = get_conditions(filters) result = frappe.db.sql('' - 'SELECT ' - 'posting_date, owner, sum(net_total) as "net_total", sum(total_taxes) as "total_taxes", ' - 'sum(paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount", ' - 'mode_of_payment, warehouse, cost_center ' - 'FROM (' - 'SELECT ' - 'parent, item_code, sum(amount) as "base_total", warehouse, cost_center ' - 'from `tabSales Invoice Item` group by parent' - ') t1 ' - 'left join ' - '(select parent, mode_of_payment from `tabSales Invoice Payment` group by parent) t3 ' - 'on (t3.parent = t1.parent) ' - 'JOIN (' - 'SELECT ' - 'docstatus, company, is_pos, name, posting_date, owner, sum(base_total) as "base_total", ' - 'sum(net_total) as "net_total", sum(total_taxes_and_charges) as "total_taxes", ' - 'sum(base_paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount" ' - 'FROM `tabSales Invoice` ' - 'GROUP BY name' - ') a ' - 'ON (' - 't1.parent = a.name and t1.base_total = a.base_total) ' - 'WHERE a.docstatus = 1' - ' AND {conditions} ' - 'GROUP BY ' - 'owner, posting_date, warehouse'.format(conditions=conditions), filters, as_dict=1 - ) + 'SELECT ' + 'posting_date, owner, sum(net_total) as "net_total", sum(total_taxes) as "total_taxes", ' + 'sum(paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount", ' + 'mode_of_payment, warehouse, cost_center ' + 'FROM (' + 'SELECT ' + 'parent, item_code, sum(amount) as "base_total", warehouse, cost_center ' + 'from `tabSales Invoice Item` group by parent' + ') t1 ' + 'left join ' + '(select parent, mode_of_payment from `tabSales Invoice Payment` group by parent) t3 ' + 'on (t3.parent = t1.parent) ' + 'JOIN (' + 'SELECT ' + 'docstatus, company, is_pos, name, posting_date, owner, sum(base_total) as "base_total", ' + 'sum(net_total) as "net_total", sum(total_taxes_and_charges) as "total_taxes", ' + 'sum(base_paid_amount) as "paid_amount", sum(outstanding_amount) as "outstanding_amount" ' + 'FROM `tabSales Invoice` ' + 'GROUP BY name' + ') a ' + 'ON (' + 't1.parent = a.name and t1.base_total = a.base_total) ' + 'WHERE a.docstatus = 1' + ' AND {conditions} ' + 'GROUP BY ' + 'owner, posting_date, warehouse'.format(conditions=conditions), filters, as_dict=1 + ) return result @@ -170,6 +170,7 @@ def get_mode_of_payments(filters): from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c where a.name = c.reference_name and b.name = c.parent + and b.docstatus = 1 and a.name in ({invoice_list_names}) union select a.owner, a.posting_date, @@ -211,6 +212,7 @@ def get_mode_of_payment_details(filters): from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c where a.name = c.reference_name and b.name = c.parent + and b.docstatus = 1 and a.name in ({invoice_list_names}) group by a.owner, a.posting_date, mode_of_payment union From ed3561279de263556a7a766c39099f3e16570749 Mon Sep 17 00:00:00 2001 From: Raffael Meyer Date: Mon, 24 Dec 2018 09:49:51 +0100 Subject: [PATCH 09/35] Update README.md (#16224) --- erpnext/selling/README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/README.md b/erpnext/selling/README.md index db05132a9d..d186133d1f 100644 --- a/erpnext/selling/README.md +++ b/erpnext/selling/README.md @@ -1,6 +1,11 @@ -Selling management module. Includes forms for capturing / managing the sales process. +Selling management module. Includes forms for capturing / managing the sales process: + +- Customer +- Campaign +- Quotation +- Sales Order + +Moved to CRM Module: - Lead - Opportunity -- Quotation -- Sales Order \ No newline at end of file From d2b9093ecce124fe165a3a6aa898483e80cc1890 Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Mon, 24 Dec 2018 14:21:07 +0530 Subject: [PATCH 10/35] fix: Check if items exist (#16248) --- erpnext/controllers/buying_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 7ed225fa28..0496ab21f4 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -462,7 +462,7 @@ class BuyingController(StockController): update_last_purchase_rate(self, is_submit = 0) def validate_schedule_date(self): - if not self.schedule_date: + if not self.schedule_date and self.get("items"): self.schedule_date = min([d.schedule_date for d in self.get("items")]) if self.schedule_date: From 91ddadeefac5ab9c31f3efa4e4c86b991d81badf Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Mon, 24 Dec 2018 14:25:12 +0530 Subject: [PATCH 11/35] fix: add currency to options (#16199) --- .../quotation_item/quotation_item.json | 120 +++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index 6b721c004e..d3f128fa6a 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -15,6 +15,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -44,11 +45,13 @@ "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -74,10 +77,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -102,10 +107,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -134,11 +141,13 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -165,10 +174,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -197,11 +208,13 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "300px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -227,10 +240,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -257,10 +272,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -288,10 +305,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -317,10 +336,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -349,11 +370,13 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -383,11 +406,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -412,10 +437,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -443,10 +470,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -473,10 +502,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -503,10 +534,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -532,10 +565,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -565,11 +600,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -599,11 +636,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -630,10 +669,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -662,10 +703,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -693,10 +736,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -725,10 +770,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -754,10 +801,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -787,11 +836,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -820,10 +871,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -852,10 +905,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -880,10 +935,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -915,11 +972,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -936,6 +995,7 @@ "label": "Net Rate", "length": 0, "no_copy": 0, + "options": "currency", "permlevel": 0, "precision": "", "print_hide": 1, @@ -946,10 +1006,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -979,11 +1041,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1011,10 +1075,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1039,10 +1105,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1072,11 +1140,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1103,10 +1173,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1136,11 +1208,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "100px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1168,10 +1242,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1198,10 +1274,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -1228,10 +1306,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1258,10 +1338,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1288,10 +1370,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1317,10 +1401,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1348,10 +1434,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -1379,10 +1467,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1410,10 +1500,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1439,10 +1531,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1469,10 +1563,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1500,10 +1596,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1530,10 +1628,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1559,10 +1659,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1592,11 +1694,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1626,11 +1730,13 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1658,10 +1764,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1686,10 +1794,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -1717,10 +1827,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1750,10 +1862,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -1783,6 +1897,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0, "width": "150px" } @@ -1798,7 +1913,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-06 05:18:38.135668", + "modified": "2018-12-12 05:52:46.135944", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", @@ -1811,5 +1926,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file From c4d38c0afc83c99fbabbbd4fc9a08e62d4851e96 Mon Sep 17 00:00:00 2001 From: Navdeep Ghai <30634335+navdeepghai1@users.noreply.github.com> Date: Mon, 24 Dec 2018 13:23:19 +0400 Subject: [PATCH 12/35] Fix the disappears of image bug after uploading and saving the employee (#16217) * Fix image disappears of image after uploading and saving the employee * Fix the codacy issue --- erpnext/hr/doctype/employee/employee.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 81671c21dc..5d5bdd99ce 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -55,8 +55,8 @@ class Employee(NestedSet): def validate_user_details(self): data = frappe.db.get_value('User', self.user_id, ['enabled', 'user_image'], as_dict=1) - - self.image = data.get("user_image") + if data.get("user_image"): + self.image = data.get("user_image") self.validate_for_enabled_user_id(data.get("enabled", 0)) self.validate_duplicate_user_id() @@ -336,4 +336,4 @@ def get_children(doctype, parent=None, company=None, is_root=False, is_tree=Fals .format(company=company, condition=condition), as_dict=1) # return employee - return employee \ No newline at end of file + return employee From 7ec5e80b707444bc1b8a2664bdfc71ee402d0677 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 24 Dec 2018 14:55:31 +0530 Subject: [PATCH 13/35] [Fix] While making sales invoice from delivery note, system not remove the returned qty (#16141) --- .../doctype/sales_order/sales_order.py | 2 +- .../doctype/delivery_note/delivery_note.py | 20 ++++++++++++++++++- .../delivery_note/test_delivery_note.py | 18 +++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 8b0ecbf370..b2c6ccc7f1 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -552,7 +552,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): def update_item(source, target, source_parent): target.amount = flt(source.amount) - flt(source.billed_amt) target.base_amount = target.amount * flt(source_parent.conversion_rate) - target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty + target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty - source.returned_qty item = frappe.db.get_value("Item", target.item_code, ["item_group", "selling_cost_center"], as_dict=1) target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \ diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index ccc6da4031..5a0d7728c3 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -382,8 +382,24 @@ def get_invoiced_qty_map(delivery_note): return invoiced_qty_map +def get_returned_qty_map(sales_orders): + """returns a map: {so_detail: returned_qty}""" + returned_qty_map = {} + + for name, returned_qty in frappe.get_all('Sales Order Item', fields = ["name", "returned_qty"], + filters = {'parent': ('in', sales_orders), 'docstatus': 1}, as_list=1): + if not returned_qty_map.get(name): + returned_qty_map[name] = 0 + returned_qty_map[name] += returned_qty + + return returned_qty_map + @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None): + doc = frappe.get_doc('Delivery Note', source_name) + sales_orders = [d.against_sales_order for d in doc.items] + returned_qty_map = get_returned_qty_map(sales_orders) + invoiced_qty_map = get_invoiced_qty_map(source_name) def set_missing_values(source, target): @@ -403,7 +419,9 @@ def make_sales_invoice(source_name, target_doc=None): target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address)) def update_item(source_doc, target_doc, source_parent): - target_doc.qty = source_doc.qty - invoiced_qty_map.get(source_doc.name, 0) + target_doc.qty = (source_doc.qty - + invoiced_qty_map.get(source_doc.name, 0) - returned_qty_map.get(source_doc.so_detail, 0)) + if source_doc.serial_no and source_parent.per_billed > 0: target_doc.serial_no = get_delivery_note_serial_no(source_doc.item_code, target_doc.qty, source_parent.name) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index c3dbb8d4e7..0771d79848 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -564,6 +564,24 @@ class TestDeliveryNote(unittest.TestCase): self.assertEqual(dn.per_billed, 100) self.assertEqual(dn.status, "Completed") + def test_make_sales_invoice_from_dn_for_returned_qty(self): + from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note + from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice + + so = make_sales_order(qty=2) + so.submit() + + dn = make_delivery_note(so.name) + dn.submit() + + dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-1, do_not_submit=True) + dn1.items[0].against_sales_order = so.name + dn1.items[0].so_detail = so.items[0].name + dn1.submit() + + si = make_sales_invoice(dn.name) + self.assertEquals(si.items[0].qty, 1) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") args = frappe._dict(args) From 94500a9d62b0bbec85c537f1d3d4b2a47391bf0a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 19 Nov 2018 13:31:06 +0530 Subject: [PATCH 14/35] [Fix] TDS is applying on the tax --- .../purchase_invoice/purchase_invoice.py | 10 + .../tax_withholding_category.py | 79 +++++--- .../test_tax_withholding_category.py | 190 +++++++++++++----- 3 files changed, 201 insertions(+), 78 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 1bb7c97b0d..bfdf451f44 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -830,6 +830,10 @@ class PurchaseInvoice(BuyingController): return tax_withholding_details = get_party_tax_withholding_details(self) + + if not tax_withholding_details: + return + accounts = [] for d in self.taxes: if d.account_head == tax_withholding_details.get("account_head"): @@ -839,6 +843,12 @@ class PurchaseInvoice(BuyingController): if not accounts or tax_withholding_details.get("account_head") not in accounts: self.append("taxes", tax_withholding_details) + to_remove = [d for d in self.taxes + if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")] + + for d in to_remove: + self.remove(d) + # calculate totals again after applying TDS self.calculate_taxes_and_totals() diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index f553cc09ec..6c31e9efed 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -24,6 +24,7 @@ def get_party_tax_withholding_details(ref_doc): .format(tax_withholding_category, ref_doc.company)) tds_amount = get_tds_amount(ref_doc, tax_details, fy) tax_row = get_tax_row(tax_details, tds_amount) + return tax_row def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): @@ -62,46 +63,64 @@ def get_tax_row(tax_details, tds_amount): def get_tds_amount(ref_doc, tax_details, fiscal_year_details): fiscal_year, year_start_date, year_end_date = fiscal_year_details tds_amount = 0 + tds_deducted = 0 - def _get_tds(): - tds_amount = 0 - if not tax_details.threshold or ref_doc.net_total >= tax_details.threshold: - tds_amount = ref_doc.net_total * tax_details.rate / 100 - return tds_amount + def _get_tds(amount): + if amount <= 0: + return 0 - if tax_details.cumulative_threshold: - entries = frappe.db.sql(""" + return amount * tax_details.rate / 100 + + entries = frappe.db.sql(""" select voucher_no, credit from `tabGL Entry` where party=%s and fiscal_year=%s and credit > 0 """, (ref_doc.supplier, fiscal_year), as_dict=1) - supplier_credit_amount = flt(sum([d.credit for d in entries])) + vouchers = [d.voucher_no for d in entries] + advance_vouchers = get_advance_vouchers(ref_doc.supplier, fiscal_year) - vouchers = [d.voucher_no for d in entries] - vouchers += get_advance_vouchers(ref_doc.supplier, fiscal_year) + tds_vouchers = vouchers + advance_vouchers - tds_deducted = 0 - if vouchers: - tds_deducted = flt(frappe.db.sql(""" - select sum(credit) - from `tabGL Entry` - where account=%s and fiscal_year=%s and credit > 0 - and voucher_no in ({0}) - """.format(', '.join(["'%s'" % d for d in vouchers])), - (tax_details.account_head, fiscal_year))[0][0]) + if tds_vouchers: + tds_deducted = frappe.db.sql(""" + SELECT sum(credit) FROM `tabGL Entry` + WHERE + account=%s and fiscal_year=%s and credit > 0 + and voucher_no in ({0})""". format(','.join(['%s'] * len(tds_vouchers))), + ((tax_details.account_head, fiscal_year) + tuple(tds_vouchers))) + + tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0 + + if tds_deducted: + tds_amount = _get_tds(ref_doc.net_total) + else: + supplier_credit_amount = frappe.get_all('Purchase Invoice Item', + fields = ['sum(net_amount)'], + filters = {'parent': ('in', vouchers), 'docstatus': 1}, as_list=1) + + supplier_credit_amount = (supplier_credit_amount[0][0] + if supplier_credit_amount and supplier_credit_amount[0][0] else 0) + + jv_supplier_credit_amt = frappe.get_all('Journal Entry Account', + fields = ['sum(credit_in_account_currency)'], + filters = { + 'parent': ('in', vouchers), 'docstatus': 1, + 'party': ref_doc.supplier, + 'reference_type': ('not in', ['Purchase Invoice']) + }, as_list=1) + + supplier_credit_amount += (jv_supplier_credit_amt[0][0] + if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0) + + supplier_credit_amount += ref_doc.net_total debit_note_amount = get_debit_note_amount(ref_doc.supplier, year_start_date, year_end_date) + supplier_credit_amount -= debit_note_amount - total_invoiced_amount = supplier_credit_amount + tds_deducted \ - + flt(ref_doc.net_total) - debit_note_amount - if total_invoiced_amount >= tax_details.cumulative_threshold: - total_applicable_tds = total_invoiced_amount * tax_details.rate / 100 - tds_amount = min(total_applicable_tds - tds_deducted, ref_doc.net_total) - else: - tds_amount = _get_tds() - else: - tds_amount = _get_tds() + if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold) + or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)): + tds_amount = _get_tds(supplier_credit_amount) return tds_amount @@ -114,7 +133,7 @@ def get_advance_vouchers(supplier, fiscal_year=None, company=None, from_date=Non select distinct voucher_no from `tabGL Entry` where party=%s and %s and debit > 0 - """, (supplier, condition)) + """, (supplier, condition)) or [] def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None): condition = "" @@ -126,4 +145,4 @@ def get_debit_note_amount(supplier, year_start_date, year_end_date, company=None from `tabPurchase Invoice` where supplier=%s %s and is_return=1 and docstatus=1 and posting_date between %s and %s - """, (supplier, condition, year_start_date, year_end_date))) + """, (supplier, condition, year_start_date, year_end_date))) \ No newline at end of file diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 20e1746e36..2530196708 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import today +from erpnext.accounts.utils import get_fiscal_year test_dependencies = ["Supplier Group"] @@ -14,65 +15,105 @@ class TestTaxWithholdingCategory(unittest.TestCase): def setUpClass(self): # create relevant supplier, etc create_records() + create_tax_with_holding_category() - def test_single_threshold_tds(self): - frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194D - Individual") - pi = create_purchase_invoice() + def test_cumulative_threshold_tds(self): + frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS") + invoices = [] + + # create invoices for lower than single threshold tax rate + for _ in xrange(2): + pi = create_purchase_invoice(supplier = "Test TDS Supplier") + pi.submit() + invoices.append(pi) + + # create another invoice whose total when added to previously created invoice, + # surpasses cumulative threshhold + pi = create_purchase_invoice(supplier = "Test TDS Supplier") pi.submit() - self.assertEqual(pi.taxes_and_charges_deducted, 800) - self.assertEqual(pi.grand_total, 15200) + # assert equal tax deduction on total invoice amount uptil now + self.assertEqual(pi.taxes_and_charges_deducted, 3000) + self.assertEqual(pi.grand_total, 7000) + invoices.append(pi) + + # TDS is already deducted, so from onward system will deduct the TDS on every invoice + pi = create_purchase_invoice(supplier = "Test TDS Supplier", rate=5000) + pi.submit() + + # assert equal tax deduction on total invoice amount uptil now + self.assertEqual(pi.taxes_and_charges_deducted, 500) + invoices.append(pi) + + #delete invoices to avoid clashing + for d in invoices: + d.cancel() + frappe.delete_doc("Purchase Invoice", d.name) + + def test_single_threshold_tds(self): + invoices = [] + frappe.db.set_value("Supplier", "Test TDS Supplier1", "tax_withholding_category", "Single Threshold TDS") + pi = create_purchase_invoice(supplier = "Test TDS Supplier1", rate = 20000) + pi.submit() + invoices.append(pi) + + self.assertEqual(pi.taxes_and_charges_deducted, 2000) + self.assertEqual(pi.grand_total, 18000) # check gl entry for the purchase invoice gl_entries = frappe.db.get_all('GL Entry', filters={'voucher_no': pi.name}, fields=["*"]) self.assertEqual(len(gl_entries), 3) for d in gl_entries: if d.account == pi.credit_to: - self.assertEqual(d.credit, 15200) + self.assertEqual(d.credit, 18000) elif d.account == pi.items[0].get("expense_account"): - self.assertEqual(d.debit, 16000) + self.assertEqual(d.debit, 20000) elif d.account == pi.taxes[0].get("account_head"): - self.assertEqual(d.credit, 800) + self.assertEqual(d.credit, 2000) else: raise ValueError("Account head does not match.") - # delete purchase invoice to avoid it interefering in other tests - pi.cancel() - frappe.delete_doc('Purchase Invoice', pi.name) - - def test_cumulative_threshold_tds(self): - frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "TDS - 194C - Individual") - invoices = [] - - # create invoices for lower than single threshold tax rate - for _ in xrange(6): - pi = create_purchase_invoice() - pi.submit() - invoices.append(pi) - - # create another invoice whose total when added to previously created invoice, - # surpasses cumulative threshhold - pi = create_purchase_invoice() + pi = create_purchase_invoice(supplier = "Test TDS Supplier1") pi.submit() - - # assert equal tax deduction on total invoice amount uptil now - self.assertEqual(pi.taxes_and_charges_deducted, 1120) - self.assertEqual(pi.grand_total, 14880) invoices.append(pi) + # TDS amount is 1000 because in previous invoices it's already deducted + self.assertEqual(pi.taxes_and_charges_deducted, 1000) + # delete invoices to avoid clashing for d in invoices: d.cancel() frappe.delete_doc("Purchase Invoice", d.name) -def create_purchase_invoice(qty=1): + def test_single_threshold_tds_with_previous_vouchers(self): + invoices = [] + frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS") + pi = create_purchase_invoice(supplier="Test TDS Supplier2") + pi.submit() + invoices.append(pi) + + pi = create_purchase_invoice(supplier="Test TDS Supplier2") + pi.submit() + invoices.append(pi) + + self.assertEqual(pi.taxes_and_charges_deducted, 2000) + self.assertEqual(pi.grand_total, 8000) + + # delete invoices to avoid clashing + for d in invoices: + d.cancel() + frappe.delete_doc("Purchase Invoice", d.name) + +def create_purchase_invoice(**args): # return sales invoice doc object item = frappe.get_doc('Item', {'item_name': 'TDS Item'}) + + args = frappe._dict(args) pi = frappe.get_doc({ "doctype": "Purchase Invoice", "posting_date": today(), "apply_tds": 1, - "supplier": frappe.get_doc('Supplier', {"supplier_name": "Test TDS Supplier"}).name, + "supplier": args.supplier, "company": '_Test Company', "taxes_and_charges": "", "currency": "INR", @@ -81,8 +122,8 @@ def create_purchase_invoice(qty=1): "items": [{ 'doctype': 'Purchase Invoice Item', 'item_code': item.name, - 'qty': qty, - 'rate': 16000, + 'qty': args.qty or 1, + 'rate': args.rate or 10000, 'cost_center': 'Main - _TC', 'expense_account': 'Stock Received But Not Billed - _TC' }] @@ -92,20 +133,73 @@ def create_purchase_invoice(qty=1): return pi def create_records(): - # create a new supplier - frappe.get_doc({ - "supplier_group": "_Test Supplier Group", - "supplier_name": "Test TDS Supplier", - "doctype": "Supplier", - "tax_withholding_category": "TDS - 194D - Individual" - }).insert() + # create a new suppliers + for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']: + if frappe.db.exists('Supplier', name): + continue + + frappe.get_doc({ + "supplier_group": "_Test Supplier Group", + "supplier_name": name, + "doctype": "Supplier", + }).insert() # create an item - frappe.get_doc({ - "doctype": "Item", - "item_code": "TDS Item", - "item_name": "TDS Item", - "item_group": "All Item Groups", - "company": "_Test Company", - "is_stock_item": 0, - }).insert() \ No newline at end of file + if not frappe.db.exists('Item', "TDS Item"): + frappe.get_doc({ + "doctype": "Item", + "item_code": "TDS Item", + "item_name": "TDS Item", + "item_group": "All Item Groups", + "is_stock_item": 0, + }).insert() + + # create an account + if not frappe.db.exists("Account", "TDS - _TC"): + frappe.get_doc({ + 'doctype': 'Account', + 'company': '_Test Company', + 'account_name': 'TDS', + 'parent_account': 'Tax Assets - _TC', + 'report_type': 'Balance Sheet', + 'root_type': 'Asset' + }).insert() + +def create_tax_with_holding_category(): + fiscal_year = get_fiscal_year(today(), company="_Test Company")[0] + + # Cummulative thresold + if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TDS"): + frappe.get_doc({ + "doctype": "Tax Withholding Category", + "name": "Cumulative Threshold TDS", + "category_name": "10% TDS", + "rates": [{ + 'fiscal_year': fiscal_year, + 'tax_withholding_rate': 10, + 'single_threshold': 0, + 'cumulative_threshold': 30000.00 + }], + "accounts": [{ + 'company': '_Test Company', + 'account': 'TDS - _TC' + }] + }).insert() + + # Single thresold + if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"): + frappe.get_doc({ + "doctype": "Tax Withholding Category", + "name": "Single Threshold TDS", + "category_name": "10% TDS", + "rates": [{ + 'fiscal_year': fiscal_year, + 'tax_withholding_rate': 10, + 'single_threshold': 20000.00, + 'cumulative_threshold': 0 + }], + "accounts": [{ + 'company': '_Test Company', + 'account': 'TDS - _TC' + }] + }).insert() \ No newline at end of file From deb96dab3e89683d1460aa08799b3291705571fe Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 25 Dec 2018 13:58:20 +0530 Subject: [PATCH 15/35] [Fix] Salary structure not saving (#16275) --- erpnext/hr/doctype/salary_structure/salary_structure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 7ead14030f..202ae9bcfe 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, cint +from frappe.utils import flt, cint, cstr from frappe import _ from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document @@ -22,7 +22,7 @@ class SalaryStructure(Document): overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"] for table in ["earnings", "deductions"]: for d in self.get(table): - component_default_value = frappe.db.get_value("Salary Component", str(d.salary_component), + component_default_value = frappe.db.get_value("Salary Component", cstr(d.salary_component), overwritten_fields + overwritten_fields_if_missing, as_dict=1) if component_default_value: for fieldname in overwritten_fields: From d3530125dd73a0a782758fbfedf73690e3f728f5 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 Dec 2018 14:30:50 +0530 Subject: [PATCH 16/35] Modify bench_init.sh - to point frappe branch in my repo with changes to check if any test fails due to the changes --- travis/bench_init.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/travis/bench_init.sh b/travis/bench_init.sh index f96269b919..38ca28b7c6 100755 --- a/travis/bench_init.sh +++ b/travis/bench_init.sh @@ -1,8 +1,8 @@ #!/bin/bash cd ~/ -curl -I https://github.com/frappe/frappe/tree/$TRAVIS_BRANCH | head -n 1 | cut -d $' ' -f2 | ( +curl -I https://github.com/surajshetty3416/frappe/tree/$TRAVIS_BRANCH | head -n 1 | cut -d $' ' -f2 | ( read response; [ $response == '200' ] && branch=$TRAVIS_BRANCH || branch='develop'; - bench init frappe-bench --frappe-path https://github.com/frappe/frappe.git --frappe-branch $branch --python $(which python) + bench init frappe-bench --frappe-path https://github.com/surajshetty3416/frappe.git --frappe-branch $branch --python $(which python) ) From 488aa86f70c2bc4207e0acc31fdea193f47d5ee4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 25 Dec 2018 14:46:44 +0530 Subject: [PATCH 17/35] Skip some codacy warnings --- .../v11_0/skip_user_permission_check_for_department.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py index 46ad6f300b..7f7cfc1327 100644 --- a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py +++ b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py @@ -50,11 +50,11 @@ def execute(): frappe.db.sql(''' INSERT INTO `tabUser Permission` (`name`, `user`, `allow`, `for_value`, `applicable_for`, `apply_to_all_doctypes`) - VALUES {}'''.format(', '.join(['%s'] * len(new_user_permissions_list))), + VALUES {}'''.format(', '.join(['%s'] * len(new_user_permissions_list))), # nosec tuple(new_user_permissions_list) ) if user_permissions_to_delete: - frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `name` IN ({})'.format( + frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `name` IN ({})'.format( # nosec ','.join(['%s'] * len(user_permissions_to_delete)) ), tuple(user_permissions_to_delete)) \ No newline at end of file From 33ba694f3a9a8c0f628faafe75276425c1dc7c7e Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 26 Dec 2018 11:32:06 +0530 Subject: [PATCH 18/35] Revert "Modify bench_init.sh" Point back to original repo This reverts commit d3530125dd73a0a782758fbfedf73690e3f728f5. --- travis/bench_init.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/travis/bench_init.sh b/travis/bench_init.sh index 38ca28b7c6..f96269b919 100755 --- a/travis/bench_init.sh +++ b/travis/bench_init.sh @@ -1,8 +1,8 @@ #!/bin/bash cd ~/ -curl -I https://github.com/surajshetty3416/frappe/tree/$TRAVIS_BRANCH | head -n 1 | cut -d $' ' -f2 | ( +curl -I https://github.com/frappe/frappe/tree/$TRAVIS_BRANCH | head -n 1 | cut -d $' ' -f2 | ( read response; [ $response == '200' ] && branch=$TRAVIS_BRANCH || branch='develop'; - bench init frappe-bench --frappe-path https://github.com/surajshetty3416/frappe.git --frappe-branch $branch --python $(which python) + bench init frappe-bench --frappe-path https://github.com/frappe/frappe.git --frappe-branch $branch --python $(which python) ) From ed94317df99493c24c58a1e1aa553a8f822e793f Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 26 Dec 2018 08:36:32 +0000 Subject: [PATCH 19/35] Test cases --- .../test_sales_payment_summary.py | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py new file mode 100644 index 0000000000..58c77849b8 --- /dev/null +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -0,0 +1,117 @@ +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import unittest +import frappe +import erpnext +from erpnext.accounts.report.sales_payment_summary.sales_payment_summary import get_mode_of_payments, get_mode_of_payment_details +from frappe.utils import nowdate +from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry + +test_dependencies = ["Sales Invoice"] + +class TestSalesPaymentSummary(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def test_get_mode_of_payments(self): + si = frappe.get_all("Sales Invoice", fields=["name", "docstatus"]) + filters = get_filters() + + for invoice in si[:2]: + doc = frappe.get_doc("Sales Invoice", invoice.name) + new_doc = frappe.copy_doc(doc) + new_doc.insert() + new_doc.submit() + try: + new_doc.submit() + except Exception as e: + pass + + if int(new_doc.name[-3:])%2 == 0: + bank_account = "_Test Cash - _TC" + mode_of_payment = "Cash" + else: + bank_account = "_Test Bank - _TC" + mode_of_payment = "Credit Card" + + pe = get_payment_entry("Sales Invoice", new_doc.name, bank_account=bank_account) + pe.reference_no = "_Test" + pe.reference_date = nowdate() + pe.mode_of_payment = mode_of_payment + pe.insert() + pe.submit() + + mop = get_mode_of_payments(filters) + self.assertTrue('Credit Card' in mop.values()[0]) + self.assertTrue('Cash' in mop.values()[0]) + + # Cancel all Cash payment entry and check if this mode of payment is still fetched. + payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Cash", "docstatus": 1}, fields=["name", "docstatus"]) + for payment_entry in payment_entries: + pe = frappe.get_doc("Payment Entry", payment_entry.name) + pe.cancel() + + mop = get_mode_of_payments(filters) + self.assertTrue('Credit Card' in mop.values()[0]) + self.assertTrue('Cash' not in mop.values()[0]) + + def test_get_mode_of_payments_details(self): + si = frappe.get_all("Sales Invoice", fields=["name", "docstatus"]) + filters = get_filters() + + for invoice in si[:2]: + doc = frappe.get_doc("Sales Invoice", invoice.name) + new_doc = frappe.copy_doc(doc) + new_doc.insert() + new_doc.submit() + try: + new_doc.submit() + except Exception as e: + pass + + if int(new_doc.name[-3:])%2 == 0: + bank_account = "_Test Cash - _TC" + mode_of_payment = "Cash" + else: + bank_account = "_Test Bank - _TC" + mode_of_payment = "Credit Card" + + pe = get_payment_entry("Sales Invoice", new_doc.name, bank_account=bank_account) + pe.reference_no = "_Test" + pe.reference_date = nowdate() + pe.mode_of_payment = mode_of_payment + pe.insert() + pe.submit() + + mopd = get_mode_of_payment_details(filters) + + mopd_values = mopd.values()[0] + for mopd_value in mopd_values: + if mopd_value[0] == "Credit Card": + cc_init_amount = mopd_value[1] + + # Cancel one Credit Card Payment Entry and check that it is not fetched in mode of payment details. + payment_entries = frappe.get_all("Payment Entry", filters={"mode_of_payment": "Credit Card", "docstatus": 1}, fields=["name", "docstatus"]) + for payment_entry in payment_entries[:1]: + pe = frappe.get_doc("Payment Entry", payment_entry.name) + pe.cancel() + + mopd = get_mode_of_payment_details(filters) + mopd_values = mopd.values()[0] + for mopd_value in mopd_values: + if mopd_value[0] == "Credit Card": + cc_final_amount = mopd_value[1] + + self.assertTrue(cc_init_amount > cc_final_amount) + +def get_filters(): + return { + "from_date": "1900-01-01", + "to_date": nowdate(), + "company": "_Test Company" + } \ No newline at end of file From 9d31452c25a43a4fd21ff97490a536ba0e5031f6 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 26 Dec 2018 08:38:42 +0000 Subject: [PATCH 20/35] Remove ununsed variable --- .../sales_payment_summary/test_sales_payment_summary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index 58c77849b8..d9d05135a6 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -29,7 +29,7 @@ class TestSalesPaymentSummary(unittest.TestCase): new_doc.submit() try: new_doc.submit() - except Exception as e: + except Exception: pass if int(new_doc.name[-3:])%2 == 0: @@ -71,7 +71,7 @@ class TestSalesPaymentSummary(unittest.TestCase): new_doc.submit() try: new_doc.submit() - except Exception as e: + except Exception: pass if int(new_doc.name[-3:])%2 == 0: From 899b9b1ea769045323bbe9ae61f2bc32a1bb17e7 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 26 Dec 2018 08:56:11 +0000 Subject: [PATCH 21/35] Codacy correction --- .../sales_payment_summary/test_sales_payment_summary.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index d9d05135a6..9b9711f5df 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import unittest import frappe -import erpnext from erpnext.accounts.report.sales_payment_summary.sales_payment_summary import get_mode_of_payments, get_mode_of_payment_details from frappe.utils import nowdate from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry @@ -29,8 +28,8 @@ class TestSalesPaymentSummary(unittest.TestCase): new_doc.submit() try: new_doc.submit() - except Exception: - pass + except Exception as e: + print(e) if int(new_doc.name[-3:])%2 == 0: bank_account = "_Test Cash - _TC" @@ -71,8 +70,8 @@ class TestSalesPaymentSummary(unittest.TestCase): new_doc.submit() try: new_doc.submit() - except Exception: - pass + except Exception as e: + print(e) if int(new_doc.name[-3:])%2 == 0: bank_account = "_Test Cash - _TC" From 9c1db688d1d8a2cb9f3c8232c3ca25981aa574dc Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 26 Dec 2018 10:33:42 +0000 Subject: [PATCH 22/35] Ignore pricing rule for Travis --- .../report/sales_payment_summary/test_sales_payment_summary.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index 9b9711f5df..8cabf6e6cb 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -24,6 +24,7 @@ class TestSalesPaymentSummary(unittest.TestCase): for invoice in si[:2]: doc = frappe.get_doc("Sales Invoice", invoice.name) new_doc = frappe.copy_doc(doc) + new_doc.ignore_pricing_rule = 1 new_doc.insert() new_doc.submit() try: From 8406720a7ad6c38b067f5cd685e3f61570ee03e6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 26 Dec 2018 12:28:37 +0530 Subject: [PATCH 23/35] [Fix] Patch rename_bom_wo_fields --- .../manufacturing/doctype/bom_item/bom_item.json | 2 +- erpnext/patches/v11_0/rename_bom_wo_fields.py | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index b520360304..8a380f755b 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -1121,7 +1121,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-11-22 15:04:55.187136", + "modified": "2018-12-26 15:04:56.187136", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", diff --git a/erpnext/patches/v11_0/rename_bom_wo_fields.py b/erpnext/patches/v11_0/rename_bom_wo_fields.py index 43fbea5ea7..c8106a6bd5 100644 --- a/erpnext/patches/v11_0/rename_bom_wo_fields.py +++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py @@ -8,7 +8,19 @@ from frappe.model.utils.rename_field import rename_field def execute(): for doctype in ['BOM Explosion Item', 'BOM Item', 'Work Order Item', 'Item']: if frappe.db.has_column(doctype, 'allow_transfer_for_manufacture'): - rename_field('BOM Item', "allow_transfer_for_manufacture", "include_item_in_manufacturing") + if doctype != 'Item': + frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype)) + else: + frappe.reload_doc('stock', 'doctype', frappe.scrub(doctype)) + + rename_field(doctype, "allow_transfer_for_manufacture", "include_item_in_manufacturing") + + if frappe.db.has_column('BOM', 'allow_same_item_multiple_times'): + frappe.db.sql(""" UPDATE tabBOM + SET + allow_same_item_multiple_times = 0 + WHERE + trim(coalesce(allow_same_item_multiple_times, '')) = '' """) for doctype in ['BOM', 'Work Order']: frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype)) From 9a9c9c2ba39b2fca2a803cb198fb2bb49e55464d Mon Sep 17 00:00:00 2001 From: Frappe Bot Date: Wed, 26 Dec 2018 11:09:34 +0000 Subject: [PATCH 24/35] bumped to version 10.1.76 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 84b56376e4..b9d86a8e84 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.1.75' +__version__ = '10.1.76' def get_default_company(user=None): '''Get default company for user''' From 23beec2bd1e00af3a015fbb86015fcd5335bb58c Mon Sep 17 00:00:00 2001 From: Frappe Bot Date: Wed, 26 Dec 2018 12:04:55 +0000 Subject: [PATCH 25/35] bumped to version 11.0.3-beta.32 --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 53ec3d6e09..14e4f68864 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" develop_version = '12.x.x-develop' -staging_version = '11.0.3-beta.31' +staging_version = '11.0.3-beta.32' error_report_email = "support@erpnext.com" From d7777696d78a92f20e4fdf7b0071adec212ffd22 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 26 Dec 2018 14:08:00 +0000 Subject: [PATCH 26/35] Corrections for Travis --- .../sales_payment_summary/test_sales_payment_summary.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index 8cabf6e6cb..096935f518 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -18,10 +18,10 @@ class TestSalesPaymentSummary(unittest.TestCase): pass def test_get_mode_of_payments(self): - si = frappe.get_all("Sales Invoice", fields=["name", "docstatus"]) + si = frappe.get_all("Sales Invoice", filters={"company": "_Test Company", "customer": "_Test Customer"}, fields=["name", "docstatus"]) filters = get_filters() - for invoice in si[:2]: + for invoice in si[:-2]: doc = frappe.get_doc("Sales Invoice", invoice.name) new_doc = frappe.copy_doc(doc) new_doc.ignore_pricing_rule = 1 @@ -61,10 +61,10 @@ class TestSalesPaymentSummary(unittest.TestCase): self.assertTrue('Cash' not in mop.values()[0]) def test_get_mode_of_payments_details(self): - si = frappe.get_all("Sales Invoice", fields=["name", "docstatus"]) + si = frappe.get_all("Sales Invoice", filters={"company": "_Test Company", "customer": "_Test Customer"}, fields=["name", "docstatus"]) filters = get_filters() - for invoice in si[:2]: + for invoice in si[:-2]: doc = frappe.get_doc("Sales Invoice", invoice.name) new_doc = frappe.copy_doc(doc) new_doc.insert() From 02aa9fb240f0944ddc2c705bf11bfd5da84ea176 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 27 Dec 2018 08:46:29 +0000 Subject: [PATCH 27/35] Understand Travis --- .../report/sales_payment_summary/test_sales_payment_summary.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index 096935f518..a9ecaaf546 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -19,10 +19,12 @@ class TestSalesPaymentSummary(unittest.TestCase): def test_get_mode_of_payments(self): si = frappe.get_all("Sales Invoice", filters={"company": "_Test Company", "customer": "_Test Customer"}, fields=["name", "docstatus"]) + print(si) filters = get_filters() for invoice in si[:-2]: doc = frappe.get_doc("Sales Invoice", invoice.name) + print(doc.__dict__) new_doc = frappe.copy_doc(doc) new_doc.ignore_pricing_rule = 1 new_doc.insert() From 0f9c47c242a494e0dd6d1915a190ef9d029fb598 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 27 Dec 2018 10:10:18 +0000 Subject: [PATCH 28/35] Remove pricing rule from items --- .../sales_payment_summary/test_sales_payment_summary.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index a9ecaaf546..70ba5a362a 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -19,14 +19,14 @@ class TestSalesPaymentSummary(unittest.TestCase): def test_get_mode_of_payments(self): si = frappe.get_all("Sales Invoice", filters={"company": "_Test Company", "customer": "_Test Customer"}, fields=["name", "docstatus"]) - print(si) filters = get_filters() for invoice in si[:-2]: doc = frappe.get_doc("Sales Invoice", invoice.name) - print(doc.__dict__) new_doc = frappe.copy_doc(doc) new_doc.ignore_pricing_rule = 1 + for item in new_doc.items: + item.pricing_rule = "" new_doc.insert() new_doc.submit() try: @@ -69,6 +69,9 @@ class TestSalesPaymentSummary(unittest.TestCase): for invoice in si[:-2]: doc = frappe.get_doc("Sales Invoice", invoice.name) new_doc = frappe.copy_doc(doc) + new_doc.ignore_pricing_rule = 1 + for item in new_doc.items: + item.pricing_rule = "" new_doc.insert() new_doc.submit() try: From 641d3e0073c7974309c48cc199cd35a023f5f131 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 27 Dec 2018 13:43:56 +0000 Subject: [PATCH 29/35] Add own records --- .../test_sales_payment_summary.py | 114 ++++++++++++------ 1 file changed, 74 insertions(+), 40 deletions(-) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index 70ba5a362a..aeb618f122 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -5,45 +5,34 @@ from __future__ import unicode_literals import unittest import frappe from erpnext.accounts.report.sales_payment_summary.sales_payment_summary import get_mode_of_payments, get_mode_of_payment_details -from frappe.utils import nowdate +from frappe.utils import today from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry test_dependencies = ["Sales Invoice"] class TestSalesPaymentSummary(unittest.TestCase): - def setUp(self): - pass - - def tearDown(self): - pass + @classmethod + def setUpClass(self): + create_records() def test_get_mode_of_payments(self): - si = frappe.get_all("Sales Invoice", filters={"company": "_Test Company", "customer": "_Test Customer"}, fields=["name", "docstatus"]) filters = get_filters() - for invoice in si[:-2]: - doc = frappe.get_doc("Sales Invoice", invoice.name) - new_doc = frappe.copy_doc(doc) - new_doc.ignore_pricing_rule = 1 - for item in new_doc.items: - item.pricing_rule = "" - new_doc.insert() - new_doc.submit() - try: - new_doc.submit() - except Exception as e: - print(e) + for dummy in range(2): + si = create_sales_invoice_record() + si.insert() + si.submit() - if int(new_doc.name[-3:])%2 == 0: + if int(si.name[-3:])%2 == 0: bank_account = "_Test Cash - _TC" mode_of_payment = "Cash" else: bank_account = "_Test Bank - _TC" mode_of_payment = "Credit Card" - pe = get_payment_entry("Sales Invoice", new_doc.name, bank_account=bank_account) + pe = get_payment_entry("Sales Invoice", si.name, bank_account=bank_account) pe.reference_no = "_Test" - pe.reference_date = nowdate() + pe.reference_date = today() pe.mode_of_payment = mode_of_payment pe.insert() pe.submit() @@ -63,32 +52,23 @@ class TestSalesPaymentSummary(unittest.TestCase): self.assertTrue('Cash' not in mop.values()[0]) def test_get_mode_of_payments_details(self): - si = frappe.get_all("Sales Invoice", filters={"company": "_Test Company", "customer": "_Test Customer"}, fields=["name", "docstatus"]) filters = get_filters() - for invoice in si[:-2]: - doc = frappe.get_doc("Sales Invoice", invoice.name) - new_doc = frappe.copy_doc(doc) - new_doc.ignore_pricing_rule = 1 - for item in new_doc.items: - item.pricing_rule = "" - new_doc.insert() - new_doc.submit() - try: - new_doc.submit() - except Exception as e: - print(e) + for dummy in range(2): + si = create_sales_invoice_record() + si.insert() + si.submit() - if int(new_doc.name[-3:])%2 == 0: + if int(si.name[-3:])%2 == 0: bank_account = "_Test Cash - _TC" mode_of_payment = "Cash" else: bank_account = "_Test Bank - _TC" mode_of_payment = "Credit Card" - pe = get_payment_entry("Sales Invoice", new_doc.name, bank_account=bank_account) + pe = get_payment_entry("Sales Invoice", si.name, bank_account=bank_account) pe.reference_no = "_Test" - pe.reference_date = nowdate() + pe.reference_date = today() pe.mode_of_payment = mode_of_payment pe.insert() pe.submit() @@ -117,6 +97,60 @@ class TestSalesPaymentSummary(unittest.TestCase): def get_filters(): return { "from_date": "1900-01-01", - "to_date": nowdate(), + "to_date": today(), "company": "_Test Company" - } \ No newline at end of file + } + +def create_sales_invoice_record(qty=1): + # return sales invoice doc object + return frappe.get_doc({ + "doctype": "Sales Invoice", + "customer": frappe.get_doc('Customer', {"customer_name": "Prestiga-Biz"}).name, + "company": '_Test Company', + "due_date": today(), + "posting_date": today(), + "currency": "INR", + "taxes_and_charges": "", + "debit_to": "Debtors - _TC", + "taxes": [], + "items": [{ + 'doctype': 'Sales Invoice Item', + 'item_code': frappe.get_doc('Item', {'item_name': 'Consulting'}).name, + 'qty': qty, + "rate": 10000, + 'income_account': 'Sales - _TC', + 'cost_center': 'Main - _TC', + 'expense_account': 'Cost of Goods Sold - _TC' + }] + }) + +def create_records(): + if frappe.db.exists("Customer", "Prestiga-Biz"): + return + + #customer + frappe.get_doc({ + "customer_group": "_Test Customer Group", + "customer_name": "Prestiga-Biz", + "customer_type": "Company", + "doctype": "Customer", + "territory": "_Test Territory" + }).insert() + + # item + item = frappe.get_doc({ + "doctype": "Item", + "item_code": "Consulting", + "item_name": "Consulting", + "item_group": "All Item Groups", + "company": "_Test Company", + "is_stock_item": 0 + }).insert() + + # item price + frappe.get_doc({ + "doctype": "Item Price", + "price_list": "Standard Selling", + "item_code": item.item_code, + "price_list_rate": 10000 + }).insert() \ No newline at end of file From 43c7bd57e4c4d47a398300150433b0384a830ebb Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 27 Dec 2018 14:43:30 +0000 Subject: [PATCH 30/35] Cancel existing payment entries for data integrity in test --- .../report/sales_payment_summary/test_sales_payment_summary.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index aeb618f122..bf9bc5ef68 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -14,6 +14,9 @@ class TestSalesPaymentSummary(unittest.TestCase): @classmethod def setUpClass(self): create_records() + pes = frappe.get_all("Payment Entry") + for pe in pes: + frappe.db.set_value("Payment Entry", pe.name, "docstatus", 2) def test_get_mode_of_payments(self): filters = get_filters() From b7339d7dcb4c39d938fcf99a36c0106cdb9be4f8 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 27 Dec 2018 15:30:56 +0000 Subject: [PATCH 31/35] Test travis --- .../report/sales_payment_summary/test_sales_payment_summary.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index bf9bc5ef68..8de3abca3c 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -51,6 +51,8 @@ class TestSalesPaymentSummary(unittest.TestCase): pe.cancel() mop = get_mode_of_payments(filters) + print(frappe.get_all("Payment Entry", filters={"docstatus": 1})) + print(mop) self.assertTrue('Credit Card' in mop.values()[0]) self.assertTrue('Cash' not in mop.values()[0]) From 8d71015bf8d517a156e6228494017d34eaee7bc6 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 27 Dec 2018 16:43:26 +0000 Subject: [PATCH 32/35] Cleanup journal entries --- .../sales_payment_summary/test_sales_payment_summary.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index 8de3abca3c..d3b3492153 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -15,8 +15,11 @@ class TestSalesPaymentSummary(unittest.TestCase): def setUpClass(self): create_records() pes = frappe.get_all("Payment Entry") + jes = frappe.get_all("Journal Entry") for pe in pes: frappe.db.set_value("Payment Entry", pe.name, "docstatus", 2) + for je in jes: + frappe.db.set_value("Journal Entry", je.name, "docstatus", 2) def test_get_mode_of_payments(self): filters = get_filters() @@ -51,8 +54,6 @@ class TestSalesPaymentSummary(unittest.TestCase): pe.cancel() mop = get_mode_of_payments(filters) - print(frappe.get_all("Payment Entry", filters={"docstatus": 1})) - print(mop) self.assertTrue('Credit Card' in mop.values()[0]) self.assertTrue('Cash' not in mop.values()[0]) From e87eb07e161fcd8bb417b6aab8645f0a415cfae3 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 27 Dec 2018 17:38:57 +0000 Subject: [PATCH 33/35] Correction for Travis --- .../report/sales_payment_summary/sales_payment_summary.py | 4 ++-- .../sales_payment_summary/test_sales_payment_summary.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) 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 f4c72b4413..c234da0fe3 100644 --- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py @@ -156,7 +156,6 @@ def get_sales_invoice_data(filters): def get_mode_of_payments(filters): - frappe.log_error(filters, 'filters') mode_of_payments = {} invoice_list = get_invoices(filters) invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list]) @@ -164,6 +163,7 @@ def get_mode_of_payments(filters): inv_mop = frappe.db.sql("""select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment from `tabSales Invoice` a, `tabSales Invoice Payment` b where a.name = b.parent + and a.docstatus = 1 and a.name in ({invoice_list_names}) union select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment @@ -197,13 +197,13 @@ def get_invoices(filters): def get_mode_of_payment_details(filters): mode_of_payment_details = {} invoice_list = get_invoices(filters) - frappe.log_error(invoice_list, 'invoice_list') 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.docstatus = 1 and a.name in ({invoice_list_names}) group by a.owner, a.posting_date, mode_of_payment union diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index d3b3492153..62843e74ef 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -16,10 +16,13 @@ class TestSalesPaymentSummary(unittest.TestCase): create_records() pes = frappe.get_all("Payment Entry") jes = frappe.get_all("Journal Entry") + sis = frappe.get_all("Sales Invoice") for pe in pes: frappe.db.set_value("Payment Entry", pe.name, "docstatus", 2) for je in jes: frappe.db.set_value("Journal Entry", je.name, "docstatus", 2) + for si in sis: + frappe.db.set_value("Sales Invoice", si.name, "docstatus", 2) def test_get_mode_of_payments(self): filters = get_filters() From df4fe922c09aaeab09c6db67a9cd221ded3a4c86 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 28 Dec 2018 16:46:36 +0530 Subject: [PATCH 34/35] [Fix] Duplicate fieldnames operation and allow_alternative_item in BOM Item --- .../doctype/bom_item/bom_item.json | 210 ++++++------------ 1 file changed, 62 insertions(+), 148 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index 8a380f755b..8d4d69b09a 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -79,67 +79,67 @@ "unique": 0 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item operation", - "length": 0, - "no_copy": 0, - "options": "Operation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "operation", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Item operation", + "length": 0, + "no_copy": 0, + "options": "Operation", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 }, { @@ -966,6 +966,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "item_code.include_item_in_manufacturing", + "fieldname": "include_item_in_manufacturing", "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, @@ -974,6 +975,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, + "label": "Include Item In Manufacturing", "length": 0, "no_copy": 0, "permlevel": 0, @@ -987,29 +989,6 @@ "search_index": 0, "set_only_once": 0, "translatable": 0, - "fieldname": "include_item_in_manufacturing", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Item In Manufacturing", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 0 }, { @@ -1044,71 +1023,6 @@ "set_only_once": 0, "translatable": 0, "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item operation", - "length": 0, - "no_copy": 0, - "options": "Operation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_alternative_item", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Alternative Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 } ], "has_web_view": 0, @@ -1121,7 +1035,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-12-26 15:04:56.187136", + "modified": "2018-12-28 16:38:56.529079", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", From 50f0a99330ae7cbf4354874baa06132f9bdcdc97 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 28 Dec 2018 17:15:56 +0530 Subject: [PATCH 35/35] fix: Checks for status in employee and for date range in upload attendance. (#16279) * fix: checks for status in employee and for date range in upload attendance. 1. Earlier the status of an amployee can be set to 'Left' even though other employees are reporting to this employee, added checks to prevent this behaviour. 2. Earlier the range of dates added to the template were not checked for date of joining and relieving date. Now the range of dates added in the template are between the date of joining and relieving date. * remove "import erpnext" * fix: replace frappe.db.sql with frappe.db.get_all * fix: refactored using list comprehension * fix: query refactoring * fix: combining list comprehensions * travis debugging * fix: doc.save --- erpnext/hr/doctype/employee/employee.py | 16 ++++++--- erpnext/hr/doctype/employee/test_employee.py | 13 +++++++ .../test_upload_attendance.py | 34 +++++++++++++++++++ .../upload_attendance/upload_attendance.py | 33 ++++++++++++++---- 4 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 erpnext/hr/doctype/upload_attendance/test_upload_attendance.py diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 66d9badd2a..d518cd8995 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -13,8 +13,8 @@ from frappe.model.document import Document from erpnext.utilities.transaction_base import delete_events from frappe.utils.nestedset import NestedSet -class EmployeeUserDisabledError(frappe.ValidationError): - pass +class EmployeeUserDisabledError(frappe.ValidationError): pass +class EmployeeLeftValidationError(frappe.ValidationError): pass class Employee(NestedSet): nsm_parent_field = 'reports_to' @@ -147,8 +147,16 @@ class Employee(NestedSet): validate_email_add(self.personal_email, True) def validate_status(self): - if self.status == 'Left' and not self.relieving_date: - throw(_("Please enter relieving date.")) + if self.status == 'Left': + reports_to = frappe.db.get_all('Employee', + filters={'reports_to': self.name} + ) + if reports_to: + link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name) for employee in reports_to] + throw(_("Employee status cannot be set to 'Left' as following employees are currently reporting to this employee: ") + + ', '.join(link_to_employees), EmployeeLeftValidationError) + if not self.relieving_date: + throw(_("Please enter relieving date.")) def validate_for_enabled_user_id(self, enabled): if not self.status == 'Active': diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index 1afb8f40b4..5a63beb283 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -7,6 +7,7 @@ import frappe import erpnext import unittest import frappe.utils +from erpnext.hr.doctype.employee.employee import EmployeeLeftValidationError test_records = frappe.get_test_records('Employee') @@ -32,6 +33,18 @@ class TestEmployee(unittest.TestCase): email_queue = frappe.db.sql("""select * from `tabEmail Queue`""", as_dict=True) self.assertTrue("Subject: Birthday Reminder" in email_queue[0].message) + def test_employee_status_left(self): + employee1 = make_employee("test_employee_1@company.com") + employee2 = make_employee("test_employee_2@company.com") + employee1_doc = frappe.get_doc("Employee", employee1) + employee2_doc = frappe.get_doc("Employee", employee2) + employee2_doc.reload() + employee2_doc.reports_to = employee1_doc.name + employee2_doc.save() + employee1_doc.reload() + employee1_doc.status = 'Left' + self.assertRaises(EmployeeLeftValidationError, employee1_doc.save) + def make_employee(user): if not frappe.db.get_value("User", user): frappe.get_doc({ diff --git a/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py new file mode 100644 index 0000000000..5ab2847dde --- /dev/null +++ b/erpnext/hr/doctype/upload_attendance/test_upload_attendance.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from frappe.utils import getdate +from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data +from erpnext.hr.doctype.employee.test_employee import make_employee + +class TestUploadAttendance(unittest.TestCase): + def test_date_range(self): + employee = make_employee("test_employee@company.com") + employee_doc = frappe.get_doc("Employee", employee) + date_of_joining = "2018-01-02" + relieving_date = "2018-01-03" + from_date = "2018-01-01" + to_date = "2018-01-04" + employee_doc.date_of_joining = date_of_joining + employee_doc.relieving_date = relieving_date + employee_doc.save() + args = { + "from_date": from_date, + "to_date": to_date + } + data = get_data(args) + filtered_data = [] + for row in data: + if row[1] == employee: + filtered_data.append(row) + print(filtered_data) + for row in filtered_data: + self.assertTrue(getdate(row[3]) >= getdate(date_of_joining) and getdate(row[3]) <= getdate(relieving_date)) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index 3d080a725e..db74b102a7 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -41,16 +41,28 @@ def add_header(w): return w def add_data(w, args): + data = get_data(args) + writedata(w, data) + return w + +def get_data(args): dates = get_dates(args) employees = get_active_employees() existing_attendance_records = get_existing_attendance_records(args) + data = [] for date in dates: for employee in employees: + if getdate(date) < getdate(employee.date_of_joining): + continue + if employee.relieving_date: + if getdate(date) > getdate(employee.relieving_date): + continue existing_attendance = {} if existing_attendance_records \ - and tuple([getdate(date), employee.name]) in existing_attendance_records: + and tuple([getdate(date), employee.name]) in existing_attendance_records \ + and getdate(employee.date_of_joining) >= getdate(date) \ + and getdate(employee.relieving_date) <= getdate(date): existing_attendance = existing_attendance_records[tuple([getdate(date), employee.name])] - row = [ existing_attendance and existing_attendance.name or "", employee.name, employee.employee_name, date, @@ -58,8 +70,12 @@ def add_data(w, args): existing_attendance and existing_attendance.leave_type or "", employee.company, existing_attendance and existing_attendance.naming_series or get_naming_series(), ] - w.writerow(row) - return w + data.append(row) + return data + +def writedata(w, data): + for row in data: + w.writerow(row) def get_dates(args): """get list of dates in between from date and to date""" @@ -68,8 +84,13 @@ def get_dates(args): return dates def get_active_employees(): - employees = frappe.db.sql("""select name, employee_name, company - from tabEmployee where docstatus < 2 and status = 'Active'""", as_dict=1) + employees = frappe.db.get_all('Employee', + fields=['name', 'employee_name', 'date_of_joining', 'company', 'relieving_date'], + filters={ + 'docstatus': ['<', 2], + 'status': 'Active' + } + ) return employees def get_existing_attendance_records(args):