bd4bdca6ad
* 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
186 lines
4.6 KiB
Python
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
|
|
|
|
|
|
|
|
|