brotherton-erpnext/erpnext/stock/report/stock_analytics/stock_analytics.py
Nabin Hait bd4bdca6ad
feat: Repost item costing (#24183)
* Repost item valuation (#24031)

* feat: Reposting logic for future finished/transferred item

* feat: added fields to identify needs to recalculate rate while reposting

* refactor: Set rate for outgoing and finished items

* refactor: Arranged fields in Stock Entry item table and added fields to identify finished and scrap item

* refactor: Arranged fields in Stock Entry item table and added fields to identify finished and scrap item

* refactor: Get outgoing rate for purchase return

* refactor: Get incoming rate for sales return

* test: Added tests for reposting valuation of transferred/finished/returned items

* feat: added incoming rate field in DN, SI and Packed Item table

* feat: get incoming rate for returned item

* fix: no error while getting valuation rate in stock entry

* fix: update stock ledger for DN and SI

* feat: update item valuation rate in PR and PI based on supplied items cost

* feat: SLE reposting logic for sales return and subcontracted item with test cases

* feat: update qty in future sle

* feat: repost future sle and gle via Repost Item Valuation

* fix: Skip unwanted function calling while reposting

* fix: repost sle for specific item and warehouse

* test: Modified tests for backdated stock reco

* fix: ignore cancelled sle in few methods

* feat: role allowed to do backdated entry

* feat: Show reposting status on stock valuation related reports

* fix: minor fixes

* fix: fixed sider issues

* fix: serial no fix related to immutable ledger

* fix: Test cases fixes related to perpetual inventory

* fix: Test cases fixed

* fix: Fixed reposting on cancel and test cases

* feat: Restart reposting item valuation

* refactor: Code cleanup using small functions and test case fixes

* fix: minor fixes

* fix: Raise on error while reposting item valuation

* fix: minor fix

* fix: Tests fixed

* fix: skip some validation ig gle made from reposting

* fix: test fixes

* fix: debugging stock and account validation

* fix: debugging stock and account validation

* fix: debugging travis for stock and account sync validation

* fix: debugging travis

* fix: debugging travis

* fix: debugging travis

* fix: removed duplicate field from pos profile
2020-12-21 20:03:59 +05:30

186 lines
4.6 KiB
Python

# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _, scrub
from frappe.utils import getdate, flt
from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details)
from erpnext.accounts.utils import get_fiscal_year
from erpnext.stock.utils import is_reposting_item_valuation_in_progress
from six import iteritems
def execute(filters=None):
is_reposting_item_valuation_in_progress()
filters = frappe._dict(filters or {})
columns = get_columns(filters)
data = get_data(filters)
chart = get_chart_data(columns)
return columns, data, None, chart
def get_columns(filters):
columns = [
{
"label": _("Item"),
"options":"Item",
"fieldname": "name",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Item Name"),
"options":"Item",
"fieldname": "item_name",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Item Group"),
"options":"Item Group",
"fieldname": "item_group",
"fieldtype": "Link",
"width": 140
},
{
"label": _("Brand"),
"fieldname": "brand",
"fieldtype": "Data",
"width": 120
},
{
"label": _("UOM"),
"fieldname": "uom",
"fieldtype": "Data",
"width": 120
}]
ranges = get_period_date_ranges(filters)
for dummy, end_date in ranges:
period = get_period(end_date, filters)
columns.append({
"label": _(period),
"fieldname":scrub(period),
"fieldtype": "Float",
"width": 120
})
return columns
def get_period_date_ranges(filters):
from dateutil.relativedelta import relativedelta
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
increment = {
"Monthly": 1,
"Quarterly": 3,
"Half-Yearly": 6,
"Yearly": 12
}.get(filters.range,1)
periodic_daterange = []
for dummy in range(1, 53, increment):
if filters.range == "Weekly":
period_end_date = from_date + relativedelta(days=6)
else:
period_end_date = from_date + relativedelta(months=increment, days=-1)
if period_end_date > to_date:
period_end_date = to_date
periodic_daterange.append([from_date, period_end_date])
from_date = period_end_date + relativedelta(days=1)
if period_end_date == to_date:
break
return periodic_daterange
def get_period(posting_date, filters):
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
if filters.range == 'Weekly':
period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year)
elif filters.range == 'Monthly':
period = str(months[posting_date.month - 1]) + " " + str(posting_date.year)
elif filters.range == 'Quarterly':
period = "Quarter " + str(((posting_date.month-1)//3)+1) +" " + str(posting_date.year)
else:
year = get_fiscal_year(posting_date, company=filters.company)
period = str(year[2])
return period
def get_periodic_data(entry, filters):
periodic_data = {}
for d in entry:
period = get_period(d.posting_date, filters)
bal_qty = 0
if d.voucher_type == "Stock Reconciliation":
if periodic_data.get(d.item_code):
bal_qty = periodic_data[d.item_code]["balance"]
qty_diff = d.qty_after_transaction - bal_qty
else:
qty_diff = d.actual_qty
if filters["value_quantity"] == 'Quantity':
value = qty_diff
else:
value = d.stock_value_difference
periodic_data.setdefault(d.item_code, {}).setdefault(period, 0.0)
periodic_data.setdefault(d.item_code, {}).setdefault("balance", 0.0)
periodic_data[d.item_code]["balance"] += value
periodic_data[d.item_code][period] = periodic_data[d.item_code]["balance"]
return periodic_data
def get_data(filters):
data = []
items = get_items(filters)
sle = get_stock_ledger_entries(filters, items)
item_details = get_item_details(items, sle, filters)
periodic_data = get_periodic_data(sle, filters)
ranges = get_period_date_ranges(filters)
for dummy, item_data in iteritems(item_details):
row = {
"name": item_data.name,
"item_name": item_data.item_name,
"item_group": item_data.item_group,
"uom": item_data.stock_uom,
"brand": item_data.brand,
}
total = 0
for dummy, end_date in ranges:
period = get_period(end_date, filters)
amount = flt(periodic_data.get(item_data.name, {}).get(period))
row[scrub(period)] = amount
total += amount
row["total"] = total
data.append(row)
return data
def get_chart_data(columns):
labels = [d.get("label") for d in columns[5:]]
chart = {
"data": {
'labels': labels,
'datasets':[]
}
}
chart["type"] = "line"
return chart