From ac0be8a19e0a7e535bdfcd9df037f93bec39fb07 Mon Sep 17 00:00:00 2001 From: Arya Dwi Putra Date: Tue, 2 Dec 2025 16:18:30 +0700 Subject: [PATCH] Add asset financial fields, vendor contracts, and maintenance SLA inputs --- app/Models/Asset.php | 35 +++++++++++++++++++ app/Models/AssetMaintenance.php | 4 +++ app/Models/VendorContract.php | 32 +++++++++++++++++ ..._000000_add_financial_fields_to_assets.php | 35 +++++++++++++++++++ ...2_000001_create_vendor_contracts_table.php | 28 +++++++++++++++ ...2_add_sla_fields_to_asset_maintenances.php | 23 ++++++++++++ .../partials/maintenance_form.blade.php | 8 +++++ 7 files changed, 165 insertions(+) create mode 100644 app/Models/VendorContract.php create mode 100644 database/migrations/2025_12_12_000000_add_financial_fields_to_assets.php create mode 100644 database/migrations/2025_12_12_000001_create_vendor_contracts_table.php create mode 100644 database/migrations/2025_12_12_000002_add_sla_fields_to_asset_maintenances.php diff --git a/app/Models/Asset.php b/app/Models/Asset.php index 45cca41..a497754 100644 --- a/app/Models/Asset.php +++ b/app/Models/Asset.php @@ -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); + } } diff --git a/app/Models/AssetMaintenance.php b/app/Models/AssetMaintenance.php index 9f0e437..daa58c8 100644 --- a/app/Models/AssetMaintenance.php +++ b/app/Models/AssetMaintenance.php @@ -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() diff --git a/app/Models/VendorContract.php b/app/Models/VendorContract.php new file mode 100644 index 0000000..d362126 --- /dev/null +++ b/app/Models/VendorContract.php @@ -0,0 +1,32 @@ + 'date', + 'end_date' => 'date', + ]; + + public function assets() + { + return $this->hasMany(Asset::class); + } +} diff --git a/database/migrations/2025_12_12_000000_add_financial_fields_to_assets.php b/database/migrations/2025_12_12_000000_add_financial_fields_to_assets.php new file mode 100644 index 0000000..725054f --- /dev/null +++ b/database/migrations/2025_12_12_000000_add_financial_fields_to_assets.php @@ -0,0 +1,35 @@ +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', + ]); + }); + } +}; diff --git a/database/migrations/2025_12_12_000001_create_vendor_contracts_table.php b/database/migrations/2025_12_12_000001_create_vendor_contracts_table.php new file mode 100644 index 0000000..c3fe563 --- /dev/null +++ b/database/migrations/2025_12_12_000001_create_vendor_contracts_table.php @@ -0,0 +1,28 @@ +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'); + } +}; diff --git a/database/migrations/2025_12_12_000002_add_sla_fields_to_asset_maintenances.php b/database/migrations/2025_12_12_000002_add_sla_fields_to_asset_maintenances.php new file mode 100644 index 0000000..174f0d8 --- /dev/null +++ b/database/migrations/2025_12_12_000002_add_sla_fields_to_asset_maintenances.php @@ -0,0 +1,23 @@ +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']); + }); + } +}; diff --git a/resources/views/assets/partials/maintenance_form.blade.php b/resources/views/assets/partials/maintenance_form.blade.php index d276cab..c4fec52 100644 --- a/resources/views/assets/partials/maintenance_form.blade.php +++ b/resources/views/assets/partials/maintenance_form.blade.php @@ -33,6 +33,14 @@ +
+ + +
+
+ + +