moved invoice to feature module (#1663)

This commit is contained in:
Aditya Kumdale 2024-06-18 16:38:15 +05:30 committed by GitHub
parent eea58d0464
commit 85c15b4bd5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 804 additions and 0 deletions

1
feature/invoices/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,14 @@
plugins {
alias(libs.plugins.mifospay.android.feature)
alias(libs.plugins.mifospay.android.library.compose)
}
android {
namespace = "org.mifospay.invoices"
}
dependencies {
implementation(projects.core.data)
implementation(projects.feature.receipt)
implementation(libs.androidx.appcompat)
}

View File

21
feature/invoices/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package org.mifospay.invoices
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("org.mifospay.invoices.test", appContext.packageName)
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>

View File

@ -0,0 +1,21 @@
package org.mifospay.feature.invoices
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
import org.mifospay.core.designsystem.theme.MifosTheme
@AndroidEntryPoint
class InvoiceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val data = intent.data
setContent {
MifosTheme {
InvoiceDetailScreen(data = data,onBackPress = { finish() })
}
}
}
}

View File

@ -0,0 +1,309 @@
package org.mifospay.feature.invoices
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mifospay.core.model.entity.Invoice
import com.mifospay.core.model.utils.DateHelper
import org.mifospay.common.Constants
import org.mifospay.core.designsystem.component.MfOverlayLoadingWheel
import org.mifospay.core.designsystem.component.MifosScaffold
import org.mifospay.core.designsystem.theme.MifosTheme
import org.mifospay.core.designsystem.theme.primaryDarkBlue
import org.mifospay.core.ui.ErrorScreenContent
import org.mifospay.feature.receipt.ReceiptActivity
import org.mifospay.invoices.R
@Composable
fun InvoiceDetailScreen(
viewModel: InvoiceDetailViewModel = hiltViewModel(),
data: Uri?,
onBackPress: () -> Unit
) {
val invoiceDetailUiState by viewModel.invoiceDetailUiState.collectAsStateWithLifecycle()
InvoiceDetailScreen(invoiceDetailUiState = invoiceDetailUiState, onBackPress = onBackPress)
LaunchedEffect(key1 = true) {
viewModel.getInvoiceDetails(data)
}
}
@Composable
fun InvoiceDetailScreen(
invoiceDetailUiState: InvoiceDetailUiState, onBackPress: () -> Unit
) {
MifosScaffold(
topBarTitle = R.string.feature_invoices_invoice,
backPress = { onBackPress.invoke() },
scaffoldContent = {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.White)
.padding(it)
) {
when (invoiceDetailUiState) {
is InvoiceDetailUiState.Error -> {
ErrorScreenContent()
}
InvoiceDetailUiState.Loading -> {
MfOverlayLoadingWheel(
contentDesc = stringResource(R.string.feature_invoices_loading)
)
}
is InvoiceDetailUiState.Success -> {
InvoiceDetailsContent(
invoiceDetailUiState.invoice,
invoiceDetailUiState.merchantId,
invoiceDetailUiState.paymentLink
)
}
}
}
})
}
@Composable
fun InvoiceDetailsContent(invoice: Invoice?, merchantId: String?, paymentLink: String?) {
val clipboardManager = LocalClipboardManager.current
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
val context = LocalContext.current
Text(
text = stringResource(R.string.feature_invoices_invoice_details),
modifier = Modifier.padding(top = 16.dp)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.feature_invoices_merchant_id),
modifier = Modifier.padding(top = 16.dp)
)
Text(
text = merchantId.toString(),
modifier = Modifier.padding(top = 16.dp)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.feature_invoices_consumer_id),
modifier = Modifier.padding(top = 8.dp)
)
Text(
text = (invoice?.consumerName + " " + invoice?.consumerId),
modifier = Modifier.padding(top = 8.dp)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.feature_invoices_amount),
modifier = Modifier.padding(top = 8.dp)
)
Text(
text = Constants.INR + " " + invoice?.amount + "",
modifier = Modifier.padding(top = 8.dp)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.feature_invoices_items_bought),
modifier = Modifier.padding(top = 8.dp)
)
Text(
text = invoice?.itemsBought.toString(),
modifier = Modifier.padding(top = 8.dp)
)
}
if (invoice?.status == 1L) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.feature_invoices_status),
modifier = Modifier.padding(top = 8.dp)
)
Text(
text = Constants.DONE,
modifier = Modifier.padding(top = 8.dp)
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.feature_invoices_transaction_id),
color = primaryDarkBlue,
modifier = Modifier
.padding(top = 10.dp)
)
Text(
text = invoice.transactionId ?: "",
color = primaryDarkBlue,
modifier = Modifier
.padding(top = 10.dp)
.then(Modifier.height(0.dp))
)
}
Divider()
Text(
text = stringResource(id = R.string.feature_invoices_unique_receipt_link),
fontWeight = FontWeight.Normal,
modifier = Modifier.padding(bottom = 10.dp)
)
Text(
text = invoice.transactionId ?: "",
color = primaryDarkBlue,
modifier = Modifier
.padding(top = 10.dp)
.pointerInput(Unit) {
detectTapGestures(
onPress = {
val intent = Intent(context, ReceiptActivity::class.java)
intent.data = Uri.parse(
Constants.RECEIPT_DOMAIN + invoice.transactionId
)
context.startActivity(intent)
},
onLongPress = {
clipboardManager.setText(AnnotatedString(Constants.RECEIPT_DOMAIN + invoice.transactionId))
})
}
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.feature_invoices_date),
modifier = Modifier.padding(top = 10.dp)
)
Text(
text = DateHelper.getDateAsString(invoice!!.date),
modifier = Modifier.padding(top = 10.dp)
)
}
Divider()
Text(
text = stringResource(id = R.string.feature_invoices_payment_options_will_be_fetched_from_upi),
fontWeight = FontWeight.Normal,
modifier = Modifier.padding(vertical = 10.dp)
)
Divider()
Text(
text = stringResource(id = R.string.feature_invoices_unique_payment_link),
fontWeight = FontWeight.Normal,
modifier = Modifier.padding(bottom = 10.dp)
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = paymentLink.toString(),
color = primaryDarkBlue,
modifier = Modifier
.padding(top = 10.dp)
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
clipboardManager.setText(AnnotatedString(paymentLink.toString()))
})
}
)
}
}
}
@Composable
fun Divider(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.height(1.dp)
.background(Color(0x44000000))
)
}
class InvoiceDetailScreenProvider : PreviewParameterProvider<InvoiceDetailUiState> {
override val values: Sequence<InvoiceDetailUiState>
get() = sequenceOf(
InvoiceDetailUiState.Loading,
InvoiceDetailUiState.Error("Some Error Occurred"),
InvoiceDetailUiState.Success(Invoice(), "", "")
)
}
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun InvoiceDetailScreenPreview(
@PreviewParameter(InvoiceDetailScreenProvider::class) invoiceDetailUiState: InvoiceDetailUiState
) {
MifosTheme {
InvoiceDetailScreen(invoiceDetailUiState = invoiceDetailUiState) {}
}
}

View File

@ -0,0 +1,53 @@
package org.mifospay.feature.invoices
import android.net.Uri
import androidx.lifecycle.ViewModel
import com.mifospay.core.model.entity.Invoice
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.mifospay.core.data.base.UseCase
import org.mifospay.core.data.base.UseCaseHandler
import org.mifospay.core.data.domain.usecase.invoice.FetchInvoice
import org.mifospay.core.datastore.PreferencesHelper
import javax.inject.Inject
@HiltViewModel
class InvoiceDetailViewModel @Inject constructor(
private val mUseCaseHandler: UseCaseHandler,
private val mPreferencesHelper: PreferencesHelper,
private val fetchInvoiceUseCase: FetchInvoice
) : ViewModel() {
private val _invoiceDetailUiState =
MutableStateFlow<InvoiceDetailUiState>(InvoiceDetailUiState.Loading)
val invoiceDetailUiState: StateFlow<InvoiceDetailUiState> = _invoiceDetailUiState
fun getInvoiceDetails(data: Uri?) {
mUseCaseHandler.execute(fetchInvoiceUseCase, FetchInvoice.RequestValues(data),
object : UseCase.UseCaseCallback<FetchInvoice.ResponseValue> {
override fun onSuccess(response: FetchInvoice.ResponseValue) {
_invoiceDetailUiState.value = InvoiceDetailUiState.Success(
response.invoices[0],
mPreferencesHelper.fullName + " "
+ mPreferencesHelper.clientId, data.toString()
)
}
override fun onError(message: String) {
_invoiceDetailUiState.value = InvoiceDetailUiState.Error(message)
}
})
}
}
sealed interface InvoiceDetailUiState {
data object Loading : InvoiceDetailUiState
data class Success(
val invoice: Invoice?,
val merchantId: String?,
val paymentLink: String?
) : InvoiceDetailUiState
data class Error(val message: String) : InvoiceDetailUiState
}

View File

@ -0,0 +1,121 @@
package org.mifospay.feature.invoices
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifospay.R
import org.mifospay.core.designsystem.theme.grey
@Composable
fun InvoiceItem(
invoiceTitle: String,
invoiceAmount: String,
invoiceStatus: String,
invoiceDate: String,
invoiceId: String,
invoiceStatusIcon: Long,
onClick: (String) -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(4.dp)
.clickable { onClick(invoiceId) },
elevation = CardDefaults.cardElevation(4.dp),
colors = CardDefaults.cardColors(Color.White)
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter = painterResource(
id = if (invoiceStatusIcon == 0L) R.drawable.ic_remove_circle_outline_black_24dp
else R.drawable.ic_check_round_black_24dp
),
contentDescription = "Invoice Status",
modifier = Modifier
.size(64.dp)
.padding(5.dp),
tint = if (invoiceStatusIcon == 0L) Color.Yellow else Color.Blue
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(end = 10.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = invoiceTitle,
color = Color.Black,
modifier = Modifier.weight(1f)
)
Text(
text = invoiceAmount,
color = Color.Black,
textAlign = TextAlign.End,
modifier = Modifier.weight(1f)
)
}
Text(
text = invoiceStatus,
color = grey,
modifier = Modifier.padding(top = 1.dp)
)
Row(
modifier = Modifier.fillMaxWidth()
) {
Text(
text = invoiceDate,
color = grey,
modifier = Modifier.weight(1f)
)
Text(
text = invoiceId,
color = grey,
textAlign = TextAlign.End,
modifier = Modifier.weight(1f)
)
}
Spacer(
modifier = Modifier
.padding(top = 10.dp)
.fillMaxWidth()
.height(1.dp)
.background(Color.Gray)
)
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun PreviewInvoiceItem() {
InvoiceItem("Logo for Richard", "$3000", "Pending", "12/3/4", "Invoice id:12345", 0L) {}
}

View File

@ -0,0 +1,128 @@
package org.mifospay.feature.invoices
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.mifospay.core.model.entity.Invoice
import org.mifospay.R
import org.mifospay.core.designsystem.component.MifosLoadingWheel
import org.mifospay.core.designsystem.icon.MifosIcons.Info
import org.mifospay.core.designsystem.theme.MifosTheme
import org.mifospay.core.ui.EmptyContentScreen
@Composable
fun InvoiceScreen(
viewModel: InvoicesViewModel = hiltViewModel()
) {
val invoiceUiState by viewModel.invoiceUiState.collectAsStateWithLifecycle()
InvoiceScreen(
invoiceUiState = invoiceUiState,
getUniqueInvoiceLink = { viewModel.getUniqueInvoiceLink(it) }
)
}
@Composable
fun InvoiceScreen(
invoiceUiState: InvoicesUiState,
getUniqueInvoiceLink: (Long) -> Uri?
) {
val context = LocalContext.current
when (invoiceUiState) {
is InvoicesUiState.Error -> {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(id = R.string.error_oops),
subTitle = stringResource(id = R.string.unexpected_error_subtitle),
iconTint = Color.Black,
iconImageVector = Info
)
}
is InvoicesUiState.InvoiceList -> {
Column(modifier = Modifier.fillMaxSize()) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(invoiceUiState.list) {
InvoiceItem(
invoiceTitle = it?.title.toString(),
invoiceAmount = it?.amount.toString(),
invoiceStatus = it?.status.toString(),
invoiceDate = it?.date.toString(),
invoiceId = it?.id.toString(),
invoiceStatusIcon = it?.status!!,
onClick = { invoiceId ->
val intent = Intent(context, InvoiceActivity::class.java)
intent.data = getUniqueInvoiceLink(invoiceId.toLong())
context.startActivity(intent)
}
)
}
}
}
}
InvoicesUiState.Empty -> {
EmptyContentScreen(
modifier = Modifier,
title = stringResource(id = R.string.error_oops),
subTitle = stringResource(id = R.string.error_no_invoices_found),
iconTint = Color.Black,
)
}
InvoicesUiState.Loading -> {
MifosLoadingWheel(
modifier = Modifier.fillMaxWidth(),
contentDesc = stringResource(R.string.loading)
)
}
}
}
class InvoicesUiStateProvider : PreviewParameterProvider<InvoicesUiState> {
override val values: Sequence<InvoicesUiState>
get() = sequenceOf(
InvoicesUiState.Loading,
InvoicesUiState.Empty,
InvoicesUiState.InvoiceList(sampleInvoiceList),
InvoicesUiState.Error("Some Error Occurred")
)
}
@Preview(showBackground = true, showSystemUi = true)
@Composable
private fun InvoiceScreenPreview(
@PreviewParameter(InvoicesUiStateProvider::class) invoiceUiState: InvoicesUiState
) {
MifosTheme {
InvoiceScreen(invoiceUiState = invoiceUiState, getUniqueInvoiceLink = { Uri.EMPTY })
}
}
val sampleInvoiceList = List(10) { index ->
Invoice(
consumerId = "123456",
consumerName = "John Doe",
amount = 250.75,
itemsBought = "2x Notebook, 1x Pen",
status = 1L,
transactionId = "txn_78910",
id = index.toLong(),
title = "Stationery Purchase",
date = mutableListOf(2024, 3, 23)
)
}

View File

@ -0,0 +1,60 @@
package org.mifospay.feature.invoices
import android.net.Uri
import androidx.lifecycle.ViewModel
import com.mifospay.core.model.entity.Invoice
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.mifospay.core.data.base.UseCase
import org.mifospay.core.data.base.UseCaseHandler
import org.mifospay.core.data.domain.usecase.invoice.FetchInvoices
import org.mifospay.common.Constants.INVOICE_DOMAIN
import org.mifospay.core.datastore.PreferencesHelper
import javax.inject.Inject
@HiltViewModel
class InvoicesViewModel @Inject constructor(
private val mUseCaseHandler: UseCaseHandler,
private val mPreferencesHelper: PreferencesHelper,
private val fetchInvoicesUseCase: FetchInvoices
) : ViewModel() {
private val _invoiceUiState = MutableStateFlow<InvoicesUiState>(InvoicesUiState.Loading)
val invoiceUiState: StateFlow<InvoicesUiState> = _invoiceUiState
private fun fetchInvoices() {
_invoiceUiState.value = InvoicesUiState.Loading
mUseCaseHandler.execute(fetchInvoicesUseCase,
FetchInvoices.RequestValues(mPreferencesHelper.clientId.toString() + ""),
object : UseCase.UseCaseCallback<FetchInvoices.ResponseValue> {
override fun onSuccess(response: FetchInvoices.ResponseValue) {
if (response.invoiceList.isNotEmpty())
_invoiceUiState.value = InvoicesUiState.InvoiceList(response.invoiceList)
else _invoiceUiState.value = InvoicesUiState.Empty
}
override fun onError(message: String) {
_invoiceUiState.value = InvoicesUiState.Error(message)
}
})
}
fun getUniqueInvoiceLink(id: Long): Uri? {
return Uri.parse(
INVOICE_DOMAIN + mPreferencesHelper.clientId + "/" + id
)
}
init {
fetchInvoices()
}
}
sealed class InvoicesUiState {
data object Loading : InvoicesUiState()
data object Empty : InvoicesUiState()
data class Error(val message: String) : InvoicesUiState()
data class InvoiceList(val list: List<Invoice?>) : InvoicesUiState()
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="feature_invoices_invoice">Invoice</string>
<string name="feature_invoices_loading">Loading</string>
<string name="feature_invoices_invoice_details">Invoice details</string>
<string name="feature_invoices_merchant_id">Merchant ID</string>
<string name="feature_invoices_consumer_id">Consumer ID</string>
<string name="feature_invoices_amount">Amount</string>
<string name="feature_invoices_items_bought">items bought</string>
<string name="feature_invoices_status">status</string>
<string name="feature_invoices_transaction_id">Transaction ID : </string>
<string name="feature_invoices_unique_receipt_link">Unique Receipt Link : </string>
<string name="feature_invoices_date">date</string>
<string name="feature_invoices_payment_options_will_be_fetched_from_upi">Payment Options (will be fetched from
UPI)
</string>
<string name="feature_invoices_unique_payment_link">Unique Payment Link</string>
</resources>

View File

@ -0,0 +1,17 @@
package org.mifospay.invoices
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@ -44,3 +44,5 @@ include(":feature:editpassword")
include(":feature:kyc")
include(":feature:savedcards")
include(":feature:invoices")