create page tax with data, upload and export group by subdistrict
This commit is contained in:
59
app/Exports/TaxSubdistrictSheetExport.php
Normal file
59
app/Exports/TaxSubdistrictSheetExport.php
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Tax;
|
||||||
|
use Maatwebsite\Excel\Concerns\FromCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithTitle;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||||
|
|
||||||
|
class TaxSubdistrictSheetExport implements FromCollection, WithTitle, WithHeadings
|
||||||
|
{
|
||||||
|
protected $subdistrict;
|
||||||
|
|
||||||
|
public function __construct(string $subdistrict)
|
||||||
|
{
|
||||||
|
$this->subdistrict = $subdistrict;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function collection()
|
||||||
|
{
|
||||||
|
return Tax::where('subdistrict', $this->subdistrict)
|
||||||
|
->select(
|
||||||
|
'tax_code',
|
||||||
|
'tax_no',
|
||||||
|
'npwpd',
|
||||||
|
'wp_name',
|
||||||
|
'business_name',
|
||||||
|
'address',
|
||||||
|
'start_validity',
|
||||||
|
'end_validity',
|
||||||
|
'tax_value',
|
||||||
|
'subdistrict',
|
||||||
|
'village'
|
||||||
|
)->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function headings(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Kode',
|
||||||
|
'No',
|
||||||
|
'NPWPD',
|
||||||
|
'Nama WP',
|
||||||
|
'Nama Usaha',
|
||||||
|
'Alamat Usaha',
|
||||||
|
'Tanggal Mulai Berlaku',
|
||||||
|
'Tanggal Berakhir Berlaku',
|
||||||
|
'Nilai Pajak',
|
||||||
|
'Kecamatan',
|
||||||
|
'Desa'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function title(): string
|
||||||
|
{
|
||||||
|
return mb_substr($this->subdistrict, 0, 31);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
23
app/Exports/TaxationsExport.php
Normal file
23
app/Exports/TaxationsExport.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exports;
|
||||||
|
|
||||||
|
use App\Models\Tax;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||||
|
|
||||||
|
class TaxationsExport implements WithMultipleSheets
|
||||||
|
{
|
||||||
|
public function sheets(): array
|
||||||
|
{
|
||||||
|
$sheets = [];
|
||||||
|
|
||||||
|
// Ambil semua subdistrict unik
|
||||||
|
$subdistricts = Tax::select('subdistrict')->distinct()->pluck('subdistrict');
|
||||||
|
|
||||||
|
foreach ($subdistricts as $subdistrict) {
|
||||||
|
$sheets[] = new TaxSubdistrictSheetExport($subdistrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sheets;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ use App\Http\Resources\CustomersResource;
|
|||||||
use App\Imports\CustomersImport;
|
use App\Imports\CustomersImport;
|
||||||
use App\Models\Customer;
|
use App\Models\Customer;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Maatwebsite\Excel\Facades\Excel;
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
|
||||||
class CustomersController extends Controller
|
class CustomersController extends Controller
|
||||||
@@ -120,7 +121,7 @@ class CustomersController extends Controller
|
|||||||
'message' => 'File uploaded successfully',
|
'message' => 'File uploaded successfully',
|
||||||
]);
|
]);
|
||||||
}catch(\Exception $e){
|
}catch(\Exception $e){
|
||||||
\Log::info($e->getMessage());
|
Log::info($e->getMessage());
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'error' => 'Failed to upload file',
|
'error' => 'Failed to upload file',
|
||||||
'message' => $e->getMessage()
|
'message' => $e->getMessage()
|
||||||
|
|||||||
63
app/Http/Controllers/Api/TaxationsController.php
Normal file
63
app/Http/Controllers/Api/TaxationsController.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use App\Exports\TaxationsExport;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\ExcelUploadRequest;
|
||||||
|
use App\Http\Resources\TaxationsResource;
|
||||||
|
use App\Imports\TaxationsImport;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Tax;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Maatwebsite\Excel\Facades\Excel;
|
||||||
|
|
||||||
|
class TaxationsController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
$query = Tax::query()->orderBy('id', 'desc');
|
||||||
|
|
||||||
|
if($request->has('search') && !empty($request->get('search'))){
|
||||||
|
$query->where('tax_no', 'like', '%'. $request->get('search') . '%')
|
||||||
|
->orWhere('wp_name', 'like', '%'. $request->get('search') . '%')
|
||||||
|
->orWhere('business_name', 'like', '%'. $request->get('search') . '%');
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaxationsResource::collection($query->paginate(config('app.paginate_per_page', 50)));
|
||||||
|
}catch(\Exception $e){
|
||||||
|
Log::info($e->getMessage());
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'Failed to get data',
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function upload(ExcelUploadRequest $request)
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
if(!$request->hasFile('file')){
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'No file provided'
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $request->file('file');
|
||||||
|
Excel::import(new TaxationsImport, $file);
|
||||||
|
return response()->json(['message' => 'File uploaded successfully'], 200);
|
||||||
|
}catch(\Exception $e){
|
||||||
|
Log::info($e->getMessage());
|
||||||
|
return response()->json([
|
||||||
|
'error' => 'Failed to upload file',
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
], 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function export(Request $request)
|
||||||
|
{
|
||||||
|
return Excel::download(new TaxationsExport, 'pajak_per_kecamatan.xlsx');
|
||||||
|
}
|
||||||
|
}
|
||||||
75
app/Http/Controllers/TaxationController.php
Normal file
75
app/Http/Controllers/TaxationController.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TaxationController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
|
||||||
|
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
|
||||||
|
$creator = $permissions['allow_create'] ?? 0;
|
||||||
|
$updater = $permissions['allow_update'] ?? 0;
|
||||||
|
$destroyer = $permissions['allow_destroy'] ?? 0;
|
||||||
|
return view('taxation.index', compact('creator', 'updater', 'destroyer', 'menuId'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function upload(Request $request)
|
||||||
|
{
|
||||||
|
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
|
||||||
|
return view('taxation.upload', compact('menuId'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for creating a new resource.
|
||||||
|
*/
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource.
|
||||||
|
*/
|
||||||
|
public function show(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the form for editing the specified resource.
|
||||||
|
*/
|
||||||
|
public function edit(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(Request $request, string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy(string $id)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
19
app/Http/Resources/TaxationsResource.php
Normal file
19
app/Http/Resources/TaxationsResource.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class TaxationsResource extends JsonResource
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return parent::toArray($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
78
app/Imports/TaxationsImport.php
Normal file
78
app/Imports/TaxationsImport.php
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Imports;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Maatwebsite\Excel\Concerns\ToCollection;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithBatchInserts;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithChunkReading;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||||
|
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use App\Models\Tax;
|
||||||
|
|
||||||
|
class TaxationsImport implements ToCollection, WithMultipleSheets, WithChunkReading, WithBatchInserts, ShouldQueue, WithHeadingRow
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Collection $collection
|
||||||
|
*/
|
||||||
|
public function collection(Collection $collection)
|
||||||
|
{
|
||||||
|
$batchData = [];
|
||||||
|
$batchSize = 1000;
|
||||||
|
|
||||||
|
foreach ($collection as $row) {
|
||||||
|
|
||||||
|
$masaPajak = trim($row['masa_pajak']) ?? '';
|
||||||
|
|
||||||
|
$masaParts = explode('-', $masaPajak);
|
||||||
|
|
||||||
|
$startValidity = null;
|
||||||
|
$endValidity = null;
|
||||||
|
|
||||||
|
if (count($masaParts) === 2) {
|
||||||
|
$startValidity = \Carbon\Carbon::createFromFormat('d/m/Y', trim($masaParts[0]))->format('Y-m-d');
|
||||||
|
$endValidity = \Carbon\Carbon::createFromFormat('d/m/Y', trim($masaParts[1]))->format('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
$batchData[] = [
|
||||||
|
'tax_code' => trim($row['kode']) ?? '',
|
||||||
|
'tax_no' => trim($row['no']) ?? '',
|
||||||
|
'npwpd' => trim($row['npwpd']) ?? '',
|
||||||
|
'wp_name' => trim($row['nama_wp']) ?? '',
|
||||||
|
'business_name' => trim($row['nama_usaha']) ?? '',
|
||||||
|
'address' => trim($row['alamat_usaha']) ?? '',
|
||||||
|
'start_validity' => $startValidity,
|
||||||
|
'end_validity' => $endValidity,
|
||||||
|
'tax_value' => (float) str_replace(',', '', trim($row['nilai_pajak']) ?? '0'),
|
||||||
|
'subdistrict' => trim($row['kecamatan']) ?? '',
|
||||||
|
'village' => trim($row['desa']) ?? '',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (count($batchData) >= $batchSize) {
|
||||||
|
Tax::upsert($batchData, ['tax_no'], ['tax_code', 'tax_no', 'npwpd', 'wp_name', 'business_name', 'address', 'start_validity', 'end_validity', 'tax_value', 'subdistrict', 'village']);
|
||||||
|
$batchData = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($batchData)) {
|
||||||
|
Tax::upsert($batchData, ['tax_no'], ['tax_code', 'tax_no', 'npwpd', 'wp_name', 'business_name', 'address', 'start_validity', 'end_validity', 'tax_value', 'subdistrict', 'village']);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public function sheets(): array {
|
||||||
|
return [
|
||||||
|
0 => $this
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function chunkSize(): int
|
||||||
|
{
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function batchSize(): int
|
||||||
|
{
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/Models/Tax.php
Normal file
23
app/Models/Tax.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Tax extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'taxs';
|
||||||
|
protected $fillable = [
|
||||||
|
'tax_code',
|
||||||
|
'tax_no',
|
||||||
|
'npwpd',
|
||||||
|
'wp_name',
|
||||||
|
'business_name',
|
||||||
|
'address',
|
||||||
|
'start_validity',
|
||||||
|
'end_validity',
|
||||||
|
'tax_value',
|
||||||
|
'subdistrict',
|
||||||
|
'village',
|
||||||
|
];
|
||||||
|
}
|
||||||
38
database/migrations/2025_08_04_231702_create_taxs_table.php
Normal file
38
database/migrations/2025_08_04_231702_create_taxs_table.php
Normal 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('taxs', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('tax_code');
|
||||||
|
$table->string('tax_no')->unique();
|
||||||
|
$table->string('npwpd');
|
||||||
|
$table->string('wp_name');
|
||||||
|
$table->string('business_name');
|
||||||
|
$table->text('address');
|
||||||
|
$table->date('start_validity');
|
||||||
|
$table->date('end_validity');
|
||||||
|
$table->decimal('tax_value', 12, 2);
|
||||||
|
$table->string('subdistrict');
|
||||||
|
$table->string('village');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('taxs');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -270,6 +270,21 @@ class MenuSeeder extends Seeder
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"name" => "Pajak",
|
||||||
|
"url" => "/tax",
|
||||||
|
"icon" => "mingcute:coin-line",
|
||||||
|
"parent_id" => null,
|
||||||
|
"sort_order" => 10,
|
||||||
|
"children" => [
|
||||||
|
[
|
||||||
|
"name" => "Data Pajak",
|
||||||
|
"url" => "taxation",
|
||||||
|
"icon" => null,
|
||||||
|
"sort_order" => 1,
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($menus as $menuData) {
|
foreach ($menus as $menuData) {
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class UsersRoleMenuSeeder extends Seeder
|
|||||||
'Menu', 'Role', 'Setting Dashboard', 'PBG', 'Reklame', 'Usaha atau Industri', 'Pariwisata',
|
'Menu', 'Role', 'Setting Dashboard', 'PBG', 'Reklame', 'Usaha atau Industri', 'Pariwisata',
|
||||||
'Lap Pariwisata', 'UMKM', 'Dashboard Potensi', 'Tata Ruang', 'PDAM', 'PETA',
|
'Lap Pariwisata', 'UMKM', 'Dashboard Potensi', 'Tata Ruang', 'PDAM', 'PETA',
|
||||||
'Lap Pimpinan', 'Dalam Sistem', 'Luar Sistem', 'Google Sheets', 'TPA TPT',
|
'Lap Pimpinan', 'Dalam Sistem', 'Luar Sistem', 'Google Sheets', 'TPA TPT',
|
||||||
'Approval Pejabat', 'Undangan', 'Rekap Pembayaran', 'Lap Rekap Data Pembayaran', 'Lap PBG (PTSP)', 'Lap Pertumbuhan'
|
'Approval Pejabat', 'Undangan', 'Rekap Pembayaran', 'Lap Rekap Data Pembayaran', 'Lap PBG (PTSP)', 'Lap Pertumbuhan',
|
||||||
|
'Pajak', 'Data Pajak'
|
||||||
])->get()->keyBy('name');
|
])->get()->keyBy('name');
|
||||||
|
|
||||||
// Define access levels for each role
|
// Define access levels for each role
|
||||||
|
|||||||
168
resources/js/taxation/index.js
Normal file
168
resources/js/taxation/index.js
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import { Grid } from "gridjs/dist/gridjs.umd.js";
|
||||||
|
import "gridjs/dist/gridjs.umd.js";
|
||||||
|
import GlobalConfig from "../global-config";
|
||||||
|
import { addThousandSeparators } from "../global-config";
|
||||||
|
|
||||||
|
class Taxation {
|
||||||
|
constructor() {
|
||||||
|
this.toastMessage = document.getElementById("toast-message");
|
||||||
|
this.toastElement = document.getElementById("toastNotification");
|
||||||
|
this.toast = new bootstrap.Toast(this.toastElement);
|
||||||
|
this.table = null;
|
||||||
|
|
||||||
|
this.initTableTaxation();
|
||||||
|
// this.initEvents();
|
||||||
|
this.handleExportToExcel();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleExportToExcel() {
|
||||||
|
const button = document.getElementById("btn-export-excel");
|
||||||
|
if (!button) {
|
||||||
|
console.error("Button not found: #btn-export-excel");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportUrl = button.getAttribute("data-url");
|
||||||
|
|
||||||
|
button.addEventListener("click", function () {
|
||||||
|
button.disabled = true;
|
||||||
|
|
||||||
|
fetch(exportUrl, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
Authorization:
|
||||||
|
"Bearer " +
|
||||||
|
document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(
|
||||||
|
"Error fetching data: " + response.statusText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return response.blob();
|
||||||
|
})
|
||||||
|
.then(function (blob) {
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = "laporan-rekap-data-pembayaran.xlsx";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
})
|
||||||
|
.finally(function () {
|
||||||
|
button.disabled = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initEvents() {
|
||||||
|
document.body.addEventListener("click", async (event) => {
|
||||||
|
const deleteButton = event.target.closest(".btn-delete-taxation");
|
||||||
|
if (deleteButton) {
|
||||||
|
event.preventDefault();
|
||||||
|
await this.handleDelete(deleteButton);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initTableTaxation() {
|
||||||
|
let tableContainer = document.getElementById("table-taxation");
|
||||||
|
|
||||||
|
if (!tableContainer) {
|
||||||
|
console.error("Table container not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear previous table content
|
||||||
|
tableContainer.innerHTML = "";
|
||||||
|
|
||||||
|
this.table = new Grid({
|
||||||
|
columns: [
|
||||||
|
"ID",
|
||||||
|
{ name: "Tax No" },
|
||||||
|
{ name: "Tax Code" },
|
||||||
|
{ name: "WP Name" },
|
||||||
|
{ name: "Business Name" },
|
||||||
|
{ name: "Address" },
|
||||||
|
{ name: "Start Validity" },
|
||||||
|
{ name: "End Validity" },
|
||||||
|
{ name: "Tax Value" },
|
||||||
|
{ name: "Subdistrict" },
|
||||||
|
{ name: "Village" },
|
||||||
|
],
|
||||||
|
pagination: {
|
||||||
|
limit: 50,
|
||||||
|
server: {
|
||||||
|
url: (prev, page) => {
|
||||||
|
let separator = prev.includes("?") ? "&" : "?";
|
||||||
|
return `${prev}${separator}page=${page + 1}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sort: true,
|
||||||
|
search: {
|
||||||
|
server: {
|
||||||
|
url: (prev, keyword) => {
|
||||||
|
let separator = prev.includes("?") ? "&" : "?";
|
||||||
|
return `${prev}${separator}search=${encodeURIComponent(
|
||||||
|
keyword
|
||||||
|
)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
debounceTimeout: 1000,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
url: `${GlobalConfig.apiHost}/api/taxs`,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content")}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
then: (data) => {
|
||||||
|
if (!data || !data.data) {
|
||||||
|
console.warn("⚠️ No data received from API");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.data.map((item) => {
|
||||||
|
return [
|
||||||
|
item.id,
|
||||||
|
item.tax_no,
|
||||||
|
item.tax_code,
|
||||||
|
item.wp_name,
|
||||||
|
item.business_name,
|
||||||
|
item.address,
|
||||||
|
item.start_validity,
|
||||||
|
item.end_validity,
|
||||||
|
addThousandSeparators(item.tax_value),
|
||||||
|
item.subdistrict,
|
||||||
|
item.village,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
total: (data) => {
|
||||||
|
let totalRecords = data?.meta?.total || 0;
|
||||||
|
return totalRecords;
|
||||||
|
},
|
||||||
|
catch: (error) => {
|
||||||
|
console.error("❌ Error fetching data:", error);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).render(tableContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function (e) {
|
||||||
|
new Taxation();
|
||||||
|
});
|
||||||
79
resources/js/taxation/upload.js
Normal file
79
resources/js/taxation/upload.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { Dropzone } from "dropzone";
|
||||||
|
Dropzone.autoDiscover = false;
|
||||||
|
|
||||||
|
class UploadTaxation {
|
||||||
|
constructor() {
|
||||||
|
this.spatialDropzone = null;
|
||||||
|
this.formElement = document.getElementById("formUploadTaxation");
|
||||||
|
this.uploadButton = document.getElementById("submit-upload");
|
||||||
|
this.spinner = document.getElementById("spinner");
|
||||||
|
if (!this.formElement) {
|
||||||
|
console.error("Element formUploadTaxation tidak ditemukan!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.initDropzone();
|
||||||
|
this.setupUploadButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
initDropzone() {
|
||||||
|
const toastNotification = document.getElementById("toastNotification");
|
||||||
|
const toast = new bootstrap.Toast(toastNotification);
|
||||||
|
let menuId = document.getElementById("menuId").value;
|
||||||
|
var previewTemplate,
|
||||||
|
dropzonePreviewNode = document.querySelector(
|
||||||
|
"#dropzone-preview-list"
|
||||||
|
);
|
||||||
|
(dropzonePreviewNode.id = ""),
|
||||||
|
dropzonePreviewNode &&
|
||||||
|
((previewTemplate = dropzonePreviewNode.parentNode.innerHTML),
|
||||||
|
dropzonePreviewNode.parentNode.removeChild(dropzonePreviewNode),
|
||||||
|
(this.spatialDropzone = new Dropzone(".dropzone", {
|
||||||
|
url: this.formElement.action,
|
||||||
|
method: "post",
|
||||||
|
acceptedFiles: ".xls,.xlsx",
|
||||||
|
previewTemplate: previewTemplate,
|
||||||
|
previewsContainer: "#dropzone-preview",
|
||||||
|
autoProcessQueue: false,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${document
|
||||||
|
.querySelector('meta[name="api-token"]')
|
||||||
|
.getAttribute("content")}`,
|
||||||
|
},
|
||||||
|
init: function () {
|
||||||
|
this.on("success", function (file, response) {
|
||||||
|
document.getElementById("toast-message").innerText =
|
||||||
|
response.message;
|
||||||
|
toast.show();
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = `/tax?menu_id=${menuId}`;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
this.on("error", function (file, errorMessage) {
|
||||||
|
document.getElementById("toast-message").innerText =
|
||||||
|
errorMessage.message;
|
||||||
|
toast.show();
|
||||||
|
this.uploadButton.disabled = false;
|
||||||
|
this.spinner.classList.add("d-none");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
setupUploadButton() {
|
||||||
|
this.uploadButton.addEventListener("click", (e) => {
|
||||||
|
if (this.spatialDropzone.files.length > 0) {
|
||||||
|
this.spatialDropzone.processQueue();
|
||||||
|
this.uploadButton.disabled = true;
|
||||||
|
this.spinner.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function (e) {
|
||||||
|
new UploadTaxation().init();
|
||||||
|
});
|
||||||
38
resources/views/taxation/index.blade.php
Normal file
38
resources/views/taxation/index.blade.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
@extends('layouts.vertical', ['subtitle' => 'Pajak'])
|
||||||
|
@section('css')
|
||||||
|
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
|
||||||
|
@endsection
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
@include('layouts.partials/page-title', ['title' => 'Pajak', 'subtitle' => 'Data Pajak'])
|
||||||
|
|
||||||
|
<x-toast-notification />
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card w-100">
|
||||||
|
<div class="card-header d-flex justify-content-end align-items-center">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<button class="btn btn-sm bg-black text-white d-flex align-items-center content-center gap-2" id="btn-export-excel" data-url="{{ route('api.taxs.export', ['menu_id' => $menuId]) }}">
|
||||||
|
<span>.xlsx</span>
|
||||||
|
<iconify-icon icon="mingcute:file-export-line" width="20" height="20" class="d-flex align-items-center"></iconify-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex flex-wrap justify-content-end align-items-center mb-2">
|
||||||
|
<a href="{{ route('taxation.upload', ['menu_id' => $menuId]) }}" class="btn btn-primary btn-sm d-block d-sm-inline w-auto">Upload</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div id="table-taxation"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('scripts')
|
||||||
|
@vite(['resources/js/taxation/index.js'])
|
||||||
|
@endsection
|
||||||
81
resources/views/taxation/upload.blade.php
Normal file
81
resources/views/taxation/upload.blade.php
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
@extends('layouts.vertical', ['subtitle' => 'Pajak'])
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
@include('layouts.partials/page-title', ['title' => 'Pajak', 'subtitle' => 'Upload'])
|
||||||
|
|
||||||
|
<x-toast-notification />
|
||||||
|
<input type="hidden" id="menuId" value="{{ $menuId ?? 0 }}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h5 class="card-title">Upload Data Pajak</h5>
|
||||||
|
<p class="card-subtitle">
|
||||||
|
Please upload a file with the extension <strong>.xls or .xlsx</strong> with a maximum size of <strong>10 MB</strong>.
|
||||||
|
<br>
|
||||||
|
For <strong>.xls</strong> and <strong>.xlsx</strong> files, ensure that the data is contained within a <strong>single sheet</strong> with the following columns:
|
||||||
|
<strong>kode, no, npwpd, nama_wp, nama_usaha, alamat_usaha, masa_pajak, nilai_pajak, kecamatan, desa</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
|
||||||
|
<div class="dropzone">
|
||||||
|
<form id="formUploadTaxation" action="{{ route('api.taxs.upload') }}" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="fallback">
|
||||||
|
<!-- <input id="file-dropzone" type="file" name="file" accept=".xlsx,.xls" multiple/> -->
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="dz-message needsclick">
|
||||||
|
<i class="h1 bx bx-cloud-upload"></i>
|
||||||
|
<h3>Drop files here or click to upload.</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="list-unstyled mb-0" id="dropzone-preview">
|
||||||
|
<li class="mt-2" id="dropzone-preview-list">
|
||||||
|
<!-- This is used as the file preview template -->
|
||||||
|
<div class="border rounded">
|
||||||
|
<div class="d-flex align-items-center p-2">
|
||||||
|
<div class="flex-shrink-0 me-3">
|
||||||
|
<div class="avatar-sm bg-light rounded">
|
||||||
|
<img data-dz-thumbnail class="img-fluid rounded d-block" src="#"
|
||||||
|
alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<div class="pt-1">
|
||||||
|
<h5 class="fs-14 mb-1" data-dz-name>
|
||||||
|
</h5>
|
||||||
|
<p class="fs-13 text-muted mb-0" data-dz-size></p>
|
||||||
|
<strong class="error text-danger" data-dz-errormessage></strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex-shrink-0 ms-3">
|
||||||
|
<button data-dz-remove class="btn btn-sm btn-danger">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<!-- end dropzon-preview -->
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<button id="submit-upload" class="btn btn-primary">
|
||||||
|
<span id="spinner" class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>
|
||||||
|
Upload Files
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div> <!-- end card body -->
|
||||||
|
</div> <!-- end card -->
|
||||||
|
</div> <!-- end col -->
|
||||||
|
</div> <!-- end row -->
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('scripts')
|
||||||
|
@vite(['resources/js/taxation/upload.js'])
|
||||||
|
@endsection
|
||||||
@@ -28,7 +28,7 @@ use App\Http\Controllers\Api\UmkmController;
|
|||||||
use App\Http\Controllers\Api\TourismController;
|
use App\Http\Controllers\Api\TourismController;
|
||||||
use App\Http\Controllers\Api\SpatialPlanningController;
|
use App\Http\Controllers\Api\SpatialPlanningController;
|
||||||
use App\Http\Controllers\Api\ChatbotController;
|
use App\Http\Controllers\Api\ChatbotController;
|
||||||
|
use App\Http\Controllers\Api\TaxationsController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::post('/login', [UsersController::class, 'login'])->name('api.user.login');
|
Route::post('/login', [UsersController::class, 'login'])->name('api.user.login');
|
||||||
@@ -194,5 +194,11 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
|
|||||||
Route::get('/growth','index')->name('api.growth');
|
Route::get('/growth','index')->name('api.growth');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::controller(TaxationsController::class)->group(function (){
|
||||||
|
Route::get('/taxs', 'index')->name('api.taxs');
|
||||||
|
Route::post('/taxs/upload', 'upload')->name('api.taxs.upload');
|
||||||
|
Route::get('/taxs/export', 'export')->name('api.taxs.export');
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Implement new retribution calculation API endpoints using the new schema
|
// TODO: Implement new retribution calculation API endpoints using the new schema
|
||||||
});
|
});
|
||||||
@@ -31,6 +31,7 @@ use App\Http\Controllers\Data\GoogleSheetsController;
|
|||||||
use App\Http\Controllers\Report\ReportTourismController;
|
use App\Http\Controllers\Report\ReportTourismController;
|
||||||
use App\Http\Controllers\Chatbot\ChatbotController;
|
use App\Http\Controllers\Chatbot\ChatbotController;
|
||||||
use App\Http\Controllers\ChatbotPimpinan\ChatbotPimpinanController;
|
use App\Http\Controllers\ChatbotPimpinan\ChatbotPimpinanController;
|
||||||
|
use App\Http\Controllers\TaxationController;
|
||||||
use App\Http\Controllers\TpatptsController;
|
use App\Http\Controllers\TpatptsController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@@ -186,4 +187,10 @@ Route::group(['middleware' => 'auth'], function(){
|
|||||||
Route::group(['prefix' => '/tools'], function (){
|
Route::group(['prefix' => '/tools'], function (){
|
||||||
Route::get('/invitations', [InvitationsController::class, 'index'])->name('invitations');
|
Route::get('/invitations', [InvitationsController::class, 'index'])->name('invitations');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// taxation
|
||||||
|
Route::group(['prefix' => '/tax'], function (){
|
||||||
|
Route::get('/', [TaxationController::class, 'index'])->name('taxation');
|
||||||
|
Route::get('/upload', [TaxationController::class, 'upload'])->name('taxation.upload');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -148,6 +148,9 @@ export default defineConfig({
|
|||||||
"resources/js/report-pbg-ptsp/index.js",
|
"resources/js/report-pbg-ptsp/index.js",
|
||||||
"resources/js/tpa-tpt/index.js",
|
"resources/js/tpa-tpt/index.js",
|
||||||
"resources/js/report-payment-recaps/index.js",
|
"resources/js/report-payment-recaps/index.js",
|
||||||
|
// taxation
|
||||||
|
"resources/js/taxation/index.js",
|
||||||
|
"resources/js/taxation/upload.js",
|
||||||
],
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user