Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: fifo queue vs qty after transaction comparison report (backport #30969) #30970

Merged
merged 2 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */

const DIFFERNCE_FIELD_NAMES = [
"fifo_qty_diff",
"fifo_value_diff",
];

frappe.query_reports["FIFO Queue vs Qty After Transaction Comparison"] = {
"filters": [
{
"fieldname": "item_code",
"fieldtype": "Link",
"label": "Item",
"options": "Item",
get_query: function() {
return {
filters: {is_stock_item: 1, has_serial_no: 0}
}
}
},
{
"fieldname": "item_group",
"fieldtype": "Link",
"label": "Item Group",
"options": "Item Group",
},
{
"fieldname": "warehouse",
"fieldtype": "Link",
"label": "Warehouse",
"options": "Warehouse",
},
{
"fieldname": "from_date",
"fieldtype": "Date",
"label": "From Posting Date",
},
{
"fieldname": "to_date",
"fieldtype": "Date",
"label": "From Posting Date",
}
],
formatter (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (DIFFERNCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
value = "<span style='color:red'>" + value + "</span>";
}
return value;
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2022-05-11 04:09:13.460652",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letter_head": "abc",
"modified": "2022-05-11 04:09:20.232177",
"modified_by": "Administrator",
"module": "Stock",
"name": "FIFO Queue vs Qty After Transaction Comparison",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Stock Ledger Entry",
"report_name": "FIFO Queue vs Qty After Transaction Comparison",
"report_type": "Script Report",
"roles": [
{
"role": "Administrator"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

import json

import frappe
from frappe import _
from frappe.utils import flt
from frappe.utils.nestedset import get_descendants_of

SLE_FIELDS = (
"name",
"item_code",
"warehouse",
"posting_date",
"posting_time",
"creation",
"voucher_type",
"voucher_no",
"actual_qty",
"qty_after_transaction",
"stock_queue",
"batch_no",
"stock_value",
"valuation_rate",
)


def execute(filters=None):
columns = get_columns()
data = get_data(filters)
return columns, data


def get_data(filters):
if not any([filters.warehouse, filters.item_code, filters.item_group]):
frappe.throw(_("Any one of following filters required: warehouse, Item Code, Item Group"))
sles = get_stock_ledger_entries(filters)
return find_first_bad_queue(sles)


def get_stock_ledger_entries(filters):

sle_filters = {"is_cancelled": 0}

if filters.warehouse:
children = get_descendants_of("Warehouse", filters.warehouse)
sle_filters["warehouse"] = ("in", children + [filters.warehouse])

if filters.item_code:
sle_filters["item_code"] = filters.item_code
elif filters.get("item_group"):
item_group = filters.get("item_group")
children = get_descendants_of("Item Group", item_group)
item_group_filter = {"item_group": ("in", children + [item_group])}
sle_filters["item_code"] = (
"in",
frappe.get_all("Item", filters=item_group_filter, pluck="name", order_by=None),
)

if filters.from_date:
sle_filters["posting_date"] = (">=", filters.from_date)
if filters.to_date:
sle_filters["posting_date"] = ("<=", filters.to_date)

return frappe.get_all(
"Stock Ledger Entry",
fields=SLE_FIELDS,
filters=sle_filters,
order_by="timestamp(posting_date, posting_time), creation",
)


def find_first_bad_queue(sles):
item_warehouse_sles = {}
for sle in sles:
item_warehouse_sles.setdefault((sle.item_code, sle.warehouse), []).append(sle)

data = []

for _item_wh, sles in item_warehouse_sles.items():
for idx, sle in enumerate(sles):
queue = json.loads(sle.stock_queue or "[]")

sle.fifo_queue_qty = 0.0
sle.fifo_stock_value = 0.0
for qty, rate in queue:
sle.fifo_queue_qty += flt(qty)
sle.fifo_stock_value += flt(qty) * flt(rate)

sle.fifo_qty_diff = sle.qty_after_transaction - sle.fifo_queue_qty
sle.fifo_value_diff = sle.stock_value - sle.fifo_stock_value

if sle.batch_no:
sle.use_batchwise_valuation = frappe.db.get_value(
"Batch", sle.batch_no, "use_batchwise_valuation", cache=True
)

if abs(sle.fifo_qty_diff) > 0.001 or abs(sle.fifo_value_diff) > 0.1:
if idx:
data.append(sles[idx - 1])
data.append(sle)
data.append({})
break

return data


def get_columns():
return [
{
"fieldname": "name",
"fieldtype": "Link",
"label": _("Stock Ledger Entry"),
"options": "Stock Ledger Entry",
},
{
"fieldname": "item_code",
"fieldtype": "Link",
"label": _("Item Code"),
"options": "Item",
},
{
"fieldname": "warehouse",
"fieldtype": "Link",
"label": _("Warehouse"),
"options": "Warehouse",
},
{
"fieldname": "posting_date",
"fieldtype": "Data",
"label": _("Posting Date"),
},
{
"fieldname": "posting_time",
"fieldtype": "Data",
"label": _("Posting Time"),
},
{
"fieldname": "creation",
"fieldtype": "Data",
"label": _("Creation"),
},
{
"fieldname": "voucher_type",
"fieldtype": "Link",
"label": _("Voucher Type"),
"options": "DocType",
},
{
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"label": _("Voucher No"),
"options": "voucher_type",
},
{
"fieldname": "batch_no",
"fieldtype": "Link",
"label": _("Batch"),
"options": "Batch",
},
{
"fieldname": "use_batchwise_valuation",
"fieldtype": "Check",
"label": _("Batchwise Valuation"),
},
{
"fieldname": "actual_qty",
"fieldtype": "Float",
"label": _("Qty Change"),
},
{
"fieldname": "qty_after_transaction",
"fieldtype": "Float",
"label": _("(A) Qty After Transaction"),
},
{
"fieldname": "stock_queue",
"fieldtype": "Data",
"label": _("FIFO/LIFO Queue"),
},
{
"fieldname": "fifo_queue_qty",
"fieldtype": "Float",
"label": _("(C) Total qty in queue"),
},
{
"fieldname": "fifo_qty_diff",
"fieldtype": "Float",
"label": _("A - C"),
},
{
"fieldname": "stock_value",
"fieldtype": "Float",
"label": _("(D) Balance Stock Value"),
},
{
"fieldname": "fifo_stock_value",
"fieldtype": "Float",
"label": _("(E) Balance Stock Value in Queue"),
},
{
"fieldname": "fifo_value_diff",
"fieldtype": "Float",
"label": _("D - E"),
},
{
"fieldname": "valuation_rate",
"fieldtype": "Float",
"label": _("(H) Valuation Rate"),
},
]
2 changes: 2 additions & 0 deletions erpnext/stock/report/test_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
("Batch Item Expiry Status", {}),
("Stock Ageing", {"range1": 30, "range2": 60, "range3": 90, "_optional": True}),
("Stock Ledger Invariant Check", {"warehouse": "_Test Warehouse - _TC", "item": "_Test Item"}),
("FIFO Queue vs Qty After Transaction Comparison", {"warehouse": "_Test Warehouse - _TC"}),
("FIFO Queue vs Qty After Transaction Comparison", {"item_group": "All Item Groups"}),
]

OPTIONAL_FILTERS = {
Expand Down