Compare commits

...

116 Commits

Author SHA1 Message Date
arifal
59cc102c5a create service count floor level and retributions 2025-06-18 18:44:30 +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
arifal
501a76bc81 partial update circle chart can click 2025-05-06 17:29:39 +07:00
arifal
460267992e handle null value on detail quick search 2025-05-06 15:32:03 +07:00
arifal
becc368069 last update feat quick search 2025-05-06 15:06:59 +07:00
arifal
2618ac06d0 partial update create page quick search 2025-05-05 18:36:56 +07:00
arifal
d95676d477 fix show page document pbg 2025-04-10 15:44:51 +07:00
arifal
7737fee30f add validation topic on send prompt if not relevan 2025-04-10 01:59:57 +07:00
arifal
48293386c7 fix change chatbot to top 2025-04-09 21:22:26 +07:00
arifal
84870b95b1 add feat upload pbg task 2025-04-09 21:10:20 +07:00
arifal
6294d2f950 add new users on seeder for role user and superadmin 2025-03-27 17:19:27 +07:00
arifal
7b70591be5 add readme and backup schema local 2025-03-27 15:28:21 +07:00
arifal
3b67bfd1fb remove duplicate use timeout when running queue 2025-03-26 15:20:03 +07:00
arifal
45e22096ed add column to model import datasources 2025-03-26 11:51:57 +07:00
arifal
c7a8d6d249 add condition show button retry 2025-03-25 22:46:34 +07:00
arifal
091b1f305e fix handle retry button 2025-03-25 15:54:02 +07:00
arifal
e7950e22f2 add flatpicker for edit pbg task data 2025-03-24 21:16:05 +07:00
arifal
b68641db03 backup local db and add global setting seeder to run default 2025-03-24 11:08:10 +07:00
arifal
fefb85ac7a remove get sample data from service 2025-03-21 19:10:17 +07:00
arifal
d28a08a24c fix add bigdata resume after syncronize 2025-03-21 19:08:18 +07:00
arifal
654d2efe19 fix syncronize using worker and add duration syncronize 2025-03-21 18:44:28 +07:00
arifal
0a080763cd try catch handle refresh token fail to login again 2025-03-21 16:03:10 +07:00
arifal
f3ef21d1be fix style error message when failed login 2025-03-21 15:10:28 +07:00
arifal
d7bff86741 fix menu with parent data and remove action google sheet not available feature 2025-03-21 14:54:18 +07:00
arifal
f36f250700 fix supervisorctl syntax on deploy 2025-03-20 20:19:38 +07:00
arifal
2c5da87856 fix reload php service on deploy 2025-03-20 20:18:09 +07:00
arifal
a195559c4b fix running syntax seeder on automation deploy syntax 2025-03-20 20:15:30 +07:00
arifal
088f173fec fix add params filter date on grid js, export excel and pdf payment recaps 2025-03-20 20:12:42 +07:00
arifal
eadfddb3a4 add export excel and pdf district payment recaps 2025-03-19 18:55:51 +07:00
arifal
47a9fb1dfb add export excel and pdf report director 2025-03-19 18:22:00 +07:00
arifal
1713e32b67 add export pdf report tourisms and report ptsp 2025-03-19 17:15:10 +07:00
arifal
cf998455e0 add export excel report tourisms 2025-03-19 16:16:10 +07:00
arifal
5e1c9f3a2e fix dashboard pbg, add soft delete users, fix js create pbg task 2025-03-19 14:06:10 +07:00
arifal
e940b8d6c7 fix handle sync using button 2025-03-18 08:10:06 +07:00
arifal
5e139bc29c change command scraping 2025-03-18 06:47:53 +07:00
arifal
f9e1aa1604 fix service scraping data 2025-03-18 06:42:42 +07:00
arifal
2e385f80cd fix optimize syncronize 2025-03-14 19:10:28 +07:00
arifal
e2c26e0eff fix reklame route index 2025-03-13 16:23:17 +07:00
arifal
ca5b8ad403 fix deploy code and readme 2025-03-13 15:59:11 +07:00
arifal
e97b7eb70d fix redirect back spatial plannings 2025-03-13 15:48:53 +07:00
arifal
0258ca9f04 fix redirect back umkm 2025-03-13 15:39:32 +07:00
arifal
0116147e06 fix redirect back reklame and tourisms 2025-03-13 15:24:59 +07:00
arifal
7787db02a3 fix redirect back business or industries 2025-03-13 14:13:06 +07:00
arifal
e47ab36d5e fix pdam redirect back and advertisement partial update redirect 2025-03-13 13:57:53 +07:00
arifal
e5db2294b4 fix redirect back data settings 2025-03-12 21:39:52 +07:00
arifal
4db457d7bd fix redirect back users crud 2025-03-12 21:17:49 +07:00
arifal
7a82ad5302 fix redirect back roles 2025-03-12 20:30:16 +07:00
arifal
b0d4d4c23b fix redirect back with params menu id 2025-03-12 17:52:11 +07:00
arifal
68ffc1c090 fix 503 page 2025-03-12 15:57:31 +07:00
arifal
a1f4bd7f81 fix seeder menu role and user assign, create 503 page and fix redirect home 2025-03-12 15:37:22 +07:00
arifal
238aaba96c fix seeder users role menu 2025-03-12 12:01:28 +07:00
arifal
c7152d9dbe fix resources report payment recaps 2025-03-12 11:50:56 +07:00
arifal
2a4b96d0b2 fix vite resources 2025-03-12 11:43:11 +07:00
arifal
dd940ebdb6 fix inside and outside dashboard 2025-03-12 11:38:07 +07:00
arifal
dce5409248 done all view dummy new modul 2025-03-12 00:32:50 +07:00
arifal
b8f7d7f655 fix back button 2025-03-11 01:17:16 +07:00
arifal
65600f1b4f add view list data from google sheet 2025-03-11 00:26:48 +07:00
arifal
b0f15a9221 fix sidebar permission user 2025-03-10 16:33:53 +07:00
arifal
bf55eb228e fix width dashboard outside sy system and loading when load maps pariwisata, backup localdb 2025-03-07 18:00:30 +07:00
arifal
755720bac9 fix failed build images from leaflet 2025-03-07 16:41:52 +07:00
arifal
098b4c605b fix missing scss on vite 2025-03-07 15:39:26 +07:00
arifal
4632e102eb add use package missing 2025-03-07 15:00:26 +07:00
arifal
0431945a42 fix conflict 2025-03-07 14:52:51 +07:00
@jamaludinarifrohman6661
fbfa2a37bb feature: set role previledge access 2025-03-07 14:39:46 +07:00
arifal
c529a5d511 add readme 2025-03-07 14:39:23 +07:00
arifal
ff244039ff add readme and env example 2025-03-07 14:37:26 +07:00
arifal
55902042f4 add readme 2025-03-07 14:27:49 +07:00
arifal
c67aa979c2 change column type expertise and fix syncronize simbg service 2025-03-07 14:04:37 +07:00
arifal
fbaa33ae13 fix height scrollbar table grid js 2025-03-07 01:55:46 +07:00
arifal
9516b6f575 fix task assignment table on pbg task 2025-03-07 01:03:22 +07:00
arifal
ffc08f26cc add dashboard inside and outside system and fix timeout when search filter 2025-03-06 23:33:31 +07:00
@jamaludinarifrohman6661
dceb46ab86 change-request: add custom geo layer 2025-03-06 16:50:13 +07:00
arifal
e0c35b8897 fix search filter on page big data resume 2025-03-06 14:39:36 +07:00
arifal
22ee7502ad Merge remote-tracking branch 'origin/feature/chatbot-sidebar' into fix/sync-task-assignment 2025-03-06 11:45:34 +07:00
arifal
2f3bc172eb add search filter 2025-03-06 11:42:59 +07:00
@jamaludinarifrohman6661
bba932b2ba Merge remote-tracking branch 'origin/dev' into feature/chatbot-sidebar 2025-03-06 11:20:45 +07:00
@jamaludinarifrohman6661
3f5d0eb1cd fix: inserting chat history into the answer generation process 2025-03-06 11:06:45 +07:00
arifal
1f33d0de4e add sync task assignment pbg 2025-03-06 00:13:13 +07:00
arifal
86d694bcac add new menu chat bedas and view 2025-03-04 17:56:30 +07:00
arifal
cb5a3243fc Merge remote-tracking branch 'origin/feature/chatbot-sidebar' into dev 2025-03-04 17:32:37 +07:00
@jamaludinarifrohman6661
15210a56ee feature: chatbot pimpinan 2025-03-04 17:31:40 +07:00
arifal
a08f2cb2b7 fix optimizing deployment 2025-03-04 16:45:32 +07:00
arifal
632433c496 fix routing spatial-plannings 2025-03-04 16:23:00 +07:00
arifal
5b4780495e fix routing spatial-plannings 2025-03-04 16:17:47 +07:00
arifal
0a7012a57c fix tourisms routing 2025-03-04 16:13:17 +07:00
arifal
435a19346b fix route umkm 2025-03-04 16:05:21 +07:00
arifal
8fcf8859d6 hot fix advertisement route conflict 2025-03-04 15:58:30 +07:00
arifal
43a246d234 add permission deployment file 2025-03-04 08:49:51 +00:00
arifal
d6d0acf8fb hot fix conflict routing web and api advertisements 2025-03-04 15:46:35 +07:00
arifal
b4ec7a9d25 merge chatbot sidebar 2025-03-04 15:19:11 +07:00
arifal
5203babe11 Merge remote-tracking branch 'origin/feature/chatbot-sidebar' into dev 2025-03-04 15:15:32 +07:00
arifal
c0faafdbd7 hot fix add time midnight scheduler 2025-03-04 15:13:33 +07:00
@jamaludinarifrohman6661
572b86299c add:setting main chatbot
fix:chatbot ui
2025-03-04 14:46:29 +07:00
524 changed files with 304033 additions and 5622 deletions

0
.editorconfig Executable file → Normal file
View File

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

@@ -68,3 +68,4 @@ VITE_APP_NAME="${APP_NAME}"
API_KEY_GOOGLE="xxxxx" API_KEY_GOOGLE="xxxxx"
SPREAD_SHEET_ID="xxxxx" SPREAD_SHEET_ID="xxxxx"
OPENAI_API_KEY="xxxxx"

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"]

81
README.md Executable file → Normal file
View File

@@ -17,13 +17,14 @@ sudo nano /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker] [program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d process_name=%(program_name)s_%(process_num)02d
command=php /path-to-your-project/artisan queue:work --tries=3 --timeout=600 command=php /home/arifal/development/sibedas-pbg-web/artisan queue:work --queue=default --timeout=82800 --tries=1
autostart=true autostart=true
autorestart=true autorestart=true
numprocs=1 numprocs=1
user=www-data
redirect_stderr=true redirect_stderr=true
stdout_logfile=/var/log/supervisor/laravel-worker.log stdout_logfile=/home/arifal/development/sibedas-pbg-web/storage/logs/worker.log
stopasgroup=true
killasgroup=true
``` ```
- Reload Supervisor - Reload Supervisor
@@ -35,3 +36,77 @@ sudo supervisorctl start laravel-worker
sudo supervisorctl restart laravel-worker sudo supervisorctl restart laravel-worker
sudo supervisorctl status sudo supervisorctl status
``` ```
# How to running
- Install composer package
```
composer install
```
- Install npm package
```
npm install && npm run build
```
- Create symlinks storage
```
php artisan storage:link
```
- Running migration
```
php artisan migrate
```
- Running seeder
```
php artisan db:seed
```
- Create view table
- excute all sql queries on folder database/view_query
# Add ENV variable
- API_KEY_GOOGLE
```
Get api key from google developer console for and turn on spreadsheet api or feaature for google sheet
```
- SPREAD_SHEET_ID
```
Get spreadsheet id from google sheet link
```
- OPENAI_API_KEY
```
Get OpenAI API key from chatgpt subscription
```
- ENV
```
API_KEY_GOOGLE="xxxxx"
SPREAD_SHEET_ID="xxxxx"
OPENAI_API_KEY="xxxxx"
```
# Technology version
- php 8.3
- Laravel 11
- node v22.13.0
- npm 10.9.2
- mariadb Ver 15.1 Distrib 10.6.18-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
- Ubuntu 24.04

View File

@@ -0,0 +1,288 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\SpatialPlanning;
use App\Models\RetributionProposal;
use App\Services\RetributionProposalService;
use Illuminate\Support\Facades\DB;
use Exception;
class CalculateRetributionProposalsCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'retribution:calculate-proposals
{--force : Force recalculate existing proposals}
{--limit= : Limit number of spatial plannings to process}
{--skip-existing : Skip spatial plannings that already have proposals}
{--dry-run : Show what would be processed without actually creating proposals}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Calculate retribution proposals for all spatial plannings';
protected RetributionProposalService $proposalService;
public function __construct(RetributionProposalService $proposalService)
{
parent::__construct();
$this->proposalService = $proposalService;
}
/**
* Execute the console command.
*/
public function handle()
{
$this->info('🧮 Starting Retribution Proposal Calculation...');
$this->newLine();
try {
// Get processing options
$force = $this->option('force');
$limit = $this->option('limit') ? (int) $this->option('limit') : null;
$skipExisting = $this->option('skip-existing');
$dryRun = $this->option('dry-run');
// Build query for spatial plannings
$query = SpatialPlanning::query();
if ($skipExisting && !$force) {
$query->whereDoesntHave('retributionProposals');
}
if ($limit) {
$query->limit($limit);
}
$spatialPlannings = $query->get();
$totalCount = $spatialPlannings->count();
if ($totalCount === 0) {
$this->warn('No spatial plannings found to process.');
return 0;
}
// Show processing summary
$this->info("📊 PROCESSING SUMMARY:");
$this->info(" Total Spatial Plannings: {$totalCount}");
$this->info(" Force Recalculate: " . ($force ? 'Yes' : 'No'));
$this->info(" Skip Existing: " . ($skipExisting ? 'Yes' : 'No'));
$this->info(" Dry Run: " . ($dryRun ? 'Yes' : 'No'));
$this->newLine();
if ($dryRun) {
$this->showDryRunPreview($spatialPlannings);
return 0;
}
// Confirm processing
if (!$this->confirm("Process {$totalCount} spatial plannings?")) {
$this->info('Operation cancelled.');
return 0;
}
// Process spatial plannings
$this->processRetributionCalculations($spatialPlannings, $force);
return 0;
} catch (Exception $e) {
$this->error('Error during retribution calculation: ' . $e->getMessage());
return 1;
}
}
/**
* Show dry run preview
*/
private function showDryRunPreview($spatialPlannings)
{
$this->info('🔍 DRY RUN PREVIEW:');
$this->newLine();
$processable = 0;
$withProposals = 0;
$withoutFunction = 0;
$withoutArea = 0;
foreach ($spatialPlannings as $spatial) {
$hasProposals = $spatial->retributionProposals()->exists();
$buildingFunction = $this->proposalService->detectBuildingFunction($spatial->getBuildingFunctionText());
$area = $spatial->getCalculationArea();
if ($hasProposals) {
$withProposals++;
}
if (!$buildingFunction) {
$withoutFunction++;
}
if ($area <= 0) {
$withoutArea++;
}
if ($buildingFunction && $area > 0) {
$processable++;
}
}
$this->table(
['Status', 'Count', 'Description'],
[
['✅ Processable', $processable, 'Can create retribution proposals'],
['🔄 With Existing Proposals', $withProposals, 'Already have proposals (will skip unless --force)'],
['❌ Missing Building Function', $withoutFunction, 'Cannot detect building function'],
['❌ Missing Area', $withoutArea, 'Area is zero or missing'],
]
);
$this->newLine();
$this->info("💡 To process: php artisan retribution:calculate-proposals");
$this->info("💡 To force recalculate: php artisan retribution:calculate-proposals --force");
}
/**
* Process retribution calculations
*/
private function processRetributionCalculations($spatialPlannings, $force)
{
$progressBar = $this->output->createProgressBar($spatialPlannings->count());
$progressBar->start();
$stats = [
'processed' => 0,
'skipped' => 0,
'errors' => 0,
'created_proposals' => 0
];
foreach ($spatialPlannings as $spatial) {
try {
$result = $this->processSingleSpatialPlanning($spatial, $force);
if ($result['status'] === 'processed') {
$stats['processed']++;
$stats['created_proposals'] += $result['proposals_created'];
} elseif ($result['status'] === 'skipped') {
$stats['skipped']++;
} else {
$stats['errors']++;
}
} catch (Exception $e) {
$stats['errors']++;
$this->newLine();
$this->error("Error processing spatial planning ID {$spatial->id}: " . $e->getMessage());
}
$progressBar->advance();
}
$progressBar->finish();
$this->newLine(2);
// Show final statistics
$this->showFinalStatistics($stats);
}
/**
* Process single spatial planning
*/
private function processSingleSpatialPlanning(SpatialPlanning $spatial, $force)
{
// Check if already has proposals
if (!$force && $spatial->retributionProposals()->exists()) {
return ['status' => 'skipped', 'reason' => 'already_has_proposals'];
}
// Detect building function
$buildingFunction = $this->proposalService->detectBuildingFunction($spatial->getBuildingFunctionText());
if (!$buildingFunction) {
return ['status' => 'error', 'reason' => 'no_building_function'];
}
// Get area
$totalArea = $spatial->getCalculationArea();
if ($totalArea <= 0) {
return ['status' => 'error', 'reason' => 'no_area'];
}
// Get number of floors
$numberOfFloors = max(1, $spatial->number_of_floors ?? 1);
// Delete existing proposals if force mode
if ($force) {
$spatial->retributionProposals()->delete();
}
$proposalsCreated = 0;
// Create proposals for each floor
for ($floor = 1; $floor <= $numberOfFloors; $floor++) {
// Calculate floor area (distribute total area across floors)
$floorArea = $totalArea / $numberOfFloors;
// Create retribution proposal
$proposal = $this->proposalService->createProposalForSpatialPlanning(
$spatial,
$buildingFunction->id,
$floor,
$floorArea,
$totalArea,
"Auto-generated from spatial planning calculation"
);
if ($proposal) {
$proposalsCreated++;
}
}
return [
'status' => 'processed',
'proposals_created' => $proposalsCreated
];
}
/**
* Show final statistics
*/
private function showFinalStatistics($stats)
{
$this->info('✅ CALCULATION COMPLETED!');
$this->newLine();
$this->table(
['Metric', 'Count'],
[
['Processed Successfully', $stats['processed']],
['Skipped (Already Exists)', $stats['skipped']],
['Errors', $stats['errors']],
['Total Proposals Created', $stats['created_proposals']],
]
);
if ($stats['errors'] > 0) {
$this->newLine();
$this->warn("⚠️ {$stats['errors']} spatial plannings had errors:");
$this->warn(" • Missing building function detection");
$this->warn(" • Missing or zero area");
$this->warn(" • Other calculation errors");
}
if ($stats['processed'] > 0) {
$this->newLine();
$this->info("🎉 Successfully created {$stats['created_proposals']} retribution proposals!");
$this->info("💡 You can view them using: php artisan retribution:list-proposals");
}
}
}

View File

@@ -34,7 +34,7 @@ class ExecuteScraping extends Command
} }
public function handle() public function handle()
{ {
SyncronizeSIMBG::dispatch(); SyncronizeSIMBG::dispatch()->onQueue('default');
Log::info("running scheduler daily scraping"); Log::info("running scheduler daily scraping");
} }
} }

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

@@ -0,0 +1,321 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\SpatialPlanning;
use App\Models\RetributionProposal;
use App\Services\RetributionProposalService;
use Illuminate\Support\Facades\DB;
use Exception;
class ProcessSpatialPlanningRetributionCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'spatial:process-retribution
{--all : Process all spatial plannings}
{--new-only : Process only spatial plannings without retribution proposals}
{--force : Force recalculate existing retribution proposals}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Process and save retribution calculations for spatial plannings';
protected RetributionProposalService $proposalService;
public function __construct(RetributionProposalService $proposalService)
{
parent::__construct();
$this->proposalService = $proposalService;
}
/**
* Execute the console command.
*/
public function handle()
{
$this->info('🏗️ Processing Spatial Planning Retribution Calculations...');
$this->newLine();
try {
// Get options
$processAll = $this->option('all');
$newOnly = $this->option('new-only');
$force = $this->option('force');
// Build query
$query = SpatialPlanning::query();
if ($newOnly || (!$processAll && !$force)) {
$query->whereDoesntHave('retributionProposals');
$this->info('📋 Mode: Processing only spatial plannings WITHOUT retribution proposals');
} elseif ($processAll) {
$this->info('📋 Mode: Processing ALL spatial plannings');
}
$spatialPlannings = $query->get();
$totalCount = $spatialPlannings->count();
if ($totalCount === 0) {
$this->warn('❌ No spatial plannings found to process.');
$this->info('💡 Try running: php artisan spatial:init first');
return 0;
}
$this->info("📊 Found {$totalCount} spatial plannings to process");
$this->newLine();
// Show preview of what will be processed
$this->showProcessingPreview($spatialPlannings);
// Confirm processing
if (!$this->confirm("Process {$totalCount} spatial plannings and create retribution proposals?")) {
$this->info('Operation cancelled.');
return 0;
}
// Process all spatial plannings
$this->processAllSpatialPlannings($spatialPlannings, $force);
return 0;
} catch (Exception $e) {
$this->error('❌ Error during processing: ' . $e->getMessage());
return 1;
}
}
/**
* Show processing preview
*/
private function showProcessingPreview($spatialPlannings)
{
$this->info('🔍 PREVIEW:');
$canProcess = 0;
$cannotProcess = 0;
$hasExisting = 0;
$sampleData = [];
$errorReasons = [];
foreach ($spatialPlannings->take(5) as $spatial) {
$buildingFunction = $this->proposalService->detectBuildingFunction($spatial->getBuildingFunctionText());
$area = $spatial->getCalculationArea();
$floors = max(1, $spatial->number_of_floors ?? 1);
$hasProposals = $spatial->retributionProposals()->exists();
if ($hasProposals) {
$hasExisting++;
}
if ($buildingFunction && $area > 0) {
$canProcess++;
$sampleData[] = [
'ID' => $spatial->id,
'Name' => substr($spatial->name ?? 'N/A', 0, 30),
'Function' => $buildingFunction->name ?? 'N/A',
'Area' => number_format($area, 2),
'Floors' => $floors,
'Status' => $hasProposals ? '🔄 Has Proposals' : '✅ Ready'
];
} else {
$cannotProcess++;
if (!$buildingFunction) {
$errorReasons[] = 'Missing building function';
}
if ($area <= 0) {
$errorReasons[] = 'Missing/zero area';
}
}
}
// Show sample data table
if (!empty($sampleData)) {
$this->table(
['ID', 'Name', 'Function', 'Area (m²)', 'Floors', 'Status'],
$sampleData
);
}
// Show summary
$this->newLine();
$this->info("📈 SUMMARY:");
$this->info(" ✅ Can Process: {$canProcess}");
$this->info(" ❌ Cannot Process: {$cannotProcess}");
$this->info(" 🔄 Has Existing Proposals: {$hasExisting}");
if ($cannotProcess > 0) {
$this->warn(" ⚠️ Common Issues: " . implode(', ', array_unique($errorReasons)));
}
$this->newLine();
}
/**
* Process all spatial plannings
*/
private function processAllSpatialPlannings($spatialPlannings, $force)
{
$this->info('🚀 Starting processing...');
$this->newLine();
$progressBar = $this->output->createProgressBar($spatialPlannings->count());
$progressBar->start();
$stats = [
'processed' => 0,
'skipped' => 0,
'errors' => 0,
'total_proposals' => 0,
'total_amount' => 0
];
$errors = [];
foreach ($spatialPlannings as $spatial) {
try {
$result = $this->processSingleSpatialPlanning($spatial, $force);
if ($result['success']) {
$stats['processed']++;
$stats['total_proposals'] += $result['proposals_count'];
$stats['total_amount'] += $result['total_amount'];
} elseif ($result['skipped']) {
$stats['skipped']++;
} else {
$stats['errors']++;
$errors[] = "ID {$spatial->id}: " . $result['error'];
}
} catch (Exception $e) {
$stats['errors']++;
$errors[] = "ID {$spatial->id}: " . $e->getMessage();
}
$progressBar->advance();
}
$progressBar->finish();
$this->newLine(2);
// Show final results
$this->showFinalResults($stats, $errors);
}
/**
* Process single spatial planning
*/
private function processSingleSpatialPlanning(SpatialPlanning $spatial, $force)
{
// Check if already has proposals
if (!$force && $spatial->retributionProposals()->exists()) {
return ['success' => false, 'skipped' => true];
}
// Detect building function
$buildingFunction = $this->proposalService->detectBuildingFunction($spatial->getBuildingFunctionText());
if (!$buildingFunction) {
return ['success' => false, 'skipped' => false, 'error' => 'Cannot detect building function from: ' . $spatial->getBuildingFunctionText()];
}
// Get area
$totalArea = $spatial->getCalculationArea();
if ($totalArea <= 0) {
return ['success' => false, 'skipped' => false, 'error' => 'Area is zero or missing'];
}
// Get number of floors
$numberOfFloors = max(1, $spatial->number_of_floors ?? 1);
// Delete existing proposals if force mode
if ($force) {
$spatial->retributionProposals()->delete();
}
$proposalsCount = 0;
$totalAmount = 0;
// Create single proposal for the spatial planning (not per floor to prevent duplicates)
// Use the highest floor for IP ketinggian calculation (worst case scenario)
$highestFloor = $numberOfFloors;
$proposal = $this->proposalService->createProposalForSpatialPlanning(
$spatial,
$buildingFunction->id,
$highestFloor, // Use highest floor for calculation
$totalArea, // Use total area, not divided by floors
$totalArea,
"Auto-calculated from spatial planning data (floors: {$numberOfFloors})"
);
if ($proposal) {
$proposalsCount = 1;
$totalAmount = $proposal->total_retribution_amount;
}
return [
'success' => true,
'skipped' => false,
'proposals_count' => $proposalsCount,
'total_amount' => $totalAmount
];
}
/**
* Show final results
*/
private function showFinalResults($stats, $errors)
{
$this->info('🎉 PROCESSING COMPLETED!');
$this->newLine();
// Main statistics table
$this->table(
['Metric', 'Count'],
[
['✅ Successfully Processed', $stats['processed']],
['⏭️ Skipped (Already Has Proposals)', $stats['skipped']],
['❌ Errors', $stats['errors']],
['📄 Total Proposals Created', $stats['total_proposals']],
['💰 Total Retribution Amount', 'Rp ' . number_format($stats['total_amount'], 2)],
]
);
// Show success message
if ($stats['processed'] > 0) {
$this->newLine();
$this->info("🎊 SUCCESS!");
$this->info(" 📊 Created {$stats['total_proposals']} retribution proposals");
$this->info(" 💵 Total calculated amount: Rp " . number_format($stats['total_amount'], 2));
$this->info(" 📋 Processed {$stats['processed']} spatial plannings");
}
// Show errors if any
if ($stats['errors'] > 0) {
$this->newLine();
$this->warn("⚠️ ERRORS ENCOUNTERED:");
foreach (array_slice($errors, 0, 10) as $error) {
$this->warn("{$error}");
}
if (count($errors) > 10) {
$this->warn(" ... and " . (count($errors) - 10) . " more errors");
}
}
// Show next steps
$this->newLine();
$this->info("📋 NEXT STEPS:");
$this->info(" • View proposals: php artisan spatial:check-constraints");
$this->info(" • Check database: SELECT COUNT(*) FROM retribution_proposals;");
$this->info(" • Access via API: GET /api/retribution-proposals");
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Console\Commands;
use App\Jobs\ScrapingDataJob;
use App\Models\ImportDatasource;
use App\Services\ServiceGoogleSheet;
use App\Services\ServicePbgTask;
use App\Services\ServiceTabPbgTask;
use App\Services\ServiceTokenSIMBG;
use GuzzleHttp\Client; // Import Guzzle Client
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
class ScrapingData extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:scraping-data';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Inject dependencies.
*/
public function __construct(
) {
parent::__construct();
}
public function handle()
{
dispatch(new ScrapingDataJob());
$this->info("Scraping job dispatched successfully");
}
}

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,125 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Services\DynamicRetributionCalculationService;
class TestExcelFormulaCommand extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'test:excel-formula';
/**
* The console command description.
*/
protected $description = 'Test Excel formula implementation for PBG retribution calculation';
/**
* Execute the console command.
*/
public function handle()
{
$service = new DynamicRetributionCalculationService();
$this->info('=== TESTING RUNDOWN 4 IMPLEMENTATION ===');
$this->info('Formula: =ROUNDDOWN(($E13*($F13+$G13+(0.5*I$3))),4)');
$this->newLine();
// Test 1: Hunian Sederhana
$this->info('1. HUNIAN SEDERHANA (1 Lantai, 100 m²)');
$result1 = $service->calculateRetribution(
buildingFunctionId: 10, // HUNIAN_SEDERHANA
floorLevel: 1,
luasBangunan: 100,
indeksLokasi: 'sedang',
includeInfrastructure: true
);
if ($result1['success']) {
$rundown4 = $result1['calculation_breakdown']['rundown_4_calculation'];
$retribution = $result1['calculation_breakdown']['retribution_calculation'];
$this->info(' ✓ Before ROUNDDOWN: ' . $rundown4['before_rounddown']);
$this->info(' ✓ After ROUNDDOWN(4): ' . $rundown4['after_rounddown']);
$this->info(' ✓ Basic Retribution: Rp ' . number_format($retribution['basic_amount']));
$this->info(' ✓ Infrastructure: Rp ' . number_format($retribution['infrastructure_amount']));
$this->info(' 💰 TOTAL: Rp ' . number_format($retribution['total_amount']));
} else {
$this->error(' ❌ Error: ' . $result1['error']);
}
$this->newLine();
// Test 2: Usaha Besar
$this->info('2. USAHA BESAR (3 Lantai, 200 m²)');
$result2 = $service->calculateRetribution(
buildingFunctionId: 9, // USAHA_BESAR
floorLevel: 3,
luasBangunan: 200,
indeksLokasi: 'tinggi',
includeInfrastructure: true
);
if ($result2['success']) {
$rundown4 = $result2['calculation_breakdown']['rundown_4_calculation'];
$retribution = $result2['calculation_breakdown']['retribution_calculation'];
$this->info(' ✓ Rundown 4 Result: ' . $rundown4['after_rounddown']);
$this->info(' ✓ Basic: Rp ' . number_format($retribution['basic_amount']));
$this->info(' ✓ Infrastructure: Rp ' . number_format($retribution['infrastructure_amount']));
$this->info(' 💰 TOTAL: Rp ' . number_format($retribution['total_amount']));
}
$this->newLine();
// Test 3: Keagamaan (Free)
$this->info('3. KEAGAMAAN (2 Lantai, 150 m²) - BEBAS RETRIBUSI');
$result3 = $service->calculateRetribution(
buildingFunctionId: 1, // KEAGAMAAN
floorLevel: 2,
luasBangunan: 150,
indeksLokasi: 'sedang',
includeInfrastructure: true
);
if ($result3['success']) {
$this->info(' ✓ Rundown 4 Result: ' . $result3['results']['h5_rundown4']);
$this->info(' 💸 RETRIBUSI BEBAS: Rp ' . number_format($result3['results']['total_retribution']));
}
$this->newLine();
// Test 4: Multi-Floor Calculation
$this->info('4. CAMPURAN BESAR - Multi Lantai (1-4 Lantai, 300 m²)');
$result4 = $service->calculateMultiFloorRetribution(
buildingFunctionId: 7, // CAMPURAN_BESAR
floors: [1, 2, 3, 4],
luasBangunan: 300,
indeksLokasi: 'tinggi'
);
if ($result4['success']) {
$this->info(' ✓ Per Floor Calculations:');
foreach ($result4['floor_details'] as $floor => $detail) {
$this->info(" Lantai {$floor}: H5={$detail['h5_rundown4']}, Total=Rp " . number_format($detail['total_retribution']));
}
$this->info(' 💰 TOTAL SEMUA LANTAI: Rp ' . number_format($result4['total_retribution']));
}
$this->newLine();
$this->info('=== RUNDOWN 4 VERIFICATION ===');
$this->info('✅ Formula: =ROUNDDOWN(($E13*($F13+$G13+(0.5*I$3))),4)');
$this->info('✅ ROUNDDOWN function with 4 decimal precision');
$this->info('✅ Tarif Dasar: Rp 7.035.000');
$this->info('✅ Infrastructure calculation: O3 * basic');
$this->info('✅ Code simplified - unused formulas removed');
$this->newLine();
$this->info('🎉 RUNDOWN 4 Implementation is CLEAN and VERIFIED!');
return Command::SUCCESS;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Enums;
enum PbgTaskApplicationTypes: string
{
case PERSETUJUAN_BG = '1';
case PERUBAHAN_BG = '2';
case SLF_BB = '4';
case SLF = '5';
public static function labels(): array
{
return [
null => "Pilih Application Type",
self::PERSETUJUAN_BG->value => 'Persetujuan Bangunan Gedung',
self::PERUBAHAN_BG->value => 'Perubahan Bangunan Gedung',
self::SLF_BB->value => 'Sertifikat Laik Fungsi - Bangunan Baru',
self::SLF->value => 'Sertifikat Laik Fungsi',
];
}
public static function getLabel(?string $status): ?string
{
return self::labels()[$status] ?? null;
}
}

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

@@ -0,0 +1,59 @@
<?php
namespace App\Enums;
enum PbgTaskStatus: int
{
case VERIFIKASI_KELENGKAPAN = 1;
case PERBAIKAN_DOKUMEN = 2;
case PERMOHONAN_DIBATALKAN = 3;
case MENUNGGU_PENUGASAN_TPT_TPA = 4;
case MENUNGGU_JADWAL_KONSULTASI = 5;
case PELAKSANAAN_KONSULTASI = 6;
case PERBAIKAN_DOKUMEN_KONSULTASI = 8;
case PERMOHONAN_DITOLAK = 9;
case PERHITUNGAN_RETRIBUSI = 10;
case MENUNGGU_PEMBAYARAN_RETRIBUSI = 14;
case VERIFIKASI_PEMBAYARAN_RETRIBUSI = 15;
case RETRIBUSI_TIDAK_SESUAI = 16;
case VERIFIKASI_SK_PBG = 18;
case PENERBITAN_SK_PBG = 19;
case SK_PBG_TERBIT = 20;
case PENERBITAN_SPPST = 24;
case PROSES_PENERBITAN_SKRD = 25;
case MENUNGGU_PENUGASAN_TPT = 26;
case VERIFIKASI_DATA_TPT = 27;
case SERTIFIKAT_SLF_TERBIT = 28;
public static function getStatuses(): array
{
return [
null => "Pilih Status",
self::VERIFIKASI_KELENGKAPAN->value => "Verifikasi Kelengkapan Dokumen",
self::PERBAIKAN_DOKUMEN->value => "Perbaikan Dokumen",
self::PERMOHONAN_DIBATALKAN->value => "Permohonan Dibatalkan",
self::MENUNGGU_PENUGASAN_TPT_TPA->value => "Menunggu Penugasan TPT/TPA",
self::MENUNGGU_JADWAL_KONSULTASI->value => "Menunggu Jadwal Konsultasi",
self::PELAKSANAAN_KONSULTASI->value => "Pelaksanaan Konsultasi",
self::PERBAIKAN_DOKUMEN_KONSULTASI->value => "Perbaikan Dokumen Konsultasi",
self::PERMOHONAN_DITOLAK->value => "Permohonan Ditolak",
self::PERHITUNGAN_RETRIBUSI->value => "Perhitungan Retribusi",
self::MENUNGGU_PEMBAYARAN_RETRIBUSI->value => "Menunggu Pembayaran Retribusi",
self::VERIFIKASI_PEMBAYARAN_RETRIBUSI->value => "Verifikasi Pembayaran Retribusi",
self::RETRIBUSI_TIDAK_SESUAI->value => "Retribusi Tidak Sesuai",
self::VERIFIKASI_SK_PBG->value => "Verifikasi SK PBG",
self::PENERBITAN_SK_PBG->value => "Penerbitan SK PBG",
self::SK_PBG_TERBIT->value => "SK PBG Terbit",
self::PENERBITAN_SPPST->value => "Penerbitan SPPST",
self::PROSES_PENERBITAN_SKRD->value => "Proses Penerbitan SKRD",
self::MENUNGGU_PENUGASAN_TPT->value => "Menunggu Penugasan TPT",
self::VERIFIKASI_DATA_TPT->value => "Verifikasi Data TPT",
self::SERTIFIKAT_SLF_TERBIT->value => "Sertifikat SLF Terbit",
];
}
public static function getLabel(?int $status): ?string
{
return self::getStatuses()[$status] ?? null;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Exports;
use App\Models\PbgTaskGoogleSheet;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class DistrictPaymentRecapExport implements FromCollection, WithHeadings
{
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return PbgTaskGoogleSheet::select(
'kecamatan',
DB::raw('SUM(nilai_retribusi_keseluruhan_simbg) as total')
)
->groupBy('kecamatan')->get();
}
public function headings(): array{
return [
'Kecamatan',
'Total'
];
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace App\Exports;
use App\Models\BigdataResume;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class ReportDirectorExport implements FromCollection, WithHeadings, WithMapping
{
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return BigdataResume::select(
'potention_count',
'potention_sum',
'non_verified_count',
'non_verified_sum',
'verified_count',
'verified_sum',
'business_count',
'business_sum',
'non_business_count',
'non_business_sum',
'spatial_count',
'spatial_sum',
'waiting_click_dpmptsp_count',
'waiting_click_dpmptsp_sum',
'issuance_realization_pbg_count',
'issuance_realization_pbg_sum',
'process_in_technical_office_count',
'process_in_technical_office_sum',
'year',
'created_at'
)->orderBy('id', 'desc')->get();
}
public function headings(): array{
return [
"Jumlah Potensi" ,
"Total Potensi" ,
"Jumlah Berkas Belum Terverifikasi" ,
"Total Berkas Belum Terverifikasi" ,
"Jumlah Berkas Terverifikasi" ,
"Total Berkas Terverifikasi" ,
"Jumlah Usaha" ,
"Total Usaha" ,
"Jumlah Non Usaha" ,
"Total Non Usaha" ,
"Jumlah Tata Ruang" ,
"Total Tata Ruang" ,
"Jumlah Menunggu Klik DPMPTSP" ,
"Total Menunggu Klik DPMPTSP" ,
"Jumlah Realisasi Terbit PBG" ,
"Total Realisasi Terbit PBG" ,
"Jumlah Proses Dinas Teknis" ,
"Total Proses Dinas Teknis",
"Tahun",
"Created"
];
}
public function map($row): array
{
return [
$row->potention_count,
$row->potention_sum,
$row->non_verified_count,
$row->non_verified_sum,
$row->verified_count,
$row->verified_sum,
$row->business_count,
$row->business_sum,
$row->non_business_count,
$row->non_business_sum,
$row->spatial_count,
$row->spatial_sum,
$row->waiting_click_dpmptsp_count,
$row->waiting_click_dpmptsp_sum,
$row->issuance_realization_pbg_count,
$row->issuance_realization_pbg_sum,
$row->process_in_technical_office_count,
$row->process_in_technical_office_sum,
$row->year,
$row->created_at ? $row->created_at->format('Y-m-d H:i:s') : null, // Format created_at as Y-m-d
];
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace App\Exports;
use App\Models\BigdataResume;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class ReportPaymentRecapExport implements FromCollection, WithHeadings
{
/**
* @return \Illuminate\Support\Collection
*/
protected $startDate;
protected $endDate;
public function __construct($startDate, $endDate){
$this->startDate = $startDate;
$this->endDate = $endDate;
}
public function collection()
{
$query = BigdataResume::query()->orderBy('id', 'desc');
if ($this->startDate && $this->endDate) {
$query->whereBetween('created_at', [$this->startDate, $this->endDate]);
}
$items = $query->get();
$categoryMap = [
'potention_sum' => 'Potensi',
'non_verified_sum' => 'Belum Terverifikasi',
'verified_sum' => 'Terverifikasi',
'business_sum' => 'Usaha',
'non_business_sum' => 'Non Usaha',
'spatial_sum' => 'Tata Ruang',
'waiting_click_dpmptsp_sum' => 'Menunggu Klik DPMPTSP',
'issuance_realization_pbg_sum' => 'Realisasi Terbit PBG',
'process_in_technical_office_sum' => 'Proses Di Dinas Teknis',
];
// Restructure response
$data = [];
foreach ($items as $item) {
$createdAt = $item->created_at;
$id = $item->id;
foreach ($item->toArray() as $key => $value) {
// Only include columns with "sum" in their names
if (strpos($key, 'sum') !== false) {
$data[] = [
'category' => $categoryMap[$key] ?? $key, // Map category
'nominal' => number_format($value, 0, ',', '.'), // Format number
'created_at' => $createdAt->format('Y-m-d H:i:s'), // Format date
];
}
}
}
return collect($data);
}
public function headings(): array{
return [
'Kategori',
'Nominal',
'Created'
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Exports;
use App\Models\PbgTask;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class ReportPbgPtspExport implements FromCollection, WithHeadings
{
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return PbgTask::select(
'status_name',
DB::raw('COUNT(*) as total')
)
->groupBy('status', 'status_name')
->get();
}
public function headings(): array
{
return [
'Status Name',
'Total'
];
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Exports;
use App\Models\TourismBasedKBLI;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class ReportTourismExport implements FromCollection, WithHeadings
{
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return TourismBasedKBLI::select('kbli_title', 'total_records')->get();
}
public function headings(): array{
return [
'Jenis Bisnis Pariwisata',
'Jumlah Total'
];
}
}

View File

@@ -2,12 +2,17 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Exports\ReportDirectorExport;
use App\Exports\ReportPaymentRecapExport;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Resources\BigdataResumeResource; use App\Http\Resources\BigdataResumeResource;
use App\Models\BigdataResume; use App\Models\BigdataResume;
use App\Models\DataSetting; use App\Models\DataSetting;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
class BigDataResumeController extends Controller class BigDataResumeController extends Controller
{ {
@@ -20,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();
@@ -162,7 +168,7 @@ class BigDataResumeController extends Controller
$query = BigdataResume::query()->orderBy('id', 'desc'); $query = BigdataResume::query()->orderBy('id', 'desc');
if($request->filled('search')){ if($request->filled('search')){
$query->where('name', 'LIKE', '%'.$request->input('search').'%'); $query->where('year', 'LIKE', '%'.$request->input('search').'%');
} }
$query = $query->paginate(config('app.paginate_per_page', 50)); $query = $query->paginate(config('app.paginate_per_page', 50));
@@ -173,6 +179,148 @@ class BigDataResumeController extends Controller
} }
} }
public function payment_recaps(Request $request)
{
try {
$query = BigdataResume::query()->orderBy('id', 'desc');
if ($request->filled('start_date') && $request->filled('end_date')) {
$startDate = Carbon::parse($request->input('start_date'))->startOfDay();
$endDate = Carbon::parse($request->input('end_date'))->endOfDay();
$query->whereBetween('created_at', [$startDate, $endDate]);
}
$data = $query->paginate(50);
// Restructure response
$transformedData = [];
foreach ($data as $item) {
$createdAt = $item->created_at;
$id = $item->id;
foreach ($item->toArray() as $key => $value) {
// Only include columns with "sum" in their names
if (strpos($key, 'sum') !== false) {
$transformedData[] = [
'id' => $id,
'category' => $key,
'nominal' => $value,
'created_at' => $createdAt,
];
}
}
}
return response()->json([
'data' => $transformedData, // Flat array
'pagination' => [
'total' => count($transformedData),
'per_page' => $data->perPage(),
'current_page' => $data->currentPage(),
'last_page' => $data->lastPage(),
]
]);
} catch (\Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Error when fetching data'], 500);
}
}
public function export_excel_payment_recaps(Request $request)
{
$startDate = null;
$endDate = null;
if ($request->filled('start_date') && $request->filled('end_date')) {
$startDate = Carbon::parse($request->input('start_date'))->startOfDay();
$endDate = Carbon::parse($request->input('end_date'))->endOfDay();
}
return Excel::download(new ReportPaymentRecapExport($startDate, $endDate), 'laporan-rekap-pembayaran.xlsx');
}
public function export_pdf_payment_recaps(Request $request){
$query = BigdataResume::query()->orderBy('id', 'desc');
if ($request->filled('start_date') && $request->filled('end_date')) {
$startDate = Carbon::parse($request->input('start_date'))->startOfDay();
$endDate = Carbon::parse($request->input('end_date'))->endOfDay();
$query->whereBetween('created_at', [$startDate, $endDate]);
}
$items = $query->get();
// Define category mapping
$categoryMap = [
'potention_sum' => 'Potensi',
'non_verified_sum' => 'Belum Terverifikasi',
'verified_sum' => 'Terverifikasi',
'business_sum' => 'Usaha',
'non_business_sum' => 'Non Usaha',
'spatial_sum' => 'Tata Ruang',
'waiting_click_dpmptsp_sum' => 'Menunggu Klik DPMPTSP',
'issuance_realization_pbg_sum' => 'Realisasi Terbit PBG',
'process_in_technical_office_sum' => 'Proses Di Dinas Teknis',
];
// Restructure response
$data = [];
foreach ($items as $item) {
$createdAt = $item->created_at;
$id = $item->id;
foreach ($item->toArray() as $key => $value) {
// Only include columns with "sum" in their names
if (strpos($key, 'sum') !== false) {
$data[] = [
'id' => $id,
'category' => $categoryMap[$key] ?? $key, // Map category
'nominal' => $value, // Format number
'created_at' => $createdAt->format('Y-m-d H:i:s'), // Format date
];
}
}
}
$pdf = Pdf::loadView('exports.payment_recaps_report', compact('data'));
return $pdf->download('laporan-rekap-pembayaran.pdf');
}
public function export_excel_report_director(){
return Excel::download(new ReportDirectorExport, 'laporan-pimpinan.xlsx');
}
public function export_pdf_report_director(){
$data = BigdataResume::select(
'potention_count',
'potention_sum',
'non_verified_count',
'non_verified_sum',
'verified_count',
'verified_sum',
'business_count',
'business_sum',
'non_business_count',
'non_business_sum',
'spatial_count',
'spatial_sum',
'waiting_click_dpmptsp_count',
'waiting_click_dpmptsp_sum',
'issuance_realization_pbg_count',
'issuance_realization_pbg_sum',
'process_in_technical_office_count',
'process_in_technical_office_sum',
'year',
'created_at'
)->orderBy('id', 'desc')->get();
$pdf = Pdf::loadView('exports.director_report', compact('data'))->setPaper('a4', 'landscape');
return $pdf->download('laporan-pimpinan.pdf');
}
private function response_empty_resume(){ private function response_empty_resume(){
$result = [ $result = [
'target_pad' => [ 'target_pad' => [

View File

@@ -7,6 +7,7 @@ use App\Services\OpenAIService;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class ChatbotController extends Controller class ChatbotController extends Controller
{ {
@@ -19,7 +20,6 @@ class ChatbotController extends Controller
public function generateText(Request $request) public function generateText(Request $request)
{ {
info($request);
$request->validate([ $request->validate([
'tab_active' => 'required|string', 'tab_active' => 'required|string',
'prompt' => 'required|string', 'prompt' => 'required|string',
@@ -33,52 +33,85 @@ class ChatbotController extends Controller
default => "UNKNOWN", default => "UNKNOWN",
}; };
$chatHistory = $request->input('chatHistory');
Log::info('Chat history sebelum disimpan:', ['history' => $chatHistory]);
if ($main_content === "UNKNOWN") { if ($main_content === "UNKNOWN") {
return response()->json(['response' => 'Invalid tab_active value.'], 400); return response()->json(['response' => 'Invalid tab_active value.'], 400);
} }
info($main_content); // info($main_content);
// Klasifikasi apakah pertanyaan butuh database atau bisa dijawab langsung $queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content, $chatHistory);
$classifyResponse = $this->openAIService->generateClassifyMainContent($request->input('prompt'), $main_content);
if ($classifyResponse === "DATABASE") { if (str_contains($queryResponse, 'tidak relevan') || str_contains($queryResponse, 'tidak valid') || str_starts_with($queryResponse, 'Prompt')) {
$queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content); return response()->json(['response' => $queryResponse], 400);
if (is_array($queryResponse)) {
info('Query Response is an array: ', $queryResponse);
} else {
info('Query Response is a string: ' . $queryResponse);
}
// Validasi query dua kali sebelum eksekusi
if (
$this->openAIService->validateSyntaxQuery($queryResponse) === "VALID" &&
$this->openAIService->validateSyntaxQuery($queryResponse) === "VALID"
) {
info($queryResponse);
$queryResponse = str_replace(['```sql', '```'], '', $queryResponse);
$resultQuery = DB::select($queryResponse);
$formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT);
$nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery);
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
return response()->json(['response' => $finalGeneratedText]);
}
return response()->json(['response' => ''], 400);
} }
if ($classifyResponse === "GENERAL") { $formattedResultQuery = "[]";
$nlpResult = $this->openAIService->generateGeneralText($request->input('prompt'), $main_content); $queryResponse = str_replace(['```sql', '```'], '', $queryResponse);
$resultQuery = DB::select($queryResponse);
$formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT);
info($formattedResultQuery);
$nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery);
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
return response()->json(['response' => $finalGeneratedText, 'nlpResponse' => $queryResponse]);
}
public function mainGenerateText(Request $request)
{
// Log hanya data yang relevan
info("Received prompt: " . $request->input('prompt'));
// Validasi input
$request->validate([
'prompt' => 'required|string',
]);
try {
// Panggil service untuk generate text
$classifyResponse = $this->openAIService->classifyMainGenerateText($request->input('prompt'));
info($classifyResponse);
// Pastikan hasil klasifikasi valid sebelum melanjutkan
$validCategories = [
'reklame', 'business_or_industries', 'customers',
'pbg', 'retribusi', 'spatial_plannings',
'tourisms', 'umkms'
];
if (!in_array($classifyResponse, $validCategories)) {
return response()->json([
'error' => ''
], 400);
}
$chatHistory = $request->input('chatHistory');
Log::info('Chat history sebelum disimpan:', ['history' => $chatHistory]);
$queryResponse = $this->openAIService->createMainQuery($classifyResponse, $request->input('prompt'), $chatHistory);
info($queryResponse);
$formattedResultQuery = "[]";
$queryResponse = str_replace(['```sql', '```'], '', $queryResponse);
$queryResult = DB::select($queryResponse);
$formattedResultQuery = json_encode($queryResult, JSON_PRETTY_PRINT);
$nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery);
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult); $finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
return response()->json(['response' => $finalGeneratedText]);
return response()->json(['response' => $finalGeneratedText, 'nlpResponse' => $queryResponse]);
} catch (\Exception $e) {
// Tangani error dan log exception
\Log::error("Error generating text: " . $e->getMessage());
return response()->json([
'error' => ''
], 500);
} }
return response()->json(['response' => ''], 500);
} }
private function classifyContent(string $prompt) {
$classifyResponse = $this->openAIService->generateClassifyContent($prompt);
return $classifyResponse;
}
} }

View File

@@ -34,7 +34,7 @@ class GlobalSettingsController extends Controller
try { try {
$data = GlobalSetting::create($request->validated()); $data = GlobalSetting::create($request->validated());
return new GlobalSettingResource($data); return new GlobalSettingResource($data);
} catch (\Exception $e) { } catch (Exception $e) {
return $this->resError($e->getMessage(), null, $e->getCode()); return $this->resError($e->getMessage(), null, $e->getCode());
} }
} }

View File

@@ -3,33 +3,15 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Services\GoogleSheetService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class GoogleSheetController extends Controller class GoogleSheetController extends Controller
{ {
protected $googleSheetService; public function index(Request $request)
public function __construct(GoogleSheetService $googleSheetService){
$this->googleSheetService = $googleSheetService;
}
/**
* Display a listing of the resource.
*/
public function index()
{ {
$dataCollection = $this->googleSheetService->getSheetDataCollection();
$result = [
"last_row" => $this->googleSheetService->getLastRowByColumn("C"),
"last_column" => $this->googleSheetService->getLastColumn(),
"header" => $this->googleSheetService->getHeader(),
"data_collection" => $dataCollection
];
return response()->json($result);
} }
/**
* Store a newly created resource in storage.
*/
public function store(Request $request) public function store(Request $request)
{ {
// //

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

@@ -22,7 +22,8 @@ class MenusController extends Controller
$query = $query->where("name", "like", "%".$request->get("search")."%"); $query = $query->where("name", "like", "%".$request->get("search")."%");
} }
return response()->json($query->paginate(config('app.paginate_per_page', 50))); // return response()->json($query->paginate(config('app.paginate_per_page', 50)));
return MenuResource::collection($query->paginate(config('app.paginate_per_page',50)));
} }
/** /**

View File

@@ -0,0 +1,109 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\PbgTaskAttachment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\Response;
class PbgTaskAttachmentsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request, $pbg_task_id)
{
try{
$request->validate([
'file' => 'required|file|mimes:jpg,png,pdf|max:5120',
'pbg_type' => 'string'
]);
$attachment = PbgTaskAttachment::create([
'pbg_task_id' => $pbg_task_id,
'file_name' => $request->file('file')->getClientOriginalName(),
'file_path' => '', // empty path initially
'pbg_type' => $request->pbg_type == 'bukti_bayar' ? 'bukti_bayar' : 'berita_acara'
]);
$file = $request->file('file');
$path = $file->store("uploads/pbg-tasks/{$pbg_task_id}/{$attachment->id}", "public");
$attachment->update([
'file_path' => $path,
]);
return response()->json([
'message' => 'File uploaded successfully.',
'attachment' => [
'id' => $attachment->id,
'file_name' => $attachment->file_name,
'file_url' => Storage::url($attachment->file_path),
'pbg_type' => $attachment->pbg_type
]
]);
}catch(\Exception $e){
\Log::error($e->getMessage());
return response()->json([
"success" => false,
"message" => $e->getTraceAsString()
]);
}
}
/**
* 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)
{
//
}
public function download(string $id)
{
try {
$data = PbgTaskAttachment::findOrFail($id);
$filePath = $data->file_path; // already relative to 'public' disk
if (!Storage::disk('public')->exists($filePath)) {
return response()->json([
"success" => false,
"message" => "File not found on server"
], Response::HTTP_NOT_FOUND);
}
return Storage::disk('public')->download($filePath, $data->file_name);
} catch (\Exception $e) {
return response()->json([
"success" => false,
"message" => $e->getMessage()
]);
}
}
}

View File

@@ -3,6 +3,8 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Enums\ImportDatasourceStatus; use App\Enums\ImportDatasourceStatus;
use App\Enums\PbgTaskApplicationTypes;
use App\Enums\PbgTaskStatus;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\PbgTaskMultiStepRequest; use App\Http\Requests\PbgTaskMultiStepRequest;
use App\Http\Resources\PbgTaskResource; use App\Http\Resources\PbgTaskResource;
@@ -14,6 +16,7 @@ use App\Services\GoogleSheetService;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rules\Enum;
class PbgTaskController extends Controller class PbgTaskController extends Controller
{ {
@@ -116,9 +119,63 @@ class PbgTaskController extends Controller
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*/ */
public function update(Request $request, string $id) public function update(Request $request, string $task_uuid)
{ {
// try{
$pbg_task = PbgTask::where('uuid',$task_uuid)->first();
if(!$pbg_task){
return response()->json([
"success"=> false,
"message"=> "Data PBG Task tidak ditemukan",
], 404);
}
$validated = $request->validate([
'name' => 'required|string|max:255',
'owner_name' => 'required|string|max:255',
'application_type' => ['nullable', new Enum(PbgTaskApplicationTypes::class)],
'condition' => 'required|string|max:255',
'registration_number' => 'required|string|max:255',
'document_number' => 'required|string|max:255',
'status' => ['nullable', new Enum(PbgTaskStatus::class)],
'address' => 'required|string|max:255',
'slf_status_name' => 'nullable|string|max:255',
'function_type' => 'required|string|max:255',
'consultation_type' => 'required|string|max:255',
'due_date' => 'nullable|date|after_or_equal:today',
]);
$statusLabel = $validated['status'] !== null ? PbgTaskStatus::getLabel($validated['status']) : null;
$applicationLabel = $validated['application_type'] !== null ? PbgTaskApplicationTypes::getLabel($validated['application_type']) : null;
$pbg_task->update([
'name' => $validated['name'],
'owner_name' => $validated['owner_name'],
'application_type' => $validated['application_type'],
'application_type_name' => $applicationLabel, // Automatically set application_type_name
'condition' => $validated['condition'],
'registration_number' => $validated['registration_number'],
'document_number' => $validated['document_number'],
'status' => $validated['status'],
'status_name' => $statusLabel, // Automatically set status_name
'address' => $validated['address'],
'slf_status_name' => $validated['slf_status_name'],
'function_type' => $validated['function_type'],
'consultation_type' => $validated['consultation_type'],
'due_date' => $validated['due_date'],
]);
return response()->json([
"success"=> true,
"message"=> "Data berhasil diubah",
"data"=> $pbg_task
]);
}catch(\Exception $e){
return response()->json([
"success"=> false,
"message"=> $e->getMessage(),
]);
}
} }
/** /**
@@ -135,255 +192,4 @@ class PbgTaskController extends Controller
]); ]);
} }
public function syncPbgFromGoogleSheet(){
$import_datasource = ImportDatasource::create([
"message" => "initialization",
"response_body" => null,
"status" => ImportDatasourceStatus::Processing->value,
]);
try{
$totalRowCount = $this->googleSheetService->getLastRowByColumn("C");
$sheetData = $this->googleSheetService->getSheetDataCollection($totalRowCount);
$sheet_big_data = $this->googleSheetService->get_data_by_sheet();
$data_setting_result = []; // Initialize result storage
$found_section = null; // Track which section is found
foreach ($sheet_big_data as $row) {
// Check for section headers
if (in_array("•PROSES PENERBITAN:", $row)) {
$found_section = "MENUNGGU_KLIK_DPMPTSP";
} elseif (in_array("•BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2024:", $row)) {
$found_section = "REALISASI_TERBIT_PBG";
} elseif (in_array("•TERPROSES DI DPUTR: belum selesai rekomtek'", $row)) {
$found_section = "PROSES_DINAS_TEKNIS";
}
// If a section is found and we reach "Grand Total", save the corresponding values
if ($found_section && isset($row[0]) && trim($row[0]) === "Grand Total") {
if ($found_section === "MENUNGGU_KLIK_DPMPTSP") {
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_COUNT"] = $row[2] ?? null;
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $row[3] ?? null;
} elseif ($found_section === "REALISASI_TERBIT_PBG") {
$data_setting_result["REALISASI_TERBIT_PBG_COUNT"] = $row[2] ?? null;
$data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $row[4] ?? null;
} elseif ($found_section === "PROSES_DINAS_TEKNIS") {
$data_setting_result["PROSES_DINAS_TEKNIS_COUNT"] = $row[2] ?? null;
$data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $row[3] ?? null;
}
// Reset section tracking after capturing "Grand Total"
$found_section = null;
}
}
foreach ($data_setting_result as $key => $value) {
DataSetting::updateOrInsert(
["key" => $key], // Find by key
["value" => $value] // Update or insert value
);
}
$mapToUpsert = [];
$count = 0;
foreach($sheetData as $data){
$mapToUpsert[] =
[
'no_registrasi' => $data['no__registrasi'] ?? null,
'jenis_konsultasi' => $data['jenis_konsultasi'] ?? null,
'fungsi_bg' => $data['fungsi_bg'] ?? null,
'tgl_permohonan' => $this->convertToDate($data['tgl_permohonan']),
'status_verifikasi' => $data['status_verifikasi'] ?? null,
'status_permohonan' => $this->convertToDate($data['status_permohonan']),
'alamat_pemilik' => $data['alamat_pemilik'] ?? null,
'no_hp' => $data['no__hp'] ?? null,
'email' => $data['e_mail'] ?? null,
'tanggal_catatan' => $this->convertToDate($data['tanggal_catatan']),
'catatan_kekurangan_dokumen' => $data['catatan_kekurangan_dokumen'] ?? null,
'gambar' => $data['gambar'] ?? null,
'krk_kkpr' => $data['krk_kkpr'] ?? null,
'no_krk' => $data['no__krk'] ?? null,
'lh' => $data['lh'] ?? null,
'ska' => $data['ska'] ?? null,
'keterangan' => $data['keterangan'] ?? null,
'helpdesk' => $data['helpdesk'] ?? null,
'pj' => $data['pj'] ?? null,
'kepemilikan' => $data['kepemilikan'] ?? null,
'potensi_taru' => $data['potensi_taru'] ?? null,
'validasi_dinas' => $data['validasi_dinas'] ?? null,
'kategori_retribusi' => $data['kategori_retribusi'] ?? null,
'no_urut_ba_tpt' => $data['no__urut_ba_tpt__2024_0001_'] ?? null,
'tanggal_ba_tpt' => $this->convertToDate($data['tanggal_ba_tpt']),
'no_urut_ba_tpa' => $data['no__urut_ba_tpa'] ?? null,
'tanggal_ba_tpa' => $this->convertToDate($data['tanggal_ba_tpa']),
'no_urut_skrd' => $data['no__urut_skrd__2024_0001_'] ?? null,
'tanggal_skrd' => $this->convertToDate($data['tanggal_skrd']),
'ptsp' => $data['ptsp'] ?? null,
'selesai_terbit' => $data['selesai_terbit'] ?? null,
'tanggal_pembayaran' => $this->convertToDate($data['tanggal_pembayaran__yyyy_mm_dd_']),
'format_sts' => $data['format_sts'] ?? null,
'tahun_terbit' => (int) $data['tahun_terbit'] ?? null,
'tahun_berjalan' => (int) $data['tahun_berjalan'] ?? null,
'kelurahan' => $data['kelurahan'] ?? null,
'kecamatan' => $data['kecamatan'] ?? null,
'lb' => $this->convertToDecimal($data['lb']) ?? null,
'tb' => $this->convertToDecimal($data['tb']) ?? null,
'jlb' => (int) $data['jlb'] ?? null,
'unit' => (int) $data['unit'] ?? null,
'usulan_retribusi' => (int) $data['usulan_retribusi'] ?? null,
'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__simbg_']) ?? null,
'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__pad_']) ?? null,
'denda' => $this->convertToDecimal($data['denda']) ?? null,
'latitude' => $data['latitude'] ?? null,
'longitude' => $data['longitude'] ?? null,
'nik_nib' => $data['nik_nib'] ?? null,
'dok_tanah' => $data['dok__tanah'] ?? null,
'temuan' => $data['temuan'] ?? null,
];
}
DB::beginTransaction();
$batchSize = 1000;
$chunks = array_chunk($mapToUpsert, $batchSize);
foreach($chunks as $chunk){
PbgTaskGoogleSheet::upsert($chunk, ["no_registrasi"],[
'jenis_konsultasi',
'nama_pemilik',
'lokasi_bg',
'fungsi_bg',
'nama_bangunan',
'tgl_permohonan',
'status_verifikasi',
'status_permohonan',
'alamat_pemilik',
'no_hp',
'email',
'tanggal_catatan',
'catatan_kekurangan_dokumen',
'gambar',
'krk_kkpr',
'no_krk',
'lh',
'ska',
'keterangan',
'helpdesk',
'pj',
'kepemilikan',
'potensi_taru',
'validasi_dinas',
'kategori_retribusi',
'no_urut_ba_tpt',
'tanggal_ba_tpt',
'no_urut_ba_tpa',
'tanggal_ba_tpa',
'no_urut_skrd',
'tanggal_skrd',
'ptsp',
'selesai_terbit',
'tanggal_pembayaran',
'format_sts',
'tahun_terbit',
'tahun_berjalan',
'kelurahan',
'kecamatan',
'lb',
'tb',
'jlb',
'unit',
'usulan_retribusi',
'nilai_retribusi_keseluruhan_simbg',
'nilai_retribusi_keseluruhan_pad',
'denda',
'latitude',
'longitude',
'nik_nib',
'dok_tanah',
'temuan',
]);
}
$total_data = count($mapToUpsert);
$import_datasource->update([
"message" => "Successfully processed: {$total_data}",
"status" => ImportDatasourceStatus::Success->value,
]);
DB::commit();
return response()->json([
"success" => true,
"message" => "Data berhasil disimpan ke database"
], 200);
}catch(\Exception $ex){
DB::rollBack();
$import_datasource->update([
"message" => "Failed to importing",
"response_body" => $ex->getMessage(),
"status" => ImportDatasourceStatus::Failed->value,
]);
return response()->json([
"success" => false,
"message" => "Gagal menyimpan data",
"error" => $ex->getMessage()
], 500);
}
}
protected function convertToDecimal(?string $value): ?float
{
if (empty($value)) {
return null; // Return null if the input is empty
}
// Remove all non-numeric characters except comma and dot
$value = preg_replace('/[^0-9,\.]/', '', $value);
// If the number contains both dot (.) and comma (,)
if (strpos($value, '.') !== false && strpos($value, ',') !== false) {
$value = str_replace('.', '', $value); // Remove thousands separator
$value = str_replace(',', '.', $value); // Convert decimal separator to dot
}
// If only a dot is present (assumed as thousands separator)
elseif (strpos($value, '.') !== false) {
$value = str_replace('.', '', $value); // Remove all dots (treat as thousands separators)
}
// If only a comma is present (assumed as decimal separator)
elseif (strpos($value, ',') !== false) {
$value = str_replace(',', '.', $value); // Convert comma to dot (decimal separator)
}
// Ensure the value is numeric before returning
return is_numeric($value) ? (float) number_format((float) $value, 2, '.', '') : null;
}
protected function convertToInteger($value) {
// Check if the value is an empty string, and return null if true
if (trim($value) === "") {
return null;
}
// Otherwise, cast to integer
return (int) $value;
}
protected function convertToDate($dateString)
{
try {
// Check if the string is empty
if (empty($dateString)) {
return null;
}
// Try to parse the date string
$date = Carbon::parse($dateString);
// Return the Carbon instance
return $date->format('Y-m-d');
} catch (\Exception $e) {
// Return null if an error occurs during parsing
return null;
}
}
} }

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\PbgTaskGoogleSheetResource;
use App\Models\PbgTaskGoogleSheet;
use Illuminate\Http\Request;
class PbgTaskGoogleSheetsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$query = PbgTaskGoogleSheet::query()->orderBy('id', 'desc');
if ($request->filled('search')) {
$query->where('no_registrasi', 'like', "%{$request->get('search')}%");
}
return PbgTaskGoogleSheetResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}
/**
* 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)
{
try{
$data = PbgTaskGoogleSheet::find($id);
$data->delete();
return response()->json(['message' => 'Data deleted successfully'], 200);
}catch(\Exception $e){
return response()->json(['message' => 'Failed to delete data'], 500);
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Api;
use App\Exports\ReportPbgPtspExport;
use App\Http\Controllers\Controller;
use App\Models\PbgTask;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;
class ReportPbgPtspController extends Controller
{
public function export_excel(){
return Excel::download(new ReportPbgPtspExport, 'laporan-ptsp.xlsx');
}
public function export_pdf(){
$data = PbgTask::select(
'status',
'status_name', // Keeping this column
DB::raw('COUNT(*) as total')
)
->groupBy('status', 'status_name')
->get();
$pdf = Pdf::loadView('exports.ptsp_report', compact('data'));
return $pdf->download('laporan-ptsp.pdf');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Api;
use App\Exports\ReportTourismExport;
use App\Http\Controllers\Controller;
use App\Models\TourismBasedKBLI;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
class ReportTourismsController extends Controller
{
public function export_excel(){
return Excel::download(new ReportTourismExport, 'laporan-pariwisata.xlsx');
}
public function export_pdf(){
$data = TourismBasedKBLI::all();
$pdf = Pdf::loadView('exports.tourisms_report', compact('data'));
return $pdf->download('laporan-pariwisata.pdf');
}
}

View File

@@ -2,10 +2,17 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Exports\DistrictPaymentRecapExport;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Resources\RequestAssignmentResouce; use App\Http\Resources\RequestAssignmentResouce;
use App\Models\PbgTask; use App\Models\PbgTask;
use App\Models\PbgTaskGoogleSheet;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\DB;
use Exception;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Log;
use Maatwebsite\Excel\Facades\Excel;
class RequestAssignmentController extends Controller class RequestAssignmentController extends Controller
{ {
@@ -14,15 +21,112 @@ class RequestAssignmentController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$query = PbgTask::query()->orderBy('id', 'desc'); $query = PbgTask::with([
if($request->has('search') && !empty($request->get("search"))){ 'attachments' => function ($q) {
$query->where('name', 'LIKE', '%'.$request->get('search').'%') $q->whereIn('pbg_type', ['berita_acara', 'bukti_bayar']);
->orWhere('registration_number', 'LIKE', '%'.$request->get('search').'%') },
->orWhere('document_number', 'LIKE', '%'.$request->get('search').'%'); '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"))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%")
->orWhere('registration_number', 'LIKE', "%$search%")
->orWhere('document_number', 'LIKE', "%$search%");
});
}
return RequestAssignmentResouce::collection($query->paginate()); return RequestAssignmentResouce::collection($query->paginate());
} }
public function report_payment_recaps(Request $request)
{
try {
// Query dengan group by kecamatan dan sum nilai_retribusi_keseluruhan_simbg
$query = PbgTaskGoogleSheet::select(
'kecamatan',
DB::raw('SUM(nilai_retribusi_keseluruhan_simbg) as total')
)
->groupBy('kecamatan')
->paginate(10);
// Return hasil dalam JSON format
return response()->json([
'success' => true,
'data' => $query
]);
} catch (Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Terjadi kesalahan: ' . $e->getMessage()], 500);
}
}
public function export_excel_district_payment_recaps(){
return Excel::download(new DistrictPaymentRecapExport, 'laporan-rekap-data-pembayaran.xlsx');
}
public function export_pdf_district_payment_recaps(){
$data = PbgTaskGoogleSheet::select(
'kecamatan',
DB::raw('SUM(nilai_retribusi_keseluruhan_simbg) as total')
)
->groupBy('kecamatan')->get();
$pdf = Pdf::loadView('exports.district_payment_report', compact('data'));
return $pdf->download('laporan-rekap-data-pembayaran.pdf');
}
public function report_pbg_ptsp()
{
try {
// Query dengan group by status dan count total per status
$query = PbgTask::select(
'status',
'status_name',
DB::raw('COUNT(*) as total')
)
->groupBy('status', 'status_name')
->paginate(10);
// Return hasil dalam JSON format
return response()->json([
'success' => true,
'data' => $query
]);
} catch (Exception $e) {
Log::error($e->getMessage());
return response()->json(['message' => 'Terjadi kesalahan: ' . $e->getMessage()], 500);
}
}
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*/ */

View File

@@ -4,9 +4,16 @@ namespace App\Http\Controllers\Api;
use App\Enums\ImportDatasourceStatus; use App\Enums\ImportDatasourceStatus;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Jobs\RetrySyncronizeJob;
use App\Jobs\ScrapingDataJob;
use App\Jobs\SyncronizeSIMBG; use App\Jobs\SyncronizeSIMBG;
use App\Models\ImportDatasource; use App\Models\ImportDatasource;
use App\Traits\GlobalApiResponse; use App\Traits\GlobalApiResponse;
use App\Services\ServiceTokenSIMBG;
use GuzzleHttp\Client;
use App\Services\ServiceGoogleSheet;
use App\Services\ServicePbgTask;
use App\Services\ServiceTabPbgTask;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -23,40 +30,33 @@ class ScrapingController extends Controller
return $this->resError("Failed to execute while processing another scraping"); return $this->resError("Failed to execute while processing another scraping");
} }
// run service artisan command // use ole schema synchronization
SyncronizeSIMBG::dispatch(); // dispatch(new SyncronizeSIMBG());
// use new schema synchronization
dispatch(new ScrapingDataJob());
return $this->resSuccess(["message" => "Success execute scraping service on background, check status for more"]); return $this->resSuccess(["message" => "Success execute scraping service on background, check status for more"]);
} }
/** public function retry_syncjob(string $import_datasource_id){
* Store a newly created resource in storage. try{
*/
public function store(Request $request)
{
//
}
/** $import_datasource = ImportDatasource::find($import_datasource_id);
* Display the specified resource. if(!$import_datasource){
*/ return $this->resError("Invalid import datasource id", null, 404);
public function show(string $id) }
{
//
}
/** dispatch(new RetrySyncronizeJob($import_datasource->id));
* Update the specified resource in storage. return response()->json([
*/ "success" => true,
public function update(Request $request, string $id) "message" => "Retrying scrape job on background, check status for more"
{ ]);
// }catch(\Exception $e){
} return response()->json([
"success" => false,
/** "message" => "Failed to retry sync job",
* Remove the specified resource from storage. "error" => $e->getMessage()
*/ ]);
public function destroy(string $id) }
{
//
} }
} }

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\TaskAssignmentsResource;
use App\Models\TaskAssignment;
use Illuminate\Http\Request;
class TaskAssignmentsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request, $uuid)
{
try{
$query = TaskAssignment::query()
->where('pbg_task_uid', $uuid)
->orderBy('id', 'desc');
if ($request->filled('search')) {
$query->where('name', 'like', "%{$request->get('search')}%")
->orWhere('email', 'like', "%{$request->get('search')}%");
}
return TaskAssignmentsResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}catch(\Exception $exception){
return response()->json(['message' => $exception->getMessage()], 500);
}
}
/**
* 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

@@ -39,7 +39,7 @@ class TourismController extends Controller
$tourisms->village_name = $village ? $village->village_name : null; $tourisms->village_name = $village ? $village->village_name : null;
$district = DB::table('districts')->where('district_code', $tourisms->district_code)->first(); $district = DB::table('districts')->where('district_code', $tourisms->district_code)->first();
$tourisms->district_name = $village ? $village->village_name : null; $tourisms->district_name = $district ? $district->district_name : null;
return $tourisms; return $tourisms;
}); });

View File

@@ -11,6 +11,7 @@ use App\Traits\GlobalApiResponse;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class UsersController extends Controller class UsersController extends Controller
{ {
@@ -29,7 +30,8 @@ class UsersController extends Controller
public function index(Request $request){ public function index(Request $request){
$query = User::query(); $query = User::query();
if($request->has('search') && !empty($request->get("search"))){ if($request->has('search') && !empty($request->get("search"))){
$query->where('name', 'LIKE', '%'.$request->get('search').'%'); $query->where('name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('email', 'LIKE', '%'.$request->get('search').'%');
} }
return UserResource::collection($query->paginate(config('app.paginate_per_page', 50))); return UserResource::collection($query->paginate(config('app.paginate_per_page', 50)));
} }
@@ -83,4 +85,17 @@ class UsersController extends Controller
return response()->json(['message' => $e->getMessage()],500); return response()->json(['message' => $e->getMessage()],500);
} }
} }
public function destroy($id){
try{
$user = User::findOrFail($id);
DB::beginTransaction();
$user->delete();
DB::commit();
return response()->json(['message' => 'Successfully deleted'], 200);
}catch(\Exception $e){
Log::error('Failed to delete user: '. $e->getMessage());
return response()->json(['message' => 'Failed to delete user'],500);
}
}
} }

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Approval;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ApprovalController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('approval.index');
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
}
}

View File

@@ -36,15 +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();
// Simpan token di session (bisa digunakan di JavaScript) // 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

View File

@@ -4,63 +4,40 @@ namespace App\Http\Controllers;
use App\Models\BusinessOrIndustry; use App\Models\BusinessOrIndustry;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class BusinessOrIndustriesController extends Controller class BusinessOrIndustriesController extends Controller
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(Request $request)
{ {
return view('business-industries.index'); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('business-industries.index', compact('creator', 'updater', 'destroyer','menuId'));
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function create() public function create(Request $request)
{ {
return view("business-industries.create"); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
} return view("business-industries.create", compact('menuId'));
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*/ */
public function edit(string $id) public function edit(string $id, Request $request)
{ {
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$data = BusinessOrIndustry::findOrFail($id); $data = BusinessOrIndustry::findOrFail($id);
return view('business-industries.edit', compact('data')); return view('business-industries.edit', compact('data', 'menuId'));
}
/**
* 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

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Controllers\ChatbotPimpinan;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ChatbotPimpinanController extends Controller
{
/**
* Display a listing of the resource
*/
public function index()
{
return view('chatbot-pimpinan.index');
}
}

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

@@ -2,7 +2,54 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
abstract class Controller abstract class Controller
{ {
// protected array $permissions = [];
public function __construct()
{
if (!Auth::check()) {
return;
}
$this->setUserPermissions();
}
protected function setUserPermissions()
{
$user = Auth::user();
if (!$user) {
return;
}
$menus = $user->roles()
->with(['menus' => function ($query) {
$query->select('menus.id', 'menus.name')
->withPivot(['allow_show' ,'allow_create', 'allow_update', 'allow_destroy']);
}])
->get()
->pluck('menus')
->flatten()
->unique('id');
// Store permissions in an associative array
foreach ($menus as $menu) {
$this->permissions[$menu->id] = [
'allow_show' => $menu->pivot->allow_show ?? 0,
'allow_create' => $menu->pivot->allow_create ?? 0,
'allow_update' => $menu->pivot->allow_update ?? 0,
'allow_destroy' => $menu->pivot->allow_destroy ?? 0,
];
}
// Share permissions globally in views
view()->share('permissions', $this->permissions);
}
public function getPermissions()
{
return $this->permissions;
}
} }

View File

@@ -4,23 +4,34 @@ namespace App\Http\Controllers;
use App\Models\Customer; use App\Models\Customer;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class CustomersController extends Controller class CustomersController extends Controller
{ {
public function index() public function index(Request $request)
{ {
return view('customers.index'); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('customers.index', compact('creator', 'updater', 'destroyer', 'menuId'));
} }
public function create() public function create(Request $request)
{ {
return view('customers.create'); $menuId = $request->query('menu_id');
return view('customers.create', compact('menuId'));
} }
public function edit(string $id) public function edit(Request $request, string $id)
{ {
$data = Customer::findOrFail($id); $data = Customer::findOrFail($id);
return view('customers.edit', compact('data')); $menuId = $request->query('menu_id');
return view('customers.edit', compact('data', 'menuId'));
} }
public function upload(){ public function upload(Request $request){
return view('customers.upload'); $menuId = $request->query('menu_id');
return view('customers.upload', compact('menuId'));
} }
} }

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Dashboards;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\ImportDatasource; use App\Models\ImportDatasource;
use App\Models\Menu;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class BigDataController extends Controller class BigDataController extends Controller
@@ -12,11 +13,21 @@ class BigDataController extends Controller
$latest_import_datasource = ImportDatasource::latest()->first(); $latest_import_datasource = ImportDatasource::latest()->first();
$latest_created = $latest_import_datasource ? $latest_created = $latest_import_datasource ?
$latest_import_datasource->created_at->format("j F Y H:i:s") : null; $latest_import_datasource->created_at->format("j F Y H:i:s") : null;
return view('dashboards.bigdata', compact('latest_created')); $menus = Menu::all();
return view('dashboards.bigdata', compact('latest_created', 'menus'));
} }
public function pbg() public function pbg()
{ {
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

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

View File

@@ -6,15 +6,23 @@ use App\Http\Controllers\Controller;
use App\Models\Advertisement; use App\Models\Advertisement;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class AdvertisementController extends Controller class AdvertisementController extends Controller
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(Request $request)
{ {
return view('data.advertisements.index'); $menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('data.advertisements.index', compact('creator', 'updater', 'destroyer','menuId'));
} }
/** /**
@@ -29,8 +37,9 @@ class AdvertisementController extends Controller
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function create() public function create(Request $request)
{ {
$menuId = $request->query('menu_id', 0);
$title = 'Advertisement'; $title = 'Advertisement';
$subtitle = 'Create Data'; $subtitle = 'Create Data';
@@ -47,14 +56,15 @@ class AdvertisementController extends Controller
// $route = 'advertisements.create'; // $route = 'advertisements.create';
// info("AdvertisementController@edit diakses dengan ID: $title"); // info("AdvertisementController@edit diakses dengan ID: $title");
return view('data.advertisements.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.advertisements.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*/ */
public function edit($id) public function edit(Request $request, $id)
{ {
$menuId = $request->query('menu_id', 0);
info("AdvertisementController@edit diakses dengan ID: $id"); info("AdvertisementController@edit diakses dengan ID: $id");
$title = 'Advertisement'; $title = 'Advertisement';
$subtitle = 'Update Data'; $subtitle = 'Update Data';
@@ -62,7 +72,7 @@ class AdvertisementController extends Controller
// Pastikan model ditemukan // Pastikan model ditemukan
if (!$modelInstance) { if (!$modelInstance) {
info("AdvertisementController@edit: Model tidak ditemukan."); info("AdvertisementController@edit: Model tidak ditemukan.");
return redirect()->route('advertisements.index')->with('error', 'Advertisement not found'); return redirect()->route('web.advertisements.index')->with('error', 'Advertisement not found');
} }
// Mengambil dan memetakan village_name dan district_name // Mengambil dan memetakan village_name dan district_name
@@ -86,7 +96,7 @@ class AdvertisementController extends Controller
// $route = 'advertisements.update'; // Menggunakan route update untuk form edit // $route = 'advertisements.update'; // Menggunakan route update untuk form edit
// info("AdvertisementController@edit diakses dengan route: $route"); // info("AdvertisementController@edit diakses dengan route: $route");
return view('data.advertisements.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.advertisements.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions', 'menuId'));
} }
private function getFields() private function getFields()

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Http\Controllers\Data;
use App\Http\Controllers\Controller;
use App\Models\PbgTaskGoogleSheet;
use Illuminate\Http\Request;
class GoogleSheetsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$menu_id = $request->query('menu_id');
$user_menu_permission = $this->permissions[$menu_id];
return view('data.google-sheet.index', compact('user_menu_permission'));
}
public function create()
{
return view('data.google-sheet.create');
}
public function show(string $id)
{
$data = PbgTaskGoogleSheet::find($id);
return view('data.google-sheet.show', compact('data'));
}
public function edit(string $id)
{
return view('data.google-sheet.edit');
}
}

View File

@@ -11,24 +11,33 @@ class SpatialPlanningController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(Request $request)
{ {
return view('data.spatialPlannings.index'); $menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('data.spatialPlannings.index', compact('creator', 'updater', 'destroyer','menuId'));
} }
/** /**
* show the form for creating a new resource. * show the form for creating a new resource.
*/ */
public function bulkCreate() public function bulkCreate(Request $request)
{ {
return view('data.spatialPlannings.form-upload'); $menuId = $request->query('menu_id', 0);
return view('data.spatialPlannings.form-upload', compact('menuId'));
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function create() public function create(Request $request)
{ {
$menuId = $request->query('menu_id', 0);
$title = 'Rencana Tata Ruang'; $title = 'Rencana Tata Ruang';
$subtitle = "Create Data"; $subtitle = "Create Data";
@@ -39,30 +48,15 @@ class SpatialPlanningController extends Controller
$fieldTypes = $this->getFieldTypes(); $fieldTypes = $this->getFieldTypes();
$apiUrl = url('/api/spatial-plannings'); $apiUrl = url('/api/spatial-plannings');
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.spatialPlannings.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*/ */
public function edit(string $id) public function edit(Request $request,string $id)
{ {
$menuId = $request->query('menu_id', 0);
$title = 'Rencana Tata Ruang'; $title = 'Rencana Tata Ruang';
$subtitle = 'Update Data'; $subtitle = 'Update Data';
@@ -78,23 +72,7 @@ class SpatialPlanningController extends Controller
$fieldTypes = $this->getFieldTypes(); $fieldTypes = $this->getFieldTypes();
$apiUrl = url('/api/spatial-plannings'); $apiUrl = url('/api/spatial-plannings');
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.spatialPlannings.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
} }
private function getFields() private function getFields()

View File

@@ -6,30 +6,39 @@ use App\Http\Controllers\Controller;
use App\Models\Tourism; use App\Models\Tourism;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class TourismController extends Controller class TourismController extends Controller
{ {
/** /**
* Display a listing of the resource * Display a listing of the resource
*/ */
public function index() public function index(Request $request)
{ {
return view('data.tourisms.index'); $menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('data.tourisms.index', compact('creator', 'updater', 'destroyer', 'menuId'));
} }
/** /**
* show the form for creating a new rsource. * show the form for creating a new rsource.
*/ */
public function bulkCreate() public function bulkCreate(Request $request)
{ {
return view('data.tourisms.form-upload'); $menuId = $request->query('menu_id', 0);
return view('data.tourisms.form-upload', compact('menuId'));
} }
/** /**
* Show th form for creating a new resource * Show th form for creating a new resource
*/ */
public function create() public function create(Request $request)
{ {
$menuId = $request->query('menu_id', 0);
$title = 'Pariwisata'; $title = 'Pariwisata';
$subtitle = 'Create Data'; $subtitle = 'Create Data';
@@ -44,21 +53,22 @@ class TourismController extends Controller
$apiUrl = url('/api/tourisms'); $apiUrl = url('/api/tourisms');
return view('data.tourisms.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.tourisms.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions', 'menuId'));
} }
/** /**
* show the form for editing the specified resource. * show the form for editing the specified resource.
*/ */
public function edit($id) public function edit(Request $request, $id)
{ {
$menuId = $request->query('menu_id', 0);
$title = 'Pariwisata'; $title = 'Pariwisata';
$subtitle = 'Update Data'; $subtitle = 'Update Data';
$modelInstance = Tourism::find($id); $modelInstance = Tourism::find($id);
// Pastikan model ditemukan // Pastikan model ditemukan
if (!$modelInstance) { if (!$modelInstance) {
return redirect()->route('tourisms.index') ->with('error', 'Pariwisata tidak ditemukan'); return redirect()->route('web-tourisms.index') ->with('error', 'Pariwisata tidak ditemukan');
} }
// Mengambil dan memetakan village_name dan district_name // Mengambil dan memetakan village_name dan district_name
@@ -78,7 +88,7 @@ class TourismController extends Controller
$apiUrl = url('/api/tourisms'); $apiUrl = url('/api/tourisms');
return view('data.tourisms.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.tourisms.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions', 'menuId'));
} }
private function getFields() private function getFields()

View File

@@ -6,30 +6,39 @@ use App\Http\Controllers\Controller;
use App\Models\Umkm; use App\Models\Umkm;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class UmkmController extends Controller class UmkmController extends Controller
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(Request $request)
{ {
return view('data.umkm.index'); $menuId = $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('data.umkm.index', compact('creator', 'updater', 'destroyer', 'menuId'));
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function bulkCreate() public function bulkCreate(Request $request)
{ {
return view('data.umkm.form-upload'); $menuId = $request->query('menu_id', 0);
return view('data.umkm.form-upload', compact('menuId'));
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function create() public function create(Request $request)
{ {
$menuId = $request->query('menu_id', 0);
$title = 'UMKM'; $title = 'UMKM';
$subtitle = 'Create Data'; $subtitle = 'Create Data';
@@ -47,20 +56,21 @@ class UmkmController extends Controller
$apiUrl = url('/api/umkm'); $apiUrl = url('/api/umkm');
return view('data.umkm.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.umkm.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*/ */
public function edit($id) public function edit(Request $request,$id)
{ {
$menuId = $request->query('menu_id', 0);
$title = 'UMKM'; $title = 'UMKM';
$subtitle = 'Update Data'; $subtitle = 'Update Data';
$modelInstance = Umkm::find($id); $modelInstance = Umkm::find($id);
// Pastikan model ditemukan // Pastikan model ditemukan
if (!$modelInstance) { if (!$modelInstance) {
return redirect()->route('umkm.index')->with('error', 'Umkm not found'); return redirect()->route('web-umkm.index')->with('error', 'Umkm not found');
} }
// Mengambil dan memetakan village_name dan district_name // Mengambil dan memetakan village_name dan district_name
@@ -96,7 +106,7 @@ class UmkmController extends Controller
$apiUrl = url('/api/umkm'); $apiUrl = url('/api/umkm');
// dd($modelInstance->business_form_id, $dropdownOptions['business_form']); // dd($modelInstance->business_form_id, $dropdownOptions['business_form']);
return view('data.umkm.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions')); return view('data.umkm.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions','menuId'));
} }
private function getFields() private function getFields()

View File

@@ -8,23 +8,31 @@ use Exception;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request as IndexRequest;
class DataSettingController extends Controller class DataSettingController extends Controller
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(IndexRequest $request)
{ {
return view("data-settings.index"); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view("data-settings.index", compact('creator', 'updater', 'destroyer','menuId'));
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function create() public function create(IndexRequest $request)
{ {
return view("data-settings.create"); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
return view("data-settings.create", compact('menuId'));
} }
/** /**
@@ -57,14 +65,15 @@ class DataSettingController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*/ */
public function edit(string $id) public function edit(IndexRequest $request,string $id)
{ {
try{ try{
$data = DataSetting::findOrFail($id); $data = DataSetting::findOrFail($id);
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
if(empty($data)){ if(empty($data)){
return redirect()->route('data-settings.index')->with('error', 'Invalid id'); return redirect()->route('data-settings.index')->with('error', 'Invalid id');
} }
return view("data-settings.edit", compact("data")); return view("data-settings.edit", compact("data", 'menuId'));
}catch(Exception $ex){ }catch(Exception $ex){
return redirect()->route("data-settings.index")->with("error", "Invalid id"); return redirect()->route("data-settings.index")->with("error", "Invalid id");
} }

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class InvitationsController extends Controller
{
public function index(Request $request){
return view('invitations.index');
}
}

View File

@@ -13,6 +13,7 @@ use Illuminate\Support\Facades\Hash;
use App\Models\User; use App\Models\User;
use App\Traits\GlobalApiResponse; use App\Traits\GlobalApiResponse;
use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
class UsersController extends Controller class UsersController extends Controller
{ {
@@ -21,13 +22,20 @@ class UsersController extends Controller
$users = User::all(); $users = User::all();
return $this->resSuccess($users); return $this->resSuccess($users);
} }
public function index(){ public function index(Request $request){
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
$users = User::paginate(); $users = User::paginate();
return view('master.users.index', compact('users')); return view('master.users.index', compact('users', 'creator', 'updater', 'destroyer','menuId'));
} }
public function create(){ public function create(Request $request){
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$roles = Role::all(); $roles = Role::all();
return view('master.users.create', compact('roles')); return view('master.users.create', compact('roles', 'menuId'));
} }
public function store(UsersRequest $request){ public function store(UsersRequest $request){
$request->validate([ $request->validate([
@@ -65,10 +73,11 @@ class UsersController extends Controller
$user = User::find($id); $user = User::find($id);
return view('master.users.show', compact('user')); return view('master.users.show', compact('user'));
} }
public function edit($id){ public function edit(Request $request, $id){
$menuId = $request->query('menu_id') ?? $request->input('menu_id');
$user = User::find($id); $user = User::find($id);
$roles = Role::all(); $roles = Role::all();
return view('master.users.edit', compact('user', 'roles')); return view('master.users.edit', compact('user', 'roles', 'menuId'));
} }
public function update(Request $request, $id){ public function update(Request $request, $id){
$user = User::find($id); $user = User::find($id);

View File

@@ -12,18 +12,35 @@ class MenusController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(Request $request)
{ {
return view('menus.index'); $menuId = (int) $request->query('menu_id', 0);
$permissions = $this->permissions[$menuId] ?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('menus.index', compact('creator', 'updater', 'destroyer', 'menuId'));
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function create() public function create(Request $request)
{ {
$parent_menus = Menu::whereNull('parent_id')->get(); $menuId = $request->query('menu_id'); // Get menu_id from request
return view("menus.create", compact('parent_menus')); $menu = Menu::with('children')->find($menuId); // Find the menu
// Get IDs of all child menus to exclude
$excludedIds = $menu ? $this->getChildMenuIds($menu) : [$menuId];
// Fetch only menus that have children and are not in the excluded list
$parent_menus = Menu::whereHas('children')
->whereNotIn('id', $excludedIds)
->get();
return view("menus.create", compact('parent_menus', 'menuId'));
} }
/** /**
@@ -56,11 +73,16 @@ class MenusController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*/ */
public function edit(string $id) public function edit(string $id, Request $request)
{ {
$menu = Menu::findOrFail($id); $menuId = $request->query('menu_id');
$parent_menus = Menu::whereNull('parent_id')->where('id','!=',$id)->get(); $menu = Menu::with('children')->find($id);
return view("menus.edit", compact('menu','parent_menus')); $excludedIds = $menu ? $this->getChildMenuIds($menu) : [$id];
$parent_menus = Menu::whereHas('children')
->whereNotIn('id', $excludedIds)
->get();
return view("menus.edit", compact('menu','parent_menus', 'menuId'));
} }
/** /**
@@ -110,4 +132,15 @@ class MenusController extends Controller
$child->delete(); $child->delete();
} }
} }
private function getChildMenuIds($menu)
{
$ids = [$menu->id]; // Start with current menu ID
foreach ($menu->children as $child) {
$ids = array_merge($ids, $this->getChildMenuIds($child)); // Recursively fetch children
}
return $ids;
}
} }

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PaymentRecapsController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('payment-recaps.index');
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers;
use App\Models\PbgTask;
use App\Models\PbgTaskAttachment;
use Illuminate\Http\Request;
class PbgTaskAttachmentsController extends Controller
{
public function show(string $id, Request $request){
try{
$title = $request->get('type') == "berita-acara" ? "Berita Acara" : "Bukti Bayar";
$data = PbgTaskAttachment::findOrFail($id);
$pbg = PbgTask::findOrFail($data->pbg_task_id);
return view('pbg-task-attachment.show', compact('data', 'pbg', 'title'));
}catch(\Exception $e){
return view('pages.404');
}
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Http\Controllers;
use App\Enums\PbgTaskApplicationTypes;
use App\Enums\PbgTaskStatus;
use App\Http\Resources\TaskAssignmentsResource;
use App\Models\PbgTask;
use App\Models\TaskAssignment;
use Illuminate\Http\Request;
class QuickSearchController extends Controller
{
public function index(){
return view("quick-search.index");
}
public function search_result(Request $request){
$keyword = $request->get("keyword");
return view('quick-search.result', compact('keyword'));
}
public function quick_search_datatable(Request $request)
{
try {
$query = PbgTask::orderBy('id', 'desc');
if ($request->filled('search')) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('name', 'LIKE', "%$search%")
->orWhere('registration_number', 'LIKE', "%$search%")
->orWhere('address', 'LIKE', "%$search%")
->orWhere('document_number', 'LIKE', "%$search%");
});
}
return response()->json($query->paginate());
} catch (\Throwable $e) {
\Log::error("Error fetching datatable data: " . $e->getMessage());
return response()->json([
'message' => 'Terjadi kesalahan saat mengambil data.',
'error' => $e->getMessage(),
], 500);
}
}
public function show($id)
{
try {
$data = PbgTask::with([
'pbg_task_retributions',
'pbg_task_index_integrations',
'pbg_task_retributions.pbg_task_prasarana'
])->findOrFail($id);
$statusOptions = PbgTaskStatus::getStatuses();
$applicationTypes = PbgTaskApplicationTypes::labels();
return view("quick-search.detail", compact("data", 'statusOptions', 'applicationTypes'));
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
\Log::warning("PbgTask with ID {$id} not found.");
return redirect()->route('quick-search.index')->with('error', 'Data tidak ditemukan.');
} catch (\Throwable $e) {
\Log::error("Error in QuickSearchController@show: " . $e->getMessage());
return response()->view('pages.404', [], 500); // Optional: create `resources/views/errors/500.blade.php`
}
}
public function task_assignments(Request $request, $uuid){
try{
$query = TaskAssignment::query()
->where('pbg_task_uid', $uuid)
->orderBy('id', 'desc');
if ($request->filled('search')) {
$query->where('name', 'like', "%{$request->get('search')}%")
->orWhere('email', 'like', "%{$request->get('search')}%");
}
return TaskAssignmentsResource::collection($query->paginate(config('app.paginate_per_page', 50)));
}catch(\Exception $exception){
return response()->json(['message' => $exception->getMessage()], 500);
}
}
}

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

@@ -15,7 +15,6 @@ class ReportTourismController extends Controller
public function index() public function index()
{ {
$tourismBasedKBLI = TourismBasedKBLI::all(); $tourismBasedKBLI = TourismBasedKBLI::all();
info($tourismBasedKBLI);
return view('report.tourisms.index', compact('tourismBasedKBLI')); return view('report.tourisms.index', compact('tourismBasedKBLI'));
} }
} }

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ReportPaymentRecapsController extends Controller
{
public function index(Request $request){
return view('report-payment-recaps.index');
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ReportPbgPTSPController extends Controller
{
public function index(Request $request){
return view('report-pbg-ptsp.index');
}
}

View File

@@ -2,18 +2,37 @@
namespace App\Http\Controllers\RequestAssignment; namespace App\Http\Controllers\RequestAssignment;
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;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use App\Enums\PbgTaskStatus;
class PbgTaskController extends Controller class PbgTaskController extends Controller
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(Request $request)
{ {
return view('pbg_task.index'); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
$filter = $request->query('filter');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view('pbg_task.index', [
'creator' => $creator,
'updater' => $updater,
'destroyer' => $destroyer,
'filter' => $filter,
'filterOptions' => PbgTaskFilterData::getAllOptions(),
]);
} }
/** /**
@@ -38,7 +57,9 @@ class PbgTaskController extends Controller
public function show(string $id) public function show(string $id)
{ {
$data = PbgTask::with(['pbg_task_retributions','pbg_task_index_integrations', 'pbg_task_retributions.pbg_task_prasarana'])->findOrFail($id); $data = PbgTask::with(['pbg_task_retributions','pbg_task_index_integrations', 'pbg_task_retributions.pbg_task_prasarana'])->findOrFail($id);
return view("pbg_task.show", compact("data")); $statusOptions = PbgTaskStatus::getStatuses();
$applicationTypes = PbgTaskApplicationTypes::labels();
return view("pbg_task.show", compact("data", 'statusOptions', 'applicationTypes'));
} }
/** /**

View File

@@ -0,0 +1,295 @@
<?php
namespace App\Http\Controllers;
use App\Models\RetributionProposal;
use App\Models\SpatialPlanning;
use App\Models\BuildingFunction;
use App\Services\RetributionProposalService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
class RetributionProposalController extends Controller
{
protected RetributionProposalService $proposalService;
public function __construct(RetributionProposalService $proposalService)
{
$this->proposalService = $proposalService;
}
/**
* Display a listing of retribution proposals
*/
public function index(Request $request): JsonResponse
{
$query = RetributionProposal::with(['spatialPlanning', 'buildingFunction', 'retributionFormula']);
// Filtering
if ($request->has('building_function_id')) {
$query->where('building_function_id', $request->building_function_id);
}
if ($request->has('spatial_planning_id')) {
$query->where('spatial_planning_id', $request->spatial_planning_id);
}
if ($request->has('floor_number')) {
$query->where('floor_number', $request->floor_number);
}
if ($request->has('has_spatial_planning')) {
if ($request->boolean('has_spatial_planning')) {
$query->whereNotNull('spatial_planning_id');
} else {
$query->whereNull('spatial_planning_id');
}
}
// Search
if ($request->has('search')) {
$search = $request->search;
$query->where(function ($q) use ($search) {
$q->where('proposal_number', 'like', "%{$search}%")
->orWhere('notes', 'like', "%{$search}%")
->orWhereHas('spatialPlanning', function ($sq) use ($search) {
$sq->where('name', 'like', "%{$search}%");
})
->orWhereHas('buildingFunction', function ($bq) use ($search) {
$bq->where('name', 'like', "%{$search}%");
});
});
}
// Sorting
$sortBy = $request->get('sort_by', 'created_at');
$sortOrder = $request->get('sort_order', 'desc');
$query->orderBy($sortBy, $sortOrder);
// Pagination
$perPage = $request->get('per_page', 15);
$proposals = $query->paginate($perPage);
return response()->json([
'success' => true,
'data' => $proposals,
'meta' => [
'total_amount' => RetributionProposal::sum('total_retribution_amount'),
'total_proposals' => RetributionProposal::count(),
'formatted_total_amount' => 'Rp ' . number_format(RetributionProposal::sum('total_retribution_amount'), 0, ',', '.')
]
]);
}
/**
* Store a new retribution proposal
*/
public function store(Request $request): JsonResponse
{
$request->validate([
'spatial_planning_id' => 'nullable|exists:spatial_plannings,id',
'building_function_id' => 'required|exists:building_functions,id',
'floor_number' => 'required|integer|min:1|max:10',
'floor_area' => 'required|numeric|min:0.01',
'total_building_area' => 'nullable|numeric|min:0.01',
'notes' => 'nullable|string|max:1000'
]);
try {
if ($request->spatial_planning_id) {
$spatialPlanning = SpatialPlanning::findOrFail($request->spatial_planning_id);
$proposal = $this->proposalService->createProposalForSpatialPlanning(
$spatialPlanning,
$request->building_function_id,
$request->floor_number,
$request->floor_area,
$request->total_building_area,
$request->notes
);
} else {
$proposal = $this->proposalService->createStandaloneProposal(
$request->building_function_id,
$request->floor_number,
$request->floor_area,
$request->total_building_area,
$request->notes
);
}
$proposal->load(['spatialPlanning', 'buildingFunction', 'retributionFormula']);
return response()->json([
'success' => true,
'message' => 'Retribution proposal created successfully',
'data' => $proposal
], 201);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to create retribution proposal',
'error' => $e->getMessage()
], 400);
}
}
/**
* Display the specified retribution proposal
*/
public function show(int $id): JsonResponse
{
$proposal = RetributionProposal::with(['spatialPlanning', 'buildingFunction', 'retributionFormula'])
->findOrFail($id);
return response()->json([
'success' => true,
'data' => $proposal
]);
}
/**
* Update the specified retribution proposal
*/
public function update(Request $request, int $id): JsonResponse
{
$proposal = RetributionProposal::findOrFail($id);
$request->validate([
'notes' => 'nullable|string|max:1000'
]);
$proposal->update($request->only(['notes']));
$proposal->load(['spatialPlanning', 'buildingFunction', 'retributionFormula']);
return response()->json([
'success' => true,
'message' => 'Retribution proposal updated successfully',
'data' => $proposal
]);
}
/**
* Remove the specified retribution proposal
*/
public function destroy(int $id): JsonResponse
{
$proposal = RetributionProposal::findOrFail($id);
$proposal->delete();
return response()->json([
'success' => true,
'message' => 'Retribution proposal deleted successfully'
]);
}
/**
* Get retribution proposal statistics
*/
public function statistics(): JsonResponse
{
$stats = $this->proposalService->getStatistics();
return response()->json([
'success' => true,
'data' => $stats
]);
}
/**
* Get total sum of all retribution proposals
*/
public function totalSum(): JsonResponse
{
$totalAmount = RetributionProposal::sum('total_retribution_amount');
return response()->json([
'success' => true,
'data' => [
'total_amount' => $totalAmount,
'formatted_total_amount' => 'Rp ' . number_format($totalAmount, 0, ',', '.'),
'total_proposals' => RetributionProposal::count()
]
]);
}
/**
* Get building functions list
*/
public function buildingFunctions(): JsonResponse
{
$buildingFunctions = BuildingFunction::whereNotNull('parent_id')
->with(['parameter'])
->orderBy('name')
->get();
return response()->json([
'success' => true,
'data' => $buildingFunctions
]);
}
/**
* Auto-detect building function and create proposal
*/
public function autoCreateProposal(Request $request): JsonResponse
{
$request->validate([
'spatial_planning_id' => 'nullable|exists:spatial_plannings,id',
'building_function_text' => 'required|string',
'floor_number' => 'required|integer|min:1|max:10',
'floor_area' => 'required|numeric|min:0.01',
'total_building_area' => 'nullable|numeric|min:0.01',
'notes' => 'nullable|string|max:1000'
]);
try {
// Auto-detect building function
$buildingFunction = $this->proposalService->detectBuildingFunction($request->building_function_text);
if (!$buildingFunction) {
return response()->json([
'success' => false,
'message' => 'Could not detect building function from text',
'suggestion' => 'Please specify building function manually'
], 400);
}
if ($request->spatial_planning_id) {
$spatialPlanning = SpatialPlanning::findOrFail($request->spatial_planning_id);
$proposal = $this->proposalService->createProposalForSpatialPlanning(
$spatialPlanning,
$buildingFunction->id,
$request->floor_number,
$request->floor_area,
$request->total_building_area,
$request->notes
);
} else {
$proposal = $this->proposalService->createStandaloneProposal(
$buildingFunction->id,
$request->floor_number,
$request->floor_area,
$request->total_building_area,
$request->notes
);
}
$proposal->load(['spatialPlanning', 'buildingFunction', 'retributionFormula']);
return response()->json([
'success' => true,
'message' => 'Retribution proposal created successfully with auto-detected building function',
'data' => $proposal,
'detected_building_function' => $buildingFunction->name
], 201);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => 'Failed to create retribution proposal',
'error' => $e->getMessage()
], 400);
}
}
}

View File

@@ -10,23 +10,31 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Auth;
class RolesController extends Controller class RolesController extends Controller
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index() public function index(Request $request)
{ {
return view("roles.index"); $menuId = $request->query('menu_id') ?? $request->input('menu_id');
$permissions = $this->permissions[$menuId]?? []; // Avoid undefined index error
$creator = $permissions['allow_create'] ?? 0;
$updater = $permissions['allow_update'] ?? 0;
$destroyer = $permissions['allow_destroy'] ?? 0;
return view("roles.index", compact('creator', 'updater', 'destroyer', 'menuId'));
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*/ */
public function create() public function create(Request $request)
{ {
return view("roles.create"); $menuId = $request->query('menu_id');
return view("roles.create", compact('menuId'));
} }
/** /**
@@ -59,10 +67,11 @@ class RolesController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*/ */
public function edit(string $id) public function edit(string $id, Request $request)
{ {
$menuId = $request->query('menu_id');
$role = Role::findOrFail($id); $role = Role::findOrFail($id);
return view("roles.edit", compact('role')); return view("roles.edit", compact('role', 'menuId'));
} }
/** /**
@@ -100,12 +109,13 @@ class RolesController extends Controller
} }
} }
public function menu_permission(string $role_id){ public function menu_permission(string $role_id, Request $request){
try{ try{
$menuId = $request->query('menu_id');
$role = Role::findOrFail($role_id); $role = Role::findOrFail($role_id);
$menus = Menu::all(); $menus = Menu::all();
$role_menus = RoleMenu::where('role_id', $role_id)->get() ?? collect(); $role_menus = RoleMenu::where('role_id', $role_id)->get() ?? collect();
return view('roles.role_menu', compact('role', 'menus', 'role_menus')); return view('roles.role_menu', compact('role', 'menus', 'role_menus', 'menuId'));
}catch(\Exception $e){ }catch(\Exception $e){
return redirect()->back()->with("error", $e->getMessage()); return redirect()->back()->with("error", $e->getMessage());
} }
@@ -113,8 +123,9 @@ class RolesController extends Controller
public function update_menu_permission(Request $request, string $role_id){ public function update_menu_permission(Request $request, string $role_id){
try{ try{
$menuId = $request->query('menu_id');
$validateData = $request->validate([ $validateData = $request->validate([
"permissions" => "array", "permissions" => "nullable|array",
"permissions.*.allow_show" => "nullable|boolean", "permissions.*.allow_show" => "nullable|boolean",
"permissions.*.allow_create" => "nullable|boolean", "permissions.*.allow_create" => "nullable|boolean",
"permissions.*.allow_update" => "nullable|boolean", "permissions.*.allow_update" => "nullable|boolean",
@@ -123,6 +134,13 @@ class RolesController extends Controller
$role = Role::find($role_id); $role = Role::find($role_id);
// Jika `permissions` tidak ada atau kosong, hapus semua permissions terkait
if (!isset($validateData['permissions']) || empty($validateData['permissions'])) {
$role->menus()->detach();
return redirect()->route("roles.index", ['menu_id' => $menuId])
->with('success', 'All menu permissions have been removed.');
}
$permissionsArray = []; $permissionsArray = [];
foreach ($validateData['permissions'] as $menu_id => $permission) { foreach ($validateData['permissions'] as $menu_id => $permission) {
$permissionsArray[$menu_id] = [ $permissionsArray[$menu_id] = [
@@ -137,7 +155,7 @@ class RolesController extends Controller
// Sync will update existing records and insert new ones // Sync will update existing records and insert new ones
$role->menus()->sync($permissionsArray); $role->menus()->sync($permissionsArray);
return redirect()->route("role-menu.permission", $role_id)->with('success','Menu Permission updated successfully'); return redirect()->route("roles.index", ['menu_id' => $menuId])->with('success','Menu Permission updated successfully');
}catch(\Exception $e){ }catch(\Exception $e){
Log::error("Error updating role_menu:", ["error" => $e->getMessage()]); Log::error("Error updating role_menu:", ["error" => $e->getMessage()]);
return redirect()->route("role-menu.permission", $role_id)->with("error", $e->getMessage()); return redirect()->route("role-menu.permission", $role_id)->with("error", $e->getMessage());

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

View File

@@ -6,6 +6,8 @@ use App\Http\Controllers\Controller;
use App\Services\ServiceSIMBG; use App\Services\ServiceSIMBG;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Exception; use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class SyncronizeController extends Controller class SyncronizeController extends Controller
{ {
protected $service_simbg; protected $service_simbg;
@@ -13,7 +15,27 @@ class SyncronizeController extends Controller
$this->service_simbg = $service_simbg; $this->service_simbg = $service_simbg;
} }
public function index(Request $request){ public function index(Request $request){
return view('settings.syncronize.index'); $menuId = $request->query('menu_id');
$user = Auth::user();
$userId = $user->id;
// Ambil role_id yang dimiliki user
$roleIds = DB::table('user_role')
->where('user_id', $userId)
->pluck('role_id');
// Ambil data akses berdasarkan role_id dan menu_id
$roleAccess = DB::table('role_menu')
->whereIn('role_id', $roleIds)
->where('menu_id', $menuId)
->first();
// Pastikan roleAccess tidak null sebelum mengakses properti
$creator = $roleAccess->allow_create ?? 0;
$updater = $roleAccess->allow_update ?? 0;
$destroyer = $roleAccess->allow_destroy ?? 0;
return view('settings.syncronize.index', compact('creator', 'updater', 'destroyer'));
} }
public function syncPbgTask(){ public function syncPbgTask(){
@@ -33,7 +55,7 @@ class SyncronizeController extends Controller
public function syncIndexIntegration(Request $request, $uuid){ public function syncIndexIntegration(Request $request, $uuid){
$token = $request->get('token'); $token = $request->get('token');
$res = $this->service_simbg->syncIndexIntegration($uuid, $token); $res = $this->service_simbg->syncIndexIntegration($uuid);
return $res; return $res;
} }
@@ -42,4 +64,9 @@ class SyncronizeController extends Controller
$res = $this->service_simbg->syncTaskDetailSubmit($uuid, $token); $res = $this->service_simbg->syncTaskDetailSubmit($uuid, $token);
return $res; return $res;
} }
public function syncTaskAssignments($uuid){
$res = $this->service_simbg->syncTaskAssignments($uuid);
return $res;
}
} }

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class TpatptsController extends Controller
{
public function index()
{
return view('tpa-tpt.index');
}
public function create()
{
//
}
public function show(string $id)
{
//
}
public function edit(string $id)
{
//
}
}

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

@@ -2,6 +2,7 @@
namespace App\Http\Resources; namespace App\Http\Resources;
use Carbon\Carbon;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\JsonResource;
@@ -14,13 +15,21 @@ class ImportDatasourceResource extends JsonResource
*/ */
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
$startTime = $this->start_time ? Carbon::parse($this->start_time) : null;
$finishTime = $this->finish_time ? Carbon::parse($this->finish_time) : null;
return [ return [
"id"=> $this->id, "id"=> $this->id,
"message" => $this->message, "message" => $this->message,
"response_body" => $this->response_body, "response_body" => $this->response_body,
"status" => $this->status, "status" => $this->status,
"start_time" => $startTime ? $startTime->toDateTimeString() : null,
"duration" => ($startTime && $finishTime)
? $finishTime->diff($startTime)->format('%H:%I:%S')
: null,
"finish_time" => $finishTime ? $finishTime->toDateTimeString() : null,
"created_at" => $this->created_at->toDateTimeString(), "created_at" => $this->created_at->toDateTimeString(),
"updated_at" => $this->updated_at->toDateTimeString(), "updated_at" => $this->updated_at->toDateTimeString(),
"failed_uuid" => $this->failed_uuid
]; ];
} }
} }

View File

@@ -14,6 +14,16 @@ class MenuResource extends JsonResource
*/ */
public function toArray(Request $request): array public function toArray(Request $request): array
{ {
return parent::toArray($request); // return parent::toArray($request);
return [
'id' => $this->id,
'name' => $this->name,
'icon' => $this->icon,
'url' => $this->url,
'sort_order' => $this->sort_order,
'parent' => $this->parent ? new MenuResource($this->parent) : null,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at
];
} }
} }

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PbgTaskGoogleSheetResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -34,6 +34,14 @@ class RequestAssignmentResouce extends JsonResource
'due_date' => $this->due_date, 'due_date' => $this->due_date,
'land_certificate_phase' => $this->land_certificate_phase, 'land_certificate_phase' => $this->land_certificate_phase,
'task_created_at' => $this->task_created_at, 'task_created_at' => $this->task_created_at,
'attachment_berita_acara' => $this->attachments
->where('pbg_type', 'berita_acara')
->sortByDesc('created_at')
->first(),
'attachment_bukti_bayar' => $this->attachments
->where('pbg_type', 'bukti_bayar')
->sortByDesc('created_at')
->first(),
]; ];
} }
} }

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TaskAssignmentsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace App\Jobs;
use App\Enums\ImportDatasourceStatus;
use App\Models\BigdataResume;
use App\Models\ImportDatasource;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use App\Services\ServiceTabPbgTask;
use App\Services\ServiceGoogleSheet;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class RetrySyncronizeJob implements ShouldQueue
{
use Queueable, Dispatchable, InteractsWithQueue, SerializesModels;
private $import_datasource_id;
public function __construct(int $import_datasource_id)
{
$this->import_datasource_id = $import_datasource_id;
}
/**
* Execute the job.
*/
public function handle(): void
{
try{
$service_tab_pbg_task = app(ServiceTabPbgTask::class);
$service_google_sheet = app(ServiceGoogleSheet::class);
$failed_import = ImportDatasource::find($this->import_datasource_id);
$failed_import->update([
'message' => "Retrying from UUID: ". $failed_import->failed_uuid,
'status' => ImportDatasourceStatus::Processing->value,
'start_time' => now()
]);
$current_failed_uuid = null;
try{
$service_tab_pbg_task->run_service($failed_import->failed_uuid);
}catch(\Exception $e){
$current_failed_uuid = $service_tab_pbg_task->getFailedUUID();
throw $e;
}
$data_setting_result = $service_google_sheet->get_big_resume_data();
BigdataResume::generateResumeData($failed_import->id, "all", $data_setting_result);
BigdataResume::generateResumeData($failed_import->id, now()->year, $data_setting_result);
$failed_import->update([
'status' => ImportDatasourceStatus::Success->value,
'message' => "Retry completed successfully from UUID: ". $failed_import->failed_uuid,
'finish_time' => now(),
'failed_uuid' => null
]);
}catch(\Exception $e){
\Log::error("RetrySyncronizeJob Failed: ". $e->getMessage(), [
'exception' => $e,
]);
if(isset($failed_import)){
$failed_import->update([
'status' => ImportDatasourceStatus::Failed->value,
'message' => "Retry failed from UUID: ". $failed_import->failed_uuid,
'finish_time' => now(),
'failed_uuid' => $current_failed_uuid
]);
}
$this->fail($e);
}
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace App\Jobs;
use App\Models\BigdataResume;
use App\Models\ImportDatasource;
use App\Services\ServiceGoogleSheet;
use App\Services\ServicePbgTask;
use App\Services\ServiceTabPbgTask;
use App\Services\ServiceTokenSIMBG;
use GuzzleHttp\Client;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class ScrapingDataJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Inject dependencies instead of creating them inside.
*/
public function __construct(
) {
}
/**
* Execute the job.
*/
public function handle()
{
try {
$client = app(Client::class);
$service_pbg_task = app(ServicePbgTask::class);
$service_tab_pbg_task = app(ServiceTabPbgTask::class);
$service_google_sheet = app(ServiceGoogleSheet::class);
$service_token = app(ServiceTokenSIMBG::class);
// Create a record with "processing" status
$import_datasource = ImportDatasource::create([
'message' => 'Initiating scraping...',
'response_body' => null,
'status' => 'processing',
'start_time' => now(),
'failed_uuid' => null
]);
$failed_uuid = null;
// Run the scraping services
$service_google_sheet->run_service();
$service_pbg_task->run_service();
try{
$service_tab_pbg_task->run_service();
}catch(\Exception $e){
$failed_uuid = $service_tab_pbg_task->getFailedUUID();
throw $e;
}
// $data_setting_result = $service_google_sheet->get_big_resume_data();
// BigdataResume::generateResumeData($import_datasource->id, "all", $data_setting_result);
// BigdataResume::generateResumeData($import_datasource->id, now()->year, $data_setting_result);
// Update status to success
$import_datasource->update([
'status' => 'success',
'message' => 'Scraping completed successfully.',
'finish_time' => now()
]);
} catch (\Exception $e) {
Log::error('Scraping failed: ' . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
// Update status to failed
if (isset($import_datasource)) {
$import_datasource->update([
'status' => 'failed',
'response_body' => 'Terjadi kesalahan, Syncronize tidak selesai',
'finish_time' => now(),
'failed_uuid' => $failed_uuid,
]);
}
// Mark the job as failed
$this->fail($e);
}
}
}

View File

@@ -2,8 +2,12 @@
namespace App\Jobs; namespace App\Jobs;
use App\Models\ImportDatasource;
use App\Services\GoogleSheetService; use App\Services\GoogleSheetService;
use App\Services\ServiceSIMBG; use App\Services\ServiceGoogleSheet;
use App\Services\ServicePbgTask;
use App\Services\ServiceTabPbgTask;
use GuzzleHttp\Client;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
@@ -14,14 +18,63 @@ class SyncronizeSIMBG implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $tries = 1;
public function __construct() public function __construct()
{ {
// Avoid injecting non-serializable dependencies here
} }
public function handle(): void public function handle(): void
{ {
$serviceSIMBG = app(ServiceSIMBG::class); $import_datasource = ImportDatasource::where('status', 'processing')->first();
$serviceSIMBG->syncTaskPBG();
if (!$import_datasource) {
$import_datasource = ImportDatasource::create([
'message' => 'Initiating scraping...',
'response_body' => null,
'status' => 'processing'
]);
}
try {
// Create an instance of GuzzleHttp\Client inside handle()
$client = new Client();
// Create instances of services inside handle()
$service_pbg_task = app(ServicePbgTask::class);
$service_tab_pbg_task = app(ServiceTabPbgTask::class);
// Create a record with "processing" status
// Run the service
$service_google_sheet = new ServiceGoogleSheet();
\Log::info('Starting Google Sheet service');
$service_google_sheet->run_service();
\Log::info('Google Sheet service completed');
\Log::info('Starting PBG Task service');
$service_pbg_task->run_service();
\Log::info('PBG Task service completed');
\Log::info('Starting Tab PBG Task service');
$service_tab_pbg_task->run_service();
\Log::info('Tab PBG Task service completed');
// Update the record status to "success" after completion
$import_datasource->update([
'status' => 'success',
'message' => 'Scraping completed successfully.'
]);
} catch (\Exception $e) {
\Log::error("SyncronizeSIMBG Job Failed: " . $e->getMessage(), [
'exception' => $e,
]);
$import_datasource->update([
'status' => 'failed',
'message' => 'Failed job'
]);
$this->fail($e); // Mark the job as failed
}
} }
} }

View File

@@ -0,0 +1,167 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\HasMany;
class BuildingFunction extends Model
{
protected $table = 'building_functions';
protected $fillable = [
'code',
'name',
'description',
'parent_id',
'level',
'sort_order',
'base_tariff'
];
protected $casts = [
'level' => 'integer',
'sort_order' => 'integer',
'base_tariff' => 'decimal:2'
];
/**
* Parent relationship (self-referencing)
*/
public function parent(): BelongsTo
{
return $this->belongsTo(BuildingFunction::class, 'parent_id');
}
/**
* Children relationship (self-referencing)
*/
public function children(): HasMany
{
return $this->hasMany(BuildingFunction::class, 'parent_id')
->orderBy('sort_order');
}
/**
* Parameters relationship (1:1)
*/
public function parameter(): HasOne
{
return $this->hasOne(BuildingFunctionParameter::class);
}
/**
* Formulas relationship (1:n) - multiple formulas for different floor numbers
*/
public function formulas(): HasMany
{
return $this->hasMany(RetributionFormula::class)->orderBy('floor_number');
}
/**
* Get appropriate formula based on floor number
*/
public function getFormulaForFloor(int $floorNumber): ?RetributionFormula
{
return $this->formulas()
->where('floor_number', $floorNumber)
->first();
}
/**
* Retribution proposals relationship (1:n)
*/
public function retributionProposals(): HasMany
{
return $this->hasMany(RetributionProposal::class, 'building_function_id');
}
/**
* Scope: Parent functions only
*/
public function scopeParents($query)
{
return $query->whereNull('parent_id');
}
/**
* Scope: Children functions only
*/
public function scopeChildren($query)
{
return $query->whereNotNull('parent_id');
}
/**
* Scope: By level
*/
public function scopeByLevel($query, int $level)
{
return $query->where('level', $level);
}
/**
* Check if building function has complete setup (parameters + at least one formula)
*/
public function hasCompleteSetup(): bool
{
return $this->parameter()->exists() && $this->formulas()->exists();
}
/**
* Get building function with all related data
*/
public function getCompleteData(): array
{
return [
'id' => $this->id,
'code' => $this->code,
'name' => $this->name,
'description' => $this->description,
'level' => $this->level,
'sort_order' => $this->sort_order,
'base_tariff' => $this->base_tariff,
'parameters' => $this->parameter?->getParametersArray(),
'formulas' => $this->formulas->map(function ($formula) {
return [
'id' => $formula->id,
'name' => $formula->name,
'expression' => $formula->formula_expression,
'floor_number' => $formula->floor_number,
'floor_description' => $this->getFloorDescription($formula->floor_number)
];
})->toArray(),
'has_complete_setup' => $this->hasCompleteSetup()
];
}
/**
* Get floor description
*/
public function getFloorDescription(?int $floorNumber): string
{
if ($floorNumber === null || $floorNumber === 0) {
return 'Semua lantai';
}
return "Lantai {$floorNumber}";
}
/**
* Check if this is a parent function
*/
public function isParent(): bool
{
return $this->parent_id === null;
}
/**
* Check if this is a child function
*/
public function isChild(): bool
{
return $this->parent_id !== null;
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class BuildingFunctionFormulaConfig extends Model
{
use HasFactory;
protected $fillable = [
'building_function_id',
'formula_id',
'floor_level',
'min_floor',
'max_floor',
'multiplier',
'is_active',
'priority',
'notes',
];
protected $casts = [
'multiplier' => 'decimal:4',
'is_active' => 'boolean',
'min_floor' => 'integer',
'max_floor' => 'integer',
'floor_level' => 'integer',
'priority' => 'integer',
];
/**
* Relationship to BuildingFunction
*/
public function buildingFunction()
{
return $this->belongsTo(BuildingFunction::class, 'building_function_id');
}
/**
* Relationship to MasterFormula
*/
public function formula()
{
return $this->belongsTo(MasterFormula::class, 'formula_id');
}
/**
* Scope untuk konfigurasi aktif
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
/**
* Scope untuk mencari berdasarkan building function dan floor level
*/
public function scopeForBuildingAndFloor($query, $buildingFunctionId, $floorLevel)
{
return $query->where('building_function_id', $buildingFunctionId)
->where(function($q) use ($floorLevel) {
$q->where('floor_level', $floorLevel)
->orWhere('floor_level', 0) // 0 = berlaku untuk semua lantai
->orWhere(function($qq) use ($floorLevel) {
$qq->whereNotNull('min_floor')
->whereNotNull('max_floor')
->where('min_floor', '<=', $floorLevel)
->where('max_floor', '>=', $floorLevel);
});
})
->orderBy('priority', 'desc')
->orderBy('floor_level', 'desc'); // Prioritas: spesifik floor > range > semua lantai
}
/**
* Method untuk mendapatkan formula yang tepat untuk building function dan floor tertentu
*/
public static function getFormulaForBuildingAndFloor($buildingFunctionId, $floorLevel)
{
return self::active()
->forBuildingAndFloor($buildingFunctionId, $floorLevel)
->with('formula')
->first();
}
/**
* Method untuk check apakah floor level masuk dalam range
*/
public function isFloorInRange($floorLevel)
{
// Jika floor_level = 0, berlaku untuk semua lantai
if ($this->floor_level == 0) {
return true;
}
// Jika ada specific floor level
if ($this->floor_level == $floorLevel) {
return true;
}
// Jika ada range min-max
if (!is_null($this->min_floor) && !is_null($this->max_floor)) {
return $floorLevel >= $this->min_floor && $floorLevel <= $this->max_floor;
}
return false;
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BuildingFunctionParameter extends Model
{
protected $fillable = [
'building_function_id',
'fungsi_bangunan',
'ip_permanen',
'ip_kompleksitas',
'indeks_lokalitas',
'asumsi_prasarana',
'koefisien_dasar',
'faktor_penyesuaian',
'custom_parameters',
'parameter_notes'
];
protected $casts = [
'fungsi_bangunan' => 'decimal:6',
'ip_permanen' => 'decimal:6',
'ip_kompleksitas' => 'decimal:6',
'indeks_lokalitas' => 'decimal:6',
'asumsi_prasarana' => 'decimal:6',
'koefisien_dasar' => 'decimal:6',
'faktor_penyesuaian' => 'decimal:6',
'custom_parameters' => 'array'
];
/**
* Building function relationship (1:1)
*/
public function buildingFunction(): BelongsTo
{
return $this->belongsTo(BuildingFunction::class);
}
/**
* Scope: Active parameters only
*/
public function scopeActive($query)
{
return $query->where('is_active', true);
}
/**
* Get all parameter values as array
*/
public function getParametersArray(): array
{
return [
'fungsi_bangunan' => $this->fungsi_bangunan,
'ip_permanen' => $this->ip_permanen,
'ip_kompleksitas' => $this->ip_kompleksitas,
'indeks_lokalitas' => $this->indeks_lokalitas,
'asumsi_prasarana' => $this->asumsi_prasarana,
'koefisien_dasar' => $this->koefisien_dasar,
'faktor_penyesuaian' => $this->faktor_penyesuaian,
'custom_parameters' => $this->custom_parameters
];
}
/**
* Get formatted parameters for display
*/
public function getFormattedParameters(): array
{
return [
'Fungsi Bangunan' => $this->fungsi_bangunan,
'IP Permanen' => $this->ip_permanen,
'IP Kompleksitas' => $this->ip_kompleksitas,
'Indeks Lokalitas' => $this->indeks_lokalitas,
'Asumsi Prasarana' => $this->asumsi_prasarana,
'Koefisien Dasar' => $this->koefisien_dasar,
'Faktor Penyesuaian' => $this->faktor_penyesuaian
];
}
/**
* Calculate floor result using the main formula
*/
public function calculateFloorResult(float $ipKetinggian): float
{
return $this->fungsi_bangunan * (
$this->ip_permanen +
$this->ip_kompleksitas +
(0.5 * $ipKetinggian)
);
}
/**
* Calculate full retribution for given building area and floor result
*/
public function calculateRetribution(float $luasBangunan, float $floorResult): float
{
$baseValue = 70350; // Base retribution value
$mainCalculation = 1 * $luasBangunan * ($this->indeks_lokalitas * $baseValue * $floorResult * 1);
$additionalCalculation = 0.5 * $mainCalculation;
return $mainCalculation + $additionalCalculation;
}
/**
* Apply custom parameters if available
*/
public function getParameterValue(string $key, $default = null)
{
// First check if it's a standard parameter
if (property_exists($this, $key) && $this->$key !== null) {
return $this->$key;
}
// Then check custom parameters
if ($this->custom_parameters && is_array($this->custom_parameters)) {
return $this->custom_parameters[$key] ?? $default;
}
return $default;
}
/**
* Set custom parameter
*/
public function setCustomParameter(string $key, $value): void
{
$customParams = $this->custom_parameters ?? [];
$customParams[$key] = $value;
$this->custom_parameters = $customParams;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class FloorHeightIndex extends Model
{
protected $fillable = [
'floor_number',
'ip_ketinggian',
'description'
];
protected $casts = [
'floor_number' => 'integer',
'ip_ketinggian' => 'decimal:6'
];
/**
* Get IP ketinggian by floor number
*/
public static function getIpKetinggianByFloor(int $floorNumber): float
{
$index = self::where('floor_number', $floorNumber)->first();
return $index ? (float) $index->ip_ketinggian : 1.0;
}
/**
* Get all IP ketinggian mapping as array
*/
public static function getAllMapping(): array
{
return self::orderBy('floor_number')
->pluck('ip_ketinggian', 'floor_number')
->toArray();
}
/**
* Get available floor numbers
*/
public static function getAvailableFloors(): array
{
return self::orderBy('floor_number')
->pluck('floor_number')
->toArray();
}
/**
* Scope: By floor number
*/
public function scopeByFloor($query, int $floorNumber)
{
return $query->where('floor_number', $floorNumber);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class FormulaParameter extends Model
{
use HasFactory;
protected $fillable = [
'formula_id',
'parameter_id',
];
/**
* Relationship to MasterFormula
*/
public function formula()
{
return $this->belongsTo(MasterFormula::class, 'formula_id');
}
/**
* Relationship to MasterParameter
*/
public function parameter()
{
return $this->belongsTo(MasterParameter::class, 'parameter_id');
}
}

View File

@@ -13,6 +13,9 @@ class ImportDatasource extends Model
'id', 'id',
'message', 'message',
'response_body', 'response_body',
'status' 'status',
'start_time',
'finish_time',
'failed_uuid'
]; ];
} }

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MasterFormula extends Model
{
use HasFactory;
protected $fillable = [
'formula_code',
'formula_name',
'formula_expression',
'formula_category',
'description',
'usage_notes',
];
/**
* Relationship to FormulaParameter (Many-to-Many dengan MasterParameter)
*/
public function formulaParameters()
{
return $this->hasMany(FormulaParameter::class, 'formula_id');
}
/**
* Relationship to MasterParameter through FormulaParameter
*/
public function parameters()
{
return $this->belongsToMany(MasterParameter::class, 'formula_parameters', 'formula_id', 'parameter_id');
}
/**
* Relationship to BuildingFunctionFormulaConfig
*/
public function buildingConfigs()
{
return $this->hasMany(BuildingFunctionFormulaConfig::class, 'formula_id');
}
/**
* Scope untuk mencari berdasarkan kategori
*/
public function scopeByCategory($query, $category)
{
return $query->where('formula_category', $category);
}
/**
* Method untuk mendapatkan parameter yang diperlukan formula ini
*/
public function getRequiredParameters()
{
return $this->parameters()->get();
}
/**
* Method untuk evaluate formula dengan parameter values
*/
public function evaluateFormula($parameterValues = [])
{
$expression = $this->formula_expression;
// Replace parameter codes dengan nilai actual
foreach ($parameterValues as $parameterCode => $value) {
$expression = str_replace('{' . $parameterCode . '}', $value, $expression);
}
// Evaluasi mathematical expression (hati-hati dengan security!)
// Untuk production, gunakan library yang aman seperti SymfonyExpressionLanguage
try {
return eval("return $expression;");
} catch (Exception $e) {
throw new \Exception("Error evaluating formula: " . $e->getMessage());
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class MasterParameter extends Model
{
use HasFactory;
protected $fillable = [
'parameter_code',
'parameter_name',
'default_value',
'unit',
'description',
];
protected $casts = [
'default_value' => 'decimal:6',
];
/**
* Relationship to FormulaParameter
*/
public function formulaParameters()
{
return $this->hasMany(FormulaParameter::class, 'parameter_id');
}
/**
* Relationship to MasterFormula through FormulaParameter
*/
public function formulas()
{
return $this->belongsToMany(MasterFormula::class, 'formula_parameters', 'parameter_id', 'formula_id');
}
/**
* Scope untuk mencari berdasarkan parameter code
*/
public function scopeByCode($query, $code)
{
return $query->where('parameter_code', $code);
}
/**
* Method untuk mendapatkan nilai parameter dengan fallback ke default
*/
public function getValue($customValue = null)
{
return $customValue !== null ? $customValue : $this->default_value;
}
}

View File

@@ -22,4 +22,7 @@ class Menu extends Model
public function children(){ public function children(){
return $this->hasMany(Menu::class,'parent_id'); return $this->hasMany(Menu::class,'parent_id');
} }
public function parent(){
return $this->belongsTo(Menu::class,'parent_id');
}
} }

View File

@@ -38,7 +38,20 @@ 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');
} }
public function taskAssignments()
{
return $this->hasMany(TaskAssignment::class, 'pbg_task_uid', 'uuid');
}
public function attachments(){
return $this->hasMany(PbgTaskAttachment::class, 'pbg_task_id', 'id');
}
} }

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PbgTaskAttachment extends Model
{
protected $fillable = ['pbg_task_id', 'file_name', 'file_path', 'pbg_type'];
public function task(){
return $this->belongsTo(PbgTask::class);
}
}

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,227 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class RetributionFormula extends Model
{
protected $fillable = [
'building_function_id',
'name',
'floor_number',
'formula_expression'
];
protected $casts = [
'floor_number' => 'integer'
];
/**
* Building function relationship (n:1)
*/
public function buildingFunction(): BelongsTo
{
return $this->belongsTo(BuildingFunction::class);
}
/**
* Retribution proposals relationship (1:n)
*/
public function retributionProposals(): HasMany
{
return $this->hasMany(RetributionProposal::class);
}
/**
* Scope: By floor number
*/
public function scopeByFloor($query, int $floorNumber)
{
return $query->where('floor_number', $floorNumber);
}
/**
* Execute formula calculation with parameters and IP ketinggian
*/
public function calculate(float $luasBangunan, array $parameters, float $ipKetinggian): float
{
// Extract parameters
$fungsi_bangunan = $parameters['fungsi_bangunan'] ?? 0;
$ip_permanen = $parameters['ip_permanen'] ?? 0;
$ip_kompleksitas = $parameters['ip_kompleksitas'] ?? 0;
$indeks_lokalitas = $parameters['indeks_lokalitas'] ?? 0;
// Calculate H13 (floor coefficient) using Excel formula
$h13 = $fungsi_bangunan * ($ip_permanen + $ip_kompleksitas + (0.5 * $ipKetinggian));
// Calculate full retribution using Excel formula
$baseValue = 70350;
$additionalFactor = 0.5;
// Main calculation: (1*D13*(N13*70350*H13*1))
$mainCalculation = 1 * $luasBangunan * ($indeks_lokalitas * $baseValue * $h13 * 1);
// Additional calculation: ($O$3*(1*D13*(N13*70350*H13*1)))
$additionalCalculation = $additionalFactor * $mainCalculation;
// Total: main + additional
return $mainCalculation + $additionalCalculation;
}
/**
* Get formula with parameter placeholders replaced for display
*/
public function getDisplayFormula(array $parameters = []): string
{
$formula = $this->formula_expression;
if (!empty($parameters)) {
foreach ($parameters as $key => $value) {
$formula = str_replace($key, "({$key}={$value})", $formula);
}
}
return $formula;
}
/**
* Check if this formula applies to given floor number
*/
public function appliesTo(int $floorNumber): bool
{
// If floor_number is 0, formula applies to all floors
if ($this->floor_number === 0) {
return true;
}
// Otherwise, exact match required
return $this->floor_number == $floorNumber;
}
/**
* Get human readable floor description
*/
public function getFloorDescription(): string
{
if ($this->floor_number === 0) {
return 'Semua lantai';
}
return "Lantai {$this->floor_number}";
}
/**
* Get default formula expression
*/
public static function getDefaultFormula(): string
{
return '(fungsi_bangunan * (ip_permanen + ip_kompleksitas + (0.5 * ip_ketinggian)))';
}
/**
* Validate formula expression syntax
*/
public function validateFormula(): bool
{
// Basic validation - check if formula contains required variables
$requiredVariables = ['fungsi_bangunan', 'ip_permanen', 'ip_kompleksitas', 'ip_ketinggian'];
foreach ($requiredVariables as $variable) {
if (strpos($this->formula_expression, $variable) === false) {
return false;
}
}
return true;
}
/**
* Get calculation breakdown for debugging
*/
public function getCalculationBreakdown(float $luasBangunan, array $parameters, float $ipKetinggian): array
{
// Extract parameters
$fungsi_bangunan = $parameters['fungsi_bangunan'] ?? 0;
$ip_permanen = $parameters['ip_permanen'] ?? 0;
$ip_kompleksitas = $parameters['ip_kompleksitas'] ?? 0;
$indeks_lokalitas = $parameters['indeks_lokalitas'] ?? 0;
// Calculate H13 (floor coefficient)
$h13 = $fungsi_bangunan * ($ip_permanen + $ip_kompleksitas + (0.5 * $ipKetinggian));
// Calculate components
$baseValue = 70350;
$additionalFactor = 0.5;
$mainCalculation = 1 * $luasBangunan * ($indeks_lokalitas * $baseValue * $h13 * 1);
$additionalCalculation = $additionalFactor * $mainCalculation;
$totalCalculation = $mainCalculation + $additionalCalculation;
return [
'input_parameters' => [
'luas_bangunan' => $luasBangunan,
'fungsi_bangunan' => $fungsi_bangunan,
'ip_permanen' => $ip_permanen,
'ip_kompleksitas' => $ip_kompleksitas,
'ip_ketinggian' => $ipKetinggian,
'indeks_lokalitas' => $indeks_lokalitas,
'base_value' => $baseValue,
'additional_factor' => $additionalFactor
],
'calculation_steps' => [
'h13_calculation' => [
'formula' => 'fungsi_bangunan * (ip_permanen + ip_kompleksitas + (0.5 * ip_ketinggian))',
'calculation' => "{$fungsi_bangunan} * ({$ip_permanen} + {$ip_kompleksitas} + (0.5 * {$ipKetinggian}))",
'result' => $h13
],
'main_calculation' => [
'formula' => '1 * luas_bangunan * (indeks_lokalitas * base_value * h13 * 1)',
'calculation' => "1 * {$luasBangunan} * ({$indeks_lokalitas} * {$baseValue} * {$h13} * 1)",
'result' => $mainCalculation
],
'additional_calculation' => [
'formula' => 'additional_factor * main_calculation',
'calculation' => "{$additionalFactor} * {$mainCalculation}",
'result' => $additionalCalculation
],
'total_calculation' => [
'formula' => 'main_calculation + additional_calculation',
'calculation' => "{$mainCalculation} + {$additionalCalculation}",
'result' => $totalCalculation
]
],
'formatted_results' => [
'h13' => number_format($h13, 6),
'main_calculation' => 'Rp ' . number_format($mainCalculation, 2),
'additional_calculation' => 'Rp ' . number_format($additionalCalculation, 2),
'total_calculation' => 'Rp ' . number_format($totalCalculation, 2)
]
];
}
/**
* Check if this formula has been used in any proposals
*/
public function hasProposals(): bool
{
return $this->retributionProposals()->exists();
}
/**
* Get total amount calculated using this formula
*/
public function getTotalCalculatedAmount(): float
{
return $this->retributionProposals()->sum('total_retribution_amount');
}
/**
* Get count of proposals using this formula
*/
public function getProposalCount(): int
{
return $this->retributionProposals()->count();
}
}

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