restructure retribution calculations table
This commit is contained in:
131
app/Models/BuildingType.php
Normal file
131
app/Models/BuildingType.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
|
||||
class BuildingType extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'code',
|
||||
'name',
|
||||
'parent_id',
|
||||
'level',
|
||||
'is_free',
|
||||
'is_active'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'level' => 'integer',
|
||||
'is_free' => 'boolean',
|
||||
'is_active' => 'boolean'
|
||||
];
|
||||
|
||||
/**
|
||||
* Parent relationship
|
||||
*/
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(BuildingType::class, 'parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Children relationship
|
||||
*/
|
||||
public function children(): HasMany
|
||||
{
|
||||
return $this->hasMany(BuildingType::class, 'parent_id')
|
||||
->where('is_active', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retribution indices relationship
|
||||
*/
|
||||
public function indices(): HasOne
|
||||
{
|
||||
return $this->hasOne(RetributionIndex::class, 'building_type_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculations relationship
|
||||
*/
|
||||
public function calculations(): HasMany
|
||||
{
|
||||
return $this->hasMany(RetributionCalculation::class, 'building_type_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope: Active only
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope: Parents only
|
||||
*/
|
||||
public function scopeParents($query)
|
||||
{
|
||||
return $query->whereNull('parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope: Children only
|
||||
*/
|
||||
public function scopeChildren($query)
|
||||
{
|
||||
return $query->whereNotNull('parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope: Non-free types
|
||||
*/
|
||||
public function scopeChargeable($query)
|
||||
{
|
||||
return $query->where('is_free', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if building type is free
|
||||
*/
|
||||
public function isFree(): bool
|
||||
{
|
||||
return $this->is_free;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a parent type
|
||||
*/
|
||||
public function isParent(): bool
|
||||
{
|
||||
return $this->parent_id === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a child type
|
||||
*/
|
||||
public function isChild(): bool
|
||||
{
|
||||
return $this->parent_id !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get complete data for calculation
|
||||
*/
|
||||
public function getCalculationData(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'code' => $this->code,
|
||||
'name' => $this->name,
|
||||
'coefficient' => $this->coefficient,
|
||||
'is_free' => $this->is_free,
|
||||
'indices' => $this->indices?->toArray(),
|
||||
'parent' => $this->parent?->only(['id', 'code', 'name'])
|
||||
];
|
||||
}
|
||||
}
|
||||
55
app/Models/HeightIndex.php
Normal file
55
app/Models/HeightIndex.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class HeightIndex extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'floor_number',
|
||||
'height_index'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'floor_number' => 'integer',
|
||||
'height_index' => 'decimal:6'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get height index by floor number
|
||||
*/
|
||||
public static function getByFloor(int $floorNumber): ?HeightIndex
|
||||
{
|
||||
return self::where('floor_number', $floorNumber)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get height index value by floor number
|
||||
*/
|
||||
public static function getHeightIndexByFloor(int $floorNumber): float
|
||||
{
|
||||
$index = self::getByFloor($floorNumber);
|
||||
return $index ? (float) $index->height_index : 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all height indices as array
|
||||
*/
|
||||
public static function getAllMapping(): array
|
||||
{
|
||||
return self::orderBy('floor_number')
|
||||
->pluck('height_index', 'floor_number')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available floor numbers
|
||||
*/
|
||||
public static function getAvailableFloors(): array
|
||||
{
|
||||
return self::orderBy('floor_number')
|
||||
->pluck('floor_number')
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
81
app/Models/RetributionCalculation.php
Normal file
81
app/Models/RetributionCalculation.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class RetributionCalculation extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'calculation_id',
|
||||
'building_type_id',
|
||||
'floor_number',
|
||||
'building_area',
|
||||
'retribution_amount',
|
||||
'calculation_detail',
|
||||
'calculated_at'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'floor_number' => 'integer',
|
||||
'building_area' => 'decimal:2',
|
||||
'retribution_amount' => 'decimal:2',
|
||||
'calculation_detail' => 'array',
|
||||
'calculated_at' => 'datetime'
|
||||
];
|
||||
|
||||
/**
|
||||
* Building type relationship
|
||||
*/
|
||||
public function buildingType(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(BuildingType::class, 'building_type_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique calculation ID
|
||||
*/
|
||||
public static function generateCalculationId(): string
|
||||
{
|
||||
return 'RTB' . Carbon::now()->format('ymdHis') . rand(10, 99);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new calculation
|
||||
*/
|
||||
public static function createCalculation(
|
||||
int $buildingTypeId,
|
||||
int $floorNumber,
|
||||
float $buildingArea,
|
||||
float $retributionAmount,
|
||||
array $calculationDetail
|
||||
): self {
|
||||
return self::create([
|
||||
'calculation_id' => self::generateCalculationId(),
|
||||
'building_type_id' => $buildingTypeId,
|
||||
'floor_number' => $floorNumber,
|
||||
'building_area' => $buildingArea,
|
||||
'retribution_amount' => $retributionAmount,
|
||||
'calculation_detail' => $calculationDetail,
|
||||
'calculated_at' => Carbon::now()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted retribution amount
|
||||
*/
|
||||
public function getFormattedAmount(): string
|
||||
{
|
||||
return 'Rp ' . number_format($this->retribution_amount, 2, ',', '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calculation breakdown
|
||||
*/
|
||||
public function getCalculationBreakdown(): array
|
||||
{
|
||||
return $this->calculation_detail ?? [];
|
||||
}
|
||||
}
|
||||
50
app/Models/RetributionConfig.php
Normal file
50
app/Models/RetributionConfig.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class RetributionConfig extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'key',
|
||||
'value',
|
||||
'description',
|
||||
'is_active'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'value' => 'decimal:2',
|
||||
'is_active' => 'boolean'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get config value by key
|
||||
*/
|
||||
public static function getValue(string $key, float $default = 0.0): float
|
||||
{
|
||||
$config = self::where('key', $key)->where('is_active', true)->first();
|
||||
return $config ? (float) $config->value : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active configs as array
|
||||
*/
|
||||
public static function getAllActive(): array
|
||||
{
|
||||
return self::where('is_active', true)
|
||||
->pluck('value', 'key')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update config value
|
||||
*/
|
||||
public static function updateValue(string $key, float $value): bool
|
||||
{
|
||||
return self::updateOrCreate(
|
||||
['key' => $key],
|
||||
['value' => $value, 'is_active' => true]
|
||||
);
|
||||
}
|
||||
}
|
||||
57
app/Models/RetributionIndex.php
Normal file
57
app/Models/RetributionIndex.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class RetributionIndex extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'building_type_id',
|
||||
'coefficient',
|
||||
'ip_permanent',
|
||||
'ip_complexity',
|
||||
'locality_index',
|
||||
'infrastructure_factor',
|
||||
'is_active'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'coefficient' => 'decimal:4',
|
||||
'ip_permanent' => 'decimal:4',
|
||||
'ip_complexity' => 'decimal:4',
|
||||
'locality_index' => 'decimal:4',
|
||||
'infrastructure_factor' => 'decimal:4',
|
||||
'is_active' => 'boolean'
|
||||
];
|
||||
|
||||
/**
|
||||
* Building type relationship
|
||||
*/
|
||||
public function buildingType(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(BuildingType::class, 'building_type_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope: Active only
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all indices as array
|
||||
*/
|
||||
public function getIndicesArray(): array
|
||||
{
|
||||
return [
|
||||
'ip_permanent' => $this->ip_permanent,
|
||||
'ip_complexity' => $this->ip_complexity,
|
||||
'locality_index' => $this->locality_index,
|
||||
'infrastructure_factor' => $this->infrastructure_factor
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user