Add asset financial fields, vendor contracts, and maintenance SLA inputs
This commit is contained in:
@@ -27,7 +27,13 @@ class Asset extends Model
|
||||
'warranty_id',
|
||||
'purchase_date',
|
||||
'warranty_end',
|
||||
'warranty_reminder_sent_at',
|
||||
'cost',
|
||||
'depreciation_method',
|
||||
'useful_life_months',
|
||||
'residual_value',
|
||||
'capex_opex',
|
||||
'vendor_contract_id',
|
||||
'qr_token',
|
||||
'qr_path',
|
||||
'metadata',
|
||||
@@ -36,6 +42,8 @@ class Asset extends Model
|
||||
protected $casts = [
|
||||
'purchase_date' => 'date',
|
||||
'warranty_end' => 'date',
|
||||
'warranty_reminder_sent_at' => 'datetime',
|
||||
'residual_value' => 'decimal:2',
|
||||
'metadata' => 'array',
|
||||
];
|
||||
|
||||
@@ -84,6 +92,11 @@ class Asset extends Model
|
||||
return $this->belongsTo(Warranty::class);
|
||||
}
|
||||
|
||||
public function vendorContract()
|
||||
{
|
||||
return $this->belongsTo(VendorContract::class);
|
||||
}
|
||||
|
||||
public function histories()
|
||||
{
|
||||
return $this->hasMany(AssetHistory::class);
|
||||
@@ -169,4 +182,26 @@ class Asset extends Model
|
||||
|
||||
return $departmentMatch || $locationMatch;
|
||||
}
|
||||
|
||||
public function bookValue(?\Carbon\Carbon $asOf = null): float
|
||||
{
|
||||
$asOf = $asOf ?: now();
|
||||
$cost = (float) ($this->cost ?? 0);
|
||||
$residual = (float) ($this->residual_value ?? 0);
|
||||
$lifeMonths = (int) ($this->useful_life_months ?? 0);
|
||||
if ($cost <= 0 || $lifeMonths <= 0 || !$this->purchase_date) {
|
||||
return $cost;
|
||||
}
|
||||
|
||||
$monthsUsed = max(0, $this->purchase_date->diffInMonths($asOf));
|
||||
if ($this->depreciation_method === 'diminishing') {
|
||||
$rate = pow($residual / max($cost, 1), 1 / max($lifeMonths, 1));
|
||||
$value = $cost * pow($rate, $monthsUsed);
|
||||
return max($value, $residual);
|
||||
}
|
||||
|
||||
$monthlyDep = ($cost - $residual) / $lifeMonths;
|
||||
$value = $cost - ($monthlyDep * $monthsUsed);
|
||||
return max($value, $residual);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ class AssetMaintenance extends Model
|
||||
'rejected_by',
|
||||
'rejected_at',
|
||||
'decision_notes',
|
||||
'sla_response_hours',
|
||||
'sla_resolution_hours',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@@ -31,6 +33,8 @@ class AssetMaintenance extends Model
|
||||
'cost' => 'decimal:2',
|
||||
'approved_at' => 'datetime',
|
||||
'rejected_at' => 'datetime',
|
||||
'sla_response_hours' => 'integer',
|
||||
'sla_resolution_hours' => 'integer',
|
||||
];
|
||||
|
||||
public function asset()
|
||||
|
||||
32
app/Models/VendorContract.php
Normal file
32
app/Models/VendorContract.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class VendorContract extends Model
|
||||
{
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $fillable = [
|
||||
'vendor_name',
|
||||
'contract_number',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'sla_response_hours',
|
||||
'sla_resolution_hours',
|
||||
'notes',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'start_date' => 'date',
|
||||
'end_date' => 'date',
|
||||
];
|
||||
|
||||
public function assets()
|
||||
{
|
||||
return $this->hasMany(Asset::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
$table->string('depreciation_method')->default('straight_line')->after('cost');
|
||||
$table->unsignedInteger('useful_life_months')->nullable()->after('depreciation_method');
|
||||
$table->decimal('residual_value', 15, 2)->nullable()->after('useful_life_months');
|
||||
$table->string('capex_opex')->nullable()->after('residual_value'); // capex|opex
|
||||
$table->foreignUuid('vendor_contract_id')->nullable()->after('capex_opex')->constrained('vendor_contracts')->nullOnDelete();
|
||||
$table->timestamp('warranty_reminder_sent_at')->nullable()->after('warranty_end');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('assets', function (Blueprint $table) {
|
||||
$table->dropForeign(['vendor_contract_id']);
|
||||
$table->dropColumn([
|
||||
'depreciation_method',
|
||||
'useful_life_months',
|
||||
'residual_value',
|
||||
'capex_opex',
|
||||
'vendor_contract_id',
|
||||
'warranty_reminder_sent_at',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('vendor_contracts', function (Blueprint $table) {
|
||||
$table->uuid('id')->primary();
|
||||
$table->string('vendor_name');
|
||||
$table->string('contract_number')->nullable();
|
||||
$table->date('start_date')->nullable();
|
||||
$table->date('end_date')->nullable();
|
||||
$table->unsignedInteger('sla_response_hours')->nullable();
|
||||
$table->unsignedInteger('sla_resolution_hours')->nullable();
|
||||
$table->text('notes')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('vendor_contracts');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('asset_maintenances', function (Blueprint $table) {
|
||||
$table->unsignedInteger('sla_response_hours')->nullable()->after('status');
|
||||
$table->unsignedInteger('sla_resolution_hours')->nullable()->after('sla_response_hours');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('asset_maintenances', function (Blueprint $table) {
|
||||
$table->dropColumn(['sla_response_hours', 'sla_resolution_hours']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -33,6 +33,14 @@
|
||||
<option value="completed" selected>Completed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">SLA Response (jam)</label>
|
||||
<input type="number" name="sla_response_hours" class="form-control" placeholder="cth: 4">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">SLA Resolution (jam)</label>
|
||||
<input type="number" name="sla_resolution_hours" class="form-control" placeholder="cth: 24">
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label class="form-label">Catatan</label>
|
||||
<textarea name="notes" class="form-control" rows="2" placeholder="Catatan tambahan"></textarea>
|
||||
|
||||
Reference in New Issue
Block a user