add 30%
This commit is contained in:
@@ -26,7 +26,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Assign retribution calculations to spatial plannings (supports recalculate for existing calculations)';
|
||||
protected $description = 'Assign retribution calculations to spatial plannings (recalculate mode applies 30% area adjustment)';
|
||||
|
||||
protected $calculatorService;
|
||||
|
||||
@@ -57,6 +57,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
$q->where('is_active', true);
|
||||
});
|
||||
$this->info('🔄 Recalculate mode: Processing spatial plannings with existing calculations');
|
||||
$this->warn('⚠️ NOTE: Recalculate mode will apply 30% area adjustment to all calculations');
|
||||
} elseif (!$force) {
|
||||
// Normal mode: only process those without active calculations
|
||||
$query->whereDoesntHave('retributionCalculations', function ($q) {
|
||||
@@ -140,6 +141,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
['Errors', $errors],
|
||||
]
|
||||
);
|
||||
$this->info('📊 Recalculate mode applied 30% area adjustment to all calculations');
|
||||
} else {
|
||||
$this->table(
|
||||
['Metric', 'Count'],
|
||||
@@ -189,7 +191,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
|
||||
if ($recalculate) {
|
||||
// Recalculate mode: Always create new calculation
|
||||
$calculationResult = $this->performCalculation($spatialPlanning, $buildingType);
|
||||
$calculationResult = $this->performCalculation($spatialPlanning, $buildingType, true);
|
||||
|
||||
// Check if spatial planning has existing active calculation
|
||||
$currentActiveCalculation = $spatialPlanning->activeRetributionCalculation;
|
||||
@@ -211,9 +213,10 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
]);
|
||||
|
||||
// Assign new calculation
|
||||
$adjustedArea = round($buildingArea * 0.3, 2);
|
||||
$spatialPlanning->assignRetributionCalculation(
|
||||
$calculation,
|
||||
"Recalculated: Area {$oldArea}→{$buildingArea}, Amount {$oldAmount}→{$newAmount}"
|
||||
"Recalculated (30% area): Original area {$oldArea}m² → Adjusted area {$adjustedArea}m², Amount {$oldAmount}→{$newAmount}"
|
||||
);
|
||||
|
||||
$isRecalculated = true;
|
||||
@@ -234,7 +237,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
|
||||
$spatialPlanning->assignRetributionCalculation(
|
||||
$calculation,
|
||||
'Recalculated (new calculation)'
|
||||
'Recalculated (new calculation with 30% area adjustment)'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -255,7 +258,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
$reused = true;
|
||||
} else {
|
||||
// Create new calculation
|
||||
$calculationResult = $this->performCalculation($spatialPlanning, $buildingType);
|
||||
$calculationResult = $this->performCalculation($spatialPlanning, $buildingType, false);
|
||||
|
||||
$calculation = RetributionCalculation::create([
|
||||
'building_type_id' => $buildingType->id,
|
||||
@@ -401,10 +404,18 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
/**
|
||||
* Perform calculation using RetributionCalculatorService
|
||||
*/
|
||||
private function performCalculation(SpatialPlanning $spatialPlanning, BuildingType $buildingType): array
|
||||
private function performCalculation(SpatialPlanning $spatialPlanning, BuildingType $buildingType, bool $recalculate = false): array
|
||||
{
|
||||
// Round area to 2 decimal places to match database storage format
|
||||
$buildingArea = round($spatialPlanning->getCalculationArea(), 2);
|
||||
|
||||
// Apply 30% multiplication for recalculate mode
|
||||
if ($recalculate) {
|
||||
$originalArea = $buildingArea;
|
||||
$buildingArea = round($buildingArea * 0.3, 2); // 30% of original area
|
||||
$this->info("Recalculate mode: Original area {$originalArea}m² → Adjusted area {$buildingArea}m² (30%)");
|
||||
}
|
||||
|
||||
$floorNumber = $spatialPlanning->number_of_floors ?: 1;
|
||||
|
||||
try {
|
||||
@@ -429,6 +440,8 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
'height_index' => $result['input_parameters']['height_index'],
|
||||
'infrastructure_factor' => $result['indices']['infrastructure_factor'],
|
||||
'building_area' => $buildingArea,
|
||||
'original_building_area' => $recalculate ? round($spatialPlanning->getCalculationArea(), 2) : null,
|
||||
'area_adjustment_factor' => $recalculate ? 0.3 : null,
|
||||
'floor_number' => $floorNumber,
|
||||
'building_function' => $spatialPlanning->building_function,
|
||||
'calculation_steps' => $result['calculation_detail'],
|
||||
@@ -436,6 +449,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
'is_free' => $buildingType->is_free,
|
||||
'calculation_date' => now()->toDateTimeString(),
|
||||
'total' => $result['total_retribution'],
|
||||
'is_recalculated' => $recalculate,
|
||||
]
|
||||
];
|
||||
|
||||
@@ -446,6 +460,13 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
// Basic fallback calculation
|
||||
$totalAmount = $buildingType->is_free ? 0 : ($buildingArea * 50000);
|
||||
|
||||
// Apply 30% multiplication for recalculate mode in fallback too
|
||||
if ($recalculate) {
|
||||
$originalAmount = $totalAmount;
|
||||
$totalAmount = round($totalAmount * 0.3, 2);
|
||||
$this->warn("Fallback recalculate: Original amount Rp{$originalAmount} → Adjusted amount Rp{$totalAmount} (30%)");
|
||||
}
|
||||
|
||||
return [
|
||||
'amount' => $totalAmount,
|
||||
'detail' => [
|
||||
@@ -453,6 +474,8 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
'building_type_name' => $buildingType->name,
|
||||
'building_type_code' => $buildingType->code,
|
||||
'building_area' => $buildingArea,
|
||||
'original_building_area' => $recalculate ? round($spatialPlanning->getCalculationArea(), 2) : null,
|
||||
'area_adjustment_factor' => $recalculate ? 0.3 : null,
|
||||
'floor_number' => $floorNumber,
|
||||
'building_function' => $spatialPlanning->building_function,
|
||||
'calculation_method' => 'fallback',
|
||||
@@ -460,6 +483,7 @@ class AssignSpatialPlanningsToCalculation extends Command
|
||||
'is_free' => $buildingType->is_free,
|
||||
'calculation_date' => now()->toDateTimeString(),
|
||||
'total' => $totalAmount,
|
||||
'is_recalculated' => $recalculate,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ class InjectSpatialPlanningsData extends Command
|
||||
{--file=storage/app/public/templates/2025.xlsx : Path to Excel file}
|
||||
{--sheet=0 : Sheet index to read from}
|
||||
{--dry-run : Run without actually inserting data}
|
||||
{--debug : Show Excel content for debugging}';
|
||||
{--debug : Show Excel content for debugging}
|
||||
{--truncate : Clear existing data before import}
|
||||
{--no-truncate : Skip truncation (keep existing data)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@@ -39,6 +41,8 @@ class InjectSpatialPlanningsData extends Command
|
||||
$sheetIndex = (int) $this->option('sheet');
|
||||
$isDryRun = $this->option('dry-run');
|
||||
$isDebug = $this->option('debug');
|
||||
$shouldTruncate = $this->option('truncate');
|
||||
$noTruncate = $this->option('no-truncate');
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
$this->error("File not found: {$filePath}");
|
||||
@@ -52,10 +56,86 @@ class InjectSpatialPlanningsData extends Command
|
||||
$this->warn("DRY RUN MODE - No data will be inserted");
|
||||
}
|
||||
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
|
||||
DB::table('spatial_plannings')->truncate();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
||||
$this->info('Spatial plannings table truncated successfully.');
|
||||
// Check existing data
|
||||
$existingCount = DB::table('spatial_plannings')->count();
|
||||
if ($existingCount > 0) {
|
||||
$this->info("Found {$existingCount} existing spatial planning records");
|
||||
} else {
|
||||
$this->info('No existing spatial planning data found');
|
||||
}
|
||||
|
||||
// Handle truncation logic
|
||||
$willTruncate = false;
|
||||
|
||||
if ($shouldTruncate) {
|
||||
$willTruncate = true;
|
||||
$this->info('Truncation requested via --truncate option');
|
||||
} elseif ($noTruncate) {
|
||||
$willTruncate = false;
|
||||
$this->info('Truncation skipped via --no-truncate option');
|
||||
} else {
|
||||
// Default behavior: ask user if not in dry run mode
|
||||
if (!$isDryRun) {
|
||||
$willTruncate = $this->confirm('Do you want to clear existing spatial planning data before import?');
|
||||
} else {
|
||||
$willTruncate = false;
|
||||
$this->info('DRY RUN MODE - Truncation will be skipped');
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm truncation if not in dry run mode and truncation is requested
|
||||
if ($willTruncate && !$isDryRun) {
|
||||
if (!$this->confirm('This will delete all existing spatial planning data and related retribution calculations. Continue?')) {
|
||||
$this->info('Operation cancelled.');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate all related data properly
|
||||
if ($willTruncate && !$isDryRun) {
|
||||
$this->info('Truncating spatial planning data and related retribution calculations...');
|
||||
|
||||
try {
|
||||
// Disable foreign key checks for safe truncation
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
|
||||
|
||||
// 1. Delete calculable retributions for spatial plannings (polymorphic relationship)
|
||||
$deletedCalculableRetributions = DB::table('calculable_retributions')
|
||||
->where('calculable_type', 'App\\Models\\SpatialPlanning')
|
||||
->count();
|
||||
|
||||
if ($deletedCalculableRetributions > 0) {
|
||||
DB::table('calculable_retributions')
|
||||
->where('calculable_type', 'App\\Models\\SpatialPlanning')
|
||||
->delete();
|
||||
$this->info("Deleted {$deletedCalculableRetributions} calculable retributions for spatial plannings.");
|
||||
}
|
||||
|
||||
// 2. Truncate spatial plannings table
|
||||
DB::table('spatial_plannings')->truncate();
|
||||
$this->info('Spatial plannings table truncated successfully.');
|
||||
|
||||
// Re-enable foreign key checks
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
||||
|
||||
$this->info('All spatial planning data and related retribution calculations cleared successfully.');
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Make sure to re-enable foreign key checks even on error
|
||||
try {
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
|
||||
} catch (Exception $fkError) {
|
||||
$this->error('Failed to re-enable foreign key checks: ' . $fkError->getMessage());
|
||||
}
|
||||
|
||||
$this->error('Failed to truncate spatial planning data: ' . $e->getMessage());
|
||||
return 1;
|
||||
}
|
||||
} elseif ($willTruncate && $isDryRun) {
|
||||
$this->info('DRY RUN MODE - Would truncate spatial planning data and related retribution calculations');
|
||||
} else {
|
||||
$this->info('Keeping existing data (no truncation)');
|
||||
}
|
||||
|
||||
$spreadsheet = IOFactory::load($filePath);
|
||||
$worksheet = $spreadsheet->getSheet($sheetIndex);
|
||||
@@ -97,8 +177,23 @@ class InjectSpatialPlanningsData extends Command
|
||||
|
||||
if (!$isDryRun) {
|
||||
$this->info("Successfully inserted {$totalInserted} spatial planning records");
|
||||
|
||||
// Show summary of what was done
|
||||
$finalCount = DB::table('spatial_plannings')->count();
|
||||
$this->info("Final spatial planning records count: {$finalCount}");
|
||||
|
||||
if ($willTruncate) {
|
||||
$this->info("✅ Data import completed with truncation");
|
||||
} else {
|
||||
$this->info("✅ Data import completed (existing data preserved)");
|
||||
}
|
||||
} else {
|
||||
$this->info("Dry run completed. Total records that would be inserted: " . count($sections));
|
||||
if ($willTruncate) {
|
||||
$this->info("Would truncate existing data before import");
|
||||
} else {
|
||||
$this->info("Would preserve existing data during import");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -574,8 +669,6 @@ class InjectSpatialPlanningsData extends Command
|
||||
strpos($activitiesLower, 'perdaganagan') !== false ||
|
||||
strpos($activitiesLower, 'waterpark') !== false ||
|
||||
strpos($activitiesLower, 'pasar') !== false ||
|
||||
strpos($activitiesLower, 'perumahan') !== false ||
|
||||
strpos($activitiesLower, 'perumhan') !== false ||
|
||||
strpos($activitiesLower, 'kantor') !== false) {
|
||||
|
||||
$buildingFunction = 'Fungsi Usaha';
|
||||
@@ -601,6 +694,8 @@ class InjectSpatialPlanningsData extends Command
|
||||
// Determine housing type based on area and keywords
|
||||
if (strpos($activitiesLower, 'mbr') !== false ||
|
||||
strpos($activitiesLower, 'masyarakat berpenghasilan rendah') !== false ||
|
||||
strpos($activitiesLower, 'perumahan') !== false ||
|
||||
strpos($activitiesLower, 'perumhan') !== false ||
|
||||
strpos($activitiesLower, 'sederhana') !== false ||
|
||||
($landArea > 0 && $landArea < 2000)) { // Small area indicates MBR
|
||||
|
||||
|
||||
@@ -537,7 +537,7 @@ class ServiceGoogleSheet
|
||||
foreach ($spatialPlannings as $spatialPlanning) {
|
||||
$activeCalculation = $spatialPlanning->activeRetributionCalculation;
|
||||
if ($activeCalculation && $activeCalculation->retributionCalculation) {
|
||||
$totalSum += $activeCalculation->retributionCalculation->retribution_amount;
|
||||
$totalSum += $activeCalculation->retributionCalculation->retribution_amount * 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class RetributionDataSeeder extends Seeder
|
||||
['id' => 9, 'code' => 'USH_BESAR', 'name' => 'Usaha Besar (Non-Mikro)', 'parent_id' => 4, 'level' => 2, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['id' => 10, 'code' => 'HUN_SEDH', 'name' => 'Hunian Sederhana <100', 'parent_id' => 5, 'level' => 2, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['id' => 11, 'code' => 'HUN_TSEDH', 'name' => 'Hunian Tidak Sederhana >100', 'parent_id' => 5, 'level' => 2, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['id' => 12, 'code' => 'MBR', 'name' => 'Rumah Tinggal MBR', 'parent_id' => 5, 'level' => 2, 'is_free' => true, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['id' => 12, 'code' => 'MBR', 'name' => 'Rumah Tinggal MBR', 'parent_id' => 5, 'level' => 2, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
]);
|
||||
|
||||
// Seed Retribution Indices berdasarkan Excel (with coefficient moved here)
|
||||
|
||||
Reference in New Issue
Block a user