diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d789f27f4ee080f95e17fcb7ef62dd4643b44b67..9beff3ec9f2a87177ae5d8fe7e4221c43fd798af 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -34,8 +34,14 @@ android {
     buildFeatures {
         viewBinding = true
     }
+
+    packaging {
+        resources.excludes.add("META-INF/DEPENDENCIES")
+    }
 }
 
+
+
 dependencies {
     implementation("com.google.android.gms:play-services-location:21.2.0")
     val roomVersion = "2.6.1"
@@ -67,6 +73,8 @@ dependencies {
     implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
     implementation("androidx.preference:preference:1.2.1")
 
+    implementation("io.github.evanrupert:excelkt:1.0.2")
+
     implementation("androidx.room:room-ktx:$roomVersion")
     ksp("androidx.room:room-compiler:$roomVersion")
 
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ac9740ef99aa043ef84dea2d2b40e1bee641cc1f..da4b0268fb3bee01b0f1eed2e5458c2b9547e803 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <application
         android:name=".ui.transactions.TransactionsApplication"
@@ -50,8 +51,17 @@
             android:exported="true">
         </activity>
 
-
-
     </application>
 
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.VIEW" />
+            <data android:scheme="geo" />
+        </intent>
+        <intent>
+            <action android:name="android.intent.action.VIEW" />
+            <data android:scheme="https" />
+        </intent>
+    </queries>
+
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt
index 26e124b1865ee2317362cdf3300a906f46b81386..4f415981d062e9ffd6f5e1af86636b4a812c704d 100644
--- a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsFragment.kt
@@ -1,18 +1,28 @@
 package com.example.bondoyap.ui.settings
 
+import android.app.AlertDialog
+import android.content.DialogInterface
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.os.Bundle
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Toast
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
 import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
 import androidx.lifecycle.ViewModelProvider
 import androidx.localbroadcastmanager.content.LocalBroadcastManager
 import com.example.bondoyap.databinding.FragmentSettingsBinding
 import com.example.bondoyap.service.api.Constants.ACTION_RANDOMIZE_TRANSACTIONS
 import com.example.bondoyap.ui.login.LoginActivity
+import com.example.bondoyap.ui.transactions.TransactionsApplication
+import com.example.bondoyap.ui.transactions.TransactionsViewModel
+import com.example.bondoyap.ui.transactions.TransactionsViewModelFactory
+import android.Manifest
 
 class SettingsFragment : Fragment() {
 
@@ -20,12 +30,21 @@ class SettingsFragment : Fragment() {
     private val binding get() = _binding!!
     private lateinit var settingsViewModel: SettingsViewModel
 
+    private val transactionsViewModel: TransactionsViewModel by viewModels {
+        TransactionsViewModelFactory((requireContext().applicationContext as TransactionsApplication).repository)
+    }
+
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?
     ): View {
         _binding = FragmentSettingsBinding.inflate(inflater, container, false)
+
+        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+            ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE)
+        }
+
         return binding.root
     }
 
@@ -72,9 +91,32 @@ class SettingsFragment : Fragment() {
             Log.d("BroadcastDebug", "Sending broadcast from SettingsFragment")
         }
 
+        val exporter = TransactionsExporter(transactionsViewModel, requireContext())
+
         saveButton.setOnClickListener {
-            Toast.makeText(appContext, "Menyimpan transaksi...", Toast.LENGTH_SHORT).show()
-            //todo
+            if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+                Toast.makeText(appContext, "Allow storage permission untuk menyimpan transaksi ke file xls/xlsx", Toast.LENGTH_SHORT).show()
+            } else {
+                val formats = arrayOf("XLS", "XLSX")
+                val builder = AlertDialog.Builder(requireContext())
+                builder.setTitle("Pilih Format File")
+                builder.setItems(formats) { dialog: DialogInterface, which: Int ->
+                    when (which) {
+                        0 -> {
+                            Toast.makeText(appContext, "Menyimpan transaksi ke xls...", Toast.LENGTH_SHORT).show()
+                            exporter.exportToXLS()
+                            Toast.makeText(appContext, "Penyimpanan xls pada folder Documents berhasil...", Toast.LENGTH_SHORT).show()
+                        }
+                        1 -> {
+                            Toast.makeText(appContext, "Menyimpan transaksi ke xlsx...", Toast.LENGTH_SHORT).show()
+                            exporter.exportToXLSX()
+                            Toast.makeText(appContext, "Penyimpanan xlsx pada folder Documents berhasil...", Toast.LENGTH_SHORT).show()
+                        }
+                    }
+                    dialog.dismiss()
+                }
+                builder.create().show()
+            }
         }
 
         sendButton.setOnClickListener {
@@ -84,6 +126,10 @@ class SettingsFragment : Fragment() {
 
     }
 
+    companion object {
+        private const val PERMISSION_REQUEST_CODE = 1001
+    }
+
     override fun onDestroyView() {
         super.onDestroyView()
         _binding = null
diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt
index 6038adcbe40a12ade0663f68d32d295401f2690a..126d2f8867aa68b32386f7eb25275f64d1044596 100644
--- a/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/settings/SettingsViewModel.kt
@@ -16,7 +16,6 @@ class SettingsViewModel(private val loginRepository: LoginRepository) : ViewMode
     fun logout(){
         loginRepository.logout()
     }
-
     fun getUser(): LoggedInUser? {
         return loginRepository.getUser()
     }
diff --git a/app/src/main/java/com/example/bondoyap/ui/settings/TransactionsExporter.kt b/app/src/main/java/com/example/bondoyap/ui/settings/TransactionsExporter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..05c76159446de6ac39e1ac0589deff017b8bfe8c
--- /dev/null
+++ b/app/src/main/java/com/example/bondoyap/ui/settings/TransactionsExporter.kt
@@ -0,0 +1,98 @@
+package com.example.bondoyap.ui.settings
+
+import android.content.Context
+import android.location.Address
+import android.location.Geocoder
+import android.os.Environment
+import android.os.Handler
+import android.os.Looper
+import android.util.Log
+import androidx.lifecycle.viewModelScope
+import com.example.bondoyap.ui.transactions.TransactionsViewModel
+import com.example.bondoyap.ui.transactions.data.Transactions
+import io.github.evanrupert.excelkt.Sheet
+import io.github.evanrupert.excelkt.workbook
+import kotlinx.coroutines.launch
+import org.apache.poi.ss.usermodel.FillPatternType
+import org.apache.poi.ss.usermodel.IndexedColors
+import java.io.File
+import java.util.Locale
+
+class TransactionsExporter(private val transactionsViewModel: TransactionsViewModel, val context: Context) {
+    fun exportToXLS() {
+        transactionsViewModel.viewModelScope.launch {
+            val transactions = transactionsViewModel.getAllTransactionsList()
+            Log.d("SaveDebug", "xls function")
+            writeToExcel("transactions.xls", transactions)
+        }
+    }
+
+    fun exportToXLSX() {
+        transactionsViewModel.viewModelScope.launch {
+            val transactions = transactionsViewModel.getAllTransactionsList()
+            Log.d("SaveDebug", "xlsx function")
+            writeToExcel("transactions.xlsx", transactions)
+        }
+    }
+
+    private fun writeToExcel(fileName: String, transactions: List<Transactions>) {
+        val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
+        val file = File(downloadsDir, fileName)
+
+        workbook {
+            sheet("Transactions") {
+                transactionsHeader()
+
+                for (transaction in transactions) {
+                    row {
+                        cell(transaction.tanggal)
+                        cell(
+                            if (transaction.isPemasukan) {
+                                "Pemasukan"
+                            } else {
+                                "Pengeluaran"
+                            }
+                        )
+                        cell(transaction.nominal)
+                        cell(transaction.judul)
+                        cell(if (transaction.longitude.isEmpty() || transaction.latitude.isEmpty()) {
+                            "Unavailable"
+                        } else {
+                            val addresses: List<Address> =
+                                Geocoder(context, Locale.getDefault()).getFromLocation(
+                                    transaction.latitude.toDouble(),
+                                    transaction.longitude.toDouble(),
+                                    1
+                                ) ?: emptyList()
+                            if (addresses.isNotEmpty()) {
+                                val locationName = addresses[0].getAddressLine(0)
+                                locationName
+                            } else {
+                                "Unavailable"
+                            }
+                        })
+                    }
+                }
+            }
+        }.write(file.absolutePath)
+    }
+
+
+    private fun Sheet.transactionsHeader() {
+        val headings = listOf("Tanggal", "Kategori Transaksi", "Nominal Transaksi", "Nama Transaksi", "Lokasi")
+
+        val headingStyle = createCellStyle {
+            setFont(createFont {
+                fontName = "IMPACT"
+                color = IndexedColors.BLACK.index
+            })
+
+            fillPattern = FillPatternType.SOLID_FOREGROUND
+            fillForegroundColor = IndexedColors.YELLOW.index
+        }
+
+        row(headingStyle) {
+            headings.forEach { cell(it) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt
index 012172e8ce21819fb181c307aa74520ef627be80..3f5582da6b50d2bb0f8c9f03e2a5e644e783f247 100644
--- a/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/TransactionsViewModel.kt
@@ -28,6 +28,10 @@ class TransactionsViewModel(
         }
         return deferred.await()
     }
+
+    suspend fun getAllTransactionsList(): List<Transactions> {
+        return repository.getAllTransactionsList()
+    }
 }
 
 class TransactionsViewModelFactory(private val repository: TransactionsRepository): ViewModelProvider.Factory {
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt
index d09b3619c20f83a05fc1a292f450f720efc04412..00dd1fc2a43c15e7ee6ac69ca1d725245c1b554c 100644
--- a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsDao.kt
@@ -18,6 +18,9 @@ interface TransactionsDao {
     @Query("SELECT * FROM transactions")
     fun getTransactions(): Flow<List<Transactions>>
 
+    @Query("SELECT * FROM transactions")
+    suspend fun getTransactionsList(): List<Transactions>
+
     @Query("SELECT * FROM transactions WHERE transactions.id == :transactionsId")
     suspend fun getTransactionById(transactionsId: Int?): Transactions
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt
index 2a5dcda7972ac83f2c201507de6d43d2fe30c68d..c376411c484b935896c904d94217d7c0dc023b15 100644
--- a/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt
+++ b/app/src/main/java/com/example/bondoyap/ui/transactions/data/TransactionsRepository.kt
@@ -19,4 +19,8 @@ class TransactionsRepository(private val transactionsDao: TransactionsDao) {
     suspend fun get(transactionId: Int?): Transactions {
         return transactionsDao.getTransactionById(transactionId)
     }
+
+    suspend fun getAllTransactionsList(): List<Transactions> {
+        return transactionsDao.getTransactionsList()
+    }
 }
\ No newline at end of file