# SetProductAttributeAssignment

## Overview

setProductAttributeAssignment adds an attribute value assignment to a product. A product can have multiple values per attribute (e.g., Color=Red, Color=Blue). Each record is uniquely identified by (productId, attributeId, valueId). If the exact triple already exists, the operation is a no-op (idempotent).

Since all attributes are variant axes, an additive-only policy applies for non-DRAFT products.

## Business Rules

- Target product must exist
- Target attribute must exist
- The value must reference a valid ProductAttributeValue belonging to the attribute
- On DRAFT products: all operations are allowed (add values, add new attributes)
- On ACTIVE products: adding new values to an already-assigned attribute is allowed (additive-only); assigning a new attribute (one not yet assigned to the product) is blocked — the attribute structure is locked once activated
- On ARCHIVED products: all assignment changes are blocked
- If the exact (productId, attributeId, valueId) assignment already exists, the command returns success without changes (idempotent)

## Process Flow

```mermaid
flowchart TD
    A[Receive set value request] --> B{Product exists?}
    B -->|No| C[Return error: PRODUCT_NOT_FOUND]
    B -->|Yes| D{Attribute exists?}
    D -->|No| E[Return error: ATTRIBUTE_NOT_FOUND]
    D -->|Yes| F{Value valid?}
    F -->|No| M[Return error: INVALID_VALUE]
    F -->|Yes| G{Already assigned?}
    G -->|Yes| O[Return existing assignment - no-op]
    G -->|No| H{Product in DRAFT?}
    H -->|Yes| N[Create assignment record]
    H -->|No| H2{Product ARCHIVED?}
    H2 -->|Yes| P[Return error: PRODUCT_ARCHIVED]
    H2 -->|No| I{New attribute for this product?}
    I -->|Yes| J[Return error: AXIS_STRUCTURE_LOCKED]
    I -->|No| N
    N --> O2[Return assignment]
```

## External Dependencies

- None

## Error Scenarios

- **PRODUCT_NOT_FOUND**: Specified product ID does not exist
- **ATTRIBUTE_NOT_FOUND**: Specified attribute ID does not exist
- **PRODUCT_ARCHIVED**: Archived products do not allow any assignment changes
- **AXIS_STRUCTURE_LOCKED**: A new attribute cannot be assigned to an ACTIVE product — the set of attributes is fixed once activated
- **INVALID_VALUE**: Value does not reference a valid ProductAttributeValue for the attribute

## Test Cases

- returns error when product not found
- returns error when attribute not found
- returns error when value not found for attribute
- returns error when product is ARCHIVED
- returns existing assignment when exact triple already exists (idempotent no-op)
- returns error when adding new attribute to ACTIVE product
- creates new assignment on DRAFT product
- adds new value to existing attribute on ACTIVE product (additive-only)
