Merge branch 'develop' into pick-list-enhance
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 462d967..14fdffc 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -193,7 +193,7 @@
all_dimensions = []
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
- children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
+ children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
all_dimensions += [c.name for c in children]
return all_dimensions
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 81effd0..7fb598b 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -434,7 +434,9 @@
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
- additional_conditions.append("{0} in %({0})s".format(dimension.fieldname))
+ additional_conditions.append("{0} in %({0})s".format(dimension.fieldname))
+ else:
+ additional_conditions.append("{0} in (%({0})s)".format(dimension.fieldname))
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 898ac13..649b363 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -202,7 +202,9 @@
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
- conditions.append("{0} in %({0})s".format(dimension.fieldname))
+ conditions.append("{0} in %({0})s".format(dimension.fieldname))
+ else:
+ conditions.append("{0} in (%({0})s)".format(dimension.fieldname))
return "and {}".format(" and ".join(conditions)) if conditions else ""
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index 9864e40..b6e61b1 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -344,16 +344,19 @@
accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions:
+ common_condition = """
+ and exists(select name from `tabSales Invoice Item`
+ where parent=`tabSales Invoice`.name
+ """
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
- conditions += """ and exists(select name from `tabSales Invoice Item`
- where parent=`tabSales Invoice`.name
- and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)""".format(dimension.fieldname)
-
+ conditions += common_condition + "and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
+ else:
+ conditions += common_condition + "and ifnull(`tabSales Invoice Item`.{0}, '') in (%({0})s))".format(dimension.fieldname)
return conditions
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 5fe6b41..d783241 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -126,7 +126,9 @@
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname))
- additional_conditions += "and {0} in %({0})s".format(dimension.fieldname)
+ additional_conditions += "and {0} in %({0})s".format(dimension.fieldname)
+ else:
+ additional_conditions += "and {0} in (%({0})s)".format(dimension.fieldname)
query_filters.update({
dimension.fieldname: filters.get(dimension.fieldname)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index fd009ec..031d41c 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -662,4 +662,6 @@
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
+erpnext.patches.v12_0.recalculate_requested_qty_in_bin
+erpnext.patches.v12_0.set_total_batch_quantity
erpnext.patches.v12_0.set_updated_purpose_in_pick_list
diff --git a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py
new file mode 100644
index 0000000..8267df9
--- /dev/null
+++ b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+import frappe
+from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
+
+def execute():
+ bin_details = frappe.db.sql("""
+ SELECT item_code, warehouse
+ FROM `tabBin`""",as_dict=1)
+
+ for entry in bin_details:
+ update_bin_qty(entry.get("item_code"), entry.get("warehouse"), {
+ "indented_qty": get_indented_qty(entry.get("item_code"), entry.get("warehouse"))
+ })
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_total_batch_quantity.py b/erpnext/patches/v12_0/set_total_batch_quantity.py
new file mode 100644
index 0000000..d373275
--- /dev/null
+++ b/erpnext/patches/v12_0/set_total_batch_quantity.py
@@ -0,0 +1,11 @@
+import frappe
+
+
+def execute():
+ frappe.reload_doc("stock", "doctype", "batch")
+
+ for batch in frappe.get_all("Batch", fields=["name", "batch_id"]):
+ batch_qty = frappe.db.get_value("Stock Ledger Entry",
+ {"docstatus": 1, "batch_no": batch.batch_id, "is_cancelled": "No"},
+ "sum(actual_qty)") or 0.0
+ frappe.db.set_value("Batch", batch.name, "batch_qty", batch_qty, update_modified=False)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 4397fe4..2c436b2 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -473,6 +473,7 @@
item_code: item.item_code,
barcode: item.barcode,
serial_no: item.serial_no,
+ batch_no: item.batch_no,
set_warehouse: me.frm.doc.set_warehouse,
warehouse: item.warehouse,
customer: me.frm.doc.customer || me.frm.doc.party_name,
@@ -637,6 +638,7 @@
// Add the new list to the serial no. field in grid with each in new line
item.serial_no = valid_serial_nos.join('\n');
+ item.conversion_factor = item.conversion_factor || 1;
refresh_field("serial_no", item.name, item.parentfield);
if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) {
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index e6d4e9d..1eb4577 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -1,554 +1,194 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
"allow_import": 1,
- "allow_rename": 0,
"autoname": "field:batch_id",
- "beta": 0,
"creation": "2013-03-05 14:50:38",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
- "editable_grid": 0,
"engine": "InnoDB",
+ "field_order": [
+ "sb_disabled",
+ "disabled",
+ "sb_batch",
+ "batch_id",
+ "item",
+ "item_name",
+ "image",
+ "parent_batch",
+ "manufacturing_date",
+ "column_break_3",
+ "batch_qty",
+ "stock_uom",
+ "expiry_date",
+ "source",
+ "supplier",
+ "column_break_9",
+ "reference_doctype",
+ "reference_name",
+ "section_break_7",
+ "description"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "default": "0",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "label": "Disabled"
+ },
+ {
"depends_on": "eval:doc.__islocal",
"fieldname": "batch_id",
"fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Batch ID",
- "length": 0,
"no_copy": 1,
"oldfieldname": "batch_id",
"oldfieldtype": "Data",
- "permlevel": 0,
- "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,
- "translatable": 0,
"unique": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "item",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
"in_standard_filter": 1,
"label": "Item",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "item",
"oldfieldtype": "Link",
"options": "Item",
- "permlevel": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "image",
- "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,
- "translatable": 0,
- "unique": 0
+ "label": "image"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"depends_on": "eval:doc.parent_batch",
"fieldname": "parent_batch",
"fieldtype": "Link",
- "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": "Parent Batch",
- "length": 0,
- "no_copy": 0,
"options": "Batch",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column 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,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "disabled",
- "fieldtype": "Check",
- "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": "Disabled",
- "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,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Today",
"fieldname": "manufacturing_date",
"fieldtype": "Date",
- "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": "Manufacturing Date",
- "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,
- "translatable": 0,
- "unique": 0
+ "label": "Manufacturing Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
"fieldname": "expiry_date",
"fieldtype": "Date",
- "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": "Expiry Date",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "expiry_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "oldfieldtype": "Date"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "source",
"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,
- "label": "Source",
- "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,
- "translatable": 0,
- "unique": 0
+ "label": "Source"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "supplier",
"fieldtype": "Link",
- "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": "Supplier",
- "length": 0,
- "no_copy": 0,
"options": "Supplier",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "column_break_9",
- "fieldtype": "Column 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,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "reference_doctype",
"fieldtype": "Link",
- "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": "Source Document Type",
- "length": 0,
- "no_copy": 0,
"options": "DocType",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
- "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": "Source Document Name",
- "length": 0,
- "no_copy": 0,
"options": "reference_doctype",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "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,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_7",
- "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,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "description",
"fieldtype": "Small Text",
- "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": "Batch Description",
- "length": 0,
- "no_copy": 0,
"oldfieldname": "description",
"oldfieldtype": "Small Text",
- "permlevel": 0,
- "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,
- "translatable": 0,
- "unique": 0,
"width": "300px"
+ },
+ {
+ "fieldname": "sb_disabled",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "sb_batch",
+ "fieldtype": "Section Break",
+ "label": "Batch Details"
+ },
+ {
+ "fetch_from": "item.item_name",
+ "fieldname": "item_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Item Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "batch_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Batch Quantity",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "item.stock_uom",
+ "fieldname": "stock_uom",
+ "fieldtype": "Link",
+ "label": "Batch UOM",
+ "options": "UOM",
+ "read_only": 1
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
"icon": "fa fa-archive",
"idx": 1,
"image_field": "image",
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
"max_attachments": 5,
- "modified": "2018-08-29 06:28:57.985997",
+ "modified": "2019-10-03 22:38:45.104056",
"modified_by": "Administrator",
"module": "Stock",
"name": "Batch",
"owner": "harshada@webnotestech.com",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
"import": 1,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Item Manager",
"set_user_permissions": 1,
"share": 1,
- "submit": 0,
"write": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
+ "sort_field": "modified",
"sort_order": "DESC",
- "title_field": "item",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "title_field": "batch_id"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/batch/batch_dashboard.py b/erpnext/stock/doctype/batch/batch_dashboard.py
new file mode 100644
index 0000000..eb6a97e
--- /dev/null
+++ b/erpnext/stock/doctype/batch/batch_dashboard.py
@@ -0,0 +1,27 @@
+from __future__ import unicode_literals
+
+from frappe import _
+
+
+def get_data():
+ return {
+ 'fieldname': 'batch_no',
+ 'transactions': [
+ {
+ 'label': _('Buy'),
+ 'items': ['Purchase Invoice', 'Purchase Receipt']
+ },
+ {
+ 'label': _('Sell'),
+ 'items': ['Sales Invoice', 'Delivery Note']
+ },
+ {
+ 'label': _('Move'),
+ 'items': ['Stock Entry']
+ },
+ {
+ 'label': _('Quality'),
+ 'items': ['Quality Inspection']
+ }
+ ]
+ }
diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js
index 7ee81aa..d4f74c3 100644
--- a/erpnext/stock/doctype/batch/batch_list.js
+++ b/erpnext/stock/doctype/batch/batch_list.js
@@ -1,12 +1,14 @@
frappe.listview_settings['Batch'] = {
- add_fields: ["item", "expiry_date"],
- get_indicator: function(doc) {
- if(doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) {
- return [__("Expired"), "red", "expiry_date,>=,Today"]
- } else if(doc.expiry_date) {
- return [__("Not Expired"), "green", "expiry_date,<,Today"]
+ add_fields: ["item", "expiry_date", "batch_qty", "disabled"],
+ get_indicator: (doc) => {
+ if (doc.disabled) {
+ return [__("Disabled"), "darkgrey", "disabled,=,1"];
+ } else if (!doc.batch_qty) {
+ return [__("Empty"), "darkgrey", "batch_qty,=,0|disabled,=,0"];
+ } else if (doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) {
+ return [__("Expired"), "red", "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0"]
} else {
- return ["Not Set", "darkgrey", ""];
- }
+ return [__("Active"), "green", "batch_qty,>,0|disabled,=,0"];
+ };
}
};
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 32445a6..1fce504 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -7,7 +7,7 @@
import unittest
from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
-from frappe.utils import cint
+from frappe.utils import cint, flt
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
class TestBatch(unittest.TestCase):
@@ -35,12 +35,13 @@
receipt = frappe.get_doc(dict(
doctype='Purchase Receipt',
supplier='_Test Supplier',
+ company='_Test Company',
items=[
dict(
item_code='ITEM-BATCH-1',
qty=batch_qty,
rate=10,
- warehouse= 'Stores - WP'
+ warehouse= 'Stores - _TC'
)
]
)).insert()
@@ -175,6 +176,18 @@
self.assertEqual(get_batch_qty('batch a', '_Test Warehouse - _TC'), 90)
+ def test_total_batch_qty(self):
+ self.make_batch_item('ITEM-BATCH-3')
+ existing_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
+ stock_entry = self.make_new_batch_and_entry('ITEM-BATCH-3', 'B100', '_Test Warehouse - _TC')
+
+ current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
+ self.assertEqual(current_batch_qty, existing_batch_qty + 90)
+
+ stock_entry.cancel()
+ current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
+ self.assertEqual(current_batch_qty, existing_batch_qty)
+
@classmethod
def make_new_batch_and_entry(cls, item_name, batch_name, warehouse):
'''Make a new stock entry for given target warehouse and batch name of item'''
@@ -208,6 +221,8 @@
stock_entry.insert()
stock_entry.submit()
+ return stock_entry
+
def test_batch_name_with_naming_series(self):
stock_settings = frappe.get_single('Stock Settings')
use_naming_series = cint(stock_settings.use_naming_series)
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index e2e84c4..7d31942 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -101,6 +101,7 @@
self.add_default_uom_in_conversion_factor_table()
self.validate_conversion_factor()
self.validate_item_type()
+ self.validate_naming_series()
self.check_for_active_boms()
self.fill_customer_code()
self.check_item_tax()
@@ -186,7 +187,7 @@
or frappe.db.get_single_value('Stock Settings', 'default_warehouse'))
if default_warehouse:
warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company")
-
+
if not default_warehouse or warehouse_company != default.company:
default_warehouse = frappe.db.get_value('Warehouse',
{'warehouse_name': _('Stores'), 'company': default.company})
@@ -522,6 +523,13 @@
if self.has_serial_no == 0 and self.serial_no_series:
self.serial_no_series = None
+ def validate_naming_series(self):
+ for field in ["serial_no_series", "batch_number_series"]:
+ series = self.get(field)
+ if series and "#" in series and "." not in series:
+ frappe.throw(_("Invalid naming series (. missing) for {0}")
+ .format(frappe.bold(self.meta.get_field(field).label)))
+
def check_for_active_boms(self):
if self.default_bom:
bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
@@ -1007,8 +1015,6 @@
order by pr.posting_date desc, pr.posting_time desc, pr.name desc
limit 1""", (item_code, cstr(doc_name)), as_dict=1)
-
-
purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
or "1900-01-01")
purchase_receipt_date = getdate(last_purchase_receipt and
@@ -1016,7 +1022,7 @@
if last_purchase_order and (purchase_order_date >= purchase_receipt_date or not last_purchase_receipt):
# use purchase order
-
+
last_purchase = last_purchase_order[0]
purchase_date = purchase_order_date
@@ -1036,7 +1042,7 @@
"discount_percentage": flt(last_purchase.discount_percentage),
"purchase_date": purchase_date
})
-
+
conversion_rate = flt(conversion_rate) or 1.0
out.update({
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 79cdc1a..b925aed 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -276,8 +276,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
@@ -331,8 +331,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 27.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 1.5)
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 27.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 1.5)
# check if per complete is as expected for Stock Entry cancelled
se.cancel()
@@ -344,8 +344,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
def test_completed_qty_for_over_transfer(self):
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
@@ -425,8 +425,8 @@
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
current_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC")
- self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0)
- self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0)
+ self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 - 54.0)
+ self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
def test_incorrect_mapping_of_stock_entry(self):
# submit material request of type Transfer
@@ -512,7 +512,7 @@
mr.submit()
#testing bin value after material request is submitted
- self.assertEqual(_get_requested_qty(), existing_requested_qty + 54.0)
+ self.assertEqual(_get_requested_qty(), existing_requested_qty - 54.0)
# receive items to allow issue
self._insert_stock_entry(60, 6, "_Test Warehouse - _TC")
@@ -609,6 +609,8 @@
def test_customer_provided_parts_mr(self):
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
+ existing_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC")
+
mr = make_material_request(item_code='CUST-0987', material_request_type='Customer Provided')
se = make_stock_entry(mr.name)
se.insert()
@@ -617,7 +619,10 @@
self.assertEqual(se.get("items")[0].material_request, mr.name)
mr = frappe.get_doc("Material Request", mr.name)
mr.submit()
+ current_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC")
+
self.assertEqual(mr.per_ordered, 100)
+ self.assertEqual(existing_requested_qty, current_requested_qty)
def make_material_request(**args):
args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 35446ec..e38bb38 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -47,6 +47,7 @@
"is_subcontracted",
"supplier_warehouse",
"items_section",
+ "scan_barcode",
"items",
"pricing_rule_details",
"pricing_rules",
@@ -1070,13 +1071,18 @@
"label": "Inter Company Reference",
"options": "Delivery Note",
"read_only": 1
+ },
+ {
+ "fieldname": "scan_barcode",
+ "fieldtype": "Data",
+ "label": "Scan Barcode"
}
],
"icon": "fa fa-truck",
"idx": 261,
"is_submittable": 1,
"links": [],
- "modified": "2019-12-30 19:12:49.709711",
+ "modified": "2020-04-06 16:31:37.444891",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 5fe89d6..45ed498 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -37,11 +37,19 @@
def on_submit(self):
self.check_stock_frozen_date()
self.actual_amt_check()
+ self.calculate_batch_qty()
if not self.get("via_landed_cost_voucher"):
from erpnext.stock.doctype.serial_no.serial_no import process_serial_no
process_serial_no(self)
+ def calculate_batch_qty(self):
+ if self.batch_no:
+ batch_qty = frappe.db.get_value("Stock Ledger Entry",
+ {"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": "No"},
+ "sum(actual_qty)") or 0
+ frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
+
#check for item quantity available in stock
def actual_amt_check(self):
if self.batch_no and not self.get("allow_negative_stock"):
@@ -139,4 +147,3 @@
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
-
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index afa2394..0a49c26 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -488,12 +488,14 @@
["has_serial_no", "has_batch_no"], as_dict=1)
serial_nos = ""
- if item_dict.get("has_serial_no"):
- qty, rate, serial_nos = get_qty_rate_for_serial_nos(item_code,
- warehouse, posting_date, posting_time, item_dict)
+ with_serial_no = True if item_dict.get("has_serial_no") else False
+ data = get_stock_balance(item_code, warehouse, posting_date, posting_time,
+ with_valuation_rate=with_valuation_rate, with_serial_no=with_serial_no)
+
+ if with_serial_no:
+ qty, rate, serial_nos = data
else:
- qty, rate = get_stock_balance(item_code, warehouse,
- posting_date, posting_time, with_valuation_rate=with_valuation_rate)
+ qty, rate = data
if item_dict.get("has_batch_no"):
qty = get_batch_qty(batch_no, warehouse) or 0
@@ -504,28 +506,6 @@
'serial_nos': serial_nos
}
-def get_qty_rate_for_serial_nos(item_code, warehouse, posting_date, posting_time, item_dict):
- args = {
- "item_code": item_code,
- "warehouse": warehouse,
- "posting_date": posting_date,
- "posting_time": posting_time,
- }
-
- serial_nos_list = [serial_no.get("name")
- for serial_no in get_available_serial_nos(args)]
-
- qty = len(serial_nos_list)
- serial_nos = '\n'.join(serial_nos_list)
- args.update({
- 'qty': qty,
- "serial_nos": serial_nos
- })
-
- rate = get_incoming_rate(args, raise_error_if_no_rate=False) or 0
-
- return qty, rate, serial_nos
-
@frappe.whitelist()
def get_difference_account(purpose, company):
if purpose == 'Stock Reconciliation':
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 9c5a8e1..b1bfc90 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -245,7 +245,7 @@
'item_group_defaults': item_group_defaults,
'brand_defaults': brand_defaults
})
-
+
warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults)
if args.get('doctype') == "Material Request" and not args.get('material_request_type'):
@@ -279,7 +279,7 @@
"cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults),
'has_serial_no': item.has_serial_no,
'has_batch_no': item.has_batch_no,
- "batch_no": None,
+ "batch_no": args.get("batch_no"),
"uom": args.uom,
"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
"qty": flt(args.qty) or 1.0,
@@ -377,7 +377,7 @@
else:
warehouse = args.get('warehouse')
-
+
return warehouse
def update_barcode_value(out):
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
index 94ec314..1af68dd 100644
--- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
@@ -35,7 +35,7 @@
gl_data = voucher_wise_gl_data.get(key) or {}
d.account_value = gl_data.get("account_value", 0)
d.difference_value = (d.stock_value - d.account_value)
- if abs(d.difference_value) > 1.0/10 ** currency_precision:
+ if abs(d.difference_value) > 0.1:
data.append(d)
return data
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index e5dc6b1..2bdb04e 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -113,13 +113,24 @@
return flt(reserved_qty[0][0]) if reserved_qty else 0
def get_indented_qty(item_code, warehouse):
- indented_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor)
+ inward_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor)
+ from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
+ where mr_item.item_code=%s and mr_item.warehouse=%s
+ and mr.material_request_type in ('Purchase', 'Manufacture')
+ and mr_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name
+ and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse))
+
+ outward_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor)
from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
where mr_item.item_code=%s and mr_item.warehouse=%s
+ and mr.material_request_type in ('Material Issue', 'Material Transfer')
and mr_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name
and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse))
- return flt(indented_qty[0][0]) if indented_qty else 0
+ inward_qty, outward_qty = flt(inward_qty[0][0]) if inward_qty else 0, flt(outward_qty[0][0]) if outward_qty else 0
+ indented_qty = inward_qty - outward_qty
+
+ return indented_qty
def get_ordered_qty(item_code, warehouse):
ordered_qty = frappe.db.sql("""
@@ -145,9 +156,9 @@
from erpnext.stock.utils import get_bin
bin = get_bin(item_code, warehouse)
mismatch = False
- for fld, val in qty_dict.items():
- if flt(bin.get(fld)) != flt(val):
- bin.set(fld, flt(val))
+ for field, value in qty_dict.items():
+ if flt(bin.get(field)) != flt(value):
+ bin.set(field, flt(value))
mismatch = True
if mismatch:
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index f3381c7..7f32b8d 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -74,7 +74,8 @@
return sum(sle_map.values())
@frappe.whitelist()
-def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None, with_valuation_rate=False):
+def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None,
+ with_valuation_rate=False, with_serial_no=False):
"""Returns stock balance quantity at given warehouse on given posting date or current date.
If `with_valuation_rate` is True, will return tuple (qty, rate)"""
@@ -84,17 +85,51 @@
if not posting_date: posting_date = nowdate()
if not posting_time: posting_time = nowtime()
- last_entry = get_previous_sle({
+ args = {
"item_code": item_code,
"warehouse":warehouse,
"posting_date": posting_date,
- "posting_time": posting_time })
+ "posting_time": posting_time
+ }
+
+ last_entry = get_previous_sle(args)
if with_valuation_rate:
- return (last_entry.qty_after_transaction, last_entry.valuation_rate) if last_entry else (0.0, 0.0)
+ if with_serial_no:
+ serial_nos = last_entry.get("serial_no")
+
+ if (serial_nos and
+ len(get_serial_nos_data(serial_nos)) < last_entry.qty_after_transaction):
+ serial_nos = get_serial_nos_data_after_transactions(args)
+
+ return ((last_entry.qty_after_transaction, last_entry.valuation_rate, serial_nos)
+ if last_entry else (0.0, 0.0, 0.0))
+ else:
+ return (last_entry.qty_after_transaction, last_entry.valuation_rate) if last_entry else (0.0, 0.0)
else:
return last_entry.qty_after_transaction if last_entry else 0.0
+def get_serial_nos_data_after_transactions(args):
+ serial_nos = []
+ data = frappe.db.sql(""" SELECT serial_no, actual_qty
+ FROM `tabStock Ledger Entry`
+ WHERE
+ item_code = %(item_code)s and warehouse = %(warehouse)s
+ and timestamp(posting_date, posting_time) < timestamp(%(posting_date)s, %(posting_time)s)
+ order by posting_date, posting_time asc """, args, as_dict=1)
+
+ for d in data:
+ if d.actual_qty > 0:
+ serial_nos.extend(get_serial_nos_data(d.serial_no))
+ else:
+ serial_nos = list(set(serial_nos) - set(get_serial_nos_data(d.serial_no)))
+
+ return '\n'.join(serial_nos)
+
+def get_serial_nos_data(serial_nos):
+ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ return get_serial_nos(serial_nos)
+
@frappe.whitelist()
def get_latest_stock_qty(item_code, warehouse=None):
values, condition = [item_code], ""