fix pbg payments

This commit is contained in:
arifal
2025-08-19 22:00:20 +07:00
parent 1bcd2023da
commit 4b28bebcc2
9 changed files with 563 additions and 16 deletions

View File

@@ -22,3 +22,20 @@ BigdataResume::generateResumeData(253,2025,"simbg");
exit
BigdataResume::generateResumeData(253,2025,"simbg");
exit
$importDatasource = \App\Models\ImportDatasource::create([
'message' => 'Testing BigdataResume Generation',
'status' => 'success',
'start_time' => now(),
'finish_time' => now()
]);
$bigdataresume = BigdataResume::generateResumeData($importDatasource->id, 2025, 'simbg');
$bigdataresume = BigdataResume::generateResumeData($importDatasource->id, 2025, 'simbg');
exit
$importDatasource = \App\Models\ImportDatasource::create([
'message' => 'Testing BigdataResume Generation',
'status' => 'success',
'start_time' => now(),
'finish_time' => now()
]);
$bigdataresume = BigdataResume::generateResumeData($importDatasource->id, 2025, 'simbg');
exit

View File

@@ -0,0 +1,88 @@
<?php
namespace App\Console\Commands;
use App\Services\ServiceGoogleSheet;
use Illuminate\Console\Command;
class SyncPbgTaskPayments extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'sync:pbg-payments';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Sync PBG task payments from Google Sheets REALISASI PAD';
/**
* Execute the console command.
*/
public function handle()
{
$this->info('🚀 Starting PBG Task Payments sync...');
$this->newLine();
try {
$service = new ServiceGoogleSheet();
// Show progress bar
$this->info('📊 Fetching data from Google Sheets...');
$result = $service->sync_pbg_task_payments();
// Display results
$this->newLine();
$this->info('✅ Sync completed successfully!');
$this->newLine();
$this->table(
['Metric', 'Count'],
[
['Total rows processed', $result['total_rows']],
['Successful syncs', $result['successful_syncs']],
['Failed syncs', $result['failed_syncs']],
['Tasks not found', $result['not_found_tasks']],
]
);
// Show success rate
$success_rate = $result['total_rows'] > 0
? round(($result['successful_syncs'] / $result['total_rows']) * 100, 2)
: 0;
$this->info("📈 Success rate: {$success_rate}%");
// Show errors if any
if (!empty($result['errors'])) {
$this->newLine();
$this->warn('⚠️ Errors encountered:');
foreach (array_slice($result['errors'], 0, 5) as $error) {
$this->line(" Row {$error['row']} ({$error['registration_number']}): {$error['error']}");
}
if (count($result['errors']) > 5) {
$remaining = count($result['errors']) - 5;
$this->line(" ... and {$remaining} more errors (check logs for details)");
}
}
$this->newLine();
$this->info('📝 Check Laravel logs for detailed information.');
return Command::SUCCESS;
} catch (\Exception $e) {
$this->newLine();
$this->error('❌ Sync failed!');
$this->error('Error: ' . $e->getMessage());
return Command::FAILURE;
}
}
}

View File

@@ -9,6 +9,7 @@ use App\Http\Resources\BigdataResumeResource;
use App\Models\BigdataResume;
use App\Models\DataSetting;
use App\Models\SpatialPlanning;
use App\Models\PbgTaskPayment;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use Illuminate\Http\Request;
@@ -59,6 +60,11 @@ class BigDataResumeController extends Controller
$tata_ruang = $spatialData['sum'];
$tata_ruang_count = $spatialData['count'];
// Get real-time PBG Task Payments data
$pbgPaymentsData = $this->getPbgTaskPaymentsData();
$pbg_task_payments_sum = $pbgPaymentsData['sum'];
$pbg_task_payments_count = $pbgPaymentsData['count'];
$kekurangan_potensi = $target_pad - $big_data_resume->potention_sum;
// percentage kekurangan potensi
@@ -107,6 +113,10 @@ class BigDataResumeController extends Controller
$proses_dinas_teknis_percentage = $big_data_resume->verified_sum > 0 && $proses_dinas_teknis_sum >= 0
? round(($proses_dinas_teknis_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
// percentage pbg_task_payments (payments / verified)
$pbg_task_payments_percentage = $realisasi_terbit_pbg_sum > 0 && $pbg_task_payments_sum >= 0
? round(($pbg_task_payments_sum / $realisasi_terbit_pbg_sum) * 100, 2) : 0;
$business_rab_count = $big_data_resume->business_rab_count;
$business_krk_count = $big_data_resume->business_krk_count;
$non_business_rab_count = $big_data_resume->non_business_rab_count;
@@ -171,7 +181,12 @@ class BigDataResumeController extends Controller
'business_krk_count' => $business_krk_count,
'non_business_rab_count' => $non_business_rab_count,
'non_business_krk_count' => $non_business_krk_count,
'business_dlh_count' => $business_dlh_count
'business_dlh_count' => $business_dlh_count,
'pbg_task_payments' => [
'sum' => (float) $pbg_task_payments_sum,
'count' => $pbg_task_payments_count,
'percentage' => $pbg_task_payments_percentage
]
];
return response()->json($result);
}catch(\Exception $e){
@@ -398,6 +413,11 @@ class BigDataResumeController extends Controller
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'pbg_task_payments' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
]
];
@@ -457,4 +477,33 @@ class BigDataResumeController extends Controller
];
}
}
/**
* Get PBG Task Payments data from database
*/
private function getPbgTaskPaymentsData(): array
{
try {
// Get sum and count from PbgTaskPayment model
$totalSum = PbgTaskPayment::whereYear('payment_date', date('Y'))->sum('pad_amount') ?? 0;
$totalCount = PbgTaskPayment::whereYear('payment_date', date('Y'))->count() ?? 0;
Log::info("Real-time PBG Task Payments Data", [
'total_records' => $totalCount,
'total_sum' => $totalSum,
'source' => 'pbg_task_payments table'
]);
return [
'sum' => (float) $totalSum,
'count' => (int) $totalCount,
];
} catch (\Exception $e) {
Log::error("Error getting PBG task payments data", ['error' => $e->getMessage()]);
return [
'sum' => 0.0,
'count' => 0,
];
}
}
}

View File

@@ -314,20 +314,19 @@ class BigdataResume extends Model
])
]);
// Get sum values using proper aggregation to handle multiple retributions
// Calculate totals using count-based formula
// Business: $business_count * 200 * 44300
// Non-Business: $non_business_count * 72 * 16000
$business_total = $business_count * 200 * 44300;
$non_business_total = $non_business_count * 72 * 16000;
$non_verified_total = $business_total + $non_business_total;
// Get other sum values using proper aggregation to handle multiple retributions
$stats = PbgTask::leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid')
->where('pbg_task.is_valid', true)
->whereYear('pbg_task.task_created_at', $year)
->selectRaw("
SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getVerified()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS verified_total,
SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS non_verified_total,
SUM(CASE WHEN (LOWER(TRIM(pbg_task.function_type)) LIKE '%fungsi usaha%'
OR LOWER(TRIM(pbg_task.function_type)) LIKE '%sebagai tempat usaha%')
AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS business_total,
SUM(CASE WHEN (LOWER(TRIM(pbg_task.function_type)) NOT LIKE '%fungsi usaha%'
AND LOWER(TRIM(pbg_task.function_type)) NOT LIKE '%sebagai tempat usaha%'
AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified())."))
OR (pbg_task.function_type IS NULL AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).")) THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS non_business_total,
SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getWaitingClickDpmptsp()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS waiting_click_dpmptsp_total,
SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getIssuanceRealizationPbg()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS issuance_realization_pbg_total,
SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getProcessInTechnicalOffice()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS process_in_technical_office_total,
@@ -338,7 +337,11 @@ class BigdataResume extends Model
->first();
\Log::info('Stats calculation result', [
'non_verified_total' => $stats->non_verified_total ?? 'NULL',
'business_count' => $business_count,
'non_business_count' => $non_business_count,
'business_total' => $business_total,
'non_business_total' => $non_business_total,
'non_verified_total' => $non_verified_total,
'non_verified_tasks_count' => $stats->non_verified_tasks_count ?? 'NULL',
'non_verified_with_retribution_count' => $stats->non_verified_with_retribution_count ?? 'NULL'
]);
@@ -352,13 +355,13 @@ class BigdataResume extends Model
'potention_count' => $potention_count,
'potention_sum' => ($stats->potention_total ?? 0),
'non_verified_count' => $non_verified_count,
'non_verified_sum' => $stats->non_verified_total ?? 0.00,
'non_verified_sum' => $non_verified_total,
'verified_count' => $verified_count,
'verified_sum' => $stats->verified_total ?? 0.00,
'business_count' => $business_count,
'business_sum' => $stats->business_total ?? 0.00,
'business_sum' => $business_total,
'non_business_count' => $non_business_count,
'non_business_sum' => $stats->non_business_total ?? 0.00,
'non_business_sum' => $non_business_total,
'year' => $year,
'waiting_click_dpmptsp_count' => $waiting_click_dpmptsp_count,
'waiting_click_dpmptsp_sum' => $stats->waiting_click_dpmptsp_total ?? 0.00,

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Carbon\Carbon;
class PbgTaskPayment extends Model
{
protected $fillable = [
'pbg_task_id',
'pbg_task_uid',
'registration_number',
'sts_form_number',
'payment_date',
'pad_amount'
];
protected $casts = [
'payment_date' => 'date',
'pad_amount' => 'decimal:2'
];
/**
* Get the PBG task that owns this payment
*/
public function pbgTask(): BelongsTo
{
return $this->belongsTo(PbgTask::class, 'pbg_task_id', 'id');
}
/**
* Clean and convert registration number for matching
*/
public static function cleanRegistrationNumber(string $registrationNumber): string
{
return trim($registrationNumber);
}
/**
* Convert pad amount string to decimal
*/
public static function convertPadAmount(?string $padAmount): float
{
if (empty($padAmount)) {
return 0.0;
}
// Remove dots (thousands separator) and convert to float
$cleaned = str_replace('.', '', $padAmount);
$cleaned = str_replace(',', '.', $cleaned); // Handle comma as decimal separator if present
return is_numeric($cleaned) ? (float) $cleaned : 0.0;
}
/**
* Convert date string to proper format
*/
public static function convertPaymentDate(?string $dateString): ?string
{
if (empty($dateString)) {
return null;
}
try {
return Carbon::parse($dateString)->format('Y-m-d');
} catch (\Exception $e) {
return null;
}
}
}

View File

@@ -427,6 +427,203 @@ class ServiceGoogleSheet
}
}
public function get_realisasi_pad_data(){
try {
// Get data from "REALISASI PAD" sheet
$sheet_data = $this->get_data_by_sheet_name("REALISASI PAD");
if (empty($sheet_data)) {
Log::warning("No data found in REALISASI PAD sheet");
return [];
}
// Column indices: C=2, AK=36, AL=37, AW=48 (0-based)
$columns = [
'C' => 2,
'AK' => 36,
'AL' => 37,
'AW' => 48
];
$result = [
'headers' => [],
'data' => []
];
foreach ($sheet_data as $row_index => $row) {
if (!is_array($row)) continue;
if ($row_index === 0) {
// First row contains headers
foreach ($columns as $column_name => $column_index) {
$result['headers'][$column_name] = isset($row[$column_index]) ? trim($row[$column_index]) : '';
}
} else {
// Data rows
$row_data = [
'row' => $row_index + 1 // 1-based row number
];
$has_data = false;
foreach ($columns as $column_name => $column_index) {
$value = isset($row[$column_index]) ? trim($row[$column_index]) : '';
$row_data[$column_name] = $value;
if ($value !== '') {
$has_data = true;
}
}
// Only add row if it has at least one non-empty value
if ($has_data) {
$result['data'][] = $row_data;
}
}
}
Log::info("REALISASI PAD Multiple Columns Data", [
'sheet_name' => 'REALISASI PAD',
'columns' => array_keys($columns),
'headers' => $result['headers'],
'total_data_rows' => count($result['data']),
'sample_data' => array_slice($result['data'], 0, 5), // Show first 5 rows as sample
'column_indices' => $columns
]);
return $result;
} catch (\Exception $e) {
Log::error("Error getting REALISASI PAD data", ['error' => $e->getMessage()]);
throw $e;
}
}
public function sync_pbg_task_payments(){
try {
// Get payment data from REALISASI PAD sheet
$payment_data = $this->get_realisasi_pad_data();
if (empty($payment_data['data'])) {
Log::warning("No payment data found to sync");
return ['success' => false, 'message' => 'No payment data found'];
}
$successful_syncs = 0;
$failed_syncs = 0;
$not_found_tasks = 0;
$not_found_tasks_registration_number = [];
$failed_sync_registration_numbers = [];
$errors = [];
foreach ($payment_data['data'] as $row) {
try {
// Clean registration number from column C
$registration_number = \App\Models\PbgTaskPayment::cleanRegistrationNumber($row['C']);
if (empty($registration_number)) {
$failed_syncs++;
$failed_sync_registration_numbers[] = [
'row' => $row['row'],
'registration_number' => $row['C'] ?? 'EMPTY',
'reason' => 'Empty registration number'
];
continue;
}
// Find PBG task by registration number
$pbg_task = \App\Models\PbgTask::where('registration_number', $registration_number)->first();
if (!$pbg_task) {
$not_found_tasks_registration_number[] = [
'row' => $row['row'],
'registration_number' => $registration_number
];
$not_found_tasks++;
Log::warning("PBG Task not found for registration number", [
'registration_number' => $registration_number,
'row' => $row['row']
]);
continue;
}
// Convert data types
$payment_date = \App\Models\PbgTaskPayment::convertPaymentDate($row['AK']);
$pad_amount = \App\Models\PbgTaskPayment::convertPadAmount($row['AW']);
$sts_form_number = !empty($row['AL']) ? trim($row['AL']) : null;
// Create or update payment record
\App\Models\PbgTaskPayment::updateOrCreate(
[
'pbg_task_id' => $pbg_task->id,
'registration_number' => $registration_number
],
[
'pbg_task_uid' => $pbg_task->uuid,
'sts_form_number' => $sts_form_number,
'payment_date' => $payment_date,
'pad_amount' => $pad_amount
]
);
$successful_syncs++;
} catch (\Exception $e) {
$failed_syncs++;
$registration_number = $row['C'] ?? 'N/A';
$failed_sync_registration_numbers[] = [
'row' => $row['row'],
'registration_number' => $registration_number,
'reason' => $e->getMessage()
];
$errors[] = [
'row' => $row['row'],
'registration_number' => $registration_number,
'error' => $e->getMessage()
];
Log::error("Error syncing payment data for row", [
'row' => $row['row'],
'registration_number' => $registration_number,
'error' => $e->getMessage()
]);
}
}
$result = [
'success' => true,
'total_rows' => count($payment_data['data']),
'successful_syncs' => $successful_syncs,
'failed_syncs' => $failed_syncs,
'not_found_tasks' => $not_found_tasks,
'not_found_tasks_registration_number' => $not_found_tasks_registration_number,
'failed_sync_registration_numbers' => $failed_sync_registration_numbers,
'errors' => $errors
];
Log::info("PBG Task Payments sync completed", $result);
// Log detailed arrays for failed syncs and not found registration numbers
if (!empty($failed_sync_registration_numbers)) {
Log::warning("Failed Sync Registration Numbers", [
'count' => count($failed_sync_registration_numbers),
'details' => $failed_sync_registration_numbers
]);
}
if (!empty($not_found_tasks_registration_number)) {
Log::warning("Not Found Registration Numbers", [
'count' => count($not_found_tasks_registration_number),
'details' => $not_found_tasks_registration_number
]);
}
return $result;
} catch (\Exception $e) {
Log::error("Error syncing PBG task payments", ['error' => $e->getMessage()]);
throw $e;
}
}
private function get_data_by_sheet($no_sheet = 8){
$spreadsheet = $this->service->spreadsheets->get($this->spreadsheetID);
$sheets = $spreadsheet->getSheets();
@@ -437,6 +634,45 @@ class ServiceGoogleSheet
return!empty($values)? $values : [];
}
private function get_data_by_sheet_name($sheet_name){
try {
$spreadsheet = $this->service->spreadsheets->get($this->spreadsheetID);
$sheets = $spreadsheet->getSheets();
// Find sheet by name
$targetSheet = null;
foreach ($sheets as $sheet) {
if ($sheet->getProperties()->getTitle() === $sheet_name) {
$targetSheet = $sheet;
break;
}
}
if (!$targetSheet) {
Log::warning("Sheet not found", ['sheet_name' => $sheet_name]);
return [];
}
$range = "{$sheet_name}";
$response = $this->service->spreadsheets_values->get($this->spreadsheetID, $range);
$values = $response->getValues();
Log::info("Sheet data retrieved", [
'sheet_name' => $sheet_name,
'total_rows' => count($values ?? [])
]);
return !empty($values) ? $values : [];
} catch (\Exception $e) {
Log::error("Error getting data by sheet name", [
'sheet_name' => $sheet_name,
'error' => $e->getMessage()
]);
return [];
}
}
/**
* Get specific values from a row that contains a specific text/section identifier
* @param string $section_identifier Text to search for in the row

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('pbg_task_payments', function (Blueprint $table) {
$table->id();
$table->foreignId('pbg_task_id')->constrained('pbg_task', 'id')->onDelete('cascade');
$table->string('pbg_task_uid');
$table->string('registration_number');
$table->string('sts_form_number')->nullable();
$table->date('payment_date')->nullable();
$table->decimal('pad_amount', 12, 2)->default(0);
$table->timestamps();
// Add index for better performance
$table->index('pbg_task_id');
$table->index('pbg_task_uid');
$table->index('registration_number');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('pbg_task_payments');
}
};

View File

@@ -535,9 +535,7 @@ class BigData {
});
}
initChartNonBusinessDLH() {
document
.querySelectorAll("#non-business-dlh-count")
.forEach((element) => {
document.querySelectorAll("#business-dlh-count").forEach((element) => {
const count = this.safeGet(
this.resumeBigData,
"business_dlh_count",
@@ -546,6 +544,41 @@ class BigData {
element.innerText = `${count}`;
});
}
initChartPotensiTataRuang() {
document
.querySelectorAll(".document-count.chart-payment-pbg-task")
.forEach((element) => {
const count = this.safeGet(
this.resumeBigData,
"pbg_task_payments.count",
0
);
element.innerText = `${count}`;
});
document
.querySelectorAll(".document-total.chart-payment-pbg-task")
.forEach((element) => {
const sum = this.safeGet(
this.resumeBigData,
"pbg_task_payments.sum",
0
);
element.innerText = `Rp.${addThousandSeparators(
sum.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-payment-pbg-task")
.forEach((element) => {
const percentage = this.safeGet(
this.resumeBigData,
"pbg_task_payments.percentage",
0
);
element.innerText = `${percentage}%`;
});
}
}
document.addEventListener("DOMContentLoaded", async function (e) {

View File

@@ -202,6 +202,17 @@
])
@endcomponent
@component('components.circle',[
'document_title' => 'Pembayaran Realisasi PBG',
'document_color' => '#8cc540',
'document_type' => 'Berkas',
'document_id' => 'chart-payment-pbg-task',
'visible_small_circle' => false,
'style' => 'top:550px;left:-150px;',
'document_url' => '#'
])
@endcomponent
<div class="square" style="top:650px;left:200px;width:250px;height:2px;background-color:black;">
</div>