Merge branch 'develop' into pick-list-enhance

This commit is contained in:
rohitwaghchaure 2020-04-07 18:04:39 +05:30 committed by GitHub
commit 36459c052f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 283 additions and 512 deletions

View File

@ -193,7 +193,7 @@ def get_dimension_with_children(doctype, dimension):
all_dimensions = [] all_dimensions = []
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"]) 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] all_dimensions += [c.name for c in children]
return all_dimensions return all_dimensions

View File

@ -434,7 +434,9 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname)) 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 "" return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""

View File

@ -202,7 +202,9 @@ def get_conditions(filters):
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname)) 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 "" return "and {}".format(" and ".join(conditions)) if conditions else ""

View File

@ -344,16 +344,19 @@ def get_conditions(filters):
accounting_dimensions = get_accounting_dimensions(as_list=False) accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions: if accounting_dimensions:
common_condition = """
and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
"""
for dimension in accounting_dimensions: for dimension in accounting_dimensions:
if filters.get(dimension.fieldname): if filters.get(dimension.fieldname):
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname)) filters.get(dimension.fieldname))
conditions += """ and exists(select name from `tabSales Invoice Item` conditions += common_condition + "and ifnull(`tabSales Invoice Item`.{0}, '') in %({0})s)".format(dimension.fieldname)
where parent=`tabSales Invoice`.name else:
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)
return conditions return conditions

View File

@ -126,7 +126,9 @@ def get_rootwise_opening_balances(filters, report_type):
if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'): if frappe.get_cached_value('DocType', dimension.document_type, 'is_tree'):
filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type, filters[dimension.fieldname] = get_dimension_with_children(dimension.document_type,
filters.get(dimension.fieldname)) 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({ query_filters.update({
dimension.fieldname: filters.get(dimension.fieldname) dimension.fieldname: filters.get(dimension.fieldname)

View File

@ -662,4 +662,6 @@ erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank 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.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.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 erpnext.patches.v12_0.set_updated_purpose_in_pick_list

View File

@ -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"))
})

View File

@ -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)

View File

@ -473,6 +473,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
item_code: item.item_code, item_code: item.item_code,
barcode: item.barcode, barcode: item.barcode,
serial_no: item.serial_no, serial_no: item.serial_no,
batch_no: item.batch_no,
set_warehouse: me.frm.doc.set_warehouse, set_warehouse: me.frm.doc.set_warehouse,
warehouse: item.warehouse, warehouse: item.warehouse,
customer: me.frm.doc.customer || me.frm.doc.party_name, customer: me.frm.doc.customer || me.frm.doc.party_name,
@ -637,6 +638,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
// Add the new list to the serial no. field in grid with each in new line // 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.serial_no = valid_serial_nos.join('\n');
item.conversion_factor = item.conversion_factor || 1;
refresh_field("serial_no", item.name, item.parentfield); refresh_field("serial_no", item.name, item.parentfield);
if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) { if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) {

View File

@ -1,554 +1,194 @@
{ {
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 0,
"autoname": "field:batch_id", "autoname": "field:batch_id",
"beta": 0,
"creation": "2013-03-05 14:50:38", "creation": "2013-03-05 14:50:38",
"custom": 0,
"docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Setup", "document_type": "Setup",
"editable_grid": 0,
"engine": "InnoDB", "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": [ "fields": [
{ {
"allow_bulk_edit": 0, "default": "0",
"allow_in_quick_entry": 0, "fieldname": "disabled",
"allow_on_submit": 0, "fieldtype": "Check",
"bold": 0, "label": "Disabled"
"collapsible": 0, },
"columns": 0, {
"depends_on": "eval:doc.__islocal", "depends_on": "eval:doc.__islocal",
"fieldname": "batch_id", "fieldname": "batch_id",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Batch ID", "label": "Batch ID",
"length": 0,
"no_copy": 1, "no_copy": 1,
"oldfieldname": "batch_id", "oldfieldname": "batch_id",
"oldfieldtype": "Data", "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, "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1 "unique": 1
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "item", "fieldname": "item",
"fieldtype": "Link", "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, "in_standard_filter": 1,
"label": "Item", "label": "Item",
"length": 0,
"no_copy": 0,
"oldfieldname": "item", "oldfieldname": "item",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Item", "options": "Item",
"permlevel": 0, "reqd": 1
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "image", "fieldname": "image",
"fieldtype": "Attach Image", "fieldtype": "Attach Image",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0, "label": "image"
"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
}, },
{ {
"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", "depends_on": "eval:doc.parent_batch",
"fieldname": "parent_batch", "fieldname": "parent_batch",
"fieldtype": "Link", "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", "label": "Parent Batch",
"length": 0,
"no_copy": 0,
"options": "Batch", "options": "Batch",
"permlevel": 0, "read_only": 1
"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
}, },
{ {
"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", "default": "Today",
"fieldname": "manufacturing_date", "fieldname": "manufacturing_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "label": "Manufacturing Date"
"ignore_user_permissions": 0, },
"ignore_xss_filter": 0, {
"in_filter": 0, "fieldname": "column_break_3",
"in_global_search": 0, "fieldtype": "Column Break"
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "expiry_date", "fieldname": "expiry_date",
"fieldtype": "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", "label": "Expiry Date",
"length": 0,
"no_copy": 0,
"oldfieldname": "expiry_date", "oldfieldname": "expiry_date",
"oldfieldtype": "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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "source", "fieldname": "source",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "label": "Source"
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "supplier", "fieldname": "supplier",
"fieldtype": "Link", "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", "label": "Supplier",
"length": 0,
"no_copy": 0,
"options": "Supplier", "options": "Supplier",
"permlevel": 0, "read_only": 1
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9", "fieldname": "column_break_9",
"fieldtype": "Column Break", "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": "reference_doctype", "fieldname": "reference_doctype",
"fieldtype": "Link", "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", "label": "Source Document Type",
"length": 0,
"no_copy": 0,
"options": "DocType", "options": "DocType",
"permlevel": 0, "read_only": 1
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reference_name", "fieldname": "reference_name",
"fieldtype": "Dynamic Link", "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", "label": "Source Document Name",
"length": 0,
"no_copy": 0,
"options": "reference_doctype", "options": "reference_doctype",
"permlevel": 0, "read_only": 1
"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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7", "fieldname": "section_break_7",
"fieldtype": "Section Break", "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
}, },
{ {
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description", "fieldname": "description",
"fieldtype": "Small Text", "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", "label": "Batch Description",
"length": 0,
"no_copy": 0,
"oldfieldname": "description", "oldfieldname": "description",
"oldfieldtype": "Small Text", "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" "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", "icon": "fa fa-archive",
"idx": 1, "idx": 1,
"image_field": "image", "image_field": "image",
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 5, "max_attachments": 5,
"modified": "2018-08-29 06:28:57.985997", "modified": "2019-10-03 22:38:45.104056",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Batch", "name": "Batch",
"owner": "harshada@webnotestech.com", "owner": "harshada@webnotestech.com",
"permissions": [ "permissions": [
{ {
"amend": 0,
"cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 1, "export": 1,
"if_owner": 0,
"import": 1, "import": 1,
"permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Item Manager", "role": "Item Manager",
"set_user_permissions": 1, "set_user_permissions": 1,
"share": 1, "share": 1,
"submit": 0,
"write": 1 "write": 1
} }
], ],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0, "sort_field": "modified",
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "item", "title_field": "batch_id"
"track_changes": 0,
"track_seen": 0,
"track_views": 0
} }

View File

@ -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']
}
]
}

View File

@ -1,12 +1,14 @@
frappe.listview_settings['Batch'] = { frappe.listview_settings['Batch'] = {
add_fields: ["item", "expiry_date"], add_fields: ["item", "expiry_date", "batch_qty", "disabled"],
get_indicator: function(doc) { get_indicator: (doc) => {
if(doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) { if (doc.disabled) {
return [__("Expired"), "red", "expiry_date,>=,Today"] return [__("Disabled"), "darkgrey", "disabled,=,1"];
} else if(doc.expiry_date) { } else if (!doc.batch_qty) {
return [__("Not Expired"), "green", "expiry_date,<,Today"] 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 { } else {
return ["Not Set", "darkgrey", ""]; return [__("Active"), "green", "batch_qty,>,0|disabled,=,0"];
} };
} }
}; };

View File

@ -7,7 +7,7 @@ from frappe.exceptions import ValidationError
import unittest import unittest
from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no 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 from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
class TestBatch(unittest.TestCase): class TestBatch(unittest.TestCase):
@ -35,12 +35,13 @@ class TestBatch(unittest.TestCase):
receipt = frappe.get_doc(dict( receipt = frappe.get_doc(dict(
doctype='Purchase Receipt', doctype='Purchase Receipt',
supplier='_Test Supplier', supplier='_Test Supplier',
company='_Test Company',
items=[ items=[
dict( dict(
item_code='ITEM-BATCH-1', item_code='ITEM-BATCH-1',
qty=batch_qty, qty=batch_qty,
rate=10, rate=10,
warehouse= 'Stores - WP' warehouse= 'Stores - _TC'
) )
] ]
)).insert() )).insert()
@ -175,6 +176,18 @@ class TestBatch(unittest.TestCase):
self.assertEqual(get_batch_qty('batch a', '_Test Warehouse - _TC'), 90) 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 @classmethod
def make_new_batch_and_entry(cls, item_name, batch_name, warehouse): 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''' '''Make a new stock entry for given target warehouse and batch name of item'''
@ -208,6 +221,8 @@ class TestBatch(unittest.TestCase):
stock_entry.insert() stock_entry.insert()
stock_entry.submit() stock_entry.submit()
return stock_entry
def test_batch_name_with_naming_series(self): def test_batch_name_with_naming_series(self):
stock_settings = frappe.get_single('Stock Settings') stock_settings = frappe.get_single('Stock Settings')
use_naming_series = cint(stock_settings.use_naming_series) use_naming_series = cint(stock_settings.use_naming_series)

View File

@ -101,6 +101,7 @@ class Item(WebsiteGenerator):
self.add_default_uom_in_conversion_factor_table() self.add_default_uom_in_conversion_factor_table()
self.validate_conversion_factor() self.validate_conversion_factor()
self.validate_item_type() self.validate_item_type()
self.validate_naming_series()
self.check_for_active_boms() self.check_for_active_boms()
self.fill_customer_code() self.fill_customer_code()
self.check_item_tax() self.check_item_tax()
@ -186,7 +187,7 @@ class Item(WebsiteGenerator):
or frappe.db.get_single_value('Stock Settings', 'default_warehouse')) or frappe.db.get_single_value('Stock Settings', 'default_warehouse'))
if default_warehouse: if default_warehouse:
warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company") warehouse_company = frappe.db.get_value("Warehouse", default_warehouse, "company")
if not default_warehouse or warehouse_company != default.company: if not default_warehouse or warehouse_company != default.company:
default_warehouse = frappe.db.get_value('Warehouse', default_warehouse = frappe.db.get_value('Warehouse',
{'warehouse_name': _('Stores'), 'company': default.company}) {'warehouse_name': _('Stores'), 'company': default.company})
@ -522,6 +523,13 @@ class Item(WebsiteGenerator):
if self.has_serial_no == 0 and self.serial_no_series: if self.has_serial_no == 0 and self.serial_no_series:
self.serial_no_series = None 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): def check_for_active_boms(self):
if self.default_bom: if self.default_bom:
bom_item = frappe.db.get_value("BOM", self.default_bom, "item") bom_item = frappe.db.get_value("BOM", self.default_bom, "item")
@ -1007,8 +1015,6 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
order by pr.posting_date desc, pr.posting_time desc, pr.name desc order by pr.posting_date desc, pr.posting_time desc, pr.name desc
limit 1""", (item_code, cstr(doc_name)), as_dict=1) limit 1""", (item_code, cstr(doc_name)), as_dict=1)
purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date purchase_order_date = getdate(last_purchase_order and last_purchase_order[0].transaction_date
or "1900-01-01") or "1900-01-01")
purchase_receipt_date = getdate(last_purchase_receipt and purchase_receipt_date = getdate(last_purchase_receipt and
@ -1016,7 +1022,7 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
if last_purchase_order and (purchase_order_date >= purchase_receipt_date or not last_purchase_receipt): if last_purchase_order and (purchase_order_date >= purchase_receipt_date or not last_purchase_receipt):
# use purchase order # use purchase order
last_purchase = last_purchase_order[0] last_purchase = last_purchase_order[0]
purchase_date = purchase_order_date purchase_date = purchase_order_date
@ -1036,7 +1042,7 @@ def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
"discount_percentage": flt(last_purchase.discount_percentage), "discount_percentage": flt(last_purchase.discount_percentage),
"purchase_date": purchase_date "purchase_date": purchase_date
}) })
conversion_rate = flt(conversion_rate) or 1.0 conversion_rate = flt(conversion_rate) or 1.0
out.update({ out.update({

View File

@ -276,8 +276,8 @@ class TestMaterialRequest(unittest.TestCase):
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") 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") 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_item1, existing_requested_qty_item1 - 54.0)
self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.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 from erpnext.stock.doctype.material_request.material_request import make_stock_entry
@ -331,8 +331,8 @@ class TestMaterialRequest(unittest.TestCase):
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") 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") 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_item1, existing_requested_qty_item1 - 27.0)
self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 1.5) self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 1.5)
# check if per complete is as expected for Stock Entry cancelled # check if per complete is as expected for Stock Entry cancelled
se.cancel() se.cancel()
@ -344,8 +344,8 @@ class TestMaterialRequest(unittest.TestCase):
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") 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") 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_item1, existing_requested_qty_item1 - 54.0)
self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0) self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
def test_completed_qty_for_over_transfer(self): def test_completed_qty_for_over_transfer(self):
existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC")
@ -425,8 +425,8 @@ class TestMaterialRequest(unittest.TestCase):
current_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") 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") 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_item1, existing_requested_qty_item1 - 54.0)
self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0) self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 - 3.0)
def test_incorrect_mapping_of_stock_entry(self): def test_incorrect_mapping_of_stock_entry(self):
# submit material request of type Transfer # submit material request of type Transfer
@ -512,7 +512,7 @@ class TestMaterialRequest(unittest.TestCase):
mr.submit() mr.submit()
#testing bin value after material request is submitted #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 # receive items to allow issue
self._insert_stock_entry(60, 6, "_Test Warehouse - _TC") self._insert_stock_entry(60, 6, "_Test Warehouse - _TC")
@ -609,6 +609,8 @@ class TestMaterialRequest(unittest.TestCase):
def test_customer_provided_parts_mr(self): def test_customer_provided_parts_mr(self):
from erpnext.stock.doctype.material_request.material_request import make_stock_entry 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) 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') mr = make_material_request(item_code='CUST-0987', material_request_type='Customer Provided')
se = make_stock_entry(mr.name) se = make_stock_entry(mr.name)
se.insert() se.insert()
@ -617,7 +619,10 @@ class TestMaterialRequest(unittest.TestCase):
self.assertEqual(se.get("items")[0].material_request, mr.name) self.assertEqual(se.get("items")[0].material_request, mr.name)
mr = frappe.get_doc("Material Request", mr.name) mr = frappe.get_doc("Material Request", mr.name)
mr.submit() mr.submit()
current_requested_qty = self._get_requested_qty("_Test Customer", "_Test Warehouse - _TC")
self.assertEqual(mr.per_ordered, 100) self.assertEqual(mr.per_ordered, 100)
self.assertEqual(existing_requested_qty, current_requested_qty)
def make_material_request(**args): def make_material_request(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -47,6 +47,7 @@
"is_subcontracted", "is_subcontracted",
"supplier_warehouse", "supplier_warehouse",
"items_section", "items_section",
"scan_barcode",
"items", "items",
"pricing_rule_details", "pricing_rule_details",
"pricing_rules", "pricing_rules",
@ -1070,13 +1071,18 @@
"label": "Inter Company Reference", "label": "Inter Company Reference",
"options": "Delivery Note", "options": "Delivery Note",
"read_only": 1 "read_only": 1
},
{
"fieldname": "scan_barcode",
"fieldtype": "Data",
"label": "Scan Barcode"
} }
], ],
"icon": "fa fa-truck", "icon": "fa fa-truck",
"idx": 261, "idx": 261,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2019-12-30 19:12:49.709711", "modified": "2020-04-06 16:31:37.444891",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt", "name": "Purchase Receipt",

View File

@ -37,11 +37,19 @@ class StockLedgerEntry(Document):
def on_submit(self): def on_submit(self):
self.check_stock_frozen_date() self.check_stock_frozen_date()
self.actual_amt_check() self.actual_amt_check()
self.calculate_batch_qty()
if not self.get("via_landed_cost_voucher"): if not self.get("via_landed_cost_voucher"):
from erpnext.stock.doctype.serial_no.serial_no import process_serial_no from erpnext.stock.doctype.serial_no.serial_no import process_serial_no
process_serial_no(self) 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 #check for item quantity available in stock
def actual_amt_check(self): def actual_amt_check(self):
if self.batch_no and not self.get("allow_negative_stock"): if self.batch_no and not self.get("allow_negative_stock"):
@ -139,4 +147,3 @@ def on_doctype_update():
frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"]) frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"]) frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])

View File

@ -488,12 +488,14 @@ def get_stock_balance_for(item_code, warehouse,
["has_serial_no", "has_batch_no"], as_dict=1) ["has_serial_no", "has_batch_no"], as_dict=1)
serial_nos = "" serial_nos = ""
if item_dict.get("has_serial_no"): with_serial_no = True if item_dict.get("has_serial_no") else False
qty, rate, serial_nos = get_qty_rate_for_serial_nos(item_code, data = get_stock_balance(item_code, warehouse, posting_date, posting_time,
warehouse, posting_date, posting_time, item_dict) with_valuation_rate=with_valuation_rate, with_serial_no=with_serial_no)
if with_serial_no:
qty, rate, serial_nos = data
else: else:
qty, rate = get_stock_balance(item_code, warehouse, qty, rate = data
posting_date, posting_time, with_valuation_rate=with_valuation_rate)
if item_dict.get("has_batch_no"): if item_dict.get("has_batch_no"):
qty = get_batch_qty(batch_no, warehouse) or 0 qty = get_batch_qty(batch_no, warehouse) or 0
@ -504,28 +506,6 @@ def get_stock_balance_for(item_code, warehouse,
'serial_nos': serial_nos '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() @frappe.whitelist()
def get_difference_account(purpose, company): def get_difference_account(purpose, company):
if purpose == 'Stock Reconciliation': if purpose == 'Stock Reconciliation':

View File

@ -245,7 +245,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
'item_group_defaults': item_group_defaults, 'item_group_defaults': item_group_defaults,
'brand_defaults': brand_defaults 'brand_defaults': brand_defaults
}) })
warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults) warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults)
if args.get('doctype') == "Material Request" and not args.get('material_request_type'): if args.get('doctype') == "Material Request" and not args.get('material_request_type'):
@ -279,7 +279,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
"cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults), "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults),
'has_serial_no': item.has_serial_no, 'has_serial_no': item.has_serial_no,
'has_batch_no': item.has_batch_no, 'has_batch_no': item.has_batch_no,
"batch_no": None, "batch_no": args.get("batch_no"),
"uom": args.uom, "uom": args.uom,
"min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "", "min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "",
"qty": flt(args.qty) or 1.0, "qty": flt(args.qty) or 1.0,
@ -377,7 +377,7 @@ def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
else: else:
warehouse = args.get('warehouse') warehouse = args.get('warehouse')
return warehouse return warehouse
def update_barcode_value(out): def update_barcode_value(out):

View File

@ -35,7 +35,7 @@ def get_data(report_filters):
gl_data = voucher_wise_gl_data.get(key) or {} gl_data = voucher_wise_gl_data.get(key) or {}
d.account_value = gl_data.get("account_value", 0) d.account_value = gl_data.get("account_value", 0)
d.difference_value = (d.stock_value - d.account_value) 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) data.append(d)
return data return data

View File

@ -113,13 +113,24 @@ def get_reserved_qty(item_code, warehouse):
return flt(reserved_qty[0][0]) if reserved_qty else 0 return flt(reserved_qty[0][0]) if reserved_qty else 0
def get_indented_qty(item_code, warehouse): 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 from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr
where mr_item.item_code=%s and mr_item.warehouse=%s 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_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name
and mr.status!='Stopped' and mr.docstatus=1""", (item_code, warehouse)) 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): def get_ordered_qty(item_code, warehouse):
ordered_qty = frappe.db.sql(""" ordered_qty = frappe.db.sql("""
@ -145,9 +156,9 @@ def update_bin_qty(item_code, warehouse, qty_dict=None):
from erpnext.stock.utils import get_bin from erpnext.stock.utils import get_bin
bin = get_bin(item_code, warehouse) bin = get_bin(item_code, warehouse)
mismatch = False mismatch = False
for fld, val in qty_dict.items(): for field, value in qty_dict.items():
if flt(bin.get(fld)) != flt(val): if flt(bin.get(field)) != flt(value):
bin.set(fld, flt(val)) bin.set(field, flt(value))
mismatch = True mismatch = True
if mismatch: if mismatch:

View File

@ -74,7 +74,8 @@ def get_stock_value_on(warehouse=None, posting_date=None, item_code=None):
return sum(sle_map.values()) return sum(sle_map.values())
@frappe.whitelist() @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. """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)""" If `with_valuation_rate` is True, will return tuple (qty, rate)"""
@ -84,17 +85,51 @@ def get_stock_balance(item_code, warehouse, posting_date=None, posting_time=None
if not posting_date: posting_date = nowdate() if not posting_date: posting_date = nowdate()
if not posting_time: posting_time = nowtime() if not posting_time: posting_time = nowtime()
last_entry = get_previous_sle({ args = {
"item_code": item_code, "item_code": item_code,
"warehouse":warehouse, "warehouse":warehouse,
"posting_date": posting_date, "posting_date": posting_date,
"posting_time": posting_time }) "posting_time": posting_time
}
last_entry = get_previous_sle(args)
if with_valuation_rate: 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: else:
return last_entry.qty_after_transaction if last_entry else 0.0 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() @frappe.whitelist()
def get_latest_stock_qty(item_code, warehouse=None): def get_latest_stock_qty(item_code, warehouse=None):
values, condition = [item_code], "" values, condition = [item_code], ""