Employee Asset Management
Track physical assets across any holder type — employees, warehouses, departments, and more — using a flexible polymorphic model with full transition history and audit logging.
Getting Started
Installation
$ composer require obelaw/ium-eam
Publish Migrations
$ php artisan vendor:publish --tag=ium-eam-migrations
$ php artisan migrate
Publish Config (optional)
$ php artisan vendor:publish --tag=ium-eam-config
Requirements
- • PHP 8.2+
- • Laravel 11.0+ or 12.0+
- •
obelaw/ium
Configuration
config/eam.php maps holder class name fragments to asset statuses. When an asset is transferred, the status is resolved by matching class_basename($holderType) against these keys.
// config/eam.php
return [
'holder_status_map' => [
'Employee' => 'assigned',
'Warehouse' => 'available',
'Maintenance' => 'maintenance',
'Department' => 'assigned',
],
]; Morph Map (recommended)
Register a morph map in your AppServiceProvider to keep class names out of the database.
use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'employee' => \App\Models\Employee::class,
'warehouse' => \App\Models\Warehouse::class,
'department' => \App\Models\Department::class,
]); Asset Holders
Any Eloquent model can become a valid asset holder by adding the HasAssets trait.
use Obelaw\Ium\Eam\Traits\HasAssets;
class Employee extends Model
{
use HasAssets;
} | Method | Description |
|---|---|
| assets() | MorphMany — all assets currently held |
| assetTransitions() | MorphMany — all transitions involving this holder |
| receiveAsset(int $assetId, array $data) | Transfer an asset to this holder |
$employee = Employee::find(42);
// All assets currently held
$employee->assets;
// Receive an asset via the trait
$employee->receiveAsset($asset->id, [
'performed_by' => 'admin',
'condition' => 'good',
]); Assets
All asset operations are accessible via ium()->eam()->assets(). Assets begin life with status available and move through a defined lifecycle.
Create an Asset
use Obelaw\Ium\Eam\Data\AssetDTO;
$asset = ium()->eam()->assets()->create(AssetDTO::from([
'code' => 'LPT-001',
'name' => 'Dell Laptop',
'description' => 'Developer laptop',
'category_id' => 1,
'serial_number' => 'SN123456',
'purchase_date' => '2024-01-15',
'purchase_value' => 1200.00,
])); Read & List
// Find by ID
$asset = ium()->eam()->assets()->find($asset->id);
// List all
$assets = ium()->eam()->assets()->list();
// List available only
$assets = ium()->eam()->assets()->available();
// Assets held by a specific holder
$assets = ium()->eam()->assets()->heldBy(\App\Models\Employee::class, 42); Update
$asset = ium()->eam()->assets()->update($asset->id, AssetDTO::from([
'name' => 'Dell Laptop Pro',
])); Transfers
Assets move between holders via transferTo(). Every transfer is recorded as an AssetTransition and the asset status is automatically updated from the config map.
Transfer to a Holder
use Obelaw\Ium\Eam\Data\TransferAssetDTO;
$transition = ium()->eam()->assets()->transferTo(TransferAssetDTO::from([
'asset_id' => $asset->id,
'toHolder' => Employee::find(42),
'performed_by' => 'admin',
'condition' => 'good',
'reason' => 'New hire equipment',
])); Return to Pool
Pass null as the holder to return the asset to the available pool.
$transition = ium()->eam()->assets()->transferTo(TransferAssetDTO::from([
'asset_id' => $asset->id,
'toHolder' => null,
'performed_by' => 'admin',
'condition' => 'good',
])); View Transition History
$history = ium()->eam()->assets()->history($asset->id); Retire an Asset
$asset = ium()->eam()->assets()->retire($asset->id, 'End of life'); Asset Lifecycle
Categories
Organize assets into hierarchical categories using ium()->eam()->categories(). Categories support self-referential parent nesting via parent_id.
use Obelaw\Ium\Eam\Data\AssetCategoryDTO;
// Create
$category = ium()->eam()->categories()->create(AssetCategoryDTO::from([
'name' => 'Laptops',
'slug' => 'laptops',
'parent_id' => null,
]));
// Find
$category = ium()->eam()->categories()->find(1);
// List all
$categories = ium()->eam()->categories()->list(); Reports
Query assets and transitions by holder, category, status, or condition via ium()->eam()->reports().
// Assets by holder
$assets = ium()->eam()->reports()->byHolder(\App\Models\Warehouse::class, 3);
// Assets by category
$assets = ium()->eam()->reports()->byCategory(1);
// Assets by status
$assets = ium()->eam()->reports()->byStatus('maintenance');
// Transition history for an asset
$transitions = ium()->eam()->reports()->transitions($asset->id);
// Overdue assets
$overdue = ium()->eam()->reports()->overdue();
// Lost and damaged assets
$assets = ium()->eam()->reports()->lostAndDamaged(); Events & Exceptions
Events
| Event | Fired when |
|---|---|
| AssetCreated | Asset is created |
| AssetTransferred | Asset changes holder |
| AssetRetired | Asset is retired |
| AssetOverdue | Overdue report runs |
Exceptions
| Exception | Thrown when |
|---|---|
| AssetNotFoundException | Asset ID not found |
| AssetAlreadyRetiredException | Transfer/retire of retired asset |
| InvalidHolderException | Holder lacks HasAssets trait |
| InvalidTransitionException | Transition not permitted in state |