v1.0.0

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

available → transfer → assigned → return → available → retire → retired
maintenance · lost

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
AssetCreatedAsset is created
AssetTransferredAsset changes holder
AssetRetiredAsset is retired
AssetOverdueOverdue report runs

Exceptions

Exception Thrown when
AssetNotFoundExceptionAsset ID not found
AssetAlreadyRetiredExceptionTransfer/retire of retired asset
InvalidHolderExceptionHolder lacks HasAssets trait
InvalidTransitionExceptionTransition not permitted in state