Product Management Guide
Overview
The Product Management system provides comprehensive catalog management for all product types, from simple physical goods to complex master/variant configurations. Built on Domain-Driven Design principles with CQRS patterns, it offers flexible product modeling, lifecycle management, and rich metadata support.
Table of Contents
- Product Types
- Product Status Lifecycle
- Product Structure
- Simple vs Master Products
- Creating Products
- Updating Products
- Product Lifecycle Operations
- Product Relationships
- Product Attributes (EAV)
- Searching and Filtering
- Bulk Operations
- CQRS Examples
- API Endpoints
Product Types
The system supports seven distinct product types, each with specific characteristics:
Physical Products
Traditional tangible goods requiring inventory tracking and shipping.
ProductType::PHYSICAL
// Characteristics:
- requiresShipping(): true
- hasInventory(): true
- isTangible(): true
- canHaveVariants(): true
- taxCategory(): 'physical_goods'Examples: Laptops, clothing, books, furniture Use Cases: E-commerce stores, retail inventory Features: Full inventory tracking, shipping calculations, variant support
Digital Products
Downloadable or streamable digital content.
ProductType::DIGITAL
// Characteristics:
- requiresShipping(): false
- hasInventory(): false
- isDownloadable(): true
- canHaveVariants(): true
- taxCategory(): 'digital_goods'Examples: Software, e-books, music files, PDF documents Use Cases: Digital marketplaces, content platforms Features: Download links, license management, no inventory tracking
Services
Professional services or consultations.
ProductType::SERVICE
// Characteristics:
- requiresShipping(): false
- hasInventory(): false
- requiresFulfillment(): true
- canHaveVariants(): true
- taxCategory(): 'services'Examples: Consulting hours, maintenance contracts, training sessions Use Cases: Service businesses, professional services Features: Booking management, resource allocation, service-specific pricing
Subscriptions
Recurring products billed periodically.
ProductType::SUBSCRIPTION
// Characteristics:
- requiresShipping(): false
- hasInventory(): false
- isRecurring(): true
- canHaveVariants(): true
- taxCategory(): 'subscription_services'Examples: Monthly memberships, SaaS subscriptions, subscription boxes Use Cases: Membership platforms, subscription businesses Features: Recurring billing, renewal management, tier variations
Bundles
Collections of multiple products sold together.
ProductType::BUNDLE
// Characteristics:
- requiresShipping(): false (depends on contents)
- hasInventory(): false
- taxCategory(): 'mixed'Examples: Starter packs, gift sets, combo deals Use Cases: Product bundles, promotional packages Features: Component tracking, bundle pricing, inventory aggregation
Gift Cards
Prepaid store credit or value cards.
ProductType::GIFT_CARD
// Characteristics:
- requiresShipping(): false
- hasInventory(): true (for physical cards)
- taxCategory(): 'gift_cards'Examples: Store gift cards, prepaid vouchers Use Cases: Gift card programs, promotional credits Features: Balance tracking, redemption codes, expiration dates
Virtual Products
Non-physical, non-downloadable products.
ProductType::VIRTUAL
// Characteristics:
- requiresShipping(): false
- hasInventory(): false
- isDownloadable(): true
- taxCategory(): 'virtual_goods'Examples: Game credits, virtual currencies, in-app purchases Use Cases: Gaming platforms, virtual economies Features: Virtual delivery, account crediting
Product Status Lifecycle
Products transition through different statuses during their lifecycle:
stateDiagram-v2
[*] --> Draft
Draft --> PendingApproval
Draft --> Active
Draft --> Rejected
PendingApproval --> Active
PendingApproval --> Rejected
PendingApproval --> Draft
Rejected --> Draft
Active --> Inactive
Active --> OutOfStock
Active --> Discontinued
Inactive --> Active
Inactive --> Discontinued
OutOfStock --> Active
OutOfStock --> Discontinued
Discontinued --> [*]Status Descriptions
Draft
Initial status for new products under development.
- Not visible in public catalog
- Can be edited freely
- No inventory tracking
- Used for product setup and configuration
Allowed Transitions:
- → Active (publish directly)
- → Pending Approval (submit for review)
- → Rejected (mark as rejected)
Use Cases:
- New product creation
- Product staging before launch
- Template products for cloning
Pending Approval
Products awaiting management approval before activation.
- Requires approval workflow
- Not yet available for sale
- Under review by authorized personnel
Allowed Transitions:
- → Active (approve)
- → Rejected (decline)
- → Draft (return for revisions)
Use Cases:
- Products requiring pricing approval
- New vendor products needing review
- Compliance verification workflows
Active
Available for sale and fully operational.
- Visible in catalog
- Inventory tracked
- Can be ordered by customers
- Full feature availability
Allowed Transitions:
- → Inactive (temporarily disable)
- → Out of Stock (inventory depleted)
- → Discontinued (permanently remove)
Use Cases:
- Standard products available for purchase
- Current catalog items
- Regular inventory items
Inactive
Temporarily disabled products not currently for sale.
- Not visible in catalog
- Inventory preserved
- Can be reactivated
- Historical data maintained
Allowed Transitions:
- → Active (re-enable)
- → Discontinued (permanently remove)
Use Cases:
- Seasonal products (off-season)
- Products under review
- Temporary supply issues
Out of Stock
Inventory depleted but product remains active.
- Visible in catalog (typically with "out of stock" notice)
- Cannot be ordered
- Awaiting replenishment
- Automatic status (set when inventory hits zero)
Allowed Transitions:
- → Active (inventory replenished)
- → Discontinued (permanently remove)
Use Cases:
- Popular items temporarily unavailable
- Awaiting supplier delivery
- Between production runs
Discontinued
Permanently removed from active catalog.
- Not visible in catalog
- Cannot be reactivated
- Historical data preserved
- Final status (no further transitions)
Allowed Transitions: None (terminal state)
Use Cases:
- End-of-life products
- Products no longer manufactured
- Regulatory discontinued items
- Legacy products
Rejected
Products that failed approval process.
- Not approved for sale
- Requires revision
- Can be returned to draft for corrections
Allowed Transitions:
- → Draft (revise and resubmit)
Use Cases:
- Products failing compliance review
- Incorrect pricing needing correction
- Products with missing required information
Product Structure
Core Properties
Every product has these fundamental properties:
[
'id' => 1, // Auto-generated ID
'name' => 'Product Name', // Display name (3-255 chars)
'slug' => 'product-name', // URL-friendly identifier
'sku' => 'PROD-001', // Stock Keeping Unit (unique)
'type' => 'physical', // Product type enum
'status' => 'active', // Status enum
'is_variant' => false, // Simple vs variant product
'parent_product_id' => null, // Master product ID (for variants)
// Pricing
'selling_price' => Money, // Retail price (Money VO)
'cost_price' => Money, // Cost basis (Money VO)
'currency' => 'USD', // Currency code
// Inventory
'stock_quantity' => 100, // Current stock level
'reserved_quantity' => 10, // Reserved for orders
'available_quantity' => 90, // Available = stock - reserved
'reorder_point' => 20, // Low stock threshold
'reorder_quantity' => 50, // Auto-reorder amount
'track_inventory' => true, // Enable inventory tracking
// Description
'short_description' => '...', // Brief summary
'description' => '...', // Full description
'meta_description' => '...', // SEO description
'meta_keywords' => '...', // SEO keywords
// Dimensions (for physical products)
'weight' => 2.5, // Weight in kg
'length' => 30.0, // Length in cm
'width' => 20.0, // Width in cm
'height' => 10.0, // Height in cm
'dimensions_unit' => 'cm', // Unit of measure
// Relationships
'category_id' => 5, // Product category
'supplier_id' => 2, // Primary supplier
'brand_id' => 3, // Product brand
'tax_class_id' => 1, // Tax classification
// Settings
'is_featured' => false, // Featured product flag
'is_taxable' => true, // Subject to tax
'requires_shipping' => true, // Needs shipping
'allow_backorders' => false, // Accept orders when out of stock
// Metadata
'created_at' => '2025-01-15 10:00:00',
'updated_at' => '2025-01-15 14:30:00',
'deleted_at' => null, // Soft delete timestamp
]Money Value Object
All monetary amounts use the Money Value Object to prevent precision errors:
use App\Shared\Domain\ValueObjects\Money;
// Creating Money instances
$sellingPrice = Money::fromFloat(19.99, 'USD');
$costPrice = Money::fromCents(1200, 'USD'); // $12.00
// Accessing values
$sellingPrice->toFloat(); // 19.99
$sellingPrice->amount(); // 1999 (cents)
$sellingPrice->currency(); // "USD"
$sellingPrice->format(); // "USD 19.99"
// Arithmetic (returns new Money instance)
$profit = $sellingPrice->subtract($costPrice);
$total = $sellingPrice->multiply(10);Why Money VO?
- Prevents floating-point precision errors
- Stores amounts as integers (cents)
- Type-safe monetary operations
- Currency awareness built-in
Simple vs Master Products
Simple Products
Single SKU products with straightforward inventory tracking.
[
'name' => 'Laptop - Dell XPS 13',
'sku' => 'DELL-XPS13-001',
'type' => 'physical',
'is_variant' => false,
'parent_product_id' => null,
'selling_price' => Money::fromFloat(999.99),
'stock_quantity' => 50,
]Characteristics:
- One product = one SKU
- Single inventory record
- Direct pricing
- No variations
Best For:
- Unique items (specific laptop model)
- Products without variations
- Services with fixed pricing
- Digital downloads (single version)
Master Products
Configuration template for generating product variants.
[
'name' => 'T-Shirt - Cotton Basic',
'type' => 'physical',
'is_variant' => false,
'variant_configuration' => [
'attributes' => [
'Size' => ['S', 'M', 'L', 'XL'],
'Color' => ['Red', 'Blue', 'Black', 'White'],
],
],
]Characteristics:
- No direct SKU (variants have SKUs)
- Template for variant generation
- Defines variant attributes
- No direct inventory tracking
Generates: 16 variants (4 sizes × 4 colors)
Variant Products
Individual products generated from master configuration.
[
'name' => 'T-Shirt - Cotton Basic (M, Blue)',
'sku' => 'TSHIRT-BASIC-M-BLUE',
'type' => 'physical',
'is_variant' => true,
'parent_product_id' => 1, // Master product ID
'variant_attributes' => [
'Size' => 'M',
'Color' => 'Blue',
],
'selling_price' => Money::fromFloat(19.99),
'stock_quantity' => 25,
]Characteristics:
- Unique SKU per variant
- Individual inventory tracking
- Can override master pricing
- Specific attribute combination
See: Product Variants Guide for detailed variant management.
Creating Products
Using CQRS Commands
The recommended approach using Command pattern:
use App\Modules\Operations\Application\Commands\CreateProductCommand;
use App\Modules\Operations\Application\DTOs\Product\CreateProductDTO;
use App\Shared\Application\Bus\CommandBus;
// In controller
public function store(CreateProductRequest $request): JsonResponse
{
$command = new CreateProductCommand(
dto: CreateProductDTO::fromArray($request->validated())
);
$product = $this->commandBus->dispatch($command);
return response()->json([
'message' => 'Product created successfully',
'data' => new ProductResource($product),
], 201);
}CreateProductDTO
Complete DTO with all available fields:
$dto = CreateProductDTO::fromArray([
// Required
'name' => 'Laptop - Dell XPS 13',
// Optional - Basic Info
'slug' => 'laptop-dell-xps-13',
'sku' => 'DELL-XPS13-001', // Auto-generated if not provided
'type' => 'physical', // Default: physical
'short_description' => 'Ultra-portable laptop',
'description' => 'Full product description...',
// Pricing
'selling_price' => 999.99, // Converted to Money VO
'cost_price' => 750.00,
'currency' => 'USD', // Default: USD
// Inventory
'stock_quantity' => 50, // Default: 0
'reorder_point' => 10,
'reorder_quantity' => 25,
'track_inventory' => true, // Default: true
'allow_backorders' => false,
// Dimensions
'weight' => 1.2, // kg
'length' => 30.0, // cm
'width' => 20.0,
'height' => 2.0,
'dimensions_unit' => 'cm',
// Relationships
'category_id' => 5,
'supplier_id' => 2,
'brand_id' => 3,
'tax_class_id' => 1,
// Settings
'is_featured' => false,
'is_taxable' => true,
'requires_shipping' => true,
// SEO
'meta_description' => 'Buy Dell XPS 13...',
'meta_keywords' => 'laptop, dell, xps',
// Tags (array of tag IDs)
'tags' => [1, 2, 3],
]);API Request Example
Creating a product via API:
curl -X POST https://api.crm.test/api/v1/operations/products \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop - Dell XPS 13",
"sku": "DELL-XPS13-001",
"type": "physical",
"selling_price": 999.99,
"cost_price": 750.00,
"stock_quantity": 50,
"reorder_point": 10,
"category_id": 5,
"supplier_id": 2,
"short_description": "Ultra-portable laptop with stunning display",
"description": "The Dell XPS 13 features...",
"weight": 1.2,
"length": 30,
"width": 20,
"height": 2,
"track_inventory": true
}'Response:
{
"message": "Product created successfully",
"data": {
"id": 42,
"name": "Laptop - Dell XPS 13",
"slug": "laptop-dell-xps-13",
"sku": "DELL-XPS13-001",
"type": "physical",
"status": "draft",
"selling_price": 999.99,
"cost_price": 750.00,
"stock_quantity": 50,
"available_quantity": 50,
"category": {
"id": 5,
"name": "Electronics"
},
"supplier": {
"id": 2,
"name": "Tech Supplies Inc"
},
"created_at": "2025-01-15T10:30:00Z"
}
}Updating Products
Using CQRS Commands
use App\Modules\Operations\Application\Commands\UpdateProductCommand;
use App\Modules\Operations\Application\DTOs\Product\UpdateProductDTO;
$command = new UpdateProductCommand(
productId: 42,
dto: UpdateProductDTO::fromArray($request->validated())
);
$product = $this->commandBus->dispatch($command);Partial Updates
UpdateProductDTO supports partial updates - only include fields you want to change:
$dto = UpdateProductDTO::fromArray([
'selling_price' => 899.99, // New price
'stock_quantity' => 75, // Update stock
// Other fields remain unchanged
]);API Request Example
curl -X PUT https://api.crm.test/api/v1/operations/products/42 \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"selling_price": 899.99,
"stock_quantity": 75,
"short_description": "Updated description"
}'Product Lifecycle Operations
Activate Product
Transition from draft to active status:
use App\Modules\Operations\Application\Commands\ActivateProductCommand;
$command = new ActivateProductCommand(productId: 42);
$product = $this->commandBus->dispatch($command);API Request:
curl -X POST https://api.crm.test/api/v1/operations/products/42/activate \
-H "Authorization: Bearer {token}"Effect:
- Status changes to
active - Product becomes visible in catalog
- Inventory tracking begins
- Can be ordered by customers
- Fires
ProductActivateddomain event
Deactivate Product
Temporarily disable a product:
use App\Modules\Operations\Application\Commands\DeactivateProductCommand;
$command = new DeactivateProductCommand(
productId: 42,
reason: 'Seasonal product - off season'
);
$product = $this->commandBus->dispatch($command);API Request:
curl -X POST https://api.crm.test/api/v1/operations/products/42/deactivate \
-H "Authorization: Bearer {token}" \
-d '{
"reason": "Seasonal product - off season"
}'Effect:
- Status changes to
inactive - Product hidden from catalog
- Cannot be ordered
- Inventory preserved
- Can be reactivated later
Discontinue Product
Permanently remove from catalog:
use App\Modules\Operations\Application\Commands\DiscontinueProductCommand;
$command = new DiscontinueProductCommand(
productId: 42,
reason: 'End of life - no longer manufactured'
);
$product = $this->commandBus->dispatch($command);API Request:
curl -X POST https://api.crm.test/api/v1/operations/products/42/discontinue \
-H "Authorization: Bearer {token}" \
-d '{
"reason": "End of life - no longer manufactured"
}'Effect:
- Status changes to
discontinued - Product permanently removed from catalog
- Cannot be reactivated
- Historical data preserved
- Final status (terminal)
Product Relationships
Categories
Hierarchical product categorization:
// Assign category during creation
'category_id' => 5
// Category relationship
$product->category; // Returns Category model
$product->category->name; // "Electronics"
$product->category->parent; // Parent category (if nested)Examples: Electronics > Laptops > Gaming Laptops
Suppliers
Primary vendor for product sourcing:
'supplier_id' => 2
$product->supplier; // Returns Supplier model
$product->supplier->name; // "Tech Supplies Inc"
$product->supplier->email; // "orders@techsupplies.com"Use Cases:
- Purchase order generation
- Reorder automation
- Supplier performance tracking
Brands
Product manufacturer or brand:
'brand_id' => 3
$product->brand; // Returns Brand model
$product->brand->name; // "Dell"
$product->brand->website; // "https://dell.com"Use Cases:
- Brand filtering
- Brand pages
- Marketing campaigns
Tags
Flexible multi-tag assignment:
// Assign tags (many-to-many)
$product->tags()->attach([1, 2, 3]);
// Tags during creation
'tags' => [1, 2, 3]
// Access tags
$product->tags; // Collection of Tag models
$product->tags->pluck('name'); // ["Bestseller", "New Arrival", "On Sale"]Use Cases:
- Product filtering
- Marketing segments
- Custom collections
- Dynamic product groups
Product Attributes (EAV)
The Entity-Attribute-Value (EAV) system provides flexible custom properties:
Defining Attributes
Attributes are defined globally and can be assigned to products:
// Attribute Types
TEXT // Short text (e.g., "Color Name")
TEXTAREA // Long text (e.g., "Care Instructions")
NUMBER // Numeric (e.g., "Megapixels")
BOOLEAN // Yes/No (e.g., "Waterproof")
DATE // Date value (e.g., "Release Date")
SELECT // Single choice (e.g., "Warranty Period")
MULTISELECT // Multiple choices (e.g., "Features")Assigning Attributes
curl -X POST https://api.crm.test/api/v1/operations/products/42/attributes \
-H "Authorization: Bearer {token}" \
-d '{
"attribute_code": "warranty_period",
"value": "2 years"
}'Querying Attributes
$product->attributes; // Collection of product attributes
// Get specific attribute
$warranty = $product->getAttribute('warranty_period');
$warranty->value; // "2 years"
// Check if attribute exists
$product->hasAttribute('waterproof'); // true/falseUse Cases
- Specifications: Technical specs (CPU, RAM, storage)
- Features: Product features (Bluetooth, GPS, 4K)
- Certifications: Compliance (CE, FCC, RoHS)
- Marketing: Custom properties (Eco-friendly, Handmade)
Searching and Filtering
Full-Text Search
Search across product name, SKU, and description:
curl -X GET "https://api.crm.test/api/v1/operations/products/search?q=laptop" \
-H "Authorization: Bearer {token}"Searches:
- Product name
- SKU
- Short description
- Long description
- Meta keywords
Filter by Status
curl -X GET "https://api.crm.test/api/v1/operations/products?status=active" \
-H "Authorization: Bearer {token}"Available Statuses: draft, active, inactive, discontinued, out_of_stock, pending_approval, rejected
Filter by Category
curl -X GET "https://api.crm.test/api/v1/operations/products?category_id=5" \
-H "Authorization: Bearer {token}"Filter by Supplier
curl -X GET "https://api.crm.test/api/v1/operations/products?supplier_id=2" \
-H "Authorization: Bearer {token}"Price Range Filter
curl -X GET "https://api.crm.test/api/v1/operations/products?min_price=500&max_price=1500" \
-H "Authorization: Bearer {token}"Stock Level Filter
# Low stock products
curl -X GET "https://api.crm.test/api/v1/operations/products?stock_status=low" \
-H "Authorization: Bearer {token}"
# Out of stock
curl -X GET "https://api.crm.test/api/v1/operations/products?stock_status=out" \
-H "Authorization: Bearer {token}"Combined Filters
curl -X GET "https://api.crm.test/api/v1/operations/products?status=active&category_id=5&min_price=500&sort_by=price&sort_direction=desc&per_page=25" \
-H "Authorization: Bearer {token}"Bulk Operations
Bulk Status Update
Update status for multiple products:
curl -X POST https://api.crm.test/api/v1/operations/products/bulk-action \
-H "Authorization: Bearer {token}" \
-d '{
"action": "activate",
"product_ids": [42, 43, 44, 45]
}'Available Actions:
activate- Activate selected productsdeactivate- Deactivate selected productsdiscontinue- Discontinue selected productsdelete- Soft delete selected products
Bulk Export
Export products to CSV or Excel:
curl -X POST https://api.crm.test/api/v1/operations/products/export \
-H "Authorization: Bearer {token}" \
-d '{
"format": "csv",
"filters": {
"status": "active",
"category_id": 5
}
}'Response:
{
"message": "Export queued successfully",
"data": {
"job_id": "export-123",
"download_url": "https://api.crm.test/downloads/products-export-123.csv",
"status": "processing",
"estimated_completion": "2025-01-15T10:35:00Z"
}
}CQRS Examples
Commands (Write Operations)
All product modifications use commands:
// Create
$command = new CreateProductCommand(dto: $dto);
$product = $this->commandBus->dispatch($command);
// Update
$command = new UpdateProductCommand(productId: 42, dto: $dto);
$product = $this->commandBus->dispatch($command);
// Activate
$command = new ActivateProductCommand(productId: 42);
$product = $this->commandBus->dispatch($command);
// Deactivate
$command = new DeactivateProductCommand(productId: 42, reason: '...');
$product = $this->commandBus->dispatch($command);
// Discontinue
$command = new DiscontinueProductCommand(productId: 42, reason: '...');
$product = $this->commandBus->dispatch($command);
// Delete
$command = new DeleteProductCommand(productId: 42);
$this->commandBus->dispatch($command);Transaction Handling: CommandBus automatically wraps in database transaction.
Queries (Read Operations)
All product retrievals use queries:
// Get single product
$query = new GetProductByIdQuery(
productId: 42,
includes: ['category', 'supplier', 'brand', 'tags']
);
$product = $this->queryBus->dispatch($query);
// Get products with filters
$query = new GetProductsQuery(
filterDTO: ProductFilterDTO::fromArray([
'status' => 'active',
'category_id' => 5,
'min_price' => 500,
'max_price' => 1500,
'sort_by' => 'price',
'sort_direction' => 'desc',
'per_page' => 25,
])
);
$products = $this->queryBus->dispatch($query);
// Search products
$query = new SearchProductsQuery(
searchTerm: 'laptop',
filters: ['status' => 'active']
);
$results = $this->queryBus->dispatch($query);
// Low stock products
$query = new GetLowStockProductsQuery(
warehouseId: 1 // Optional: filter by warehouse
);
$lowStockProducts = $this->queryBus->dispatch($query);No Transactions: Queries are read-only, no transaction wrapping.
Domain Events
Product operations emit domain events:
// Events emitted automatically by domain methods
ProductCreated::class
- Fired when: New product created
- Payload: Product ID, name, SKU, status
- Listeners: Sync to search index, notify catalog managers
ProductUpdated::class
- Fired when: Product details changed
- Payload: Product ID, changed fields
- Listeners: Update search index, invalidate cache
ProductActivated::class
- Fired when: Product activated
- Payload: Product ID, activation timestamp
- Listeners: Publish to catalog, notify sales team
ProductDeactivated::class
- Fired when: Product deactivated
- Payload: Product ID, reason
- Listeners: Remove from catalog, notify sales team
ProductDiscontinued::class
- Fired when: Product discontinued
- Payload: Product ID, reason
- Listeners: Archive product, update reporting
ProductDeleted::class
- Fired when: Product deleted
- Payload: Product ID, deletion timestamp
- Listeners: Clean up related data, audit logEvent Listeners: Register in App\Providers\AppServiceProvider
API Endpoints
Product CRUD
| Method | Endpoint | Description |
|---|---|---|
| GET | /products | List products with filters/pagination |
| POST | /products | Create new product |
| GET | /products/{id} | Get single product details |
| PUT | /products/{id} | Update product |
| DELETE | /products/{id} | Delete product (soft delete) |
Product Lifecycle
| Method | Endpoint | Description |
|---|---|---|
| POST | /products/{id}/activate | Activate product |
| POST | /products/{id}/deactivate | Deactivate product |
| POST | /products/{id}/discontinue | Discontinue product |
Product Search
| Method | Endpoint | Description |
|---|---|---|
| GET | /products/search | Full-text search |
| GET | /products/catalog | Public catalog (active only) |
| GET | /products/low-stock | Products below reorder point |
| GET | /products/out-of-stock | Out of stock products |
Product Attributes
| Method | Endpoint | Description |
|---|---|---|
| GET | /products/{id}/attributes | Get product attributes |
| POST | /products/{id}/attributes | Assign attribute to product |
| PUT | /products/{id}/attributes/{attribute} | Update attribute value |
| DELETE | /products/{id}/attributes/{attribute} | Remove attribute from product |
Bulk Operations
| Method | Endpoint | Description |
|---|---|---|
| POST | /products/bulk-action | Bulk status update/delete |
| POST | /products/export | Export products to CSV/Excel |
| GET | /products/statistics | Product statistics dashboard |
See: API Reference for complete documentation.
Related Guides
- Product Variants - Master/variant product management
- Multi-Warehouse Inventory - Stock tracking across warehouses
- Purchase Order Workflow - Supplier ordering process
- CQRS Pattern - Command/Query separation
- Value Objects - Money, SKU, and other VOs
Next Steps
- Create Products - Follow the Creating Products Tutorial
- Setup Variants - Read the Product Variants Guide
- Manage Inventory - Explore Multi-Warehouse Inventory
- Order from Suppliers - Learn Purchase Order Workflow