From d313553ae3620df3f1ccec319754da15301324c9 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Thu, 25 Feb 2016 18:59:20 +0530 Subject: [PATCH] [enhancement] get valuation rate and gross profit on sales order item --- erpnext/controllers/selling_controller.py | 4 +- erpnext/public/js/controllers/transaction.js | 8 ++- .../doctype/sales_order/sales_order.js | 16 +----- .../sales_order_item/sales_order_item.json | 52 ++++++++++++++++++- erpnext/selling/sales_common.js | 13 +++-- .../stock/doctype/stock_entry/stock_entry.py | 4 +- erpnext/stock/get_item_details.py | 25 ++++++--- 7 files changed, 91 insertions(+), 31 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 09a8c94b9e9..b8b2c31385f 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -6,7 +6,7 @@ import frappe from frappe.utils import cint, flt, cstr, comma_or from erpnext.setup.utils import get_company_currency from frappe import _, throw -from erpnext.stock.get_item_details import get_available_qty +from erpnext.stock.get_item_details import get_bin_details from erpnext.controllers.stock_controller import StockController @@ -24,7 +24,7 @@ class SellingController(StockController): def onload(self): if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"): for item in self.get("items"): - item.update(get_available_qty(item.item_code, + item.update(get_bin_details(item.item_code, item.warehouse)) def validate(self): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index c1e801d2faf..eec40d085eb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -603,6 +603,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ callback: function(r) { if (!r.exc && r.message) { me._set_values_for_item_list(r.message); + if(item) me.gross_profit(item); if(calculate_taxes_and_totals) me.calculate_taxes_and_totals(); } } @@ -876,6 +877,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ refresh_field('to_date'); } } + }, + + gross_profit: function(item) { + item.gross_profit = flt((((item.rate - item.valuation_rate) * item.qty) * (this.frm.doc.conversion_rate || 1)), precision("amount", item)); } }); @@ -888,7 +893,8 @@ frappe.ui.form.on(cur_frm.doctype + " Item", "rate", function(frm, cdt, cdn) { } else { item.discount_percentage = 0.0; } - + + cur_frm.cscript.gross_profit(item); cur_frm.cscript.calculate_taxes_and_totals(); }) diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index b7dff77a6d3..fed5a63b215 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -117,21 +117,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( tc_name: function() { this.get_terms(); }, - - warehouse: function(doc, cdt, cdn) { - var item = frappe.get_doc(cdt, cdn); - if(item.item_code && item.warehouse) { - return this.frm.call({ - method: "erpnext.stock.get_item_details.get_available_qty", - child: item, - args: { - item_code: item.item_code, - warehouse: item.warehouse, - }, - }); - } - }, - + make_material_request: function() { frappe.model.open_mapped_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_material_request", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 9e8c2a8caa8..6a6f4545fd8 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -1218,6 +1218,56 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "valuation_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Valuation Rate", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "gross_profit", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Gross Profit", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1340,7 +1390,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2016-02-22 09:35:19.701876", + "modified": "2016-02-25 17:52:22.402065", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index ce64f328c27..a29111f42a6 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -124,7 +124,8 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0), precision("rate", item)); - + + this.gross_profit(item); this.calculate_taxes_and_totals(); }, @@ -135,6 +136,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ } else { this.price_list_rate(doc, cdt, cdn); } + this.gross_profit(item); }, commission_rate: function() { @@ -177,16 +179,21 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ warehouse: function(doc, cdt, cdn) { var me = this; - this.batch_no(doc, cdt, cdn); var item = frappe.get_doc(cdt, cdn); + if(item.item_code && item.warehouse) { return this.frm.call({ - method: "erpnext.stock.get_item_details.get_available_qty", + method: "erpnext.stock.get_item_details.get_bin_details", child: item, args: { item_code: item.item_code, warehouse: item.warehouse, }, + callback:function(r){ + if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + me.batch_no(doc, cdt, cdn); + } + } }); } }, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 28939cae4c0..4c376f12a5a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -8,7 +8,7 @@ from frappe import _ from frappe.utils import cstr, cint, flt, comma_or, getdate, nowdate from erpnext.stock.utils import get_incoming_rate from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError -from erpnext.stock.get_item_details import get_available_qty, get_default_cost_center, get_conversion_factor +from erpnext.stock.get_item_details import get_bin_details, get_default_cost_center, get_conversion_factor from erpnext.manufacturing.doctype.bom.bom import validate_bom_no from erpnext.accounts.utils import validate_fiscal_year import json @@ -30,7 +30,7 @@ class StockEntry(StockController): def onload(self): if self.docstatus==1: for item in self.get("items"): - item.update(get_available_qty(item.item_code, item.s_warehouse)) + item.update(get_bin_details(item.item_code, item.s_warehouse)) def validate(self): self.pro_doc = None diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index e9728684846..e036a5d04f5 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -43,8 +43,7 @@ def get_item_details(args): get_party_item_code(args, item_doc, out) if out.get("warehouse"): - out.update(get_available_qty(args.item_code, out.warehouse)) - out.update(get_projected_qty(item.name, out.warehouse)) + out.update(get_bin_details(args.item_code, out.warehouse)) get_price_list_rate(args, item_doc, out) @@ -68,6 +67,8 @@ def get_item_details(args): if args.get("is_subcontracted") == "Yes": out.bom = get_default_bom(args.item_code) + + get_goss_profit(out) return out @@ -136,13 +137,15 @@ def get_basic_details(args, item): user_default_warehouse_list = get_user_default_as_list('Warehouse') user_default_warehouse = user_default_warehouse_list[0] \ if len(user_default_warehouse_list)==1 else "" + + warehouse = user_default_warehouse or args.warehouse or item.default_warehouse out = frappe._dict({ "item_code": item.name, "item_name": item.item_name, "description": cstr(item.description).strip(), "image": cstr(item.image).strip(), - "warehouse": user_default_warehouse or args.warehouse or item.default_warehouse, + "warehouse": warehouse, "income_account": get_default_income_account(args, item), "expense_account": get_default_expense_account(args, item), "cost_center": get_default_cost_center(args, item), @@ -164,7 +167,8 @@ def get_basic_details(args, item): "net_amount": 0.0, "discount_percentage": 0.0, "supplier": item.default_supplier, - "delivered_by_supplier": item.delivered_by_supplier + "delivered_by_supplier": item.delivered_by_supplier, + "valuation_rate": get_bin_details(item.name, warehouse) }) # if default specified in item is for another company, fetch from company @@ -302,7 +306,7 @@ def get_pos_profile_item_details(company, args, pos_profile=None): res[fieldname] = pos_profile.get(fieldname) if res.get("warehouse"): - res.actual_qty = get_available_qty(args.item_code, + res.actual_qty = get_bin_details(args.item_code, res.warehouse).get("actual_qty") return res @@ -353,9 +357,9 @@ def get_projected_qty(item_code, warehouse): {"item_code": item_code, "warehouse": warehouse}, "projected_qty")} @frappe.whitelist() -def get_available_qty(item_code, warehouse): +def get_bin_details(item_code, warehouse): return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, - ["projected_qty", "actual_qty"], as_dict=True) or {"projected_qty": 0, "actual_qty": 0} + ["projected_qty", "actual_qty", "valuation_rate"], as_dict=True) or {"projected_qty": 0, "actual_qty": 0} @frappe.whitelist() def get_batch_qty(batch_no,warehouse,item_code): @@ -464,3 +468,10 @@ def get_default_bom(item_code=None): return bom else: frappe.throw(_("No default BOM exists for Item {0}").format(item_code)) + + +def get_goss_profit(out): + out.update({ + "gross_profit": ((out.price_list_rate - out.valuation_rate) * out.qty) * (out.conversio_rate or 1) + }) + return out