diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index c5d2b3114ba..3a713042cff 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -129,7 +129,7 @@ def get_serial_no_for_item(args): "name": args.name, "serial_no": args.serial_no }) - if args.get("parenttype") in ("Sales Invoice", "Delivery Note") and args.qty > 0: + if args.get("parenttype") in ("Sales Invoice", "Delivery Note") and args.stock_qty > 0: item_details.serial_no = get_serial_no(args) return item_details diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 890641b8737..164e30aa175 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -794,7 +794,7 @@ def make_delivery_note(source_name, target_doc=None): flt(source_doc.base_rate) target_doc.amount = (flt(source_doc.qty) - flt(source_doc.delivered_qty)) * \ flt(source_doc.rate) - target_doc.qty = flt(source_doc.qty) - flt(source_doc.delivered_qty) + target_doc.qty = flt(source_doc.qty) - flt(source_doc.delivered_qty) * flt(source_doc.conversion_factor) doclist = get_mapped_doc("Sales Invoice", source_name, { "Sales Invoice": { diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json index e58c5ab0026..a0222254357 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_records.json +++ b/erpnext/accounts/doctype/sales_invoice/test_records.json @@ -20,7 +20,8 @@ "item_name": "138-CMS Shoe", "parentfield": "items", "qty": 1.0, - "rate": 500.0 + "rate": 500.0, + "stock_uom": "_Test UOM" } ], "base_grand_total": 561.8, @@ -89,7 +90,8 @@ "item_name": "_Test Item", "parentfield": "items", "price_list_rate": 500.0, - "qty": 1.0 + "qty": 1.0, + "stock_uom": "_Test UOM" } ], "base_grand_total": 630.0, @@ -264,6 +266,7 @@ "price_list_rate": 62.5, "qty": 10, "stock_uom": "_Test UOM" + }, { "cost_center": "_Test Cost Center - _TC", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 42190533c31..64debd3c6a7 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -366,8 +366,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "price_list_rate", - "fieldtype": "Currency", + "fieldname": "stock_uom", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -375,14 +375,12 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Price List Rate", + "label": "Stock UOM", "length": 0, "no_copy": 0, - "oldfieldname": "ref_rate", - "oldfieldtype": "Currency", - "options": "currency", + "options": "UOM", "permlevel": 0, - "print_hide": 1, + "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 1, "remember_last_selected_value": 0, @@ -424,7 +422,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "stock_uom", + "fieldname": "uom", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -438,8 +436,97 @@ "no_copy": 0, "options": "UOM", "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "UOM Conversion Factor", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_17", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Price List Rate", + "length": 0, + "no_copy": 0, + "oldfieldname": "ref_rate", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, @@ -1820,6 +1907,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "stock_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Qty as per Stock UOM", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1976,7 +2092,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-02-17 16:28:57.719409", + "modified": "2017-02-22 01:46:28.390397", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/buying/doctype/purchase_common/purchase_common.js b/erpnext/buying/doctype/purchase_common/purchase_common.js index 46a59b7146f..b6b147cfd26 100644 --- a/erpnext/buying/doctype/purchase_common/purchase_common.js +++ b/erpnext/buying/doctype/purchase_common/purchase_common.js @@ -107,26 +107,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ this.price_list_rate(doc, cdt, cdn); }, - uom: function(doc, cdt, cdn) { - var me = this; - var item = frappe.get_doc(cdt, cdn); - if(item.item_code && item.uom) { - return this.frm.call({ - method: "erpnext.stock.get_item_details.get_conversion_factor", - child: item, - args: { - item_code: item.item_code, - uom: item.uom - }, - callback: function(r) { - if(!r.exc) { - me.conversion_factor(me.frm.doc, cdt, cdn); - } - } - }); - } - }, - qty: function(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); if ((doc.doctype == "Purchase Receipt") || (doc.doctype == "Purchase Invoice" && doc.update_stock)) { @@ -140,8 +120,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ } this._super(doc, cdt, cdn); - this.conversion_factor(doc, cdt, cdn); - }, received_qty: function(doc, cdt, cdn) { @@ -160,15 +138,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ this.qty(doc, cdt, cdn); }, - conversion_factor: function(doc, cdt, cdn) { - if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { - var item = frappe.get_doc(cdt, cdn); - frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); - item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); - refresh_field("stock_qty", item.name, item.parentfield); - } - }, - warehouse: function(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); if(item.item_code && item.warehouse) { diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 60cd68c6350..7003618bb6e 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -9,6 +9,7 @@ from frappe import _, throw from erpnext.stock.get_item_details import get_bin_details from erpnext.stock.utils import get_incoming_rate from erpnext.stock.stock_ledger import get_valuation_rate +from erpnext.stock.get_item_details import get_conversion_factor from erpnext.controllers.stock_controller import StockController @@ -34,6 +35,7 @@ class SellingController(StockController): super(SellingController, self).validate() self.validate_max_discount() self.validate_selling_price() + self.set_qty_as_per_stock_uom() check_active_sales_items(self) def set_missing_values(self, for_validate=False): @@ -163,6 +165,13 @@ class SellingController(StockController): if discount and flt(d.discount_percentage) > discount: frappe.throw(_("Maxiumm discount for Item {0} is {1}%").format(d.item_code, discount)) + 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: + frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx)) + d.stock_qty = flt(d.qty) * flt(d.conversion_factor) + def validate_selling_price(self): def throw_message(item_name, rate, ref_rate_field): frappe.throw(_("""Selling price for item {0} is lower than its {1}. Selling price should be atleast {2}""") @@ -212,8 +221,9 @@ class SellingController(StockController): 'warehouse': d.warehouse, 'item_code': d.item_code, 'qty': d.qty, - 'uom': d.stock_uom, + 'uom': d.uom, 'stock_uom': d.stock_uom, + 'conversion_factor': d.conversion_factor, 'batch_no': cstr(d.get("batch_no")).strip(), 'serial_no': cstr(d.get("serial_no")).strip(), 'name': d.name, @@ -282,6 +292,10 @@ class SellingController(StockController): sl_entries = [] for d in self.get_item_list(): if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty): + if flt(d.conversion_factor)==0.0: + d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0 + + qty_in_stock_uom = flt(d.qty * d.conversion_factor) return_rate = 0 if cint(self.is_return) and self.return_against and self.docstatus==1: return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against) @@ -292,13 +306,13 @@ class SellingController(StockController): if d.warehouse and ((not cint(self.is_return) and self.docstatus==1) or (cint(self.is_return) and self.docstatus==2)): sl_entries.append(self.get_sl_entries(d, { - "actual_qty": -1*flt(d.qty), + "actual_qty": -1*qty_in_stock_uom, "incoming_rate": return_rate })) if d.target_warehouse: target_warehouse_sle = self.get_sl_entries(d, { - "actual_qty": flt(d.qty), + "actual_qty": qty_in_stock_uom, "warehouse": d.target_warehouse }) @@ -324,7 +338,7 @@ class SellingController(StockController): if d.warehouse and ((not cint(self.is_return) and self.docstatus==2) or (cint(self.is_return) and self.docstatus==1)): sl_entries.append(self.get_sl_entries(d, { - "actual_qty": -1*flt(d.qty), + "actual_qty": -1*qty_in_stock_uom, "incoming_rate": return_rate })) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 59b2ca07aa7..0e34ee61b34 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -378,3 +378,4 @@ erpnext.patches.v7_2.rename_att_date_attendance erpnext.patches.v7_2.update_attendance_docstatus erpnext.patches.v7_2.move_dates_from_salary_structure_to_employee erpnext.patches.v7_2.make_all_assessment_group +erpnext.patches.v7_2.stock_uom_in_selling diff --git a/erpnext/patches/v7_2/stock_uom_in_selling.py b/erpnext/patches/v7_2/stock_uom_in_selling.py new file mode 100644 index 00000000000..51d6ea74236 --- /dev/null +++ b/erpnext/patches/v7_2/stock_uom_in_selling.py @@ -0,0 +1,13 @@ +import frappe + +def execute(): + frappe.reload_doctype('Sales Order') + frappe.reload_doctype('Sales Invoice') + frappe.reload_doctype('Quotation') + frappe.reload_doctype('Delivery Note') + + doctype_list = ['Sales Order Item', 'Delivery Note Item', 'Quotation Item', 'Sales Invoice Item'] + + for doctype in doctype_list: + frappe.db.sql("""update `tab{doctype}` + set uom = stock_uom, conversion_factor = 1, stock_qty = qty""".format(doctype=doctype)) \ No newline at end of file diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4e082870042..6f4a19643a0 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -537,8 +537,38 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, + uom: function(doc, cdt, cdn) { + var me = this; + var item = frappe.get_doc(cdt, cdn); + if(item.item_code && item.uom) { + return this.frm.call({ + method: "erpnext.stock.get_item_details.get_conversion_factor", + child: item, + args: { + item_code: item.item_code, + uom: item.uom + }, + callback: function(r) { + if(!r.exc) { + me.conversion_factor(me.frm.doc, cdt, cdn); + } + } + }); + } + }, + + conversion_factor: function(doc, cdt, cdn) { + if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { + var item = frappe.get_doc(cdt, cdn); + frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); + item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); + refresh_field("stock_qty", item.name, item.parentfield); + } + }, + qty: function(doc, cdt, cdn) { this.apply_pricing_rule(frappe.get_doc(cdt, cdn), true); + this.conversion_factor(doc, cdt, cdn); }, set_dynamic_labels: function() { @@ -995,7 +1025,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ 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)); + item.gross_profit = flt(((rate - item.valuation_rate) * item.stock_qty), precision("amount", item)); } }, diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 56a365edc62..d32fe77d8ea 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe.model.mapper import get_mapped_doc +from frappe.utils import flt from frappe import _ from erpnext.controllers.selling_controller import SellingController @@ -100,6 +101,9 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): target.run_method("set_missing_values") target.run_method("calculate_taxes_and_totals") + def update_item(obj, target, source_parent): + target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor) + doclist = get_mapped_doc("Quotation", source_name, { "Quotation": { "doctype": "Sales Order", @@ -111,7 +115,8 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): "doctype": "Sales Order Item", "field_map": { "parent": "prevdoc_docname" - } + }, + "postprocess": update_item }, "Sales Taxes and Charges": { "doctype": "Sales Taxes and Charges", diff --git a/erpnext/selling/doctype/quotation/test_records.json b/erpnext/selling/doctype/quotation/test_records.json index f173a7c6aa5..b41fcc9697c 100644 --- a/erpnext/selling/doctype/quotation/test_records.json +++ b/erpnext/selling/doctype/quotation/test_records.json @@ -22,7 +22,9 @@ "item_name": "CPU", "parentfield": "items", "qty": 10.0, - "rate": 100.0 + "rate": 100.0, + "stock_uom": "_Test UOM", + "conversion_factor": 1.0 } ], "quotation_to": "Customer", diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index 254e6c348bf..51c0978e9e9 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -345,8 +345,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "price_list_rate", - "fieldtype": "Currency", + "fieldname": "stock_uom", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -354,14 +354,14 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Price List Rate", + "label": "Stock UOM", "length": 0, "no_copy": 0, - "oldfieldname": "ref_rate", - "oldfieldtype": "Currency", - "options": "currency", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", "permlevel": 0, - "print_hide": 1, + "print_hide": 0, "print_hide_if_no_value": 0, "print_width": "100px", "read_only": 1, @@ -405,7 +405,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "stock_uom", + "fieldname": "uom", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -417,12 +417,99 @@ "label": "UOM", "length": 0, "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", "options": "UOM", "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "UOM Conversion Factor", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_16", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Price List Rate", + "length": 0, + "no_copy": 0, + "oldfieldname": "ref_rate", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, "print_width": "100px", "read_only": 1, "remember_last_selected_value": 0, @@ -1069,6 +1156,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "stock_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Qty as per Stock UOM", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1415,7 +1531,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2017-02-20 13:23:20.175080", + "modified": "2017-02-22 01:51:58.405422", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index ae59ae789a0..891f37cb282 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -29,7 +29,7 @@ frappe.ui.form.on("Sales Order", { // formatter for material request item frm.set_indicator_formatter('item_code', - function(doc) { return (doc.qty<=doc.delivered_qty) ? "green" : "orange" }) + function(doc) { return (doc.stock_qty<=doc.delivered_qty) ? "green" : "orange" }) erpnext.queries.setup_warehouse_query(frm); } diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 9aa9cadf23c..ca29f3ecac6 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -33,6 +33,7 @@ class SalesOrder(SellingController): self.validate_proj_cust() self.validate_po() self.validate_uom_is_integer("stock_uom", "qty") + self.validate_uom_is_integer("uom", "qty") self.validate_for_items() self.validate_warehouse() self.validate_drop_ship() @@ -616,6 +617,7 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier, target_doc= def update_item(source, target, source_parent): target.schedule_date = source_parent.delivery_date target.qty = flt(source.qty) - flt(source.ordered_qty) + target.stock_qty = (flt(source.qty) - flt(source.ordered_qty)) * flt(source.conversion_factor) doclist = get_mapped_doc("Sales Order", source_name, { "Sales Order": { @@ -636,7 +638,9 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier, target_doc= "field_map": [ ["name", "sales_order_item"], ["parent", "sales_order"], - ["uom", "stock_uom"], + ["stock_uom", "stock_uom"], + ["uom", "uom"], + ["conversion_factor", "conversion_factor"], ["delivery_date", "schedule_date"] ], "field_no_map": [ diff --git a/erpnext/selling/doctype/sales_order/test_records.json b/erpnext/selling/doctype/sales_order/test_records.json index 9df66f45d01..7b13fb77402 100644 --- a/erpnext/selling/doctype/sales_order/test_records.json +++ b/erpnext/selling/doctype/sales_order/test_records.json @@ -26,7 +26,9 @@ "parentfield": "items", "qty": 10.0, "rate": 100.0, - "warehouse": "_Test Warehouse - _TC" + "warehouse": "_Test Warehouse - _TC", + "stock_uom": "_Test UOM", + "conversion_factor": 1.0 } ], "selling_price_list": "_Test Price List", 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 ae30450bec4..b6d0bf19656 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -345,8 +345,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "price_list_rate", - "fieldtype": "Currency", + "fieldname": "stock_uom", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -354,14 +354,14 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Price List Rate", + "label": "Stock UOM", "length": 0, "no_copy": 0, - "oldfieldname": "ref_rate", - "oldfieldtype": "Currency", - "options": "currency", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", "permlevel": 0, - "print_hide": 1, + "print_hide": 0, "print_hide_if_no_value": 0, "print_width": "70px", "read_only": 1, @@ -405,7 +405,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "stock_uom", + "fieldname": "uom", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -417,12 +417,99 @@ "label": "UOM", "length": 0, "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", "options": "UOM", "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "UOM Conversion Factor", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_16", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Price List Rate", + "length": 0, + "no_copy": 0, + "oldfieldname": "ref_rate", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, "print_width": "70px", "read_only": 1, "remember_last_selected_value": 0, @@ -1373,6 +1460,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "stock_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Qty as per Stock UOM", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 1, "bold": 0, @@ -1754,7 +1870,7 @@ "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2017-02-20 13:23:31.696583", + "modified": "2017-02-22 01:47:17.147012", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py b/erpnext/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py index c7baf0cc914..e57636c8f01 100644 --- a/erpnext/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py +++ b/erpnext/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py @@ -92,7 +92,7 @@ def get_achieved_details(filters, sales_person, all_sales_persons, target_item_g item_details = frappe.db.sql(""" select - sum(soi.qty * (st.allocated_percentage/100)) as qty, + sum(soi.stock_qty * (st.allocated_percentage/100)) as qty, sum(soi.base_net_amount * (st.allocated_percentage/100)) as amount, st.sales_person, MONTHNAME(so.transaction_date) as month_name from diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py index 0c6914daced..152e5a761e9 100644 --- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py +++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -48,7 +48,7 @@ def get_entries(filters): entries = frappe.db.sql(""" select dt.name, dt.customer, dt.territory, dt.%s as posting_date, dt_item.item_code, - dt_item.qty, dt_item.base_net_amount, st.sales_person, st.allocated_percentage, + dt_item.stock_qty, dt_item.base_net_amount, st.sales_person, st.allocated_percentage, dt_item.base_net_amount*st.allocated_percentage/100 as contribution_amt from `tab%s` dt, `tab%s Item` dt_item, `tabSales Team` st diff --git a/erpnext/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py b/erpnext/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py index dd3433399ea..5d6396e132e 100644 --- a/erpnext/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py +++ b/erpnext/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py @@ -92,7 +92,7 @@ def get_achieved_details(filters, territory, item_groups): item_details = frappe.db.sql(""" select - soi.item_code, sum(soi.qty) as qty, sum(soi.base_net_amount) as amount, + soi.item_code, sum(soi.stock_qty) as qty sum(soi.base_net_amount) as amount, MONTHNAME(so.transaction_date) as month_name from `tabSales Order Item` soi, `tabSales Order` so diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 9d9511e7cbc..da9c771b05d 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -215,7 +215,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ args: { item_code: item.item_code, warehouse: item.warehouse, - qty: item.qty, + stock_qty: item.stock_qty, serial_no: item.serial_no || "" }, callback:function(r){ diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 5e31ac3098c..3b2c44c8b7b 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -102,6 +102,7 @@ class DeliveryNote(SellingController): self.validate_for_items() self.validate_warehouse() self.validate_uom_is_integer("stock_uom", "qty") + self.validate_uom_is_integer("uom", "qty") self.validate_with_previous_doc() from erpnext.stock.doctype.packed_item.packed_item import make_packing_list diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 679e31da82b..48aa6536918 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -373,33 +373,33 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "price_list_rate", - "fieldtype": "Currency", + "fieldname": "stock_uom", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, - "label": "Price List Rate", + "label": "Stock UOM", "length": 0, "no_copy": 0, - "oldfieldname": "ref_rate", - "oldfieldtype": "Currency", - "options": "currency", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", "permlevel": 0, - "print_hide": 1, + "print_hide": 0, "print_hide_if_no_value": 0, - "print_width": "100px", + "print_width": "50px", "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0, - "width": "100px" + "width": "50px" }, { "allow_on_submit": 0, @@ -433,33 +433,120 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "stock_uom", + "fieldname": "uom", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 1, + "in_list_view": 0, "in_standard_filter": 0, "label": "UOM", "length": 0, "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", "options": "UOM", "permlevel": 0, + "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "print_width": "50px", - "read_only": 1, + "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "UOM Conversion Factor", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "section_break_17", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Price List Rate", + "length": 0, + "no_copy": 0, + "oldfieldname": "ref_rate", + "oldfieldtype": "Currency", + "options": "currency", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "print_width": "100px", + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, "unique": 0, - "width": "50px" + "width": "100px" }, { "allow_on_submit": 0, @@ -1674,6 +1761,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "stock_qty", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Qty as per Stock UOM", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -1777,7 +1893,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-02-20 13:30:37.815725", + "modified": "2017-02-22 01:49:51.752713", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py index 09affe04943..582960de4df 100644 --- a/erpnext/stock/doctype/packed_item/packed_item.py +++ b/erpnext/stock/doctype/packed_item/packed_item.py @@ -67,7 +67,7 @@ def make_packing_list(doc): for d in doc.get("items"): if frappe.db.get_value("Product Bundle", {"new_item_code": d.item_code}): for i in get_product_bundle_items(d.item_code): - update_packing_list_item(doc, i.item_code, flt(i.qty)*flt(d.qty), d, i.description) + update_packing_list_item(doc, i.item_code, flt(i.qty)*flt(d.stock_qty), d, i.description) if [d.item_code, d.name] not in parent_items: parent_items.append([d.item_code, d.name]) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index b9b11a88557..7c279287c33 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -73,7 +73,7 @@ def get_item_details(args): out.update(get_pricing_rule_for_item(args)) - if args.get("doctype") in ("Sales Invoice", "Delivery Note") and out.qty > 0: + if args.get("doctype") in ("Sales Invoice", "Delivery Note") and out.stock_qty > 0: out.serial_no = get_serial_no(out) if args.transaction_date and item.lead_time_days: @@ -345,7 +345,7 @@ def get_serial_nos_by_fifo(args): order by timestamp(purchase_date, purchase_time) asc limit %(qty)s""", { "item_code": args.item_code, "warehouse": args.warehouse, - "qty": abs(cint(args.qty)) + "qty": abs(cint(args.stock_qty)) })) def get_actual_batch_qty(batch_no,warehouse,item_code): @@ -378,17 +378,17 @@ def get_bin_details(item_code, warehouse): or {"projected_qty": 0, "actual_qty": 0} @frappe.whitelist() -def get_serial_no_details(item_code, warehouse, qty, serial_no): - args = frappe._dict({"item_code":item_code, "warehouse":warehouse, "qty":qty, "serial_no":serial_no}) +def get_serial_no_details(item_code, warehouse, stock_qty, serial_no): + args = frappe._dict({"item_code":item_code, "warehouse":warehouse, "stock_qty":stock_qty, "serial_no":serial_no}) serial_no = get_serial_no(args) return {'serial_no': serial_no} @frappe.whitelist() -def get_bin_details_and_serial_nos(item_code, warehouse, qty=None, serial_no=None): +def get_bin_details_and_serial_nos(item_code, warehouse, stock_qty=None, serial_no=None): bin_details_and_serial_nos = {} bin_details_and_serial_nos.update(get_bin_details(item_code, warehouse)) - if qty > 0: - bin_details_and_serial_nos.update(get_serial_no_details(item_code, warehouse, qty, serial_no)) + if stock_qty > 0: + bin_details_and_serial_nos.update(get_serial_no_details(item_code, warehouse, stock_qty, serial_no)) return bin_details_and_serial_nos @frappe.whitelist() @@ -522,7 +522,7 @@ def get_valuation_rate(item_code, warehouse=None): def get_gross_profit(out): if out.valuation_rate: out.update({ - "gross_profit": ((out.base_rate - out.valuation_rate) * out.qty) + "gross_profit": ((out.base_rate - out.valuation_rate) * out.stock_qty) }) return out @@ -533,10 +533,10 @@ def get_serial_no(args): args = json.loads(args) args = frappe._dict(args) - if args.get('warehouse') and args.get('qty') and args.get('item_code'): + if args.get('warehouse') and args.get('stock_qty') and args.get('item_code'): if frappe.get_value('Item', {'item_code': args.item_code}, "has_serial_no") == 1: - args = json.dumps({"item_code": args.get('item_code'),"warehouse": args.get('warehouse'),"qty": args.get('qty')}) + args = json.dumps({"item_code": args.get('item_code'),"warehouse": args.get('warehouse'),"stock_qty": args.get('stock_qty')}) args = process_args(args) serial_no = get_serial_nos_by_fifo(args) return serial_no diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index 096f6c07b6e..4c8cc262881 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -73,7 +73,7 @@ def get_reserved_qty(item_code, warehouse): (select qty as dnpi_qty, ( - select qty from `tabSales Order Item` + select stock_qty from `tabSales Order Item` where name = dnpi.parent_detail_docname and (delivered_by_supplier is null or delivered_by_supplier = 0) ) as so_item_qty, @@ -94,7 +94,7 @@ def get_reserved_qty(item_code, warehouse): where name = dnpi_in.parent and docstatus = 1 and status != 'Closed') ) dnpi) union - (select qty as dnpi_qty, qty as so_item_qty, + (select qty as dnpi_qty, stock_qty as so_item_qty, delivered_qty as so_item_delivered_qty, parent, name from `tabSales Order Item` so_item where item_code = %s and warehouse = %s