mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 10:11:20 +00:00
stock reco fixes
This commit is contained in:
@@ -26,7 +26,7 @@ class Bin(Document):
|
|||||||
def update_stock(self, args):
|
def update_stock(self, args):
|
||||||
self.update_qty(args)
|
self.update_qty(args)
|
||||||
|
|
||||||
if args.get("actual_qty"):
|
if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
|
||||||
from erpnext.stock.stock_ledger import update_entries_after
|
from erpnext.stock.stock_ledger import update_entries_after
|
||||||
|
|
||||||
if not args.get("posting_date"):
|
if not args.get("posting_date"):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 1,
|
"allow_copy": 1,
|
||||||
"autoname": "SR/.######",
|
"autoname": "SR/.######",
|
||||||
"creation": "2013-03-28 10:35:31",
|
"creation": "2013-03-28 10:35:31",
|
||||||
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
|
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"default": "Today",
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
@@ -118,7 +119,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2014-05-26 03:05:54.024413",
|
"modified": "2014-10-07 12:43:52.825575",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Reconciliation",
|
"name": "Stock Reconciliation",
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import date_diff
|
from frappe.utils import date_diff, flt
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
|
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
item_details = get_fifo_queue(filters)
|
item_details = get_fifo_queue(filters)
|
||||||
to_date = filters["to_date"]
|
to_date = filters["to_date"]
|
||||||
@@ -16,35 +16,40 @@ def execute(filters=None):
|
|||||||
fifo_queue = item_dict["fifo_queue"]
|
fifo_queue = item_dict["fifo_queue"]
|
||||||
details = item_dict["details"]
|
details = item_dict["details"]
|
||||||
if not fifo_queue: continue
|
if not fifo_queue: continue
|
||||||
|
|
||||||
average_age = get_average_age(fifo_queue, to_date)
|
average_age = get_average_age(fifo_queue, to_date)
|
||||||
earliest_age = date_diff(to_date, fifo_queue[0][1])
|
earliest_age = date_diff(to_date, fifo_queue[0][1])
|
||||||
latest_age = date_diff(to_date, fifo_queue[-1][1])
|
latest_age = date_diff(to_date, fifo_queue[-1][1])
|
||||||
|
|
||||||
data.append([item, details.item_name, details.description, details.item_group,
|
data.append([item, details.item_name, details.description, details.item_group,
|
||||||
details.brand, average_age, earliest_age, latest_age, details.stock_uom])
|
details.brand, average_age, earliest_age, latest_age, details.stock_uom])
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_average_age(fifo_queue, to_date):
|
def get_average_age(fifo_queue, to_date):
|
||||||
batch_age = age_qty = total_qty = 0.0
|
batch_age = age_qty = total_qty = 0.0
|
||||||
for batch in fifo_queue:
|
for batch in fifo_queue:
|
||||||
batch_age = date_diff(to_date, batch[1])
|
batch_age = date_diff(to_date, batch[1])
|
||||||
age_qty += batch_age * batch[0]
|
age_qty += batch_age * batch[0]
|
||||||
total_qty += batch[0]
|
total_qty += batch[0]
|
||||||
|
|
||||||
return (age_qty / total_qty) if total_qty else 0.0
|
return (age_qty / total_qty) if total_qty else 0.0
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
return [_("Item Code") + ":Link/Item:100", _("Item Name") + "::100", _("Description") + "::200",
|
return [_("Item Code") + ":Link/Item:100", _("Item Name") + "::100", _("Description") + "::200",
|
||||||
_("Item Group") + ":Link/Item Group:100", _("Brand") + ":Link/Brand:100", _("Average Age") + ":Float:100",
|
_("Item Group") + ":Link/Item Group:100", _("Brand") + ":Link/Brand:100", _("Average Age") + ":Float:100",
|
||||||
_("Earliest") + ":Int:80", _("Latest") + ":Int:80", _("UOM") + ":Link/UOM:100"]
|
_("Earliest") + ":Int:80", _("Latest") + ":Int:80", _("UOM") + ":Link/UOM:100"]
|
||||||
|
|
||||||
def get_fifo_queue(filters):
|
def get_fifo_queue(filters):
|
||||||
item_details = {}
|
item_details = {}
|
||||||
|
prev_qty = 0.0
|
||||||
for d in get_stock_ledger_entries(filters):
|
for d in get_stock_ledger_entries(filters):
|
||||||
item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
|
item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
|
||||||
fifo_queue = item_details[d.name]["fifo_queue"]
|
fifo_queue = item_details[d.name]["fifo_queue"]
|
||||||
|
|
||||||
|
if d.voucher_type == "Stock Reconciliation":
|
||||||
|
d.actual_qty = flt(d.qty_after_transaction) - flt(prev_qty)
|
||||||
|
|
||||||
if d.actual_qty > 0:
|
if d.actual_qty > 0:
|
||||||
fifo_queue.append([d.actual_qty, d.posting_date])
|
fifo_queue.append([d.actual_qty, d.posting_date])
|
||||||
else:
|
else:
|
||||||
@@ -52,7 +57,7 @@ def get_fifo_queue(filters):
|
|||||||
while qty_to_pop:
|
while qty_to_pop:
|
||||||
batch = fifo_queue[0] if fifo_queue else [0, None]
|
batch = fifo_queue[0] if fifo_queue else [0, None]
|
||||||
if 0 < batch[0] <= qty_to_pop:
|
if 0 < batch[0] <= qty_to_pop:
|
||||||
# if batch qty > 0
|
# if batch qty > 0
|
||||||
# not enough or exactly same qty in current batch, clear batch
|
# not enough or exactly same qty in current batch, clear batch
|
||||||
qty_to_pop -= batch[0]
|
qty_to_pop -= batch[0]
|
||||||
fifo_queue.pop(0)
|
fifo_queue.pop(0)
|
||||||
@@ -61,12 +66,14 @@ def get_fifo_queue(filters):
|
|||||||
batch[0] -= qty_to_pop
|
batch[0] -= qty_to_pop
|
||||||
qty_to_pop = 0
|
qty_to_pop = 0
|
||||||
|
|
||||||
|
prev_qty = d.qty_after_transaction
|
||||||
|
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
def get_stock_ledger_entries(filters):
|
def get_stock_ledger_entries(filters):
|
||||||
return frappe.db.sql("""select
|
return frappe.db.sql("""select
|
||||||
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
||||||
actual_qty, posting_date
|
actual_qty, posting_date, voucher_type, qty_after_transaction
|
||||||
from `tabStock Ledger Entry` sle,
|
from `tabStock Ledger Entry` sle,
|
||||||
(select name, item_name, description, stock_uom, brand, item_group
|
(select name, item_name, description, stock_uom, brand, item_group
|
||||||
from `tabItem` {item_conditions}) item
|
from `tabItem` {item_conditions}) item
|
||||||
@@ -77,19 +84,19 @@ def get_stock_ledger_entries(filters):
|
|||||||
order by posting_date, posting_time, sle.name"""\
|
order by posting_date, posting_time, sle.name"""\
|
||||||
.format(item_conditions=get_item_conditions(filters),
|
.format(item_conditions=get_item_conditions(filters),
|
||||||
sle_conditions=get_sle_conditions(filters)), filters, as_dict=True)
|
sle_conditions=get_sle_conditions(filters)), filters, as_dict=True)
|
||||||
|
|
||||||
def get_item_conditions(filters):
|
def get_item_conditions(filters):
|
||||||
conditions = []
|
conditions = []
|
||||||
if filters.get("item_code"):
|
if filters.get("item_code"):
|
||||||
conditions.append("item_code=%(item_code)s")
|
conditions.append("item_code=%(item_code)s")
|
||||||
if filters.get("brand"):
|
if filters.get("brand"):
|
||||||
conditions.append("brand=%(brand)s")
|
conditions.append("brand=%(brand)s")
|
||||||
|
|
||||||
return "where {}".format(" and ".join(conditions)) if conditions else ""
|
return "where {}".format(" and ".join(conditions)) if conditions else ""
|
||||||
|
|
||||||
def get_sle_conditions(filters):
|
def get_sle_conditions(filters):
|
||||||
conditions = []
|
conditions = []
|
||||||
if filters.get("warehouse"):
|
if filters.get("warehouse"):
|
||||||
conditions.append("warehouse=%(warehouse)s")
|
conditions.append("warehouse=%(warehouse)s")
|
||||||
|
|
||||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ def update_entries_after(args, verbose=1):
|
|||||||
|
|
||||||
entries_to_fix = get_sle_after_datetime(previous_sle or \
|
entries_to_fix = get_sle_after_datetime(previous_sle or \
|
||||||
{"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
|
{"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
|
||||||
|
|
||||||
valuation_method = get_valuation_method(args["item_code"])
|
valuation_method = get_valuation_method(args["item_code"])
|
||||||
stock_value_difference = 0.0
|
stock_value_difference = 0.0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user