From 05d3bcb63df47162bfe64412255dd5875b61dbe8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 28 Apr 2019 18:39:18 +0530 Subject: [PATCH] stock recon for serial no, batch no --- erpnext/stock/doctype/serial_no/serial_no.py | 2 +- .../stock_ledger_entry/stock_ledger_entry.py | 2 +- .../stock_reconciliation.js | 5 +- .../stock_reconciliation.py | 187 ++++++++++--- .../stock_reconciliation_item.json | 260 +++++++++++++++++- erpnext/stock/stock_ledger.py | 8 + erpnext/stock/utils.py | 4 + 7 files changed, 420 insertions(+), 48 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index c1aef95216..b91fddfc02 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -222,7 +222,7 @@ def validate_serial_no(sle, item_det): frappe.throw(_("Serial No {0} has already been received").format(serial_no), SerialNoDuplicateError) - if (sr.delivery_document_no and sle.voucher_type != 'Stock Entry' + if (sr.delivery_document_no and sle.voucher_type not in ['Stock Entry', 'Stock Reconciliation'] and sle.voucher_type == sr.delivery_document_type): return_against = frappe.db.get_value(sle.voucher_type, sle.voucher_no, 'return_against') if return_against and return_against != sr.delivery_document_no: 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 79da70e313..5fe89d6e22 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -38,7 +38,7 @@ class StockLedgerEntry(Document): self.check_stock_frozen_date() self.actual_amt_check() - if not self.get("via_landed_cost_voucher") and self.voucher_type != 'Stock Reconciliation': + if not self.get("via_landed_cost_voucher"): from erpnext.stock.doctype.serial_no.serial_no import process_serial_no process_serial_no(self) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index ed9d77092a..818a671cab 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -12,8 +12,7 @@ frappe.ui.form.on("Stock Reconciliation", { return { query: "erpnext.controllers.queries.item_query", filters:{ - "is_stock_item": 1, - "has_serial_no": 0 + "is_stock_item": 1 } } }); @@ -93,7 +92,7 @@ frappe.ui.form.on("Stock Reconciliation", { frappe.model.set_value(cdt, cdn, "current_valuation_rate", r.message.rate); frappe.model.set_value(cdt, cdn, "current_amount", r.message.rate * r.message.qty); frappe.model.set_value(cdt, cdn, "amount", r.message.rate * r.message.qty); - + frappe.model.set_value(cdt, cdn, "current_serial_no", r.message.serial_nos); } }); } diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 205beed744..b1abe89275 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -9,7 +9,8 @@ from frappe.utils import cstr, flt, cint from erpnext.stock.stock_ledger import update_entries_after from erpnext.controllers.stock_controller import StockController from erpnext.accounts.utils import get_company_default -from erpnext.stock.utils import get_stock_balance +from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +from erpnext.stock.utils import get_stock_balance, get_incoming_rate, get_available_serial_nos class OpeningEntryAccountError(frappe.ValidationError): pass class EmptyStockReconciliationItemsError(frappe.ValidationError): pass @@ -42,23 +43,28 @@ class StockReconciliation(StockController): """Remove items if qty or rate is not changed""" self.difference_amount = 0.0 def _changed(item): - qty, rate = get_stock_balance(item.item_code, item.warehouse, - self.posting_date, self.posting_time, with_valuation_rate=True) - if (item.qty==None or item.qty==qty) and (item.valuation_rate==None or item.valuation_rate==rate): + item_dict = get_stock_balance_for(item.item_code, item.warehouse, + self.posting_date, self.posting_time) + + if ((item.qty==None or item.qty==item_dict.get("qty")) + and (item.valuation_rate==None or item.valuation_rate==item_dict.get("rate"))): return False else: # set default as current rates if item.qty==None: - item.qty = qty + item.qty = item_dict.get("qty") if item.valuation_rate==None: - item.valuation_rate = rate + item.valuation_rate = item_dict.get("rate") - item.current_qty = qty - item.current_valuation_rate = rate + if item_dict.get("serial_nos"): + item.current_serial_no = item_dict.get("serial_nos") + + item.current_qty = item_dict.get("qty") + item.current_valuation_rate = item_dict.get("rate") self.difference_amount += (flt(item.qty, item.precision("qty")) * \ flt(item.valuation_rate or rate, item.precision("valuation_rate")) \ - - flt(qty, item.precision("qty")) * flt(rate, item.precision("valuation_rate"))) + - flt(item_dict.get("qty"), item.precision("qty")) * flt(item_dict.get("rate"), item.precision("valuation_rate"))) return True items = list(filter(lambda d: _changed(d), self.items)) @@ -89,7 +95,7 @@ class StockReconciliation(StockController): else: item_warehouse_combinations.append([row.item_code, row.warehouse]) - self.validate_item(row.item_code, row_num+1) + self.validate_item(row.item_code, row) # validate warehouse if not frappe.db.get_value("Warehouse", row.warehouse): @@ -131,7 +137,7 @@ class StockReconciliation(StockController): raise frappe.ValidationError(self.validation_messages) - def validate_item(self, item_code, row_num): + def validate_item(self, item_code, row): from erpnext.stock.doctype.item.item import validate_end_of_life, \ validate_is_stock_item, validate_cancelled_item @@ -145,51 +151,121 @@ class StockReconciliation(StockController): validate_is_stock_item(item_code, item.is_stock_item, verbose=0) # item should not be serialized - if item.has_serial_no == 1: - raise frappe.ValidationError(_("Serialized Item {0} cannot be updated using Stock Reconciliation, please use Stock Entry").format(item_code)) + if item.has_serial_no and not row.serial_no: + raise frappe.ValidationError(_("Serial nos are required for serialized item {0}").format(item_code)) # item managed batch-wise not allowed - if item.has_batch_no == 1: - raise frappe.ValidationError(_("Batched Item {0} cannot be updated using Stock Reconciliation, instead use Stock Entry").format(item_code)) + if item.has_batch_no and not row.batch: + raise frappe.ValidationError(_("Batch no is required for batched item {0}").format(item_code)) # docstatus should be < 2 validate_cancelled_item(item_code, item.docstatus, verbose=0) except Exception as e: - self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e)) + self.validation_messages.append(_("Row # ") + ("%d: " % (row.idx)) + cstr(e)) def update_stock_ledger(self): """ find difference between current and expected entries and create stock ledger entries based on the difference""" from erpnext.stock.stock_ledger import get_previous_sle + sl_entries = [] for row in self.items: + if row.serial_no: + self.get_sle_for_serialized_items(row, sl_entries) + else: + previous_sle = get_previous_sle({ + "item_code": row.item_code, + "warehouse": row.warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time + }) + + if previous_sle: + if row.qty in ("", None): + row.qty = previous_sle.get("qty_after_transaction", 0) + + if row.valuation_rate in ("", None): + row.valuation_rate = previous_sle.get("valuation_rate", 0) + + if row.qty and not row.valuation_rate: + frappe.throw(_("Valuation Rate required for Item in row {0}").format(row.idx)) + + if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction") + and (row.valuation_rate == previous_sle.get("valuation_rate") or row.qty == 0)) + or (not previous_sle and not row.qty)): + continue + + sl_entries.append(self.get_sle_for_items(row)) + + if sl_entries: + self.make_sl_entries(sl_entries) + + def get_sle_for_serialized_items(self, row, sl_entries): + from erpnext.stock.stock_ledger import get_previous_sle + + # To issue existing serial nos + if row.current_serial_no: + args = self.get_sle_for_items(row) + args.update({ + 'actual_qty': -1 * row.current_qty, + 'serial_no': row.current_serial_no, + 'qty_after_transaction': 0, + 'valuation_rate': row.current_valuation_rate + }) + + sl_entries.append(args) + + for serial_no in get_serial_nos(row.serial_no): + args = self.get_sle_for_items(row, [serial_no]) + previous_sle = get_previous_sle({ "item_code": row.item_code, - "warehouse": row.warehouse, "posting_date": self.posting_date, - "posting_time": self.posting_time + "posting_time": self.posting_time, + "serial_no": serial_no }) - if previous_sle: - if row.qty in ("", None): - row.qty = previous_sle.get("qty_after_transaction", 0) - if row.valuation_rate in ("", None): - row.valuation_rate = previous_sle.get("valuation_rate", 0) + if previous_sle and row.warehouse != previous_sle.get("warehouse"): + # If serial no exists in different warehouse - if row.qty and not row.valuation_rate: - frappe.throw(_("Valuation Rate required for Item in row {0}").format(row.idx)) + new_args = args.copy() + new_args.update({ + 'actual_qty': -1, + 'qty_after_transaction': cint(previous_sle.get('qty_after_transaction')) - 1, + 'warehouse': previous_sle.get("warehouse", '') or row.warehouse, + 'valuation_rate': previous_sle.get("valuation_rate") + }) - if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction") - and (row.valuation_rate == previous_sle.get("valuation_rate") or row.qty == 0)) - or (not previous_sle and not row.qty)): - continue + sl_entries.append(new_args) - self.insert_entries(row) + if self.docstatus == 2: + args.update({ + 'actual_qty': 1, + 'incoming_rate': row.valuation_rate, + 'valuation_rate': row.valuation_rate + }) - def insert_entries(self, row): + sl_entries.append(args) + + if self.docstatus == 1: + args = self.get_sle_for_items(row) + + args.update({ + 'actual_qty': row.qty, + 'incoming_rate': row.valuation_rate, + 'valuation_rate': row.valuation_rate + }) + + sl_entries.append(args) + + def get_sle_for_items(self, row, serial_nos=None): """Insert Stock Ledger Entries""" - args = frappe._dict({ + + if not serial_nos and row.serial_no: + serial_nos = get_serial_nos(row.serial_no) + + return frappe._dict({ "doctype": "Stock Ledger Entry", "item_code": row.item_code, "warehouse": row.warehouse, @@ -199,11 +275,11 @@ class StockReconciliation(StockController): "voucher_no": self.name, "company": self.company, "stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"), - "is_cancelled": "No", + "is_cancelled": "No" if self.docstatus != 2 else "Yes", "qty_after_transaction": flt(row.qty, row.precision("qty")), + "serial_no": '\n'.join(serial_nos) if serial_nos else '', "valuation_rate": flt(row.valuation_rate, row.precision("valuation_rate")) }) - self.make_sl_entries([args]) def delete_and_repost_sle(self): """ Delete Stock Ledger Entries related to this voucher @@ -217,6 +293,15 @@ class StockReconciliation(StockController): frappe.db.sql("""delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name)) + sl_entries = [] + for row in self.items: + if row.serial_no: + self.get_sle_for_serialized_items(row, sl_entries) + + if sl_entries: + sl_entries.reverse() + self.make_sl_entries(sl_entries) + # repost future entries for selected item_code, warehouse for entries in existing_entries: update_entries_after({ @@ -310,17 +395,43 @@ def get_items(warehouse, posting_date, posting_time, company): return res @frappe.whitelist() -def get_stock_balance_for(item_code, warehouse, posting_date, posting_time): +def get_stock_balance_for(item_code, warehouse, posting_date, posting_time, with_valuation_rate= True): frappe.has_permission("Stock Reconciliation", "write", throw = True) - qty, rate = get_stock_balance(item_code, warehouse, - posting_date, posting_time, with_valuation_rate=True) + has_serial_no = frappe.get_cached_value("Item", item_code, "has_serial_no") + + serial_nos = "" + if has_serial_no: + qty, rate, serial_nos = get_qty_rate_for_serial_nos(item_code, + warehouse, posting_date, posting_time) + else: + qty, rate = get_stock_balance(item_code, warehouse, + posting_date, posting_time, with_valuation_rate=with_valuation_rate) return { 'qty': qty, - 'rate': rate + 'rate': rate, + 'serial_nos': serial_nos } +def get_qty_rate_for_serial_nos(item_code, warehouse, posting_date, posting_time): + serial_nos_list = [serial_no.get("name") + for serial_no in get_available_serial_nos(item_code, warehouse)] + + qty = len(serial_nos_list) + serial_nos = '\n'.join(serial_nos_list) + + rate = get_incoming_rate({ + "item_code": item_code, + "warehouse": warehouse, + "posting_date": posting_date, + "posting_time": posting_time, + "qty": qty, + "serial_no":serial_nos, + }) + + return qty, rate, serial_nos + @frappe.whitelist() def get_difference_account(purpose, company): if purpose == 'Stock Reconciliation': diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index 0fafe8306c..d64c218a6d 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, @@ -14,10 +15,12 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "barcode", "fieldtype": "Data", "hidden": 0, @@ -40,14 +43,17 @@ "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": 3, + "fetch_if_empty": 0, "fieldname": "item_code", "fieldtype": "Link", "hidden": 0, @@ -71,14 +77,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "item_name", "fieldtype": "Data", "hidden": 0, @@ -101,14 +110,17 @@ "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": 3, + "fetch_if_empty": 0, "fieldname": "warehouse", "fieldtype": "Link", "hidden": 0, @@ -132,14 +144,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "column_break_6", "fieldtype": "Column Break", "hidden": 0, @@ -161,15 +176,18 @@ "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": 2, "description": "", + "fetch_if_empty": 0, "fieldname": "qty", "fieldtype": "Float", "hidden": 0, @@ -192,15 +210,18 @@ "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": 2, "description": "", + "fetch_if_empty": 0, "fieldname": "valuation_rate", "fieldtype": "Currency", "hidden": 0, @@ -224,14 +245,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "amount", "fieldtype": "Currency", "hidden": 0, @@ -255,14 +279,149 @@ "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, + "fetch_if_empty": 0, + "fieldname": "serial_no_and_batch_section", + "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": "Serial No and Batch", + "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, + "fetch_if_empty": 0, + "fieldname": "serial_no", + "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": "Serial No", + "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, + "fetch_if_empty": 0, + "fieldname": "column_break_11", + "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, + "fetch_if_empty": 0, + "fieldname": "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": "Batch", + "length": 0, + "no_copy": 0, + "options": "Batch", + "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, + "fetch_if_empty": 0, "fieldname": "section_break_3", "fieldtype": "Section Break", "hidden": 0, @@ -285,15 +444,18 @@ "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, "description": "", + "fetch_if_empty": 0, "fieldname": "current_qty", "fieldtype": "Float", "hidden": 0, @@ -316,14 +478,85 @@ "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, + "fetch_if_empty": 0, + "fieldname": "current_batch", + "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": "Current Batch No", + "length": 0, + "no_copy": 1, + "options": "", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "current_serial_no", + "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": "Current Serial No", + "length": 0, + "no_copy": 1, + "options": "", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_9", "fieldtype": "Column Break", "hidden": 0, @@ -345,15 +578,18 @@ "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, "description": "", + "fetch_if_empty": 0, "fieldname": "current_valuation_rate", "fieldtype": "Currency", "hidden": 0, @@ -377,15 +613,18 @@ "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, "description": "", + "fetch_if_empty": 0, "fieldname": "current_amount", "fieldtype": "Currency", "hidden": 0, @@ -409,14 +648,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "section_break_14", "fieldtype": "Section Break", "hidden": 0, @@ -438,14 +680,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "quantity_difference", "fieldtype": "Read Only", "hidden": 0, @@ -468,14 +713,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "column_break_16", "fieldtype": "Column Break", "hidden": 0, @@ -497,14 +745,17 @@ "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, + "fetch_if_empty": 0, "fieldname": "amount_difference", "fieldtype": "Currency", "hidden": 0, @@ -528,21 +779,20 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "idx": 0, - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, "menu_index": 0, - "modified": "2017-08-03 00:03:40.412071", + "modified": "2019-04-24 19:07:59.113660", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", @@ -551,10 +801,10 @@ "permissions": [], "quick_entry": 1, "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index c8706b291e..6a92ce8bd2 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -157,7 +157,11 @@ class update_entries_after(object): if sle.serial_no: self.get_serialized_values(sle) self.qty_after_transaction += flt(sle.actual_qty) + if sle.voucher_type == "Stock Reconciliation": + self.qty_after_transaction = sle.qty_after_transaction + self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate) + frappe.errprint([self.stock_value, self.qty_after_transaction, self.valuation_rate]) else: if sle.voucher_type=="Stock Reconciliation": # assert @@ -177,6 +181,7 @@ class update_entries_after(object): # rounding as per precision self.stock_value = flt(self.stock_value, self.precision) + frappe.errprint([self.stock_value, self.qty_after_transaction, self.valuation_rate, "wefjlk"]) if self.prev_stock_value < 0 and self.stock_value >= 0 and sle.voucher_type != 'Stock Reconciliation': stock_value_difference = sle.actual_qty * self.valuation_rate @@ -420,6 +425,9 @@ def get_stock_ledger_entries(previous_sle, operator=None, order="desc", limit=No elif previous_sle.get("warehouse_condition"): conditions += " and " + previous_sle.get("warehouse_condition") + if previous_sle.get("serial_no"): + conditions += " and serial_no like {}".format(frappe.db.escape('%{0}%'.format(previous_sle.get("serial_no")))) + if not previous_sle.get("posting_date"): previous_sle["posting_date"] = "1900-01-01" if not previous_sle.get("posting_time"): diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 76631fad64..ea8e8805a6 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -277,3 +277,7 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto new_row.append(None) result[row_idx] = new_row + +def get_available_serial_nos(item_code, warehouse): + return frappe.get_all("Serial No", filters = {'item_code': item_code, + 'warehouse': warehouse, 'delivery_document_no': ''}) or [] \ No newline at end of file