mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-27 13:55:19 +00:00
chore: resolve conflict
This commit is contained in:
@@ -3,24 +3,13 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.tests.classes.context_managers import freeze_time
|
from frappe.tests.classes.context_managers import freeze_time
|
||||||
from frappe.utils import add_days, flt, formatdate, today
|
from frappe.utils import add_days, formatdate, today
|
||||||
|
|
||||||
from erpnext.accounts.doctype.tax_rule.test_tax_rule import make_tax_rule
|
|
||||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
|
||||||
from erpnext.manufacturing.report.material_requirements_planning_report.material_requirements_planning_report import (
|
from erpnext.manufacturing.report.material_requirements_planning_report.material_requirements_planning_report import (
|
||||||
MaterialRequirementsPlanningReport,
|
MaterialRequirementsPlanningReport,
|
||||||
execute,
|
|
||||||
get_item_lead_time,
|
|
||||||
make_order,
|
|
||||||
)
|
)
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
|
||||||
from erpnext.tests.utils import ERPNextTestSuite
|
from erpnext.tests.utils import ERPNextTestSuite
|
||||||
|
|
||||||
COMPANY = "_Test Company"
|
|
||||||
WAREHOUSE = "_Test Warehouse - _TC"
|
|
||||||
SUPPLIER = "_Test Supplier"
|
|
||||||
TAX_TEMPLATE = "_Test Purchase Taxes and Charges Template - _TC"
|
|
||||||
|
|
||||||
|
|
||||||
class TestMaterialRequirementsPlanningReport(ERPNextTestSuite):
|
class TestMaterialRequirementsPlanningReport(ERPNextTestSuite):
|
||||||
def test_detailed_chart_includes_full_date_range(self):
|
def test_detailed_chart_includes_full_date_range(self):
|
||||||
@@ -70,67 +59,6 @@ class TestMaterialRequirementsPlanningReport(ERPNextTestSuite):
|
|||||||
[formatdate(delivery_date, "dd MMM") for delivery_date in delivery_dates],
|
[formatdate(delivery_date, "dd MMM") for delivery_date in delivery_dates],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_manufacture_lead_time_is_not_int_truncated(self):
|
|
||||||
"""lead_time = 1440 / manufacturing_time_in_mins + buffer_time. Both columns are Int;
|
|
||||||
integer/integer division truncates on Postgres (1440/7 -> 205) while MariaDB yields a
|
|
||||||
decimal, so the computed lead time (and the derived release date) diverged by engine."""
|
|
||||||
item = make_item("_Test MRP Lead Time Item", {"is_stock_item": 1}).name
|
|
||||||
frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Item Lead Time",
|
|
||||||
"item_code": item,
|
|
||||||
"manufacturing_time_in_mins": 7,
|
|
||||||
"buffer_time": 2,
|
|
||||||
}
|
|
||||||
).insert()
|
|
||||||
|
|
||||||
lead_time = get_item_lead_time(item, "Manufacture")
|
|
||||||
# 1440 / 7 + 2 = 207.714...; a truncating integer division on Postgres would give 207.
|
|
||||||
self.assertAlmostEqual(float(lead_time), 1440 / 7 + 2, places=2)
|
|
||||||
|
|
||||||
def test_make_order_creates_draft_purchase_and_work_orders(self):
|
|
||||||
plan = make_mrp_plan(self)
|
|
||||||
|
|
||||||
make_order(plan.rows, COMPANY, warehouse=WAREHOUSE, mps=plan.mps)
|
|
||||||
|
|
||||||
purchase_order = get_created_order(plan.mps, "Purchase Order")
|
|
||||||
self.assertEqual(purchase_order.docstatus, 0)
|
|
||||||
self.assertEqual(purchase_order.supplier, SUPPLIER)
|
|
||||||
self.assertEqual([d.item_code for d in purchase_order.items], [plan.rm_item])
|
|
||||||
self.assertEqual(purchase_order.items[0].qty, plan.planned_qty * plan.rm_qty)
|
|
||||||
|
|
||||||
work_order = get_created_order(plan.mps, "Work Order")
|
|
||||||
self.assertEqual(work_order.docstatus, 0)
|
|
||||||
self.assertEqual(work_order.production_item, plan.fg_item)
|
|
||||||
self.assertEqual(work_order.bom_no, plan.bom)
|
|
||||||
self.assertEqual(work_order.qty, plan.planned_qty)
|
|
||||||
|
|
||||||
def test_purchase_order_gets_defaults_from_set_missing_values(self):
|
|
||||||
plan = make_mrp_plan(self)
|
|
||||||
make_tax_rule(tax_type="Purchase", purchase_tax_template=TAX_TEMPLATE, priority=1, save=1)
|
|
||||||
frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Item Price",
|
|
||||||
"item_code": plan.rm_item,
|
|
||||||
"price_list": "Standard Buying",
|
|
||||||
"price_list_rate": 100,
|
|
||||||
}
|
|
||||||
).insert()
|
|
||||||
|
|
||||||
make_order(plan.rows, COMPANY, warehouse=WAREHOUSE, mps=plan.mps)
|
|
||||||
|
|
||||||
purchase_order = get_created_order(plan.mps, "Purchase Order")
|
|
||||||
self.assertEqual(purchase_order.buying_price_list, "Standard Buying")
|
|
||||||
self.assertEqual(purchase_order.items[0].rate, 100)
|
|
||||||
template = frappe.get_doc("Purchase Taxes and Charges Template", TAX_TEMPLATE)
|
|
||||||
self.assertEqual(purchase_order.taxes_and_charges, TAX_TEMPLATE)
|
|
||||||
self.assertEqual([d.rate for d in purchase_order.taxes], [d.rate for d in template.taxes])
|
|
||||||
|
|
||||||
net_total = flt(purchase_order.net_total)
|
|
||||||
self.assertEqual(
|
|
||||||
purchase_order.grand_total, net_total + net_total * flt(template.taxes[0].rate) / 100
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def make_chart_row(delivery_date, planned_qty=1):
|
def make_chart_row(delivery_date, planned_qty=1):
|
||||||
return frappe._dict(
|
return frappe._dict(
|
||||||
@@ -142,83 +70,3 @@ def make_chart_row(delivery_date, planned_qty=1):
|
|||||||
"wo_ordered_qty": 0,
|
"wo_ordered_qty": 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def make_mrp_plan(test_case, planned_qty=10, rm_qty=2):
|
|
||||||
"""Build a finished good with a submitted BOM and an MPS demanding it, then return the
|
|
||||||
report's own output rows -- the same payload the report's client sends to `make_order`."""
|
|
||||||
rm_item = make_item(
|
|
||||||
properties={
|
|
||||||
"is_stock_item": 1,
|
|
||||||
"is_purchase_item": 1,
|
|
||||||
"item_defaults": [
|
|
||||||
{"company": COMPANY, "default_warehouse": WAREHOUSE, "default_supplier": SUPPLIER}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
).name
|
|
||||||
fg_item = make_item(
|
|
||||||
properties={
|
|
||||||
"is_stock_item": 1,
|
|
||||||
"item_defaults": [{"company": COMPANY, "default_warehouse": WAREHOUSE}],
|
|
||||||
}
|
|
||||||
).name
|
|
||||||
|
|
||||||
# on_submit sets Item.default_bom, which is how the report finds the raw materials
|
|
||||||
bom = make_bom(item=fg_item, raw_materials=[rm_item], rm_qty=rm_qty, rate=100).name
|
|
||||||
|
|
||||||
mps = frappe.get_doc(
|
|
||||||
{
|
|
||||||
"doctype": "Master Production Schedule",
|
|
||||||
"company": COMPANY,
|
|
||||||
"posting_date": today(),
|
|
||||||
"from_date": today(),
|
|
||||||
"parent_warehouse": WAREHOUSE,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"item_code": fg_item,
|
|
||||||
"warehouse": WAREHOUSE,
|
|
||||||
"delivery_date": add_days(today(), 30),
|
|
||||||
"planned_qty": planned_qty,
|
|
||||||
"uom": frappe.get_cached_value("Item", fg_item, "stock_uom"),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# left in draft: on_submit enqueues MRP Log creation in a background job
|
|
||||||
mps.insert()
|
|
||||||
|
|
||||||
_, data, _, _ = execute(
|
|
||||||
frappe._dict(
|
|
||||||
{
|
|
||||||
"company": COMPANY,
|
|
||||||
"from_date": today(),
|
|
||||||
"to_date": add_days(today(), 90),
|
|
||||||
"warehouse": WAREHOUSE,
|
|
||||||
"mps": mps.name,
|
|
||||||
"type_of_material": "All",
|
|
||||||
"add_safety_stock": 0,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# the report separates each finished good with a blank row
|
|
||||||
rows = [row for row in data if row.get("item_code")]
|
|
||||||
test_case.assertTrue(rows, msg="the report returned no rows to create orders from")
|
|
||||||
|
|
||||||
return frappe._dict(
|
|
||||||
rm_item=rm_item,
|
|
||||||
fg_item=fg_item,
|
|
||||||
bom=bom,
|
|
||||||
mps=mps.name,
|
|
||||||
planned_qty=planned_qty,
|
|
||||||
rm_qty=rm_qty,
|
|
||||||
rows=rows,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_created_order(mps, doctype):
|
|
||||||
names = frappe.get_all(doctype, filters={"mps": mps}, pluck="name")
|
|
||||||
if len(names) != 1:
|
|
||||||
frappe.throw(f"Expected exactly one {doctype} for {mps}, got {names}")
|
|
||||||
|
|
||||||
return frappe.get_doc(doctype, names[0])
|
|
||||||
|
|||||||
Reference in New Issue
Block a user