diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d3ceb09eaa0..bfe8539d5ee 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__ = '11.1.52' +__version__ = '11.1.53' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 8a17c075b48..440529ab760 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -613,13 +613,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre orders = [] if voucher_type: - ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total" + if party_account_currency == company_currency: + grand_total_field = "base_grand_total" + rounded_total_field = "base_rounded_total" + else: + grand_total_field = "grand_total" + rounded_total_field = "rounded_total" orders = frappe.db.sql(""" select name as voucher_no, - {ref_field} as invoice_amount, - ({ref_field} - advance_paid) as outstanding_amount, + if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount, + (if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount, transaction_date as posting_date from `tab{voucher_type}` @@ -627,17 +632,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre {party_type} = %s and docstatus = 1 and ifnull(status, "") != "Closed" - and {ref_field} > advance_paid + and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid and abs(100 - per_billed) > 0.01 {condition} order by transaction_date, name """.format(**{ - "ref_field": ref_field, + "rounded_total_field": rounded_total_field, + "grand_total_field": grand_total_field, "voucher_type": voucher_type, "party_type": scrub(party_type), "condition": condition - }), party, as_dict=True) + }), (party), as_dict=True) order_list = [] for d in orders: diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py index 9ad1c5cf85c..52f33672bec 100644 --- a/erpnext/buying/utils.py +++ b/erpnext/buying/utils.py @@ -30,7 +30,9 @@ def update_last_purchase_rate(doc, is_submit): # for it to be considered for latest purchase rate if flt(d.conversion_factor): last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor) - else: + # Check if item code is present + # Conversion factor should not be mandatory for non itemized items + elif d.item_code: frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx)) # update last purchsae rate @@ -84,13 +86,13 @@ def get_linked_material_requests(items): items = json.loads(items) mr_list = [] for item in items: - material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name, - (mr_item.qty - mr_item.ordered_qty) AS qty, + material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name, + (mr_item.qty - mr_item.ordered_qty) AS qty, mr_item.item_code AS item_code, - mr_item.name AS mr_item + mr_item.name AS mr_item FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item WHERE mr.name = mr_item.parent - AND mr_item.item_code = %(item)s + AND mr_item.item_code = %(item)s AND mr.material_request_type = 'Purchase' AND mr.per_ordered < 99.99 AND mr.docstatus = 1 @@ -98,6 +100,6 @@ def get_linked_material_requests(items): ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1) if material_request: mr_list.append(material_request) - + return mr_list diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index be25d56129f..7ebf73721d0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1112,6 +1112,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name): .format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate")) + if flt(child_item.price_list_rate): + child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0, \ + child_item.precision("discount_percentage")) + child_item.flags.ignore_validate_update_after_submit = True child_item.save() diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 917c901cfcb..74427436fa4 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -395,7 +395,9 @@ class BuyingController(StockController): def set_qty_as_per_stock_uom(self): for d in self.get("items"): if d.meta.get_field("stock_qty"): - if not d.conversion_factor: + # Check if item code is present + # Conversion factor should not be mandatory for non itemized items + if not d.conversion_factor and d.item_code: frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx)) d.stock_qty = flt(d.qty) * flt(d.conversion_factor) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 34677ebc3e3..f925f94e8d8 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -77,7 +77,7 @@ class calculate_taxes_and_totals(object): item.net_rate = item.rate - if not item.qty and self.doc.is_return: + if not item.qty and self.doc.get("is_return"): item.amount = flt(-1 * item.rate, item.precision("amount")) else: item.amount = flt(item.rate * item.qty, item.precision("amount")) diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index ed48fd1ab4a..187eaed107f 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -77,7 +77,7 @@ def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_len if or_filters: for r in frappe.get_list(doctype, fields=fields,filters=filters, or_filters=or_filters, - limit_start=limit_start, limit_page_length=limit_page_length, + limit_start=limit_start, limit_page_length=limit_page_length, ignore_permissions=ignore_permissions, order_by=order_by): data.append(r) @@ -130,38 +130,56 @@ def get_customers_suppliers(doctype, user): suppliers = [] meta = frappe.get_meta(doctype) + customer_field_name = get_customer_field_name(doctype) + + has_customer_field = meta.has_field(customer_field_name) + has_supplier_field = meta.has_field('supplier') + if has_common(["Supplier", "Customer"], frappe.get_roles(user)): contacts = frappe.db.sql(""" - select + select `tabContact`.email_id, `tabDynamic Link`.link_doctype, `tabDynamic Link`.link_name - from + from `tabContact`, `tabDynamic Link` where `tabContact`.name=`tabDynamic Link`.parent and `tabContact`.email_id =%s """, user, as_dict=1) - customers = [c.link_name for c in contacts if c.link_doctype == 'Customer'] \ - if meta.get_field("customer") else None - suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier'] \ - if meta.get_field("supplier") else None + customers = [c.link_name for c in contacts if c.link_doctype == 'Customer'] + suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier'] elif frappe.has_permission(doctype, 'read', user=user): - customers = [customer.name for customer in frappe.get_list("Customer")] \ - if meta.get_field("customer") else None - suppliers = [supplier.name for supplier in frappe.get_list("Customer")] \ - if meta.get_field("supplier") else None + customer_list = frappe.get_list("Customer") + customers = suppliers = [customer.name for customer in customer_list] - return customers, suppliers + return customers if has_customer_field else None, \ + suppliers if has_supplier_field else None def has_website_permission(doc, ptype, user, verbose=False): doctype = doc.doctype customers, suppliers = get_customers_suppliers(doctype, user) if customers: - return frappe.get_all(doctype, filters=[(doctype, "customer", "in", customers), - (doctype, "name", "=", doc.name)]) and True or False + return frappe.db.exists(doctype, filters=get_customer_filter(doc, customers)) elif suppliers: fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier' - return frappe.get_all(doctype, filters=[(doctype, fieldname, "in", suppliers), - (doctype, "name", "=", doc.name)]) and True or False + return frappe.db.exists(doctype, filters={ + 'name': doc.name, + fieldname: ["in", suppliers] + }) else: return False + +def get_customer_filter(doc, customers): + doctype = doc.doctype + filters = frappe._dict() + filters.name = doc.name + filters[get_customer_field_name(doctype)] = ['in', customers] + if doctype == 'Quotation': + filters.party_type = 'Customer' + return filters + +def get_customer_field_name(doctype): + if doctype == 'Quotation': + return 'party_name' + else: + return 'customer' \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index 1c8b5f96626..7813da78ca4 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -64,13 +64,20 @@ class EmployeeAdvance(Document): def update_claimed_amount(self): claimed_amount = frappe.db.sql(""" - select sum(ifnull(allocated_amount, 0)) - from `tabExpense Claim Advance` - where employee_advance = %s and docstatus=1 and allocated_amount > 0 + SELECT sum(ifnull(allocated_amount, 0)) + FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec + WHERE + eca.employee_advance = %s + AND ec.approval_status="Approved" + AND ec.name = eca.parent + AND ec.docstatus=1 + AND eca.allocated_amount > 0 """, self.name)[0][0] or 0 - if claimed_amount: - frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount)) + frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount)) + self.reload() + self.set_status() + frappe.db.set_value("Employee Advance", self.name, "status", self.status) @frappe.whitelist() def get_due_advance_amount(employee, posting_date): diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 51bcf9f904f..cfdb813cb8c 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -178,7 +178,7 @@ frappe.ui.form.on("Expense Claim", { refresh: function(frm) { frm.trigger("toggle_fields"); - if(frm.doc.docstatus == 1) { + if(frm.doc.docstatus === 1 && frm.doc.approval_status !== "Rejected") { frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, @@ -189,7 +189,7 @@ frappe.ui.form.on("Expense Claim", { }, __("View")); } - if (frm.doc.docstatus===1 + if (frm.doc.docstatus===1 && !cint(frm.doc.is_paid) && cint(frm.doc.grand_total) > 0 && (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount)) && frappe.model.can_create("Payment Entry")) { frm.add_custom_button(__('Payment'), diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 84c1693d20c..247c9e11d5d 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -262,27 +262,44 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( }); return; } else { - var fields = [ - {fieldtype:'Table', fieldname: 'items', - description: __('Select BOM and Qty for Production'), - fields: [ - {fieldtype:'Read Only', fieldname:'item_code', - label: __('Item Code'), in_list_view:1}, - {fieldtype:'Link', fieldname:'bom', options: 'BOM', reqd: 1, - label: __('Select BOM'), in_list_view:1, get_query: function(doc) { - return {filters: {item: doc.item_code}}; - }}, - {fieldtype:'Float', fieldname:'pending_qty', reqd: 1, - label: __('Qty'), in_list_view:1}, - {fieldtype:'Data', fieldname:'sales_order_item', reqd: 1, - label: __('Sales Order Item'), hidden:1} - ], - data: r.message, - get_data: function() { - return r.message + const fields = [{ + label: 'Items', + fieldtype: 'Table', + fieldname: 'items', + description: __('Select BOM and Qty for Production'), + fields: [{ + fieldtype: 'Read Only', + fieldname: 'item_code', + label: __('Item Code'), + in_list_view: 1 + }, { + fieldtype: 'Link', + fieldname: 'bom', + options: 'BOM', + reqd: 1, + label: __('Select BOM'), + in_list_view: 1, + get_query: function (doc) { + return { filters: { item: doc.item_code } }; } + }, { + fieldtype: 'Float', + fieldname: 'pending_qty', + reqd: 1, + label: __('Qty'), + in_list_view: 1 + }, { + fieldtype: 'Data', + fieldname: 'sales_order_item', + reqd: 1, + label: __('Sales Order Item'), + hidden: 1 + }], + data: r.message, + get_data: () => { + return r.message } - ] + }] var d = new frappe.ui.Dialog({ title: __('Select Items to Manufacture'), fields: fields, diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 6fdc649e3f5..e98466b6769 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -202,13 +202,12 @@ def _get_cart_quotation(party=None): if quotation: qdoc = frappe.get_doc("Quotation", quotation[0].name) else: - [company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"]) + company = frappe.db.get_value("Shopping Cart Settings", None, ["company"]) qdoc = frappe.get_doc({ "doctype": "Quotation", "naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-", "quotation_to": party.doctype, "company": company, - "selling_price_list": price_list, "order_type": "Shopping Cart", "status": "Draft", "docstatus": 0,