Compare commits

...

26 Commits

Author SHA1 Message Date
arifal
285e89d5d0 done restructure calculation retribution 2025-06-19 13:48:35 +07:00
arifal hidayat
4c3443c2d6 restructure retribution calculations table 2025-06-18 22:53:44 +07:00
arifal
df70a47bd1 add build 2025-06-18 15:45:12 +07:00
arifal
e71dd7d213 count spatial plannings business and non business and create pbg task detail and add to syncrone daily 2025-06-18 13:45:35 +07:00
arifal hidayat
f2eb998ac5 build and add init spatial 2025-06-18 02:57:30 +07:00
arifal hidayat
fc54e20fa4 add spatial plannings retribution calculations 2025-06-18 02:54:41 +07:00
arifal
6946fa7074 update retribution calculation spatial plannings 2025-06-17 17:58:37 +07:00
arifal
236b6f9bfc add build frontend 2025-06-17 12:04:55 +07:00
arifal
285ff46c2b fix data setting get datatable using api token 2025-06-17 11:54:02 +07:00
arifal hidayat
a8b02afad9 add build production manifest 2025-06-14 04:22:57 +07:00
arifal hidayat
a0666e78d2 fix value dashboard verified component 2025-06-14 04:20:26 +07:00
arifal hidayat
799e409ce2 create build script 2025-06-14 02:48:00 +07:00
arifal hidayat
780ba60224 add sync to leader dashboard new from google spreadsheet 2025-06-14 02:15:49 +07:00
arifal
baed8cc487 fix 401 hit api 2025-06-13 20:43:16 +07:00
arifal
e17f5beaf0 production code 2025-06-13 13:36:27 +00:00
arifal
766e1a430c fix permision deploy 2025-06-13 20:08:14 +07:00
arifal
6677c320fc build and create deploy production 2025-06-13 19:53:23 +07:00
arifal hidayat
9437eb949f add docker 2025-06-06 22:42:41 +07:00
arifal
6f77120c33 backup database 2025-06-02 15:41:03 +07:00
arifal
f8d0573e5c remove description body response on datatable 2025-06-02 15:30:43 +07:00
arifal
ca74d0143f hot fix show all chart growth 2025-05-20 11:59:50 +07:00
arifal
34e082c31b fix bug sync google sheet and handle to long data address on pbg task 2025-05-20 11:13:21 +07:00
arifal
c4d865bf2b fix get menu id and height auto chart growth 2025-05-15 18:09:28 +07:00
arifal
a103b38265 create page growth report 2025-05-15 17:02:02 +07:00
arifal
9aa3d32b6e add link in potential dashboard 2025-05-15 14:18:27 +07:00
arifal
99e2c214b6 create redirect link for dashboard pimpinan 2025-05-14 20:56:36 +07:00
312 changed files with 290455 additions and 2896 deletions

0
.editorconfig Executable file → Normal file
View File

0
.env.example Executable file → Normal file
View File

76
.env.local.backup Normal file
View File

@@ -0,0 +1,76 @@
APP_NAME=SIBEDAS-PBG
APP_ENV=local
APP_KEY=base64:xqCpwixWKqgu1Ca22gFizoOt44p7h+cgTOKuhS/P0Jw=
APP_DEBUG=true
APP_TIMEZONE=Asia/Jakarta
APP_URL=http://localhost:8000
API_URL=http://localhost:8000
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mariadb
DB_HOST=db
DB_PORT=3306
DB_DATABASE=sibedas_db
DB_USERNAME=root
DB_PASSWORD=root
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
SIMBG_HOST="https://simbg.pu.go.id/"
SIMBG_EMAIL="dputr@bandungkab.go.id"
SIMBG_PASSWORD="Simbg123"
API_KEY_GOOGLE="AIzaSyBxfEShFkKmykkc7RJR3lVzkQ_xGHK3qr0"
SPREAD_SHEET_ID="1QoXzuLdEX3MK70Yrfigz0Qj5rAt4T819jX85vubBNdY"
OPENAI_API_KEY="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"

0
.gitattributes vendored Executable file → Normal file
View File

0
.gitignore vendored Executable file → Normal file
View File

View File

@@ -0,0 +1,42 @@
no,pemohon,alamat,activities,luas_lahan,bcr_kawasan,area,no_tapak,no_skkl,no_ukl,fungsi_bangunan,sub_fungsi_bangunan,jumlah_lantai
01/222,PT CIPTA INDAH PERTIWI No Telpon : 082217633434,Jl Salam No 51 Desa/Kel. Cihapit Kec. Bandung wetan Kota Bandung,PERUMAHAN LA LA TOWN,16629,0.2449,4072.4421,648.11./222 - SP /TR 07 Januari 2025;644.2./207 - SP /TR 02 Januari 2025,500.10.29.15/631-REKTEK/TR Tanggal 14 Februari 2023;500.10.29.15/2837-REKTEK/Bid. Taru Tanggal 25 Juli 2024,14022310313204030 Tanggal 14 Februari 2023;500.10.29.7/Kep.618-DLH/2024 Tanggal 13 Desember 2024;161023087545 Tanggal 16 Oktober2023,Fungsi Hunian,Fungsi Hunian,1
03/481,MUHAMAD GUNTUR JULIADI No Telpon : 081223334486,Cangkuang Residence RT 004/003 Ds Cangkuang Wetan Kec. Dayeuhkolot Kab. Bandung,PERUMAHAN CANGKUANG RESIDENCE 2,2360,0.5924,1398.064,648.11./481 - SP /TR 15 Januari 2025;647./226 - SP /TR 07 Januari 2025;647/225 - SP /TR 07 Januari 2025;644/221 - SP /TR 07 Januari 2025;645.4/557 - SP /TR 20 Januari 2025,500.10.29.15/2894-REKTEK/Bid. Taru Tanggal 08 Agustus 2024;400.7.22/4927-KRK/04/Bid.Taru Tanggal 06 Desember 2024;500.10.29.15/2619-REKTEK/Bid. Taru Tanggal 10 Juli 2024;500.10.29.15/4155-REKTEK/Bid. Taru Tanggal 18 Nopember 2024;503/123/III-DPMPTSP/2020 Tanggal 24 MAret 2020,167240099471 Tanggal 16 Juli 2024;9120111161314 Tanggal 11 Nopember 2019;8120312190151 Tanggal 04 Desember 2023;500.10.29.7/Kep.614-DLH/2024 Tanggal 20 Nopember 2024;2910240290118 Tanggal 29 Oktober 2024;8120001992981 Tanggal 12 Juli 2024;SK.294/Menlhk/Setjen/PLA.4/5/2021 Tanggal 31 Mei 2021,Fungsi Hunian,Fungsi Hunian,1
08/480,PT SURYA KHARISMA PARAHYANGAN No Telpon : 081394461006 ,Jalan Cibaligo No 48 Desa /Kel. Cigugur tengah Kec. Cimahi tengah kota Cimahi ,PERUMAHAN PARAHYANGAN GARDEN CITY,116360,0.5164,60088.304,648.11./480 - SP /TR 15 Januari 2025,500.10.29.15/603-REKTEK/TR Tanggal 05 Desember 2023,1810220245722 Tanggal 18 Oktober 2022;500.10.29.7/Kep.632-DLH/2024 Tanggal 24 Desember 2024,Fungsi Hunian,Fungsi Hunian,2
09/223,PT CLARICHEM INDONESIA No Telpon : 081315399694,Kawasan pergudangan taman tekno sektor XI blok L2 No 30 Ds/Kel. Setu Kec. Setu kota Tangerag Banten ,PERGUDANGAN DAN PENYIMPANAN,1586,0.7017,1112.8962,644./223 - SP /TR 07 Januari 2025,400.7.22/4427-KRK/03/Bid.Taru Tanggal 06 Desember 2024,9120008832767 Tanggal 26 Nopember 2024,Usaha Besar (Non-Mikro),Fungsi Usaha,1
10/657,PT HALKA GITA No Telpon : 0816734374,Jl Raya Majalaya KM 2 RT 02/09 Desa Hegarmanah Kec. Cikancung Kab. Bandung,REVISI 1 KOMPLEK PERGUDANGAN,19100,0.699,13350.9,644./657 - SP /TR 23 Januari 2025,500.10.29.15/6750-REKTEK/TR Tanggal 21 Desember 2023,9120300510839 Tanggal 16 Desember 2023;500.10.29.7/Kep.609-DLH/2024 Tanggal 12 Nopember 2024,Usaha Besar (Non-Mikro),Fungsi Usaha,2
11/658,PT SURYA TIRTA KENCANA No Telpon : 08986155493,Kutawaringin Industrial Park Kav. 276-277 Desa Jelegong Kec. Kutawaringin Kab. Bandung,PERGUDANGAN DAN PENYIMPANAN BAHAN KIMIA,3932,0.6995,2750.434,644./658 - SP /TR 23 Januari 2025;640/540 - SP /TR 20 Januari 2025,10112410113204010 Tanggal 10 Nopember 2024;591.4/031/IX-DPMPTSP/2020 Tanggal 03 Sseptember 2020,8120017120594 Tanggal 31 Agustus 2018;600.4.5/056/6838-Ktr/TL Tanggal 24 Desember 2024;0220305221438 Tanggal 20 Nopember 2021;500.10.29.7/4295/TL Tanggal 21 September 2024,Usaha Besar (Non-Mikro),Fungsi Usaha,1
13 / 550,PT BERKAH SEPUH JAYA No Telpon : 082118781624,Ruko komplek dewaadru blok R3 RT 005 ds/kec. Bojongsoang kab. Bandung,REVISI 1 PERUMAHAN VILLA CEMARA ASRI,17379,0.5476,9516.7404,648.11/550 - SP/TR 20 Januari 2025,150223101113204049 Tanggal 15 Februari 2023,1233000451657 Tanggal 5 Mei 2023;500.10.29.7/kep.392-DLH/2025 Tgl. 2 Januari 2025,Fungsi Hunian,Fungsi Hunian,2
14 / 655,PT RISKI ANUGERAH SEDJAHTERA No Telpon : 082118781624,Griya permata asri blok C2 No 18 Ds Lengkong Kec. Bojongsoang Kab. Bandung,REVISI 1 PERUMAHAN TAMAN KATAPANG INDAH,28425,0.6532,18567.21,648.11 /655 - SP /TR 23 Januari 2025,500.10.29.15/2333-REKTEK/TR Tanggal 22 Mei 2024;500.10.29.15/3811-REKTEK/Bid. Taru Tanggal 10 Oktober 2024,1205000302875 Tanggal 02 April 2024;500.10.29.7/Kep.408-DLH/2025 Tanggal 10 Januari 2025,Fungsi Hunian,Fungsi Hunian,2
15 / 551,PT BUMI SUWARNA SEJAHTERA No Telpon : 082118781624,Jl Ciwastra no 73 Kel. Mekarmulya kec. Rancasari kota bandung,REVISI 1 PERUMAHAN BUMI KARA RESIDENCE,29711,0.5424,16115.2464,648.11/551-SP/TR 20 Januari 2025,500.10.29.15/6097-REKTEK/TR Tanggal 27 Nopember 2023,1311230078306 Tgl 13 Nopember 2023;500.10.29.7/Kep.493-DLH/2024 Tanggal 20 Maret 2024,Fungsi Hunian,Fungsi Hunian,2
16 / 656,PT AMBER HASYA No Telpon : 081312411661,Jl Sarijadi raya No 111 Kel. Sarijadi Kec. Sukasarii kota bandung,REVISI 1 PERUMAHAN GREEN HARMONI RESIDENCE ,39520,0.2726,10773.152,648.11/656-SP/TR 23 Januari 2025,500.10.29.15/2732-REKTEK/Bid. Taru Tanggal 19 Juli 2024,9120004882226 Tgl 22 Agustus 2019;500.10.29.7/Kep.617-DLH/2024 Tanggal 4 Desember 2024,Fungsi Hunian,Fungsi Hunian,2
17 / 224,PT KARYA UTAMA SEJATI JAYA No Telpon : 081320772222,Kp. Andir DS/Kel. Manggahang Kec. Baleendah Kab. Bandung,PERUMAHAN GRAND CIPARAY RESIDENCE,12045,0.5945,7160.7525,648.11/224-SP/TR 07 Januari 2025;644/822-SP/TR 13 Februari 2025;640/524-SP/TR 17 Januari 2025;642/763-SP/TR 11 Februari 2025,500/5300-PERTEKTR/2022/TR Tanggal 04 Agustus 2022;400.7.22/4872-KRK/25/Bid.TR Tanggal 24 Desember 2024;Pu.650/7580-REKTEK/TR Tanggal 20 Desember 2022;01112410113204120 Tanggal 1 November 2024,1801220061544 Tgl 05 September 2022;600.4.5/052/1722-Ktr/TL Tanggal 20 Mei 2024;9120401971173 Tgl 11 Desember 2024;1406220076826 Tgl 15 November 2022;8120103802893 Tgl 29 Agustus 2018,Fungsi Hunian,Fungsi Hunian,2
21 / 688,PT PRAKARSA FAJAR PROPERTINDO No Telpon : 082315094712,Jl. Otto Iskandardinata No 429 Kel. Pungkur Kec. Regol Kota Bandung,PERUMAHAN ALAM BUMI CIPARAY,17417,0.6793,11831.3681,648.11/688-SP/TR 03 Februari 2025,500.10.29.15/1075-REKTEK/TR Tanggal 04 Maret 2024,9120007841259 Tgl 15 Agustus 2019;500.10.29.7/Kep.405-DLH/2025 Tanggal 06 Januari 2025,Fungsi Hunian,Fungsi Hunian,2
22 / 767,PT CITRA SENTOSA JAYATAMA No Telpon : 087825393706,Jl. Nyengseret No 29 RT 01 Kel. Pelindung hewaqn Kota Bandung,PERUMAHAN CITRA ASRI RESIDENCE,28900,0.692,19998.8,648.11/767-SP/TR 12 Februari 2025,500.10.29.15/2617-REKTEK/Bid. Taru Tanggal 10 Juli 2024,9120107971097 Tgl 19 September 2019;500.10.29.7/Kep.612-DLH/2024 Tanggal 18 November 2024,Fungsi Hunian,Fungsi Hunian,2
23/ 641,PT PESONA MITRA KEMBAR MAS No Telpon : 081220763283,Jl Kembar mas IV No 2A RT 05/09 Kel. Pasirluyu Kec. Regol Kota Bandung,REVISI 5 PERUMAHAN PODOMORO PARK,1160011,0.6691,776163.3601,648.11/641- SP/TR 22 Januari 2025,591/057-DPMPTSP/2017 Tanggal 28 Desember 2017,650/Kep.628-DLH/2018 Tanggal 30 Nopember 2018,Fungsi Hunian,Fungsi Hunian,2
24 / 659,PT PAVITRA PARA ARTHA No Telpon : 081809063866,Jl . Kubang Beureum No 39 RT 007 / 011 Kelurahan Sekejati Kecamatan Buahbatu,REVISI 4 PERUMAHAN PRIVATE VILLAGE,88245,0.5819,51349.7655,648.11/659 - SP/TR 23 Januari 2025;645/932 - SP/TR 21 Februari 2025;645/552-SP/TR 20 Januari 2025;647/640-SP/TR 22 Januari 2025;647/930-SP/TR 21 Februari 2025,591.4/001-BPMP/2014 Tanggal 06 Januari 2014;591.4/031-DPMPTSP/2019 Tanggal 10 Juli 2019;503/104/XII-DPMPTSP/2019- Tgl 20 Desember 2019;400.1.22/4521-KRK/15/Bid.Taru Tgl 16 Desember 2024;650/2597-KRTR/2021/TR Tgl 9 Desember 2021;500.10.29.15/2839-REKTEK/Bid.TAru Tgl 2 Agustus 2024,500.10.29.6/4898/TL Tanggal 08 Nopember 2023;667/2995/TL Tanggal 11 Agustus 2020;9120000431996 Tanggal 20 Oktober 2021;500.10.29.7/Kep.629-DLH/2024;9120605793306 Tanggal 9 Mei 2023;1601240112040181 Tanggal 16 Januari 2024;8120215192631 Tanggal 3 Januari 2022;34/LH.01.06.05/DLH Tanggal 3 Januari 2025;9120101841825 Tanggal 18 Oktober 2023,Fungsi Hunian,Fungsi Hunian,2
29 / 931,PT KARYA HAFANA INTAN MANDIRI No Telpon : 082120002925 ,Kp./Desa Ciluluk Kecamatan Cikancung Kabupaten Bandung,PERUMAHAN GRIYA CIHANYIR PERMAI,24432.33,0.5945,14525.02019,648.11/931 - SP/TR 21 Februari 2025,400.7.22/4840-KRK/21/Bid.Taru Tgl. 18 Desember 2024,2908210007625 Tanggal 06 Mei 2024;500.10.29.7/Kep.414-DLH/2025 Tanggal 30 Januari 2025,Fungsi Hunian,Fungsi Hunian,2
30 / 933,PT ARUM JAYA PROPERTI No Telpon : 085100942672,Perumahan Banyu Arum Residence Blok A Kp. Tegal Tengah RT 003/013 Desa Cangkuang Kec. Rancaekek,REVISI 1 PERUMAHAN BUMI ARUM REGENCY,58899,0.4838,28495.3362,648.11/933 - SP/PR 21 FEBRUARI 2025,591.4/026-DPMPTSP/2018 Tgl 24 September 2018,667/7210/TL Tanggal 16 Nopember 2018;500.10.29.6/Kep.420-DLH/2025 Tanmggal 3 Februari 2025,Fungsi Hunian,Fungsi Hunian,2
31 / 762,SANDY SALMAN No Telpon : 082118781624,Jl Kembar VIII No 26 RT 005/010 Kel Cigereleng Kec. Regol Kota Bandung 40253,REVISI 1 PERUMAHAN D HEUVEL WIWAHA PADASUKA,3095,0.4295,1329.3025,648.11/ 762 - SP / PR 11 Februari 2025;648/523-SP/TR 17 Januari 2025;647/1140-SP/TR 27 Februari 2025;647/934-SP/TR 21 Februari 2025;647/1233-SP/TR 06 Maret 2025,"650/5689-PETEKTR/2022/TR Tanggal 30 Agustus 2022;650/2748-KRTR/2021/TR Tgl 21 Desember 2021;593.SK.93-BKPMD/90 Tanggal 11 April 1990;593/SK.225-BKPMD/1991 Tanggal 28 Juni 1991;591,4/009-BPMP/2014 Tanggal 27 Maret 2014;500.10.29.15/711-REKTEK/TR Tanggal 15 Februari 2024;500.10.29.15/2877-REKTEK/Bid.Taru Tanggal 19 Agustus 2024 ",LH.01.04/5866-SPPL/TL Tanggal 27 Desember 2022;1402220064386 Tanggal 04 Februari 2022;500.10.29.7/Kep.593-DLH/2024 Tanggal 27 September 2024;667/3257/BPLH Tanggal 06 Oktober 2014;2711230388253 Tanggal 27 Nopember 2023;500.10.29.7/Kep.630-DLH/2024 Tanggal 20 Desember 2024;9120312050674 Tanggal 07 Oktober 2019;500.10.29.7/Kep.426-DLH/2025 Tanggal 14 Februari 2025,Fungsi Hunian,Fungsi Hunian,1
36/ 1141,HANDRIAWAN No Telpon : 081313350382,Jalan Taman Holis Indah E-5 No 4-5 RT 03/06 Ds/Kel. Cigondewah Kec. Bandung kulon. Kota Bandung,TOKO DAN GUDANG,2025,0.7848,1589.22,647/1141-SP/TR 27 Februari 2025;647/1211-SP/TR 04 Maret 2025;648.2/1091-SP/TR 25 Februari 2025,400.7.22/559-KRK/14/Bid.Taru Tanggal 17 Januari 2025;400.7.22/859-KRK/38/Bid.Taru Tanggal 17 Februari 2025;500.10.29.15/4064 - REKTEK/Bid.Taru Tanggal 25 Nopember 2024,1401250067962 Tanggal 14 Januari 2025;1812240057832 Tanggal 18 Desember 2024;1611210013009 Tanggal 16 Nopember 2021,Usaha Besar (Non-Mikro),Fungsi Usaha,1
39/ 1232,JOHANSJAH SUGIANTO No Telpon : 081314974495,Jalan Kepodang VI/6 blok k2 Rengas Kec. Ciputat timur kota Tangerang selatan,TOKO,2490,21.9,54531,644.2/1232-SP/TR 06 Maret 2025;644.2/1234-SP/TR 06 Maret 2025,400.7.22/1089 - KRK/45//Bid.Taru Tanggal 24 Februari 2025;503/0017-PKKPRNB/DPMPTSP/II/2025 Tanggal 12 Februari 2025,3101250002573 Tanggal 31 Januari 2025;2107230112343 Tanggal 21 Juli 2023,Tidak Diketahui,Tidak Diketahui,3
41 / 1090,TIM AD HOC PERUMAHAN SUKAWANGI RESIDENCE No Telpon : 08112255770,Kp. Cihalimun RT 02/04 Ds. Cibeureum Kec. Kertasari Kab. Bandung,PERUMAHAN SUKAWANGI RESIDENCE,,1,,648.11/ 1090 - SP / PR 25 Februari 2025,,,Fungsi Hunian,Fungsi Hunian,2
42 / 1298,PT TIGA REKAN INDONESIA No Telpon : 08122444717,Jl Gradiul No 40 RT 04/07 Kel. Rancaekek Kencana Kec. Rancaekek Kab. Bandung,PERUMAHAN GREEN HILL VILLAGE,16548,0.5917,9791.4516,648.11/1298 - SP/TR 17 Maret 2025,50.10.29.15/2742-REKTEK/Bid. Taru Tanggal 24 Juli 2024,2505240130006 Tanggal 25 Mei 2024;600.4.5/061/832-Ktr/Bid. TL/2025 Tgl. 19 Februari 2025,Fungsi Hunian,Fungsi Hunian,2
43 / 1322,PT UNILOA ARDIYANTO INVESTAMA No Telpon : 081220180480,Jl Raya Ebah 103 Desa Sukamantri Kecamatan Paseh Kab. Bandung,PERUMAHAN MARISON CIPAKU,19209,0.6146,11805.8514,648.11/1322 - SP/TR 18 Maret 2025,24042410313204051 Tanggal 24 JApril 2024,0238010110358 Tanggal 11 Januari 2024;500.10.29.7/Kep. 451-DLH/2025 Tgl. 13 Maret 2025,Fungsi Hunian,Fungsi Hunian,2
44 / 1323,PT RADINAKA KRAMAT ABADI No Telpon : 082118781624,Jl. Sukamukti RT 01/06 Desa Sukamukti Kec. Katapang Kab. Bandung,PERUMAHAN GAHARU PALEDANG RESIDENCE,14665,0.68003,9972.63995,648.11/1323 - SP/TR 18 Maret 2025,02032510213204087 Tanggal 02 Maret 2025,0238010110358 Tanggal 11 Januari 2024;600.4.5/064/1165-Ktr/Bid. TL/2025 Tgl. 12 Maret 2025,Fungsi Hunian,Fungsi Hunian,2
45 / 1361,PT SANGKURIANG KRAMAT ABADI No Telpon : 082118781624,Kampung rancakasiat Desa Rancamulya Kec. Pameungpeuk Kab. Bandung,PERUMAHAN PONDOK ASRI SUKAMUKTI,20451,0.6806,13918.9506,648.11/1361 - SP/TR 24 Maret 2025,500.10.29.15/3451-REKTEK/Bid.Taru Tanggal 26 September 2024,0811230037204 Tanggal 08 Nopember 2023;500.10.29.7/Kep.460-DLH/2025 Tgl. 21 Maret 2025,Fungsi Hunian,Fungsi Hunian,2
46 / 1360,PT AUF ABDURACCHMAN JAYA No Telpon : 082118781624,Jl. Rancabungur rancakasiat Desa Malakasari Kec. Baleendah Kab. Bandung,PERUMAHAN BUMI SHANGRILA RESIDENCE,12846,0.6412,8236.8552,648.11/1360 - SP/TR 24 Maret 2025,400.7.22/093/1390-KRK/Bid.Taru Tanggal 17 Maret 2025,0603250028082 Tanggal 06 Maret 2025;600.4.5/072/1270-Ktr/Bid.TL/2025 Tgl. 21 Maret 2025,Fungsi Hunian,Fungsi Hunian,2
47 / 1142,ANTO DWI HARTANTO No Telpon : 082126215611,Jl. Elang V No 8 RT 08/01 Kel. Garuda Kec. Andir Kota Bandung,PERGUDANGAN DAN PENYIMPANAN,1307,0.5616,734.0112,647/1142 - SP/TR 27 Februari 2025;648.11/823 - SP/TR 13 Februari 2025;644.2/1389 - SP/TR 26 Maret 2025;644.2/1388 - SP/TR 26 Maret 2025,500.10.29.15/3827-REKTEK/Bid.Taru Tanggal 17 Oktober 2024;500.10.29.15/3944-REKTEK/Bid.Taru Tanggal 28 Oktober 2024;503/0072-DPMPTSP/XI/2023 Tanggal 29 November 2023;400.7.22/1046-KRK/Bid.Taru Tanggal 21 Februari 2025,2009240075774 Tanggal 20 September 2024 2025;2009240075774 Tanggal 20 September 2025;500.10.29.7/Hep-461-DLH/2025 Tanggal 21 Maret 2025;0298000921888 Tanggal 24 Januari 2022,Usaha Besar (Non-Mikro),Fungsi Usaha,1
51 / 1161,PT INGRIA PRATAMA CAPITALINDO Tbk No Telpon : 087722361043,Ruko Pondok Cabe Mutiara Jl Pondok cabe No 27 Kel. Pamulang Kec. Pamulang Kota Tangerang Selatan,REVISI 2 PERUMAHAN BUKIT ESMA CICALENGKA 2 ,74300,0.6292,46749.56,648.11/1161 - SP/TR 28 Februari 2025,50.10.29.15/2927-REKTEK/Bid. Taru Tanggal 14 Agustus 2024,591.4/005/IX-DPMPTSP/2019 Tanggal 09 September 2019;500.10.29.7/Kep.613-DLH/2024 Tgl. 18 November 2025,Fungsi Hunian,Fungsi Hunian,2
52 / 1441 ,PT ABADI MUKTI KIRANA No Telpon : 087821848944,Jl Terusan jamika No 88 Kel. Jamika Kec. Bojongloa kaler Kota Bandung, PERUMAHAN KOTA BARU ARJASARI,222816,1,222816,648.11/1441 - SP/TR 11 April 2025,400.7.28/1710/TR Tanggal 23 April 2024,9120108201236 Tanggal 13 Februari 2019;500.10.29.7/Kep.419-DLH/2025 Tgl. 03 Februari 2025,Fungsi Hunian,Fungsi Hunian,2
53 / 1566,PT MAKMUR INDAH DAMAI No Telpon : 081394722234,Jl. Podomoro Boulevard utara I Desa Lengkong Kec. Bojongsoang Kab. Bandung,PERUMAHAN BAROS INDAH RESIDENCE,17513,1,17513,647/1566 - SP/TR 25 April 2025,50.10.29.15/2507-REKTEK/Bid. Taru Tanggal 25 Juni 2024,2106240023693 Tanggal 21 Juni 2024;500.10.29.7/Kep.447-DLH/2024 Tgl. 10 Maret 2025,Fungsi Hunian,Fungsi Hunian,2
54 / 1343,PT KARYA BUMI BESTARI No Telpon : 08156080428,Kembar Mas IV No 2A RT 05/09 Kel. Pasirluyu Kec. Regol Kota Bandung,PERUMAHAN BESTARI MAS,26134,1,26134,648.11/1343 - SP/TR 20 Maret 2025;647/1565 - SP/TR 25 April 2025;648.1/1543 - SP/TR 17 April 2025,12042310213204004 Tanggal 12 April 2023;23122410113204057 Tanggal 23 Desember 2024;400.7.22/1047-KRK/42/Bid.Taru Tanggal 21 Februari 2025,1204230043635 Tanggal 12 April 2023;500.10.29.7/964/Bid. TL/2025 Tgl. 19 Februari 2025;9120304390956 Tanggal 05 Desember 2023;0307230050445 Tanggal 03 Juli 2023,Fungsi Hunian,Fungsi Hunian,2
57 / 1372,TENNI DIANA No Telpon : 082116106640,Cikaahuripan RT 002/005 Kec. Neglasari Kota Tangerang,PERADAGANGAN KHUSUS KARPET PERMADANI DAN PENUTUP DINDING DN LANTAI DI TOKO,1290,1,1290,644/1372 - SP/TR 26 Maret 2025;647/1557 - SP/TR 22 April 2025;640/1559 - SP/TR 22 April 2025;644/1371 - SP/TR 26 Maret 2025;644/1375 - SP/TR 26 Maret 2025;644/1373 - SP/TR 26 Maret 2025,500.10.29.15/4355-REKTEK/TR Tanggal 25 Agustus 2023;500.10.29.15/1073-REKTEK/TR Tanggal 04 Maret 2024;400.7.22/759-KRK/34/Bid.Taru Tanggal 11 Februari 2025;500.10.29.15/4654-REKTEK/Bid. Taru Tanggal 08 September 2023;500.10.29.15/4160-REKTEK/Bid. Taru Tanggal 15 Nopember 2024;500.10.29.15/2500-REKTEK/Bid. Taru Tanggal 19 Juni 2024,9120306652149 Tanggal 26 Juni 2019;0220107462528 Tanggal 08 September 2022;1283000240318 Tanggal 01 Februari 2021;600.4.5/071-KTR/Bid.TL/2025 Tgl. 05 Mei 2025;0220301251224 Tanggal 11 Agustus 2023;1263000210596 Tanggal 09 Februari 2021;1405240295325 5 Mei 2024,Tidak Diketahui,Tidak Diketahui,1
63 / 1567,Ir. BERSIH TARIGANT No Telpon : 081322777581,Jl Multatuli No 3 rt 001/001 Ds/Kel. Lebakgede Kec. Coblong Kota Bandung,PERUMAHAN TUSCANY HILL,23796,0.2428,5777.6688,648.11/1567 - SP/TR 25 April 2025,50.10.29.15/3829-REKTEK/Bid. Taru Tanggal 15 Oktober 2024,2007220014068 Tanggal 20 Juli 2025;500.10.29.7/Kep.429-DLH/2025 Tgl. 10 Maret 2025,Fungsi Hunian,Fungsi Hunian,2
64 / 1682,PT CIPTA BERKAT PROPERTI No Telpon : 081910661980,Ruko Matahari Cigado No 25 Jalan Anggadireja Kel. Baleendah Kec. Baleendah Kab. Bandung,PERUMAHAN GARDEN VIEW CICALENGKA,45666,1,45666,648.11/1682 - SP/TR 06 Mei 2025,50.10.29.15/3156-REKTEK/Bid. Taru Tanggal 05 September 2024,2310210017936 Tanggal 23 Oktober 2021;500.10.29.7/Kep.477-DLH/2025 Tgl. 17 April 2025,Fungsi Hunian,Fungsi Hunian,2
64 / 1682,PT CIPTA BERKAT PROPERTI No Telpon : 081910661980,Ruko Matahari Cigado No 25 Jalan Anggadireja Kel. Baleendah Kec. Baleendah Kab. Bandung,C. KWT PERUMAHAN,0,1,0,,,,Fungsi Hunian,Fungsi Hunian,1
65 / 1644,PT ARGUNA JAYA PROPERTY No Telpon : 081214211164,Jl Pidada IV/4 Denpasar BR/Link Sedana metra Kel. Ubung Kec. Denpasar Utara Kota Denpasar,REVISI 1 PERUMAHAN ARGUNA SINDANGPANON,8553,0.53,4533.09,648.11/1644 - SP/TR 29 April 2025;644/1558 - SP/TR 22 April 2025,503/009/II-DPMPTSP/2020 Tanggal 25 Februari 2020;50.10.29.15/4063-REKTEK/Bid. Taru Tanggal 25 Nopember 2025,9120004890734 Tanggal 03 Agustus 2019;667/2081/TL Tgl. 08 Juni 2020;0609240042720001 Tanggal 06 September 2024,Fungsi Hunian,Fungsi Hunian,1
67/ 1370,NENENG FATIMAH No Telpon : 08977980040,Jalan Batu Indah I No 26 RT 002/003 Kel. Batununggal Kec. Bandung Kidul Kota Bandung,PERGUDANGAN DAN PENYIMPANAN,2034,1,2034,644/1370 - SP/TR 26 Maret 2025,50.10.29.15/3340-REKTEK/Bid. Taru Tanggal 18 September 2025,1009240038601 Tanggal 10 September 2024,Usaha Besar (Non-Mikro),Fungsi Usaha,1
68/ 1374,LILI JOJON No Telpon : 082219855556,Kp. Cibisoro RT 004/008 Desa Nanjung Kec. Mrgaasih Kab. Bandung,PERGUDANGAN DAN PENYIMPANAN,1654,1,1654,644/1374 - SP/TR 26 Maret 2025;644.2/1683 - SP/TR 06 Mei 2025;645/1730 - SP/TR 07 Mei 2025,50.10.29.15/3210-REKTEK/Bid. Taru Tanggal 10 September 2024;400.7.22/1254-KRK/58/Bid. Taru Tanggal 10 Maret 2025;50.10.29.15/3443-REKTEK/Bid. Taru Tanggal 26 September 2024,1220000361065 Tanggal 16 Maret 2021;1712240056863 Tanggal 17 Desember 2024;0103230081544 Tanggal 01 Maret 2023;600.4.5/071/1503-Ktr/Bid. TL/2025 Tanggal 17 April 2025,Usaha Besar (Non-Mikro),Fungsi Usaha,1
71/ 1845,PT SANGKURIANG KRAMAT ABADI No Telpon : 082116602145,Kp. Rancakasiat Desa Rancamulya Kec. Pameungpeuk Kab. Bandung,PERUMAHAN CLUSTER GAHARU EMERALD,19889,1,19889,648.11 /1845 - SP/TR 19 Mei 2025,400.7.22/1526-KRK/96/Bid. Taru Tanggal 22 April 2025,0811230037204 Tanggal 08 Nopember 2023;500.10.29.7/Kep. 489-DLH/2025 Tanggal 15 Mei 2025,Fungsi Hunian,Fungsi Hunian,2
72/ 1731,PT MARGA TIRTA KENCANA No Telpon : 082130000146,Jlan BKR Lingkar selatan No 140 Kota Bandung,PERUMAHAN TAMAN CIBADUYUT INDAH 2 DAN 3,205344,1,205344,648.11 /1731 - SP/TR 07 Mei 2025;648.11 /1946 - SP/TR 22 Mei 2025;643 /1881 - SP/TR 20 Mei 2025;644 /1684 - SP/TR 06 Mei 2025;648.12 /1825 - SP/TR 16 Mei 2025,591.4/002-BPMP/2010 Tanggal 10 Januari 2010;500.10.29.15/3108-REKTEK/Bid.taru Tanggal 29 Agustus 2024;500.10.29.15/3319-REKTEK/TR Tanggal 14 Juli 2023;12092410313204101 Tanggal 12 September 2024;400.7.22/1231-KRK/Bid.Taru Tanggal 06 Maret 2025,667/3803/DLH Tanggal 22 Desember 2016;9120204202309 Tanggal 20 Februari 2019;2002230028317 Tanggal 20 Februari 2023;600.4.5/073/1562-Ktr/Bid.TL/2025 Tanggal 23 April 2025;8120006931402 Tanggal 28 September 2022;12710006324580004 Tanggal 05 Maret 2025,Fungsi Hunian,Fungsi Hunian,2
77/ 1879,PT PUTRA RAHMAN PRADANA No Telpon : 081312214962,Perum Cemerlang Permai Blok C No 16 Kel. Sukakarya Kec. Warudoyongf Kota Sukabumi,REVISI 1 PERUMAHAN BUKIT PINUS BANIARAN,90711,1,90711,648.11 /1879 - SP/TR 20 Mei 2025,22122310313204009 Tanggal 22 Desember 2023,500.10.29.7/Kep.482-DLH/2025 Tanggal 24 April 2025,Fungsi Hunian,Fungsi Hunian,2
1 no pemohon alamat activities luas_lahan bcr_kawasan area no_tapak no_skkl no_ukl fungsi_bangunan sub_fungsi_bangunan jumlah_lantai
2 01/222 PT CIPTA INDAH PERTIWI No Telpon : 082217633434 Jl Salam No 51 Desa/Kel. Cihapit Kec. Bandung wetan Kota Bandung PERUMAHAN LA LA TOWN 16629 0.2449 4072.4421 648.11./222 - SP /TR 07 Januari 2025;644.2./207 - SP /TR 02 Januari 2025 500.10.29.15/631-REKTEK/TR Tanggal 14 Februari 2023;500.10.29.15/2837-REKTEK/Bid. Taru Tanggal 25 Juli 2024 14022310313204030 Tanggal 14 Februari 2023;500.10.29.7/Kep.618-DLH/2024 Tanggal 13 Desember 2024;161023087545 Tanggal 16 Oktober2023 Fungsi Hunian Fungsi Hunian 1
3 03/481 MUHAMAD GUNTUR JULIADI No Telpon : 081223334486 Cangkuang Residence RT 004/003 Ds Cangkuang Wetan Kec. Dayeuhkolot Kab. Bandung PERUMAHAN CANGKUANG RESIDENCE 2 2360 0.5924 1398.064 648.11./481 - SP /TR 15 Januari 2025;647./226 - SP /TR 07 Januari 2025;647/225 - SP /TR 07 Januari 2025;644/221 - SP /TR 07 Januari 2025;645.4/557 - SP /TR 20 Januari 2025 500.10.29.15/2894-REKTEK/Bid. Taru Tanggal 08 Agustus 2024;400.7.22/4927-KRK/04/Bid.Taru Tanggal 06 Desember 2024;500.10.29.15/2619-REKTEK/Bid. Taru Tanggal 10 Juli 2024;500.10.29.15/4155-REKTEK/Bid. Taru Tanggal 18 Nopember 2024;503/123/III-DPMPTSP/2020 Tanggal 24 MAret 2020 167240099471 Tanggal 16 Juli 2024;9120111161314 Tanggal 11 Nopember 2019;8120312190151 Tanggal 04 Desember 2023;500.10.29.7/Kep.614-DLH/2024 Tanggal 20 Nopember 2024;2910240290118 Tanggal 29 Oktober 2024;8120001992981 Tanggal 12 Juli 2024;SK.294/Menlhk/Setjen/PLA.4/5/2021 Tanggal 31 Mei 2021 Fungsi Hunian Fungsi Hunian 1
4 08/480 PT SURYA KHARISMA PARAHYANGAN No Telpon : 081394461006 Jalan Cibaligo No 48 Desa /Kel. Cigugur tengah Kec. Cimahi tengah kota Cimahi PERUMAHAN PARAHYANGAN GARDEN CITY 116360 0.5164 60088.304 648.11./480 - SP /TR 15 Januari 2025 500.10.29.15/603-REKTEK/TR Tanggal 05 Desember 2023 1810220245722 Tanggal 18 Oktober 2022;500.10.29.7/Kep.632-DLH/2024 Tanggal 24 Desember 2024 Fungsi Hunian Fungsi Hunian 2
5 09/223 PT CLARICHEM INDONESIA No Telpon : 081315399694 Kawasan pergudangan taman tekno sektor XI blok L2 No 30 Ds/Kel. Setu Kec. Setu kota Tangerag Banten PERGUDANGAN DAN PENYIMPANAN 1586 0.7017 1112.8962 644./223 - SP /TR 07 Januari 2025 400.7.22/4427-KRK/03/Bid.Taru Tanggal 06 Desember 2024 9120008832767 Tanggal 26 Nopember 2024 Usaha Besar (Non-Mikro) Fungsi Usaha 1
6 10/657 PT HALKA GITA No Telpon : 0816734374 Jl Raya Majalaya KM 2 RT 02/09 Desa Hegarmanah Kec. Cikancung Kab. Bandung REVISI 1 KOMPLEK PERGUDANGAN 19100 0.699 13350.9 644./657 - SP /TR 23 Januari 2025 500.10.29.15/6750-REKTEK/TR Tanggal 21 Desember 2023 9120300510839 Tanggal 16 Desember 2023;500.10.29.7/Kep.609-DLH/2024 Tanggal 12 Nopember 2024 Usaha Besar (Non-Mikro) Fungsi Usaha 2
7 11/658 PT SURYA TIRTA KENCANA No Telpon : 08986155493 Kutawaringin Industrial Park Kav. 276-277 Desa Jelegong Kec. Kutawaringin Kab. Bandung PERGUDANGAN DAN PENYIMPANAN BAHAN KIMIA 3932 0.6995 2750.434 644./658 - SP /TR 23 Januari 2025;640/540 - SP /TR 20 Januari 2025 10112410113204010 Tanggal 10 Nopember 2024;591.4/031/IX-DPMPTSP/2020 Tanggal 03 Sseptember 2020 8120017120594 Tanggal 31 Agustus 2018;600.4.5/056/6838-Ktr/TL Tanggal 24 Desember 2024;0220305221438 Tanggal 20 Nopember 2021;500.10.29.7/4295/TL Tanggal 21 September 2024 Usaha Besar (Non-Mikro) Fungsi Usaha 1
8 13 / 550 PT BERKAH SEPUH JAYA No Telpon : 082118781624 Ruko komplek dewaadru blok R3 RT 005 ds/kec. Bojongsoang kab. Bandung REVISI 1 PERUMAHAN VILLA CEMARA ASRI 17379 0.5476 9516.7404 648.11/550 - SP/TR 20 Januari 2025 150223101113204049 Tanggal 15 Februari 2023 1233000451657 Tanggal 5 Mei 2023;500.10.29.7/kep.392-DLH/2025 Tgl. 2 Januari 2025 Fungsi Hunian Fungsi Hunian 2
9 14 / 655 PT RISKI ANUGERAH SEDJAHTERA No Telpon : 082118781624 Griya permata asri blok C2 No 18 Ds Lengkong Kec. Bojongsoang Kab. Bandung REVISI 1 PERUMAHAN TAMAN KATAPANG INDAH 28425 0.6532 18567.21 648.11 /655 - SP /TR 23 Januari 2025 500.10.29.15/2333-REKTEK/TR Tanggal 22 Mei 2024;500.10.29.15/3811-REKTEK/Bid. Taru Tanggal 10 Oktober 2024 1205000302875 Tanggal 02 April 2024;500.10.29.7/Kep.408-DLH/2025 Tanggal 10 Januari 2025 Fungsi Hunian Fungsi Hunian 2
10 15 / 551 PT BUMI SUWARNA SEJAHTERA No Telpon : 082118781624 Jl Ciwastra no 73 Kel. Mekarmulya kec. Rancasari kota bandung REVISI 1 PERUMAHAN BUMI KARA RESIDENCE 29711 0.5424 16115.2464 648.11/551-SP/TR 20 Januari 2025 500.10.29.15/6097-REKTEK/TR Tanggal 27 Nopember 2023 1311230078306 Tgl 13 Nopember 2023;500.10.29.7/Kep.493-DLH/2024 Tanggal 20 Maret 2024 Fungsi Hunian Fungsi Hunian 2
11 16 / 656 PT AMBER HASYA No Telpon : 081312411661 Jl Sarijadi raya No 111 Kel. Sarijadi Kec. Sukasarii kota bandung REVISI 1 PERUMAHAN GREEN HARMONI RESIDENCE 39520 0.2726 10773.152 648.11/656-SP/TR 23 Januari 2025 500.10.29.15/2732-REKTEK/Bid. Taru Tanggal 19 Juli 2024 9120004882226 Tgl 22 Agustus 2019;500.10.29.7/Kep.617-DLH/2024 Tanggal 4 Desember 2024 Fungsi Hunian Fungsi Hunian 2
12 17 / 224 PT KARYA UTAMA SEJATI JAYA No Telpon : 081320772222 Kp. Andir DS/Kel. Manggahang Kec. Baleendah Kab. Bandung PERUMAHAN GRAND CIPARAY RESIDENCE 12045 0.5945 7160.7525 648.11/224-SP/TR 07 Januari 2025;644/822-SP/TR 13 Februari 2025;640/524-SP/TR 17 Januari 2025;642/763-SP/TR 11 Februari 2025 500/5300-PERTEKTR/2022/TR Tanggal 04 Agustus 2022;400.7.22/4872-KRK/25/Bid.TR Tanggal 24 Desember 2024;Pu.650/7580-REKTEK/TR Tanggal 20 Desember 2022;01112410113204120 Tanggal 1 November 2024 1801220061544 Tgl 05 September 2022;600.4.5/052/1722-Ktr/TL Tanggal 20 Mei 2024;9120401971173 Tgl 11 Desember 2024;1406220076826 Tgl 15 November 2022;8120103802893 Tgl 29 Agustus 2018 Fungsi Hunian Fungsi Hunian 2
13 21 / 688 PT PRAKARSA FAJAR PROPERTINDO No Telpon : 082315094712 Jl. Otto Iskandardinata No 429 Kel. Pungkur Kec. Regol Kota Bandung PERUMAHAN ALAM BUMI CIPARAY 17417 0.6793 11831.3681 648.11/688-SP/TR 03 Februari 2025 500.10.29.15/1075-REKTEK/TR Tanggal 04 Maret 2024 9120007841259 Tgl 15 Agustus 2019;500.10.29.7/Kep.405-DLH/2025 Tanggal 06 Januari 2025 Fungsi Hunian Fungsi Hunian 2
14 22 / 767 PT CITRA SENTOSA JAYATAMA No Telpon : 087825393706 Jl. Nyengseret No 29 RT 01 Kel. Pelindung hewaqn Kota Bandung PERUMAHAN CITRA ASRI RESIDENCE 28900 0.692 19998.8 648.11/767-SP/TR 12 Februari 2025 500.10.29.15/2617-REKTEK/Bid. Taru Tanggal 10 Juli 2024 9120107971097 Tgl 19 September 2019;500.10.29.7/Kep.612-DLH/2024 Tanggal 18 November 2024 Fungsi Hunian Fungsi Hunian 2
15 23/ 641 PT PESONA MITRA KEMBAR MAS No Telpon : 081220763283 Jl Kembar mas IV No 2A RT 05/09 Kel. Pasirluyu Kec. Regol Kota Bandung REVISI 5 PERUMAHAN PODOMORO PARK 1160011 0.6691 776163.3601 648.11/641- SP/TR 22 Januari 2025 591/057-DPMPTSP/2017 Tanggal 28 Desember 2017 650/Kep.628-DLH/2018 Tanggal 30 Nopember 2018 Fungsi Hunian Fungsi Hunian 2
16 24 / 659 PT PAVITRA PARA ARTHA No Telpon : 081809063866 Jl . Kubang Beureum No 39 RT 007 / 011 Kelurahan Sekejati Kecamatan Buahbatu REVISI 4 PERUMAHAN PRIVATE VILLAGE 88245 0.5819 51349.7655 648.11/659 - SP/TR 23 Januari 2025;645/932 - SP/TR 21 Februari 2025;645/552-SP/TR 20 Januari 2025;647/640-SP/TR 22 Januari 2025;647/930-SP/TR 21 Februari 2025 591.4/001-BPMP/2014 Tanggal 06 Januari 2014;591.4/031-DPMPTSP/2019 Tanggal 10 Juli 2019;503/104/XII-DPMPTSP/2019- Tgl 20 Desember 2019;400.1.22/4521-KRK/15/Bid.Taru Tgl 16 Desember 2024;650/2597-KRTR/2021/TR Tgl 9 Desember 2021;500.10.29.15/2839-REKTEK/Bid.TAru Tgl 2 Agustus 2024 500.10.29.6/4898/TL Tanggal 08 Nopember 2023;667/2995/TL Tanggal 11 Agustus 2020;9120000431996 Tanggal 20 Oktober 2021;500.10.29.7/Kep.629-DLH/2024;9120605793306 Tanggal 9 Mei 2023;1601240112040181 Tanggal 16 Januari 2024;8120215192631 Tanggal 3 Januari 2022;34/LH.01.06.05/DLH Tanggal 3 Januari 2025;9120101841825 Tanggal 18 Oktober 2023 Fungsi Hunian Fungsi Hunian 2
17 29 / 931 PT KARYA HAFANA INTAN MANDIRI No Telpon : 082120002925 Kp./Desa Ciluluk Kecamatan Cikancung Kabupaten Bandung PERUMAHAN GRIYA CIHANYIR PERMAI 24432.33 0.5945 14525.02019 648.11/931 - SP/TR 21 Februari 2025 400.7.22/4840-KRK/21/Bid.Taru Tgl. 18 Desember 2024 2908210007625 Tanggal 06 Mei 2024;500.10.29.7/Kep.414-DLH/2025 Tanggal 30 Januari 2025 Fungsi Hunian Fungsi Hunian 2
18 30 / 933 PT ARUM JAYA PROPERTI No Telpon : 085100942672 Perumahan Banyu Arum Residence Blok A Kp. Tegal Tengah RT 003/013 Desa Cangkuang Kec. Rancaekek REVISI 1 PERUMAHAN BUMI ARUM REGENCY 58899 0.4838 28495.3362 648.11/933 - SP/PR 21 FEBRUARI 2025 591.4/026-DPMPTSP/2018 Tgl 24 September 2018 667/7210/TL Tanggal 16 Nopember 2018;500.10.29.6/Kep.420-DLH/2025 Tanmggal 3 Februari 2025 Fungsi Hunian Fungsi Hunian 2
19 31 / 762 SANDY SALMAN No Telpon : 082118781624 Jl Kembar VIII No 26 RT 005/010 Kel Cigereleng Kec. Regol Kota Bandung 40253 REVISI 1 PERUMAHAN D HEUVEL WIWAHA PADASUKA 3095 0.4295 1329.3025 648.11/ 762 - SP / PR 11 Februari 2025;648/523-SP/TR 17 Januari 2025;647/1140-SP/TR 27 Februari 2025;647/934-SP/TR 21 Februari 2025;647/1233-SP/TR 06 Maret 2025 650/5689-PETEKTR/2022/TR Tanggal 30 Agustus 2022;650/2748-KRTR/2021/TR Tgl 21 Desember 2021;593.SK.93-BKPMD/90 Tanggal 11 April 1990;593/SK.225-BKPMD/1991 Tanggal 28 Juni 1991;591,4/009-BPMP/2014 Tanggal 27 Maret 2014;500.10.29.15/711-REKTEK/TR Tanggal 15 Februari 2024;500.10.29.15/2877-REKTEK/Bid.Taru Tanggal 19 Agustus 2024 LH.01.04/5866-SPPL/TL Tanggal 27 Desember 2022;1402220064386 Tanggal 04 Februari 2022;500.10.29.7/Kep.593-DLH/2024 Tanggal 27 September 2024;667/3257/BPLH Tanggal 06 Oktober 2014;2711230388253 Tanggal 27 Nopember 2023;500.10.29.7/Kep.630-DLH/2024 Tanggal 20 Desember 2024;9120312050674 Tanggal 07 Oktober 2019;500.10.29.7/Kep.426-DLH/2025 Tanggal 14 Februari 2025 Fungsi Hunian Fungsi Hunian 1
20 36/ 1141 HANDRIAWAN No Telpon : 081313350382 Jalan Taman Holis Indah E-5 No 4-5 RT 03/06 Ds/Kel. Cigondewah Kec. Bandung kulon. Kota Bandung TOKO DAN GUDANG 2025 0.7848 1589.22 647/1141-SP/TR 27 Februari 2025;647/1211-SP/TR 04 Maret 2025;648.2/1091-SP/TR 25 Februari 2025 400.7.22/559-KRK/14/Bid.Taru Tanggal 17 Januari 2025;400.7.22/859-KRK/38/Bid.Taru Tanggal 17 Februari 2025;500.10.29.15/4064 - REKTEK/Bid.Taru Tanggal 25 Nopember 2024 1401250067962 Tanggal 14 Januari 2025;1812240057832 Tanggal 18 Desember 2024;1611210013009 Tanggal 16 Nopember 2021 Usaha Besar (Non-Mikro) Fungsi Usaha 1
21 39/ 1232 JOHANSJAH SUGIANTO No Telpon : 081314974495 Jalan Kepodang VI/6 blok k2 Rengas Kec. Ciputat timur kota Tangerang selatan TOKO 2490 21.9 54531 644.2/1232-SP/TR 06 Maret 2025;644.2/1234-SP/TR 06 Maret 2025 400.7.22/1089 - KRK/45//Bid.Taru Tanggal 24 Februari 2025;503/0017-PKKPRNB/DPMPTSP/II/2025 Tanggal 12 Februari 2025 3101250002573 Tanggal 31 Januari 2025;2107230112343 Tanggal 21 Juli 2023 Tidak Diketahui Tidak Diketahui 3
22 41 / 1090 TIM AD HOC PERUMAHAN SUKAWANGI RESIDENCE No Telpon : 08112255770 Kp. Cihalimun RT 02/04 Ds. Cibeureum Kec. Kertasari Kab. Bandung PERUMAHAN SUKAWANGI RESIDENCE 1 648.11/ 1090 - SP / PR 25 Februari 2025 Fungsi Hunian Fungsi Hunian 2
23 42 / 1298 PT TIGA REKAN INDONESIA No Telpon : 08122444717 Jl Gradiul No 40 RT 04/07 Kel. Rancaekek Kencana Kec. Rancaekek Kab. Bandung PERUMAHAN GREEN HILL VILLAGE 16548 0.5917 9791.4516 648.11/1298 - SP/TR 17 Maret 2025 50.10.29.15/2742-REKTEK/Bid. Taru Tanggal 24 Juli 2024 2505240130006 Tanggal 25 Mei 2024;600.4.5/061/832-Ktr/Bid. TL/2025 Tgl. 19 Februari 2025 Fungsi Hunian Fungsi Hunian 2
24 43 / 1322 PT UNILOA ARDIYANTO INVESTAMA No Telpon : 081220180480 Jl Raya Ebah 103 Desa Sukamantri Kecamatan Paseh Kab. Bandung PERUMAHAN MARISON CIPAKU 19209 0.6146 11805.8514 648.11/1322 - SP/TR 18 Maret 2025 24042410313204051 Tanggal 24 JApril 2024 0238010110358 Tanggal 11 Januari 2024;500.10.29.7/Kep. 451-DLH/2025 Tgl. 13 Maret 2025 Fungsi Hunian Fungsi Hunian 2
25 44 / 1323 PT RADINAKA KRAMAT ABADI No Telpon : 082118781624 Jl. Sukamukti RT 01/06 Desa Sukamukti Kec. Katapang Kab. Bandung PERUMAHAN GAHARU PALEDANG RESIDENCE 14665 0.68003 9972.63995 648.11/1323 - SP/TR 18 Maret 2025 02032510213204087 Tanggal 02 Maret 2025 0238010110358 Tanggal 11 Januari 2024;600.4.5/064/1165-Ktr/Bid. TL/2025 Tgl. 12 Maret 2025 Fungsi Hunian Fungsi Hunian 2
26 45 / 1361 PT SANGKURIANG KRAMAT ABADI No Telpon : 082118781624 Kampung rancakasiat Desa Rancamulya Kec. Pameungpeuk Kab. Bandung PERUMAHAN PONDOK ASRI SUKAMUKTI 20451 0.6806 13918.9506 648.11/1361 - SP/TR 24 Maret 2025 500.10.29.15/3451-REKTEK/Bid.Taru Tanggal 26 September 2024 0811230037204 Tanggal 08 Nopember 2023;500.10.29.7/Kep.460-DLH/2025 Tgl. 21 Maret 2025 Fungsi Hunian Fungsi Hunian 2
27 46 / 1360 PT AUF ABDURACCHMAN JAYA No Telpon : 082118781624 Jl. Rancabungur rancakasiat Desa Malakasari Kec. Baleendah Kab. Bandung PERUMAHAN BUMI SHANGRILA RESIDENCE 12846 0.6412 8236.8552 648.11/1360 - SP/TR 24 Maret 2025 400.7.22/093/1390-KRK/Bid.Taru Tanggal 17 Maret 2025 0603250028082 Tanggal 06 Maret 2025;600.4.5/072/1270-Ktr/Bid.TL/2025 Tgl. 21 Maret 2025 Fungsi Hunian Fungsi Hunian 2
28 47 / 1142 ANTO DWI HARTANTO No Telpon : 082126215611 Jl. Elang V No 8 RT 08/01 Kel. Garuda Kec. Andir Kota Bandung PERGUDANGAN DAN PENYIMPANAN 1307 0.5616 734.0112 647/1142 - SP/TR 27 Februari 2025;648.11/823 - SP/TR 13 Februari 2025;644.2/1389 - SP/TR 26 Maret 2025;644.2/1388 - SP/TR 26 Maret 2025 500.10.29.15/3827-REKTEK/Bid.Taru Tanggal 17 Oktober 2024;500.10.29.15/3944-REKTEK/Bid.Taru Tanggal 28 Oktober 2024;503/0072-DPMPTSP/XI/2023 Tanggal 29 November 2023;400.7.22/1046-KRK/Bid.Taru Tanggal 21 Februari 2025 2009240075774 Tanggal 20 September 2024 2025;2009240075774 Tanggal 20 September 2025;500.10.29.7/Hep-461-DLH/2025 Tanggal 21 Maret 2025;0298000921888 Tanggal 24 Januari 2022 Usaha Besar (Non-Mikro) Fungsi Usaha 1
29 51 / 1161 PT INGRIA PRATAMA CAPITALINDO Tbk No Telpon : 087722361043 Ruko Pondok Cabe Mutiara Jl Pondok cabe No 27 Kel. Pamulang Kec. Pamulang Kota Tangerang Selatan REVISI 2 PERUMAHAN BUKIT ESMA CICALENGKA 2 74300 0.6292 46749.56 648.11/1161 - SP/TR 28 Februari 2025 50.10.29.15/2927-REKTEK/Bid. Taru Tanggal 14 Agustus 2024 591.4/005/IX-DPMPTSP/2019 Tanggal 09 September 2019;500.10.29.7/Kep.613-DLH/2024 Tgl. 18 November 2025 Fungsi Hunian Fungsi Hunian 2
30 52 / 1441 PT ABADI MUKTI KIRANA No Telpon : 087821848944 Jl Terusan jamika No 88 Kel. Jamika Kec. Bojongloa kaler Kota Bandung PERUMAHAN KOTA BARU ARJASARI 222816 1 222816 648.11/1441 - SP/TR 11 April 2025 400.7.28/1710/TR Tanggal 23 April 2024 9120108201236 Tanggal 13 Februari 2019;500.10.29.7/Kep.419-DLH/2025 Tgl. 03 Februari 2025 Fungsi Hunian Fungsi Hunian 2
31 53 / 1566 PT MAKMUR INDAH DAMAI No Telpon : 081394722234 Jl. Podomoro Boulevard utara I Desa Lengkong Kec. Bojongsoang Kab. Bandung PERUMAHAN BAROS INDAH RESIDENCE 17513 1 17513 647/1566 - SP/TR 25 April 2025 50.10.29.15/2507-REKTEK/Bid. Taru Tanggal 25 Juni 2024 2106240023693 Tanggal 21 Juni 2024;500.10.29.7/Kep.447-DLH/2024 Tgl. 10 Maret 2025 Fungsi Hunian Fungsi Hunian 2
32 54 / 1343 PT KARYA BUMI BESTARI No Telpon : 08156080428 Kembar Mas IV No 2A RT 05/09 Kel. Pasirluyu Kec. Regol Kota Bandung PERUMAHAN BESTARI MAS 26134 1 26134 648.11/1343 - SP/TR 20 Maret 2025;647/1565 - SP/TR 25 April 2025;648.1/1543 - SP/TR 17 April 2025 12042310213204004 Tanggal 12 April 2023;23122410113204057 Tanggal 23 Desember 2024;400.7.22/1047-KRK/42/Bid.Taru Tanggal 21 Februari 2025 1204230043635 Tanggal 12 April 2023;500.10.29.7/964/Bid. TL/2025 Tgl. 19 Februari 2025;9120304390956 Tanggal 05 Desember 2023;0307230050445 Tanggal 03 Juli 2023 Fungsi Hunian Fungsi Hunian 2
33 57 / 1372 TENNI DIANA No Telpon : 082116106640 Cikaahuripan RT 002/005 Kec. Neglasari Kota Tangerang PERADAGANGAN KHUSUS KARPET PERMADANI DAN PENUTUP DINDING DN LANTAI DI TOKO 1290 1 1290 644/1372 - SP/TR 26 Maret 2025;647/1557 - SP/TR 22 April 2025;640/1559 - SP/TR 22 April 2025;644/1371 - SP/TR 26 Maret 2025;644/1375 - SP/TR 26 Maret 2025;644/1373 - SP/TR 26 Maret 2025 500.10.29.15/4355-REKTEK/TR Tanggal 25 Agustus 2023;500.10.29.15/1073-REKTEK/TR Tanggal 04 Maret 2024;400.7.22/759-KRK/34/Bid.Taru Tanggal 11 Februari 2025;500.10.29.15/4654-REKTEK/Bid. Taru Tanggal 08 September 2023;500.10.29.15/4160-REKTEK/Bid. Taru Tanggal 15 Nopember 2024;500.10.29.15/2500-REKTEK/Bid. Taru Tanggal 19 Juni 2024 9120306652149 Tanggal 26 Juni 2019;0220107462528 Tanggal 08 September 2022;1283000240318 Tanggal 01 Februari 2021;600.4.5/071-KTR/Bid.TL/2025 Tgl. 05 Mei 2025;0220301251224 Tanggal 11 Agustus 2023;1263000210596 Tanggal 09 Februari 2021;1405240295325 5 Mei 2024 Tidak Diketahui Tidak Diketahui 1
34 63 / 1567 Ir. BERSIH TARIGANT No Telpon : 081322777581 Jl Multatuli No 3 rt 001/001 Ds/Kel. Lebakgede Kec. Coblong Kota Bandung PERUMAHAN TUSCANY HILL 23796 0.2428 5777.6688 648.11/1567 - SP/TR 25 April 2025 50.10.29.15/3829-REKTEK/Bid. Taru Tanggal 15 Oktober 2024 2007220014068 Tanggal 20 Juli 2025;500.10.29.7/Kep.429-DLH/2025 Tgl. 10 Maret 2025 Fungsi Hunian Fungsi Hunian 2
35 64 / 1682 PT CIPTA BERKAT PROPERTI No Telpon : 081910661980 Ruko Matahari Cigado No 25 Jalan Anggadireja Kel. Baleendah Kec. Baleendah Kab. Bandung PERUMAHAN GARDEN VIEW CICALENGKA 45666 1 45666 648.11/1682 - SP/TR 06 Mei 2025 50.10.29.15/3156-REKTEK/Bid. Taru Tanggal 05 September 2024 2310210017936 Tanggal 23 Oktober 2021;500.10.29.7/Kep.477-DLH/2025 Tgl. 17 April 2025 Fungsi Hunian Fungsi Hunian 2
36 64 / 1682 PT CIPTA BERKAT PROPERTI No Telpon : 081910661980 Ruko Matahari Cigado No 25 Jalan Anggadireja Kel. Baleendah Kec. Baleendah Kab. Bandung C. KWT PERUMAHAN 0 1 0 Fungsi Hunian Fungsi Hunian 1
37 65 / 1644 PT ARGUNA JAYA PROPERTY No Telpon : 081214211164 Jl Pidada IV/4 Denpasar BR/Link Sedana metra Kel. Ubung Kec. Denpasar Utara Kota Denpasar REVISI 1 PERUMAHAN ARGUNA SINDANGPANON 8553 0.53 4533.09 648.11/1644 - SP/TR 29 April 2025;644/1558 - SP/TR 22 April 2025 503/009/II-DPMPTSP/2020 Tanggal 25 Februari 2020;50.10.29.15/4063-REKTEK/Bid. Taru Tanggal 25 Nopember 2025 9120004890734 Tanggal 03 Agustus 2019;667/2081/TL Tgl. 08 Juni 2020;0609240042720001 Tanggal 06 September 2024 Fungsi Hunian Fungsi Hunian 1
38 67/ 1370 NENENG FATIMAH No Telpon : 08977980040 Jalan Batu Indah I No 26 RT 002/003 Kel. Batununggal Kec. Bandung Kidul Kota Bandung PERGUDANGAN DAN PENYIMPANAN 2034 1 2034 644/1370 - SP/TR 26 Maret 2025 50.10.29.15/3340-REKTEK/Bid. Taru Tanggal 18 September 2025 1009240038601 Tanggal 10 September 2024 Usaha Besar (Non-Mikro) Fungsi Usaha 1
39 68/ 1374 LILI JOJON No Telpon : 082219855556 Kp. Cibisoro RT 004/008 Desa Nanjung Kec. Mrgaasih Kab. Bandung PERGUDANGAN DAN PENYIMPANAN 1654 1 1654 644/1374 - SP/TR 26 Maret 2025;644.2/1683 - SP/TR 06 Mei 2025;645/1730 - SP/TR 07 Mei 2025 50.10.29.15/3210-REKTEK/Bid. Taru Tanggal 10 September 2024;400.7.22/1254-KRK/58/Bid. Taru Tanggal 10 Maret 2025;50.10.29.15/3443-REKTEK/Bid. Taru Tanggal 26 September 2024 1220000361065 Tanggal 16 Maret 2021;1712240056863 Tanggal 17 Desember 2024;0103230081544 Tanggal 01 Maret 2023;600.4.5/071/1503-Ktr/Bid. TL/2025 Tanggal 17 April 2025 Usaha Besar (Non-Mikro) Fungsi Usaha 1
40 71/ 1845 PT SANGKURIANG KRAMAT ABADI No Telpon : 082116602145 Kp. Rancakasiat Desa Rancamulya Kec. Pameungpeuk Kab. Bandung PERUMAHAN CLUSTER GAHARU EMERALD 19889 1 19889 648.11 /1845 - SP/TR 19 Mei 2025 400.7.22/1526-KRK/96/Bid. Taru Tanggal 22 April 2025 0811230037204 Tanggal 08 Nopember 2023;500.10.29.7/Kep. 489-DLH/2025 Tanggal 15 Mei 2025 Fungsi Hunian Fungsi Hunian 2
41 72/ 1731 PT MARGA TIRTA KENCANA No Telpon : 082130000146 Jlan BKR Lingkar selatan No 140 Kota Bandung PERUMAHAN TAMAN CIBADUYUT INDAH 2 DAN 3 205344 1 205344 648.11 /1731 - SP/TR 07 Mei 2025;648.11 /1946 - SP/TR 22 Mei 2025;643 /1881 - SP/TR 20 Mei 2025;644 /1684 - SP/TR 06 Mei 2025;648.12 /1825 - SP/TR 16 Mei 2025 591.4/002-BPMP/2010 Tanggal 10 Januari 2010;500.10.29.15/3108-REKTEK/Bid.taru Tanggal 29 Agustus 2024;500.10.29.15/3319-REKTEK/TR Tanggal 14 Juli 2023;12092410313204101 Tanggal 12 September 2024;400.7.22/1231-KRK/Bid.Taru Tanggal 06 Maret 2025 667/3803/DLH Tanggal 22 Desember 2016;9120204202309 Tanggal 20 Februari 2019;2002230028317 Tanggal 20 Februari 2023;600.4.5/073/1562-Ktr/Bid.TL/2025 Tanggal 23 April 2025;8120006931402 Tanggal 28 September 2022;12710006324580004 Tanggal 05 Maret 2025 Fungsi Hunian Fungsi Hunian 2
42 77/ 1879 PT PUTRA RAHMAN PRADANA No Telpon : 081312214962 Perum Cemerlang Permai Blok C No 16 Kel. Sukakarya Kec. Warudoyongf Kota Sukabumi REVISI 1 PERUMAHAN BUKIT PINUS BANIARAN 90711 1 90711 648.11 /1879 - SP/TR 20 Mei 2025 22122310313204009 Tanggal 22 Desember 2023 500.10.29.7/Kep.482-DLH/2025 Tanggal 24 April 2025 Fungsi Hunian Fungsi Hunian 2

56
Dockerfile Normal file
View File

@@ -0,0 +1,56 @@
FROM node:18 AS node-base
# Development stage
FROM node-base AS development
WORKDIR /var/www
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 5173
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
# Production stage
FROM php:8.2-fpm AS production
WORKDIR /var/www
# Install PHP extensions
RUN apt-get update && apt-get install -y \
git curl zip unzip libpng-dev libonig-dev libxml2-dev libzip-dev \
&& docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
# Install Node.js
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs
# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Copy application files
COPY . .
# Install dependencies
RUN composer install --no-dev --optimize-autoloader
# Install and build frontend assets
RUN npm install \
&& npm run build \
&& ls -la public/build \
&& mkdir -p public/assets \
&& cp -r public/build/* public/assets/ \
&& ls -la public/assets \
&& rm -rf node_modules \
&& rm -rf public/build
# Laravel caches
RUN php artisan config:clear \
&& php artisan route:clear \
&& php artisan view:clear \
&& php artisan optimize
RUN php artisan storage:link
# Permissions
RUN chown -R www-data:www-data /var/www && chmod -R 755 /var/www/storage /var/www/public
EXPOSE 9000
CMD ["php-fpm"]

View File

@@ -0,0 +1,210 @@
# Struktur Tabel Retribusi PBG yang Dioptimalkan
## Ringkasan Optimasi
Struktur tabel baru ini **lebih sederhana**, **fokus pada perhitungan**, dan **menghilangkan redundansi** dari struktur sebelumnya.
## Perbandingan Struktur
### SEBELUM (Kompleks)
- `building_functions` - 8 kolom + relationship kompleks
- `building_function_parameters` - 12 kolom dengan mismatch model/migration
- `retribution_formulas` - Menyimpan formula sebagai string
- `retribution_proposals` - 15+ kolom dengan banyak redundansi
- `floor_height_indices` - OK, tidak berubah
### SESUDAH (Sederhana)
- `building_types` - **7 kolom**, hierarki sederhana
- `retribution_indices` - **6 kolom**, parameter calculation saja
- `height_indices` - **3 kolom**, sama seperti sebelumnya
- `retribution_configs` - **5 kolom**, konfigurasi global
- `retribution_calculations` - **8 kolom**, hasil perhitungan saja
---
## Detail Struktur Tabel Baru
### 1. `building_types`
**Fungsi:** Menyimpan jenis fungsi bangunan dengan hierarki sederhana
| Kolom | Tipe | Keterangan |
| ------------- | ------------ | ---------------------------------- |
| `id` | bigint | Primary key |
| `code` | varchar(10) | Kode unik (UMKM, KEAGAMAAN, dll) |
| `name` | varchar(100) | Nama fungsi bangunan |
| `parent_id` | bigint | ID parent (untuk hierarki) |
| `level` | tinyint | Level hierarki (1=parent, 2=child) |
| `coefficient` | decimal(8,4) | **Koefisien untuk perhitungan** |
| `is_free` | boolean | **Apakah gratis (keagamaan, MBR)** |
### 2. `retribution_indices`
**Fungsi:** Menyimpan parameter indeks untuk perhitungan (1:1 dengan building_types)
| Kolom | Tipe | Keterangan |
| ----------------------- | ------------ | ---------------------------------- |
| `id` | bigint | Primary key |
| `building_type_id` | bigint | FK ke building_types |
| `ip_permanent` | decimal(8,4) | **Indeks Permanensi** |
| `ip_complexity` | decimal(8,4) | **Indeks Kompleksitas** |
| `locality_index` | decimal(8,4) | **Indeks Lokalitas** |
| `infrastructure_factor` | decimal(8,4) | **Faktor prasarana (default 50%)** |
### 3. `height_indices`
**Fungsi:** Indeks ketinggian per lantai (sama seperti sebelumnya)
| Kolom | Tipe | Keterangan |
| -------------- | ------------ | ---------------------------------------- |
| `id` | bigint | Primary key |
| `floor_number` | tinyint | Nomor lantai (1,2,3,4,5,6) |
| `height_index` | decimal(8,6) | **IP Ketinggian (1.0, 1.09, 1.12, dst)** |
### 4. `retribution_configs`
**Fungsi:** Konfigurasi global untuk perhitungan (menggantikan hard-coded values)
| Kolom | Tipe | Keterangan |
| ------------- | ------------- | --------------------- |
| `id` | bigint | Primary key |
| `key` | varchar(50) | Kunci konfigurasi |
| `value` | decimal(15,2) | **Nilai konfigurasi** |
| `description` | varchar(200) | Deskripsi |
**Data yang disimpan:**
- `BASE_VALUE` = 70350 (nilai dasar)
- `INFRASTRUCTURE_MULTIPLIER` = 0.5 (50% prasarana)
- `HEIGHT_MULTIPLIER` = 0.5 (pengali indeks ketinggian)
### 5. `retribution_calculations`
**Fungsi:** Hasil perhitungan retribusi (history)
| Kolom | Tipe | Keterangan |
| -------------------- | ------------- | -------------------------------- |
| `id` | bigint | Primary key |
| `calculation_id` | varchar(20) | ID unik perhitungan |
| `building_type_id` | bigint | FK ke building_types |
| `floor_number` | tinyint | Lantai yang dipilih |
| `building_area` | decimal(12,2) | **Luas bangunan input** |
| `retribution_amount` | decimal(15,2) | **Hasil perhitungan** |
| `calculation_detail` | json | **Detail breakdown perhitungan** |
| `calculated_at` | timestamp | Waktu perhitungan |
---
## Formula Perhitungan
### Formula Excel yang Diimplementasikan:
```
H13 = coefficient * (ip_permanent + ip_complexity + (0.5 * height_index))
Main Calculation = building_area * (locality_index * BASE_VALUE * H13)
Infrastructure = INFRASTRUCTURE_MULTIPLIER * Main Calculation
Total Retribution = Main Calculation + Infrastructure
```
### Implementasi dalam Service:
```php
// Step 1: Calculate H13 coefficient
$h13 = $buildingType->coefficient * (
$indices->ip_permanent +
$indices->ip_complexity +
(0.5 * $heightIndex)
);
// Step 2: Main calculation
$mainCalculation = $buildingArea * ($indices->locality_index * $baseValue * $h13);
// Step 3: Infrastructure (50% additional)
$infrastructureCalculation = 0.5 * $mainCalculation;
// Step 4: Total
$totalRetribution = $mainCalculation + $infrastructureCalculation;
```
---
## Keuntungan Struktur Baru
### ✅ **Simplicity**
- **5 tabel** vs 8+ tabel sebelumnya
- **Kolom minimal** hanya yang diperlukan untuk perhitungan
- **No redundant data** seperti ip_ketinggian di proposals
### ✅ **Performance**
- **Proper indexes** untuk query yang sering digunakan
- **Normalized structure** mengurangi storage
- **Cached configs** untuk values yang jarang berubah
### ✅ **Maintainability**
- **Clear separation** antara master data dan calculation results
- **Configurable values** tidak hard-coded lagi
- **Single responsibility** setiap tabel punya tujuan jelas
### ✅ **Flexibility**
- **Easy to extend** untuk fungsi bangunan baru
- **Configurable formulas** lewat RetributionConfig
- **Audit trail** lewat calculation history
### ✅ **Data Integrity**
- **Proper constraints** untuk validasi data
- **Foreign key relationships** yang benar
- **No model-migration mismatch**
---
## Migration Guide
### Langkah Implementasi:
1. **Run Migration:** `php artisan migrate` untuk tabel baru
2. **Seed Data:** Data master berdasarkan Excel akan otomatis ter-seed
3. **Update Code:** Ganti penggunaan model lama dengan model baru
4. **Test Calculation:** Verifikasi hasil perhitungan sama dengan Excel
5. **Deploy:** Struktur siap production
### Data Migration (Optional):
Jika ada data existing di tabel lama yang perlu dipindahkan, buat script migration untuk transfer data dari struktur lama ke struktur baru.
---
## Usage Example
```php
// Initialize service
$calculator = new RetributionCalculatorService();
// Calculate retribution
$result = $calculator->calculate(
buildingTypeId: 8, // UMKM
floorNumber: 2, // 2 lantai
buildingArea: 100.50, // 100.5 m2
saveResult: true // Simpan ke database
);
// Result structure
[
'building_type' => [...],
'total_retribution' => 31658.25,
'formatted_amount' => 'Rp 31,658.25',
'calculation_steps' => [...],
'calculation_id' => 'RTB-20250130140530-123'
]
```
Struktur ini **jauh lebih clean**, **mudah dipahami**, dan **optimal untuk perhitungan retribusi PBG**!

0
README.md Executable file → Normal file
View File

View File

@@ -0,0 +1,467 @@
<?php
namespace App\Console\Commands;
use App\Models\SpatialPlanning;
use App\Models\RetributionCalculation;
use App\Models\BuildingType;
use App\Services\RetributionCalculatorService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class AssignSpatialPlanningsToCalculation extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'spatial-planning:assign-calculations
{--force : Force assign even if already has calculation}
{--recalculate : Recalculate existing calculations with new values}
{--chunk=100 : Process in chunks}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Assign retribution calculations to spatial plannings (supports recalculate for existing calculations)';
protected $calculatorService;
public function __construct(RetributionCalculatorService $calculatorService)
{
parent::__construct();
$this->calculatorService = $calculatorService;
}
/**
* Execute the console command.
*/
public function handle()
{
$this->info('🏗️ Starting spatial planning calculation assignment...');
// Get processing options
$force = $this->option('force');
$recalculate = $this->option('recalculate');
$chunkSize = (int) $this->option('chunk');
// Get spatial plannings query
$query = SpatialPlanning::query();
if ($recalculate) {
// Recalculate mode: only process those WITH active calculations
$query->whereHas('retributionCalculations', function ($q) {
$q->where('is_active', true);
});
$this->info('🔄 Recalculate mode: Processing spatial plannings with existing calculations');
} elseif (!$force) {
// Normal mode: only process those without active calculations
$query->whereDoesntHave('retributionCalculations', function ($q) {
$q->where('is_active', true);
});
$this->info(' Normal mode: Processing spatial plannings without calculations');
} else {
// Force mode: process all
$this->info('🔥 Force mode: Processing ALL spatial plannings');
}
$totalRecords = $query->count();
if ($totalRecords === 0) {
$this->warn('No spatial plannings found to process.');
return 0;
}
$this->info("Found {$totalRecords} spatial planning(s) to process");
if (!$this->confirm('Do you want to continue?')) {
$this->info('Operation cancelled.');
return 0;
}
// Process in chunks
$processed = 0;
$errors = 0;
$reused = 0;
$created = 0;
$buildingTypeStats = [];
$progressBar = $this->output->createProgressBar($totalRecords);
$progressBar->start();
$recalculated = 0;
$query->chunk($chunkSize, function ($spatialPlannings) use (&$processed, &$errors, &$reused, &$created, &$recalculated, &$buildingTypeStats, $progressBar, $recalculate) {
foreach ($spatialPlannings as $spatialPlanning) {
try {
$result = $this->assignCalculationToSpatialPlanning($spatialPlanning, $recalculate);
if ($result['reused']) {
$reused++;
} elseif (isset($result['recalculated']) && $result['recalculated']) {
$recalculated++;
} else {
$created++;
}
// Track building type statistics
$buildingTypeName = $result['building_type_name'] ?? 'Unknown';
if (!isset($buildingTypeStats[$buildingTypeName])) {
$buildingTypeStats[$buildingTypeName] = 0;
}
$buildingTypeStats[$buildingTypeName]++;
$processed++;
} catch (\Exception $e) {
$errors++;
$this->error("Error processing ID {$spatialPlanning->id}: " . $e->getMessage());
}
$progressBar->advance();
}
});
$progressBar->finish();
// Show summary
$this->newLine(2);
$this->info('✅ Assignment completed!');
if ($recalculate) {
$this->table(
['Metric', 'Count'],
[
['Total Processed', $processed],
['Recalculated (Changed)', $recalculated],
['Unchanged', $reused],
['Errors', $errors],
]
);
} else {
$this->table(
['Metric', 'Count'],
[
['Total Processed', $processed],
['Calculations Created', $created],
['Calculations Reused', $reused],
['Errors', $errors],
]
);
}
// Show building type statistics
if (!empty($buildingTypeStats)) {
$this->newLine();
$this->info('📊 Building Type Distribution:');
$statsRows = [];
arsort($buildingTypeStats); // Sort by count descending
foreach ($buildingTypeStats as $typeName => $count) {
$percentage = round(($count / $processed) * 100, 1);
$statsRows[] = [$typeName, $count, $percentage . '%'];
}
$this->table(['Building Type', 'Count', 'Percentage'], $statsRows);
}
return 0;
}
/**
* Assign calculation to a spatial planning
*/
private function assignCalculationToSpatialPlanning(SpatialPlanning $spatialPlanning, bool $recalculate = false): array
{
// 1. Detect building type
$buildingType = $this->detectBuildingType($spatialPlanning->building_function);
// 2. Get calculation parameters (round to 2 decimal places)
$floorNumber = $spatialPlanning->number_of_floors ?: 1;
$buildingArea = round($spatialPlanning->getCalculationArea(), 2);
if ($buildingArea <= 0) {
throw new \Exception("Invalid building area: {$buildingArea}");
}
$reused = false;
$isRecalculated = false;
if ($recalculate) {
// Recalculate mode: Always create new calculation
$calculationResult = $this->performCalculation($spatialPlanning, $buildingType);
// Check if spatial planning has existing active calculation
$currentActiveCalculation = $spatialPlanning->activeRetributionCalculation;
if ($currentActiveCalculation) {
$oldAmount = $currentActiveCalculation->retributionCalculation->retribution_amount;
$oldArea = $currentActiveCalculation->retributionCalculation->building_area;
$newAmount = $calculationResult['amount'];
// Check if there's a significant difference (more than 1 rupiah)
if (abs($oldAmount - $newAmount) > 1) {
// Create new calculation
$calculation = RetributionCalculation::create([
'building_type_id' => $buildingType->id,
'floor_number' => $floorNumber,
'building_area' => $buildingArea,
'retribution_amount' => $calculationResult['amount'],
'calculation_detail' => $calculationResult['detail'],
]);
// Assign new calculation
$spatialPlanning->assignRetributionCalculation(
$calculation,
"Recalculated: Area {$oldArea}{$buildingArea}, Amount {$oldAmount}{$newAmount}"
);
$isRecalculated = true;
} else {
// No significant difference, keep existing
$calculation = $currentActiveCalculation->retributionCalculation;
$reused = true;
}
} else {
// No existing calculation, create new
$calculation = RetributionCalculation::create([
'building_type_id' => $buildingType->id,
'floor_number' => $floorNumber,
'building_area' => $buildingArea,
'retribution_amount' => $calculationResult['amount'],
'calculation_detail' => $calculationResult['detail'],
]);
$spatialPlanning->assignRetributionCalculation(
$calculation,
'Recalculated (new calculation)'
);
}
} else {
// Normal mode: Check if calculation already exists with same parameters
$existingCalculation = RetributionCalculation::where([
'building_type_id' => $buildingType->id,
'floor_number' => $floorNumber,
])
->whereBetween('building_area', [
$buildingArea * 0.99, // 1% tolerance
$buildingArea * 1.01
])
->first();
if ($existingCalculation) {
// Reuse existing calculation
$calculation = $existingCalculation;
$reused = true;
} else {
// Create new calculation
$calculationResult = $this->performCalculation($spatialPlanning, $buildingType);
$calculation = RetributionCalculation::create([
'building_type_id' => $buildingType->id,
'floor_number' => $floorNumber,
'building_area' => $buildingArea,
'retribution_amount' => $calculationResult['amount'],
'calculation_detail' => $calculationResult['detail'],
]);
}
// Assign to spatial planning
$spatialPlanning->assignRetributionCalculation(
$calculation,
$reused ? 'Auto-assigned (reused calculation)' : 'Auto-assigned (new calculation)'
);
}
return [
'calculation' => $calculation,
'reused' => $reused,
'recalculated' => $isRecalculated,
'building_type_name' => $buildingType->name,
'building_type_code' => $buildingType->code,
];
}
/**
* Detect building type based on building function using database
*/
private function detectBuildingType(string $buildingFunction = null): BuildingType
{
$function = strtolower($buildingFunction ?? '');
// Mapping building functions to building type codes from database
$mappings = [
// Religious
'masjid' => 'KEAGAMAAN',
'gereja' => 'KEAGAMAAN',
'vihara' => 'KEAGAMAAN',
'pura' => 'KEAGAMAAN',
'keagamaan' => 'KEAGAMAAN',
'religious' => 'KEAGAMAAN',
// Residential/Housing
'rumah' => 'HUN_SEDH', // Default to simple housing
'perumahan' => 'HUN_SEDH',
'hunian' => 'HUN_SEDH',
'residential' => 'HUN_SEDH',
'tinggal' => 'HUN_SEDH',
'mbr' => 'MBR', // Specifically for MBR
'masyarakat berpenghasilan rendah' => 'MBR',
// Commercial/Business - default to UMKM
'toko' => 'UMKM',
'warung' => 'UMKM',
'perdagangan' => 'UMKM',
'dagang' => 'UMKM',
'usaha' => 'UMKM',
'komersial' => 'UMKM',
'commercial' => 'UMKM',
'pasar' => 'UMKM',
'kios' => 'UMKM',
// Large commercial
'mall' => 'USH_BESAR',
'plaza' => 'USH_BESAR',
'supermarket' => 'USH_BESAR',
'department' => 'USH_BESAR',
'hotel' => 'USH_BESAR',
'resort' => 'USH_BESAR',
// Office
'kantor' => 'UMKM', // Can be UMKM or USH_BESAR depending on size
'perkantoran' => 'UMKM',
'office' => 'UMKM',
// Industry (usually big business)
'industri' => 'USH_BESAR',
'pabrik' => 'USH_BESAR',
'gudang' => 'USH_BESAR',
'warehouse' => 'USH_BESAR',
'manufacturing' => 'USH_BESAR',
// Social/Cultural
'sekolah' => 'SOSBUDAYA',
'pendidikan' => 'SOSBUDAYA',
'universitas' => 'SOSBUDAYA',
'kampus' => 'SOSBUDAYA',
'rumah sakit' => 'SOSBUDAYA',
'klinik' => 'SOSBUDAYA',
'kesehatan' => 'SOSBUDAYA',
'puskesmas' => 'SOSBUDAYA',
'museum' => 'SOSBUDAYA',
'perpustakaan' => 'SOSBUDAYA',
'gedung olahraga' => 'SOSBUDAYA',
// Mixed use
'campuran' => 'CAMP_KECIL', // Default to small mixed
'mixed' => 'CAMP_KECIL',
];
// Try to match building function
$detectedCode = null;
foreach ($mappings as $keyword => $code) {
if (str_contains($function, $keyword)) {
$detectedCode = $code;
break;
}
}
// Find building type in database by code
if ($detectedCode) {
$buildingType = BuildingType::where('code', $detectedCode)
->whereHas('indices') // Only types with indices
->first();
if ($buildingType) {
return $buildingType;
}
}
// Default to "UMKM" type if not detected (most common business type)
$defaultType = BuildingType::where('code', 'UMKM')
->whereHas('indices')
->first();
if ($defaultType) {
return $defaultType;
}
// Fallback to any available type with indices
$fallbackType = BuildingType::whereHas('indices')
->where('is_active', true)
->first();
if (!$fallbackType) {
throw new \Exception('No building types with indices found in database. Please run: php artisan db:seed --class=RetributionDataSeeder');
}
return $fallbackType;
}
/**
* Perform calculation using RetributionCalculatorService
*/
private function performCalculation(SpatialPlanning $spatialPlanning, BuildingType $buildingType): array
{
// Round area to 2 decimal places to match database storage format
$buildingArea = round($spatialPlanning->getCalculationArea(), 2);
$floorNumber = $spatialPlanning->number_of_floors ?: 1;
try {
// Use the same calculation service as TestRetributionCalculation
$result = $this->calculatorService->calculate(
$buildingType->id,
$floorNumber,
$buildingArea,
false // Don't save to database, we'll handle that separately
);
return [
'amount' => $result['total_retribution'],
'detail' => [
'building_type_id' => $buildingType->id,
'building_type_name' => $buildingType->name,
'building_type_code' => $buildingType->code,
'coefficient' => $result['indices']['coefficient'],
'ip_permanent' => $result['indices']['ip_permanent'],
'ip_complexity' => $result['indices']['ip_complexity'],
'locality_index' => $result['indices']['locality_index'],
'height_index' => $result['input_parameters']['height_index'],
'infrastructure_factor' => $result['indices']['infrastructure_factor'],
'building_area' => $buildingArea,
'floor_number' => $floorNumber,
'building_function' => $spatialPlanning->building_function,
'calculation_steps' => $result['calculation_detail'],
'base_value' => $result['input_parameters']['base_value'],
'is_free' => $buildingType->is_free,
'calculation_date' => now()->toDateTimeString(),
'total' => $result['total_retribution'],
]
];
} catch (\Exception $e) {
// Fallback to basic calculation if service fails
$this->warn("Calculation service failed for {$spatialPlanning->name}: {$e->getMessage()}. Using fallback calculation.");
// Basic fallback calculation
$totalAmount = $buildingType->is_free ? 0 : ($buildingArea * 50000);
return [
'amount' => $totalAmount,
'detail' => [
'building_type_id' => $buildingType->id,
'building_type_name' => $buildingType->name,
'building_type_code' => $buildingType->code,
'building_area' => $buildingArea,
'floor_number' => $floorNumber,
'building_function' => $spatialPlanning->building_function,
'calculation_method' => 'fallback',
'error_message' => $e->getMessage(),
'is_free' => $buildingType->is_free,
'calculation_date' => now()->toDateTimeString(),
'total' => $totalAmount,
]
];
}
}
}

View File

@@ -0,0 +1,317 @@
<?php
namespace App\Console\Commands;
use App\Models\SpatialPlanning;
use Illuminate\Console\Command;
use Maatwebsite\Excel\Facades\Excel;
use Illuminate\Support\Facades\DB;
use Exception;
class InitSpatialPlanningDatas extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'spatial:init {file? : Path to the CSV/Excel file} {--truncate : Clear existing data before import} {--safe-truncate : Clear only spatial plannings without retribution proposals} {--force-truncate : Force truncate by disabling foreign key checks}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Import spatial planning data from CSV/Excel file for retribution';
/**
* Execute the console command.
*/
public function handle()
{
$filePath = $this->argument('file') ?? 'public/templates/Data_2025___Estimasi_Jumlah_Lantai.csv';
$fullPath = storage_path('app/' . $filePath);
// Check if file exists
if (!file_exists($fullPath)) {
$this->error("File not found: {$fullPath}");
$this->info("Available files in templates:");
$this->listAvailableFiles();
return 1;
}
// Handle truncate options
if (($this->option('truncate') && $this->option('safe-truncate')) ||
($this->option('truncate') && $this->option('force-truncate')) ||
($this->option('safe-truncate') && $this->option('force-truncate'))) {
$this->error('Cannot use multiple truncate options together. Choose only one.');
return 1;
}
// Confirm truncate if requested
if ($this->option('truncate')) {
if ($this->confirm('This will delete all existing spatial planning data and related retribution proposals. Continue?')) {
$this->info('Truncating tables...');
try {
// First delete retribution proposals that reference spatial plannings
$deletedProposals = DB::table('retribution_proposals')
->whereNotNull('spatial_planning_id')
->count();
if ($deletedProposals > 0) {
DB::table('retribution_proposals')
->whereNotNull('spatial_planning_id')
->delete();
$this->info("Deleted {$deletedProposals} retribution proposals linked to spatial plannings.");
}
// Method 1: Try truncate with disabled foreign key checks
try {
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.');
} catch (\Exception $truncateError) {
// Method 2: Fallback to delete if truncate fails
$this->warn('Truncate failed, using delete method...');
$deletedSpatial = DB::table('spatial_plannings')->delete();
$this->info("Deleted {$deletedSpatial} spatial planning records.");
// Reset auto increment
DB::statement('ALTER TABLE spatial_plannings AUTO_INCREMENT = 1');
}
} catch (\Exception $e) {
$this->error('Failed to truncate tables: ' . $e->getMessage());
return 1;
}
} else {
$this->info('Operation cancelled.');
return 0;
}
}
// Force truncate - disable foreign key checks and truncate everything
if ($this->option('force-truncate')) {
if ($this->confirm('This will FORCE truncate ALL spatial planning data by disabling foreign key checks. This is risky! Continue?')) {
$this->info('Force truncating with disabled foreign key checks...');
try {
DB::beginTransaction();
// Disable foreign key checks
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
// Truncate both tables
DB::table('retribution_proposals')->truncate();
DB::table('spatial_plannings')->truncate();
// Re-enable foreign key checks
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
$this->info('Force truncate completed successfully.');
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
// 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 force truncate: ' . $e->getMessage());
return 1;
}
} else {
$this->info('Operation cancelled.');
return 0;
}
}
// Safe truncate - only delete spatial plannings without retribution proposals
if ($this->option('safe-truncate')) {
if ($this->confirm('This will delete only spatial planning data that have no retribution proposals. Continue?')) {
$this->info('Safe truncating spatial plannings...');
try {
DB::beginTransaction();
// Count spatial plannings with retribution proposals
$withProposals = DB::table('spatial_plannings')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('retribution_proposals')
->whereColumn('retribution_proposals.spatial_planning_id', 'spatial_plannings.id');
})
->count();
// Delete spatial plannings without retribution proposals
$deletedCount = DB::table('spatial_plannings')
->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('retribution_proposals')
->whereColumn('retribution_proposals.spatial_planning_id', 'spatial_plannings.id');
})
->delete();
$this->info("Deleted {$deletedCount} spatial plannings without retribution proposals.");
$this->info("Kept {$withProposals} spatial plannings that have retribution proposals.");
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
$this->error('Failed to safe truncate: ' . $e->getMessage());
return 1;
}
} else {
$this->info('Operation cancelled.');
return 0;
}
}
$this->info("Starting import from: {$filePath}");
$this->info("Full path: {$fullPath}");
try {
DB::beginTransaction();
$data = Excel::toArray([], $fullPath);
if (empty($data) || empty($data[0])) {
$this->error('No data found in the file.');
return 1;
}
$rows = $data[0]; // Get first sheet
$headers = array_shift($rows); // Remove header row
$this->info("Found " . count($rows) . " data rows to import.");
$this->info("Headers: " . implode(', ', $headers));
$progressBar = $this->output->createProgressBar(count($rows));
$progressBar->start();
$imported = 0;
$skipped = 0;
foreach ($rows as $index => $row) {
try {
// Skip empty rows
if (empty(array_filter($row))) {
$skipped++;
$progressBar->advance();
continue;
}
// Map CSV columns to model attributes
$spatialData = [
'name' => $this->cleanString($row[1] ?? ''), // pemohon
'location' => $this->cleanString($row[2] ?? ''), // alamat
'activities' => $this->cleanString($row[3] ?? ''), // activities
'land_area' => $this->cleanNumber($row[4] ?? 0), // luas_lahan
'site_bcr' => $this->cleanNumber($row[5] ?? 0), // bcr_kawasan
'area' => $this->cleanNumber($row[6] ?? 0), // area
'no_tapak' => $this->cleanString($row[7] ?? ''), // no_tapak
'no_skkl' => $this->cleanString($row[8] ?? ''), // no_skkl
'no_ukl' => $this->cleanString($row[9] ?? ''), // no_ukl
'building_function' => $this->cleanString($row[10] ?? ''), // fungsi_bangunan
'sub_building_function' => $this->cleanString($row[11] ?? ''), // sub_fungsi_bangunan
'number_of_floors' => $this->cleanNumber($row[12] ?? 1), // jumlah_lantai
'number' => $this->cleanString($row[0] ?? ''), // no
'date' => now(), // Set current date
'kbli' => null, // Not in CSV, set as null
];
// Validate required fields
if (empty($spatialData['name']) && empty($spatialData['activities'])) {
$skipped++;
$progressBar->advance();
continue;
}
SpatialPlanning::create($spatialData);
$imported++;
} catch (Exception $e) {
$this->newLine();
$this->error("Error importing row " . ($index + 2) . ": " . $e->getMessage());
$skipped++;
}
$progressBar->advance();
}
$progressBar->finish();
$this->newLine(2);
DB::commit();
$this->info("Import completed successfully!");
$this->info("Imported: {$imported} records");
$this->info("Skipped: {$skipped} records");
return 0;
} catch (Exception $e) {
DB::rollBack();
$this->error("Import failed: " . $e->getMessage());
return 1;
}
}
/**
* Clean string data
*/
private function cleanString($value)
{
if (is_null($value)) return null;
return trim(str_replace(["\n", "\r", "\t"], ' ', $value));
}
/**
* Clean numeric data
*/
private function cleanNumber($value)
{
if (is_null($value) || $value === '') return 0;
// Remove non-numeric characters except decimal point
$cleaned = preg_replace('/[^0-9.]/', '', $value);
return is_numeric($cleaned) ? (float) $cleaned : 0;
}
/**
* List available template files
*/
private function listAvailableFiles()
{
$templatesPath = storage_path('app/public/templates');
if (is_dir($templatesPath)) {
$this->info("Files in storage/app/public/templates:");
$extensions = ['csv', 'xlsx', 'xls'];
foreach ($extensions as $ext) {
$files = glob($templatesPath . '/*.' . $ext);
foreach ($files as $file) {
$this->line(' - ' . basename($file));
}
}
}
$publicTemplatesPath = public_path('templates');
if (is_dir($publicTemplatesPath)) {
$this->info("Files in public/templates:");
$extensions = ['csv', 'xlsx', 'xls'];
foreach ($extensions as $ext) {
$files = glob($publicTemplatesPath . '/*.' . $ext);
foreach ($files as $file) {
$this->line(' - ' . basename($file));
}
}
}
}
}

View File

@@ -42,54 +42,4 @@ class ScrapingData extends Command
$this->info("Scraping job dispatched successfully"); $this->info("Scraping job dispatched successfully");
} }
/**
* Execute the console command.
*/
// public function handle()
// {
// try {
// // Create a record with "processing" status
// $import_datasource = ImportDatasource::create([
// 'message' => 'Initiating scraping...',
// 'response_body' => null,
// 'status' => 'processing',
// 'start_time' => now()
// ]);
// // Run the service
// $service_google_sheet = new ServiceGoogleSheet();
// $service_google_sheet->run_service();
// // Run the ServicePbgTask with injected Guzzle Client
// $this->service_pbg_task->run_service();
// // run the service pbg task assignments
// $this->service_tab_pbg_task->run_service();
// // Update the record status to "success" after completion
// $import_datasource->update([
// 'status' => 'success',
// 'message' => 'Scraping completed successfully.',
// 'finish_time' => now()
// ]);
// } catch (\Exception $e) {
// // Log the error for debugging
// Log::error('Scraping failed: ' . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
// // Handle errors by updating the status to "failed"
// if (isset($import_datasource)) {
// $import_datasource->update([
// 'status' => 'failed',
// 'response_body' => 'Error: ' . $e->getMessage(),
// 'finish_time' => now()
// ]);
// }
// }
// }
} }

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use App\Services\ServiceGoogleSheet;
use Illuminate\Console\Command;
class ScrapingLeaderData extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:scraping-leader-data';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Scraping leader data from google spreadsheet and save to database';
/**
* Execute the console command.
*/
public function handle()
{
$service_google_sheet = app(ServiceGoogleSheet::class);
$service_google_sheet->sync_leader_data();
$this->info('Leader data synced successfully');
}
}

View File

@@ -0,0 +1,265 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\RetributionCalculatorService;
use App\Models\BuildingType;
class TestRetributionCalculation extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'retribution:test
{--area= : Luas bangunan dalam m2}
{--floor= : Jumlah lantai (1-6)}
{--type= : ID atau kode building type}
{--all : Test semua building types}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Test perhitungan retribusi PBG dengan input luas bangunan dan tinggi lantai';
protected $calculatorService;
public function __construct(RetributionCalculatorService $calculatorService)
{
parent::__construct();
$this->calculatorService = $calculatorService;
}
/**
* Execute the console command.
*/
public function handle()
{
$this->info('🏢 SISTEM TEST PERHITUNGAN RETRIBUSI PBG');
$this->info('=' . str_repeat('=', 50));
// Test all building types if --all flag is used
if ($this->option('all')) {
return $this->testAllBuildingTypes();
}
// Get input parameters
$area = $this->getArea();
$floor = $this->getFloor();
$buildingTypeId = $this->getBuildingType();
if (!$area || !$floor || !$buildingTypeId) {
$this->error('❌ Parameter tidak lengkap!');
return 1;
}
// Perform calculation
$this->performCalculation($buildingTypeId, $floor, $area);
return 0;
}
protected function getArea()
{
$area = $this->option('area');
if (!$area) {
$area = $this->ask('📐 Masukkan luas bangunan (m²)');
}
if (!is_numeric($area) || $area <= 0) {
$this->error('❌ Luas bangunan harus berupa angka positif!');
return null;
}
return (float) $area;
}
protected function getFloor()
{
$floor = $this->option('floor');
if (!$floor) {
$floor = $this->ask('🏗️ Masukkan jumlah lantai (1-6)');
}
if (!is_numeric($floor) || $floor < 1 || $floor > 6) {
$this->error('❌ Jumlah lantai harus antara 1-6!');
return null;
}
return (int) $floor;
}
protected function getBuildingType()
{
$type = $this->option('type');
if (!$type) {
$this->showBuildingTypes();
$type = $this->ask('🏢 Masukkan ID atau kode building type');
}
// Try to find by ID first, then by code
$buildingType = null;
if (is_numeric($type)) {
$buildingType = BuildingType::find($type);
} else {
$buildingType = BuildingType::where('code', strtoupper($type))->first();
}
if (!$buildingType) {
$this->error('❌ Building type tidak ditemukan!');
return null;
}
return $buildingType->id;
}
protected function showBuildingTypes()
{
$this->info('📋 DAFTAR BUILDING TYPES:');
$this->line('');
$buildingTypes = BuildingType::with('indices')
->whereHas('indices') // Only types that have indices
->get();
$headers = ['ID', 'Kode', 'Nama', 'Coefficient', 'Free'];
$rows = [];
foreach ($buildingTypes as $type) {
$rows[] = [
$type->id,
$type->code,
$type->name,
$type->indices ? number_format($type->indices->coefficient, 4) : 'N/A',
$type->is_free ? '✅' : '❌'
];
}
$this->table($headers, $rows);
$this->line('');
}
protected function performCalculation($buildingTypeId, $floor, $area)
{
try {
// Round area to 2 decimal places to match database storage format
$roundedArea = round($area, 2);
$result = $this->calculatorService->calculate($buildingTypeId, $floor, $roundedArea, false);
$this->displayResults($result, $roundedArea, $floor);
} catch (\Exception $e) {
$this->error('❌ Error: ' . $e->getMessage());
return 1;
}
}
protected function displayResults($result, $area, $floor)
{
$this->info('');
$this->info('📊 HASIL PERHITUNGAN RETRIBUSI');
$this->info('=' . str_repeat('=', 40));
// Building info
$this->line('🏢 <fg=cyan>Building Type:</> ' . $result['building_type']['name']);
$this->line('📐 <fg=cyan>Luas Bangunan:</> ' . number_format($area, 0) . ' m²');
$this->line('🏗️ <fg=cyan>Jumlah Lantai:</> ' . $floor);
if (isset($result['building_type']['is_free']) && $result['building_type']['is_free']) {
$this->line('');
$this->info('🎉 GRATIS - Building type ini tidak dikenakan retribusi');
$this->line('💰 <fg=green>Total Retribusi: Rp 0</fg=green>');
return;
}
$this->line('');
// Parameters
$this->info('📋 PARAMETER PERHITUNGAN:');
$indices = $result['indices'];
$this->line('• Coefficient: ' . number_format($indices['coefficient'], 4));
$this->line('• IP Permanent: ' . number_format($indices['ip_permanent'], 4));
$this->line('• IP Complexity: ' . number_format($indices['ip_complexity'], 4));
$this->line('• Locality Index: ' . number_format($indices['locality_index'], 4));
$this->line('• Height Index: ' . number_format($result['input_parameters']['height_index'], 4));
$this->line('');
// Calculation steps
$this->info('🔢 LANGKAH PERHITUNGAN:');
$detail = $result['calculation_detail'];
$this->line('1. H5 Raw: ' . number_format($detail['h5_raw'], 6));
$this->line('2. H5 Rounded: ' . number_format($detail['h5'], 4));
$this->line('3. Main Calculation: Rp ' . number_format($detail['main'], 2));
$this->line('4. Infrastructure (50%): Rp ' . number_format($detail['infrastructure'], 2));
$this->line('');
// Final result
$this->info('💰 <fg=green>TOTAL RETRIBUSI: ' . $result['formatted_amount'] . '</fg=green>');
$this->line('📈 <fg=yellow>Per m²: Rp ' . number_format($result['total_retribution'] / $area, 2) . '</fg=yellow>');
}
protected function testAllBuildingTypes()
{
$area = round($this->option('area') ?: 100, 2);
$floor = $this->option('floor') ?: 2;
$this->info("🧪 TESTING SEMUA BUILDING TYPES");
$this->info("📐 Luas: {$area} m² | 🏗️ Lantai: {$floor}");
$this->info('=' . str_repeat('=', 60));
$buildingTypes = BuildingType::with('indices')
->whereHas('indices') // Only types that have indices
->orderBy('level')
->orderBy('name')
->get();
$headers = ['Kode', 'Nama', 'Coefficient', 'Total Retribusi', 'Per m²'];
$rows = [];
foreach ($buildingTypes as $type) {
try {
$result = $this->calculatorService->calculate($type->id, $floor, $area, false);
if ($type->is_free) {
$rows[] = [
$type->code,
$type->name,
'FREE',
'Rp 0',
'Rp 0'
];
} else {
$rows[] = [
$type->code,
$type->name,
number_format($result['indices']['coefficient'], 4),
'Rp ' . number_format($result['total_retribution'], 0),
'Rp ' . number_format($result['total_retribution'] / $area, 0)
];
}
} catch (\Exception $e) {
$rows[] = [
$type->code,
$type->name,
'ERROR',
$e->getMessage(),
'-'
];
}
}
$this->table($headers, $rows);
return 0;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Enums;
enum PbgTaskFilterData : string
{
case non_business = 'non-business';
case business = 'business';
case verified = 'verified';
case non_verified = 'non-verified';
case all = 'all';
public static function getAllOptions() : array {
return [
self::all->value => 'Potensi Berkas',
self::business->value => 'Usaha',
self::non_business->value => 'Bukan Usaha',
self::verified->value => 'Terverifikasi',
self::non_verified->value => 'Belum Terverifikasi',
];
}
}

View File

@@ -25,12 +25,13 @@ class BigDataResumeController extends Controller
$filterDate = $request->get("filterByDate"); $filterDate = $request->get("filterByDate");
if (!$filterDate || $filterDate === "latest") { if (!$filterDate || $filterDate === "latest") {
$big_data_resume = BigdataResume::where('year', now()->year)->latest()->first(); $big_data_resume = BigdataResume::where('year', 'leader')->latest()->first();
if (!$big_data_resume) { if (!$big_data_resume) {
return $this->response_empty_resume(); return $this->response_empty_resume();
} }
} else { } else {
$big_data_resume = BigdataResume::whereDate('created_at', $filterDate) $big_data_resume = BigdataResume::where('year', 'leader')
->whereDate('created_at', $filterDate)
->orderBy('id', 'desc') ->orderBy('id', 'desc')
->first(); ->first();

View File

@@ -0,0 +1,91 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\BigdataResume;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class GrowthReportAPIController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
// Get current date
$today = Carbon::today();
// Define default range: 1 month back from today
$defaultStart = $today->copy()->subMonth();
$defaultEnd = $today;
// Use request values if provided, else use defaults
// $startDate = $request->input('start_date', $defaultStart->toDateString());
// $endDate = $request->input('end_date', $defaultEnd->toDateString());
// Optional year filter (used if specified)
$year = $request->input('year', now()->year);
// $query = BigdataResume::selectRaw("
// DATE(created_at) as date,
// SUM(potention_sum) as potention_sum,
// SUM(verified_sum) as verified_sum,
// SUM(non_verified_sum) as non_verified_sum
// ")
// ->whereBetween('created_at', [$startDate, $endDate]);
$query = BigdataResume::selectRaw("
DATE(created_at) as date,
SUM(potention_sum) as potention_sum,
SUM(verified_sum) as verified_sum,
SUM(non_verified_sum) as non_verified_sum
");
$query->whereNotNull('year')
->where('year', '!=', 'all');
$data = $query->groupBy(DB::raw('DATE(created_at)'))
->orderBy(DB::raw('DATE(created_at)'))
->get()
->map(function ($item) {
$item->date = Carbon::parse($item->date)->format('d M Y');
return $item;
});
return response()->json($data);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(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)
{
//
}
}

View File

@@ -17,12 +17,16 @@ class LackOfPotentialController extends Controller
$total_reklame = Advertisement::count(); $total_reklame = Advertisement::count();
$total_pdam = Customer::count(); $total_pdam = Customer::count();
$total_tata_ruang = SpatialPlanning::count(); $total_tata_ruang = SpatialPlanning::count();
$total_tata_ruang_usaha = SpatialPlanning::where('building_function','like', '%usaha%')->count();
$total_tata_ruang_non_usaha = SpatialPlanning::where('building_function','like', '%hunian%')->count();
$data_report_tourism = TourismBasedKBLI::all(); $data_report_tourism = TourismBasedKBLI::all();
return response()->json([ return response()->json([
'total_reklame' => $total_reklame, 'total_reklame' => $total_reklame,
'total_pdam' => $total_pdam, 'total_pdam' => $total_pdam,
'total_tata_ruang' => $total_tata_ruang, 'total_tata_ruang' => $total_tata_ruang,
'total_tata_ruang_usaha' => $total_tata_ruang_usaha,
'total_tata_ruang_non_usaha' => $total_tata_ruang_non_usaha,
'data_report' => $data_report_tourism, 'data_report' => $data_report_tourism,
], 200); ], 200);
}catch(\Exception $e){ }catch(\Exception $e){

View File

@@ -8,7 +8,7 @@ use App\Http\Resources\RequestAssignmentResouce;
use App\Models\PbgTask; use App\Models\PbgTask;
use App\Models\PbgTaskGoogleSheet; use App\Models\PbgTaskGoogleSheet;
use Barryvdh\DomPDF\Facade\Pdf; use Barryvdh\DomPDF\Facade\Pdf;
use DB; use Illuminate\Support\Facades\DB;
use Exception; use Exception;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Log; use Log;
@@ -21,15 +21,48 @@ class RequestAssignmentController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$query = PbgTask::with(['attachments' => function ($q) { $query = PbgTask::with([
$q->whereIn('pbg_type', ['berita_acara', 'bukti_bayar']); 'attachments' => function ($q) {
}])->orderBy('id', 'desc'); $q->whereIn('pbg_type', ['berita_acara', 'bukti_bayar']);
},
'googleSheet'
])->orderBy('id', 'desc');
if ($request->has('filter') && !empty($request->get('filter'))) {
$filter = strtolower($request->get('filter'));
switch ($filter) {
case 'non-business':
$query->whereRaw("LOWER(function_type) != ?", ['sebagai tempat usaha'])->orWhereNull('function_type');
break;
case 'business':
$query->whereRaw("LOWER(function_type) = ?", ['sebagai tempat usaha']);
break;
case 'verified':
$query->whereHas('googleSheet', function ($q) {
$q->whereRaw("LOWER(status_verifikasi) = ?", ['selesai verifikasi']);
});
break;
case 'non-verified':
$query->where(function ($q) {
$q->whereDoesntHave('googleSheet')
->orWhereHas('googleSheet', function ($q2) {
$q2->whereRaw("LOWER(status_verifikasi) != ?", ['selesai verifikasi'])->orWhereNull('status_verifikasi');
});
});
break;
}
}
if ($request->has('search') && !empty($request->get("search"))) { if ($request->has('search') && !empty($request->get("search"))) {
$query->where(function ($q) use ($request) { $search = $request->get('search');
$q->where('name', 'LIKE', '%' . $request->get('search') . '%') $query->where(function ($q) use ($search) {
->orWhere('registration_number', 'LIKE', '%' . $request->get('search') . '%') $q->where('name', 'LIKE', "%$search%")
->orWhere('document_number', 'LIKE', '%' . $request->get('search') . '%'); ->orWhere('registration_number', 'LIKE', "%$search%")
->orWhere('document_number', 'LIKE', "%$search%");
}); });
} }

View File

@@ -36,13 +36,68 @@ class AuthenticatedSessionController extends Controller
// Ambil user yang sedang login // Ambil user yang sedang login
$user = Auth::user(); $user = Auth::user();
// Buat token untuk API // Hapus token lama jika ada
$token = $user->createToken(env('APP_KEY'))->plainTextToken; $user->tokens()->delete();
// Buat token untuk API dengan scope dan expiration
$tokenName = config('app.name', 'Laravel') . '-' . $user->id . '-' . time();
// Token dengan scope (opsional)
$token = $user->createToken($tokenName, ['*'], now()->addDays(30))->plainTextToken;
// Simpan token di session untuk digunakan di frontend
session(['api_token' => $token]); session(['api_token' => $token]);
return redirect()->intended(RouteServiceProvider::HOME); return redirect()->intended(RouteServiceProvider::HOME);
} }
/**
* Generate API token for authenticated user
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function generateApiToken(Request $request)
{
$user = Auth::user();
if (!$user) {
return response()->json(['error' => 'Unauthorized'], 401);
}
// Delete existing tokens
$user->tokens()->delete();
// Generate new token
$tokenName = config('app.name', 'Laravel') . '-' . $user->id . '-' . time();
$token = $user->createToken($tokenName, ['*'], now()->addDays(30))->plainTextToken;
return response()->json([
'token' => $token,
'token_type' => 'Bearer',
'expires_in' => 30 * 24 * 60 * 60, // 30 days in seconds
]);
}
/**
* Revoke API token for authenticated user
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function revokeApiToken(Request $request)
{
$user = Auth::user();
if (!$user) {
return response()->json(['error' => 'Unauthorized'], 401);
}
$user->tokens()->delete();
return response()->json(['message' => 'All tokens revoked successfully']);
}
/** /**
* Destroy an authenticated session. * Destroy an authenticated session.
* *

View File

View File

View File

0
app/Http/Controllers/Auth/NewPasswordController.php Executable file → Normal file
View File

View File

0
app/Http/Controllers/Auth/RegisteredUserController.php Executable file → Normal file
View File

0
app/Http/Controllers/Auth/VerifyEmailController.php Executable file → Normal file
View File

0
app/Http/Controllers/Controller.php Executable file → Normal file
View File

View File

@@ -21,4 +21,13 @@ class BigDataController extends Controller
{ {
return view('dashboards.pbg'); return view('dashboards.pbg');
} }
public function leader()
{
$latest_import_datasource = ImportDatasource::latest()->first();
$latest_created = $latest_import_datasource ?
$latest_import_datasource->created_at->format("j F Y H:i:s") : null;
$menus = Menu::all();
return view('dashboards.leader', compact('latest_created', 'menus'));
}
} }

View File

@@ -3,12 +3,14 @@
namespace App\Http\Controllers\Dashboards; namespace App\Http\Controllers\Dashboards;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Menu;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class PotentialsController extends Controller class PotentialsController extends Controller
{ {
public function inside_system(){ public function inside_system(){
return view('dashboards.potentials.inside_system'); $menus = Menu::all();
return view('dashboards.potentials.inside_system', compact('menus'));
} }
public function outside_system(){ public function outside_system(){
return view('dashboards.potentials.outside_system'); return view('dashboards.potentials.outside_system');

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Http\Controllers\Report;
use App\Http\Controllers\Controller;
use App\Models\Menu;
use Illuminate\Http\Request;
class GrowthReportsController extends Controller
{
public function index(){
return view('report.growth-report.index');
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers\RequestAssignment; namespace App\Http\Controllers\RequestAssignment;
use App\Enums\PbgTaskApplicationTypes; use App\Enums\PbgTaskApplicationTypes;
use App\Enums\PbgTaskFilterData;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\PbgTask; use App\Models\PbgTask;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -17,27 +18,21 @@ class PbgTaskController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$menuId = $request->query('menu_id'); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
$user = Auth::user(); $filter = $request->query('filter');
$userId = $user->id;
// Ambil role_id yang dimiliki user $permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$roleIds = DB::table('user_role') $creator = $permissions['allow_create'] ?? 0;
->where('user_id', $userId) $updater = $permissions['allow_update'] ?? 0;
->pluck('role_id'); $destroyer = $permissions['allow_destroy'] ?? 0;
// Ambil data akses berdasarkan role_id dan menu_id return view('pbg_task.index', [
$roleAccess = DB::table('role_menu') 'creator' => $creator,
->whereIn('role_id', $roleIds) 'updater' => $updater,
->where('menu_id', $menuId) 'destroyer' => $destroyer,
->first(); 'filter' => $filter,
'filterOptions' => PbgTaskFilterData::getAllOptions(),
// Pastikan roleAccess tidak null sebelum mengakses properti ]);
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('pbg_task.index', compact('creator', 'updater', 'destroyer'));
} }
/** /**

0
app/Http/Controllers/RoutingController.php Executable file → Normal file
View File

0
app/Http/Requests/Auth/LoginRequest.php Executable file → Normal file
View File

View File

@@ -19,8 +19,8 @@ class DataSettingResource extends JsonResource
'key' => $this->key, 'key' => $this->key,
'value' => $this->value, 'value' => $this->value,
'type' => $this->type, 'type' => $this->type,
'created_at' => $this->created_at->toDateTimeString(), 'created_at' => $this->created_at ? $this->created_at->toDateTimeString() : null,
'updated_at' => $this->updated_at->toDateTimeString(), 'updated_at' => $this->updated_at ? $this->updated_at->toDateTimeString() : null,
]; ];
} }
} }

View File

@@ -60,10 +60,10 @@ class ScrapingDataJob implements ShouldQueue
throw $e; throw $e;
} }
$data_setting_result = $service_google_sheet->get_big_resume_data(); // $data_setting_result = $service_google_sheet->get_big_resume_data();
BigdataResume::generateResumeData($import_datasource->id, "all", $data_setting_result); // BigdataResume::generateResumeData($import_datasource->id, "all", $data_setting_result);
BigdataResume::generateResumeData($import_datasource->id, now()->year, $data_setting_result); // BigdataResume::generateResumeData($import_datasource->id, now()->year, $data_setting_result);
// Update status to success // Update status to success
$import_datasource->update([ $import_datasource->update([
@@ -79,7 +79,7 @@ class ScrapingDataJob implements ShouldQueue
if (isset($import_datasource)) { if (isset($import_datasource)) {
$import_datasource->update([ $import_datasource->update([
'status' => 'failed', 'status' => 'failed',
'response_body' => 'Error: ' . $e->getMessage(), 'response_body' => 'Terjadi kesalahan, Syncronize tidak selesai',
'finish_time' => now(), 'finish_time' => now(),
'failed_uuid' => $failed_uuid, 'failed_uuid' => $failed_uuid,
]); ]);

131
app/Models/BuildingType.php Normal file
View 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'])
];
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class CalculableRetribution extends Model
{
protected $fillable = [
'retribution_calculation_id',
'calculable_id',
'calculable_type',
'is_active',
'assigned_at',
'notes',
];
protected $casts = [
'is_active' => 'boolean',
'assigned_at' => 'timestamp',
];
/**
* Get the owning calculable model (polymorphic)
*/
public function calculable(): MorphTo
{
return $this->morphTo();
}
/**
* Get the retribution calculation
*/
public function retributionCalculation(): BelongsTo
{
return $this->belongsTo(RetributionCalculation::class);
}
/**
* Scope: Only active assignments
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
/**
* Scope: Only inactive assignments
*/
public function scopeInactive($query)
{
return $query->where('is_active', false);
}
/**
* Scope: For specific calculable type
*/
public function scopeForType($query, string $type)
{
return $query->where('calculable_type', $type);
}
}

View 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();
}
}

View File

@@ -38,6 +38,10 @@ class PbgTask extends Model
return $this->hasOne(PbgTaskIndexIntegrations::class, 'pbg_task_uid', 'uuid'); return $this->hasOne(PbgTaskIndexIntegrations::class, 'pbg_task_uid', 'uuid');
} }
public function pbg_task_detail(){
return $this->hasOne(PbgTaskDetail::class, 'pbg_task_uid', 'uuid');
}
public function googleSheet(){ public function googleSheet(){
return $this->hasOne(PbgTaskGoogleSheet::class, 'no_registrasi', 'registration_number'); return $this->hasOne(PbgTaskGoogleSheet::class, 'no_registrasi', 'registration_number');
} }

View File

@@ -0,0 +1,252 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Carbon\Carbon;
class PbgTaskDetail extends Model
{
protected $table = 'pbg_task_details';
protected $fillable = [
'pbg_task_uid',
'uid',
'nik',
'type_card',
'ownership',
'owner_name',
'ward_id',
'ward_name',
'district_id',
'district_name',
'regency_id',
'regency_name',
'province_id',
'province_name',
'address',
'owner_email',
'owner_phone',
'user',
'name',
'email',
'phone',
'user_nik',
'user_province_id',
'user_province_name',
'user_regency_id',
'user_regency_name',
'user_district_id',
'user_district_name',
'user_address',
'status',
'status_name',
'slf_status',
'slf_status_name',
'sppst_status',
'sppst_file',
'sppst_status_name',
'file_pbg',
'file_pbg_date',
'due_date',
'start_date',
'document_number',
'registration_number',
'function_type',
'application_type',
'application_type_name',
'consultation_type',
'condition',
'prototype',
'permanency',
'building_type',
'building_type_name',
'building_purpose',
'building_use',
'occupancy',
'name_building',
'total_area',
'area',
'area_type',
'height',
'floor',
'floor_area',
'basement',
'basement_height',
'basement_area',
'unit',
'prev_retribution',
'prev_pbg',
'prev_total_area',
'koefisien_dasar_bangunan',
'koefisien_lantai_bangunan',
'koefisien_lantai_hijau',
'koefisien_tapak_basement',
'ketinggian_bangunan',
'jalan_arteri',
'jalan_kolektor',
'jalan_bangunan',
'gsb',
'kkr_number',
'unit_data',
'is_mbr',
'code',
'building_ward_id',
'building_ward_name',
'building_district_id',
'building_district_name',
'building_regency_id',
'building_regency_name',
'building_province_id',
'building_province_name',
'building_address',
'latitude',
'longitude',
'building_photo',
'pbg_parent',
'api_created_at',
];
protected $casts = [
'unit_data' => 'array',
'is_mbr' => 'boolean',
'total_area' => 'decimal:2',
'area' => 'decimal:2',
'height' => 'decimal:2',
'floor_area' => 'decimal:2',
'basement_height' => 'decimal:2',
'basement_area' => 'decimal:2',
'prev_retribution' => 'decimal:2',
'prev_total_area' => 'decimal:2',
'koefisien_dasar_bangunan' => 'decimal:4',
'koefisien_lantai_bangunan' => 'decimal:4',
'koefisien_lantai_hijau' => 'decimal:4',
'koefisien_tapak_basement' => 'decimal:4',
'ketinggian_bangunan' => 'decimal:2',
'gsb' => 'decimal:2',
'latitude' => 'decimal:8',
'longitude' => 'decimal:8',
'file_pbg_date' => 'date',
'due_date' => 'date',
'start_date' => 'date',
'api_created_at' => 'datetime',
];
/**
* Get the PBG task that owns this detail
*/
public function pbgTask(): BelongsTo
{
return $this->belongsTo(PbgTask::class, 'pbg_task_uid', 'uuid');
}
/**
* Create or update PbgTaskDetail from API response
*/
public static function createFromApiResponse(array $data, string $pbgTaskUuid): self
{
$detailData = [
'pbg_task_uid' => $pbgTaskUuid,
'uid' => $data['uid'] ?? null,
'nik' => $data['nik'] ?? null,
'type_card' => $data['type_card'] ?? null,
'ownership' => $data['ownership'] ?? null,
'owner_name' => $data['owner_name'] ?? null,
'ward_id' => $data['ward_id'] ?? null,
'ward_name' => $data['ward_name'] ?? null,
'district_id' => $data['district_id'] ?? null,
'district_name' => $data['district_name'] ?? null,
'regency_id' => $data['regency_id'] ?? null,
'regency_name' => $data['regency_name'] ?? null,
'province_id' => $data['province_id'] ?? null,
'province_name' => $data['province_name'] ?? null,
'address' => $data['address'] ?? null,
'owner_email' => $data['owner_email'] ?? null,
'owner_phone' => $data['owner_phone'] ?? null,
'user' => $data['user'] ?? null,
'name' => $data['name'] ?? null,
'email' => $data['email'] ?? null,
'phone' => $data['phone'] ?? null,
'user_nik' => $data['user_nik'] ?? null,
'user_province_id' => $data['user_province_id'] ?? null,
'user_province_name' => $data['user_province_name'] ?? null,
'user_regency_id' => $data['user_regency_id'] ?? null,
'user_regency_name' => $data['user_regency_name'] ?? null,
'user_district_id' => $data['user_district_id'] ?? null,
'user_district_name' => $data['user_district_name'] ?? null,
'user_address' => $data['user_address'] ?? null,
'status' => $data['status'] ?? null,
'status_name' => $data['status_name'] ?? null,
'slf_status' => $data['slf_status'] ?? null,
'slf_status_name' => $data['slf_status_name'] ?? null,
'sppst_status' => $data['sppst_status'] ?? null,
'sppst_file' => $data['sppst_file'] ?? null,
'sppst_status_name' => $data['sppst_status_name'] ?? null,
'file_pbg' => $data['file_pbg'] ?? null,
'file_pbg_date' => isset($data['file_pbg_date']) ? Carbon::parse($data['file_pbg_date'])->format('Y-m-d') : null,
'due_date' => isset($data['due_date']) ? Carbon::parse($data['due_date'])->format('Y-m-d') : null,
'start_date' => isset($data['start_date']) ? Carbon::parse($data['start_date'])->format('Y-m-d') : null,
'document_number' => $data['document_number'] ?? null,
'registration_number' => $data['registration_number'] ?? null,
'function_type' => $data['function_type'] ?? null,
'application_type' => $data['application_type'] ?? null,
'application_type_name' => $data['application_type_name'] ?? null,
'consultation_type' => $data['consultation_type'] ?? null,
'condition' => $data['condition'] ?? null,
'prototype' => $data['prototype'] ?? null,
'permanency' => $data['permanency'] ?? null,
'building_type' => $data['building_type'] ?? null,
'building_type_name' => $data['building_type_name'] ?? null,
'building_purpose' => $data['building_purpose'] ?? null,
'building_use' => $data['building_use'] ?? null,
'occupancy' => $data['occupancy'] ?? null,
'name_building' => $data['name_building'] ?? null,
'total_area' => $data['total_area'] ?? null,
'area' => $data['area'] ?? null,
'area_type' => $data['area_type'] ?? null,
'height' => $data['height'] ?? null,
'floor' => $data['floor'] ?? null,
'floor_area' => $data['floor_area'] ?? null,
'basement' => $data['basement'] ?? null,
'basement_height' => $data['basement_height'] ?? null,
'basement_area' => $data['basement_area'] ?? null,
'unit' => $data['unit'] ?? null,
'prev_retribution' => $data['prev_retribution'] ?? null,
'prev_pbg' => $data['prev_pbg'] ?? null,
'prev_total_area' => $data['prev_total_area'] ?? null,
'koefisien_dasar_bangunan' => $data['koefisien_dasar_bangunan'] ?? null,
'koefisien_lantai_bangunan' => $data['koefisien_lantai_bangunan'] ?? null,
'koefisien_lantai_hijau' => $data['koefisien_lantai_hijau'] ?? null,
'koefisien_tapak_basement' => $data['koefisien_tapak_basement'] ?? null,
'ketinggian_bangunan' => $data['ketinggian_bangunan'] ?? null,
'jalan_arteri' => $data['jalan_arteri'] ?? null,
'jalan_kolektor' => $data['jalan_kolektor'] ?? null,
'jalan_bangunan' => $data['jalan_bangunan'] ?? null,
'gsb' => $data['gsb'] ?? null,
'kkr_number' => $data['kkr_number'] ?? null,
'unit_data' => $data['unit_data'] ?? null,
'is_mbr' => $data['is_mbr'] ?? false,
'code' => $data['code'] ?? null,
'building_ward_id' => $data['building_ward_id'] ?? null,
'building_ward_name' => $data['building_ward_name'] ?? null,
'building_district_id' => $data['building_district_id'] ?? null,
'building_district_name' => $data['building_district_name'] ?? null,
'building_regency_id' => $data['building_regency_id'] ?? null,
'building_regency_name' => $data['building_regency_name'] ?? null,
'building_province_id' => $data['building_province_id'] ?? null,
'building_province_name' => $data['building_province_name'] ?? null,
'building_address' => $data['building_address'] ?? null,
'latitude' => $data['latitude'] ?? null,
'longitude' => $data['longitude'] ?? null,
'building_photo' => $data['building_photo'] ?? null,
'pbg_parent' => $data['pbg_parent'] ?? null,
'api_created_at' => isset($data['created_at']) ? Carbon::parse($data['created_at'])->format('Y-m-d H:i:s') : null,
];
return static::updateOrCreate(
['uid' => $data['uid']],
$detailData
);
}
}

View File

@@ -0,0 +1,139 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
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 = [
'building_area' => 'decimal:2',
'retribution_amount' => 'decimal:2',
'calculation_detail' => 'array',
'calculated_at' => 'timestamp',
'floor_number' => 'integer',
];
/**
* Get the building type
*/
public function buildingType(): BelongsTo
{
return $this->belongsTo(BuildingType::class);
}
/**
* Get all calculable assignments
*/
public function calculableRetributions(): HasMany
{
return $this->hasMany(CalculableRetribution::class);
}
/**
* Get active assignments only
*/
public function activeAssignments(): HasMany
{
return $this->hasMany(CalculableRetribution::class)->where('is_active', true);
}
/**
* Generate unique calculation ID
*/
public static function generateCalculationId(): string
{
return 'CALC-' . date('Ymd') . '-' . str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT);
}
/**
* Boot method to auto-generate calculation_id
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
if (empty($model->calculation_id)) {
$model->calculation_id = self::generateCalculationId();
}
if (empty($model->calculated_at)) {
$model->calculated_at = now();
}
});
}
/**
* Check if calculation is being used
*/
public function isInUse(): bool
{
return $this->activeAssignments()->exists();
}
/**
* Get calculation summary
*/
public function getSummary(): array
{
return [
'calculation_id' => $this->calculation_id,
'building_type' => $this->buildingType->name ?? 'Unknown',
'floor_number' => $this->floor_number,
'building_area' => $this->building_area,
'retribution_amount' => $this->retribution_amount,
'calculated_at' => $this->calculated_at->format('Y-m-d H:i:s'),
'in_use' => $this->isInUse(),
];
}
/**
* 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 ?? [];
}
}

View 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]
);
}
}

View 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
];
}
}

View File

@@ -2,8 +2,10 @@
namespace App\Models; namespace App\Models;
use App\Traits\HasRetributionCalculation;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/** /**
* Class SpatialPlanning * Class SpatialPlanning
* *
@@ -23,6 +25,7 @@ use Illuminate\Database\Eloquent\Model;
*/ */
class SpatialPlanning extends Model class SpatialPlanning extends Model
{ {
use HasRetributionCalculation;
protected $perPage = 20; protected $perPage = 20;
@@ -31,7 +34,35 @@ class SpatialPlanning extends Model
* *
* @var array<int, string> * @var array<int, string>
*/ */
protected $fillable = ['name', 'kbli', 'activities', 'area', 'location', 'number', 'date']; protected $fillable = ['name', 'kbli', 'activities', 'area', 'location', 'number', 'date', 'no_tapak', 'no_skkl', 'no_ukl', 'building_function', 'sub_building_function', 'number_of_floors', 'land_area', 'site_bcr'];
protected $casts = [
'area' => 'decimal:6',
'land_area' => 'decimal:6',
'site_bcr' => 'decimal:6',
'number_of_floors' => 'integer',
'date' => 'date'
];
/**
* Get building function text for detection
*/
public function getBuildingFunctionText(): string
{
return $this->building_function ?? $this->activities ?? '';
}
/**
* Get area for calculation (prioritize area, fallback to land_area)
*/
public function getCalculationArea(): float
{
return (float) ($this->area ?? $this->land_area ?? 0);
}
} }

0
app/Models/User.php Executable file → Normal file
View File

0
app/Providers/AppServiceProvider.php Executable file → Normal file
View File

0
app/Providers/AuthServiceProvider.php Executable file → Normal file
View File

0
app/Providers/BroadcastServiceProvider.php Executable file → Normal file
View File

0
app/Providers/EventServiceProvider.php Executable file → Normal file
View File

0
app/Providers/RouteServiceProvider.php Executable file → Normal file
View File

View File

@@ -0,0 +1,254 @@
<?php
namespace App\Services;
use App\Models\BuildingType;
use App\Models\HeightIndex;
use App\Models\RetributionConfig;
use App\Models\RetributionCalculation;
class RetributionCalculatorService
{
/**
* Calculate retribution for given parameters
*/
public function calculate(
int $buildingTypeId,
int $floorNumber,
float $buildingArea,
bool $saveResult = true,
bool $excelCompatibleMode = false
): array {
// Get building type with indices
$buildingType = BuildingType::with('indices')->findOrFail($buildingTypeId);
// Check if building type is free
if ($buildingType->isFree()) {
return $this->createFreeResult($buildingType, $floorNumber, $buildingArea, $saveResult);
}
// Get height index
$heightIndex = HeightIndex::getHeightIndexByFloor($floorNumber);
// Get configuration values
$baseValue = RetributionConfig::getValue('BASE_VALUE', 70350);
$infrastructureMultiplier = RetributionConfig::getValue('INFRASTRUCTURE_MULTIPLIER', 0.5);
$heightMultiplier = RetributionConfig::getValue('HEIGHT_MULTIPLIER', 0.5);
// Get indices
$indices = $buildingType->indices;
if (!$indices) {
throw new \Exception("Indices not found for building type: {$buildingType->name}");
}
// Calculate using Excel formula
$result = $this->executeCalculation(
$buildingType,
$indices,
$heightIndex,
$baseValue,
$infrastructureMultiplier,
$heightMultiplier,
$floorNumber,
$buildingArea,
$excelCompatibleMode
);
// Save result if requested
if ($saveResult) {
$calculation = RetributionCalculation::createCalculation(
$buildingTypeId,
$floorNumber,
$buildingArea,
$result['total_retribution'],
$result['calculation_detail']
);
$result['calculation_id'] = $calculation->calculation_id;
}
return $result;
}
/**
* Execute the main calculation logic
*/
protected function executeCalculation(
BuildingType $buildingType,
$indices,
float $heightIndex,
float $baseValue,
float $infrastructureMultiplier,
float $heightMultiplier,
int $floorNumber,
float $buildingArea,
bool $excelCompatibleMode = false
): array {
// Step 1: Calculate H5 coefficient (Excel formula: RUNDOWN(($E5*($F5+$G5+(0.5*H$3))),4))
// H5 = coefficient * (ip_permanent + ip_complexity + (height_multiplier * height_index))
$h5Raw = $indices->coefficient * (
$indices->ip_permanent +
$indices->ip_complexity +
($heightMultiplier * $heightIndex)
);
// Apply RUNDOWN (floor to 4 decimal places)
$h5 = floor($h5Raw * 10000) / 10000;
// Step 2: Main calculation (Excel: 1*D5*(N5*base_value*H5*1))
// Main = building_area * locality_index * base_value * h5
$mainCalculation = $buildingArea * $indices->locality_index * $baseValue * $h5;
// Step 3: Infrastructure calculation (Excel: O3*(1*D5*(N5*base_value*H5*1)))
// Additional = infrastructure_multiplier * main_calculation
$infrastructureCalculation = $infrastructureMultiplier * $mainCalculation;
// Step 4: Total retribution (Main + Infrastructure)
if ($excelCompatibleMode) {
// Try to match Excel exactly - round intermediate calculations
$mainCalculation = round($mainCalculation, 0);
$infrastructureCalculation = round($infrastructureCalculation, 0);
$totalRetribution = $mainCalculation + $infrastructureCalculation;
} else {
// Apply standard rounding to match Excel results more closely
$totalRetribution = round($mainCalculation + $infrastructureCalculation, 0);
}
return [
'building_type' => [
'id' => $buildingType->id,
'code' => $buildingType->code,
'name' => $buildingType->name,
'is_free' => $buildingType->is_free
],
'input_parameters' => [
'building_area' => $buildingArea,
'floor_number' => $floorNumber,
'height_index' => $heightIndex,
'base_value' => $baseValue,
'infrastructure_multiplier' => $infrastructureMultiplier,
'height_multiplier' => $heightMultiplier
],
'indices' => [
'coefficient' => $indices->coefficient,
'ip_permanent' => $indices->ip_permanent,
'ip_complexity' => $indices->ip_complexity,
'locality_index' => $indices->locality_index,
'infrastructure_factor' => $indices->infrastructure_factor
],
'calculation_steps' => [
'h5_coefficient' => [
'formula' => 'RUNDOWN((coefficient * (ip_permanent + ip_complexity + (height_multiplier * height_index))), 4)',
'calculation' => "RUNDOWN(({$indices->coefficient} * ({$indices->ip_permanent} + {$indices->ip_complexity} + ({$heightMultiplier} * {$heightIndex}))), 4)",
'raw_result' => $h5Raw,
'result' => $h5
],
'main_calculation' => [
'formula' => 'building_area * locality_index * base_value * h5',
'calculation' => "{$buildingArea} * {$indices->locality_index} * {$baseValue} * {$h5}",
'result' => $mainCalculation
],
'infrastructure_calculation' => [
'formula' => 'infrastructure_multiplier * main_calculation',
'calculation' => "{$infrastructureMultiplier} * {$mainCalculation}",
'result' => $infrastructureCalculation
],
'total_calculation' => [
'formula' => 'main_calculation + infrastructure_calculation',
'calculation' => "{$mainCalculation} + {$infrastructureCalculation}",
'result' => $totalRetribution
]
],
'total_retribution' => $totalRetribution,
'formatted_amount' => 'Rp ' . number_format($totalRetribution, 2, ',', '.'),
'calculation_detail' => [
'h5_raw' => $h5Raw,
'h5' => $h5,
'main' => $mainCalculation,
'infrastructure' => $infrastructureCalculation,
'total' => $totalRetribution
]
];
}
/**
* Create result for free building types
*/
protected function createFreeResult(
BuildingType $buildingType,
int $floorNumber,
float $buildingArea,
bool $saveResult
): array {
$result = [
'building_type' => [
'id' => $buildingType->id,
'code' => $buildingType->code,
'name' => $buildingType->name,
'is_free' => true
],
'input_parameters' => [
'building_area' => $buildingArea,
'floor_number' => $floorNumber
],
'total_retribution' => 0.0,
'formatted_amount' => 'Rp 0 (Gratis)',
'calculation_detail' => [
'reason' => 'Building type is free of charge',
'total' => 0.0
]
];
if ($saveResult) {
$calculation = RetributionCalculation::createCalculation(
$buildingType->id,
$floorNumber,
$buildingArea,
0.0,
$result['calculation_detail']
);
$result['calculation_id'] = $calculation->calculation_id;
}
return $result;
}
/**
* Get calculation by ID
*/
public function getCalculationById(string $calculationId): ?RetributionCalculation
{
return RetributionCalculation::with('buildingType')
->where('calculation_id', $calculationId)
->first();
}
/**
* Get all available building types for calculation
*/
public function getAvailableBuildingTypes(): array
{
return BuildingType::with('indices')
->active()
->children() // Only child types can be used for calculation
->get()
->map(function ($type) {
return [
'id' => $type->id,
'code' => $type->code,
'name' => $type->name,
'is_free' => $type->is_free,
'has_indices' => $type->indices !== null,
'coefficient' => $type->indices ? $type->indices->coefficient : null
];
})
->toArray();
}
/**
* Get all available floor numbers
*/
public function getAvailableFloors(): array
{
return HeightIndex::getAvailableFloors();
}
}

View File

@@ -1,14 +1,18 @@
<?php <?php
namespace App\Services; namespace App\Services;
use App\Models\BigdataResume;
use App\Models\DataSetting; use App\Models\DataSetting;
use App\Models\ImportDatasource; use App\Models\ImportDatasource;
use App\Models\PbgTaskGoogleSheet; use App\Models\PbgTaskGoogleSheet;
use App\Models\SpatialPlanning;
use App\Models\RetributionCalculation;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Google_Client; use Google\Client as Google_Client;
use Google_Service_Sheets; use Google\Service\Sheets as Google_Service_Sheets;
use Log; use Illuminate\Support\Facades\Log;
class ServiceGoogleSheet class ServiceGoogleSheet
{ {
protected $client; protected $client;
@@ -107,7 +111,7 @@ class ServiceGoogleSheet
'tanggal_skrd' => $this->convertToDate($cleanValue($row[33] ?? null)), 'tanggal_skrd' => $this->convertToDate($cleanValue($row[33] ?? null)),
'ptsp' => $cleanValue($row[34] ?? null), 'ptsp' => $cleanValue($row[34] ?? null),
'selesai_terbit' => $cleanValue($row[35] ?? null), 'selesai_terbit' => $cleanValue($row[35] ?? null),
'tanggal_pembayaran' => $cleanValue($row[36] ?? null), 'tanggal_pembayaran' => $this->convertToDate($cleanValue($row[36] ?? null)),
'format_sts' => $cleanValue($row[37] ?? null), 'format_sts' => $cleanValue($row[37] ?? null),
'tahun_terbit' => (int) $cleanValue($row[38] ?? null), 'tahun_terbit' => (int) $cleanValue($row[38] ?? null),
'tahun_berjalan' => (int) $cleanValue($row[39] ?? null), 'tahun_berjalan' => (int) $cleanValue($row[39] ?? null),
@@ -210,6 +214,110 @@ class ServiceGoogleSheet
} }
} }
public function sync_leader_data(){
$import_datasource = ImportDatasource::create([
'message' => 'Processing leader data',
'status' => 'processing',
'start_time' => now(),
'failed_uuid' => null
]);
try {
$sections = [
'TARGET_PAD' => "TARGET PAD 2024",
'KEKURANGAN_POTENSI' => "DEVIASI TARGET DENGAN POTENSI TOTAL BERKAS",
'TOTAL_POTENSI_BERKAS' => "•TOTAL BERKAS 2025",
'BELUM_TERVERIFIKASI' => "•BERKAS AKTUAL BELUM TERVERIFIKASI (POTENSI):",
'TERVERIFIKASI' => "•BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2025:",
'NON_USAHA' => "•NON USAHA: HUNIAN, SOSBUD, KEAGAMAAN",
'USAHA' => "•USAHA: USAHA, CAMPURAN, KOLEKTIF, PRASARANA",
'PROSES_DINAS_TEKNIS' => "•TERPROSES DI DPUTR: belum selesai rekomtek'",
'WAITING_KLIK_DPMPTSP' => "•TERPROSES DI PTSP: Pengiriman SKRD/ Validasi di PTSP",
'REALISASI_TERBIT_PBG' => "•BERKAS YANG TERBIT PBG 2025:"
];
$result = [];
foreach ($sections as $key => $identifier) {
$values = $this->get_values_from_section($identifier, [10, 11], 2);
if (!empty($values)) {
$result[$key] = [
'identifier' => $identifier,
'total' => $values[0] ?? null, // index 0 untuk total/jumlah
'nominal' => $values[1] ?? null // index 1 untuk nominal
];
}
}
BigdataResume::create([
'import_datasource_id' => $import_datasource->id,
'year' => 'leader',
// USAHA
'business_count' => $this->convertToInteger($result['USAHA']['total'] ?? null) ?? 0,
'business_sum' => $this->convertToDecimal($result['USAHA']['nominal'] ?? null) ?? 0,
// NON USAHA
'non_business_count' => $this->convertToInteger($result['NON_USAHA']['total'] ?? null) ?? 0,
'non_business_sum' => $this->convertToDecimal($result['NON_USAHA']['nominal'] ?? null) ?? 0,
// TERVERIFIKASI
'verified_count' => $this->convertToInteger($result['TERVERIFIKASI']['total'] ?? null) ?? 0,
'verified_sum' => $this->convertToDecimal($result['TERVERIFIKASI']['nominal'] ?? null) ?? 0,
// BELUM TERVERIFIKASI
'non_verified_count' => $this->convertToInteger($result['BELUM_TERVERIFIKASI']['total'] ?? null) ?? 0,
'non_verified_sum' => $this->convertToDecimal($result['BELUM_TERVERIFIKASI']['nominal'] ?? null) ?? 0,
// TOTAL POTENSI BERKAS
'potention_count' => $this->convertToInteger($result['TOTAL_POTENSI_BERKAS']['total'] ?? null) ?? 0,
'potention_sum' => $this->convertToDecimal($result['TOTAL_POTENSI_BERKAS']['nominal'] ?? null) ?? 0,
// REALISASI TERBIT PBG
'issuance_realization_pbg_count' => $this->convertToInteger($result['REALISASI_TERBIT_PBG']['total'] ?? null) ?? 0,
'issuance_realization_pbg_sum' => $this->convertToDecimal($result['REALISASI_TERBIT_PBG']['nominal'] ?? null) ?? 0,
// WAITING KLIK DPMPTSP
'waiting_click_dpmptsp_count' => $this->convertToInteger($result['WAITING_KLIK_DPMPTSP']['total'] ?? null) ?? 0,
'waiting_click_dpmptsp_sum' => $this->convertToDecimal($result['WAITING_KLIK_DPMPTSP']['nominal'] ?? null) ?? 0,
// PROSES DINAS TEKNIS
'process_in_technical_office_count' => $this->convertToInteger($result['PROSES_DINAS_TEKNIS']['total'] ?? null) ?? 0,
'process_in_technical_office_sum' => $this->convertToDecimal($result['PROSES_DINAS_TEKNIS']['nominal'] ?? null) ?? 0,
// TATA RUANG
'spatial_count' => $this->getSpatialPlanningWithCalculationCount(),
'spatial_sum' => $this->getSpatialPlanningCalculationSum()
]);
// Save data settings
$dataSettings = [
'TARGET_PAD' => $result['TARGET_PAD']['nominal'] ?? null,
'KEKURANGAN_POTENSI' => $result['KEKURANGAN_POTENSI']['nominal'] ?? null,
'REALISASI_TERBIT_PBG_COUNT' => $result['REALISASI_TERBIT_PBG']['total'] ?? null,
'REALISASI_TERBIT_PBG_SUM' => $result['REALISASI_TERBIT_PBG']['nominal'] ?? null,
'MENUNGGU_KLIK_DPMPTSP_COUNT' => $result['WAITING_KLIK_DPMPTSP']['total'] ?? null,
'MENUNGGU_KLIK_DPMPTSP_SUM' => $result['WAITING_KLIK_DPMPTSP']['nominal'] ?? null,
'PROSES_DINAS_TEKNIS_COUNT' => $result['PROSES_DINAS_TEKNIS']['total'] ?? null,
'PROSES_DINAS_TEKNIS_SUM' => $result['PROSES_DINAS_TEKNIS']['nominal'] ?? null,
];
foreach ($dataSettings as $key => $value) {
DataSetting::updateOrInsert(
['key' => $key],
['value' => $this->convertToInteger($value) ?? 0]
);
}
$import_datasource->update([
'status' => 'success',
'response_body' => json_encode($result),
'message' => 'Leader data synced',
'finish_time' => now()
]);
return $result;
} catch (\Exception $e) {
Log::error("Error syncing leader data", ['error' => $e->getMessage()]);
$import_datasource->update([
'status' => 'failed',
'message' => 'Leader data sync failed',
'finish_time' => now()
]);
throw $e;
}
}
public function get_big_resume_data(){ public function get_big_resume_data(){
try { try {
$sheet_big_data = $this->get_data_by_sheet(); $sheet_big_data = $this->get_data_by_sheet();
@@ -261,9 +369,71 @@ class ServiceGoogleSheet
return!empty($values)? $values : []; return!empty($values)? $values : [];
} }
/**
* Get specific values from a row that contains a specific text/section identifier
* @param string $section_identifier Text to search for in the row
* @param array $column_indices Array of column indices to extract values from
* @param int $no_sheet Sheet number (0-based)
* @return array Array of values from specified columns, or empty array if section not found
*/
private function get_values_from_section(string $section_identifier, array $column_indices = [], int $no_sheet = 1) {
try {
$sheet_data = $this->get_data_by_sheet($no_sheet);
if (empty($sheet_data)) {
Log::warning("No data found in sheet", ['sheet' => $no_sheet]);
return [];
}
// Search for the row containing the section identifier
$target_row = null;
foreach ($sheet_data as $row_index => $row) {
if (is_array($row)) {
foreach ($row as $cell) {
if (is_string($cell) && strpos($cell, $section_identifier) !== false) {
$target_row = $row;
break 2; // Break out of both loops
}
}
}
}
if ($target_row === null) {
Log::warning("Section not found", ['section_identifier' => $section_identifier]);
return [];
}
// Extract values from specified column indices
$extracted_values = [];
foreach ($column_indices as $col_index) {
if (isset($target_row[$col_index])) {
$value = trim($target_row[$col_index]);
$extracted_values[] = $value !== '' ? $value : null;
} else {
$extracted_values[] = null;
}
}
Log::info("Values extracted from section", [
'section_identifier' => $section_identifier,
'column_indices' => $column_indices,
'extracted_values' => $extracted_values
]);
return $extracted_values;
} catch (\Exception $e) {
Log::error("Error getting values from section", [
'error' => $e->getMessage(),
'section_identifier' => $section_identifier,
'sheet' => $no_sheet
]);
return [];
}
}
private function convertToInteger($value) { private function convertToInteger($value) {
// Check if the value is an empty string, and return null if true // Check if the value is null or empty string, and return null if true
if (trim($value) === "") { if ($value === null || trim($value) === "") {
return null; return null;
} }
@@ -300,6 +470,48 @@ class ServiceGoogleSheet
return is_numeric($value) ? (float) number_format((float) $value, 2, '.', '') : null; return is_numeric($value) ? (float) number_format((float) $value, 2, '.', '') : null;
} }
/**
* Get count of spatial plannings that have active retribution calculations
*/
public function getSpatialPlanningWithCalculationCount(): int
{
try {
return SpatialPlanning::whereHas('retributionCalculations', function ($query) {
$query->where('is_active', true);
})->count();
} catch (\Exception $e) {
Log::error("Error getting spatial planning with calculation count", ['error' => $e->getMessage()]);
return 0;
}
}
/**
* Get total sum of retribution amounts for spatial plannings with active calculations
*/
public function getSpatialPlanningCalculationSum(): float
{
try {
// Get all spatial plannings that have active calculations
$spatialPlannings = SpatialPlanning::whereHas('retributionCalculations', function ($query) {
$query->where('is_active', true);
})->with(['retributionCalculations.retributionCalculation'])
->get();
$totalSum = 0;
foreach ($spatialPlannings as $spatialPlanning) {
$activeCalculation = $spatialPlanning->activeRetributionCalculation;
if ($activeCalculation && $activeCalculation->retributionCalculation) {
$totalSum += $activeCalculation->retributionCalculation->retribution_amount;
}
}
return (float) $totalSum;
} catch (\Exception $e) {
Log::error("Error getting spatial planning calculation sum", ['error' => $e->getMessage()]);
return 0.0;
}
}
private function convertToDate($dateString) private function convertToDate($dateString)
{ {
try { try {

View File

@@ -4,6 +4,7 @@ namespace App\Services;
use App\Models\GlobalSetting; use App\Models\GlobalSetting;
use App\Models\PbgTask; use App\Models\PbgTask;
use App\Models\PbgTaskDetail;
use App\Models\PbgTaskIndexIntegrations; use App\Models\PbgTaskIndexIntegrations;
use App\Models\PbgTaskPrasarana; use App\Models\PbgTaskPrasarana;
use App\Models\PbgTaskRetributions; use App\Models\PbgTaskRetributions;
@@ -53,6 +54,7 @@ class ServiceTabPbgTask
} }
try{ try{
$this->current_uuid = $pbg_task->uuid; $this->current_uuid = $pbg_task->uuid;
$this->scraping_task_details($pbg_task->uuid);
$this->scraping_task_assignments($pbg_task->uuid); $this->scraping_task_assignments($pbg_task->uuid);
$this->scraping_task_retributions($pbg_task->uuid); $this->scraping_task_retributions($pbg_task->uuid);
$this->scraping_task_integrations($pbg_task->uuid); $this->scraping_task_integrations($pbg_task->uuid);
@@ -71,6 +73,75 @@ class ServiceTabPbgTask
return $this->current_uuid; return $this->current_uuid;
} }
private function scraping_task_details($uuid)
{
$url = "{$this->simbg_host}/api/pbg/v1/detail/{$uuid}/";
$options = [
'headers' => [
'Authorization' => "Bearer {$this->user_token}",
'Content-Type' => 'application/json'
]
];
$maxRetries = 3;
$initialDelay = 1;
$retriedAfter401 = false;
for ($retryCount = 0; $retryCount < $maxRetries; $retryCount++) {
try {
$response = $this->client->get($url, $options);
$responseData = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
if (empty($responseData['data']) || !is_array($responseData['data'])) {
return true;
}
$data = $responseData['data'];
// Use the static method from PbgTaskDetail model to create/update
PbgTaskDetail::createFromApiResponse($data, $uuid);
return $responseData;
} catch (\GuzzleHttp\Exception\ClientException $e) {
if ($e->getCode() === 401 && !$retriedAfter401) {
Log::warning("401 Unauthorized - Refreshing token and retrying...");
try{
$this->refreshToken();
$options['headers']['Authorization'] = "Bearer {$this->user_token}";
$retriedAfter401 = true;
continue;
}catch(\Exception $refreshError){
Log::error("Token refresh and login failed: " . $refreshError->getMessage());
return false;
}
}
return false;
} catch (\GuzzleHttp\Exception\ServerException | \GuzzleHttp\Exception\ConnectException $e) {
if ($e->getCode() === 502) {
Log::warning("502 Bad Gateway - Retrying in {$initialDelay} seconds...");
} else {
Log::error("Network error ({$e->getCode()}) - Retrying in {$initialDelay} seconds...");
}
sleep($initialDelay);
$initialDelay *= 2;
} catch (\GuzzleHttp\Exception\RequestException $e) {
Log::error("Request error ({$e->getCode()}): " . $e->getMessage());
return false;
} catch (\JsonException $e) {
Log::error("JSON decoding error: " . $e->getMessage());
return false;
} catch (\Throwable $e) {
Log::critical("Unhandled error: " . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
return false;
}
}
Log::error("Failed to fetch task details for UUID {$uuid} after {$maxRetries} retries.");
throw new \Exception("Failed to fetch task details for UUID {$uuid} after retries.");
}
private function scraping_task_assignments($uuid) private function scraping_task_assignments($uuid)
{ {
$url = "{$this->simbg_host}/api/pbg/v1/list-tim-penilai/{$uuid}/?page=1&size=10"; $url = "{$this->simbg_host}/api/pbg/v1/list-tim-penilai/{$uuid}/?page=1&size=10";

View File

@@ -0,0 +1,79 @@
<?php
namespace App\Traits;
use App\Models\RetributionCalculation;
use App\Models\CalculableRetribution;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
trait HasRetributionCalculation
{
/**
* Get all retribution calculations for this model (polymorphic many-to-many)
*/
public function retributionCalculations(): MorphMany
{
return $this->morphMany(CalculableRetribution::class, 'calculable');
}
/**
* Get active retribution calculation
*/
public function activeRetributionCalculation(): MorphOne
{
return $this->morphOne(CalculableRetribution::class, 'calculable')
->where('is_active', true)
->latest('assigned_at');
}
/**
* Assign calculation to this model
*/
public function assignRetributionCalculation(RetributionCalculation $calculation, string $notes = null): CalculableRetribution
{
// Deactivate previous active calculation
$this->retributionCalculations()
->where('is_active', true)
->update(['is_active' => false]);
// Create new assignment
return $this->retributionCalculations()->create([
'retribution_calculation_id' => $calculation->id,
'is_active' => true,
'assigned_at' => now(),
'notes' => $notes,
]);
}
/**
* Get current retribution amount
*/
public function getCurrentRetributionAmount(): float
{
$activeCalculation = $this->activeRetributionCalculation;
return $activeCalculation
? $activeCalculation->retributionCalculation->retribution_amount
: 0;
}
/**
* Check if has active calculation
*/
public function hasActiveRetributionCalculation(): bool
{
return $this->activeRetributionCalculation()->exists();
}
/**
* Get calculation history
*/
public function getRetributionCalculationHistory()
{
return $this->retributionCalculations()
->with('retributionCalculation')
->orderBy('assigned_at', 'desc')
->get();
}
}

0
bootstrap/app.php Executable file → Normal file
View File

0
bootstrap/providers.php Executable file → Normal file
View File

34
build-and-zip.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
# Build and Zip Script for Laravel Vite Project
echo "🚀 Starting build process..."
# Clean previous build
echo "🧹 Cleaning previous build..."
rm -rf public/build
rm -f build.zip
# Run npm build
echo "📦 Building assets with Vite..."
npm run build
# Check if build was successful
if [ $? -eq 0 ]; then
echo "✅ Build completed successfully!"
# Create zip file
echo "📁 Creating build.zip..."
cd public && zip -r ../build.zip build/
cd ..
echo "✅ build.zip created successfully!"
echo "📊 Build folder size:"
du -sh public/build
echo "📊 Zip file size:"
du -sh build.zip
echo "🎉 Process completed! You can now upload build.zip to your server."
else
echo "❌ Build failed! Please check the errors above."
exit 1
fi

BIN
build.zip Normal file

Binary file not shown.

0
composer.json Executable file → Normal file
View File

0
config/app.php Executable file → Normal file
View File

0
config/auth.php Executable file → Normal file
View File

0
config/cache.php Executable file → Normal file
View File

0
config/database.php Executable file → Normal file
View File

0
config/filesystems.php Executable file → Normal file
View File

0
config/logging.php Executable file → Normal file
View File

0
config/mail.php Executable file → Normal file
View File

0
config/queue.php Executable file → Normal file
View File

0
config/services.php Executable file → Normal file
View File

0
config/session.php Executable file → Normal file
View File

0
database/.gitignore vendored Executable file → Normal file
View File

0
database/factories/UserFactory.php Executable file → Normal file
View File

View File

View File

View File

View File

@@ -0,0 +1,26 @@
<?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::table('pbg_task', function(Blueprint $table){
$table->text('address')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@@ -0,0 +1,44 @@
<?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::table('spatial_plannings', function (Blueprint $table) {
$table->text('no_tapak')->nullable();
$table->text('no_skkl')->nullable();
$table->text('no_ukl')->nullable();
$table->string('building_function')->nullable();
$table->string('sub_building_function')->nullable();
$table->integer('number_of_floors')->default(1);
$table->decimal('land_area', 18, 6)->nullable();
$table->decimal('site_bcr', 10, 6)->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('spatial_plannings', function (Blueprint $table) {
$table->dropColumn([
'no_tapak',
'no_skkl',
'no_ukl',
'building_function',
'sub_building_function',
'number_of_floors',
'land_area',
'site_bcr',
]);
});
}
};

View File

@@ -0,0 +1,39 @@
<?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('building_functions', function (Blueprint $table) {
$table->id();
$table->string('code')->unique()->comment('Kode unik fungsi bangunan');
$table->string('name', 255)->comment('Nama fungsi bangunan');
$table->text('description')->nullable()->comment('Deskripsi detail fungsi bangunan');
$table->unsignedBigInteger('parent_id')->nullable()->comment('ID parent untuk hierarki');
$table->foreign('parent_id')->references('id')->on('building_functions')->onDelete('cascade');
$table->integer('level')->default(0)->comment('Level hierarki (0=root, 1=child, dst)');
$table->integer('sort_order')->default(0)->comment('Urutan tampilan');
$table->decimal('base_tariff', 15, 2)->nullable()->comment('Tarif dasar per m2');
// Indexes untuk performa
$table->index(['parent_id', 'level']);
$table->index(['level', 'sort_order']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('building_functions');
}
};

View File

@@ -0,0 +1,34 @@
<?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('floor_height_indices', function (Blueprint $table) {
$table->id();
$table->integer('floor_number')->comment('Nomor lantai');
$table->decimal('ip_ketinggian', 10, 6)->comment('Indeks ketinggian per lantai');
$table->text('description')->nullable()->comment('Deskripsi indeks ketinggian');
$table->timestamps();
// Unique constraint untuk floor_number
$table->unique('floor_number');
$table->index('floor_number');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('floor_height_indices');
}
};

View File

@@ -0,0 +1,37 @@
<?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('building_function_parameters', function (Blueprint $table) {
$table->id();
$table->foreignId('building_function_id')->constrained('building_functions')->onDelete('cascade');
$table->decimal('fungsi_bangunan', 10, 6)->nullable()->comment('Parameter fungsi bangunan');
$table->decimal('ip_permanen', 10, 6)->nullable()->comment('Parameter IP permanen');
$table->decimal('ip_kompleksitas', 10, 6)->nullable()->comment('Parameter IP kompleksitas');
$table->decimal('indeks_lokalitas', 10, 6)->nullable()->comment('Parameter indeks lokalitas');
$table->decimal('asumsi_prasarana', 8, 6)->nullable()->comment('Parameter asumsi prasarana untuk perhitungan retribusi');
$table->decimal('koefisien_dasar', 15, 6)->nullable()->comment('Koefisien dasar perhitungan');
$table->timestamps();
// Unique constraint untuk 1:1 relationship
$table->unique('building_function_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('building_function_parameters');
}
};

View File

@@ -0,0 +1,34 @@
<?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('retribution_formulas', function (Blueprint $table) {
$table->id();
$table->foreignId('building_function_id')->constrained('building_functions')->onDelete('cascade');
$table->string('name', 255)->comment('Nama formula');
$table->integer('floor_number')->comment('Nomor lantai (1, 2, 3, dst, 0=semua lantai)');
$table->text('formula_expression')->comment('Rumus matematika untuk perhitungan');
$table->timestamps();
// Indexes untuk performa
$table->index(['building_function_id', 'floor_number'], 'idx_building_floor');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('retribution_formulas');
}
};

View File

@@ -0,0 +1,46 @@
<?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('retribution_proposals', function (Blueprint $table) {
$table->id();
$table->foreignId('spatial_planning_id')->nullable()->constrained('spatial_plannings')->onDelete('cascade');
$table->foreignId('building_function_id')->constrained('building_functions')->onDelete('cascade');
$table->foreignId('retribution_formula_id')->constrained('retribution_formulas')->onDelete('cascade');
$table->string('proposal_number')->unique()->comment('Nomor usulan retribusi');
$table->integer('floor_number')->comment('Nomor lantai (1, 2, 3, dst)');
$table->decimal('floor_area', 15, 6)->comment('Luas lantai ini (m2)');
$table->decimal('total_building_area', 15, 6)->comment('Total luas bangunan (m2)');
$table->decimal('ip_ketinggian', 10, 6)->comment('IP ketinggian untuk lantai ini');
$table->decimal('floor_retribution_amount', 15, 2)->comment('Jumlah retribusi untuk lantai ini');
$table->decimal('total_retribution_amount', 15, 2)->comment('Total retribusi keseluruhan');
$table->json('calculation_parameters')->nullable()->comment('Parameter yang digunakan dalam perhitungan');
$table->json('calculation_breakdown')->nullable()->comment('Breakdown detail perhitungan');
$table->text('notes')->nullable()->comment('Catatan tambahan');
$table->datetime('calculated_at')->comment('Waktu perhitungan dilakukan');
$table->timestamps();
// Indexes untuk performa
$table->index(['spatial_planning_id', 'floor_number'], 'idx_spatial_floor');
$table->index(['building_function_id'], 'idx_function');
$table->index('calculated_at');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('retribution_proposals');
}
};

View File

@@ -0,0 +1,166 @@
<?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_details', function (Blueprint $table) {
$table->id();
// Foreign key relationship
$table->string('pbg_task_uid')->index();
// Basic information
$table->string('uid')->unique();
$table->string('nik');
$table->string('type_card');
$table->string('ownership')->nullable();
$table->string('owner_name');
// Owner location information
$table->bigInteger('ward_id');
$table->string('ward_name');
$table->integer('district_id');
$table->string('district_name');
$table->integer('regency_id');
$table->string('regency_name');
$table->integer('province_id');
$table->string('province_name');
$table->text('address');
// Owner contact information
$table->string('owner_email');
$table->string('owner_phone');
// User information
$table->integer('user');
$table->string('name');
$table->string('email');
$table->string('phone');
$table->string('user_nik');
// User location information
$table->integer('user_province_id');
$table->string('user_province_name');
$table->integer('user_regency_id');
$table->string('user_regency_name');
$table->integer('user_district_id');
$table->string('user_district_name');
$table->text('user_address');
// Status information
$table->integer('status');
$table->string('status_name');
$table->integer('slf_status')->nullable();
$table->string('slf_status_name')->nullable();
$table->integer('sppst_status');
$table->string('sppst_file')->nullable();
$table->string('sppst_status_name');
// Files and documents
$table->string('file_pbg')->nullable();
$table->date('file_pbg_date')->nullable();
$table->date('due_date')->nullable();
$table->date('start_date');
$table->string('document_number')->nullable();
$table->string('registration_number');
// Application information
$table->string('function_type')->nullable();
$table->string('application_type')->nullable();
$table->string('application_type_name')->nullable();
$table->string('consultation_type')->nullable();
$table->string('condition')->nullable();
$table->string('prototype')->nullable();
$table->string('permanency')->nullable();
// Building information
$table->integer('building_type')->nullable();
$table->string('building_type_name')->nullable();
$table->string('building_purpose')->nullable();
$table->string('building_use')->nullable();
$table->string('occupancy')->nullable();
$table->string('name_building')->nullable();
// Building dimensions and specifications
$table->decimal('total_area', 10, 2);
$table->decimal('area', 10, 2)->nullable();
$table->string('area_type')->nullable();
$table->decimal('height', 8, 2);
$table->integer('floor');
$table->decimal('floor_area', 10, 2)->nullable();
$table->integer('basement');
$table->decimal('basement_height', 8, 2)->nullable();
$table->decimal('basement_area', 10, 2);
$table->integer('unit')->nullable();
// Previous information
$table->decimal('prev_retribution', 15, 2)->nullable();
$table->string('prev_pbg')->nullable();
$table->decimal('prev_total_area', 10, 2)->nullable();
// Coefficients
$table->decimal('koefisien_dasar_bangunan', 8, 4)->nullable();
$table->decimal('koefisien_lantai_bangunan', 8, 4)->nullable();
$table->decimal('koefisien_lantai_hijau', 8, 4)->nullable();
$table->decimal('koefisien_tapak_basement', 8, 4)->nullable();
$table->decimal('ketinggian_bangunan', 8, 2)->nullable();
// Road information
$table->string('jalan_arteri')->nullable();
$table->string('jalan_kolektor')->nullable();
$table->string('jalan_bangunan')->nullable();
$table->decimal('gsb', 8, 2)->nullable();
$table->string('kkr_number')->nullable();
// Unit data as JSON
$table->json('unit_data')->nullable();
// Additional flags
$table->boolean('is_mbr')->default(false);
$table->string('code');
// Building location information
$table->bigInteger('building_ward_id');
$table->string('building_ward_name');
$table->integer('building_district_id');
$table->string('building_district_name');
$table->integer('building_regency_id');
$table->string('building_regency_name');
$table->integer('building_province_id');
$table->string('building_province_name');
$table->text('building_address');
// Coordinates
$table->decimal('latitude', 10, 8)->nullable();
$table->decimal('longitude', 11, 8)->nullable();
// Additional files
$table->string('building_photo')->nullable();
$table->string('pbg_parent')->nullable();
// Original created_at from API
$table->timestamp('api_created_at')->nullable();
$table->timestamps();
// Add foreign key constraint
$table->foreign('pbg_task_uid')->references('uuid')->on('pbg_task')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('pbg_task_details');
}
};

View File

@@ -0,0 +1,95 @@
<?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
{
// 1. Tabel Fungsi Bangunan (Simplified)
Schema::create('building_types', function (Blueprint $table) {
$table->id();
$table->string('code', 10)->unique()->comment('Kode fungsi bangunan');
$table->string('name', 100)->comment('Nama fungsi bangunan');
$table->unsignedBigInteger('parent_id')->nullable()->comment('Parent ID untuk hierarki');
$table->tinyInteger('level')->default(1)->comment('Level hierarki (1=parent, 2=child)');
$table->boolean('is_free')->default(false)->comment('Apakah gratis (keagamaan, MBR)');
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->index(['parent_id', 'level']);
$table->index('is_active');
$table->foreign('parent_id')->references('id')->on('building_types')->onDelete('cascade');
});
// 2. Tabel Parameter Indeks (Simplified)
Schema::create('retribution_indices', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('building_type_id');
$table->decimal('coefficient', 8, 4)->comment('Koefisien fungsi bangunan');
$table->decimal('ip_permanent', 8, 4)->comment('Indeks Permanensi');
$table->decimal('ip_complexity', 8, 4)->comment('Indeks Kompleksitas');
$table->decimal('locality_index', 8, 4)->comment('Indeks Lokalitas');
$table->decimal('infrastructure_factor', 8, 4)->default(0.5)->comment('Faktor prasarana (default 50%)');
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->unique('building_type_id');
$table->foreign('building_type_id')->references('id')->on('building_types')->onDelete('cascade');
});
// 3. Tabel Indeks Ketinggian (Simplified)
Schema::create('height_indices', function (Blueprint $table) {
$table->id();
$table->tinyInteger('floor_number')->unique()->comment('Nomor lantai');
$table->decimal('height_index', 8, 6)->comment('Indeks ketinggian');
$table->timestamps();
$table->index('floor_number');
});
// 4. Tabel Konfigurasi Global
Schema::create('retribution_configs', function (Blueprint $table) {
$table->id();
$table->string('key', 50)->unique()->comment('Kunci konfigurasi');
$table->decimal('value', 15, 2)->comment('Nilai konfigurasi');
$table->string('description', 200)->comment('Deskripsi konfigurasi');
$table->boolean('is_active')->default(true);
$table->timestamps();
});
// 5. Tabel Hasil Perhitungan (Simplified)
Schema::create('retribution_calculations', function (Blueprint $table) {
$table->id();
$table->string('calculation_id', 20)->unique()->comment('ID unik perhitungan');
$table->unsignedBigInteger('building_type_id');
$table->tinyInteger('floor_number');
$table->decimal('building_area', 12, 2)->comment('Luas bangunan (m2)');
$table->decimal('retribution_amount', 15, 2)->comment('Jumlah retribusi');
$table->json('calculation_detail')->comment('Detail perhitungan');
$table->timestamp('calculated_at');
$table->timestamps();
$table->index(['building_type_id', 'floor_number']);
$table->index('calculated_at');
$table->foreign('building_type_id')->references('id')->on('building_types');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('retribution_calculations');
Schema::dropIfExists('retribution_configs');
Schema::dropIfExists('height_indices');
Schema::dropIfExists('retribution_indices');
Schema::dropIfExists('building_types');
}
};

View File

@@ -0,0 +1,42 @@
<?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('calculable_retributions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('retribution_calculation_id');
$table->morphs('calculable'); // calculable_id & calculable_type (automatically creates index)
$table->boolean('is_active')->default(true)->comment('Status aktif calculation');
$table->timestamp('assigned_at')->useCurrent()->comment('Kapan calculation di-assign');
$table->text('notes')->nullable()->comment('Catatan assignment');
$table->timestamps();
// Additional indexes for better performance
$table->index('is_active');
$table->index('assigned_at');
// Foreign key constraint
$table->foreign('retribution_calculation_id')
->references('id')
->on('retribution_calculations')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('calculable_retributions');
}
};

1
database/seeders/DatabaseSeeder.php Executable file → Normal file
View File

@@ -50,6 +50,7 @@ class DatabaseSeeder extends Seeder
MenuSeeder::class, MenuSeeder::class,
UsersRoleMenuSeeder::class, UsersRoleMenuSeeder::class,
GlobalSettingSeeder::class, GlobalSettingSeeder::class,
RetributionDataSeeder::class,
]); ]);
} }
} }

View File

@@ -30,7 +30,7 @@ class MenuSeeder extends Seeder
"sort_order" => 2, "sort_order" => 2,
"children" => [ "children" => [
[ [
"name" => "Dashboard Pimpinan", "name" => "Dashboard Pimpinan SIMBG",
"url" => "dashboard.home", "url" => "dashboard.home",
"icon" => null, "icon" => null,
"sort_order" => 1, "sort_order" => 1,
@@ -67,6 +67,12 @@ class MenuSeeder extends Seeder
"icon" => null, "icon" => null,
"sort_order" => 4, "sort_order" => 4,
], ],
[
"name" => "Dashboard Pimpinan",
"url" => "dashboard.leader",
"icon" => null,
"sort_order" => 5,
],
], ],
], ],
[ [
@@ -208,23 +214,29 @@ class MenuSeeder extends Seeder
"icon" => null, "icon" => null,
"sort_order" => 2, "sort_order" => 2,
], ],
[
"name" => "Lap Pertumbuhan",
"url" => "growths",
"icon" => null,
"sort_order" => 3,
],
[ [
"name" => "Rekap Pembayaran", "name" => "Rekap Pembayaran",
"url" => "payment-recaps", "url" => "payment-recaps",
"icon" => null, "icon" => null,
"sort_order" => 3, "sort_order" => 4,
], ],
[ [
"name" => "Lap Rekap Data Pembayaran", "name" => "Lap Rekap Data Pembayaran",
"url" => "report-payment-recaps", "url" => "report-payment-recaps",
"icon" => null, "icon" => null,
"sort_order" => 4, "sort_order" => 5,
], ],
[ [
"name" => "Lap PBG (PTSP)", "name" => "Lap PBG (PTSP)",
"url" => "report-pbg-ptsp", "url" => "report-pbg-ptsp",
"icon" => null, "icon" => null,
"sort_order" => 5, "sort_order" => 6,
], ],
] ]
], ],

View File

@@ -0,0 +1,71 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class RetributionDataSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// Seed Building Types berdasarkan Excel (without coefficient)
DB::table('building_types')->insert([
// Parent Functions
['id' => 1, 'code' => 'KEAGAMAAN', 'name' => 'Fungsi Keagamaan', 'parent_id' => null, 'level' => 1, 'is_free' => true, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['id' => 2, 'code' => 'SOSBUDAYA', 'name' => 'Fungsi Sosial Budaya', 'parent_id' => null, 'level' => 1, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['id' => 3, 'code' => 'CAMPURAN', 'name' => 'Fungsi Campuran (lebih dari 1)', 'parent_id' => null, 'level' => 1, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['id' => 4, 'code' => 'USAHA', 'name' => 'Fungsi Usaha', 'parent_id' => null, 'level' => 1, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['id' => 5, 'code' => 'HUNIAN', 'name' => 'Fungsi Hunian', 'parent_id' => null, 'level' => 1, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
// Child Functions
['id' => 6, 'code' => 'CAMP_KECIL', 'name' => 'Campuran Kecil', 'parent_id' => 3, 'level' => 2, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['id' => 7, 'code' => 'CAMP_BESAR', 'name' => 'Campuran Besar', 'parent_id' => 3, 'level' => 2, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['id' => 8, 'code' => 'UMKM', 'name' => 'Fungsi Usaha (UMKM)', 'parent_id' => 4, 'level' => 2, 'is_free' => false, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['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()],
]);
// Seed Retribution Indices berdasarkan Excel (with coefficient moved here)
DB::table('retribution_indices')->insert([
['building_type_id' => 1, 'coefficient' => 0.0000, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.0000, 'locality_index' => 0.0000, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // Keagamaan
['building_type_id' => 2, 'coefficient' => 0.3000, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.6000, 'locality_index' => 0.0030, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // Sosial Budaya
['building_type_id' => 6, 'coefficient' => 0.6000, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.6000, 'locality_index' => 0.0050, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // Campuran Kecil
['building_type_id' => 7, 'coefficient' => 0.8000, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.6000, 'locality_index' => 0.0050, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // Campuran Besar
['building_type_id' => 8, 'coefficient' => 0.5000, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.6000, 'locality_index' => 0.0040, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // UMKM
['building_type_id' => 9, 'coefficient' => 0.7000, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.6000, 'locality_index' => 0.0050, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // Usaha Besar
['building_type_id' => 10, 'coefficient' => 0.1500, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.3000, 'locality_index' => 0.0040, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // Hunian Sederhana
['building_type_id' => 11, 'coefficient' => 0.1700, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.6000, 'locality_index' => 0.0040, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // Hunian Tidak Sederhana
['building_type_id' => 12, 'coefficient' => 0.0000, 'ip_permanent' => 0.4000, 'ip_complexity' => 0.0000, 'locality_index' => 0.0000, 'infrastructure_factor' => 0.5000, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], // MBR
]);
// Seed Height Indices berdasarkan Excel
DB::table('height_indices')->insert([
['floor_number' => 1, 'height_index' => 1.0000, 'created_at' => now(), 'updated_at' => now()],
['floor_number' => 2, 'height_index' => 1.0900, 'created_at' => now(), 'updated_at' => now()],
['floor_number' => 3, 'height_index' => 1.1200, 'created_at' => now(), 'updated_at' => now()],
['floor_number' => 4, 'height_index' => 1.1350, 'created_at' => now(), 'updated_at' => now()],
['floor_number' => 5, 'height_index' => 1.1620, 'created_at' => now(), 'updated_at' => now()],
['floor_number' => 6, 'height_index' => 1.1970, 'created_at' => now(), 'updated_at' => now()],
]);
// Seed Retribution Configs
DB::table('retribution_configs')->insert([
['key' => 'BASE_VALUE', 'value' => 7035000.00, 'description' => 'Nilai dasar perhitungan retribusi', 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['key' => 'INFRASTRUCTURE_MULTIPLIER', 'value' => 0.50, 'description' => 'Pengali asumsi prasarana (50%)', 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
['key' => 'HEIGHT_MULTIPLIER', 'value' => 0.50, 'description' => 'Pengali indeks ketinggian dalam formula', 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
]);
$this->command->info('✅ Retribution data seeded successfully!');
$this->command->info('📊 Building Types: 12 records');
$this->command->info('📊 Retribution Indices: 9 records');
$this->command->info('📊 Height Indices: 6 records');
$this->command->info('📊 Retribution Configs: 3 records');
}
}

View File

@@ -25,7 +25,7 @@ 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)' 'Approval Pejabat', 'Undangan', 'Rekap Pembayaran', 'Lap Rekap Data Pembayaran', 'Lap PBG (PTSP)', 'Lap Pertumbuhan'
])->get()->keyBy('name'); ])->get()->keyBy('name');
// Define access levels for each role // Define access levels for each role
@@ -36,7 +36,7 @@ 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', 'Dalam Sistem', 'Lap Pariwisata', 'UMKM', 'Dashboard Potensi', 'Tata Ruang', 'PDAM', 'Dalam Sistem',
'Luar Sistem', 'Lap Pimpinan', 'Google Sheets', 'TPA TPT', 'Approval Pejabat', 'Luar Sistem', 'Lap Pimpinan', 'Google Sheets', 'TPA TPT', 'Approval Pejabat',
'Undangan', 'Rekap Pembayaran', 'Lap Rekap Data Pembayaran', 'Lap PBG (PTSP)' 'Undangan', 'Rekap Pembayaran', 'Lap Rekap Data Pembayaran', 'Lap PBG (PTSP)', 'Lap Pertumbuhan'
], ],
'user' => ['Dashboard', 'Data', 'Laporan', 'Neng Bedas', 'user' => ['Dashboard', 'Data', 'Laporan', 'Neng Bedas',
'Approval', 'Tools', 'Dashboard Pimpinan', 'Dashboard PBG', 'Users', 'Syncronize', 'Approval', 'Tools', 'Dashboard Pimpinan', 'Dashboard PBG', 'Users', 'Syncronize',
@@ -68,9 +68,9 @@ class UsersRoleMenuSeeder extends Seeder
// Attach User to role super admin // Attach User to role super admin
$accountSuperadmin = User::where('email', 'superadmin@sibedas.com')->first(); $accountSuperadmin = User::where('email', 'superadmin@sibedas.com')->first();
$accountUser = User::where('email', 'user@sibedas.com')->first(); $accountUser = User::where('email', 'user@sibedas.com')->first();
$accountDefault = User::where('email','user@demo.com')->first(); // $accountDefault = User::where('email','user@demo.com')->first();
$accountSuperadmin->roles()->sync([$roles['superadmin']->id]); $accountSuperadmin->roles()->sync([$roles['superadmin']->id]);
$accountUser->roles()->sync([$roles['user']->id]); $accountUser->roles()->sync([$roles['user']->id]);
$accountDefault->roles()->sync([$roles['user']->id]); // $accountDefault->roles()->sync([$roles['user']->id]);
} }
} }

View File

@@ -1,39 +0,0 @@
GIT_BRANCH="dev"
PHP_VERSION="php8.3"
echo "🚀 Starting deployment..."
php artisan down
echo "📥 Pulling latest changes from Git..."
git fetch origin $GIT_BRANCH
git reset --hard origin/$GIT_BRANCH
git pull origin $GIT_BRANCH
echo "⚡ Installing NPM dependencies and building assets..."
npm ci --no-audit --no-fund
npm run build
echo "📦 Installing composer dependencies..."
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-interaction --optimize-autoloader
echo "🗄️ Running migrations..."
php artisan migrate --force
echo "Running seeders..."
php artisan db:seed --force
echo "⚡ Optimizing application..."
php artisan optimize:clear
echo "🔄 Restarting PHP service..."
systemctl reload $PHP_VERSION-fpm
echo "🔁 Restarting Supervisor queue workers..."
php artisan queue:restart
supervisorctl reread
supervisorctl update
supervisorctl restart all
php artisan up
echo "🚀 Deployment completed successfully!"

80
docker-compose.yml Normal file
View File

@@ -0,0 +1,80 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
container_name: sibedas_app
restart: unless-stopped
environment:
APP_ENV: local
APP_DEBUG: true
APP_URL: http://localhost
VITE_APP_URL: http://localhost
DB_CONNECTION: mariadb
DB_HOST: db
DB_PORT: 3306
DB_DATABASE: sibedas_db
DB_USERNAME: root
DB_PASSWORD: root
volumes:
- .:/var/www
depends_on:
- db
networks:
- sibedas_net
nginx:
image: nginx:alpine
container_name: sibedas_nginx
restart: unless-stopped
ports:
- "8000:80"
volumes:
- .:/var/www
- ./docker/nginx/conf.d/app.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- sibedas_net
db:
image: mariadb:10.6
container_name: sibedas_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: sibedas_db
MYSQL_USER: root
MYSQL_PASSWORD: root
ports:
- "3306:3306"
volumes:
- dbdata:/var/lib/mysql
- ./sibedas.sql:/docker-entrypoint-initdb.d/sibedas.sql
networks:
- sibedas_net
vite:
build:
context: .
dockerfile: Dockerfile
target: development
container_name: sibedas_vite
restart: unless-stopped
environment:
VITE_APP_URL: http://localhost
volumes:
- .:/var/www
- /var/www/node_modules
ports:
- "5173:5173"
networks:
- sibedas_net
volumes:
dbdata:
networks:
sibedas_net:
driver: bridge

View File

@@ -0,0 +1,50 @@
server {
listen 80;
index index.php index.html;
server_name localhost;
root /var/www/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Proxy Vite requests in development
location /@vite {
proxy_pass http://vite:5173;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /build/ {
alias /var/www/public/build/;
access_log off;
expires max;
}
location /assets/ {
alias /var/www/public/assets/;
access_log off;
expires max;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
location ~ /\.ht {
deny all;
}
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot|otf)$ {
expires max;
log_not_found off;
}
}

59
docker/startup.sh Normal file
View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Enable error reporting
set -e
set -x
# Create necessary directories with proper permissions as root
mkdir -p /var/log/supervisor
mkdir -p /var/run/supervisor
chown -R www-data:www-data /var/log/supervisor
chown -R www-data:www-data /var/run/supervisor
chmod -R 775 /var/log/supervisor
chmod -R 775 /var/run/supervisor
# Create storage directories with proper permissions
mkdir -p /var/www/storage/logs
chown -R www-data:www-data /var/www/storage
chmod -R 775 /var/www/storage
# Ensure Laravel storage and cache directories are writable
chown -R www-data:www-data /var/www/bootstrap/cache
chmod -R 775 /var/www/bootstrap/cache
# Wait for database to be ready (with increased timeout and better error handling)
max_tries=30
count=0
echo "Waiting for database connection..."
# First, wait a bit for the database container to fully initialize
sleep 10
while ! php artisan db:monitor > /dev/null 2>&1; do
count=$((count + 1))
if [ $count -gt $max_tries ]; then
echo "Database connection timeout after $max_tries attempts"
echo "Checking database container status..."
# Try to connect directly to MySQL to get more detailed error
mysql -h db -u admindb_arifal -parifal201 -e "SELECT 1" || true
exit 1
fi
echo "Waiting for database... ($count/$max_tries)"
sleep 5
done
echo "Database connection established!"
# Run database-dependent commands
echo "Running database migrations..."
php artisan migrate --force
echo "Running database seeders..."
php artisan db:seed --force
echo "Optimizing Laravel..."
php artisan optimize:clear
php artisan optimize
# Start supervisor (which will manage PHP-FPM)
exec /usr/bin/supervisord -n -c /etc/supervisor/conf.d/supervisord.conf

View File

@@ -0,0 +1,41 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[program:laravel-queue-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/pupr/artisan queue:work --queue=default --timeout=82800 --tries=3 --memory=512 --sleep=3
directory=/var/www/pupr
autostart=true
autorestart=true
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/pupr/storage/logs/queue-worker.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stopasgroup=true
killasgroup=true
user=www-data
priority=10
[program:laravel-scheduler]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/pupr/artisan schedule:work
directory=/var/www/pupr
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/pupr/storage/logs/scheduler.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=5
stopasgroup=true
killasgroup=true
user=www-data
priority=20
[group:laravel]
programs=laravel-queue-worker,laravel-scheduler
priority=999

View File

@@ -0,0 +1,31 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan queue:work --queue=default --timeout=82800 --tries=1
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/worker.log
stopasgroup=true
killasgroup=true
user=www-data
priority=10
[program:laravel-scheduler]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/artisan schedule:work
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/storage/logs/scheduler.log
stopasgroup=true
killasgroup=true
user=www-data
priority=20

4
package.json Executable file → Normal file
View File

@@ -3,6 +3,10 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "vite build", "build": "vite build",
"build:prod": "NODE_OPTIONS='--max-old-space-size=2048 --max-semi-space-size=1024' vite build --config vite.config.production.js",
"build:chunked": "./build-chunked.sh",
"build:local": "vite build && echo 'Build completed! Now run: ./deploy.sh to upload to server'",
"build:zip": "./build-and-zip.sh",
"dev": "vite" "dev": "vite"
}, },
"devDependencies": { "devDependencies": {

0
phpunit.xml Executable file → Normal file
View File

0
public/.htaccess Executable file → Normal file
View File

View File

@@ -1 +0,0 @@
var u=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function f(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function l(e){if(e.__esModule)return e;var r=e.default;if(typeof r=="function"){var t=function o(){return this instanceof o?Reflect.construct(r,arguments,this.constructor):r.apply(this,arguments)};t.prototype=r.prototype}else t={};return Object.defineProperty(t,"__esModule",{value:!0}),Object.keys(e).forEach(function(o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}),t}export{f as a,u as c,l as g};

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More