feat: global material override on OutputType for x-ray/clay render modes

- Add `material_override` nullable column on OutputType (DB migration)
- When set, ALL product parts get rendered with this single material
- Override applies after alias resolution in render_order_line task
- Admin UI: dropdown in OutputType table to select a library material
- Display: amber badge showing active override material name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 13:16:00 +01:00
parent b6bac080bb
commit 7c606953ec
6 changed files with 87 additions and 2 deletions
@@ -198,6 +198,12 @@ def render_order_line_task(self, order_line_id: str):
from app.services.material_service import resolve_material_map
material_map = resolve_material_map(material_map)
# Apply global material override from OutputType (e.g. x-ray mode)
if line.output_type and line.output_type.material_override:
override_mat = line.output_type.material_override
material_map = {k: override_mat for k in material_map}
emit(order_line_id, f"Material override active: all parts → {override_mat}")
if template:
emit(order_line_id, f"Using render template: {template.name} (collection={template.target_collection}, material_replace={template.material_replace_enabled}, lighting_only={template.lighting_only})")
logger.info(f"Render template resolved: '{template.name}' path={template.blend_file_path}, lighting_only={template.lighting_only}")
+2
View File
@@ -44,6 +44,8 @@ class OutputType(Base):
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
)
material_override: Mapped[str | None] = mapped_column(String(200), nullable=True, default=None)
workflow_definition_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("workflow_definitions.id", ondelete="SET NULL"), nullable=True
)
+3
View File
@@ -17,6 +17,7 @@ class OutputTypeCreate(BaseModel):
transparent_bg: bool = False
pricing_tier_id: int | None = None
cycles_device: str | None = None
material_override: str | None = None
class OutputTypePatch(BaseModel):
@@ -34,6 +35,7 @@ class OutputTypePatch(BaseModel):
pricing_tier_id: int | None = None
cycles_device: str | None = None
workflow_definition_id: uuid.UUID | None = None
material_override: str | None = None
class OutputTypeOut(BaseModel):
@@ -54,6 +56,7 @@ class OutputTypeOut(BaseModel):
price_per_item: float | None = None
workflow_definition_id: uuid.UUID | None = None
workflow_name: str | None = None
material_override: str | None = None
is_active: bool
created_at: datetime
updated_at: datetime