Skip to content

Commit

Permalink
Port HU tests / staging to V15 (#118)
Browse files Browse the repository at this point in the history
Co-authored-by: ViralKansodiya-Fosserp <141210323+viralkansodiya@users.noreply.github.com>
Co-authored-by: viralpatel15 <viralkansodiya167@gmail.com>
Co-authored-by: Heather Kusmierz <heather.kusmierz@gmail.com>
  • Loading branch information
4 people authored May 13, 2024
1 parent 41ddda6 commit 7fd6960
Show file tree
Hide file tree
Showing 10 changed files with 530 additions and 233 deletions.
1 change: 1 addition & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- version-15
- version-14
pull_request:
branches:
- version-15
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,6 @@ cypress/videos

# JetBrains IDEs
.idea/

beam/www/beam/index.js
beam/www/beam/style.css
70 changes: 70 additions & 0 deletions beam/beam/custom/item.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"custom_fields": [
{
"_assign": null,
"_comments": null,
"_liked_by": null,
"_user_tags": null,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": null,
"columns": 0,
"creation": "2024-02-26 23:52:53.051024",
"default": "1",
"depends_on": null,
"description": null,
"docstatus": 0,
"dt": "Item",
"fetch_from": null,
"fetch_if_empty": 0,
"fieldname": "enable_handling_unit",
"fieldtype": "Check",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 11,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "has_variants",
"is_system_generated": 0,
"is_virtual": 0,
"label": "Enable Handling Unit",
"length": 0,
"mandatory_depends_on": null,
"modified": "2024-02-26 23:52:53.051024",
"modified_by": "Administrator",
"module": "BEAM",
"name": "Item-enable_handling_unit",
"no_copy": 0,
"non_negative": 0,
"options": null,
"owner": "Administrator",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"print_width": null,
"read_only": 0,
"read_only_depends_on": null,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"sort_options": 0,
"translatable": 0,
"unique": 0,
"width": null
}
],
"custom_perms": [],
"doctype": "Item",
"links": [],
"property_setters": [],
"sync_on_migrate": 1
}
31 changes: 17 additions & 14 deletions beam/beam/handling_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ def generate_handling_units(doc, method=None):
return doc

for row in doc.items:
if not frappe.get_value("Item", row.item_code, "is_stock_item"):
is_stock_item, enable_handling_unit = frappe.get_value(
"Item", row.item_code, ["is_stock_item", "enable_handling_unit"]
)
if not (is_stock_item and enable_handling_unit):
continue

if (
Expand All @@ -44,6 +47,11 @@ def generate_handling_units(doc, method=None):
row.to_handling_unit = handling_unit.name
continue

if doc.doctype == "Subcontracting Receipt" and not row.handling_unit:
handling_unit = frappe.new_doc("Handling Unit")
handling_unit.save()
row.handling_unit = handling_unit.name

if doc.doctype == "Stock Entry" and doc.purpose == "Manufacture" and row.is_scrap_item:
create_handling_unit = frappe.get_value(
"BOM Scrap Item", {"item_code": row.item_code, "parent": doc.bom_no}, "create_handling_unit"
Expand Down Expand Up @@ -90,7 +98,7 @@ def validate_handling_unit_overconsumption(doc, method=None):
if doc.doctype == "Stock Entry" and doc.purpose == "Material Receipt":
return doc

qty_field = "actual_qty" if doc.doctype == "Stock Entry" else "stock_qty"
qty_field = "transfer_qty" if doc.doctype == "Stock Entry" else "stock_qty"

for row in doc.get("items"):
error = False
Expand All @@ -104,23 +112,18 @@ def validate_handling_unit_overconsumption(doc, method=None):
precision_denominator = 1 / pow(100, frappe.get_precision(row.doctype, qty_field))

if doc.doctype == "Stock Entry":
# incoming
if row.get("s_warehouse") and not row.get("t_warehouse"):
if abs(row.get(qty_field) - hu.stock_qty) != 0.0 and (
(row.get(qty_field) - hu.stock_qty) < precision_denominator
):
error = True
# outgoing
elif row.get("t_warehouse") and not row.get("s_warehouse"):
if row.get("t_warehouse") and not row.get("s_warehouse"):
if (
abs(hu.stock_qty - row.get(qty_field)) != 0.0
abs(hu.stock_qty - row.get(qty_field)) > 0.0
and (hu.stock_qty - row.get(qty_field) > precision_denominator)
and not row.is_scrap_item
):
error = True
else: # transfer / same warehouse
if abs(hu.stock_qty - row.get(qty_field)) != 0.0 and (
hu.stock_qty - row.get(qty_field) < precision_denominator
else: # incoming and transfer / same warehouse
if (
abs(hu.stock_qty - row.get(qty_field)) > 0.0
and hu.stock_qty - row.get(qty_field) < precision_denominator
):
error = True

Expand All @@ -133,7 +136,7 @@ def validate_handling_unit_overconsumption(doc, method=None):
if error == True:
frappe.throw(
frappe._(
f"Row #{row.idx}: Handling Unit for {row.item_code} cannot be more than {hu.stock_qty} {hu.stock_uom}. You have {row.get(qty_field)} {row.stock_uom}"
f"Row #{row.idx}: Handling Unit for {row.item_code} cannot be more than {hu.stock_qty:.1f} {hu.stock_uom}. You have {row.get(qty_field):.1f} {row.stock_uom}"
),
NegativeStockError,
title=frappe._("Insufficient Stock"),
Expand Down
234 changes: 128 additions & 106 deletions beam/beam/overrides/stock_entry.py
Original file line number Diff line number Diff line change
@@ -1,106 +1,128 @@
import copy

import frappe
from erpnext.stock.doctype.stock_entry.stock_entry import StockEntry
from erpnext.stock.doctype.stock_entry_detail.stock_entry_detail import StockEntryDetail
from frappe.utils import cstr, flt
from typing_extensions import Self

from beam.beam.doctype.beam_settings.beam_settings import create_beam_settings


class BEAMStockEntry(StockEntry):
def update_stock_ledger(self):
settings = (
create_beam_settings(self.company)
if not frappe.db.exists("BEAM Settings", {"company": self.company})
else frappe.get_doc("BEAM Settings", {"company": self.company})
)
sl_entries = []
finished_item_row = self.get_finished_item_row()
self.get_sle_for_source_warehouse(sl_entries, finished_item_row)
self.get_sle_for_target_warehouse(sl_entries, finished_item_row)
if self.docstatus == 2:
sl_entries.reverse()
self.make_sl_entries(sl_entries)

if self.docstatus == 2 and settings.enable_handling_units:
hu_sles = self.make_handling_unit_sles()
self.make_sl_entries(hu_sles)

def make_handling_unit_sles(self):
hu_sles = []
for d in self.get("items"):
if self.docstatus == 2 and not d.recombine_on_cancel and d.handling_unit and d.to_handling_unit:
sle = self.get_sl_entries(
d,
{
"warehouse": cstr(d.s_warehouse),
"actual_qty": -flt(d.transfer_qty),
"incoming_rate": flt(d.valuation_rate),
},
)
sle["handling_unit"] = d.handling_unit
sle["is_cancelled"] = 0
hu_sles.append(sle)
_sle = self.get_sl_entries(
d,
{
"warehouse": cstr(d.t_warehouse),
"actual_qty": flt(d.transfer_qty),
"incoming_rate": flt(d.valuation_rate),
},
)
_sle["handling_unit"] = d.to_handling_unit
_sle["is_cancelled"] = 0
hu_sles.append(_sle)
return hu_sles


@frappe.whitelist()
def set_rows_to_recombine(docname: str, to_recombine=None) -> None:
doc = frappe.get_doc("Stock Entry", docname)
settings = (
create_beam_settings(doc.company)
if not frappe.db.exists("BEAM Settings", {"company": doc.company})
else frappe.get_doc("BEAM Settings", {"company": doc.company})
)
if not settings.enable_handling_units:
return
if not to_recombine:
return
for row in doc.items:
if row.name in to_recombine:
row.db_set("recombine_on_cancel", True)
return


@frappe.whitelist()
@frappe.read_only()
def get_handling_units_for_item_code(doctype, txt, searchfield, start, page_len, filters):
StockLedgerEntry = frappe.qb.DocType("Stock Ledger Entry")
return (
frappe.qb.from_(StockLedgerEntry)
.select(StockLedgerEntry.handling_unit)
.where(
(StockLedgerEntry.item_code == filters.get("item_code"))
& (StockLedgerEntry.handling_unit != "")
)
.orderby(StockLedgerEntry.posting_date, order=frappe.qb.desc)
.groupby(StockLedgerEntry.handling_unit)
.run(as_dict=False)
)


@frappe.whitelist()
@frappe.read_only()
def get_handling_unit_qty(voucher_no, handling_unit, warehouse):
return frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_no": voucher_no,
"handling_unit": handling_unit,
"warehouse": warehouse,
},
["qty_after_transaction"],
)
import copy

import frappe
from erpnext.stock.doctype.stock_entry.stock_entry import StockEntry
from erpnext.stock.doctype.stock_entry_detail.stock_entry_detail import StockEntryDetail
from frappe.utils import cstr, flt
from typing_extensions import Self

from beam.beam.doctype.beam_settings.beam_settings import create_beam_settings


class BEAMStockEntry(StockEntry):
def update_stock_ledger(self):
settings = (
create_beam_settings(self.company)
if not frappe.db.exists("BEAM Settings", {"company": self.company})
else frappe.get_doc("BEAM Settings", {"company": self.company})
)
sl_entries = []
finished_item_row = self.get_finished_item_row()
self.get_sle_for_source_warehouse(sl_entries, finished_item_row)
self.get_sle_for_target_warehouse(sl_entries, finished_item_row)
if self.docstatus == 2:
sl_entries.reverse()
self.make_sl_entries(sl_entries)

if self.docstatus == 2 and settings.enable_handling_units:
hu_sles = self.make_handling_unit_sles()
self.make_sl_entries(hu_sles)

def make_handling_unit_sles(self):
hu_sles = []
for d in self.get("items"):
if self.docstatus == 2 and not d.recombine_on_cancel and d.handling_unit and d.to_handling_unit:
sle = self.get_sl_entries(
d,
{
"warehouse": cstr(d.s_warehouse),
"actual_qty": -flt(d.transfer_qty),
"incoming_rate": flt(d.valuation_rate),
},
)
sle["handling_unit"] = d.handling_unit
sle["is_cancelled"] = 0
hu_sles.append(sle)
_sle = self.get_sl_entries(
d,
{
"warehouse": cstr(d.t_warehouse),
"actual_qty": flt(d.transfer_qty),
"incoming_rate": flt(d.valuation_rate),
},
)
_sle["handling_unit"] = d.to_handling_unit
_sle["is_cancelled"] = 0
hu_sles.append(_sle)
return hu_sles


@frappe.whitelist()
def set_rows_to_recombine(docname: str, to_recombine=None) -> None:
doc = frappe.get_doc("Stock Entry", docname)
settings = (
create_beam_settings(doc.company)
if not frappe.db.exists("BEAM Settings", {"company": doc.company})
else frappe.get_doc("BEAM Settings", {"company": doc.company})
)
if not settings.enable_handling_units:
return
if not to_recombine:
return
for row in doc.items:
if row.name in to_recombine:
row.db_set("recombine_on_cancel", True)
return


@frappe.whitelist()
@frappe.read_only()
def get_handling_units_for_item_code(doctype, txt, searchfield, start, page_len, filters):
StockLedgerEntry = frappe.qb.DocType("Stock Ledger Entry")
return (
frappe.qb.from_(StockLedgerEntry)
.select(StockLedgerEntry.handling_unit)
.where(
(StockLedgerEntry.item_code == filters.get("item_code"))
& (StockLedgerEntry.handling_unit != "")
)
.orderby(StockLedgerEntry.posting_date, order=frappe.qb.desc)
.groupby(StockLedgerEntry.handling_unit)
.run(as_dict=False)
)


@frappe.whitelist()
@frappe.read_only()
def get_handling_unit_qty(voucher_no, handling_unit, warehouse):
return frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_no": voucher_no,
"handling_unit": handling_unit,
"warehouse": warehouse,
},
["qty_after_transaction"],
)


# This function validates stock entry items to prevent missing handling units.
def validate_items_with_handling_unit(doc, method=None):
if doc.stock_entry_type != "Material Receipt":
for row in doc.items:
if not frappe.get_value("Item", row.item_code, "enable_handling_unit"):
continue
elif row.is_scrap_item and not frappe.get_value(
"BOM Scrap Item",
{"item_code": row.item_code, "parent": doc.get("bom_no")},
"create_handling_unit",
):
continue
elif (
doc.stock_entry_type in ["Repack", "Manufacture"]
and not (row.t_warehouse or row.is_finished_item or row.is_scrap_item)
and not row.handling_unit
):
frappe.throw(frappe._(f"Row #{row.idx}: Handling Unit is missing for item {row.item_code}"))
elif not row.handling_unit:
frappe.throw(frappe._(f"Row #{row.idx}: Handling Unit is missing for item {row.item_code}"))
Loading

0 comments on commit 7fd6960

Please sign in to comment.