diff --git a/erpnext/__version__.py b/erpnext/__version__.py index 26a6c390a50..6dd6cf9b2b6 100644 --- a/erpnext/__version__.py +++ b/erpnext/__version__.py @@ -1 +1 @@ -__version__ = '4.4.0' +__version__ = '4.4.1' diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 76e7b4a1632..21fcb29d98e 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -9,29 +9,29 @@ from erpnext.stock.utils import get_buying_amount, get_sales_bom_buying_amount def execute(filters=None): if not filters: filters = {} - + stock_ledger_entries = get_stock_ledger_entries(filters) source = get_source_data(filters) item_sales_bom = get_item_sales_bom() - - columns = [__("Delivery Note/Sales Invoice") + "::120", _("Link") + "::30", _("Posting Date") + ":Date", _("Posting Time"), + + columns = [_("Delivery Note/Sales Invoice") + "::120", _("Link") + "::30", _("Posting Date") + ":Date", _("Posting Time"), _("Item Code") + ":Link/Item", _("Item Name"), _("Description"), _("Warehouse") + ":Link/Warehouse", - _("Qty") + ":Float", _("Selling Rate") + ":Currency", _("Avg. Buying Rate") + ":Currency", + _("Qty") + ":Float", _("Selling Rate") + ":Currency", _("Avg. Buying Rate") + ":Currency", _("Selling Amount") + ":Currency", _("Buying Amount") + ":Currency", _("Gross Profit") + ":Currency", _("Gross Profit %") + ":Percent", _("Project") + ":Link/Project"] data = [] for row in source: selling_amount = flt(row.base_amount) - + item_sales_bom_map = item_sales_bom.get(row.parenttype, {}).get(row.name, frappe._dict()) - + if item_sales_bom_map.get(row.item_code): - buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse, + buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse, row.parenttype, row.name, row.item_row, stock_ledger_entries, item_sales_bom_map) else: buying_amount = get_buying_amount(row.parenttype, row.name, row.item_row, stock_ledger_entries.get((row.item_code, row.warehouse), [])) - + buying_amount = buying_amount > 0 and buying_amount or 0 gross_profit = selling_amount - buying_amount @@ -39,41 +39,41 @@ def execute(filters=None): gross_profit_percent = (gross_profit / selling_amount) * 100.0 else: gross_profit_percent = 0.0 - + icon = """""" \ % ("/".join(["#Form", row.parenttype, row.name]),) data.append([row.name, icon, row.posting_date, row.posting_time, row.item_code, row.item_name, - row.description, row.warehouse, row.qty, row.base_rate, + row.description, row.warehouse, row.qty, row.base_rate, row.qty and (buying_amount / row.qty) or 0, row.base_amount, buying_amount, gross_profit, gross_profit_percent, row.project]) - + return columns, data - -def get_stock_ledger_entries(filters): + +def get_stock_ledger_entries(filters): query = """select item_code, voucher_type, voucher_no, voucher_detail_no, posting_date, posting_time, stock_value, warehouse, actual_qty as qty from `tabStock Ledger Entry`""" - + if filters.get("company"): query += """ where company=%(company)s""" - + query += " order by item_code desc, warehouse desc, posting_date desc, posting_time desc, name desc" - + res = frappe.db.sql(query, filters, as_dict=True) - + out = {} for r in res: if (r.item_code, r.warehouse) not in out: out[(r.item_code, r.warehouse)] = [] - + out[(r.item_code, r.warehouse)].append(r) return out - + def get_item_sales_bom(): item_sales_bom = {} - + for d in frappe.db.sql("""select parenttype, parent, parent_item, item_code, warehouse, -1*qty as total_qty, parent_detail_docname from `tabPacked Item` where docstatus=1""", as_dict=True): @@ -81,7 +81,7 @@ def get_item_sales_bom(): frappe._dict()).setdefault(d.parent_item, []).append(d) return item_sales_bom - + def get_source_data(filters): conditions = "" if filters.get("company"): @@ -90,9 +90,9 @@ def get_source_data(filters): conditions += " and posting_date>=%(from_date)s" if filters.get("to_date"): conditions += " and posting_date<=%(to_date)s" - - delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name, - dn.posting_date, dn.posting_time, dn.project_name, + + delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name, + dn.posting_date, dn.posting_time, dn.project_name, item.item_code, item.item_name, item.description, item.warehouse, item.qty, item.base_rate, item.base_amount, item.name as "item_row", timestamp(dn.posting_date, dn.posting_time) as posting_datetime @@ -100,7 +100,7 @@ def get_source_data(filters): where item.parent = dn.name and dn.docstatus = 1 %s order by dn.posting_date desc, dn.posting_time desc""" % (conditions,), filters, as_dict=1) - sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name, + sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name, si.posting_date, si.posting_time, si.project_name, item.item_code, item.item_name, item.description, item.warehouse, item.qty, item.base_rate, item.base_amount, item.name as "item_row", @@ -109,9 +109,9 @@ def get_source_data(filters): where item.parent = si.name and si.docstatus = 1 %s and si.update_stock = 1 order by si.posting_date desc, si.posting_time desc""" % (conditions,), filters, as_dict=1) - + source = delivery_note_items + sales_invoice_items if len(source) > len(delivery_note_items): source.sort(key=lambda d: d.posting_datetime, reverse=True) - - return source \ No newline at end of file + + return source diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 5d500452a9c..bf1440122d0 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -4,7 +4,7 @@ app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors" app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations" app_icon = "icon-th" app_color = "#e74c3c" -app_version = "4.4.0" +app_version = "4.4.1" error_report_email = "support@erpnext.com" diff --git a/erpnext/setup/doctype/authorization_control/authorization_control.py b/erpnext/setup/doctype/authorization_control/authorization_control.py index 03c187b15b4..be97964861f 100644 --- a/erpnext/setup/doctype/authorization_control/authorization_control.py +++ b/erpnext/setup/doctype/authorization_control/authorization_control.py @@ -3,19 +3,11 @@ from __future__ import unicode_literals import frappe - -from frappe.utils import cstr, flt, has_common, make_esc, comma_or - +from frappe.utils import cstr, flt, has_common, comma_or from frappe import session, _ - - from erpnext.utilities.transaction_base import TransactionBase class AuthorizationControl(TransactionBase): - - - # Get Names of all Approving Users and Roles - # ------------------------------------------- def get_appr_user_role(self, det, doctype_name, total, based_on, condition, item, company): amt_list, appr_users, appr_roles = [], [], [] users, roles = '','' @@ -24,10 +16,18 @@ class AuthorizationControl(TransactionBase): amt_list.append(flt(x[0])) max_amount = max(amt_list) - app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and company = %s %s" % ('%s', '%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on, company)) + app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule` + where transaction = %s and (value = %s or value > %s) + and docstatus != 2 and based_on = %s and company = %s %s""" % + ('%s', '%s', '%s', '%s', '%s', condition), + (doctype_name, flt(max_amount), total, based_on, company)) if not app_dtl: - app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and ifnull(company,'') = '' %s" % ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on)) + app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule` + where transaction = %s and (value = %s or value > %s) and docstatus != 2 + and based_on = %s and ifnull(company,'') = '' %s""" % + ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on)) + for d in app_dtl: if(d[0]): appr_users.append(d[0]) if(d[1]): appr_roles.append(d[1]) @@ -36,43 +36,56 @@ class AuthorizationControl(TransactionBase): frappe.msgprint(_("Not authroized since {0} exceeds limits").format(_(based_on))) frappe.throw(_("Can be approved by {0}").format(comma_or(appr_roles + appr_users))) - - # Check if authorization rule is set specific to user - # ---------------------------------------------------- def validate_auth_rule(self, doctype_name, total, based_on, cond, company, item = ''): chk = 1 add_cond1,add_cond2 = '','' if based_on == 'Itemwise Discount': - add_cond1 += " and master_name = '"+cstr(item)+"'" - itemwise_exists = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company)) + add_cond1 += " and master_name = '"+cstr(item).replace("'", "\'")+"'" + itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule` + where transaction = %s and value <= %s + and based_on = %s and company = %s and docstatus != 2 %s %s""" % + ('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company)) + if not itemwise_exists: - itemwise_exists = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s" % ('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on)) + itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule` + where transaction = %s and value <= %s and based_on = %s + and ifnull(company,'') = '' and docstatus != 2 %s %s""" % + ('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on)) + if itemwise_exists: self.get_appr_user_role(itemwise_exists, doctype_name, total, based_on, cond+add_cond1, item,company) chk = 0 if chk == 1: - if based_on == 'Itemwise Discount': add_cond2 += " and ifnull(master_name,'') = ''" - appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company)) + if based_on == 'Itemwise Discount': + add_cond2 += " and ifnull(master_name,'') = ''" + + appr = frappe.db.sql("""select value from `tabAuthorization Rule` + where transaction = %s and value <= %s and based_on = %s + and company = %s and docstatus != 2 %s %s""" % + ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company)) if not appr: - appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s"% ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on)) + appr = frappe.db.sql("""select value from `tabAuthorization Rule` + where transaction = %s and value <= %s and based_on = %s + and ifnull(company,'') = '' and docstatus != 2 %s %s""" % + ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on)) + self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company) - - # Bifurcate Authorization based on type - # -------------------------------------- def bifurcate_based_on_type(self, doctype_name, total, av_dis, based_on, doc_obj, val, company): add_cond = '' auth_value = av_dis - if val == 1: add_cond += " and system_user = '"+session['user']+"'" + + if val == 1: add_cond += " and system_user = '"+session['user'].replace("'", "\'")+"'" elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.user.get_roles())+"')") else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''" + if based_on == 'Grand Total': auth_value = total elif based_on == 'Customerwise Discount': if doc_obj: if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer else: customer = doc_obj.customer_name - add_cond = " and master_name = '"+make_esc("'")(cstr(customer))+"'" + add_cond = " and master_name = '"+cstr(customer).replace("'", "\'")+"'" if based_on == 'Itemwise Discount': if doc_obj: for t in doc_obj.get(doc_obj.fname): @@ -80,9 +93,6 @@ class AuthorizationControl(TransactionBase): else: self.validate_auth_rule(doctype_name, auth_value, based_on, add_cond, company) - - # Check Approving Authority for transactions other than expense voucher and Appraisal - # ------------------------- def validate_approving_authority(self, doctype_name,company, total, doc_obj = ''): av_dis = 0 if doc_obj: @@ -94,11 +104,12 @@ class AuthorizationControl(TransactionBase): if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate) final_based_on = ['Grand Total','Average Discount','Customerwise Discount','Itemwise Discount'] - # Individual User - # ================ - # Check for authorization set for individual user - based_on = [x[0] for x in frappe.db.sql("select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company))] + # Check for authorization set for individual user + based_on = [x[0] for x in frappe.db.sql("""select distinct based_on from `tabAuthorization Rule` + where transaction = %s and system_user = %s + and (company = %s or ifnull(company,'')='') and docstatus != 2""", + (doctype_name, session['user'], company))] for d in based_on: self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company) @@ -107,8 +118,6 @@ class AuthorizationControl(TransactionBase): for r in based_on: if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r) - # Specific Role - # =============== # Check for authorization set on particular roles based_on = [x[0] for x in frappe.db.sql("""select based_on from `tabAuthorization Rule` @@ -124,19 +133,24 @@ class AuthorizationControl(TransactionBase): for r in based_on: if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r) - # Global Rule - # ============= # Check for global authorization for g in final_based_on: self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company) - #======================================================================================================================== - # payroll related check def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company): val_lst =[] - val = frappe.db.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and company = %s and docstatus!=2",(doctype_name,employee,employee,total_claimed_amount,company)) + val = frappe.db.sql("""select value from `tabAuthorization Rule` + where transaction=%s and (to_emp=%s or + to_designation IN (select designation from `tabEmployee` where name=%s)) + and ifnull(value,0)< %s and company = %s and docstatus!=2""", + (doctype_name,employee,employee,total_claimed_amount,company)) + if not val: - val = frappe.db.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2",(doctype_name, employee, employee, total_claimed_amount)) + val = frappe.db.sql("""select value from `tabAuthorization Rule` + where transaction=%s and (to_emp=%s or + to_designation IN (select designation from `tabEmployee` where name=%s)) + and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2""", + (doctype_name, employee, employee, total_claimed_amount)) if val: val_lst = [y[0] for y in val] @@ -144,13 +158,23 @@ class AuthorizationControl(TransactionBase): val_lst.append(0) max_val = max(val_lst) - rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and company = %s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,company,employee,employee,flt(max_val)), as_dict=1) + rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user + from `tabAuthorization Rule` + where transaction=%s and company = %s + and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) + and ifnull(value,0)= %s and docstatus!=2""", + (doctype_name,company,employee,employee,flt(max_val)), as_dict=1) + if not rule: - rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and ifnull(company,'') = '' and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,employee,employee,flt(max_val)), as_dict=1) + rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user + from `tabAuthorization Rule` + where transaction=%s and ifnull(company,'') = '' + and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) + and ifnull(value,0)= %s and docstatus!=2""", + (doctype_name,employee,employee,flt(max_val)), as_dict=1) return rule - #--------------------------------------------------------------------------------------------------------------------- # related to payroll module only def get_approver_name(self, doctype_name, total, doc_obj=''): app_user=[] @@ -159,11 +183,22 @@ class AuthorizationControl(TransactionBase): if doc_obj: if doctype_name == 'Expense Claim': - rule = self.get_value_based_rule(doctype_name,doc_obj.employee,doc_obj.total_claimed_amount, doc_obj.company) + rule = self.get_value_based_rule(doctype_name, doc_obj.employee, + doc_obj.total_claimed_amount, doc_obj.company) elif doctype_name == 'Appraisal': - rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and company = %s and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1) + rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user + from `tabAuthorization Rule` where transaction=%s + and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) + and company = %s and docstatus!=2""", + (doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1) + if not rule: - rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(company,'') = '' and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee),as_dict=1) + rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user + from `tabAuthorization Rule` + where transaction=%s and (to_emp=%s or + to_designation IN (select designation from `tabEmployee` where name=%s)) + and ifnull(company,'') = '' and docstatus!=2""", + (doctype_name,doc_obj.employee, doc_obj.employee), as_dict=1) if rule: for m in rule: @@ -171,7 +206,11 @@ class AuthorizationControl(TransactionBase): if m['approving_user']: app_specific_user.append(m['approving_user']) elif m['approving_role']: - user_lst = [z[0] for z in frappe.db.sql("select distinct t1.name from `tabUser` t1, `tabUserRole` t2 where t2.role=%s and t2.parent=t1.name and t1.name !='Administrator' and t1.name != 'Guest' and t1.docstatus !=2",m['approving_role'])] + user_lst = [z[0] for z in frappe.db.sql("""select distinct t1.name + from `tabUser` t1, `tabUserRole` t2 where t2.role=%s + and t2.parent=t1.name and t1.name !='Administrator' + and t1.name != 'Guest' and t1.docstatus !=2""", m['approving_role'])] + for x in user_lst: if not x in app_user: app_user.append(x) diff --git a/setup.py b/setup.py index 70174bd1043..855c5239f85 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import os -version = "4.4.0" +version = "4.4.1" with open("requirements.txt", "r") as f: install_requires = f.readlines()