Merge pull request #4876 from saurabh6790/valuation_rate_on_so
[enhancement] get valuation rate and gross profit on sales order item
This commit is contained in:
commit
b1f0a7d52e
@ -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):
|
||||
|
@ -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.set_gross_profit(item);
|
||||
if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
|
||||
}
|
||||
}
|
||||
@ -876,6 +877,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
refresh_field('to_date');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
set_gross_profit: function(item) {
|
||||
if (this.frm.doc.doctype == "Sales Order" && item.valuation_rate) {
|
||||
rate = flt(item.rate) * flt(this.frm.doc.conversion_rate || 1);
|
||||
item.gross_profit = flt(((rate - item.valuation_rate) * item.qty), precision("amount", item));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -888,7 +896,8 @@ frappe.ui.form.on(cur_frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
|
||||
} else {
|
||||
item.discount_percentage = 0.0;
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.set_gross_profit(item);
|
||||
cur_frm.cscript.calculate_taxes_and_totals();
|
||||
})
|
||||
|
||||
|
@ -14,6 +14,7 @@ class ProductBundle(Document):
|
||||
|
||||
def validate(self):
|
||||
self.validate_main_item()
|
||||
self.validate_child_items()
|
||||
from erpnext.utilities.transaction_base import validate_uom_is_integer
|
||||
validate_uom_is_integer(self, "uom", "qty")
|
||||
|
||||
@ -21,7 +22,12 @@ class ProductBundle(Document):
|
||||
"""Validates, main Item is not a stock item"""
|
||||
if frappe.db.get_value("Item", self.new_item_code, "is_stock_item"):
|
||||
frappe.throw(_("Parent Item {0} must not be a Stock Item").format(self.new_item_code))
|
||||
|
||||
|
||||
def validate_child_items(self):
|
||||
for item in self.items:
|
||||
if frappe.db.exists("Product Bundle", item.item_code):
|
||||
frappe.throw(_("Child Item should not be a Product Bundle. Please remove item `{0}` and save").format(item.item_code))
|
||||
|
||||
def get_new_item_code(doctype, txt, searchfield, start, page_len, filters):
|
||||
from erpnext.controllers.queries import get_match_cond
|
||||
|
||||
|
@ -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",
|
||||
|
@ -1218,6 +1218,58 @@
|
||||
"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,
|
||||
"options": "Company:company:default_currency",
|
||||
"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,
|
||||
"options": "Company:company:default_currency",
|
||||
"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 +1392,7 @@
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2016-02-22 09:35:19.701876",
|
||||
"modified": "2016-02-26 11:08:24.708912",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
@ -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.set_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.set_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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -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
|
||||
import json
|
||||
|
||||
@ -29,7 +29,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
|
||||
|
@ -43,8 +43,23 @@ 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))
|
||||
|
||||
if frappe.db.exists("Product Bundle", args.item_code):
|
||||
valuation_rate = 0.0
|
||||
bundled_items = frappe.get_doc("Product Bundle", args.item_code)
|
||||
|
||||
for bundle_item in bundled_items.items:
|
||||
valuation_rate += \
|
||||
flt(get_valuation_rate(bundle_item.item_code, out.get("warehouse")).get("valuation_rate") \
|
||||
* bundle_item.qty)
|
||||
|
||||
out.update({
|
||||
"valuation_rate": valuation_rate
|
||||
})
|
||||
|
||||
else:
|
||||
out.update(get_valuation_rate(args.item_code, out.get("warehouse")))
|
||||
|
||||
get_price_list_rate(args, item_doc, out)
|
||||
|
||||
@ -68,6 +83,8 @@ def get_item_details(args):
|
||||
|
||||
if args.get("is_subcontracted") == "Yes":
|
||||
out.bom = get_default_bom(args.item_code)
|
||||
|
||||
get_gross_profit(out)
|
||||
|
||||
return out
|
||||
|
||||
@ -136,13 +153,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),
|
||||
@ -302,7 +321,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 +372,10 @@ 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"], as_dict=True) \
|
||||
or {"projected_qty": 0, "actual_qty": 0, "valuation_rate": 0}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_batch_qty(batch_no,warehouse,item_code):
|
||||
@ -464,3 +484,31 @@ def get_default_bom(item_code=None):
|
||||
return bom
|
||||
else:
|
||||
frappe.throw(_("No default BOM exists for Item {0}").format(item_code))
|
||||
|
||||
def get_valuation_rate(item_code, warehouse=None):
|
||||
item = frappe.get_doc("Item", item_code)
|
||||
if item.is_stock_item:
|
||||
if not warehouse:
|
||||
warehouse = item.default_warehouse
|
||||
|
||||
return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse},
|
||||
["valuation_rate"], as_dict=True) or {"valuation_rate": 0}
|
||||
|
||||
elif not item.is_stock_item:
|
||||
valuation_rate =frappe.db.sql("""select sum(base_net_amount) / sum(qty)
|
||||
from `tabPurchase Invoice Item`
|
||||
where item_code = %s and docstatus=1""", item_code)
|
||||
|
||||
if valuation_rate:
|
||||
return {"valuation_rate": valuation_rate[0][0] or 0.0}
|
||||
else:
|
||||
return {"valuation_rate": 0.0}
|
||||
|
||||
def get_gross_profit(out):
|
||||
if out.valuation_rate:
|
||||
out.update({
|
||||
"gross_profit": ((out.base_rate - out.valuation_rate) * out.qty)
|
||||
})
|
||||
|
||||
return out
|
||||
|
Loading…
x
Reference in New Issue
Block a user