[enhancement] get valuation rate and gross profit on sales order item

This commit is contained in:
Saurabh
2016-02-25 18:59:20 +05:30
parent 27e9b77a3d
commit d313553ae3
7 changed files with 91 additions and 31 deletions

View File

@@ -6,7 +6,7 @@ import frappe
from frappe.utils import cint, flt, cstr, comma_or from frappe.utils import cint, flt, cstr, comma_or
from erpnext.setup.utils import get_company_currency from erpnext.setup.utils import get_company_currency
from frappe import _, throw 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 from erpnext.controllers.stock_controller import StockController
@@ -24,7 +24,7 @@ class SellingController(StockController):
def onload(self): def onload(self):
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"): if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"):
for item in self.get("items"): for item in self.get("items"):
item.update(get_available_qty(item.item_code, item.update(get_bin_details(item.item_code,
item.warehouse)) item.warehouse))
def validate(self): def validate(self):

View File

@@ -603,6 +603,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
callback: function(r) { callback: function(r) {
if (!r.exc && r.message) { if (!r.exc && r.message) {
me._set_values_for_item_list(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(); 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'); 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 { } else {
item.discount_percentage = 0.0; item.discount_percentage = 0.0;
} }
cur_frm.cscript.gross_profit(item);
cur_frm.cscript.calculate_taxes_and_totals(); cur_frm.cscript.calculate_taxes_and_totals();
}) })

View File

@@ -117,21 +117,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
tc_name: function() { tc_name: function() {
this.get_terms(); 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() { make_material_request: function() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_material_request", method: "erpnext.selling.doctype.sales_order.sales_order.make_material_request",

View File

@@ -1218,6 +1218,56 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1340,7 +1390,7 @@
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2016-02-22 09:35:19.701876", "modified": "2016-02-25 17:52:22.402065",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@@ -124,7 +124,8 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0), item.rate = flt(item.price_list_rate * (1 - item.discount_percentage / 100.0),
precision("rate", item)); precision("rate", item));
this.gross_profit(item);
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
}, },
@@ -135,6 +136,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
} else { } else {
this.price_list_rate(doc, cdt, cdn); this.price_list_rate(doc, cdt, cdn);
} }
this.gross_profit(item);
}, },
commission_rate: function() { commission_rate: function() {
@@ -177,16 +179,21 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
warehouse: function(doc, cdt, cdn) { warehouse: function(doc, cdt, cdn) {
var me = this; var me = this;
this.batch_no(doc, cdt, cdn);
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_doc(cdt, cdn);
if(item.item_code && item.warehouse) { if(item.item_code && item.warehouse) {
return this.frm.call({ return this.frm.call({
method: "erpnext.stock.get_item_details.get_available_qty", method: "erpnext.stock.get_item_details.get_bin_details",
child: item, child: item,
args: { args: {
item_code: item.item_code, item_code: item.item_code,
warehouse: item.warehouse, warehouse: item.warehouse,
}, },
callback:function(r){
if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
me.batch_no(doc, cdt, cdn);
}
}
}); });
} }
}, },

View File

@@ -8,7 +8,7 @@ from frappe import _
from frappe.utils import cstr, cint, flt, comma_or, getdate, nowdate from frappe.utils import cstr, cint, flt, comma_or, getdate, nowdate
from erpnext.stock.utils import get_incoming_rate from erpnext.stock.utils import get_incoming_rate
from erpnext.stock.stock_ledger import get_previous_sle, NegativeStockError 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.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.accounts.utils import validate_fiscal_year from erpnext.accounts.utils import validate_fiscal_year
import json import json
@@ -30,7 +30,7 @@ class StockEntry(StockController):
def onload(self): def onload(self):
if self.docstatus==1: if self.docstatus==1:
for item in self.get("items"): 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): def validate(self):
self.pro_doc = None self.pro_doc = None

View File

@@ -43,8 +43,7 @@ def get_item_details(args):
get_party_item_code(args, item_doc, out) get_party_item_code(args, item_doc, out)
if out.get("warehouse"): if out.get("warehouse"):
out.update(get_available_qty(args.item_code, out.warehouse)) out.update(get_bin_details(args.item_code, out.warehouse))
out.update(get_projected_qty(item.name, out.warehouse))
get_price_list_rate(args, item_doc, out) get_price_list_rate(args, item_doc, out)
@@ -68,6 +67,8 @@ def get_item_details(args):
if args.get("is_subcontracted") == "Yes": if args.get("is_subcontracted") == "Yes":
out.bom = get_default_bom(args.item_code) out.bom = get_default_bom(args.item_code)
get_goss_profit(out)
return 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_list = get_user_default_as_list('Warehouse')
user_default_warehouse = user_default_warehouse_list[0] \ user_default_warehouse = user_default_warehouse_list[0] \
if len(user_default_warehouse_list)==1 else "" if len(user_default_warehouse_list)==1 else ""
warehouse = user_default_warehouse or args.warehouse or item.default_warehouse
out = frappe._dict({ out = frappe._dict({
"item_code": item.name, "item_code": item.name,
"item_name": item.item_name, "item_name": item.item_name,
"description": cstr(item.description).strip(), "description": cstr(item.description).strip(),
"image": cstr(item.image).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), "income_account": get_default_income_account(args, item),
"expense_account": get_default_expense_account(args, item), "expense_account": get_default_expense_account(args, item),
"cost_center": get_default_cost_center(args, item), "cost_center": get_default_cost_center(args, item),
@@ -164,7 +167,8 @@ def get_basic_details(args, item):
"net_amount": 0.0, "net_amount": 0.0,
"discount_percentage": 0.0, "discount_percentage": 0.0,
"supplier": item.default_supplier, "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 # 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) res[fieldname] = pos_profile.get(fieldname)
if res.get("warehouse"): 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") res.warehouse).get("actual_qty")
return res return res
@@ -353,9 +357,9 @@ def get_projected_qty(item_code, warehouse):
{"item_code": item_code, "warehouse": warehouse}, "projected_qty")} {"item_code": item_code, "warehouse": warehouse}, "projected_qty")}
@frappe.whitelist() @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}, 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() @frappe.whitelist()
def get_batch_qty(batch_no,warehouse,item_code): def get_batch_qty(batch_no,warehouse,item_code):
@@ -464,3 +468,10 @@ def get_default_bom(item_code=None):
return bom return bom
else: else:
frappe.throw(_("No default BOM exists for Item {0}").format(item_code)) 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