Skip to content

Conversation

@mihir-kandoi
Copy link
Contributor

@mihir-kandoi mihir-kandoi commented Nov 5, 2025

closes #48472

  • Add settings to BOM DocType
  • Change explosion logic on applicable DocTypes
  • Change explosion logic on applicable reports
  • Add support in BOM creator
  • Test cases
  • Documentation

https://docs.frappe.io/erpnext/user/manual/en/bill-of-materials

@mihir-kandoi mihir-kandoi marked this pull request as ready for review November 6, 2025 10:03
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 6, 2025

📝 Walkthrough

Walkthrough

This PR adds native Phantom BOM support across manufacturing: introduces is_phantom_bom on BOM and is_phantom_item on BOM items and propagates these flags through BOM creation, BOM configurator UI, BOM explorer, production planning, work orders, subcontracting material resolution, reports, and tests. Phantom BOMs/items are recursively exploded to raw materials (multi-level explosion) and phantom entries are removed from top-level results. Several doctype JSONs, backend queries, JS UI handlers, and tests were added or updated to carry and honor phantom semantics.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • Recursive phantom explosion and de-duplication logic in subcontracting controller (__get_materials_from_bom) and report processors (explode_phantom_boms).
    • BOM model and item flows in bom.py, bom_item.py, bom_creator.py (flag propagation, quantity math, do_not_explode interactions).
    • Production planning and work order code paths: get_sub_assembly_items, get_subitems, and callers — ensure correct filtering and quantity aggregation.
    • JS surface: BOM configurator bundle, BOM form (bom.js), routing JS — ensure dialog/filter behavior and server payloads consistently include phantom flags.
    • DocType JSON changes (bom.json, bom_item.json, bom_creator_item.json) — verify depends_on, read-only rules, and field ordering for UI parity.
    • Tests: new/modified tests and duplicated helper definitions in multiple test files (verify no accidental duplication or conflicting fixtures).

Possibly related PRs

Suggested labels

needs-tests

Suggested reviewers

  • rohitwaghchaure
  • ruthra-kumar

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.22% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: phantom bom' is concise and directly indicates the main feature being added. It matches the core functionality described in the PR objectives and linked issues.
Description check ✅ Passed The description references the linked issue #48472 and lists completed checklist items covering BOM DocType settings, explosion logic changes, reports, BOM creator support, tests, and documentation.
Linked Issues check ✅ Passed The PR implements all core requirements from issue #48472: native Phantom BOM support with multi-level explosion, phantom item handling in Work Orders, BOM updates, and test coverage across affected DocTypes and reports.
Out of Scope Changes check ✅ Passed All code changes are directly scoped to implementing Phantom BOM functionality. Changes include BOM fields, explosion logic in production planning/work orders, test cases, UI updates, and related reports—all aligned with the linked issue objectives.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4cdfb2c and d8948d8.

📒 Files selected for processing (1)
  • erpnext/manufacturing/doctype/bom/bom_item_preview.html (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Patch Test
  • GitHub Check: Python Unit Tests (1)
  • GitHub Check: Python Unit Tests (4)
  • GitHub Check: Python Unit Tests (3)
  • GitHub Check: Python Unit Tests (2)
  • GitHub Check: Summary
🔇 Additional comments (1)
erpnext/manufacturing/doctype/bom/bom_item_preview.html (1)

15-18: LGTM!

The phantom item indicator is cleanly implemented using proper Jinja2 syntax and i18n. The label placement before the description is logical, and undefined variable handling via Jinja2 defaults is safe.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
erpnext/manufacturing/doctype/bom_creator/bom_creator.py (1)

333-338: Scrap allowance flips off for every non-phantom row.

item.get("is_phantom_item") frequently arrives as the string "0", which is truthy in Python. not "0" therefore evaluates to False, so every normal component ends up with allow_scrap_items = 0. Cast the flag first so only actual phantom rows disable scrap.

 			item_args.update(
 				{
 					"bom_no": bom_no,
 					"allow_alternative_item": 1,
-					"allow_scrap_items": not item.get("is_phantom_item"),
+					"allow_scrap_items": not cint(item.get("is_phantom_item")),
 					"include_item_in_manufacturing": 1,
 				}
 			)
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc2cf5 and bdd297a.

📒 Files selected for processing (20)
  • erpnext/controllers/subcontracting_controller.py (4 hunks)
  • erpnext/controllers/tests/test_subcontracting_controller.py (4 hunks)
  • erpnext/manufacturing/doctype/bom/bom.js (2 hunks)
  • erpnext/manufacturing/doctype/bom/bom.json (12 hunks)
  • erpnext/manufacturing/doctype/bom/bom.py (11 hunks)
  • erpnext/manufacturing/doctype/bom/test_bom.py (4 hunks)
  • erpnext/manufacturing/doctype/bom_creator/bom_creator.py (5 hunks)
  • erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json (4 hunks)
  • erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py (1 hunks)
  • erpnext/manufacturing/doctype/bom_item/bom_item.json (5 hunks)
  • erpnext/manufacturing/doctype/bom_item/bom_item.py (1 hunks)
  • erpnext/manufacturing/doctype/production_plan/production_plan.py (1 hunks)
  • erpnext/manufacturing/doctype/production_plan/test_production_plan.py (1 hunks)
  • erpnext/manufacturing/doctype/routing/routing.js (1 hunks)
  • erpnext/manufacturing/doctype/work_order/test_work_order.py (1 hunks)
  • erpnext/manufacturing/report/bom_explorer/bom_explorer.py (3 hunks)
  • erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py (3 hunks)
  • erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py (2 hunks)
  • erpnext/manufacturing/report/bom_stock_report/test_bom_stock_report.py (1 hunks)
  • erpnext/public/js/bom_configurator/bom_configurator.bundle.js (9 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-30T11:04:46.510Z
Learnt from: rohitwaghchaure
Repo: frappe/erpnext PR: 49766
File: erpnext/manufacturing/doctype/production_plan/production_plan.py:1717-1717
Timestamp: 2025-09-30T11:04:46.510Z
Learning: In the Production Plan's `get_items_for_material_requests` function in `erpnext/manufacturing/doctype/production_plan/production_plan.py`, always use `data.get("sales_order")` instead of `doc.get("sales_order")` when iterating over `po_items`. This ensures raw materials are correctly grouped by each production item's respective Sales Order, not a global document-level Sales Order.

Applied to files:

  • erpnext/manufacturing/doctype/production_plan/production_plan.py
📚 Learning: 2025-10-17T14:11:06.959Z
Learnt from: sagarvora
Repo: frappe/erpnext PR: 50155
File: erpnext/controllers/accounts_controller.py:2992-3005
Timestamp: 2025-10-17T14:11:06.959Z
Learning: In ERPNext, item child doctypes (like "Sales Invoice Item", "Delivery Note Item", etc.) have exactly one non-custom Link field that references their parent transaction doctype (like "Sales Order", "Purchase Order", etc.). This is a schema design pattern that can be relied upon when determining reference fields for mapping logic.

Applied to files:

  • erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py
  • erpnext/manufacturing/doctype/bom_item/bom_item.json
📚 Learning: 2025-08-01T11:04:59.343Z
Learnt from: karm1000
Repo: frappe/erpnext PR: 48865
File: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json:1657-1664
Timestamp: 2025-08-01T11:04:59.343Z
Learning: In ERPNext/Frappe framework, virtual fields (marked with "is_virtual": 1) are always read-only by default and do not require an explicit "read_only": 1 property in their JSON definition.

Applied to files:

  • erpnext/manufacturing/doctype/bom/bom.json
  • erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json
🧬 Code graph analysis (6)
erpnext/manufacturing/doctype/production_plan/test_production_plan.py (2)
erpnext/manufacturing/doctype/bom/test_bom.py (1)
  • create_tree_for_phantom_bom_tests (914-923)
erpnext/manufacturing/doctype/production_plan/production_plan.py (3)
  • get_sub_assembly_items (999-1066)
  • get_sub_assembly_items (1840-1911)
  • get_items_for_material_requests (1583-1771)
erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py (1)
erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py (1)
  • explode_phantom_boms (154-166)
erpnext/manufacturing/doctype/bom/bom.py (2)
erpnext/stock/doctype/stock_entry/stock_entry.js (6)
  • d (340-373)
  • d (394-394)
  • d (696-699)
  • d (714-714)
  • fields (620-620)
  • fields (654-685)
erpnext/manufacturing/doctype/bom/bom.js (4)
  • qty (525-525)
  • fields (97-97)
  • fields (307-307)
  • fields (979-979)
erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py (1)
erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py (1)
  • explode_phantom_boms (93-107)
erpnext/manufacturing/doctype/work_order/test_work_order.py (1)
erpnext/manufacturing/doctype/bom/test_bom.py (1)
  • create_tree_for_phantom_bom_tests (914-923)
erpnext/controllers/tests/test_subcontracting_controller.py (1)
erpnext/manufacturing/doctype/bom/test_bom.py (1)
  • create_tree_for_phantom_bom_tests (914-923)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Python Unit Tests (2)
  • GitHub Check: Python Unit Tests (3)
  • GitHub Check: Python Unit Tests (1)
  • GitHub Check: Python Unit Tests (4)
  • GitHub Check: Patch Test
  • GitHub Check: Summary
🔇 Additional comments (16)
erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py (1)

28-28: Type hints cover phantom flag nicely

Good to see the TYPE_CHECKING stub exposing is_phantom_item; it keeps our static analysis aligned with the new phantom payloads.

erpnext/manufacturing/doctype/bom_item/bom_item.py (1)

28-28: Consistent phantom typing

Matching the BOM item stub with is_phantom_item keeps downstream typing accurate—thanks for keeping these in sync.

erpnext/manufacturing/doctype/work_order/test_work_order.py (1)

3273-3279: Nice coverage for phantom work orders

Appreciate the focused fixture reuse; validating required_items against the phantom tree guards the multi-level explosion path well.

erpnext/manufacturing/doctype/production_plan/test_production_plan.py (1)

2417-2440: Production plan phantom flow well exercised

Great to see the production-plan side validated—checking both sub-assembly entries and MR item flattening should catch regressions in phantom handling.

erpnext/public/js/bom_configurator/bom_configurator.bundle.js (1)

143-189: UI wiring for phantom nodes looks solid

The phantom-aware toolbar actions and modal plumbing (including the inverted stock filter) align with backend expectations—thanks for keeping the UX consistent.

erpnext/manufacturing/doctype/bom_item/bom_item.json (2)

45-46: LGTM! Well-structured phantom item field.

The new is_phantom_item field is properly configured as read-only with appropriate default value, correctly positioned in field order after is_sub_assembly_item.

Also applies to: 318-324


85-85: Field dependencies properly enforce phantom item semantics.

The dependency logic correctly ensures:

  • Phantom items must have a BOM reference (mandatory_depends_on)
  • Phantom items cannot be sourced by supplier (hidden via depends_on)
  • do_not_explode is read-only for phantom items (enforces explosion)
  • is_sub_assembly_item is hidden to avoid semantic confusion

Also applies to: 283-283, 293-293, 311-311

erpnext/manufacturing/doctype/bom/bom.py (7)

138-138: LGTM! Type annotation follows framework conventions.


451-463: Phantom item propagation and explosion enforcement is correct.

The logic properly:

  1. Fetches is_phantom_bom from the linked BOM and propagates to the item
  2. Forces do_not_explode = 0 for phantom items to ensure multi-level explosion

This correctly implements the core phantom BOM requirement.


491-493: Phantom items correctly use BOM-based costing.

The condition ensures phantom items always derive their rate from the BOM unit cost rather than item valuation, which is correct since phantoms are logical groupings without independent inventory valuation.


894-894: Phantom item integration in cost calculation is sound.

Including is_phantom_item alongside is_stock_item in rate recalculation ensures phantom items receive proper BOM-based costing while maintaining the existing stock item logic.

Also applies to: 905-905


1284-1327: SQL modifications correctly include phantom items in BOM explosion.

The query logic ensures phantom items are included regardless of include_non_stock_items setting:

  • Line 1319: or bom_item.is_phantom_item ensures phantoms are always fetched
  • Line 1324: Necessary fields (is_phantom_item, bom_no) are selected for expansion

This enables proper multi-level explosion of phantom BOMs.


1412-1412: LGTM! Field addition enables phantom item visibility in BOM tree.


1334-1345: Recursion in phantom item expansion is protected by validation.

The recursive expansion of phantom items at lines 1334-1345 is safe because check_recursion() (called during BOM save) prevents cycles from being stored in the database. Since only acyclic BOM structures can exist, the runtime recursion cannot loop infinitely. Practical nesting depths are shallow (typically 5-10 levels), and stack limits are not a concern.

Adding a clarifying comment about this validation-based protection would improve code maintainability.

erpnext/manufacturing/doctype/bom/bom.json (2)

19-19: LGTM! Phantom BOM field properly defined.

The field is correctly positioned in the Production Item tab alongside other BOM-level configuration options.

Also applies to: 674-679


205-205: Field dependencies correctly enforce phantom BOM constraints.

The UI properly hides sections that don't apply to phantom BOMs:

  • Operations, scrap, and quality inspection (phantoms aren't physically produced)
  • Website sections (phantoms are internal logical groupings)
  • Project linkage (phantoms are components, not work order targets)

Notably, raw_material_cost remains visible, which is correct since phantom BOMs still require material cost calculation for explosion.

Also applies to: 298-298, 316-316, 331-331, 344-344, 361-361, 390-390, 438-438, 548-548, 583-583

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdd297a and 4ed177c.

📒 Files selected for processing (5)
  • erpnext/manufacturing/doctype/bom/bom.py (12 hunks)
  • erpnext/manufacturing/doctype/production_plan/production_plan.py (10 hunks)
  • erpnext/manufacturing/doctype/production_plan/test_production_plan.py (1 hunks)
  • erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py (5 hunks)
  • erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-30T11:04:46.510Z
Learnt from: rohitwaghchaure
Repo: frappe/erpnext PR: 49766
File: erpnext/manufacturing/doctype/production_plan/production_plan.py:1717-1717
Timestamp: 2025-09-30T11:04:46.510Z
Learning: In the Production Plan's `get_items_for_material_requests` function in `erpnext/manufacturing/doctype/production_plan/production_plan.py`, always use `data.get("sales_order")` instead of `doc.get("sales_order")` when iterating over `po_items`. This ensures raw materials are correctly grouped by each production item's respective Sales Order, not a global document-level Sales Order.

Applied to files:

  • erpnext/manufacturing/doctype/production_plan/production_plan.py
🧬 Code graph analysis (4)
erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py (2)
erpnext/manufacturing/doctype/bom/bom.py (1)
  • BOM (102-1143)
erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py (1)
  • explode_phantom_boms (163-185)
erpnext/manufacturing/doctype/production_plan/production_plan.py (2)
erpnext/controllers/queries.py (1)
  • bom (280-307)
erpnext/manufacturing/doctype/bom/bom.py (1)
  • is_sub_assembly_item (756-764)
erpnext/manufacturing/doctype/production_plan/test_production_plan.py (2)
erpnext/manufacturing/doctype/bom/test_bom.py (1)
  • create_tree_for_phantom_bom_tests (914-923)
erpnext/manufacturing/doctype/production_plan/production_plan.py (2)
  • get_sub_assembly_items (999-1066)
  • get_sub_assembly_items (1863-1936)
erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py (1)
erpnext/manufacturing/report/bom_stock_report/bom_stock_report.py (1)
  • explode_phantom_boms (95-106)
🪛 Ruff (0.14.3)
erpnext/manufacturing/doctype/bom/bom.py

1403-1403: Unused function argument: is_root

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Python Unit Tests (2)
  • GitHub Check: Python Unit Tests (3)
  • GitHub Check: Python Unit Tests (1)
  • GitHub Check: Python Unit Tests (4)
  • GitHub Check: Patch Test
  • GitHub Check: Summary

@mohsinalimat
Copy link
Contributor

@mihir-kandoi Please add screenshots or videos for better to understand this feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Phantom BOM with Multi-Level Explosion

3 participants